mirror of
https://github.com/immich-app/immich.git
synced 2024-12-31 00:43:56 -05:00
feat(web): Add "set as featured" option for an asset (#14879)
This commit is contained in:
parent
c3be74c450
commit
b88f98bf66
6 changed files with 46 additions and 1 deletions
|
@ -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…
Reference in a new issue