Skip to content

Commit 228e31f

Browse files
alextran1502savely-krasovsky
authored andcommitted
fix: hard link navigation (immich-app#18489)
1 parent 4df0913 commit 228e31f

File tree

2 files changed

+56
-31
lines changed

2 files changed

+56
-31
lines changed

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

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -124,26 +124,38 @@
124124
scrollTo(0);
125125
};
126126
127+
const scrollToAsset = async (assetId: string) => {
128+
try {
129+
const bucket = await assetStore.findBucketForAsset(assetId);
130+
if (bucket) {
131+
const height = bucket.findAssetAbsolutePosition(assetId);
132+
if (height) {
133+
scrollTo(height);
134+
assetStore.updateIntersections();
135+
return true;
136+
}
137+
}
138+
} catch {
139+
// ignore errors - asset may not be in the store
140+
}
141+
return false;
142+
};
143+
127144
const completeNav = async () => {
128145
const scrollTarget = $gridScrollTarget?.at;
146+
let scrolled = false;
129147
if (scrollTarget) {
130-
try {
131-
const bucket = await assetStore.findBucketForAsset(scrollTarget);
132-
if (bucket) {
133-
const height = bucket.findAssetAbsolutePosition(scrollTarget);
134-
if (height) {
135-
scrollTo(height);
136-
assetStore.updateIntersections();
137-
return;
138-
}
139-
}
140-
} catch {
141-
// ignore errors - asset may not be in the store
142-
}
148+
scrolled = await scrollToAsset(scrollTarget);
149+
}
150+
151+
if (!scrolled) {
152+
// if the asset is not found, scroll to the top
153+
scrollToTop();
143154
}
144-
scrollToTop();
145155
};
156+
146157
beforeNavigate(() => (assetStore.suspendTransitions = true));
158+
147159
afterNavigate((nav) => {
148160
const { complete } = nav;
149161
complete.then(completeNav, completeNav);

web/src/lib/stores/assets-store.svelte.ts

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,8 @@ export class AssetDateGroup {
217217
return { moveAssets, processedIds, unprocessedIds, changedGeometry };
218218
}
219219

220-
layout(options: CommonLayoutOptions) {
221-
if (!this.bucket.intersecting) {
220+
layout(options: CommonLayoutOptions, noDefer: boolean) {
221+
if (!noDefer && !this.bucket.intersecting) {
222222
this.deferredLayout = true;
223223
return;
224224
}
@@ -602,6 +602,8 @@ export class AssetBucket {
602602
}
603603

604604
findAssetAbsolutePosition(assetId: string) {
605+
this.store.clearDeferredLayout(this);
606+
605607
for (const group of this.dateGroups) {
606608
const intersectingAsset = group.intersetingAssets.find((asset) => asset.id === assetId);
607609
if (intersectingAsset) {
@@ -658,6 +660,12 @@ type AssetStoreLayoutOptions = {
658660
headerHeight?: number;
659661
gap?: number;
660662
};
663+
664+
interface UpdateGeometryOptions {
665+
invalidateHeight: boolean;
666+
noDefer?: boolean;
667+
}
668+
661669
export class AssetStore {
662670
// --- public ----
663671
isInitialized = $state(false);
@@ -932,6 +940,16 @@ export class AssetStore {
932940
);
933941
}
934942

943+
clearDeferredLayout(bucket: AssetBucket) {
944+
const hasDeferred = bucket.dateGroups.some((group) => group.deferredLayout);
945+
if (hasDeferred) {
946+
this.#updateGeometry(bucket, { invalidateHeight: true, noDefer: true });
947+
for (const group of bucket.dateGroups) {
948+
group.deferredLayout = false;
949+
}
950+
}
951+
}
952+
935953
#updateIntersection(bucket: AssetBucket) {
936954
const actuallyIntersecting = this.#calculateIntersecting(bucket, 0, 0);
937955
let preIntersecting = false;
@@ -941,13 +959,7 @@ export class AssetStore {
941959
bucket.intersecting = actuallyIntersecting || preIntersecting;
942960
bucket.actuallyIntersecting = actuallyIntersecting;
943961
if (preIntersecting || actuallyIntersecting) {
944-
const hasDeferred = bucket.dateGroups.some((group) => group.deferredLayout);
945-
if (hasDeferred) {
946-
this.#updateGeometry(bucket, true);
947-
for (const group of bucket.dateGroups) {
948-
group.deferredLayout = false;
949-
}
950-
}
962+
this.clearDeferredLayout(bucket);
951963
}
952964
}
953965

@@ -1052,7 +1064,7 @@ export class AssetStore {
10521064
return;
10531065
}
10541066
for (const bucket of this.buckets) {
1055-
this.#updateGeometry(bucket, changedWidth);
1067+
this.#updateGeometry(bucket, { invalidateHeight: changedWidth });
10561068
}
10571069
this.updateIntersections();
10581070
this.#createScrubBuckets();
@@ -1079,7 +1091,8 @@ export class AssetStore {
10791091
};
10801092
}
10811093

1082-
#updateGeometry(bucket: AssetBucket, invalidateHeight: boolean) {
1094+
#updateGeometry(bucket: AssetBucket, options: UpdateGeometryOptions) {
1095+
const { invalidateHeight, noDefer = false } = options;
10831096
if (invalidateHeight) {
10841097
bucket.isBucketHeightActual = false;
10851098
}
@@ -1094,10 +1107,10 @@ export class AssetStore {
10941107
}
10951108
return;
10961109
}
1097-
this.#layoutBucket(bucket);
1110+
this.#layoutBucket(bucket, noDefer);
10981111
}
10991112

1100-
#layoutBucket(bucket: AssetBucket) {
1113+
#layoutBucket(bucket: AssetBucket, noDefer: boolean = false) {
11011114
// these are top offsets, for each row
11021115
let cummulativeHeight = 0;
11031116
// these are left offsets of each group, for each row
@@ -1112,7 +1125,7 @@ export class AssetStore {
11121125
rowSpaceRemaining.fill(this.viewportWidth, 0, bucket.dateGroups.length);
11131126
const options = this.createLayoutOptions();
11141127
for (const assetGroup of bucket.dateGroups) {
1115-
assetGroup.layout(options);
1128+
assetGroup.layout(options, noDefer);
11161129
rowSpaceRemaining[dateGroupRow] -= assetGroup.width - 1;
11171130
if (dateGroupCol > 0) {
11181131
rowSpaceRemaining[dateGroupRow] -= this.gap;
@@ -1266,7 +1279,7 @@ export class AssetStore {
12661279

12671280
for (const bucket of addContext.updatedBuckets) {
12681281
bucket.sortDateGroups();
1269-
this.#updateGeometry(bucket, true);
1282+
this.#updateGeometry(bucket, { invalidateHeight: true });
12701283
}
12711284
this.updateIntersections();
12721285
}
@@ -1357,7 +1370,7 @@ export class AssetStore {
13571370
}
13581371
const changedGeometry = changedBuckets.size > 0;
13591372
for (const bucket of changedBuckets) {
1360-
this.#updateGeometry(bucket, true);
1373+
this.#updateGeometry(bucket, { invalidateHeight: true });
13611374
}
13621375
if (changedGeometry) {
13631376
this.updateIntersections();
@@ -1397,7 +1410,7 @@ export class AssetStore {
13971410

13981411
refreshLayout() {
13991412
for (const bucket of this.buckets) {
1400-
this.#updateGeometry(bucket, true);
1413+
this.#updateGeometry(bucket, { invalidateHeight: true });
14011414
}
14021415
this.updateIntersections();
14031416
}

0 commit comments

Comments
 (0)