1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-25 23:02:26 +00:00

sort tag files

This commit is contained in:
zhouwenxuan
2025-03-20 16:29:39 +08:00
committed by Michael An
parent e9c61f2bec
commit d27ea6be9f
6 changed files with 171 additions and 54 deletions

View File

@@ -12,7 +12,8 @@ import MetadataViewToolBar from '../../metadata/components/view-toolbar';
import TagsTableSearcher from '../../tag/views/all-tags/tags-table/tags-searcher'; import TagsTableSearcher from '../../tag/views/all-tags/tags-table/tags-searcher';
import { PRIVATE_FILE_TYPE } from '../../constants'; import { PRIVATE_FILE_TYPE } from '../../constants';
import { ALL_TAGS_ID } from '../../tag/constants'; import { ALL_TAGS_ID } from '../../tag/constants';
import SortSetter from '../../tag/views/all-tags/tags-table/sort-setter'; import AllTagsSortSetter from '../../tag/views/all-tags/tags-table/sort-setter';
import TagFilesSortSetter from '../../tag/views/tag-files/sort-setter';
const propTypes = { const propTypes = {
repoID: PropTypes.string.isRequired, repoID: PropTypes.string.isRequired,
@@ -120,8 +121,8 @@ class DirTool extends React.Component {
if (isTagView) { if (isTagView) {
return ( return (
<div className="dir-tool"> <div className="dir-tool">
<TagsTableSearcher /> {isAllTagsView && <TagsTableSearcher />}
{isAllTagsView && <SortSetter />} {isAllTagsView ? <AllTagsSortSetter /> : <TagFilesSortSetter />}
</div> </div>
); );
} }
@@ -131,57 +132,57 @@ class DirTool extends React.Component {
<div className="dir-tool d-flex"> <div className="dir-tool d-flex">
<ViewModes currentViewMode={currentMode} switchViewMode={this.props.switchViewMode} /> <ViewModes currentViewMode={currentMode} switchViewMode={this.props.switchViewMode} />
<SortMenu sortBy={sortBy} sortOrder={sortOrder} onSelectSortOption={this.onSelectSortOption} /> <SortMenu sortBy={sortBy} sortOrder={sortOrder} onSelectSortOption={this.onSelectSortOption} />
{(!isCustomPermission) && {(!isCustomPermission) && (
<div className="cur-view-path-btn" onClick={onToggleDetail}> <div className="cur-view-path-btn" onClick={onToggleDetail}>
<span className="sf3-font sf3-font-info" aria-label={propertiesText} title={propertiesText}></span> <span className="sf3-font sf3-font-info" aria-label={propertiesText} title={propertiesText}></span>
</div> </div>
} )}
{menuItems.length > 0 && {menuItems.length > 0 && (
<Dropdown isOpen={isDropdownMenuOpen} toggle={this.toggleDropdownMenu}> <Dropdown isOpen={isDropdownMenuOpen} toggle={this.toggleDropdownMenu}>
<DropdownToggle <DropdownToggle
tag="i" tag="i"
id="cur-folder-more-op-toggle" id="cur-folder-more-op-toggle"
className='cur-view-path-btn sf3-font-more sf3-font' className='cur-view-path-btn sf3-font-more sf3-font'
data-toggle="dropdown" data-toggle="dropdown"
title={gettext('More operations')} title={gettext('More operations')}
aria-label={gettext('More operations')} aria-label={gettext('More operations')}
aria-expanded={isDropdownMenuOpen} aria-expanded={isDropdownMenuOpen}
> >
</DropdownToggle> </DropdownToggle>
<DropdownMenu> <DropdownMenu>
{menuItems.map((menuItem, index) => { {menuItems.map((menuItem, index) => {
if (menuItem === 'Divider') { if (menuItem === 'Divider') {
return <DropdownItem key={index} divider />; return <DropdownItem key={index} divider />;
} else { } else {
return ( return (
<DropdownItem <DropdownItem
key={index} key={index}
onClick={this.onMenuItemClick.bind(this, menuItem)} onClick={this.onMenuItemClick.bind(this, menuItem)}
onKeyDown={this.onMenuItemKeyDown.bind(this, menuItem)} onKeyDown={this.onMenuItemKeyDown.bind(this, menuItem)}
>{menuItem.value} >{menuItem.value}
</DropdownItem> </DropdownItem>
); );
} }
})} })}
</DropdownMenu> </DropdownMenu>
</Dropdown> </Dropdown>
} )}
</div> </div>
{this.state.isRepoTagDialogOpen && {this.state.isRepoTagDialogOpen && (
<CustomizePopover <CustomizePopover
popoverClassName="list-tag-popover" popoverClassName="list-tag-popover"
target="cur-folder-more-op-toggle" target="cur-folder-more-op-toggle"
hidePopover={this.hidePopover} hidePopover={this.hidePopover}
hidePopoverWithEsc={this.hidePopover} hidePopoverWithEsc={this.hidePopover}
boundariesElement={document.body} boundariesElement={document.body}
placement={'bottom-end'} placement={'bottom-end'}
> >
<ListTagPopover <ListTagPopover
repoID={repoID} repoID={repoID}
onListTagCancel={this.toggleCancel} onListTagCancel={this.toggleCancel}
/> />
</CustomizePopover> </CustomizePopover>
} )}
</React.Fragment> </React.Fragment>
); );
} }

View File

@@ -98,6 +98,7 @@ export const EVENT_BUS_TYPE = {
DELETE_TAG_FILES: 'delete_tag_files', DELETE_TAG_FILES: 'delete_tag_files',
SELECT_TAG_FILES: 'select_tag_files', SELECT_TAG_FILES: 'select_tag_files',
UNSELECT_TAG_FILES: 'unselect_tag_files', UNSELECT_TAG_FILES: 'unselect_tag_files',
MODIFY_TAG_FILES_SORT: 'modify_tag_files_sort',
// tags // tags
SELECT_TAGS: 'select_tags', SELECT_TAGS: 'select_tags',

View File

@@ -1,12 +1,21 @@
export const ALL_TAGS_SORT = 'all_tags_sort'; export const ALL_TAGS_SORT = 'all_tags_sort';
export const TAGS_DEFAULT_SORT = { export const TAGS_DEFAULT_SORT = {
sort_by: 'name', sort_by: 'name',
order: 'asc' order: 'asc'
}; };
export const ALL_TAGS_SORT_KEY = { export const ALL_TAGS_SORT_KEY = {
NAME: 'name', NAME: 'name',
CHILD_TAGS_COUNT: 'child_tags_count', CHILD_TAGS_COUNT: 'child_tags_count',
TAG_FILE_COUNT: 'tag_file_count' TAG_FILE_COUNT: 'tag_file_count'
}; };
export const TAG_FILES_SORT = 'tag_files_sort';
export const TAG_FILES_DEFAULT_SORT = {
sort_by: 'name',
order: 'asc'
};
export const TAG_FILES_SORT_KEY = {
NAME: 'name',
SIZE: 'size',
TIME: 'time'
};

View File

@@ -9,12 +9,13 @@ import { getAllChildTagsIdsFromNode } from '../utils/tree';
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import { TAG_FILE_KEY } from '../constants/file'; import { TAG_FILE_KEY } from '../constants/file';
import { EVENT_BUS_TYPE } from '../../metadata/constants'; import { EVENT_BUS_TYPE } from '../../metadata/constants';
import { getFileById } from '../utils/file'; import { getFileById, sortTagFiles } from '../utils/file';
import { getRowById } from '../../components/sf-table/utils/table'; import { getRowById } from '../../components/sf-table/utils/table';
import { getTagFilesLinks } from '../utils/cell'; import { getTagFilesLinks } from '../utils/cell';
import { PRIVATE_COLUMN_KEY } from '../constants'; import { PRIVATE_COLUMN_KEY } from '../constants';
import URLDecorator from '../../utils/url-decorator'; import URLDecorator from '../../utils/url-decorator';
import { fileServerRoot, useGoFileserver } from '../../utils/constants'; import { fileServerRoot, useGoFileserver } from '../../utils/constants';
import { TAG_FILES_DEFAULT_SORT, TAG_FILES_SORT } from '../constants/sort';
// This hook provides content related to seahub interaction, such as whether to enable extended attributes, views data, etc. // This hook provides content related to seahub interaction, such as whether to enable extended attributes, views data, etc.
const TagViewContext = React.createContext(null); const TagViewContext = React.createContext(null);
@@ -156,6 +157,14 @@ export const TagViewProvider = ({
}); });
}, [repoID, convertFileCallback]); }, [repoID, convertFileCallback]);
const sortFiles = useCallback((sort) => {
const sorted = sortTagFiles(tagFiles, sort);
setTagFiles({
...tagFiles,
rows: sorted,
});
}, [tagFiles]);
useEffect(() => { useEffect(() => {
setLoading(true); setLoading(true);
const childTagsIds = getChildTagsIds(tagID, nodeKey); const childTagsIds = getChildTagsIds(tagID, nodeKey);
@@ -165,7 +174,10 @@ export const TagViewProvider = ({
} }
tagsAPI.getTagsFiles(repoID, tagsIds).then(res => { tagsAPI.getTagsFiles(repoID, tagsIds).then(res => {
const rows = res.data?.results || []; const rows = res.data?.results || [];
setTagFiles({ columns: res.data?.metadata || [], rows }); const savedSort = window.sfTagsDataContext?.localStorage?.getItem(TAG_FILES_SORT);
const sort = savedSort ? JSON.parse(savedSort) : TAG_FILES_DEFAULT_SORT;
const sorted = sortTagFiles(rows, sort);
setTagFiles({ columns: res.data?.metadata || [], rows: sorted });
setLoading(false); setLoading(false);
}).catch(error => { }).catch(error => {
const errorMessage = Utils.getErrorMsg(error); const errorMessage = Utils.getErrorMsg(error);
@@ -175,6 +187,21 @@ export const TagViewProvider = ({
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [repoID, tagID, nodeKey]); }, [repoID, tagID, nodeKey]);
useEffect(() => {
const unsubscribeModifyTagFilesSort = window.sfTagsDataContext?.eventBus?.subscribe(EVENT_BUS_TYPE.MODIFY_TAG_FILES_SORT, (sort) => {
const files = tagFiles?.rows || [];
const sorted = sortTagFiles(files, sort);
setTagFiles({
...tagFiles,
rows: sorted,
});
});
return () => {
unsubscribeModifyTagFilesSort && unsubscribeModifyTagFilesSort();
};
}, [tagFiles]);
return ( return (
<TagViewContext.Provider value={{ <TagViewContext.Provider value={{
isLoading, isLoading,
@@ -194,6 +221,7 @@ export const TagViewProvider = ({
downloadTagFiles, downloadTagFiles,
renameTagFile, renameTagFile,
convertFile, convertFile,
sortFiles,
}}> }}>
{children} {children}
</TagViewContext.Provider> </TagViewContext.Provider>

View File

@@ -1,7 +1,11 @@
import { getFileMTimeFromRecord, getFileSizedFromRecord } from '../../metadata/utils/cell';
import { compareString } from '../../metadata/utils/sort';
import { enableSeadoc, fileAuditEnabled, isPro } from '../../utils/constants'; import { enableSeadoc, fileAuditEnabled, isPro } from '../../utils/constants';
import TextTranslation from '../../utils/text-translation'; import TextTranslation from '../../utils/text-translation';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import { TAG_FILE_KEY } from '../constants/file'; import { TAG_FILE_KEY } from '../constants/file';
import { TAG_FILES_SORT_KEY } from '../constants/sort';
import { getSortBy, getSortOrder } from './sort';
export const getFileById = (tagFiles, fileId) => { export const getFileById = (tagFiles, fileId) => {
return fileId ? tagFiles.rows.find(file => file._id === fileId) : ''; return fileId ? tagFiles.rows.find(file => file._id === fileId) : '';
@@ -15,6 +19,14 @@ export const getFileParentDir = (file) => {
return file ? file[TAG_FILE_KEY.PARENT_DIR] : ''; return file ? file[TAG_FILE_KEY.PARENT_DIR] : '';
}; };
export const getFileMTime = (file) => {
return file ? file[TAG_FILE_KEY.FILE_MTIME] : '';
};
export const getFileSize = (file) => {
return file ? file[TAG_FILE_KEY.SIZE] : '';
};
export const getTagFileOperationList = (fileName, repo, canModify) => { export const getTagFileOperationList = (fileName, repo, canModify) => {
const { const {
SHARE, DOWNLOAD, DELETE, RENAME, MOVE, COPY, HISTORY, ACCESS_LOG, OPEN_VIA_CLIENT, CONVERT_AND_EXPORT, CONVERT_TO_MARKDOWN, CONVERT_TO_DOCX, EXPORT_DOCX, CONVERT_TO_SDOC, EXPORT_SDOC SHARE, DOWNLOAD, DELETE, RENAME, MOVE, COPY, HISTORY, ACCESS_LOG, OPEN_VIA_CLIENT, CONVERT_AND_EXPORT, CONVERT_TO_MARKDOWN, CONVERT_TO_DOCX, EXPORT_DOCX, CONVERT_TO_SDOC, EXPORT_SDOC
@@ -60,3 +72,38 @@ export const getTagFileOperationList = (fileName, repo, canModify) => {
} }
return menuList; return menuList;
}; };
export const sortTagFiles = (files, sort) => {
const sortBy = getSortBy(sort);
const order = getSortOrder(sort);
const compare = (a, b) => {
let valueA = '';
let valueB = '';
switch (sortBy) {
case TAG_FILES_SORT_KEY.NAME:
valueA = getFileName(a);
valueB = getFileName(b);
break;
case TAG_FILES_SORT_KEY.SIZE:
valueA = getFileSize(a);
valueB = getFileSize(b);
break;
case TAG_FILES_SORT_KEY.TIME:
valueA = getFileMTime(a);
valueB = getFileMTime(b);
break;
default:
break;
}
const result =
sortBy === TAG_FILES_SORT_KEY.SIZE
? valueA - valueB
: compareString(valueA, valueB);
return order === 'asc' ? result : -result;
};
return files.sort(compare);
};

View File

@@ -0,0 +1,31 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import SortMenu from '../../../components/sort-menu';
import { EVENT_BUS_TYPE } from '../../../metadata/constants';
import { TAG_FILES_SORT, TAG_FILES_DEFAULT_SORT } from '../../constants/sort';
import { getSortBy, getSortOrder } from '../../utils/sort';
const SortSetter = () => {
const [sort, setSort] = useState(TAG_FILES_DEFAULT_SORT);
const eventBus = useMemo(() => window.sfTagsDataContext?.eventBus, []);
const localStorage = useMemo(() => window.sfTagsDataContext?.localStorage, []);
const onSelectSortOption = useCallback((item) => {
const [sortBy, order] = item.value.split('-');
const newSort = { sort_by: sortBy, order };
setSort(newSort);
eventBus && eventBus.dispatch(EVENT_BUS_TYPE.MODIFY_TAG_FILES_SORT, newSort);
}, [eventBus]);
useEffect(() => {
const storedSort = localStorage && localStorage.getItem(TAG_FILES_SORT);
const sort = storedSort ? JSON.parse(storedSort) : TAG_FILES_DEFAULT_SORT;
setSort(sort);
}, [localStorage]);
return (
<SortMenu sortBy={getSortBy(sort)} sortOrder={getSortOrder(sort)} onSelectSortOption={onSelectSortOption} />
);
};
export default SortSetter;