From 0fd316d775a0ef330546ea62eb58e470a1d43bcb Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Mon, 17 Nov 2025 23:22:48 +0530 Subject: [PATCH] fix(sync): retry sync books for previous failed sync, closes #2441 (#2462) --- .../src/app/library/hooks/useBooksSync.ts | 14 ++++++++++---- .../src/app/reader/components/ReaderContent.tsx | 10 +++++++--- apps/readest-app/src/components/Providers.tsx | 13 ++++++++++++- apps/readest-app/src/hooks/usePullToRefresh.ts | 2 +- apps/readest-app/src/store/bookDataStore.ts | 2 +- apps/readest-app/src/types/book.ts | 1 + apps/readest-app/src/utils/nav.ts | 7 +++++++ 7 files changed, 39 insertions(+), 10 deletions(-) diff --git a/apps/readest-app/src/app/library/hooks/useBooksSync.ts b/apps/readest-app/src/app/library/hooks/useBooksSync.ts index 223b7dca..4842a3b7 100644 --- a/apps/readest-app/src/app/library/hooks/useBooksSync.ts +++ b/apps/readest-app/src/app/library/hooks/useBooksSync.ts @@ -18,7 +18,10 @@ export const useBooksSync = () => { if (!user) return {}; const library = useLibraryStore.getState().library; const newBooks = library.filter( - (book) => lastSyncedAtBooks < book.updatedAt || lastSyncedAtBooks < (book.deletedAt ?? 0), + (book) => + !book.syncedAt || + lastSyncedAtBooks < book.updatedAt || + lastSyncedAtBooks < (book.deletedAt ?? 0), ); return { books: newBooks, @@ -67,7 +70,9 @@ export const useBooksSync = () => { const pushLibrary = useCallback(async () => { if (!user) return; const newBooks = getNewBooks(); - await syncBooks(newBooks?.books, 'push'); + if (newBooks.lastSyncedAt) { + await syncBooks(newBooks?.books, 'push'); + } }, [user, syncBooks, getNewBooks]); useEffect(() => { @@ -95,8 +100,8 @@ export const useBooksSync = () => { } const mergedBook = matchingBook.updatedAt > oldBook.updatedAt - ? { ...oldBook, ...matchingBook } - : { ...matchingBook, ...oldBook }; + ? { ...oldBook, ...matchingBook, syncedAt: Date.now() } + : { ...matchingBook, ...oldBook, syncedAt: Date.now() }; return mergedBook; } return oldBook; @@ -117,6 +122,7 @@ export const useBooksSync = () => { const processNewBook = async (newBook: Book) => { newBook.coverImageUrl = await appService?.generateCoverImageUrl(newBook); + newBook.syncedAt = Date.now(); updatedLibrary.push(newBook); }; diff --git a/apps/readest-app/src/app/reader/components/ReaderContent.tsx b/apps/readest-app/src/app/reader/components/ReaderContent.tsx index ca9c0106..a1c07409 100644 --- a/apps/readest-app/src/app/reader/components/ReaderContent.tsx +++ b/apps/readest-app/src/app/reader/components/ReaderContent.tsx @@ -131,9 +131,13 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i clearViewState(bookKey); }; + const navigateBackToLibrary = () => { + navigateToLibrary(router, '', undefined, true); + }; + const saveSettingsAndGoToLibrary = () => { saveSettings(envConfig, settings); - navigateToLibrary(router); + navigateBackToLibrary(); }; const handleCloseBooks = throttle(async () => { @@ -147,12 +151,12 @@ const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ i if (isTauriAppPlatform()) { const currentWindow = getCurrentWindow(); if (currentWindow.label === 'main') { - navigateToLibrary(router); + navigateBackToLibrary(); } else { currentWindow.close(); } } else { - navigateToLibrary(router); + navigateBackToLibrary(); } }; diff --git a/apps/readest-app/src/components/Providers.tsx b/apps/readest-app/src/components/Providers.tsx index 01bebb41..e4ef2ce2 100644 --- a/apps/readest-app/src/components/Providers.tsx +++ b/apps/readest-app/src/components/Providers.tsx @@ -2,8 +2,9 @@ import '@/utils/polyfill'; import i18n from '@/i18n/i18n'; -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; import { IconContext } from 'react-icons'; +import { usePathname } from 'next/navigation'; import { AuthProvider } from '@/context/AuthContext'; import { useEnv } from '@/context/EnvContext'; import { CSPostHogProvider } from '@/context/PHContext'; @@ -27,6 +28,16 @@ const Providers = ({ children }: { children: React.ReactNode }) => { const iconSize = useDefaultIconSize(); useSafeAreaInsets(); // Initialize safe area insets + const pathname = usePathname(); + const prevPathnameRef = useRef(null); + + useEffect(() => { + if (prevPathnameRef.current !== null) { + sessionStorage.setItem('lastPath', prevPathnameRef.current); + } + prevPathnameRef.current = pathname; + }, [pathname]); + useEffect(() => { const handlerLanguageChanged = (lng: string) => { document.documentElement.lang = lng; diff --git a/apps/readest-app/src/hooks/usePullToRefresh.ts b/apps/readest-app/src/hooks/usePullToRefresh.ts index b09099ac..2e621050 100644 --- a/apps/readest-app/src/hooks/usePullToRefresh.ts +++ b/apps/readest-app/src/hooks/usePullToRefresh.ts @@ -65,7 +65,7 @@ export const usePullToRefresh = (ref: React.RefObject, onTrigger const headerbar = document.querySelector('.titlebar'); const pullIndicator = document.createElement('div'); const headerBottom = headerbar?.getBoundingClientRect().bottom || 0; - pullIndicator.style.top = `${headerBottom + 20}px`; + pullIndicator.style.top = `${headerBottom + 30}px`; pullIndicator.className = 'pull-indicator text-gray-500'; pullIndicator.innerHTML = ` diff --git a/apps/readest-app/src/store/bookDataStore.ts b/apps/readest-app/src/store/bookDataStore.ts index 820775c6..03b77d4b 100644 --- a/apps/readest-app/src/store/bookDataStore.ts +++ b/apps/readest-app/src/store/bookDataStore.ts @@ -75,7 +75,7 @@ export const useBookDataStore = create((set, get) => ({ book.updatedAt = Date.now(); book.downloadedAt = book.downloadedAt || Date.now(); library.unshift(book); - setLibrary(library); + setLibrary([...library]); config.updatedAt = Date.now(); await appService.saveBookConfig(book, config, settings); await appService.saveLibraryBooks(library); diff --git a/apps/readest-app/src/types/book.ts b/apps/readest-app/src/types/book.ts index b673608a..2ad45fa7 100644 --- a/apps/readest-app/src/types/book.ts +++ b/apps/readest-app/src/types/book.ts @@ -34,6 +34,7 @@ export interface Book { uploadedAt?: number | null; downloadedAt?: number | null; coverDownloadedAt?: number | null; + syncedAt?: number | null; lastUpdated?: number; // deprecated in favor of updatedAt progress?: [number, number]; // Add progress field: [current, total], 1-based page number diff --git a/apps/readest-app/src/utils/nav.ts b/apps/readest-app/src/utils/nav.ts index fc09615f..384be0e2 100644 --- a/apps/readest-app/src/utils/nav.ts +++ b/apps/readest-app/src/utils/nav.ts @@ -80,7 +80,14 @@ export const navigateToLibrary = ( router: ReturnType, queryParams?: string, navOptions?: { scroll?: boolean }, + navBack?: boolean, ) => { + const lastPath = typeof window !== 'undefined' ? sessionStorage.getItem('lastPath') : null; + if (navBack && lastPath === '/library') { + router.back(); + return; + } + router.replace(`/library${queryParams ? `?${queryParams}` : ''}`, navOptions); };