forked from akai/readest
ux: make toast info and hint info less disturbing (#272)
This commit is contained in:
@@ -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 };
|
||||
};
|
||||
|
||||
@@ -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'),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user