feat(web): shared link filters (#15948)

This commit is contained in:
Jason Rasmussen
2025-02-07 13:05:15 -05:00
committed by GitHub
parent 23014c263b
commit c5360e78c5
22 changed files with 520 additions and 112 deletions
@@ -0,0 +1,119 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/state';
import GroupTab from '$lib/components/elements/group-tab.svelte';
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte';
import { dialogController } from '$lib/components/shared-components/dialog/dialog';
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import SharedLinkCard from '$lib/components/sharedlinks-page/shared-link-card.svelte';
import { AppRoute } from '$lib/constants';
import { handleError } from '$lib/utils/handle-error';
import { getAllSharedLinks, removeSharedLink, SharedLinkType, type SharedLinkResponseDto } from '@immich/sdk';
import { onMount } from 'svelte';
import { t } from 'svelte-i18n';
import type { PageData } from './$types';
type Props = {
data: PageData;
};
const { data }: Props = $props();
let sharedLinks: SharedLinkResponseDto[] = $state([]);
let sharedLink = $derived(sharedLinks.find(({ id }) => id === page.params.id));
const refresh = async () => {
sharedLinks = await getAllSharedLinks();
};
onMount(async () => {
await refresh();
});
const handleDeleteLink = async (id: string) => {
const isConfirmed = await dialogController.show({
title: $t('delete_shared_link'),
prompt: $t('confirm_delete_shared_link'),
confirmText: $t('delete'),
});
if (!isConfirmed) {
return;
}
try {
await removeSharedLink({ id });
notificationController.show({ message: $t('deleted_shared_link'), type: NotificationType.Info });
await refresh();
} catch (error) {
handleError(error, $t('errors.unable_to_delete_shared_link'));
}
};
const handleEditDone = async () => {
await refresh();
await goto(AppRoute.SHARED_LINKS);
};
type Filter = 'all' | 'album' | 'individual';
const filterMap: Record<Filter, string> = {
all: $t('all'),
album: $t('albums'),
individual: $t('individual_shares'),
};
let filters = Object.keys(filterMap);
let labels = Object.values(filterMap);
const getActiveTab = (url: URL) => {
const filter = url.searchParams.get('filter');
return filter && filters.includes(filter) ? filter : 'all';
};
let selectedTab = $derived(getActiveTab(page.url));
const handleSelectTab = async (value: string) => {
await goto(`${AppRoute.SHARED_LINKS}?filter=${value}`);
};
let filteredSharedLinks = $derived(
sharedLinks.filter(
({ type }) =>
selectedTab === 'all' ||
(type === SharedLinkType.Album && selectedTab === 'album') ||
(type === SharedLinkType.Individual && selectedTab === 'individual'),
),
);
</script>
<UserPageLayout title={data.meta.title}>
{#snippet buttons()}
<div class="hidden xl:block h-10">
<GroupTab label={$t('show_shared_links')} {filters} {labels} selected={selectedTab} onSelect={handleSelectTab} />
</div>
{/snippet}
<div>
{#if sharedLinks.length === 0}
<div
class="flex place-content-center place-items-center rounded-lg bg-gray-100 dark:bg-immich-dark-gray dark:text-immich-gray p-12"
>
<p>{$t('you_dont_have_any_shared_links')}</p>
</div>
{:else}
<div class="flex flex-col gap-2">
{#each filteredSharedLinks as link (link.id)}
<SharedLinkCard {link} onDelete={() => handleDeleteLink(link.id)} />
{/each}
</div>
{/if}
{#if sharedLink}
<CreateSharedLinkModal editingLink={sharedLink} onClose={handleEditDone} />
{/if}
</div>
</UserPageLayout>
@@ -0,0 +1,14 @@
import { authenticate } from '$lib/utils/auth';
import { getFormatter } from '$lib/utils/i18n';
import type { PageLoad } from './$types';
export const load = (async () => {
await authenticate();
const $t = await getFormatter();
return {
meta: {
title: $t('shared_links'),
},
};
}) satisfies PageLoad;
@@ -1,89 +0,0 @@
<script lang="ts">
import { goto, afterNavigate } from '$app/navigation';
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
import CreateSharedLinkModal from '$lib/components/shared-components/create-share-link-modal/create-shared-link-modal.svelte';
import {
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import SharedLinkCard from '$lib/components/sharedlinks-page/shared-link-card.svelte';
import { AppRoute } from '$lib/constants';
import { handleError } from '$lib/utils/handle-error';
import { getAllSharedLinks, removeSharedLink, type SharedLinkResponseDto } from '@immich/sdk';
import { mdiArrowLeft } from '@mdi/js';
import { onMount } from 'svelte';
import { dialogController } from '$lib/components/shared-components/dialog/dialog';
import { t } from 'svelte-i18n';
let sharedLinks: SharedLinkResponseDto[] = $state([]);
let editSharedLink: SharedLinkResponseDto | null = $state(null);
const refresh = async () => {
sharedLinks = await getAllSharedLinks();
};
onMount(async () => {
await refresh();
});
const handleDeleteLink = async (id: string) => {
const isConfirmed = await dialogController.show({
title: $t('delete_shared_link'),
prompt: $t('confirm_delete_shared_link'),
confirmText: $t('delete'),
});
if (!isConfirmed) {
return;
}
try {
await removeSharedLink({ id });
notificationController.show({ message: $t('deleted_shared_link'), type: NotificationType.Info });
await refresh();
} catch (error) {
handleError(error, $t('errors.unable_to_delete_shared_link'));
}
};
const handleEditDone = async () => {
await refresh();
editSharedLink = null;
};
let backUrl: string = AppRoute.SHARING;
afterNavigate(({ from }) => {
let url: string | undefined = from?.url?.pathname;
backUrl = url || AppRoute.SHARING;
});
</script>
<ControlAppBar backIcon={mdiArrowLeft} onClose={() => goto(backUrl)}>
{#snippet leading()}
{$t('shared_links')}
{/snippet}
</ControlAppBar>
<section class="mt-[120px] flex flex-col pb-[120px] container max-w-screen-lg mx-auto px-3">
<div class="mb-4 dark:text-immich-gray">
<p>{$t('manage_shared_links')}</p>
</div>
{#if sharedLinks.length === 0}
<div
class="flex place-content-center place-items-center rounded-lg bg-gray-100 dark:bg-immich-dark-gray dark:text-immich-gray p-12"
>
<p>{$t('you_dont_have_any_shared_links')}</p>
</div>
{:else}
<div class="flex flex-col">
{#each sharedLinks as link (link.id)}
<SharedLinkCard {link} onDelete={() => handleDeleteLink(link.id)} onEdit={() => (editSharedLink = link)} />
{/each}
</div>
{/if}
</section>
{#if editSharedLink}
<CreateSharedLinkModal editingLink={editSharedLink} onClose={handleEditDone} />
{/if}
@@ -1,14 +1,7 @@
import { authenticate } from '$lib/utils/auth';
import { getFormatter } from '$lib/utils/i18n';
import { AppRoute } from '$lib/constants';
import { redirect } from '@sveltejs/kit';
import type { PageLoad } from './$types';
export const load = (async () => {
await authenticate();
const $t = await getFormatter();
return {
meta: {
title: $t('shared_links'),
},
};
export const load = (() => {
redirect(307, AppRoute.SHARED_LINKS);
}) satisfies PageLoad;