From ebe1c10c84223940166ecafe7a5719e8099e55c6 Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Fri, 6 Jun 2025 17:47:26 +0800 Subject: [PATCH] sync: also update deleted notes in synchronization, closes #1356 (#1357) --- .../src/app/library/hooks/useBooksSync.ts | 36 ++++++---------- .../src/app/reader/hooks/useNotesSync.ts | 41 ++++++++----------- .../src/app/reader/hooks/useProgressSync.ts | 4 +- apps/readest-app/src/services/constants.ts | 4 +- 4 files changed, 35 insertions(+), 50 deletions(-) diff --git a/apps/readest-app/src/app/library/hooks/useBooksSync.ts b/apps/readest-app/src/app/library/hooks/useBooksSync.ts index 6208eef5..6d2c317c 100644 --- a/apps/readest-app/src/app/library/hooks/useBooksSync.ts +++ b/apps/readest-app/src/app/library/hooks/useBooksSync.ts @@ -1,10 +1,11 @@ -import { useEffect, useRef } from 'react'; +import { useCallback, useEffect, useRef } from 'react'; import { useAuth } from '@/context/AuthContext'; import { useEnv } from '@/context/EnvContext'; import { useSync } from '@/hooks/useSync'; import { useLibraryStore } from '@/store/libraryStore'; -import { SYNC_BOOKS_INTERVAL_SEC } from '@/services/constants'; import { Book } from '@/types/book'; +import { SYNC_BOOKS_INTERVAL_SEC } from '@/services/constants'; +import { debounce } from '@/utils/debounce'; export interface UseBooksSyncProps { onSyncStart?: () => void; @@ -38,9 +39,6 @@ export const useBooksSync = ({ onSyncStart, onSyncEnd }: UseBooksSyncProps) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const lastSyncTime = useRef(0); - const syncTimeoutRef = useRef | null>(null); - const getNewBooks = () => { if (!user) return []; const newBooks = library.filter( @@ -49,26 +47,18 @@ export const useBooksSync = ({ onSyncStart, onSyncEnd }: UseBooksSyncProps) => { return newBooks; }; - useEffect(() => { - if (!user) return; - const now = Date.now(); - const timeSinceLastSync = now - lastSyncTime.current; - if (timeSinceLastSync > SYNC_BOOKS_INTERVAL_SEC * 1000) { - lastSyncTime.current = now; + // eslint-disable-next-line react-hooks/exhaustive-deps + const handleAutoSync = useCallback( + debounce(() => { const newBooks = getNewBooks(); syncBooks(newBooks, 'both'); - } else { - if (syncTimeoutRef.current) clearTimeout(syncTimeoutRef.current); - syncTimeoutRef.current = setTimeout( - () => { - lastSyncTime.current = Date.now(); - const newBooks = getNewBooks(); - syncBooks(newBooks, 'both'); - syncTimeoutRef.current = null; - }, - SYNC_BOOKS_INTERVAL_SEC * 1000 - timeSinceLastSync, - ); - } + }, SYNC_BOOKS_INTERVAL_SEC * 1000), + [library, lastSyncedAtBooks], + ); + + useEffect(() => { + if (!user) return; + handleAutoSync(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [library]); diff --git a/apps/readest-app/src/app/reader/hooks/useNotesSync.ts b/apps/readest-app/src/app/reader/hooks/useNotesSync.ts index a43cb007..ae4ca3fa 100644 --- a/apps/readest-app/src/app/reader/hooks/useNotesSync.ts +++ b/apps/readest-app/src/app/reader/hooks/useNotesSync.ts @@ -1,9 +1,10 @@ -import { useEffect, useRef } from 'react'; +import { useCallback, useEffect } from 'react'; import { useAuth } from '@/context/AuthContext'; import { useSync } from '@/hooks/useSync'; import { BookNote } from '@/types/book'; import { useBookDataStore } from '@/store/bookDataStore'; import { SYNC_NOTES_INTERVAL_SEC } from '@/services/constants'; +import { debounce } from '@/utils/debounce'; export const useNotesSync = (bookKey: string) => { const { user } = useAuth(); @@ -13,10 +14,8 @@ export const useNotesSync = (bookKey: string) => { const config = getConfig(bookKey); const bookHash = bookKey.split('-')[0]!; - const lastSyncTime = useRef(0); - const syncTimeoutRef = useRef | null>(null); - const getNewNotes = () => { + const config = getConfig(bookKey); if (!config?.location || !user) return []; const bookNotes = config.booknotes ?? []; const newNotes = bookNotes.filter( @@ -28,35 +27,31 @@ export const useNotesSync = (bookKey: string) => { return newNotes; }; - useEffect(() => { - if (!config?.location || !user) return; - const now = Date.now(); - const timeSinceLastSync = now - lastSyncTime.current; - if (timeSinceLastSync > SYNC_NOTES_INTERVAL_SEC * 1000) { - lastSyncTime.current = now; + // eslint-disable-next-line react-hooks/exhaustive-deps + const handleAutoSync = useCallback( + debounce(() => { const newNotes = getNewNotes(); syncNotes(newNotes, bookHash, 'both'); - } else { - if (syncTimeoutRef.current) clearTimeout(syncTimeoutRef.current); - syncTimeoutRef.current = setTimeout( - () => { - lastSyncTime.current = Date.now(); - const newNotes = getNewNotes(); - syncNotes(newNotes, bookHash, 'both'); - syncTimeoutRef.current = null; - }, - SYNC_NOTES_INTERVAL_SEC * 1000 - timeSinceLastSync, - ); - } + }, SYNC_NOTES_INTERVAL_SEC * 1000), + [lastSyncedAtNotes], + ); + + useEffect(() => { + if (!config?.location || !user) return; + handleAutoSync(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [config]); useEffect(() => { const processNewNote = (note: BookNote) => { + const config = getConfig(bookKey); const oldNotes = config?.booknotes ?? []; const existingNote = oldNotes.find((oldNote) => oldNote.id === note.id); if (existingNote) { - if (existingNote.updatedAt < note.updatedAt) { + if ( + existingNote.updatedAt < note.updatedAt || + (existingNote.deletedAt ?? 0) < (note.deletedAt ?? 0) + ) { return { ...existingNote, ...note }; } else { return { ...note, ...existingNote }; diff --git a/apps/readest-app/src/app/reader/hooks/useProgressSync.ts b/apps/readest-app/src/app/reader/hooks/useProgressSync.ts index 4d6b8ece..721ef4b7 100644 --- a/apps/readest-app/src/app/reader/hooks/useProgressSync.ts +++ b/apps/readest-app/src/app/reader/hooks/useProgressSync.ts @@ -69,7 +69,7 @@ export const useProgressSync = (bookKey: string) => { }, [bookKey]); // eslint-disable-next-line react-hooks/exhaustive-deps - const debouncedAutoSync = useCallback( + const handleAutoSync = useCallback( debounce(() => { syncConfig(); }, SYNC_PROGRESS_INTERVAL_SEC * 1000), @@ -79,7 +79,7 @@ export const useProgressSync = (bookKey: string) => { // Push: auto-push progress when progress changes with a debounce useEffect(() => { if (!progress?.location || !user) return; - debouncedAutoSync(); + handleAutoSync(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [progress]); diff --git a/apps/readest-app/src/services/constants.ts b/apps/readest-app/src/services/constants.ts index c7cf7866..35b5434c 100644 --- a/apps/readest-app/src/services/constants.ts +++ b/apps/readest-app/src/services/constants.ts @@ -521,8 +521,8 @@ export const READEST_UPDATER_FILE = `${GITHUB_LATEST_DOWNLOAD}/latest.json`; export const READEST_CHANGELOG_FILE = `${GITHUB_LATEST_DOWNLOAD}/release-notes.json`; export const SYNC_PROGRESS_INTERVAL_SEC = 3; -export const SYNC_NOTES_INTERVAL_SEC = 10; -export const SYNC_BOOKS_INTERVAL_SEC = 10; +export const SYNC_NOTES_INTERVAL_SEC = 5; +export const SYNC_BOOKS_INTERVAL_SEC = 5; export const CHECK_UPDATE_INTERVAL_SEC = 24 * 60 * 60; export const MAX_ZOOM_LEVEL = 500;