mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-25 23:02:26 +00:00
236 lines
9.7 KiB
JavaScript
236 lines
9.7 KiB
JavaScript
import React, { useContext, useEffect, useCallback, useState, useMemo, useRef } from 'react';
|
|
import metadataAPI from '../api';
|
|
import { Utils } from '../../utils/utils';
|
|
import toaster from '../../components/toast';
|
|
import { useMetadataStatus } from '../../hooks/metadata-status';
|
|
import { SYSTEM_FOLDERS } from '../../constants';
|
|
import Column from '../model/column';
|
|
import { normalizeFields } from '../components/metadata-details/utils';
|
|
import { CellType, EVENT_BUS_TYPE, PRIVATE_COLUMN_KEY } from '../constants';
|
|
import { getCellValueByColumn, getColumnOptionNamesByIds, getColumnOptionNameById, getRecordIdFromRecord, getServerOptions } from '../utils/cell';
|
|
import tagsAPI from '../../tag/api';
|
|
import { getColumnByKey, getColumnOptions, getColumnOriginName } from '../utils/column';
|
|
import ObjectUtils from '../../utils/object';
|
|
import { NOT_DISPLAY_COLUMN_KEYS } from '../components/metadata-details/constants';
|
|
|
|
const MetadataDetailsContext = React.createContext(null);
|
|
|
|
export const MetadataDetailsProvider = ({ repoID, repoInfo, path, dirent, direntDetail, direntType, modifyLocalFileTags, onErrMessage, children }) => {
|
|
const { enableMetadata, detailsSettings, modifyDetailsSettings } = useMetadataStatus();
|
|
|
|
const [isLoading, setLoading] = useState(true);
|
|
const [record, setRecord] = useState(null);
|
|
const [originColumns, setOriginColumns] = useState([]);
|
|
|
|
const canModifyRecord = useMemo(() => repoInfo.permission !== 'admin' && repoInfo.permission !== 'rw' ? false : true, [repoInfo]);
|
|
const canModifyDetails = useMemo(() => repoInfo.is_admin, [repoInfo]);
|
|
|
|
const allColumnsRef = useRef([]);
|
|
const direntRef = useRef(null);
|
|
|
|
const columns = useMemo(() => {
|
|
const orderAndHiddenColumns = detailsSettings?.columns || [];
|
|
if (!Array.isArray(orderAndHiddenColumns) || orderAndHiddenColumns.length === 0) {
|
|
return originColumns.map(c => ({ ...c, shown: true }));
|
|
}
|
|
const oldColumnsMap = orderAndHiddenColumns.reduce((pre, cur) => {
|
|
pre[cur.key] = true;
|
|
return pre;
|
|
}, {});
|
|
const columnsMap = originColumns.reduce((pre, cur) => {
|
|
pre[cur.key] = cur;
|
|
return pre;
|
|
}, {});
|
|
const exitColumnsOrder = orderAndHiddenColumns.map(c => {
|
|
const column = columnsMap[c.key];
|
|
if (column) return { ...c, ...column };
|
|
return null;
|
|
}).filter(c => c);
|
|
const newColumns = originColumns.filter(c => !oldColumnsMap[c.key]).map(c => ({ ...c, shown: false }));
|
|
return [...exitColumnsOrder, ...newColumns];
|
|
}, [originColumns, detailsSettings]);
|
|
|
|
const onLocalRecordChange = useCallback((recordId, updates) => {
|
|
if (getRecordIdFromRecord(record) !== recordId) return;
|
|
const newRecord = { ...record, ...updates };
|
|
setRecord(newRecord);
|
|
}, [record]);
|
|
|
|
const onChange = useCallback((fieldKey, newValue) => {
|
|
const field = getColumnByKey(allColumnsRef.current, fieldKey);
|
|
const columnName = getColumnOriginName(field);
|
|
const recordId = getRecordIdFromRecord(record);
|
|
let update = { [columnName]: newValue };
|
|
if (field.type === CellType.SINGLE_SELECT) {
|
|
update = { [columnName]: getColumnOptionNameById(field, newValue) };
|
|
} else if (field.type === CellType.MULTIPLE_SELECT) {
|
|
update = { [columnName]: newValue ? getColumnOptionNamesByIds(field, newValue) : [] };
|
|
}
|
|
metadataAPI.modifyRecord(repoID, { recordId }, update).then(res => {
|
|
setRecord({ ...record, ...update });
|
|
if (window?.sfMetadataContext?.eventBus) {
|
|
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.LOCAL_RECORD_CHANGED, { recordId }, update);
|
|
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.LOCAL_RECORD_DETAIL_CHANGED, recordId, update);
|
|
}
|
|
}).catch(error => {
|
|
const errorMsg = Utils.getErrorMsg(error);
|
|
toaster.danger(errorMsg);
|
|
});
|
|
}, [repoID, record, allColumnsRef]);
|
|
|
|
const modifyColumnData = useCallback((fieldKey, newData) => {
|
|
let newColumns = originColumns.slice(0);
|
|
const oldColumn = getColumnByKey(originColumns, fieldKey);
|
|
let newColumn = null;
|
|
let update;
|
|
if (oldColumn.type === CellType.SINGLE_SELECT) {
|
|
newData.options = getServerOptions({ key: fieldKey, data: newData });
|
|
}
|
|
metadataAPI.modifyColumnData(repoID, fieldKey, newData).then(res => {
|
|
newColumn = new Column(res.data.column);
|
|
const fieldIndex = originColumns.findIndex(f => f.key === fieldKey);
|
|
newColumns[fieldIndex] = newColumn;
|
|
return newColumn;
|
|
}).then((newField) => {
|
|
const fileName = getColumnOriginName(newField);
|
|
const options = getColumnOptions(newField);
|
|
const newOption = options[options.length - 1];
|
|
update = { [fileName]: newOption.id };
|
|
if (newField.type === CellType.SINGLE_SELECT) {
|
|
update = { [fileName]: getColumnOptionNameById(newField, newOption.id) };
|
|
} else if (newField.type === CellType.MULTIPLE_SELECT) {
|
|
const oldValue = getCellValueByColumn(record, newField) || [];
|
|
update = { [fileName]: [...oldValue, newOption.name] };
|
|
}
|
|
return metadataAPI.modifyRecord(repoID, { recordId: record._id }, update);
|
|
}).then(res => {
|
|
setOriginColumns(newColumns);
|
|
setRecord({ ...record, ...update });
|
|
const eventBus = window?.sfMetadataContext?.eventBus;
|
|
if (eventBus) {
|
|
eventBus.dispatch(EVENT_BUS_TYPE.LOCAL_COLUMN_DATA_CHANGED, fieldKey, newColumn.data, oldColumn.data);
|
|
eventBus.dispatch(EVENT_BUS_TYPE.LOCAL_RECORD_CHANGED, { recordId: record._id }, update);
|
|
}
|
|
}).catch(error => {
|
|
const errorMsg = Utils.getErrorMsg(error);
|
|
toaster.danger(errorMsg);
|
|
});
|
|
}, [repoID, record, originColumns]);
|
|
|
|
const updateFileTags = useCallback((updateRecords) => {
|
|
const { record_id, tags } = updateRecords[0];
|
|
|
|
tagsAPI.updateFileTags(repoID, [{ record_id, tags }]).then(res => {
|
|
const newValue = tags ? tags.map(id => ({ row_id: id, display_value: id })) : [];
|
|
const update = { [PRIVATE_COLUMN_KEY.TAGS]: newValue };
|
|
setRecord({ ...record, ...update });
|
|
modifyLocalFileTags && modifyLocalFileTags(record_id, tags);
|
|
if (window?.sfMetadataContext?.eventBus) {
|
|
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.LOCAL_RECORD_CHANGED, { recordId: record_id }, update);
|
|
}
|
|
}).catch(error => {
|
|
const errorMsg = Utils.getErrorMsg(error);
|
|
toaster.danger(errorMsg);
|
|
});
|
|
}, [repoID, record, modifyLocalFileTags]);
|
|
|
|
const saveColumns = useCallback((columns) => {
|
|
modifyDetailsSettings && modifyDetailsSettings({ columns: columns.map(c => ({ key: c.key, shown: c.shown })) });
|
|
}, [modifyDetailsSettings]);
|
|
|
|
const modifyHiddenColumns = useCallback((hiddenColumns) => {
|
|
let newColumns = columns.slice(0);
|
|
newColumns = newColumns.map(c => ({ ...c, shown: !hiddenColumns.includes(c.key) }));
|
|
saveColumns(newColumns);
|
|
}, [columns, saveColumns]);
|
|
|
|
const modifyColumnOrder = useCallback((sourceColumnKey, targetColumnKey) => {
|
|
const targetColumnIndex = columns.findIndex(c => c.key === targetColumnKey);
|
|
const sourceColumn = columns.find(c => c.key === sourceColumnKey);
|
|
let newColumns = columns.slice(0);
|
|
newColumns = newColumns.filter(c => c.key !== sourceColumnKey);
|
|
newColumns.splice(targetColumnIndex, 0, sourceColumn);
|
|
saveColumns(newColumns);
|
|
}, [columns, saveColumns]);
|
|
|
|
useEffect(() => {
|
|
if (!dirent || !direntDetail || !enableMetadata || SYSTEM_FOLDERS.find(folderPath => path.startsWith(folderPath))) {
|
|
setLoading(true);
|
|
direntRef.current = null;
|
|
setRecord(null);
|
|
setOriginColumns([]);
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
if (ObjectUtils.isSameObject(direntRef.current, dirent, ['name'])) return;
|
|
|
|
setLoading(true);
|
|
direntRef.current = dirent;
|
|
|
|
const fileName = dirent.name;
|
|
let parentDir = path.split('/').pop() === fileName ? Utils.getDirName(path) : path;
|
|
|
|
if (!parentDir.startsWith('/')) {
|
|
parentDir = '/' + parentDir;
|
|
}
|
|
metadataAPI.getRecord(repoID, { parentDir, fileName }).then(res => {
|
|
const { results, metadata } = res.data;
|
|
const record = Array.isArray(results) && results.length > 0 ? results[0] : {};
|
|
const allColumns = normalizeFields(metadata).map(field => new Column(field));
|
|
allColumnsRef.current = allColumns;
|
|
const columns = allColumns.filter(c => !NOT_DISPLAY_COLUMN_KEYS.includes(c.key));
|
|
setRecord(record);
|
|
setOriginColumns(columns);
|
|
setLoading(false);
|
|
}).catch(error => {
|
|
if (error.response && error.response.status === 404 && onErrMessage) {
|
|
const err = `${direntType === 'file' ? 'File' : 'Folder' } does not exist`;
|
|
onErrMessage(err);
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
const errMessage = Utils.getErrorMsg(error);
|
|
toaster.danger(errMessage);
|
|
setLoading(false);
|
|
});
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [enableMetadata, repoID, path, direntType, dirent, direntDetail]);
|
|
|
|
useEffect(() => {
|
|
const eventBus = window?.sfMetadataContext?.eventBus;
|
|
if (!eventBus) return;
|
|
const unsubscribeLocalRecordChanged = eventBus.subscribe(EVENT_BUS_TYPE.LOCAL_RECORD_DETAIL_CHANGED, onLocalRecordChange);
|
|
return () => {
|
|
unsubscribeLocalRecordChanged();
|
|
};
|
|
}, [onLocalRecordChange]);
|
|
|
|
return (
|
|
<MetadataDetailsContext.Provider
|
|
value={{
|
|
isLoading,
|
|
canModifyRecord,
|
|
canModifyDetails,
|
|
record,
|
|
columns,
|
|
onChange,
|
|
onLocalRecordChange,
|
|
modifyColumnData,
|
|
updateFileTags,
|
|
modifyHiddenColumns,
|
|
modifyColumnOrder,
|
|
}}
|
|
>
|
|
{children}
|
|
</MetadataDetailsContext.Provider>
|
|
);
|
|
};
|
|
|
|
export const useMetadataDetails = () => {
|
|
const context = useContext(MetadataDetailsContext);
|
|
if (!context) {
|
|
throw new Error('\'MetadataDetailsContext\' is null');
|
|
}
|
|
return context;
|
|
};
|