mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
Merge asset viewer components into asset-grid-asset-viewer
• Rename asset-viewer-actions.svelte to asset-grid-asset-viewer.svelte • Move AssetViewer rendering from asset-viewer-and-actions into asset-grid-asset-viewer • Remove asset-viewer-and-actions.svelte intermediate wrapper • Remove unused isShowDeleteConfirmation prop
This commit is contained in:
parent
14954c669b
commit
76efc48b1b
2 changed files with 191 additions and 12 deletions
|
|
@ -0,0 +1,188 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { Action } from '$lib/components/asset-viewer/actions/action';
|
||||||
|
import { AssetAction } from '$lib/constants';
|
||||||
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
|
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
||||||
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
|
import { updateStackedAssetInTimeline, updateUnstackedAssetInTimeline } from '$lib/utils/actions';
|
||||||
|
import { navigate } from '$lib/utils/navigation';
|
||||||
|
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||||
|
import { getAssetInfo, type AlbumResponseDto, type AssetResponseDto, type PersonResponseDto } from '@immich/sdk';
|
||||||
|
|
||||||
|
let { asset: viewingAsset, gridScrollTarget, mutex, preloadAssets } = assetViewingStore;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
timelineManager: TimelineManager;
|
||||||
|
showSkeleton: boolean;
|
||||||
|
withStacked?: boolean;
|
||||||
|
isShared?: boolean;
|
||||||
|
album?: AlbumResponseDto | null;
|
||||||
|
person?: PersonResponseDto | null;
|
||||||
|
|
||||||
|
removeAction?:
|
||||||
|
| AssetAction.UNARCHIVE
|
||||||
|
| AssetAction.ARCHIVE
|
||||||
|
| AssetAction.FAVORITE
|
||||||
|
| AssetAction.UNFAVORITE
|
||||||
|
| AssetAction.SET_VISIBILITY_TIMELINE;
|
||||||
|
handlePreAction?: (action: Action) => Promise<void>;
|
||||||
|
handleAction?: (action: Action) => void;
|
||||||
|
handleNext?: () => Promise<boolean>;
|
||||||
|
handlePrevious?: () => Promise<boolean>;
|
||||||
|
handleRandom?: () => Promise<AssetResponseDto | undefined>;
|
||||||
|
handleClose?: (asset: { id: string }) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let {
|
||||||
|
timelineManager = $bindable(),
|
||||||
|
showSkeleton = $bindable(false),
|
||||||
|
removeAction,
|
||||||
|
withStacked = false,
|
||||||
|
isShared = false,
|
||||||
|
album = null,
|
||||||
|
person = null,
|
||||||
|
handlePreAction = $bindable(),
|
||||||
|
handleAction = $bindable(),
|
||||||
|
handleNext = $bindable(),
|
||||||
|
handlePrevious = $bindable(),
|
||||||
|
handleRandom = $bindable(),
|
||||||
|
handleClose = $bindable(),
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
|
handlePrevious = async () => {
|
||||||
|
const release = await mutex.acquire();
|
||||||
|
const laterAsset = await timelineManager.getLaterAsset($viewingAsset);
|
||||||
|
|
||||||
|
if (laterAsset) {
|
||||||
|
const preloadAsset = await timelineManager.getLaterAsset(laterAsset);
|
||||||
|
const asset = await getAssetInfo({ ...authManager.params, id: laterAsset.id });
|
||||||
|
assetViewingStore.setAsset(asset, preloadAsset ? [preloadAsset] : []);
|
||||||
|
await navigate({ targetRoute: 'current', assetId: laterAsset.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
release();
|
||||||
|
return !!laterAsset;
|
||||||
|
};
|
||||||
|
|
||||||
|
handleNext = async () => {
|
||||||
|
const release = await mutex.acquire();
|
||||||
|
const earlierAsset = await timelineManager.getEarlierAsset($viewingAsset);
|
||||||
|
|
||||||
|
if (earlierAsset) {
|
||||||
|
const preloadAsset = await timelineManager.getEarlierAsset(earlierAsset);
|
||||||
|
const asset = await getAssetInfo({ ...authManager.params, id: earlierAsset.id });
|
||||||
|
assetViewingStore.setAsset(asset, preloadAsset ? [preloadAsset] : []);
|
||||||
|
await navigate({ targetRoute: 'current', assetId: earlierAsset.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
release();
|
||||||
|
return !!earlierAsset;
|
||||||
|
};
|
||||||
|
|
||||||
|
handleRandom = async () => {
|
||||||
|
const randomAsset = await timelineManager.getRandomAsset();
|
||||||
|
|
||||||
|
if (randomAsset) {
|
||||||
|
const asset = await getAssetInfo({ ...authManager.params, id: randomAsset.id });
|
||||||
|
assetViewingStore.setAsset(asset);
|
||||||
|
await navigate({ targetRoute: 'current', assetId: randomAsset.id });
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleClose = async (asset: { id: string }) => {
|
||||||
|
assetViewingStore.showAssetViewer(false);
|
||||||
|
showSkeleton = true;
|
||||||
|
$gridScrollTarget = { at: asset.id };
|
||||||
|
await navigate({ targetRoute: 'current', assetId: null, assetGridRouteSearchParams: $gridScrollTarget });
|
||||||
|
};
|
||||||
|
|
||||||
|
handlePreAction = async (action: Action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case removeAction:
|
||||||
|
case AssetAction.TRASH:
|
||||||
|
case AssetAction.RESTORE:
|
||||||
|
case AssetAction.DELETE:
|
||||||
|
case AssetAction.ARCHIVE:
|
||||||
|
case AssetAction.SET_VISIBILITY_LOCKED:
|
||||||
|
case AssetAction.SET_VISIBILITY_TIMELINE: {
|
||||||
|
// find the next asset to show or close the viewer
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
|
(await handleNext()) || (await handlePrevious()) || (await handleClose(action.asset));
|
||||||
|
|
||||||
|
// delete after find the next one
|
||||||
|
timelineManager.removeAssets([action.asset.id]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
handleAction = (action: Action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case AssetAction.ARCHIVE:
|
||||||
|
case AssetAction.UNARCHIVE:
|
||||||
|
case AssetAction.FAVORITE:
|
||||||
|
case AssetAction.UNFAVORITE: {
|
||||||
|
timelineManager.updateAssets([action.asset]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AssetAction.ADD: {
|
||||||
|
timelineManager.addAssets([action.asset]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AssetAction.UNSTACK: {
|
||||||
|
updateUnstackedAssetInTimeline(timelineManager, action.assets);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AssetAction.REMOVE_ASSET_FROM_STACK: {
|
||||||
|
timelineManager.addAssets([toTimelineAsset(action.asset)]);
|
||||||
|
if (action.stack) {
|
||||||
|
//Have to unstack then restack assets in timeline in order to update the stack count in the timeline.
|
||||||
|
updateUnstackedAssetInTimeline(
|
||||||
|
timelineManager,
|
||||||
|
action.stack.assets.map((asset) => toTimelineAsset(asset)),
|
||||||
|
);
|
||||||
|
updateStackedAssetInTimeline(timelineManager, {
|
||||||
|
stack: action.stack,
|
||||||
|
toDeleteIds: action.stack.assets
|
||||||
|
.filter((asset) => asset.id !== action.stack?.primaryAssetId)
|
||||||
|
.map((asset) => asset.id),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AssetAction.SET_STACK_PRIMARY_ASSET: {
|
||||||
|
//Have to unstack then restack assets in timeline in order for the currently removed new primary asset to be made visible.
|
||||||
|
updateUnstackedAssetInTimeline(
|
||||||
|
timelineManager,
|
||||||
|
action.stack.assets.map((asset) => toTimelineAsset(asset)),
|
||||||
|
);
|
||||||
|
updateStackedAssetInTimeline(timelineManager, {
|
||||||
|
stack: action.stack,
|
||||||
|
toDeleteIds: action.stack.assets
|
||||||
|
.filter((asset) => asset.id !== action.stack.primaryAssetId)
|
||||||
|
.map((asset) => asset.id),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await import('../asset-viewer/asset-viewer.svelte') then { default: AssetViewer }}
|
||||||
|
<AssetViewer
|
||||||
|
{withStacked}
|
||||||
|
asset={$viewingAsset}
|
||||||
|
preloadAssets={$preloadAssets}
|
||||||
|
{isShared}
|
||||||
|
{album}
|
||||||
|
{person}
|
||||||
|
preAction={handlePreAction}
|
||||||
|
onAction={handleAction}
|
||||||
|
onPrevious={handlePrevious}
|
||||||
|
onNext={handleNext}
|
||||||
|
onRandom={handleRandom}
|
||||||
|
onClose={handleClose}
|
||||||
|
/>
|
||||||
|
{/await}
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
import { resizeObserver, type OnResizeCallback } from '$lib/actions/resize-observer';
|
import { resizeObserver, type OnResizeCallback } from '$lib/actions/resize-observer';
|
||||||
import AssetDateGroupSelectionAware from '$lib/components/photos-page/asset-date-group-selection-aware.svelte';
|
import AssetDateGroupSelectionAware from '$lib/components/photos-page/asset-date-group-selection-aware.svelte';
|
||||||
import AssetGridActions from '$lib/components/photos-page/asset-grid-actions.svelte';
|
import AssetGridActions from '$lib/components/photos-page/asset-grid-actions.svelte';
|
||||||
import AssetViewerAndActions from '$lib/components/photos-page/asset-viewer-and-actions.svelte';
|
import AssetGridAssetViewer from '$lib/components/photos-page/asset-grid-asset-viewer.svelte';
|
||||||
import Skeleton from '$lib/components/photos-page/skeleton.svelte';
|
import Skeleton from '$lib/components/photos-page/skeleton.svelte';
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
|
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
|
||||||
|
|
@ -93,10 +93,6 @@
|
||||||
|
|
||||||
let scrubberWidth = $state(0);
|
let scrubberWidth = $state(0);
|
||||||
|
|
||||||
// 60 is the bottom spacer element at 60px
|
|
||||||
let bottomSectionHeight = 60;
|
|
||||||
let leadout = $state(false);
|
|
||||||
|
|
||||||
const maxMd = $derived(mobileDevice.maxMd);
|
const maxMd = $derived(mobileDevice.maxMd);
|
||||||
const usingMobileDevice = $derived(mobileDevice.pointerCoarse);
|
const usingMobileDevice = $derived(mobileDevice.pointerCoarse);
|
||||||
const isEmpty = $derived(timelineManager.isInitialized && timelineManager.months.length === 0);
|
const isEmpty = $derived(timelineManager.isInitialized && timelineManager.months.length === 0);
|
||||||
|
|
@ -273,10 +269,6 @@
|
||||||
disposeHmr();
|
disposeHmr();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
let onDateGroupSelect = <({ title, assets }: { title: string; assets: TimelineAsset[] }) => void>$state();
|
|
||||||
let onSelectAssets = <(asset: TimelineAsset) => Promise<void>>$state();
|
|
||||||
let onSelectAssetCandidates = <(asset: TimelineAsset | null) => void>$state();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AssetGridActions {scrollToAsset} {timelineManager} {assetInteraction} bind:isShowDeleteConfirmation {onEscape}
|
<AssetGridActions {scrollToAsset} {timelineManager} {assetInteraction} bind:isShowDeleteConfirmation {onEscape}
|
||||||
|
|
@ -369,7 +361,7 @@
|
||||||
|
|
||||||
<Portal target="body">
|
<Portal target="body">
|
||||||
{#if $showAssetViewer}
|
{#if $showAssetViewer}
|
||||||
<AssetViewerAndActions
|
<AssetGridAssetViewer
|
||||||
bind:showSkeleton
|
bind:showSkeleton
|
||||||
{timelineManager}
|
{timelineManager}
|
||||||
{removeAction}
|
{removeAction}
|
||||||
|
|
@ -377,8 +369,7 @@
|
||||||
{isShared}
|
{isShared}
|
||||||
{album}
|
{album}
|
||||||
{person}
|
{person}
|
||||||
{isShowDeleteConfirmation}
|
/>
|
||||||
></AssetViewerAndActions>
|
|
||||||
{/if}
|
{/if}
|
||||||
</Portal>
|
</Portal>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue