diff --git a/frontend/src/tag/api.js b/frontend/src/tag/api.js
index a34428664db..22fd9024d71 100644
--- a/frontend/src/tag/api.js
+++ b/frontend/src/tag/api.js
@@ -62,8 +62,8 @@ class TagsManagerAPI {
return this.req.delete(url);
};
- getTags = (repoID) => {
- const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/tags/';
+ getTags = (repoID, start = 0, limit = 1000) => {
+ const url = this.server + '/api/v2.1/repos/' + repoID + '/metadata/tags/?start=' + start + '&limit=' + limit;
return this.req.get(url);
};
diff --git a/frontend/src/tag/context.js b/frontend/src/tag/context.js
index b4e5a421e68..5a37dafc90e 100644
--- a/frontend/src/tag/context.js
+++ b/frontend/src/tag/context.js
@@ -93,8 +93,8 @@ class Context {
};
// tags
- getTags = () => {
- return this.api.getTags(this.repoId);
+ getTags = ({ start, limit }) => {
+ return this.api.getTags(this.repoId, start, limit);
};
addTags = (tags = []) => {
diff --git a/frontend/src/tag/model/tagsData.js b/frontend/src/tag/model/tagsData.js
index 76625764317..59102b1c4ee 100644
--- a/frontend/src/tag/model/tagsData.js
+++ b/frontend/src/tag/model/tagsData.js
@@ -16,6 +16,8 @@ class TagsData {
this.row_ids.push(record._id);
this.id_row_map[record._id] = record;
});
+
+ this.hasMore = true;
}
}
diff --git a/frontend/src/tag/store/operations/apply.js b/frontend/src/tag/store/operations/apply.js
index ac17908f8b2..c092b3e0883 100644
--- a/frontend/src/tag/store/operations/apply.js
+++ b/frontend/src/tag/store/operations/apply.js
@@ -14,11 +14,17 @@ export default function apply(data, operation) {
case OPERATION_TYPE.ADD_RECORDS: {
const { tags } = operation;
const { rows } = data;
- const updatedRows = [...rows, ...tags];
+ const updatedRows = [...rows];
tags.forEach(tag => {
- const id = tag[PRIVATE_COLUMN_KEY.ID];
- data.id_row_map[id] = tag;
- data.row_ids.push(id);
+ const tagID = tag[PRIVATE_COLUMN_KEY.ID];
+ const rowIndex = updatedRows.findIndex(r => r._id === tagID);
+ data.id_row_map[tagID] = tag;
+ if (rowIndex === -1) {
+ data.row_ids.push(tagID);
+ updatedRows.push(tag);
+ } else {
+ updatedRows[rowIndex] = tag;
+ }
});
data.rows = updatedRows;
return data;
diff --git a/frontend/src/tag/views/all-tags/index.js b/frontend/src/tag/views/all-tags/index.js
index ac436652b17..6ed3352aa93 100644
--- a/frontend/src/tag/views/all-tags/index.js
+++ b/frontend/src/tag/views/all-tags/index.js
@@ -2,15 +2,18 @@ import React, { useEffect, useState, useMemo, useCallback } from 'react';
import { CenteredLoading } from '@seafile/sf-metadata-ui-component';
import { useTags } from '../../hooks';
import Main from './main';
-import { EVENT_BUS_TYPE } from '../../../metadata/constants';
+import { EVENT_BUS_TYPE, PER_LOAD_NUMBER } from '../../../metadata/constants';
import TagFiles from './tag-files';
+import { Utils } from '../../../utils/utils';
+import toaster from '../../../components/toast';
import './index.css';
const AllTags = ({ ...params }) => {
const [displayTag, setDisplayTag] = useState('');
+ const [isLoadingMore, setLoadingMore] = useState(false);
- const { isLoading, isReloading, tagsData, context } = useTags();
+ const { isLoading, isReloading, tagsData, store, context } = useTags();
useEffect(() => {
const eventBus = context.eventBus;
@@ -28,6 +31,23 @@ const AllTags = ({ ...params }) => {
setDisplayTag(tagID);
}, [displayTag]);
+ const onLoadMore = useCallback(async () => {
+ if (isLoadingMore) return;
+ if (!tagsData.hasMore) return;
+ setLoadingMore(true);
+
+ try {
+ await store.loadMore(PER_LOAD_NUMBER);
+ setLoadingMore(false);
+ } catch (error) {
+ const errorMsg = Utils.getErrorMsg(error);
+ toaster.danger(errorMsg);
+ setLoadingMore(false);
+ return;
+ }
+
+ }, [isLoadingMore, tagsData, store]);
+
if (isLoading || isReloading) return ();
if (displayTag) {
@@ -38,7 +58,7 @@ const AllTags = ({ ...params }) => {
diff --git a/frontend/src/tag/views/all-tags/main/index.js b/frontend/src/tag/views/all-tags/main/index.js
index ae0b7444654..d34fb0b5131 100644
--- a/frontend/src/tag/views/all-tags/main/index.js
+++ b/frontend/src/tag/views/all-tags/main/index.js
@@ -9,7 +9,7 @@ import { isCellValueChanged } from '../../../../metadata/utils/cell';
import './index.css';
-const Main = React.memo(({ context, tags, onChangeDisplayTag }) => {
+const Main = React.memo(({ context, tags, onChangeDisplayTag, onLoadMore }) => {
const tableRef = useRef(null);
useEffect(() => {
@@ -22,10 +22,16 @@ const Main = React.memo(({ context, tags, onChangeDisplayTag }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
- const handelScroll = debounce(() => {
+ const handelScroll = debounce(async () => {
+ if (!tableRef.current) return;
+ const { scrollTop, scrollHeight, clientHeight } = tableRef.current;
const currentLocalStorage = context.localStorage;
- const currentScrollTop = tableRef.current.scrollTop || 0;
+ const currentScrollTop = scrollTop || 0;
currentLocalStorage.setItem('scroll_top', currentScrollTop);
+
+ if (scrollTop + clientHeight >= scrollHeight - 10) {
+ onLoadMore();
+ }
}, 200);
if (tags.length === 0) {
@@ -57,6 +63,7 @@ Main.propTypes = {
context: PropTypes.object,
tags: PropTypes.array,
onChangeDisplayTag: PropTypes.func,
+ onLoadMore: PropTypes.func,
};
export default Main;
diff --git a/seahub/repo_metadata/apis.py b/seahub/repo_metadata/apis.py
index 7b17e630aee..72ad8deb117 100644
--- a/seahub/repo_metadata/apis.py
+++ b/seahub/repo_metadata/apis.py
@@ -1660,6 +1660,24 @@ class MetadataTags(APIView):
throttle_classes = (UserRateThrottle,)
def get(self, request, repo_id):
+ start = request.GET.get('start', 0)
+ limit = request.GET.get('limit', 100)
+
+ try:
+ start = int(start)
+ limit = int(limit)
+ except:
+ start = 0
+ limit = 1000
+
+ if start < 0:
+ error_msg = 'start invalid'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if limit < 0:
+ error_msg = 'limit invalid'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
metadata = RepoMetadata.objects.filter(repo_id=repo_id).first()
if not metadata or not metadata.enabled:
error_msg = f'The metadata module is disabled for repo {repo_id}.'
@@ -1690,7 +1708,7 @@ def get(self, request, repo_id):
if not tags_table_id:
return api_error(status.HTTP_404_NOT_FOUND, 'tags not be used')
- sql = f'SELECT * FROM `{TAGS_TABLE.name}` ORDER BY `_ctime` LIMIT {0}, {1000}'
+ sql = f'SELECT * FROM `{TAGS_TABLE.name}` ORDER BY `_ctime` LIMIT {start}, {limit}'
try:
query_result = metadata_server_api.query_rows(sql)
@@ -1715,7 +1733,7 @@ def post(self, request, repo_id):
repo = seafile_api.get_repo(repo_id)
if not repo:
- error_msg = 'Library %s not found.' % repo_id
+ error_msg = f'Library {repo_id} not found.'
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if not can_read_metadata(request, repo_id):
@@ -1738,29 +1756,55 @@ def post(self, request, repo_id):
if not tags_table_id:
return api_error(status.HTTP_404_NOT_FOUND, 'tags not be used')
+ exist_tags = []
+ new_tags = []
+ tags_names = [tag_data.get(TAGS_TABLE.columns.name.name, '') for tag_data in tags_data]
+ tags_names_str = ', '.join([f'"{tag_name}"' for tag_name in tags_names])
+ sql = f'SELECT * FROM {TAGS_TABLE.name} WHERE `{TAGS_TABLE.columns.name.name}` in ({tags_names_str})'
+
try:
- resp = metadata_server_api.insert_rows(tags_table_id, tags_data)
+ exist_rows = metadata_server_api.query_rows(sql)
+ exist_tags = exist_rows.get('results', [])
except Exception as e:
- logger.error(e)
+ logger.exception(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
- row_ids = resp.get('row_ids', [])
+ if exist_tags:
+ for tag_data in tags_data:
+ tag_name = tag_data.get(TAGS_TABLE.columns.name.name, '')
+ if tag_name not in tags_names:
+ new_tags.append(tag_data)
+ else:
+ new_tags = tags_data
- if not row_ids:
+ tags = exist_tags
+ if not new_tags:
+ return Response({ 'tags': tags })
+
+ try:
+ resp = metadata_server_api.insert_rows(tags_table_id, new_tags)
+ except Exception as e:
+ logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
- sql = 'SELECT * FROM %s WHERE `%s` in (%s)' % (TAGS_TABLE.name, TAGS_TABLE.columns.id.name, ', '.join(["'%s'" % id for id in row_ids]))
+ row_ids = resp.get('row_ids', [])
+ if not row_ids:
+ return Response({ 'tags': tags })
+ row_ids_str = ', '.join([f'"{id}"' for id in row_ids])
+ sql = f'SELECT * FROM {TAGS_TABLE.name} WHERE `{TAGS_TABLE.columns.id.name}` in ({row_ids_str})'
try:
query_new_rows = metadata_server_api.query_rows(sql)
+ new_tags_data = query_new_rows.get('results', [])
+ tags.extend(new_tags_data)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
- return Response({ 'tags': query_new_rows.get('results', []) })
+ return Response({ 'tags': tags })
def put(self, request, repo_id):
tags_data = request.data.get('tags_data')