resolve merge conflict in AlbumForm
This commit is contained in:
commit
2555067837
4 changed files with 34 additions and 14 deletions
|
|
@ -8,7 +8,7 @@ export interface AutoSaveStoreOptions<TPayload, TResponse = unknown> {
|
|||
onSaved?: (res: TResponse, ctx: { prime: (payload: TPayload) => void }) => void
|
||||
}
|
||||
|
||||
export interface AutoSaveStore<TPayload> {
|
||||
export interface AutoSaveStore<TPayload, _TResponse = unknown> {
|
||||
readonly status: AutoSaveStatus
|
||||
readonly lastError: string | null
|
||||
schedule: () => void
|
||||
|
|
@ -36,7 +36,7 @@ export interface AutoSaveStore<TPayload> {
|
|||
*/
|
||||
export function createAutoSaveStore<TPayload, TResponse = unknown>(
|
||||
opts: AutoSaveStoreOptions<TPayload, TResponse>
|
||||
): AutoSaveStore<TPayload> {
|
||||
): AutoSaveStore<TPayload, unknown> {
|
||||
const debounceMs = opts.debounceMs ?? 2000
|
||||
const idleResetMs = opts.idleResetMs ?? 2000
|
||||
let timer: ReturnType<typeof setTimeout> | null = null
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { loadDraft, clearDraft, timeAgo } from '$lib/admin/draftStore'
|
||||
|
||||
export function useDraftRecovery<TPayload>(options: {
|
||||
draftKey: string | null
|
||||
draftKey: () => string | null
|
||||
onRestore: (payload: TPayload) => void
|
||||
enabled?: boolean
|
||||
}) {
|
||||
|
|
@ -17,9 +17,10 @@ export function useDraftRecovery<TPayload>(options: {
|
|||
|
||||
// Auto-detect draft on mount using $effect
|
||||
$effect(() => {
|
||||
if (!options.draftKey || options.enabled === false) return
|
||||
const key = options.draftKey()
|
||||
if (!key || options.enabled === false) return
|
||||
|
||||
const draft = loadDraft<TPayload>(options.draftKey)
|
||||
const draft = loadDraft<TPayload>(key)
|
||||
if (draft) {
|
||||
showPrompt = true
|
||||
draftTimestamp = draft.ts
|
||||
|
|
@ -43,19 +44,21 @@ export function useDraftRecovery<TPayload>(options: {
|
|||
draftTimeText,
|
||||
|
||||
restore() {
|
||||
if (!options.draftKey) return
|
||||
const draft = loadDraft<TPayload>(options.draftKey)
|
||||
const key = options.draftKey()
|
||||
if (!key) return
|
||||
const draft = loadDraft<TPayload>(key)
|
||||
if (!draft) return
|
||||
|
||||
options.onRestore(draft.payload)
|
||||
showPrompt = false
|
||||
clearDraft(options.draftKey)
|
||||
clearDraft(key)
|
||||
},
|
||||
|
||||
dismiss() {
|
||||
if (!options.draftKey) return
|
||||
const key = options.draftKey()
|
||||
if (!key) return
|
||||
showPrompt = false
|
||||
clearDraft(options.draftKey)
|
||||
clearDraft(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ import { beforeNavigate } from '$app/navigation'
|
|||
import { toast } from '$lib/stores/toast'
|
||||
import type { AutoSaveStore } from '$lib/admin/autoSave.svelte'
|
||||
|
||||
export function useFormGuards(autoSave: AutoSaveStore<unknown, unknown> | null) {
|
||||
export function useFormGuards<TPayload = unknown, _TResponse = unknown>(
|
||||
autoSave: AutoSaveStore<TPayload, unknown> | null
|
||||
) {
|
||||
if (!autoSave) return // No guards needed for create mode
|
||||
|
||||
// Navigation guard: flush autosave before route change
|
||||
|
|
@ -21,8 +23,12 @@ export function useFormGuards(autoSave: AutoSaveStore<unknown, unknown> | null)
|
|||
|
||||
// Warn before closing browser tab/window if unsaved changes
|
||||
$effect(() => {
|
||||
// Capture autoSave in closure to avoid non-null assertions
|
||||
const store = autoSave
|
||||
if (!store) return
|
||||
|
||||
function handleBeforeUnload(event: BeforeUnloadEvent) {
|
||||
if (autoSave!.status !== 'saved') {
|
||||
if (store.status !== 'saved') {
|
||||
event.preventDefault()
|
||||
event.returnValue = ''
|
||||
}
|
||||
|
|
@ -34,13 +40,17 @@ export function useFormGuards(autoSave: AutoSaveStore<unknown, unknown> | null)
|
|||
|
||||
// Cmd/Ctrl+S keyboard shortcut for immediate save
|
||||
$effect(() => {
|
||||
// Capture autoSave in closure to avoid non-null assertions
|
||||
const store = autoSave
|
||||
if (!store) return
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
const key = event.key.toLowerCase()
|
||||
const isModifier = event.metaKey || event.ctrlKey
|
||||
|
||||
if (isModifier && key === 's') {
|
||||
event.preventDefault()
|
||||
autoSave!.flush().catch((error) => {
|
||||
store.flush().catch((error) => {
|
||||
console.error('Autosave flush failed:', error)
|
||||
toast.error('Failed to save changes')
|
||||
})
|
||||
|
|
|
|||
|
|
@ -104,10 +104,15 @@
|
|||
}
|
||||
|
||||
// Autosave store (edit mode only)
|
||||
// Initialized as null and created reactively when album data becomes available
|
||||
let autoSave = $state<ReturnType<typeof createAutoSaveStore<ReturnType<typeof buildPayload>, Album>> | null>(null)
|
||||
|
||||
// INITIALIZATION ORDER:
|
||||
// 1. This effect creates autoSave when album prop becomes available
|
||||
// 2. useFormGuards is called immediately after creation (same effect)
|
||||
// 3. Other effects check for autoSave existence before using it
|
||||
$effect(() => {
|
||||
// Create or update autoSave when album becomes available
|
||||
// Create autoSave when album becomes available (only once)
|
||||
if (mode === 'edit' && album && !autoSave) {
|
||||
const albumId = album.id // Capture album ID to avoid null reference
|
||||
autoSave = createAutoSaveStore({
|
||||
|
|
@ -180,6 +185,8 @@
|
|||
})
|
||||
|
||||
// Trigger autosave when form data changes
|
||||
// Using `void` operator to explicitly track dependencies without using their values
|
||||
// This effect re-runs whenever any of these form fields change
|
||||
$effect(() => {
|
||||
void formData.title
|
||||
void formData.slug
|
||||
|
|
|
|||
Loading…
Reference in a new issue