Explorar o código

feat(web): make assets cachable (#1724)

Michel Heusschen %!s(int64=2) %!d(string=hai) anos
pai
achega
d91cc3616b
Modificáronse 38 ficheiros con 91 adicións e 82 borrados
  1. 2 0
      web/jest.config.mjs
  2. 34 0
      web/package-lock.json
  3. 1 0
      web/package.json
  4. 2 2
      web/src/app.css
  5. 0 1
      web/src/app.html
  6. 0 0
      web/src/lib/assets/empty-1.svg
  7. 0 0
      web/src/lib/assets/empty-2.svg
  8. 0 0
      web/src/lib/assets/favicon.png
  9. 0 0
      web/src/lib/assets/feature-panel.png
  10. 0 0
      web/src/lib/assets/fonts/SnowburstOne-Regular.ttf
  11. 0 0
      web/src/lib/assets/fonts/WorkSans-Italic-VariableFont_wght.ttf
  12. 0 0
      web/src/lib/assets/fonts/WorkSans-VariableFont_wght.ttf
  13. 0 0
      web/src/lib/assets/immich-logo-no-outline.png
  14. 0 0
      web/src/lib/assets/immich-logo.svg
  15. 0 0
      web/src/lib/assets/no-thumbnail.png
  16. 3 3
      web/src/lib/components/album-page/__tests__/album-card.spec.ts
  17. 3 4
      web/src/lib/components/album-page/album-card.svelte
  18. 2 7
      web/src/lib/components/album-page/album-viewer.svelte
  19. 2 1
      web/src/lib/components/album-page/user-selection-modal.svelte
  20. 3 9
      web/src/lib/components/forms/admin-registration-form.svelte
  21. 2 8
      web/src/lib/components/forms/change-password-form.svelte
  22. 2 8
      web/src/lib/components/forms/create-user-form.svelte
  23. 2 8
      web/src/lib/components/forms/login-form.svelte
  24. 2 1
      web/src/lib/components/share-page/individual-shared-viewer.svelte
  25. 2 8
      web/src/lib/components/shared-components/drag-and-drop-upload-overlay.svelte
  26. 7 0
      web/src/lib/components/shared-components/immich-logo.svelte
  27. 2 1
      web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte
  28. 4 7
      web/src/lib/components/shared-components/upload-asset-preview.svelte
  29. 2 1
      web/src/lib/components/sharing-page/shared-album-list-tile.svelte
  30. 2 1
      web/src/routes/+error.svelte
  31. 2 0
      web/src/routes/+layout.svelte
  32. 2 8
      web/src/routes/+page.svelte
  33. 2 1
      web/src/routes/albums/+page.svelte
  34. 2 1
      web/src/routes/favorites/+page.svelte
  35. 2 1
      web/src/routes/share/[key]/+page.server.ts
  36. 2 1
      web/src/routes/sharing/+page.svelte
  37. BIN=BIN
      web/static/svelte-welcome.png
  38. BIN=BIN
      web/static/svelte-welcome.webp

+ 2 - 0
web/jest.config.mjs

@@ -86,6 +86,8 @@ export default {
 
 	// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
 	moduleNameMapper: {
+		'\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
+			'identity-obj-proxy',
 		'^\\$lib(.*)$': '<rootDir>/src/lib$1',
 		'^\\@api(.*)$': '<rootDir>/src/api$1',
 		'^\\@test-data(.*)$': '<rootDir>/src/test-data$1'

+ 34 - 0
web/package-lock.json

@@ -45,6 +45,7 @@
 				"eslint-config-prettier": "^8.3.0",
 				"eslint-plugin-svelte3": "^4.0.0",
 				"factory.ts": "^1.2.0",
+				"identity-obj-proxy": "^3.0.0",
 				"jest": "^29.0.2",
 				"jest-environment-jsdom": "^29.0.2",
 				"postcss": "^8.4.13",
@@ -6202,6 +6203,12 @@
 				"uglify-js": "^3.1.4"
 			}
 		},
+		"node_modules/harmony-reflect": {
+			"version": "1.6.2",
+			"resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz",
+			"integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==",
+			"dev": true
+		},
 		"node_modules/has": {
 			"version": "1.0.3",
 			"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -6337,6 +6344,18 @@
 				"node": ">=0.10.0"
 			}
 		},
+		"node_modules/identity-obj-proxy": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz",
+			"integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==",
+			"dev": true,
+			"dependencies": {
+				"harmony-reflect": "^1.4.6"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
 		"node_modules/ignore": {
 			"version": "5.2.1",
 			"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz",
@@ -15822,6 +15841,12 @@
 				"wordwrap": "^1.0.0"
 			}
 		},
+		"harmony-reflect": {
+			"version": "1.6.2",
+			"resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz",
+			"integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==",
+			"dev": true
+		},
 		"has": {
 			"version": "1.0.3",
 			"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -15918,6 +15943,15 @@
 				"safer-buffer": ">= 2.1.2 < 3.0.0"
 			}
 		},
+		"identity-obj-proxy": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz",
+			"integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==",
+			"dev": true,
+			"requires": {
+				"harmony-reflect": "^1.4.6"
+			}
+		},
 		"ignore": {
 			"version": "5.2.1",
 			"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz",

+ 1 - 0
web/package.json

@@ -43,6 +43,7 @@
 		"eslint-config-prettier": "^8.3.0",
 		"eslint-plugin-svelte3": "^4.0.0",
 		"factory.ts": "^1.2.0",
+		"identity-obj-proxy": "^3.0.0",
 		"jest": "^29.0.2",
 		"jest-environment-jsdom": "^29.0.2",
 		"postcss": "^8.4.13",

+ 2 - 2
web/src/app.css

@@ -4,13 +4,13 @@
 
 @font-face {
 	font-family: 'Work Sans';
-	src: url('/fonts/WorkSans-VariableFont_wght.ttf') format('truetype-variations');
+	src: url('$lib/assets/fonts/WorkSans-VariableFont_wght.ttf') format('truetype-variations');
 	font-weight: 1 999;
 }
 
 @font-face {
 	font-family: 'Snowburst One';
-	src: url('/fonts/SnowburstOne-Regular.ttf') format('truetype');
+	src: url('$lib/assets/fonts/SnowburstOne-Regular.ttf') format('truetype');
 }
 
 :root {

+ 0 - 1
web/src/app.html

@@ -2,7 +2,6 @@
 <html lang="en" class="dark">
 	<head>
 		<meta charset="utf-8" />
-		<link rel="icon" href="%sveltekit.assets%/favicon.png" />
 		<meta name="viewport" content="width=device-width, initial-scale=1" />
 		%sveltekit.head%
 	</head>

+ 0 - 0
web/static/empty-1.svg → web/src/lib/assets/empty-1.svg


+ 0 - 0
web/static/empty-2.svg → web/src/lib/assets/empty-2.svg


+ 0 - 0
web/static/favicon.png → web/src/lib/assets/favicon.png


+ 0 - 0
web/static/feature-panel.png → web/src/lib/assets/feature-panel.png


+ 0 - 0
web/static/fonts/SnowburstOne-Regular.ttf → web/src/lib/assets/fonts/SnowburstOne-Regular.ttf


+ 0 - 0
web/static/fonts/WorkSans-Italic-VariableFont_wght.ttf → web/src/lib/assets/fonts/WorkSans-Italic-VariableFont_wght.ttf


+ 0 - 0
web/static/fonts/WorkSans-VariableFont_wght.ttf → web/src/lib/assets/fonts/WorkSans-VariableFont_wght.ttf


+ 0 - 0
web/static/immich-logo-no-outline.png → web/src/lib/assets/immich-logo-no-outline.png


+ 0 - 0
web/static/immich-logo.svg → web/src/lib/assets/immich-logo.svg


+ 0 - 0
web/static/no-thumbnail.png → web/src/lib/assets/no-thumbnail.png


+ 3 - 3
web/src/lib/components/album-page/__tests__/album-card.spec.ts

@@ -44,10 +44,10 @@ describe('AlbumCard component', () => {
 			const albumDetailsElement = sut.getByTestId('album-details');
 			const detailsText = `${count} items` + (shared ? ' . Shared' : '');
 
-			expect(albumImgElement).toHaveAttribute('src', 'no-thumbnail.png');
+			expect(albumImgElement).toHaveAttribute('src');
 			expect(albumImgElement).toHaveAttribute('alt', album.id);
 
-			await waitFor(() => expect(albumImgElement).toHaveAttribute('src', 'no-thumbnail.png'));
+			await waitFor(() => expect(albumImgElement).toHaveAttribute('src'));
 
 			expect(albumImgElement).toHaveAttribute('alt', album.id);
 			expect(apiMock.assetApi.getAssetThumbnail).not.toHaveBeenCalled();
@@ -108,7 +108,7 @@ describe('AlbumCard component', () => {
 			sut = render(AlbumCard, { album });
 
 			const albumImgElement = sut.getByTestId('album-image');
-			await waitFor(() => expect(albumImgElement).toHaveAttribute('src', 'no-thumbnail.png'));
+			await waitFor(() => expect(albumImgElement).toHaveAttribute('src'));
 		});
 
 		it('dispatches custom "click" event with the album in context', async () => {

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

@@ -16,14 +16,13 @@
 	import { createEventDispatcher, onMount } from 'svelte';
 	import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
 	import CircleIconButton from '../shared-components/circle-icon-button.svelte';
+	import noThumbnailUrl from '$lib/assets/no-thumbnail.png';
 
 	export let album: AlbumResponseDto;
 
-	const NO_THUMBNAIL = 'no-thumbnail.png';
-
 	let imageData = `/api/asset/thumbnail/${album.albumThumbnailAssetId}?format=${ThumbnailFormat.Webp}`;
 	if (!album.albumThumbnailAssetId) {
-		imageData = NO_THUMBNAIL;
+		imageData = noThumbnailUrl;
 	}
 
 	const dispatchClick = createEventDispatcher<OnClick>();
@@ -51,7 +50,7 @@
 	};
 
 	onMount(async () => {
-		imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || NO_THUMBNAIL;
+		imageData = (await loadHighQualityThumbnail(album.albumThumbnailAssetId)) || noThumbnailUrl;
 	});
 
 	const locale = navigator.language;

+ 2 - 7
web/src/lib/components/album-page/album-viewer.svelte

@@ -40,6 +40,7 @@
 	import { openFileUploadDialog } from '$lib/utils/file-uploader';
 	import { bulkDownload } from '$lib/utils/asset-utils';
 	import GalleryViewer from '../shared-components/gallery-viewer/gallery-viewer.svelte';
+	import ImmichLogo from '../shared-components/immich-logo.svelte';
 
 	export let album: AlbumResponseDto;
 	export let sharedLink: SharedLinkResponseDto | undefined = undefined;
@@ -419,13 +420,7 @@
 						class="flex gap-2 place-items-center hover:cursor-pointer ml-6"
 						href="https://immich.app"
 					>
-						<img
-							src="/immich-logo.svg"
-							alt="immich logo"
-							height="30"
-							width="30"
-							draggable="false"
-						/>
+						<ImmichLogo height={30} width={30} />
 						<h1 class="font-immich-title text-lg text-immich-primary dark:text-immich-dark-primary">
 							IMMICH
 						</h1>

+ 2 - 1
web/src/lib/components/album-page/user-selection-modal.svelte

@@ -6,6 +6,7 @@
 	import Link from 'svelte-material-icons/Link.svelte';
 	import ShareCircle from 'svelte-material-icons/ShareCircle.svelte';
 	import { goto } from '$app/navigation';
+	import ImmichLogo from '../shared-components/immich-logo.svelte';
 
 	export let album: AlbumResponseDto;
 	export let sharedUsersInAlbum: Set<UserResponseDto>;
@@ -53,7 +54,7 @@
 <BaseModal on:close={() => dispatch('close')}>
 	<svelte:fragment slot="title">
 		<span class="flex gap-2 place-items-center">
-			<img src="/immich-logo.svg" width="24" alt="Immich" draggable="false" />
+			<ImmichLogo width={24} />
 			<p class="font-medium">Invite to album</p>
 		</span>
 	</svelte:fragment>

+ 3 - 9
web/src/lib/components/forms/admin-registration-form.svelte

@@ -1,7 +1,8 @@
 <script lang="ts">
 	import { goto } from '$app/navigation';
-
 	import { api } from '@api';
+	import ImmichLogo from '../shared-components/immich-logo.svelte';
+
 	let error: string;
 	let success: string;
 
@@ -55,14 +56,7 @@
 	class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-3xl py-8 dark:text-immich-dark-fg"
 >
 	<div class="flex flex-col place-items-center place-content-center gap-4 px-4">
-		<img
-			class="text-center"
-			src="/immich-logo.svg"
-			height="100"
-			width="100"
-			alt="immich-logo"
-			draggable="false"
-		/>
+		<ImmichLogo class="text-center" height="100" width="100" />
 		<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
 			Admin Registration
 		</h1>

+ 2 - 8
web/src/lib/components/forms/change-password-form.svelte

@@ -1,6 +1,7 @@
 <script lang="ts">
 	import { api, UserResponseDto } from '@api';
 	import { createEventDispatcher } from 'svelte';
+	import ImmichLogo from '../shared-components/immich-logo.svelte';
 
 	export let user: UserResponseDto;
 	let error: string;
@@ -47,14 +48,7 @@
 	class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-3xl py-8 dark:text-immich-dark-fg"
 >
 	<div class="flex flex-col place-items-center place-content-center gap-4 px-4">
-		<img
-			class="text-center"
-			src="/immich-logo.svg"
-			height="100"
-			width="100"
-			alt="immich-logo"
-			draggable="false"
-		/>
+		<ImmichLogo class="text-center" height="100" width="100" />
 		<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
 			Change Password
 		</h1>

+ 2 - 8
web/src/lib/components/forms/create-user-form.svelte

@@ -1,6 +1,7 @@
 <script lang="ts">
 	import { api } from '@api';
 	import { createEventDispatcher } from 'svelte';
+	import ImmichLogo from '../shared-components/immich-logo.svelte';
 	import {
 		notificationController,
 		NotificationType
@@ -80,14 +81,7 @@
 	class="border bg-immich-bg dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-3xl py-8 dark:text-immich-dark-fg"
 >
 	<div class="flex flex-col place-items-center place-content-center gap-4 px-4">
-		<img
-			class="text-center"
-			src="/immich-logo.svg"
-			height="100"
-			width="100"
-			alt="immich-logo"
-			draggable="false"
-		/>
+		<ImmichLogo class="text-center" height="100" width="100" />
 		<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">
 			Create new user
 		</h1>

+ 2 - 8
web/src/lib/components/forms/login-form.svelte

@@ -5,6 +5,7 @@
 	import { handleError } from '$lib/utils/handle-error';
 	import { api, oauth, OAuthConfigResponseDto } from '@api';
 	import { createEventDispatcher, onMount } from 'svelte';
+	import ImmichLogo from '../shared-components/immich-logo.svelte';
 
 	let error: string;
 	let email = '';
@@ -77,14 +78,7 @@
 	class="border bg-white dark:bg-immich-dark-gray dark:border-immich-dark-gray p-4 shadow-sm w-[500px] max-w-[95vw] rounded-md py-8"
 >
 	<div class="flex flex-col place-items-center place-content-center gap-4 px-4">
-		<img
-			class="text-center"
-			src="/immich-logo.svg"
-			height="100"
-			width="100"
-			alt="immich-logo"
-			draggable="false"
-		/>
+		<ImmichLogo class="text-center" height="100" width="100" />
 		<h1 class="text-2xl text-immich-primary dark:text-immich-dark-primary font-medium">Login</h1>
 	</div>
 

+ 2 - 1
web/src/lib/components/share-page/individual-shared-viewer.svelte

@@ -17,6 +17,7 @@
 		notificationController,
 		NotificationType
 	} from '../shared-components/notification/notification';
+	import ImmichLogo from '../shared-components/immich-logo.svelte';
 
 	export let sharedLink: SharedLinkResponseDto;
 	export let isOwned: boolean;
@@ -122,7 +123,7 @@
 					class="flex gap-2 place-items-center hover:cursor-pointer ml-6"
 					href="https://immich.app"
 				>
-					<img src="/immich-logo.svg" alt="immich logo" height="30" width="30" draggable="false" />
+					<ImmichLogo height="30" width="30" />
 					<h1 class="font-immich-title text-lg text-immich-primary dark:text-immich-dark-primary">
 						IMMICH
 					</h1>

+ 2 - 8
web/src/lib/components/shared-components/drag-and-drop-upload-overlay.svelte

@@ -1,5 +1,6 @@
 <script lang="ts">
 	import { fade } from 'svelte/transition';
+	import ImmichLogo from './immich-logo.svelte';
 
 	export let dropHandler: (event: DragEvent) => void;
 	export let dragOverHandler: (event: DragEvent) => void;
@@ -14,13 +15,6 @@
 	on:dragleave={dragLeaveHandler}
 	class="fixed inset-0 w-full h-full z-[1000] flex flex-col items-center justify-center bg-gray-100/90 dark:bg-immich-dark-bg/90 text-immich-dark-gray dark:text-immich-gray"
 >
-	<img
-		src="/immich-logo.svg"
-		alt="immich logo"
-		height="200"
-		width="200"
-		class="animate-bounce pb-16"
-		draggable="false"
-	/>
+	<ImmichLogo height="200" width="200" class="animate-bounce pb-16" />
 	<div class="text-2xl">Drop files anywhere to upload</div>
 </div>

+ 7 - 0
web/src/lib/components/shared-components/immich-logo.svelte

@@ -0,0 +1,7 @@
+<script lang="ts">
+	import immichLogoUrl from '$lib/assets/immich-logo.svg';
+
+	export let draggable = false;
+</script>
+
+<img src={immichLogoUrl} alt="Immich Logo" {draggable} {...$$restProps} />

+ 2 - 1
web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte

@@ -8,6 +8,7 @@
 	import ThemeButton from '../theme-button.svelte';
 	import { AppRoute } from '../../../constants';
 	import AccountInfoPanel from './account-info-panel.svelte';
+	import ImmichLogo from '../immich-logo.svelte';
 	export let user: UserResponseDto;
 	export let shouldShowUploadButton = true;
 
@@ -50,7 +51,7 @@
 			class="flex gap-2 place-items-center hover:cursor-pointer"
 			href="/photos"
 		>
-			<img src="/immich-logo.svg" alt="immich logo" height="35" width="35" draggable="false" />
+			<ImmichLogo height="35" width="35" />
 			<h1 class="font-immich-title text-2xl text-immich-primary dark:text-immich-dark-primary">
 				IMMICH
 			</h1>

+ 4 - 7
web/src/lib/components/shared-components/upload-asset-preview.svelte

@@ -2,6 +2,7 @@
 	import { fade } from 'svelte/transition';
 	import { asByteUnitString } from '$lib/utils/byte-units';
 	import { UploadAsset } from '$lib/models/upload-asset';
+	import ImmichLogo from './immich-logo.svelte';
 
 	export let uploadAsset: UploadAsset;
 
@@ -16,13 +17,9 @@
 >
 	<div class="relative">
 		{#if showFallbackImage}
-			<img
-				in:fade={{ duration: 250 }}
-				src="immich-logo.svg"
-				alt="Immich Logo"
-				class="h-[70px] w-[70px] object-cover rounded-tl-lg rounded-bl-lg"
-				draggable="false"
-			/>
+			<div in:fade={{ duration: 250 }}>
+				<ImmichLogo class="h-[70px] w-[70px] object-cover rounded-tl-lg rounded-bl-lg" />
+			</div>
 		{:else}
 			<img
 				in:fade={{ duration: 250 }}

+ 2 - 1
web/src/lib/components/sharing-page/shared-album-list-tile.svelte

@@ -1,13 +1,14 @@
 <script lang="ts">
 	import { AlbumResponseDto, api, ThumbnailFormat, UserResponseDto } from '@api';
 	import { fade } from 'svelte/transition';
+	import noThumbnailUrl from '$lib/assets/no-thumbnail.png';
 
 	export let album: AlbumResponseDto;
 	export let user: UserResponseDto;
 
 	const loadImageData = async (thubmnailId: string | null) => {
 		if (thubmnailId == null) {
-			return '/no-thumbnail.png';
+			return noThumbnailUrl;
 		}
 
 		const { data } = await api.assetApi.getAssetThumbnail(thubmnailId, ThumbnailFormat.Webp, {

+ 2 - 1
web/src/routes/+error.svelte

@@ -9,6 +9,7 @@
 		NotificationType
 	} from '$lib/components/shared-components/notification/notification';
 	import { handleError } from '$lib/utils/handle-error';
+	import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte';
 
 	const handleCopy = async () => {
 		//
@@ -33,7 +34,7 @@
 	<section class="bg-immich-bg dark:bg-immich-dark-bg">
 		<div class="flex border-b dark:border-b-immich-dark-gray place-items-center px-6 py-4">
 			<a class="flex gap-2 place-items-center hover:cursor-pointer" href="/photos">
-				<img src="/immich-logo.svg" alt="immich logo" height="35" width="35" draggable="false" />
+				<ImmichLogo height="35" width="35" />
 				<h1 class="font-immich-title text-2xl text-immich-primary dark:text-immich-dark-primary">
 					IMMICH
 				</h1>

+ 2 - 0
web/src/routes/+layout.svelte

@@ -13,6 +13,7 @@
 	import NavigationLoadingBar from '$lib/components/shared-components/navigation-loading-bar.svelte';
 	import NotificationList from '$lib/components/shared-components/notification/notification-list.svelte';
 	import { fileUploadHandler } from '$lib/utils/file-uploader';
+	import faviconUrl from '$lib/assets/favicon.png';
 
 	let shouldShowAnnouncement: boolean;
 	let localVersion: string;
@@ -80,6 +81,7 @@
 <svelte:head>
 	<title>{$page.data.meta?.title || 'Web'} - Immich</title>
 	{#if $page.data.meta}
+		<link rel="icon" href={faviconUrl} />
 		<meta name="description" content={$page.data.meta.description} />
 
 		<!-- Facebook Meta Tags -->

+ 2 - 8
web/src/routes/+page.svelte

@@ -1,18 +1,12 @@
 <script lang="ts">
 	import { goto } from '$app/navigation';
+	import ImmichLogo from '$lib/components/shared-components/immich-logo.svelte';
 </script>
 
 <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"
-				draggable="false"
-			/>
+			<ImmichLogo class="text-center" height="200" width="200" />
 		</div>
 		<h1
 			class="text-4xl text-immich-primary dark:text-immich-dark-primary font-bold font-immich-title"

+ 2 - 1
web/src/routes/albums/+page.svelte

@@ -10,6 +10,7 @@
 	import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
 	import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte';
 	import { useAlbums } from './albums.bloc';
+	import empty1Url from '$lib/assets/empty-1.svg';
 
 	export let data: PageData;
 
@@ -93,7 +94,7 @@
 					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="/empty-1.svg" alt="Empty shared album" width="500" draggable="false" />
+					<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

+ 2 - 1
web/src/routes/favorites/+page.svelte

@@ -13,6 +13,7 @@
 	import StarMinusOutline from 'svelte-material-icons/StarMinusOutline.svelte';
 	import Error from '../+error.svelte';
 	import type { PageData } from './$types';
+	import empty1Url from '$lib/assets/empty-1.svg';
 
 	export let data: PageData;
 
@@ -126,7 +127,7 @@
 				<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="/empty-1.svg" alt="Empty shared album" width="500" draggable="false" />
+					<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

+ 2 - 1
web/src/routes/share/[key]/+page.server.ts

@@ -4,6 +4,7 @@ import { error } from '@sveltejs/kit';
 import { getThumbnailUrl } from '$lib/utils/asset-utils';
 import { serverApi, ThumbnailFormat } from '@api';
 import type { PageServerLoad } from './$types';
+import featurePanelUrl from '$lib/assets/feature-panel.png';
 
 export const load: PageServerLoad = async ({ params, parent }) => {
 	const { user } = await parent();
@@ -23,7 +24,7 @@ export const load: PageServerLoad = async ({ params, parent }) => {
 				description: sharedLink.description || `${assetCount} shared photos & videos.`,
 				imageUrl: assetId
 					? getThumbnailUrl(assetId, ThumbnailFormat.Webp, sharedLink.key)
-					: 'feature-panel.png'
+					: featurePanelUrl
 			},
 			user
 		};

+ 2 - 1
web/src/routes/sharing/+page.svelte

@@ -12,6 +12,7 @@
 		notificationController,
 		NotificationType
 	} from '$lib/components/shared-components/notification/notification';
+	import empty2Url from '$lib/assets/empty-2.svg';
 
 	export let data: PageData;
 
@@ -94,7 +95,7 @@
 				<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="/empty-2.svg" alt="Empty shared album" width="500" draggable="false" />
+					<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>

BIN=BIN
web/static/svelte-welcome.png


BIN=BIN
web/static/svelte-welcome.webp