diff --git a/src/lib/components/ClipOverlay.svelte b/src/lib/components/ClipOverlay.svelte index ee8210d..9884ed9 100644 --- a/src/lib/components/ClipOverlay.svelte +++ b/src/lib/components/ClipOverlay.svelte @@ -25,6 +25,7 @@ autoScroll, gifEnabled = false, openComments = false, + fromActivity = false, ondismiss }: { clipId: string; @@ -33,6 +34,7 @@ autoScroll: boolean; gifEnabled?: boolean; openComments?: boolean; + fromActivity?: boolean; ondismiss: () => void; } = $props(); @@ -67,6 +69,13 @@ console.log('[ClipOverlay] handleDismiss called for:', clipId); dismissed = true; ondismiss(); + if (fromActivity) { + // Opened from /activity → /?clip=X (pushed feed entry) plus the + // overlay's own pushed state. ondismiss() unmounts the overlay; + // the overlay history cleanup pops one entry (back to /). Pop one + // more so the user lands directly on /activity. + setTimeout(() => history.back(), 0); + } } // Load clip data diff --git a/src/lib/components/MeReelView.svelte b/src/lib/components/MeReelView.svelte index 39d6d26..659ea6e 100644 --- a/src/lib/components/MeReelView.svelte +++ b/src/lib/components/MeReelView.svelte @@ -49,12 +49,21 @@ beforeNavigate(overlay.onBeforeNavigate); // Mount: push history state, scroll to start index + // Guard the initial scroll so subsequent effect re-runs (e.g. after a + // nested CommentsSheet pushes/pops state) don't yank the reel back to + // startIndex and make it look like the feed jumped to the top. + let didInitialScroll = false; $effect(() => { const cleanupHistory = overlay.attach(close); - tick().then(() => { - if (reelContainer) reelContainer.scrollTop = startIndex * reelContainer.clientHeight; - }); + if (!didInitialScroll) { + tick().then(() => { + if (reelContainer) { + reelContainer.scrollTop = startIndex * reelContainer.clientHeight; + didInitialScroll = true; + } + }); + } return cleanupHistory; }); diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index f60904e..390946b 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -61,6 +61,7 @@ // Clip overlay (dedicated single-clip view) let overlayClipId = $state(null); let overlayOpenComments = $state(false); + let overlayFromActivity = $state(false); const overlayActive = $derived(overlayClipId !== null); let pullDistance = $state(0); @@ -703,6 +704,7 @@ function handleOverlayDismiss() { overlayClipId = null; overlayOpenComments = false; + overlayFromActivity = false; // Refresh feed to reflect any changes made in the overlay loadInitialClips(); fetchUnwatchedCount(); @@ -802,11 +804,13 @@ const params = new URLSearchParams(window.location.search); const deepClipId = params.get('clip'); const deepComments = params.get('comments') === 'true'; + const deepFromActivity = params.get('from') === 'activity'; if (deepClipId) { // Clean URL without triggering navigation const clean = new URL(window.location.href); clean.searchParams.delete('clip'); clean.searchParams.delete('comments'); + clean.searchParams.delete('from'); history.replaceState(null, '', clean.pathname + clean.search || '/'); // Clear the notification stash so the layout's visibilitychange handler // doesn't double-process this same deep-link. @@ -818,6 +822,7 @@ // pushState throws if called before the router is ready (cold start). setTimeout(() => { overlayOpenComments = deepComments; + overlayFromActivity = deepFromActivity; clipOverlaySignal.set(deepClipId); }, 0); } @@ -1010,6 +1015,7 @@ {autoScroll} {gifEnabled} openComments={overlayOpenComments} + fromActivity={overlayFromActivity} ondismiss={handleOverlayDismiss} /> {/if} diff --git a/src/routes/(app)/activity/+page.svelte b/src/routes/(app)/activity/+page.svelte index c181ff4..45e3229 100644 --- a/src/routes/(app)/activity/+page.svelte +++ b/src/routes/(app)/activity/+page.svelte @@ -93,8 +93,11 @@ function handleNotificationClick(e: Event, n: Notification) { e.preventDefault(); const shouldOpenComments = n.type !== 'reaction'; - const q = shouldOpenComments ? `clip=${n.clipId}&comments=true` : `clip=${n.clipId}`; - goto(resolve(`/?${q}`)); + const base = shouldOpenComments ? `clip=${n.clipId}&comments=true` : `clip=${n.clipId}`; + // `from=activity` tells the feed/overlay the user arrived from the + // activity page, so a single back press pops both the feed entry and + // the overlay entry to land them back here. + goto(resolve(`/?${base}&from=activity`)); } function clipLabel(n: Notification): string {