feat(web/server) public album sharing (#1266)

This commit is contained in:
Alex
2023-01-09 14:16:08 -06:00
committed by GitHub
parent fd15cdbf40
commit 10789503c1
101 changed files with 4879 additions and 347 deletions

View File

@@ -17,6 +17,7 @@
} from '$lib/stores/asset-interaction.store';
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
import Close from 'svelte-material-icons/Close.svelte';
import CloudDownloadOutline from 'svelte-material-icons/CloudDownloadOutline.svelte';
import CircleIconButton from '$lib/components/shared-components/circle-icon-button.svelte';
import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
import Plus from 'svelte-material-icons/Plus.svelte';
@@ -26,7 +27,7 @@
NotificationType
} from '$lib/components/shared-components/notification/notification';
import { assetStore } from '$lib/stores/assets.store';
import { addAssetsToAlbum } from '$lib/utils/asset-utils';
import { addAssetsToAlbum, bulkDownload } from '$lib/utils/asset-utils';
export let data: PageData;
@@ -106,6 +107,12 @@
assetInteractionStore.clearMultiselect();
});
};
const handleDownloadFiles = async () => {
await bulkDownload('immich', Array.from($selectedAssets), () => {
assetInteractionStore.clearMultiselect();
});
};
</script>
<svelte:head>
@@ -125,6 +132,11 @@
</p>
</svelte:fragment>
<svelte:fragment slot="trailing">
<CircleIconButton
title="Download"
logo={CloudDownloadOutline}
on:click={handleDownloadFiles}
/>
<CircleIconButton title="Add" logo={Plus} on:click={handleShowMenu} />
<CircleIconButton
title="Delete"

View File

@@ -0,0 +1,9 @@
<svelte:head>
<title>Opps! Error - Immich</title>
</svelte:head>
<section class="w-screen h-screen flex place-items-center place-content-center">
<div class="p-20 text-4xl dark:text-immich-dark-primary text-immich-primary">
Page not found :/
</div>
</section>

View File

@@ -0,0 +1,18 @@
export const prerender = false;
import { error } from '@sveltejs/kit';
import { serverApi } from '@api';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params }) => {
const { key } = params;
try {
const { data: sharedLink } = await serverApi.shareApi.getMySharedLink({ params: { key } });
return { sharedLink };
} catch (e) {
throw error(404, {
message: 'Invalid shared link'
});
}
};

View File

@@ -0,0 +1,22 @@
<script lang="ts">
import AlbumViewer from '$lib/components/album-page/album-viewer.svelte';
import { AlbumResponseDto } from '../../../api';
import type { PageData } from './$types';
export let data: PageData;
let album: AlbumResponseDto | null = null;
if (data.sharedLink.album) {
album = { ...data.sharedLink.album, assets: data.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} />
</div>
{/if}

View File

@@ -0,0 +1,21 @@
export const prerender = false;
import { error } from '@sveltejs/kit';
import { serverApi } from '@api';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params }) => {
try {
const { key, assetId } = params;
const { data: asset } = await serverApi.assetApi.getAssetById(assetId, {
params: { key }
});
if (!asset) {
return error(404, 'Asset not found');
}
return { asset, key };
} catch (e) {
console.log('Error', e);
}
};

View File

@@ -0,0 +1,17 @@
<script lang="ts">
import AssetViewer from '$lib/components/asset-viewer/asset-viewer.svelte';
import type { PageData } from './$types';
import { goto } from '$app/navigation';
export let data: PageData;
</script>
{#if data.asset && data.key}
<AssetViewer
asset={data.asset}
publicSharedKey={data.key}
on:navigate-previous={() => null}
on:navigate-next={() => null}
showNavigation={false}
on:close={() => goto(`/share/${data.key}`)}
/>
{/if}

View File

@@ -2,6 +2,8 @@
import NavigationBar from '$lib/components/shared-components/navigation-bar/navigation-bar.svelte';
import SideBar from '$lib/components/shared-components/side-bar/side-bar.svelte';
import PlusBoxOutline from 'svelte-material-icons/PlusBoxOutline.svelte';
import Link from 'svelte-material-icons/Link.svelte';
import SharedAlbumListTile from '$lib/components/sharing-page/shared-album-list-tile.svelte';
import { goto } from '$app/navigation';
import { api } from '@api';
@@ -55,7 +57,7 @@
<p class="font-medium">Sharing</p>
</div>
<div>
<div class="flex">
<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 dark:hover:bg-immich-dark-primary/25 dark:text-immich-dark-fg"
@@ -65,6 +67,16 @@
</span>
<p>Create shared album</p>
</button>
<button
on:click={() => goto('/sharing/sharedlinks')}
class="flex place-items-center gap-1 text-sm hover:bg-immich-primary/5 p-2 rounded-lg font-medium hover:text-gray-700 dark:hover:bg-immich-dark-primary/25 dark:text-immich-dark-fg"
>
<span>
<Link size="18" />
</span>
<p>Shared links</p>
</button>
</div>
</div>

View File

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

View File

@@ -0,0 +1,109 @@
<script lang="ts">
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
import { api, SharedLinkResponseDto } from '@api';
import { goto } from '$app/navigation';
import SharedLinkCard from '$lib/components/sharedlinks-page/shared-link-card.svelte';
import {
notificationController,
NotificationType
} from '$lib/components/shared-components/notification/notification';
import { onMount } from 'svelte';
import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte';
let sharedLinks: SharedLinkResponseDto[] = [];
let showEditForm = false;
let editSharedLink: SharedLinkResponseDto;
onMount(async () => {
sharedLinks = await getSharedLinks();
});
const getSharedLinks = async () => {
const { data: sharedLinks } = await api.shareApi.getAllSharedLinks();
return sharedLinks;
};
const handleDeleteLink = async (linkId: string) => {
if (window.confirm('Do you want to delete the shared link? ')) {
try {
await api.shareApi.removeSharedLink(linkId);
notificationController.show({
message: 'Shared link deleted',
type: NotificationType.Info
});
sharedLinks = await getSharedLinks();
} catch (e) {
console.error(e);
notificationController.show({
message: 'Failed to delete shared link',
type: NotificationType.Error
});
}
}
};
const handleEditLink = async (id: string) => {
const { data } = await api.shareApi.getSharedLinkById(id);
editSharedLink = data;
showEditForm = true;
};
const handleEditDone = async () => {
sharedLinks = await getSharedLinks();
showEditForm = false;
};
const handleCopy = async (key: string) => {
const link = `${window.location.origin}/share/${key}`;
await navigator.clipboard.writeText(link);
notificationController.show({
message: 'Link copied to clipboard',
type: NotificationType.Info
});
};
</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>
<section class="flex flex-col pb-[120px] mt-[120px]">
<div class="w-[50%] m-auto mb-4 dark:text-immich-gray">
<p>Manage shared links</p>
</div>
{#if sharedLinks.length === 0}
<div
class="w-[50%] m-auto bg-gray-100 flex place-items-center place-content-center rounded-lg p-12"
>
<p>You don't have any shared links</p>
</div>
{:else}
<div class="flex flex-col w-[50%] m-auto">
{#each sharedLinks as link (link.id)}
<SharedLinkCard
{link}
on:delete={() => handleDeleteLink(link.id)}
on:edit={() => handleEditLink(link.id)}
on:copy={() => handleCopy(link.key)}
/>
{/each}
</div>
{/if}
</section>
{#if showEditForm}
<CreateSharedLinkModal
editingLink={editSharedLink}
shareType={editSharedLink.type}
album={editSharedLink.album}
on:close={handleEditDone}
/>
{/if}