From 8136d7fd545d192fe945a8c2ef4f369ab6ae0000 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 8 Jan 2026 16:37:58 -0500 Subject: [PATCH] refactor(web): tag service (#25142) --- web/src/lib/managers/event-manager.svelte.ts | 6 ++ web/src/lib/modals/TagCreateModal.svelte | 55 +++-------- web/src/lib/modals/TagEditModal.svelte | 38 ++----- web/src/lib/services/tag.service.ts | 98 +++++++++++++++++++ .../[[assetId=id]]/+page.svelte | 98 ++++++------------- 5 files changed, 157 insertions(+), 138 deletions(-) create mode 100644 web/src/lib/services/tag.service.ts diff --git a/web/src/lib/managers/event-manager.svelte.ts b/web/src/lib/managers/event-manager.svelte.ts index 8f1ff80607..ef4cc61daf 100644 --- a/web/src/lib/managers/event-manager.svelte.ts +++ b/web/src/lib/managers/event-manager.svelte.ts @@ -1,5 +1,6 @@ import type { ThemeSetting } from '$lib/managers/theme-manager.svelte'; import type { ReleaseEvent } from '$lib/types'; +import type { TreeNode } from '$lib/utils/tree-utils'; import type { AlbumResponseDto, ApiKeyResponseDto, @@ -10,6 +11,7 @@ import type { QueueResponseDto, SharedLinkResponseDto, SystemConfigDto, + TagResponseDto, UserAdminResponseDto, WorkflowResponseDto, } from '@immich/sdk'; @@ -42,6 +44,10 @@ export type Events = { SharedLinkUpdate: [SharedLinkResponseDto]; SharedLinkDelete: [SharedLinkResponseDto]; + TagCreate: [TagResponseDto]; + TagUpdate: [TagResponseDto]; + TagDelete: [TreeNode]; + UserAdminCreate: [UserAdminResponseDto]; UserAdminUpdate: [UserAdminResponseDto]; UserAdminRestore: [UserAdminResponseDto]; diff --git a/web/src/lib/modals/TagCreateModal.svelte b/web/src/lib/modals/TagCreateModal.svelte index 5c48e83b08..268053a4cf 100644 --- a/web/src/lib/modals/TagCreateModal.svelte +++ b/web/src/lib/modals/TagCreateModal.svelte @@ -1,14 +1,12 @@ - - -
-

- {$t('create_tag_description')} -

-
- -
-
- -
-
-
- - - - - - - -
+ + {$t('create_tag_description')} + + + + diff --git a/web/src/lib/modals/TagEditModal.svelte b/web/src/lib/modals/TagEditModal.svelte index 40c8655d5e..14a137a16a 100644 --- a/web/src/lib/modals/TagEditModal.svelte +++ b/web/src/lib/modals/TagEditModal.svelte @@ -1,47 +1,29 @@ - - -
-
- -
-
-
- - - - - - - -
+ + + diff --git a/web/src/lib/services/tag.service.ts b/web/src/lib/services/tag.service.ts new file mode 100644 index 0000000000..bbdc843c46 --- /dev/null +++ b/web/src/lib/services/tag.service.ts @@ -0,0 +1,98 @@ +import { eventManager } from '$lib/managers/event-manager.svelte'; +import TagCreateModal from '$lib/modals/TagCreateModal.svelte'; +import TagEditModal from '$lib/modals/TagEditModal.svelte'; +import { handleError } from '$lib/utils/handle-error'; +import { getFormatter } from '$lib/utils/i18n'; +import type { TreeNode } from '$lib/utils/tree-utils'; +import { deleteTag, updateTag, upsertTags, type TagUpdateDto } from '@immich/sdk'; +import { modalManager, toastManager, type ActionItem } from '@immich/ui'; +import { mdiPencil, mdiPlus, mdiTrashCanOutline } from '@mdi/js'; +import { type MessageFormatter } from 'svelte-i18n'; + +export const getTagActions = ($t: MessageFormatter, tag: TreeNode) => { + const Create: ActionItem = { + title: $t('create_tag'), + icon: mdiPlus, + onAction: () => modalManager.show(TagCreateModal, { baseTag: tag }), + }; + + const Update: ActionItem = { + title: $t('edit_tag'), + icon: mdiPencil, + $if: () => tag.path.length > 0, + onAction: () => modalManager.show(TagEditModal, { tag }), + }; + + const Delete: ActionItem = { + title: $t('delete_tag'), + icon: mdiTrashCanOutline, + $if: () => tag.path.length > 0, + onAction: () => handleDeleteTag(tag), + }; + + return { Create, Update, Delete }; +}; + +export const handleCreateTag = async (tagValue: string) => { + const $t = await getFormatter(); + + try { + const [tag] = await upsertTags({ tagUpsertDto: { tags: [tagValue] } }); + if (!tag) { + return; + } + + toastManager.success($t('tag_created', { values: { tag: tag.value } })); + eventManager.emit('TagCreate', tag); + + return true; + } catch (error) { + handleError(error, $t('errors.something_went_wrong')); + } +}; + +export const handleUpdateTag = async (tag: TreeNode, dto: TagUpdateDto) => { + const $t = await getFormatter(); + + if (!tag.id) { + return; + } + + try { + const response = await updateTag({ id: tag.id, tagUpdateDto: dto }); + + toastManager.success($t('tag_updated', { values: { tag: tag.value } })); + eventManager.emit('TagUpdate', response); + + return true; + } catch (error) { + handleError(error, $t('errors.something_went_wrong')); + } +}; + +const handleDeleteTag = async (tag: TreeNode) => { + const $t = await getFormatter(); + + const tagId = tag.id; + if (!tagId) { + return; + } + + const confirmed = await modalManager.showDialog({ + title: $t('delete_tag'), + prompt: $t('delete_tag_confirmation_prompt', { values: { tagName: tag.value } }), + confirmText: $t('delete'), + }); + + if (!confirmed) { + return; + } + + try { + await deleteTag({ id: tagId }); + eventManager.emit('TagDelete', tag); + toastManager.success(); + } catch (error) { + handleError(error, $t('errors.something_went_wrong')); + } +}; diff --git a/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte index 7f873aa918..4d163f78ed 100644 --- a/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -1,24 +1,14 @@ - + + + {#snippet sidebar()} @@ -114,23 +93,6 @@ {/snippet} - {#snippet buttons()} - - - - {#if tag.path.length > 0} - - - {/if} - - {/snippet} -