feat: tags (#11980)

* feat: tags

* fix: folder tree icons

* navigate to tag from detail panel

* delete tag

* Tag position and add tag button

* Tag asset in detail panel

* refactor form

* feat: navigate to tag page from clicking on a tag

* feat: delete tags from the tag page

* refactor: moving tag section in detail panel and add + tag button

* feat: tag asset action in detail panel

* refactor add tag form

* fdisable add tag button when there is no selection

* feat: tag bulk endpoint

* feat: tag colors

* chore: clean up

* chore: unit tests

* feat: write tags to sidecar

* Remove tag and auto focus on tag creation form opened

* chore: regenerate migration

* chore: linting

* add color picker to tag edit form

* fix: force render tags timeline on navigating back from asset viewer

* feat: read tags from keywords

* chore: clean up

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Jason Rasmussen 2024-08-29 12:14:03 -04:00 committed by GitHub
parent 682adaa334
commit d08a20bd57
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
68 changed files with 3032 additions and 814 deletions

View file

@ -41,7 +41,10 @@ export const requireAccess = async (access: IAccessRepository, request: AccessRe
}
};
export const checkAccess = async (access: IAccessRepository, { ids, auth, permission }: AccessRequest) => {
export const checkAccess = async (
access: IAccessRepository,
{ ids, auth, permission }: AccessRequest,
): Promise<Set<string>> => {
const idSet = Array.isArray(ids) ? new Set(ids) : ids;
if (idSet.size === 0) {
return new Set<string>();
@ -52,7 +55,10 @@ export const checkAccess = async (access: IAccessRepository, { ids, auth, permis
: checkOtherAccess(access, { auth, permission, ids: idSet });
};
const checkSharedLinkAccess = async (access: IAccessRepository, request: SharedLinkAccessRequest) => {
const checkSharedLinkAccess = async (
access: IAccessRepository,
request: SharedLinkAccessRequest,
): Promise<Set<string>> => {
const { sharedLink, permission, ids } = request;
const sharedLinkId = sharedLink.id;
@ -96,7 +102,7 @@ const checkSharedLinkAccess = async (access: IAccessRepository, request: SharedL
}
};
const checkOtherAccess = async (access: IAccessRepository, request: OtherAccessRequest) => {
const checkOtherAccess = async (access: IAccessRepository, request: OtherAccessRequest): Promise<Set<string>> => {
const { auth, permission, ids } = request;
switch (permission) {
@ -211,6 +217,13 @@ const checkOtherAccess = async (access: IAccessRepository, request: OtherAccessR
return await access.authDevice.checkOwnerAccess(auth.user.id, ids);
}
case Permission.TAG_ASSET:
case Permission.TAG_READ:
case Permission.TAG_UPDATE:
case Permission.TAG_DELETE: {
return await access.tag.checkOwnerAccess(auth.user.id, ids);
}
case Permission.TIMELINE_READ: {
const isOwner = ids.has(auth.user.id) ? new Set([auth.user.id]) : new Set<string>();
const isPartner = await access.timeline.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner));

View file

@ -2,4 +2,4 @@ export const fromChecksum = (checksum: string): Buffer => {
return Buffer.from(checksum, checksum.length === 28 ? 'base64' : 'hex');
};
export const fromMaybeArray = (param: string | string[] | undefined) => (Array.isArray(param) ? param[0] : param);
export const fromMaybeArray = <T>(param: T | T[]) => (Array.isArray(param) ? param[0] : param);

30
server/src/utils/tag.ts Normal file
View file

@ -0,0 +1,30 @@
import { TagEntity } from 'src/entities/tag.entity';
import { ITagRepository } from 'src/interfaces/tag.interface';
type UpsertRequest = { userId: string; tags: string[] };
export const upsertTags = async (repository: ITagRepository, { userId, tags }: UpsertRequest) => {
tags = [...new Set(tags)];
const results: TagEntity[] = [];
for (const tag of tags) {
const parts = tag.split('/');
let parent: TagEntity | undefined;
for (const part of parts) {
const value = parent ? `${parent.value}/${part}` : part;
let tag = await repository.getByValue(userId, value);
if (!tag) {
tag = await repository.create({ userId, value, parent });
}
parent = tag;
}
if (parent) {
results.push(parent);
}
}
return results;
};