mirror of
https://github.com/immich-app/immich.git
synced 2025-12-18 01:11:07 +03:00
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:
@@ -6169,7 +6169,7 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateTagDto"
|
||||
"$ref": "#/components/schemas/TagCreateDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6201,6 +6201,91 @@
|
||||
"tags": [
|
||||
"Tags"
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"operationId": "upsertTags",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TagUpsertDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TagResponseDto"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Tags"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/tags/assets": {
|
||||
"put": {
|
||||
"operationId": "bulkTagAssets",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TagBulkAssetsDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TagBulkAssetsResponseDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Tags"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/tags/{id}": {
|
||||
@@ -6218,7 +6303,7 @@
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"204": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
@@ -6277,7 +6362,7 @@
|
||||
"Tags"
|
||||
]
|
||||
},
|
||||
"patch": {
|
||||
"put": {
|
||||
"operationId": "updateTag",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -6294,7 +6379,7 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpdateTagDto"
|
||||
"$ref": "#/components/schemas/TagUpdateDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6346,7 +6431,7 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AssetIdsDto"
|
||||
"$ref": "#/components/schemas/BulkIdsDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6358,50 +6443,7 @@
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/AssetIdsResponseDto"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Tags"
|
||||
]
|
||||
},
|
||||
"get": {
|
||||
"operationId": "getTagAssets",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/AssetResponseDto"
|
||||
"$ref": "#/components/schemas/BulkIdResponseDto"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
@@ -6442,7 +6484,7 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AssetIdsDto"
|
||||
"$ref": "#/components/schemas/BulkIdsDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6454,7 +6496,7 @@
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/AssetIdsResponseDto"
|
||||
"$ref": "#/components/schemas/BulkIdResponseDto"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
@@ -6549,6 +6591,15 @@
|
||||
"$ref": "#/components/schemas/TimeBucketSize"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tagId",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "timeBucket",
|
||||
"required": true,
|
||||
@@ -6684,6 +6735,15 @@
|
||||
"$ref": "#/components/schemas/TimeBucketSize"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tagId",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "userId",
|
||||
"required": false,
|
||||
@@ -8685,21 +8745,6 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"CreateTagDto": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/components/schemas/TagTypeEnum"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"type"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"DownloadArchiveInfo": {
|
||||
"properties": {
|
||||
"assetIds": {
|
||||
@@ -10053,6 +10098,7 @@
|
||||
"tag.read",
|
||||
"tag.update",
|
||||
"tag.delete",
|
||||
"tag.asset",
|
||||
"admin.user.create",
|
||||
"admin.user.read",
|
||||
"admin.user.update",
|
||||
@@ -11848,36 +11894,113 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TagBulkAssetsDto": {
|
||||
"properties": {
|
||||
"assetIds": {
|
||||
"items": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"tagIds": {
|
||||
"items": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"assetIds",
|
||||
"tagIds"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TagBulkAssetsResponseDto": {
|
||||
"properties": {
|
||||
"count": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"count"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TagCreateDto": {
|
||||
"properties": {
|
||||
"color": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parentId": {
|
||||
"format": "uuid",
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TagResponseDto": {
|
||||
"properties": {
|
||||
"color": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdAt": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/components/schemas/TagTypeEnum"
|
||||
"updatedAt": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"userId": {
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"createdAt",
|
||||
"id",
|
||||
"name",
|
||||
"type",
|
||||
"userId"
|
||||
"updatedAt",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TagTypeEnum": {
|
||||
"enum": [
|
||||
"OBJECT",
|
||||
"FACE",
|
||||
"CUSTOM"
|
||||
"TagUpdateDto": {
|
||||
"properties": {
|
||||
"color": {
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"TagUpsertDto": {
|
||||
"properties": {
|
||||
"tags": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"tags"
|
||||
],
|
||||
"type": "string"
|
||||
"type": "object"
|
||||
},
|
||||
"TimeBucketResponseDto": {
|
||||
"properties": {
|
||||
@@ -12021,14 +12144,6 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"UpdateTagDto": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"UsageByUserDto": {
|
||||
"properties": {
|
||||
"photos": {
|
||||
|
||||
@@ -198,10 +198,12 @@ export type AssetStackResponseDto = {
|
||||
primaryAssetId: string;
|
||||
};
|
||||
export type TagResponseDto = {
|
||||
color?: string;
|
||||
createdAt: string;
|
||||
id: string;
|
||||
name: string;
|
||||
"type": TagTypeEnum;
|
||||
userId: string;
|
||||
updatedAt: string;
|
||||
value: string;
|
||||
};
|
||||
export type AssetResponseDto = {
|
||||
/** base64 encoded sha1 hash */
|
||||
@@ -1171,12 +1173,23 @@ export type ReverseGeocodingStateResponseDto = {
|
||||
lastImportFileName: string | null;
|
||||
lastUpdate: string | null;
|
||||
};
|
||||
export type CreateTagDto = {
|
||||
export type TagCreateDto = {
|
||||
color?: string;
|
||||
name: string;
|
||||
"type": TagTypeEnum;
|
||||
parentId?: string | null;
|
||||
};
|
||||
export type UpdateTagDto = {
|
||||
name?: string;
|
||||
export type TagUpsertDto = {
|
||||
tags: string[];
|
||||
};
|
||||
export type TagBulkAssetsDto = {
|
||||
assetIds: string[];
|
||||
tagIds: string[];
|
||||
};
|
||||
export type TagBulkAssetsResponseDto = {
|
||||
count: number;
|
||||
};
|
||||
export type TagUpdateDto = {
|
||||
color?: string | null;
|
||||
};
|
||||
export type TimeBucketResponseDto = {
|
||||
count: number;
|
||||
@@ -2835,8 +2848,8 @@ export function getAllTags(opts?: Oazapfts.RequestOpts) {
|
||||
...opts
|
||||
}));
|
||||
}
|
||||
export function createTag({ createTagDto }: {
|
||||
createTagDto: CreateTagDto;
|
||||
export function createTag({ tagCreateDto }: {
|
||||
tagCreateDto: TagCreateDto;
|
||||
}, opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 201;
|
||||
@@ -2844,7 +2857,31 @@ export function createTag({ createTagDto }: {
|
||||
}>("/tags", oazapfts.json({
|
||||
...opts,
|
||||
method: "POST",
|
||||
body: createTagDto
|
||||
body: tagCreateDto
|
||||
})));
|
||||
}
|
||||
export function upsertTags({ tagUpsertDto }: {
|
||||
tagUpsertDto: TagUpsertDto;
|
||||
}, opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
data: TagResponseDto[];
|
||||
}>("/tags", oazapfts.json({
|
||||
...opts,
|
||||
method: "PUT",
|
||||
body: tagUpsertDto
|
||||
})));
|
||||
}
|
||||
export function bulkTagAssets({ tagBulkAssetsDto }: {
|
||||
tagBulkAssetsDto: TagBulkAssetsDto;
|
||||
}, opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
data: TagBulkAssetsResponseDto;
|
||||
}>("/tags/assets", oazapfts.json({
|
||||
...opts,
|
||||
method: "PUT",
|
||||
body: tagBulkAssetsDto
|
||||
})));
|
||||
}
|
||||
export function deleteTag({ id }: {
|
||||
@@ -2865,56 +2902,46 @@ export function getTagById({ id }: {
|
||||
...opts
|
||||
}));
|
||||
}
|
||||
export function updateTag({ id, updateTagDto }: {
|
||||
export function updateTag({ id, tagUpdateDto }: {
|
||||
id: string;
|
||||
updateTagDto: UpdateTagDto;
|
||||
tagUpdateDto: TagUpdateDto;
|
||||
}, opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
data: TagResponseDto;
|
||||
}>(`/tags/${encodeURIComponent(id)}`, oazapfts.json({
|
||||
...opts,
|
||||
method: "PATCH",
|
||||
body: updateTagDto
|
||||
method: "PUT",
|
||||
body: tagUpdateDto
|
||||
})));
|
||||
}
|
||||
export function untagAssets({ id, assetIdsDto }: {
|
||||
export function untagAssets({ id, bulkIdsDto }: {
|
||||
id: string;
|
||||
assetIdsDto: AssetIdsDto;
|
||||
bulkIdsDto: BulkIdsDto;
|
||||
}, opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
data: AssetIdsResponseDto[];
|
||||
data: BulkIdResponseDto[];
|
||||
}>(`/tags/${encodeURIComponent(id)}/assets`, oazapfts.json({
|
||||
...opts,
|
||||
method: "DELETE",
|
||||
body: assetIdsDto
|
||||
body: bulkIdsDto
|
||||
})));
|
||||
}
|
||||
export function getTagAssets({ id }: {
|
||||
export function tagAssets({ id, bulkIdsDto }: {
|
||||
id: string;
|
||||
bulkIdsDto: BulkIdsDto;
|
||||
}, opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
data: AssetResponseDto[];
|
||||
}>(`/tags/${encodeURIComponent(id)}/assets`, {
|
||||
...opts
|
||||
}));
|
||||
}
|
||||
export function tagAssets({ id, assetIdsDto }: {
|
||||
id: string;
|
||||
assetIdsDto: AssetIdsDto;
|
||||
}, opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
data: AssetIdsResponseDto[];
|
||||
data: BulkIdResponseDto[];
|
||||
}>(`/tags/${encodeURIComponent(id)}/assets`, oazapfts.json({
|
||||
...opts,
|
||||
method: "PUT",
|
||||
body: assetIdsDto
|
||||
body: bulkIdsDto
|
||||
})));
|
||||
}
|
||||
export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key, order, personId, size, timeBucket, userId, withPartners, withStacked }: {
|
||||
export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key, order, personId, size, tagId, timeBucket, userId, withPartners, withStacked }: {
|
||||
albumId?: string;
|
||||
isArchived?: boolean;
|
||||
isFavorite?: boolean;
|
||||
@@ -2923,6 +2950,7 @@ export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key,
|
||||
order?: AssetOrder;
|
||||
personId?: string;
|
||||
size: TimeBucketSize;
|
||||
tagId?: string;
|
||||
timeBucket: string;
|
||||
userId?: string;
|
||||
withPartners?: boolean;
|
||||
@@ -2940,6 +2968,7 @@ export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key,
|
||||
order,
|
||||
personId,
|
||||
size,
|
||||
tagId,
|
||||
timeBucket,
|
||||
userId,
|
||||
withPartners,
|
||||
@@ -2948,7 +2977,7 @@ export function getTimeBucket({ albumId, isArchived, isFavorite, isTrashed, key,
|
||||
...opts
|
||||
}));
|
||||
}
|
||||
export function getTimeBuckets({ albumId, isArchived, isFavorite, isTrashed, key, order, personId, size, userId, withPartners, withStacked }: {
|
||||
export function getTimeBuckets({ albumId, isArchived, isFavorite, isTrashed, key, order, personId, size, tagId, userId, withPartners, withStacked }: {
|
||||
albumId?: string;
|
||||
isArchived?: boolean;
|
||||
isFavorite?: boolean;
|
||||
@@ -2957,6 +2986,7 @@ export function getTimeBuckets({ albumId, isArchived, isFavorite, isTrashed, key
|
||||
order?: AssetOrder;
|
||||
personId?: string;
|
||||
size: TimeBucketSize;
|
||||
tagId?: string;
|
||||
userId?: string;
|
||||
withPartners?: boolean;
|
||||
withStacked?: boolean;
|
||||
@@ -2973,6 +3003,7 @@ export function getTimeBuckets({ albumId, isArchived, isFavorite, isTrashed, key
|
||||
order,
|
||||
personId,
|
||||
size,
|
||||
tagId,
|
||||
userId,
|
||||
withPartners,
|
||||
withStacked
|
||||
@@ -3162,11 +3193,6 @@ export enum AlbumUserRole {
|
||||
Editor = "editor",
|
||||
Viewer = "viewer"
|
||||
}
|
||||
export enum TagTypeEnum {
|
||||
Object = "OBJECT",
|
||||
Face = "FACE",
|
||||
Custom = "CUSTOM"
|
||||
}
|
||||
export enum AssetTypeEnum {
|
||||
Image = "IMAGE",
|
||||
Video = "VIDEO",
|
||||
@@ -3257,6 +3283,7 @@ export enum Permission {
|
||||
TagRead = "tag.read",
|
||||
TagUpdate = "tag.update",
|
||||
TagDelete = "tag.delete",
|
||||
TagAsset = "tag.asset",
|
||||
AdminUserCreate = "admin.user.create",
|
||||
AdminUserRead = "admin.user.read",
|
||||
AdminUserUpdate = "admin.user.update",
|
||||
|
||||
Reference in New Issue
Block a user