diff --git a/apps/readest-app/src/app/auth/page.tsx b/apps/readest-app/src/app/auth/page.tsx index 478e59af..8f491bd1 100644 --- a/apps/readest-app/src/app/auth/page.tsx +++ b/apps/readest-app/src/app/auth/page.tsx @@ -129,7 +129,7 @@ export default function AuthPage() { }; const handleOAuthUrl = async (url: string) => { - console.log('Received OAuth URL:', url); + console.log('Handle OAuth URL:', url); const hashMatch = url.match(/#(.*)/); if (hashMatch) { const hash = hashMatch[1]; diff --git a/apps/readest-app/src/app/library/page.tsx b/apps/readest-app/src/app/library/page.tsx index e2916270..e522b799 100644 --- a/apps/readest-app/src/app/library/page.tsx +++ b/apps/readest-app/src/app/library/page.tsx @@ -2,8 +2,8 @@ import clsx from 'clsx'; import * as React from 'react'; -import { useState, useRef, useEffect } from 'react'; -import { useRouter } from 'next/navigation'; +import { useState, useRef, useEffect, Suspense } from 'react'; +import { ReadonlyURLSearchParams, useRouter, useSearchParams } from 'next/navigation'; import { Book } from '@/types/book'; import { AppService } from '@/types/system'; @@ -28,6 +28,7 @@ import { usePullToRefresh } from '@/hooks/usePullToRefresh'; import { useDemoBooks } from './hooks/useDemoBooks'; import { useBooksSync } from './hooks/useBooksSync'; import { useScreenWakeLock } from '@/hooks/useScreenWakeLock'; +import { useOpenWithBooks } from '@/hooks/useOpenWithBooks'; import { tauriQuitApp } from '@/utils/window'; import { AboutWindow } from '@/components/AboutWindow'; @@ -38,7 +39,12 @@ import Bookshelf from './components/Bookshelf'; import BookDetailModal from '@/components/BookDetailModal'; import useShortcuts from '@/hooks/useShortcuts'; -const LibraryPage = () => { +const LibraryPageWithSearchParams = () => { + const searchParams = useSearchParams(); + return ; +}; + +const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchParams | null }) => { const router = useRouter(); const { envConfig, appService } = useEnv(); const { token, user } = useAuth(); @@ -47,7 +53,7 @@ const LibraryPage = () => { updateBook, setLibrary, checkOpenWithBooks, - clearOpenWithBooks, + setCheckOpenWithBooks, } = useLibraryStore(); const _ = useTranslation(); const { updateAppTheme } = useTheme(); @@ -63,6 +69,8 @@ const LibraryPage = () => { const demoBooks = useDemoBooks(); const containerRef = useRef(null); + useOpenWithBooks(); + const { pullLibrary, pushLibrary } = useBooksSync({ onSyncStart: () => setLoading(true), onSyncEnd: () => setLoading(false), @@ -113,7 +121,9 @@ const LibraryPage = () => { console.log('Opening books:', bookIds); if (bookIds.length > 0) { - navigateToReader(router, bookIds); + setTimeout(() => { + navigateToReader(router, bookIds); + }, 0); } }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -148,7 +158,7 @@ const LibraryPage = () => { if (checkOpenWithBooks && isTauriAppPlatform()) { await handleOpenWithBooks(appService, libraryBooks); } else { - clearOpenWithBooks(); + setCheckOpenWithBooks(false); setLibrary(libraryBooks); } @@ -163,7 +173,7 @@ const LibraryPage = () => { if (openWithFiles.length > 0) { await processOpenWithFiles(appService, openWithFiles, libraryBooks); } else { - clearOpenWithBooks(); + setCheckOpenWithBooks(false); setLibrary(libraryBooks); } }; @@ -171,10 +181,11 @@ const LibraryPage = () => { initLogin(); initLibrary(); return () => { - clearOpenWithBooks(); + setCheckOpenWithBooks(false); + isInitiating.current = false; }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [searchParams]); useEffect(() => { if (demoBooks.length > 0 && libraryLoaded) { @@ -449,4 +460,18 @@ const LibraryPage = () => { ); }; +const LibraryPage = () => { + return ( + + + + } + > + + + ); +}; + export default LibraryPage; diff --git a/apps/readest-app/src/app/reader/hooks/useProgressAutoSave.ts b/apps/readest-app/src/app/reader/hooks/useProgressAutoSave.ts index 791d7591..d401236c 100644 --- a/apps/readest-app/src/app/reader/hooks/useProgressAutoSave.ts +++ b/apps/readest-app/src/app/reader/hooks/useProgressAutoSave.ts @@ -6,7 +6,7 @@ import { useSettingsStore } from '@/store/settingsStore'; import { throttle } from '@/utils/throttle'; export const useProgressAutoSave = (bookKey: string) => { - const { envConfig, appService } = useEnv(); + const { envConfig } = useEnv(); const { getConfig, saveConfig } = useBookDataStore(); const { getProgress } = useReaderStore(); const progress = getProgress(bookKey); @@ -22,9 +22,6 @@ export const useProgressAutoSave = (bookKey: string) => { ); useEffect(() => { - // FIXME: On Android and iOS we need a better way to be notified - // when the app is about to go in background - if (!appService?.isMobile || !progress) return; saveBookConfig(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [progress, bookKey]); diff --git a/apps/readest-app/src/app/reader/page.tsx b/apps/readest-app/src/app/reader/page.tsx index 78ad075d..45168b19 100644 --- a/apps/readest-app/src/app/reader/page.tsx +++ b/apps/readest-app/src/app/reader/page.tsx @@ -5,6 +5,7 @@ import { useTheme } from '@/hooks/useTheme'; import { hasUpdater } from '@/services/environment'; import { checkForAppUpdates } from '@/helpers/updater'; import { useTranslation } from '@/hooks/useTranslation'; +import { useOpenWithBooks } from '@/hooks/useOpenWithBooks'; import { useSettingsStore } from '@/store/settingsStore'; import Reader from './components/Reader'; @@ -13,6 +14,7 @@ export default function Page() { const { settings } = useSettingsStore(); useTheme(); + useOpenWithBooks(); useEffect(() => { const doCheckAppUpdates = async () => { diff --git a/apps/readest-app/src/hooks/useOpenWithBooks.ts b/apps/readest-app/src/hooks/useOpenWithBooks.ts new file mode 100644 index 00000000..dc4f8eb4 --- /dev/null +++ b/apps/readest-app/src/hooks/useOpenWithBooks.ts @@ -0,0 +1,44 @@ +import { useEffect, useRef } from 'react'; +import { useRouter } from 'next/navigation'; +import { onOpenUrl } from '@tauri-apps/plugin-deep-link'; +import { isTauriAppPlatform } from '@/services/environment'; +import { useLibraryStore } from '@/store/libraryStore'; +import { navigateToLibrary } from '@/utils/nav'; + +export function useOpenWithBooks() { + const router = useRouter(); + const { setCheckOpenWithBooks } = useLibraryStore(); + const listenedOpenWithBooks = useRef(false); + + const handleOpenWithFileUrl = (url: string) => { + let filePath = url; + if (filePath.startsWith('file://')) { + filePath = decodeURI(filePath.replace('file://', '')); + } + if (filePath.startsWith('/')) { + window.OPEN_WITH_FILES = [filePath]; + setCheckOpenWithBooks(true); + navigateToLibrary(router, `reload=${Date.now()}`); + } + }; + + useEffect(() => { + if (!isTauriAppPlatform()) return; + if (listenedOpenWithBooks.current) return; + listenedOpenWithBooks.current = true; + + const listenOpenWithFiles = async () => { + return await onOpenUrl((urls) => { + urls.forEach((url) => { + handleOpenWithFileUrl(url); + }); + }); + }; + + const unlisten = listenOpenWithFiles(); + return () => { + unlisten.then((f) => f()); + }; + // 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 ea563465..bdf2b3f3 100644 --- a/apps/readest-app/src/store/libraryStore.ts +++ b/apps/readest-app/src/store/libraryStore.ts @@ -5,7 +5,7 @@ import { EnvConfigType } from '@/services/environment'; interface LibraryState { library: Book[]; checkOpenWithBooks: boolean; - clearOpenWithBooks: () => void; + setCheckOpenWithBooks: (check: boolean) => void; setLibrary: (books: Book[]) => void; updateBook: (envConfig: EnvConfigType, book: Book) => void; } @@ -13,7 +13,7 @@ interface LibraryState { export const useLibraryStore = create((set, get) => ({ library: [], checkOpenWithBooks: true, - clearOpenWithBooks: () => set({ checkOpenWithBooks: false }), + setCheckOpenWithBooks: (check) => set({ checkOpenWithBooks: check }), setLibrary: (books) => set({ library: books }), updateBook: async (envConfig: EnvConfigType, book: Book) => { const appService = await envConfig.getAppService();