mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:17:11 -05:00 
			
		
		
		
	refactor(web): common layout for user pages (#1995)
* refactor(web): common layout for user pages * remove unused imports
This commit is contained in:
		
							parent
							
								
									dd02f1025f
								
							
						
					
					
						commit
						9a332074c7
					
				
							
								
								
									
										47
									
								
								web/src/lib/components/layouts/user-page-layout.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								web/src/lib/components/layouts/user-page-layout.svelte
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
						import { openFileUploadDialog } from '$lib/utils/file-uploader';
 | 
				
			||||||
 | 
						import type { UserResponseDto } from '@api';
 | 
				
			||||||
 | 
						import NavigationBar from '../shared-components/navigation-bar/navigation-bar.svelte';
 | 
				
			||||||
 | 
						import SideBar from '../shared-components/side-bar/side-bar.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						export let user: UserResponseDto;
 | 
				
			||||||
 | 
						export let hideNavbar = false;
 | 
				
			||||||
 | 
						export let showUploadButton = false;
 | 
				
			||||||
 | 
						export let title: string | undefined = undefined;
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<header>
 | 
				
			||||||
 | 
						{#if !hideNavbar}
 | 
				
			||||||
 | 
							<NavigationBar
 | 
				
			||||||
 | 
								{user}
 | 
				
			||||||
 | 
								shouldShowUploadButton={showUploadButton}
 | 
				
			||||||
 | 
								on:uploadClicked={() => openFileUploadDialog()}
 | 
				
			||||||
 | 
							/>
 | 
				
			||||||
 | 
						{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<slot name="header" />
 | 
				
			||||||
 | 
					</header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<main
 | 
				
			||||||
 | 
						class="grid grid-cols-[250px_auto] relative pt-[4.25rem] h-screen bg-immich-bg dark:bg-immich-dark-bg immich-scrollbar"
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
						<SideBar />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<slot name="content">
 | 
				
			||||||
 | 
							<section class="my-8 mx-4 bg-immich-bg dark:bg-immich-dark-bg">
 | 
				
			||||||
 | 
								{#if title}
 | 
				
			||||||
 | 
									<div class="flex justify-between place-items-center dark:text-immich-dark-fg px-4 h-10">
 | 
				
			||||||
 | 
										<p class="font-medium">{title}</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										<slot name="buttons" />
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									<div class="my-4">
 | 
				
			||||||
 | 
										<hr class="dark:border-immich-dark-gray" />
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								<slot />
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
						</slot>
 | 
				
			||||||
 | 
					</main>
 | 
				
			||||||
@ -40,14 +40,14 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<section
 | 
					<section
 | 
				
			||||||
	id="dashboard-navbar"
 | 
						id="dashboard-navbar"
 | 
				
			||||||
	class="fixed w-screen z-[900] bg-immich-bg dark:bg-immich-dark-bg text-sm"
 | 
						class="fixed h-[4.25rem] w-screen z-[900] bg-immich-bg dark:bg-immich-dark-bg text-sm"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
	<div
 | 
						<div
 | 
				
			||||||
		class="grid grid-cols-[250px_auto] border-b dark:border-b-immich-dark-gray items-center py-2"
 | 
							class="grid grid-cols-[250px_auto] border-b dark:border-b-immich-dark-gray items-center py-2"
 | 
				
			||||||
	>
 | 
						>
 | 
				
			||||||
		<a
 | 
							<a
 | 
				
			||||||
			data-sveltekit-preload-data="hover"
 | 
								data-sveltekit-preload-data="hover"
 | 
				
			||||||
			class="flex gap-2 px-6 place-items-center hover:cursor-pointer"
 | 
								class="flex gap-2 mx-6 place-items-center"
 | 
				
			||||||
			href={AppRoute.PHOTOS}
 | 
								href={AppRoute.PHOTOS}
 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<ImmichLogo height="35" width="35" />
 | 
								<ImmichLogo height="35" width="35" />
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<form
 | 
					<form
 | 
				
			||||||
	autocomplete="off"
 | 
						autocomplete="off"
 | 
				
			||||||
	class="relative text-base"
 | 
						class="relative text-sm"
 | 
				
			||||||
	action={AppRoute.SEARCH}
 | 
						action={AppRoute.SEARCH}
 | 
				
			||||||
	on:reset={() => (value = '')}
 | 
						on:reset={() => (value = '')}
 | 
				
			||||||
	on:submit|preventDefault={onSearch}
 | 
						on:submit|preventDefault={onSearch}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,13 @@
 | 
				
			|||||||
 | 
					import { AppRoute } from '$lib/constants';
 | 
				
			||||||
import { redirect } from '@sveltejs/kit';
 | 
					import { redirect } from '@sveltejs/kit';
 | 
				
			||||||
import type { PageServerLoad } from './$types';
 | 
					import type { PageServerLoad } from './$types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const load = (async ({ parent, locals: { api } }) => {
 | 
					export const load = (async ({ locals: { api, user } }) => {
 | 
				
			||||||
 | 
						if (!user) {
 | 
				
			||||||
 | 
							throw redirect(302, AppRoute.AUTH_LOGIN);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		const { user } = await parent();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (!user) {
 | 
					 | 
				
			||||||
			throw Error('User is not logged in');
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		const { data: albums } = await api.albumApi.getAllAlbums();
 | 
							const { data: albums } = await api.albumApi.getAllAlbums();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
@ -19,6 +18,6 @@ export const load = (async ({ parent, locals: { api } }) => {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	} catch (e) {
 | 
						} catch (e) {
 | 
				
			||||||
		throw redirect(302, '/auth/login');
 | 
							throw redirect(302, AppRoute.AUTH_LOGIN);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}) satisfies PageServerLoad;
 | 
					}) satisfies PageServerLoad;
 | 
				
			||||||
 | 
				
			|||||||
@ -5,11 +5,10 @@
 | 
				
			|||||||
	import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
 | 
						import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
 | 
				
			||||||
	import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
 | 
						import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
 | 
				
			||||||
	import type { PageData } from './$types';
 | 
						import type { PageData } from './$types';
 | 
				
			||||||
	import NavigationBar from '$lib/components/shared-components/navigation-bar/navigation-bar.svelte';
 | 
					 | 
				
			||||||
	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
 | 
					 | 
				
			||||||
	import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte';
 | 
						import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte';
 | 
				
			||||||
	import { useAlbums } from './albums.bloc';
 | 
						import { useAlbums } from './albums.bloc';
 | 
				
			||||||
	import empty1Url from '$lib/assets/empty-1.svg';
 | 
						import empty1Url from '$lib/assets/empty-1.svg';
 | 
				
			||||||
 | 
						import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	export let data: PageData;
 | 
						export let data: PageData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -31,84 +30,57 @@
 | 
				
			|||||||
	};
 | 
						};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<section>
 | 
					<UserPageLayout user={data.user} title={data.meta.title}>
 | 
				
			||||||
	<NavigationBar user={data.user} shouldShowUploadButton={false} />
 | 
						<div slot="buttons">
 | 
				
			||||||
</section>
 | 
							<button
 | 
				
			||||||
 | 
								on:click={handleCreateAlbum}
 | 
				
			||||||
<section
 | 
								class="immich-text-button text-sm dark:hover:bg-immich-dark-primary/25 dark:text-immich-dark-fg"
 | 
				
			||||||
	class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg  dark:bg-immich-dark-bg"
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
	<SideBar />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<!-- Main Section -->
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<section class="overflow-y-auto relative immich-scrollbar">
 | 
					 | 
				
			||||||
		<section
 | 
					 | 
				
			||||||
			id="album-content"
 | 
					 | 
				
			||||||
			class="relative pt-8 pl-4 pr-4 mb-12 bg-immich-bg dark:bg-immich-dark-bg"
 | 
					 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<div class="px-4 flex justify-between place-items-center dark:text-immich-dark-fg">
 | 
								<span>
 | 
				
			||||||
				<div>
 | 
									<PlusBoxOutline size="18" />
 | 
				
			||||||
					<p class="font-medium">Albums</p>
 | 
								</span>
 | 
				
			||||||
				</div>
 | 
								<p>Create album</p>
 | 
				
			||||||
 | 
							</button>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				<div>
 | 
						<!-- Album Card -->
 | 
				
			||||||
					<button
 | 
						<div class="grid grid-cols-[repeat(auto-fill,minmax(15rem,1fr))] gap-8">
 | 
				
			||||||
						on:click={handleCreateAlbum}
 | 
							{#each $albums as album}
 | 
				
			||||||
						class="immich-text-button text-sm dark:hover:bg-immich-dark-primary/25 dark:text-immich-dark-fg"
 | 
								{#key album.id}
 | 
				
			||||||
					>
 | 
									<a data-sveltekit-preload-data="hover" href={`albums/${album.id}`}>
 | 
				
			||||||
						<span>
 | 
										<AlbumCard
 | 
				
			||||||
							<PlusBoxOutline size="18" />
 | 
											{album}
 | 
				
			||||||
						</span>
 | 
											on:showalbumcontextmenu={(e) => showAlbumContextMenu(e.detail, album)}
 | 
				
			||||||
						<p>Create album</p>
 | 
										/>
 | 
				
			||||||
					</button>
 | 
									</a>
 | 
				
			||||||
				</div>
 | 
								{/key}
 | 
				
			||||||
			</div>
 | 
							{/each}
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			<div class="my-4">
 | 
						<!-- Empty Message -->
 | 
				
			||||||
				<hr class="dark:border-immich-dark-gray" />
 | 
						{#if $albums.length === 0}
 | 
				
			||||||
			</div>
 | 
							<div
 | 
				
			||||||
 | 
								on:click={handleCreateAlbum}
 | 
				
			||||||
 | 
								on:keydown={handleCreateAlbum}
 | 
				
			||||||
 | 
								class="border dark:border-immich-dark-gray hover:bg-immich-primary/5 dark:hover:bg-immich-dark-primary/25 hover:cursor-pointer p-5 w-[50%] m-auto mt-10 bg-gray-50 dark:bg-immich-dark-gray rounded-3xl flex flex-col place-content-center place-items-center"
 | 
				
			||||||
 | 
							>
 | 
				
			||||||
 | 
								<img src={empty1Url} alt="Empty shared album" width="500" draggable="false" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			<!-- Album Card -->
 | 
								<p class="text-center text-immich-text-gray-500 dark:text-immich-dark-fg">
 | 
				
			||||||
			<div class="grid grid-cols-[repeat(auto-fill,minmax(15rem,1fr))] gap-8">
 | 
									Create an album to organize your photos and videos
 | 
				
			||||||
				{#each $albums as album}
 | 
								</p>
 | 
				
			||||||
					{#key album.id}
 | 
							</div>
 | 
				
			||||||
						<a data-sveltekit-preload-data="hover" href={`albums/${album.id}`}>
 | 
					 | 
				
			||||||
							<AlbumCard
 | 
					 | 
				
			||||||
								{album}
 | 
					 | 
				
			||||||
								on:showalbumcontextmenu={(e) => showAlbumContextMenu(e.detail, album)}
 | 
					 | 
				
			||||||
							/>
 | 
					 | 
				
			||||||
						</a>
 | 
					 | 
				
			||||||
					{/key}
 | 
					 | 
				
			||||||
				{/each}
 | 
					 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			<!-- Empty Message -->
 | 
					 | 
				
			||||||
			{#if $albums.length === 0}
 | 
					 | 
				
			||||||
				<div
 | 
					 | 
				
			||||||
					on:click={handleCreateAlbum}
 | 
					 | 
				
			||||||
					on:keydown={handleCreateAlbum}
 | 
					 | 
				
			||||||
					class="border dark:border-immich-dark-gray hover:bg-immich-primary/5 dark:hover:bg-immich-dark-primary/25 hover:cursor-pointer p-5 w-[50%] m-auto mt-10 bg-gray-50 dark:bg-immich-dark-gray rounded-3xl flex flex-col place-content-center place-items-center"
 | 
					 | 
				
			||||||
				>
 | 
					 | 
				
			||||||
					<img src={empty1Url} alt="Empty shared album" width="500" draggable="false" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					<p class="text-center text-immich-text-gray-500 dark:text-immich-dark-fg">
 | 
					 | 
				
			||||||
						Create an album to organize your photos and videos
 | 
					 | 
				
			||||||
					</p>
 | 
					 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
			{/if}
 | 
					 | 
				
			||||||
		</section>
 | 
					 | 
				
			||||||
	</section>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<!-- Context Menu -->
 | 
					 | 
				
			||||||
	{#if $isShowContextMenu}
 | 
					 | 
				
			||||||
		<ContextMenu {...$contextMenuPosition} on:clickoutside={closeAlbumContextMenu}>
 | 
					 | 
				
			||||||
			<MenuOption on:click={deleteSelectedContextAlbum}>
 | 
					 | 
				
			||||||
				<span class="flex place-items-center place-content-center gap-2">
 | 
					 | 
				
			||||||
					<DeleteOutline size="18" />
 | 
					 | 
				
			||||||
					<p>Delete album</p>
 | 
					 | 
				
			||||||
				</span>
 | 
					 | 
				
			||||||
			</MenuOption>
 | 
					 | 
				
			||||||
		</ContextMenu>
 | 
					 | 
				
			||||||
	{/if}
 | 
						{/if}
 | 
				
			||||||
</section>
 | 
					</UserPageLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!-- Context Menu -->
 | 
				
			||||||
 | 
					{#if $isShowContextMenu}
 | 
				
			||||||
 | 
						<ContextMenu {...$contextMenuPosition} on:clickoutside={closeAlbumContextMenu}>
 | 
				
			||||||
 | 
							<MenuOption on:click={deleteSelectedContextAlbum}>
 | 
				
			||||||
 | 
								<span class="flex place-items-center place-content-center gap-2">
 | 
				
			||||||
 | 
									<DeleteOutline size="18" />
 | 
				
			||||||
 | 
									<p>Delete album</p>
 | 
				
			||||||
 | 
								</span>
 | 
				
			||||||
 | 
							</MenuOption>
 | 
				
			||||||
 | 
						</ContextMenu>
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,10 @@
 | 
				
			|||||||
 | 
					import { AppRoute } from '$lib/constants';
 | 
				
			||||||
import { redirect } from '@sveltejs/kit';
 | 
					import { redirect } from '@sveltejs/kit';
 | 
				
			||||||
import type { PageServerLoad } from './$types';
 | 
					import type { PageServerLoad } from './$types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const load = (async ({ parent, params, locals: { api } }) => {
 | 
					export const load = (async ({ params, locals: { api, user } }) => {
 | 
				
			||||||
	const { user } = await parent();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!user) {
 | 
						if (!user) {
 | 
				
			||||||
		throw redirect(302, '/auth/login');
 | 
							throw redirect(302, AppRoute.AUTH_LOGIN);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const albumId = params['albumId'];
 | 
						const albumId = params['albumId'];
 | 
				
			||||||
@ -19,6 +18,6 @@ export const load = (async ({ parent, params, locals: { api } }) => {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	} catch (e) {
 | 
						} catch (e) {
 | 
				
			||||||
		throw redirect(302, '/albums');
 | 
							throw redirect(302, AppRoute.ALBUMS);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}) satisfies PageServerLoad;
 | 
					}) satisfies PageServerLoad;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
						import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
 | 
				
			||||||
	import ImmichThumbnail from '$lib/components/shared-components/immich-thumbnail.svelte';
 | 
						import ImmichThumbnail from '$lib/components/shared-components/immich-thumbnail.svelte';
 | 
				
			||||||
	import NavigationBar from '$lib/components/shared-components/navigation-bar/navigation-bar.svelte';
 | 
					 | 
				
			||||||
	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
 | 
					 | 
				
			||||||
	import { AppRoute } from '$lib/constants';
 | 
						import { AppRoute } from '$lib/constants';
 | 
				
			||||||
	import { AssetTypeEnum, SearchExploreItem } from '@api';
 | 
						import { AssetTypeEnum, SearchExploreItem } from '@api';
 | 
				
			||||||
	import ClockOutline from 'svelte-material-icons/ClockOutline.svelte';
 | 
						import ClockOutline from 'svelte-material-icons/ClockOutline.svelte';
 | 
				
			||||||
@ -39,135 +38,109 @@
 | 
				
			|||||||
	places = places.slice(0, MAX_ITEMS);
 | 
						places = places.slice(0, MAX_ITEMS);
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<section>
 | 
					<UserPageLayout user={data.user} title={data.meta.title}>
 | 
				
			||||||
	<NavigationBar user={data.user} shouldShowUploadButton={false} />
 | 
						<div class="mx-4 flex flex-col">
 | 
				
			||||||
</section>
 | 
							{#if places.length > 0}
 | 
				
			||||||
 | 
								<div class="mb-6 mt-2">
 | 
				
			||||||
<section
 | 
					 | 
				
			||||||
	class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg dark:bg-immich-dark-bg"
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
	<SideBar />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<section class="overflow-y-auto relative immich-scrollbar">
 | 
					 | 
				
			||||||
		<section
 | 
					 | 
				
			||||||
			id="album-content"
 | 
					 | 
				
			||||||
			class="relative pt-8 pl-4 mb-12 bg-immich-bg dark:bg-immich-dark-bg"
 | 
					 | 
				
			||||||
		>
 | 
					 | 
				
			||||||
			<!-- Main Section -->
 | 
					 | 
				
			||||||
			<div class="px-4 flex justify-between place-items-center dark:text-immich-dark-fg">
 | 
					 | 
				
			||||||
				<div>
 | 
									<div>
 | 
				
			||||||
					<p class="font-medium">Explore</p>
 | 
										<p class="mb-4 dark:text-immich-dark-fg font-medium">Places</p>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
									<div class="flex flex-row flex-wrap gap-4">
 | 
				
			||||||
 | 
										{#each places as item}
 | 
				
			||||||
			<div class="my-4">
 | 
											<a class="relative" href="/search?{Field.CITY}={item.value}" draggable="false">
 | 
				
			||||||
				<hr class="dark:border-immich-dark-gray" />
 | 
												<div class="filter brightness-75 rounded-xl overflow-hidden">
 | 
				
			||||||
			</div>
 | 
													<ImmichThumbnail
 | 
				
			||||||
 | 
														isRoundedCorner={true}
 | 
				
			||||||
			<div class="mx-4 flex flex-col">
 | 
														thumbnailSize={156}
 | 
				
			||||||
				{#if places.length > 0}
 | 
														asset={item.data}
 | 
				
			||||||
					<div class="mb-6 mt-2">
 | 
														readonly={true}
 | 
				
			||||||
						<div>
 | 
													/>
 | 
				
			||||||
							<p class="mb-4 dark:text-immich-dark-fg font-medium">Places</p>
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
						<div class="flex flex-row flex-wrap gap-4">
 | 
					 | 
				
			||||||
							{#each places as item}
 | 
					 | 
				
			||||||
								<a class="relative" href="/search?{Field.CITY}={item.value}" draggable="false">
 | 
					 | 
				
			||||||
									<div class="filter brightness-75 rounded-xl overflow-hidden">
 | 
					 | 
				
			||||||
										<ImmichThumbnail
 | 
					 | 
				
			||||||
											isRoundedCorner={true}
 | 
					 | 
				
			||||||
											thumbnailSize={156}
 | 
					 | 
				
			||||||
											asset={item.data}
 | 
					 | 
				
			||||||
											readonly={true}
 | 
					 | 
				
			||||||
										/>
 | 
					 | 
				
			||||||
									</div>
 | 
					 | 
				
			||||||
									<span
 | 
					 | 
				
			||||||
										class="capitalize absolute bottom-2 w-full text-center text-sm font-medium text-white text-ellipsis w-100 px-1 hover:cursor-pointer backdrop-blur-[1px]"
 | 
					 | 
				
			||||||
									>
 | 
					 | 
				
			||||||
										{item.value}
 | 
					 | 
				
			||||||
									</span>
 | 
					 | 
				
			||||||
								</a>
 | 
					 | 
				
			||||||
							{/each}
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
				{/if}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				{#if things.length > 0}
 | 
					 | 
				
			||||||
					<div class="mb-6 mt-2">
 | 
					 | 
				
			||||||
						<div>
 | 
					 | 
				
			||||||
							<p class="mb-4 dark:text-immich-dark-fg font-medium">Things</p>
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
						<div class="flex flex-row flex-wrap gap-4">
 | 
					 | 
				
			||||||
							{#each things as item}
 | 
					 | 
				
			||||||
								<a class="relative" href="/search?{Field.OBJECTS}={item.value}" draggable="false">
 | 
					 | 
				
			||||||
									<div class="filter brightness-75 rounded-xl overflow-hidden">
 | 
					 | 
				
			||||||
										<ImmichThumbnail
 | 
					 | 
				
			||||||
											isRoundedCorner={true}
 | 
					 | 
				
			||||||
											thumbnailSize={156}
 | 
					 | 
				
			||||||
											asset={item.data}
 | 
					 | 
				
			||||||
											readonly={true}
 | 
					 | 
				
			||||||
										/>
 | 
					 | 
				
			||||||
									</div>
 | 
					 | 
				
			||||||
									<span
 | 
					 | 
				
			||||||
										class="capitalize absolute bottom-2 w-full text-center text-sm font-medium text-white text-ellipsis w-100 px-1 hover:cursor-pointer backdrop-blur-[1px]"
 | 
					 | 
				
			||||||
									>
 | 
					 | 
				
			||||||
										{item.value}
 | 
					 | 
				
			||||||
									</span>
 | 
					 | 
				
			||||||
								</a>
 | 
					 | 
				
			||||||
							{/each}
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
				{/if}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				<hr class="dark:border-immich-dark-gray mb-4" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				<div
 | 
					 | 
				
			||||||
					class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-8"
 | 
					 | 
				
			||||||
				>
 | 
					 | 
				
			||||||
					<div class="flex flex-col gap-6 dark:text-immich-dark-fg">
 | 
					 | 
				
			||||||
						<p class="text-sm">YOUR ACTIVITY</p>
 | 
					 | 
				
			||||||
						<div class="flex flex-col gap-4 dark:text-immich-dark-fg/80">
 | 
					 | 
				
			||||||
							<a
 | 
					 | 
				
			||||||
								href={AppRoute.FAVORITES}
 | 
					 | 
				
			||||||
								class="w-full flex text-sm font-medium hover:text-immich-primary dark:hover:text-immich-dark-primary content-center gap-2"
 | 
					 | 
				
			||||||
								draggable="false"
 | 
					 | 
				
			||||||
							>
 | 
					 | 
				
			||||||
								<StarOutline size={24} />
 | 
					 | 
				
			||||||
								<span>Favorites</span>
 | 
					 | 
				
			||||||
							</a>
 | 
					 | 
				
			||||||
							<a
 | 
					 | 
				
			||||||
								href="/search?recent=true"
 | 
					 | 
				
			||||||
								class="w-full flex text-sm font-medium hover:text-immich-primary dark:hover:text-immich-dark-primary content-center gap-2"
 | 
					 | 
				
			||||||
								draggable="false"
 | 
					 | 
				
			||||||
							>
 | 
					 | 
				
			||||||
								<ClockOutline size={24} />
 | 
					 | 
				
			||||||
								<span>Recently added</span>
 | 
					 | 
				
			||||||
							</a>
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
					<div class="flex flex-col gap-6 dark:text-immich-dark-fg">
 | 
					 | 
				
			||||||
						<p class="text-sm">CATEGORIES</p>
 | 
					 | 
				
			||||||
						<div class="flex flex-col gap-4 dark:text-immich-dark-fg/80">
 | 
					 | 
				
			||||||
							<a
 | 
					 | 
				
			||||||
								href="/search?type={AssetTypeEnum.Video}"
 | 
					 | 
				
			||||||
								class="w-full flex text-sm font-medium hover:text-immich-primary dark:hover:text-immich-dark-primary items-center gap-2"
 | 
					 | 
				
			||||||
							>
 | 
					 | 
				
			||||||
								<PlayCircleOutline size={24} />
 | 
					 | 
				
			||||||
								<span>Videos</span>
 | 
					 | 
				
			||||||
							</a>
 | 
					 | 
				
			||||||
							<div>
 | 
					 | 
				
			||||||
								<a
 | 
					 | 
				
			||||||
									href="/search?motion=true"
 | 
					 | 
				
			||||||
									class="w-full flex text-sm font-medium hover:text-immich-primary dark:hover:text-immich-dark-primary items-center gap-2"
 | 
					 | 
				
			||||||
								>
 | 
					 | 
				
			||||||
									<MotionPlayOutline size={24} />
 | 
					 | 
				
			||||||
									<span>Motion photos</span>
 | 
					 | 
				
			||||||
								</a>
 | 
					 | 
				
			||||||
							</div>
 | 
												</div>
 | 
				
			||||||
						</div>
 | 
												<span
 | 
				
			||||||
 | 
													class="capitalize absolute bottom-2 w-full text-center text-sm font-medium text-white text-ellipsis w-100 px-1 hover:cursor-pointer backdrop-blur-[1px]"
 | 
				
			||||||
 | 
												>
 | 
				
			||||||
 | 
													{item.value}
 | 
				
			||||||
 | 
												</span>
 | 
				
			||||||
 | 
											</a>
 | 
				
			||||||
 | 
										{/each}
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{#if things.length > 0}
 | 
				
			||||||
 | 
								<div class="mb-6 mt-2">
 | 
				
			||||||
 | 
									<div>
 | 
				
			||||||
 | 
										<p class="mb-4 dark:text-immich-dark-fg font-medium">Things</p>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
									<div class="flex flex-row flex-wrap gap-4">
 | 
				
			||||||
 | 
										{#each things as item}
 | 
				
			||||||
 | 
											<a class="relative" href="/search?{Field.OBJECTS}={item.value}" draggable="false">
 | 
				
			||||||
 | 
												<div class="filter brightness-75 rounded-xl overflow-hidden">
 | 
				
			||||||
 | 
													<ImmichThumbnail
 | 
				
			||||||
 | 
														isRoundedCorner={true}
 | 
				
			||||||
 | 
														thumbnailSize={156}
 | 
				
			||||||
 | 
														asset={item.data}
 | 
				
			||||||
 | 
														readonly={true}
 | 
				
			||||||
 | 
													/>
 | 
				
			||||||
 | 
												</div>
 | 
				
			||||||
 | 
												<span
 | 
				
			||||||
 | 
													class="capitalize absolute bottom-2 w-full text-center text-sm font-medium text-white text-ellipsis w-100 px-1 hover:cursor-pointer backdrop-blur-[1px]"
 | 
				
			||||||
 | 
												>
 | 
				
			||||||
 | 
													{item.value}
 | 
				
			||||||
 | 
												</span>
 | 
				
			||||||
 | 
											</a>
 | 
				
			||||||
 | 
										{/each}
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<hr class="dark:border-immich-dark-gray mb-4" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<div
 | 
				
			||||||
 | 
								class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-8"
 | 
				
			||||||
 | 
							>
 | 
				
			||||||
 | 
								<div class="flex flex-col gap-6 dark:text-immich-dark-fg">
 | 
				
			||||||
 | 
									<p class="text-sm">YOUR ACTIVITY</p>
 | 
				
			||||||
 | 
									<div class="flex flex-col gap-4 dark:text-immich-dark-fg/80">
 | 
				
			||||||
 | 
										<a
 | 
				
			||||||
 | 
											href={AppRoute.FAVORITES}
 | 
				
			||||||
 | 
											class="w-full flex text-sm font-medium hover:text-immich-primary dark:hover:text-immich-dark-primary content-center gap-2"
 | 
				
			||||||
 | 
											draggable="false"
 | 
				
			||||||
 | 
										>
 | 
				
			||||||
 | 
											<StarOutline size={24} />
 | 
				
			||||||
 | 
											<span>Favorites</span>
 | 
				
			||||||
 | 
										</a>
 | 
				
			||||||
 | 
										<a
 | 
				
			||||||
 | 
											href="/search?recent=true"
 | 
				
			||||||
 | 
											class="w-full flex text-sm font-medium hover:text-immich-primary dark:hover:text-immich-dark-primary content-center gap-2"
 | 
				
			||||||
 | 
											draggable="false"
 | 
				
			||||||
 | 
										>
 | 
				
			||||||
 | 
											<ClockOutline size={24} />
 | 
				
			||||||
 | 
											<span>Recently added</span>
 | 
				
			||||||
 | 
										</a>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<div class="flex flex-col gap-6 dark:text-immich-dark-fg">
 | 
				
			||||||
 | 
									<p class="text-sm">CATEGORIES</p>
 | 
				
			||||||
 | 
									<div class="flex flex-col gap-4 dark:text-immich-dark-fg/80">
 | 
				
			||||||
 | 
										<a
 | 
				
			||||||
 | 
											href="/search?type={AssetTypeEnum.Video}"
 | 
				
			||||||
 | 
											class="w-full flex text-sm font-medium hover:text-immich-primary dark:hover:text-immich-dark-primary items-center gap-2"
 | 
				
			||||||
 | 
										>
 | 
				
			||||||
 | 
											<PlayCircleOutline size={24} />
 | 
				
			||||||
 | 
											<span>Videos</span>
 | 
				
			||||||
 | 
										</a>
 | 
				
			||||||
 | 
										<div>
 | 
				
			||||||
 | 
											<a
 | 
				
			||||||
 | 
												href="/search?motion=true"
 | 
				
			||||||
 | 
												class="w-full flex text-sm font-medium hover:text-immich-primary dark:hover:text-immich-dark-primary items-center gap-2"
 | 
				
			||||||
 | 
											>
 | 
				
			||||||
 | 
												<MotionPlayOutline size={24} />
 | 
				
			||||||
 | 
												<span>Motion photos</span>
 | 
				
			||||||
 | 
											</a>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		</section>
 | 
							</div>
 | 
				
			||||||
	</section>
 | 
						</div>
 | 
				
			||||||
</section>
 | 
					</UserPageLayout>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +1,16 @@
 | 
				
			|||||||
import type { PageServerLoad } from './$types';
 | 
					import type { PageServerLoad } from './$types';
 | 
				
			||||||
import { redirect, error } from '@sveltejs/kit';
 | 
					import { redirect } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					import { AppRoute } from '$lib/constants';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const load: PageServerLoad = async ({ parent }) => {
 | 
					export const load = (async ({ locals: { user } }) => {
 | 
				
			||||||
	try {
 | 
						if (!user) {
 | 
				
			||||||
		const { user } = await parent();
 | 
							throw redirect(302, AppRoute.AUTH_LOGIN);
 | 
				
			||||||
		if (!user) {
 | 
					 | 
				
			||||||
			throw error(400, 'Not logged in');
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			user,
 | 
					 | 
				
			||||||
			meta: {
 | 
					 | 
				
			||||||
				title: 'Favorites'
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	} catch (e) {
 | 
					 | 
				
			||||||
		console.log('Photo page load error', e);
 | 
					 | 
				
			||||||
		throw redirect(302, '/auth/login');
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
};
 | 
					
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							user,
 | 
				
			||||||
 | 
							meta: {
 | 
				
			||||||
 | 
								title: 'Favorites'
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}) satisfies PageServerLoad;
 | 
				
			||||||
 | 
				
			|||||||
@ -3,8 +3,6 @@
 | 
				
			|||||||
	import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
 | 
						import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
 | 
				
			||||||
	import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte';
 | 
						import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte';
 | 
				
			||||||
	import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte';
 | 
						import GalleryViewer from '$lib/components/shared-components/gallery-viewer/gallery-viewer.svelte';
 | 
				
			||||||
	import NavigationBar from '$lib/components/shared-components/navigation-bar/navigation-bar.svelte';
 | 
					 | 
				
			||||||
	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
 | 
					 | 
				
			||||||
	import { handleError } from '$lib/utils/handle-error';
 | 
						import { handleError } from '$lib/utils/handle-error';
 | 
				
			||||||
	import { api, AssetResponseDto, SharedLinkType } from '@api';
 | 
						import { api, AssetResponseDto, SharedLinkType } from '@api';
 | 
				
			||||||
	import { onMount } from 'svelte';
 | 
						import { onMount } from 'svelte';
 | 
				
			||||||
@ -12,15 +10,16 @@
 | 
				
			|||||||
	import ShareVariantOutline from 'svelte-material-icons/ShareVariantOutline.svelte';
 | 
						import ShareVariantOutline from 'svelte-material-icons/ShareVariantOutline.svelte';
 | 
				
			||||||
	import StarMinusOutline from 'svelte-material-icons/StarMinusOutline.svelte';
 | 
						import StarMinusOutline from 'svelte-material-icons/StarMinusOutline.svelte';
 | 
				
			||||||
	import Error from '../../+error.svelte';
 | 
						import Error from '../../+error.svelte';
 | 
				
			||||||
	import type { PageData } from './$types';
 | 
					 | 
				
			||||||
	import empty1Url from '$lib/assets/empty-1.svg';
 | 
						import empty1Url from '$lib/assets/empty-1.svg';
 | 
				
			||||||
 | 
						import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
 | 
				
			||||||
	export let data: PageData;
 | 
						import type { PageData } from './$types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let favorites: AssetResponseDto[] = [];
 | 
						let favorites: AssetResponseDto[] = [];
 | 
				
			||||||
	let isShowCreateSharedLinkModal = false;
 | 
						let isShowCreateSharedLinkModal = false;
 | 
				
			||||||
	let selectedAssets: Set<AssetResponseDto> = new Set();
 | 
						let selectedAssets: Set<AssetResponseDto> = new Set();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						export let data: PageData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	$: isMultiSelectionMode = selectedAssets.size > 0;
 | 
						$: isMultiSelectionMode = selectedAssets.size > 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	onMount(async () => {
 | 
						onMount(async () => {
 | 
				
			||||||
@ -61,81 +60,57 @@
 | 
				
			|||||||
	};
 | 
						};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<section>
 | 
					<!-- Multiselection mode app bar -->
 | 
				
			||||||
	<NavigationBar user={data.user} shouldShowUploadButton={false} />
 | 
					{#if isMultiSelectionMode}
 | 
				
			||||||
</section>
 | 
						<ControlAppBar
 | 
				
			||||||
 | 
							on:close-button-click={clearMultiSelectAssetAssetHandler}
 | 
				
			||||||
 | 
							backIcon={Close}
 | 
				
			||||||
 | 
							tailwindClasses={'bg-white shadow-md'}
 | 
				
			||||||
 | 
						>
 | 
				
			||||||
 | 
							<svelte:fragment slot="leading">
 | 
				
			||||||
 | 
								<p class="font-medium text-immich-primary dark:text-immich-dark-primary">
 | 
				
			||||||
 | 
									Selected {selectedAssets.size}
 | 
				
			||||||
 | 
								</p>
 | 
				
			||||||
 | 
							</svelte:fragment>
 | 
				
			||||||
 | 
							<svelte:fragment slot="trailing">
 | 
				
			||||||
 | 
								<CircleIconButton
 | 
				
			||||||
 | 
									title="Share"
 | 
				
			||||||
 | 
									logo={ShareVariantOutline}
 | 
				
			||||||
 | 
									on:click={handleCreateSharedLink}
 | 
				
			||||||
 | 
								/>
 | 
				
			||||||
 | 
								<CircleIconButton
 | 
				
			||||||
 | 
									title="Remove Favorite"
 | 
				
			||||||
 | 
									logo={StarMinusOutline}
 | 
				
			||||||
 | 
									on:click={handleRemoveFavorite}
 | 
				
			||||||
 | 
								/>
 | 
				
			||||||
 | 
							</svelte:fragment>
 | 
				
			||||||
 | 
						</ControlAppBar>
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<section
 | 
					<!-- Create shared link modal -->
 | 
				
			||||||
	class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg  dark:bg-immich-dark-bg"
 | 
					{#if isShowCreateSharedLinkModal}
 | 
				
			||||||
>
 | 
						<CreateSharedLinkModal
 | 
				
			||||||
	<SideBar />
 | 
							sharedAssets={Array.from(selectedAssets)}
 | 
				
			||||||
 | 
							shareType={SharedLinkType.Individual}
 | 
				
			||||||
 | 
							on:close={handleCloseSharedLinkModal}
 | 
				
			||||||
 | 
						/>
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<!-- Multiselection mode app bar -->
 | 
					<UserPageLayout user={data.user} title={data.meta.title} hideNavbar={isMultiSelectionMode}>
 | 
				
			||||||
	{#if isMultiSelectionMode}
 | 
						<section>
 | 
				
			||||||
		<ControlAppBar
 | 
							<!-- Empty Message -->
 | 
				
			||||||
			on:close-button-click={clearMultiSelectAssetAssetHandler}
 | 
							{#if favorites.length === 0}
 | 
				
			||||||
			backIcon={Close}
 | 
								<div
 | 
				
			||||||
			tailwindClasses={'bg-white shadow-md'}
 | 
									class="border dark:border-immich-dark-gray hover:bg-immich-primary/5 dark:hover:bg-immich-dark-primary/25 hover:cursor-pointer p-5 w-[50%] m-auto mt-10 bg-gray-50 dark:bg-immich-dark-gray rounded-3xl flex flex-col place-content-center place-items-center"
 | 
				
			||||||
		>
 | 
								>
 | 
				
			||||||
			<svelte:fragment slot="leading">
 | 
									<img src={empty1Url} alt="Empty shared album" width="500" draggable="false" />
 | 
				
			||||||
				<p class="font-medium text-immich-primary dark:text-immich-dark-primary">
 | 
					
 | 
				
			||||||
					Selected {selectedAssets.size}
 | 
									<p class="text-center text-immich-text-gray-500 dark:text-immich-dark-fg">
 | 
				
			||||||
 | 
										Add favorites to quickly find your best pictures and videos
 | 
				
			||||||
				</p>
 | 
									</p>
 | 
				
			||||||
			</svelte:fragment>
 | 
					 | 
				
			||||||
			<svelte:fragment slot="trailing">
 | 
					 | 
				
			||||||
				<CircleIconButton
 | 
					 | 
				
			||||||
					title="Share"
 | 
					 | 
				
			||||||
					logo={ShareVariantOutline}
 | 
					 | 
				
			||||||
					on:click={handleCreateSharedLink}
 | 
					 | 
				
			||||||
				/>
 | 
					 | 
				
			||||||
				<CircleIconButton
 | 
					 | 
				
			||||||
					title="Remove Favorite"
 | 
					 | 
				
			||||||
					logo={StarMinusOutline}
 | 
					 | 
				
			||||||
					on:click={handleRemoveFavorite}
 | 
					 | 
				
			||||||
				/>
 | 
					 | 
				
			||||||
			</svelte:fragment>
 | 
					 | 
				
			||||||
		</ControlAppBar>
 | 
					 | 
				
			||||||
	{/if}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<!-- Create shared link modal -->
 | 
					 | 
				
			||||||
	{#if isShowCreateSharedLinkModal}
 | 
					 | 
				
			||||||
		<CreateSharedLinkModal
 | 
					 | 
				
			||||||
			sharedAssets={Array.from(selectedAssets)}
 | 
					 | 
				
			||||||
			shareType={SharedLinkType.Individual}
 | 
					 | 
				
			||||||
			on:close={handleCloseSharedLinkModal}
 | 
					 | 
				
			||||||
		/>
 | 
					 | 
				
			||||||
	{/if}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<!-- Main Section -->
 | 
					 | 
				
			||||||
	<section class="overflow-y-auto relative immich-scrollbar">
 | 
					 | 
				
			||||||
		<section
 | 
					 | 
				
			||||||
			id="favorite-content"
 | 
					 | 
				
			||||||
			class="relative pt-8 pl-4 mb-12 bg-immich-bg dark:bg-immich-dark-bg"
 | 
					 | 
				
			||||||
		>
 | 
					 | 
				
			||||||
			<div class="px-4 flex justify-between place-items-center dark:text-immich-dark-fg">
 | 
					 | 
				
			||||||
				<div>
 | 
					 | 
				
			||||||
					<p class="font-medium">Favorites</p>
 | 
					 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 | 
							{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			<div class="my-4">
 | 
							<GalleryViewer assets={favorites} bind:selectedAssets />
 | 
				
			||||||
				<hr class="dark:border-immich-dark-gray" />
 | 
					 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			<!-- Empty Message -->
 | 
					 | 
				
			||||||
			{#if favorites.length === 0}
 | 
					 | 
				
			||||||
				<div
 | 
					 | 
				
			||||||
					class="border dark:border-immich-dark-gray hover:bg-immich-primary/5 dark:hover:bg-immich-dark-primary/25 hover:cursor-pointer p-5 w-[50%] m-auto mt-10 bg-gray-50 dark:bg-immich-dark-gray rounded-3xl flex flex-col place-content-center place-items-center"
 | 
					 | 
				
			||||||
				>
 | 
					 | 
				
			||||||
					<img src={empty1Url} alt="Empty shared album" width="500" draggable="false" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					<p class="text-center text-immich-text-gray-500 dark:text-immich-dark-fg">
 | 
					 | 
				
			||||||
						Add favorites to quickly find your best pictures and videos
 | 
					 | 
				
			||||||
					</p>
 | 
					 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
			{/if}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			<GalleryViewer assets={favorites} bind:selectedAssets />
 | 
					 | 
				
			||||||
		</section>
 | 
					 | 
				
			||||||
	</section>
 | 
						</section>
 | 
				
			||||||
</section>
 | 
					</UserPageLayout>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +1,16 @@
 | 
				
			|||||||
import type { PageServerLoad } from './$types';
 | 
					import type { PageServerLoad } from './$types';
 | 
				
			||||||
import { redirect, error } from '@sveltejs/kit';
 | 
					import { redirect } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					import { AppRoute } from '$lib/constants';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const load: PageServerLoad = async ({ parent }) => {
 | 
					export const load = (async ({ locals: { user } }) => {
 | 
				
			||||||
	try {
 | 
						if (!user) {
 | 
				
			||||||
		const { user } = await parent();
 | 
							throw redirect(302, AppRoute.AUTH_LOGIN);
 | 
				
			||||||
		if (!user) {
 | 
					 | 
				
			||||||
			throw error(400, 'Not logged in');
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			user,
 | 
					 | 
				
			||||||
			meta: {
 | 
					 | 
				
			||||||
				title: 'Photos'
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	} catch (e) {
 | 
					 | 
				
			||||||
		console.log('Photo page load error', e);
 | 
					 | 
				
			||||||
		throw redirect(302, '/auth/login');
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
};
 | 
					
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							user,
 | 
				
			||||||
 | 
							meta: {
 | 
				
			||||||
 | 
								title: 'Photos'
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}) satisfies PageServerLoad;
 | 
				
			||||||
 | 
				
			|||||||
@ -7,12 +7,10 @@
 | 
				
			|||||||
	import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
 | 
						import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
 | 
				
			||||||
	import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
 | 
						import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
 | 
				
			||||||
	import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte';
 | 
						import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte';
 | 
				
			||||||
	import NavigationBar from '$lib/components/shared-components/navigation-bar/navigation-bar.svelte';
 | 
					 | 
				
			||||||
	import {
 | 
						import {
 | 
				
			||||||
		notificationController,
 | 
							notificationController,
 | 
				
			||||||
		NotificationType
 | 
							NotificationType
 | 
				
			||||||
	} from '$lib/components/shared-components/notification/notification';
 | 
						} from '$lib/components/shared-components/notification/notification';
 | 
				
			||||||
	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
 | 
					 | 
				
			||||||
	import {
 | 
						import {
 | 
				
			||||||
		assetInteractionStore,
 | 
							assetInteractionStore,
 | 
				
			||||||
		isMultiSelectStoreState,
 | 
							isMultiSelectStoreState,
 | 
				
			||||||
@ -20,17 +18,18 @@
 | 
				
			|||||||
	} from '$lib/stores/asset-interaction.store';
 | 
						} from '$lib/stores/asset-interaction.store';
 | 
				
			||||||
	import { assetStore } from '$lib/stores/assets.store';
 | 
						import { assetStore } from '$lib/stores/assets.store';
 | 
				
			||||||
	import { addAssetsToAlbum, bulkDownload } from '$lib/utils/asset-utils';
 | 
						import { addAssetsToAlbum, bulkDownload } from '$lib/utils/asset-utils';
 | 
				
			||||||
	import { openFileUploadDialog } from '$lib/utils/file-uploader';
 | 
					 | 
				
			||||||
	import { AlbumResponseDto, api, SharedLinkType } from '@api';
 | 
						import { AlbumResponseDto, api, SharedLinkType } from '@api';
 | 
				
			||||||
	import Close from 'svelte-material-icons/Close.svelte';
 | 
						import Close from 'svelte-material-icons/Close.svelte';
 | 
				
			||||||
	import CloudDownloadOutline from 'svelte-material-icons/CloudDownloadOutline.svelte';
 | 
						import CloudDownloadOutline from 'svelte-material-icons/CloudDownloadOutline.svelte';
 | 
				
			||||||
	import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
 | 
						import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
 | 
				
			||||||
	import Plus from 'svelte-material-icons/Plus.svelte';
 | 
						import Plus from 'svelte-material-icons/Plus.svelte';
 | 
				
			||||||
	import ShareVariantOutline from 'svelte-material-icons/ShareVariantOutline.svelte';
 | 
						import ShareVariantOutline from 'svelte-material-icons/ShareVariantOutline.svelte';
 | 
				
			||||||
	import type { PageData } from './$types';
 | 
					 | 
				
			||||||
	import { locale } from '$lib/stores/preferences.store';
 | 
						import { locale } from '$lib/stores/preferences.store';
 | 
				
			||||||
 | 
						import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
 | 
				
			||||||
 | 
						import type { PageData } from './$types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	export let data: PageData;
 | 
						export let data: PageData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let isShowCreateSharedLinkModal = false;
 | 
						let isShowCreateSharedLinkModal = false;
 | 
				
			||||||
	const deleteSelectedAssetHandler = async () => {
 | 
						const deleteSelectedAssetHandler = async () => {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
@ -144,73 +143,68 @@
 | 
				
			|||||||
	};
 | 
						};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<section>
 | 
					<UserPageLayout user={data.user} hideNavbar={$isMultiSelectStoreState} showUploadButton>
 | 
				
			||||||
	{#if $isMultiSelectStoreState}
 | 
						<svelte:fragment slot="header">
 | 
				
			||||||
		<ControlAppBar
 | 
							{#if $isMultiSelectStoreState}
 | 
				
			||||||
			on:close-button-click={() => assetInteractionStore.clearMultiselect()}
 | 
								<ControlAppBar
 | 
				
			||||||
			backIcon={Close}
 | 
									on:close-button-click={() => assetInteractionStore.clearMultiselect()}
 | 
				
			||||||
			tailwindClasses={'bg-white shadow-md'}
 | 
									backIcon={Close}
 | 
				
			||||||
		>
 | 
									tailwindClasses={'bg-white shadow-md'}
 | 
				
			||||||
			<svelte:fragment slot="leading">
 | 
								>
 | 
				
			||||||
				<p class="font-medium text-immich-primary dark:text-immich-dark-primary">
 | 
									<svelte:fragment slot="leading">
 | 
				
			||||||
					Selected {$selectedAssets.size.toLocaleString($locale)}
 | 
										<p class="font-medium text-immich-primary dark:text-immich-dark-primary">
 | 
				
			||||||
				</p>
 | 
											Selected {$selectedAssets.size.toLocaleString($locale)}
 | 
				
			||||||
			</svelte:fragment>
 | 
										</p>
 | 
				
			||||||
			<svelte:fragment slot="trailing">
 | 
									</svelte:fragment>
 | 
				
			||||||
				<CircleIconButton
 | 
									<svelte:fragment slot="trailing">
 | 
				
			||||||
					title="Share"
 | 
										<CircleIconButton
 | 
				
			||||||
					logo={ShareVariantOutline}
 | 
											title="Share"
 | 
				
			||||||
					on:click={handleCreateSharedLink}
 | 
											logo={ShareVariantOutline}
 | 
				
			||||||
				/>
 | 
											on:click={handleCreateSharedLink}
 | 
				
			||||||
				<CircleIconButton
 | 
										/>
 | 
				
			||||||
					title="Download"
 | 
										<CircleIconButton
 | 
				
			||||||
					logo={CloudDownloadOutline}
 | 
											title="Download"
 | 
				
			||||||
					on:click={handleDownloadFiles}
 | 
											logo={CloudDownloadOutline}
 | 
				
			||||||
				/>
 | 
											on:click={handleDownloadFiles}
 | 
				
			||||||
				<CircleIconButton title="Add" logo={Plus} on:click={handleShowMenu} />
 | 
										/>
 | 
				
			||||||
				<CircleIconButton
 | 
										<CircleIconButton title="Add" logo={Plus} on:click={handleShowMenu} />
 | 
				
			||||||
					title="Delete"
 | 
										<CircleIconButton
 | 
				
			||||||
					logo={DeleteOutline}
 | 
											title="Delete"
 | 
				
			||||||
					on:click={deleteSelectedAssetHandler}
 | 
											logo={DeleteOutline}
 | 
				
			||||||
				/>
 | 
											on:click={deleteSelectedAssetHandler}
 | 
				
			||||||
			</svelte:fragment>
 | 
										/>
 | 
				
			||||||
		</ControlAppBar>
 | 
									</svelte:fragment>
 | 
				
			||||||
	{:else}
 | 
								</ControlAppBar>
 | 
				
			||||||
		<NavigationBar user={data.user} on:uploadClicked={() => openFileUploadDialog()} />
 | 
							{/if}
 | 
				
			||||||
	{/if}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	{#if isShowAddMenu}
 | 
							{#if isShowAddMenu}
 | 
				
			||||||
		<ContextMenu {...contextMenuPosition} on:clickoutside={() => (isShowAddMenu = false)}>
 | 
								<ContextMenu {...contextMenuPosition} on:clickoutside={() => (isShowAddMenu = false)}>
 | 
				
			||||||
			<div class="flex flex-col rounded-lg ">
 | 
									<div class="flex flex-col rounded-lg ">
 | 
				
			||||||
				<MenuOption on:click={handleAddToFavorites} text="Add to Favorites" />
 | 
										<MenuOption on:click={handleAddToFavorites} text="Add to Favorites" />
 | 
				
			||||||
				<MenuOption on:click={() => handleShowAlbumPicker(false)} text="Add to Album" />
 | 
										<MenuOption on:click={() => handleShowAlbumPicker(false)} text="Add to Album" />
 | 
				
			||||||
				<MenuOption on:click={() => handleShowAlbumPicker(true)} text="Add to Shared Album" />
 | 
										<MenuOption on:click={() => handleShowAlbumPicker(true)} text="Add to Shared Album" />
 | 
				
			||||||
			</div>
 | 
									</div>
 | 
				
			||||||
		</ContextMenu>
 | 
								</ContextMenu>
 | 
				
			||||||
	{/if}
 | 
							{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	{#if isShowAlbumPicker}
 | 
							{#if isShowAlbumPicker}
 | 
				
			||||||
		<AlbumSelectionModal
 | 
								<AlbumSelectionModal
 | 
				
			||||||
			shared={addToSharedAlbum}
 | 
									shared={addToSharedAlbum}
 | 
				
			||||||
			on:newAlbum={handleAddToNewAlbum}
 | 
									on:newAlbum={handleAddToNewAlbum}
 | 
				
			||||||
			on:newSharedAlbum={handleAddToNewAlbum}
 | 
									on:newSharedAlbum={handleAddToNewAlbum}
 | 
				
			||||||
			on:album={handleAddToAlbum}
 | 
									on:album={handleAddToAlbum}
 | 
				
			||||||
			on:close={() => (isShowAlbumPicker = false)}
 | 
									on:close={() => (isShowAlbumPicker = false)}
 | 
				
			||||||
		/>
 | 
								/>
 | 
				
			||||||
	{/if}
 | 
							{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	{#if isShowCreateSharedLinkModal}
 | 
							{#if isShowCreateSharedLinkModal}
 | 
				
			||||||
		<CreateSharedLinkModal
 | 
								<CreateSharedLinkModal
 | 
				
			||||||
			sharedAssets={Array.from($selectedAssets)}
 | 
									sharedAssets={Array.from($selectedAssets)}
 | 
				
			||||||
			shareType={SharedLinkType.Individual}
 | 
									shareType={SharedLinkType.Individual}
 | 
				
			||||||
			on:close={handleCloseSharedLinkModal}
 | 
									on:close={handleCloseSharedLinkModal}
 | 
				
			||||||
		/>
 | 
								/>
 | 
				
			||||||
	{/if}
 | 
							{/if}
 | 
				
			||||||
</section>
 | 
						</svelte:fragment>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<section
 | 
						<AssetGrid slot="content" />
 | 
				
			||||||
	class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg dark:bg-immich-dark-bg"
 | 
					</UserPageLayout>
 | 
				
			||||||
>
 | 
					 | 
				
			||||||
	<SideBar />
 | 
					 | 
				
			||||||
	<AssetGrid />
 | 
					 | 
				
			||||||
</section>
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,10 @@
 | 
				
			|||||||
export const prerender = false;
 | 
					 | 
				
			||||||
import { error } from '@sveltejs/kit';
 | 
					import { error } from '@sveltejs/kit';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import { getThumbnailUrl } from '$lib/utils/asset-utils';
 | 
					import { getThumbnailUrl } from '$lib/utils/asset-utils';
 | 
				
			||||||
import { ThumbnailFormat } from '@api';
 | 
					import { ThumbnailFormat } from '@api';
 | 
				
			||||||
import type { PageServerLoad } from './$types';
 | 
					import type { PageServerLoad } from './$types';
 | 
				
			||||||
import featurePanelUrl from '$lib/assets/feature-panel.png';
 | 
					import featurePanelUrl from '$lib/assets/feature-panel.png';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const load: PageServerLoad = async ({ params, parent, locals: { api } }) => {
 | 
					export const load = (async ({ params, locals: { api } }) => {
 | 
				
			||||||
	const { user } = await parent();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const { key } = params;
 | 
						const { key } = params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
@ -25,12 +21,11 @@ export const load: PageServerLoad = async ({ params, parent, locals: { api } })
 | 
				
			|||||||
				imageUrl: assetId
 | 
									imageUrl: assetId
 | 
				
			||||||
					? getThumbnailUrl(assetId, ThumbnailFormat.Webp, sharedLink.key)
 | 
										? getThumbnailUrl(assetId, ThumbnailFormat.Webp, sharedLink.key)
 | 
				
			||||||
					: featurePanelUrl
 | 
										: featurePanelUrl
 | 
				
			||||||
			},
 | 
								}
 | 
				
			||||||
			user
 | 
					 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	} catch (e) {
 | 
						} catch (e) {
 | 
				
			||||||
		throw error(404, {
 | 
							throw error(404, {
 | 
				
			||||||
			message: 'Invalid shared link'
 | 
								message: 'Invalid shared link'
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
};
 | 
					}) satisfies PageServerLoad;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,23 @@
 | 
				
			|||||||
export const prerender = false;
 | 
					import { AppRoute } from '$lib/constants';
 | 
				
			||||||
 | 
					 | 
				
			||||||
import { redirect } from '@sveltejs/kit';
 | 
					import { redirect } from '@sveltejs/kit';
 | 
				
			||||||
import type { PageServerLoad } from './$types';
 | 
					import type { PageServerLoad } from './$types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const load = (async ({ parent, locals: { api } }) => {
 | 
					export const load = (async ({ locals: { api, user } }) => {
 | 
				
			||||||
	try {
 | 
						if (!user) {
 | 
				
			||||||
		const { user } = await parent();
 | 
							throw redirect(302, AppRoute.AUTH_LOGIN);
 | 
				
			||||||
		if (!user) {
 | 
						}
 | 
				
			||||||
			throw redirect(302, '/auth/login');
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
		const { data: sharedAlbums } = await api.albumApi.getAllAlbums(true);
 | 
							const { data: sharedAlbums } = await api.albumApi.getAllAlbums(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			user: user,
 | 
								user,
 | 
				
			||||||
			sharedAlbums,
 | 
								sharedAlbums,
 | 
				
			||||||
			meta: {
 | 
								meta: {
 | 
				
			||||||
				title: 'Albums'
 | 
									title: 'Albums'
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	} catch (e) {
 | 
						} catch (e) {
 | 
				
			||||||
		throw redirect(302, '/auth/login');
 | 
							throw redirect(302, AppRoute.AUTH_LOGIN);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}) satisfies PageServerLoad;
 | 
					}) satisfies PageServerLoad;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,6 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import NavigationBar from '$lib/components/shared-components/navigation-bar/navigation-bar.svelte';
 | 
					 | 
				
			||||||
	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
 | 
					 | 
				
			||||||
	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 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';
 | 
				
			||||||
@ -13,6 +10,7 @@
 | 
				
			|||||||
		NotificationType
 | 
							NotificationType
 | 
				
			||||||
	} from '$lib/components/shared-components/notification/notification';
 | 
						} from '$lib/components/shared-components/notification/notification';
 | 
				
			||||||
	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';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	export let data: PageData;
 | 
						export let data: PageData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -34,73 +32,49 @@
 | 
				
			|||||||
	};
 | 
						};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<section>
 | 
					<UserPageLayout user={data.user} title={data.meta.title}>
 | 
				
			||||||
	<NavigationBar user={data.user} shouldShowUploadButton={false} />
 | 
						<div class="flex" slot="buttons">
 | 
				
			||||||
</section>
 | 
							<button
 | 
				
			||||||
 | 
								on:click={createSharedAlbum}
 | 
				
			||||||
<section
 | 
								class="flex place-items-center gap-1 text-sm hover:bg-immich-primary/5 p-2 rounded-lg font-medium hover:text-gray-700 dark:hover:bg-immich-dark-primary/25 dark:text-immich-dark-fg"
 | 
				
			||||||
	class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg dark:bg-immich-dark-bg"
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
	<SideBar />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<section class="overflow-y-auto relative">
 | 
					 | 
				
			||||||
		<section
 | 
					 | 
				
			||||||
			id="album-content"
 | 
					 | 
				
			||||||
			class="relative pt-8 pl-4 mb-12 bg-immich-bg dark:bg-immich-dark-bg"
 | 
					 | 
				
			||||||
		>
 | 
							>
 | 
				
			||||||
			<!-- Main Section -->
 | 
								<span>
 | 
				
			||||||
			<div class="px-4 flex justify-between place-items-center dark:text-immich-dark-fg">
 | 
									<PlusBoxOutline size="18" />
 | 
				
			||||||
				<div>
 | 
								</span>
 | 
				
			||||||
					<p class="font-medium">Sharing</p>
 | 
								<p>Create shared album</p>
 | 
				
			||||||
				</div>
 | 
							</button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				<div class="flex">
 | 
							<button
 | 
				
			||||||
					<button
 | 
								on:click={() => goto('/sharing/sharedlinks')}
 | 
				
			||||||
						on:click={createSharedAlbum}
 | 
								class="flex place-items-center gap-1 text-sm hover:bg-immich-primary/5 p-2 rounded-lg font-medium hover:text-gray-700 dark:hover:bg-immich-dark-primary/25 dark:text-immich-dark-fg"
 | 
				
			||||||
						class="flex place-items-center gap-1 text-sm hover:bg-immich-primary/5 p-2 rounded-lg font-medium hover:text-gray-700 dark:hover:bg-immich-dark-primary/25 dark:text-immich-dark-fg"
 | 
							>
 | 
				
			||||||
					>
 | 
								<span>
 | 
				
			||||||
						<span>
 | 
									<Link size="18" />
 | 
				
			||||||
							<PlusBoxOutline size="18" />
 | 
								</span>
 | 
				
			||||||
						</span>
 | 
								<p>Shared links</p>
 | 
				
			||||||
						<p>Create shared album</p>
 | 
							</button>
 | 
				
			||||||
					</button>
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					<button
 | 
						<section>
 | 
				
			||||||
						on:click={() => goto('/sharing/sharedlinks')}
 | 
							<!-- Share Album List -->
 | 
				
			||||||
						class="flex place-items-center gap-1 text-sm hover:bg-immich-primary/5 p-2 rounded-lg font-medium hover:text-gray-700 dark:hover:bg-immich-dark-primary/25 dark:text-immich-dark-fg"
 | 
							<div class="w-full flex flex-col place-items-center">
 | 
				
			||||||
					>
 | 
								{#each data.sharedAlbums as album}
 | 
				
			||||||
						<span>
 | 
									<a data-sveltekit-preload-data="hover" href={`albums/${album.id}`}>
 | 
				
			||||||
							<Link size="18" />
 | 
										<SharedAlbumListTile {album} user={data.user} />
 | 
				
			||||||
						</span>
 | 
									</a>
 | 
				
			||||||
						<p>Shared links</p>
 | 
								{/each}
 | 
				
			||||||
					</button>
 | 
							</div>
 | 
				
			||||||
				</div>
 | 
					
 | 
				
			||||||
 | 
							<!-- Empty List -->
 | 
				
			||||||
 | 
							{#if data.sharedAlbums.length === 0}
 | 
				
			||||||
 | 
								<div
 | 
				
			||||||
 | 
									class="border dark:border-immich-dark-gray p-5 w-[50%] m-auto mt-10 bg-gray-50 dark:bg-immich-dark-gray rounded-3xl flex flex-col place-content-center place-items-center dark:text-immich-dark-fg"
 | 
				
			||||||
 | 
								>
 | 
				
			||||||
 | 
									<img src={empty2Url} alt="Empty shared album" width="500" draggable="false" />
 | 
				
			||||||
 | 
									<p class="text-center text-immich-text-gray-500">
 | 
				
			||||||
 | 
										Create a shared album to share photos and videos with people in your network
 | 
				
			||||||
 | 
									</p>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 | 
							{/if}
 | 
				
			||||||
			<div class="my-4">
 | 
					 | 
				
			||||||
				<hr class="dark:border-immich-dark-gray" />
 | 
					 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			<!-- Share Album List -->
 | 
					 | 
				
			||||||
			<div class="w-full flex flex-col place-items-center">
 | 
					 | 
				
			||||||
				{#each data.sharedAlbums as album}
 | 
					 | 
				
			||||||
					<a data-sveltekit-preload-data="hover" href={`albums/${album.id}`}>
 | 
					 | 
				
			||||||
						<SharedAlbumListTile {album} user={data.user} />
 | 
					 | 
				
			||||||
					</a>
 | 
					 | 
				
			||||||
				{/each}
 | 
					 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			<!-- Empty List -->
 | 
					 | 
				
			||||||
			{#if data.sharedAlbums.length === 0}
 | 
					 | 
				
			||||||
				<div
 | 
					 | 
				
			||||||
					class="border dark:border-immich-dark-gray p-5 w-[50%] m-auto mt-10 bg-gray-50 dark:bg-immich-dark-gray rounded-3xl flex flex-col place-content-center place-items-center dark:text-immich-dark-fg"
 | 
					 | 
				
			||||||
				>
 | 
					 | 
				
			||||||
					<img src={empty2Url} alt="Empty shared album" width="500" draggable="false" />
 | 
					 | 
				
			||||||
					<p class="text-center text-immich-text-gray-500">
 | 
					 | 
				
			||||||
						Create a shared album to share photos and videos with people in your network
 | 
					 | 
				
			||||||
					</p>
 | 
					 | 
				
			||||||
				</div>
 | 
					 | 
				
			||||||
			{/if}
 | 
					 | 
				
			||||||
		</section>
 | 
					 | 
				
			||||||
	</section>
 | 
						</section>
 | 
				
			||||||
</section>
 | 
					</UserPageLayout>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +1,16 @@
 | 
				
			|||||||
 | 
					import { AppRoute } from '$lib/constants';
 | 
				
			||||||
import { redirect } from '@sveltejs/kit';
 | 
					import { redirect } from '@sveltejs/kit';
 | 
				
			||||||
export const prerender = false;
 | 
					 | 
				
			||||||
import type { PageServerLoad } from './$types';
 | 
					import type { PageServerLoad } from './$types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const load: PageServerLoad = async ({ parent }) => {
 | 
					export const load = (async ({ locals: { user } }) => {
 | 
				
			||||||
	try {
 | 
						if (!user) {
 | 
				
			||||||
		const { user } = await parent();
 | 
							throw redirect(302, AppRoute.AUTH_LOGIN);
 | 
				
			||||||
		if (!user) {
 | 
					 | 
				
			||||||
			throw redirect(302, '/auth/login');
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			user,
 | 
					 | 
				
			||||||
			meta: {
 | 
					 | 
				
			||||||
				title: 'Shared Links'
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	} catch (e) {
 | 
					 | 
				
			||||||
		throw redirect(302, '/auth/login');
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
};
 | 
					
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							user,
 | 
				
			||||||
 | 
							meta: {
 | 
				
			||||||
 | 
								title: 'Shared Links'
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}) satisfies PageServerLoad;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +1,16 @@
 | 
				
			|||||||
 | 
					import { AppRoute } from '$lib/constants';
 | 
				
			||||||
import { redirect } from '@sveltejs/kit';
 | 
					import { redirect } from '@sveltejs/kit';
 | 
				
			||||||
import type { PageServerLoad } from './$types';
 | 
					import type { PageServerLoad } from './$types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const load: PageServerLoad = async ({ parent }) => {
 | 
					export const load = (async ({ locals: { user } }) => {
 | 
				
			||||||
	try {
 | 
						if (!user) {
 | 
				
			||||||
		const { user } = await parent();
 | 
							throw redirect(302, AppRoute.AUTH_LOGIN);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (!user) {
 | 
					 | 
				
			||||||
			throw Error('User is not logged in');
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			user,
 | 
					 | 
				
			||||||
			meta: {
 | 
					 | 
				
			||||||
				title: 'Settings'
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	} catch (e) {
 | 
					 | 
				
			||||||
		throw redirect(302, '/auth/login');
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
};
 | 
					
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							user,
 | 
				
			||||||
 | 
							meta: {
 | 
				
			||||||
 | 
								title: 'Settings'
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}) satisfies PageServerLoad;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,36 +1,15 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
	import NavigationBar from '$lib/components/shared-components/navigation-bar/navigation-bar.svelte';
 | 
						import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
 | 
				
			||||||
	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
 | 
					 | 
				
			||||||
	import UserSettingsList from '$lib/components/user-settings-page/user-settings-list.svelte';
 | 
						import UserSettingsList from '$lib/components/user-settings-page/user-settings-list.svelte';
 | 
				
			||||||
	import type { PageData } from './$types';
 | 
						import type { PageData } from './$types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	export let data: PageData;
 | 
						export let data: PageData;
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<section>
 | 
					<UserPageLayout user={data.user} title={data.meta.title}>
 | 
				
			||||||
	<NavigationBar user={data.user} shouldShowUploadButton={false} />
 | 
						<section class="flex place-content-center mx-4">
 | 
				
			||||||
</section>
 | 
							<div class="w-full max-w-3xl">
 | 
				
			||||||
 | 
								<UserSettingsList user={data.user} />
 | 
				
			||||||
<section
 | 
					 | 
				
			||||||
	class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg dark:bg-immich-dark-bg"
 | 
					 | 
				
			||||||
>
 | 
					 | 
				
			||||||
	<SideBar />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<section class="overflow-y-auto ">
 | 
					 | 
				
			||||||
		<div
 | 
					 | 
				
			||||||
			id="user-setting-title"
 | 
					 | 
				
			||||||
			class="pt-10 fixed w-full z-10 bg-immich-bg dark:bg-immich-dark-bg"
 | 
					 | 
				
			||||||
		>
 | 
					 | 
				
			||||||
			<h1 class="text-lg ml-8 mb-4 text-immich-primary dark:text-immich-dark-primary font-medium">
 | 
					 | 
				
			||||||
				User Settings
 | 
					 | 
				
			||||||
			</h1>
 | 
					 | 
				
			||||||
			<hr class="dark:border-immich-dark-gray" />
 | 
					 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
		<section id="user-setting-content" class="pt-[85px] flex place-content-center">
 | 
					 | 
				
			||||||
			<section class="w-[800px] pt-5">
 | 
					 | 
				
			||||||
				<UserSettingsList user={data.user} />
 | 
					 | 
				
			||||||
			</section>
 | 
					 | 
				
			||||||
		</section>
 | 
					 | 
				
			||||||
	</section>
 | 
						</section>
 | 
				
			||||||
</section>
 | 
					</UserPageLayout>
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user