Selaa lähdekoodia

Migrate SvelteKit to the latest version `431` (#526)

Alex 2 vuotta sitten
vanhempi
commit
db2ed2d881
47 muutettua tiedostoa jossa 837 lisäystä ja 889 poistoa
  1. 3 0
      docker/docker-compose.dev.yml
  2. 2 2
      server/Dockerfile
  3. 342 209
      web/package-lock.json
  4. 0 1
      web/package.json
  5. 0 6
      web/src/app.d.ts
  6. 2 28
      web/src/hooks.ts
  7. 1 1
      web/src/lib/components/album-page/album-card.svelte
  8. 0 1
      web/src/lib/components/shared-components/upload-panel.svelte
  9. 29 0
      web/src/routes/+error.svelte
  10. 23 0
      web/src/routes/+layout.server.ts
  11. 3 13
      web/src/routes/+layout.svelte
  12. 29 0
      web/src/routes/+page.svelte
  13. 21 0
      web/src/routes/+page.ts
  14. 19 0
      web/src/routes/admin/+page.server.ts
  15. 12 55
      web/src/routes/admin/+page.svelte
  16. 22 0
      web/src/routes/albums/+page.server.ts
  17. 17 56
      web/src/routes/albums/+page.svelte
  18. 23 0
      web/src/routes/albums/[albumId]/+page.server.ts
  19. 14 0
      web/src/routes/albums/[albumId]/+page.svelte
  20. 0 57
      web/src/routes/albums/[albumId]/index.svelte
  21. 0 28
      web/src/routes/albums/[albumId]/photos/[assetId].svelte
  22. 0 0
      web/src/routes/albums/[albumId]/photos/[assetId]/+page.svelte
  23. 18 0
      web/src/routes/albums/[albumId]/photos/[assetId]/+page.ts
  24. 25 0
      web/src/routes/auth/change-password/+page.svelte
  25. 21 0
      web/src/routes/auth/change-password/+page.ts
  26. 0 56
      web/src/routes/auth/change-password/index.svelte
  27. 1 1
      web/src/routes/auth/login/+page.svelte
  28. 0 19
      web/src/routes/auth/logout.ts
  29. 27 0
      web/src/routes/auth/logout/+server.ts
  30. 13 0
      web/src/routes/auth/register/+page.server.ts
  31. 11 0
      web/src/routes/auth/register/+page.svelte
  32. 0 31
      web/src/routes/auth/register/index.svelte
  33. 0 1
      web/src/routes/data/README.md
  34. 0 18
      web/src/routes/data/album/get-album-info.ts
  35. 0 18
      web/src/routes/data/album/get-all-albums.ts
  36. 0 15
      web/src/routes/data/asset/get-all-assets.ts
  37. 0 17
      web/src/routes/data/user/get-all-users.ts
  38. 0 15
      web/src/routes/data/user/get-my-user-info.ts
  39. 0 58
      web/src/routes/index.svelte
  40. 21 0
      web/src/routes/photos/+page.server.ts
  41. 18 43
      web/src/routes/photos/+page.svelte
  42. 0 20
      web/src/routes/photos/[assetId].svelte
  43. 14 0
      web/src/routes/photos/[assetId]/+page.server.ts
  44. 0 0
      web/src/routes/photos/[assetId]/+page.svelte
  45. 23 0
      web/src/routes/sharing/+page.server.ts
  46. 83 0
      web/src/routes/sharing/+page.svelte
  47. 0 120
      web/src/routes/sharing/index.svelte

+ 3 - 0
docker/docker-compose.dev.yml

@@ -6,6 +6,7 @@ services:
     build:
       context: ../server
       dockerfile: Dockerfile
+      target: builder
     command: npm run start:dev immich
     volumes:
       - ../server:/usr/src/app
@@ -24,6 +25,7 @@ services:
     build:
       context: ../machine-learning
       dockerfile: Dockerfile
+      target: builder
     command: npm run start:dev
     volumes:
       - ../machine-learning:/usr/src/app
@@ -41,6 +43,7 @@ services:
     build:
       context: ../server
       dockerfile: Dockerfile
+      target: builder
     command: npm run start:dev microservices
     volumes:
       - ../server:/usr/src/app

+ 2 - 2
server/Dockerfile

@@ -5,7 +5,7 @@ WORKDIR /usr/src/app
 
 COPY package.json package-lock.json ./
 
-RUN apk add --update-cache build-base python3 libheif vips-dev
+RUN apk add --update-cache build-base python3 libheif vips-dev ffmpeg
 RUN npm ci
 
 COPY . .
@@ -22,7 +22,7 @@ COPY package.json package-lock.json ./
 COPY start-server.sh start-microservices.sh ./
 
 RUN mkdir -p /usr/src/app/dist \
-    && apk add --no-cache libheif vips ffmpeg
+  && apk add --no-cache libheif vips ffmpeg
 
 COPY --from=builder /usr/src/app/node_modules ./node_modules
 COPY --from=builder /usr/src/app/dist ./dist

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 342 - 209
web/package-lock.json


+ 0 - 1
web/package.json

@@ -6,7 +6,6 @@
 		"build": "vite build",
 		"package": "svelte-kit package",
 		"preview": "vite preview",
-		"prepare": "svelte-kit sync",
 		"check": "svelte-check --tsconfig ./tsconfig.json",
 		"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
 		"lint": "prettier --check --plugin-search-dir=. . && eslint .",

+ 0 - 6
web/src/app.d.ts

@@ -8,10 +8,4 @@ declare namespace App {
 	}
 
 	// interface Platform {}
-
-	interface Session {
-		user?: import('@api').UserResponseDto;
-	}
-
-	// interface Stuff {}
 }

+ 2 - 28
web/src/hooks.ts

@@ -1,31 +1,5 @@
-import type { ExternalFetch, GetSession, Handle } from '@sveltejs/kit';
-import * as cookie from 'cookie';
-import { serverApi } from '@api';
+import type { Handle } from '@sveltejs/kit';
 
 export const handle: Handle = async ({ event, resolve }) => {
-	const cookies = cookie.parse(event.request.headers.get('cookie') || '');
-
-	if (!cookies['immich_is_authenticated']) {
-		return await resolve(event);
-	}
-	const accessToken = cookies['immich_access_token'];
-
-	try {
-		serverApi.setAccessToken(accessToken);
-		const { data } = await serverApi.userApi.getMyUserInfo();
-		event.locals.user = data;
-
-		return await resolve(event);
-	} catch (error) {
-		event.locals.user = undefined;
-		return await resolve(event);
-	}
-};
-
-export const getSession: GetSession = async ({ locals }) => {
-	if (!locals.user) return {};
-
-	return {
-		user: locals.user
-	};
+	return await resolve(event);
 };

+ 1 - 1
web/src/lib/components/album-page/album-card.svelte

@@ -2,7 +2,7 @@
 	import { AlbumResponseDto, api, ThumbnailFormat } from '@api';
 	import { createEventDispatcher, onMount } from 'svelte';
 	import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
-	import { fade } from 'svelte/transition';
+	import { fly } from 'svelte/transition';
 	import CircleIconButton from '../shared-components/circle-icon-button.svelte';
 
 	export let album: AlbumResponseDto;

+ 0 - 1
web/src/lib/components/shared-components/upload-panel.svelte

@@ -6,7 +6,6 @@
 	import WindowMinimize from 'svelte-material-icons/WindowMinimize.svelte';
 	import type { UploadAsset } from '$lib/models/upload-asset';
 	import { getAssetsInfo } from '$lib/stores/assets';
-	import { session } from '$app/stores';
 	let showDetail = true;
 
 	let uploadLength = 0;

+ 29 - 0
web/src/routes/+error.svelte

@@ -0,0 +1,29 @@
+<script>
+	import { page } from '$app/stores';
+</script>
+
+<div class="h-screen w-screen  flex place-items-center place-content-center flex-col">
+	<div class="min-w-[500px]  bg-gray-300 rounded-2xl my-4 p-4">
+		<code class="text-xs text-red-500">Error code {$page.status}</code>
+		<br />
+		<code class="text-sm">
+			{$page.error.message}
+		</code>
+		<br />
+		<div class="mt-5">
+			<p class="text-sm font-medium">Verbose</p>
+			<pre class="text-xs">{Object.values($page.error)}</pre>
+		</div>
+
+		<a
+			href="https://github.com/immich-app/immich/issues/new/choose"
+			target="_blank"
+			rel="noopener noreferrer"
+		>
+			<button
+				class="px-5 py-2 rounded-lg text-sm mt-6 bg-immich-primary text-white hover:bg-immich-primary/75"
+				>Get help</button
+			>
+		</a>
+	</div>
+</div>

+ 23 - 0
web/src/routes/+layout.server.ts

@@ -0,0 +1,23 @@
+import { browser } from '$app/env';
+import { api, serverApi } from '@api';
+import * as cookieParser from 'cookie';
+
+import type { LayoutServerLoad } from './$types';
+
+export const load: LayoutServerLoad = async ({ request }) => {
+	const cookies = cookieParser.parse(request.headers.get('cookie') || '');
+	const accessToken = cookies['immich_access_token'];
+
+	if (!accessToken) {
+		return {
+			user: undefined
+		};
+	}
+
+	serverApi.setAccessToken(accessToken);
+	const { data: userInfo } = await serverApi.userApi.getMyUserInfo();
+
+	return {
+		user: userInfo
+	};
+};

+ 3 - 13
web/src/routes/__layout.svelte → web/src/routes/+layout.svelte

@@ -1,14 +1,3 @@
-<script context="module" lang="ts">
-	import type { Load } from '@sveltejs/kit';
-	import { checkAppVersion } from '$lib/utils/check-app-version';
-
-	export const load: Load = async ({ url }) => {
-		return {
-			props: { url }
-		};
-	};
-</script>
-
 <script lang="ts">
 	import '../app.css';
 
@@ -17,8 +6,9 @@
 	import AnnouncementBox from '$lib/components/shared-components/announcement-box.svelte';
 	import UploadPanel from '$lib/components/shared-components/upload-panel.svelte';
 	import { onMount } from 'svelte';
+	import { checkAppVersion } from '$lib/utils/check-app-version';
+	import { page } from '$app/stores';
 
-	export let url: string;
 	let shouldShowAnnouncement: boolean;
 	let localVersion: string;
 	let remoteVersion: string;
@@ -33,7 +23,7 @@
 </script>
 
 <main>
-	{#key url}
+	{#key $page.url}
 		<div in:fade={{ duration: 100 }}>
 			<slot />
 			<DownloadPanel />

+ 29 - 0
web/src/routes/+page.svelte

@@ -0,0 +1,29 @@
+<script lang="ts">
+	import { goto } from '$app/navigation';
+	import type { PageData } from './$types';
+
+	export let data: PageData;
+
+	async function onGettingStartedClicked() {
+		data.isAdminUserExist ? await goto('/auth/login') : await goto('/auth/register');
+	}
+</script>
+
+<svelte:head>
+	<title>Welcome 🎉 - Immich</title>
+	<meta name="description" content="Immich Web Interface" />
+</svelte:head>
+
+<section class="h-screen w-screen flex place-items-center place-content-center">
+	<div class="flex flex-col place-items-center gap-8 text-center max-w-[350px]">
+		<div class="flex place-items-center place-content-center ">
+			<img class="text-center" src="immich-logo.svg" height="200" width="200" alt="immich-logo" />
+		</div>
+		<h1 class="text-4xl text-immich-primary font-bold font-immich-title">Welcome to IMMICH Web</h1>
+		<button
+			class="border px-4 py-2 rounded-md bg-immich-primary hover:bg-immich-primary/75 text-white font-bold w-[200px]"
+			on:click={onGettingStartedClicked}
+			>Getting Started
+		</button>
+	</div>
+</section>

+ 21 - 0
web/src/routes/+page.ts

@@ -0,0 +1,21 @@
+export const prerender = false;
+import { redirect } from '@sveltejs/kit';
+import { api } from '@api';
+import { browser } from '$app/env';
+import type { PageLoad } from './$types';
+import { goto } from '$app/navigation';
+
+export const load: PageLoad = async ({ parent }) => {
+	const { user } = await parent();
+	if (user) {
+		throw redirect(302, '/photos');
+	}
+
+	if (browser) {
+		const { data } = await api.userApi.getUserCount();
+
+		return {
+			isAdminUserExist: data.userCount != 0
+		};
+	}
+};

+ 19 - 0
web/src/routes/admin/+page.server.ts

@@ -0,0 +1,19 @@
+import { redirect } from '@sveltejs/kit';
+import { serverApi, UserResponseDto } from '@api';
+import type { PageServerLoad } from './$types';
+
+export const load: PageServerLoad = async ({ parent }) => {
+	const { user } = await parent();
+
+	if (!user) {
+		throw redirect(302, '/auth/login');
+	} else if (!user.isAdmin) {
+		throw redirect(302, '/photos');
+	}
+
+	const { data: allUsers } = await serverApi.userApi.getAllUsers(false);
+	return {
+		user: user,
+		allUsers: allUsers
+	};
+};

+ 12 - 55
web/src/routes/admin/index.svelte → web/src/routes/admin/+page.svelte

@@ -1,47 +1,3 @@
-<script context="module" lang="ts">
-	import type { Load } from '@sveltejs/kit';
-	import { api, UserResponseDto } from '@api';
-	import { browser } from '$app/env';
-
-	export const load: Load = async ({ fetch, session }) => {
-		if (!browser && !session.user) {
-			return {
-				status: 302,
-				redirect: '/auth/login'
-			};
-		}
-
-		try {
-			const user: UserResponseDto = await fetch('/data/user/get-my-user-info').then((r) =>
-				r.json()
-			);
-			const allUsers: UserResponseDto[] = await fetch('/data/user/get-all-users?isAll=false').then(
-				(r) => r.json()
-			);
-
-			if (!user.isAdmin) {
-				return {
-					status: 302,
-					redirect: '/photos'
-				};
-			}
-
-			return {
-				status: 200,
-				props: {
-					user: user,
-					allUsers: allUsers
-				}
-			};
-		} catch (e) {
-			return {
-				status: 302,
-				redirect: '/auth/login'
-			};
-		}
-	};
-</script>
-
 <script lang="ts">
 	import { onMount } from 'svelte';
 
@@ -54,11 +10,12 @@
 	import CreateUserForm from '$lib/components/forms/create-user-form.svelte';
 	import EditUserForm from '$lib/components/forms/edit-user-form.svelte';
 	import StatusBox from '$lib/components/shared-components/status-box.svelte';
+	import type { PageData } from './$types';
+	import { api, UserResponseDto } from '@api';
 
 	let selectedAction: AdminSideBarSelection = AdminSideBarSelection.USER_MANAGEMENT;
 
-	export let user: UserResponseDto;
-	export let allUsers: UserResponseDto[];
+	export let data: PageData;
 
 	let editUser: UserResponseDto;
 
@@ -75,8 +32,8 @@
 	});
 
 	const onUserCreated = async () => {
-		const { data } = await api.userApi.getAllUsers(false);
-		allUsers = data;
+		const getAllUsersRes = await api.userApi.getAllUsers(false);
+		data.allUsers = getAllUsersRes.data;
 		shouldShowCreateUserForm = false;
 	};
 
@@ -87,14 +44,14 @@
 	};
 
 	const onEditUserSuccess = async () => {
-		const { data } = await api.userApi.getAllUsers(false);
-		allUsers = data;
+		const getAllUsersRes = await api.userApi.getAllUsers(false);
+		data.allUsers = getAllUsersRes.data;
 		shouldShowEditUserForm = false;
 	};
 
 	const onEditPasswordSuccess = async () => {
-		const { data } = await api.userApi.getAllUsers(false);
-		allUsers = data;
+		const getAllUsersRes = await api.userApi.getAllUsers(false);
+		data.allUsers = getAllUsersRes.data;
 		shouldShowEditUserForm = false;
 		shouldShowInfoPanel = true;
 	};
@@ -104,7 +61,7 @@
 	<title>Administration - Immich</title>
 </svelte:head>
 
-<NavigationBar {user} />
+<NavigationBar user={data.user} />
 
 {#if shouldShowCreateUserForm}
 	<FullScreenModal on:clickOutside={() => (shouldShowCreateUserForm = false)}>
@@ -125,7 +82,7 @@
 {#if shouldShowInfoPanel}
 	<FullScreenModal on:clickOutside={() => (shouldShowInfoPanel = false)}>
 		<div class="border bg-white shadow-sm w-[500px] rounded-3xl p-8 text-sm">
-			<h1 class="font-bold text-immich-primary text-lg mb-4">Password reset success</h1>
+			<h1 class="font-medium text-immich-primary text-lg mb-4">Password reset success</h1>
 
 			<p>
 				The user's password has been reset to the default <code
@@ -170,7 +127,7 @@
 			<section class="w-[800px] pt-4">
 				{#if selectedAction === AdminSideBarSelection.USER_MANAGEMENT}
 					<UserManagement
-						{allUsers}
+						allUsers={data.allUsers}
 						on:create-user={() => (shouldShowCreateUserForm = true)}
 						on:edit-user={editUserHandler}
 					/>

+ 22 - 0
web/src/routes/albums/+page.server.ts

@@ -0,0 +1,22 @@
+import { redirect } from '@sveltejs/kit';
+import type { PageServerLoad } from './$types';
+import { AlbumResponseDto, serverApi } from '@api';
+
+export const load: PageServerLoad = async ({ parent }) => {
+	try {
+		const { user } = await parent();
+
+		if (!user) {
+			throw Error('User is not logged in');
+		}
+
+		const { data: albums } = await serverApi.albumApi.getAllAlbums();
+
+		return {
+			user: user,
+			albums: albums
+		};
+	} catch (e) {
+		throw redirect(302, '/auth/login');
+	}
+};

+ 17 - 56
web/src/routes/albums/index.svelte → web/src/routes/albums/+page.svelte

@@ -1,44 +1,3 @@
-<script context="module" lang="ts">
-	export const prerender = false;
-
-	import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte';
-
-	import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte';
-	import { ImmichUser } from '$lib/models/immich-user';
-	import type { Load } from '@sveltejs/kit';
-	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
-	import { AlbumResponseDto, api } from '@api';
-
-	export const load: Load = async ({ fetch, session }) => {
-		if (!browser && !session.user) {
-			return {
-				status: 302,
-				redirect: '/auth/login'
-			};
-		}
-
-		try {
-			const [user, albums] = await Promise.all([
-				fetch('/data/user/get-my-user-info').then((r) => r.json()),
-				fetch('/data/album/get-all-albums').then((r) => r.json())
-			]);
-
-			return {
-				status: 200,
-				props: {
-					user: user,
-					albums: albums
-				}
-			};
-		} catch (e) {
-			return {
-				status: 302,
-				redirect: '/auth/login'
-			};
-		}
-	};
-</script>
-
 <script lang="ts">
 	import AlbumCard from '$lib/components/album-page/album-card.svelte';
 	import { goto } from '$app/navigation';
@@ -46,27 +5,29 @@
 	import ContextMenu from '$lib/components/shared-components/context-menu/context-menu.svelte';
 	import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
 	import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
-	import { browser } from '$app/env';
+	import type { PageData } from './$types';
+	import { AlbumResponseDto, api } from '@api';
+	import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte';
+	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
+	import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte';
 
-	export let user: ImmichUser;
-	export let albums: AlbumResponseDto[];
+	export let data: PageData;
 
 	let isShowContextMenu = false;
 	let contextMenuPosition = { x: 0, y: 0 };
 	let targetAlbum: AlbumResponseDto;
 
 	onMount(async () => {
-		const { data } = await api.albumApi.getAllAlbums();
-		albums = data;
+		const getAllAlbumsRes = await api.albumApi.getAllAlbums();
+		data.albums = getAllAlbumsRes.data;
 
 		// Delete album that has no photos and is named 'Untitled'
-		for (const album of albums) {
+		for (const album of data.albums) {
 			if (album.albumName === 'Untitled' && album.assetCount === 0) {
-				const isDeleted = await autoDeleteAlbum(album);
-
-				if (isDeleted) {
-					albums = albums.filter((a) => a.id !== album.id);
-				}
+				setTimeout(async () => {
+					await autoDeleteAlbum(album);
+					data.albums = data.albums.filter((a) => a.id !== album.id);
+				}, 500);
 			}
 		}
 	});
@@ -101,7 +62,7 @@
 		) {
 			try {
 				await api.albumApi.deleteAlbum(targetAlbum.id);
-				albums = albums.filter((a) => a.id !== targetAlbum.id);
+				data.albums = data.albums.filter((a) => a.id !== targetAlbum.id);
 			} catch (e) {
 				console.log('Error [userDeleteMenu] ', e);
 			}
@@ -127,7 +88,7 @@
 </svelte:head>
 
 <section>
-	<NavigationBar {user} on:uploadClicked={() => {}} />
+	<NavigationBar user={data.user} on:uploadClicked={() => {}} />
 </section>
 
 <section class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg ">
@@ -158,7 +119,7 @@
 
 			<!-- Album Card -->
 			<div class="flex flex-wrap gap-8">
-				{#each albums as album}
+				{#each data.albums as album}
 					{#key album.id}
 						<a sveltekit:prefetch href={`albums/${album.id}`}>
 							<AlbumCard {album} on:showalbumcontextmenu={(e) => showAlbumContextMenu(e, album)} />
@@ -168,7 +129,7 @@
 			</div>
 
 			<!-- Empty Message -->
-			{#if albums.length === 0}
+			{#if data.albums.length === 0}
 				<div
 					class="border p-5 w-[50%] m-auto mt-10 bg-gray-50 rounded-3xl flex flex-col place-content-center place-items-center"
 				>

+ 23 - 0
web/src/routes/albums/[albumId]/+page.server.ts

@@ -0,0 +1,23 @@
+import { redirect } from '@sveltejs/kit';
+
+import type { PageServerLoad } from './$types';
+import { serverApi } from '@api';
+
+export const load: PageServerLoad = async ({ parent, params }) => {
+	const { user } = await parent();
+
+	if (!user) {
+		throw redirect(302, '/auth/login');
+	}
+
+	const albumId = params['albumId'];
+
+	try {
+		const { data: album } = await serverApi.albumApi.getAlbumInfo(albumId);
+		return {
+			album
+		};
+	} catch (e) {
+		throw redirect(302, '/albums');
+	}
+};

+ 14 - 0
web/src/routes/albums/[albumId]/+page.svelte

@@ -0,0 +1,14 @@
+<script lang="ts">
+	import AlbumViewer from '$lib/components/album-page/album-viewer.svelte';
+	import type { PageData } from './$types';
+
+	export let data: PageData;
+</script>
+
+<svelte:head>
+	<title>{data.album.albumName} - Immich</title>
+</svelte:head>
+
+<div class="immich-scrollbar">
+	<AlbumViewer album={data.album} />
+</div>

+ 0 - 57
web/src/routes/albums/[albumId]/index.svelte

@@ -1,57 +0,0 @@
-<script context="module" lang="ts">
-	export const prerender = false;
-
-	import type { Load } from '@sveltejs/kit';
-	import { AlbumResponseDto } from '@api';
-
-	export const load: Load = async ({ fetch, params, session }) => {
-		if (!browser && !session.user) {
-			return {
-				status: 302,
-				redirect: '/auth/login'
-			};
-		}
-
-		try {
-			const albumId = params['albumId'];
-
-			const albumInfo = await fetch(`/data/album/get-album-info?albumId=${albumId}`).then((r) =>
-				r.json()
-			);
-
-			return {
-				status: 200,
-				props: {
-					album: albumInfo
-				}
-			};
-		} catch (e: any) {
-			if (e.response?.status === 404) {
-				return {
-					status: 302,
-					redirect: '/albums'
-				};
-			}
-
-			return {
-				status: 302,
-				redirect: '/auth/login'
-			};
-		}
-	};
-</script>
-
-<script lang="ts">
-	import AlbumViewer from '$lib/components/album-page/album-viewer.svelte';
-	import { browser } from '$app/env';
-
-	export let album: AlbumResponseDto;
-</script>
-
-<svelte:head>
-	<title>{album.albumName} - Immich</title>
-</svelte:head>
-
-<div class="immich-scrollbar">
-	<AlbumViewer {album} />
-</div>

+ 0 - 28
web/src/routes/albums/[albumId]/photos/[assetId].svelte

@@ -1,28 +0,0 @@
-<script context="module" lang="ts">
-	export const prerender = false;
-	import { browser } from '$app/env';
-	import type { Load } from '@sveltejs/kit';
-
-	export const load: Load = async ({ params, session }) => {
-		if (!browser && !session.user) {
-			return {
-				status: 302,
-				redirect: '/auth/login'
-			};
-		}
-
-		const albumId = params['albumId'];
-
-		if (albumId) {
-			return {
-				status: 302,
-				redirect: `/albums/${albumId}`
-			};
-		} else {
-			return {
-				status: 302,
-				redirect: `/photos`
-			};
-		}
-	};
-</script>

+ 0 - 0
web/src/routes/albums/[albumId]/photos/[assetId]/+page.svelte


+ 18 - 0
web/src/routes/albums/[albumId]/photos/[assetId]/+page.ts

@@ -0,0 +1,18 @@
+import { redirect } from '@sveltejs/kit';
+export const prerender = false;
+import type { PageLoad } from './$types';
+
+export const load: PageLoad = async ({ params, parent }) => {
+	const { user } = await parent();
+	if (!user) {
+		throw redirect(302, '/auth/login');
+	}
+
+	const albumId = params['albumId'];
+
+	if (albumId) {
+		throw redirect(302, `/albums/${albumId}`);
+	} else {
+		throw redirect(302, `/photos`);
+	}
+};

+ 25 - 0
web/src/routes/auth/change-password/+page.svelte

@@ -0,0 +1,25 @@
+<script lang="ts">
+	import { goto } from '$app/navigation';
+	import { fade } from 'svelte/transition';
+
+	import ChangePasswordForm from '$lib/components/forms/change-password-form.svelte';
+	import type { PageData } from './$types';
+
+	export let data: PageData;
+
+	const onSuccessHandler = async () => {
+		await fetch('auth/logout', { method: 'POST' });
+
+		goto('/auth/login');
+	};
+</script>
+
+<svelte:head>
+	<title>Change Password - Immich</title>
+</svelte:head>
+
+<section class="h-screen w-screen flex place-items-center place-content-center">
+	<div in:fade={{ duration: 100 }} out:fade={{ duration: 100 }}>
+		<ChangePasswordForm user={data.user} on:success={onSuccessHandler} />
+	</div>
+</section>

+ 21 - 0
web/src/routes/auth/change-password/+page.ts

@@ -0,0 +1,21 @@
+import { api } from '@api';
+import { redirect } from '@sveltejs/kit';
+export const prerender = false;
+
+import type { PageLoad } from './$types';
+
+export const load: PageLoad = async () => {
+	try {
+		const { data: userInfo } = await api.userApi.getMyUserInfo();
+
+		if (userInfo.shouldChangePassword) {
+			return {
+				user: userInfo
+			};
+		} else {
+			throw redirect(302, '/photos');
+		}
+	} catch (e) {
+		throw redirect(302, '/auth/login');
+	}
+};

+ 0 - 56
web/src/routes/auth/change-password/index.svelte

@@ -1,56 +0,0 @@
-<script context="module" lang="ts">
-	export const prerender = false;
-
-	import type { Load } from '@sveltejs/kit';
-
-	export const load: Load = async () => {
-		try {
-			const { data: userInfo } = await api.userApi.getMyUserInfo();
-
-			if (userInfo.shouldChangePassword) {
-				return {
-					status: 200,
-					props: {
-						user: userInfo
-					}
-				};
-			} else {
-				return {
-					status: 302,
-					redirect: '/photos'
-				};
-			}
-		} catch (e) {
-			return {
-				status: 302,
-				redirect: '/auth/login'
-			};
-		}
-	};
-</script>
-
-<script lang="ts">
-	import { goto } from '$app/navigation';
-	import { fade } from 'svelte/transition';
-
-	import ChangePasswordForm from '$lib/components/forms/change-password-form.svelte';
-	import { api, UserResponseDto } from '@api';
-
-	export let user: UserResponseDto;
-
-	const onSuccessHandler = async () => {
-		await fetch('auth/logout', { method: 'POST' });
-		
-		goto('/auth/login');
-	};
-</script>
-
-<svelte:head>
-	<title>Change Password - Immich</title>
-</svelte:head>
-
-<section class="h-screen w-screen flex place-items-center place-content-center">
-	<div in:fade={{ duration: 100 }} out:fade={{ duration: 100 }}>
-		<ChangePasswordForm {user} on:success={onSuccessHandler} />
-	</div>
-</section>

+ 1 - 1
web/src/routes/auth/login/index.svelte → web/src/routes/auth/login/+page.svelte

@@ -4,7 +4,7 @@
 
 	import LoginForm from '$lib/components/forms/login-form.svelte';
 
-	const onLoginSuccess = async () => {
+	const onLoginSuccess = () => {
 		goto('/photos');
 	};
 </script>

+ 0 - 19
web/src/routes/auth/logout.ts

@@ -1,19 +0,0 @@
-import { api, serverApi } from '@api';
-import type { RequestHandler } from '@sveltejs/kit';
-
-export const POST: RequestHandler = async () => {
-	api.removeAccessToken();
-	serverApi.removeAccessToken();
-
-	return {
-		headers: {
-			'Set-Cookie': [
-				'immich_is_authenticated=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT;',
-				'immich_access_token=delete; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT'
-			]
-		},
-		body: {
-			ok: true
-		}
-	};
-};

+ 27 - 0
web/src/routes/auth/logout/+server.ts

@@ -0,0 +1,27 @@
+import { json } from '@sveltejs/kit';
+import { api, serverApi } from '@api';
+import type { RequestHandler } from '@sveltejs/kit';
+
+export const POST: RequestHandler = async () => {
+	api.removeAccessToken();
+	serverApi.removeAccessToken();
+
+	const headers = new Headers();
+
+	headers.append(
+		'set-cookie',
+		'immich_is_authenticated=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT;'
+	);
+	headers.append(
+		'set-cookie',
+		'immich_access_token=delete; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT'
+	);
+	return json(
+		{
+			ok: true
+		},
+		{
+			headers
+		}
+	);
+};

+ 13 - 0
web/src/routes/auth/register/+page.server.ts

@@ -0,0 +1,13 @@
+import { redirect } from '@sveltejs/kit';
+import type { PageServerLoad } from './$types';
+import { serverApi } from '@api';
+
+export const load: PageServerLoad = async () => {
+	const { data } = await serverApi.userApi.getUserCount();
+	if (data.userCount != 0) {
+		// Admin has been registered, redirect to login
+		throw redirect(302, '/auth/login');
+	}
+
+	return;
+};

+ 11 - 0
web/src/routes/auth/register/+page.svelte

@@ -0,0 +1,11 @@
+<script lang="ts">
+	import AdminRegistrationForm from '$lib/components/forms/admin-registration-form.svelte';
+</script>
+
+<svelte:head>
+	<title>Admin Registration - Immich</title>
+</svelte:head>
+
+<section class="h-screen w-screen flex place-items-center place-content-center">
+	<AdminRegistrationForm />
+</section>

+ 0 - 31
web/src/routes/auth/register/index.svelte

@@ -1,31 +0,0 @@
-<script context="module" lang="ts">
-	import type { Load } from '@sveltejs/kit';
-
-	export const load: Load = async () => {
-		const { data } = await api.userApi.getUserCount();
-		if (data.userCount != 0) {
-			// Admin has been registered, redirect to login
-			return {
-				status: 302,
-				redirect: '/auth/login'
-			};
-		}
-
-		return {
-			status: 200
-		};
-	};
-</script>
-
-<script lang="ts">
-	import AdminRegistrationForm from '$lib/components/forms/admin-registration-form.svelte';
-	import { api } from '@api';
-</script>
-
-<svelte:head>
-	<title>Admin Registration - Immich</title>
-</svelte:head>
-
-<section class="h-screen w-screen flex place-items-center place-content-center">
-	<AdminRegistrationForm />
-</section>

+ 0 - 1
web/src/routes/data/README.md

@@ -1 +0,0 @@
-This directory contain SSR endpoints to user serverApi instance to make request directly to DNS

+ 0 - 18
web/src/routes/data/album/get-album-info.ts

@@ -1,18 +0,0 @@
-import { AlbumResponseDto, serverApi } from '@api';
-import type { RequestEvent, RequestHandlerOutput } from '@sveltejs/kit';
-
-export const GET = async ({
-	url
-}: RequestEvent): Promise<RequestHandlerOutput<AlbumResponseDto>> => {
-	try {
-		const albumId = url.searchParams.get('albumId') || '';
-		const { data } = await serverApi.albumApi.getAlbumInfo(albumId);
-		return {
-			body: data
-		};
-	} catch {
-		return {
-			status: 500
-		};
-	}
-};

+ 0 - 18
web/src/routes/data/album/get-all-albums.ts

@@ -1,18 +0,0 @@
-import { AlbumResponseDto, serverApi } from '@api';
-import type { RequestEvent, RequestHandler, RequestHandlerOutput } from '@sveltejs/kit';
-
-export const GET = async ({
-	url
-}: RequestEvent): Promise<RequestHandlerOutput<AlbumResponseDto[]>> => {
-	try {
-		const isShared = url.searchParams.get('isShared') === 'true' || undefined;
-		const { data } = await serverApi.albumApi.getAllAlbums(isShared);
-		return {
-			body: data
-		};
-	} catch {
-		return {
-			status: 500
-		};
-	}
-};

+ 0 - 15
web/src/routes/data/asset/get-all-assets.ts

@@ -1,15 +0,0 @@
-import { AssetResponseDto, serverApi } from '@api';
-import type { RequestHandlerOutput } from '@sveltejs/kit';
-
-export const GET = async (): Promise<RequestHandlerOutput<AssetResponseDto[]>> => {
-	try {
-		const { data } = await serverApi.assetApi.getAllAssets();
-		return {
-			body: data
-		};
-	} catch {
-		return {
-			status: 500
-		};
-	}
-};

+ 0 - 17
web/src/routes/data/user/get-all-users.ts

@@ -1,17 +0,0 @@
-import { serverApi, UserResponseDto } from '@api';
-import type { RequestEvent, RequestHandlerOutput } from '@sveltejs/kit';
-
-export const GET = async ({url} : RequestEvent): Promise<RequestHandlerOutput<UserResponseDto[]>> => {
-	try {
-    const isAll = url.searchParams.get('isAll') === 'true';
-
-		const { data } = await serverApi.userApi.getAllUsers(isAll);
-		return {
-			body: data
-		};
-	} catch {
-		return {
-			status: 500
-		};
-	}
-};

+ 0 - 15
web/src/routes/data/user/get-my-user-info.ts

@@ -1,15 +0,0 @@
-import { serverApi, UserResponseDto } from '@api';
-import type { RequestHandlerOutput } from '@sveltejs/kit';
-
-export const GET = async (): Promise<RequestHandlerOutput<UserResponseDto>> => {
-	try {
-		const { data } = await serverApi.userApi.getMyUserInfo();
-		return {
-			body: data
-		};
-	} catch {
-		return {
-			status: 500
-		};
-	}
-};

+ 0 - 58
web/src/routes/index.svelte

@@ -1,58 +0,0 @@
-<script context="module" lang="ts">
-  export const prerender = false;
-  import type { Load } from '@sveltejs/kit';
-  import { api } from '@api';
-  import { browser } from '$app/env';
-
-  export const load: Load = async () => {
-    if (browser) {
-      try {
-        const {data: user} = await api.userApi.getMyUserInfo();
-
-        return {
-          status: 302,
-          redirect: '/photos'
-        };
-      } catch (e) {
-      }
-
-      const {data} = await api.userApi.getUserCount();
-
-      return {
-        status: 200,
-        props: {
-          isAdminUserExist: data.userCount != 0
-        }
-      };
-    }
-  };
-</script>
-
-<script lang="ts">
-  import { goto } from '$app/navigation';
-
-  export let isAdminUserExist: boolean;
-
-  async function onGettingStartedClicked() {
-    isAdminUserExist ? await goto('/auth/login') : await goto('/auth/register');
-  }
-</script>
-
-<svelte:head>
-    <title>Welcome 🎉 - Immich</title>
-    <meta name="description" content="Immich Web Interface"/>
-</svelte:head>
-
-<section class="h-screen w-screen flex place-items-center place-content-center">
-    <div class="flex flex-col place-items-center gap-8 text-center max-w-[350px]">
-        <div class="flex place-items-center place-content-center ">
-            <img class="text-center" src="immich-logo.svg" height="200" width="200" alt="immich-logo"/>
-        </div>
-        <h1 class="text-4xl text-immich-primary font-bold font-immich-title">Welcome to IMMICH Web</h1>
-        <button
-                class="border px-4 py-2 rounded-md bg-immich-primary hover:bg-immich-primary/75 text-white font-bold w-[200px]"
-                on:click={onGettingStartedClicked}>Getting Started
-        </button
-        >
-    </div>
-</section>

+ 21 - 0
web/src/routes/photos/+page.server.ts

@@ -0,0 +1,21 @@
+import { serverApi } from './../../api/api';
+import type { PageServerLoad } from './$types';
+import { redirect, error } from '@sveltejs/kit';
+
+export const load: PageServerLoad = async ({ parent }) => {
+	try {
+		const { user } = await parent();
+		if (!user) {
+			throw error(400, 'Not logged in');
+		}
+
+		const { data: assets } = await serverApi.assetApi.getAllAssets();
+
+		return {
+			user,
+			assets
+		};
+	} catch (e) {
+		throw redirect(302, '/auth/login');
+	}
+};

+ 18 - 43
web/src/routes/photos/index.svelte → web/src/routes/photos/+page.svelte

@@ -1,60 +1,28 @@
-<script context="module" lang="ts">
-	export const prerender = false;
-
-	import type { Load } from '@sveltejs/kit';
-	import { setAssetInfo } from '$lib/stores/assets';
-
-	export const load: Load = async ({ fetch, session }) => {
-		if (!browser && !session.user) {
-			return {
-				status: 302,
-				redirect: '/auth/login'
-			};
-		}
-
-		try {
-			const [userInfo, assets] = await Promise.all([
-				fetch('/data/user/get-my-user-info').then((r) => r.json()),
-				fetch('/data/asset/get-all-assets').then((r) => r.json())
-			]);
-
-			setAssetInfo(assets);
-
-			return {
-				status: 200,
-				props: {
-					user: userInfo
-				}
-			};
-		} catch (e) {
-			console.log('ERROR load photos index');
-			return {
-				status: 302,
-				redirect: '/auth/login'
-			};
-		}
-	};
-</script>
-
 <script lang="ts">
 	import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte';
 	import CheckCircle from 'svelte-material-icons/CheckCircle.svelte';
 	import { fly } from 'svelte/transition';
-	import { assetsGroupByDate, flattenAssetGroupByDate, assets } from '$lib/stores/assets';
+	import {
+		assetsGroupByDate,
+		flattenAssetGroupByDate,
+		assets,
+		setAssetInfo
+	} from '$lib/stores/assets';
 	import ImmichThumbnail from '$lib/components/shared-components/immich-thumbnail.svelte';
 	import moment from 'moment';
 	import AssetViewer from '$lib/components/asset-viewer/asset-viewer.svelte';
 	import { openFileUploadDialog, UploadType } from '$lib/utils/file-uploader';
-	import { api, AssetResponseDto, UserResponseDto } from '@api';
+	import { api, AssetResponseDto } from '@api';
 	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
 	import CircleOutline from 'svelte-material-icons/CircleOutline.svelte';
 	import CircleIconButton from '$lib/components/shared-components/circle-icon-button.svelte';
 	import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
 	import Close from 'svelte-material-icons/Close.svelte';
-	import { browser } from '$app/env';
 	import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
+	import type { PageData } from './$types';
+	import { onMount } from 'svelte';
 
-	export let user: UserResponseDto;
+	export let data: PageData;
 
 	let selectedGroupThumbnail: number | null;
 	let isMouseOverGroup: boolean;
@@ -73,6 +41,10 @@
 	let currentViewAssetIndex = 0;
 	let selectedAsset: AssetResponseDto;
 
+	onMount(() => {
+		setAssetInfo(data.assets);
+	});
+
 	const thumbnailMouseEventHandler = (event: CustomEvent) => {
 		const { selectedGroupIndex }: { selectedGroupIndex: number } = event.detail;
 
@@ -234,7 +206,10 @@
 	{/if}
 
 	{#if !isMultiSelectionMode}
-		<NavigationBar {user} on:uploadClicked={() => openFileUploadDialog(UploadType.GENERAL)} />
+		<NavigationBar
+			user={data.user}
+			on:uploadClicked={() => openFileUploadDialog(UploadType.GENERAL)}
+		/>
 	{/if}
 </section>
 

+ 0 - 20
web/src/routes/photos/[assetId].svelte

@@ -1,20 +0,0 @@
-<script context="module" lang="ts">
-	export const prerender = false;
-
-	import { browser } from '$app/env';
-	import type { Load } from '@sveltejs/kit';
-
-	export const load: Load = async ({ session }) => {
-		if (!browser && !session.user) {
-			return {
-				status: 302,
-				redirect: '/auth/login'
-			};
-		} else {
-			return {
-				status: 302,
-				redirect: '/photos'
-			};
-		}
-	};
-</script>

+ 14 - 0
web/src/routes/photos/[assetId]/+page.server.ts

@@ -0,0 +1,14 @@
+import { redirect } from '@sveltejs/kit';
+export const prerender = false;
+
+import type { PageServerLoad } from './$types';
+
+export const load: PageServerLoad = async ({ parent }) => {
+	const { user } = await parent();
+
+	if (!user) {
+		throw redirect(302, '/auth/login');
+	} else {
+		throw redirect(302, '/photos');
+	}
+};

+ 0 - 0
web/src/routes/photos/[assetId]/+page.svelte


+ 23 - 0
web/src/routes/sharing/+page.server.ts

@@ -0,0 +1,23 @@
+import { redirect } from '@sveltejs/kit';
+export const prerender = false;
+
+import { serverApi } from '@api';
+import type { PageServerLoad } from './$types';
+
+export const load: PageServerLoad = async ({ parent }) => {
+	try {
+		const { user } = await parent();
+		if (!user) {
+			throw redirect(302, '/auth/login');
+		}
+
+		const { data: sharedAlbums } = await serverApi.albumApi.getAllAlbums(true);
+
+		return {
+			user: user,
+			sharedAlbums: sharedAlbums
+		};
+	} catch (e) {
+		throw redirect(302, '/auth/login');
+	}
+};

+ 83 - 0
web/src/routes/sharing/+page.svelte

@@ -0,0 +1,83 @@
+<script lang="ts">
+	import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte';
+	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
+	import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.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';
+
+	export let data: PageData;
+
+	const createSharedAlbum = async () => {
+		try {
+			const { data: newAlbum } = await api.albumApi.createAlbum({
+				albumName: 'Untitled'
+			});
+
+			goto('/albums/' + newAlbum.id);
+		} catch (e) {
+			console.log('Error [createAlbum] ', e);
+		}
+	};
+</script>
+
+<svelte:head>
+	<title>Albums - Immich</title>
+</svelte:head>
+
+<section>
+	<NavigationBar user={data.user} on:uploadClicked={() => {}} />
+</section>
+
+<section class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg">
+	<SideBar />
+
+	<section class="overflow-y-auto relative">
+		<section id="album-content" class="relative pt-8 pl-4 mb-12 bg-immich-bg">
+			<!-- Main Section -->
+			<div class="px-4 flex justify-between place-items-center">
+				<div>
+					<p class="font-medium">Sharing</p>
+				</div>
+
+				<div>
+					<button
+						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"
+					>
+						<span>
+							<PlusBoxOutline size="18" />
+						</span>
+						<p>Create shared album</p>
+					</button>
+				</div>
+			</div>
+
+			<div class="my-4">
+				<hr />
+			</div>
+
+			<!-- Share Album List -->
+			<div class="w-full flex flex-col place-items-center">
+				{#each data.sharedAlbums as album}
+					<a sveltekit:prefetch href={`albums/${album.id}`}>
+						<SharedAlbumListTile {album} user={data.user} />
+					</a>
+				{/each}
+			</div>
+
+			<!-- Empty List -->
+			{#if data.sharedAlbums.length === 0}
+				<div
+					class="border p-5 w-[50%] m-auto mt-10 bg-gray-50 rounded-3xl flex flex-col place-content-center place-items-center"
+				>
+					<img src="/empty-2.svg" alt="Empty shared album" width="500" />
+					<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>

+ 0 - 120
web/src/routes/sharing/index.svelte

@@ -1,120 +0,0 @@
-<script context="module" lang="ts">
-  export const prerender = false;
-
-  import type { Load } from '@sveltejs/kit';
-  import { AlbumResponseDto, api, UserResponseDto } from '@api';
-  import { browser } from '$app/env';
-
-  export const load: Load = async ({fetch, session}) => {
-    if (!browser && !session.user) {
-      return {
-        status: 302,
-        redirect: '/auth/login'
-      };
-    }
-
-    try {
-      const [user, sharedAlbums] = await Promise.all([
-        fetch('/data/user/get-my-user-info').then((r) => r.json()),
-        fetch('/data/album/get-all-albums?isShared=true').then((r) => r.json())
-      ]);
-
-      return {
-        status: 200,
-        props: {
-          user: user,
-          sharedAlbums: sharedAlbums
-        }
-      };
-    } catch (e) {
-      return {
-        status: 302,
-        redirect: '/auth/login'
-      };
-    }
-  };
-</script>
-
-<script lang="ts">
-  import NavigationBar from '$lib/components/shared-components/navigation-bar.svelte';
-  import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
-  import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte';
-  import SharedAlbumListTile from '$lib/components/sharing-page/shared-album-list-tile.svelte';
-  import { goto } from '$app/navigation';
-
-  export let user: UserResponseDto;
-  export let sharedAlbums: AlbumResponseDto[];
-
-  const createSharedAlbum = async () => {
-    try {
-      const {data: newAlbum} = await api.albumApi.createAlbum({
-        albumName: 'Untitled'
-      });
-
-      goto('/albums/' + newAlbum.id);
-    } catch (e) {
-      console.log('Error [createAlbum] ', e);
-    }
-  };
-</script>
-
-<svelte:head>
-    <title>Albums - Immich</title>
-</svelte:head>
-
-<section>
-    <NavigationBar {user} on:uploadClicked={() => {}}/>
-</section>
-
-<section class="grid grid-cols-[250px_auto] relative pt-[72px] h-screen bg-immich-bg">
-    <SideBar/>
-
-    <section class="overflow-y-auto relative">
-        <section id="album-content" class="relative pt-8 pl-4 mb-12 bg-immich-bg">
-            <!-- Main Section -->
-            <div class="px-4 flex justify-between place-items-center">
-                <div>
-                    <p class="font-medium">Sharing</p>
-                </div>
-
-                <div>
-                    <button
-                            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"
-                    >
-						<span>
-							<PlusBoxOutline size="18"/>
-						</span>
-                        <p>Create shared album</p>
-                    </button>
-                </div>
-            </div>
-
-            <div class="my-4">
-                <hr/>
-            </div>
-
-            <!-- Share Album List -->
-            <div class="w-full flex flex-col place-items-center">
-                {#each sharedAlbums as album}
-                    <a sveltekit:prefetch href={`albums/${album.id}`}>
-                        <SharedAlbumListTile {album} {user}/>
-                    </a
-                    >
-                {/each}
-            </div>
-
-            <!-- Empty List -->
-            {#if sharedAlbums.length === 0}
-                <div
-                        class="border p-5 w-[50%] m-auto mt-10 bg-gray-50 rounded-3xl flex flex-col place-content-center place-items-center"
-                >
-                    <img src="/empty-2.svg" alt="Empty shared album" width="500"/>
-                    <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>

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä