forked from akai/readest
This commit is contained in:
@@ -34,6 +34,7 @@ import { useTheme } from '@/hooks/useTheme';
|
||||
import { useUICSS } from '@/hooks/useUICSS';
|
||||
import { useDemoBooks } from './hooks/useDemoBooks';
|
||||
import { useBooksSync } from './hooks/useBooksSync';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { useScreenWakeLock } from '@/hooks/useScreenWakeLock';
|
||||
import { useOpenWithBooks } from '@/hooks/useOpenWithBooks';
|
||||
import { SelectedFile, useFileSelector } from '@/hooks/useFileSelector';
|
||||
@@ -87,6 +88,7 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP
|
||||
const _ = useTranslation();
|
||||
const { selectFiles } = useFileSelector(appService, _);
|
||||
const { safeAreaInsets: insets, isRoundedWindow } = useThemeStore();
|
||||
const { clearBookData } = useBookDataStore();
|
||||
const { settings, setSettings, saveSettings } = useSettingsStore();
|
||||
const { isSettingsDialogOpen, setSettingsDialogOpen } = useSettingsStore();
|
||||
const [showCatalogManager, setShowCatalogManager] = useState(false);
|
||||
@@ -531,6 +533,7 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP
|
||||
try {
|
||||
await appService?.deleteBook(book, deleteAction);
|
||||
await updateBook(envConfig, book);
|
||||
clearBookData(book.hash);
|
||||
if (syncBooks) pushLibrary();
|
||||
eventDispatcher.dispatch('toast', {
|
||||
type: 'info',
|
||||
|
||||
@@ -5,7 +5,6 @@ import { Book } from '@/types/book';
|
||||
import { BookMetadata } from '@/libs/document';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useMetadataEdit } from './useMetadataEdit';
|
||||
import { DeleteAction } from '@/types/system';
|
||||
@@ -46,14 +45,13 @@ const BookDetailModal: React.FC<BookDetailModalProps> = ({
|
||||
handleBookMetadataUpdate,
|
||||
}) => {
|
||||
const _ = useTranslation();
|
||||
const { envConfig } = useEnv();
|
||||
const { safeAreaInsets } = useThemeStore();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [activeDeleteAction, setActiveDeleteAction] = useState<DeleteAction | null>(null);
|
||||
const [editMode, setEditMode] = useState(false);
|
||||
const [bookMeta, setBookMeta] = useState<BookMetadata | null>(null);
|
||||
const [fileSize, setFileSize] = useState<number | null>(null);
|
||||
const { envConfig } = useEnv();
|
||||
const { settings } = useSettingsStore();
|
||||
|
||||
// Initialize metadata edit hook
|
||||
const {
|
||||
@@ -97,7 +95,7 @@ const BookDetailModal: React.FC<BookDetailModalProps> = ({
|
||||
const fetchBookDetails = async () => {
|
||||
const appService = await envConfig.getAppService();
|
||||
try {
|
||||
const details = book.metadata || (await appService.fetchBookDetails(book, settings));
|
||||
const details = book.metadata || (await appService.fetchBookDetails(book));
|
||||
setBookMeta(details);
|
||||
const size = await appService.getBookFileSize(book);
|
||||
setFileSize(size);
|
||||
|
||||
@@ -19,7 +19,8 @@ const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
|
||||
const { token } = useAuth();
|
||||
const { envConfig } = useEnv();
|
||||
const { settings, applyUILanguage } = useSettingsStore();
|
||||
const { getViewSettings, setViewSettings, recreateViewer } = useReaderStore();
|
||||
const { getView, getViewSettings, setViewSettings, recreateViewer } = useReaderStore();
|
||||
const view = getView(bookKey);
|
||||
const viewSettings = getViewSettings(bookKey) || settings.globalViewSettings;
|
||||
|
||||
const [uiLanguage, setUILanguage] = useState(viewSettings.uiLanguage);
|
||||
@@ -191,7 +192,7 @@ const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
|
||||
bookKey,
|
||||
'replaceQuotationMarks',
|
||||
replaceQuotationMarks,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
).then(() => {
|
||||
recreateViewer(envConfig, bookKey);
|
||||
@@ -231,7 +232,7 @@ const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
|
||||
bookKey,
|
||||
'convertChineseVariant',
|
||||
convertChineseVariant,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
).then(() => {
|
||||
recreateViewer(envConfig, bookKey);
|
||||
@@ -268,6 +269,7 @@ const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
|
||||
className='toggle'
|
||||
checked={translationEnabled}
|
||||
onChange={() => setTranslationEnabled(!translationEnabled)}
|
||||
disabled={!bookKey}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -311,7 +313,7 @@ const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isCJKEnv() && (
|
||||
{(isCJKEnv() || view?.language.isCJK) && (
|
||||
<div className='w-full'>
|
||||
<h2 className='mb-2 font-medium'>{_('Punctuation')}</h2>
|
||||
<div className='card border-base-200 bg-base-100 border shadow'>
|
||||
@@ -333,7 +335,7 @@ const LangPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterReset
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isCJKEnv() && (
|
||||
{(isCJKEnv() || view?.language.isCJK) && (
|
||||
<div className='w-full'>
|
||||
<h2 className='mb-2 font-medium'>{_('Convert Simplified and Traditional Chinese')}</h2>
|
||||
<div className='card border-base-200 bg-base-100 border shadow'>
|
||||
|
||||
@@ -668,7 +668,7 @@ export abstract class BaseAppService implements AppService {
|
||||
return null;
|
||||
}
|
||||
|
||||
async loadBookContent(book: Book, settings: SystemSettings): Promise<BookContent> {
|
||||
async loadBookContent(book: Book): Promise<BookContent> {
|
||||
let file: File;
|
||||
const fp = getLocalBookFilename(book);
|
||||
if (await this.fs.exists(fp, 'Books')) {
|
||||
@@ -692,7 +692,7 @@ export abstract class BaseAppService implements AppService {
|
||||
throw new Error(BOOK_FILE_NOT_FOUND_ERROR);
|
||||
}
|
||||
}
|
||||
return { book, file, config: await this.loadBookConfig(book, settings) };
|
||||
return { book, file };
|
||||
}
|
||||
|
||||
async loadBookConfig(book: Book, settings: SystemSettings): Promise<BookConfig> {
|
||||
@@ -711,13 +711,13 @@ export abstract class BaseAppService implements AppService {
|
||||
}
|
||||
}
|
||||
|
||||
async fetchBookDetails(book: Book, settings: SystemSettings) {
|
||||
async fetchBookDetails(book: Book) {
|
||||
const fp = getLocalBookFilename(book);
|
||||
if (!(await this.fs.exists(fp, 'Books')) && book.uploadedAt) {
|
||||
await this.downloadBook(book);
|
||||
}
|
||||
const { file } = (await this.loadBookContent(book, settings)) as BookContent;
|
||||
const bookDoc = (await new DocumentLoader(file).open()).book as BookDoc;
|
||||
const { file } = await this.loadBookContent(book);
|
||||
const bookDoc = (await new DocumentLoader(file).open()).book;
|
||||
const f = file as ClosableFile;
|
||||
if (f && f.close) {
|
||||
await f.close();
|
||||
|
||||
@@ -27,6 +27,7 @@ interface BookDataState {
|
||||
) => void;
|
||||
updateBooknotes: (key: string, booknotes: BookNote[]) => BookConfig | undefined;
|
||||
getBookData: (keyOrId: string) => BookData | null;
|
||||
clearBookData: (keyOrId: string) => void;
|
||||
}
|
||||
|
||||
export const useBookDataStore = create<BookDataState>((set, get) => ({
|
||||
@@ -35,6 +36,16 @@ export const useBookDataStore = create<BookDataState>((set, get) => ({
|
||||
const id = keyOrId.split('-')[0]!;
|
||||
return get().booksData[id] || null;
|
||||
},
|
||||
clearBookData: (keyOrId: string) => {
|
||||
const id = keyOrId.split('-')[0]!;
|
||||
set((state) => {
|
||||
const newBooksData = { ...state.booksData };
|
||||
delete newBooksData[id];
|
||||
return {
|
||||
booksData: newBooksData,
|
||||
};
|
||||
});
|
||||
},
|
||||
getConfig: (key: string | null) => {
|
||||
if (!key) return null;
|
||||
const id = key.split('-')[0]!;
|
||||
|
||||
@@ -144,51 +144,53 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
},
|
||||
}));
|
||||
try {
|
||||
const appService = await envConfig.getAppService();
|
||||
const { settings } = useSettingsStore.getState();
|
||||
if (!bookData || reload) {
|
||||
const appService = await envConfig.getAppService();
|
||||
const { library } = useLibraryStore.getState();
|
||||
const book = library.find((b) => b.hash === id);
|
||||
if (!book) {
|
||||
throw new Error('Book not found');
|
||||
}
|
||||
const content = (await appService.loadBookContent(book, settings)) as BookContent;
|
||||
const { file, config } = content;
|
||||
console.log('Loading book', key);
|
||||
const { book: bookDoc } = await new DocumentLoader(file).open();
|
||||
await updateToc(
|
||||
bookDoc,
|
||||
config.viewSettings?.sortedTOC ?? false,
|
||||
config.viewSettings?.convertChineseVariant ?? 'none',
|
||||
);
|
||||
if (!bookDoc.metadata.title) {
|
||||
bookDoc.metadata.title = getBaseFilename(file.name);
|
||||
}
|
||||
book.sourceTitle = formatTitle(bookDoc.metadata.title);
|
||||
// Correct language codes mistakenly set with language names
|
||||
if (typeof bookDoc.metadata?.language === 'string') {
|
||||
if (bookDoc.metadata.language in SUPPORTED_LANGNAMES) {
|
||||
bookDoc.metadata.language = SUPPORTED_LANGNAMES[bookDoc.metadata.language]!;
|
||||
}
|
||||
}
|
||||
// Set the book's language for formerly imported books, newly imported books have this field set
|
||||
const primaryLanguage = getPrimaryLanguage(bookDoc.metadata.language);
|
||||
book.primaryLanguage = book.primaryLanguage ?? primaryLanguage;
|
||||
book.metadata = book.metadata ?? bookDoc.metadata;
|
||||
// TODO: uncomment this when we can ensure metaHash is correctly generated for all books
|
||||
// book.metaHash = book.metaHash ?? getMetadataHash(bookDoc.metadata);
|
||||
book.metaHash = getMetadataHash(bookDoc.metadata);
|
||||
|
||||
const isFixedLayout = FIXED_LAYOUT_FORMATS.has(book.format);
|
||||
useBookDataStore.setState((state) => ({
|
||||
booksData: {
|
||||
...state.booksData,
|
||||
[id]: { id, book, file, config, bookDoc, isFixedLayout },
|
||||
},
|
||||
}));
|
||||
const { library } = useLibraryStore.getState();
|
||||
const book = library.find((b) => b.hash === id);
|
||||
if (!book) {
|
||||
throw new Error('Book not found');
|
||||
}
|
||||
const booksData = useBookDataStore.getState().booksData;
|
||||
const config = booksData[id]?.config as BookConfig;
|
||||
let bookDoc = bookData?.bookDoc;
|
||||
let file = bookData?.file;
|
||||
if (!bookDoc || !file || reload) {
|
||||
const content = (await appService.loadBookContent(book)) as BookContent;
|
||||
file = content.file;
|
||||
console.log('Loading book', key);
|
||||
const doc = await new DocumentLoader(file).open();
|
||||
bookDoc = doc.book;
|
||||
}
|
||||
const config = await appService.loadBookConfig(book, settings);
|
||||
await updateToc(
|
||||
bookDoc,
|
||||
config.viewSettings?.sortedTOC ?? false,
|
||||
config.viewSettings?.convertChineseVariant ?? 'none',
|
||||
);
|
||||
if (!bookDoc.metadata.title) {
|
||||
bookDoc.metadata.title = getBaseFilename(file.name);
|
||||
}
|
||||
book.sourceTitle = formatTitle(bookDoc.metadata.title);
|
||||
// Correct language codes mistakenly set with language names
|
||||
if (typeof bookDoc.metadata?.language === 'string') {
|
||||
if (bookDoc.metadata.language in SUPPORTED_LANGNAMES) {
|
||||
bookDoc.metadata.language = SUPPORTED_LANGNAMES[bookDoc.metadata.language]!;
|
||||
}
|
||||
}
|
||||
// Set the book's language for formerly imported books, newly imported books have this field set
|
||||
const primaryLanguage = getPrimaryLanguage(bookDoc.metadata.language);
|
||||
book.primaryLanguage = book.primaryLanguage ?? primaryLanguage;
|
||||
book.metadata = book.metadata ?? bookDoc.metadata;
|
||||
// TODO: uncomment this when we can ensure metaHash is correctly generated for all books
|
||||
// book.metaHash = book.metaHash ?? getMetadataHash(bookDoc.metadata);
|
||||
book.metaHash = getMetadataHash(bookDoc.metadata);
|
||||
|
||||
const isFixedLayout = FIXED_LAYOUT_FORMATS.has(book.format);
|
||||
useBookDataStore.setState((state) => ({
|
||||
booksData: {
|
||||
...state.booksData,
|
||||
[id]: { id, book, file, config, bookDoc, isFixedLayout },
|
||||
},
|
||||
}));
|
||||
const configViewSettings = config.viewSettings!;
|
||||
const globalViewSettings = settings.globalViewSettings;
|
||||
set((state) => ({
|
||||
|
||||
@@ -293,5 +293,4 @@ export interface BooksGroup {
|
||||
export interface BookContent {
|
||||
book: Book;
|
||||
file: File;
|
||||
config: BookConfig;
|
||||
}
|
||||
|
||||
@@ -112,9 +112,9 @@ export interface AppService {
|
||||
isBookAvailable(book: Book): Promise<boolean>;
|
||||
getBookFileSize(book: Book): Promise<number | null>;
|
||||
loadBookConfig(book: Book, settings: SystemSettings): Promise<BookConfig>;
|
||||
fetchBookDetails(book: Book, settings: SystemSettings): Promise<BookMetadata>;
|
||||
fetchBookDetails(book: Book): Promise<BookMetadata>;
|
||||
saveBookConfig(book: Book, config: BookConfig, settings?: SystemSettings): Promise<void>;
|
||||
loadBookContent(book: Book, settings: SystemSettings): Promise<BookContent>;
|
||||
loadBookContent(book: Book): Promise<BookContent>;
|
||||
loadLibraryBooks(): Promise<Book[]>;
|
||||
saveLibraryBooks(books: Book[]): Promise<void>;
|
||||
getCoverImageUrl(book: Book): string;
|
||||
|
||||
Reference in New Issue
Block a user