mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	feat(web): Replicate albums view for sharing view (#2433)
* replicate album view for sharing view * Remove unused file * fix test * correct title
This commit is contained in:
		@@ -12,7 +12,7 @@
 | 
				
			|||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import { AlbumResponseDto, api, ThumbnailFormat } from '@api';
 | 
						import { AlbumResponseDto, api, ThumbnailFormat, UserResponseDto } from '@api';
 | 
				
			||||||
	import { createEventDispatcher, onMount } from 'svelte';
 | 
						import { createEventDispatcher, onMount } from 'svelte';
 | 
				
			||||||
	import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
 | 
						import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
 | 
				
			||||||
	import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
 | 
						import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
@@ -20,6 +20,8 @@
 | 
				
			|||||||
	import { locale } from '$lib/stores/preferences.store';
 | 
						import { locale } from '$lib/stores/preferences.store';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	export let album: AlbumResponseDto;
 | 
						export let album: AlbumResponseDto;
 | 
				
			||||||
 | 
						export let isSharingView = false;
 | 
				
			||||||
 | 
						export let user: UserResponseDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let imageData = `/api/asset/thumbnail/${album.albumThumbnailAssetId}?format=${ThumbnailFormat.Webp}`;
 | 
						let imageData = `/api/asset/thumbnail/${album.albumThumbnailAssetId}?format=${ThumbnailFormat.Webp}`;
 | 
				
			||||||
	if (!album.albumThumbnailAssetId) {
 | 
						if (!album.albumThumbnailAssetId) {
 | 
				
			||||||
@@ -58,6 +60,12 @@
 | 
				
			|||||||
	onMount(async () => {
 | 
						onMount(async () => {
 | 
				
			||||||
		imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || noThumbnailUrl;
 | 
							imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || noThumbnailUrl;
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const getAlbumOwnerInfo = async (): Promise<UserResponseDto> => {
 | 
				
			||||||
 | 
							const { data } = await api.userApi.getUserById(album.ownerId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return data;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div
 | 
					<div
 | 
				
			||||||
@@ -67,14 +75,16 @@
 | 
				
			|||||||
	data-testid="album-card"
 | 
						data-testid="album-card"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	<!-- svelte-ignore a11y-click-events-have-key-events -->
 | 
						<!-- svelte-ignore a11y-click-events-have-key-events -->
 | 
				
			||||||
	<div
 | 
						{#if !isSharingView}
 | 
				
			||||||
		id={`icon-${album.id}`}
 | 
							<div
 | 
				
			||||||
		class="absolute top-6 right-6 z-10"
 | 
								id={`icon-${album.id}`}
 | 
				
			||||||
		on:click|stopPropagation|preventDefault={showAlbumContextMenu}
 | 
								class="absolute top-6 right-6 z-10"
 | 
				
			||||||
		data-testid="context-button-parent"
 | 
								on:click|stopPropagation|preventDefault={showAlbumContextMenu}
 | 
				
			||||||
	>
 | 
								data-testid="context-button-parent"
 | 
				
			||||||
		<CircleIconButton logo={DotsVertical} size={'20'} />
 | 
							>
 | 
				
			||||||
	</div>
 | 
								<CircleIconButton logo={DotsVertical} size={'20'} />
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div class={`aspect-square relative`}>
 | 
						<div class={`aspect-square relative`}>
 | 
				
			||||||
		<img
 | 
							<img
 | 
				
			||||||
@@ -84,7 +94,11 @@
 | 
				
			|||||||
			data-testid="album-image"
 | 
								data-testid="album-image"
 | 
				
			||||||
			draggable="false"
 | 
								draggable="false"
 | 
				
			||||||
		/>
 | 
							/>
 | 
				
			||||||
		<div class="w-full h-full absolute top-0 rounded-3xl group-hover:bg-indigo-800/25" />
 | 
							<div
 | 
				
			||||||
 | 
								class="w-full h-full absolute top-0 rounded-3xl {isSharingView
 | 
				
			||||||
 | 
									? 'group-hover:bg-yellow-800/25'
 | 
				
			||||||
 | 
									: 'group-hover:bg-indigo-800/25'} "
 | 
				
			||||||
 | 
							/>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div class="mt-4">
 | 
						<div class="mt-4">
 | 
				
			||||||
@@ -101,9 +115,20 @@
 | 
				
			|||||||
				{album.assetCount.toLocaleString($locale)}
 | 
									{album.assetCount.toLocaleString($locale)}
 | 
				
			||||||
				{album.assetCount == 1 ? `item` : `items`}
 | 
									{album.assetCount == 1 ? `item` : `items`}
 | 
				
			||||||
			</p>
 | 
								</p>
 | 
				
			||||||
 | 
								<p>·</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			{#if album.shared}
 | 
								{#if isSharingView}
 | 
				
			||||||
				<p>·</p>
 | 
									{#await getAlbumOwnerInfo() then albumOwner}
 | 
				
			||||||
 | 
										{#if user.email == albumOwner.email}
 | 
				
			||||||
 | 
											<p>Owned</p>
 | 
				
			||||||
 | 
										{:else}
 | 
				
			||||||
 | 
											<p>
 | 
				
			||||||
 | 
												Shared by {albumOwner.firstName}
 | 
				
			||||||
 | 
												{albumOwner.lastName}
 | 
				
			||||||
 | 
											</p>
 | 
				
			||||||
 | 
										{/if}
 | 
				
			||||||
 | 
									{/await}
 | 
				
			||||||
 | 
								{:else if album.shared}
 | 
				
			||||||
				<p>Shared</p>
 | 
									<p>Shared</p>
 | 
				
			||||||
			{/if}
 | 
								{/if}
 | 
				
			||||||
		</span>
 | 
							</span>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,74 +0,0 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					 | 
				
			||||||
	import { AlbumResponseDto, api, ThumbnailFormat, UserResponseDto } from '@api';
 | 
					 | 
				
			||||||
	import { fade } from 'svelte/transition';
 | 
					 | 
				
			||||||
	import noThumbnailUrl from '$lib/assets/no-thumbnail.png';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	export let album: AlbumResponseDto;
 | 
					 | 
				
			||||||
	export let user: UserResponseDto;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const loadImageData = async (thubmnailId: string | null) => {
 | 
					 | 
				
			||||||
		if (thubmnailId == null) {
 | 
					 | 
				
			||||||
			return noThumbnailUrl;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		const { data } = await api.assetApi.getAssetThumbnail(
 | 
					 | 
				
			||||||
			thubmnailId,
 | 
					 | 
				
			||||||
			ThumbnailFormat.Webp,
 | 
					 | 
				
			||||||
			undefined,
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				responseType: 'blob'
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		if (data instanceof Blob) {
 | 
					 | 
				
			||||||
			return URL.createObjectURL(data);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const getAlbumOwnerInfo = async (): Promise<UserResponseDto> => {
 | 
					 | 
				
			||||||
		const { data } = await api.userApi.getUserById(album.ownerId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return data;
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<div
 | 
					 | 
				
			||||||
	class="grid grid-cols-[75px_1fr] w-full md:w-[500px] border-b border-gray-300 dark:border-immich-dark-gray place-items-center py-4  gap-6 transition-all hover:border-immich-primary dark:hover:border-immich-dark-primary"
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
	<div>
 | 
					 | 
				
			||||||
		{#await loadImageData(album.albumThumbnailAssetId)}
 | 
					 | 
				
			||||||
			<div
 | 
					 | 
				
			||||||
				class={`bg-immich-primary/10 w-[75px] h-[75px] flex place-items-center place-content-center rounded-xl`}
 | 
					 | 
				
			||||||
			>
 | 
					 | 
				
			||||||
				...
 | 
					 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
		{:then imageData}
 | 
					 | 
				
			||||||
			<img
 | 
					 | 
				
			||||||
				in:fade={{ duration: 250 }}
 | 
					 | 
				
			||||||
				src={imageData}
 | 
					 | 
				
			||||||
				alt={album.id}
 | 
					 | 
				
			||||||
				class={`object-cover w-[75px] h-[75px] transition-all z-0 rounded-xl duration-300 `}
 | 
					 | 
				
			||||||
				draggable="false"
 | 
					 | 
				
			||||||
			/>
 | 
					 | 
				
			||||||
		{/await}
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<div class="justify-self-start">
 | 
					 | 
				
			||||||
		<p class="font-medium text-gray-800 dark:text-immich-dark-primary">
 | 
					 | 
				
			||||||
			{album.albumName}
 | 
					 | 
				
			||||||
		</p>
 | 
					 | 
				
			||||||
		<span class="text-xs flex gap-2 dark:text-immich-dark-fg" data-testid="album-details"
 | 
					 | 
				
			||||||
			><p>{album.assetCount} items</p>
 | 
					 | 
				
			||||||
			<p>·</p>
 | 
					 | 
				
			||||||
			{#await getAlbumOwnerInfo() then albumOwner}
 | 
					 | 
				
			||||||
				{#if user.email == albumOwner.email}
 | 
					 | 
				
			||||||
					<p class="text-xs text-gray-600 dark:text-immich-dark-fg">Owned</p>
 | 
					 | 
				
			||||||
				{:else}
 | 
					 | 
				
			||||||
					<p class="text-xs text-gray-600 dark:text-immich-dark-fg">
 | 
					 | 
				
			||||||
						Shared by {albumOwner.firstName}
 | 
					 | 
				
			||||||
						{albumOwner.lastName}
 | 
					 | 
				
			||||||
					</p>
 | 
					 | 
				
			||||||
				{/if}
 | 
					 | 
				
			||||||
			{/await}
 | 
					 | 
				
			||||||
		</span>
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
@@ -69,7 +69,11 @@
 | 
				
			|||||||
				href={`albums/${album.id}`}
 | 
									href={`albums/${album.id}`}
 | 
				
			||||||
				animate:flip={{ duration: 200 }}
 | 
									animate:flip={{ duration: 200 }}
 | 
				
			||||||
			>
 | 
								>
 | 
				
			||||||
				<AlbumCard {album} on:showalbumcontextmenu={(e) => showAlbumContextMenu(e.detail, album)} />
 | 
									<AlbumCard
 | 
				
			||||||
 | 
										{album}
 | 
				
			||||||
 | 
										on:showalbumcontextmenu={(e) => showAlbumContextMenu(e.detail, album)}
 | 
				
			||||||
 | 
										user={data.user}
 | 
				
			||||||
 | 
									/>
 | 
				
			||||||
			</a>
 | 
								</a>
 | 
				
			||||||
		{/each}
 | 
							{/each}
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ export const load = (async ({ locals: { api, user } }) => {
 | 
				
			|||||||
			user,
 | 
								user,
 | 
				
			||||||
			sharedAlbums,
 | 
								sharedAlbums,
 | 
				
			||||||
			meta: {
 | 
								meta: {
 | 
				
			||||||
				title: 'Albums'
 | 
									title: 'Sharing'
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	} catch (e) {
 | 
						} catch (e) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte';
 | 
						import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte';
 | 
				
			||||||
	import Link from 'svelte-material-icons/Link.svelte';
 | 
						import Link from 'svelte-material-icons/Link.svelte';
 | 
				
			||||||
	import SharedAlbumListTile from '$lib/components/sharing-page/shared-album-list-tile.svelte';
 | 
					 | 
				
			||||||
	import { goto } from '$app/navigation';
 | 
						import { goto } from '$app/navigation';
 | 
				
			||||||
	import { api } from '@api';
 | 
						import { api } from '@api';
 | 
				
			||||||
	import type { PageData } from './$types';
 | 
						import type { PageData } from './$types';
 | 
				
			||||||
@@ -12,6 +11,8 @@
 | 
				
			|||||||
	import empty2Url from '$lib/assets/empty-2.svg';
 | 
						import empty2Url from '$lib/assets/empty-2.svg';
 | 
				
			||||||
	import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
 | 
						import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
 | 
				
			||||||
	import LinkButton from '$lib/components/elements/buttons/link-button.svelte';
 | 
						import LinkButton from '$lib/components/elements/buttons/link-button.svelte';
 | 
				
			||||||
 | 
						import { flip } from 'svelte/animate';
 | 
				
			||||||
 | 
						import AlbumCard from '$lib/components/album-page/album-card.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	export let data: PageData;
 | 
						export let data: PageData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -51,11 +52,14 @@
 | 
				
			|||||||
	</div>
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<section>
 | 
						<section>
 | 
				
			||||||
		<!-- Share Album List -->
 | 
							<div class="grid grid-cols-[repeat(auto-fill,minmax(15rem,1fr))]">
 | 
				
			||||||
		<div class="md:w-full flex flex-col place-items-center">
 | 
								{#each data.sharedAlbums as album (album.id)}
 | 
				
			||||||
			{#each data.sharedAlbums as album}
 | 
									<a
 | 
				
			||||||
				<a class="max-md:w-full" data-sveltekit-preload-data="hover" href={`albums/${album.id}`}>
 | 
										data-sveltekit-preload-data="hover"
 | 
				
			||||||
					<SharedAlbumListTile {album} user={data.user} />
 | 
										href={`albums/${album.id}`}
 | 
				
			||||||
 | 
										animate:flip={{ duration: 200 }}
 | 
				
			||||||
 | 
									>
 | 
				
			||||||
 | 
										<AlbumCard {album} user={data.user} isSharingView />
 | 
				
			||||||
				</a>
 | 
									</a>
 | 
				
			||||||
			{/each}
 | 
								{/each}
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user