From 93ec2c74ab21a67230c48dfe6a21fa77d661b02b Mon Sep 17 00:00:00 2001 From: bymyself Date: Wed, 26 Nov 2025 15:32:21 -0800 Subject: [PATCH] use update service --- src/App.vue | 23 +----- .../maskeditor/gpu/brushShaders.ts | 12 ++-- .../settings/constants/coreSettings.ts | 2 +- src/platform/settings/settingStore.ts | 3 + .../updates/common/cloudNotificationStore.ts | 71 +++++++++++++++++++ .../CloudNotificationOrchestrator.vue | 42 +++++++++++ src/services/newUserService.ts | 16 +++++ src/views/GraphView.vue | 2 + 8 files changed, 142 insertions(+), 29 deletions(-) create mode 100644 src/platform/updates/common/cloudNotificationStore.ts create mode 100644 src/platform/updates/components/CloudNotificationOrchestrator.vue diff --git a/src/App.vue b/src/App.vue index 7ba2d05f6..6b7c56be0 100644 --- a/src/App.vue +++ b/src/App.vue @@ -17,7 +17,6 @@ import { computed, onMounted } from 'vue' import GlobalDialog from '@/components/dialog/GlobalDialog.vue' import config from '@/config' import { t } from '@/i18n' -import { useSettingStore } from '@/platform/settings/settingStore' import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore' import { app } from '@/scripts/app' import { useDialogService } from '@/services/dialogService' @@ -48,7 +47,7 @@ const showContextMenu = (event: MouseEvent) => { } } -onMounted(async () => { +onMounted(() => { window['__COMFYUI_FRONTEND_VERSION__'] = config.app_version if (isElectron()) { @@ -78,25 +77,5 @@ onMounted(async () => { // Initialize conflict detection in background // This runs async and doesn't block UI setup void conflictDetection.initializeConflictDetection() - - // Show cloud notification for macOS desktop users (one-time) - // Delayed to ensure it appears after workflow loading (missing models dialog, etc.) - if (isElectron()) { - const isMacOS = navigator.platform.toLowerCase().includes('mac') - if (isMacOS) { - const settingStore = useSettingStore() - const hasShownNotification = settingStore.get( - 'Comfy.Desktop.CloudNotificationShown' - ) - - if (!hasShownNotification) { - // Delay to show after initial workflow loading completes - setTimeout(async () => { - dialogService.showCloudNotification() - await settingStore.set('Comfy.Desktop.CloudNotificationShown', true) - }, 2000) - } - } - } }) diff --git a/src/composables/maskeditor/gpu/brushShaders.ts b/src/composables/maskeditor/gpu/brushShaders.ts index 659c9ddb4..9dff94efb 100644 --- a/src/composables/maskeditor/gpu/brushShaders.ts +++ b/src/composables/maskeditor/gpu/brushShaders.ts @@ -1,4 +1,4 @@ -import tgpu from 'typegpu' +import typegpu from 'typegpu' import * as d from 'typegpu/data' import { BrushUniforms } from './gpuSchema' @@ -38,7 +38,7 @@ fn vs( } ` -export const brushVertex = tgpu.resolve({ +export const brushVertex = typegpu.resolve({ template: brushVertexTemplate, externals: { BrushUniforms, @@ -75,7 +75,7 @@ fn fs(v: VertexOutput) -> @location(0) vec4 { } ` -export const brushFragment = tgpu.resolve({ +export const brushFragment = typegpu.resolve({ template: brushFragmentTemplate, externals: { VertexOutput, @@ -100,7 +100,7 @@ const blitShaderTemplate = ` } ` -export const blitShader = tgpu.resolve({ +export const blitShader = typegpu.resolve({ template: blitShaderTemplate, externals: {} }) @@ -123,7 +123,7 @@ const compositeShaderTemplate = ` } ` -export const compositeShader = tgpu.resolve({ +export const compositeShader = typegpu.resolve({ template: compositeShaderTemplate, externals: { BrushUniforms @@ -165,7 +165,7 @@ fn main(@builtin(global_invocation_id) id: vec3) { } ` -export const readbackShader = tgpu.resolve({ +export const readbackShader = typegpu.resolve({ template: readbackShaderTemplate, externals: {} }) diff --git a/src/platform/settings/constants/coreSettings.ts b/src/platform/settings/constants/coreSettings.ts index 9a361e2c3..26aacc6b6 100644 --- a/src/platform/settings/constants/coreSettings.ts +++ b/src/platform/settings/constants/coreSettings.ts @@ -296,7 +296,7 @@ export const CORE_SETTINGS: SettingParams[] = [ { id: 'Comfy.Desktop.CloudNotificationShown', name: 'Cloud notification shown', - type: 'boolean', + type: 'hidden', defaultValue: false }, { diff --git a/src/platform/settings/settingStore.ts b/src/platform/settings/settingStore.ts index 5a1573efb..c8eb93c11 100644 --- a/src/platform/settings/settingStore.ts +++ b/src/platform/settings/settingStore.ts @@ -45,6 +45,7 @@ function onChange( export const useSettingStore = defineStore('setting', () => { const settingValues = ref>({}) const settingsById = ref>({}) + const isInitialized = ref(false) /** * Check if a setting's value exists, i.e. if the user has set it manually. @@ -196,6 +197,7 @@ export const useSettingStore = defineStore('setting', () => { // Migrate old zoom threshold setting to new font size setting await migrateZoomThresholdToFontSize() + isInitialized.value = true } /** @@ -240,6 +242,7 @@ export const useSettingStore = defineStore('setting', () => { return { settingValues, settingsById, + isInitialized, addSetting, loadSettingValues, set, diff --git a/src/platform/updates/common/cloudNotificationStore.ts b/src/platform/updates/common/cloudNotificationStore.ts new file mode 100644 index 000000000..94614217c --- /dev/null +++ b/src/platform/updates/common/cloudNotificationStore.ts @@ -0,0 +1,71 @@ +import { until } from '@vueuse/core' +import { defineStore } from 'pinia' +import { computed, ref } from 'vue' + +import { useSettingStore } from '@/platform/settings/settingStore' +import { useSystemStatsStore } from '@/stores/systemStatsStore' +import { isElectron } from '@/utils/envUtil' + +export const useCloudNotificationStore = defineStore( + 'cloudNotification', + () => { + const settingStore = useSettingStore() + const systemStatsStore = useSystemStatsStore() + + const isReady = ref(false) + const hasShownThisSession = ref(false) + // Temporary override so developers can test the dialog in non-mac/non-desktop envs + const devOverrideEnabled = import.meta.env.DEV + + async function initialize() { + if (isReady.value) return + await until(settingStore.isInitialized) + await until(systemStatsStore.isInitialized) + isReady.value = true + } + + const isEligiblePlatform = computed(() => { + if (!isReady.value) return false + if (devOverrideEnabled) return true + if (!isElectron()) return false + + const osString = + systemStatsStore.systemStats?.system?.os?.toLowerCase() ?? '' + const platformString = + typeof navigator === 'undefined' ? '' : navigator.platform.toLowerCase() + + return ( + osString.includes('darwin') || + osString.includes('mac') || + platformString.includes('mac') + ) + }) + + const hasSeenNotification = computed(() => { + if (!isReady.value) return true + return !!settingStore.get('Comfy.Desktop.CloudNotificationShown') + }) + + const shouldShowNotification = computed(() => { + if (!isReady.value) return false + if (!isEligiblePlatform.value) return false + + return !hasSeenNotification.value && !hasShownThisSession.value + }) + + function markSessionShown() { + hasShownThisSession.value = true + } + + async function persistNotificationShown() { + await settingStore.set('Comfy.Desktop.CloudNotificationShown', true) + } + + return { + initialize, + shouldShowNotification, + markSessionShown, + persistNotificationShown + } + } +) diff --git a/src/platform/updates/components/CloudNotificationOrchestrator.vue b/src/platform/updates/components/CloudNotificationOrchestrator.vue new file mode 100644 index 000000000..91a5bbc7e --- /dev/null +++ b/src/platform/updates/components/CloudNotificationOrchestrator.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/services/newUserService.ts b/src/services/newUserService.ts index fff9eb72c..11fa278bb 100644 --- a/src/services/newUserService.ts +++ b/src/services/newUserService.ts @@ -3,11 +3,17 @@ import type { useSettingStore } from '@/platform/settings/settingStore' let pendingCallbacks: Array<() => Promise> = [] let isNewUserDetermined = false let isNewUserCached: boolean | null = null +// Temporary override to always treat dev builds as "new user" for local testing +const devForceNewUser = import.meta.env.DEV export const newUserService = () => { function checkIsNewUser( settingStore: ReturnType ): boolean { + if (devForceNewUser) { + return true + } + const isNewUserSettings = Object.keys(settingStore.settingValues).length === 0 || !settingStore.get('Comfy.TutorialCompleted') @@ -41,6 +47,16 @@ export const newUserService = () => { isNewUserCached = checkIsNewUser(settingStore) isNewUserDetermined = true + if (devForceNewUser) { + localStorage.removeItem('workflow') + localStorage.removeItem('Comfy.PreviousWorkflow') + try { + await settingStore.set('Comfy.TutorialCompleted', false) + } catch (error) { + console.error('[newUserService] Failed to reset tutorial flag', error) + } + } + if (!isNewUserCached) { pendingCallbacks = [] return diff --git a/src/views/GraphView.vue b/src/views/GraphView.vue index 560398421..4c756b3b9 100644 --- a/src/views/GraphView.vue +++ b/src/views/GraphView.vue @@ -18,6 +18,7 @@ + @@ -56,6 +57,7 @@ import { useSettingStore } from '@/platform/settings/settingStore' import { useTelemetry } from '@/platform/telemetry' import { useFrontendVersionMismatchWarning } from '@/platform/updates/common/useFrontendVersionMismatchWarning' import { useVersionCompatibilityStore } from '@/platform/updates/common/versionCompatibilityStore' +import CloudNotificationOrchestrator from '@/platform/updates/components/CloudNotificationOrchestrator.vue' import { useCanvasStore } from '@/renderer/core/canvas/canvasStore' import type { StatusWsMessageStatus } from '@/schemas/apiSchema' import { api } from '@/scripts/api'