react-native-inapp-inspector 2.3.14 → 2.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/commonjs/components/AnimatedEntrance.js +3 -3
  2. package/dist/commonjs/components/ConsoleLogCard.js +174 -100
  3. package/dist/commonjs/components/Inspector/ConsoleTab.js +25 -16
  4. package/dist/commonjs/components/Inspector/CrashTab.js +19 -5
  5. package/dist/commonjs/components/Inspector/DeviceInfoTab.js +198 -98
  6. package/dist/commonjs/components/Inspector/InspectorHeader.js +2 -2
  7. package/dist/commonjs/components/Inspector/MainScreen.js +56 -56
  8. package/dist/commonjs/components/Inspector/NetworkTab.js +1 -335
  9. package/dist/commonjs/components/Inspector/PerformanceTab.js +32 -6
  10. package/dist/commonjs/components/Inspector/ReduxTab.js +3 -1
  11. package/dist/commonjs/components/Inspector/StorageTab.js +23 -5
  12. package/dist/commonjs/components/JsonViewer.js +14 -2
  13. package/dist/commonjs/components/LogCard.js +428 -227
  14. package/dist/commonjs/constants/version.d.ts +1 -1
  15. package/dist/commonjs/constants/version.js +1 -1
  16. package/dist/commonjs/customHooks/analyticsLogger.js +1 -1
  17. package/dist/commonjs/customHooks/consoleLogger.js +34 -2
  18. package/dist/commonjs/customHooks/crashHandler.js +1 -1
  19. package/dist/commonjs/customHooks/networkLogger.js +184 -26
  20. package/dist/commonjs/helpers/searchQueryParser.d.ts +1 -1
  21. package/dist/commonjs/helpers/searchQueryParser.js +37 -291
  22. package/dist/commonjs/index.js +141 -27
  23. package/dist/esm/components/AnimatedEntrance.js +3 -3
  24. package/dist/esm/components/ConsoleLogCard.js +141 -100
  25. package/dist/esm/components/Inspector/ConsoleTab.js +25 -16
  26. package/dist/esm/components/Inspector/CrashTab.js +19 -5
  27. package/dist/esm/components/Inspector/DeviceInfoTab.js +198 -98
  28. package/dist/esm/components/Inspector/InspectorHeader.js +3 -3
  29. package/dist/esm/components/Inspector/MainScreen.js +56 -56
  30. package/dist/esm/components/Inspector/NetworkTab.js +2 -336
  31. package/dist/esm/components/Inspector/PerformanceTab.js +32 -6
  32. package/dist/esm/components/Inspector/ReduxTab.js +3 -1
  33. package/dist/esm/components/Inspector/StorageTab.js +23 -5
  34. package/dist/esm/components/JsonViewer.js +14 -2
  35. package/dist/esm/components/LogCard.js +422 -221
  36. package/dist/esm/constants/version.d.ts +1 -1
  37. package/dist/esm/constants/version.js +1 -1
  38. package/dist/esm/customHooks/analyticsLogger.js +1 -1
  39. package/dist/esm/customHooks/consoleLogger.js +34 -2
  40. package/dist/esm/customHooks/crashHandler.js +1 -1
  41. package/dist/esm/customHooks/networkLogger.js +184 -26
  42. package/dist/esm/helpers/searchQueryParser.d.ts +1 -1
  43. package/dist/esm/helpers/searchQueryParser.js +37 -291
  44. package/dist/esm/index.js +141 -27
  45. package/package.json +1 -1
  46. package/src/components/AnimatedEntrance.tsx +3 -3
  47. package/src/components/ConsoleLogCard.tsx +154 -103
  48. package/src/components/Inspector/ConsoleTab.tsx +27 -18
  49. package/src/components/Inspector/CrashTab.tsx +19 -5
  50. package/src/components/Inspector/DeviceInfoTab.tsx +224 -102
  51. package/src/components/Inspector/InspectorHeader.tsx +5 -3
  52. package/src/components/Inspector/MainScreen.tsx +2 -3
  53. package/src/components/Inspector/NetworkTab.tsx +1 -402
  54. package/src/components/Inspector/PerformanceTab.tsx +36 -7
  55. package/src/components/Inspector/ReduxTab.tsx +9 -1
  56. package/src/components/Inspector/StorageTab.tsx +27 -7
  57. package/src/components/JsonViewer.tsx +15 -2
  58. package/src/components/LogCard.tsx +528 -339
  59. package/src/constants/version.ts +1 -1
  60. package/src/customHooks/analyticsLogger.ts +1 -1
  61. package/src/customHooks/consoleLogger.ts +36 -3
  62. package/src/customHooks/crashHandler.ts +1 -1
  63. package/src/customHooks/networkLogger.ts +214 -26
  64. package/src/helpers/searchQueryParser.ts +40 -285
  65. package/src/index.tsx +332 -211
@@ -1,5 +1,5 @@
1
1
  import {Method} from '../types';
2
- import React, {useEffect, useRef} from 'react';
2
+ import React, {useEffect, useRef, useMemo} from 'react';
3
3
  import {
4
4
  Alert,
5
5
  Animated,
@@ -17,6 +17,8 @@ import {
17
17
  getDurationColor,
18
18
  formatDateTime,
19
19
  getSize,
20
+ getPath,
21
+ getBaseUrl,
20
22
  } from '../helpers';
21
23
  import {
22
24
  CalendarIcon,
@@ -30,7 +32,6 @@ import {
30
32
  PinIcon,
31
33
  } from './NetworkIcons';
32
34
  import {AppFonts} from '../styles/AppFonts';
33
- import styles from '../styles';
34
35
  import {LogCardProps} from '../types';
35
36
  import HighlightText from './HighlightText';
36
37
  import TouchableScale from './TouchableScale';
@@ -49,6 +50,21 @@ const LogCard = React.memo(function LogCard({
49
50
  const {t} = useTranslation();
50
51
  const methodColor = METHOD_COLORS[item.method as Method] ?? METHOD_COLORS.ALL;
51
52
 
53
+ const urlParsed = useMemo(() => {
54
+ try {
55
+ const u = new URL(item.url);
56
+ const host = u.host;
57
+ const path = u.pathname + (u.search ? u.search : '');
58
+ const isHttps = u.protocol === 'https:';
59
+ return {host, path: path || '/', isHttps};
60
+ } catch {
61
+ const isHttps = item.url.toLowerCase().startsWith('https:');
62
+ const path = getPath(item.url);
63
+ const host = getBaseUrl(item.url).replace(/^https?:\/\//, '');
64
+ return {host, path: path || item.url, isHttps};
65
+ }
66
+ }, [item.url]);
67
+
52
68
  const handleOpenUrl = (e?: any) => {
53
69
  e?.stopPropagation?.();
54
70
  Alert.alert(
@@ -86,15 +102,6 @@ const LogCard = React.memo(function LogCard({
86
102
  ? AppColors.errorColor
87
103
  : AppColors.greenColor;
88
104
 
89
- const leftPercent =
90
- timelineTotalRange > 0
91
- ? ((item.startTime - timelineMinStart) / timelineTotalRange) * 100
92
- : 0;
93
- const widthPercent =
94
- timelineTotalRange > 0
95
- ? ((item.duration || 10) / timelineTotalRange) * 100
96
- : 100;
97
-
98
105
  const shimmerOpacity = useRef(new Animated.Value(isNew ? 0.35 : 0)).current;
99
106
  useEffect(() => {
100
107
  if (isNew) {
@@ -106,398 +113,580 @@ const LogCard = React.memo(function LogCard({
106
113
  }
107
114
  }, [isNew]);
108
115
 
116
+ const getStatusText = () => {
117
+ if (isLoading) return '...';
118
+ if (item.status === 0 || item.status == null) {
119
+ return t('network.statusFailed') || 'FAILED';
120
+ }
121
+ const num = typeof item.status === 'number' ? item.status : parseInt(String(item.status || 0), 10);
122
+ if (num === 200) return '200 OK';
123
+ if (num === 201) return '201 Created';
124
+ if (num === 204) return '204 No Content';
125
+ if (num === 304) return '304 Not Modified';
126
+ if (num === 400) return '400 Bad Req';
127
+ if (num === 401) return '401 Unauth';
128
+ if (num === 403) return '403 Forbidden';
129
+ if (num === 404) return '404 Not Found';
130
+ if (num === 500) return '500 Error';
131
+ return String(item.status);
132
+ };
133
+
109
134
  const renderStatusIcon = () => {
110
135
  if (isLoading) {
111
- return <ClockIcon color={AppColors.darkOrange} size={9} />;
136
+ return <ClockIcon color={AppColors.darkOrange} size={10} />;
112
137
  }
113
138
  const num = typeof item.status === 'number' ? item.status : parseInt(String(item.status || 0), 10);
114
139
  if (num >= 200 && num < 300) {
115
- return <CircleCheckIcon color={AppColors.greenColor} size={9} />;
140
+ return <CircleCheckIcon color={AppColors.greenColor} size={10} />;
116
141
  }
117
142
  if (num >= 300 && num < 400) {
118
- return <RepeatIcon color={AppColors.amber700} size={9} />;
143
+ return <RepeatIcon color={AppColors.amber700} size={10} />;
119
144
  }
120
145
  if (num >= 400 && num < 500) {
121
- return <CircleAlertIcon color={AppColors.darkOrange} size={9} />;
146
+ return <CircleAlertIcon color={AppColors.darkOrange} size={10} />;
122
147
  }
123
- return <CircleXIcon color={AppColors.errorColor} size={9} />;
148
+ return <CircleXIcon color={AppColors.errorColor} size={10} />;
124
149
  };
125
150
 
126
151
  const triggeredAt = formatDateTime(item.startTime);
127
152
  const isJson = item.url.split('?')[0].toLowerCase().endsWith('.json');
128
153
 
129
154
  return (
130
- <TouchableScale
131
- onPress={onPress}
132
- style={[
133
- styles.card,
134
- {
135
- borderLeftWidth: 3.5,
136
- borderLeftColor: cardStatusColor,
137
- },
138
- ]}>
139
- <View style={styles.cardBody}>
140
- {/* Row 1: Header (Checkbox, Serial, Method Badge, Client Tag, Status Pill) */}
141
- <View style={styles.cardHeaderRow}>
142
- <View style={styles.cardHeaderLeft}>
143
- <Pressable
144
- onPress={() => onToggleSelect(item.id)}
145
- hitSlop={12}
146
- style={[
147
- styles.smallCheckbox,
148
- isSelected && styles.smallCheckboxChecked,
149
- ]}>
150
- {isSelected && (
151
- <Svg width={9} height={9} viewBox="0 0 24 24" fill="none">
152
- <Path
153
- d="M20 6L9 17l-5-5"
154
- stroke={AppColors.white}
155
- strokeWidth="4"
156
- strokeLinecap="round"
157
- strokeLinejoin="round"
158
- />
159
- </Svg>
160
- )}
161
- </Pressable>
155
+ <View style={styles.container}>
156
+ <TouchableScale
157
+ onPress={onPress}
158
+ style={[
159
+ styles.card,
160
+ {
161
+ borderLeftWidth: 3.5,
162
+ borderLeftColor: cardStatusColor,
163
+ backgroundColor: isFailed ? '#FFF8F8' : AppColors.white,
164
+ },
165
+ ]}>
166
+ <View style={styles.cardBody}>
167
+ {/* Row 1: Header (Checkbox, Serial, Method Badge, Client Tag, Protocol, Status Pill) */}
168
+ <View style={styles.cardHeaderRow}>
169
+ <View style={styles.cardHeaderLeft}>
170
+ <Pressable
171
+ onPress={() => onToggleSelect(item.id)}
172
+ hitSlop={12}
173
+ style={[
174
+ styles.smallCheckbox,
175
+ isSelected && styles.smallCheckboxChecked,
176
+ ]}>
177
+ {isSelected && (
178
+ <Svg width={9} height={9} viewBox="0 0 24 24" fill="none">
179
+ <Path
180
+ d="M20 6L9 17l-5-5"
181
+ stroke={AppColors.white}
182
+ strokeWidth="4"
183
+ strokeLinecap="round"
184
+ strokeLinejoin="round"
185
+ />
186
+ </Svg>
187
+ )}
188
+ </Pressable>
162
189
 
163
- <Text style={styles.serialNumber}>#{item.id + 1}</Text>
190
+ <Text style={styles.serialNumber}>#{item.id + 1}</Text>
164
191
 
165
- <View
166
- style={[styles.methodBadge, {backgroundColor: methodColor}]}>
167
- <Text style={[styles.methodBadgeText, {color: AppColors.white}]}>
168
- {item.method}
169
- </Text>
170
- </View>
171
-
172
- {item.client && (
173
192
  <View
174
- style={[
175
- styles.chip,
176
- {
177
- backgroundColor:
178
- item.client === 'axios'
179
- ? `${AppColors.purple}14`
180
- : item.client === 'apollo' || item.client === 'graphql'
181
- ? `${AppColors.pink500}14`
182
- : item.client === 'xhr'
183
- ? `${AppColors.amber500}14`
184
- : `${AppColors.sky500}14`,
185
- borderColor:
186
- item.client === 'axios'
187
- ? `${AppColors.purple}30`
188
- : item.client === 'apollo' || item.client === 'graphql'
189
- ? `${AppColors.pink500}30`
190
- : item.client === 'xhr'
191
- ? `${AppColors.amber500}30`
192
- : `${AppColors.sky500}30`,
193
- paddingHorizontal: 4.5,
194
- paddingVertical: 1,
195
- borderRadius: 4,
196
- },
197
- ]}>
198
- <Text
193
+ style={[styles.methodBadge, {backgroundColor: methodColor}]}>
194
+ <Text style={styles.methodBadgeText}>
195
+ {item.method}
196
+ </Text>
197
+ </View>
198
+
199
+ {item.client && (
200
+ <View
199
201
  style={[
200
- styles.chipText,
202
+ styles.chip,
201
203
  {
202
- fontFamily: AppFonts.interBold,
203
- fontSize: 8.5,
204
- color:
204
+ backgroundColor:
205
+ item.client === 'axios'
206
+ ? `${AppColors.purple}14`
207
+ : item.client === 'apollo' || item.client === 'graphql'
208
+ ? `${AppColors.pink500}14`
209
+ : item.client === 'xhr'
210
+ ? `${AppColors.amber500}14`
211
+ : `${AppColors.sky500}14`,
212
+ borderColor:
205
213
  item.client === 'axios'
206
- ? AppColors.purple
214
+ ? `${AppColors.purple}30`
207
215
  : item.client === 'apollo' || item.client === 'graphql'
208
- ? AppColors.pink500
216
+ ? `${AppColors.pink500}30`
209
217
  : item.client === 'xhr'
210
- ? AppColors.amber700
211
- : AppColors.sky600,
218
+ ? `${AppColors.amber500}30`
219
+ : `${AppColors.sky500}30`,
212
220
  },
213
221
  ]}>
214
- {item.client.toUpperCase()}
215
- </Text>
216
- </View>
217
- )}
222
+ <Text
223
+ style={[
224
+ styles.chipText,
225
+ {
226
+ color:
227
+ item.client === 'axios'
228
+ ? AppColors.purple
229
+ : item.client === 'apollo' || item.client === 'graphql'
230
+ ? AppColors.pink500
231
+ : item.client === 'xhr'
232
+ ? AppColors.amber700
233
+ : AppColors.sky600,
234
+ },
235
+ ]}>
236
+ {item.client.toUpperCase()}
237
+ </Text>
238
+ </View>
239
+ )}
240
+
241
+ {/* GraphQL Indicator */}
242
+ {(item.url.toLowerCase().includes('graphql') ||
243
+ item.client === 'apollo' ||
244
+ item.client === 'graphql') && (
245
+ <View
246
+ style={[
247
+ styles.chip,
248
+ {
249
+ backgroundColor: AppColors.roseBg,
250
+ borderColor: AppColors.roseBorder,
251
+ },
252
+ ]}>
253
+ <Text
254
+ style={[
255
+ styles.chipText,
256
+ {color: AppColors.pink600},
257
+ ]}>
258
+ GQL
259
+ </Text>
260
+ </View>
261
+ )}
262
+ </View>
218
263
 
219
- {/* Protocol / HTTPS Badge */}
220
264
  <View
221
265
  style={[
222
- styles.chip,
266
+ styles.statusPill,
223
267
  {
224
- backgroundColor: item.url.toLowerCase().startsWith('https')
225
- ? AppColors.mintGreenBg
226
- : AppColors.amberWarmBg,
227
- borderColor: item.url.toLowerCase().startsWith('https')
228
- ? AppColors.mintGreenBorder
229
- : AppColors.amberWarmBorder,
230
- paddingHorizontal: 4,
231
- paddingVertical: 1,
232
- borderRadius: 4,
268
+ backgroundColor: isFailed
269
+ ? `${AppColors.errorColor}14`
270
+ : isLoading
271
+ ? `${AppColors.darkOrange}14`
272
+ : `${statusColor}14`,
273
+ borderColor: isFailed
274
+ ? `${AppColors.errorColor}33`
275
+ : isLoading
276
+ ? `${AppColors.darkOrange}33`
277
+ : `${statusColor}33`,
233
278
  },
234
279
  ]}>
280
+ {renderStatusIcon()}
235
281
  <Text
236
282
  style={[
237
- styles.chipText,
283
+ styles.statusPillText,
238
284
  {
239
- fontFamily: AppFonts.interBold,
240
- fontSize: 8,
241
- color: item.url.toLowerCase().startsWith('https')
242
- ? AppColors.mintGreenText
243
- : AppColors.amber700,
285
+ color: isFailed
286
+ ? AppColors.errorColor
287
+ : isLoading
288
+ ? AppColors.darkOrange
289
+ : statusColor,
244
290
  },
245
291
  ]}>
246
- {item.url.toLowerCase().startsWith('https') ? 'HTTPS' : 'HTTP'}
292
+ {getStatusText()}
247
293
  </Text>
248
294
  </View>
249
-
250
- {/* GraphQL Indicator */}
251
- {(item.url.toLowerCase().includes('graphql') ||
252
- item.client === 'apollo' ||
253
- item.client === 'graphql') && (
254
- <View
255
- style={[
256
- styles.chip,
257
- {
258
- backgroundColor: AppColors.roseBg,
259
- borderColor: AppColors.roseBorder,
260
- paddingHorizontal: 4,
261
- paddingVertical: 1,
262
- borderRadius: 4,
263
- },
264
- ]}>
265
- <Text
266
- style={[
267
- styles.chipText,
268
- {
269
- fontFamily: AppFonts.interBold,
270
- fontSize: 8,
271
- color: AppColors.pink600,
272
- },
273
- ]}>
274
- GQL
275
- </Text>
276
- </View>
277
- )}
278
- </View>
279
-
280
- <View
281
- style={[
282
- styles.statusPill,
283
- {
284
- flexDirection: 'row',
285
- alignItems: 'center',
286
- gap: 3.5,
287
- backgroundColor: isFailed
288
- ? `${AppColors.errorColor}15`
289
- : isLoading
290
- ? `${AppColors.darkOrange}15`
291
- : `${statusColor}15`,
292
- borderColor: isFailed
293
- ? `${AppColors.errorColor}30`
294
- : isLoading
295
- ? `${AppColors.darkOrange}30`
296
- : `${statusColor}30`,
297
- flexShrink: 0,
298
- },
299
- ]}>
300
- {renderStatusIcon()}
301
- <Text
302
- style={[
303
- styles.statusPillText,
304
- {
305
- color: isFailed
306
- ? AppColors.errorColor
307
- : isLoading
308
- ? AppColors.darkOrange
309
- : statusColor,
310
- },
311
- ]}>
312
- {isLoading
313
- ? '...'
314
- : isFailed
315
- ? (item.status ? `${item.status}` : t('network.statusFailed'))
316
- : item.status}
317
- </Text>
318
295
  </View>
319
- </View>
320
296
 
321
- {/* Row 2: Full URL Capsule Container (Clickable with link prompt) */}
322
- <View style={styles.cardSlugBox}>
297
+ {/* Row 2: Clean Smart URL Container (Path prominent, Host subtitle) */}
323
298
  <Pressable
324
299
  onPress={handleOpenUrl}
325
- style={styles.slugLeft}
326
- hitSlop={6}>
327
- <View
328
- style={{
329
- width: 17,
330
- height: 17,
331
- borderRadius: 4,
332
- backgroundColor: `${AppColors.skyBlue}1A`,
333
- alignItems: 'center',
334
- justifyContent: 'center',
335
- marginRight: 4,
336
- flexShrink: 0,
337
- }}>
338
- <GlobeIcon color={AppColors.skyBlue} size={11} />
339
- </View>
340
- <HighlightText
341
- text={item.url}
342
- search={searchStr}
343
- style={[styles.slugText, {color: AppColors.skyBlue, textDecorationLine: 'underline'}]}
344
- highlightStyle={styles.highlight}
345
- numberOfLines={2}
346
- ellipsizeMode="middle"
347
- />
348
- </Pressable>
349
-
350
- <View style={styles.slugRight}>
351
- {isJson && (
352
- <View
353
- style={[
354
- styles.chip,
355
- {
356
- backgroundColor: `${AppColors.darkOrange}12`,
357
- borderColor: `${AppColors.darkOrange}28`,
358
- },
359
- ]}>
360
- <Text style={[styles.chipText, {color: AppColors.darkOrange}]}>
361
- .json
362
- </Text>
300
+ hitSlop={6}
301
+ style={styles.urlBox}>
302
+ <View style={styles.urlMainRow}>
303
+ <HighlightText
304
+ text={urlParsed.path}
305
+ search={searchStr}
306
+ style={styles.pathText}
307
+ highlightStyle={styles.highlight}
308
+ numberOfLines={2}
309
+ ellipsizeMode="middle"
310
+ />
311
+ <View style={styles.urlBadgeRow}>
312
+ {isJson && (
313
+ <View style={styles.jsonBadge}>
314
+ <Text style={styles.jsonBadgeText}>.json</Text>
315
+ </View>
316
+ )}
317
+ {item.duplicateCount != null && item.duplicateCount > 1 && (
318
+ <View style={styles.dupBadge}>
319
+ <Text style={styles.dupBadgeText}>×{item.duplicateCount}</Text>
320
+ </View>
321
+ )}
363
322
  </View>
364
- )}
323
+ </View>
365
324
 
366
- {item.duplicateCount != null && item.duplicateCount > 1 && (
367
- <View
368
- style={[
369
- styles.chip,
370
- {
371
- backgroundColor: `${AppColors.purple}12`,
372
- borderColor: `${AppColors.purple}28`,
373
- },
374
- ]}>
375
- <Text
325
+ {urlParsed.host ? (
326
+ <View style={styles.hostRow}>
327
+ <View
376
328
  style={[
377
- styles.chipText,
378
- {color: AppColors.purple, fontWeight: '700'},
329
+ styles.protoBadge,
330
+ {
331
+ backgroundColor: urlParsed.isHttps
332
+ ? `${AppColors.emerald600}12`
333
+ : `${AppColors.amber600}12`,
334
+ borderColor: urlParsed.isHttps
335
+ ? `${AppColors.emerald600}2B`
336
+ : `${AppColors.amber600}2B`,
337
+ },
379
338
  ]}>
380
- ×{item.duplicateCount}
339
+ <Text
340
+ style={[
341
+ styles.protoBadgeText,
342
+ {
343
+ color: urlParsed.isHttps
344
+ ? AppColors.emerald600
345
+ : AppColors.amber700,
346
+ },
347
+ ]}>
348
+ {urlParsed.isHttps ? 'HTTPS' : 'HTTP'}
349
+ </Text>
350
+ </View>
351
+ <Text
352
+ style={styles.hostText}
353
+ numberOfLines={1}
354
+ ellipsizeMode="tail">
355
+ {urlParsed.host}
381
356
  </Text>
382
357
  </View>
383
- )}
384
- </View>
385
- </View>
358
+ ) : null}
359
+ </Pressable>
360
+
361
+ {/* Row 3: Footer (Timestamp & Origin on Left, Response Size & Duration on Right) */}
362
+ <View style={styles.cardFooterRow}>
363
+ <View style={styles.footerLeft}>
364
+ <View style={styles.cardDateRow}>
365
+ <CalendarIcon color={AppColors.grayTextWeak} size={10} />
366
+ <Text style={styles.cardDateText}>{triggeredAt}</Text>
367
+ </View>
386
368
 
387
- {/* Row 3: Footer (Timestamp & Origin on Left, Response Size & Duration on Right) */}
388
- <View style={styles.cardFooterRow}>
389
- <View style={{flexDirection: 'row', alignItems: 'center', gap: 6, flex: 1, minWidth: 0, marginRight: 8}}>
390
- <View style={[styles.cardDateRow, {flexShrink: 0}]}>
391
- <CalendarIcon color={AppColors.grayTextWeak} size={10} />
392
- <Text style={styles.cardDateText}>{triggeredAt}</Text>
369
+ {item.caller && item.caller !== 'Unknown' && (
370
+ <View style={styles.callerChip}>
371
+ <PinIcon color={AppColors.sky600} size={8.5} />
372
+ <Text
373
+ style={styles.callerChipText}
374
+ numberOfLines={1}
375
+ ellipsizeMode="middle">
376
+ {item.caller}
377
+ </Text>
378
+ </View>
379
+ )}
393
380
  </View>
394
381
 
395
- {item.caller && item.caller !== 'Unknown' && (
396
- <View
397
- style={[
398
- styles.chip,
399
- {
400
- flexDirection: 'row',
401
- alignItems: 'center',
402
- gap: 3,
403
- backgroundColor: `${AppColors.skyBlue}14`,
404
- borderColor: `${AppColors.skyBlue}30`,
405
- paddingHorizontal: 5,
406
- paddingVertical: 1,
407
- borderRadius: 4,
408
- flexShrink: 1,
409
- minWidth: 0,
410
- },
411
- ]}>
412
- <PinIcon color={AppColors.skyBlue} size={8.5} />
413
- <Text
382
+ <View style={styles.footerRight}>
383
+ {item.response != null && (
384
+ <View style={styles.metaStatChip}>
385
+ <SizeIcon color={AppColors.purple} size={9} />
386
+ <Text style={styles.metaStatText}>
387
+ {getSize(item.response)}
388
+ </Text>
389
+ </View>
390
+ )}
391
+
392
+ {item.duration != null && !isFailed && (
393
+ <View
414
394
  style={[
415
- styles.chipText,
395
+ styles.metaStatChip,
416
396
  {
417
- color: AppColors.skyBlue,
418
- fontSize: 8.5,
419
- fontFamily: AppFonts.interMedium,
397
+ backgroundColor: `${durationColor}12`,
398
+ borderColor: `${durationColor}2E`,
420
399
  },
421
- ]}
422
- numberOfLines={1}
423
- ellipsizeMode="middle">
424
- {item.caller}
425
- </Text>
426
- </View>
427
- )}
428
- </View>
429
-
430
- <View style={styles.cardFooterRight}>
431
- {item.response != null && (
432
- <View
433
- style={[
434
- styles.chip,
435
- {
436
- backgroundColor: `${AppColors.purple}12`,
437
- borderColor: `${AppColors.purple}2E`,
438
- },
439
- ]}>
440
- <SizeIcon color={AppColors.purple} size={9} />
441
- <Text
442
- style={[
443
- styles.chipText,
444
- {color: AppColors.purple, fontSize: 9.5},
445
400
  ]}>
446
- {getSize(item.response)}
447
- </Text>
448
- </View>
449
- )}
401
+ <ClockIcon color={durationColor} size={9} />
402
+ <Text style={[styles.metaStatText, {color: durationColor}]}>
403
+ {item.duration}ms
404
+ </Text>
405
+ </View>
406
+ )}
407
+ </View>
408
+ </View>
450
409
 
451
- {item.duration != null && !isFailed && (
410
+ {/* Mini Latency Timing Waterfall Bar */}
411
+ {typeof item.duration === 'number' && item.duration > 0 && !isFailed && (
412
+ <View style={styles.waterfallContainer}>
452
413
  <View
453
414
  style={[
454
- styles.chip,
415
+ styles.waterfallBar,
455
416
  {
456
- backgroundColor: `${durationColor}15`,
457
- borderColor: `${durationColor}30`,
417
+ width: `${Math.min(100, Math.max(6, (item.duration / 1200) * 100))}%`,
418
+ backgroundColor: durationColor,
458
419
  },
459
- ]}>
460
- <ClockIcon color={durationColor} size={9} />
461
- <Text style={[styles.chipText, {color: durationColor, fontSize: 9.5}]}>
462
- {item.duration}ms
463
- </Text>
464
- </View>
465
- )}
466
- </View>
467
- </View>
468
-
469
- {/* Mini Latency Timing Waterfall Bar */}
470
- {typeof item.duration === 'number' && item.duration > 0 && !isFailed && (
471
- <View style={{marginTop: 6, gap: 2}}>
472
- <View
473
- style={{
474
- height: 2.5,
475
- width: '100%',
476
- backgroundColor: AppColors.grayBorderSecondary,
477
- borderRadius: 1.5,
478
- overflow: 'hidden',
479
- }}>
480
- <View
481
- style={{
482
- height: '100%',
483
- width: `${Math.min(100, Math.max(6, (item.duration / 1200) * 100))}%`,
484
- backgroundColor: durationColor,
485
- borderRadius: 1.5,
486
- }}
420
+ ]}
487
421
  />
488
422
  </View>
489
- </View>
490
- )}
491
- </View>
492
- <Animated.View
493
- pointerEvents="none"
494
- style={[
495
- StyleSheet.absoluteFill,
496
- {backgroundColor: methodColor, opacity: shimmerOpacity},
497
- ]}
498
- />
499
- </TouchableScale>
423
+ )}
424
+ </View>
425
+
426
+ <Animated.View
427
+ pointerEvents="none"
428
+ style={[
429
+ StyleSheet.absoluteFill,
430
+ {backgroundColor: methodColor, opacity: shimmerOpacity},
431
+ ]}
432
+ />
433
+ </TouchableScale>
434
+ </View>
500
435
  );
501
436
  });
502
437
 
438
+ const styles = StyleSheet.create({
439
+ container: {
440
+ paddingHorizontal: 10,
441
+ paddingVertical: 3.5,
442
+ },
443
+ card: {
444
+ alignSelf: 'stretch',
445
+ borderRadius: 11,
446
+ overflow: 'hidden',
447
+ shadowColor: AppColors.black,
448
+ shadowOffset: {width: 0, height: 1.5},
449
+ shadowOpacity: 0.04,
450
+ shadowRadius: 3,
451
+ elevation: 1.5,
452
+ borderWidth: 1,
453
+ borderColor: AppColors.grayBorderSecondary,
454
+ backgroundColor: AppColors.white,
455
+ },
456
+ cardBody: {
457
+ paddingHorizontal: 12,
458
+ paddingTop: 9,
459
+ paddingBottom: 8,
460
+ },
461
+ cardHeaderRow: {
462
+ flexDirection: 'row',
463
+ alignItems: 'center',
464
+ justifyContent: 'space-between',
465
+ marginBottom: 6,
466
+ minHeight: 22,
467
+ },
468
+ cardHeaderLeft: {
469
+ flexDirection: 'row',
470
+ alignItems: 'center',
471
+ flex: 1,
472
+ marginRight: 6,
473
+ gap: 5.5,
474
+ minWidth: 0,
475
+ flexWrap: 'wrap',
476
+ },
477
+ smallCheckbox: {
478
+ width: 15,
479
+ height: 15,
480
+ borderRadius: 4,
481
+ borderWidth: 1.6,
482
+ borderColor: AppColors.grayTextWeak,
483
+ alignItems: 'center',
484
+ justifyContent: 'center',
485
+ backgroundColor: AppColors.white,
486
+ },
487
+ smallCheckboxChecked: {
488
+ backgroundColor: AppColors.purple,
489
+ borderColor: AppColors.purple,
490
+ },
491
+ serialNumber: {
492
+ fontFamily: AppFonts.interBold,
493
+ color: AppColors.grayTextWeak,
494
+ fontSize: 10,
495
+ },
496
+ methodBadge: {
497
+ paddingHorizontal: 6.5,
498
+ paddingVertical: 2,
499
+ borderRadius: 5,
500
+ alignItems: 'center',
501
+ justifyContent: 'center',
502
+ },
503
+ methodBadgeText: {
504
+ fontFamily: AppFonts.interBold,
505
+ fontSize: 9.5,
506
+ letterSpacing: 0.5,
507
+ color: AppColors.white,
508
+ },
509
+ chip: {
510
+ paddingHorizontal: 5,
511
+ paddingVertical: 1.5,
512
+ borderRadius: 4,
513
+ borderWidth: 1,
514
+ },
515
+ chipText: {
516
+ fontFamily: AppFonts.interBold,
517
+ fontSize: 8.5,
518
+ },
519
+ statusPill: {
520
+ flexDirection: 'row',
521
+ alignItems: 'center',
522
+ gap: 4,
523
+ paddingHorizontal: 7,
524
+ paddingVertical: 2.5,
525
+ borderRadius: 6,
526
+ borderWidth: 1,
527
+ flexShrink: 0,
528
+ },
529
+ statusPillText: {
530
+ fontFamily: AppFonts.interBold,
531
+ fontSize: 9.5,
532
+ },
533
+ urlBox: {
534
+ backgroundColor: AppColors.grayBackground,
535
+ borderRadius: 8,
536
+ borderWidth: 1,
537
+ borderColor: AppColors.dividerColor,
538
+ paddingHorizontal: 9,
539
+ paddingVertical: 6,
540
+ marginVertical: 4,
541
+ gap: 4,
542
+ },
543
+ urlMainRow: {
544
+ flexDirection: 'row',
545
+ alignItems: 'flex-start',
546
+ justifyContent: 'space-between',
547
+ gap: 6,
548
+ },
549
+ pathText: {
550
+ fontFamily: AppFonts.interBold,
551
+ fontSize: 12,
552
+ lineHeight: 16.5,
553
+ color: AppColors.primaryBlack,
554
+ flex: 1,
555
+ },
556
+ urlBadgeRow: {
557
+ flexDirection: 'row',
558
+ alignItems: 'center',
559
+ gap: 4,
560
+ flexShrink: 0,
561
+ },
562
+ jsonBadge: {
563
+ backgroundColor: `${AppColors.darkOrange}14`,
564
+ borderColor: `${AppColors.darkOrange}2E`,
565
+ borderWidth: 1,
566
+ paddingHorizontal: 4,
567
+ paddingVertical: 1,
568
+ borderRadius: 3,
569
+ },
570
+ jsonBadgeText: {
571
+ fontFamily: AppFonts.interBold,
572
+ fontSize: 8.5,
573
+ color: AppColors.darkOrange,
574
+ },
575
+ dupBadge: {
576
+ backgroundColor: `${AppColors.purple}14`,
577
+ borderColor: `${AppColors.purple}2E`,
578
+ borderWidth: 1,
579
+ paddingHorizontal: 4,
580
+ paddingVertical: 1,
581
+ borderRadius: 3,
582
+ },
583
+ dupBadgeText: {
584
+ fontFamily: AppFonts.interBold,
585
+ fontSize: 8.5,
586
+ color: AppColors.purple,
587
+ },
588
+ hostRow: {
589
+ flexDirection: 'row',
590
+ alignItems: 'center',
591
+ gap: 5,
592
+ },
593
+ protoBadge: {
594
+ paddingHorizontal: 4,
595
+ paddingVertical: 1,
596
+ borderRadius: 3,
597
+ borderWidth: 1,
598
+ },
599
+ protoBadgeText: {
600
+ fontFamily: AppFonts.interBold,
601
+ fontSize: 7.5,
602
+ },
603
+ hostText: {
604
+ fontFamily: AppFonts.interMedium,
605
+ fontSize: 10.5,
606
+ color: AppColors.grayText,
607
+ flex: 1,
608
+ },
609
+ highlight: {
610
+ backgroundColor: AppColors.yellowHighlight,
611
+ color: AppColors.primaryBlack,
612
+ borderRadius: 2,
613
+ },
614
+ cardFooterRow: {
615
+ flexDirection: 'row',
616
+ alignItems: 'center',
617
+ justifyContent: 'space-between',
618
+ marginTop: 4,
619
+ gap: 6,
620
+ },
621
+ footerLeft: {
622
+ flexDirection: 'row',
623
+ alignItems: 'center',
624
+ gap: 6,
625
+ flex: 1,
626
+ minWidth: 0,
627
+ },
628
+ footerRight: {
629
+ flexDirection: 'row',
630
+ alignItems: 'center',
631
+ gap: 5,
632
+ flexShrink: 0,
633
+ },
634
+ cardDateRow: {
635
+ flexDirection: 'row',
636
+ alignItems: 'center',
637
+ gap: 3.5,
638
+ },
639
+ cardDateText: {
640
+ fontFamily: AppFonts.interRegular,
641
+ fontSize: 9.5,
642
+ color: AppColors.grayTextWeak,
643
+ },
644
+ callerChip: {
645
+ flexDirection: 'row',
646
+ alignItems: 'center',
647
+ gap: 3,
648
+ backgroundColor: `${AppColors.sky600}10`,
649
+ borderColor: `${AppColors.sky600}2B`,
650
+ borderWidth: 1,
651
+ paddingHorizontal: 5,
652
+ paddingVertical: 1,
653
+ borderRadius: 4,
654
+ flexShrink: 1,
655
+ minWidth: 0,
656
+ },
657
+ callerChipText: {
658
+ color: AppColors.sky600,
659
+ fontSize: 8.5,
660
+ fontFamily: AppFonts.interBold,
661
+ },
662
+ metaStatChip: {
663
+ flexDirection: 'row',
664
+ alignItems: 'center',
665
+ gap: 3,
666
+ backgroundColor: `${AppColors.purple}10`,
667
+ borderColor: `${AppColors.purple}28`,
668
+ borderWidth: 1,
669
+ paddingHorizontal: 5,
670
+ paddingVertical: 1.5,
671
+ borderRadius: 4,
672
+ },
673
+ metaStatText: {
674
+ fontFamily: AppFonts.interBold,
675
+ color: AppColors.purple,
676
+ fontSize: 9,
677
+ },
678
+ waterfallContainer: {
679
+ height: 2.5,
680
+ width: '100%',
681
+ backgroundColor: AppColors.grayBorderSecondary,
682
+ borderRadius: 1.5,
683
+ overflow: 'hidden',
684
+ marginTop: 6,
685
+ },
686
+ waterfallBar: {
687
+ height: '100%',
688
+ borderRadius: 1.5,
689
+ },
690
+ });
691
+
503
692
  export default LogCard;