forked from akai/readest
* added current time to desktop bar * added time prototype to footer, needs code cleanup and settings toggle * fixed settings toggle, added translations and code cleanup * added battery support and moved Statusbar to own Component * #3306 added 24 hour clock support * refactored code styling and getting rid of any type in battery hook * Add battery info for Tauri Apps --------- Co-authored-by: Huang Xin <chrox.huang@gmail.com>
This commit is contained in:
@@ -16,7 +16,7 @@ import SectionInfo from './SectionInfo';
|
||||
import HeaderBar from './HeaderBar';
|
||||
import PageNavigationButtons from './PageNavigationButtons';
|
||||
import FooterBar from './footerbar/FooterBar';
|
||||
import ProgressInfoView from './ProgressInfo';
|
||||
import ProgressBar from './ProgressBar';
|
||||
import Ribbon from './Ribbon';
|
||||
import Annotator from './annotator/Annotator';
|
||||
import FootnotePopup from './FootnotePopup';
|
||||
@@ -210,7 +210,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook, onGoToLibr
|
||||
/>
|
||||
)}
|
||||
{showFooter && (
|
||||
<ProgressInfoView
|
||||
<ProgressBar
|
||||
bookKey={bookKey}
|
||||
horizontalGap={horizontalGapPercent}
|
||||
contentInsets={contentInsets}
|
||||
|
||||
+103
-53
@@ -9,15 +9,17 @@ import { useBookDataStore } from '@/store/bookDataStore';
|
||||
import { formatNumber, formatProgress } from '@/utils/progress';
|
||||
import { saveViewSettings } from '@/helpers/settings';
|
||||
import { SIZE_PER_LOC, SIZE_PER_TIME_UNIT } from '@/services/constants';
|
||||
import type { ProgressBarMode } from '@/types/book.ts';
|
||||
import StatusInfo from './StatusInfo.tsx';
|
||||
|
||||
interface PageInfoProps {
|
||||
interface ProgressBarProps {
|
||||
bookKey: string;
|
||||
horizontalGap: number;
|
||||
contentInsets: Insets;
|
||||
gridInsets: Insets;
|
||||
}
|
||||
|
||||
const ProgressInfoView: React.FC<PageInfoProps> = ({
|
||||
const ProgressBar: React.FC<ProgressBarProps> = ({
|
||||
bookKey,
|
||||
horizontalGap,
|
||||
contentInsets,
|
||||
@@ -78,49 +80,73 @@ const ProgressInfoView: React.FC<PageInfoProps> = ({
|
||||
|
||||
const showPagesLeft = total - 1 > current;
|
||||
|
||||
const [progressInfoMode, setProgressInfoMode] = useState(viewSettings.progressInfoMode);
|
||||
const [progressBarMode, setProgressBarMode] = useState<string>(viewSettings.progressInfoMode);
|
||||
|
||||
const hasRemainingInfo = viewSettings.showRemainingTime || viewSettings.showRemainingPages;
|
||||
const hasProgressInfo = viewSettings.showProgressInfo;
|
||||
const hasTimeInfo = viewSettings.showCurrentTime;
|
||||
const hasBatteryInfo = viewSettings.showCurrentBatteryStatus;
|
||||
const cycleProgressInfoModes = () => {
|
||||
if (!viewSettings.tapToToggleFooter) return;
|
||||
|
||||
const hasRemainingInfo = viewSettings.showRemainingTime || viewSettings.showRemainingPages;
|
||||
const hasProgressInfo = viewSettings.showProgressInfo;
|
||||
const modeSequence: (typeof progressInfoMode)[] = ['all', 'remaining', 'progress', 'none'];
|
||||
const currentIndex = modeSequence.indexOf(progressInfoMode);
|
||||
const modeSequence: string[] = [
|
||||
'all',
|
||||
`${hasRemainingInfo ? 'remaining+' : ''}${hasProgressInfo ? 'progress' : ''}`,
|
||||
`${hasRemainingInfo ? 'remaining' : ''}`,
|
||||
`${hasProgressInfo ? 'progress' : ''}`,
|
||||
`${hasBatteryInfo ? 'battery+' : ''}${hasTimeInfo ? 'time' : ''}`,
|
||||
`${hasBatteryInfo ? 'battery' : ''}`,
|
||||
`${hasTimeInfo ? 'time' : ''}`,
|
||||
'none',
|
||||
]
|
||||
.map((mode) => mode.replace(/^\+|\+$/g, ''))
|
||||
.filter((mode) => mode !== '')
|
||||
.filter((mode, index, self) => self.indexOf(mode) === index);
|
||||
|
||||
const currentMode = progressBarMode;
|
||||
const currentIndex = modeSequence.indexOf(currentMode);
|
||||
for (let i = 1; i <= modeSequence.length; i++) {
|
||||
const nextIndex = (currentIndex + i) % modeSequence.length;
|
||||
const nextMode = modeSequence[nextIndex]!;
|
||||
|
||||
const currentRenders = {
|
||||
remaining:
|
||||
progressInfoMode === 'all' || progressInfoMode === 'remaining' ? hasRemainingInfo : false,
|
||||
currentMode === 'all' || currentMode.includes('remaining') ? hasRemainingInfo : false,
|
||||
progress:
|
||||
progressInfoMode === 'all' || progressInfoMode === 'progress' ? hasProgressInfo : false,
|
||||
currentMode === 'all' || currentMode.includes('progress') ? hasProgressInfo : false,
|
||||
battery: currentMode === 'all' || currentMode.includes('battery') ? hasBatteryInfo : false,
|
||||
time: currentMode === 'all' || currentMode.includes('time') ? hasTimeInfo : false,
|
||||
none: currentMode === 'none',
|
||||
};
|
||||
|
||||
const nextRenders = {
|
||||
remaining: nextMode === 'all' || nextMode === 'remaining' ? hasRemainingInfo : false,
|
||||
progress: nextMode === 'all' || nextMode === 'progress' ? hasProgressInfo : false,
|
||||
remaining: nextMode === 'all' || nextMode.includes('remaining') ? hasRemainingInfo : false,
|
||||
progress: nextMode === 'all' || nextMode.includes('progress') ? hasProgressInfo : false,
|
||||
battery: nextMode === 'all' || nextMode.includes('battery') ? hasBatteryInfo : false,
|
||||
time: nextMode === 'all' || nextMode.includes('time') ? hasTimeInfo : false,
|
||||
none: nextMode === 'none',
|
||||
};
|
||||
|
||||
const isDifferent =
|
||||
currentRenders.remaining !== nextRenders.remaining ||
|
||||
currentRenders.progress !== nextRenders.progress;
|
||||
|
||||
currentRenders.progress !== nextRenders.progress ||
|
||||
currentRenders.battery !== nextRenders.battery ||
|
||||
currentRenders.time !== nextRenders.time ||
|
||||
currentRenders.none !== nextRenders.none;
|
||||
if (isDifferent) {
|
||||
setProgressInfoMode(nextMode);
|
||||
setProgressBarMode(nextMode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const nextIndex = (currentIndex + 1) % modeSequence.length;
|
||||
setProgressInfoMode(modeSequence[nextIndex]!);
|
||||
setProgressBarMode(modeSequence[nextIndex]!);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
saveViewSettings(envConfig, bookKey, 'progressInfoMode', progressInfoMode);
|
||||
saveViewSettings(envConfig, bookKey, 'progressInfoMode', progressBarMode as ProgressBarMode);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [progressInfoMode]);
|
||||
}, [progressBarMode]);
|
||||
|
||||
const isMobile = appService?.isMobile || window.innerWidth < 640;
|
||||
|
||||
@@ -171,45 +197,69 @@ const ProgressInfoView: React.FC<PageInfoProps> = ({
|
||||
isVertical ? 'h-full' : 'h-[52px] w-full',
|
||||
)}
|
||||
>
|
||||
{(progressInfoMode === 'all' || progressInfoMode === 'remaining') && (
|
||||
<>
|
||||
{viewSettings.showRemainingTime ? (
|
||||
<span className='time-left-label text-start'>{timeLeftStr}</span>
|
||||
) : viewSettings.showRemainingPages && showPagesLeft ? (
|
||||
<span className='text-start'>
|
||||
{localize ? (
|
||||
<Trans
|
||||
i18nKey='{{number}} pages left in chapter'
|
||||
values={{ number: formatNumber(pagesLeft, localize, lang) }}
|
||||
>
|
||||
<span className='pages-left-number'>{'{{number}}'}</span>
|
||||
<span className='pages-left-label'>{' pages left in chapter'}</span>
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans i18nKey='{{count}} pages left in chapter' count={pagesLeft}>
|
||||
<span className='pages-left-number'>{'{{count}}'}</span>
|
||||
<span className='pages-left-label'>{' pages left in chapter'}</span>
|
||||
</Trans>
|
||||
)}
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
{(progressBarMode === 'all' || progressBarMode.includes('remaining')) &&
|
||||
hasRemainingInfo && (
|
||||
<div className='remaining-info flex-1 overflow-hidden whitespace-nowrap text-start'>
|
||||
{viewSettings.showRemainingTime ? (
|
||||
<span className='time-left-label text-start'>{timeLeftStr}</span>
|
||||
) : viewSettings.showRemainingPages && showPagesLeft ? (
|
||||
<span className='text-start'>
|
||||
{localize ? (
|
||||
<Trans
|
||||
i18nKey='{{number}} pages left in chapter'
|
||||
values={{ number: formatNumber(pagesLeft, localize, lang) }}
|
||||
>
|
||||
<span className='pages-left-number'>{'{{number}}'}</span>
|
||||
<span className='pages-left-label'>{' pages left in chapter'}</span>
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans i18nKey='{{count}} pages left in chapter' count={pagesLeft}>
|
||||
<span className='pages-left-number'>{'{{count}}'}</span>
|
||||
<span className='pages-left-label'>{' pages left in chapter'}</span>
|
||||
</Trans>
|
||||
)}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(progressInfoMode === 'all' || progressInfoMode === 'progress') && (
|
||||
<>
|
||||
{viewSettings.showProgressInfo && (
|
||||
<span
|
||||
className={clsx('progress-info-label text-end', isVertical ? 'mt-auto' : 'ms-auto')}
|
||||
>
|
||||
{progressInfo}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{(progressBarMode === 'all' ||
|
||||
progressBarMode.includes('battery') ||
|
||||
progressBarMode.includes('time')) &&
|
||||
progressInfo && (
|
||||
<StatusInfo
|
||||
showTime={
|
||||
(progressBarMode === 'all' || progressBarMode.includes('time')) && hasTimeInfo
|
||||
}
|
||||
use24Hour={viewSettings.use24HourClock}
|
||||
showBattery={
|
||||
(progressBarMode === 'all' || progressBarMode.includes('battery')) && hasBatteryInfo
|
||||
}
|
||||
showBatteryPercentage={viewSettings.showBatteryPercentage}
|
||||
isVertical={isVertical}
|
||||
isEink={isEink}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className='progress-info flex-1 items-center overflow-hidden whitespace-nowrap text-end tabular-nums'>
|
||||
{(progressBarMode === 'all' || progressBarMode.includes('progress')) && (
|
||||
<>
|
||||
{viewSettings.showProgressInfo && (
|
||||
<span
|
||||
className={clsx(
|
||||
'progress-info-label text-end',
|
||||
isVertical ? 'mt-auto' : 'ms-auto',
|
||||
)}
|
||||
>
|
||||
{progressInfo}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProgressInfoView;
|
||||
export default ProgressBar;
|
||||
@@ -0,0 +1,81 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useCurrentTime } from '../hooks/useCurrentTime';
|
||||
import { useCurrentBatteryStatus } from '../hooks/useCurrentBattery';
|
||||
|
||||
interface StatusInfoProps {
|
||||
showTime: boolean;
|
||||
showBattery: boolean;
|
||||
showBatteryPercentage: boolean;
|
||||
use24Hour?: boolean;
|
||||
isVertical?: boolean;
|
||||
isEink?: boolean;
|
||||
}
|
||||
|
||||
const StatusInfo: React.FC<StatusInfoProps> = ({
|
||||
showTime,
|
||||
use24Hour = true,
|
||||
showBattery,
|
||||
showBatteryPercentage,
|
||||
isVertical,
|
||||
isEink,
|
||||
}) => {
|
||||
const formattedTime = useCurrentTime(showTime, use24Hour);
|
||||
const batteryLevel = useCurrentBatteryStatus(showBattery);
|
||||
|
||||
if (!showTime && !showBattery) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'status-bar flex shrink-0 items-center gap-2 whitespace-nowrap tabular-nums',
|
||||
isVertical ? 'my-auto flex-col' : 'flex-row',
|
||||
)}
|
||||
>
|
||||
{showTime && <span>{formattedTime}</span>}
|
||||
{showBattery && batteryLevel !== null && (
|
||||
<span className='relative inline-flex translate-y-[-0.5px] items-center justify-center'>
|
||||
<svg width='25' height='12' viewBox='0 0 25 12' fill='none'>
|
||||
<rect
|
||||
x='0.5'
|
||||
y='0.5'
|
||||
width='21'
|
||||
height='11'
|
||||
rx='2'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1'
|
||||
opacity={isEink ? 1.0 : 0.75}
|
||||
/>
|
||||
<rect
|
||||
x='1.5'
|
||||
y='1.5'
|
||||
width={(batteryLevel / 100) * 19}
|
||||
height='9'
|
||||
rx='1'
|
||||
fill='currentColor'
|
||||
opacity={isEink ? 1.0 : 0.3}
|
||||
/>
|
||||
<path
|
||||
d='M23 4V8C23.8 8 24 7 24 6C24 5 23.8 4 23 4Z'
|
||||
fill='currentColor'
|
||||
opacity={isEink ? 1.0 : 0.75}
|
||||
/>
|
||||
</svg>
|
||||
{showBatteryPercentage && batteryLevel !== null && (
|
||||
<span
|
||||
className={clsx(
|
||||
'absolute text-[8px] font-medium leading-none invert',
|
||||
isEink ? 'text-black mix-blend-difference' : 'text-base-300 mix-blend-luminosity',
|
||||
)}
|
||||
style={{ left: '11px', transform: 'translateX(-50%)' }}
|
||||
>
|
||||
{batteryLevel}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StatusInfo;
|
||||
Reference in New Issue
Block a user