diff --git a/apps/readest-app/src/app/library/hooks/useBooksSync.ts b/apps/readest-app/src/app/library/hooks/useBooksSync.ts index 64027a17..62b4daeb 100644 --- a/apps/readest-app/src/app/library/hooks/useBooksSync.ts +++ b/apps/readest-app/src/app/library/hooks/useBooksSync.ts @@ -6,11 +6,13 @@ import { useAuth } from '@/context/AuthContext'; import { useLibraryStore } from '@/store/libraryStore'; import { SYNC_BOOKS_INTERVAL_SEC } from '@/services/constants'; import { throttle } from '@/utils/throttle'; +import { debounce } from '@/utils/debounce'; export const useBooksSync = () => { const { user } = useAuth(); const { appService } = useEnv(); - const { library, isSyncing, setLibrary, setIsSyncing, setSyncProgress } = useLibraryStore(); + const { library, isSyncing, libraryLoaded } = useLibraryStore(); + const { setLibrary, setIsSyncing, setSyncProgress } = useLibraryStore(); const { useSyncInited, syncedBooks, syncBooks, lastSyncedAtBooks } = useSync(); const isPullingRef = useRef(false); @@ -37,11 +39,12 @@ export const useBooksSync = () => { } try { isPullingRef.current = true; - await syncBooks([], 'pull'); + const library = useLibraryStore.getState().library; + await syncBooks([], 'pull', libraryLoaded && library.length === 0 ? 0 : undefined); } finally { isPullingRef.current = false; } - }, [user, syncBooks]); + }, [user, libraryLoaded, syncBooks]); // eslint-disable-next-line react-hooks/exhaustive-deps const handleAutoSync = useCallback( @@ -75,12 +78,11 @@ export const useBooksSync = () => { }, [user, syncBooks, getNewBooks]); useEffect(() => { - if (!user || !useSyncInited) return; + if (!user || !useSyncInited || !libraryLoaded) return; pullLibrary(); - }, [user, useSyncInited, pullLibrary]); + }, [user, useSyncInited, libraryLoaded, pullLibrary]); - const updateLibrary = async () => { - if (isSyncing) return; + const updateLibrary = useCallback(async () => { if (!syncedBooks?.length) return; // Process old books first so that when we update the library the order is preserved @@ -149,12 +151,23 @@ export const useBooksSync = () => { setIsSyncing(false); } } - }; - - useEffect(() => { - updateLibrary(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [syncedBooks]); + // eslint-disable-next-line react-hooks/exhaustive-deps + const debouncedUpdateLibrary = useCallback( + debounce(() => updateLibrary(), 10000), + [updateLibrary], + ); + + useEffect(() => { + if (isSyncing) { + debouncedUpdateLibrary(); + } else { + updateLibrary(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [syncedBooks, updateLibrary, debouncedUpdateLibrary]); + return { pullLibrary, pushLibrary }; }; diff --git a/apps/readest-app/src/hooks/useSync.ts b/apps/readest-app/src/hooks/useSync.ts index c1158d3f..a7eae8e3 100644 --- a/apps/readest-app/src/hooks/useSync.ts +++ b/apps/readest-app/src/hooks/useSync.ts @@ -1,5 +1,6 @@ import { useCallback, useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; +import { useEnv } from '@/context/EnvContext'; import { useSyncContext } from '@/context/SyncContext'; import { SyncData, SyncOp, SyncResult, SyncType } from '@/libs/sync'; import { useSettingsStore } from '@/store/settingsStore'; @@ -36,7 +37,8 @@ const computeMaxTimestamp = (records: BookDataRecord[]): number => { const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000; export function useSync(bookKey?: string) { const router = useRouter(); - const { settings, setSettings } = useSettingsStore(); + const { envConfig } = useEnv(); + const { settings, setSettings, saveSettings } = useSettingsStore(); const { getConfig, setConfig } = useBookDataStore(); const { setIsSyncing } = useReaderStore(); const config = bookKey ? getConfig(bookKey) : null; @@ -152,6 +154,7 @@ export function useSync(bookKey?: string) { } } finally { setSyncing(false); + saveSettings(envConfig, settings); } }; @@ -175,13 +178,18 @@ export function useSync(bookKey?: string) { }; const syncBooks = useCallback( - async (books?: Book[], op: SyncOp = 'both') => { + async (books?: Book[], op: SyncOp = 'both', since?: number) => { if (!lastSyncedAtInited) return; if ((op === 'push' || op === 'both') && books?.length) { await pushChanges({ books }); } if (op === 'pull' || op === 'both') { - await pullChanges('books', lastSyncedAtBooks + 1, setLastSyncedAtBooks, setSyncingBooks); + await pullChanges( + 'books', + since ?? lastSyncedAtBooks + 1, + setLastSyncedAtBooks, + setSyncingBooks, + ); } }, // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/apps/readest-app/src/store/libraryStore.ts b/apps/readest-app/src/store/libraryStore.ts index 1a8e98cd..d053851c 100644 --- a/apps/readest-app/src/store/libraryStore.ts +++ b/apps/readest-app/src/store/libraryStore.ts @@ -6,6 +6,7 @@ import { md5Fingerprint } from '@/utils/md5'; interface LibraryState { library: Book[]; // might contain deleted books + libraryLoaded: boolean; isSyncing: boolean; syncProgress: number; checkOpenWithBooks: boolean; @@ -36,6 +37,7 @@ interface LibraryState { export const useLibraryStore = create((set, get) => ({ library: [], + libraryLoaded: false, isSyncing: false, syncProgress: 0, currentBookshelf: [], @@ -56,7 +58,7 @@ export const useLibraryStore = create((set, get) => ({ setCheckLastOpenBooks: (check) => set({ checkLastOpenBooks: check }), setLibrary: (books) => { const { refreshGroups } = get(); - set({ library: books }); + set({ library: books, libraryLoaded: true }); refreshGroups(); }, updateBook: async (envConfig: EnvConfigType, book: Book) => {