Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: tag duplicate name #7209

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions frontend/src/tag/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/tag/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []) => {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/tag/model/tagsData.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class TagsData {
this.row_ids.push(record._id);
this.id_row_map[record._id] = record;
});

this.hasMore = true;
}

}
Expand Down
14 changes: 10 additions & 4 deletions frontend/src/tag/store/operations/apply.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
26 changes: 23 additions & 3 deletions frontend/src/tag/views/all-tags/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 (<CenteredLoading />);

if (displayTag) {
Expand All @@ -38,7 +58,7 @@ const AllTags = ({ ...params }) => {
<div className="sf-metadata-tags-wrapper sf-metadata-all-tags-wrapper">
<div className="sf-metadata-tags-main">
<div className="sf-metadata-all-tags-container">
<Main tags={tags} context={context} onChangeDisplayTag={onChangeDisplayTag} />
<Main tags={tags} context={context} onChangeDisplayTag={onChangeDisplayTag} onLoadMore={onLoadMore} />
</div>
</div>
</div>
Expand Down
13 changes: 10 additions & 3 deletions frontend/src/tag/views/all-tags/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand All @@ -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) {
Expand Down Expand Up @@ -57,6 +63,7 @@ Main.propTypes = {
context: PropTypes.object,
tags: PropTypes.array,
onChangeDisplayTag: PropTypes.func,
onLoadMore: PropTypes.func,
};

export default Main;
60 changes: 52 additions & 8 deletions seahub/repo_metadata/apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}.'
Expand Down Expand Up @@ -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)
Expand All @@ -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):
Expand All @@ -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')
Expand Down
Loading