diff --git a/frontend/src/components/cur-dir-path/dir-tool.js b/frontend/src/components/cur-dir-path/dir-tool.js
index 7b0333d08c..a2dbf928db 100644
--- a/frontend/src/components/cur-dir-path/dir-tool.js
+++ b/frontend/src/components/cur-dir-path/dir-tool.js
@@ -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 (
-
- {isAllTagsView &&
}
+ {isAllTagsView &&
}
+ {isAllTagsView ?
:
}
);
}
@@ -137,50 +138,50 @@ class DirTool extends React.Component {
}
{menuItems.length > 0 &&
-
-
-
-
- {menuItems.map((menuItem, index) => {
- if (menuItem === 'Divider') {
- return ;
- } else {
- return (
- {menuItem.value}
-
- );
- }
- })}
-
-
+
+
+
+
+ {menuItems.map((menuItem, index) => {
+ if (menuItem === 'Divider') {
+ return ;
+ } else {
+ return (
+ {menuItem.value}
+
+ );
+ }
+ })}
+
+
}
{this.state.isRepoTagDialogOpen &&
-
-
-
+
+
+
}
);
diff --git a/frontend/src/metadata/components/cell-formatter/file-tags/index.js b/frontend/src/metadata/components/cell-formatter/file-tags/index.js
index bff1e99bed..2f064c375b 100644
--- a/frontend/src/metadata/components/cell-formatter/file-tags/index.js
+++ b/frontend/src/metadata/components/cell-formatter/file-tags/index.js
@@ -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 ;
+ if (!showName) return (
+
+ );
return (
diff --git a/frontend/src/metadata/constants/event-bus-type.js b/frontend/src/metadata/constants/event-bus-type.js
index 8dda5ca3d1..2d977a37bb 100644
--- a/frontend/src/metadata/constants/event-bus-type.js
+++ b/frontend/src/metadata/constants/event-bus-type.js
@@ -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',
diff --git a/frontend/src/tag/constants/sort.js b/frontend/src/tag/constants/sort.js
index 64eb8ff106..43d4ecabb4 100644
--- a/frontend/src/tag/constants/sort.js
+++ b/frontend/src/tag/constants/sort.js
@@ -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'
+};
diff --git a/frontend/src/tag/hooks/tag-view.js b/frontend/src/tag/hooks/tag-view.js
index e7edfaa22c..580569a3aa 100644
--- a/frontend/src/tag/hooks/tag-view.js
+++ b/frontend/src/tag/hooks/tag-view.js
@@ -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 (
{children}
diff --git a/frontend/src/tag/utils/file.js b/frontend/src/tag/utils/file.js
index 64c1591e0e..aa48b9fd6d 100644
--- a/frontend/src/tag/utils/file.js
+++ b/frontend/src/tag/utils/file.js
@@ -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);
+};
diff --git a/frontend/src/tag/views/tag-files/sort-setter.js b/frontend/src/tag/views/tag-files/sort-setter.js
new file mode 100644
index 0000000000..e8881f5bd1
--- /dev/null
+++ b/frontend/src/tag/views/tag-files/sort-setter.js
@@ -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 (
+
+ );
+};
+
+export default SortSetter;