Skip to content

Commit 50ac3de

Browse files
authored
feat: use request histories for the history panel (#69)
* feat: use request histories for the history panel * feat: improve JSON views * feat: display history detailed data * fix: apply styles for network detailed panel * fix: remove an invalid character
1 parent 88555cb commit 50ac3de

8 files changed

Lines changed: 207 additions & 16 deletions

File tree

packages/swr-devtools-extensions/src/panel.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<!DOCTYPE html>
22
<html>
33
<head>
4+
<meta charSet="utf-8"/>
45
<style>
56
html {
67
height: 100%;

packages/swr-devtools-panel/src/components/CacheData.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ const CacheDataView = ({ data }: { data: any }) => {
4545
data={data}
4646
theme="railscasts"
4747
invertTheme={!matchMedia("(prefers-color-scheme: dark)").matches}
48+
hideRoot
49+
shouldExpandNode={(_keyPath, _data, level) => level < 3}
4850
/>
4951
);
5052
};

packages/swr-devtools-panel/src/components/Panel.tsx renamed to packages/swr-devtools-panel/src/components/CachePanel.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import React, { useState } from "react";
22
import styled from "styled-components";
3-
import { Cache } from "swr";
43

54
import { PanelType } from "./SWRDevToolPanel";
65
import { CacheData } from "./CacheData";
76
import { CacheKey } from "./CacheKey";
8-
import { useDevToolsCache } from "../devtools-cache";
97
import { SearchInput } from "./SearchInput";
108
import { DevToolsCacheData } from "swr-devtools/lib/swr-cache";
119

12-
export const Panel = ({
10+
export const CachePanel = ({
1311
cacheData,
1412
type,
1513
selectedItemKey,
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import React, { useState } from "react";
2+
import styled from "styled-components";
3+
import { CacheData } from "./CacheData";
4+
5+
const TYPE_MAP = {
6+
success: "✅",
7+
ongoing: "🏃‍♂️",
8+
error: "❌",
9+
};
10+
11+
export const HistoryPanel = ({
12+
tracks,
13+
selectedItem,
14+
onSelectedItem,
15+
}: {
16+
tracks: any[];
17+
selectedItem: any;
18+
onSelectedItem: (data: any) => void;
19+
}) => {
20+
const histories = tracks.flatMap((track) => track.items);
21+
histories.sort((a, b) => {
22+
if (a.data.startTime < b.data.startTime) return 1;
23+
if (a.data.startTime > b.data.startTime) return -1;
24+
return 0;
25+
});
26+
return (
27+
<PanelWrapper>
28+
<PanelItem>
29+
<CacheItems>
30+
{histories.map((track) => (
31+
<CacheItem
32+
key={track.key}
33+
isSelected={selectedItem && selectedItem.id === track.data.id}
34+
>
35+
<CacheItemButton
36+
onClick={() => {
37+
if (
38+
track.data.type === "success" ||
39+
track.data.type === "error"
40+
) {
41+
onSelectedItem({
42+
...track.data,
43+
timestamp: track.data.endTime,
44+
timestampString: formatTime(track.data.endTime),
45+
});
46+
}
47+
}}
48+
>
49+
<CacheText>
50+
<div>{track.data.key}</div>
51+
{/* @ts-expect-error */}
52+
<Labels>{TYPE_MAP[track.data.type]}</Labels>
53+
</CacheText>
54+
<Timestamp>{formatTime(track.data.startTime)}</Timestamp>
55+
</CacheItemButton>
56+
</CacheItem>
57+
))}
58+
</CacheItems>
59+
</PanelItem>
60+
<VerticalDivider />
61+
<PanelItem>
62+
{selectedItem && <CacheData devToolsCacheData={selectedItem} />}
63+
</PanelItem>
64+
</PanelWrapper>
65+
);
66+
};
67+
68+
const formatTime = (date: Date) =>
69+
`${String(date.getHours()).padStart(2, "0")}:${String(
70+
date.getMinutes()
71+
).padStart(2, "0")}:${String(date.getSeconds()).padStart(2, "0")}`;
72+
73+
const PanelWrapper = styled.section`
74+
box-sizing: border-box;
75+
display: flex;
76+
justify-content: space-around;
77+
padding: 0;
78+
height: 100%;
79+
border-top: solid 1px var(--swr-devtools-border-color);
80+
`;
81+
82+
const PanelItem = styled.div`
83+
flex: 1;
84+
overflow: auto;
85+
`;
86+
87+
const CacheItems = styled.ul`
88+
margin: 0;
89+
list-style: none;
90+
padding-inline-start: 0;
91+
`;
92+
93+
const CacheItem = styled.li<{ isSelected: boolean }>`
94+
padding: 0;
95+
border-bottom: solid 1px var(--swr-devtools-border-color);
96+
background-color: ${(props) =>
97+
props.isSelected ? "var(--swr-devtools-selected-bg-color)" : "none"};
98+
&:hover {
99+
background-color: ${(props) =>
100+
props.isSelected
101+
? "var(--swr-devtools-selected-bg-color)"
102+
: "var(--swr-devtools-hover-bg-color)"};
103+
}
104+
`;
105+
106+
const VerticalDivider = styled.div`
107+
background-color: var(--swr-devtools-border-color);
108+
width: 1px;
109+
`;
110+
111+
const CacheText = styled.div`
112+
display: inline-flex;
113+
align-items: center;
114+
justify-content: space-between;
115+
margin-right: auto;
116+
flex: 1;
117+
padding-left: 8px;
118+
min-height: 2em;
119+
`;
120+
121+
const CacheItemButton = styled.button`
122+
display: flex;
123+
align-items: center;
124+
gap: 2px;
125+
width: 100%;
126+
height: 100%;
127+
padding: 0.2rem 0;
128+
border: none;
129+
background: transparent;
130+
color: var(--swr-devtools-text-color);
131+
cursor: pointer;
132+
text-align: left;
133+
`;
134+
135+
const Timestamp = styled.span`
136+
margin-right: 8px;
137+
`;
138+
139+
const Labels = styled.div`
140+
display: flex;
141+
gap: 4px;
142+
align-items: center;
143+
margin-left: 8px;
144+
margin-right: 4px;
145+
`;

packages/swr-devtools-panel/src/components/NetworkPanel.tsx

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { MouseEvent, useCallback, useState } from "react";
22
import styled from "styled-components";
33
import { Cache } from "swr";
44
import { EventEmitter, RequestsById, useRequests, useTracks } from "../request";
5+
import { CacheData } from "./CacheData";
56

67
import { PanelType } from "./SWRDevToolPanel";
78
import { Timeline } from "./timeline";
@@ -12,6 +13,11 @@ function formatTime(time: number, step: number) {
1213
return time + "ms";
1314
}
1415

16+
const formatDateTime = (date: Date) =>
17+
`${String(date.getHours()).padStart(2, "0")}:${String(
18+
date.getMinutes()
19+
).padStart(2, "0")}:${String(date.getSeconds()).padStart(2, "0")}`;
20+
1521
export const NetworkPanel = ({
1622
requestsById,
1723
tracks,
@@ -21,7 +27,7 @@ export const NetworkPanel = ({
2127
tracks: any[];
2228
startTime: number;
2329
}) => {
24-
const [requestDetail, setRequestDetail] = useState<null | string>(null);
30+
const [requestDetail, setRequestDetail] = useState<null | any>(null);
2531

2632
const [trackScale, setTrackScale] = React.useState(5);
2733
const [timelineHoverX, setTimelineHoverX] = React.useState(-1);
@@ -46,8 +52,10 @@ export const NetworkPanel = ({
4652
<PanelWrapper>
4753
{requestDetail ? (
4854
<Detail>
49-
{requestDetail}
50-
<button onClick={() => setRequestDetail(null)}>Close</button>
55+
<CacheData devToolsCacheData={requestDetail} />
56+
<CloseButtonWrapper>
57+
<button onClick={() => setRequestDetail(null)}>Close</button>
58+
</CloseButtonWrapper>
5159
</Detail>
5260
) : null}
5361
<TimelineWrapper>
@@ -119,7 +127,13 @@ export const NetworkPanel = ({
119127
<Request
120128
title={`(${item.data.type}) ` + item.data.key}
121129
type={item.data.type}
122-
onClick={() => setRequestDetail(JSON.stringify(item.data))}
130+
onClick={() =>
131+
setRequestDetail({
132+
...item.data,
133+
timestamp: item.data.endTime,
134+
timestampString: formatDateTime(item.data.endTime),
135+
})
136+
}
123137
>
124138
{item.data.key}
125139
</Request>
@@ -228,16 +242,22 @@ const TrackLabel = styled.div<{ i: number }>`
228242

229243
const Detail = styled.div`
230244
position: absolute;
231-
background: white;
245+
background: var(--swr-devtools-network-panel-bg-color);
232246
z-index: 2;
233247
height: 70%;
234248
top: 15%;
235249
padding: 10px;
250+
overflow: scroll;
236251
box-sizing: border-box;
237252
border-radius: 5px;
238253
box-shadow: 0 3px 10px #0000001a;
239254
border: 1px solid #0000001a;
240-
max-width: 500px;
255+
width: 50%;
256+
`;
257+
258+
const CloseButtonWrapper = styled.div`
259+
text-align: center;
260+
padding: 10px;
241261
`;
242262

243263
const Header = styled.div`

packages/swr-devtools-panel/src/components/SWRDevToolPanel.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { Cache } from "swr";
44
import { NetworkPanel } from "./NetworkPanel";
55
import { DevToolsCacheData } from "swr-devtools/lib/swr-cache";
66

7-
import { Panel } from "./Panel";
7+
import { CachePanel } from "./CachePanel";
8+
import { HistoryPanel } from "./HistoryPanel";
89
import { Tab } from "./Tab";
910
import { EventEmitter, useRequests, useTracks } from "../request";
1011
import { useDevToolsCache } from "../devtools-cache";
@@ -79,11 +80,12 @@ export const SWRDevToolPanel = ({ cache, events }: Props) => {
7980
const [activePanel, setActivePanel] = useState<Panel["key"]>("current");
8081
const [selectedDevToolsCacheData, selectDevToolsCacheData] =
8182
useState<DevToolsCacheData | null>(null);
83+
const [selectedHistoryData, setSelectedHistoryData] = useState<any>(null);
8284

8385
const requestsById = useRequests(events);
8486
const tracks = useTracks(requestsById);
8587
const startTime = useState(() => Date.now())[0];
86-
const [currentCacheData, historyCacheData] = useDevToolsCache(cache);
88+
const [cacheData] = useDevToolsCache(cache);
8789

8890
return (
8991
<DevToolWindow>
@@ -116,11 +118,15 @@ export const SWRDevToolPanel = ({ cache, events }: Props) => {
116118
tracks={tracks}
117119
startTime={startTime}
118120
/>
121+
) : activePanel === "history" ? (
122+
<HistoryPanel
123+
tracks={tracks}
124+
selectedItem={selectedHistoryData}
125+
onSelectedItem={(data: any) => setSelectedHistoryData(data)}
126+
/>
119127
) : (
120-
<Panel
121-
cacheData={
122-
activePanel === "current" ? currentCacheData : historyCacheData
123-
}
128+
<CachePanel
129+
cacheData={cacheData}
124130
type={activePanel}
125131
selectedItemKey={selectedDevToolsCacheData}
126132
onSelectItem={selectDevToolsCacheData}

packages/swr-devtools-panel/src/request.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ type SWRRequest = {
1111
type: "success" | "error" | "discarded" | "ongoing";
1212
startTime: Date;
1313
endTime: Date | null;
14+
data?: any;
15+
error?: any;
1416
};
1517

1618
export type RequestsById = Record<string, SWRRequest[]>;
@@ -25,7 +27,15 @@ export function useRequests(events: EventEmitter | null) {
2527
if (events === null) return;
2628

2729
return events.subscribe(
28-
(type, { id, key }: { id: number; key: string }) => {
30+
(
31+
type,
32+
{
33+
id,
34+
key,
35+
data,
36+
error,
37+
}: { id: number; key: string; data?: any; error?: any }
38+
) => {
2939
setRequestsById((currentRequestsByKey) => {
3040
let channelKey = key;
3141
let channelNum = 0;
@@ -65,6 +75,8 @@ export function useRequests(events: EventEmitter | null) {
6575

6676
activeRequests[id].type = "success";
6777
activeRequests[id].endTime = new Date();
78+
activeRequests[id].data = data;
79+
6880
delete activeRequests[id];
6981
delete activeRequests[channelKey];
7082

@@ -77,6 +89,7 @@ export function useRequests(events: EventEmitter | null) {
7789

7890
activeRequests[id].type = "error";
7991
activeRequests[id].endTime = new Date();
92+
activeRequests[id].error = error;
8093
delete activeRequests[id];
8194
delete activeRequests[channelKey];
8295

packages/swr-devtools/src/createSWRDevTools.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,15 @@ export type DevToolsMessage =
4545
payload: {
4646
key: string;
4747
id: number;
48+
data: any;
4849
};
4950
}
5051
| {
5152
type: "request_error";
5253
payload: {
5354
key: string;
5455
id: number;
56+
error: any;
5557
};
5658
}
5759
| {
@@ -186,13 +188,15 @@ export const createSWRDevtools = () => {
186188
events.emit("request_success", {
187189
key: unstable_serialize(args[1]),
188190
id: requestIdRef.current,
191+
data: convertToSerializableObject(args[0]),
189192
});
190193
window.postMessage(
191194
{
192195
type: "request_success",
193196
payload: {
194197
key: unstable_serialize(args[1]),
195198
id: requestIdRef.current,
199+
data: convertToSerializableObject(args[0]),
196200
},
197201
},
198202
"*"
@@ -203,13 +207,15 @@ export const createSWRDevtools = () => {
203207
events.emit("request_error", {
204208
key: unstable_serialize(args[1]),
205209
id: requestIdRef.current,
210+
error: convertToSerializableObject(args[0]),
206211
});
207212
window.postMessage(
208213
{
209214
type: "request_error",
210215
payload: {
211216
key: unstable_serialize(args[1]),
212217
id: requestIdRef.current,
218+
error: convertToSerializableObject(args[0]),
213219
},
214220
},
215221
"*"

0 commit comments

Comments
 (0)