forked from akai/readest
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 519e222883 | |||
| 488a242787 | |||
| 523a03d212 | |||
| d49f3442e8 | |||
| 48dc570809 | |||
| 509320813f | |||
| 54d7e73acb |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@readest/readest-app",
|
||||
"version": "0.9.80",
|
||||
"version": "0.9.81",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "dotenv -e .env.tauri -- next dev",
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
"Apply to This Book": "Застосувати лише до цієї книги",
|
||||
"Unable to fetch the translation. Try again later.": "Не вдалося отримати переклад. Спробуйте пізніше.",
|
||||
"Check Update": "Перевірити оновлення",
|
||||
"Already the latest version": "У вас вже встановлена остання версія",
|
||||
"Already the latest version": "У вас вже встановлена найновіша версія",
|
||||
"Book Details": "Деталі книги",
|
||||
"From Local File": "З локального файлу",
|
||||
"TOC": "Зміст",
|
||||
@@ -499,7 +499,7 @@
|
||||
"Sync Conflict": "Конфлікт синхронізації",
|
||||
"Sync reading progress from \"{{deviceName}}\"?": "Синхронізувати прогрес читання із \"{{deviceName}}\"?",
|
||||
"another device": "інший пристрій",
|
||||
"Local Progress": "Локальний прогрес",
|
||||
"Local Progress": "Локальний проґрес",
|
||||
"Remote Progress": "Проґрес у хмарі",
|
||||
"Page {{page}} of {{total}} ({{percentage}}%)": "Сторінка {{page}} з {{total}} ({{percentage}}%)",
|
||||
"Current position": "Поточна позиція",
|
||||
@@ -547,8 +547,8 @@
|
||||
"Search Options": "Параметри пошуку",
|
||||
"Close": "Закрити",
|
||||
"Delete Book Options": "Параметри видалення книги",
|
||||
"ON": "УВІМКНЕННЯ",
|
||||
"OFF": "ВИМКНЕННЯ",
|
||||
"ON": "УВІМКНЕННО",
|
||||
"OFF": "ВИМКНЕНО",
|
||||
"Reading Progress": "Проґрес читання",
|
||||
"Page Margin": "Поля сторінки",
|
||||
"Remove Bookmark": "Видалити закладку",
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
{
|
||||
"releases": {
|
||||
"0.9.81": {
|
||||
"date": "2025-09-19",
|
||||
"notes": [
|
||||
"TTS: Added desktop common media control support when TTS is playing",
|
||||
"PDF: Disabled swipe-up gesture for toggling the action bar when zoomed in",
|
||||
"Fonts: Fixed an issue where embedded fonts in ebooks were occasionally not applied",
|
||||
"Reader: Fixed an issue where newly created highlights sometimes would not appear",
|
||||
"Reader: Enabled scrolling in the menu when the menu is too long"
|
||||
]
|
||||
},
|
||||
"0.9.80": {
|
||||
"date": "2025-09-19",
|
||||
"notes": [
|
||||
|
||||
@@ -183,7 +183,6 @@ const TOCView: React.FC<{
|
||||
(activeItem as HTMLElement).setAttribute('aria-current', 'page');
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [flatItems, activeHref]);
|
||||
|
||||
const virtualItemSize = useMemo(() => {
|
||||
|
||||
@@ -121,7 +121,7 @@ const TTSControl: React.FC<TTSControlProps> = ({ bookKey, gridInsets }) => {
|
||||
const progress = getProgress(bookKey);
|
||||
const { sectionLabel } = progress || {};
|
||||
const mark = (e as CustomEvent<TTSMark>).detail;
|
||||
if (appService?.isMobileApp && 'mediaSession' in navigator) {
|
||||
if ('mediaSession' in navigator) {
|
||||
navigator.mediaSession.metadata = new MediaMetadata({
|
||||
title: mark?.text || '',
|
||||
artist: sectionLabel || title,
|
||||
@@ -439,10 +439,18 @@ const TTSControl: React.FC<TTSControlProps> = ({ bookKey, gridInsets }) => {
|
||||
});
|
||||
|
||||
navigator.mediaSession.setActionHandler('seekforward', () => {
|
||||
handleForward();
|
||||
handleForward(true);
|
||||
});
|
||||
|
||||
navigator.mediaSession.setActionHandler('seekbackward', () => {
|
||||
handleBackward(true);
|
||||
});
|
||||
|
||||
navigator.mediaSession.setActionHandler('nexttrack', () => {
|
||||
handleForward();
|
||||
});
|
||||
|
||||
navigator.mediaSession.setActionHandler('previoustrack', () => {
|
||||
handleBackward();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { debounce } from '@/utils/debounce';
|
||||
import { ScrollSource } from './usePagination';
|
||||
|
||||
@@ -61,6 +62,7 @@ export const useTouchEvent = (
|
||||
handlePageFlip: (msg: CustomEvent) => void,
|
||||
handleContinuousScroll: (source: ScrollSource, delta: number, threshold: number) => void,
|
||||
) => {
|
||||
const { getBookData } = useBookDataStore();
|
||||
const { hoveredBookKey, setHoveredBookKey, getViewSettings } = useReaderStore();
|
||||
|
||||
let touchStart: IframeTouch | null = null;
|
||||
@@ -104,6 +106,7 @@ export const useTouchEvent = (
|
||||
const windowWidth = window.innerWidth;
|
||||
if (touchEnd) {
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const bookData = getBookData(bookKey)!;
|
||||
const deltaY = touchEnd.screenY - touchStart.screenY;
|
||||
const deltaX = touchEnd.screenX - touchStart.screenX;
|
||||
// also check for deltaX to prevent swipe page turn from triggering the toggle
|
||||
@@ -113,7 +116,11 @@ export const useTouchEvent = (
|
||||
Math.abs(deltaX) < windowWidth * 0.3
|
||||
) {
|
||||
// swipe up to toggle the header bar and the footer bar, only for horizontal page mode
|
||||
if (!viewSettings!.scrolled && !viewSettings!.vertical) {
|
||||
if (
|
||||
!viewSettings!.scrolled && // not scrolled
|
||||
!viewSettings!.vertical && // not vertical
|
||||
(!bookData.isFixedLayout || viewSettings.zoomLevel <= 100) // for fixed layout, not when zoomed in
|
||||
) {
|
||||
setHoveredBookKey(hoveredBookKey ? null : bookKey);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -99,7 +99,7 @@ export const useProgressSync = (bookKey: string) => {
|
||||
if (!progress?.location || !user) return;
|
||||
handleAutoSync();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [progress]);
|
||||
}, [progress?.location]);
|
||||
|
||||
// Pull: pull progress once when the book is opened
|
||||
useEffect(() => {
|
||||
|
||||
@@ -22,7 +22,11 @@ const Menu: React.FC<MenuProps> = ({ children, className }) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={menuRef} role='none' className={clsx(className)}>
|
||||
<div
|
||||
ref={menuRef}
|
||||
role='none'
|
||||
className={clsx('max-h-[calc(100vh-96px)] overflow-y-auto', className)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -65,7 +65,8 @@ export function useSync(bookKey?: string) {
|
||||
const { syncClient } = useSyncContext();
|
||||
|
||||
useEffect(() => {
|
||||
setIsSyncing(bookKey || '', syncing);
|
||||
if (!bookKey) return;
|
||||
setIsSyncing(bookKey, syncing);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [bookKey, syncing]);
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
getViewsById: (id: string) => {
|
||||
const { viewStates } = get();
|
||||
return Object.values(viewStates)
|
||||
.filter((state) => state.key.startsWith(id))
|
||||
.filter((state) => state.key && state.key.startsWith(id))
|
||||
.map((state) => state.view!);
|
||||
},
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ const getFontStyles = (
|
||||
...FALLBACK_FONTS,
|
||||
];
|
||||
const monospaceFonts = [monospace, ...MONOSPACE_FONTS.filter((font) => font !== monospace)];
|
||||
const defaultFontFamily = defaultFont.toLowerCase() === 'serif' ? '--serif' : '--sans-serif';
|
||||
const fontStyles = `
|
||||
html {
|
||||
--serif: ${serifFonts.map((font) => `"${font}"`).join(', ')}, serif;
|
||||
@@ -55,12 +56,19 @@ const getFontStyles = (
|
||||
--monospace: ${monospaceFonts.map((font) => `"${font}"`).join(', ')}, monospace;
|
||||
}
|
||||
html, body {
|
||||
font-family: var(${defaultFont.toLowerCase() === 'serif' ? '--serif' : '--sans-serif'}) ${overrideFont ? '!important' : ''};
|
||||
font-size: ${fontSize}px !important;
|
||||
font-weight: ${fontWeight};
|
||||
-webkit-text-size-adjust: none;
|
||||
text-size-adjust: none;
|
||||
}
|
||||
/* lower specificity than ebook built-in font styles */
|
||||
html {
|
||||
font-family: var(${defaultFontFamily}) ${overrideFont ? '!important' : ''};
|
||||
}
|
||||
/* higher specificity than ebook built-in font styles */
|
||||
html body {
|
||||
${overrideFont ? `font-family: var(${defaultFontFamily}) !important;` : ''}
|
||||
}
|
||||
font[size="1"] {
|
||||
font-size: ${minFontSize}px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user