mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:27:09 -05:00 
			
		
		
		
	feat(web): Add "set as featured" option for an asset (#14879)
This commit is contained in:
		
							parent
							
								
									c3be74c450
								
							
						
					
					
						commit
						b88f98bf66
					
				@ -1142,6 +1142,7 @@
 | 
				
			|||||||
  "set": "Set",
 | 
					  "set": "Set",
 | 
				
			||||||
  "set_as_album_cover": "Set as album cover",
 | 
					  "set_as_album_cover": "Set as album cover",
 | 
				
			||||||
  "set_as_profile_picture": "Set as profile picture",
 | 
					  "set_as_profile_picture": "Set as profile picture",
 | 
				
			||||||
 | 
					  "set_as_featured_photo": "Set as featured photo",
 | 
				
			||||||
  "set_date_of_birth": "Set date of birth",
 | 
					  "set_date_of_birth": "Set date of birth",
 | 
				
			||||||
  "set_profile_picture": "Set profile picture",
 | 
					  "set_profile_picture": "Set profile picture",
 | 
				
			||||||
  "set_slideshow_to_fullscreen": "Set Slideshow to fullscreen",
 | 
					  "set_slideshow_to_fullscreen": "Set Slideshow to fullscreen",
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					  import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
 | 
				
			||||||
 | 
					  import {
 | 
				
			||||||
 | 
					    notificationController,
 | 
				
			||||||
 | 
					    NotificationType,
 | 
				
			||||||
 | 
					  } from '$lib/components/shared-components/notification/notification';
 | 
				
			||||||
 | 
					  import { handleError } from '$lib/utils/handle-error';
 | 
				
			||||||
 | 
					  import { updatePerson, type AssetResponseDto, type PersonResponseDto } from '@immich/sdk';
 | 
				
			||||||
 | 
					  import { mdiFaceManProfile } from '@mdi/js';
 | 
				
			||||||
 | 
					  import { t } from 'svelte-i18n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  interface Props {
 | 
				
			||||||
 | 
					    asset: AssetResponseDto;
 | 
				
			||||||
 | 
					    person: PersonResponseDto;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let { asset, person }: Props = $props();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleSelectFeaturePhoto = async () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await updatePerson({ id: person.id, personUpdateDto: { featureFaceAssetId: asset.id } });
 | 
				
			||||||
 | 
					      notificationController.show({ message: $t('feature_photo_updated'), type: NotificationType.Info });
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      handleError(error, $t('errors.unable_to_set_feature_photo'));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<MenuOption text={$t('set_as_featured_photo')} icon={mdiFaceManProfile} onClick={handleSelectFeaturePhoto} />
 | 
				
			||||||
@ -9,6 +9,7 @@
 | 
				
			|||||||
  import FavoriteAction from '$lib/components/asset-viewer/actions/favorite-action.svelte';
 | 
					  import FavoriteAction from '$lib/components/asset-viewer/actions/favorite-action.svelte';
 | 
				
			||||||
  import RestoreAction from '$lib/components/asset-viewer/actions/restore-action.svelte';
 | 
					  import RestoreAction from '$lib/components/asset-viewer/actions/restore-action.svelte';
 | 
				
			||||||
  import SetAlbumCoverAction from '$lib/components/asset-viewer/actions/set-album-cover-action.svelte';
 | 
					  import SetAlbumCoverAction from '$lib/components/asset-viewer/actions/set-album-cover-action.svelte';
 | 
				
			||||||
 | 
					  import SetFeaturedPhotoAction from '$lib/components/asset-viewer/actions/set-person-featured-action.svelte';
 | 
				
			||||||
  import SetProfilePictureAction from '$lib/components/asset-viewer/actions/set-profile-picture-action.svelte';
 | 
					  import SetProfilePictureAction from '$lib/components/asset-viewer/actions/set-profile-picture-action.svelte';
 | 
				
			||||||
  import ShareAction from '$lib/components/asset-viewer/actions/share-action.svelte';
 | 
					  import ShareAction from '$lib/components/asset-viewer/actions/share-action.svelte';
 | 
				
			||||||
  import ShowDetailAction from '$lib/components/asset-viewer/actions/show-detail-action.svelte';
 | 
					  import ShowDetailAction from '$lib/components/asset-viewer/actions/show-detail-action.svelte';
 | 
				
			||||||
@ -27,6 +28,7 @@
 | 
				
			|||||||
    AssetTypeEnum,
 | 
					    AssetTypeEnum,
 | 
				
			||||||
    type AlbumResponseDto,
 | 
					    type AlbumResponseDto,
 | 
				
			||||||
    type AssetResponseDto,
 | 
					    type AssetResponseDto,
 | 
				
			||||||
 | 
					    type PersonResponseDto,
 | 
				
			||||||
    type StackResponseDto,
 | 
					    type StackResponseDto,
 | 
				
			||||||
  } from '@immich/sdk';
 | 
					  } from '@immich/sdk';
 | 
				
			||||||
  import {
 | 
					  import {
 | 
				
			||||||
@ -50,6 +52,7 @@
 | 
				
			|||||||
  interface Props {
 | 
					  interface Props {
 | 
				
			||||||
    asset: AssetResponseDto;
 | 
					    asset: AssetResponseDto;
 | 
				
			||||||
    album?: AlbumResponseDto | null;
 | 
					    album?: AlbumResponseDto | null;
 | 
				
			||||||
 | 
					    person?: PersonResponseDto | null;
 | 
				
			||||||
    stack?: StackResponseDto | null;
 | 
					    stack?: StackResponseDto | null;
 | 
				
			||||||
    showDetailButton: boolean;
 | 
					    showDetailButton: boolean;
 | 
				
			||||||
    showSlideshow?: boolean;
 | 
					    showSlideshow?: boolean;
 | 
				
			||||||
@ -67,6 +70,7 @@
 | 
				
			|||||||
  let {
 | 
					  let {
 | 
				
			||||||
    asset,
 | 
					    asset,
 | 
				
			||||||
    album = null,
 | 
					    album = null,
 | 
				
			||||||
 | 
					    person = null,
 | 
				
			||||||
    stack = null,
 | 
					    stack = null,
 | 
				
			||||||
    showDetailButton,
 | 
					    showDetailButton,
 | 
				
			||||||
    showSlideshow = false,
 | 
					    showSlideshow = false,
 | 
				
			||||||
@ -169,6 +173,9 @@
 | 
				
			|||||||
          {#if album}
 | 
					          {#if album}
 | 
				
			||||||
            <SetAlbumCoverAction {asset} {album} />
 | 
					            <SetAlbumCoverAction {asset} {album} />
 | 
				
			||||||
          {/if}
 | 
					          {/if}
 | 
				
			||||||
 | 
					          {#if person}
 | 
				
			||||||
 | 
					            <SetFeaturedPhotoAction {asset} {person} />
 | 
				
			||||||
 | 
					          {/if}
 | 
				
			||||||
          {#if asset.type === AssetTypeEnum.Image}
 | 
					          {#if asset.type === AssetTypeEnum.Image}
 | 
				
			||||||
            <SetProfilePictureAction {asset} />
 | 
					            <SetProfilePictureAction {asset} />
 | 
				
			||||||
          {/if}
 | 
					          {/if}
 | 
				
			||||||
 | 
				
			|||||||
@ -30,6 +30,7 @@
 | 
				
			|||||||
    type ActivityResponseDto,
 | 
					    type ActivityResponseDto,
 | 
				
			||||||
    type AlbumResponseDto,
 | 
					    type AlbumResponseDto,
 | 
				
			||||||
    type AssetResponseDto,
 | 
					    type AssetResponseDto,
 | 
				
			||||||
 | 
					    type PersonResponseDto,
 | 
				
			||||||
    type StackResponseDto,
 | 
					    type StackResponseDto,
 | 
				
			||||||
  } from '@immich/sdk';
 | 
					  } from '@immich/sdk';
 | 
				
			||||||
  import { onDestroy, onMount, untrack } from 'svelte';
 | 
					  import { onDestroy, onMount, untrack } from 'svelte';
 | 
				
			||||||
@ -56,6 +57,7 @@
 | 
				
			|||||||
    withStacked?: boolean;
 | 
					    withStacked?: boolean;
 | 
				
			||||||
    isShared?: boolean;
 | 
					    isShared?: boolean;
 | 
				
			||||||
    album?: AlbumResponseDto | null;
 | 
					    album?: AlbumResponseDto | null;
 | 
				
			||||||
 | 
					    person?: PersonResponseDto | null;
 | 
				
			||||||
    onAction?: OnAction | undefined;
 | 
					    onAction?: OnAction | undefined;
 | 
				
			||||||
    reactions?: ActivityResponseDto[];
 | 
					    reactions?: ActivityResponseDto[];
 | 
				
			||||||
    onClose: (dto: { asset: AssetResponseDto }) => void;
 | 
					    onClose: (dto: { asset: AssetResponseDto }) => void;
 | 
				
			||||||
@ -72,6 +74,7 @@
 | 
				
			|||||||
    withStacked = false,
 | 
					    withStacked = false,
 | 
				
			||||||
    isShared = false,
 | 
					    isShared = false,
 | 
				
			||||||
    album = null,
 | 
					    album = null,
 | 
				
			||||||
 | 
					    person = null,
 | 
				
			||||||
    onAction = undefined,
 | 
					    onAction = undefined,
 | 
				
			||||||
    reactions = $bindable([]),
 | 
					    reactions = $bindable([]),
 | 
				
			||||||
    onClose,
 | 
					    onClose,
 | 
				
			||||||
@ -429,6 +432,7 @@
 | 
				
			|||||||
      <AssetViewerNavBar
 | 
					      <AssetViewerNavBar
 | 
				
			||||||
        {asset}
 | 
					        {asset}
 | 
				
			||||||
        {album}
 | 
					        {album}
 | 
				
			||||||
 | 
					        {person}
 | 
				
			||||||
        {stack}
 | 
					        {stack}
 | 
				
			||||||
        showDetailButton={enableDetailPanel}
 | 
					        showDetailButton={enableDetailPanel}
 | 
				
			||||||
        showSlideshow={!!assetStore}
 | 
					        showSlideshow={!!assetStore}
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,7 @@
 | 
				
			|||||||
    type ScrollTargetListener,
 | 
					    type ScrollTargetListener,
 | 
				
			||||||
  } from '$lib/utils/timeline-util';
 | 
					  } from '$lib/utils/timeline-util';
 | 
				
			||||||
  import { TUNABLES } from '$lib/utils/tunables';
 | 
					  import { TUNABLES } from '$lib/utils/tunables';
 | 
				
			||||||
  import type { AlbumResponseDto, AssetResponseDto } from '@immich/sdk';
 | 
					  import type { AlbumResponseDto, AssetResponseDto, PersonResponseDto } from '@immich/sdk';
 | 
				
			||||||
  import { throttle } from 'lodash-es';
 | 
					  import { throttle } from 'lodash-es';
 | 
				
			||||||
  import { onDestroy, onMount, type Snippet } from 'svelte';
 | 
					  import { onDestroy, onMount, type Snippet } from 'svelte';
 | 
				
			||||||
  import Portal from '../shared-components/portal/portal.svelte';
 | 
					  import Portal from '../shared-components/portal/portal.svelte';
 | 
				
			||||||
@ -52,6 +52,7 @@
 | 
				
			|||||||
    showArchiveIcon?: boolean;
 | 
					    showArchiveIcon?: boolean;
 | 
				
			||||||
    isShared?: boolean;
 | 
					    isShared?: boolean;
 | 
				
			||||||
    album?: AlbumResponseDto | null;
 | 
					    album?: AlbumResponseDto | null;
 | 
				
			||||||
 | 
					    person?: PersonResponseDto | null;
 | 
				
			||||||
    isShowDeleteConfirmation?: boolean;
 | 
					    isShowDeleteConfirmation?: boolean;
 | 
				
			||||||
    onSelect?: (asset: AssetResponseDto) => void;
 | 
					    onSelect?: (asset: AssetResponseDto) => void;
 | 
				
			||||||
    onEscape?: () => void;
 | 
					    onEscape?: () => void;
 | 
				
			||||||
@ -70,6 +71,7 @@
 | 
				
			|||||||
    showArchiveIcon = false,
 | 
					    showArchiveIcon = false,
 | 
				
			||||||
    isShared = false,
 | 
					    isShared = false,
 | 
				
			||||||
    album = null,
 | 
					    album = null,
 | 
				
			||||||
 | 
					    person = null,
 | 
				
			||||||
    isShowDeleteConfirmation = $bindable(false),
 | 
					    isShowDeleteConfirmation = $bindable(false),
 | 
				
			||||||
    onSelect = () => {},
 | 
					    onSelect = () => {},
 | 
				
			||||||
    onEscape = () => {},
 | 
					    onEscape = () => {},
 | 
				
			||||||
@ -914,6 +916,7 @@
 | 
				
			|||||||
        preloadAssets={$preloadAssets}
 | 
					        preloadAssets={$preloadAssets}
 | 
				
			||||||
        {isShared}
 | 
					        {isShared}
 | 
				
			||||||
        {album}
 | 
					        {album}
 | 
				
			||||||
 | 
					        {person}
 | 
				
			||||||
        onAction={handleAction}
 | 
					        onAction={handleAction}
 | 
				
			||||||
        onPrevious={handlePrevious}
 | 
					        onPrevious={handlePrevious}
 | 
				
			||||||
        onNext={handleNext}
 | 
					        onNext={handleNext}
 | 
				
			||||||
 | 
				
			|||||||
@ -454,6 +454,7 @@
 | 
				
			|||||||
  {#key person.id}
 | 
					  {#key person.id}
 | 
				
			||||||
    <AssetGrid
 | 
					    <AssetGrid
 | 
				
			||||||
      enableRouting={true}
 | 
					      enableRouting={true}
 | 
				
			||||||
 | 
					      {person}
 | 
				
			||||||
      {assetStore}
 | 
					      {assetStore}
 | 
				
			||||||
      {assetInteraction}
 | 
					      {assetInteraction}
 | 
				
			||||||
      isSelectionMode={viewMode === PersonPageViewMode.SELECT_PERSON}
 | 
					      isSelectionMode={viewMode === PersonPageViewMode.SELECT_PERSON}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user