Skip to content

Commit e789fb3

Browse files
committed
refactor: asset-viewer/actions
1 parent 13563fc commit e789fb3

File tree

3 files changed

+254
-145
lines changed

3 files changed

+254
-145
lines changed

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

Lines changed: 19 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@
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';
15-
import { authManager } from '$lib/managers/auth-manager.svelte';
1615
import type { MonthGroup } from '$lib/managers/timeline-manager/month-group.svelte';
1716
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
1817
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
@@ -26,11 +25,11 @@
2625
import { searchStore } from '$lib/stores/search.svelte';
2726
import { featureFlags } from '$lib/stores/server-config.store';
2827
import { handlePromiseError } from '$lib/utils';
29-
import { deleteAssets, updateStackedAssetInTimeline, updateUnstackedAssetInTimeline } from '$lib/utils/actions';
28+
import { deleteAssets, updateStackedAssetInTimeline } from '$lib/utils/actions';
3029
import { archiveAssets, cancelMultiselect, selectAllAssets, stackAssets } from '$lib/utils/asset-utils';
3130
import { navigate } from '$lib/utils/navigation';
32-
import { getTimes, toTimelineAsset, type ScrubberListener, type TimelineYearMonth } from '$lib/utils/timeline-util';
33-
import { AssetVisibility, getAssetInfo, type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
31+
import { getTimes, type ScrubberListener, type TimelineYearMonth } from '$lib/utils/timeline-util';
32+
import { AssetVisibility, type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
3433
import { modalManager } from '@immich/ui';
3534
import { DateTime } from 'luxon';
3635
import { onMount, type Snippet } from 'svelte';
@@ -53,8 +52,7 @@
5352
| AssetAction.ARCHIVE
5453
| AssetAction.FAVORITE
5554
| AssetAction.UNFAVORITE
56-
| AssetAction.SET_VISIBILITY_TIMELINE
57-
| null;
55+
| AssetAction.SET_VISIBILITY_TIMELINE;
5856
withStacked?: boolean;
5957
showArchiveIcon?: boolean;
6058
isShared?: boolean;
@@ -73,7 +71,7 @@
7371
enableRouting,
7472
timelineManager = $bindable(),
7573
assetInteraction,
76-
removeAction = null,
74+
removeAction,
7775
withStacked = false,
7876
showArchiveIcon = false,
7977
isShared = false,
@@ -86,7 +84,7 @@
8684
empty,
8785
}: Props = $props();
8886
89-
let { isViewing: showAssetViewer, asset: viewingAsset, preloadAssets, gridScrollTarget, mutex } = assetViewingStore;
87+
let { isViewing: showAssetViewer, gridScrollTarget, mutex } = assetViewingStore;
9088
9189
let element: HTMLElement | undefined = $state();
9290
@@ -448,126 +446,6 @@
448446
}
449447
};
450448
451-
const handlePrevious = async () => {
452-
const release = await mutex.acquire();
453-
const laterAsset = await timelineManager.getLaterAsset($viewingAsset);
454-
455-
if (laterAsset) {
456-
const preloadAsset = await timelineManager.getLaterAsset(laterAsset);
457-
const asset = await getAssetInfo({ ...authManager.params, id: laterAsset.id });
458-
assetViewingStore.setAsset(asset, preloadAsset ? [preloadAsset] : []);
459-
await navigate({ targetRoute: 'current', assetId: laterAsset.id });
460-
}
461-
462-
release();
463-
return !!laterAsset;
464-
};
465-
466-
const handleNext = async () => {
467-
const release = await mutex.acquire();
468-
const earlierAsset = await timelineManager.getEarlierAsset($viewingAsset);
469-
470-
if (earlierAsset) {
471-
const preloadAsset = await timelineManager.getEarlierAsset(earlierAsset);
472-
const asset = await getAssetInfo({ ...authManager.params, id: earlierAsset.id });
473-
assetViewingStore.setAsset(asset, preloadAsset ? [preloadAsset] : []);
474-
await navigate({ targetRoute: 'current', assetId: earlierAsset.id });
475-
}
476-
477-
release();
478-
return !!earlierAsset;
479-
};
480-
481-
const handleRandom = async () => {
482-
const randomAsset = await timelineManager.getRandomAsset();
483-
484-
if (randomAsset) {
485-
const asset = await getAssetInfo({ ...authManager.params, id: randomAsset.id });
486-
assetViewingStore.setAsset(asset);
487-
await navigate({ targetRoute: 'current', assetId: randomAsset.id });
488-
return asset;
489-
}
490-
};
491-
492-
const handleClose = async (asset: { id: string }) => {
493-
assetViewingStore.showAssetViewer(false);
494-
showSkeleton = true;
495-
$gridScrollTarget = { at: asset.id };
496-
await navigate({ targetRoute: 'current', assetId: null, assetGridRouteSearchParams: $gridScrollTarget });
497-
};
498-
499-
const handlePreAction = async (action: Action) => {
500-
switch (action.type) {
501-
case removeAction:
502-
case AssetAction.TRASH:
503-
case AssetAction.RESTORE:
504-
case AssetAction.DELETE:
505-
case AssetAction.ARCHIVE:
506-
case AssetAction.SET_VISIBILITY_LOCKED:
507-
case AssetAction.SET_VISIBILITY_TIMELINE: {
508-
// find the next asset to show or close the viewer
509-
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
510-
(await handleNext()) || (await handlePrevious()) || (await handleClose(action.asset));
511-
512-
// delete after find the next one
513-
timelineManager.removeAssets([action.asset.id]);
514-
break;
515-
}
516-
}
517-
};
518-
const handleAction = (action: Action) => {
519-
switch (action.type) {
520-
case AssetAction.ARCHIVE:
521-
case AssetAction.UNARCHIVE:
522-
case AssetAction.FAVORITE:
523-
case AssetAction.UNFAVORITE: {
524-
timelineManager.updateAssets([action.asset]);
525-
break;
526-
}
527-
528-
case AssetAction.ADD: {
529-
timelineManager.addAssets([action.asset]);
530-
break;
531-
}
532-
533-
case AssetAction.UNSTACK: {
534-
updateUnstackedAssetInTimeline(timelineManager, action.assets);
535-
break;
536-
}
537-
case AssetAction.REMOVE_ASSET_FROM_STACK: {
538-
timelineManager.addAssets([toTimelineAsset(action.asset)]);
539-
if (action.stack) {
540-
//Have to unstack then restack assets in timeline in order to update the stack count in the timeline.
541-
updateUnstackedAssetInTimeline(
542-
timelineManager,
543-
action.stack.assets.map((asset) => toTimelineAsset(asset)),
544-
);
545-
updateStackedAssetInTimeline(timelineManager, {
546-
stack: action.stack,
547-
toDeleteIds: action.stack.assets
548-
.filter((asset) => asset.id !== action.stack?.primaryAssetId)
549-
.map((asset) => asset.id),
550-
});
551-
}
552-
break;
553-
}
554-
case AssetAction.SET_STACK_PRIMARY_ASSET: {
555-
//Have to unstack then restack assets in timeline in order for the currently removed new primary asset to be made visible.
556-
updateUnstackedAssetInTimeline(
557-
timelineManager,
558-
action.stack.assets.map((asset) => toTimelineAsset(asset)),
559-
);
560-
updateStackedAssetInTimeline(timelineManager, {
561-
stack: action.stack,
562-
toDeleteIds: action.stack.assets
563-
.filter((asset) => asset.id !== action.stack.primaryAssetId)
564-
.map((asset) => asset.id),
565-
});
566-
break;
567-
}
568-
}
569-
};
570-
571449
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
572450
573451
let shiftKeyIsDown = $state(false);
@@ -955,26 +833,22 @@
955833
</section>
956834
</section>
957835

958-
<Portal target="body">
959-
{#if $showAssetViewer}
960-
{#await import('../asset-viewer/asset-viewer.svelte') then { default: AssetViewer }}
961-
<AssetViewer
836+
{#if !albumMapViewManager.isInMapView}
837+
<Portal target="body">
838+
{#if $showAssetViewer}
839+
<AssetViewerAndActions
840+
bind:showSkeleton
841+
{timelineManager}
842+
{removeAction}
962843
{withStacked}
963-
asset={$viewingAsset}
964-
preloadAssets={$preloadAssets}
965844
{isShared}
966845
{album}
967846
{person}
968-
preAction={handlePreAction}
969-
onAction={handleAction}
970-
onPrevious={handlePrevious}
971-
onNext={handleNext}
972-
onRandom={handleRandom}
973-
onClose={handleClose}
974-
/>
975-
{/await}
976-
{/if}
977-
</Portal>
847+
{isShowDeleteConfirmation}
848+
></AssetViewerAndActions>
849+
{/if}
850+
</Portal>
851+
{/if}
978852

979853
<style>
980854
#asset-grid {
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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, mutex } = 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 release = await mutex.acquire();
45+
const laterAsset = await timelineManager.getLaterAsset($viewingAsset);
46+
47+
if (laterAsset) {
48+
const preloadAsset = await timelineManager.getLaterAsset(laterAsset);
49+
const asset = await getAssetInfo({ ...authManager.params, id: laterAsset.id });
50+
assetViewingStore.setAsset(asset, preloadAsset ? [preloadAsset] : []);
51+
await navigate({ targetRoute: 'current', assetId: laterAsset.id });
52+
}
53+
54+
release();
55+
return !!laterAsset;
56+
};
57+
58+
handleNext = async () => {
59+
const release = await mutex.acquire();
60+
const earlierAsset = await timelineManager.getEarlierAsset($viewingAsset);
61+
62+
if (earlierAsset) {
63+
const preloadAsset = await timelineManager.getEarlierAsset(earlierAsset);
64+
const asset = await getAssetInfo({ ...authManager.params, id: earlierAsset.id });
65+
assetViewingStore.setAsset(asset, preloadAsset ? [preloadAsset] : []);
66+
await navigate({ targetRoute: 'current', assetId: earlierAsset.id });
67+
}
68+
69+
release();
70+
return !!earlierAsset;
71+
};
72+
73+
handleRandom = async () => {
74+
const randomAsset = await timelineManager.getRandomAsset();
75+
76+
if (randomAsset) {
77+
const asset = await getAssetInfo({ ...authManager.params, id: randomAsset.id });
78+
assetViewingStore.setAsset(asset);
79+
await navigate({ targetRoute: 'current', assetId: randomAsset.id });
80+
return asset;
81+
}
82+
};
83+
84+
handleClose = async (asset: { id: string }) => {
85+
assetViewingStore.showAssetViewer(false);
86+
showSkeleton = true;
87+
$gridScrollTarget = { at: asset.id };
88+
await navigate({ targetRoute: 'current', assetId: null, assetGridRouteSearchParams: $gridScrollTarget });
89+
};
90+
91+
handlePreAction = async (action: Action) => {
92+
switch (action.type) {
93+
case removeAction:
94+
case AssetAction.TRASH:
95+
case AssetAction.RESTORE:
96+
case AssetAction.DELETE:
97+
case AssetAction.ARCHIVE:
98+
case AssetAction.SET_VISIBILITY_LOCKED:
99+
case AssetAction.SET_VISIBILITY_TIMELINE: {
100+
// find the next asset to show or close the viewer
101+
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
102+
(await handleNext()) || (await handlePrevious()) || (await handleClose(action.asset));
103+
104+
// delete after find the next one
105+
timelineManager.removeAssets([action.asset.id]);
106+
break;
107+
}
108+
}
109+
};
110+
handleAction = (action: Action) => {
111+
switch (action.type) {
112+
case AssetAction.ARCHIVE:
113+
case AssetAction.UNARCHIVE:
114+
case AssetAction.FAVORITE:
115+
case AssetAction.UNFAVORITE: {
116+
timelineManager.updateAssets([action.asset]);
117+
break;
118+
}
119+
120+
case AssetAction.ADD: {
121+
timelineManager.addAssets([action.asset]);
122+
break;
123+
}
124+
125+
case AssetAction.UNSTACK: {
126+
updateUnstackedAssetInTimeline(timelineManager, action.assets);
127+
break;
128+
}
129+
case AssetAction.REMOVE_ASSET_FROM_STACK: {
130+
timelineManager.addAssets([toTimelineAsset(action.asset)]);
131+
if (action.stack) {
132+
//Have to unstack then restack assets in timeline in order to update the stack count in the timeline.
133+
updateUnstackedAssetInTimeline(
134+
timelineManager,
135+
action.stack.assets.map((asset) => toTimelineAsset(asset)),
136+
);
137+
updateStackedAssetInTimeline(timelineManager, {
138+
stack: action.stack,
139+
toDeleteIds: action.stack.assets
140+
.filter((asset) => asset.id !== action.stack?.primaryAssetId)
141+
.map((asset) => asset.id),
142+
});
143+
}
144+
break;
145+
}
146+
case AssetAction.SET_STACK_PRIMARY_ASSET: {
147+
//Have to unstack then restack assets in timeline in order for the currently removed new primary asset to be made visible.
148+
updateUnstackedAssetInTimeline(
149+
timelineManager,
150+
action.stack.assets.map((asset) => toTimelineAsset(asset)),
151+
);
152+
updateStackedAssetInTimeline(timelineManager, {
153+
stack: action.stack,
154+
toDeleteIds: action.stack.assets
155+
.filter((asset) => asset.id !== action.stack.primaryAssetId)
156+
.map((asset) => asset.id),
157+
});
158+
break;
159+
}
160+
}
161+
};
162+
</script>

0 commit comments

Comments
 (0)