diff --git a/apps/readest-app/src/app/reader/components/BookmarkToggler.tsx b/apps/readest-app/src/app/reader/components/BookmarkToggler.tsx index 9634e61c..5880498e 100644 --- a/apps/readest-app/src/app/reader/components/BookmarkToggler.tsx +++ b/apps/readest-app/src/app/reader/components/BookmarkToggler.tsx @@ -10,6 +10,7 @@ import { useEnv } from '@/context/EnvContext'; import { BookNote } from '@/types/book'; import { uniqueId } from '@/utils/misc'; import Button from '@/components/Button'; +import { getCurrentPage } from '@/utils/book'; interface BookmarkTogglerProps { bookKey: string; @@ -19,10 +20,11 @@ const BookmarkToggler: React.FC = ({ bookKey }) => { const _ = useTranslation(); const { envConfig } = useEnv(); const { settings } = useSettingsStore(); - const { getConfig, saveConfig, updateBooknotes } = useBookDataStore(); + const { getConfig, saveConfig, getBookData, updateBooknotes } = useBookDataStore(); const { getProgress, setBookmarkRibbonVisibility } = useReaderStore(); const config = getConfig(bookKey)!; const progress = getProgress(bookKey)!; + const bookData = getBookData(bookKey)!; const [isBookmarked, setIsBookmarked] = useState(false); @@ -38,7 +40,7 @@ const BookmarkToggler: React.FC = ({ bookKey }) => { id: uniqueId(), type: 'bookmark', cfi, - text: truncatedText, + text: truncatedText ? truncatedText : `${getCurrentPage(bookData.book!, progress)}`, note: '', createdAt: Date.now(), updatedAt: Date.now(), @@ -49,6 +51,7 @@ const BookmarkToggler: React.FC = ({ bookKey }) => { if (existingBookmark) { existingBookmark.deletedAt = null; existingBookmark.updatedAt = Date.now(); + existingBookmark.text = bookmark.text; } else { bookmarks.push(bookmark); } diff --git a/apps/readest-app/src/app/reader/components/notebook/Notebook.tsx b/apps/readest-app/src/app/reader/components/notebook/Notebook.tsx index c4fcf755..9c99f449 100644 --- a/apps/readest-app/src/app/reader/components/notebook/Notebook.tsx +++ b/apps/readest-app/src/app/reader/components/notebook/Notebook.tsx @@ -191,12 +191,7 @@ const Notebook: React.FC = ({}) => { )}
    {annotationNotes.map((item, index) => ( - + ))}
diff --git a/apps/readest-app/src/app/reader/components/sidebar/BooknoteItem.tsx b/apps/readest-app/src/app/reader/components/sidebar/BooknoteItem.tsx index 4c9935fb..c985e2ab 100644 --- a/apps/readest-app/src/app/reader/components/sidebar/BooknoteItem.tsx +++ b/apps/readest-app/src/app/reader/components/sidebar/BooknoteItem.tsx @@ -13,15 +13,14 @@ import useScrollToItem from '../../hooks/useScrollToItem'; interface BooknoteItemProps { bookKey: string; item: BookNote; - editable?: boolean; } -const BooknoteItem: React.FC = ({ bookKey, item, editable = false }) => { +const BooknoteItem: React.FC = ({ bookKey, item }) => { const _ = useTranslation(); const { envConfig } = useEnv(); const { settings } = useSettingsStore(); const { getConfig, saveConfig, updateBooknotes } = useBookDataStore(); - const { getProgress, getView } = useReaderStore(); + const { getProgress, getView, getViewsById } = useReaderStore(); const { setNotebookEditAnnotation, setNotebookVisible } = useNotebookStore(); const { text, cfi, note } = item; @@ -44,6 +43,8 @@ const BooknoteItem: React.FC = ({ bookKey, item, editable = f booknotes.forEach((item) => { if (item.id === note.id) { item.deletedAt = Date.now(); + const views = getViewsById(bookKey.split('-')[0]!); + views.forEach((view) => view?.addAnnotation(item, true)); } }); const updatedConfig = updateBooknotes(bookKey, booknotes); @@ -53,6 +54,7 @@ const BooknoteItem: React.FC = ({ bookKey, item, editable = f }; const editNote = (note: BookNote) => { + setNotebookVisible(true); setNotebookEditAnnotation(note); }; @@ -60,15 +62,15 @@ const BooknoteItem: React.FC = ({ bookKey, item, editable = f
  • = ({ bookKey, item, editable = f
    - {editable && ( -
    e.stopPropagation()} - > -
    - {item.note && ( - - )} +
    e.stopPropagation()} + > +
    + {item.note && ( -
    + )} +
    - )} +
  • ); }; diff --git a/apps/readest-app/src/app/reader/components/sidebar/BooknoteView.tsx b/apps/readest-app/src/app/reader/components/sidebar/BooknoteView.tsx index 7adb9cc1..2f0c2067 100644 --- a/apps/readest-app/src/app/reader/components/sidebar/BooknoteView.tsx +++ b/apps/readest-app/src/app/reader/components/sidebar/BooknoteView.tsx @@ -26,7 +26,7 @@ const BooknoteView: React.FC<{ const booknoteGroups: { [href: string]: BooknoteGroup } = {}; for (const booknote of booknotes) { - const tocItem = findTocItemBS(toc, booknote.cfi); + const tocItem = findTocItemBS(toc ?? [], booknote.cfi); const href = tocItem?.href || ''; const label = tocItem?.label || ''; const id = tocItem?.id || 0; diff --git a/apps/readest-app/src/app/reader/components/sidebar/Content.tsx b/apps/readest-app/src/app/reader/components/sidebar/Content.tsx index 59ef290c..ceca2d95 100644 --- a/apps/readest-app/src/app/reader/components/sidebar/Content.tsx +++ b/apps/readest-app/src/app/reader/components/sidebar/Content.tsx @@ -1,18 +1,20 @@ import clsx from 'clsx'; -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { BookDoc } from '@/libs/document'; +import { useBookDataStore } from '@/store/bookDataStore'; import TOCView from './TOCView'; import BooknoteView from './BooknoteView'; import TabNavigation from './TabNavigation'; const SidebarContent: React.FC<{ - activeTab: string; bookDoc: BookDoc; sideBarBookKey: string; - onTabChange: (tab: string) => void; -}> = ({ activeTab, bookDoc, sideBarBookKey, onTabChange }) => { +}> = ({ bookDoc, sideBarBookKey }) => { const scrollContainerRef = useRef(null); + const { getConfig, setConfig } = useBookDataStore(); + const config = getConfig(sideBarBookKey); + const [activeTab, setActiveTab] = useState(config?.viewSettings?.sideBarTab || 'toc'); useEffect(() => { const container = scrollContainerRef.current; @@ -39,6 +41,20 @@ const SidebarContent: React.FC<{ }; }, []); + useEffect(() => { + if (!sideBarBookKey) return; + const config = getConfig(sideBarBookKey!)!; + setActiveTab(config.viewSettings!.sideBarTab!); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [sideBarBookKey]); + + const handleTabChange = (tab: string) => { + setActiveTab(tab); + const config = getConfig(sideBarBookKey!)!; + config.viewSettings!.sideBarTab = tab; + setConfig(sideBarBookKey!, config); + }; + return ( <>
    )} {activeTab === 'annotations' && ( - + )} {activeTab === 'bookmarks' && ( - + )}
    - +
    ); diff --git a/apps/readest-app/src/app/reader/components/sidebar/SideBar.tsx b/apps/readest-app/src/app/reader/components/sidebar/SideBar.tsx index 2811c507..c4d60f57 100644 --- a/apps/readest-app/src/app/reader/components/sidebar/SideBar.tsx +++ b/apps/readest-app/src/app/reader/components/sidebar/SideBar.tsx @@ -1,7 +1,6 @@ import clsx from 'clsx'; import React, { useEffect, useState } from 'react'; -import { useEnv } from '@/context/EnvContext'; import { useSettingsStore } from '@/store/settingsStore'; import { useBookDataStore } from '@/store/bookDataStore'; import { useReaderStore } from '@/store/readerStore'; @@ -23,15 +22,13 @@ const MAX_SIDEBAR_WIDTH = 0.45; const SideBar: React.FC<{ onGoToLibrary: () => void; }> = ({ onGoToLibrary }) => { - const { envConfig } = useEnv(); - const { settings, saveSettings } = useSettingsStore(); + const { settings } = useSettingsStore(); const { sideBarBookKey } = useSidebarStore(); const { getBookData } = useBookDataStore(); const { getView } = useReaderStore(); const [isSearchBarVisible, setIsSearchBarVisible] = useState(false); const [searchResults, setSearchResults] = useState(null); const [searchTerm, setSearchTerm] = useState(''); - const [activeTab, setActiveTab] = useState(settings.globalReadSettings.sideBarTab); const { sideBarWidth, isSideBarPinned, @@ -70,12 +67,6 @@ const SideBar: React.FC<{ setSideBarVisible(false); }; - const handleTabChange = (tab: string) => { - setActiveTab(tab); - settings.globalReadSettings.sideBarTab = tab; - saveSettings(envConfig, settings); - }; - const handleToggleSearchBar = () => { setIsSearchBarVisible((prev) => !prev); if (isSearchBarVisible) { @@ -144,12 +135,7 @@ const SideBar: React.FC<{ onSelectResult={handleSearchResultClick} /> ) : ( - + )}
    { + getView(sideBarBookKey)?.history.back(); + }; + + const goForward = () => { + getView(sideBarBookKey)?.history.forward(); + }; + const reloadPage = () => { window.location.reload(); }; @@ -67,6 +75,8 @@ const useBookShortcuts = ({ sideBarBookKey, bookKeys }: UseBookShortcutsProps) = onGoRight: goRight, onGoPrev: goPrev, onGoNext: goNext, + onGoBack: goBack, + onGoForward: goForward, }, [sideBarBookKey, bookKeys], ); diff --git a/apps/readest-app/src/helpers/shortcuts.ts b/apps/readest-app/src/helpers/shortcuts.ts index 117e1faa..b389165a 100644 --- a/apps/readest-app/src/helpers/shortcuts.ts +++ b/apps/readest-app/src/helpers/shortcuts.ts @@ -10,6 +10,8 @@ export interface ShortcutConfig { onGoRight: string[]; onGoNext: string[]; onGoPrev: string[]; + onGoBack: string[]; + onGoForward: string[]; } const DEFAULT_SHORTCUTS: ShortcutConfig = { @@ -24,6 +26,8 @@ const DEFAULT_SHORTCUTS: ShortcutConfig = { onGoRight: ['ArrowRight', 'PageDown', 'l'], onGoNext: ['ArrowDown', 'j'], onGoPrev: ['ArrowUp', 'k'], + onGoBack: ['shift+ArrowLeft', 'shift+h'], + onGoForward: ['shift+ArrowRight', 'shift+l'], }; // Load shortcuts from localStorage or fallback to defaults diff --git a/apps/readest-app/src/libs/document.ts b/apps/readest-app/src/libs/document.ts index 58be5cd8..50eca147 100644 --- a/apps/readest-app/src/libs/document.ts +++ b/apps/readest-app/src/libs/document.ts @@ -57,8 +57,8 @@ export interface BookDoc { editor?: string; publisher?: string; }; - toc: Array; - sections: Array; + toc?: Array; + sections?: Array; splitTOCHref(href: string): Array; getCover(): Promise; } diff --git a/apps/readest-app/src/services/appService.ts b/apps/readest-app/src/services/appService.ts index dede7e71..7e3273ed 100644 --- a/apps/readest-app/src/services/appService.ts +++ b/apps/readest-app/src/services/appService.ts @@ -21,6 +21,7 @@ import { DEFAULT_BOOK_LAYOUT, DEFAULT_BOOK_STYLE, DEFAULT_BOOK_FONT, + DEFAULT_VIEW_CONFIG, DEFAULT_READSETTINGS, SYSTEM_SETTINGS_VERSION, DEFAULT_BOOK_SEARCH_CONFIG, @@ -65,6 +66,7 @@ export abstract class BaseAppService implements AppService { ...DEFAULT_BOOK_LAYOUT, ...DEFAULT_BOOK_STYLE, ...DEFAULT_BOOK_FONT, + ...DEFAULT_VIEW_CONFIG, ...settings.globalViewSettings, }; } catch { @@ -80,6 +82,7 @@ export abstract class BaseAppService implements AppService { ...DEFAULT_BOOK_LAYOUT, ...DEFAULT_BOOK_STYLE, ...DEFAULT_BOOK_FONT, + ...DEFAULT_VIEW_CONFIG, }, }; @@ -220,7 +223,7 @@ export abstract class BaseAppService implements AppService { } async fetchBookDetails(book: Book, settings: SystemSettings) { - const { file } = await this.loadBookContent(book, settings) as BookContent; + const { file } = (await this.loadBookContent(book, settings)) as BookContent; const bookDoc = (await new DocumentLoader(file).open()).book as BookDoc; return bookDoc.metadata; } diff --git a/apps/readest-app/src/services/constants.ts b/apps/readest-app/src/services/constants.ts index 17a9f496..8719dd1f 100644 --- a/apps/readest-app/src/services/constants.ts +++ b/apps/readest-app/src/services/constants.ts @@ -1,4 +1,4 @@ -import { BookFont, BookLayout, BookSearchConfig, BookStyle } from '@/types/book'; +import { BookFont, BookLayout, BookSearchConfig, BookStyle, ViewConfig } from '@/types/book'; import { ReadSettings } from '@/types/settings'; export const LOCAL_BOOKS_SUBDIR = 'Readest/Books'; @@ -10,7 +10,6 @@ export const FILE_ACCEPT_FORMATS = SUPPORTED_FILE_EXTS.map((ext) => `.${ext}`).j export const DEFAULT_READSETTINGS: ReadSettings = { sideBarWidth: '25%', isSideBarPinned: true, - sideBarTab: 'toc', notebookWidth: '25%', isNotebookPinned: false, autohideCursor: true, @@ -57,6 +56,10 @@ export const DEFAULT_BOOK_STYLE: BookStyle = { userStylesheet: '', }; +export const DEFAULT_VIEW_CONFIG: ViewConfig = { + sideBarTab: 'toc', +}; + export const DEFAULT_BOOK_SEARCH_CONFIG: BookSearchConfig = { scope: 'book', matchCase: false, diff --git a/apps/readest-app/src/store/readerStore.ts b/apps/readest-app/src/store/readerStore.ts index 0ecc3208..37b9c433 100644 --- a/apps/readest-app/src/store/readerStore.ts +++ b/apps/readest-app/src/store/readerStore.ts @@ -114,7 +114,7 @@ export const useReaderStore = create((set, get) => ({ console.log('Loading book', key); const { book: loadedBookDoc } = await new DocumentLoader(file).open(); const bookDoc = loadedBookDoc as BookDoc; - if (bookDoc.toc?.length > 0 && bookDoc.sections?.length > 0) { + if (bookDoc.toc?.length && bookDoc.sections?.length) { updateTocID(bookDoc.toc); const sections = bookDoc.sections.reduce((map: Record, section) => { map[section.id] = section; diff --git a/apps/readest-app/src/types/book.ts b/apps/readest-app/src/types/book.ts index d6ad182b..f2ad0c41 100644 --- a/apps/readest-app/src/types/book.ts +++ b/apps/readest-app/src/types/book.ts @@ -75,7 +75,11 @@ export interface BookFont { fontWeight: number; } -export interface ViewSettings extends BookLayout, BookStyle, BookFont {} +export interface ViewConfig { + sideBarTab: string; +} + +export interface ViewSettings extends BookLayout, BookStyle, BookFont, ViewConfig {} export interface BookProgress { location: string; diff --git a/apps/readest-app/src/types/settings.ts b/apps/readest-app/src/types/settings.ts index 91335dc2..b17d5d91 100644 --- a/apps/readest-app/src/types/settings.ts +++ b/apps/readest-app/src/types/settings.ts @@ -5,7 +5,6 @@ export type ThemeType = 'light' | 'dark' | 'auto'; export interface ReadSettings { sideBarWidth: string; isSideBarPinned: boolean; - sideBarTab: string; notebookWidth: string; isNotebookPinned: boolean; autohideCursor: boolean; diff --git a/apps/readest-app/src/utils/book.ts b/apps/readest-app/src/utils/book.ts index a445713c..4798b6e3 100644 --- a/apps/readest-app/src/utils/book.ts +++ b/apps/readest-app/src/utils/book.ts @@ -1,5 +1,5 @@ import { EXTS } from '@/libs/document'; -import { Book, BookConfig } from '@/types/book'; +import { Book, BookConfig, BookProgress } from '@/types/book'; import { getUserLang, makeSafeFilename } from './misc'; export const getDir = (book: Book) => { @@ -98,3 +98,15 @@ export const formatSubject = (subject: string | string[] | undefined) => { if (!subject) return ''; return Array.isArray(subject) ? subject.join(', ') : subject; }; + +export const getCurrentPage = (book: Book, progress: BookProgress) => { + const bookFormat = book.format; + const { section, pageinfo } = progress; + return bookFormat === 'PDF' + ? section + ? section.current + 1 + : 0 + : pageinfo + ? pageinfo.current + 1 + : 0; +};