1
0
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:
Aries 2025-01-03 15:11:06 +08:00 committed by GitHub
parent 3795c054ea
commit db287ccf9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 110 additions and 121 deletions

View File

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

View File

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

View File

@ -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) {

View File

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

View File

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

View File

@ -363,6 +363,7 @@ export const MetadataViewProvider = ({
insertColumn,
updateFileTags,
addFolder: params.addFolder,
updateCurrentPath: params.updateCurrentPath,
}}
>
{children}

View File

@ -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`;

View File

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

View File

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

View File

@ -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);

View File

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

View File

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

View File

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

View File

@ -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>
);

View File

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