mirror of
https://github.com/immich-app/immich.git
synced 2024-12-25 10:43:13 +02:00
feat(web): meta tags for share links (#1290)
* feat(web): meta tags for share links * refactor: svelte head tags * chore: clean up * chore: linting
This commit is contained in:
parent
a3688fe642
commit
fa31a6e441
@ -1,10 +1,18 @@
|
|||||||
import { api, AddAssetsResponseDto, AssetResponseDto } from '@api';
|
import { api, AddAssetsResponseDto, AssetResponseDto, ThumbnailFormat } from '@api';
|
||||||
import {
|
import {
|
||||||
notificationController,
|
notificationController,
|
||||||
NotificationType
|
NotificationType
|
||||||
} from '$lib/components/shared-components/notification/notification';
|
} from '$lib/components/shared-components/notification/notification';
|
||||||
import { downloadAssets } from '$lib/stores/download';
|
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 (
|
export const addAssetsToAlbum = async (
|
||||||
albumId: string,
|
albumId: string,
|
||||||
assetIds: Array<string>,
|
assetIds: Array<string>,
|
||||||
|
@ -77,6 +77,25 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</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)}>
|
<main on:dragenter={() => (showUploadCover = true)}>
|
||||||
{#if canShow}
|
{#if canShow}
|
||||||
<div in:fade={{ duration: 100 }}>
|
<div in:fade={{ duration: 100 }}>
|
||||||
|
@ -2,11 +2,6 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
</script>
|
</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">
|
<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 flex-col place-items-center gap-8 text-center max-w-[350px]">
|
||||||
<div class="flex place-items-center place-content-center ">
|
<div class="flex place-items-center place-content-center ">
|
||||||
|
@ -7,4 +7,11 @@ export const load: PageLoad = async ({ parent }) => {
|
|||||||
if (user) {
|
if (user) {
|
||||||
throw redirect(302, '/photos');
|
throw redirect(302, '/photos');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
meta: {
|
||||||
|
title: 'Welcome 🎉',
|
||||||
|
description: 'Immich Web Interface'
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -26,10 +26,6 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Administration - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<NavigationBar user={$page.data.user} />
|
<NavigationBar user={$page.data.user} />
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
@ -9,4 +9,10 @@ export const load: PageServerLoad = async ({ parent }) => {
|
|||||||
} else if (!user.isAdmin) {
|
} else if (!user.isAdmin) {
|
||||||
throw redirect(302, '/photos');
|
throw redirect(302, '/photos');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
meta: {
|
||||||
|
title: 'Job Status'
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -2,10 +2,6 @@
|
|||||||
import JobsPanel from '$lib/components/admin-page/jobs/jobs-panel.svelte';
|
import JobsPanel from '$lib/components/admin-page/jobs/jobs-panel.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Jobs Status - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<JobsPanel />
|
<JobsPanel />
|
||||||
</section>
|
</section>
|
||||||
|
@ -13,5 +13,10 @@ export const load: PageServerLoad = async ({ parent }) => {
|
|||||||
|
|
||||||
const { data: allUsers } = await serverApi.userApi.getAllUsers(false);
|
const { data: allUsers } = await serverApi.userApi.getAllUsers(false);
|
||||||
|
|
||||||
return { allUsers };
|
return {
|
||||||
|
allUsers,
|
||||||
|
meta: {
|
||||||
|
title: 'Server Status'
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -3,10 +3,6 @@
|
|||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Server Status - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
{#if $page.data.allUsers}
|
{#if $page.data.allUsers}
|
||||||
<ServerStatsPanel allUsers={$page.data.allUsers} />
|
<ServerStatsPanel allUsers={$page.data.allUsers} />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -10,5 +10,10 @@ export const load: PageServerLoad = async ({ parent }) => {
|
|||||||
throw redirect(302, '/photos');
|
throw redirect(302, '/photos');
|
||||||
}
|
}
|
||||||
|
|
||||||
return { user };
|
return {
|
||||||
|
user,
|
||||||
|
meta: {
|
||||||
|
title: 'System Settings'
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -17,10 +17,6 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>System Settings - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<section class="">
|
<section class="">
|
||||||
{#await getConfig()}
|
{#await getConfig()}
|
||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
|
@ -13,5 +13,11 @@ export const load: PageServerLoad = async ({ parent }) => {
|
|||||||
|
|
||||||
const { data: allUsers } = await serverApi.userApi.getAllUsers(false);
|
const { data: allUsers } = await serverApi.userApi.getAllUsers(false);
|
||||||
|
|
||||||
return { user, allUsers };
|
return {
|
||||||
|
user,
|
||||||
|
allUsers,
|
||||||
|
meta: {
|
||||||
|
title: 'User Management'
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -101,10 +101,6 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>User Management - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
{#if shouldShowCreateUserForm}
|
{#if shouldShowCreateUserForm}
|
||||||
<FullScreenModal on:clickOutside={() => (shouldShowCreateUserForm = false)}>
|
<FullScreenModal on:clickOutside={() => (shouldShowCreateUserForm = false)}>
|
||||||
|
@ -14,7 +14,10 @@ export const load: PageServerLoad = async ({ parent }) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
user: user,
|
user: user,
|
||||||
albums: albums
|
albums: albums,
|
||||||
|
meta: {
|
||||||
|
title: 'Albums'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw redirect(302, '/auth/login');
|
throw redirect(302, '/auth/login');
|
||||||
|
@ -34,10 +34,6 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Albums - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<NavigationBar user={data.user} shouldShowUploadButton={false} />
|
<NavigationBar user={data.user} shouldShowUploadButton={false} />
|
||||||
</section>
|
</section>
|
||||||
|
@ -15,7 +15,10 @@ export const load: PageServerLoad = async ({ parent, params }) => {
|
|||||||
try {
|
try {
|
||||||
const { data: album } = await serverApi.albumApi.getAlbumInfo(albumId);
|
const { data: album } = await serverApi.albumApi.getAlbumInfo(albumId);
|
||||||
return {
|
return {
|
||||||
album
|
album,
|
||||||
|
meta: {
|
||||||
|
title: album.albumName
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw redirect(302, '/albums');
|
throw redirect(302, '/albums');
|
||||||
|
@ -5,10 +5,6 @@
|
|||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>{data.album.albumName} - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<div class="immich-scrollbar">
|
<div class="immich-scrollbar">
|
||||||
<AlbumViewer album={data.album} />
|
<AlbumViewer album={data.album} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,10 +14,6 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Change Password - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<section class="h-screen w-screen flex place-items-center place-content-center">
|
<section class="h-screen w-screen flex place-items-center place-content-center">
|
||||||
<div in:fade={{ duration: 100 }} out:fade={{ duration: 100 }}>
|
<div in:fade={{ duration: 100 }} out:fade={{ duration: 100 }}>
|
||||||
<ChangePasswordForm user={data.user} on:success={onSuccessHandler} />
|
<ChangePasswordForm user={data.user} on:success={onSuccessHandler} />
|
||||||
|
@ -10,7 +10,10 @@ export const load: PageLoad = async () => {
|
|||||||
|
|
||||||
if (userInfo.shouldChangePassword) {
|
if (userInfo.shouldChangePassword) {
|
||||||
return {
|
return {
|
||||||
user: userInfo
|
user: userInfo,
|
||||||
|
meta: {
|
||||||
|
title: 'Change Password'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
throw redirect(302, '/photos');
|
throw redirect(302, '/photos');
|
||||||
|
@ -9,5 +9,9 @@ export const load: PageServerLoad = async () => {
|
|||||||
throw redirect(302, '/auth/register');
|
throw redirect(302, '/auth/register');
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return {
|
||||||
|
meta: {
|
||||||
|
title: 'Login'
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -5,10 +5,6 @@
|
|||||||
import LoginForm from '$lib/components/forms/login-form.svelte';
|
import LoginForm from '$lib/components/forms/login-form.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Login - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<section class="h-screen w-screen flex place-items-center place-content-center">
|
<section class="h-screen w-screen flex place-items-center place-content-center">
|
||||||
<div in:fade={{ duration: 100 }} out:fade={{ duration: 100 }}>
|
<div in:fade={{ duration: 100 }} out:fade={{ duration: 100 }}>
|
||||||
<LoginForm
|
<LoginForm
|
||||||
|
@ -9,5 +9,9 @@ export const load: PageServerLoad = async () => {
|
|||||||
throw redirect(302, '/auth/login');
|
throw redirect(302, '/auth/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return {
|
||||||
|
meta: {
|
||||||
|
title: 'Admin Registration'
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
@ -2,10 +2,6 @@
|
|||||||
import AdminRegistrationForm from '$lib/components/forms/admin-registration-form.svelte';
|
import AdminRegistrationForm from '$lib/components/forms/admin-registration-form.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Admin Registration - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<section class="h-screen w-screen flex place-items-center place-content-center">
|
<section class="h-screen w-screen flex place-items-center place-content-center">
|
||||||
<AdminRegistrationForm />
|
<AdminRegistrationForm />
|
||||||
</section>
|
</section>
|
||||||
|
@ -9,7 +9,10 @@ export const load: PageServerLoad = async ({ parent }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user
|
user,
|
||||||
|
meta: {
|
||||||
|
title: 'Photos'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Photo page load error', e);
|
console.log('Photo page load error', e);
|
||||||
|
@ -116,10 +116,6 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Photos - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
{#if $isMultiSelectStoreState}
|
{#if $isMultiSelectStoreState}
|
||||||
<ControlAppBar
|
<ControlAppBar
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
import { error } from '@sveltejs/kit';
|
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';
|
import type { PageServerLoad } from './$types';
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ params }) => {
|
export const load: PageServerLoad = async ({ params }) => {
|
||||||
@ -9,7 +10,20 @@ export const load: PageServerLoad = async ({ params }) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const { data: sharedLink } = await serverApi.shareApi.getMySharedLink({ params: { key } });
|
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) {
|
} catch (e) {
|
||||||
throw error(404, {
|
throw error(404, {
|
||||||
message: 'Invalid shared link'
|
message: 'Invalid shared link'
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import AlbumViewer from '$lib/components/album-page/album-viewer.svelte';
|
import AlbumViewer from '$lib/components/album-page/album-viewer.svelte';
|
||||||
import { AlbumResponseDto } from '../../../api';
|
import { AlbumResponseDto } from '@api';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
|
const { sharedLink } = data;
|
||||||
|
|
||||||
let album: AlbumResponseDto | null = null;
|
let album: AlbumResponseDto | null = null;
|
||||||
if (data.sharedLink.album) {
|
if (sharedLink.album) {
|
||||||
album = { ...data.sharedLink.album, assets: data.sharedLink.assets };
|
album = { ...sharedLink.album, assets: sharedLink.assets };
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>{data.sharedLink.album?.albumName || 'Public Shared'} - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
{#if album}
|
{#if album}
|
||||||
<div class="immich-scrollbar">
|
<div class="immich-scrollbar">
|
||||||
<AlbumViewer {album} sharedLink={data.sharedLink} />
|
<AlbumViewer {album} {sharedLink} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -15,7 +15,10 @@ export const load: PageServerLoad = async ({ parent }) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
user: user,
|
user: user,
|
||||||
sharedAlbums: sharedAlbums
|
sharedAlbums,
|
||||||
|
meta: {
|
||||||
|
title: 'Albums'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw redirect(302, '/auth/login');
|
throw redirect(302, '/auth/login');
|
||||||
|
@ -33,10 +33,6 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Albums - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<NavigationBar user={data.user} shouldShowUploadButton={false} />
|
<NavigationBar user={data.user} shouldShowUploadButton={false} />
|
||||||
</section>
|
</section>
|
||||||
|
@ -10,7 +10,10 @@ export const load: PageServerLoad = async ({ parent }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user
|
user,
|
||||||
|
meta: {
|
||||||
|
title: 'Shared Links'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw redirect(302, '/auth/login');
|
throw redirect(302, '/auth/login');
|
||||||
|
@ -67,10 +67,6 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Shared links - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<ControlAppBar backIcon={ArrowLeft} on:close-button-click={() => goto('/sharing')}>
|
<ControlAppBar backIcon={ArrowLeft} on:close-button-click={() => goto('/sharing')}>
|
||||||
<svelte:fragment slot="leading">Shared links</svelte:fragment>
|
<svelte:fragment slot="leading">Shared links</svelte:fragment>
|
||||||
</ControlAppBar>
|
</ControlAppBar>
|
||||||
|
@ -10,7 +10,10 @@ export const load: PageServerLoad = async ({ parent }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user: user
|
user,
|
||||||
|
meta: {
|
||||||
|
title: 'Settings'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw redirect(302, '/auth/login');
|
throw redirect(302, '/auth/login');
|
||||||
|
@ -7,10 +7,6 @@
|
|||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Settings - Immich</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<NavigationBar user={data.user} shouldShowUploadButton={false} />
|
<NavigationBar user={data.user} shouldShowUploadButton={false} />
|
||||||
</section>
|
</section>
|
||||||
|
BIN
web/static/feature-panel.png
Normal file
BIN
web/static/feature-panel.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
Loading…
Reference in New Issue
Block a user