Skip to content

Commit 5ea66e8

Browse files
committed
refactor: asset-viewer/actions
1 parent db68d1a commit 5ea66e8

File tree

3 files changed

+231
-124
lines changed

3 files changed

+231
-124
lines changed

web/src/lib/components/photos-page/asset-grid.svelte

Lines changed: 18 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,16 @@
33
import { page } from '$app/stores';
44
import { resizeObserver, type OnResizeCallback } from '$lib/actions/resize-observer';
55
import { shortcuts, type ShortcutOptions } from '$lib/actions/shortcut';
6-
import type { Action } from '$lib/components/asset-viewer/actions/action';
76
import {
87
setFocusToAsset as setFocusAssetInit,
98
setFocusTo as setFocusToInit,
109
} from '$lib/components/photos-page/actions/focus-actions';
10+
import AssetViewerAndActions from '$lib/components/photos-page/asset-viewer-and-actions.svelte';
1111
import Skeleton from '$lib/components/photos-page/skeleton.svelte';
1212
import ChangeDate from '$lib/components/shared-components/change-date.svelte';
1313
import Scrubber from '$lib/components/shared-components/scrubber/scrubber.svelte';
1414
import { AppRoute, AssetAction } from '$lib/constants';
1515
import { albumMapViewManager } from '$lib/managers/album-view-map.manager.svelte';
16-
import { authManager } from '$lib/managers/auth-manager.svelte';
1716
import { modalManager } from '$lib/managers/modal-manager.svelte';
1817
import type { MonthGroup } from '$lib/managers/timeline-manager/month-group.svelte';
1918
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
@@ -28,11 +27,11 @@
2827
import { searchStore } from '$lib/stores/search.svelte';
2928
import { featureFlags } from '$lib/stores/server-config.store';
3029
import { handlePromiseError } from '$lib/utils';
31-
import { deleteAssets, updateStackedAssetInTimeline, updateUnstackedAssetInTimeline } from '$lib/utils/actions';
30+
import { deleteAssets, updateStackedAssetInTimeline } from '$lib/utils/actions';
3231
import { archiveAssets, cancelMultiselect, selectAllAssets, stackAssets } from '$lib/utils/asset-utils';
3332
import { navigate } from '$lib/utils/navigation';
34-
import { toTimelineAsset, type ScrubberListener, type TimelinePlainYearMonth } from '$lib/utils/timeline-util';
35-
import { AssetVisibility, getAssetInfo, type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
33+
import { type ScrubberListener, type TimelinePlainYearMonth } from '$lib/utils/timeline-util';
34+
import { AssetVisibility, type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
3635
import { DateTime } from 'luxon';
3736
import { onMount, type Snippet } from 'svelte';
3837
import type { UpdatePayload } from 'vite';
@@ -54,8 +53,7 @@
5453
| AssetAction.ARCHIVE
5554
| AssetAction.FAVORITE
5655
| AssetAction.UNFAVORITE
57-
| AssetAction.SET_VISIBILITY_TIMELINE
58-
| null;
56+
| AssetAction.SET_VISIBILITY_TIMELINE;
5957
withStacked?: boolean;
6058
showArchiveIcon?: boolean;
6159
isShared?: boolean;
@@ -74,7 +72,7 @@
7472
enableRouting,
7573
timelineManager = $bindable(),
7674
assetInteraction,
77-
removeAction = null,
75+
removeAction,
7876
withStacked = false,
7977
showArchiveIcon = false,
8078
isShared = false,
@@ -87,7 +85,7 @@
8785
empty,
8886
}: Props = $props();
8987
90-
let { isViewing: showAssetViewer, asset: viewingAsset, preloadAssets, gridScrollTarget } = assetViewingStore;
88+
let { isViewing: showAssetViewer, gridScrollTarget } = assetViewingStore;
9189
9290
let element: HTMLElement | undefined = $state();
9391
@@ -433,104 +431,6 @@
433431
}
434432
};
435433
436-
const handlePrevious = async () => {
437-
const laterAsset = await timelineManager.getLaterAsset($viewingAsset);
438-
439-
if (laterAsset) {
440-
const preloadAsset = await timelineManager.getLaterAsset(laterAsset);
441-
const asset = await getAssetInfo({ id: laterAsset.id, key: authManager.key });
442-
assetViewingStore.setAsset(asset, preloadAsset ? [preloadAsset] : []);
443-
await navigate({ targetRoute: 'current', assetId: laterAsset.id });
444-
}
445-
446-
return !!laterAsset;
447-
};
448-
449-
const handleNext = async () => {
450-
const earlierAsset = await timelineManager.getEarlierAsset($viewingAsset);
451-
if (earlierAsset) {
452-
const preloadAsset = await timelineManager.getEarlierAsset(earlierAsset);
453-
const asset = await getAssetInfo({ id: earlierAsset.id, key: authManager.key });
454-
assetViewingStore.setAsset(asset, preloadAsset ? [preloadAsset] : []);
455-
await navigate({ targetRoute: 'current', assetId: earlierAsset.id });
456-
}
457-
458-
return !!earlierAsset;
459-
};
460-
461-
const handleRandom = async () => {
462-
const randomAsset = await timelineManager.getRandomAsset();
463-
464-
if (randomAsset) {
465-
const asset = await getAssetInfo({ id: randomAsset.id, key: authManager.key });
466-
assetViewingStore.setAsset(asset);
467-
await navigate({ targetRoute: 'current', assetId: randomAsset.id });
468-
return asset;
469-
}
470-
};
471-
472-
const handleClose = async (asset: { id: string }) => {
473-
assetViewingStore.showAssetViewer(false);
474-
showSkeleton = true;
475-
$gridScrollTarget = { at: asset.id };
476-
await navigate({ targetRoute: 'current', assetId: null, assetGridRouteSearchParams: $gridScrollTarget });
477-
};
478-
479-
const handlePreAction = async (action: Action) => {
480-
switch (action.type) {
481-
case removeAction:
482-
case AssetAction.TRASH:
483-
case AssetAction.RESTORE:
484-
case AssetAction.DELETE:
485-
case AssetAction.ARCHIVE:
486-
case AssetAction.SET_VISIBILITY_LOCKED:
487-
case AssetAction.SET_VISIBILITY_TIMELINE: {
488-
// find the next asset to show or close the viewer
489-
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
490-
(await handleNext()) || (await handlePrevious()) || (await handleClose(action.asset));
491-
492-
// delete after find the next one
493-
timelineManager.removeAssets([action.asset.id]);
494-
break;
495-
}
496-
}
497-
};
498-
const handleAction = (action: Action) => {
499-
switch (action.type) {
500-
case AssetAction.ARCHIVE:
501-
case AssetAction.UNARCHIVE:
502-
case AssetAction.FAVORITE:
503-
case AssetAction.UNFAVORITE: {
504-
timelineManager.updateAssets([action.asset]);
505-
break;
506-
}
507-
508-
case AssetAction.ADD: {
509-
timelineManager.addAssets([action.asset]);
510-
break;
511-
}
512-
513-
case AssetAction.UNSTACK: {
514-
updateUnstackedAssetInTimeline(timelineManager, action.assets);
515-
break;
516-
}
517-
case AssetAction.SET_STACK_PRIMARY_ASSET: {
518-
//Have to unstack then restack assets in timeline in order for the currently removed new primary asset to be made visible.
519-
updateUnstackedAssetInTimeline(
520-
timelineManager,
521-
action.stack.assets.map((asset) => toTimelineAsset(asset)),
522-
);
523-
updateStackedAssetInTimeline(timelineManager, {
524-
stack: action.stack,
525-
toDeleteIds: action.stack.assets
526-
.filter((asset) => asset.id !== action.stack.primaryAssetId)
527-
.map((asset) => asset.id),
528-
});
529-
break;
530-
}
531-
}
532-
};
533-
534434
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
535435
536436
let shiftKeyIsDown = $state(false);
@@ -703,7 +603,7 @@
703603
}
704604
705605
isShortcutModalOpen = true;
706-
await modalManager.show(ShortcutsModal);
606+
await modalManager.show(ShortcutsModal, {});
707607
isShortcutModalOpen = false;
708608
};
709609
@@ -914,22 +814,16 @@
914814
{#if !albumMapViewManager.isInMapView}
915815
<Portal target="body">
916816
{#if $showAssetViewer}
917-
{#await import('../asset-viewer/asset-viewer.svelte') then { default: AssetViewer }}
918-
<AssetViewer
919-
{withStacked}
920-
asset={$viewingAsset}
921-
preloadAssets={$preloadAssets}
922-
{isShared}
923-
{album}
924-
{person}
925-
preAction={handlePreAction}
926-
onAction={handleAction}
927-
onPrevious={handlePrevious}
928-
onNext={handleNext}
929-
onRandom={handleRandom}
930-
onClose={handleClose}
931-
/>
932-
{/await}
817+
<AssetViewerAndActions
818+
bind:showSkeleton
819+
{timelineManager}
820+
{removeAction}
821+
{withStacked}
822+
{isShared}
823+
{album}
824+
{person}
825+
{isShowDeleteConfirmation}
826+
></AssetViewerAndActions>
933827
{/if}
934828
</Portal>
935829
{/if}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<script lang="ts">
2+
import type { Action } from '$lib/components/asset-viewer/actions/action';
3+
import { AssetAction } from '$lib/constants';
4+
import { authManager } from '$lib/managers/auth-manager.svelte';
5+
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
6+
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
7+
import { updateStackedAssetInTimeline, updateUnstackedAssetInTimeline } from '$lib/utils/actions';
8+
import { navigate } from '$lib/utils/navigation';
9+
import { toTimelineAsset } from '$lib/utils/timeline-util';
10+
import { getAssetInfo, type AssetResponseDto } from '@immich/sdk';
11+
12+
let { asset: viewingAsset, gridScrollTarget } = assetViewingStore;
13+
14+
interface Props {
15+
timelineManager: TimelineManager;
16+
showSkeleton: boolean;
17+
removeAction?:
18+
| AssetAction.UNARCHIVE
19+
| AssetAction.ARCHIVE
20+
| AssetAction.FAVORITE
21+
| AssetAction.UNFAVORITE
22+
| AssetAction.SET_VISIBILITY_TIMELINE;
23+
handlePreAction?: (action: Action) => Promise<void>;
24+
handleAction?: (action: Action) => void;
25+
handleNext?: () => Promise<boolean>;
26+
handlePrevious?: () => Promise<boolean>;
27+
handleRandom?: () => Promise<AssetResponseDto | undefined>;
28+
handleClose?: (asset: { id: string }) => Promise<void>;
29+
}
30+
31+
let {
32+
timelineManager = $bindable(),
33+
showSkeleton = $bindable(false),
34+
removeAction,
35+
handlePreAction = $bindable(),
36+
handleAction = $bindable(),
37+
handleNext = $bindable(),
38+
handlePrevious = $bindable(),
39+
handleRandom = $bindable(),
40+
handleClose = $bindable(),
41+
}: Props = $props();
42+
43+
handlePrevious = async () => {
44+
const laterAsset = await timelineManager.getLaterAsset($viewingAsset);
45+
46+
if (laterAsset) {
47+
const preloadAsset = await timelineManager.getLaterAsset(laterAsset);
48+
const asset = await getAssetInfo({ id: laterAsset.id, key: authManager.key });
49+
assetViewingStore.setAsset(asset, preloadAsset ? [preloadAsset] : []);
50+
await navigate({ targetRoute: 'current', assetId: laterAsset.id });
51+
}
52+
53+
return !!laterAsset;
54+
};
55+
56+
handleNext = async () => {
57+
const earlierAsset = await timelineManager.getEarlierAsset($viewingAsset);
58+
if (earlierAsset) {
59+
const preloadAsset = await timelineManager.getEarlierAsset(earlierAsset);
60+
const asset = await getAssetInfo({ id: earlierAsset.id, key: authManager.key });
61+
assetViewingStore.setAsset(asset, preloadAsset ? [preloadAsset] : []);
62+
await navigate({ targetRoute: 'current', assetId: earlierAsset.id });
63+
}
64+
65+
return !!earlierAsset;
66+
};
67+
68+
handleRandom = async () => {
69+
const randomAsset = await timelineManager.getRandomAsset();
70+
71+
if (randomAsset) {
72+
const asset = await getAssetInfo({ id: randomAsset.id, key: authManager.key });
73+
assetViewingStore.setAsset(asset);
74+
await navigate({ targetRoute: 'current', assetId: randomAsset.id });
75+
return asset;
76+
}
77+
};
78+
79+
handleClose = async (asset: { id: string }) => {
80+
assetViewingStore.showAssetViewer(false);
81+
showSkeleton = true;
82+
$gridScrollTarget = { at: asset.id };
83+
await navigate({ targetRoute: 'current', assetId: null, assetGridRouteSearchParams: $gridScrollTarget });
84+
};
85+
86+
handlePreAction = async (action: Action) => {
87+
switch (action.type) {
88+
case removeAction:
89+
case AssetAction.TRASH:
90+
case AssetAction.RESTORE:
91+
case AssetAction.DELETE:
92+
case AssetAction.ARCHIVE:
93+
case AssetAction.SET_VISIBILITY_LOCKED:
94+
case AssetAction.SET_VISIBILITY_TIMELINE: {
95+
// find the next asset to show or close the viewer
96+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
97+
(await handleNext()) || (await handlePrevious()) || (await handleClose(action.asset));
98+
99+
// delete after find the next one
100+
timelineManager.removeAssets([action.asset.id]);
101+
break;
102+
}
103+
}
104+
};
105+
handleAction = (action: Action) => {
106+
switch (action.type) {
107+
case AssetAction.ARCHIVE:
108+
case AssetAction.UNARCHIVE:
109+
case AssetAction.FAVORITE:
110+
case AssetAction.UNFAVORITE: {
111+
timelineManager.updateAssets([action.asset]);
112+
break;
113+
}
114+
115+
case AssetAction.ADD: {
116+
timelineManager.addAssets([action.asset]);
117+
break;
118+
}
119+
120+
case AssetAction.UNSTACK: {
121+
updateUnstackedAssetInTimeline(timelineManager, action.assets);
122+
break;
123+
}
124+
case AssetAction.SET_STACK_PRIMARY_ASSET: {
125+
//Have to unstack then restack assets in timeline in order for the currently removed new primary asset to be made visible.
126+
updateUnstackedAssetInTimeline(
127+
timelineManager,
128+
action.stack.assets.map((asset) => toTimelineAsset(asset)),
129+
);
130+
updateStackedAssetInTimeline(timelineManager, {
131+
stack: action.stack,
132+
toDeleteIds: action.stack.assets
133+
.filter((asset) => asset.id !== action.stack.primaryAssetId)
134+
.map((asset) => asset.id),
135+
});
136+
break;
137+
}
138+
}
139+
};
140+
</script>

0 commit comments

Comments
 (0)