Files
readest/apps/readest-app/src/app/reader/components/ReviewModeToggler.tsx
T
2026-07-10 12:37:13 +08:00

130 lines
3.8 KiB
TypeScript

import clsx from 'clsx';
import React from 'react';
import Button from '@/components/Button';
import { useEnv } from '@/context/EnvContext';
import { useTranslation } from '@/hooks/useTranslation';
import { useBookDataStore } from '@/store/bookDataStore';
import { useReaderStore } from '@/store/readerStore';
import { useReviewModeStore } from '@/store/reviewModeStore';
import { eventDispatcher } from '@/utils/event';
import {
canLaunchInlineReviewEditor,
launchInlineReviewEditor,
loadInlineReviewData,
} from '@/services/reviewEditorService';
interface ReviewModeTogglerProps {
bookKey: string;
}
const ReviewModeToggler: React.FC<ReviewModeTogglerProps> = ({ bookKey }) => {
const _ = useTranslation();
const { appService } = useEnv();
const { setHoveredBookKey } = useReaderStore();
const bookData = useBookDataStore((state) => state.getBookData(bookKey));
const reviewState = useReviewModeStore((state) => state.books[bookKey]);
const {
setActiveBookKey,
setPanelVisible,
setBookLoading,
setBookError,
setBookEnabled,
setBookData,
} = useReviewModeStore();
const enabled = !!reviewState?.enabled;
const loading = !!reviewState?.loading;
if (!canLaunchInlineReviewEditor(appService) || bookData?.book?.format !== 'EPUB') return null;
const handleToggleReviewMode = async () => {
if (appService?.isMobile) {
setHoveredBookKey('');
}
if (enabled) {
if (
reviewState?.hasUnsavedEdit &&
appService &&
!(await appService.ask('当前段落有未保存的修改,确定关闭审校模式吗?'))
) {
return;
}
setBookEnabled(bookKey, false);
return;
}
if (!bookData?.book) {
setBookError(bookKey, _('Unable to open book'));
return;
}
const activeState = useReviewModeStore.getState();
const activeReview = activeState.books[activeState.activeBookKey || ''];
if (
activeState.activeBookKey !== bookKey &&
activeReview?.hasUnsavedEdit &&
appService &&
!(await appService.ask('另一册书的当前段落有未保存修改,确定切换审校书籍吗?'))
) {
return;
}
if (activeState.activeBookKey && activeReview?.hasUnsavedEdit) {
activeState.setBookUnsavedEdit(activeState.activeBookKey, false);
}
setActiveBookKey(bookKey);
setPanelVisible(true);
setBookLoading(bookKey, true);
try {
const launch = await launchInlineReviewEditor(appService, bookData.book);
const data = await loadInlineReviewData(
launch.url,
launch.sessionId,
launch.accessToken,
);
setBookData(bookKey, {
baseUrl: launch.url,
accessToken: launch.accessToken,
sessionId: launch.sessionId,
reviewRoot: launch.reviewRoot,
version: launch.version,
session: data.session,
rows: data.rows,
gptConfig: data.gptConfig,
selectedRowId: data.rows[0]?.id || '',
});
setBookEnabled(bookKey, true);
eventDispatcher.dispatch('toast', {
type: 'info',
message: data.rows.length
? `审校模式已开启,共 ${data.rows.length} 段`
: '审校模式已开启,但当前 EPUB 没有识别到双语段落',
timeout: 2500,
});
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
setBookError(bookKey, message);
eventDispatcher.dispatch('toast', {
type: 'error',
message,
timeout: 3500,
});
}
};
return (
<Button
icon={<span className='text-sm font-semibold leading-none'></span>}
onClick={handleToggleReviewMode}
disabled={loading}
label={enabled ? _('Disable Review Mode') : _('Review Mode')}
className={clsx(enabled && 'bg-base-300/50')}
/>
);
};
export default ReviewModeToggler;