1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-28 16:17:02 +00:00
Files
seahub/frontend/src/metadata/components/dialog/ocr-result-dialog/index.js

149 lines
5.4 KiB
JavaScript
Raw Normal View History

2025-06-06 13:55:53 +08:00
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { I18nextProvider } from 'react-i18next';
import { SimpleEditor, getBrowserInfo, LongTextModal, BrowserTip, slateToMdString, MarkdownPreview } from '@seafile/seafile-editor';
import { gettext, lang } from '../../../../utils/constants';
import { getFileNameFromRecord, getParentDirFromRecord } from '../../../utils/cell';
import { Utils } from '../../../../utils/utils';
import i18n from '../../../../_i18n/i18n-seafile-editor';
import Icon from '../../../../components/icon';
2025-06-09 16:10:29 +08:00
import metadataAPI from '../../../api';
import { useMetadataAIOperations } from '../../../../hooks/metadata-ai-operation';
2025-06-06 13:55:53 +08:00
import './index.css';
2025-06-09 16:10:29 +08:00
const OCRResultDialog = ({ repoID, record, onToggle, saveToDescription }) => {
2025-06-06 13:55:53 +08:00
const [isLoading, setLoading] = useState(true);
2025-06-06 15:54:30 +08:00
const [errorMessage, setErrorMessage] = useState('');
2025-06-06 13:55:53 +08:00
const [isFullScreen, setIsFullScreen] = useState(false);
const [dialogStyle, setDialogStyle] = useState({});
2025-06-09 16:10:29 +08:00
const { canModify } = useMetadataAIOperations();
2025-06-06 13:55:53 +08:00
const ocrResult = useRef(null);
const parentDir = useMemo(() => getParentDirFromRecord(record), [record]);
const fileName = useMemo(() => getFileNameFromRecord(record), [record]);
const editorRef = useRef(null);
const { isValidBrowser, isWindowsWechat } = useMemo(() => {
return getBrowserInfo(true);
}, []);
const onFullScreenToggle = useCallback(() => {
let containerStyle = {};
if (!isFullScreen) {
containerStyle = {
width: '100%',
height: '100%',
top: 0,
border: 'none'
};
}
setIsFullScreen(!isFullScreen);
setDialogStyle(containerStyle);
}, [isFullScreen]);
const onContainerKeyDown = useCallback((event) => {
event.stopPropagation();
if (event.keyCode === 27) {
event.preventDefault();
onToggle();
}
}, [onToggle]);
const onSave = useCallback(() => {
const newContent = editorRef.current?.getSlateValue();
const value = slateToMdString(newContent);
saveToDescription(value);
onToggle();
}, [saveToDescription, onToggle]);
useEffect(() => {
2025-06-06 15:54:30 +08:00
const path = Utils.joinPath(parentDir, fileName);
2025-06-09 16:10:29 +08:00
metadataAPI.ocr(repoID, path).then(res => {
2025-06-06 13:55:53 +08:00
const result = res.data?.ocr_result || '';
2025-06-06 15:54:30 +08:00
ocrResult.current = result.replaceAll('\f', '\n').replaceAll('\n\n', '\n').replaceAll('\n', '\n\n');
2025-06-06 13:55:53 +08:00
setLoading(false);
}).catch(error => {
const errorMessage = gettext('Failed to extract text');
2025-06-06 15:54:30 +08:00
setErrorMessage(errorMessage);
2025-06-06 13:55:53 +08:00
setLoading(false);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
2025-06-06 15:54:30 +08:00
useEffect(() => {
document.addEventListener('keydown', onContainerKeyDown);
return () => {
document.removeEventListener('keydown', onContainerKeyDown);
};
}, [onContainerKeyDown]);
2025-06-06 13:55:53 +08:00
return (
<I18nextProvider i18n={ i18n }>
<LongTextModal onModalClick={onToggle} containerClass="sf-metadata-ocr-file-dialog">
<div style={dialogStyle} className="longtext-dialog-container">
2025-06-06 15:54:30 +08:00
<div className={classnames('longtext-header-container', {
'longtext-header-container-border': isWindowsWechat,
'longtext-header-divider': isLoading || errorMessage
})}>
2025-06-06 13:55:53 +08:00
<div className="longtext-header">
<span className="longtext-header-name">{gettext('OCR result')}</span>
<div className="longtext-header-tool">
2025-06-09 16:10:29 +08:00
{!isLoading && !errorMessage && canModify && (
2025-06-06 15:54:30 +08:00
<span
className="longtext-header-tool-item d-flex align-items-center mr-1"
title={gettext('Save to description property')}
onClick={onSave}
>
<Icon symbol="save" />
</span>
)}
2025-06-06 13:55:53 +08:00
<span
className={classnames('longtext-header-tool-item mr-1 iconfont icon-full-screen', { 'long-text-full-screen': isFullScreen })}
onClick={onFullScreenToggle}
title={gettext('Full screen')}
>
</span>
<span
className="longtext-header-tool-item iconfont icon-x"
onClick={onToggle}
title={gettext('Close')}
>
</span>
</div>
</div>
{!isValidBrowser && <BrowserTip lang={lang} isWindowsWechat={isWindowsWechat} />}
</div>
<div
onKeyDown={onContainerKeyDown}
className={classnames('longtext-content-container', { 'longtext-container-scroll': isWindowsWechat })}
>
2025-06-06 15:54:30 +08:00
{errorMessage ? (
<div className="w-100 h-100 d-flex align-items-center justify-content-center error">{errorMessage}</div>
2025-06-06 13:55:53 +08:00
) : (
<>
{isWindowsWechat ? (
<MarkdownPreview isWindowsWechat={isWindowsWechat} value={ocrResult.current} isShowOutline={false} />
) : (
2025-06-06 15:54:30 +08:00
<SimpleEditor ref={editorRef} isFetching={isLoading} focusEnd={false} value={ocrResult.current} />
2025-06-06 13:55:53 +08:00
)}
</>
)}
</div>
</div>
</LongTextModal>
</I18nextProvider>
);
};
OCRResultDialog.propTypes = {
record: PropTypes.object,
onToggle: PropTypes.func,
saveToDescription: PropTypes.func,
};
export default OCRResultDialog;