forked from akai/readest
Refactor progress following for TOC and Booknote items
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import * as CFI from 'foliate-js/epubcfi.js';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
@@ -17,6 +17,7 @@ const BooknoteItem: React.FC<BooknoteItemProps> = ({ bookKey, item, editable = f
|
||||
const { settings, getConfig, saveConfig, getProgress, getView } = useReaderStore();
|
||||
const { updateBooknotes, setNotebookEditAnnotation, setNotebookVisible } = useReaderStore();
|
||||
const [isCurrent, setIsCurrent] = useState(false);
|
||||
const viewRef = useRef<HTMLLIElement | null>(null);
|
||||
|
||||
const { text, cfi, note } = item;
|
||||
const progress = getProgress(bookKey);
|
||||
@@ -26,7 +27,17 @@ const BooknoteItem: React.FC<BooknoteItemProps> = ({ bookKey, item, editable = f
|
||||
const { location } = progress;
|
||||
const start = CFI.collapse(location);
|
||||
const end = CFI.collapse(location, true);
|
||||
setIsCurrent(CFI.compare(start, cfi) * CFI.compare(end, cfi) <= 0);
|
||||
const isCurrent = CFI.compare(cfi, start) >= 0 && CFI.compare(cfi, end) <= 0;
|
||||
setIsCurrent(isCurrent);
|
||||
|
||||
if (isCurrent && viewRef.current) {
|
||||
const rect = viewRef.current.getBoundingClientRect();
|
||||
const isVisible = rect.top >= 0 && rect.bottom <= window.innerHeight;
|
||||
if (!isVisible) {
|
||||
(viewRef.current as HTMLElement).scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
(viewRef.current as HTMLElement).setAttribute('aria-current', 'page');
|
||||
}
|
||||
}, [progress]);
|
||||
|
||||
const handleClickItem = (event: React.MouseEvent) => {
|
||||
@@ -55,6 +66,7 @@ const BooknoteItem: React.FC<BooknoteItemProps> = ({ bookKey, item, editable = f
|
||||
|
||||
return (
|
||||
<li
|
||||
ref={viewRef}
|
||||
className={clsx(
|
||||
'border-base-300 my-2 cursor-pointer rounded-lg p-2 text-sm',
|
||||
editable && 'collapse-arrow collapse',
|
||||
|
||||
@@ -7,18 +7,15 @@ import BooknoteView from './BooknoteView';
|
||||
const SidebarContent: React.FC<{
|
||||
activeTab: string;
|
||||
bookDoc: BookDoc;
|
||||
currentHref: string | null;
|
||||
sideBarBookKey: string;
|
||||
}> = ({ activeTab, bookDoc, currentHref, sideBarBookKey }) => (
|
||||
}> = ({ activeTab, bookDoc, sideBarBookKey }) => (
|
||||
<div className='sidebar-content overflow-y-auto font-sans text-sm font-light shadow-inner'>
|
||||
{activeTab === 'toc' && bookDoc.toc && (
|
||||
<TOCView toc={bookDoc.toc} bookKey={sideBarBookKey} currentHref={currentHref} />
|
||||
)}
|
||||
{activeTab === 'toc' && bookDoc.toc && <TOCView toc={bookDoc.toc} bookKey={sideBarBookKey} />}
|
||||
{activeTab === 'annotations' && (
|
||||
<BooknoteView type={'annotation'} toc={bookDoc.toc} bookKey={sideBarBookKey} />
|
||||
<BooknoteView type='annotation' toc={bookDoc.toc} bookKey={sideBarBookKey} />
|
||||
)}
|
||||
{activeTab === 'bookmarks' && (
|
||||
<BooknoteView type={'bookmark'} toc={bookDoc.toc} bookKey={sideBarBookKey} />
|
||||
<BooknoteView type='bookmark' toc={bookDoc.toc} bookKey={sideBarBookKey} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
@@ -21,9 +21,8 @@ const SideBar: React.FC<{
|
||||
}> = ({ width, isPinned, onGoToLibrary, onOpenSplitView }) => {
|
||||
const { envConfig } = useEnv();
|
||||
const { sideBarBookKey, settings } = useReaderStore();
|
||||
const { saveSettings, getBookData, getProgress } = useReaderStore();
|
||||
const { saveSettings, getBookData } = useReaderStore();
|
||||
const [activeTab, setActiveTab] = useState(settings.globalReadSettings.sideBarTab);
|
||||
const [currentHref, setCurrentHref] = useState<string | null>(null);
|
||||
const {
|
||||
sideBarWidth,
|
||||
isSideBarVisible,
|
||||
@@ -39,12 +38,6 @@ const SideBar: React.FC<{
|
||||
};
|
||||
const { handleMouseDown } = useDragBar(handleDragMove);
|
||||
|
||||
useEffect(() => {
|
||||
if (!sideBarBookKey) return;
|
||||
const progress = getProgress(sideBarBookKey);
|
||||
setCurrentHref(progress?.tocHref || null);
|
||||
}, [sideBarBookKey]);
|
||||
|
||||
const handleClickOverlay = () => {
|
||||
setSideBarVisible(false);
|
||||
};
|
||||
@@ -86,12 +79,7 @@ const SideBar: React.FC<{
|
||||
<div className='border-b px-3'>
|
||||
<BookCard cover={book.coverImageUrl!} title={book.title} author={book.author} />
|
||||
</div>
|
||||
<SidebarContent
|
||||
activeTab={activeTab}
|
||||
bookDoc={bookDoc}
|
||||
currentHref={currentHref}
|
||||
sideBarBookKey={sideBarBookKey!}
|
||||
/>
|
||||
<SidebarContent activeTab={activeTab} bookDoc={bookDoc} sideBarBookKey={sideBarBookKey!} />
|
||||
<TabNavigation activeTab={activeTab} onTabChange={handleTabChange} />
|
||||
<div
|
||||
className='drag-bar absolute right-0 top-0 h-full w-0.5 cursor-col-resize'
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { md5 } from 'js-md5';
|
||||
import { TOCItem } from '@/libs/document';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { findParentPath } from '@/utils/toc';
|
||||
import { useFoliateEvents } from '../../hooks/useFoliateEvents';
|
||||
|
||||
const getHrefMd5 = (href: string) => md5(JSON.stringify(href));
|
||||
import { getContentMd5 } from '@/utils/misc';
|
||||
|
||||
const createExpanderIcon = (isExpanded: boolean) => {
|
||||
return (
|
||||
@@ -26,12 +23,11 @@ const TOCItemView: React.FC<{
|
||||
bookKey: string;
|
||||
item: TOCItem;
|
||||
depth: number;
|
||||
setCurrentHref: (href: string) => void;
|
||||
currentHref: string | null;
|
||||
expandedItems: string[];
|
||||
}> = ({ bookKey, item, depth, setCurrentHref, currentHref, expandedItems }) => {
|
||||
}> = ({ bookKey, item, depth, expandedItems }) => {
|
||||
const [isExpanded, setIsExpanded] = useState(expandedItems.includes(item.href || ''));
|
||||
const { getView } = useReaderStore();
|
||||
const { getView, getProgress } = useReaderStore();
|
||||
const progress = getProgress(bookKey);
|
||||
|
||||
const handleToggleExpand = (event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
@@ -43,11 +39,10 @@ const TOCItemView: React.FC<{
|
||||
event.preventDefault();
|
||||
if (item.href) {
|
||||
getView(bookKey)?.goTo(item.href);
|
||||
setCurrentHref(item.href);
|
||||
}
|
||||
};
|
||||
|
||||
const isActive = currentHref === item.href;
|
||||
const isActive = progress ? progress.tocHref === item.href : false;
|
||||
|
||||
useEffect(() => {
|
||||
setIsExpanded(expandedItems.includes(item.href || ''));
|
||||
@@ -62,7 +57,7 @@ const TOCItemView: React.FC<{
|
||||
style={{ paddingInlineStart: `${(depth + 1) * 12}px` }}
|
||||
aria-expanded={isExpanded ? 'true' : 'false'}
|
||||
aria-selected={isActive ? 'true' : 'false'}
|
||||
data-href={item.href ? getHrefMd5(item.href) : undefined}
|
||||
data-href={item.href ? getContentMd5(item.href) : undefined}
|
||||
className={`flex w-full cursor-pointer items-center rounded-md py-2 ${
|
||||
isActive ? 'bg-gray-300 hover:bg-gray-400' : 'hover:bg-gray-300'
|
||||
}`}
|
||||
@@ -91,8 +86,6 @@ const TOCItemView: React.FC<{
|
||||
key={`${index}-${subitem.href}`}
|
||||
item={subitem}
|
||||
depth={depth + 1}
|
||||
setCurrentHref={setCurrentHref}
|
||||
currentHref={currentHref}
|
||||
expandedItems={expandedItems}
|
||||
/>
|
||||
))}
|
||||
@@ -105,26 +98,12 @@ const TOCItemView: React.FC<{
|
||||
const TOCView: React.FC<{
|
||||
bookKey: string;
|
||||
toc: TOCItem[];
|
||||
currentHref: string | null;
|
||||
}> = ({ bookKey, toc, currentHref: href }) => {
|
||||
const [currentHref, setCurrentHref] = useState<string | null>(href);
|
||||
}> = ({ bookKey, toc }) => {
|
||||
const { sideBarBookKey, getProgress } = useReaderStore();
|
||||
const progress = getProgress(bookKey);
|
||||
|
||||
const [expandedItems, setExpandedItems] = useState<string[]>([]);
|
||||
const { getView } = useReaderStore();
|
||||
const tocRef = useRef<HTMLUListElement | null>(null);
|
||||
|
||||
const tocRelocateHandler = (event: Event) => {
|
||||
const detail = (event as CustomEvent).detail;
|
||||
const { tocItem } = detail;
|
||||
if (tocItem?.href) {
|
||||
setCurrentHref(tocItem.href);
|
||||
}
|
||||
};
|
||||
|
||||
useFoliateEvents(getView(bookKey), { onRelocate: tocRelocateHandler });
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentHref(href);
|
||||
}, [href]);
|
||||
const viewRef = useRef<HTMLUListElement | null>(null);
|
||||
|
||||
const expandParents = (toc: TOCItem[], href: string) => {
|
||||
const parentPath = findParentPath(toc, href).map((item) => item.href);
|
||||
@@ -132,8 +111,10 @@ const TOCView: React.FC<{
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const hrefMd5 = currentHref ? getHrefMd5(currentHref) : '';
|
||||
const currentItem = tocRef.current?.querySelector(`[data-href="${hrefMd5}"]`);
|
||||
if (!progress) return;
|
||||
const { tocHref: currentHref } = progress;
|
||||
const hrefMd5 = currentHref ? getContentMd5(currentHref) : '';
|
||||
const currentItem = viewRef.current?.querySelector(`[data-href="${hrefMd5}"]`);
|
||||
if (currentItem) {
|
||||
const rect = currentItem.getBoundingClientRect();
|
||||
const isVisible = rect.top >= 0 && rect.bottom <= window.innerHeight;
|
||||
@@ -145,12 +126,12 @@ const TOCView: React.FC<{
|
||||
if (currentHref) {
|
||||
expandParents(toc, currentHref);
|
||||
}
|
||||
}, [toc, currentHref]);
|
||||
}, [toc, progress, sideBarBookKey]);
|
||||
|
||||
return (
|
||||
<div className='relative'>
|
||||
<div className='max-h-[calc(100vh-173px)] overflow-y-auto rounded pt-2'>
|
||||
<ul role='tree' ref={tocRef} className='overflow-y-auto px-2'>
|
||||
<ul role='tree' ref={viewRef} className='overflow-y-auto px-2'>
|
||||
{toc &&
|
||||
toc.map((item, index) => (
|
||||
<TOCItemView
|
||||
@@ -158,8 +139,6 @@ const TOCView: React.FC<{
|
||||
key={`${index}-${item.href}`}
|
||||
item={item}
|
||||
depth={0}
|
||||
setCurrentHref={setCurrentHref}
|
||||
currentHref={currentHref}
|
||||
expandedItems={expandedItems}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
import { md5 } from 'js-md5';
|
||||
|
||||
export const uniqueId = () => Math.random().toString(36).substring(2, 9);
|
||||
|
||||
export const getContentMd5 = (content: unknown) => md5(JSON.stringify(content));
|
||||
|
||||
Reference in New Issue
Block a user