Use bookID in path in Web version

This commit is contained in:
chrox
2024-12-06 10:27:27 +01:00
parent f21f3a3fc0
commit 242fbf6743
12 changed files with 99 additions and 56 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ const internalHost = process.env.TAURI_DEV_HOST || 'localhost';
const nextConfig = {
// Ensure Next.js uses SSG instead of SSR
// https://nextjs.org/docs/pages/building-your-application/deploying/static-exports
output: 'export',
output: process.env['NEXT_PUBLIC_APP_PLATFORM'] === 'web' ? undefined : 'output',
// Note: This feature is required to use the Next.js Image component in SSG mode.
// See https://nextjs.org/docs/messages/export-image-api for different workarounds.
images: {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@readest/readest-app",
"version": "0.8.1",
"version": "0.8.2",
"private": true,
"scripts": {
"dev": "dotenv -e .env.tauri -- next dev",
@@ -11,6 +11,7 @@ import Image from 'next/image';
import { Book, BooksGroup } from '@/types/book';
import { useEnv } from '@/context/EnvContext';
import { useLibraryStore } from '@/store/libraryStore';
import { navigateToReader } from '@/utils/nav';
import Alert from '@/components/Alert';
import Spinner from '@/components/Spinner';
@@ -68,7 +69,7 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
setClickedImage(id);
setTimeout(() => setClickedImage(null), 300);
setTimeout(() => setLoading(true), 200);
router.push(`/reader?ids=${id}`);
navigateToReader(router, [id]);
}
};
@@ -80,7 +81,7 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
const openSelectedBooks = () => {
setTimeout(() => setLoading(true), 200);
router.push(`/reader?ids=${selectedBooks.join(',')}`);
navigateToReader(router, selectedBooks);
};
const confirmDelete = () => {
+2 -1
View File
@@ -6,6 +6,7 @@ import { useRouter } from 'next/navigation';
import { Book } from '@/types/book';
import { AppService } from '@/types/system';
import { navigateToReader } from '@/utils/nav';
import { parseOpenWithFiles } from '@/helpers/cli';
import { isTauriAppPlatform } from '@/services/environment';
import { FILE_ACCEPT_FORMATS, SUPPORTED_FILE_EXTS } from '@/services/constants';
@@ -47,7 +48,7 @@ const LibraryPage = () => {
console.log('Opening books:', bookIds);
if (bookIds.length > 0) {
router.push(`/reader?ids=${bookIds.join(',')}`);
navigateToReader(router, bookIds);
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
+2 -1
View File
@@ -2,6 +2,7 @@
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { navigateToLibrary } from '@/utils/nav';
import Spinner from '@/components/Spinner';
@@ -9,7 +10,7 @@ const HomePage = () => {
const router = useRouter();
useEffect(() => {
router.push('/library');
navigateToLibrary(router);
}, [router]);
return <Spinner loading={true} />;
@@ -0,0 +1,6 @@
import Reader from '../components/Reader';
export default async function Page({ params }: { params: Promise<{ ids: string }> }) {
const ids = decodeURIComponent((await params).ids);
return <Reader ids={ids} />;
}
@@ -0,0 +1,46 @@
'use client';
import * as React from 'react';
import { useEffect, Suspense, useRef } from 'react';
import { useEnv } from '@/context/EnvContext';
import { useLibraryStore } from '@/store/libraryStore';
import ReaderContent from './ReaderContent';
import { AboutWindow } from '@/components/AboutWindow';
import { useSettingsStore } from '@/store/settingsStore';
const Reader: React.FC<{ ids?: string }> = ({ ids }) => {
const { envConfig } = useEnv();
const { settings, setSettings } = useSettingsStore();
const { library, setLibrary } = useLibraryStore();
const isInitiating = useRef(false);
useEffect(() => {
if (isInitiating.current) return;
isInitiating.current = true;
const initLibrary = async () => {
const appService = await envConfig.getAppService();
const settings = await appService.loadSettings();
setSettings(settings);
setLibrary(await appService.loadLibraryBooks());
};
initLibrary();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
library.length > 0 &&
settings.globalReadSettings && (
<div className='reader-page bg-base-100 text-base-content min-h-screen select-none'>
<Suspense>
<ReaderContent ids={ids} settings={settings} />
<AboutWindow />
</Suspense>
</div>
)
);
};
export default Reader;
@@ -13,6 +13,8 @@ import { SystemSettings } from '@/types/settings';
import { parseOpenWithFiles } from '@/helpers/cli';
import { tauriHandleClose } from '@/utils/window';
import { uniqueId } from '@/utils/misc';
import { navigateToLibrary } from '@/utils/nav';
import { BOOK_IDS_SEPARATOR } from '@/services/constants';
import useBooksManager from '../hooks/useBooksManager';
import useBookShortcuts from '../hooks/useBookShortcuts';
@@ -21,7 +23,7 @@ import SideBar from './sidebar/SideBar';
import Notebook from './notebook/Notebook';
import BooksGrid from './BooksGrid';
const ReaderContent: React.FC<{ settings: SystemSettings }> = ({ settings }) => {
const ReaderContent: React.FC<{ ids?: string; settings: SystemSettings }> = ({ ids, settings }) => {
const router = useRouter();
const searchParams = useSearchParams();
const { envConfig } = useEnv();
@@ -40,7 +42,8 @@ const ReaderContent: React.FC<{ settings: SystemSettings }> = ({ settings }) =>
if (isInitiating.current) return;
isInitiating.current = true;
const initialIds = (searchParams.get('ids') || '').split(',').filter(Boolean);
const bookIds = ids || searchParams.get('ids') || '';
const initialIds = bookIds.split(BOOK_IDS_SEPARATOR).filter(Boolean);
const initialBookKeys = initialIds.map((id) => `${id}-${uniqueId()}`);
setBookKeys(initialBookKeys);
const uniqueIds = new Set<string>();
@@ -72,7 +75,7 @@ const ReaderContent: React.FC<{ settings: SystemSettings }> = ({ settings }) =>
const saveSettingsAndGoToLibrary = () => {
saveSettings(envConfig, settings);
router.replace('/library');
navigateToLibrary(router);
};
const handleCloseBooks = () => {
@@ -5,6 +5,7 @@ import { useReaderStore } from '@/store/readerStore';
import { useSidebarStore } from '@/store/sidebarStore';
import { uniqueId } from '@/utils/misc';
import { useParallelViewStore } from '@/store/parallelViewStore';
import { navigateToReader } from '@/utils/nav';
const useBooksManager = () => {
const router = useRouter();
@@ -18,11 +19,9 @@ const useBooksManager = () => {
useEffect(() => {
if (shouldUpdateSearchParams) {
const ids = bookKeys.map((key) => key.split('-')[0]).join(',');
const ids = bookKeys.map((key) => key.split('-')[0]!);
if (ids) {
const params = new URLSearchParams(searchParams.toString());
params.set('ids', ids);
router.replace(`?${params.toString()}`, { scroll: false });
navigateToReader(router, ids, searchParams.toString(), { scroll: false });
}
setShouldUpdateSearchParams(false);
}
+4 -43
View File
@@ -1,46 +1,7 @@
'use client';
import * as React from 'react';
import { useEffect, Suspense, useRef } from 'react';
import Reader from './components/Reader';
import { useEnv } from '@/context/EnvContext';
import { useLibraryStore } from '@/store/libraryStore';
import ReaderContent from './components/ReaderContent';
import { AboutWindow } from '@/components/AboutWindow';
import { useSettingsStore } from '@/store/settingsStore';
const ReaderPage = () => {
const { envConfig } = useEnv();
const { settings, setSettings } = useSettingsStore();
const { library, setLibrary } = useLibraryStore();
const isInitiating = useRef(false);
useEffect(() => {
if (isInitiating.current) return;
isInitiating.current = true;
const initLibrary = async () => {
const appService = await envConfig.getAppService();
const settings = await appService.loadSettings();
setSettings(settings);
setLibrary(await appService.loadLibraryBooks());
};
initLibrary();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
library.length > 0 &&
settings.globalReadSettings && (
<div className='reader-page bg-base-100 text-base-content min-h-screen select-none'>
<Suspense>
<ReaderContent settings={settings} />
<AboutWindow />
</Suspense>
</div>
)
);
};
export default ReaderPage;
export default function Page() {
return <Reader />;
}
@@ -78,3 +78,5 @@ export const SANS_SERIF_FONTS = ['Roboto', 'Noto Sans', 'Open Sans', 'Helvetica'
export const MONOSPACE_FONTS = ['Fira Code', 'Lucida Console', 'Consolas', 'Courier New'];
export const ONE_COLUMN_MAX_INLINE_SIZE = 9999;
export const BOOK_IDS_SEPARATOR = '+';
+23
View File
@@ -0,0 +1,23 @@
import { useRouter } from 'next/navigation';
import { isWebAppPlatform } from '@/services/environment';
import { BOOK_IDS_SEPARATOR } from '@/services/constants';
export const navigateToReader = (
router: ReturnType<typeof useRouter>,
bookIds: string[],
queryParams?: string,
navOptions?: { scroll?: boolean },
) => {
const ids = bookIds.join(BOOK_IDS_SEPARATOR);
if (isWebAppPlatform()) {
router.push(`/reader/${ids}${queryParams ? `?${queryParams}` : ''}`, navOptions);
} else {
const params = new URLSearchParams(queryParams || '');
params.set('ids', ids);
router.push(`/reader?${params.toString()}`, navOptions);
}
};
export const navigateToLibrary = (router: ReturnType<typeof useRouter>) => {
router.push('/library');
};