ux: make toast info and hint info less disturbing (#272)

This commit is contained in:
Huang Xin
2025-01-30 16:46:17 +01:00
committed by GitHub
parent fef75f123d
commit 90e46860a5
7 changed files with 90 additions and 11 deletions
@@ -18,6 +18,12 @@ export const useBooksSync = () => {
syncBooks([], 'pull');
};
const pushLibrary = async () => {
if (!user) return;
const newBooks = getNewBooks();
syncBooks(newBooks, 'push');
};
useEffect(() => {
if (!user) return;
if (syncBooksPullingRef.current) return;
@@ -104,5 +110,5 @@ export const useBooksSync = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [syncedBooks]);
return { pullLibrary };
return { pullLibrary, pushLibrary };
};
+7 -4
View File
@@ -52,7 +52,7 @@ const LibraryPage = () => {
const demoBooks = useDemoBooks();
const containerRef = useRef<HTMLDivElement>(null);
const { pullLibrary } = useBooksSync();
const { pullLibrary, pushLibrary } = useBooksSync();
usePullToRefresh(containerRef, pullLibrary);
@@ -216,9 +216,11 @@ const LibraryPage = () => {
setLoading(true);
try {
await appService?.uploadBook(book);
updateBook(envConfig, book);
await updateBook(envConfig, book);
pushLibrary();
eventDispatcher.dispatch('toast', {
type: 'success',
type: 'info',
timeout: 2000,
message: _('Book uploaded: {{title}}', {
title: book.title,
}),
@@ -241,7 +243,8 @@ const LibraryPage = () => {
await appService?.downloadBook(book);
updateBook(envConfig, book);
eventDispatcher.dispatch('toast', {
type: 'success',
type: 'info',
timeout: 2000,
message: _('Book downloaded: {{title}}', {
title: book.title,
}),
@@ -15,6 +15,7 @@ import Ribbon from './Ribbon';
import SettingsDialog from './settings/SettingsDialog';
import Annotator from './annotator/Annotator';
import FootnotePopup from './FootnotePopup';
import HintInfo from './HintInfo';
interface BooksGridProps {
bookKeys: string[];
@@ -76,6 +77,7 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook }) => {
{viewSettings.scrolled ? null : (
<>
<SectionInfo section={sectionLabel} gapLeft={marginGap} />
<HintInfo bookKey={bookKey} gapRight={marginGap} />
<PageInfoView
bookFormat={book.format}
section={section ?? null}
@@ -0,0 +1,58 @@
import clsx from 'clsx';
import React, { useEffect, useRef } from 'react';
import { useSidebarStore } from '@/store/sidebarStore';
import { eventDispatcher } from '@/utils/event';
import useTrafficLight from '@/hooks/useTrafficLight';
interface SectionInfoProps {
bookKey: string;
gapRight: string;
}
const HintInfo: React.FC<SectionInfoProps> = ({ bookKey, gapRight }) => {
const { isSideBarVisible } = useSidebarStore();
const { isTrafficLightVisible } = useTrafficLight();
const [hintMessage, setHintMessage] = React.useState<string | null>(null);
const hintTimeout = useRef(2000);
const dismissTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
const handleShowHint = (event: CustomEvent) => {
const { message, bookKey: hintBookKey, timeout = 2000 } = event.detail;
if (hintBookKey !== bookKey) return;
setHintMessage(message);
hintTimeout.current = timeout;
};
useEffect(() => {
eventDispatcher.on('hint', handleShowHint);
return () => {
eventDispatcher.off('hint', handleShowHint);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (dismissTimeout.current) clearTimeout(dismissTimeout.current);
dismissTimeout.current = setTimeout(() => setHintMessage(''), hintTimeout.current);
return () => {
if (dismissTimeout.current) clearTimeout(dismissTimeout.current);
};
}, [hintMessage]);
return (
<div
className={clsx(
'pageinfo absolute right-0 top-0 flex max-w-[50%] items-end',
isTrafficLightVisible && !isSideBarVisible ? 'h-[44px]' : 'h-[30px]',
hintMessage ? '' : '',
)}
style={{ right: gapRight }}
>
<h2 className='text-neutral-content line-clamp-1 text-center font-sans text-xs font-light'>
{hintMessage || ''}
</h2>
</div>
);
};
export default HintInfo;
@@ -15,12 +15,12 @@ const SectionInfo: React.FC<SectionInfoProps> = ({ section, gapLeft }) => {
return (
<div
className={clsx(
'pageinfo absolute right-0 top-0 flex items-end',
'pageinfo absolute right-0 top-0 flex max-w-[50%] items-end',
isTrafficLightVisible && !isSideBarVisible ? 'h-[44px]' : 'h-[30px]',
)}
style={{ left: gapLeft }}
>
<h2 className='text-neutral-content text-center font-sans text-xs font-light'>
<h2 className='text-neutral-content line-clamp-1 text-center font-sans text-xs font-light'>
{section || ''}
</h2>
</div>
@@ -118,8 +118,8 @@ export const useProgressSync = (bookKey: string) => {
if (CFI.compare(configCFI, syncedCFI) < 0) {
if (view) {
view.goTo(syncedCFI);
eventDispatcher.dispatch('toast', {
type: 'success',
eventDispatcher.dispatch('hint', {
bookKey,
message: _('Reading Progress Synced'),
});
}
+12 -2
View File
@@ -48,14 +48,24 @@ export const Toast = () => {
return (
toastMessage && (
<div className={clsx('toast toast-center toast-middle', toastClassMap[toastType.current])}>
<div
className={clsx(
'toast toast-center toast-middle w-auto max-w-screen-sm',
toastClassMap[toastType.current],
)}
>
<div
className={clsx(
'alert flex max-w-80 items-center justify-center border-0',
alertClassMap[toastType.current],
)}
>
<span className={clsx('whitespace-normal break-words', messageClass.current)}>
<span
className={clsx(
'max-h-[50vh] min-w-[30vw] overflow-scroll whitespace-normal break-words text-center',
messageClass.current,
)}
>
{toastMessage}
</span>
</div>