From b879184dc651d54cc3496ba5850c0dab2f50b3ea Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Thu, 6 Feb 2025 19:07:48 +0100 Subject: [PATCH] ux: incrementally loading new books after synchronization (#303) * auth: workaround to get deeplink data when completing oauth on Android * ux: incrementally loading new books after synchronization --- apps/readest-app/src/app/auth/page.tsx | 24 +++++++++++++------ .../src/app/library/hooks/useBooksSync.ts | 18 +++++++++++--- apps/readest-app/src/app/library/page.tsx | 5 +++- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/apps/readest-app/src/app/auth/page.tsx b/apps/readest-app/src/app/auth/page.tsx index f8ceaa6f..dc4c69b5 100644 --- a/apps/readest-app/src/app/auth/page.tsx +++ b/apps/readest-app/src/app/auth/page.tsx @@ -62,6 +62,14 @@ export default function AuthPage() { const [isMounted, setIsMounted] = useState(false); const osPlatform = getOSPlatform(); + const getTauriRedirectTo = () => { + return process.env.NODE_ENV === 'production' + ? ['android', 'ios'].includes(osPlatform) + ? 'https://web.readest.com/auth/callback' + : 'readest://auth/callback' + : `http://localhost:${port}`; + }; + const tauriSignIn = async (provider: OAuthProvider) => { if (!supabase) { throw new Error('No backend connected'); @@ -71,12 +79,7 @@ export default function AuthPage() { provider, options: { skipBrowserRedirect: true, - redirectTo: - process.env.NODE_ENV === 'production' - ? ['android', 'ios'].includes(osPlatform) - ? 'https://web.readest.com/auth/callback' - : 'readest://auth/callback' - : `http://localhost:${port}`, + redirectTo: getTauriRedirectTo(), }, }); @@ -85,6 +88,13 @@ export default function AuthPage() { return; } openUrl(data.url); + + // FIXME: For Android we need a better way to trigger the deeplink redirect + if (getOSPlatform() === 'android') { + setTimeout(() => { + router.back(); + }, 5000); + } }; const handleOAuthUrl = async (url: string) => { @@ -226,7 +236,7 @@ export default function AuthPage() { theme={isDarkMode ? 'dark' : 'light'} magicLink={true} providers={[]} - redirectTo={`http://localhost:${port}`} + redirectTo={getTauriRedirectTo()} /> diff --git a/apps/readest-app/src/app/library/hooks/useBooksSync.ts b/apps/readest-app/src/app/library/hooks/useBooksSync.ts index 4af4c679..713db7cf 100644 --- a/apps/readest-app/src/app/library/hooks/useBooksSync.ts +++ b/apps/readest-app/src/app/library/hooks/useBooksSync.ts @@ -6,7 +6,12 @@ import { useLibraryStore } from '@/store/libraryStore'; import { SYNC_BOOKS_INTERVAL_SEC } from '@/services/constants'; import { Book } from '@/types/book'; -export const useBooksSync = () => { +export interface UseBooksSyncProps { + onSyncStart?: () => void; + onSyncEnd?: () => void; +} + +export const useBooksSync = ({ onSyncStart, onSyncEnd }: UseBooksSyncProps) => { const { user } = useAuth(); const { appService } = useEnv(); const { library, setLibrary } = useLibraryStore(); @@ -91,16 +96,23 @@ export const useBooksSync = () => { if (!updatedLibrary.some((oldBook) => oldBook.hash === newBook.hash)) { if (newBook.uploadedAt && !newBook.deletedAt) { try { - updatedLibrary.push(newBook); await appService?.downloadBook(newBook, true); newBook.coverImageUrl = await appService?.generateCoverImageUrl(newBook); + updatedLibrary.push(newBook); + setLibrary(updatedLibrary); } catch { console.error('Failed to download book:', newBook); } } } }; - await Promise.all(syncedBooks.map(processNewBook)); + onSyncStart?.(); + const batchSize = 3; + for (let i = 0; i < syncedBooks.length; i += batchSize) { + const batch = syncedBooks.slice(i, i + batchSize); + await Promise.all(batch.map(processNewBook)); + } + onSyncEnd?.(); setLibrary(updatedLibrary); appService?.saveLibraryBooks(updatedLibrary); }; diff --git a/apps/readest-app/src/app/library/page.tsx b/apps/readest-app/src/app/library/page.tsx index 59c46087..34949f0f 100644 --- a/apps/readest-app/src/app/library/page.tsx +++ b/apps/readest-app/src/app/library/page.tsx @@ -57,7 +57,10 @@ const LibraryPage = () => { const demoBooks = useDemoBooks(); const containerRef = useRef(null); - const { pullLibrary, pushLibrary } = useBooksSync(); + const { pullLibrary, pushLibrary } = useBooksSync({ + onSyncStart: () => setLoading(true), + onSyncEnd: () => setLoading(false), + }); usePullToRefresh(containerRef, pullLibrary);