1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-27 15:54:39 +00:00

Optimize/table view toolbar (#7567)

* optimize table view toolbar

* optimize

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
This commit is contained in:
Aries
2025-03-10 14:17:09 +08:00
committed by GitHub
parent 7b293d0771
commit 00707d1d05
11 changed files with 426 additions and 105 deletions

View File

@@ -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 && (

View 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;

View File

@@ -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',

View File

@@ -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}

View File

@@ -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}
/>

View File

@@ -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}
/>
</div>
);

View File

@@ -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}

View File

@@ -564,6 +564,7 @@ class RecordsBody extends Component {
getTableCanvasContainerRect={this.props.getTableCanvasContainerRect}
updateFileTags={this.props.updateFileTags}
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}>
{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;

View File

@@ -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 {
<ContextMenu
isGroupView={isGroupView}
recordGetterByIndex={this.props.recordGetterByIndex}
updateRecords={this.props.updateRecords}
deleteRecords={this.props.deleteRecords}
moveRecord={this.props.moveRecord}
addFolder={this.props.addFolder}
selectNone={this.selectNone}
updateRecordDetails={this.props.updateRecordDetails}
updateRecordDescription={this.props.updateRecordDescription}
ocr={this.props.ocr}
/>
),
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;

View File

@@ -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 ? (
<TagFilesToolbar currentRepoInfo={this.state.currentRepoInfo} />
:
) : this.state.currentMode === METADATA_MODE ? (
<TableFilesToolbar repoID={this.props.repoID} />
) : (
<SelectedDirentsToolbar
repoID={this.props.repoID}
path={this.state.path}
@@ -2305,6 +2308,7 @@ class LibContentView extends React.Component {
onItemConvert={this.onConvertItem}
onAddFolder={this.onAddFolder}
/>
)
) : (
<CurDirPath
currentRepoInfo={this.state.currentRepoInfo}

View File

@@ -197,7 +197,65 @@ const TextTranslation = {
ADD_VIEW: {
key: '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;