diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte
index 1047f4a2dfcf0..ff2e98761f8be 100644
--- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte
+++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte
@@ -45,6 +45,7 @@
imageClass?: ClassValue;
brokenAssetClass?: ClassValue;
dimmed?: boolean;
+ ownerName?: string;
onClick?: (asset: TimelineAsset) => void;
onSelect?: (asset: TimelineAsset) => void;
onMouseEvent?: (event: { isMouseOver: boolean; selectedGroupIndex: number }) => void;
@@ -69,6 +70,7 @@
imageClass = '',
brokenAssetClass = '',
dimmed = false,
+ ownerName = undefined,
}: Props = $props();
let {
@@ -260,6 +262,15 @@
{/if}
+
+ {#if !authManager.isSharedLink && ownerName}
+
+ {/if}
+
{#if !authManager.isSharedLink && asset.isFavorite}
diff --git a/web/src/lib/components/timeline/Timeline.svelte b/web/src/lib/components/timeline/Timeline.svelte
index f94926ab2627a..47359f889c8d9 100644
--- a/web/src/lib/components/timeline/Timeline.svelte
+++ b/web/src/lib/components/timeline/Timeline.svelte
@@ -599,6 +599,7 @@
{isSelectionMode}
{singleSelect}
{monthGroup}
+ {album}
onSelect={({ title, assets }) => handleGroupSelect(timelineManager, title, assets)}
onSelectAssetCandidates={handleSelectAssetCandidates}
onSelectAssets={handleSelectAssets}
diff --git a/web/src/lib/components/timeline/TimelineDateGroup.svelte b/web/src/lib/components/timeline/TimelineDateGroup.svelte
index cd0dc9a212850..eb911fb108579 100644
--- a/web/src/lib/components/timeline/TimelineDateGroup.svelte
+++ b/web/src/lib/components/timeline/TimelineDateGroup.svelte
@@ -13,6 +13,7 @@
import { mdiCheckCircle, mdiCircleOutline } from '@mdi/js';
import { fromTimelinePlainDate, getDateLocaleString } from '$lib/utils/timeline-util';
+ import type { AlbumResponseDto } from '@immich/sdk';
import { Icon } from '@immich/ui';
import { type Snippet } from 'svelte';
import { flip } from 'svelte/animate';
@@ -29,6 +30,7 @@
timelineManager: TimelineManager;
assetInteraction: AssetInteraction;
customLayout?: Snippet<[TimelineAsset]>;
+ album?: AlbumResponseDto | null;
onSelect: ({ title, assets }: { title: string; assets: TimelineAsset[] }) => void;
onSelectAssets: (asset: TimelineAsset) => void;
@@ -55,6 +57,7 @@
assetInteraction,
timelineManager,
customLayout,
+ album = null,
onSelect,
onSelectAssets,
onSelectAssetCandidates,
@@ -133,6 +136,16 @@
});
return getDateLocaleString(date);
};
+
+ const getSharedAssetOwnerName = (album: AlbumResponseDto | null, asset: TimelineAsset): string | undefined => {
+ if (!album || !album.albumUsers || album.albumUsers.length === 0) {
+ return undefined;
+ }
+ if (album.owner.id === asset.ownerId) {
+ return album.owner.name;
+ }
+ return album.albumUsers.find((user) => user.user.id === asset.ownerId)?.user.name;
+ };
{#each filterIntersecting(monthGroup.dayGroups) as dayGroup, groupIndex (dayGroup.day)}
@@ -210,6 +223,7 @@
{showArchiveIcon}
{asset}
{groupIndex}
+ ownerName={getSharedAssetOwnerName(album, asset)}
onClick={(asset) => {
if (typeof onThumbnailClick === 'function') {
onThumbnailClick(asset, timelineManager, dayGroup, _onClick);