react-native-inapp-inspector 2.2.2 → 2.2.3

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 (97) hide show
  1. package/dist/commonjs/constants/version.d.ts +1 -1
  2. package/dist/commonjs/constants/version.js +1 -1
  3. package/dist/esm/constants/version.d.ts +1 -1
  4. package/dist/esm/constants/version.js +1 -1
  5. package/package.json +2 -1
  6. package/src/analytics.ts +35 -0
  7. package/src/bundle.ts +25 -0
  8. package/src/components/AnalyticsDetail.tsx +865 -0
  9. package/src/components/AnalyticsEventCard.tsx +441 -0
  10. package/src/components/AnalyticsGraph.tsx +583 -0
  11. package/src/components/AnimatedEntrance.tsx +64 -0
  12. package/src/components/AppHeaderLogo.tsx +109 -0
  13. package/src/components/BrandCircleIcon.tsx +144 -0
  14. package/src/components/BrandSquareIcon.tsx +144 -0
  15. package/src/components/CodeSnippet.tsx +725 -0
  16. package/src/components/ConsoleLogCard.tsx +628 -0
  17. package/src/components/CopyButton.tsx +87 -0
  18. package/src/components/DiffViewer.tsx +82 -0
  19. package/src/components/DomainHeader.tsx +237 -0
  20. package/src/components/EmptyState.tsx +73 -0
  21. package/src/components/EndOfListFooter.tsx +100 -0
  22. package/src/components/ErrorBoundary.tsx +688 -0
  23. package/src/components/HeadersSection.tsx +192 -0
  24. package/src/components/HighlightText.tsx +100 -0
  25. package/src/components/Inspector/AnalyticsFilterModal.tsx +1250 -0
  26. package/src/components/Inspector/AnalyticsTab.tsx +1336 -0
  27. package/src/components/Inspector/BundleTab.tsx +4731 -0
  28. package/src/components/Inspector/ConsoleTab.tsx +702 -0
  29. package/src/components/Inspector/CrashDetail.tsx +941 -0
  30. package/src/components/Inspector/CrashFilterModal.tsx +721 -0
  31. package/src/components/Inspector/CrashTab.tsx +871 -0
  32. package/src/components/Inspector/FabLauncher.tsx +80 -0
  33. package/src/components/Inspector/InspectorContext.tsx +28 -0
  34. package/src/components/Inspector/InspectorHeader.tsx +973 -0
  35. package/src/components/Inspector/LogDetail.tsx +1427 -0
  36. package/src/components/Inspector/MainScreen.tsx +258 -0
  37. package/src/components/Inspector/NavigationTracker.tsx +13 -0
  38. package/src/components/Inspector/NetworkDetail.tsx +794 -0
  39. package/src/components/Inspector/NetworkTab.tsx +1095 -0
  40. package/src/components/Inspector/NpmUpdateToast.tsx +392 -0
  41. package/src/components/Inspector/PerformanceTab.tsx +1894 -0
  42. package/src/components/Inspector/ReduxDetail.tsx +1651 -0
  43. package/src/components/Inspector/ReduxTab.tsx +954 -0
  44. package/src/components/Inspector/SettingsPanel.tsx +3181 -0
  45. package/src/components/Inspector/TabBar.tsx +187 -0
  46. package/src/components/Inspector/TelemetryConsentModal.tsx +392 -0
  47. package/src/components/Inspector/UpdateAvailableModal.tsx +545 -0
  48. package/src/components/JsonViewer.tsx +486 -0
  49. package/src/components/LogCard.tsx +491 -0
  50. package/src/components/LogSyntaxHighlighter.tsx +178 -0
  51. package/src/components/MetaAccordion.tsx +340 -0
  52. package/src/components/MiniBarChart.tsx +42 -0
  53. package/src/components/MiniLineChart.tsx +33 -0
  54. package/src/components/NetworkIcons.tsx +2118 -0
  55. package/src/components/SectionHeader.tsx +113 -0
  56. package/src/components/SegmentedTabs.tsx +83 -0
  57. package/src/components/Slider.tsx +300 -0
  58. package/src/components/SourcePageCard.tsx +148 -0
  59. package/src/components/Toast.tsx +131 -0
  60. package/src/components/TouchableScale.tsx +91 -0
  61. package/src/components/TreeNode.tsx +186 -0
  62. package/src/console.ts +24 -0
  63. package/src/constants/index.ts +38 -0
  64. package/src/constants/version.ts +3 -0
  65. package/src/crash.ts +32 -0
  66. package/src/customHooks/analyticsLogger.ts +336 -0
  67. package/src/customHooks/bundleAnalyzer.ts +1231 -0
  68. package/src/customHooks/consoleLogger.ts +497 -0
  69. package/src/customHooks/crashHandler.ts +944 -0
  70. package/src/customHooks/logFilters.ts +32 -0
  71. package/src/customHooks/networkLogger.ts +419 -0
  72. package/src/customHooks/performanceTracker.ts +1014 -0
  73. package/src/customHooks/reduxLogger.ts +406 -0
  74. package/src/customHooks/useAccordion.tsx +60 -0
  75. package/src/decorators/index.ts +184 -0
  76. package/src/helpers/gaAnalyticsRegistry.ts +204 -0
  77. package/src/helpers/index.ts +860 -0
  78. package/src/helpers/memoryManager.ts +138 -0
  79. package/src/helpers/searchQueryParser.ts +283 -0
  80. package/src/helpers/settingsStore.ts +171 -0
  81. package/src/helpers/telemetry.ts +505 -0
  82. package/src/helpers/toast.ts +17 -0
  83. package/src/i18n/index.ts +69 -0
  84. package/src/i18n/locales/en.json +1052 -0
  85. package/src/index.tsx +2336 -0
  86. package/src/native/NativeInspector.ts +397 -0
  87. package/src/native/NativeNetworkInspector.ts +24 -0
  88. package/src/network.ts +22 -0
  89. package/src/performance.ts +38 -0
  90. package/src/redux.ts +23 -0
  91. package/src/styles/AppColors.ts +408 -0
  92. package/src/styles/AppFonts.ts +8 -0
  93. package/src/styles/common.ts +209 -0
  94. package/src/styles/index.ts +1570 -0
  95. package/src/types/enums.ts +194 -0
  96. package/src/types/index.ts +34 -0
  97. package/src/types/interfaces.ts +515 -0
@@ -0,0 +1,628 @@
1
+ import React from 'react';
2
+ import {useTranslation} from '../i18n';
3
+ import {
4
+ Pressable,
5
+ StyleSheet,
6
+ Text,
7
+ View,
8
+ } from 'react-native';
9
+ import {AppColors} from '../styles/AppColors';
10
+ import {AppFonts} from '../styles/AppFonts';
11
+ import {ConsoleLogCardProps} from '../types';
12
+ import {
13
+ formatTime,
14
+ getJsonContent,
15
+ getJsonPreviewText,
16
+ parseStackLine,
17
+ openInVSCode,
18
+ } from '../helpers';
19
+ import HighlightText from './HighlightText';
20
+ import CopyButton from './CopyButton';
21
+ import LogSyntaxHighlighter from './LogSyntaxHighlighter';
22
+ import {
23
+ ChevronIcon,
24
+ ExternalLinkIcon,
25
+ FlaskIcon,
26
+ ZapIcon,
27
+ GlobeIcon,
28
+ DiceIcon,
29
+ AtomIcon,
30
+ BarChartIcon,
31
+ KeyIcon,
32
+ SmartphoneIcon,
33
+ AlertTriangleIcon,
34
+ BugIcon,
35
+ TagIcon,
36
+ } from './NetworkIcons';
37
+ import {useInspector} from './Inspector/InspectorContext';
38
+
39
+ const getLogMessageWithBadges = (
40
+ message: string,
41
+ searchStr: string,
42
+ textStyle: any,
43
+ highlightStyle: any,
44
+ numberOfLines?: number,
45
+ ) => {
46
+ if (!message) return null;
47
+ const prefixRegex = /^((?:\[[^\]]+\]\s*)+)/;
48
+ const match = message.match(prefixRegex);
49
+ if (match) {
50
+ const fullPrefix = match[1];
51
+ const remainingText = message.substring(fullPrefix.length);
52
+ const tags = fullPrefix.match(/\[[^\]]+\]/g) || [];
53
+
54
+ const getTagDecorator = (tag: string) => {
55
+ const clean = tag.replace(/[\[\]]/g, '').trim().toUpperCase();
56
+ if (clean === 'AXIOS') {
57
+ return { color: AppColors.emerald600, Icon: ZapIcon, label: 'AXIOS' };
58
+ }
59
+ if (clean === 'API' || clean === 'FETCH' || clean === 'HTTP' || clean === 'NETWORK') {
60
+ return { color: AppColors.sky600, Icon: GlobeIcon, label: clean };
61
+ }
62
+ if (clean === 'SAMPLE' || clean === 'BATCH') {
63
+ return { color: AppColors.indigo600Alt, Icon: DiceIcon, label: clean };
64
+ }
65
+ if (clean === 'REDUX' || clean === 'STORE' || clean === 'STATE') {
66
+ return { color: AppColors.violet600, Icon: AtomIcon, label: clean };
67
+ }
68
+ if (clean === 'ANALYTICS' || clean === 'FIREBASE' || clean === 'GA4' || clean === 'GA') {
69
+ return { color: AppColors.teal600, Icon: BarChartIcon, label: clean };
70
+ }
71
+ if (clean === 'AUTH' || clean === 'TOKEN' || clean === 'SESSION') {
72
+ return { color: AppColors.amber600, Icon: KeyIcon, label: clean };
73
+ }
74
+ if (clean === 'APP') {
75
+ return { color: AppColors.indigo600Alt, Icon: SmartphoneIcon, label: 'APP' };
76
+ }
77
+ if (clean === 'TEST') {
78
+ return { color: AppColors.emerald500, Icon: FlaskIcon, label: 'TEST' };
79
+ }
80
+ if (clean === 'WARN' || clean === 'WARNING') {
81
+ return { color: AppColors.amber600, Icon: AlertTriangleIcon, label: 'WARN' };
82
+ }
83
+ if (clean === 'ERROR' || clean === 'CRASH' || clean === 'BUG') {
84
+ return { color: AppColors.red600, Icon: BugIcon, label: 'ERROR' };
85
+ }
86
+ if (clean === 'PERF' || clean === 'RENDER') {
87
+ return { color: AppColors.violet500, Icon: ZapIcon, label: clean };
88
+ }
89
+ return { color: AppColors.slate600, Icon: TagIcon, label: clean };
90
+ };
91
+
92
+ return (
93
+ <View style={{flexDirection: 'column', gap: 6}}>
94
+ <View style={{flexDirection: 'row', flexWrap: 'wrap', gap: 5}}>
95
+ {tags.map((tag, i) => {
96
+ const dec = getTagDecorator(tag);
97
+ const IconComp = dec.Icon;
98
+ return (
99
+ <View
100
+ key={i}
101
+ style={[
102
+ styles.prefixTag,
103
+ {
104
+ backgroundColor: `${dec.color}15`,
105
+ borderColor: `${dec.color}35`,
106
+ },
107
+ ]}>
108
+ <IconComp color={dec.color} size={10} />
109
+ <Text style={[styles.prefixTagText, {color: dec.color}]}>
110
+ {dec.label}
111
+ </Text>
112
+ </View>
113
+ );
114
+ })}
115
+ </View>
116
+ <LogSyntaxHighlighter
117
+ text={remainingText}
118
+ search={searchStr}
119
+ style={textStyle}
120
+ numberOfLines={numberOfLines}
121
+ detectLinks={true}
122
+ />
123
+ </View>
124
+ );
125
+ }
126
+
127
+ return (
128
+ <LogSyntaxHighlighter
129
+ text={message}
130
+ search={searchStr}
131
+ style={textStyle}
132
+ numberOfLines={numberOfLines}
133
+ detectLinks={true}
134
+ />
135
+ );
136
+ };
137
+
138
+ export const ConsoleLogCard = React.memo(function ConsoleLogCard({
139
+ item,
140
+ searchStr = '',
141
+ }: ConsoleLogCardProps) {
142
+ const {setSelectedLog} = useInspector();
143
+ const {t} = useTranslation();
144
+ const jsonContent = getJsonContent(item.message);
145
+ const isAnalyticsError = item.message
146
+ .toLowerCase()
147
+ .includes('[analytics error]');
148
+ const isUserLog = item.sourceMethod === 'log';
149
+ const parsedCaller = item.caller && item.caller !== 'Unknown'
150
+ ? parseStackLine(item.caller, true)
151
+ : null;
152
+
153
+ const getLogColors = () => {
154
+ const type = (item.type || 'log').toLowerCase();
155
+ const method = ((item as any).sourceMethod || type).toLowerCase();
156
+
157
+ if (isAnalyticsError || type === 'error' || method === 'error') {
158
+ return {
159
+ border: AppColors.errorColor,
160
+ badgeBg: AppColors.errorCardBg,
161
+ badgeText: AppColors.rose600,
162
+ label: 'ERROR',
163
+ cardBg: AppColors.errorCardBg,
164
+ methodBorder: AppColors.errorBorder,
165
+ methodBg: AppColors.errorCardBg,
166
+ methodText: AppColors.rose600,
167
+ textColor: AppColors.redErrorText,
168
+ };
169
+ }
170
+ if (type === 'warn' || method === 'warn') {
171
+ return {
172
+ border: AppColors.amber600,
173
+ badgeBg: AppColors.amberBg,
174
+ badgeText: AppColors.amber700,
175
+ label: 'WARN',
176
+ cardBg: AppColors.amberWarmBg,
177
+ methodBorder: AppColors.amberWarmBorder,
178
+ methodBg: AppColors.amberBg,
179
+ methodText: AppColors.amber700,
180
+ textColor: AppColors.amberWarmText,
181
+ };
182
+ }
183
+ if (type === 'debug' || method === 'debug') {
184
+ return {
185
+ border: AppColors.purple500,
186
+ badgeBg: AppColors.violetSoftBg,
187
+ badgeText: AppColors.violetSoftText,
188
+ label: 'DEBUG',
189
+ cardBg: AppColors.purpleShade50,
190
+ methodBorder: AppColors.violetSoftBorder,
191
+ methodBg: AppColors.violetSoftBg,
192
+ methodText: AppColors.violetSoftText,
193
+ textColor: AppColors.purpleText,
194
+ };
195
+ }
196
+ if (type === 'info' || method === 'info') {
197
+ return {
198
+ border: AppColors.sky500,
199
+ badgeBg: AppColors.skySoftBg,
200
+ badgeText: AppColors.skySoftText,
201
+ label: 'INFO',
202
+ cardBg: AppColors.blueBg,
203
+ methodBorder: AppColors.skySoftBorder,
204
+ methodBg: AppColors.skySoftBg,
205
+ methodText: AppColors.skySoftText,
206
+ textColor: AppColors.blue800,
207
+ };
208
+ }
209
+
210
+ // Default / Standard console.log
211
+ return {
212
+ border: AppColors.indigo500,
213
+ badgeBg: AppColors.indigo50,
214
+ badgeText: AppColors.indigo600Alt,
215
+ label: 'LOG',
216
+ cardBg: AppColors.primaryLight,
217
+ methodBorder: AppColors.dividerColor,
218
+ methodBg: AppColors.indigo50,
219
+ methodText: AppColors.indigo600Alt,
220
+ textColor: AppColors.primaryBlack,
221
+ };
222
+ };
223
+
224
+ const colors = getLogColors();
225
+
226
+ const jsonPreview = jsonContent
227
+ ? getJsonPreviewText(jsonContent.data)
228
+ : null;
229
+
230
+ const openDetail = () => {
231
+ setSelectedLog(item);
232
+ };
233
+
234
+ return (
235
+ <View style={styles.container}>
236
+ <Pressable
237
+ onPress={openDetail}
238
+ style={({pressed}) => [
239
+ styles.card,
240
+ {
241
+ borderLeftColor: colors.border,
242
+ borderLeftWidth: 3.5,
243
+ backgroundColor: colors.cardBg,
244
+ },
245
+ pressed && styles.cardPressed,
246
+ ]}>
247
+ <View style={styles.cardHeader}>
248
+ <View style={styles.headerLeft}>
249
+ <View style={[styles.typeChip, {backgroundColor: colors.badgeBg}]}>
250
+ <View
251
+ style={[styles.typeDot, {backgroundColor: colors.badgeText}]}
252
+ />
253
+ <Text style={[styles.typeChipText, {color: colors.badgeText, fontFamily: AppFonts.interBold}]}>
254
+ {colors.label}
255
+ </Text>
256
+ </View>
257
+ <View
258
+ style={[
259
+ styles.metaChip,
260
+ {
261
+ backgroundColor: colors.methodBg,
262
+ borderColor: colors.methodBorder,
263
+ },
264
+ ]}>
265
+ <Text
266
+ style={[styles.metaChipText, {color: colors.methodText, fontFamily: AppFonts.interBold}]}>
267
+ console.
268
+ {('sourceMethod' in item ? item.sourceMethod : undefined) ||
269
+ item.type ||
270
+ 'log'}
271
+ </Text>
272
+ </View>
273
+ {jsonContent && (
274
+ <View
275
+ style={[
276
+ styles.metaChip,
277
+ {
278
+ backgroundColor: AppColors.mintGreenBg,
279
+ borderColor: AppColors.mintGreenBorder,
280
+ },
281
+ ]}>
282
+ <Text
283
+ style={[styles.metaChipText, {color: AppColors.mintGreenText, fontFamily: AppFonts.interBold}]}>
284
+ {Array.isArray(jsonContent.data)
285
+ ? `Array[${jsonContent.data.length}]`
286
+ : `Object{${Object.keys(jsonContent.data).length}}`}
287
+ </Text>
288
+ </View>
289
+ )}
290
+ {'duplicateCount' in item &&
291
+ item.duplicateCount != null &&
292
+ item.duplicateCount > 1 && (
293
+ <View
294
+ style={[
295
+ styles.metaChip,
296
+ {
297
+ backgroundColor: `${AppColors.purple}1A`,
298
+ borderColor: `${AppColors.purple}3D`,
299
+ },
300
+ ]}>
301
+ <Text
302
+ style={[styles.metaChipText, {color: AppColors.purple, fontFamily: AppFonts.interBold}]}>
303
+ ×{item.duplicateCount}
304
+ </Text>
305
+ </View>
306
+ )}
307
+ </View>
308
+
309
+ <View style={styles.headerRight}>
310
+ <CopyButton value={item.message} label={t('console.logMessage')} />
311
+ <ChevronIcon color={AppColors.grayTextWeak} size={14} />
312
+ </View>
313
+ </View>
314
+
315
+ <View style={styles.cardBody}>
316
+ {jsonContent ? (
317
+ <>
318
+ {jsonContent.header ? (
319
+ getLogMessageWithBadges(
320
+ jsonContent.header,
321
+ searchStr,
322
+ [styles.messageText, {color: AppColors.primaryBlack}],
323
+ styles.highlight,
324
+ 2,
325
+ )
326
+ ) : null}
327
+ {jsonPreview && (
328
+ <View style={styles.jsonPreviewContainer}>
329
+ <LogSyntaxHighlighter
330
+ text={jsonPreview.text}
331
+ search={searchStr}
332
+ style={[
333
+ styles.jsonPreviewText,
334
+ {color: AppColors.primaryBlack},
335
+ ]}
336
+ detectLinks={false}
337
+ numberOfLines={5}
338
+ />
339
+ </View>
340
+ )}
341
+ </>
342
+ ) : (
343
+ getLogMessageWithBadges(
344
+ item.message,
345
+ searchStr,
346
+ [styles.messageText, {color: AppColors.primaryBlack}],
347
+ styles.highlight,
348
+ 3,
349
+ )
350
+ )}
351
+ </View>
352
+
353
+ <View style={styles.cardFooter}>
354
+ <View style={styles.footerLeft}>
355
+ <Text style={styles.footerText}>#{item.id + 1}</Text>
356
+ <View style={styles.footerDot} />
357
+ <Text style={styles.footerText}>{formatTime(item.timestamp)}</Text>
358
+ {parsedCaller && (
359
+ <>
360
+ <View style={styles.footerDot} />
361
+ <Pressable
362
+ onPress={e => {
363
+ e.stopPropagation?.();
364
+ openInVSCode(
365
+ parsedCaller.rawFilePath ||
366
+ parsedCaller.fullPath ||
367
+ parsedCaller.fileName,
368
+ parsedCaller.lineNumber,
369
+ parsedCaller.columnNumber,
370
+ );
371
+ }}
372
+ hitSlop={8}
373
+ style={{
374
+ flexDirection: 'row',
375
+ alignItems: 'center',
376
+ gap: 4,
377
+ maxWidth: 185,
378
+ backgroundColor: `${AppColors.sky600}10`,
379
+ borderColor: `${AppColors.sky600}2B`,
380
+ borderWidth: 1,
381
+ borderRadius: 5,
382
+ paddingHorizontal: 5,
383
+ paddingVertical: 1.5,
384
+ }}>
385
+ {parsedCaller.fileExt && parsedCaller.fileExt !== 'other' && (
386
+ <View
387
+ style={{
388
+ backgroundColor:
389
+ parsedCaller.fileExt === 'tsx' || parsedCaller.fileExt === 'ts'
390
+ ? `${AppColors.brandPurple}22`
391
+ : `${AppColors.teal600}22`,
392
+ borderRadius: 3,
393
+ paddingHorizontal: 3.5,
394
+ paddingVertical: 0.5,
395
+ }}>
396
+ <Text
397
+ style={{
398
+ fontFamily: AppFonts.interBold,
399
+ fontSize: 8,
400
+ color:
401
+ parsedCaller.fileExt === 'tsx' || parsedCaller.fileExt === 'ts'
402
+ ? AppColors.brandPurple
403
+ : AppColors.teal600,
404
+ }}>
405
+ {parsedCaller.fileExt.toUpperCase()}
406
+ </Text>
407
+ </View>
408
+ )}
409
+ <Text
410
+ style={[
411
+ styles.footerText,
412
+ {
413
+ fontFamily: AppFonts.interBold,
414
+ color: AppColors.sky600,
415
+ fontSize: 9.5,
416
+ },
417
+ ]}
418
+ numberOfLines={1}
419
+ ellipsizeMode="middle">
420
+ {parsedCaller.fileName}
421
+ {parsedCaller.lineNumber ? `:${parsedCaller.lineNumber}` : ''}
422
+ </Text>
423
+ <ExternalLinkIcon color={AppColors.sky600} size={8.5} />
424
+ </Pressable>
425
+ </>
426
+ )}
427
+ </View>
428
+
429
+ <View style={styles.footerRight}>
430
+ {isAnalyticsError && (
431
+ <View
432
+ style={[
433
+ styles.footerBadge,
434
+ {
435
+ backgroundColor: `${AppColors.skyBlue}15`,
436
+ borderColor: `${AppColors.skyBlue}30`,
437
+ },
438
+ ]}>
439
+ <Text style={[styles.footerBadgeText, {color: AppColors.skyBlue}]}>
440
+ {t('console.analyticsBadge')}
441
+ </Text>
442
+ </View>
443
+ )}
444
+ {isUserLog && (
445
+ <View
446
+ style={[
447
+ styles.footerBadge,
448
+ {
449
+ backgroundColor: `${AppColors.slate600}12`,
450
+ borderColor: `${AppColors.slate600}26`,
451
+ },
452
+ ]}>
453
+ <Text style={[styles.footerBadgeText, {color: AppColors.grayTextStrong}]}>
454
+ {t('console.userLogBadge')}
455
+ </Text>
456
+ </View>
457
+ )}
458
+ </View>
459
+ </View>
460
+ </Pressable>
461
+ </View>
462
+ );
463
+ });
464
+
465
+ const styles = StyleSheet.create({
466
+ container: {
467
+ paddingHorizontal: 12,
468
+ paddingVertical: 4,
469
+ },
470
+ card: {
471
+ alignSelf: 'stretch',
472
+ borderRadius: 12,
473
+ borderWidth: 1,
474
+ borderColor: AppColors.grayBorderSecondary,
475
+ borderLeftWidth: 3.5,
476
+ padding: 12,
477
+ backgroundColor: AppColors.primaryLight,
478
+ shadowColor: AppColors.black,
479
+ shadowOpacity: 0.05,
480
+ shadowRadius: 4,
481
+ shadowOffset: {width: 0, height: 2},
482
+ elevation: 2,
483
+ },
484
+ cardPressed: {
485
+ opacity: 0.85,
486
+ transform: [{scale: 0.99}],
487
+ },
488
+ cardHeader: {
489
+ flexDirection: 'row',
490
+ justifyContent: 'space-between',
491
+ alignItems: 'center',
492
+ marginBottom: 8,
493
+ gap: 8,
494
+ },
495
+ headerLeft: {
496
+ flexDirection: 'row',
497
+ alignItems: 'center',
498
+ flexWrap: 'wrap',
499
+ gap: 6,
500
+ flex: 1,
501
+ },
502
+ headerRight: {
503
+ flexDirection: 'row',
504
+ alignItems: 'center',
505
+ gap: 4,
506
+ },
507
+ typeChip: {
508
+ flexDirection: 'row',
509
+ alignItems: 'center',
510
+ gap: 4,
511
+ paddingHorizontal: 7,
512
+ paddingVertical: 2.5,
513
+ borderRadius: 6,
514
+ },
515
+ typeDot: {
516
+ width: 5,
517
+ height: 5,
518
+ borderRadius: 2.5,
519
+ },
520
+ typeChipText: {
521
+ fontFamily: AppFonts.interBold,
522
+ fontSize: 9.5,
523
+ letterSpacing: 0.5,
524
+ },
525
+ metaChip: {
526
+ paddingHorizontal: 6,
527
+ paddingVertical: 2.5,
528
+ borderRadius: 6,
529
+ borderWidth: 1,
530
+ },
531
+ metaChipText: {
532
+ fontFamily: AppFonts.interBold,
533
+ fontSize: 9.5,
534
+ letterSpacing: 0.2,
535
+ },
536
+ cardBody: {
537
+ backgroundColor: AppColors.primaryLight,
538
+ paddingHorizontal: 10,
539
+ paddingVertical: 8,
540
+ borderRadius: 9,
541
+ borderWidth: 1,
542
+ borderColor: AppColors.grayBorderSecondary,
543
+ },
544
+ messageText: {
545
+ fontFamily: AppFonts.interMedium,
546
+ fontSize: 12.5,
547
+ color: AppColors.primaryBlack,
548
+ lineHeight: 18,
549
+ },
550
+ highlight: {
551
+ backgroundColor: AppColors.yellowHighlight,
552
+ color: AppColors.primaryBlack,
553
+ borderRadius: 2,
554
+ },
555
+ jsonPreviewContainer: {
556
+ marginTop: 6,
557
+ backgroundColor: AppColors.grayBackground,
558
+ borderRadius: 7,
559
+ borderWidth: 1,
560
+ borderColor: AppColors.grayBorderSecondary,
561
+ padding: 8,
562
+ },
563
+ jsonPreviewText: {
564
+ fontFamily: AppFonts.interRegular,
565
+ fontSize: 11,
566
+ color: AppColors.primaryBlack,
567
+ lineHeight: 16,
568
+ },
569
+ cardFooter: {
570
+ flexDirection: 'row',
571
+ alignItems: 'center',
572
+ justifyContent: 'space-between',
573
+ marginTop: 8,
574
+ gap: 6,
575
+ },
576
+ footerLeft: {
577
+ flexDirection: 'row',
578
+ alignItems: 'center',
579
+ gap: 5,
580
+ flex: 1,
581
+ flexWrap: 'wrap',
582
+ },
583
+ footerRight: {
584
+ flexDirection: 'row',
585
+ alignItems: 'center',
586
+ gap: 4,
587
+ },
588
+ footerDot: {
589
+ width: 3,
590
+ height: 3,
591
+ borderRadius: 1.5,
592
+ backgroundColor: AppColors.grayTextWeak,
593
+ opacity: 0.6,
594
+ },
595
+ footerText: {
596
+ fontFamily: AppFonts.interRegular,
597
+ fontSize: 10,
598
+ color: AppColors.grayTextWeak,
599
+ letterSpacing: 0.1,
600
+ },
601
+ footerCaller: {
602
+ flexShrink: 1,
603
+ },
604
+ footerBadge: {
605
+ paddingHorizontal: 6,
606
+ paddingVertical: 1.5,
607
+ borderRadius: 5,
608
+ borderWidth: 1,
609
+ },
610
+ footerBadgeText: {
611
+ fontFamily: AppFonts.interBold,
612
+ fontSize: 9,
613
+ },
614
+ prefixTag: {
615
+ flexDirection: 'row',
616
+ alignItems: 'center',
617
+ gap: 3.5,
618
+ paddingHorizontal: 6.5,
619
+ paddingVertical: 2,
620
+ borderRadius: 5,
621
+ borderWidth: 1,
622
+ },
623
+ prefixTagText: {
624
+ fontFamily: AppFonts.interBold,
625
+ fontSize: 9.5,
626
+ letterSpacing: 0.3,
627
+ },
628
+ });
@@ -0,0 +1,87 @@
1
+ import React, {useState, useCallback} from 'react';
2
+ import {Share} from 'react-native';
3
+
4
+ // Components
5
+ import TouchableScale from './TouchableScale';
6
+
7
+ // Helpers
8
+ import {copyToClipboard} from '../helpers';
9
+
10
+ import {triggerNativeHaptic} from '../native/NativeInspector';
11
+
12
+ // Assets
13
+ import {TerminalIcon, FetchIcon, CopyIcon, CheckIcon} from './NetworkIcons';
14
+
15
+ // Stylesheet
16
+ import {AppColors} from '../styles/AppColors';
17
+ import styles from '../styles';
18
+
19
+ // Type Definition
20
+ import {CopyButtonProps} from '../types';
21
+
22
+ const CopyButton = React.memo(({value, label, iconType = 'copy'}: CopyButtonProps) => {
23
+ const [copied, setCopied] = useState<boolean>(false);
24
+
25
+ const handlePress = useCallback(() => {
26
+ triggerNativeHaptic('success');
27
+ const resolvedValue = typeof value === 'function' ? (value as Function)() : value;
28
+ copyToClipboard(resolvedValue, label);
29
+ setCopied(true);
30
+ setTimeout(() => setCopied(false), 1200);
31
+ }, [value, label]);
32
+
33
+ const handleLongPress = useCallback(() => {
34
+ try {
35
+ triggerNativeHaptic('medium');
36
+ const resolvedValue = typeof value === 'function' ? (value as Function)() : value;
37
+ const textToShare =
38
+ typeof resolvedValue === 'string'
39
+ ? resolvedValue
40
+ : JSON.stringify(resolvedValue, null, 2);
41
+ Share.share({
42
+ message: textToShare,
43
+ title: label,
44
+ });
45
+ } catch {}
46
+ }, [value, label]);
47
+
48
+ const containerStyle = [
49
+ styles.iconSquareBtn,
50
+ copied && styles.iconSquareBtnSuccess,
51
+ ];
52
+
53
+ const IconComponent =
54
+ iconType === 'terminal'
55
+ ? TerminalIcon
56
+ : iconType === 'fetch'
57
+ ? FetchIcon
58
+ : CopyIcon;
59
+
60
+ return (
61
+ <TouchableScale
62
+ accessible={true}
63
+ accessibilityRole="button"
64
+ accessibilityLabel={
65
+ label
66
+ ? copied
67
+ ? `${label} copied`
68
+ : `Copy ${label}`
69
+ : copied
70
+ ? 'Copied to clipboard'
71
+ : 'Copy to clipboard'
72
+ }
73
+ accessibilityHint="Double tap to copy, long press to share"
74
+ onPress={handlePress}
75
+ onLongPress={handleLongPress}
76
+ hitSlop={12}
77
+ style={containerStyle}>
78
+ {copied ? (
79
+ <CheckIcon color={AppColors.greenColor} size={14} />
80
+ ) : (
81
+ <IconComponent color={AppColors.grayTextWeak} size={14} />
82
+ )}
83
+ </TouchableScale>
84
+ );
85
+ });
86
+
87
+ export default CopyButton;