From a9ce155cf84646abbb78b7b0516569ff649b4405 Mon Sep 17 00:00:00 2001 From: Mees Frensel <33722705+meesfrensel@users.noreply.github.com> Date: Tue, 22 Sep 2026 22:38:00 +0200 Subject: [PATCH] refactor(web): use actions and ui lib context menu (#31729) --- .../asset-viewer/ActivityViewer.svelte | 67 +++++++---------- web/src/routes/(user)/people/+page.svelte | 47 +----------- .../routes/(user)/people/PeopleCard.svelte | 75 ++++++------------- 3 files changed, 50 insertions(+), 139 deletions(-) diff --git a/web/src/lib/components/asset-viewer/ActivityViewer.svelte b/web/src/lib/components/asset-viewer/ActivityViewer.svelte index 01c970e62..984f48745 100644 --- a/web/src/lib/components/asset-viewer/ActivityViewer.svelte +++ b/web/src/lib/components/asset-viewer/ActivityViewer.svelte @@ -1,7 +1,5 @@ +{#snippet reactionMenu(reaction: ActivityResponseDto, index: number, title: string)} + {#if reaction.user.id === authManager.user.id || isAlbumOwner} + handleDeleteReaction(reaction, index), + }, + ]} + /> + {/if} +{/snippet} +
@@ -133,7 +150,7 @@ > {#each activityManager.activities as reaction, index (reaction.id)} {#if reaction.type === ReactionType.Comment} -
+
@@ -148,24 +165,7 @@ /> {/if} - {#if reaction.user.id === authManager.user.id || isAlbumOwner} -
- - handleDeleteReaction(reaction, index)} - /> - -
- {/if} + {@render reactionMenu(reaction, index, $t('comment_options'))}
{#if (index !== activityManager.activities.length - 1 && !shouldGroup(activityManager.activities[index].createdAt, activityManager.activities[index + 1].createdAt)) || index === activityManager.activities.length - 1} @@ -178,7 +178,7 @@ {/if} {:else if reaction.type === ReactionType.Like}
-
+
@@ -198,24 +198,7 @@ /> {/if} - {#if reaction.user.id === authManager.user.id || isAlbumOwner} -
- - handleDeleteReaction(reaction, index)} - /> - -
- {/if} + {@render reactionMenu(reaction, index, $t('reaction_options'))}
{#if (index !== activityManager.activities.length - 1 && isTenMinutesApart(activityManager.activities[index].createdAt, activityManager.activities[index + 1].createdAt)) || index === activityManager.activities.length - 1}
{ - try { - const updatedPerson = await updatePerson({ - id: detail.id, - personUpdateDto: { isHidden: true }, - }); - - people = people.map((person: PersonResponseDto) => { - if (person.id === updatedPerson.id) { - return updatedPerson; - } - return person; - }); - - toastManager.primary($t('changed_visibility_successfully')); - } catch (error) { - handleError(error, $t('errors.unable_to_hide_person')); - } - }; - - const handleToggleFavorite = async (detail: PersonResponseDto) => { - try { - const updatedPerson = await updatePerson({ - id: detail.id, - personUpdateDto: { isFavorite: !detail.isFavorite }, - }); - - people = people.map((person: PersonResponseDto) => { - if (person.id === updatedPerson.id) { - return updatedPerson; - } - return person; - }); - - toastManager.primary(updatedPerson.isFavorite ? $t('added_to_favorites') : $t('removed_from_favorites')); - } catch (error) { - handleError(error, $t('errors.unable_to_add_remove_favorites', { values: { favorite: detail.isFavorite } })); - } - }; - const handleMergePeople = async (detail: PersonResponseDto) => { await goto(Route.viewPerson(detail, { previousRoute: Route.people(), action: 'merge' })); }; @@ -344,12 +304,7 @@
- handleMergePeople(person)} - onHidePerson={() => handleHidePerson(person)} - onToggleFavorite={() => handleToggleFavorite(person)} - /> + handleMergePeople(person)} /> - import { focusOutside } from '$lib/actions/focus-outside'; - import ActionMenuItem from '$lib/components/ActionMenuItem.svelte'; - import ButtonContextMenu from '$lib/components/shared-components/context-menu/ButtonContextMenu.svelte'; import { Route } from '$lib/route'; import { getPersonActions } from '$lib/services/person.service'; import { getPeopleThumbnailUrl } from '$lib/utils'; import { type PersonResponseDto } from '@immich/sdk'; - import { Icon } from '@immich/ui'; - import { - mdiAccountMultipleCheckOutline, - mdiDotsVertical, - mdiEyeOffOutline, - mdiHeart, - mdiHeartMinusOutline, - mdiHeartOutline, - } from '@mdi/js'; + import { ContextMenuButton, Icon } from '@immich/ui'; + import { mdiAccountMultipleCheckOutline, mdiHeart } from '@mdi/js'; import { t } from 'svelte-i18n'; import ImageThumbnail from '$lib/components/assets/thumbnail/ImageThumbnail.svelte'; - import MenuOption from '$lib/components/shared-components/context-menu/MenuOption.svelte'; type Props = { person: PersonResponseDto; onMergePeople: () => void; - onHidePerson: () => void; - onToggleFavorite: () => void; }; - let { person, onMergePeople, onHidePerson, onToggleFavorite }: Props = $props(); + let { person, onMergePeople }: Props = $props(); - let showVerticalDots = $state(false); + const { SetDateOfBirth, HidePerson, Favorite, Unfavorite } = $derived(getPersonActions($t, person)); - const { SetDateOfBirth } = $derived(getPersonActions($t, person)); + const items = $derived([ + HidePerson, + SetDateOfBirth, + { + icon: mdiAccountMultipleCheckOutline, + title: $t('merge_people'), + onAction: onMergePeople, + }, + Favorite, + Unfavorite, + ]); -
(showVerticalDots = true)} - onmouseleave={() => (showVerticalDots = false)} - role="group" - use:focusOutside={{ onFocusOut: () => (showVerticalDots = false) }} -> - (showVerticalDots = true)} - > +
+
{/if}
-
- {#if showVerticalDots} -
- + - - - - - + class="icon-white-drop-shadow" + translations={{ open_menu: $t('show_person_options') }} + {items} + />
- {/if} +