mirror of
https://github.com/haiwen/seahub.git
synced 2025-07-15 07:52:14 +00:00
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>
This commit is contained in:
parent
3795c054ea
commit
db287ccf9d
@ -9,7 +9,7 @@ import { siteRoot, gettext } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { PRIVATE_FILE_TYPE } from '../../constants';
|
||||
import { debounce } from '../../metadata/utils/common';
|
||||
import { EVENT_BUS_TYPE } from '../../metadata/constants';
|
||||
import { EVENT_BUS_TYPE, FACE_RECOGNITION_VIEW_ID } from '../../metadata/constants';
|
||||
|
||||
const propTypes = {
|
||||
currentRepoInfo: PropTypes.object.isRequired,
|
||||
@ -118,63 +118,59 @@ class DirPath extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
handelRefresh = debounce(() => {
|
||||
handleRefresh = debounce(() => {
|
||||
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.RELOAD_DATA);
|
||||
}, 200);
|
||||
|
||||
turnViewPathToLink = (pathList) => {
|
||||
if (!Array.isArray(pathList) || pathList.length === 0) return null;
|
||||
const [, , viewId, children] = pathList;
|
||||
const isViewSupportClick = viewId === FACE_RECOGNITION_VIEW_ID && children;
|
||||
return (
|
||||
<>
|
||||
<span className="path-split">/</span>
|
||||
<span className="path-item">{gettext('Views')}</span>
|
||||
<span className="path-split">/</span>
|
||||
<span className="path-item" role={isViewSupportClick ? 'button' : null} onClick={isViewSupportClick ? this.handleRefresh : () => {}}>
|
||||
<MetadataViewName id={viewId} />
|
||||
</span>
|
||||
{children && (
|
||||
<>
|
||||
<span className="path-split">/</span>
|
||||
<span className="path-item">{children}</span>
|
||||
</>
|
||||
)}
|
||||
<div className="path-item-refresh" id="sf-metadata-view-refresh" onClick={this.handleRefresh}>
|
||||
<i className="sf3-font sf3-font-refresh"></i>
|
||||
<UncontrolledTooltip target="sf-metadata-view-refresh" placement="bottom">
|
||||
{gettext('Refresh the view')}
|
||||
</UncontrolledTooltip>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
turnTagPathToLink = (pathList) => {
|
||||
if (!Array.isArray(pathList) || pathList.length === 0) return null;
|
||||
const [, , tagId] = pathList;
|
||||
return (
|
||||
<>
|
||||
<span className="path-split">/</span>
|
||||
<span className="path-item">{gettext('Tags')}</span>
|
||||
<span className="path-split">/</span>
|
||||
<TagViewName id={tagId} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
turnPathToLink = (path) => {
|
||||
path = path[path.length - 1] === '/' ? path.slice(0, path.length - 1) : path;
|
||||
let pathList = path.split('/');
|
||||
const pathList = path.split('/');
|
||||
if (pathList.includes(PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES)) return this.turnViewPathToLink(pathList);
|
||||
if (pathList.includes(PRIVATE_FILE_TYPE.TAGS_PROPERTIES)) return this.turnTagPathToLink(pathList);
|
||||
let nodePath = '';
|
||||
if (pathList.length === 2 && !pathList[0] && [PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES, PRIVATE_FILE_TYPE.TAGS_PROPERTIES].includes(pathList[1])) {
|
||||
return null;
|
||||
}
|
||||
let pathElem = pathList.map((item, index) => {
|
||||
if (item === '') {
|
||||
return null;
|
||||
}
|
||||
if (index === pathList.length - 2 && item === PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES) {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<span className="path-split">/</span>
|
||||
<span className="path-item">{gettext('Views')}</span>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
if (index === pathList.length - 2 && item === PRIVATE_FILE_TYPE.TAGS_PROPERTIES) {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<span className="path-split">/</span>
|
||||
<span className="path-item">{gettext('Tags')}</span>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
if (index === pathList.length - 1 && pathList[pathList.length - 2] === PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES) {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<span className="path-split">/</span>
|
||||
<span className="path-item"><MetadataViewName id={item} /></span>
|
||||
<div className="path-item-refresh" id="sf-metadata-view-refresh" onClick={this.handelRefresh}>
|
||||
<i className="sf3-font sf3-font-refresh"></i>
|
||||
<UncontrolledTooltip target="sf-metadata-view-refresh" placement="bottom">
|
||||
{gettext('Refresh the view')}
|
||||
</UncontrolledTooltip>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
if (index === pathList.length - 1 && pathList[pathList.length - 2] === PRIVATE_FILE_TYPE.TAGS_PROPERTIES) {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<span className="path-split">/</span>
|
||||
<TagViewName id={item} />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
if (item === '') return null;
|
||||
if (index === (pathList.length - 1)) {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
@ -221,16 +217,9 @@ class DirPath extends React.Component {
|
||||
return pathElem;
|
||||
};
|
||||
|
||||
isViewMetadata = () => {
|
||||
const { currentPath } = this.props;
|
||||
const path = currentPath[currentPath.length - 1] === '/' ? currentPath.slice(0, currentPath.length - 1) : currentPath;
|
||||
const pathList = path.split('/');
|
||||
return pathList[pathList.length - 2] === PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES;
|
||||
};
|
||||
|
||||
render() {
|
||||
let { currentPath, repoName } = this.props;
|
||||
let pathElem = this.turnPathToLink(currentPath);
|
||||
const { currentPath, repoName } = this.props;
|
||||
const pathElem = this.turnPathToLink(currentPath);
|
||||
return (
|
||||
<div className="path-container dir-view-path">
|
||||
<span className="cur-view-path-btn mr-1" onClick={this.props.toggleTreePanel}>
|
||||
|
@ -83,6 +83,7 @@ const propTypes = {
|
||||
onItemsScroll: PropTypes.func.isRequired,
|
||||
eventBus: PropTypes.object,
|
||||
updateCurrentDirent: PropTypes.func.isRequired,
|
||||
updateCurrentPath: PropTypes.func,
|
||||
};
|
||||
|
||||
class DirColumnView extends React.Component {
|
||||
@ -211,6 +212,7 @@ class DirColumnView extends React.Component {
|
||||
addFolder={this.props.onAddFolder}
|
||||
updateCurrentDirent={this.props.updateCurrentDirent}
|
||||
showDirentDetail={this.props.showDirentDetail}
|
||||
updateCurrentPath={this.props.updateCurrentPath}
|
||||
/>
|
||||
)}
|
||||
{currentMode === TAGS_MODE && (
|
||||
|
@ -7,6 +7,7 @@ import ObjectUtils from '../../metadata/utils/object-utils';
|
||||
import { MetadataContext } from '../../metadata';
|
||||
import { PRIVATE_FILE_TYPE } from '../../constants';
|
||||
import { METADATA_MODE, TAGS_MODE } from '../dir-view-mode/constants';
|
||||
import { FACE_RECOGNITION_VIEW_ID } from '../../metadata/constants';
|
||||
|
||||
const Detail = React.memo(({ repoID, path, currentMode, dirent, currentRepoInfo, repoTags, fileTags, onClose, onFileTagChanged }) => {
|
||||
const isView = useMemo(() => currentMode === METADATA_MODE || path.startsWith('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES), [currentMode, path]);
|
||||
@ -30,9 +31,12 @@ const Detail = React.memo(({ repoID, path, currentMode, dirent, currentRepoInfo,
|
||||
|
||||
if (isTag) return null;
|
||||
|
||||
if (isView) {
|
||||
const viewId = path.split('/').pop();
|
||||
if (!dirent) return (<ViewDetails viewId={viewId} onClose={onClose} />);
|
||||
if (isView && !dirent) {
|
||||
const pathParts = path.split('/');
|
||||
const [, , viewId, children] = pathParts;
|
||||
if (!viewId) return null;
|
||||
if (viewId === FACE_RECOGNITION_VIEW_ID && !children) return null;
|
||||
return (<ViewDetails viewId={viewId} onClose={onClose} />);
|
||||
}
|
||||
|
||||
if (path === '/' && !dirent) {
|
||||
|
@ -52,13 +52,13 @@ const FaceRecognitionViewToolbar = ({ readOnly, isCustomPermission, onToggleDeta
|
||||
columns={viewColumns}
|
||||
modifySorts={modifySorts}
|
||||
/>
|
||||
{!isCustomPermission && (
|
||||
<div className="cur-view-path-btn ml-2" onClick={onToggleDetail}>
|
||||
<span className="sf3-font sf3-font-info" aria-label={gettext('Properties')} title={gettext('Properties')}></span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!isCustomPermission && (
|
||||
<div className="cur-view-path-btn ml-2" onClick={onToggleDetail}>
|
||||
<span className="sf3-font sf3-font-info" aria-label={gettext('Properties')} title={gettext('Properties')}></span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="sf-metadata-tool-right-operations"></div>
|
||||
</>
|
||||
|
@ -3,7 +3,7 @@
|
||||
export const CELL_MASK = 1;
|
||||
export const TABLE_MAIN_INTERVAL = 1;
|
||||
export const RESIZE_HANDLE = 1;
|
||||
export const SEQUENCE_COLUMN = 1;
|
||||
export const SEQUENCE_COLUMN = 2;
|
||||
|
||||
// higher than unfrozen header cell(0), RESIZE_HANDLE
|
||||
export const FROZEN_HEADER_CELL = 2;
|
||||
|
@ -363,6 +363,7 @@ export const MetadataViewProvider = ({
|
||||
insertColumn,
|
||||
updateFileTags,
|
||||
addFolder: params.addFolder,
|
||||
updateCurrentPath: params.updateCurrentPath,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -375,7 +375,7 @@ export const MetadataProvider = ({ repoID, currentPath, repoInfo, selectMetadata
|
||||
if (idViewMap[FACE_RECOGNITION_VIEW_ID]) {
|
||||
let isSelected = false;
|
||||
if (currentPath.includes('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/')) {
|
||||
const currentViewId = currentPath.split('/').pop();
|
||||
const [, , currentViewId] = currentPath.split('/');
|
||||
isSelected = currentViewId === FACE_RECOGNITION_VIEW_ID;
|
||||
}
|
||||
const folders = navigation.filter((nav) => nav.type === VIEWS_TYPE_FOLDER);
|
||||
@ -428,7 +428,7 @@ export const MetadataProvider = ({ repoID, currentPath, repoInfo, selectMetadata
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentPath.includes('/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/')) return;
|
||||
const currentViewId = currentPath.split('/').pop();
|
||||
const [, , currentViewId] = currentPath.split('/');
|
||||
const currentView = idViewMap[currentViewId];
|
||||
if (currentView) {
|
||||
document.title = `${currentView.name} - Seafile`;
|
||||
|
@ -130,7 +130,7 @@ const MetadataTreeView = ({ userPerm, currentPath }) => {
|
||||
|
||||
const renderView = (view) => {
|
||||
const viewPath = '/' + PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES + '/' + view._id;
|
||||
const isSelected = currentPath === viewPath;
|
||||
const isSelected = currentPath.includes(viewPath);
|
||||
return (
|
||||
<ViewItem
|
||||
key={`metadata-views-folder-${view._id}`}
|
||||
|
@ -483,6 +483,17 @@ class Store {
|
||||
this.applyOperation(operation);
|
||||
}
|
||||
|
||||
modifyLocalView(update) {
|
||||
const type = OPERATION_TYPE.MODIFY_LOCAL_VIEW;
|
||||
const operation = this.createOperation({
|
||||
type,
|
||||
update,
|
||||
repo_id: this.repoId,
|
||||
view_id: this.viewId,
|
||||
});
|
||||
this.applyOperation(operation);
|
||||
}
|
||||
|
||||
modifyGroupbys(groupbys) {
|
||||
const type = OPERATION_TYPE.MODIFY_GROUPBYS;
|
||||
const operation = this.createOperation({
|
||||
|
@ -167,6 +167,11 @@ export default function apply(data, operation) {
|
||||
data.view.hidden_columns = hidden_columns;
|
||||
return data;
|
||||
}
|
||||
case OPERATION_TYPE.MODIFY_LOCAL_VIEW: {
|
||||
const { update } = operation;
|
||||
data.view = { ...data.view, ...update };
|
||||
return data;
|
||||
}
|
||||
case OPERATION_TYPE.INSERT_COLUMN: {
|
||||
const { column } = operation;
|
||||
const newColumn = new Column(column);
|
||||
|
@ -5,6 +5,7 @@ export const OPERATION_TYPE = {
|
||||
MODIFY_GROUPBYS: 'modify_groupbys',
|
||||
MODIFY_HIDDEN_COLUMNS: 'modify_hidden_columns',
|
||||
MODIFY_SETTINGS: 'modify_settings',
|
||||
MODIFY_LOCAL_VIEW: 'modify_local_view',
|
||||
|
||||
// column
|
||||
INSERT_COLUMN: 'insert_column',
|
||||
@ -56,6 +57,7 @@ export const OPERATION_ATTRIBUTES = {
|
||||
[OPERATION_TYPE.MODIFY_SORTS]: ['repo_id', 'view_id', 'sorts'],
|
||||
[OPERATION_TYPE.MODIFY_GROUPBYS]: ['repo_id', 'view_id', 'groupbys'],
|
||||
[OPERATION_TYPE.MODIFY_HIDDEN_COLUMNS]: ['repo_id', 'view_id', 'hidden_columns'],
|
||||
[OPERATION_TYPE.MODIFY_LOCAL_VIEW]: ['repo_id', 'view_id', 'update'],
|
||||
|
||||
[OPERATION_TYPE.INSERT_COLUMN]: ['repo_id', 'name', 'column_type', 'column_key', 'data', 'column'],
|
||||
[OPERATION_TYPE.RENAME_COLUMN]: ['repo_id', 'column_key', 'new_name', 'old_name'],
|
||||
@ -91,6 +93,7 @@ export const LOCAL_APPLY_OPERATION_TYPE = [
|
||||
OPERATION_TYPE.MODIFY_LOCAL_RECORD,
|
||||
OPERATION_TYPE.MODIFY_LOCAL_COLUMN_DATA,
|
||||
OPERATION_TYPE.DELETE_PEOPLE_PHOTOS,
|
||||
OPERATION_TYPE.MODIFY_LOCAL_VIEW,
|
||||
];
|
||||
|
||||
// apply operation after exec operation on the server
|
||||
|
@ -1,7 +1,10 @@
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useMetadataView } from '../../hooks/metadata-view';
|
||||
import Peoples from './peoples';
|
||||
import PeoplePhotos from './person-photos';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
import { PRIVATE_FILE_TYPE } from '../../../constants';
|
||||
import { FACE_RECOGNITION_VIEW_ID } from '../../constants';
|
||||
|
||||
import './index.css';
|
||||
|
||||
@ -9,7 +12,7 @@ const FaceRecognition = () => {
|
||||
const [showPeopleFaces, setShowPeopleFaces] = useState(false);
|
||||
const peopleRef = useRef(null);
|
||||
|
||||
const { metadata, store, updateCurrentDirent } = useMetadataView();
|
||||
const { metadata, store, updateCurrentDirent, updateCurrentPath } = useMetadataView();
|
||||
|
||||
const peoples = useMemo(() => {
|
||||
if (!Array.isArray(metadata.rows) || metadata.rows.length === 0) return [];
|
||||
@ -26,19 +29,27 @@ const FaceRecognition = () => {
|
||||
|
||||
const openPeople = useCallback((people) => {
|
||||
peopleRef.current = people;
|
||||
const name = people._is_someone ? (people._name || gettext('Person image')) : gettext('Unknown people');
|
||||
updateCurrentPath(`/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${FACE_RECOGNITION_VIEW_ID}/${name}`);
|
||||
setShowPeopleFaces(true);
|
||||
}, []);
|
||||
}, [updateCurrentPath]);
|
||||
|
||||
const closePeople = useCallback(() => {
|
||||
peopleRef.current = null;
|
||||
setShowPeopleFaces(false);
|
||||
updateCurrentDirent();
|
||||
}, [updateCurrentDirent]);
|
||||
updateCurrentPath(`/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${FACE_RECOGNITION_VIEW_ID}`);
|
||||
}, [updateCurrentDirent, updateCurrentPath]);
|
||||
|
||||
const onRename = useCallback((id, newName, oldName) => {
|
||||
store.renamePeopleName(id, newName, oldName);
|
||||
}, [store]);
|
||||
|
||||
useEffect(() => {
|
||||
updateCurrentPath(`/${PRIVATE_FILE_TYPE.FILE_EXTENDED_PROPERTIES}/${FACE_RECOGNITION_VIEW_ID}`);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="sf-metadata-container">
|
||||
{showPeopleFaces ? (
|
||||
|
@ -3,14 +3,6 @@
|
||||
overflow-y: hidden !important;
|
||||
}
|
||||
|
||||
.sf-metadata-people-photos-container .sf-metadata-people-photos-header {
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 16px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.sf-metadata-people-photos-container .sf-metadata-icon-btn {
|
||||
margin-left: -4px;
|
||||
border-radius: 3px;
|
||||
@ -21,35 +13,6 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sf-metadata-people-photos-container .sf-metadata-people-photos-header .sf-metadata-people-photos-header-back {
|
||||
font-size: 14px;
|
||||
height: 24px;
|
||||
min-width: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: -5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.sf-metadata-people-photos-container .sf-metadata-people-photos-header .sf-metadata-people-photos-header-back:hover {
|
||||
background-color: #EFEFEF;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sf-metadata-people-photos-container .sf-metadata-people-photos-header-back .sf3-font-arrow {
|
||||
color: #666;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.sf-metadata-people-photos-container .sf-metadata-people-photos-header .sf-metadata-people-name {
|
||||
margin-left: 4px;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sf-metadata-people-photos-container .sf-metadata-gallery-container {
|
||||
height: calc(100% - 48px);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ const PeoplePhotos = ({ view, people, onClose, onDeletePeoplePhotos, onRemovePeo
|
||||
const [metadata, setMetadata] = useState({ rows: [] });
|
||||
const repoID = window.sfMetadataContext.getSetting('repoID');
|
||||
|
||||
const { deleteFilesCallback } = useMetadataView();
|
||||
const { deleteFilesCallback, store } = useMetadataView();
|
||||
|
||||
const onLoadMore = useCallback(async () => {
|
||||
if (isLoadingMore) return;
|
||||
@ -141,13 +141,14 @@ const PeoplePhotos = ({ view, people, onClose, onDeletePeoplePhotos, onRemovePeo
|
||||
|
||||
const onViewChange = useCallback((update) => {
|
||||
metadataAPI.modifyView(repoID, FACE_RECOGNITION_VIEW_ID, update).then(res => {
|
||||
store.modifyLocalView(update);
|
||||
const newView = { ...metadata.view, ...update };
|
||||
loadData(newView);
|
||||
}).catch(error => {
|
||||
const errorMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errorMessage);
|
||||
});
|
||||
}, [repoID, metadata, loadData]);
|
||||
}, [repoID, metadata, store, loadData]);
|
||||
|
||||
useEffect(() => {
|
||||
loadData({ sorts: view.sorts });
|
||||
@ -173,12 +174,6 @@ const PeoplePhotos = ({ view, people, onClose, onDeletePeoplePhotos, onRemovePeo
|
||||
|
||||
return (
|
||||
<div className="sf-metadata-face-recognition-container sf-metadata-people-photos-container">
|
||||
<div className="sf-metadata-people-photos-header">
|
||||
<div className="sf-metadata-people-photos-header-back" onClick={onClose}>
|
||||
<i className="sf3-font sf3-font-arrow rotate-180"></i>
|
||||
</div>
|
||||
<div className="sf-metadata-people-name">{people._name || gettext('Person image')}</div>
|
||||
</div>
|
||||
<Gallery metadata={metadata} isLoadingMore={isLoadingMore} onLoadMore={onLoadMore} onDelete={handelDelete} onRemoveImage={handelRemove} />
|
||||
</div>
|
||||
);
|
||||
|
@ -2147,6 +2147,10 @@ class LibContentView extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
updatePath = (path) => {
|
||||
this.setState({ path });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { repoID } = this.props;
|
||||
let { currentRepoInfo, userPerm, isCopyMoveProgressDialogShow, isDeleteFolderDialogOpen, errorMsg,
|
||||
@ -2387,6 +2391,7 @@ class LibContentView extends React.Component {
|
||||
onItemsScroll={this.onItemsScroll}
|
||||
eventBus={this.props.eventBus}
|
||||
updateCurrentDirent={this.updateCurrentDirent}
|
||||
updateCurrentPath={this.updatePath}
|
||||
/>
|
||||
:
|
||||
<div className="message err-tip">{gettext('Folder does not exist.')}</div>
|
||||
|
Loading…
Reference in New Issue
Block a user