Bläddra i källkod

feat(web): meta tags for share links (#1290)

* feat(web): meta tags for share links

* refactor: svelte head tags

* chore: clean up

* chore: linting
Jason Rasmussen 2 år sedan
förälder
incheckning
fa31a6e441
34 ändrade filer med 120 tillägg och 84 borttagningar
  1. 9 1
      web/src/lib/utils/asset-utils.ts
  2. 19 0
      web/src/routes/+layout.svelte
  3. 0 5
      web/src/routes/+page.svelte
  4. 7 0
      web/src/routes/+page.ts
  5. 0 4
      web/src/routes/admin/+layout.svelte
  6. 6 0
      web/src/routes/admin/jobs-status/+page.server.ts
  7. 0 4
      web/src/routes/admin/jobs-status/+page.svelte
  8. 6 1
      web/src/routes/admin/server-status/+page.server.ts
  9. 0 4
      web/src/routes/admin/server-status/+page.svelte
  10. 6 1
      web/src/routes/admin/system-settings/+page.server.ts
  11. 0 4
      web/src/routes/admin/system-settings/+page.svelte
  12. 7 1
      web/src/routes/admin/user-management/+page.server.ts
  13. 0 4
      web/src/routes/admin/user-management/+page.svelte
  14. 4 1
      web/src/routes/albums/+page.server.ts
  15. 0 4
      web/src/routes/albums/+page.svelte
  16. 4 1
      web/src/routes/albums/[albumId]/+page.server.ts
  17. 0 4
      web/src/routes/albums/[albumId]/+page.svelte
  18. 0 4
      web/src/routes/auth/change-password/+page.svelte
  19. 4 1
      web/src/routes/auth/change-password/+page.ts
  20. 5 1
      web/src/routes/auth/login/+page.server.ts
  21. 0 4
      web/src/routes/auth/login/+page.svelte
  22. 5 1
      web/src/routes/auth/register/+page.server.ts
  23. 0 4
      web/src/routes/auth/register/+page.svelte
  24. 4 1
      web/src/routes/photos/+page.server.ts
  25. 0 4
      web/src/routes/photos/+page.svelte
  26. 16 2
      web/src/routes/share/[key]/+page.server.ts
  27. 6 8
      web/src/routes/share/[key]/+page.svelte
  28. 4 1
      web/src/routes/sharing/+page.server.ts
  29. 0 4
      web/src/routes/sharing/+page.svelte
  30. 4 1
      web/src/routes/sharing/sharedlinks/+page.server.ts
  31. 0 4
      web/src/routes/sharing/sharedlinks/+page.svelte
  32. 4 1
      web/src/routes/user-settings/+page.server.ts
  33. 0 4
      web/src/routes/user-settings/+page.svelte
  34. BIN
      web/static/feature-panel.png

+ 9 - 1
web/src/lib/utils/asset-utils.ts

@@ -1,10 +1,18 @@
-import { api, AddAssetsResponseDto, AssetResponseDto } from '@api';
+import { api, AddAssetsResponseDto, AssetResponseDto, ThumbnailFormat } from '@api';
 import {
 	notificationController,
 	NotificationType
 } from '$lib/components/shared-components/notification/notification';
 import { downloadAssets } from '$lib/stores/download';
 
+export const getThumbnailUrl = (assetId: string, format: ThumbnailFormat, key?: string) => {
+	let url = `/api/asset/thumbnail/${assetId}?format=${format}`;
+	if (key) {
+		url += `&key=${key}`;
+	}
+	return url;
+};
+
 export const addAssetsToAlbum = async (
 	albumId: string,
 	assetIds: Array<string>,

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

@@ -77,6 +77,25 @@
 	};
 </script>
 
+<svelte:head>
+	<title>{$page.data.meta?.title} - Immich</title>
+	{#if $page.data.meta}
+		<meta name="description" content={$page.data.meta.description} />
+
+		<!-- Facebook Meta Tags -->
+		<meta property="og:type" content="website" />
+		<meta property="og:title" content={$page.data.meta.title} />
+		<meta property="og:description" content={$page.data.meta.description} />
+		<meta property="og:image" content={$page.data.meta.imageUrl} />
+
+		<!-- Twitter Meta Tags -->
+		<meta name="twitter:card" content="summary_large_image" />
+		<meta name="twitter:title" content={$page.data.meta.title} />
+		<meta name="twitter:description" content={$page.data.meta.description} />
+		<meta name="twitter:image" content={$page.data.meta.imageUrl} />
+	{/if}
+</svelte:head>
+
 <main on:dragenter={() => (showUploadCover = true)}>
 	{#if canShow}
 		<div in:fade={{ duration: 100 }}>

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

@@ -2,11 +2,6 @@
 	import { goto } from '$app/navigation';
 </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 ">

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

@@ -7,4 +7,11 @@ export const load: PageLoad = async ({ parent }) => {
 	if (user) {
 		throw redirect(302, '/photos');
 	}
+
+	return {
+		meta: {
+			title: 'Welcome 🎉',
+			description: 'Immich Web Interface'
+		}
+	};
 };

+ 0 - 4
web/src/routes/admin/+layout.svelte

@@ -26,10 +26,6 @@
 	};
 </script>
 
-<svelte:head>
-	<title>Administration - Immich</title>
-</svelte:head>
-
 <NavigationBar user={$page.data.user} />
 
 <main>

+ 6 - 0
web/src/routes/admin/jobs-status/+page.server.ts

@@ -9,4 +9,10 @@ export const load: PageServerLoad = async ({ parent }) => {
 	} else if (!user.isAdmin) {
 		throw redirect(302, '/photos');
 	}
+
+	return {
+		meta: {
+			title: 'Job Status'
+		}
+	};
 };

+ 0 - 4
web/src/routes/admin/jobs-status/+page.svelte

@@ -2,10 +2,6 @@
 	import JobsPanel from '$lib/components/admin-page/jobs/jobs-panel.svelte';
 </script>
 
-<svelte:head>
-	<title>Jobs Status - Immich</title>
-</svelte:head>
-
 <section>
 	<JobsPanel />
 </section>

+ 6 - 1
web/src/routes/admin/server-status/+page.server.ts

@@ -13,5 +13,10 @@ export const load: PageServerLoad = async ({ parent }) => {
 
 	const { data: allUsers } = await serverApi.userApi.getAllUsers(false);
 
-	return { allUsers };
+	return {
+		allUsers,
+		meta: {
+			title: 'Server Status'
+		}
+	};
 };

+ 0 - 4
web/src/routes/admin/server-status/+page.svelte

@@ -3,10 +3,6 @@
 	import { page } from '$app/stores';
 </script>
 
-<svelte:head>
-	<title>Server Status - Immich</title>
-</svelte:head>
-
 {#if $page.data.allUsers}
 	<ServerStatsPanel allUsers={$page.data.allUsers} />
 {/if}

+ 6 - 1
web/src/routes/admin/system-settings/+page.server.ts

@@ -10,5 +10,10 @@ export const load: PageServerLoad = async ({ parent }) => {
 		throw redirect(302, '/photos');
 	}
 
-	return { user };
+	return {
+		user,
+		meta: {
+			title: 'System Settings'
+		}
+	};
 };

+ 0 - 4
web/src/routes/admin/system-settings/+page.svelte

@@ -17,10 +17,6 @@
 	};
 </script>
 
-<svelte:head>
-	<title>System Settings - Immich</title>
-</svelte:head>
-
 <section class="">
 	{#await getConfig()}
 		<LoadingSpinner />

+ 7 - 1
web/src/routes/admin/user-management/+page.server.ts

@@ -13,5 +13,11 @@ export const load: PageServerLoad = async ({ parent }) => {
 
 	const { data: allUsers } = await serverApi.userApi.getAllUsers(false);
 
-	return { user, allUsers };
+	return {
+		user,
+		allUsers,
+		meta: {
+			title: 'User Management'
+		}
+	};
 };

+ 0 - 4
web/src/routes/admin/user-management/+page.svelte

@@ -101,10 +101,6 @@
 	};
 </script>
 
-<svelte:head>
-	<title>User Management - Immich</title>
-</svelte:head>
-
 <section>
 	{#if shouldShowCreateUserForm}
 		<FullScreenModal on:clickOutside={() => (shouldShowCreateUserForm = false)}>

+ 4 - 1
web/src/routes/albums/+page.server.ts

@@ -14,7 +14,10 @@ export const load: PageServerLoad = async ({ parent }) => {
 
 		return {
 			user: user,
-			albums: albums
+			albums: albums,
+			meta: {
+				title: 'Albums'
+			}
 		};
 	} catch (e) {
 		throw redirect(302, '/auth/login');

+ 0 - 4
web/src/routes/albums/+page.svelte

@@ -34,10 +34,6 @@
 	};
 </script>
 
-<svelte:head>
-	<title>Albums - Immich</title>
-</svelte:head>
-
 <section>
 	<NavigationBar user={data.user} shouldShowUploadButton={false} />
 </section>

+ 4 - 1
web/src/routes/albums/[albumId]/+page.server.ts

@@ -15,7 +15,10 @@ export const load: PageServerLoad = async ({ parent, params }) => {
 	try {
 		const { data: album } = await serverApi.albumApi.getAlbumInfo(albumId);
 		return {
-			album
+			album,
+			meta: {
+				title: album.albumName
+			}
 		};
 	} catch (e) {
 		throw redirect(302, '/albums');

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

@@ -5,10 +5,6 @@
 	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 - 4
web/src/routes/auth/change-password/+page.svelte

@@ -14,10 +14,6 @@
 	};
 </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} />

+ 4 - 1
web/src/routes/auth/change-password/+page.ts

@@ -10,7 +10,10 @@ export const load: PageLoad = async () => {
 
 		if (userInfo.shouldChangePassword) {
 			return {
-				user: userInfo
+				user: userInfo,
+				meta: {
+					title: 'Change Password'
+				}
 			};
 		} else {
 			throw redirect(302, '/photos');

+ 5 - 1
web/src/routes/auth/login/+page.server.ts

@@ -9,5 +9,9 @@ export const load: PageServerLoad = async () => {
 		throw redirect(302, '/auth/register');
 	}
 
-	return;
+	return {
+		meta: {
+			title: 'Login'
+		}
+	};
 };

+ 0 - 4
web/src/routes/auth/login/+page.svelte

@@ -5,10 +5,6 @@
 	import LoginForm from '$lib/components/forms/login-form.svelte';
 </script>
 
-<svelte:head>
-	<title>Login - 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 }}>
 		<LoginForm

+ 5 - 1
web/src/routes/auth/register/+page.server.ts

@@ -9,5 +9,9 @@ export const load: PageServerLoad = async () => {
 		throw redirect(302, '/auth/login');
 	}
 
-	return;
+	return {
+		meta: {
+			title: 'Admin Registration'
+		}
+	};
 };

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

@@ -2,10 +2,6 @@
 	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>

+ 4 - 1
web/src/routes/photos/+page.server.ts

@@ -9,7 +9,10 @@ export const load: PageServerLoad = async ({ parent }) => {
 		}
 
 		return {
-			user
+			user,
+			meta: {
+				title: 'Photos'
+			}
 		};
 	} catch (e) {
 		console.log('Photo page load error', e);

+ 0 - 4
web/src/routes/photos/+page.svelte

@@ -116,10 +116,6 @@
 	};
 </script>
 
-<svelte:head>
-	<title>Photos - Immich</title>
-</svelte:head>
-
 <section>
 	{#if $isMultiSelectStoreState}
 		<ControlAppBar

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

@@ -1,7 +1,8 @@
 export const prerender = false;
 import { error } from '@sveltejs/kit';
 
-import { serverApi } from '@api';
+import { getThumbnailUrl } from '$lib/utils/asset-utils';
+import { serverApi, ThumbnailFormat } from '@api';
 import type { PageServerLoad } from './$types';
 
 export const load: PageServerLoad = async ({ params }) => {
@@ -9,7 +10,20 @@ export const load: PageServerLoad = async ({ params }) => {
 
 	try {
 		const { data: sharedLink } = await serverApi.shareApi.getMySharedLink({ params: { key } });
-		return { sharedLink };
+
+		const assetCount = sharedLink.assets.length;
+		const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id;
+
+		return {
+			sharedLink,
+			meta: {
+				title: sharedLink.album ? sharedLink.album.albumName : 'Public Share',
+				description: sharedLink.description || `${assetCount} shared photos & videos.`,
+				imageUrl: assetId
+					? getThumbnailUrl(assetId, ThumbnailFormat.Webp, sharedLink.key)
+					: 'feature-panel.png'
+			}
+		};
 	} catch (e) {
 		throw error(404, {
 			message: 'Invalid shared link'

+ 6 - 8
web/src/routes/share/[key]/+page.svelte

@@ -1,22 +1,20 @@
 <script lang="ts">
 	import AlbumViewer from '$lib/components/album-page/album-viewer.svelte';
-	import { AlbumResponseDto } from '../../../api';
+	import { AlbumResponseDto } from '@api';
 	import type { PageData } from './$types';
 
 	export let data: PageData;
 
+	const { sharedLink } = data;
+
 	let album: AlbumResponseDto | null = null;
-	if (data.sharedLink.album) {
-		album = { ...data.sharedLink.album, assets: data.sharedLink.assets };
+	if (sharedLink.album) {
+		album = { ...sharedLink.album, assets: sharedLink.assets };
 	}
 </script>
 
-<svelte:head>
-	<title>{data.sharedLink.album?.albumName || 'Public Shared'} - Immich</title>
-</svelte:head>
-
 {#if album}
 	<div class="immich-scrollbar">
-		<AlbumViewer {album} sharedLink={data.sharedLink} />
+		<AlbumViewer {album} {sharedLink} />
 	</div>
 {/if}

+ 4 - 1
web/src/routes/sharing/+page.server.ts

@@ -15,7 +15,10 @@ export const load: PageServerLoad = async ({ parent }) => {
 
 		return {
 			user: user,
-			sharedAlbums: sharedAlbums
+			sharedAlbums,
+			meta: {
+				title: 'Albums'
+			}
 		};
 	} catch (e) {
 		throw redirect(302, '/auth/login');

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

@@ -33,10 +33,6 @@
 	};
 </script>
 
-<svelte:head>
-	<title>Albums - Immich</title>
-</svelte:head>
-
 <section>
 	<NavigationBar user={data.user} shouldShowUploadButton={false} />
 </section>

+ 4 - 1
web/src/routes/sharing/sharedlinks/+page.server.ts

@@ -10,7 +10,10 @@ export const load: PageServerLoad = async ({ parent }) => {
 		}
 
 		return {
-			user
+			user,
+			meta: {
+				title: 'Shared Links'
+			}
 		};
 	} catch (e) {
 		throw redirect(302, '/auth/login');

+ 0 - 4
web/src/routes/sharing/sharedlinks/+page.svelte

@@ -67,10 +67,6 @@
 	};
 </script>
 
-<svelte:head>
-	<title>Shared links - Immich</title>
-</svelte:head>
-
 <ControlAppBar backIcon={ArrowLeft} on:close-button-click={() => goto('/sharing')}>
 	<svelte:fragment slot="leading">Shared links</svelte:fragment>
 </ControlAppBar>

+ 4 - 1
web/src/routes/user-settings/+page.server.ts

@@ -10,7 +10,10 @@ export const load: PageServerLoad = async ({ parent }) => {
 		}
 
 		return {
-			user: user
+			user,
+			meta: {
+				title: 'Settings'
+			}
 		};
 	} catch (e) {
 		throw redirect(302, '/auth/login');

+ 0 - 4
web/src/routes/user-settings/+page.svelte

@@ -7,10 +7,6 @@
 	export let data: PageData;
 </script>
 
-<svelte:head>
-	<title>Settings - Immich</title>
-</svelte:head>
-
 <section>
 	<NavigationBar user={data.user} shouldShowUploadButton={false} />
 </section>

BIN
web/static/feature-panel.png