diff --git a/frontend/src/components/dir-view-mode/dir-column-view.js b/frontend/src/components/dir-view-mode/dir-column-view.js
index 5294a7c6d0..aef5a78696 100644
--- a/frontend/src/components/dir-view-mode/dir-column-view.js
+++ b/frontend/src/components/dir-view-mode/dir-column-view.js
@@ -216,6 +216,7 @@ class DirColumnView extends React.Component {
updateCurrentDirent={this.props.updateCurrentDirent}
showDirentDetail={this.props.showDirentDetail}
updateCurrentPath={this.props.updateCurrentPath}
+ toggleShowDirentToolbar={this.props.toggleShowDirentToolbar}
/>
)}
{currentMode === TAGS_MODE && (
diff --git a/frontend/src/components/toolbar/table-files-toolbar.js b/frontend/src/components/toolbar/table-files-toolbar.js
new file mode 100644
index 0000000000..e9dc4b563d
--- /dev/null
+++ b/frontend/src/components/toolbar/table-files-toolbar.js
@@ -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 (
+
+
+
+ {length}{' '}{gettext('selected')}
+
+ {(length === 1 && canModify) &&
+ <>
+
+
+
+ >
+ }
+ {canModify &&
+
+
+
+ }
+ {length > 0 && (
+
+ )}
+
+ );
+};
+
+TableFilesToolbar.propTypes = {
+ repoID: PropTypes.string.isRequired,
+};
+
+export default TableFilesToolbar;
diff --git a/frontend/src/metadata/constants/event-bus-type.js b/frontend/src/metadata/constants/event-bus-type.js
index c0ceb1814c..f2df3be297 100644
--- a/frontend/src/metadata/constants/event-bus-type.js
+++ b/frontend/src/metadata/constants/event-bus-type.js
@@ -38,6 +38,14 @@ export const EVENT_BUS_TYPE = {
LOCAL_RECORD_DETAIL_CHANGED: 'local_record_detail_changed',
LOCAL_COLUMN_DATA_CHANGED: 'local_column_data_changed',
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
RELOAD_DATA: 'reload_data',
diff --git a/frontend/src/metadata/hooks/metadata-view.js b/frontend/src/metadata/hooks/metadata-view.js
index f663a2281e..52e9ffdd21 100644
--- a/frontend/src/metadata/hooks/metadata-view.js
+++ b/frontend/src/metadata/hooks/metadata-view.js
@@ -8,10 +8,12 @@ import { Utils, validateName } from '../../utils/utils';
import { useMetadata } from './metadata';
import { useCollaborators } from './collaborators';
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 { checkIsDir } from '../utils/row';
import { useTags } from '../../tag/hooks';
+import { useMetadataAIOperations } from '../../hooks/metadata-ai-operation';
+import { getColumnByKey } from '../utils/column';
const MetadataViewContext = React.createContext(null);
@@ -23,6 +25,7 @@ export const MetadataViewProvider = ({
deleteFilesCallback,
moveFileCallback,
copyFileCallback,
+ toggleShowDirentToolbar,
...params
}) => {
const { modifyLocalFileTags } = useTags();
@@ -35,6 +38,7 @@ export const MetadataViewProvider = ({
const { collaborators } = useCollaborators();
const { isBeingBuilt, setIsBeingBuilt } = useMetadata();
+ const { onOCR, generateDescription, extractFilesDetails } = useMetadataAIOperations();
const tableChanged = useCallback(() => {
setMetadata(storeRef.current.data);
@@ -96,7 +100,7 @@ export const MetadataViewProvider = ({
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);
let newName = null;
if (isRename) {
@@ -140,7 +144,7 @@ export const MetadataViewProvider = ({
success_callback && success_callback();
},
});
- };
+ }, [metadata, storeRef, renameFileCallback]);
const deleteRecords = (recordsIds, { success_callback, fail_callback } = {}) => {
if (!Array.isArray(recordsIds) || recordsIds.length === 0) return;
@@ -284,6 +288,89 @@ export const MetadataViewProvider = ({
storeRef.current.updateFileTags(data);
}, [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
useEffect(() => {
setLoading(true);
@@ -316,6 +403,12 @@ export const MetadataViewProvider = ({
const unsubscribeModifySettings = eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_SETTINGS, modifySettings);
const unsubscribeLocalRecordChanged = eventBus.subscribe(EVENT_BUS_TYPE.LOCAL_RECORD_CHANGED, updateLocalRecord);
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 () => {
if (window.sfMetadataContext) {
@@ -335,6 +428,12 @@ export const MetadataViewProvider = ({
unsubscribeModifySettings();
unsubscribeLocalRecordChanged();
unsubscribeLocalColumnChanged();
+ unsubscribeUpdateSelectedRecordIds();
+ unsubscribeMoveRecord();
+ unsubscribeDeleteRecords();
+ unsubscribeUpdateDetails();
+ unsubscribeUpdateDescription();
+ unsubscribeOCR();
delayReloadDataTimer.current && clearTimeout(delayReloadDataTimer.current);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -370,6 +469,10 @@ export const MetadataViewProvider = ({
updateFileTags,
addFolder: params.addFolder,
updateCurrentPath: params.updateCurrentPath,
+ updateSelectedRecordIds,
+ updateRecordDetails,
+ updateRecordDescription,
+ ocr,
}}
>
{children}
diff --git a/frontend/src/metadata/views/table/context-menu/index.js b/frontend/src/metadata/views/table/context-menu/index.js
index 87b03a69c7..98df10fa79 100644
--- a/frontend/src/metadata/views/table/context-menu/index.js
+++ b/frontend/src/metadata/views/table/context-menu/index.js
@@ -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 toaster from '../../../../components/toast';
import { gettext } from '../../../../utils/constants';
import { Utils } from '../../../../utils/utils';
import { useMetadataView } from '../../../hooks/metadata-view';
@@ -8,15 +7,12 @@ import { useMetadataStatus } from '../../../../hooks';
import { getColumnByKey, isNameColumn } from '../../../utils/column';
import { checkIsDir } from '../../../utils/row';
import { EVENT_BUS_TYPE, EVENT_BUS_TYPE as METADATA_EVENT_BUS_TYPE, PRIVATE_COLUMN_KEY } from '../../../constants';
-import { getFileNameFromRecord, getParentDirFromRecord, getFileObjIdFromRecord,
- getRecordIdFromRecord,
-} from '../../../utils/cell';
+import { getFileNameFromRecord, getParentDirFromRecord, getRecordIdFromRecord } from '../../../utils/cell';
import FileTagsDialog from '../../../components/dialog/file-tags-dialog';
import { openInNewTab, openParentFolder } from '../../../utils/file';
import DeleteFolderDialog from '../../../../components/dialog/delete-folder-dialog';
import MoveDirent from '../../../../components/dialog/move-dirent-dialog';
import { Dirent } from '../../../../models';
-import { useMetadataAIOperations } from '../../../../hooks/metadata-ai-operation';
import ContextMenuComponent from '../../../components/context-menu';
import RowUtils from '../utils/row-utils';
@@ -38,8 +34,9 @@ const OPERATION = {
};
const ContextMenu = ({
- isGroupView, selectedRange, selectedPosition, recordMetrics, recordGetterByIndex, onClearSelected, onCopySelected, updateRecords,
- getTableContentRect, getTableCanvasContainerRect, deleteRecords, selectNone, updateFileTags, moveRecord, addFolder
+ isGroupView, selectedRange, selectedPosition, recordMetrics, recordGetterByIndex, onClearSelected, onCopySelected,
+ getTableContentRect, getTableCanvasContainerRect, deleteRecords, selectNone, updateFileTags, moveRecord, addFolder, updateRecordDetails,
+ updateRecordDescription, ocr,
}) => {
const currentRecord = useRef(null);
@@ -49,7 +46,6 @@ const ContextMenu = ({
const { metadata } = useMetadataView();
const { enableOCR } = useMetadataStatus();
- const { onOCR, generateDescription, extractFilesDetails } = useMetadataAIOperations();
const repoID = window.sfMetadataStore.repoId;
@@ -223,87 +219,14 @@ const ContextMenu = ({
return list;
}, [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) => {
setFileTagsRecord(record);
}, []);
- const ocr = useCallback((record) => {
- if (!checkCanModifyRow(record)) return;
- 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 };
- 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 handleMoveRecord = useCallback((...params) => {
+ selectNone();
+ moveRecord && moveRecord(...params);
+ }, [moveRecord, selectNone]);
const handleOptionClick = useCallback((option, event) => {
switch (option.value) {
@@ -329,7 +252,7 @@ const ContextMenu = ({
case OPERATION.GENERATE_DESCRIPTION: {
const { record } = option;
if (!record) break;
- handelGenerateDescription(record);
+ updateRecordDescription(record);
break;
}
case OPERATION.FILE_TAGS: {
@@ -374,12 +297,12 @@ const ContextMenu = ({
}
case OPERATION.FILE_DETAILS: {
const { records } = option;
- updateFileDetails(records);
+ updateRecordDetails(records);
break;
}
case OPERATION.FILE_DETAIL: {
const { record } = option;
- updateFileDetails([record]);
+ updateRecordDetails([record]);
break;
}
case OPERATION.MOVE: {
@@ -392,7 +315,15 @@ const ContextMenu = ({
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 fileName = getFileNameFromRecord(currentRecord.current);
@@ -424,7 +355,7 @@ const ContextMenu = ({
repoID={repoID}
dirent={new Dirent({ name: fileName })}
isMultipleOperation={false}
- onItemMove={(...params) => moveRecord(currentRecordId, ...params)}
+ onItemMove={(...params) => handleMoveRecord(currentRecordId, ...params)}
onCancelMove={toggleMoveDialog}
onAddFolder={addFolder}
/>
diff --git a/frontend/src/metadata/views/table/index.js b/frontend/src/metadata/views/table/index.js
index 026c60d788..ce25223e26 100644
--- a/frontend/src/metadata/views/table/index.js
+++ b/frontend/src/metadata/views/table/index.js
@@ -27,7 +27,11 @@ const Table = () => {
insertColumn,
updateFileTags,
moveRecord,
- addFolder
+ addFolder,
+ updateSelectedRecordIds,
+ updateRecordDetails,
+ updateRecordDescription,
+ ocr,
} = useMetadataView();
const containerRef = useRef(null);
@@ -176,6 +180,10 @@ const Table = () => {
onGridKeyUp={onHotKeyUp}
moveRecord={moveRecord}
addFolder={addFolder}
+ updateSelectedRecordIds={updateSelectedRecordIds}
+ updateRecordDetails={updateRecordDetails}
+ updateRecordDescription={updateRecordDescription}
+ ocr={ocr}
/>
);
diff --git a/frontend/src/metadata/views/table/table-main/index.js b/frontend/src/metadata/views/table/table-main/index.js
index d1437cd2f5..b937ddf172 100644
--- a/frontend/src/metadata/views/table/table-main/index.js
+++ b/frontend/src/metadata/views/table/table-main/index.js
@@ -46,10 +46,6 @@ const TableMain = ({
modifyRecord && modifyRecord(rowId, updates, oldRowData, originalUpdates, originalOldRowData);
}, [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 }) => {
insertColumn && insertColumn(name, type, { key, data });
}, [insertColumn]);
@@ -74,7 +70,6 @@ const TableMain = ({
paste={paste}
groupOffsetLeft={groupOffset}
modifyRecord={updateRecord}
- updateRecords={updateRecords}
deleteRecords={props.deleteRecords}
getCopiedRecordsAndColumnsFromRange={getCopiedRecordsAndColumnsFromRange}
recordGetterById={recordGetterById}
diff --git a/frontend/src/metadata/views/table/table-main/records/body.js b/frontend/src/metadata/views/table/table-main/records/body.js
index 8589d1d987..a8bd9353c0 100644
--- a/frontend/src/metadata/views/table/table-main/records/body.js
+++ b/frontend/src/metadata/views/table/table-main/records/body.js
@@ -564,6 +564,7 @@ class RecordsBody extends Component {
getTableCanvasContainerRect={this.props.getTableCanvasContainerRect}
updateFileTags={this.props.updateFileTags}
deleteRecords={this.props.deleteRecords}
+ moveRecord={this.props.moveRecord}
/>
{this.renderRecords()}
@@ -631,6 +632,8 @@ RecordsBody.propTypes = {
cacheDownloadFilesProps: PropTypes.func,
onCellContextMenu: PropTypes.func,
getTableCanvasContainerRect: PropTypes.func,
+ moveRecord: PropTypes.func,
+ addFolder: PropTypes.func,
};
export default RecordsBody;
diff --git a/frontend/src/metadata/views/table/table-main/records/index.js b/frontend/src/metadata/views/table/table-main/records/index.js
index a2465b45b6..02879c105b 100644
--- a/frontend/src/metadata/views/table/table-main/records/index.js
+++ b/frontend/src/metadata/views/table/table-main/records/index.js
@@ -487,6 +487,8 @@ class Records extends Component {
this.setState({
recordMetrics: updatedRecordMetrics,
});
+ const ids = Object.keys(updatedRecordMetrics.idSelectedRecordMap);
+ this.props.updateSelectedRecordIds(ids);
};
selectRecordsById = (recordIds) => {
@@ -500,6 +502,7 @@ class Records extends Component {
this.setState({
recordMetrics: updatedRecordMetrics,
});
+ this.props.updateSelectedRecordIds(recordIds);
};
deselectRecord = (recordId) => {
@@ -512,6 +515,8 @@ class Records extends Component {
this.setState({
recordMetrics: updatedRecordMetrics,
});
+ const ids = Object.keys(updatedRecordMetrics.idSelectedRecordMap);
+ this.props.updateSelectedRecordIds(ids);
};
selectAllRecords = () => {
@@ -539,6 +544,7 @@ class Records extends Component {
this.setState({
recordMetrics: updatedRecordMetrics,
});
+ this.props.updateSelectedRecordIds(selectedRowIds);
};
onDeselectAllRecords = () => {
@@ -552,6 +558,7 @@ class Records extends Component {
recordMetrics: updatedRecordMetrics,
lastRowIdxUiSelected: { groupRecordIndex: -1, recordIndex: -1 },
});
+ this.props.updateSelectedRecordIds([]);
};
hasSelectedCell = ({ groupRecordIndex, recordIndex }, selectedPosition) => {
@@ -609,6 +616,7 @@ class Records extends Component {
const recordId = record._id;
if (!RecordMetrics.isRecordSelected(recordId, recordMetrics)) {
this.setState({ recordMetrics: this.createRowMetrics() });
+ this.props.updateSelectedRecordIds([]);
}
// select cell when click out of selectRange
@@ -633,10 +641,13 @@ class Records extends Component {
),
hasSelectedRecord: this.hasSelectedRecord(),
@@ -651,6 +662,8 @@ class Records extends Component {
cacheScrollTop: this.storeScrollTop,
onCellContextMenu: this.onCellContextMenu,
getTableCanvasContainerRect: this.getTableCanvasContainerRect,
+ moveRecord: this.props.moveRecord,
+ addFolder: this.props.addFolder
};
if (this.props.isGroupView) {
return (
@@ -766,7 +779,6 @@ Records.propTypes = {
getTableContentRect: PropTypes.func,
scrollToLoadMore: PropTypes.func,
updateRecord: PropTypes.func,
- updateRecords: PropTypes.func,
recordGetterById: PropTypes.func,
recordGetterByIndex: PropTypes.func,
loadAll: PropTypes.func,
@@ -779,6 +791,7 @@ Records.propTypes = {
getCopiedRecordsAndColumnsFromRange: PropTypes.func,
moveRecord: PropTypes.func,
addFolder: PropTypes.func,
+ updateSelectedRecordIds: PropTypes.func,
};
export default Records;
diff --git a/frontend/src/pages/lib-content-view/lib-content-view.js b/frontend/src/pages/lib-content-view/lib-content-view.js
index 4a9a5a2664..46451896a0 100644
--- a/frontend/src/pages/lib-content-view/lib-content-view.js
+++ b/frontend/src/pages/lib-content-view/lib-content-view.js
@@ -31,6 +31,7 @@ import Detail from '../../components/dirent-detail';
import DirColumnView from '../../components/dir-view-mode/dir-column-view';
import SelectedDirentsToolbar from '../../components/toolbar/selected-dirents-toolbar';
import TagFilesToolbar from '../../components/toolbar/tag-files-toolbar';
+import TableFilesToolbar from '../../components/toolbar/table-files-toolbar';
import '../../css/lib-content-view.css';
@@ -2277,9 +2278,11 @@ class LibContentView extends React.Component {
'animation-children': isDirentSelected
})}>
{isDirentSelected ? (
- this.state.currentMode === TAGS_MODE ?
+ this.state.currentMode === TAGS_MODE ? (
- :
+ ) : this.state.currentMode === METADATA_MODE ? (
+
+ ) : (
+ )
) : (