1
0
mirror of https://github.com/immich-app/immich.git synced 2024-11-28 09:33:27 +02:00

refactor: auto advance asset viewer (#3554)

This commit is contained in:
Jason Rasmussen 2023-08-04 23:26:28 -04:00 committed by GitHub
parent c6abef186c
commit 68b5202730
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 77 additions and 78 deletions

View File

@ -449,7 +449,7 @@
{/if}
{#if album.assetCount > 0}
<GalleryViewer assets={album.assets} {sharedLink} bind:selectedAssets={multiSelectAsset} viewFrom="album-page" />
<GalleryViewer assets={album.assets} {sharedLink} bind:selectedAssets={multiSelectAsset} />
{:else}
<!-- Album is empty - Show asset selectection buttons -->
<section id="empty-album" class=" mt-[200px] flex place-content-center place-items-center">

View File

@ -30,7 +30,16 @@
export let showNavigation = true;
export let sharedLink: SharedLinkResponseDto | undefined = undefined;
const dispatch = createEventDispatcher();
const dispatch = createEventDispatcher<{
archived: AssetResponseDto;
unarchived: AssetResponseDto;
favorite: AssetResponseDto;
unfavorite: AssetResponseDto;
close: void;
next: void;
previous: void;
}>();
let appearsInAlbums: AlbumResponseDto[] = [];
let isShowAlbumPicker = false;
let isShowDeleteConfirmation = false;
@ -105,18 +114,16 @@
closeViewer();
};
const closeViewer = () => {
dispatch('close');
};
const closeViewer = () => dispatch('close');
const navigateAssetForward = (e?: Event) => {
e?.stopPropagation();
dispatch('navigate-next');
dispatch('next');
};
const navigateAssetBackward = (e?: Event) => {
e?.stopPropagation();
dispatch('navigate-previous');
dispatch('previous');
};
const showDetailInfoHandler = () => {
@ -159,7 +166,8 @@
});
asset.isFavorite = data.isFavorite;
assetStore?.updateAsset(asset.id, data.isFavorite);
assetStore?.updateAsset(data);
dispatch(data.isFavorite ? 'favorite' : 'unfavorite', data);
notificationController.show({
type: NotificationType.Info,
@ -215,12 +223,8 @@
});
asset.isArchived = data.isArchived;
if (data.isArchived) {
dispatch('archived', data);
} else {
dispatch('unarchived', data);
}
assetStore?.updateAsset(data);
dispatch(data.isArchived ? 'archived' : 'unarchived', data);
notificationController.show({
type: NotificationType.Info,

View File

@ -268,7 +268,7 @@
bottom={-200}
>
<div id="gallery-memory" bind:this={memoryGallery}>
<GalleryViewer assets={currentMemory.assets} viewFrom="album-page" />
<GalleryViewer assets={currentMemory.assets} />
</div>
</IntersectionObserver>
</section>

View File

@ -13,7 +13,7 @@
import { browser } from '$app/environment';
import { goto } from '$app/navigation';
import { AppRoute } from '$lib/constants';
import { AppRoute, AssetAction } from '$lib/constants';
import type { AssetInteractionStore } from '$lib/stores/asset-interaction.store';
import { BucketPosition, type AssetStore, type Viewport } from '$lib/stores/assets.store';
import { isSearchEnabled } from '$lib/stores/search.store';
@ -22,6 +22,7 @@
export let isAlbumSelectionMode = false;
export let assetStore: AssetStore;
export let assetInteractionStore: AssetInteractionStore;
export let removeAction: AssetAction | null = null;
const { assetSelectionCandidates, assetSelectionStart, selectedAssets, isMultiSelectState } = assetInteractionStore;
const viewport: Viewport = { width: 0, height: 0 };
@ -81,18 +82,34 @@
element.scrollBy(0, event.detail.heightDelta);
}
const navigateToPreviousAsset = async () => {
const prevAsset = await assetStore.getPreviousAssetId($viewingAsset.id);
if (prevAsset) {
assetViewingStore.setAssetId(prevAsset);
const handlePrevious = async () => {
const previousAsset = await assetStore.getPreviousAssetId($viewingAsset.id);
if (previousAsset) {
assetViewingStore.setAssetId(previousAsset);
}
return !!previousAsset;
};
const navigateToNextAsset = async () => {
const handleNext = async () => {
const nextAsset = await assetStore.getNextAssetId($viewingAsset.id);
if (nextAsset) {
assetViewingStore.setAssetId(nextAsset);
}
return !!nextAsset;
};
const handleClose = () => assetViewingStore.showAssetViewer(false);
const handleAction = async (asset: AssetResponseDto, action: AssetAction) => {
if (removeAction === action) {
// find the next asset to show or close the viewer
(await handleNext()) || (await handlePrevious()) || handleClose();
// delete after find the next one
assetStore.removeAsset(asset.id);
}
};
let animationTick = false;
@ -109,12 +126,6 @@
});
};
const handleArchiveSuccess = (e: CustomEvent) => {
const asset = e.detail as AssetResponseDto;
navigateToNextAsset();
assetStore.removeAsset(asset.id);
};
let lastAssetMouseEvent: AssetResponseDto | null = null;
$: if (!lastAssetMouseEvent) {
@ -319,12 +330,13 @@
<AssetViewer
{assetStore}
asset={$viewingAsset}
on:navigate-previous={navigateToPreviousAsset}
on:navigate-next={navigateToNextAsset}
on:close={() => {
assetViewingStore.showAssetViewer(false);
}}
on:archived={handleArchiveSuccess}
on:previous={() => handlePrevious()}
on:next={() => handleNext()}
on:close={() => handleClose()}
on:archived={({ detail: asset }) => handleAction(asset, AssetAction.ARCHIVE)}
on:unarchived={({ detail: asset }) => handleAction(asset, AssetAction.UNARCHIVE)}
on:favorite={({ detail: asset }) => handleAction(asset, AssetAction.FAVORITE)}
on:unfavorite={({ detail: asset }) => handleAction(asset, AssetAction.UNFAVORITE)}
/>
{/if}
</Portal>

View File

@ -106,6 +106,6 @@
</ControlAppBar>
{/if}
<section class="my-[160px] flex flex-col px-6 sm:px-12 md:px-24 lg:px-40">
<GalleryViewer {assets} {sharedLink} bind:selectedAssets viewFrom="shared-link-page" />
<GalleryViewer {assets} {sharedLink} bind:selectedAssets />
</section>
</section>

View File

@ -1,7 +1,3 @@
<script lang="ts" context="module">
export type ViewFrom = 'archive-page' | 'album-page' | 'favorites-page' | 'search-page' | 'shared-link-page';
</script>
<script lang="ts">
import { page } from '$app/stores';
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
@ -9,7 +5,6 @@
import { AssetResponseDto, SharedLinkResponseDto, ThumbnailFormat } from '@api';
import AssetViewer from '../../asset-viewer/asset-viewer.svelte';
import { flip } from 'svelte/animate';
import { archivedAsset } from '$lib/stores/archived-asset.store';
import { getThumbnailSize } from '$lib/utils/thumbnail-util';
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
@ -17,7 +12,6 @@
export let sharedLink: SharedLinkResponseDto | undefined = undefined;
export let selectedAssets: Set<AssetResponseDto> = new Set();
export let disableAssetSelect = false;
export let viewFrom: ViewFrom;
export let showArchiveIcon = false;
let { isViewing: showAssetViewer } = assetViewingStore;
@ -86,16 +80,6 @@
$showAssetViewer = false;
history.pushState(null, '', `${$page.url.pathname}`);
};
const handleUnarchivedSuccess = (event: CustomEvent) => {
const asset = event.detail as AssetResponseDto;
switch (viewFrom) {
case 'archive-page':
$archivedAsset = $archivedAsset.filter((a) => a.id != asset.id);
navigateAssetForward();
break;
}
};
</script>
{#if assets.length > 0}
@ -124,9 +108,8 @@
asset={selectedAsset}
publicSharedKey={sharedLink?.key}
{sharedLink}
on:navigate-previous={navigateAssetBackward}
on:navigate-next={navigateAssetForward}
on:previous={navigateAssetBackward}
on:next={navigateAssetForward}
on:close={closeViewer}
on:unarchived={handleUnarchivedSuccess}
/>
{/if}

View File

@ -1,6 +1,13 @@
import { env } from '$env/dynamic/public';
export const loginPageMessage: string | undefined = env.PUBLIC_LOGIN_PAGE_MESSAGE;
export enum AssetAction {
ARCHIVE = 'archive',
UNARCHIVE = 'unarchive',
FAVORITE = 'favorite',
UNFAVORITE = 'unfavorite',
}
export enum AppRoute {
ADMIN_USER_MANAGEMENT = '/admin/user-management',
ADMIN_SETTINGS = '/admin/system-settings',

View File

@ -1,4 +0,0 @@
import type { AssetResponseDto } from '@api';
import { writable } from 'svelte/store';
export const archivedAsset = writable<AssetResponseDto[]>([]);

View File

@ -146,13 +146,14 @@ export class AssetStore {
return this.assetToBucket[assetId]?.bucketIndex ?? null;
}
updateAsset(assetId: string, isFavorite: boolean) {
const asset = this.assets.find((asset) => asset.id === assetId);
updateAsset(_asset: AssetResponseDto) {
const asset = this.assets.find((asset) => asset.id === _asset.id);
if (!asset) {
return;
}
asset.isFavorite = isFavorite;
Object.assign(asset, _asset);
this.emit(false);
}

View File

@ -11,6 +11,7 @@
import AssetSelectContextMenu from '$lib/components/photos-page/asset-select-context-menu.svelte';
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
import { AssetAction } from '$lib/constants';
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
import { AssetStore } from '$lib/stores/assets.store';
import { api, TimeBucketSize } from '@api';
@ -53,7 +54,7 @@
<UserPageLayout user={data.user} hideNavbar={$isMultiSelectState} title={data.meta.title} scrollbar={!assetCount}>
{#if assetCount}
<AssetGrid {assetStore} {assetInteractionStore} />
<AssetGrid {assetStore} {assetInteractionStore} removeAction={AssetAction.UNARCHIVE} />
{:else}
<EmptyPlaceholder text="Archive photos and videos to hide them from your Photos view" alt="Empty archive" />
{/if}

View File

@ -11,6 +11,7 @@
import AssetSelectContextMenu from '$lib/components/photos-page/asset-select-context-menu.svelte';
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
import { AssetAction } from '$lib/constants';
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
import { AssetStore } from '$lib/stores/assets.store';
import { api, TimeBucketSize } from '@api';
@ -54,7 +55,7 @@
<UserPageLayout user={data.user} hideNavbar={$isMultiSelectState} title={data.meta.title} scrollbar={!assetCount}>
{#if assetCount}
<AssetGrid {assetStore} {assetInteractionStore} />
<AssetGrid {assetStore} {assetInteractionStore} removeAction={AssetAction.UNFAVORITE} />
{:else}
<EmptyPlaceholder text="Add favorites to quickly find your best pictures and videos" alt="Empty favorites" />
{/if}

View File

@ -143,11 +143,9 @@
<AssetViewer
asset={$viewingAsset}
showNavigation={viewingAssets.length > 1}
on:navigate-next={navigateNext}
on:navigate-previous={navigatePrevious}
on:close={() => {
assetViewingStore.showAssetViewer(false);
}}
on:next={navigateNext}
on:previous={navigatePrevious}
on:close={() => assetViewingStore.showAssetViewer(false)}
/>
{/if}
</Portal>

View File

@ -242,7 +242,7 @@
<section class="relative mb-12 bg-immich-bg pt-8 dark:bg-immich-dark-bg sm:px-4">
<section class="immich-scrollbar relative overflow-y-scroll">
<section id="search-content" class="relative bg-immich-bg dark:bg-immich-dark-bg">
<GalleryViewer assets={data.assets} viewFrom="search-page" showArchiveIcon={true} bind:selectedAssets />
<GalleryViewer assets={data.assets} showArchiveIcon={true} bind:selectedAssets />
</section>
</section>
</section>

View File

@ -12,6 +12,7 @@
import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
import MemoryLane from '$lib/components/photos-page/memory-lane.svelte';
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
import { AssetAction } from '$lib/constants';
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
import { AssetStore } from '$lib/stores/assets.store';
import { openFileUploadDialog } from '$lib/utils/file-uploader';
@ -31,7 +32,7 @@
$: isAllFavorite = Array.from($selectedAssets).every((asset) => asset.isFavorite);
onMount(async () => {
const { data: stats } = await api.assetApi.getAssetStats();
const { data: stats } = await api.assetApi.getAssetStats({ isArchived: false });
assetCount = stats.total;
});
</script>
@ -57,7 +58,7 @@
</svelte:fragment>
<svelte:fragment slot="content">
{#if assetCount}
<AssetGrid {assetStore} {assetInteractionStore}>
<AssetGrid {assetStore} {assetInteractionStore} removeAction={AssetAction.ARCHIVE}>
<MemoryLane />
</AssetGrid>
{:else}

View File

@ -138,12 +138,7 @@
<section id="search-content" class="relative bg-immich-bg dark:bg-immich-dark-bg">
{#if data.results?.assets?.items.length > 0}
<div class="pl-4">
<GalleryViewer
assets={searchResultAssets}
bind:selectedAssets
viewFrom="search-page"
showArchiveIcon={true}
/>
<GalleryViewer assets={searchResultAssets} bind:selectedAssets showArchiveIcon={true} />
</div>
{:else}
<div class="flex min-h-[calc(66vh_-_11rem)] w-full place-content-center items-center dark:text-white">

View File

@ -9,9 +9,9 @@
<AssetViewer
asset={data.asset}
publicSharedKey={data.key}
on:navigate-previous={() => null}
on:navigate-next={() => null}
showNavigation={false}
on:previous={() => null}
on:next={() => null}
on:close={() => goto(`/share/${data.key}`)}
/>
{/if}