Various fixes
This commit is contained in:
@@ -11,7 +11,7 @@ rust-version = "1.71"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
name = "readest"
|
||||
name = "readestlib"
|
||||
crate-type = ["staticlib", "cdylib", "lib"]
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
fn main() {
|
||||
readest::run();
|
||||
readestlib::run();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { useState } from 'react';
|
||||
import { PiPlus } from 'react-icons/pi';
|
||||
import { MdDelete, MdOpenInNew } from 'react-icons/md';
|
||||
import { MdCheckCircle, MdCheckCircleOutline } from 'react-icons/md';
|
||||
@@ -11,6 +12,7 @@ import Alert from '@/components/Alert';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import clsx from 'clsx';
|
||||
import Spinner from '@/components/Spinner';
|
||||
|
||||
type BookshelfItem = Book | BooksGroup;
|
||||
|
||||
@@ -48,9 +50,10 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
|
||||
const router = useRouter();
|
||||
const { envConfig } = useEnv();
|
||||
const { deleteBook } = useReaderStore();
|
||||
const [selectedBooks, setSelectedBooks] = React.useState<string[]>([]);
|
||||
const [showDeleteAlert, setShowDeleteAlert] = React.useState(false);
|
||||
const [clickedImage, setClickedImage] = React.useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [selectedBooks, setSelectedBooks] = useState<string[]>([]);
|
||||
const [showDeleteAlert, setShowDeleteAlert] = useState(false);
|
||||
const [clickedImage, setClickedImage] = useState<string | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
setSelectedBooks([]);
|
||||
@@ -64,6 +67,7 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
|
||||
} else {
|
||||
setClickedImage(id);
|
||||
setTimeout(() => setClickedImage(null), 300);
|
||||
setTimeout(() => setLoading(true), 200);
|
||||
router.push(`/reader?ids=${id}`);
|
||||
}
|
||||
};
|
||||
@@ -75,6 +79,7 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
|
||||
};
|
||||
|
||||
const openSelectedBooks = () => {
|
||||
setTimeout(() => setLoading(true), 200);
|
||||
router.push(`/reader?ids=${selectedBooks.join(',')}`);
|
||||
};
|
||||
|
||||
@@ -123,8 +128,8 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
|
||||
/>
|
||||
<div
|
||||
className={clsx(
|
||||
'invisible absolute inset-0 flex items-center justify-center',
|
||||
'rounded-none text-center font-sans text-lg font-medium text-gray-600',
|
||||
'invisible absolute inset-0 flex items-center justify-center p-1',
|
||||
'text-neutral-content rounded-none text-center font-serif text-base font-medium',
|
||||
)}
|
||||
>
|
||||
{item.title}
|
||||
@@ -196,6 +201,11 @@ const Bookshelf: React.FC<BookshelfProps> = ({ libraryBooks, isSelectMode, onImp
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{loading && (
|
||||
<div className='fixed inset-0 z-50 flex items-center justify-center'>
|
||||
<Spinner loading />
|
||||
</div>
|
||||
)}
|
||||
{showDeleteAlert && (
|
||||
<Alert
|
||||
title='Confirm Deletion'
|
||||
|
||||
@@ -13,17 +13,19 @@ import Bookshelf from '@/app/library/components/Bookshelf';
|
||||
const LibraryPage = () => {
|
||||
const { envConfig, appService } = useEnv();
|
||||
const { library: libraryBooks, setLibrary } = useReaderStore();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const isInitiating = useRef(false);
|
||||
const [isSelectMode, setIsSelectMode] = useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isInitiating.current) return;
|
||||
isInitiating.current = true;
|
||||
setLoading(true);
|
||||
|
||||
const loadingTimeout = setTimeout(() => setLoading(true), 200);
|
||||
envConfig.getAppService().then(async (appService) => {
|
||||
console.log('Loading library books...');
|
||||
setLibrary(await appService.loadLibraryBooks());
|
||||
if (loadingTimeout) clearTimeout(loadingTimeout);
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
@@ -73,7 +75,7 @@ const LibraryPage = () => {
|
||||
};
|
||||
|
||||
if (!appService) {
|
||||
return <Spinner loading />;
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -90,29 +92,29 @@ const LibraryPage = () => {
|
||||
<Spinner loading />
|
||||
</div>
|
||||
)}
|
||||
<div className='mt-12 flex-grow overflow-auto px-2'>
|
||||
{libraryBooks.length > 0 ? (
|
||||
{libraryBooks.length > 0 ? (
|
||||
<div className='mt-12 flex-grow overflow-auto px-2'>
|
||||
<Bookshelf
|
||||
libraryBooks={libraryBooks}
|
||||
isSelectMode={isSelectMode}
|
||||
onImportBooks={handleImportBooks}
|
||||
/>
|
||||
) : (
|
||||
<div className='hero h-full items-center justify-center'>
|
||||
<div className='hero-content text-neutral-content text-center'>
|
||||
<div className='max-w-md'>
|
||||
<h1 className='mb-5 text-5xl font-bold'>Your Library</h1>
|
||||
<p className='mb-5'>
|
||||
Welcome to your library. You can upload your books here and read them anytime.
|
||||
</p>
|
||||
<button className='btn btn-primary' onClick={handleImportBooks}>
|
||||
Upload Books
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className='hero h-full items-center justify-center'>
|
||||
<div className='hero-content text-neutral-content text-center'>
|
||||
<div className='max-w-md'>
|
||||
<h1 className='mb-5 text-5xl font-bold'>Your Library</h1>
|
||||
<p className='mb-5'>
|
||||
Welcome to your library. You can import your books here and read them anytime.
|
||||
</p>
|
||||
<button className='btn btn-primary rounded-xl' onClick={handleImportBooks}>
|
||||
Import Books
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -116,6 +116,8 @@ const FoliateViewer: React.FC<{
|
||||
|
||||
useEffect(() => {
|
||||
if (isViewCreated.current) return;
|
||||
isViewCreated.current = true;
|
||||
|
||||
const openBook = async () => {
|
||||
console.log('Opening book', bookKey);
|
||||
await import('foliate-js/view.js');
|
||||
@@ -161,13 +163,6 @@ const FoliateViewer: React.FC<{
|
||||
};
|
||||
|
||||
openBook();
|
||||
isViewCreated.current = true;
|
||||
|
||||
return () => {
|
||||
console.log('Closing book', bookKey);
|
||||
viewRef.current?.close();
|
||||
viewRef.current?.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const initAnnotations = () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { useState, useRef } from 'react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
@@ -24,10 +25,15 @@ const ReaderContent: React.FC<{ settings: SystemSettings }> = ({ settings }) =>
|
||||
useReaderStore();
|
||||
const { setBookKeys, getBookData, initViewState, getViewState, clearViewState } =
|
||||
useReaderStore();
|
||||
const isInitiating = useRef(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useBookShortcuts({ sideBarBookKey, bookKeys });
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isInitiating.current) return;
|
||||
isInitiating.current = true;
|
||||
|
||||
const initialIds = (searchParams.get('ids') || '').split(',').filter(Boolean);
|
||||
const initialBookKeys = initialIds.map((id) => `${id}-${uniqueId()}`);
|
||||
setBookKeys(initialBookKeys);
|
||||
@@ -45,6 +51,7 @@ const ReaderContent: React.FC<{ settings: SystemSettings }> = ({ settings }) =>
|
||||
}, []);
|
||||
|
||||
const saveConfigAndCloseBook = (bookKey: string) => {
|
||||
console.log('Closing book', bookKey);
|
||||
getView(bookKey)?.close();
|
||||
getView(bookKey)?.remove();
|
||||
const config = getConfig(bookKey);
|
||||
@@ -82,10 +89,13 @@ const ReaderContent: React.FC<{ settings: SystemSettings }> = ({ settings }) =>
|
||||
if (!bookKeys || bookKeys.length === 0) return null;
|
||||
const bookData = getBookData(bookKeys[0]!);
|
||||
if (!bookData || !bookData.book || !bookData.bookDoc) {
|
||||
setTimeout(() => setLoading(true), 200);
|
||||
return (
|
||||
<div className={'hero hero-content min-h-screen'}>
|
||||
<Spinner loading={true} />
|
||||
</div>
|
||||
loading && (
|
||||
<div className={'hero hero-content min-h-screen'}>
|
||||
<Spinner loading={true} />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,13 @@ const BookCard: React.FC<BookCardProps> = ({ cover, title, author }) => {
|
||||
width={56}
|
||||
height={80}
|
||||
className='mr-4 aspect-auto max-h-20 w-[15%] max-w-14 rounded-sm object-cover shadow-md'
|
||||
onError={(e) => {
|
||||
(e.target as HTMLImageElement).style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
<div className='min-w-0 flex-1'>
|
||||
<h4 className='line-clamp-2 w-[90%] text-sm font-semibold'>{title}</h4>
|
||||
<p className='text-neutral-content truncate text-sm'>{formatAuthors(author)}</p>
|
||||
<p className='text-neutral-content truncate text-xs'>{formatAuthors(author)}</p>
|
||||
</div>
|
||||
<button
|
||||
className='btn btn-ghost hover:bg-base-300 h-6 min-h-6 w-6 rounded-full p-0 transition-colors'
|
||||
|
||||
Reference in New Issue
Block a user