mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-27 23:56:18 +00:00
Optimize/table view toolbar (#7567)
* optimize table view toolbar * optimize --------- Co-authored-by: zhouwenxuan <aries@Mac.local>
This commit is contained in:
@@ -216,6 +216,7 @@ class DirColumnView extends React.Component {
|
|||||||
updateCurrentDirent={this.props.updateCurrentDirent}
|
updateCurrentDirent={this.props.updateCurrentDirent}
|
||||||
showDirentDetail={this.props.showDirentDetail}
|
showDirentDetail={this.props.showDirentDetail}
|
||||||
updateCurrentPath={this.props.updateCurrentPath}
|
updateCurrentPath={this.props.updateCurrentPath}
|
||||||
|
toggleShowDirentToolbar={this.props.toggleShowDirentToolbar}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{currentMode === TAGS_MODE && (
|
{currentMode === TAGS_MODE && (
|
||||||
|
197
frontend/src/components/toolbar/table-files-toolbar.js
Normal file
197
frontend/src/components/toolbar/table-files-toolbar.js
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ItemDropdownMenu from '../dropdown-menu/item-dropdown-menu';
|
||||||
|
import { gettext } from '../../utils/constants';
|
||||||
|
import { EVENT_BUS_TYPE, PRIVATE_COLUMN_KEY } from '../../metadata/constants';
|
||||||
|
import TextTranslation from '../../utils/text-translation';
|
||||||
|
import { getFileName } from '../../tag/utils/file';
|
||||||
|
import RowUtils from '../../metadata/views/table/utils/row-utils';
|
||||||
|
import { checkIsDir } from '../../metadata/utils/row';
|
||||||
|
import { Utils } from '../../utils/utils';
|
||||||
|
import { getFileNameFromRecord } from '../../metadata/utils/cell';
|
||||||
|
import { getColumnByKey } from '../../metadata/utils/column';
|
||||||
|
import { useMetadataStatus } from '../../hooks';
|
||||||
|
import { openInNewTab, openParentFolder } from '../../metadata/utils/file';
|
||||||
|
|
||||||
|
const TableFilesToolbar = ({ repoID }) => {
|
||||||
|
const [selectedRecordIds, setSelectedRecordIds] = useState([]);
|
||||||
|
const metadataRef = useRef([]);
|
||||||
|
const { enableOCR } = useMetadataStatus();
|
||||||
|
|
||||||
|
const canModify = window.sfMetadataContext && window.sfMetadataContext.canModify();
|
||||||
|
const eventBus = window.sfMetadataContext && window.sfMetadataContext.eventBus;
|
||||||
|
|
||||||
|
const records = useMemo(() => selectedRecordIds.map(id => RowUtils.getRecordById(id, metadataRef.current)).filter(Boolean) || [], [selectedRecordIds]);
|
||||||
|
|
||||||
|
const unSelect = useCallback(() => {
|
||||||
|
setSelectedRecordIds([]);
|
||||||
|
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.UPDATE_SELECTED_RECORD_IDS, []);
|
||||||
|
eventBus.dispatch(EVENT_BUS_TYPE.SELECT_NONE);
|
||||||
|
}, [eventBus]);
|
||||||
|
|
||||||
|
const deleteRecords = useCallback(() => {
|
||||||
|
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.DELETE_RECORDS, selectedRecordIds, {
|
||||||
|
success_callback: () => {
|
||||||
|
eventBus.dispatch(EVENT_BUS_TYPE.SELECT_NONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [eventBus, selectedRecordIds]);
|
||||||
|
|
||||||
|
const toggleMoveDialog = useCallback(() => {
|
||||||
|
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.TOGGLE_MOVE_DIALOG, records[0]);
|
||||||
|
}, [eventBus, records]);
|
||||||
|
|
||||||
|
const checkCanModifyRow = (row) => window.sfMetadataContext.canModifyRow(row);
|
||||||
|
|
||||||
|
const getMenuList = useCallback(() => {
|
||||||
|
const { EXTRACT_FILE_DETAIL, EXTRACT_FILE_DETAILS, OPEN_FILE_IN_NEW_TAB, OPEN_FOLDER_IN_NEW_TAB, OPEN_PARENT_FOLDER, GENERATE_DESCRIPTION, OCR } = TextTranslation;
|
||||||
|
const length = selectedRecordIds.length;
|
||||||
|
const list = [];
|
||||||
|
if (length > 1) {
|
||||||
|
const imageOrVideoRecords = records.filter(record => {
|
||||||
|
const isFolder = checkIsDir(record);
|
||||||
|
if (isFolder) return false;
|
||||||
|
const canModifyRow = checkCanModifyRow(record);
|
||||||
|
if (!canModifyRow) return false;
|
||||||
|
const fileName = getFileName(record);
|
||||||
|
return Utils.imageCheck(fileName) || Utils.videoCheck(fileName);
|
||||||
|
});
|
||||||
|
if (imageOrVideoRecords.length > 0) {
|
||||||
|
list.push(EXTRACT_FILE_DETAILS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = records[0];
|
||||||
|
const isFolder = checkIsDir(record);
|
||||||
|
const canModifyRow = checkCanModifyRow(record);
|
||||||
|
|
||||||
|
list.push(isFolder ? OPEN_FOLDER_IN_NEW_TAB : OPEN_FILE_IN_NEW_TAB);
|
||||||
|
list.push(OPEN_PARENT_FOLDER);
|
||||||
|
|
||||||
|
const modifyOptions = [];
|
||||||
|
|
||||||
|
if (modifyOptions.length > 0) {
|
||||||
|
list.push('Divider');
|
||||||
|
list.push(...modifyOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isFolder && canModifyRow) {
|
||||||
|
const { columns } = metadataRef.current;
|
||||||
|
const fileName = getFileNameFromRecord(record);
|
||||||
|
const isDescribableFile = canModifyRow && Utils.isDescriptionSupportedFile(fileName);
|
||||||
|
const isImage = Utils.imageCheck(fileName);
|
||||||
|
const isVideo = Utils.videoCheck(fileName);
|
||||||
|
const descriptionColumn = getColumnByKey(columns, PRIVATE_COLUMN_KEY.FILE_DESCRIPTION);
|
||||||
|
const aiOptions = [];
|
||||||
|
|
||||||
|
if (isImage || isVideo) {
|
||||||
|
aiOptions.push(EXTRACT_FILE_DETAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptionColumn && isDescribableFile) {
|
||||||
|
aiOptions.push(GENERATE_DESCRIPTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableOCR && isImage) {
|
||||||
|
aiOptions.push(OCR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aiOptions.length > 0) {
|
||||||
|
list.push('Divider');
|
||||||
|
list.push(...aiOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}, [selectedRecordIds, records, enableOCR]);
|
||||||
|
|
||||||
|
const onMenuItemClick = useCallback((operation) => {
|
||||||
|
const records = selectedRecordIds.map(id => RowUtils.getRecordById(id, metadataRef.current)).filter(Boolean);
|
||||||
|
switch (operation) {
|
||||||
|
case TextTranslation.EXTRACT_FILE_DETAIL.key:
|
||||||
|
case TextTranslation.EXTRACT_FILE_DETAILS.key: {
|
||||||
|
const imageOrVideoRecords = records.filter(record => {
|
||||||
|
const isFolder = checkIsDir(record);
|
||||||
|
if (isFolder) return false;
|
||||||
|
const canModifyRow = checkCanModifyRow(record);
|
||||||
|
if (!canModifyRow) return false;
|
||||||
|
const fileName = getFileNameFromRecord(record);
|
||||||
|
return Utils.imageCheck(fileName) || Utils.videoCheck(fileName);
|
||||||
|
});
|
||||||
|
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.UPDATE_RECORD_DETAILS, imageOrVideoRecords);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TextTranslation.OPEN_FILE_IN_NEW_TAB.key: {
|
||||||
|
openInNewTab(repoID, records[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TextTranslation.OPEN_FOLDER_IN_NEW_TAB.key: {
|
||||||
|
openParentFolder(records[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TextTranslation.OPEN_PARENT_FOLDER.key: {
|
||||||
|
openParentFolder(records[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TextTranslation.GENERATE_DESCRIPTION.key: {
|
||||||
|
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.GENERATE_DESCRIPTION, records[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TextTranslation.OCR.key: {
|
||||||
|
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.OCR, records[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, [repoID, eventBus, selectedRecordIds]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const unsubscribeSelectedFileIds = eventBus && eventBus.subscribe(EVENT_BUS_TYPE.SELECT_RECORDS, (ids, metadata) => {
|
||||||
|
metadataRef.current = metadata || [];
|
||||||
|
setSelectedRecordIds(ids);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubscribeSelectedFileIds && unsubscribeSelectedFileIds();
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const length = selectedRecordIds.length;
|
||||||
|
return (
|
||||||
|
<div className="selected-dirents-toolbar">
|
||||||
|
<span className="cur-view-path-btn px-2" onClick={unSelect}>
|
||||||
|
<span className="sf3-font-x-01 sf3-font mr-2" aria-label={gettext('Unselect')} title={gettext('Unselect')}></span>
|
||||||
|
<span>{length}{' '}{gettext('selected')}</span>
|
||||||
|
</span>
|
||||||
|
{(length === 1 && canModify) &&
|
||||||
|
<>
|
||||||
|
<span className="cur-view-path-btn" onClick={toggleMoveDialog}>
|
||||||
|
<span className="sf3-font-move1 sf3-font" aria-label={gettext('Move')} title={gettext('Move')}></span>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
{canModify &&
|
||||||
|
<span className="cur-view-path-btn" onClick={deleteRecords}>
|
||||||
|
<span className="sf3-font-delete1 sf3-font" aria-label={gettext('Delete')} title={gettext('Delete')}></span>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
{length > 0 && (
|
||||||
|
<ItemDropdownMenu
|
||||||
|
item={{}}
|
||||||
|
toggleClass={'cur-view-path-btn sf3-font-more-vertical sf3-font'}
|
||||||
|
onMenuItemClick={onMenuItemClick}
|
||||||
|
getMenuList={getMenuList}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
TableFilesToolbar.propTypes = {
|
||||||
|
repoID: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TableFilesToolbar;
|
@@ -38,6 +38,14 @@ export const EVENT_BUS_TYPE = {
|
|||||||
LOCAL_RECORD_DETAIL_CHANGED: 'local_record_detail_changed',
|
LOCAL_RECORD_DETAIL_CHANGED: 'local_record_detail_changed',
|
||||||
LOCAL_COLUMN_DATA_CHANGED: 'local_column_data_changed',
|
LOCAL_COLUMN_DATA_CHANGED: 'local_column_data_changed',
|
||||||
FOCUS_CANVAS: 'focus_canvas',
|
FOCUS_CANVAS: 'focus_canvas',
|
||||||
|
UPDATE_SELECTED_RECORD_IDS: 'update_selected_record_ids',
|
||||||
|
SELECT_RECORDS: 'select_records',
|
||||||
|
TOGGLE_MOVE_DIALOG: 'toggle_move_dialog',
|
||||||
|
MOVE_RECORD: 'move_record',
|
||||||
|
DELETE_RECORDS: 'delete_records',
|
||||||
|
UPDATE_RECORD_DETAILS: 'update_record_details',
|
||||||
|
GENERATE_DESCRIPTION: 'generate_description',
|
||||||
|
OCR: 'ocr',
|
||||||
|
|
||||||
// metadata
|
// metadata
|
||||||
RELOAD_DATA: 'reload_data',
|
RELOAD_DATA: 'reload_data',
|
||||||
|
@@ -8,10 +8,12 @@ import { Utils, validateName } from '../../utils/utils';
|
|||||||
import { useMetadata } from './metadata';
|
import { useMetadata } from './metadata';
|
||||||
import { useCollaborators } from './collaborators';
|
import { useCollaborators } from './collaborators';
|
||||||
import { getRowById } from '../../components/sf-table/utils/table';
|
import { getRowById } from '../../components/sf-table/utils/table';
|
||||||
import { getFileNameFromRecord, getParentDirFromRecord, getRecordIdFromRecord, getUniqueFileName } from '../utils/cell';
|
import { getFileNameFromRecord, getFileObjIdFromRecord, getParentDirFromRecord, getRecordIdFromRecord, getUniqueFileName } from '../utils/cell';
|
||||||
import { gettext } from '../../utils/constants';
|
import { gettext } from '../../utils/constants';
|
||||||
import { checkIsDir } from '../utils/row';
|
import { checkIsDir } from '../utils/row';
|
||||||
import { useTags } from '../../tag/hooks';
|
import { useTags } from '../../tag/hooks';
|
||||||
|
import { useMetadataAIOperations } from '../../hooks/metadata-ai-operation';
|
||||||
|
import { getColumnByKey } from '../utils/column';
|
||||||
|
|
||||||
const MetadataViewContext = React.createContext(null);
|
const MetadataViewContext = React.createContext(null);
|
||||||
|
|
||||||
@@ -23,6 +25,7 @@ export const MetadataViewProvider = ({
|
|||||||
deleteFilesCallback,
|
deleteFilesCallback,
|
||||||
moveFileCallback,
|
moveFileCallback,
|
||||||
copyFileCallback,
|
copyFileCallback,
|
||||||
|
toggleShowDirentToolbar,
|
||||||
...params
|
...params
|
||||||
}) => {
|
}) => {
|
||||||
const { modifyLocalFileTags } = useTags();
|
const { modifyLocalFileTags } = useTags();
|
||||||
@@ -35,6 +38,7 @@ export const MetadataViewProvider = ({
|
|||||||
|
|
||||||
const { collaborators } = useCollaborators();
|
const { collaborators } = useCollaborators();
|
||||||
const { isBeingBuilt, setIsBeingBuilt } = useMetadata();
|
const { isBeingBuilt, setIsBeingBuilt } = useMetadata();
|
||||||
|
const { onOCR, generateDescription, extractFilesDetails } = useMetadataAIOperations();
|
||||||
|
|
||||||
const tableChanged = useCallback(() => {
|
const tableChanged = useCallback(() => {
|
||||||
setMetadata(storeRef.current.data);
|
setMetadata(storeRef.current.data);
|
||||||
@@ -96,7 +100,7 @@ export const MetadataViewProvider = ({
|
|||||||
storeRef.current.modifyLocalColumnData(columnKey, newData, oldData);
|
storeRef.current.modifyLocalColumnData(columnKey, newData, oldData);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const modifyRecords = (rowIds, idRowUpdates, idOriginalRowUpdates, idOldRowData, idOriginalOldRowData, isCopyPaste = false, { success_callback, fail_callback } = {}) => {
|
const modifyRecords = useCallback((rowIds, idRowUpdates, idOriginalRowUpdates, idOldRowData, idOriginalOldRowData, isCopyPaste = false, { success_callback, fail_callback } = {}) => {
|
||||||
const isRename = storeRef.current.checkIsRenameFileOperator(rowIds, idOriginalRowUpdates);
|
const isRename = storeRef.current.checkIsRenameFileOperator(rowIds, idOriginalRowUpdates);
|
||||||
let newName = null;
|
let newName = null;
|
||||||
if (isRename) {
|
if (isRename) {
|
||||||
@@ -140,7 +144,7 @@ export const MetadataViewProvider = ({
|
|||||||
success_callback && success_callback();
|
success_callback && success_callback();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
}, [metadata, storeRef, renameFileCallback]);
|
||||||
|
|
||||||
const deleteRecords = (recordsIds, { success_callback, fail_callback } = {}) => {
|
const deleteRecords = (recordsIds, { success_callback, fail_callback } = {}) => {
|
||||||
if (!Array.isArray(recordsIds) || recordsIds.length === 0) return;
|
if (!Array.isArray(recordsIds) || recordsIds.length === 0) return;
|
||||||
@@ -284,6 +288,89 @@ export const MetadataViewProvider = ({
|
|||||||
storeRef.current.updateFileTags(data);
|
storeRef.current.updateFileTags(data);
|
||||||
}, [storeRef, modifyLocalFileTags]);
|
}, [storeRef, modifyLocalFileTags]);
|
||||||
|
|
||||||
|
const updateSelectedRecordIds = useCallback((ids) => {
|
||||||
|
toggleShowDirentToolbar(ids.length > 0);
|
||||||
|
setTimeout(() => {
|
||||||
|
window.sfMetadataContext && window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.SELECT_RECORDS, ids, metadata);
|
||||||
|
}, 0);
|
||||||
|
}, [metadata, toggleShowDirentToolbar]);
|
||||||
|
|
||||||
|
const updateRecordDetails = useCallback((records) => {
|
||||||
|
const recordObjIds = records.map(record => getFileObjIdFromRecord(record));
|
||||||
|
if (recordObjIds.length > 50) {
|
||||||
|
toaster.danger(gettext('Select up to 50 files'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recordIds = records.map(record => getRecordIdFromRecord(record));
|
||||||
|
extractFilesDetails(recordObjIds, {
|
||||||
|
success_callback: ({ details }) => {
|
||||||
|
const captureColumn = getColumnByKey(metadata.columns, PRIVATE_COLUMN_KEY.CAPTURE_TIME);
|
||||||
|
if (!captureColumn) return;
|
||||||
|
let idOldRecordData = {};
|
||||||
|
let idOriginalOldRecordData = {};
|
||||||
|
const captureColumnKey = PRIVATE_COLUMN_KEY.CAPTURE_TIME;
|
||||||
|
records.forEach(record => {
|
||||||
|
idOldRecordData[record[PRIVATE_COLUMN_KEY.ID]] = { [captureColumnKey]: record[captureColumnKey] };
|
||||||
|
idOriginalOldRecordData[record[PRIVATE_COLUMN_KEY.ID]] = { [captureColumnKey]: record[captureColumnKey] };
|
||||||
|
});
|
||||||
|
let idRecordUpdates = {};
|
||||||
|
let idOriginalRecordUpdates = {};
|
||||||
|
details.forEach(detail => {
|
||||||
|
const updateRecordId = detail[PRIVATE_COLUMN_KEY.ID];
|
||||||
|
idRecordUpdates[updateRecordId] = { [captureColumnKey]: detail[captureColumnKey] };
|
||||||
|
idOriginalRecordUpdates[updateRecordId] = { [captureColumnKey]: detail[captureColumnKey] };
|
||||||
|
});
|
||||||
|
modifyRecords({ recordIds, idRecordUpdates, idOriginalRecordUpdates, idOldRecordData, idOriginalOldRecordData });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [metadata, extractFilesDetails, modifyRecords]);
|
||||||
|
|
||||||
|
const updateRecordDescription = useCallback((record) => {
|
||||||
|
const parentDir = getParentDirFromRecord(record);
|
||||||
|
const fileName = getFileNameFromRecord(record);
|
||||||
|
if (!fileName || !parentDir) return;
|
||||||
|
const checkIsDescribableFile = Utils.isDescriptionSupportedFile(fileName);
|
||||||
|
if (!checkIsDescribableFile) return;
|
||||||
|
|
||||||
|
const descriptionColumnKey = PRIVATE_COLUMN_KEY.FILE_DESCRIPTION;
|
||||||
|
let idOldRecordData = { [record[PRIVATE_COLUMN_KEY.ID]]: { [descriptionColumnKey]: record[descriptionColumnKey] } };
|
||||||
|
let idOriginalOldRecordData = { [record[PRIVATE_COLUMN_KEY.ID]]: { [descriptionColumnKey]: record[descriptionColumnKey] } };
|
||||||
|
generateDescription({ parentDir, fileName }, {
|
||||||
|
success_callback: ({ description }) => {
|
||||||
|
const updateRecordId = record[PRIVATE_COLUMN_KEY.ID];
|
||||||
|
const recordIds = [updateRecordId];
|
||||||
|
let idRecordUpdates = {};
|
||||||
|
let idOriginalRecordUpdates = {};
|
||||||
|
idRecordUpdates[updateRecordId] = { [descriptionColumnKey]: description };
|
||||||
|
idOriginalRecordUpdates[updateRecordId] = { [descriptionColumnKey]: description };
|
||||||
|
modifyRecords({ recordIds, idRecordUpdates, idOriginalRecordUpdates, idOldRecordData, idOriginalOldRecordData });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [modifyRecords, generateDescription]);
|
||||||
|
|
||||||
|
const ocr = useCallback((record) => {
|
||||||
|
const parentDir = getParentDirFromRecord(record);
|
||||||
|
const fileName = getFileNameFromRecord(record);
|
||||||
|
if (!Utils.imageCheck(fileName)) return;
|
||||||
|
|
||||||
|
const ocrResultColumnKey = PRIVATE_COLUMN_KEY.OCR;
|
||||||
|
let idOldRecordData = { [record[PRIVATE_COLUMN_KEY.ID]]: { [ocrResultColumnKey]: record[ocrResultColumnKey] } };
|
||||||
|
let idOriginalOldRecordData = { [record[PRIVATE_COLUMN_KEY.ID]]: { [ocrResultColumnKey]: record[ocrResultColumnKey] } };
|
||||||
|
onOCR({ parentDir, fileName }, {
|
||||||
|
success_callback: ({ ocrResult }) => {
|
||||||
|
if (!ocrResult) return;
|
||||||
|
const updateRecordId = record[PRIVATE_COLUMN_KEY.ID];
|
||||||
|
const recordIds = [updateRecordId];
|
||||||
|
let idRecordUpdates = {};
|
||||||
|
let idOriginalRecordUpdates = {};
|
||||||
|
idRecordUpdates[updateRecordId] = { [ocrResultColumnKey]: ocrResult ? JSON.stringify(ocrResult) : null };
|
||||||
|
idOriginalRecordUpdates[updateRecordId] = { [ocrResultColumnKey]: ocrResult ? JSON.stringify(ocrResult) : null };
|
||||||
|
modifyRecords({ recordIds, idRecordUpdates, idOriginalRecordUpdates, idOldRecordData, idOriginalOldRecordData });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [modifyRecords, onOCR]);
|
||||||
|
|
||||||
// init
|
// init
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -316,6 +403,12 @@ export const MetadataViewProvider = ({
|
|||||||
const unsubscribeModifySettings = eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_SETTINGS, modifySettings);
|
const unsubscribeModifySettings = eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_SETTINGS, modifySettings);
|
||||||
const unsubscribeLocalRecordChanged = eventBus.subscribe(EVENT_BUS_TYPE.LOCAL_RECORD_CHANGED, updateLocalRecord);
|
const unsubscribeLocalRecordChanged = eventBus.subscribe(EVENT_BUS_TYPE.LOCAL_RECORD_CHANGED, updateLocalRecord);
|
||||||
const unsubscribeLocalColumnChanged = eventBus.subscribe(EVENT_BUS_TYPE.LOCAL_COLUMN_DATA_CHANGED, updateLocalColumnData);
|
const unsubscribeLocalColumnChanged = eventBus.subscribe(EVENT_BUS_TYPE.LOCAL_COLUMN_DATA_CHANGED, updateLocalColumnData);
|
||||||
|
const unsubscribeUpdateSelectedRecordIds = eventBus.subscribe(EVENT_BUS_TYPE.UPDATE_SELECTED_RECORD_IDS, updateSelectedRecordIds);
|
||||||
|
const unsubscribeMoveRecord = eventBus.subscribe(EVENT_BUS_TYPE.MOVE_RECORD, moveRecord);
|
||||||
|
const unsubscribeDeleteRecords = eventBus.subscribe(EVENT_BUS_TYPE.DELETE_RECORDS, deleteRecords);
|
||||||
|
const unsubscribeUpdateDetails = eventBus.subscribe(EVENT_BUS_TYPE.UPDATE_RECORD_DETAILS, updateRecordDetails);
|
||||||
|
const unsubscribeUpdateDescription = eventBus.subscribe(EVENT_BUS_TYPE.GENERATE_DESCRIPTION, updateRecordDescription);
|
||||||
|
const unsubscribeOCR = eventBus.subscribe(EVENT_BUS_TYPE.OCR, ocr);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (window.sfMetadataContext) {
|
if (window.sfMetadataContext) {
|
||||||
@@ -335,6 +428,12 @@ export const MetadataViewProvider = ({
|
|||||||
unsubscribeModifySettings();
|
unsubscribeModifySettings();
|
||||||
unsubscribeLocalRecordChanged();
|
unsubscribeLocalRecordChanged();
|
||||||
unsubscribeLocalColumnChanged();
|
unsubscribeLocalColumnChanged();
|
||||||
|
unsubscribeUpdateSelectedRecordIds();
|
||||||
|
unsubscribeMoveRecord();
|
||||||
|
unsubscribeDeleteRecords();
|
||||||
|
unsubscribeUpdateDetails();
|
||||||
|
unsubscribeUpdateDescription();
|
||||||
|
unsubscribeOCR();
|
||||||
delayReloadDataTimer.current && clearTimeout(delayReloadDataTimer.current);
|
delayReloadDataTimer.current && clearTimeout(delayReloadDataTimer.current);
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -370,6 +469,10 @@ export const MetadataViewProvider = ({
|
|||||||
updateFileTags,
|
updateFileTags,
|
||||||
addFolder: params.addFolder,
|
addFolder: params.addFolder,
|
||||||
updateCurrentPath: params.updateCurrentPath,
|
updateCurrentPath: params.updateCurrentPath,
|
||||||
|
updateSelectedRecordIds,
|
||||||
|
updateRecordDetails,
|
||||||
|
updateRecordDescription,
|
||||||
|
ocr,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import React, { useState, useRef, useCallback, useMemo } from 'react';
|
import React, { useState, useRef, useCallback, useMemo, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import toaster from '../../../../components/toast';
|
|
||||||
import { gettext } from '../../../../utils/constants';
|
import { gettext } from '../../../../utils/constants';
|
||||||
import { Utils } from '../../../../utils/utils';
|
import { Utils } from '../../../../utils/utils';
|
||||||
import { useMetadataView } from '../../../hooks/metadata-view';
|
import { useMetadataView } from '../../../hooks/metadata-view';
|
||||||
@@ -8,15 +7,12 @@ import { useMetadataStatus } from '../../../../hooks';
|
|||||||
import { getColumnByKey, isNameColumn } from '../../../utils/column';
|
import { getColumnByKey, isNameColumn } from '../../../utils/column';
|
||||||
import { checkIsDir } from '../../../utils/row';
|
import { checkIsDir } from '../../../utils/row';
|
||||||
import { EVENT_BUS_TYPE, EVENT_BUS_TYPE as METADATA_EVENT_BUS_TYPE, PRIVATE_COLUMN_KEY } from '../../../constants';
|
import { EVENT_BUS_TYPE, EVENT_BUS_TYPE as METADATA_EVENT_BUS_TYPE, PRIVATE_COLUMN_KEY } from '../../../constants';
|
||||||
import { getFileNameFromRecord, getParentDirFromRecord, getFileObjIdFromRecord,
|
import { getFileNameFromRecord, getParentDirFromRecord, getRecordIdFromRecord } from '../../../utils/cell';
|
||||||
getRecordIdFromRecord,
|
|
||||||
} from '../../../utils/cell';
|
|
||||||
import FileTagsDialog from '../../../components/dialog/file-tags-dialog';
|
import FileTagsDialog from '../../../components/dialog/file-tags-dialog';
|
||||||
import { openInNewTab, openParentFolder } from '../../../utils/file';
|
import { openInNewTab, openParentFolder } from '../../../utils/file';
|
||||||
import DeleteFolderDialog from '../../../../components/dialog/delete-folder-dialog';
|
import DeleteFolderDialog from '../../../../components/dialog/delete-folder-dialog';
|
||||||
import MoveDirent from '../../../../components/dialog/move-dirent-dialog';
|
import MoveDirent from '../../../../components/dialog/move-dirent-dialog';
|
||||||
import { Dirent } from '../../../../models';
|
import { Dirent } from '../../../../models';
|
||||||
import { useMetadataAIOperations } from '../../../../hooks/metadata-ai-operation';
|
|
||||||
import ContextMenuComponent from '../../../components/context-menu';
|
import ContextMenuComponent from '../../../components/context-menu';
|
||||||
import RowUtils from '../utils/row-utils';
|
import RowUtils from '../utils/row-utils';
|
||||||
|
|
||||||
@@ -38,8 +34,9 @@ const OPERATION = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ContextMenu = ({
|
const ContextMenu = ({
|
||||||
isGroupView, selectedRange, selectedPosition, recordMetrics, recordGetterByIndex, onClearSelected, onCopySelected, updateRecords,
|
isGroupView, selectedRange, selectedPosition, recordMetrics, recordGetterByIndex, onClearSelected, onCopySelected,
|
||||||
getTableContentRect, getTableCanvasContainerRect, deleteRecords, selectNone, updateFileTags, moveRecord, addFolder
|
getTableContentRect, getTableCanvasContainerRect, deleteRecords, selectNone, updateFileTags, moveRecord, addFolder, updateRecordDetails,
|
||||||
|
updateRecordDescription, ocr,
|
||||||
}) => {
|
}) => {
|
||||||
const currentRecord = useRef(null);
|
const currentRecord = useRef(null);
|
||||||
|
|
||||||
@@ -49,7 +46,6 @@ const ContextMenu = ({
|
|||||||
|
|
||||||
const { metadata } = useMetadataView();
|
const { metadata } = useMetadataView();
|
||||||
const { enableOCR } = useMetadataStatus();
|
const { enableOCR } = useMetadataStatus();
|
||||||
const { onOCR, generateDescription, extractFilesDetails } = useMetadataAIOperations();
|
|
||||||
|
|
||||||
const repoID = window.sfMetadataStore.repoId;
|
const repoID = window.sfMetadataStore.repoId;
|
||||||
|
|
||||||
@@ -223,87 +219,14 @@ const ContextMenu = ({
|
|||||||
return list;
|
return list;
|
||||||
}, [isGroupView, selectedPosition, recordMetrics, selectedRange, metadata, recordGetterByIndex, checkIsDescribableFile, enableOCR, getAbleDeleteRecords]);
|
}, [isGroupView, selectedPosition, recordMetrics, selectedRange, metadata, recordGetterByIndex, checkIsDescribableFile, enableOCR, getAbleDeleteRecords]);
|
||||||
|
|
||||||
const handelGenerateDescription = useCallback((record) => {
|
|
||||||
if (!checkCanModifyRow(record)) return;
|
|
||||||
const parentDir = getParentDirFromRecord(record);
|
|
||||||
const fileName = getFileNameFromRecord(record);
|
|
||||||
if (!fileName || !parentDir) return;
|
|
||||||
const checkIsDescribableFile = Utils.isDescriptionSupportedFile(fileName);
|
|
||||||
if (!checkIsDescribableFile) return;
|
|
||||||
|
|
||||||
const descriptionColumnKey = PRIVATE_COLUMN_KEY.FILE_DESCRIPTION;
|
|
||||||
let idOldRecordData = { [record[PRIVATE_COLUMN_KEY.ID]]: { [descriptionColumnKey]: record[descriptionColumnKey] } };
|
|
||||||
let idOriginalOldRecordData = { [record[PRIVATE_COLUMN_KEY.ID]]: { [descriptionColumnKey]: record[descriptionColumnKey] } };
|
|
||||||
generateDescription({ parentDir, fileName }, {
|
|
||||||
success_callback: ({ description }) => {
|
|
||||||
const updateRecordId = record[PRIVATE_COLUMN_KEY.ID];
|
|
||||||
const recordIds = [updateRecordId];
|
|
||||||
let idRecordUpdates = {};
|
|
||||||
let idOriginalRecordUpdates = {};
|
|
||||||
idRecordUpdates[updateRecordId] = { [descriptionColumnKey]: description };
|
|
||||||
idOriginalRecordUpdates[updateRecordId] = { [descriptionColumnKey]: description };
|
|
||||||
updateRecords({ recordIds, idRecordUpdates, idOriginalRecordUpdates, idOldRecordData, idOriginalOldRecordData });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [updateRecords, generateDescription]);
|
|
||||||
|
|
||||||
const toggleFileTagsRecord = useCallback((record = null) => {
|
const toggleFileTagsRecord = useCallback((record = null) => {
|
||||||
setFileTagsRecord(record);
|
setFileTagsRecord(record);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const ocr = useCallback((record) => {
|
const handleMoveRecord = useCallback((...params) => {
|
||||||
if (!checkCanModifyRow(record)) return;
|
selectNone();
|
||||||
const parentDir = getParentDirFromRecord(record);
|
moveRecord && moveRecord(...params);
|
||||||
const fileName = getFileNameFromRecord(record);
|
}, [moveRecord, selectNone]);
|
||||||
if (!Utils.imageCheck(fileName)) return;
|
|
||||||
|
|
||||||
const ocrResultColumnKey = PRIVATE_COLUMN_KEY.OCR;
|
|
||||||
let idOldRecordData = { [record[PRIVATE_COLUMN_KEY.ID]]: { [ocrResultColumnKey]: record[ocrResultColumnKey] } };
|
|
||||||
let idOriginalOldRecordData = { [record[PRIVATE_COLUMN_KEY.ID]]: { [ocrResultColumnKey]: record[ocrResultColumnKey] } };
|
|
||||||
onOCR({ parentDir, fileName }, {
|
|
||||||
success_callback: ({ ocrResult }) => {
|
|
||||||
if (!ocrResult) return;
|
|
||||||
const updateRecordId = record[PRIVATE_COLUMN_KEY.ID];
|
|
||||||
const recordIds = [updateRecordId];
|
|
||||||
let idRecordUpdates = {};
|
|
||||||
let idOriginalRecordUpdates = {};
|
|
||||||
idRecordUpdates[updateRecordId] = { [ocrResultColumnKey]: ocrResult ? JSON.stringify(ocrResult) : null };
|
|
||||||
idOriginalRecordUpdates[updateRecordId] = { [ocrResultColumnKey]: ocrResult ? JSON.stringify(ocrResult) : null };
|
|
||||||
updateRecords({ recordIds, idRecordUpdates, idOriginalRecordUpdates, idOldRecordData, idOriginalOldRecordData });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}, [updateRecords, onOCR]);
|
|
||||||
|
|
||||||
const updateFileDetails = useCallback((records) => {
|
|
||||||
const recordObjIds = records.map(record => getFileObjIdFromRecord(record));
|
|
||||||
if (recordObjIds.length > 50) {
|
|
||||||
toaster.danger(gettext('Select up to 50 files'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const recordIds = records.map(record => getRecordIdFromRecord(record));
|
|
||||||
extractFilesDetails(recordObjIds, {
|
|
||||||
success_callback: ({ details }) => {
|
|
||||||
const captureColumn = getColumnByKey(metadata.columns, PRIVATE_COLUMN_KEY.CAPTURE_TIME);
|
|
||||||
if (!captureColumn) return;
|
|
||||||
let idOldRecordData = {};
|
|
||||||
let idOriginalOldRecordData = {};
|
|
||||||
const captureColumnKey = PRIVATE_COLUMN_KEY.CAPTURE_TIME;
|
|
||||||
records.forEach(record => {
|
|
||||||
idOldRecordData[record[PRIVATE_COLUMN_KEY.ID]] = { [captureColumnKey]: record[captureColumnKey] };
|
|
||||||
idOriginalOldRecordData[record[PRIVATE_COLUMN_KEY.ID]] = { [captureColumnKey]: record[captureColumnKey] };
|
|
||||||
});
|
|
||||||
let idRecordUpdates = {};
|
|
||||||
let idOriginalRecordUpdates = {};
|
|
||||||
details.forEach(detail => {
|
|
||||||
const updateRecordId = detail[PRIVATE_COLUMN_KEY.ID];
|
|
||||||
idRecordUpdates[updateRecordId] = { [captureColumnKey]: detail[captureColumnKey] };
|
|
||||||
idOriginalRecordUpdates[updateRecordId] = { [captureColumnKey]: detail[captureColumnKey] };
|
|
||||||
});
|
|
||||||
updateRecords({ recordIds, idRecordUpdates, idOriginalRecordUpdates, idOldRecordData, idOriginalOldRecordData });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [metadata, extractFilesDetails, updateRecords]);
|
|
||||||
|
|
||||||
const handleOptionClick = useCallback((option, event) => {
|
const handleOptionClick = useCallback((option, event) => {
|
||||||
switch (option.value) {
|
switch (option.value) {
|
||||||
@@ -329,7 +252,7 @@ const ContextMenu = ({
|
|||||||
case OPERATION.GENERATE_DESCRIPTION: {
|
case OPERATION.GENERATE_DESCRIPTION: {
|
||||||
const { record } = option;
|
const { record } = option;
|
||||||
if (!record) break;
|
if (!record) break;
|
||||||
handelGenerateDescription(record);
|
updateRecordDescription(record);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OPERATION.FILE_TAGS: {
|
case OPERATION.FILE_TAGS: {
|
||||||
@@ -374,12 +297,12 @@ const ContextMenu = ({
|
|||||||
}
|
}
|
||||||
case OPERATION.FILE_DETAILS: {
|
case OPERATION.FILE_DETAILS: {
|
||||||
const { records } = option;
|
const { records } = option;
|
||||||
updateFileDetails(records);
|
updateRecordDetails(records);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OPERATION.FILE_DETAIL: {
|
case OPERATION.FILE_DETAIL: {
|
||||||
const { record } = option;
|
const { record } = option;
|
||||||
updateFileDetails([record]);
|
updateRecordDetails([record]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OPERATION.MOVE: {
|
case OPERATION.MOVE: {
|
||||||
@@ -392,7 +315,15 @@ const ContextMenu = ({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [repoID, onCopySelected, onClearSelected, handelGenerateDescription, ocr, deleteRecords, toggleDeleteFolderDialog, selectNone, updateFileDetails, toggleFileTagsRecord, toggleMoveDialog]);
|
}, [repoID, onCopySelected, onClearSelected, updateRecordDescription, ocr, deleteRecords, toggleDeleteFolderDialog, selectNone, updateRecordDetails, toggleFileTagsRecord, toggleMoveDialog]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const unsubscribeToggleMoveDialog = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.TOGGLE_MOVE_DIALOG, toggleMoveDialog);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubscribeToggleMoveDialog();
|
||||||
|
};
|
||||||
|
}, [toggleMoveDialog]);
|
||||||
|
|
||||||
const currentRecordId = getRecordIdFromRecord(currentRecord.current);
|
const currentRecordId = getRecordIdFromRecord(currentRecord.current);
|
||||||
const fileName = getFileNameFromRecord(currentRecord.current);
|
const fileName = getFileNameFromRecord(currentRecord.current);
|
||||||
@@ -424,7 +355,7 @@ const ContextMenu = ({
|
|||||||
repoID={repoID}
|
repoID={repoID}
|
||||||
dirent={new Dirent({ name: fileName })}
|
dirent={new Dirent({ name: fileName })}
|
||||||
isMultipleOperation={false}
|
isMultipleOperation={false}
|
||||||
onItemMove={(...params) => moveRecord(currentRecordId, ...params)}
|
onItemMove={(...params) => handleMoveRecord(currentRecordId, ...params)}
|
||||||
onCancelMove={toggleMoveDialog}
|
onCancelMove={toggleMoveDialog}
|
||||||
onAddFolder={addFolder}
|
onAddFolder={addFolder}
|
||||||
/>
|
/>
|
||||||
|
@@ -27,7 +27,11 @@ const Table = () => {
|
|||||||
insertColumn,
|
insertColumn,
|
||||||
updateFileTags,
|
updateFileTags,
|
||||||
moveRecord,
|
moveRecord,
|
||||||
addFolder
|
addFolder,
|
||||||
|
updateSelectedRecordIds,
|
||||||
|
updateRecordDetails,
|
||||||
|
updateRecordDescription,
|
||||||
|
ocr,
|
||||||
} = useMetadataView();
|
} = useMetadataView();
|
||||||
const containerRef = useRef(null);
|
const containerRef = useRef(null);
|
||||||
|
|
||||||
@@ -176,6 +180,10 @@ const Table = () => {
|
|||||||
onGridKeyUp={onHotKeyUp}
|
onGridKeyUp={onHotKeyUp}
|
||||||
moveRecord={moveRecord}
|
moveRecord={moveRecord}
|
||||||
addFolder={addFolder}
|
addFolder={addFolder}
|
||||||
|
updateSelectedRecordIds={updateSelectedRecordIds}
|
||||||
|
updateRecordDetails={updateRecordDetails}
|
||||||
|
updateRecordDescription={updateRecordDescription}
|
||||||
|
ocr={ocr}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@@ -46,10 +46,6 @@ const TableMain = ({
|
|||||||
modifyRecord && modifyRecord(rowId, updates, oldRowData, originalUpdates, originalOldRowData);
|
modifyRecord && modifyRecord(rowId, updates, oldRowData, originalUpdates, originalOldRowData);
|
||||||
}, [modifyRecord]);
|
}, [modifyRecord]);
|
||||||
|
|
||||||
const updateRecords = useCallback(({ recordIds, idRecordUpdates, idOriginalRecordUpdates, idOldRecordData, idOriginalOldRecordData, isCopyPaste = false }) => {
|
|
||||||
modifyRecords && modifyRecords(recordIds, idRecordUpdates, idOriginalRecordUpdates, idOldRecordData, idOriginalOldRecordData, isCopyPaste);
|
|
||||||
}, [modifyRecords]);
|
|
||||||
|
|
||||||
const handelInsertColumn = useCallback((name, type, { key, data }) => {
|
const handelInsertColumn = useCallback((name, type, { key, data }) => {
|
||||||
insertColumn && insertColumn(name, type, { key, data });
|
insertColumn && insertColumn(name, type, { key, data });
|
||||||
}, [insertColumn]);
|
}, [insertColumn]);
|
||||||
@@ -74,7 +70,6 @@ const TableMain = ({
|
|||||||
paste={paste}
|
paste={paste}
|
||||||
groupOffsetLeft={groupOffset}
|
groupOffsetLeft={groupOffset}
|
||||||
modifyRecord={updateRecord}
|
modifyRecord={updateRecord}
|
||||||
updateRecords={updateRecords}
|
|
||||||
deleteRecords={props.deleteRecords}
|
deleteRecords={props.deleteRecords}
|
||||||
getCopiedRecordsAndColumnsFromRange={getCopiedRecordsAndColumnsFromRange}
|
getCopiedRecordsAndColumnsFromRange={getCopiedRecordsAndColumnsFromRange}
|
||||||
recordGetterById={recordGetterById}
|
recordGetterById={recordGetterById}
|
||||||
|
@@ -564,6 +564,7 @@ class RecordsBody extends Component {
|
|||||||
getTableCanvasContainerRect={this.props.getTableCanvasContainerRect}
|
getTableCanvasContainerRect={this.props.getTableCanvasContainerRect}
|
||||||
updateFileTags={this.props.updateFileTags}
|
updateFileTags={this.props.updateFileTags}
|
||||||
deleteRecords={this.props.deleteRecords}
|
deleteRecords={this.props.deleteRecords}
|
||||||
|
moveRecord={this.props.moveRecord}
|
||||||
/>
|
/>
|
||||||
<div className="sf-metadata-result-table" style={{ width: this.props.totalWidth + SEQUENCE_COLUMN_WIDTH }} ref={this.setResultRef}>
|
<div className="sf-metadata-result-table" style={{ width: this.props.totalWidth + SEQUENCE_COLUMN_WIDTH }} ref={this.setResultRef}>
|
||||||
{this.renderRecords()}
|
{this.renderRecords()}
|
||||||
@@ -631,6 +632,8 @@ RecordsBody.propTypes = {
|
|||||||
cacheDownloadFilesProps: PropTypes.func,
|
cacheDownloadFilesProps: PropTypes.func,
|
||||||
onCellContextMenu: PropTypes.func,
|
onCellContextMenu: PropTypes.func,
|
||||||
getTableCanvasContainerRect: PropTypes.func,
|
getTableCanvasContainerRect: PropTypes.func,
|
||||||
|
moveRecord: PropTypes.func,
|
||||||
|
addFolder: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RecordsBody;
|
export default RecordsBody;
|
||||||
|
@@ -487,6 +487,8 @@ class Records extends Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
recordMetrics: updatedRecordMetrics,
|
recordMetrics: updatedRecordMetrics,
|
||||||
});
|
});
|
||||||
|
const ids = Object.keys(updatedRecordMetrics.idSelectedRecordMap);
|
||||||
|
this.props.updateSelectedRecordIds(ids);
|
||||||
};
|
};
|
||||||
|
|
||||||
selectRecordsById = (recordIds) => {
|
selectRecordsById = (recordIds) => {
|
||||||
@@ -500,6 +502,7 @@ class Records extends Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
recordMetrics: updatedRecordMetrics,
|
recordMetrics: updatedRecordMetrics,
|
||||||
});
|
});
|
||||||
|
this.props.updateSelectedRecordIds(recordIds);
|
||||||
};
|
};
|
||||||
|
|
||||||
deselectRecord = (recordId) => {
|
deselectRecord = (recordId) => {
|
||||||
@@ -512,6 +515,8 @@ class Records extends Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
recordMetrics: updatedRecordMetrics,
|
recordMetrics: updatedRecordMetrics,
|
||||||
});
|
});
|
||||||
|
const ids = Object.keys(updatedRecordMetrics.idSelectedRecordMap);
|
||||||
|
this.props.updateSelectedRecordIds(ids);
|
||||||
};
|
};
|
||||||
|
|
||||||
selectAllRecords = () => {
|
selectAllRecords = () => {
|
||||||
@@ -539,6 +544,7 @@ class Records extends Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
recordMetrics: updatedRecordMetrics,
|
recordMetrics: updatedRecordMetrics,
|
||||||
});
|
});
|
||||||
|
this.props.updateSelectedRecordIds(selectedRowIds);
|
||||||
};
|
};
|
||||||
|
|
||||||
onDeselectAllRecords = () => {
|
onDeselectAllRecords = () => {
|
||||||
@@ -552,6 +558,7 @@ class Records extends Component {
|
|||||||
recordMetrics: updatedRecordMetrics,
|
recordMetrics: updatedRecordMetrics,
|
||||||
lastRowIdxUiSelected: { groupRecordIndex: -1, recordIndex: -1 },
|
lastRowIdxUiSelected: { groupRecordIndex: -1, recordIndex: -1 },
|
||||||
});
|
});
|
||||||
|
this.props.updateSelectedRecordIds([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
hasSelectedCell = ({ groupRecordIndex, recordIndex }, selectedPosition) => {
|
hasSelectedCell = ({ groupRecordIndex, recordIndex }, selectedPosition) => {
|
||||||
@@ -609,6 +616,7 @@ class Records extends Component {
|
|||||||
const recordId = record._id;
|
const recordId = record._id;
|
||||||
if (!RecordMetrics.isRecordSelected(recordId, recordMetrics)) {
|
if (!RecordMetrics.isRecordSelected(recordId, recordMetrics)) {
|
||||||
this.setState({ recordMetrics: this.createRowMetrics() });
|
this.setState({ recordMetrics: this.createRowMetrics() });
|
||||||
|
this.props.updateSelectedRecordIds([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// select cell when click out of selectRange
|
// select cell when click out of selectRange
|
||||||
@@ -633,10 +641,13 @@ class Records extends Component {
|
|||||||
<ContextMenu
|
<ContextMenu
|
||||||
isGroupView={isGroupView}
|
isGroupView={isGroupView}
|
||||||
recordGetterByIndex={this.props.recordGetterByIndex}
|
recordGetterByIndex={this.props.recordGetterByIndex}
|
||||||
updateRecords={this.props.updateRecords}
|
|
||||||
deleteRecords={this.props.deleteRecords}
|
deleteRecords={this.props.deleteRecords}
|
||||||
moveRecord={this.props.moveRecord}
|
moveRecord={this.props.moveRecord}
|
||||||
addFolder={this.props.addFolder}
|
addFolder={this.props.addFolder}
|
||||||
|
selectNone={this.selectNone}
|
||||||
|
updateRecordDetails={this.props.updateRecordDetails}
|
||||||
|
updateRecordDescription={this.props.updateRecordDescription}
|
||||||
|
ocr={this.props.ocr}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
hasSelectedRecord: this.hasSelectedRecord(),
|
hasSelectedRecord: this.hasSelectedRecord(),
|
||||||
@@ -651,6 +662,8 @@ class Records extends Component {
|
|||||||
cacheScrollTop: this.storeScrollTop,
|
cacheScrollTop: this.storeScrollTop,
|
||||||
onCellContextMenu: this.onCellContextMenu,
|
onCellContextMenu: this.onCellContextMenu,
|
||||||
getTableCanvasContainerRect: this.getTableCanvasContainerRect,
|
getTableCanvasContainerRect: this.getTableCanvasContainerRect,
|
||||||
|
moveRecord: this.props.moveRecord,
|
||||||
|
addFolder: this.props.addFolder
|
||||||
};
|
};
|
||||||
if (this.props.isGroupView) {
|
if (this.props.isGroupView) {
|
||||||
return (
|
return (
|
||||||
@@ -766,7 +779,6 @@ Records.propTypes = {
|
|||||||
getTableContentRect: PropTypes.func,
|
getTableContentRect: PropTypes.func,
|
||||||
scrollToLoadMore: PropTypes.func,
|
scrollToLoadMore: PropTypes.func,
|
||||||
updateRecord: PropTypes.func,
|
updateRecord: PropTypes.func,
|
||||||
updateRecords: PropTypes.func,
|
|
||||||
recordGetterById: PropTypes.func,
|
recordGetterById: PropTypes.func,
|
||||||
recordGetterByIndex: PropTypes.func,
|
recordGetterByIndex: PropTypes.func,
|
||||||
loadAll: PropTypes.func,
|
loadAll: PropTypes.func,
|
||||||
@@ -779,6 +791,7 @@ Records.propTypes = {
|
|||||||
getCopiedRecordsAndColumnsFromRange: PropTypes.func,
|
getCopiedRecordsAndColumnsFromRange: PropTypes.func,
|
||||||
moveRecord: PropTypes.func,
|
moveRecord: PropTypes.func,
|
||||||
addFolder: PropTypes.func,
|
addFolder: PropTypes.func,
|
||||||
|
updateSelectedRecordIds: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Records;
|
export default Records;
|
||||||
|
@@ -31,6 +31,7 @@ import Detail from '../../components/dirent-detail';
|
|||||||
import DirColumnView from '../../components/dir-view-mode/dir-column-view';
|
import DirColumnView from '../../components/dir-view-mode/dir-column-view';
|
||||||
import SelectedDirentsToolbar from '../../components/toolbar/selected-dirents-toolbar';
|
import SelectedDirentsToolbar from '../../components/toolbar/selected-dirents-toolbar';
|
||||||
import TagFilesToolbar from '../../components/toolbar/tag-files-toolbar';
|
import TagFilesToolbar from '../../components/toolbar/tag-files-toolbar';
|
||||||
|
import TableFilesToolbar from '../../components/toolbar/table-files-toolbar';
|
||||||
|
|
||||||
import '../../css/lib-content-view.css';
|
import '../../css/lib-content-view.css';
|
||||||
|
|
||||||
@@ -2277,9 +2278,11 @@ class LibContentView extends React.Component {
|
|||||||
'animation-children': isDirentSelected
|
'animation-children': isDirentSelected
|
||||||
})}>
|
})}>
|
||||||
{isDirentSelected ? (
|
{isDirentSelected ? (
|
||||||
this.state.currentMode === TAGS_MODE ?
|
this.state.currentMode === TAGS_MODE ? (
|
||||||
<TagFilesToolbar currentRepoInfo={this.state.currentRepoInfo} />
|
<TagFilesToolbar currentRepoInfo={this.state.currentRepoInfo} />
|
||||||
:
|
) : this.state.currentMode === METADATA_MODE ? (
|
||||||
|
<TableFilesToolbar repoID={this.props.repoID} />
|
||||||
|
) : (
|
||||||
<SelectedDirentsToolbar
|
<SelectedDirentsToolbar
|
||||||
repoID={this.props.repoID}
|
repoID={this.props.repoID}
|
||||||
path={this.state.path}
|
path={this.state.path}
|
||||||
@@ -2305,6 +2308,7 @@ class LibContentView extends React.Component {
|
|||||||
onItemConvert={this.onConvertItem}
|
onItemConvert={this.onConvertItem}
|
||||||
onAddFolder={this.onAddFolder}
|
onAddFolder={this.onAddFolder}
|
||||||
/>
|
/>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<CurDirPath
|
<CurDirPath
|
||||||
currentRepoInfo={this.state.currentRepoInfo}
|
currentRepoInfo={this.state.currentRepoInfo}
|
||||||
|
@@ -197,7 +197,65 @@ const TextTranslation = {
|
|||||||
ADD_VIEW: {
|
ADD_VIEW: {
|
||||||
key: 'ADD_VIEW',
|
key: 'ADD_VIEW',
|
||||||
value: gettext('Add view')
|
value: gettext('Add view')
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// table view
|
||||||
|
OPEN_FILE_IN_NEW_TAB: {
|
||||||
|
key: 'Open file in new tab',
|
||||||
|
value: gettext('Open file in new tab'),
|
||||||
|
},
|
||||||
|
OPEN_FOLDER_IN_NEW_TAB: {
|
||||||
|
key: 'Open folder in new tab',
|
||||||
|
value: gettext('Open folder in new tab'),
|
||||||
|
},
|
||||||
|
OPEN_PARENT_FOLDER: {
|
||||||
|
key: 'Open parent folder',
|
||||||
|
value: gettext('Open parent folder'),
|
||||||
|
},
|
||||||
|
EXTRACT_FILE_DETAIL: {
|
||||||
|
key: 'Extract file detail',
|
||||||
|
value: gettext('Extract file detail'),
|
||||||
|
},
|
||||||
|
EXTRACT_FILE_DETAILS: {
|
||||||
|
key: 'Extract file details',
|
||||||
|
value: gettext('Extract file details'),
|
||||||
|
},
|
||||||
|
DELETE_FILE: {
|
||||||
|
key: 'Delete file',
|
||||||
|
value: gettext('Delete file'),
|
||||||
|
},
|
||||||
|
DELETE_FOLDER: {
|
||||||
|
key: 'Delete folder',
|
||||||
|
value: gettext('Delete folder'),
|
||||||
|
},
|
||||||
|
MOVE_FILE: {
|
||||||
|
key: 'Move file',
|
||||||
|
value: gettext('Move file'),
|
||||||
|
},
|
||||||
|
MOVE_FOLDER: {
|
||||||
|
key: 'Move folder',
|
||||||
|
value: gettext('Move folder'),
|
||||||
|
},
|
||||||
|
RENAME_FILE: {
|
||||||
|
key: 'Rename file',
|
||||||
|
value: gettext('Rename file'),
|
||||||
|
},
|
||||||
|
RENAME_FOLDER: {
|
||||||
|
key: 'Rename folder',
|
||||||
|
value: gettext('Rename folder'),
|
||||||
|
},
|
||||||
|
GENERATE_DESCRIPTION: {
|
||||||
|
key: 'Generate description',
|
||||||
|
value: gettext('Generate description'),
|
||||||
|
},
|
||||||
|
GENERATE_TAGS: {
|
||||||
|
key: 'Generate tags',
|
||||||
|
value: gettext('Generate file tags'),
|
||||||
|
},
|
||||||
|
OCR: {
|
||||||
|
key: 'OCR',
|
||||||
|
value: gettext('OCR'),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TextTranslation;
|
export default TextTranslation;
|
||||||
|
Reference in New Issue
Block a user