|
@@ -2,7 +2,7 @@
|
|
import { afterNavigate, goto } from '$app/navigation';
|
|
import { afterNavigate, goto } from '$app/navigation';
|
|
import { page } from '$app/stores';
|
|
import { page } from '$app/stores';
|
|
import { AlbumResponseDto, api, AssetResponseDto, ThumbnailFormat, UserResponseDto } from '@api';
|
|
import { AlbumResponseDto, api, AssetResponseDto, ThumbnailFormat, UserResponseDto } from '@api';
|
|
- import { createEventDispatcher, onMount } from 'svelte';
|
|
|
|
|
|
+ import { onMount } from 'svelte';
|
|
import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
|
|
import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
|
|
import Plus from 'svelte-material-icons/Plus.svelte';
|
|
import Plus from 'svelte-material-icons/Plus.svelte';
|
|
import FileImagePlusOutline from 'svelte-material-icons/FileImagePlusOutline.svelte';
|
|
import FileImagePlusOutline from 'svelte-material-icons/FileImagePlusOutline.svelte';
|
|
@@ -16,8 +16,8 @@
|
|
import UserSelectionModal from './user-selection-modal.svelte';
|
|
import UserSelectionModal from './user-selection-modal.svelte';
|
|
import ShareInfoModal from './share-info-modal.svelte';
|
|
import ShareInfoModal from './share-info-modal.svelte';
|
|
import CircleIconButton from '../shared-components/circle-icon-button.svelte';
|
|
import CircleIconButton from '../shared-components/circle-icon-button.svelte';
|
|
-
|
|
|
|
- const dispatch = createEventDispatcher();
|
|
|
|
|
|
+ import Close from 'svelte-material-icons/Close.svelte';
|
|
|
|
+ import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
|
|
export let album: AlbumResponseDto;
|
|
export let album: AlbumResponseDto;
|
|
|
|
|
|
let isShowAssetViewer = false;
|
|
let isShowAssetViewer = false;
|
|
@@ -39,6 +39,9 @@
|
|
|
|
|
|
$: isOwned = currentUser?.id == album.ownerId;
|
|
$: isOwned = currentUser?.id == album.ownerId;
|
|
|
|
|
|
|
|
+ let multiSelectAsset: Set<AssetResponseDto> = new Set();
|
|
|
|
+ $: isMultiSelectionMode = multiSelectAsset.size > 0;
|
|
|
|
+
|
|
afterNavigate(({ from }) => {
|
|
afterNavigate(({ from }) => {
|
|
backUrl = from?.pathname ?? '/albums';
|
|
backUrl = from?.pathname ?? '/albums';
|
|
|
|
|
|
@@ -81,15 +84,46 @@
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
- const viewAsset = (event: CustomEvent) => {
|
|
|
|
- const { assetId, deviceId }: { assetId: string; deviceId: string } = event.detail;
|
|
|
|
|
|
+ const viewAssetHandler = (event: CustomEvent) => {
|
|
|
|
+ const { asset }: { asset: AssetResponseDto } = event.detail;
|
|
|
|
|
|
- currentViewAssetIndex = album.assets.findIndex((a) => a.id == assetId);
|
|
|
|
|
|
+ currentViewAssetIndex = album.assets.findIndex((a) => a.id == asset.id);
|
|
selectedAsset = album.assets[currentViewAssetIndex];
|
|
selectedAsset = album.assets[currentViewAssetIndex];
|
|
isShowAssetViewer = true;
|
|
isShowAssetViewer = true;
|
|
pushState(selectedAsset.id);
|
|
pushState(selectedAsset.id);
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ const selectAssetHandler = (event: CustomEvent) => {
|
|
|
|
+ const { asset }: { asset: AssetResponseDto } = event.detail;
|
|
|
|
+ let temp = new Set(multiSelectAsset);
|
|
|
|
+
|
|
|
|
+ if (multiSelectAsset.has(asset)) {
|
|
|
|
+ temp.delete(asset);
|
|
|
|
+ } else {
|
|
|
|
+ temp.add(asset);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ multiSelectAsset = temp;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const clearMultiSelectAssetAssetHandler = () => {
|
|
|
|
+ multiSelectAsset = new Set();
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const removeSelectedAssetFromAlbum = async () => {
|
|
|
|
+ if (window.confirm('Do you want to remove selected assets from the album?')) {
|
|
|
|
+ try {
|
|
|
|
+ const { data } = await api.albumApi.removeAssetFromAlbum(album.id, {
|
|
|
|
+ assetIds: Array.from(multiSelectAsset).map((a) => a.id)
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ album = data;
|
|
|
|
+ multiSelectAsset = new Set();
|
|
|
|
+ } catch (e) {
|
|
|
|
+ console.log('Error [album-viewer] [removeAssetFromAlbum]', e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ };
|
|
const navigateAssetForward = () => {
|
|
const navigateAssetForward = () => {
|
|
try {
|
|
try {
|
|
if (currentViewAssetIndex < album.assets.length - 1) {
|
|
if (currentViewAssetIndex < album.assets.length - 1) {
|
|
@@ -191,32 +225,60 @@
|
|
</script>
|
|
</script>
|
|
|
|
|
|
<section class="bg-immich-bg">
|
|
<section class="bg-immich-bg">
|
|
- <AlbumAppBar on:close-button-click={() => goto(backUrl)} backIcon={ArrowLeft}>
|
|
|
|
- <svelte:fragment slot="trailing">
|
|
|
|
- {#if album.assets.length > 0}
|
|
|
|
- <CircleIconButton
|
|
|
|
- title="Add Photos"
|
|
|
|
- on:click={() => (isShowAssetSelection = true)}
|
|
|
|
- logo={FileImagePlusOutline}
|
|
|
|
- />
|
|
|
|
-
|
|
|
|
- <CircleIconButton
|
|
|
|
- title="Share"
|
|
|
|
- on:click={() => (isShowShareUserSelection = true)}
|
|
|
|
- logo={ShareVariantOutline}
|
|
|
|
- />
|
|
|
|
- {/if}
|
|
|
|
|
|
+ <!-- Multiselection mode app bar -->
|
|
|
|
+ {#if isMultiSelectionMode}
|
|
|
|
+ <AlbumAppBar
|
|
|
|
+ on:close-button-click={clearMultiSelectAssetAssetHandler}
|
|
|
|
+ backIcon={Close}
|
|
|
|
+ tailwindClasses={'bg-white shadow-md'}
|
|
|
|
+ >
|
|
|
|
+ <svelte:fragment slot="leading">
|
|
|
|
+ <p class="font-medium text-immich-primary">Selected {multiSelectAsset.size}</p>
|
|
|
|
+ </svelte:fragment>
|
|
|
|
+ <svelte:fragment slot="trailing">
|
|
|
|
+ {#if isOwned}
|
|
|
|
+ <CircleIconButton
|
|
|
|
+ title="Remove from album"
|
|
|
|
+ on:click={removeSelectedAssetFromAlbum}
|
|
|
|
+ logo={DeleteOutline}
|
|
|
|
+ />
|
|
|
|
+ {/if}
|
|
|
|
+ </svelte:fragment>
|
|
|
|
+ </AlbumAppBar>
|
|
|
|
+ {/if}
|
|
|
|
+
|
|
|
|
+ <!-- Default app bar -->
|
|
|
|
+ {#if !isMultiSelectionMode}
|
|
|
|
+ <AlbumAppBar on:close-button-click={() => goto(backUrl)} backIcon={ArrowLeft}>
|
|
|
|
+ <svelte:fragment slot="trailing">
|
|
|
|
+ {#if album.assets.length > 0}
|
|
|
|
+ <CircleIconButton
|
|
|
|
+ title="Add Photos"
|
|
|
|
+ on:click={() => (isShowAssetSelection = true)}
|
|
|
|
+ logo={FileImagePlusOutline}
|
|
|
|
+ />
|
|
|
|
+
|
|
|
|
+ <!-- Sharing only for owner -->
|
|
|
|
+ {#if isOwned}
|
|
|
|
+ <CircleIconButton
|
|
|
|
+ title="Share"
|
|
|
|
+ on:click={() => (isShowShareUserSelection = true)}
|
|
|
|
+ logo={ShareVariantOutline}
|
|
|
|
+ />
|
|
|
|
+ {/if}
|
|
|
|
+ {/if}
|
|
|
|
|
|
- {#if isCreatingSharedAlbum && album.sharedUsers.length == 0}
|
|
|
|
- <button
|
|
|
|
- disabled={album.assets.length == 0}
|
|
|
|
- on:click={() => (isShowShareUserSelection = true)}
|
|
|
|
- class="immich-text-button border bg-immich-primary text-gray-50 hover:bg-immich-primary/75 px-6 text-sm disabled:opacity-25 disabled:bg-gray-500 disabled:cursor-not-allowed"
|
|
|
|
- ><span class="px-2">Share</span></button
|
|
|
|
- >
|
|
|
|
- {/if}
|
|
|
|
- </svelte:fragment>
|
|
|
|
- </AlbumAppBar>
|
|
|
|
|
|
+ {#if isCreatingSharedAlbum && album.sharedUsers.length == 0}
|
|
|
|
+ <button
|
|
|
|
+ disabled={album.assets.length == 0}
|
|
|
|
+ on:click={() => (isShowShareUserSelection = true)}
|
|
|
|
+ class="immich-text-button border bg-immich-primary text-gray-50 hover:bg-immich-primary/75 px-6 text-sm disabled:opacity-25 disabled:bg-gray-500 disabled:cursor-not-allowed"
|
|
|
|
+ ><span class="px-2">Share</span></button
|
|
|
|
+ >
|
|
|
|
+ {/if}
|
|
|
|
+ </svelte:fragment>
|
|
|
|
+ </AlbumAppBar>
|
|
|
|
+ {/if}
|
|
|
|
|
|
<section class="m-auto my-[160px] w-[60%]">
|
|
<section class="m-auto my-[160px] w-[60%]">
|
|
<input
|
|
<input
|
|
@@ -237,9 +299,11 @@
|
|
{#if album.shared}
|
|
{#if album.shared}
|
|
<div class="my-6 flex">
|
|
<div class="my-6 flex">
|
|
{#each album.sharedUsers as user}
|
|
{#each album.sharedUsers as user}
|
|
- <span class="mr-1">
|
|
|
|
- <CircleAvatar {user} on:click={() => (isShowShareInfoModal = true)} />
|
|
|
|
- </span>
|
|
|
|
|
|
+ {#key user.id}
|
|
|
|
+ <span class="mr-1">
|
|
|
|
+ <CircleAvatar {user} on:click={() => (isShowShareInfoModal = true)} />
|
|
|
|
+ </span>
|
|
|
|
+ {/key}
|
|
{/each}
|
|
{/each}
|
|
|
|
|
|
<button
|
|
<button
|
|
@@ -255,16 +319,28 @@
|
|
{#if album.assets.length > 0}
|
|
{#if album.assets.length > 0}
|
|
<div class="flex flex-wrap gap-1 w-full pb-20" bind:clientWidth={viewWidth}>
|
|
<div class="flex flex-wrap gap-1 w-full pb-20" bind:clientWidth={viewWidth}>
|
|
{#each album.assets as asset}
|
|
{#each album.assets as asset}
|
|
- {#if album.assets.length < 7}
|
|
|
|
- <ImmichThumbnail
|
|
|
|
- {asset}
|
|
|
|
- {thumbnailSize}
|
|
|
|
- format={ThumbnailFormat.Jpeg}
|
|
|
|
- on:click={viewAsset}
|
|
|
|
- />
|
|
|
|
- {:else}
|
|
|
|
- <ImmichThumbnail {asset} {thumbnailSize} on:click={viewAsset} />
|
|
|
|
- {/if}
|
|
|
|
|
|
+ {#key asset.id}
|
|
|
|
+ {#if album.assets.length < 7}
|
|
|
|
+ <ImmichThumbnail
|
|
|
|
+ {asset}
|
|
|
|
+ {thumbnailSize}
|
|
|
|
+ format={ThumbnailFormat.Jpeg}
|
|
|
|
+ on:click={(e) =>
|
|
|
|
+ isMultiSelectionMode ? selectAssetHandler(e) : viewAssetHandler(e)}
|
|
|
|
+ on:select={selectAssetHandler}
|
|
|
|
+ selected={multiSelectAsset.has(asset)}
|
|
|
|
+ />
|
|
|
|
+ {:else}
|
|
|
|
+ <ImmichThumbnail
|
|
|
|
+ {asset}
|
|
|
|
+ {thumbnailSize}
|
|
|
|
+ on:click={(e) =>
|
|
|
|
+ isMultiSelectionMode ? selectAssetHandler(e) : viewAssetHandler(e)}
|
|
|
|
+ on:select={selectAssetHandler}
|
|
|
|
+ selected={multiSelectAsset.has(asset)}
|
|
|
|
+ />
|
|
|
|
+ {/if}
|
|
|
|
+ {/key}
|
|
{/each}
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
{:else}
|
|
{:else}
|