forked from akai/readest
settings: more reasonable constraints for top and bottom margins (#1428)
This commit is contained in:
@@ -29,7 +29,8 @@ interface BooksGridProps {
|
||||
const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
const { appService } = useEnv();
|
||||
const { getConfig, getBookData } = useBookDataStore();
|
||||
const { getProgress, getViewState, getViewSettings, hoveredBookKey } = useReaderStore();
|
||||
const { getProgress, getViewState, getViewSettings } = useReaderStore();
|
||||
const { getGridInsets, setGridInsets, hoveredBookKey } = useReaderStore();
|
||||
const { isSideBarVisible, sideBarBookKey } = useSidebarStore();
|
||||
const { isFontLayoutSettingsDialogOpen, setFontLayoutSettingsDialogOpen } = useSettingsStore();
|
||||
|
||||
@@ -45,6 +46,21 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [sideBarBookKey]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!screenInsets) return;
|
||||
bookKeys.forEach((bookKey, index) => {
|
||||
const { top, right, bottom, left } = getInsetEdges(index, bookKeys.length, aspectRatio);
|
||||
const gridInsets = {
|
||||
top: top ? screenInsets.top : 0,
|
||||
right: right ? screenInsets.right : 0,
|
||||
bottom: bottom ? screenInsets.bottom : 0,
|
||||
left: left ? screenInsets.left : 0,
|
||||
};
|
||||
setGridInsets(bookKey, gridInsets);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [bookKeys, screenInsets]);
|
||||
|
||||
if (!screenInsets) return null;
|
||||
|
||||
return (
|
||||
@@ -56,26 +72,18 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
}}
|
||||
>
|
||||
{bookKeys.map((bookKey, index) => {
|
||||
const { top, right, bottom, left } = getInsetEdges(index, bookKeys.length, aspectRatio);
|
||||
const bookData = getBookData(bookKey);
|
||||
const config = getConfig(bookKey);
|
||||
const progress = getProgress(bookKey);
|
||||
const viewSettings = getViewSettings(bookKey);
|
||||
const gridInsets = getGridInsets(bookKey);
|
||||
const { book, bookDoc } = bookData || {};
|
||||
if (!book || !config || !bookDoc || !viewSettings) return null;
|
||||
if (!book || !config || !bookDoc || !viewSettings || !gridInsets) return null;
|
||||
|
||||
const { section, pageinfo, timeinfo, sectionLabel } = progress || {};
|
||||
const isBookmarked = getViewState(bookKey)?.ribbonVisible;
|
||||
const horizontalGapPercent = viewSettings.gapPercent;
|
||||
const viewInsets = getViewInsets(viewSettings);
|
||||
// insets for the grid cell
|
||||
const gridInsets = {
|
||||
top: top ? screenInsets.top : 0,
|
||||
right: right ? screenInsets.right : 0,
|
||||
bottom: bottom ? screenInsets.bottom : 0,
|
||||
left: left ? screenInsets.left : 0,
|
||||
};
|
||||
// insets for the content inside the grid cell
|
||||
const contentInsets = {
|
||||
top: gridInsets.top + viewInsets.top,
|
||||
right: gridInsets.right + viewInsets.right,
|
||||
@@ -110,7 +118,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
|
||||
bookKey={bookKey}
|
||||
bookDoc={bookDoc}
|
||||
config={config}
|
||||
insets={contentInsets}
|
||||
contentInsets={contentInsets}
|
||||
/>
|
||||
{viewSettings.vertical && viewSettings.scrolled && (
|
||||
<>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
||||
import { BookDoc, getDirection } from '@/libs/document';
|
||||
import { BookConfig } from '@/types/book';
|
||||
import { FoliateView, wrappedFoliateView } from '@/types/view';
|
||||
import { Insets } from '@/types/misc';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useReaderStore } from '@/store/readerStore';
|
||||
@@ -38,8 +39,8 @@ const FoliateViewer: React.FC<{
|
||||
bookKey: string;
|
||||
bookDoc: BookDoc;
|
||||
config: BookConfig;
|
||||
insets: { top: number; right: number; bottom: number; left: number };
|
||||
}> = ({ bookKey, bookDoc, config, insets }) => {
|
||||
contentInsets: Insets;
|
||||
}> = ({ bookKey, bookDoc, config, contentInsets: insets }) => {
|
||||
const { getView, setView: setFoliateView, setProgress } = useReaderStore();
|
||||
const { getViewSettings, setViewSettings } = useReaderStore();
|
||||
const { getParallels } = useParallelViewStore();
|
||||
@@ -248,11 +249,16 @@ const FoliateViewer: React.FC<{
|
||||
const showDoubleBorder = viewSettings.vertical && viewSettings.doubleBorder;
|
||||
const showDoubleBorderHeader = showDoubleBorder && viewSettings.showHeader;
|
||||
const showDoubleBorderFooter = showDoubleBorder && viewSettings.showFooter;
|
||||
const showTopHeader = viewSettings.showHeader && !viewSettings.vertical;
|
||||
const showBottomFooter = viewSettings.showFooter && !viewSettings.vertical;
|
||||
|
||||
const moreTopInset = showTopHeader ? Math.max(0, 44 - insets.top) : 0;
|
||||
const moreBottomInset = showBottomFooter ? Math.max(0, 44 - insets.bottom) : 0;
|
||||
const moreRightInset = showDoubleBorderHeader ? 32 : 0;
|
||||
const moreLeftInset = showDoubleBorderFooter ? 32 : 0;
|
||||
viewRef.current?.renderer.setAttribute('margin-top', `${insets.top}px`);
|
||||
viewRef.current?.renderer.setAttribute('margin-top', `${insets.top + moreTopInset}px`);
|
||||
viewRef.current?.renderer.setAttribute('margin-right', `${insets.right + moreRightInset}px`);
|
||||
viewRef.current?.renderer.setAttribute('margin-bottom', `${insets.bottom}px`);
|
||||
viewRef.current?.renderer.setAttribute('margin-bottom', `${insets.bottom + moreBottomInset}px`);
|
||||
viewRef.current?.renderer.setAttribute('margin-left', `${insets.left + moreLeftInset}px`);
|
||||
viewRef.current?.renderer.setAttribute('gap', `${viewSettings.gapPercent}%`);
|
||||
if (viewSettings.scrolled) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Insets } from '@/types/misc';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
|
||||
interface SectionInfoProps {
|
||||
@@ -22,6 +24,13 @@ const HintInfo: React.FC<SectionInfoProps> = ({
|
||||
contentInsets,
|
||||
gridInsets,
|
||||
}) => {
|
||||
const { appService } = useEnv();
|
||||
const { systemUIVisible, statusBarHeight } = useThemeStore();
|
||||
const topInset = Math.max(
|
||||
gridInsets.top,
|
||||
appService?.isAndroidApp && systemUIVisible ? statusBarHeight / 2 : 0,
|
||||
);
|
||||
|
||||
const [hintMessage, setHintMessage] = React.useState<string | null>(null);
|
||||
const hintTimeout = useRef(2000);
|
||||
const dismissTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
@@ -57,7 +66,7 @@ const HintInfo: React.FC<SectionInfoProps> = ({
|
||||
hintMessage ? 'bg-base-100' : 'bg-transparent',
|
||||
)}
|
||||
style={{
|
||||
height: `${gridInsets.top}px`,
|
||||
height: `${topInset}px`,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
@@ -83,7 +92,7 @@ const HintInfo: React.FC<SectionInfoProps> = ({
|
||||
width: showDoubleBorder ? '30px' : `${horizontalGap}%`,
|
||||
}
|
||||
: {
|
||||
top: `${gridInsets.top}px`,
|
||||
top: `${topInset}px`,
|
||||
insetInlineEnd: `calc(${horizontalGap / 2}% + ${contentInsets.right}px)`,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { Insets } from '@/types/misc';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
|
||||
interface SectionInfoProps {
|
||||
section?: string;
|
||||
@@ -21,6 +23,13 @@ const SectionInfo: React.FC<SectionInfoProps> = ({
|
||||
contentInsets,
|
||||
gridInsets,
|
||||
}) => {
|
||||
const { appService } = useEnv();
|
||||
const { systemUIVisible, statusBarHeight } = useThemeStore();
|
||||
const topInset = Math.max(
|
||||
gridInsets.top,
|
||||
appService?.isAndroidApp && systemUIVisible ? statusBarHeight / 2 : 0,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@@ -29,7 +38,7 @@ const SectionInfo: React.FC<SectionInfoProps> = ({
|
||||
isScrolled && !isVertical && 'bg-base-100',
|
||||
)}
|
||||
style={{
|
||||
height: `${gridInsets.top}px`,
|
||||
height: `${topInset}px`,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
@@ -49,7 +58,7 @@ const SectionInfo: React.FC<SectionInfoProps> = ({
|
||||
height: `calc(100% - ${contentInsets.top + contentInsets.bottom}px)`,
|
||||
}
|
||||
: {
|
||||
top: `${gridInsets.top}px`,
|
||||
top: `${topInset}px`,
|
||||
insetInlineStart: `calc(${horizontalGap / 2}% + ${contentInsets.left}px)`,
|
||||
width: `calc(100% - ${contentInsets.left + contentInsets.right}px)`,
|
||||
}
|
||||
|
||||
@@ -18,11 +18,12 @@ import NumberInput from './NumberInput';
|
||||
const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
const _ = useTranslation();
|
||||
const { envConfig } = useEnv();
|
||||
const { getView, getViewSettings, setViewSettings } = useReaderStore();
|
||||
const { getView, getViewSettings, getGridInsets, setViewSettings } = useReaderStore();
|
||||
const { getBookData } = useBookDataStore();
|
||||
const view = getView(bookKey);
|
||||
const bookData = getBookData(bookKey)!;
|
||||
const viewSettings = getViewSettings(bookKey)!;
|
||||
const gridInsets = getGridInsets(bookKey)!;
|
||||
|
||||
const [paragraphMargin, setParagraphMargin] = useState(viewSettings.paragraphMargin!);
|
||||
const [lineHeight, setLineHeight] = useState(viewSettings.lineHeight!);
|
||||
@@ -273,7 +274,8 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
useEffect(() => {
|
||||
if (showHeader === viewSettings.showHeader) return;
|
||||
if (showHeader && !viewSettings.vertical) {
|
||||
viewSettings.marginTopPx = Math.max(viewSettings.marginTopPx, 44);
|
||||
const minMarginTop = Math.max(0, Math.round((44 - gridInsets.top) / 4) * 4);
|
||||
viewSettings.marginTopPx = Math.max(viewSettings.marginTopPx, minMarginTop);
|
||||
setMarginTopPx(viewSettings.marginTopPx);
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
}
|
||||
@@ -285,7 +287,8 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
useEffect(() => {
|
||||
if (showFooter === viewSettings.showFooter) return;
|
||||
if (showFooter && !viewSettings.vertical) {
|
||||
viewSettings.marginBottomPx = Math.max(viewSettings.marginBottomPx, 44);
|
||||
const minMarginBottom = Math.max(0, Math.round((44 - gridInsets.bottom) / 4) * 4);
|
||||
viewSettings.marginBottomPx = Math.max(viewSettings.marginBottomPx, minMarginBottom);
|
||||
setMarginBottomPx(viewSettings.marginBottomPx);
|
||||
setViewSettings(bookKey, viewSettings);
|
||||
}
|
||||
@@ -462,7 +465,11 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
label={_('Top Margin (px)')}
|
||||
value={showHeader && !isVertical ? marginTopPx : compactMarginTopPx}
|
||||
onChange={showHeader && !isVertical ? setMarginTopPx : setCompactMarginTopPx}
|
||||
min={0}
|
||||
min={
|
||||
showHeader && !isVertical
|
||||
? Math.max(0, Math.round((44 - gridInsets.top) / 4) * 4)
|
||||
: 0
|
||||
}
|
||||
max={88}
|
||||
step={4}
|
||||
/>
|
||||
@@ -470,7 +477,11 @@ const LayoutPanel: React.FC<{ bookKey: string }> = ({ bookKey }) => {
|
||||
label={_('Bottom Margin (px)')}
|
||||
value={showFooter && !isVertical ? marginBottomPx : compactMarginBottomPx}
|
||||
onChange={showFooter && !isVertical ? setMarginBottomPx : setCompactMarginBottomPx}
|
||||
min={0}
|
||||
min={
|
||||
showFooter && !isVertical
|
||||
? Math.max(0, Math.round((44 - gridInsets.bottom) / 4) * 4)
|
||||
: 0
|
||||
}
|
||||
max={88}
|
||||
step={4}
|
||||
/>
|
||||
|
||||
@@ -133,7 +133,9 @@ const BooknoteItem: React.FC<BooknoteItemProps> = ({ bookKey, item }) => {
|
||||
>
|
||||
<div className='flex cursor-default items-center justify-between'>
|
||||
<div className='flex items-center'>
|
||||
<span className='text-gray-500'>{dayjs(item.createdAt).fromNow()}</span>
|
||||
<span className='text-sm text-gray-500 sm:text-xs'>
|
||||
{dayjs(item.createdAt).fromNow()}
|
||||
</span>
|
||||
</div>
|
||||
<div className='flex items-center justify-end space-x-3 p-2' dir='ltr'>
|
||||
{item.note && (
|
||||
|
||||
@@ -192,7 +192,7 @@ const Dialog: React.FC<DialogProps> = ({
|
||||
>
|
||||
<div className='bg-base-content/50 h-1 w-10 rounded-full'></div>
|
||||
</div>
|
||||
<div className='dialog-header bg-base-100 sticky top-1 z-10 flex items-center justify-between px-4'>
|
||||
<div className='dialog-header bg-base-100 sticky top-1 z-10 flex items-center justify-between px-2 sm:px-4'>
|
||||
{header ? (
|
||||
header
|
||||
) : (
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
ViewSettings,
|
||||
TimeInfo,
|
||||
} from '@/types/book';
|
||||
import { Insets } from '@/types/misc';
|
||||
import { EnvConfigType } from '@/services/environment';
|
||||
import { FoliateView } from '@/types/view';
|
||||
import { DocumentLoader, TOCItem } from '@/libs/document';
|
||||
@@ -27,6 +28,7 @@ interface ViewState {
|
||||
progress: BookProgress | null;
|
||||
ribbonVisible: boolean;
|
||||
ttsEnabled: boolean;
|
||||
gridInsets: Insets | null;
|
||||
/* View settings for the view:
|
||||
generally view settings have a hierarchy of global settings < book settings < view settings
|
||||
view settings for primary view are saved to book config which is persisted to config file
|
||||
@@ -67,6 +69,8 @@ interface ReaderStore {
|
||||
) => Promise<void>;
|
||||
clearViewState: (key: string) => void;
|
||||
getViewState: (key: string) => ViewState | null;
|
||||
getGridInsets: (key: string) => Insets | null;
|
||||
setGridInsets: (key: string, insets: Insets | null) => void;
|
||||
}
|
||||
|
||||
export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
@@ -114,6 +118,7 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
progress: null,
|
||||
ribbonVisible: false,
|
||||
ttsEnabled: false,
|
||||
gridInsets: null,
|
||||
viewSettings: null,
|
||||
},
|
||||
},
|
||||
@@ -159,6 +164,7 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
progress: null,
|
||||
ribbonVisible: false,
|
||||
ttsEnabled: false,
|
||||
gridInsets: null,
|
||||
viewSettings: { ...globalViewSettings, ...configViewSettings },
|
||||
},
|
||||
},
|
||||
@@ -178,6 +184,7 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
progress: null,
|
||||
ribbonVisible: false,
|
||||
ttsEnabled: false,
|
||||
gridInsets: null,
|
||||
viewSettings: null,
|
||||
},
|
||||
},
|
||||
@@ -306,4 +313,16 @@ export const useReaderStore = create<ReaderStore>((set, get) => ({
|
||||
},
|
||||
},
|
||||
})),
|
||||
|
||||
getGridInsets: (key: string) => get().viewStates[key]?.gridInsets || null,
|
||||
setGridInsets: (key: string, insets: Insets | null) =>
|
||||
set((state) => ({
|
||||
viewStates: {
|
||||
...state.viewStates,
|
||||
[key]: {
|
||||
...state.viewStates[key]!,
|
||||
gridInsets: insets,
|
||||
},
|
||||
},
|
||||
})),
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user