From 639a770cc684bd006aa68774659a7469c47a3015 Mon Sep 17 00:00:00 2001 From: mar4enkom Date: Thu, 30 Oct 2025 21:59:33 +0100 Subject: [PATCH] bugfix: fix debounce for save draft functionality --- src/features/listing-builder/atoms/index.ts | 10 --- .../components/ListingBuilderProvider.tsx | 8 --- src/features/listing-builder/hooks/index.ts | 72 ++++++------------- 3 files changed, 20 insertions(+), 70 deletions(-) diff --git a/src/features/listing-builder/atoms/index.ts b/src/features/listing-builder/atoms/index.ts index a5a055fe1..bbcbb3ece 100644 --- a/src/features/listing-builder/atoms/index.ts +++ b/src/features/listing-builder/atoms/index.ts @@ -20,15 +20,6 @@ const descriptionKeyAtom = atom(1); const skillsKeyAtom = atom(1); const isListingInReviewAtom = atom(false); -interface SaveQueueState { - isProcessing: boolean; - shouldProcessNext: boolean; -} -const draftQueueAtom = atom({ - isProcessing: false, - shouldProcessNext: false, -}); - const confirmModalAtom = atom<'SUCCESS' | 'VERIFICATION' | undefined>( undefined, ); @@ -62,7 +53,6 @@ const submitListingMutationAtom = atomWithMutation((get) => ({ export { confirmModalAtom, descriptionKeyAtom, - draftQueueAtom, hackathonsAtom, hideAutoSaveAtom, isDraftSavingAtom, diff --git a/src/features/listing-builder/components/ListingBuilderProvider.tsx b/src/features/listing-builder/components/ListingBuilderProvider.tsx index 1d89bf66d..851a2b96d 100644 --- a/src/features/listing-builder/components/ListingBuilderProvider.tsx +++ b/src/features/listing-builder/components/ListingBuilderProvider.tsx @@ -17,7 +17,6 @@ import { Header } from '@/features/navbar/components/Header'; import { confirmModalAtom, - draftQueueAtom, hackathonsAtom, isEditingAtom, isGodAtom, @@ -187,13 +186,6 @@ function ListingBuilderProvider({ isListingInReviewAtom, isEditing && listing && dayjs().isAfter(listing.deadline), ], - [ - draftQueueAtom, - { - isProcessing: false, - shouldProcessNext: false, - }, - ], ]} > ) => { + if (data.deadline) { + if (!data.deadline.endsWith('Z')) data.deadline += dayjs().format('Z'); + } + return data; +}; + interface UseListingFormReturn extends UseFormReturn { saveDraft: () => void; submitListing: () => Promise; @@ -87,62 +93,22 @@ export const useListingForm = ( const saveDraftMutation = useAtomValue(saveDraftMutationAtom); const submitListingMutation = useAtomValue(submitListingMutationAtom); const [, setDraftSaving] = useAtom(isDraftSavingAtom); - - const [queueRef, setQueueRef] = useAtom(draftQueueAtom); - const [, setHideAutoSave] = useAtom(hideAutoSaveAtom); - const queueRefRef = useRef(queueRef); - - useEffect(() => { - queueRefRef.current = queueRef; - }, [queueRef]); - // queue ensures eeach call for auto save is sent synchronously - const processSaveQueue = useCallback(async () => { + const saveDraft = useCallback(async () => { if (isEditing) return; setDraftSaving(true); - if (queueRefRef.current.isProcessing) { - setQueueRef((q) => ({ - ...q, - shouldProcessNext: true, - })); - return; - } - - setQueueRef((q) => ({ - ...q, - shouldProcessNext: false, - isProcessing: true, - })); try { - const dataToSave = getValues(); - - if (dataToSave.deadline) { - if (!dataToSave.deadline.endsWith('Z')) - dataToSave.deadline += dayjs().format('Z'); - } + const listingData = getValues(); + const dataToSave = formatDraftData(listingData); const data = await saveDraftMutation.mutateAsync(dataToSave); setHideAutoSave(false); formMethods.setValue('id', data.id); if (!dataToSave.slug) formMethods.setValue('slug', data.slug); - setQueueRef((q) => ({ - ...q, - })); } catch (error) { - console.log('Error processSaveQueue', error); + console.log('Error saving draft', error); } finally { setDraftSaving(false); - setQueueRef((q) => ({ - ...q, - isProcessing: false, - })); - // Check if we need to process another save - if (queueRefRef.current.shouldProcessNext) { - // Use setTimeout to break the call stack and ensure queue state is updated - setTimeout(() => { - void processSaveQueue(); - }, 0); - } } }, [ getValues, @@ -153,19 +119,21 @@ export const useListingForm = ( isEditing, ]); - const debouncedSaveRef = useRef>(undefined); + const latestSaveDraftRef = useRef<() => void>(saveDraft); + useEffect(() => { + latestSaveDraftRef.current = saveDraft; + }, [saveDraft]); + const debouncedRef = useRef | null>(null); useEffect(() => { - debouncedSaveRef.current = debounce(() => { - void processSaveQueue(); - }, 1000); - }, [processSaveQueue]); + debouncedRef.current = debounce(latestSaveDraftRef.current, 1000); + return () => debouncedRef.current?.cancel(); + }, []); const onChange = useCallback(() => { setHideAutoSave(true); if (!isEditing) { - debouncedSaveRef.current?.cancel(); - debouncedSaveRef.current?.(); + debouncedRef.current?.(); } }, [isEditing]);