forked from akai/readest
This commit is contained in:
@@ -203,6 +203,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
{showFooter && (
|
||||
<ProgressInfoView
|
||||
bookKey={bookKey}
|
||||
toc={bookDoc.toc || []}
|
||||
section={section}
|
||||
pageinfo={pageinfo}
|
||||
timeinfo={timeinfo}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { Insets } from '@/types/misc';
|
||||
import { PageInfo, TimeInfo } from '@/types/book';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
@@ -8,9 +8,12 @@ import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { formatNumber, formatProgress } from '@/utils/progress';
|
||||
import { saveViewSettings } from '@/helpers/settings';
|
||||
import { TOCItem } from '@/libs/document';
|
||||
import { SIZE_PER_LOC, SIZE_PER_TIME_UNIT } from '@/services/constants';
|
||||
|
||||
interface PageInfoProps {
|
||||
bookKey: string;
|
||||
toc: TOCItem[];
|
||||
section?: PageInfo;
|
||||
pageinfo?: PageInfo;
|
||||
timeinfo?: TimeInfo;
|
||||
@@ -21,9 +24,9 @@ interface PageInfoProps {
|
||||
|
||||
const ProgressInfoView: React.FC<PageInfoProps> = ({
|
||||
bookKey,
|
||||
toc,
|
||||
section,
|
||||
pageinfo,
|
||||
timeinfo,
|
||||
horizontalGap,
|
||||
contentInsets,
|
||||
gridInsets,
|
||||
@@ -31,10 +34,10 @@ const ProgressInfoView: React.FC<PageInfoProps> = ({
|
||||
const _ = useTranslation();
|
||||
const { envConfig, appService } = useEnv();
|
||||
const { getBookData } = useBookDataStore();
|
||||
const { getView, getViewSettings } = useReaderStore();
|
||||
const view = getView(bookKey);
|
||||
const { getProgress, getViewSettings } = useReaderStore();
|
||||
const bookData = getBookData(bookKey);
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const progress = getProgress(bookKey);
|
||||
|
||||
const showDoubleBorder = viewSettings.vertical && viewSettings.doubleBorder;
|
||||
const isScrolled = viewSettings.scrolled;
|
||||
@@ -51,23 +54,41 @@ const ProgressInfoView: React.FC<PageInfoProps> = ({
|
||||
|
||||
const lang = localStorage?.getItem('i18nextLng') || '';
|
||||
const localize = isVertical && lang.toLowerCase().startsWith('zh');
|
||||
const progress = bookData?.isFixedLayout ? section : pageinfo;
|
||||
const progressInfo = formatProgress(progress?.current, progress?.total, template, localize, lang);
|
||||
const pageInfo = bookData?.isFixedLayout ? section : pageinfo;
|
||||
const progressInfo = formatProgress(pageInfo?.current, pageInfo?.total, template, localize, lang);
|
||||
|
||||
const timeLeft = timeinfo
|
||||
? _('{{time}} min left in chapter', {
|
||||
time: formatNumber(Math.round(timeinfo.section), localize, lang),
|
||||
})
|
||||
: '';
|
||||
const { page = 0, pages = 0 } = view?.renderer || {};
|
||||
const activeHref = useMemo(() => progress?.sectionHref || null, [progress?.sectionHref]);
|
||||
const activeTOCItem = useMemo(() => {
|
||||
if (!activeHref) return null;
|
||||
for (const item of toc) {
|
||||
if (item.href === activeHref) return item;
|
||||
const subitem = item.subitems?.find((sub) => sub.href === activeHref);
|
||||
if (subitem) return subitem;
|
||||
}
|
||||
return null;
|
||||
}, [activeHref, toc]);
|
||||
|
||||
const current = pageInfo?.current || 0;
|
||||
const total = activeTOCItem?.location ? activeTOCItem.location.next : pageInfo?.total || 0;
|
||||
const pages = Math.max(total - current, 0);
|
||||
const timeLeft =
|
||||
total - 1 >= current
|
||||
? _('{{time}} min left in chapter', {
|
||||
time: formatNumber(
|
||||
Math.round((pages * SIZE_PER_LOC) / SIZE_PER_TIME_UNIT),
|
||||
localize,
|
||||
lang,
|
||||
),
|
||||
})
|
||||
: '';
|
||||
const pageLeft =
|
||||
pages - 1 > page
|
||||
total - 1 >= current
|
||||
? localize
|
||||
? _('{{number}} pages left in chapter', {
|
||||
number: formatNumber(pages - page - 1, localize, lang),
|
||||
number: formatNumber(pages, localize, lang),
|
||||
})
|
||||
: _('{{count}} pages left in chapter', {
|
||||
count: pages - page - 1,
|
||||
count: pages,
|
||||
})
|
||||
: '';
|
||||
|
||||
@@ -131,8 +152,8 @@ const ProgressInfoView: React.FC<PageInfoProps> = ({
|
||||
aria-label={[
|
||||
progress
|
||||
? _('On {{current}} of {{total}} page', {
|
||||
current: progress.current + 1,
|
||||
total: progress.total,
|
||||
current: current + 1,
|
||||
total: total,
|
||||
})
|
||||
: '',
|
||||
timeLeft,
|
||||
|
||||
@@ -26,8 +26,10 @@ export interface SectionItem {
|
||||
cfi: string;
|
||||
size: number;
|
||||
linear: string;
|
||||
href?: string;
|
||||
location?: Location;
|
||||
pageSpread?: 'left' | 'right' | 'center' | '';
|
||||
subitems?: Array<SectionItem>;
|
||||
|
||||
createDocument: () => Promise<Document>;
|
||||
}
|
||||
|
||||
@@ -707,6 +707,9 @@ export const DOUBLE_CLICK_INTERVAL_THRESHOLD_MS = 250;
|
||||
export const DISABLE_DOUBLE_CLICK_ON_MOBILE = true;
|
||||
export const LONG_HOLD_THRESHOLD = 500;
|
||||
|
||||
export const SIZE_PER_LOC = 1500;
|
||||
export const SIZE_PER_TIME_UNIT = 1600;
|
||||
|
||||
export const CUSTOM_THEME_TEMPLATES = [
|
||||
{
|
||||
light: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ConvertChineseVariant } from '@/types/book';
|
||||
import { SectionItem, TOCItem, CFI, BookDoc } from '@/libs/document';
|
||||
import { initSimpleCC, runSimpleCC } from '@/utils/simplecc';
|
||||
import { SIZE_PER_LOC } from '@/services/constants';
|
||||
|
||||
export const findParentPath = (toc: TOCItem[], href: string): TOCItem[] => {
|
||||
for (const item of toc) {
|
||||
@@ -69,17 +70,38 @@ export const updateToc = async (
|
||||
return acc;
|
||||
}, []);
|
||||
const totalSize = cumulativeSizes[cumulativeSizes.length - 1] || 0;
|
||||
const sizePerLoc = 1500;
|
||||
const totalLocations = Math.floor(totalSize / SIZE_PER_LOC);
|
||||
sections.forEach((section, index) => {
|
||||
section.location = {
|
||||
current: Math.floor(cumulativeSizes[index]! / sizePerLoc),
|
||||
next: Math.floor((cumulativeSizes[index]! + sizes[index]!) / sizePerLoc),
|
||||
total: Math.floor(totalSize / sizePerLoc),
|
||||
current: Math.floor(cumulativeSizes[index]! / SIZE_PER_LOC),
|
||||
next: Math.floor((cumulativeSizes[index]! + sizes[index]!) / SIZE_PER_LOC),
|
||||
total: totalLocations,
|
||||
};
|
||||
const subitems = section.subitems || [];
|
||||
subitems.forEach((subitem, subitemIndex) => {
|
||||
subitem.location = {
|
||||
current: Math.floor(
|
||||
(cumulativeSizes[index]! +
|
||||
subitems.slice(0, subitemIndex).reduce((sum, t) => sum + (t.size || 0), 0)) /
|
||||
SIZE_PER_LOC,
|
||||
),
|
||||
next: Math.floor(
|
||||
(cumulativeSizes[index]! +
|
||||
subitems.slice(0, subitemIndex + 1).reduce((sum, t) => sum + (t.size || 0), 0)) /
|
||||
SIZE_PER_LOC,
|
||||
),
|
||||
total: totalLocations,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const sectionsMap = sections.reduce((map: Record<string, SectionItem>, section) => {
|
||||
map[section.id] = section;
|
||||
section.subitems?.forEach((subitem) => {
|
||||
if (subitem.href) {
|
||||
map[subitem.href] = subitem;
|
||||
}
|
||||
});
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
@@ -112,12 +134,10 @@ const updateTocData = (
|
||||
item.id ??= index++;
|
||||
if (item.href) {
|
||||
const id = bookDoc.splitTOCHref(item.href)[0]!;
|
||||
const section = sectionsMap[id];
|
||||
const section = sectionsMap[item.href] || sectionsMap[id];
|
||||
if (section) {
|
||||
item.cfi = section.cfi;
|
||||
// Add location only when toc item is at the same level as the section
|
||||
// otherwise the location will not be accurate
|
||||
if (id === item.href || items.length <= sections.length) {
|
||||
if (id === item.href || items.length <= sections.length || item.href === section.href) {
|
||||
item.location = section.location;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
Submodule packages/foliate-js updated: 29fd7aadf9...245d64e653
Reference in New Issue
Block a user