From bd549d2a0fa9e0e1250c151029f012878f8ecde2 Mon Sep 17 00:00:00 2001 From: chrox Date: Tue, 17 Dec 2024 22:33:18 +0100 Subject: [PATCH] Get rid of unnecessary data fields in BookConfig --- .../app/reader/components/BookmarkToggler.tsx | 3 +- .../reader/components/annotator/Annotator.tsx | 4 -- .../reader/components/notebook/Notebook.tsx | 1 - .../components/sidebar/BooknoteView.tsx | 19 ++++----- apps/readest-app/src/libs/document.ts | 11 ++++++ apps/readest-app/src/store/readerStore.ts | 11 ++++-- apps/readest-app/src/types/book.ts | 4 -- apps/readest-app/src/utils/toc.ts | 39 ++++++++++++++++++- packages/foliate-js | 2 +- 9 files changed, 67 insertions(+), 27 deletions(-) diff --git a/apps/readest-app/src/app/reader/components/BookmarkToggler.tsx b/apps/readest-app/src/app/reader/components/BookmarkToggler.tsx index 6ea04118..670c4c00 100644 --- a/apps/readest-app/src/app/reader/components/BookmarkToggler.tsx +++ b/apps/readest-app/src/app/reader/components/BookmarkToggler.tsx @@ -26,7 +26,7 @@ const BookmarkToggler: React.FC = ({ bookKey }) => { const toggleBookmark = () => { const { booknotes: bookmarks = [] } = config; - const { location: cfi, sectionHref: href, range } = progress; + const { location: cfi, range } = progress; if (!cfi) return; if (!isBookmarked) { setIsBookmarked(true); @@ -36,7 +36,6 @@ const BookmarkToggler: React.FC = ({ bookKey }) => { id: uniqueId(), type: 'bookmark', cfi, - href, text: truncatedText, note: '', created: Date.now(), diff --git a/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx b/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx index 2b4033e2..9197f52a 100644 --- a/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx +++ b/apps/readest-app/src/app/reader/components/annotator/Annotator.tsx @@ -217,14 +217,12 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => { const { booknotes: annotations = [] } = config; if (selection) navigator.clipboard.writeText(selection.text); - const { sectionHref: href } = progress; const cfi = view?.getCFI(selection.index, selection.range); if (!cfi) return; const annotation: BookNote = { id: uniqueId(), type: 'excerpt', cfi, - href, text: selection.text, note: '', created: Date.now(), @@ -250,7 +248,6 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => { if (!selection || !selection.text) return; setHighlightOptionsVisible(true); const { booknotes: annotations = [] } = config; - const { sectionHref: href } = progress; const cfi = view?.getCFI(selection.index, selection.range); if (!cfi) return; const style = settings.globalReadSettings.highlightStyle; @@ -259,7 +256,6 @@ const Annotator: React.FC<{ bookKey: string }> = ({ bookKey }) => { id: uniqueId(), type: 'annotation', cfi, - href, style, color, text: selection.text, 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 83ab369d..ad21be8a 100644 --- a/apps/readest-app/src/app/reader/components/notebook/Notebook.tsx +++ b/apps/readest-app/src/app/reader/components/notebook/Notebook.tsx @@ -72,7 +72,6 @@ const Notebook: React.FC = ({}) => { type: 'annotation', cfi, note, - href: selection.href || '', text: selection.text, created: Date.now(), }; 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 da70c623..d840159f 100644 --- a/apps/readest-app/src/app/reader/components/sidebar/BooknoteView.tsx +++ b/apps/readest-app/src/app/reader/components/sidebar/BooknoteView.tsx @@ -2,7 +2,7 @@ import React from 'react'; import * as CFI from 'foliate-js/epubcfi.js'; import { useBookDataStore } from '@/store/bookDataStore'; -import { findParentPath } from '@/utils/toc'; +import { findTocItemBS } from '@/utils/toc'; import { TOCItem } from '@/libs/document'; import { BookNote, BookNoteType } from '@/types/book'; import BooknoteItem from './BooknoteItem'; @@ -26,17 +26,14 @@ const BooknoteView: React.FC<{ const booknoteGroups: { [href: string]: BooknoteGroup } = {}; for (const booknote of booknotes) { - const parentPath = findParentPath(toc, booknote.href); - if (parentPath.length > 0) { - const href = parentPath[0]!.href || ''; - const label = parentPath[0]!.label || ''; - const id = toc.findIndex((item) => item.href === href) || Infinity; - booknote.href = href; - if (!booknoteGroups[href]) { - booknoteGroups[href] = { id, href, label, booknotes: [] }; - } - booknoteGroups[href].booknotes.push(booknote); + const tocItem = findTocItemBS(toc, booknote.cfi); + const href = tocItem?.href || ''; + const label = tocItem?.label || ''; + const id = tocItem?.id || 0; + if (!booknoteGroups[href]) { + booknoteGroups[href] = { id, href, label, booknotes: [] }; } + booknoteGroups[href].booknotes.push(booknote); } Object.values(booknoteGroups).forEach((group) => { diff --git a/apps/readest-app/src/libs/document.ts b/apps/readest-app/src/libs/document.ts index 9889aa96..5515d678 100644 --- a/apps/readest-app/src/libs/document.ts +++ b/apps/readest-app/src/libs/document.ts @@ -1,4 +1,5 @@ import { BookFormat } from '@/types/book'; +import * as epubcfi from 'foliate-js/epubcfi.js'; // A groupBy polyfill for foliate-js Object.groupBy ??= (iterable, callbackfn) => { @@ -30,15 +31,24 @@ Map.groupBy ??= (iterable, callbackfn) => { return map; }; +export const CFI = epubcfi; + export type DocumentFile = File; export interface TOCItem { id: number; label: string; href: string; + cfi?: string; subitems?: TOCItem[]; } +export interface SectionItem { + id: string; + cfi: string; + size: number; +} + export interface BookDoc { metadata: { title: string; @@ -48,6 +58,7 @@ export interface BookDoc { publisher?: string; }; toc: Array; + sections: Array; getCover(): Promise; } diff --git a/apps/readest-app/src/store/readerStore.ts b/apps/readest-app/src/store/readerStore.ts index 1a3ec024..314c88d6 100644 --- a/apps/readest-app/src/store/readerStore.ts +++ b/apps/readest-app/src/store/readerStore.ts @@ -3,8 +3,8 @@ import { create } from 'zustand'; import { BookContent, BookConfig, PageInfo, BookProgress, ViewSettings } from '@/types/book'; import { EnvConfigType } from '@/services/environment'; import { FoliateView } from '@/app/reader/components/FoliateViewer'; -import { BookDoc, DocumentLoader, TOCItem } from '@/libs/document'; -import { updateTocID } from '@/utils/toc'; +import { BookDoc, DocumentLoader, SectionItem, TOCItem } from '@/libs/document'; +import { updateTocCFI, updateTocID } from '@/utils/toc'; import { useSettingsStore } from './settingsStore'; import { useBookDataStore } from './bookDataStore'; import { useLibraryStore } from './libraryStore'; @@ -114,8 +114,13 @@ 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) { + if (bookDoc.toc && bookDoc.sections) { updateTocID(bookDoc.toc); + const sections = bookDoc.sections.reduce((map: Record, section) => { + map[section.id] = section; + return map; + }, {}); + updateTocCFI(bookDoc.toc, sections); } useBookDataStore.setState((state) => ({ booksData: { diff --git a/apps/readest-app/src/types/book.ts b/apps/readest-app/src/types/book.ts index 1cfe0514..f2ce94b5 100644 --- a/apps/readest-app/src/types/book.ts +++ b/apps/readest-app/src/types/book.ts @@ -27,7 +27,6 @@ export interface BookNote { id: string; type: BookNoteType; cfi: string; - href: string; text?: string; style?: HighlightStyle; color?: HighlightColor; @@ -111,10 +110,7 @@ export interface BookConfig { lastUpdated: number; progress?: [number, number]; location?: string; - booknotes?: BookNote[]; - removedNotesTimestamps?: Record; - searchConfig?: BookSearchConfig; viewSettings?: Partial; } diff --git a/apps/readest-app/src/utils/toc.ts b/apps/readest-app/src/utils/toc.ts index 8979eadc..e12b9229 100644 --- a/apps/readest-app/src/utils/toc.ts +++ b/apps/readest-app/src/utils/toc.ts @@ -1,4 +1,4 @@ -import { TOCItem } from '@/libs/document'; +import { SectionItem, TOCItem, CFI } from '@/libs/document'; export const findParentPath = (toc: TOCItem[], href: string): TOCItem[] => { for (const item of toc) { @@ -15,6 +15,28 @@ export const findParentPath = (toc: TOCItem[], href: string): TOCItem[] => { return []; }; +export const findTocItemBS = (toc: TOCItem[], cfi: string): TOCItem | null => { + let left = 0; + let right = toc.length - 1; + let result: TOCItem | null = null; + + while (left <= right) { + const mid = Math.floor((left + right) / 2); + const currentCfi = toc[mid]!.cfi || ''; + const comparison = CFI.compare(currentCfi, cfi); + if (comparison === 0) { + return toc[mid]!; + } else if (comparison < 0) { + result = toc[mid]!; + left = mid + 1; + } else { + right = mid - 1; + } + } + + return result; +}; + export const updateTocID = (items: TOCItem[], index = 0): number => { items.forEach((item) => { item.id ??= index++; @@ -24,3 +46,18 @@ export const updateTocID = (items: TOCItem[], index = 0): number => { }); return index; }; + +export const updateTocCFI = (items: TOCItem[], sections: { [id: string]: SectionItem }): void => { + items.forEach((item) => { + if (item.href) { + const id = item.href.split('#')[0]!; + const section = sections[id]; + if (section) { + item.cfi = section.cfi; + } + } + if (item.subitems) { + updateTocCFI(item.subitems, sections); + } + }); +}; diff --git a/packages/foliate-js b/packages/foliate-js index cfdc0206..bb8d44cc 160000 --- a/packages/foliate-js +++ b/packages/foliate-js @@ -1 +1 @@ -Subproject commit cfdc02069d218bbfe5e072ce3caa2e737acb040d +Subproject commit bb8d44cc040d351ebea47df9ffb2565310d7d7c6