refactor: album service (#23768)

This commit is contained in:
Jason Rasmussen
2025-11-10 13:40:58 -05:00
committed by GitHub
parent d27c01ef70
commit 8de6ec1a1b
9 changed files with 114 additions and 107 deletions

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import SharedLinkCopy from '$lib/components/sharedlinks-page/actions/shared-link-copy.svelte'; import SharedLinkCopy from '$lib/components/sharedlinks-page/actions/shared-link-copy.svelte';
import { handleViewSharedLinkQrCode } from '$lib/services/shared-link.service'; import { handleShowSharedLinkQrCode } from '$lib/services/shared-link.service';
import { locale } from '$lib/stores/preferences.store'; import { locale } from '$lib/stores/preferences.store';
import type { AlbumResponseDto, SharedLinkResponseDto } from '@immich/sdk'; import type { AlbumResponseDto, SharedLinkResponseDto } from '@immich/sdk';
import { IconButton, Text } from '@immich/ui'; import { IconButton, Text } from '@immich/ui';
@@ -46,7 +46,7 @@
color="secondary" color="secondary"
variant="ghost" variant="ghost"
icon={mdiQrcode} icon={mdiQrcode}
onclick={() => handleViewSharedLinkQrCode(sharedLink)} onclick={() => handleShowSharedLinkQrCode(sharedLink)}
/> />
<SharedLinkCopy {sharedLink} /> <SharedLinkCopy {sharedLink} />
</div> </div>

View File

@@ -11,7 +11,6 @@
import AlbumShareModal from '$lib/modals/AlbumShareModal.svelte'; import AlbumShareModal from '$lib/modals/AlbumShareModal.svelte';
import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte'; import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte';
import { handleConfirmAlbumDelete, handleDownloadAlbum } from '$lib/services/album.service'; import { handleConfirmAlbumDelete, handleDownloadAlbum } from '$lib/services/album.service';
import { handleViewSharedLinkQrCode } from '$lib/services/shared-link.service';
import { import {
AlbumFilter, AlbumFilter,
AlbumGroupBy, AlbumGroupBy,
@@ -255,10 +254,11 @@
} }
case 'sharedLink': { case 'sharedLink': {
const sharedLink = await modalManager.show(SharedLinkCreateModal, { albumId: selectedAlbum.id }); const success = await modalManager.show(SharedLinkCreateModal, { albumId: selectedAlbum.id });
if (sharedLink) { if (success) {
handleSharedLinkCreated(selectedAlbum); selectedAlbum.shared = true;
await handleViewSharedLinkQrCode(sharedLink); selectedAlbum.hasSharedLink = true;
updateAlbumInfo(selectedAlbum);
} }
break; break;
} }
@@ -346,12 +346,6 @@
albumToShare = null; albumToShare = null;
} }
}; };
const handleSharedLinkCreated = (album: AlbumResponseDto) => {
album.shared = true;
album.hasSharedLink = true;
updateAlbumInfo(album);
};
</script> </script>
{#if albums.length > 0} {#if albums.length > 0}

View File

@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte'; import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte';
import { handleViewSharedLinkQrCode } from '$lib/services/shared-link.service';
import type { AssetResponseDto } from '@immich/sdk'; import type { AssetResponseDto } from '@immich/sdk';
import { IconButton, modalManager } from '@immich/ui'; import { IconButton, modalManager } from '@immich/ui';
import { mdiShareVariantOutline } from '@mdi/js'; import { mdiShareVariantOutline } from '@mdi/js';
@@ -13,10 +12,7 @@
let { asset }: Props = $props(); let { asset }: Props = $props();
const handleClick = async () => { const handleClick = async () => {
const sharedLink = await modalManager.show(SharedLinkCreateModal, { assetIds: [asset.id] }); await modalManager.show(SharedLinkCreateModal, { assetIds: [asset.id] });
if (sharedLink) {
await handleViewSharedLinkQrCode(sharedLink);
}
}; };
</script> </script>

View File

@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte'; import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte'; import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte';
import { handleViewSharedLinkQrCode } from '$lib/services/shared-link.service';
import { IconButton, modalManager } from '@immich/ui'; import { IconButton, modalManager } from '@immich/ui';
import { mdiShareVariantOutline } from '@mdi/js'; import { mdiShareVariantOutline } from '@mdi/js';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
@@ -9,13 +8,7 @@
const { getAssets } = getAssetControlContext(); const { getAssets } = getAssetControlContext();
const handleClick = async () => { const handleClick = async () => {
const sharedLink = await modalManager.show(SharedLinkCreateModal, { await modalManager.show(SharedLinkCreateModal, { assetIds: [...getAssets()].map(({ id }) => id) });
assetIds: [...getAssets()].map(({ id }) => id),
});
if (sharedLink) {
await handleViewSharedLinkQrCode(sharedLink);
}
}; };
</script> </script>

View File

@@ -1,5 +1,5 @@
import type { ThemeSetting } from '$lib/managers/theme-manager.svelte'; import type { ThemeSetting } from '$lib/managers/theme-manager.svelte';
import type { LoginResponseDto } from '@immich/sdk'; import type { LoginResponseDto, SharedLinkResponseDto } from '@immich/sdk';
export type Events = { export type Events = {
AppInit: []; AppInit: [];
@@ -8,6 +8,8 @@ export type Events = {
AuthLogout: []; AuthLogout: [];
LanguageChange: [{ name: string; code: string; rtl?: boolean }]; LanguageChange: [{ name: string; code: string; rtl?: boolean }];
ThemeChange: [ThemeSetting]; ThemeChange: [ThemeSetting];
SharedLinkCreate: [SharedLinkResponseDto];
SharedLinkUpdate: [SharedLinkResponseDto];
}; };
type Listener<EventMap extends Record<string, unknown[]>, K extends keyof EventMap> = (...params: EventMap[K]) => void; type Listener<EventMap extends Record<string, unknown[]>, K extends keyof EventMap> = (...params: EventMap[K]) => void;

View File

@@ -1,26 +1,15 @@
<script lang="ts"> <script lang="ts">
import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte'; import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
import { handleCreateSharedLink, handleUpdateSharedLink } from '$lib/services/shared-link.service';
import { locale } from '$lib/stores/preferences.store'; import { locale } from '$lib/stores/preferences.store';
import { handleError } from '$lib/utils/handle-error'; import { SharedLinkType, type SharedLinkResponseDto } from '@immich/sdk';
import { SharedLinkType, createSharedLink, updateSharedLink, type SharedLinkResponseDto } from '@immich/sdk'; import { Button, Field, Input, Modal, ModalBody, ModalFooter, PasswordInput, Switch, Text } from '@immich/ui';
import {
Button,
Field,
Input,
Modal,
ModalBody,
ModalFooter,
PasswordInput,
Switch,
Text,
toastManager,
} from '@immich/ui';
import { mdiLink } from '@mdi/js'; import { mdiLink } from '@mdi/js';
import { DateTime, Duration } from 'luxon'; import { DateTime, Duration } from 'luxon';
import { t } from 'svelte-i18n'; import { t } from 'svelte-i18n';
interface Props { interface Props {
onClose: (sharedLink?: SharedLinkResponseDto) => void; onClose: (success?: boolean) => void;
albumId?: string | undefined; albumId?: string | undefined;
assetIds?: string[]; assetIds?: string[];
editingLink?: SharedLinkResponseDto | undefined; editingLink?: SharedLinkResponseDto | undefined;
@@ -81,56 +70,46 @@
assetIds = editingLink.assets.map(({ id }) => id); assetIds = editingLink.assets.map(({ id }) => id);
} }
const handleCreateSharedLink = async () => { const onCreate = async () => {
const expirationDate = expirationOption > 0 ? DateTime.now().plus(expirationOption).toISO() : undefined; const success = await handleCreateSharedLink({
type: shareType,
albumId,
assetIds,
expiresAt: expirationOption > 0 ? DateTime.now().plus(expirationOption).toISO() : undefined,
allowUpload,
description,
password,
allowDownload,
showMetadata,
slug,
});
try { if (success) {
const data = await createSharedLink({ onClose(true);
sharedLinkCreateDto: {
type: shareType,
albumId,
assetIds,
expiresAt: expirationDate,
allowUpload,
description,
password,
allowDownload,
showMetadata,
slug,
},
});
onClose(data);
} catch (error) {
handleError(error, $t('errors.failed_to_create_shared_link'));
} }
}; };
const handleEditLink = async () => { const onUpdate = async () => {
if (!editingLink) { if (!editingLink) {
return; return;
} }
try { const success = await handleUpdateSharedLink(editingLink, {
const expirationDate = expirationOption > 0 ? DateTime.now().plus(expirationOption).toISO() : null; description,
password: password ?? null,
expiresAt: shouldChangeExpirationTime
? expirationOption > 0
? DateTime.now().plus(expirationOption).toISO()
: null
: undefined,
allowUpload,
allowDownload,
showMetadata,
slug: slug.trim() ?? null,
});
const updatedLink = await updateSharedLink({ if (success) {
id: editingLink.id, onClose(true);
sharedLinkEditDto: {
description,
password: password ?? null,
expiresAt: shouldChangeExpirationTime ? expirationDate : undefined,
allowUpload,
allowDownload,
showMetadata,
slug: slug.trim() ?? null,
},
});
toastManager.success($t('saved'));
onClose(updatedLink);
} catch (error) {
handleError(error, $t('errors.failed_to_edit_shared_link'));
} }
}; };
@@ -219,9 +198,9 @@
<ModalFooter> <ModalFooter>
{#if editingLink} {#if editingLink}
<Button fullWidth onclick={handleEditLink}>{$t('confirm')}</Button> <Button fullWidth onclick={onUpdate}>{$t('confirm')}</Button>
{:else} {:else}
<Button fullWidth onclick={handleCreateSharedLink}>{$t('create_link')}</Button> <Button fullWidth onclick={onCreate}>{$t('create_link')}</Button>
{/if} {/if}
</ModalFooter> </ModalFooter>
</Modal> </Modal>

View File

@@ -1,9 +1,17 @@
import { eventManager } from '$lib/managers/event-manager.svelte';
import QrCodeModal from '$lib/modals/QrCodeModal.svelte'; import QrCodeModal from '$lib/modals/QrCodeModal.svelte';
import { serverConfig } from '$lib/stores/server-config.store'; import { serverConfig } from '$lib/stores/server-config.store';
import { copyToClipboard } from '$lib/utils'; import { copyToClipboard } from '$lib/utils';
import { handleError } from '$lib/utils/handle-error';
import { getFormatter } from '$lib/utils/i18n'; import { getFormatter } from '$lib/utils/i18n';
import type { SharedLinkResponseDto } from '@immich/sdk'; import {
import { modalManager } from '@immich/ui'; createSharedLink,
updateSharedLink,
type SharedLinkCreateDto,
type SharedLinkEditDto,
type SharedLinkResponseDto,
} from '@immich/sdk';
import { modalManager, toastManager } from '@immich/ui';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
const makeSharedLinkUrl = (sharedLink: SharedLinkResponseDto) => { const makeSharedLinkUrl = (sharedLink: SharedLinkResponseDto) => {
@@ -11,7 +19,42 @@ const makeSharedLinkUrl = (sharedLink: SharedLinkResponseDto) => {
return new URL(path, get(serverConfig).externalDomain || globalThis.location.origin).href; return new URL(path, get(serverConfig).externalDomain || globalThis.location.origin).href;
}; };
export const handleViewSharedLinkQrCode = async (sharedLink: SharedLinkResponseDto) => { export const handleCreateSharedLink = async (dto: SharedLinkCreateDto) => {
const $t = await getFormatter();
try {
const sharedLink = await createSharedLink({ sharedLinkCreateDto: dto });
eventManager.emit('SharedLinkCreate', sharedLink);
// prevent nested modal
void handleShowSharedLinkQrCode(sharedLink);
return true;
} catch (error) {
handleError(error, $t('errors.failed_to_create_shared_link'));
return false;
}
};
export const handleUpdateSharedLink = async (sharedLink: SharedLinkResponseDto, dto: SharedLinkEditDto) => {
const $t = await getFormatter();
try {
const response = await updateSharedLink({ id: sharedLink.id, sharedLinkEditDto: dto });
eventManager.emit('SharedLinkUpdate', { album: sharedLink.album, ...response });
toastManager.success($t('saved'));
return true;
} catch (error) {
handleError(error, $t('errors.failed_to_edit_shared_link'));
return false;
}
};
export const handleShowSharedLinkQrCode = async (sharedLink: SharedLinkResponseDto) => {
const $t = await getFormatter(); const $t = await getFormatter();
await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink) }); await modalManager.show(QrCodeModal, { title: $t('view_link'), value: makeSharedLinkUrl(sharedLink) });
}; };

View File

@@ -8,6 +8,7 @@
import AlbumTitle from '$lib/components/album-page/album-title.svelte'; import AlbumTitle from '$lib/components/album-page/album-title.svelte';
import ActivityStatus from '$lib/components/asset-viewer/activity-status.svelte'; import ActivityStatus from '$lib/components/asset-viewer/activity-status.svelte';
import ActivityViewer from '$lib/components/asset-viewer/activity-viewer.svelte'; import ActivityViewer from '$lib/components/asset-viewer/activity-viewer.svelte';
import OnEvents from '$lib/components/OnEvents.svelte';
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte'; import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte'; import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
@@ -36,7 +37,6 @@
import AlbumUsersModal from '$lib/modals/AlbumUsersModal.svelte'; import AlbumUsersModal from '$lib/modals/AlbumUsersModal.svelte';
import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte'; import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte';
import { handleConfirmAlbumDelete, handleDownloadAlbum } from '$lib/services/album.service'; import { handleConfirmAlbumDelete, handleDownloadAlbum } from '$lib/services/album.service';
import { handleViewSharedLinkQrCode } from '$lib/services/shared-link.service';
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte'; import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { assetViewingStore } from '$lib/stores/asset-viewing.store'; import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import { featureFlags } from '$lib/stores/server-config.store'; import { featureFlags } from '$lib/stores/server-config.store';
@@ -384,12 +384,12 @@
} }
}; };
const onSharedLinkCreate = async () => {
await refreshAlbum();
};
const handleShareLink = async () => { const handleShareLink = async () => {
const sharedLink = await modalManager.show(SharedLinkCreateModal, { albumId: album.id }); await modalManager.show(SharedLinkCreateModal, { albumId: album.id });
if (sharedLink) {
await refreshAlbum();
await handleViewSharedLinkQrCode(sharedLink);
}
}; };
const handleEditUsers = async () => { const handleEditUsers = async () => {
@@ -424,6 +424,8 @@
}; };
</script> </script>
<OnEvents {onSharedLinkCreate} />
<div class="flex overflow-hidden" use:scrollMemoryClearer={{ routeStartsWith: AppRoute.ALBUMS }}> <div class="flex overflow-hidden" use:scrollMemoryClearer={{ routeStartsWith: AppRoute.ALBUMS }}>
<div class="relative w-full shrink"> <div class="relative w-full shrink">
<main class="relative h-dvh overflow-hidden px-2 md:px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)"> <main class="relative h-dvh overflow-hidden px-2 md:px-6 max-md:pt-(--navbar-height-md) pt-(--navbar-height)">

View File

@@ -2,6 +2,7 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { page } from '$app/state'; import { page } from '$app/state';
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte'; import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
import OnEvents from '$lib/components/OnEvents.svelte';
import SharedLinkCard from '$lib/components/sharedlinks-page/shared-link-card.svelte'; import SharedLinkCard from '$lib/components/sharedlinks-page/shared-link-card.svelte';
import { AppRoute } from '$lib/constants'; import { AppRoute } from '$lib/constants';
import GroupTab from '$lib/elements/GroupTab.svelte'; import GroupTab from '$lib/elements/GroupTab.svelte';
@@ -50,18 +51,6 @@
} }
}; };
const handleEditDone = async (updatedLink?: SharedLinkResponseDto) => {
if (updatedLink) {
const index = sharedLinks.findIndex((link) => link.id === updatedLink.id);
if (index !== -1) {
sharedLinks[index] = updatedLink;
}
} else {
await refresh();
}
await goto(AppRoute.SHARED_LINKS);
};
type Filter = 'all' | 'album' | 'individual'; type Filter = 'all' | 'album' | 'individual';
const filterMap: Record<Filter, string> = { const filterMap: Record<Filter, string> = {
@@ -91,8 +80,17 @@
(type === SharedLinkType.Individual && selectedTab === 'individual'), (type === SharedLinkType.Individual && selectedTab === 'individual'),
), ),
); );
const onSharedLinkUpdate = (sharedLink: SharedLinkResponseDto) => {
const index = sharedLinks.findIndex((link) => link.id === sharedLink.id);
if (index !== -1) {
sharedLinks[index] = sharedLink;
}
};
</script> </script>
<OnEvents {onSharedLinkUpdate} />
<UserPageLayout title={data.meta.title}> <UserPageLayout title={data.meta.title}>
{#snippet buttons()} {#snippet buttons()}
<div class="hidden xl:block h-10"> <div class="hidden xl:block h-10">
@@ -116,7 +114,7 @@
{/if} {/if}
{#if sharedLink} {#if sharedLink}
<SharedLinkCreateModal editingLink={sharedLink} onClose={handleEditDone} /> <SharedLinkCreateModal editingLink={sharedLink} onClose={() => goto(AppRoute.SHARED_LINKS)} />
{/if} {/if}
</div> </div>
</UserPageLayout> </UserPageLayout>