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 lang="ts">
 | 
			
		||||
	import { AlbumResponseDto, api, ThumbnailFormat } from '@api';
 | 
			
		||||
	import { AlbumResponseDto, api, ThumbnailFormat, UserResponseDto } from '@api';
 | 
			
		||||
	import { createEventDispatcher, onMount } from 'svelte';
 | 
			
		||||
	import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
 | 
			
		||||
	import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
 | 
			
		||||
@@ -20,6 +20,8 @@
 | 
			
		||||
	import { locale } from '$lib/stores/preferences.store';
 | 
			
		||||
 | 
			
		||||
	export let album: AlbumResponseDto;
 | 
			
		||||
	export let isSharingView = false;
 | 
			
		||||
	export let user: UserResponseDto;
 | 
			
		||||
 | 
			
		||||
	let imageData = `/api/asset/thumbnail/${album.albumThumbnailAssetId}?format=${ThumbnailFormat.Webp}`;
 | 
			
		||||
	if (!album.albumThumbnailAssetId) {
 | 
			
		||||
@@ -58,6 +60,12 @@
 | 
			
		||||
	onMount(async () => {
 | 
			
		||||
		imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || noThumbnailUrl;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	const getAlbumOwnerInfo = async (): Promise<UserResponseDto> => {
 | 
			
		||||
		const { data } = await api.userApi.getUserById(album.ownerId);
 | 
			
		||||
 | 
			
		||||
		return data;
 | 
			
		||||
	};
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div
 | 
			
		||||
@@ -67,6 +75,7 @@
 | 
			
		||||
	data-testid="album-card"
 | 
			
		||||
>
 | 
			
		||||
	<!-- svelte-ignore a11y-click-events-have-key-events -->
 | 
			
		||||
	{#if !isSharingView}
 | 
			
		||||
		<div
 | 
			
		||||
			id={`icon-${album.id}`}
 | 
			
		||||
			class="absolute top-6 right-6 z-10"
 | 
			
		||||
@@ -75,6 +84,7 @@
 | 
			
		||||
		>
 | 
			
		||||
			<CircleIconButton logo={DotsVertical} size={'20'} />
 | 
			
		||||
		</div>
 | 
			
		||||
	{/if}
 | 
			
		||||
 | 
			
		||||
	<div class={`aspect-square relative`}>
 | 
			
		||||
		<img
 | 
			
		||||
@@ -84,7 +94,11 @@
 | 
			
		||||
			data-testid="album-image"
 | 
			
		||||
			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 class="mt-4">
 | 
			
		||||
@@ -101,9 +115,20 @@
 | 
			
		||||
				{album.assetCount.toLocaleString($locale)}
 | 
			
		||||
				{album.assetCount == 1 ? `item` : `items`}
 | 
			
		||||
			</p>
 | 
			
		||||
 | 
			
		||||
			{#if album.shared}
 | 
			
		||||
			<p>·</p>
 | 
			
		||||
 | 
			
		||||
			{#if isSharingView}
 | 
			
		||||
				{#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>
 | 
			
		||||
			{/if}
 | 
			
		||||
		</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}`}
 | 
			
		||||
				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>
 | 
			
		||||
		{/each}
 | 
			
		||||
	</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ export const load = (async ({ locals: { api, user } }) => {
 | 
			
		||||
			user,
 | 
			
		||||
			sharedAlbums,
 | 
			
		||||
			meta: {
 | 
			
		||||
				title: 'Albums'
 | 
			
		||||
				title: 'Sharing'
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	} catch (e) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
	import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.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 { api } from '@api';
 | 
			
		||||
	import type { PageData } from './$types';
 | 
			
		||||
@@ -12,6 +11,8 @@
 | 
			
		||||
	import empty2Url from '$lib/assets/empty-2.svg';
 | 
			
		||||
	import UserPageLayout from '$lib/components/layouts/user-page-layout.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;
 | 
			
		||||
 | 
			
		||||
@@ -51,11 +52,14 @@
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<section>
 | 
			
		||||
		<!-- Share Album List -->
 | 
			
		||||
		<div class="md:w-full flex flex-col place-items-center">
 | 
			
		||||
			{#each data.sharedAlbums as album}
 | 
			
		||||
				<a class="max-md:w-full" data-sveltekit-preload-data="hover" href={`albums/${album.id}`}>
 | 
			
		||||
					<SharedAlbumListTile {album} user={data.user} />
 | 
			
		||||
		<div class="grid grid-cols-[repeat(auto-fill,minmax(15rem,1fr))]">
 | 
			
		||||
			{#each data.sharedAlbums as album (album.id)}
 | 
			
		||||
				<a
 | 
			
		||||
					data-sveltekit-preload-data="hover"
 | 
			
		||||
					href={`albums/${album.id}`}
 | 
			
		||||
					animate:flip={{ duration: 200 }}
 | 
			
		||||
				>
 | 
			
		||||
					<AlbumCard {album} user={data.user} isSharingView />
 | 
			
		||||
				</a>
 | 
			
		||||
			{/each}
 | 
			
		||||
		</div>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user