diff --git a/apps/readest-app/src/app/layout.tsx b/apps/readest-app/src/app/layout.tsx index 132d8fd2..d694c277 100644 --- a/apps/readest-app/src/app/layout.tsx +++ b/apps/readest-app/src/app/layout.tsx @@ -16,10 +16,7 @@ export default function RootLayout({ return ( - + diff --git a/apps/readest-app/src/app/page.tsx b/apps/readest-app/src/app/page.tsx index 0950656a..79fbbad2 100644 --- a/apps/readest-app/src/app/page.tsx +++ b/apps/readest-app/src/app/page.tsx @@ -2,13 +2,14 @@ import * as React from 'react'; import { useState } from 'react'; -import Image from 'next/image'; -import { Book, BooksGroup } from '../types/book'; +import { Book, BooksGroup, LibraryItem } from '../types/book'; import { useEnv } from '../context/EnvContext'; -import { FaSearch, FaPlus } from 'react-icons/fa'; + +import Navbar from '@/components/Navbar'; +import Spinner from '@/components/Spinner'; +import Bookshelf from '@/components/Bookshelf'; type AppState = 'Init' | 'Loading' | 'Library' | 'Reader'; -type LibraryItem = Book | BooksGroup; const generateLibraryItems = (groups: BooksGroup[]): LibraryItem[] => { const ungroupedBooks: Book[] = groups.find((group) => group.id === 'ungrouped')?.books || []; @@ -20,12 +21,12 @@ const LibraryPage = () => { const { envConfig } = useEnv(); const [appState, setAppState] = useState('Init'); const [libraryItems, setLibraryItems] = useState([]); - const [loadingInfo, setLoadingInfo] = useState(''); + const [loading, setLoading] = useState(false); React.useEffect(() => { if (appState !== 'Init') return; setAppState('Loading'); - setLoadingInfo('Loading library books...'); + setLoading(true); envConfig.appService().then((appService) => { appService.loadSettings().then((settings) => { console.log('Settings', settings); @@ -36,12 +37,12 @@ const LibraryPage = () => { console.log('Library items:', libraryItems); setLibraryItems(libraryItems); setAppState('Library'); - setLoadingInfo(''); + setLoading(false); }) .catch((err) => { console.error(err); - setLoadingInfo(''); - appService.showMessage('Failed to load library books', 'error'); + setLoading(false); + appService.showMessage(`Failed to load library books: ${err}`, 'error'); }); }); }); @@ -54,96 +55,11 @@ const LibraryPage = () => { return (
- {/* Sticky Top Navbar */} -
-
- {/* Search Input with Magnifier and Plus Icon */} -
- {/* Magnifier Icon */} - - - - {/* Search Input */} - - {/* Plus Icon */} - - -
    -
  • - -
  • -
-
-
-
-
+
- {loadingInfo && ( -
- {/* Spacer to offset fixed navbar height */} - -
- )} - {/* Books Grid */} -
- {libraryItems.map((item, index) => ( -
-
- {'format' in item ? ( -
-
- {(item -
-
-

{(item as Book).title}

-
-
- ) : ( -
- {(item as BooksGroup).books.map((book) => ( -
-
- {book.title} -
-
- ))} -

{(item as BooksGroup).name}

-
- )} -
-
- ))} - - {libraryItems.length > 0 && ( -
- -
- )} -
+ +
diff --git a/apps/readest-app/src/components/Bookshelf.tsx b/apps/readest-app/src/components/Bookshelf.tsx new file mode 100644 index 00000000..0a4fdca1 --- /dev/null +++ b/apps/readest-app/src/components/Bookshelf.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import Image from 'next/image'; +import { Book, BooksGroup, LibraryItem } from '../types/book'; +import { FaPlus } from 'react-icons/fa'; + +interface BookshelfProps { + libraryItems: LibraryItem[]; + onImport: () => void; +} + +const Bookshelf: React.FC = ({ libraryItems, onImport }) => { + return ( +
+ {/* Books Grid */} +
+ {libraryItems.map((item, index) => ( +
+
+ {'format' in item ? ( +
+
+ {(item +
+
+

{(item as Book).title}

+
+
+ ) : ( +
+ {(item as BooksGroup).books.map((book) => ( +
+
+ {book.title} +
+
+ ))} +

{(item as BooksGroup).name}

+
+ )} +
+
+ ))} + + {libraryItems.length > 0 && ( +
+ +
+ )} +
+
+ ); +}; + +export default Bookshelf; diff --git a/apps/readest-app/src/components/Navbar.tsx b/apps/readest-app/src/components/Navbar.tsx new file mode 100644 index 00000000..d3dd3d05 --- /dev/null +++ b/apps/readest-app/src/components/Navbar.tsx @@ -0,0 +1,42 @@ +import * as React from 'react'; +import { FaSearch, FaPlus } from 'react-icons/fa'; + +interface NavbarProps { + onImport: () => void; +} + +const Navbar: React.FC = ({ onImport }) => { + return ( +
+
+ {/* Search Input with Magnifier and Plus Icon */} +
+ {/* Magnifier Icon */} + + + + {/* Search Input */} + + {/* Plus Icon */} + + +
    +
  • + +
  • +
+
+
+
+
+ ); +}; + +export default Navbar; diff --git a/apps/readest-app/src/components/Spinner.tsx b/apps/readest-app/src/components/Spinner.tsx new file mode 100644 index 00000000..76956883 --- /dev/null +++ b/apps/readest-app/src/components/Spinner.tsx @@ -0,0 +1,21 @@ +import React from 'react'; + +interface SpinnerProps { + loading: boolean; +} + +const Spinner: React.FC = ({ loading }) => { + if (!loading) return null; + + return ( +
+ + Loading... +
+ ); +}; + +export default Spinner; diff --git a/apps/readest-app/src/services/nativeAppService.ts b/apps/readest-app/src/services/nativeAppService.ts index b916bfac..8b69edc3 100644 --- a/apps/readest-app/src/services/nativeAppService.ts +++ b/apps/readest-app/src/services/nativeAppService.ts @@ -9,7 +9,6 @@ import { remove, BaseDirectory, } from '@tauri-apps/plugin-fs'; -import { type as osType } from '@tauri-apps/plugin-os'; import { join, appConfigDir, @@ -25,7 +24,6 @@ import { Book, BooksGroup } from '../types/book'; import { SystemSettings } from '../types/settings'; import { AppService, BaseDir, ToastType } from '../types/system'; -const IS_MOBILE = osType() === 'ios' || osType() === 'android'; const BOOKS_SUBDIR = 'DigestLibrary/Books'; let BOOKS_DIR = ''; @@ -53,7 +51,7 @@ function resolvePath( return { baseDir: BaseDirectory.AppLog, fp, base, dir: appLogDir }; case 'Books': return { - baseDir: IS_MOBILE ? BaseDirectory.AppData : BaseDirectory.Document, + baseDir: BaseDirectory.Document, fp: `${BOOKS_SUBDIR}/${fp}`, base, dir: () => new Promise((r) => r(`${BOOKS_DIR}/`)), @@ -132,10 +130,8 @@ export const nativeAppService: AppService = { const txt = await nativeAppService.fs.readFile(fp, base, 'text'); settings = JSON.parse(txt as string); } catch { - const INIT_BOOKS_DIR = await join( - IS_MOBILE ? await appDataDir() : await documentDir(), - BOOKS_SUBDIR, - ); + const INIT_BOOKS_DIR = await join(await documentDir(), BOOKS_SUBDIR); + await nativeAppService.fs.createDir('', 'Books', true); settings = { localBooksDir: INIT_BOOKS_DIR, globalReadSettings: { diff --git a/apps/readest-app/src/types/book.ts b/apps/readest-app/src/types/book.ts index e15fa836..390aba50 100644 --- a/apps/readest-app/src/types/book.ts +++ b/apps/readest-app/src/types/book.ts @@ -36,3 +36,5 @@ export interface BooksGroup { books: Book[]; lastUpdated: number; } + +export type LibraryItem = Book | BooksGroup;