Skip to content

Commit 431664c

Browse files
authored
fix(overlay) : routes persistence (#856)
Fixes #811
1 parent c9f3883 commit 431664c

File tree

7 files changed

+84
-26
lines changed

7 files changed

+84
-26
lines changed

.changeset/shaggy-rocks-lead.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@spotlightjs/overlay": patch
3+
---
4+
5+
- Added route persistence for overlay

packages/overlay/src/App.tsx

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { useCallback, useEffect, useMemo, useState } from "react";
2-
import { useNavigate } from "react-router-dom";
2+
import { useLocation, useNavigate } from "react-router-dom";
33
import Debugger from "./components/Debugger";
44
import Trigger from "./components/Trigger";
55
import { SPOTLIGHT_OPEN_CLASS_NAME } from "./constants";
66
import type { Integration, IntegrationData } from "./integrations/integration";
7+
import { getPanelsFromIntegrations } from "./integrations/utils/extractPanelsFromIntegrations";
78
import { base64Decode } from "./lib/base64";
89
import * as db from "./lib/db";
910
import { getSpotlightEventTarget } from "./lib/eventTarget";
1011
import { log } from "./lib/logger";
1112
import useKeyPress from "./lib/useKeyPress";
13+
import { getRouteStorageKey } from "./overlay/utils/routePersistence";
1214
import { connectToSidecar } from "./sidecar";
1315
import type { NotificationCount, SpotlightOverlayOptions } from "./types";
1416

@@ -164,6 +166,16 @@ export default function App({
164166
// as our <Route>s are scattered around a bit
165167
// See https://github.com/remix-run/react-router/issues/7634
166168
const navigate = useNavigate();
169+
const location = useLocation();
170+
171+
// contextId for namespacing of routes in sessionStorage
172+
const contextId = sidecarUrl;
173+
174+
// helper to get valid panel routes
175+
const getValidRoutes = useCallback(() => {
176+
return new Set(getPanelsFromIntegrations(integrations, integrationData).map(panel => `/${panel.id}`));
177+
}, [integrationData, integrations]);
178+
167179
const clearEvents = useCallback(async () => {
168180
try {
169181
const clearEventsUrl: string = new URL("/clear", sidecarUrl).href;
@@ -192,9 +204,24 @@ export default function App({
192204
) => {
193205
log("Open");
194206
setOpen(true);
195-
if (e.detail.path) navigate(e.detail.path);
207+
if (e.detail.path) {
208+
navigate(e.detail.path);
209+
} else {
210+
try {
211+
const lastRoute = sessionStorage.getItem(getRouteStorageKey(contextId));
212+
const validRoutes = getValidRoutes();
213+
if (lastRoute && lastRoute !== location.pathname && validRoutes.has(lastRoute)) {
214+
navigate(lastRoute);
215+
}
216+
} catch (error) {
217+
log("Failed to retrieve or navigate to the last route from browser storage", {
218+
error,
219+
currentPath: location.pathname,
220+
});
221+
}
222+
}
196223
},
197-
[navigate],
224+
[navigate, location.pathname, contextId, integrationData, integrations],
198225
);
199226

200227
const onClose = useCallback(() => {
@@ -275,6 +302,7 @@ export default function App({
275302
setTriggerButtonCount={setTriggerButtonCount}
276303
fullPage={fullPage}
277304
showClearEventsButton={showClearEventsButton}
305+
contextId={contextId}
278306
/>
279307
</>
280308
);

packages/overlay/src/components/Debugger.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export default function Debugger({
4040
setTriggerButtonCount: setNotificationCount,
4141
fullPage,
4242
showClearEventsButton,
43+
contextId,
4344
}: {
4445
integrations: Integration[];
4546
isOpen: boolean;
@@ -49,6 +50,7 @@ export default function Debugger({
4950
setTriggerButtonCount: (count: NotificationCount) => void;
5051
fullPage: boolean;
5152
showClearEventsButton: boolean;
53+
contextId: string;
5254
}) {
5355
return (
5456
<FullscreenBlur isOpen={isOpen} setOpen={setOpen} fullPage={fullPage}>
@@ -70,6 +72,7 @@ export default function Debugger({
7072
setOpen={setOpen}
7173
isOnline={isOnline}
7274
showClearEventsButton={showClearEventsButton}
75+
contextId={contextId}
7376
/>
7477
</div>
7578
</FullscreenBlur>

packages/overlay/src/components/Overview.tsx

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { createElement, useEffect, useState } from "react";
2-
import { Route, Routes } from "react-router-dom";
2+
import { Route, Routes, useLocation } from "react-router-dom";
33
import type { Integration, IntegrationData } from "~/integrations/integration";
4+
import { log } from "~/lib/logger";
45
import type { NotificationCount } from "~/types";
6+
import { getPanelsFromIntegrations } from "../integrations/utils/extractPanelsFromIntegrations";
7+
import { getRouteStorageKey } from "../overlay/utils/routePersistence";
58
import Navigation from "./navigation";
69

710
export default function Overview({
@@ -11,30 +14,31 @@ export default function Overview({
1114
setOpen,
1215
isOnline,
1316
showClearEventsButton,
17+
contextId,
1418
}: {
1519
integrations: Integration[];
1620
integrationData: IntegrationData<unknown>;
1721
setTriggerButtonCount: (count: NotificationCount) => void;
1822
setOpen: (value: boolean) => void;
1923
isOnline: boolean;
2024
showClearEventsButton: boolean;
25+
contextId: string;
2126
}) {
2227
const [notificationCountSum, setNotificationCountSum] = useState<NotificationCount>({ count: 0, severe: false });
28+
const location = useLocation();
2329

24-
const panels = integrations.flatMap(integration => {
25-
if (integration.panels || integration.tabs) {
26-
const processedEvents = integrationData[integration.name]?.map(container => container.event) || [];
27-
return (
28-
integration.panels?.({ processedEvents }) ||
29-
integration.tabs?.({ processedEvents }).map(tab => ({
30-
...tab,
31-
processedEvents: processedEvents,
32-
})) ||
33-
[]
34-
);
30+
useEffect(() => {
31+
try {
32+
sessionStorage.setItem(getRouteStorageKey(contextId), location.pathname);
33+
} catch (error) {
34+
log("Failed to set current route to browser storage", {
35+
error,
36+
currentPath: location.pathname,
37+
});
3538
}
36-
return [];
37-
});
39+
}, [location.pathname, contextId]);
40+
41+
const panels = getPanelsFromIntegrations(integrations, integrationData);
3842

3943
const newNotificationSum = panels.reduce(
4044
(sum, panel) => ({

packages/overlay/src/index.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
import globalStyles from "./index.css?inline";
1313
import { type SpotlightContext, initIntegrations } from "./integrations/integration";
1414
import { default as sentry } from "./integrations/sentry/index";
15+
import { getPanelsFromIntegrations } from "./integrations/utils/extractPanelsFromIntegrations";
1516
import { off, on, trigger } from "./lib/eventTarget";
1617
import initSentry from "./lib/instrumentation";
1718
import { activateLogger, log } from "./lib/logger";
@@ -176,15 +177,7 @@ export async function init(initOptions: SpotlightOverlayOptions = {}) {
176177
});
177178
}
178179

179-
const tabs = initializedIntegrations.flatMap(
180-
integration =>
181-
integration.panels?.({ processedEvents: [] }) ||
182-
integration.tabs?.({ processedEvents: [] }).map(tab => ({
183-
...tab,
184-
processedEvents: [],
185-
})) ||
186-
[],
187-
);
180+
const tabs = getPanelsFromIntegrations(initializedIntegrations, {});
188181

189182
const initialTab = startFrom || (tabs.length ? `/${tabs[0].id}` : "/no-tabs");
190183
log("Starting from", initialTab);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* util to extract all panels/tabs from integrations, with processedEvents.
3+
*/
4+
import type { Integration, IntegrationData } from "../../integrations/integration";
5+
6+
export function getPanelsFromIntegrations(integrations: Integration[], integrationData: IntegrationData<unknown>) {
7+
return integrations.flatMap(integration => {
8+
if (integration.panels || integration.tabs) {
9+
const processedEvents = integrationData[integration.name]?.map(container => container.event) || [];
10+
return (
11+
integration.panels?.({ processedEvents }) ||
12+
integration.tabs?.({ processedEvents }).map(tab => ({ ...tab, processedEvents })) ||
13+
[]
14+
);
15+
}
16+
return [];
17+
});
18+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* returns a namespaced sessionStorage key for storing the last route.
3+
* @param contextId unique identifier for the current context, current sidecarUrl
4+
*/
5+
export function getRouteStorageKey(contextId: string) {
6+
return `spotlight:lastRoute:${contextId}`;
7+
}

0 commit comments

Comments
 (0)