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
68 changed files with 3032 additions and 814 deletions

View File

@@ -46,4 +46,8 @@ export interface IAccessRepository {
stack: {
checkOwnerAccess(userId: string, stackIds: Set<string>): Promise<Set<string>>;
};
tag: {
checkOwnerAccess(userId: string, tagIds: Set<string>): Promise<Set<string>>;
};
}

View File

@@ -51,6 +51,7 @@ export interface AssetBuilderOptions {
isTrashed?: boolean;
isDuplicate?: boolean;
albumId?: string;
tagId?: string;
personId?: string;
userIds?: string[];
withStacked?: boolean;

View File

@@ -17,6 +17,10 @@ type EmitEventMap = {
'album.update': [{ id: string; updatedBy: string }];
'album.invite': [{ id: string; userId: string }];
// tag events
'asset.tag': [{ assetId: string }];
'asset.untag': [{ assetId: string }];
// user events
'user.signup': [{ notify: boolean; id: string; tempPassword?: string }];
};

View File

@@ -155,6 +155,7 @@ export interface ISidecarWriteJob extends IEntityJob {
latitude?: number;
longitude?: number;
rating?: number;
tags?: true;
}
export interface IDeferrableJob extends IEntityJob {

View File

@@ -1,17 +1,19 @@
import { AssetEntity } from 'src/entities/asset.entity';
import { TagEntity } from 'src/entities/tag.entity';
import { IBulkAsset } from 'src/utils/asset.util';
export const ITagRepository = 'ITagRepository';
export interface ITagRepository {
getById(userId: string, tagId: string): Promise<TagEntity | null>;
export type AssetTagItem = { assetId: string; tagId: string };
export interface ITagRepository extends IBulkAsset {
getAll(userId: string): Promise<TagEntity[]>;
getByValue(userId: string, value: string): Promise<TagEntity | null>;
create(tag: Partial<TagEntity>): Promise<TagEntity>;
update(tag: Partial<TagEntity>): Promise<TagEntity>;
remove(tag: TagEntity): Promise<void>;
hasName(userId: string, name: string): Promise<boolean>;
hasAsset(userId: string, tagId: string, assetId: string): Promise<boolean>;
getAssets(userId: string, tagId: string): Promise<AssetEntity[]>;
addAssets(userId: string, tagId: string, assetIds: string[]): Promise<void>;
removeAssets(userId: string, tagId: string, assetIds: string[]): Promise<void>;
get(id: string): Promise<TagEntity | null>;
update(tag: { id: string } & Partial<TagEntity>): Promise<TagEntity>;
delete(id: string): Promise<void>;
upsertAssetTags({ assetId, tagIds }: { assetId: string; tagIds: string[] }): Promise<void>;
upsertAssetIds(items: AssetTagItem[]): Promise<AssetTagItem[]>;
}