refactor(server, web): create shared link (#2879)

* refactor: shared links

* chore: open api

* fix: tsc error
This commit is contained in:
Jason Rasmussen 2023-06-20 21:08:43 -04:00 committed by GitHub
parent 746ca5d5ed
commit 868f629f32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 2150 additions and 2642 deletions

View file

@ -31,7 +31,7 @@
});
const getSharedLinks = async () => {
const { data } = await api.shareApi.getAllSharedLinks();
const { data } = await api.sharedLinkApi.getAllSharedLinks();
sharedLinks = data.filter((link) => link.album?.id === album.id);
};

View file

@ -1,34 +1,65 @@
<script lang="ts">
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import { AssetResponseDto, SharedLinkResponseDto, api } from '@api';
import { SharedLinkResponseDto, api } from '@api';
import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
import ConfirmDialogue from '../../shared-components/confirm-dialogue.svelte';
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
import {
NotificationType,
notificationController
} from '../../shared-components/notification/notification';
import { handleError } from '../../../utils/handle-error';
export let sharedLink: SharedLinkResponseDto;
export let allAssets: AssetResponseDto[];
let removing = false;
const { getAssets, clearSelect } = getAssetControlContext();
const handleRemoveAssetsFromSharedLink = async () => {
if (window.confirm('Do you want to remove selected assets from the shared link?')) {
// TODO: Rename API method or change functionality. The assetIds passed
// in are kept instead of removed.
const assetsToKeep = allAssets.filter((a) => !getAssets().has(a));
await api.assetApi.removeAssetsFromSharedLink({
removeAssetsDto: {
assetIds: assetsToKeep.map((a) => a.id)
const handleRemove = async () => {
try {
const { data: results } = await api.sharedLinkApi.removeSharedLinkAssets({
id: sharedLink.id,
assetIdsDto: {
assetIds: Array.from(getAssets()).map((asset) => asset.id)
},
key: sharedLink?.key
key: sharedLink.key
});
for (const result of results) {
if (!result.success) {
continue;
}
sharedLink.assets = sharedLink.assets.filter((asset) => asset.id !== result.assetId);
}
const count = results.filter((item) => item.success).length;
notificationController.show({
type: NotificationType.Info,
message: `Removed ${count} assets`
});
sharedLink.assets = assetsToKeep;
clearSelect();
} catch (error) {
handleError(error, 'Unable to remove assets from shared link');
}
};
</script>
<CircleIconButton
title="Remove from album"
on:click={handleRemoveAssetsFromSharedLink}
title="Remove from shared link"
on:click={() => (removing = true)}
logo={DeleteOutline}
/>
{#if removing}
<ConfirmDialogue
title="Remove Assets?"
prompt="Are you sure you want to remove {getAssets().size} asset(s) from this shared link?"
confirmText="Remove"
on:confirm={() => handleRemove()}
on:cancel={() => (removing = false)}
/>
{/if}

View file

@ -17,6 +17,7 @@
notificationController,
NotificationType
} from '../shared-components/notification/notification';
import { handleError } from '../../utils/handle-error';
export let sharedLink: SharedLinkResponseDto;
export let isOwned: boolean;
@ -26,43 +27,40 @@
$: assets = sharedLink.assets;
$: isMultiSelectionMode = selectedAssets.size > 0;
const clearMultiSelectAssetAssetHandler = () => {
selectedAssets = new Set();
};
const downloadAssets = async () => {
await bulkDownload('immich-shared', assets, undefined, sharedLink?.key);
await bulkDownload('immich-shared', assets, undefined, sharedLink.key);
};
const handleUploadAssets = async () => {
try {
const results = await openFileUploadDialog(undefined, sharedLink?.key);
const results = await openFileUploadDialog(undefined, sharedLink.key);
const assetIds = results.filter((id) => !!id) as string[];
await api.assetApi.addAssetsToSharedLink({
addAssetsDto: {
assetIds
const { data } = await api.sharedLinkApi.addSharedLinkAssets({
id: sharedLink.id,
assetIdsDto: {
assetIds: results.filter((id) => !!id) as string[]
},
key: sharedLink?.key
key: sharedLink.key
});
const added = data.filter((item) => item.success).length;
notificationController.show({
message: `Successfully add ${assetIds.length} to the shared link`,
message: `Added ${added} assets`,
type: NotificationType.Info
});
} catch (e) {
console.error('handleUploadAssets', e);
handleError(e, 'Unable to add assets to shared link');
}
};
</script>
<section class="bg-immich-bg dark:bg-immich-dark-bg">
{#if isMultiSelectionMode}
<AssetSelectControlBar assets={selectedAssets} clearSelect={clearMultiSelectAssetAssetHandler}>
<AssetSelectControlBar assets={selectedAssets} clearSelect={() => (selectedAssets = new Set())}>
<DownloadAction filename="immich-shared" sharedLinkKey={sharedLink.key} />
{#if isOwned}
<RemoveFromSharedLink bind:sharedLink allAssets={assets} />
<RemoveFromSharedLink bind:sharedLink />
{/if}
</AssetSelectControlBar>
{:else}

View file

@ -7,31 +7,31 @@
import { handleError } from '$lib/utils/handle-error';
import {
AlbumResponseDto,
api,
AssetResponseDto,
SharedLinkResponseDto,
SharedLinkType,
api
SharedLinkType
} from '@api';
import { createEventDispatcher, onMount } from 'svelte';
import Link from 'svelte-material-icons/Link.svelte';
import BaseModal from '../base-modal.svelte';
import type { ImmichDropDownOption } from '../dropdown-button.svelte';
import DropdownButton from '../dropdown-button.svelte';
import { NotificationType, notificationController } from '../notification/notification';
import { notificationController, NotificationType } from '../notification/notification';
export let shareType: SharedLinkType;
export let sharedAssets: AssetResponseDto[] = [];
export let album: AlbumResponseDto | undefined = undefined;
export let editingLink: SharedLinkResponseDto | undefined = undefined;
let isShowSharedLink = false;
let expirationTime = '';
let isAllowUpload = false;
let sharedLink = '';
let sharedLink: string | null = null;
let description = '';
let allowDownload = true;
let allowUpload = false;
let showExif = true;
let expirationTime = '';
let shouldChangeExpirationTime = false;
let isAllowDownload = true;
let shouldShowExif = true;
const dispatch = createEventDispatcher();
const expiredDateOption: ImmichDropDownOption = {
@ -44,9 +44,9 @@
if (editingLink.description) {
description = editingLink.description;
}
isAllowUpload = editingLink.allowUpload;
isAllowDownload = editingLink.allowDownload;
shouldShowExif = editingLink.showExif;
allowUpload = editingLink.allowUpload;
allowDownload = editingLink.allowDownload;
showExif = editingLink.showExif;
}
});
@ -58,49 +58,32 @@
: undefined;
try {
if (shareType === SharedLinkType.Album && album) {
const { data } = await api.albumApi.createAlbumSharedLink({
createAlbumShareLinkDto: {
albumId: album.id,
expiresAt: expirationDate,
allowUpload: isAllowUpload,
description: description,
allowDownload: isAllowDownload,
showExif: shouldShowExif
}
});
buildSharedLink(data);
} else {
const { data } = await api.assetApi.createAssetsSharedLink({
createAssetsShareLinkDto: {
assetIds: sharedAssets.map((a) => a.id),
expiresAt: expirationDate,
allowUpload: isAllowUpload,
description: description,
allowDownload: isAllowDownload,
showExif: shouldShowExif
}
});
buildSharedLink(data);
}
const { data } = await api.sharedLinkApi.createSharedLink({
sharedLinkCreateDto: {
type: shareType,
albumId: album ? album.id : undefined,
assetIds: sharedAssets.map((a) => a.id),
expiresAt: expirationDate,
allowUpload,
description,
allowDownload,
showExif
}
});
sharedLink = `${window.location.origin}/share/${data.key}`;
} catch (e) {
handleError(e, 'Failed to create shared link');
}
isShowSharedLink = true;
};
const buildSharedLink = (createdLink: SharedLinkResponseDto) => {
sharedLink = `${window.location.origin}/share/${createdLink.key}`;
};
const handleCopy = async () => {
if (!sharedLink) {
return;
}
try {
await navigator.clipboard.writeText(sharedLink);
notificationController.show({
message: 'Copied to clipboard!',
type: NotificationType.Info
});
notificationController.show({ message: 'Copied to clipboard!', type: NotificationType.Info });
} catch (e) {
handleError(
e,
@ -129,34 +112,36 @@
};
const handleEditLink = async () => {
if (editingLink) {
try {
const expirationTime = getExpirationTimeInMillisecond();
const currentTime = new Date().getTime();
const expirationDate: string | null = expirationTime
? new Date(currentTime + expirationTime).toISOString()
: null;
if (!editingLink) {
return;
}
await api.shareApi.updateSharedLink({
id: editingLink.id,
editSharedLinkDto: {
description,
expiresAt: shouldChangeExpirationTime ? expirationDate : undefined,
allowUpload: isAllowUpload,
allowDownload: isAllowDownload,
showExif: shouldShowExif
}
});
try {
const expirationTime = getExpirationTimeInMillisecond();
const currentTime = new Date().getTime();
const expirationDate: string | null = expirationTime
? new Date(currentTime + expirationTime).toISOString()
: null;
notificationController.show({
type: NotificationType.Info,
message: 'Edited'
});
await api.sharedLinkApi.updateSharedLink({
id: editingLink.id,
sharedLinkEditDto: {
description,
expiresAt: shouldChangeExpirationTime ? expirationDate : undefined,
allowUpload: allowUpload,
allowDownload: allowDownload,
showExif: showExif
}
});
dispatch('close');
} catch (e) {
handleError(e, 'Failed to edit shared link');
}
notificationController.show({
type: NotificationType.Info,
message: 'Edited'
});
dispatch('close');
} catch (e) {
handleError(e, 'Failed to edit shared link');
}
};
</script>
@ -212,15 +197,15 @@
</div>
<div class="my-3">
<SettingSwitch bind:checked={shouldShowExif} title={'Show metadata'} />
<SettingSwitch bind:checked={showExif} title={'Show metadata'} />
</div>
<div class="my-3">
<SettingSwitch bind:checked={isAllowDownload} title={'Allow public user to download'} />
<SettingSwitch bind:checked={allowDownload} title={'Allow public user to download'} />
</div>
<div class="my-3">
<SettingSwitch bind:checked={isAllowUpload} title={'Allow public user to upload'} />
<SettingSwitch bind:checked={allowUpload} title={'Allow public user to upload'} />
</div>
<div class="text-sm">
@ -248,7 +233,7 @@
<hr />
<section class="m-6">
{#if !isShowSharedLink}
{#if !sharedLink}
{#if editingLink}
<div class="flex justify-end">
<Button size="sm" rounded="lg" on:click={handleEditLink}>Confirm</Button>
@ -258,9 +243,7 @@
<Button size="sm" rounded="lg" on:click={handleCreateSharedLink}>Create link</Button>
</div>
{/if}
{/if}
{#if isShowSharedLink}
{:else}
<div class="flex w-full gap-4">
<input class="immich-form-input w-full" bind:value={sharedLink} disabled />