Compare commits

...

3 Commits

Author SHA1 Message Date
Connor Byrne
20dcc369dc test(newUserService): drop unneeded global version write
The Vite `define` for `__COMFYUI_FRONTEND_VERSION__` is applied at
transform time in tests too, so the ad-hoc global assignment and
companion @ts-expect-error were unnecessary. All 29 tests still pass.

Addresses review feedback:
https://github.com/Comfy-Org/ComfyUI_frontend/pull/11728#pullrequestreview-3635064775
2026-05-04 16:06:32 -07:00
Connor Byrne
e0c1c4e994 fix(newUserService): require non-empty V2 draft index for existing-user signal
migrateV1toV2() writes an empty index (`createEmptyIndex()`) at startup
when there is no V1 data, so genuine new users would have the V2 index
key present before initializeIfNewUser() ever runs. Treating key
existence as evidence of prior usage misclassified them as existing.

Now parse the index and require a non-empty `order` or `entries` to
count as draft history. Malformed JSON is treated as no history.

Addresses review feedback:
https://github.com/Comfy-Org/ComfyUI_frontend/pull/11728#pullrequestreview-3642597247
2026-05-04 16:05:12 -07:00
bymyself
ac7a825fd3 fix: detect V1/V2 draft storage keys in new-user check
`checkIsNewUser()` only checked legacy pre-V1 localStorage keys
(`workflow`, `Comfy.PreviousWorkflow`). A returning user who had only
ever used the V1 or V2 draft persistence system would have neither of
those keys, causing the getting-started tab to appear on every dialog
open after a settings reset.

Now also checks V1 keys (`Comfy.Workflow.Drafts`,
`Comfy.Workflow.DraftOrder`) and the V2 draft index key
(`Comfy.Workflow.DraftIndex.v2:personal`).
2026-05-01 21:30:28 -07:00
2 changed files with 109 additions and 7 deletions

View File

@@ -26,9 +26,6 @@ vi.mock('@/platform/settings/settingStore', () => ({
useSettingStore: () => mockSettingStore
}))
//@ts-expect-error Define global for the test
global.__COMFYUI_FRONTEND_VERSION__ = '1.24.0'
import { useNewUserService } from '@/services/useNewUserService'
describe('useNewUserService', () => {
@@ -120,6 +117,73 @@ describe('useNewUserService', () => {
expect(service.isNewUser()).toBe(false)
})
it('should identify existing user when V1 draft store keys exist', async () => {
mockSettingStore.settingValues = {}
mockSettingStore.get.mockReturnValue(undefined)
mockLocalStorage.getItem.mockImplementation((key: string) => {
if (key === 'Comfy.Workflow.Drafts') return '{}'
return null
})
await service.initializeIfNewUser()
expect(service.isNewUser()).toBe(false)
})
it('should identify existing user when V1 draft order key exists', async () => {
mockSettingStore.settingValues = {}
mockSettingStore.get.mockReturnValue(undefined)
mockLocalStorage.getItem.mockImplementation((key: string) => {
if (key === 'Comfy.Workflow.DraftOrder') return '[]'
return null
})
await service.initializeIfNewUser()
expect(service.isNewUser()).toBe(false)
})
it('should identify existing user when V2 draft index has entries', async () => {
mockSettingStore.settingValues = {}
mockSettingStore.get.mockReturnValue(undefined)
mockLocalStorage.getItem.mockImplementation((key: string) => {
if (key === 'Comfy.Workflow.DraftIndex.v2:personal')
return '{"v":2,"updatedAt":1,"order":["abc"],"entries":{"abc":{"path":"workflows/Untitled.json","name":"Untitled","isTemporary":true,"updatedAt":1}}}'
return null
})
await service.initializeIfNewUser()
expect(service.isNewUser()).toBe(false)
})
it('should identify new user when V2 draft index exists but is empty', async () => {
mockSettingStore.settingValues = {}
mockSettingStore.get.mockReturnValue(undefined)
mockLocalStorage.getItem.mockImplementation((key: string) => {
if (key === 'Comfy.Workflow.DraftIndex.v2:personal')
return '{"v":2,"updatedAt":1,"order":[],"entries":{}}'
return null
})
await service.initializeIfNewUser()
expect(service.isNewUser()).toBe(true)
})
it('should identify new user when V2 draft index is malformed', async () => {
mockSettingStore.settingValues = {}
mockSettingStore.get.mockReturnValue(undefined)
mockLocalStorage.getItem.mockImplementation((key: string) => {
if (key === 'Comfy.Workflow.DraftIndex.v2:personal') return 'not json'
return null
})
await service.initializeIfNewUser()
expect(service.isNewUser()).toBe(true)
})
it('should identify new user when tutorial is explicitly false', async () => {
mockSettingStore.settingValues = { 'Comfy.TutorialCompleted': false }
mockSettingStore.get.mockImplementation((key: string) => {

View File

@@ -2,6 +2,24 @@ import { ref, shallowRef } from 'vue'
import { createSharedComposable } from '@vueuse/core'
import { useSettingStore } from '@/platform/settings/settingStore'
function hasV2DraftHistory(raw: string | null): boolean {
if (!raw) return false
try {
const parsed = JSON.parse(raw) as {
order?: unknown
entries?: unknown
}
const orderLength = Array.isArray(parsed.order) ? parsed.order.length : 0
const entriesCount =
parsed.entries && typeof parsed.entries === 'object'
? Object.keys(parsed.entries as Record<string, unknown>).length
: 0
return orderLength > 0 || entriesCount > 0
} catch {
return false
}
}
function _useNewUserService() {
const settingStore = useSettingStore()
const pendingCallbacks = shallowRef<Array<() => Promise<void>>>([])
@@ -18,12 +36,32 @@ function _useNewUserService() {
const isNewUserSettings =
Object.keys(settingStore.settingValues).length === 0 ||
!settingStore.get('Comfy.TutorialCompleted')
const hasNoWorkflow = !localStorage.getItem('workflow')
const hasNoPreviousWorkflow = !localStorage.getItem(
'Comfy.PreviousWorkflow'
// Legacy keys (pre-V1 and V1 persistence)
const hasNoLegacyWorkflow =
!localStorage.getItem('workflow') &&
!localStorage.getItem('Comfy.PreviousWorkflow')
// V1 draft store keys
const hasNoV1Drafts =
!localStorage.getItem('Comfy.Workflow.Drafts') &&
!localStorage.getItem('Comfy.Workflow.DraftOrder')
// V2 draft index key (scoped to personal workspace; cloud workspace id
// comes from sessionStorage which may not be set yet at this point).
// Check for actual draft history rather than key existence: an empty
// index is written by `migrateV1toV2()` for genuine new users during
// startup, so key presence alone is not evidence of prior usage.
const hasNoV2DraftIndex = !hasV2DraftHistory(
localStorage.getItem('Comfy.Workflow.DraftIndex.v2:personal')
)
return isNewUserSettings && hasNoWorkflow && hasNoPreviousWorkflow
return (
isNewUserSettings &&
hasNoLegacyWorkflow &&
hasNoV1Drafts &&
hasNoV2DraftIndex
)
}
async function registerInitCallback(callback: () => Promise<void>) {