1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-07-15 16:04:01 +00:00
seahub/frontend/src/metadata/hooks/metadata-view.js
Aries db287ccf9d
show people name in path (#7281)
* show people name in path

* optimize

* optimize

* optimize

* feat: optimize code

---------

Co-authored-by: zhouwenxuan <aries@Mac.local>
Co-authored-by: 杨国璇 <ygx@Hello-word.local>
2025-01-03 15:11:06 +08:00

381 lines
15 KiB
JavaScript

/* eslint-disable react/prop-types */
import React, { useContext, useEffect, useRef, useState, useCallback } from 'react';
import toaster from '../../components/toast';
import Context from '../context';
import Store from '../store';
import { EVENT_BUS_TYPE, PER_LOAD_NUMBER, PRIVATE_COLUMN_KEY } from '../constants';
import { Utils, validateName } from '../../utils/utils';
import { useMetadata } from './metadata';
import { useCollaborators } from './collaborators';
import { getRowById } from '../utils/table';
import { getFileNameFromRecord, getParentDirFromRecord, getRecordIdFromRecord, getUniqueFileName } from '../utils/cell';
import { gettext } from '../../utils/constants';
import { checkIsDir } from '../utils/row';
const MetadataViewContext = React.createContext(null);
export const MetadataViewProvider = ({
children,
repoID,
viewID,
renameFileCallback,
deleteFilesCallback,
moveFileCallback,
copyFileCallback,
...params
}) => {
const [isLoading, setLoading] = useState(true);
const [metadata, setMetadata] = useState({ rows: [], columns: [], view: {} });
const [errorMessage, setErrorMessage] = useState(null);
const storeRef = useRef(null);
const delayReloadDataTimer = useRef(null);
const { collaborators } = useCollaborators();
const { isBeingBuilt, setIsBeingBuilt } = useMetadata();
const tableChanged = useCallback(() => {
setMetadata(storeRef.current.data);
}, []);
const handleTableError = useCallback((error) => {
toaster.danger(error.error);
}, []);
const updateMetadata = useCallback((data) => {
setMetadata(data);
}, []);
const reloadMetadata = useCallback(() => {
setLoading(true);
storeRef.current.reload(PER_LOAD_NUMBER).then(() => {
setMetadata(storeRef.current.data);
setLoading(false);
delayReloadDataTimer.current = null;
}).catch(error => {
const errorMsg = Utils.getErrorMsg(error);
setErrorMessage(errorMsg);
setLoading(false);
});
}, []);
const delayReloadMetadata = useCallback(() => {
delayReloadDataTimer.current && clearTimeout(delayReloadDataTimer.current);
delayReloadDataTimer.current = setTimeout(() => {
reloadMetadata();
}, 600);
}, [reloadMetadata]);
const modifyFilters = useCallback((filters, filterConjunction, basicFilters) => {
storeRef.current.modifyFilters(filterConjunction, filters, basicFilters);
}, [storeRef]);
const modifySorts = useCallback((sorts, displaySorts = false) => {
storeRef.current.modifySorts(sorts, displaySorts);
}, [storeRef]);
const modifyGroupbys = useCallback((groupbys) => {
storeRef.current.modifyGroupbys(groupbys);
}, [storeRef]);
const modifyHiddenColumns = useCallback((hiddenColumns) => {
storeRef.current.modifyHiddenColumns(hiddenColumns);
}, [storeRef]);
const modifySettings = useCallback((settings) => {
storeRef.current.modifySettings(settings);
}, [storeRef]);
const updateLocalRecord = useCallback(({ recordId, parentDir, fileName }, update) => {
storeRef.current.modifyLocalRecord({ record_id: recordId, parent_dir: parentDir, file_name: fileName }, update);
}, [storeRef]);
const updateLocalColumnData = useCallback((columnKey, newData, oldData) => {
storeRef.current.modifyLocalColumnData(columnKey, newData, oldData);
}, []);
const modifyRecords = (rowIds, idRowUpdates, idOriginalRowUpdates, idOldRowData, idOriginalOldRowData, isCopyPaste = false, { success_callback, fail_callback } = {}) => {
const isRename = storeRef.current.checkIsRenameFileOperator(rowIds, idOriginalRowUpdates);
let newName = null;
if (isRename) {
const rowId = rowIds[0];
const row = getRowById(metadata, rowId);
const rowUpdates = idOriginalRowUpdates[rowId];
const { _parent_dir, _name } = row;
newName = getFileNameFromRecord(rowUpdates);
const { isValid, errMessage } = validateName(newName);
if (!isValid) {
toaster.danger(errMessage);
return;
}
if (newName === _name) {
return;
}
if (storeRef.current.checkDuplicatedName(newName, _parent_dir)) {
let errMessage = gettext('The name "{name}" is already taken. Please choose a different name.');
errMessage = errMessage.replace('{name}', Utils.HTMLescape(newName));
toaster.danger(errMessage);
return;
}
}
storeRef.current.modifyRecords(rowIds, idRowUpdates, idOriginalRowUpdates, idOldRowData, idOriginalOldRowData, isCopyPaste, isRename, {
fail_callback: (error) => {
fail_callback && fail_callback(error);
error && toaster.danger(error);
},
success_callback: (operation) => {
if (operation.is_rename) {
const rowId = operation.row_ids[0];
const row = getRowById(metadata, rowId);
const rowUpdates = operation.id_original_row_updates[rowId];
const oldRow = operation.id_original_old_row_data[rowId];
const parentDir = getParentDirFromRecord(row);
const oldName = getFileNameFromRecord(oldRow);
const path = Utils.joinPath(parentDir, oldName);
const newName = getFileNameFromRecord(rowUpdates);
renameFileCallback(path, newName);
}
success_callback && success_callback();
},
});
};
const deleteRecords = (recordsIds, { success_callback, fail_callback } = {}) => {
if (!Array.isArray(recordsIds) || recordsIds.length === 0) return;
let paths = [];
let fileNames = [];
recordsIds.forEach((recordId) => {
const record = getRowById(metadata, recordId);
const { _parent_dir, _name } = record || {};
if (_parent_dir && _name) {
const path = Utils.joinPath(_parent_dir, _name);
paths.push(path);
fileNames.push(_name);
}
});
storeRef.current.deleteRecords(recordsIds, {
fail_callback: (error) => {
fail_callback && fail_callback(error);
error && toaster.danger(error);
},
success_callback: () => {
deleteFilesCallback(paths, fileNames);
let msg = fileNames.length > 1
? gettext('Successfully deleted {name} and {n} other items')
: gettext('Successfully deleted {name}');
msg = msg.replace('{name}', fileNames[0])
.replace('{n}', fileNames.length - 1);
toaster.success(msg);
success_callback && success_callback();
},
});
};
const modifyRecord = (rowId, updates, oldRowData, originalUpdates, originalOldRowData, isCopyPaste, { success_callback, fail_callback } = {}) => {
const rowIds = [rowId];
const idRowUpdates = { [rowId]: updates };
const idOriginalRowUpdates = { [rowId]: originalUpdates };
const idOldRowData = { [rowId]: oldRowData };
const idOriginalOldRowData = { [rowId]: originalOldRowData };
modifyRecords(rowIds, idRowUpdates, idOriginalRowUpdates, idOldRowData, idOriginalOldRowData, isCopyPaste, { success_callback, fail_callback });
};
const moveRecord = (rowId, targetRepo, dirent, targetParentPath, sourceParentPath, isByDialog) => {
const targetRepoId = targetRepo.repo_id;
const row = getRowById(metadata, rowId);
const { rows } = metadata;
const isDir = checkIsDir(row);
const oldName = dirent.name;
const oldParentPath = Utils.joinPath(sourceParentPath, oldName);
let needDeletedRowIds = [];
let updateRowIds = [];
let idRowUpdates = {};
let idOldRowData = {};
if (repoID === targetRepoId) {
const newName = getUniqueFileName(rows, targetParentPath, oldName);
updateRowIds.push(rowId);
idRowUpdates[rowId] = { [PRIVATE_COLUMN_KEY.PARENT_DIR]: targetParentPath, [PRIVATE_COLUMN_KEY.FILE_NAME]: newName };
idOldRowData[rowId] = { [PRIVATE_COLUMN_KEY.PARENT_DIR]: sourceParentPath, [PRIVATE_COLUMN_KEY.FILE_NAME]: oldName };
if (isDir) {
const newPath = Utils.joinPath(targetParentPath, newName);
rows.forEach((row) => {
const parentDir = getParentDirFromRecord(row);
if (row && parentDir.startsWith(oldParentPath)) {
const updateRowId = getRecordIdFromRecord(row);
updateRowIds.push(updateRowId);
idRowUpdates[updateRowId] = { [PRIVATE_COLUMN_KEY.PARENT_DIR]: parentDir.replace(oldParentPath, newPath) };
idOldRowData[updateRowId] = { [PRIVATE_COLUMN_KEY.PARENT_DIR]: parentDir };
}
});
}
} else {
needDeletedRowIds = [rowId];
if (isDir) {
rows.forEach((row) => {
const parentDir = getParentDirFromRecord(row);
if (row && parentDir.startsWith(oldParentPath)) {
const id = getRecordIdFromRecord(row);
needDeletedRowIds.push(id);
}
});
}
}
storeRef.current.moveRecord(rowId, targetRepoId, dirent, targetParentPath, sourceParentPath, {
modify_row_ids: updateRowIds,
modify_id_row_updates: idRowUpdates,
modify_id_old_row_data: idOldRowData,
delete_row_ids: needDeletedRowIds,
}, {
success_callback: (operation) => {
moveFileCallback && moveFileCallback(repoID, targetRepo, dirent, targetParentPath, sourceParentPath, operation.task_id, isByDialog);
},
fail_callback: (error) => {
error && toaster.danger(error);
}
});
};
const duplicateRecord = (rowId, targetRepo, dirent, targetPath, nodeParentPath, isByDialog) => {
storeRef.current.duplicateRecord(rowId, targetRepo.repo_id, dirent, targetPath, nodeParentPath, {
success_callback: (operation) => {
copyFileCallback && copyFileCallback(repoID, targetRepo, dirent, targetPath, nodeParentPath, operation.task_id, isByDialog);
if (repoID === targetRepo.repo_id) {
delayReloadMetadata();
}
},
fail_callback: (error) => {
error && toaster.danger(error);
}
});
};
const renameColumn = useCallback((columnKey, newName, oldName) => {
storeRef.current.renameColumn(columnKey, newName, oldName);
}, [storeRef]);
const deleteColumn = useCallback((columnKey, oldColumn) => {
storeRef.current.deleteColumn(columnKey, oldColumn);
}, [storeRef]);
const modifyColumnData = useCallback((columnKey, newData, oldData, { optionModifyType } = {}) => {
storeRef.current.modifyColumnData(columnKey, newData, oldData, { optionModifyType });
}, [storeRef]);
const modifyColumnWidth = useCallback((columnKey, newWidth) => {
storeRef.current.modifyColumnWidth(columnKey, newWidth);
}, [storeRef]);
const modifyColumnOrder = useCallback((sourceColumnKey, targetColumnKey) => {
storeRef.current.modifyColumnOrder(sourceColumnKey, targetColumnKey);
}, [storeRef]);
const insertColumn = useCallback((name, type, { key, data }) => {
storeRef.current.insertColumn(name, type, { key, data });
}, [storeRef]);
const updateFileTags = useCallback((data) => {
storeRef.current.updateFileTags(data);
}, [storeRef]);
// init
useEffect(() => {
setLoading(true);
// init context
const context = new Context();
window.sfMetadataContext = context;
window.sfMetadataContext.init({ ...params, repoID, viewID });
storeRef.current = new Store({ context: window.sfMetadataContext, repoId: repoID, viewId: viewID, collaborators });
window.sfMetadataStore = storeRef.current;
storeRef.current.initStartIndex();
storeRef.current.load(PER_LOAD_NUMBER, isBeingBuilt).then(() => {
setMetadata(storeRef.current.data);
setIsBeingBuilt(false);
setLoading(false);
}).catch(error => {
const errorMsg = Utils.getErrorMsg(error);
toaster.danger(errorMsg);
});
const eventBus = window.sfMetadataContext.eventBus;
const unsubscribeServerTableChanged = eventBus.subscribe(EVENT_BUS_TYPE.SERVER_TABLE_CHANGED, tableChanged);
const unsubscribeTableChanged = eventBus.subscribe(EVENT_BUS_TYPE.LOCAL_TABLE_CHANGED, tableChanged);
const unsubscribeHandleTableError = eventBus.subscribe(EVENT_BUS_TYPE.TABLE_ERROR, handleTableError);
const unsubscribeUpdateRows = eventBus.subscribe(EVENT_BUS_TYPE.UPDATE_TABLE_ROWS, updateMetadata);
const unsubscribeReloadData = eventBus.subscribe(EVENT_BUS_TYPE.RELOAD_DATA, reloadMetadata);
const unsubscribeModifyFilters = eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_FILTERS, modifyFilters);
const unsubscribeModifySorts = eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_SORTS, modifySorts);
const unsubscribeModifyGroupbys = eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_GROUPBYS, modifyGroupbys);
const unsubscribeModifyHiddenColumns = eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_HIDDEN_COLUMNS, modifyHiddenColumns);
const unsubscribeModifyColumnOrder = eventBus.subscribe(EVENT_BUS_TYPE.MODIFY_COLUMN_ORDER, modifyColumnOrder);
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);
return () => {
if (window.sfMetadataContext) {
window.sfMetadataContext.destroy();
}
window.sfMetadataStore.destroy();
unsubscribeServerTableChanged();
unsubscribeTableChanged();
unsubscribeHandleTableError();
unsubscribeUpdateRows();
unsubscribeReloadData();
unsubscribeModifyFilters();
unsubscribeModifySorts();
unsubscribeModifyGroupbys();
unsubscribeModifyHiddenColumns();
unsubscribeModifyColumnOrder();
unsubscribeModifySettings();
unsubscribeLocalRecordChanged();
unsubscribeLocalColumnChanged();
delayReloadDataTimer.current && clearTimeout(delayReloadDataTimer.current);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [repoID, viewID]);
return (
<MetadataViewContext.Provider
value={{
isLoading,
isBeingBuilt,
errorMessage,
metadata,
store: storeRef.current,
isDirentDetailShow: params.isDirentDetailShow,
updateCurrentDirent: params.updateCurrentDirent,
showDirentDetail: params.showDirentDetail,
deleteFilesCallback: deleteFilesCallback,
renameFileCallback: renameFileCallback,
modifySettings,
modifyRecords,
deleteRecords,
modifyRecord,
moveRecord,
duplicateRecord,
renameColumn,
deleteColumn,
modifyColumnOrder,
modifyColumnData,
modifyColumnWidth,
insertColumn,
updateFileTags,
addFolder: params.addFolder,
updateCurrentPath: params.updateCurrentPath,
}}
>
{children}
</MetadataViewContext.Provider>
);
};
export const useMetadataView = () => {
const context = useContext(MetadataViewContext);
if (!context) {
throw new Error('\'MetadataContext\' is null');
}
return context;
};