Skip to content

Commit e320c2e

Browse files
stevenconnerSteve Conner
andauthored
Add support for tron display (#17)
* feat(display): add support for tron displays * add a command to run the node process to package.json --------- Co-authored-by: Steve Conner <steve-ir@Steves-MacBook-Pro.local>
1 parent a5e5daf commit e320c2e

File tree

8 files changed

+186
-29
lines changed

8 files changed

+186
-29
lines changed

app/components/DetailPanel.tsx

Lines changed: 111 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
import { View, Text, ScrollView, type ViewStyle, type TextStyle } from "react-native"
1+
import {
2+
View,
3+
Text,
4+
ScrollView,
5+
type ViewStyle,
6+
type TextStyle,
7+
Image,
8+
type ImageStyle,
9+
Pressable,
10+
Linking,
11+
} from "react-native"
212
import { themed } from "../theme/theme"
313
import { TimelineItem } from "../types"
414
import { TreeView } from "./TreeView"
@@ -31,15 +41,38 @@ export function DetailPanel({ selectedItem, onClose }: DetailPanelProps) {
3141
)
3242
}
3343

44+
const getHeaderTitle = () => {
45+
switch (selectedItem.type) {
46+
case "log":
47+
return "Log Details"
48+
case "display":
49+
return "Display Details"
50+
default:
51+
return "Network Details"
52+
}
53+
}
54+
55+
const renderDetailContent = () => {
56+
switch (selectedItem.type) {
57+
case "log":
58+
return <LogDetailContent item={selectedItem} />
59+
case "display":
60+
return <DisplayDetailContent item={selectedItem} />
61+
case "api.request":
62+
case "api.response":
63+
return <NetworkDetailContent item={selectedItem} />
64+
default:
65+
return null
66+
}
67+
}
68+
3469
return (
3570
<View style={$container()}>
3671
<View style={$header()}>
3772
<View style={$flex}>
3873
<View style={$headerTitleRow}>
3974
<View style={$selectedIndicator()} />
40-
<Text style={$headerTitle()}>
41-
{selectedItem.type === "log" ? "Log Details" : "Network Details"}
42-
</Text>
75+
<Text style={$headerTitle()}>{getHeaderTitle()}</Text>
4376
</View>
4477
<View style={$headerInfo()}>
4578
<Text style={$headerInfoText()}>{formatTime(new Date(selectedItem.date))}</Text>
@@ -69,16 +102,79 @@ export function DetailPanel({ selectedItem, onClose }: DetailPanelProps) {
69102
contentContainerStyle={$scrollContent()}
70103
>
71104
{/* Render appropriate content based on timeline item type */}
72-
{selectedItem.type === "log" ? (
73-
<LogDetailContent item={selectedItem} />
74-
) : (
75-
<NetworkDetailContent item={selectedItem} />
76-
)}
105+
{renderDetailContent()}
77106
</ScrollView>
78107
</View>
79108
)
80109
}
81110

111+
function DisplayDetailContent({ item }: { item: TimelineItem & { type: "display" } }) {
112+
const { payload } = item
113+
const { name, image, preview, ...rest } = payload
114+
115+
const renderImage = () => {
116+
if (image) {
117+
let imgComponent = null
118+
if (typeof image === "string") {
119+
imgComponent = <Image source={{ uri: image }} style={$image()} />
120+
} else {
121+
imgComponent = <Image source={image} style={$image()} />
122+
}
123+
return (
124+
<DetailSection title="Image">
125+
<Pressable
126+
onPress={() => {
127+
Linking.openURL(typeof image === "string" ? image : image.uri)
128+
}}
129+
>
130+
{imgComponent}
131+
</Pressable>
132+
</DetailSection>
133+
)
134+
}
135+
}
136+
137+
return (
138+
<View style={$detailContent()}>
139+
{name ? (
140+
<DetailSection title="Name">
141+
{typeof name === "string" ? (
142+
<Text style={$valueText()}>{name}</Text>
143+
) : (
144+
<TreeView data={name} />
145+
)}
146+
</DetailSection>
147+
) : null}
148+
{preview ? (
149+
<DetailSection title="Preview">
150+
{typeof preview === "string" ? (
151+
<Text style={$valueText()}>{preview}</Text>
152+
) : (
153+
<TreeView data={preview} />
154+
)}
155+
</DetailSection>
156+
) : null}
157+
{renderImage()}
158+
<DetailSection title="Full Payload">
159+
<TreeView data={rest} />
160+
</DetailSection>
161+
<DetailSection title="Metadata">
162+
<TreeView
163+
data={{
164+
id: item.id,
165+
clientId: item.clientId,
166+
connectionId: item.connectionId,
167+
messageId: item.messageId,
168+
important: item.important,
169+
date: item.date,
170+
deltaTime: item.deltaTime,
171+
}}
172+
/>
173+
</DetailSection>
174+
</View>
175+
)
176+
}
177+
82178
/**
83179
* Renders detailed content for log timeline items including level, message, stack trace, and metadata.
84180
*/
@@ -340,3 +436,9 @@ const $errorText = themed<TextStyle>(({ colors, typography }) => ({
340436
fontSize: typography.body,
341437
fontFamily: typography.code.normal,
342438
}))
439+
440+
const $image = themed<ImageStyle>(() => ({
441+
width: 200,
442+
height: 200,
443+
resizeMode: "contain",
444+
}))
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { TimelineItemDisplay } from "../types"
2+
import { TimelineItem } from "./TimelineItem"
3+
4+
type TimelineDisplayItemProps = {
5+
item: TimelineItemDisplay
6+
isSelected?: boolean
7+
onSelect?: () => void
8+
}
9+
10+
/**
11+
* A single display item in the timeline.
12+
*/
13+
export function TimelineDisplayItem({
14+
item,
15+
isSelected = false,
16+
onSelect,
17+
}: TimelineDisplayItemProps) {
18+
const { payload, date, deltaTime, important } = item
19+
20+
// Type guard to ensure this is a display item
21+
if (item.type !== "display") return null
22+
23+
return (
24+
<TimelineItem
25+
title={payload.name}
26+
date={new Date(date)}
27+
deltaTime={deltaTime}
28+
preview={payload.preview ?? ""}
29+
isImportant={important}
30+
isTagged={important}
31+
isSelected={isSelected}
32+
onSelect={onSelect}
33+
/>
34+
)
35+
}

app/components/TimelineToolbar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type FilterType = "all" | "log" | "api.request" | "api.response"
1+
export type FilterType = "all" | "log" | "display" | "api.request" | "api.response"
22
// export type LogLevel = "all" | "debug" | "warn" | "error"
33
// export type SortBy = "time-newest" | "time-oldest" | "type" | "level"
44

app/components/TreeView.tsx

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -112,29 +112,28 @@ export function TreeView({ data, path = [], level = 0, onNodePress }: TreeViewPr
112112
<>
113113
{/* Show this root node value */}
114114
<Pressable style={$nodeRow(level)} onPress={handlePress}>
115-
{isExpandable && <Text style={$expandIcon()}>{isExpanded ? "▼" : "▶"}</Text>}
115+
{isExpandable ? <Text style={$expandIcon()}>{isExpanded ? "▼" : "▶"}</Text> : null}
116116
<Text style={$nodeLabel()}>{label}</Text>
117117
{renderValue()}
118118
</Pressable>
119119

120120
{/* If has children, loop TreeView */}
121-
{isExpandable &&
122-
isExpanded &&
123-
level < MAX_LEVEL &&
124-
getChildren().map(({ key, label: childLabel, value }) => (
125-
<TreeView
126-
key={key}
127-
data={value}
128-
path={[...path, childLabel]}
129-
level={level + 1}
130-
onNodePress={onNodePress}
131-
/>
132-
))}
133-
{isExpandable && isExpanded && level >= MAX_LEVEL && (
121+
{isExpandable && isExpanded && level < MAX_LEVEL
122+
? getChildren().map(({ key, label: childLabel, value }) => (
123+
<TreeView
124+
key={key}
125+
data={value}
126+
path={[...path, childLabel]}
127+
level={level + 1}
128+
onNodePress={onNodePress}
129+
/>
130+
))
131+
: null}
132+
{isExpandable && isExpanded && level >= MAX_LEVEL ? (
134133
<Text pointerEvents="none" style={$defaultValue()}>
135134
{JSON.stringify(data, null, 2)}
136135
</Text>
137-
)}
136+
) : null}
138137
</>
139138
)
140139
}

app/screens/TimelineScreen.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useGlobal } from "../state/useGlobal"
22
import { TimelineItem } from "../types"
33
import { TimelineLogItem } from "../components/TimelineLogItem"
44
import { TimelineNetworkItem } from "../components/TimelineNetworkItem"
5+
import { TimelineDisplayItem } from "../components/TimelineDisplayItem"
56
import { DetailPanel } from "../components/DetailPanel"
67
import { ResizableDivider } from "../components/ResizableDivider"
78
import { LegendList } from "@legendapp/list"
@@ -35,6 +36,9 @@ const TimelineItemRenderer = ({
3536
if (item.type === "log") {
3637
return <TimelineLogItem item={item} isSelected={isSelected} onSelect={handleSelectItem} />
3738
}
39+
if (item.type === "display") {
40+
return <TimelineDisplayItem item={item} isSelected={isSelected} onSelect={handleSelectItem} />
41+
}
3842
if (item.type === "api.response") {
3943
return <TimelineNetworkItem item={item} isSelected={isSelected} onSelect={handleSelectItem} />
4044
}
@@ -44,7 +48,7 @@ const TimelineItemRenderer = ({
4448

4549
export function TimelineScreen() {
4650
// TODO: Use a global state for the filters, set by the user in the TimelineToolbar
47-
const timelineItems = useTimeline({ types: ["log", "api.request", "api.response"] })
51+
const timelineItems = useTimeline({ types: ["log", "display", "api.request", "api.response"] })
4852
const [timelineWidth, setTimelineWidth] = useGlobal<number>("timelineWidth", 300, {
4953
persist: true,
5054
})

app/state/connectToServer.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,11 @@ export function connectToServer(props: { port: number } = { port: 9292 }): Unsub
8282
if (data.type === "command") {
8383
if (data.cmd.type === "clear") setTimelineItems([])
8484

85-
if (data.cmd.type === "log" || data.cmd.type === "api.response") {
85+
if (
86+
data.cmd.type === "log" ||
87+
data.cmd.type === "api.response" ||
88+
data.cmd.type === "display"
89+
) {
8690
// Add a unique ID to the timeline item
8791
data.cmd.id = `${data.cmd.clientId}-${data.cmd.messageId}`
8892

app/types.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ export type LogPayload =
5858
}
5959
| ErrorLogPayload
6060

61+
export interface DisplayPayload {
62+
name: string
63+
value: any
64+
preview: string
65+
image: string | { uri: string }
66+
}
67+
6168
export interface NetworkRequest {
6269
url: string
6370
method: string
@@ -101,4 +108,9 @@ export type TimelineItemNetwork = TimelineItemBase & {
101108
payload: NetworkPayload
102109
}
103110

104-
export type TimelineItem = TimelineItemLog | TimelineItemNetwork
111+
export type TimelineItemDisplay = TimelineItemBase & {
112+
type: "display"
113+
payload: DisplayPayload
114+
}
115+
116+
export type TimelineItem = TimelineItemLog | TimelineItemNetwork | TimelineItemDisplay

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"ci": "npm run lint",
1818
"start": "REACT_NATIVE_PATH=./node_modules/react-native-macos RCT_SCRIPT_RN_DIR=$REACT_NATIVE_PATH RCT_NEW_ARCH_ENABLED=1 ./node_modules/react-native-macos/scripts/packager.sh start",
1919
"test": "jest",
20-
"postinstall": "ln -sf $(pwd)/node_modules/react-native-macos $(pwd)/node_modules/react-native && patch-package"
20+
"postinstall": "ln -sf $(pwd)/node_modules/react-native-macos $(pwd)/node_modules/react-native && patch-package",
21+
"node-process": "node -e \"require('./standalone-server').startReactotronServer({ port: 9292 })\""
2122
},
2223
"dependencies": {
2324
"@expo-google-fonts/space-grotesk": "^0.3.0",

0 commit comments

Comments
 (0)