mirror of
https://github.com/immich-app/immich.git
synced 2025-12-06 01:10:00 +03:00
feat(web): Shared album owner labels (#21171)
* - pass available album users along to the thumbnail through the asset-date-group - show a small user-avatar in bottom right of thumbnail * - change owner to their name in white text instead of the avatar * cleanup * - cleanup albumUsers creation - use font-light for the user's name * fix lint * format * - add toggle to show/hide asset owner names * update new Timeline with albumUsers * add @idubnori suggestion for the name font * Don't show 'view owners' button if the album doesn't have editors * add missing import * format * fix(web): #21171 (#24298) fix: Bind timelineManager to Timeline component --------- Co-authored-by: idubnori <i.dub.nori@gmail.com> Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
import { getAssetOriginalUrl, getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { timeToSeconds } from '$lib/utils/date-time';
|
||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
||||
import { AssetMediaSize, AssetVisibility } from '@immich/sdk';
|
||||
import { AssetMediaSize, AssetVisibility, type UserResponseDto } from '@immich/sdk';
|
||||
import {
|
||||
mdiArchiveArrowDownOutline,
|
||||
mdiCameraBurst,
|
||||
@@ -46,6 +46,7 @@
|
||||
imageClass?: ClassValue;
|
||||
brokenAssetClass?: ClassValue;
|
||||
dimmed?: boolean;
|
||||
albumUsers?: UserResponseDto[];
|
||||
onClick?: (asset: TimelineAsset) => void;
|
||||
onSelect?: (asset: TimelineAsset) => void;
|
||||
onMouseEvent?: (event: { isMouseOver: boolean; selectedGroupIndex: number }) => void;
|
||||
@@ -64,6 +65,7 @@
|
||||
readonly = false,
|
||||
showArchiveIcon = false,
|
||||
showStackedIcon = true,
|
||||
albumUsers = [],
|
||||
onClick = undefined,
|
||||
onSelect = undefined,
|
||||
onMouseEvent = undefined,
|
||||
@@ -85,6 +87,8 @@
|
||||
let width = $derived(thumbnailSize || thumbnailWidth || 235);
|
||||
let height = $derived(thumbnailSize || thumbnailHeight || 235);
|
||||
|
||||
let assetOwner = $derived(albumUsers?.find((user) => user.id === asset.ownerId) ?? null);
|
||||
|
||||
const onIconClickedHandler = (e?: MouseEvent) => {
|
||||
e?.stopPropagation();
|
||||
e?.preventDefault();
|
||||
@@ -268,6 +272,14 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !!assetOwner}
|
||||
<div class="absolute bottom-1 end-2 max-w-[50%]">
|
||||
<p class="text-xs font-medium text-white drop-shadow-lg max-w-[100%] truncate">
|
||||
{assetOwner.name}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !authManager.isSharedLink && showArchiveIcon && asset.visibility === AssetVisibility.Archive}
|
||||
<div class={['absolute start-2', asset.isFavorite ? 'bottom-10' : 'bottom-2']}>
|
||||
<Icon data-icon-archive icon={mdiArchiveArrowDownOutline} size="24" class="text-white" />
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
import { mobileDevice } from '$lib/stores/mobile-device.svelte';
|
||||
import { isAssetViewerRoute, navigate } from '$lib/utils/navigation';
|
||||
import { getTimes, type ScrubberListener } from '$lib/utils/timeline-util';
|
||||
import { type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
|
||||
import { type AlbumResponseDto, type PersonResponseDto, type UserResponseDto } from '@immich/sdk';
|
||||
import { DateTime } from 'luxon';
|
||||
import { onDestroy, onMount, type Snippet } from 'svelte';
|
||||
import type { UpdatePayload } from 'vite';
|
||||
@@ -49,6 +49,7 @@
|
||||
showArchiveIcon?: boolean;
|
||||
isShared?: boolean;
|
||||
album?: AlbumResponseDto | null;
|
||||
albumUsers?: UserResponseDto[];
|
||||
person?: PersonResponseDto | null;
|
||||
isShowDeleteConfirmation?: boolean;
|
||||
onSelect?: (asset: TimelineAsset) => void;
|
||||
@@ -81,6 +82,7 @@
|
||||
showArchiveIcon = false,
|
||||
isShared = false,
|
||||
album = null,
|
||||
albumUsers = [],
|
||||
person = null,
|
||||
isShowDeleteConfirmation = $bindable(false),
|
||||
onSelect = () => {},
|
||||
@@ -702,6 +704,7 @@
|
||||
showStackedIcon={withStacked}
|
||||
{showArchiveIcon}
|
||||
{asset}
|
||||
{albumUsers}
|
||||
{groupIndex}
|
||||
onClick={(asset) => {
|
||||
if (typeof onThumbnailClick === 'function') {
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
} from '@immich/sdk';
|
||||
import { Button, Icon, IconButton, modalManager, toastManager } from '@immich/ui';
|
||||
import {
|
||||
mdiAccountEyeOutline,
|
||||
mdiArrowLeft,
|
||||
mdiCogOutline,
|
||||
mdiDeleteOutline,
|
||||
@@ -100,6 +101,7 @@
|
||||
let isCreatingSharedAlbum = $state(false);
|
||||
let isShowActivity = $state(false);
|
||||
let albumOrder: AssetOrder | undefined = $state(data.album.order);
|
||||
let showAlbumUsers = $state(false);
|
||||
|
||||
const assetInteraction = new AssetInteraction();
|
||||
const timelineInteraction = new AssetInteraction();
|
||||
@@ -290,6 +292,11 @@
|
||||
let album = $derived(data.album);
|
||||
let albumId = $derived(album.id);
|
||||
|
||||
const containsEditors = $derived(album?.shared && album.albumUsers.some(({ role }) => role === AlbumUserRole.Editor));
|
||||
const albumUsers = $derived(
|
||||
showAlbumUsers && containsEditors ? [album.owner, ...album.albumUsers.map(({ user }) => user)] : [],
|
||||
);
|
||||
|
||||
$effect(() => {
|
||||
if (!album.isActivityEnabled && activityManager.commentCount === 0) {
|
||||
isShowActivity = false;
|
||||
@@ -418,6 +425,7 @@
|
||||
<Timeline
|
||||
enableRouting={viewMode === AlbumPageViewMode.SELECT_ASSETS ? false : true}
|
||||
{album}
|
||||
{albumUsers}
|
||||
bind:timelineManager
|
||||
{options}
|
||||
assetInteraction={currentAssetIntersection}
|
||||
@@ -597,6 +605,17 @@
|
||||
{#snippet trailing()}
|
||||
<CastButton />
|
||||
|
||||
{#if containsEditors}
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
shape="round"
|
||||
color="secondary"
|
||||
aria-label="view asset owners"
|
||||
icon={mdiAccountEyeOutline}
|
||||
onclick={() => (showAlbumUsers = !showAlbumUsers)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if isEditor}
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
|
||||
Reference in New Issue
Block a user