|
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" |
2 | 12 | import { themed } from "../theme/theme" |
3 | 13 | import { TimelineItem } from "../types" |
4 | 14 | import { TreeView } from "./TreeView" |
@@ -31,15 +41,38 @@ export function DetailPanel({ selectedItem, onClose }: DetailPanelProps) { |
31 | 41 | ) |
32 | 42 | } |
33 | 43 |
|
| 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 | + |
34 | 69 | return ( |
35 | 70 | <View style={$container()}> |
36 | 71 | <View style={$header()}> |
37 | 72 | <View style={$flex}> |
38 | 73 | <View style={$headerTitleRow}> |
39 | 74 | <View style={$selectedIndicator()} /> |
40 | | - <Text style={$headerTitle()}> |
41 | | - {selectedItem.type === "log" ? "Log Details" : "Network Details"} |
42 | | - </Text> |
| 75 | + <Text style={$headerTitle()}>{getHeaderTitle()}</Text> |
43 | 76 | </View> |
44 | 77 | <View style={$headerInfo()}> |
45 | 78 | <Text style={$headerInfoText()}>{formatTime(new Date(selectedItem.date))}</Text> |
@@ -69,16 +102,79 @@ export function DetailPanel({ selectedItem, onClose }: DetailPanelProps) { |
69 | 102 | contentContainerStyle={$scrollContent()} |
70 | 103 | > |
71 | 104 | {/* Render appropriate content based on timeline item type */} |
72 | | - {selectedItem.type === "log" ? ( |
73 | | - <LogDetailContent item={selectedItem} /> |
74 | | - ) : ( |
75 | | - <NetworkDetailContent item={selectedItem} /> |
76 | | - )} |
| 105 | + {renderDetailContent()} |
77 | 106 | </ScrollView> |
78 | 107 | </View> |
79 | 108 | ) |
80 | 109 | } |
81 | 110 |
|
| 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 | + |
82 | 178 | /** |
83 | 179 | * Renders detailed content for log timeline items including level, message, stack trace, and metadata. |
84 | 180 | */ |
@@ -340,3 +436,9 @@ const $errorText = themed<TextStyle>(({ colors, typography }) => ({ |
340 | 436 | fontSize: typography.body, |
341 | 437 | fontFamily: typography.code.normal, |
342 | 438 | })) |
| 439 | + |
| 440 | +const $image = themed<ImageStyle>(() => ({ |
| 441 | + width: 200, |
| 442 | + height: 200, |
| 443 | + resizeMode: "contain", |
| 444 | +})) |
0 commit comments