1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-05-09 08:28:42 +00:00

Merge pull request from haiwen/feature/sort_tag_files

Feature/sort tag files
This commit is contained in:
Michael An 2025-03-23 09:41:50 +08:00 committed by GitHub
commit 1841d799a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 169 additions and 47 deletions
frontend/src
components/cur-dir-path
metadata
components/cell-formatter/file-tags
constants
tag
constants
hooks
utils
views/tag-files

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 { PRIVATE_FILE_TYPE } from '../../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 = {
repoID: PropTypes.string.isRequired,
@ -120,8 +121,8 @@ class DirTool extends React.Component {
if (isTagView) {
return (
<div className="dir-tool">
<TagsTableSearcher />
{isAllTagsView && <SortSetter />}
{isAllTagsView && <TagsTableSearcher />}
{isAllTagsView ? <AllTagsSortSetter /> : <TagFilesSortSetter />}
</div>
);
}
@ -137,50 +138,50 @@ class DirTool extends React.Component {
</div>
}
{menuItems.length > 0 &&
<Dropdown isOpen={isDropdownMenuOpen} toggle={this.toggleDropdownMenu}>
<DropdownToggle
tag="i"
id="cur-folder-more-op-toggle"
className='cur-view-path-btn sf3-font-more sf3-font'
data-toggle="dropdown"
title={gettext('More operations')}
aria-label={gettext('More operations')}
aria-expanded={isDropdownMenuOpen}
>
</DropdownToggle>
<DropdownMenu>
{menuItems.map((menuItem, index) => {
if (menuItem === 'Divider') {
return <DropdownItem key={index} divider />;
} else {
return (
<DropdownItem
key={index}
onClick={this.onMenuItemClick.bind(this, menuItem)}
onKeyDown={this.onMenuItemKeyDown.bind(this, menuItem)}
>{menuItem.value}
</DropdownItem>
);
}
})}
</DropdownMenu>
</Dropdown>
<Dropdown isOpen={isDropdownMenuOpen} toggle={this.toggleDropdownMenu}>
<DropdownToggle
tag="i"
id="cur-folder-more-op-toggle"
className='cur-view-path-btn sf3-font-more sf3-font'
data-toggle="dropdown"
title={gettext('More operations')}
aria-label={gettext('More operations')}
aria-expanded={isDropdownMenuOpen}
>
</DropdownToggle>
<DropdownMenu>
{menuItems.map((menuItem, index) => {
if (menuItem === 'Divider') {
return <DropdownItem key={index} divider />;
} else {
return (
<DropdownItem
key={index}
onClick={this.onMenuItemClick.bind(this, menuItem)}
onKeyDown={this.onMenuItemKeyDown.bind(this, menuItem)}
>{menuItem.value}
</DropdownItem>
);
}
})}
</DropdownMenu>
</Dropdown>
}
</div>
{this.state.isRepoTagDialogOpen &&
<CustomizePopover
popoverClassName="list-tag-popover"
target="cur-folder-more-op-toggle"
hidePopover={this.hidePopover}
hidePopoverWithEsc={this.hidePopover}
boundariesElement={document.body}
placement={'bottom-end'}
>
<ListTagPopover
repoID={repoID}
onListTagCancel={this.toggleCancel}
/>
</CustomizePopover>
<CustomizePopover
popoverClassName="list-tag-popover"
target="cur-folder-more-op-toggle"
hidePopover={this.hidePopover}
hidePopoverWithEsc={this.hidePopover}
boundariesElement={document.body}
placement={'bottom-end'}
>
<ListTagPopover
repoID={repoID}
onListTagCancel={this.toggleCancel}
/>
</CustomizePopover>
}
</React.Fragment>
);

View File

@ -20,7 +20,9 @@ const FileTagsFormatter = ({ tagsData, value: oldValue, className, children: emp
const tag = getRowById(tagsData, item);
const tagColor = getTagColor(tag);
const tagName = getTagName(tag);
if (!showName) return <span className="sf-metadata-ui-tag-color" style={{ backgroundColor: tagColor }}></span>;
if (!showName) return (
<span key={item} className="sf-metadata-ui-tag-color" style={{ backgroundColor: tagColor }}></span>
);
return (
<div key={item} className="sf-metadata-ui-tag" title={tagName}>
<span className="sf-metadata-ui-tag-color mr-1" style={{ backgroundColor: tagColor }}></span>

View File

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

View File

@ -10,3 +10,16 @@ export const ALL_TAGS_SORT_KEY = {
CHILD_TAGS_COUNT: 'child_tags_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 { TAG_FILE_KEY } from '../constants/file';
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 { getTagFilesLinks } from '../utils/cell';
import { PRIVATE_COLUMN_KEY } from '../constants';
import URLDecorator from '../../utils/url-decorator';
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.
const TagViewContext = React.createContext(null);
@ -156,6 +157,14 @@ export const TagViewProvider = ({
});
}, [repoID, convertFileCallback]);
const sortFiles = useCallback((sort) => {
const sorted = sortTagFiles(tagFiles, sort);
setTagFiles({
...tagFiles,
rows: sorted,
});
}, [tagFiles]);
useEffect(() => {
setLoading(true);
const childTagsIds = getChildTagsIds(tagID, nodeKey);
@ -165,7 +174,10 @@ export const TagViewProvider = ({
}
tagsAPI.getTagsFiles(repoID, tagsIds).then(res => {
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);
}).catch(error => {
const errorMessage = Utils.getErrorMsg(error);
@ -175,6 +187,21 @@ export const TagViewProvider = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [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 (
<TagViewContext.Provider value={{
isLoading,
@ -194,6 +221,7 @@ export const TagViewProvider = ({
downloadTagFiles,
renameTagFile,
convertFile,
sortFiles,
}}>
{children}
</TagViewContext.Provider>

View File

@ -1,7 +1,10 @@
import { compareString } from '../../metadata/utils/sort';
import { enableSeadoc, fileAuditEnabled, isPro } from '../../utils/constants';
import TextTranslation from '../../utils/text-translation';
import { Utils } from '../../utils/utils';
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) => {
return fileId ? tagFiles.rows.find(file => file._id === fileId) : '';
@ -15,6 +18,14 @@ export const getFileParentDir = (file) => {
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) => {
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
@ -60,3 +71,38 @@ export const getTagFileOperationList = (fileName, repo, canModify) => {
}
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;