react-native-inapp-inspector 1.1.25 → 1.1.26

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 (70) hide show
  1. package/android/build.gradle +36 -0
  2. package/android/src/main/AndroidManifest.xml +3 -0
  3. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +114 -0
  4. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +28 -0
  5. package/dist/commonjs/components/ErrorBoundary.d.ts +1 -1
  6. package/dist/commonjs/components/ErrorBoundary.js +9 -145
  7. package/dist/commonjs/components/Inspector/CrashDetail.d.ts +3 -0
  8. package/dist/commonjs/components/Inspector/CrashDetail.js +796 -0
  9. package/dist/commonjs/components/Inspector/CrashFilterModal.d.ts +20 -0
  10. package/dist/commonjs/components/Inspector/CrashFilterModal.js +597 -0
  11. package/dist/commonjs/components/Inspector/CrashTab.d.ts +3 -0
  12. package/dist/commonjs/components/Inspector/CrashTab.js +727 -0
  13. package/dist/commonjs/components/Inspector/InspectorHeader.js +52 -23
  14. package/dist/commonjs/components/Inspector/LogDetail.js +4 -3
  15. package/dist/commonjs/components/Inspector/MainScreen.js +12 -3
  16. package/dist/commonjs/components/Inspector/SettingsPanel.js +90 -1
  17. package/dist/commonjs/components/Inspector/TabBar.js +15 -3
  18. package/dist/commonjs/components/NetworkIcons.d.ts +11 -0
  19. package/dist/commonjs/components/NetworkIcons.js +96 -1
  20. package/dist/commonjs/components/SegmentedTabs.js +2 -1
  21. package/dist/commonjs/constants/version.d.ts +1 -1
  22. package/dist/commonjs/constants/version.js +1 -1
  23. package/dist/commonjs/customHooks/analyticsLogger.js +70 -44
  24. package/dist/commonjs/customHooks/consoleLogger.d.ts +1 -0
  25. package/dist/commonjs/customHooks/consoleLogger.js +70 -6
  26. package/dist/commonjs/customHooks/crashHandler.d.ts +64 -0
  27. package/dist/commonjs/customHooks/crashHandler.js +681 -0
  28. package/dist/commonjs/customHooks/networkLogger.js +64 -48
  29. package/dist/commonjs/helpers/gaAnalyticsRegistry.js +1 -1
  30. package/dist/commonjs/i18n/locales/en.json +131 -0
  31. package/dist/commonjs/index.d.ts +3 -1
  32. package/dist/commonjs/index.js +95 -1
  33. package/dist/commonjs/types/enums.d.ts +41 -0
  34. package/dist/commonjs/types/enums.js +37 -1
  35. package/dist/commonjs/types/interfaces.d.ts +51 -1
  36. package/dist/esm/components/ErrorBoundary.d.ts +1 -1
  37. package/dist/esm/components/ErrorBoundary.js +10 -113
  38. package/dist/esm/components/Inspector/CrashDetail.d.ts +3 -0
  39. package/dist/esm/components/Inspector/CrashDetail.js +758 -0
  40. package/dist/esm/components/Inspector/CrashFilterModal.d.ts +20 -0
  41. package/dist/esm/components/Inspector/CrashFilterModal.js +557 -0
  42. package/dist/esm/components/Inspector/CrashTab.d.ts +3 -0
  43. package/dist/esm/components/Inspector/CrashTab.js +689 -0
  44. package/dist/esm/components/Inspector/InspectorHeader.js +52 -23
  45. package/dist/esm/components/Inspector/LogDetail.js +4 -3
  46. package/dist/esm/components/Inspector/MainScreen.js +12 -3
  47. package/dist/esm/components/Inspector/SettingsPanel.js +91 -2
  48. package/dist/esm/components/Inspector/TabBar.js +16 -4
  49. package/dist/esm/components/NetworkIcons.d.ts +11 -0
  50. package/dist/esm/components/NetworkIcons.js +83 -0
  51. package/dist/esm/components/SegmentedTabs.js +2 -1
  52. package/dist/esm/constants/version.d.ts +1 -1
  53. package/dist/esm/constants/version.js +1 -1
  54. package/dist/esm/customHooks/analyticsLogger.js +70 -44
  55. package/dist/esm/customHooks/consoleLogger.d.ts +1 -0
  56. package/dist/esm/customHooks/consoleLogger.js +68 -5
  57. package/dist/esm/customHooks/crashHandler.d.ts +64 -0
  58. package/dist/esm/customHooks/crashHandler.js +659 -0
  59. package/dist/esm/customHooks/networkLogger.js +64 -48
  60. package/dist/esm/helpers/gaAnalyticsRegistry.js +1 -1
  61. package/dist/esm/i18n/locales/en.json +131 -0
  62. package/dist/esm/index.d.ts +3 -1
  63. package/dist/esm/index.js +73 -1
  64. package/dist/esm/types/enums.d.ts +41 -0
  65. package/dist/esm/types/enums.js +36 -0
  66. package/dist/esm/types/interfaces.d.ts +51 -1
  67. package/ios/NetworkInspectorModule.h +6 -0
  68. package/ios/NetworkInspectorModule.m +125 -0
  69. package/package.json +5 -2
  70. package/react-native-inapp-inspector.podspec +18 -0
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { CrashFilterType } from '../../types/enums';
3
+ export interface CrashFilters {
4
+ types: Set<CrashFilterType>;
5
+ timeWindow: 'all' | '15m' | '1h' | '24h' | '7d';
6
+ platform: 'all' | 'ios' | 'android';
7
+ engine: 'all' | 'hermes' | 'jsc';
8
+ sortBy: 'time_desc' | 'time_asc';
9
+ }
10
+ export declare const DEFAULT_CRASH_FILTERS: CrashFilters;
11
+ export declare const isCrashFiltersDefault: (f: CrashFilters) => boolean;
12
+ interface CrashFilterModalProps {
13
+ visible: boolean;
14
+ onClose: () => void;
15
+ filters: CrashFilters;
16
+ onApply: (filters: CrashFilters) => void;
17
+ searchQuery?: string;
18
+ }
19
+ declare const CrashFilterModal: React.FC<CrashFilterModalProps>;
20
+ export default CrashFilterModal;
@@ -0,0 +1,597 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.isCrashFiltersDefault = exports.DEFAULT_CRASH_FILTERS = void 0;
40
+ const react_1 = __importStar(require("react"));
41
+ const react_native_1 = require("react-native");
42
+ const InspectorContext_1 = require("./InspectorContext");
43
+ const AppColors_1 = require("../../styles/AppColors");
44
+ const AppFonts_1 = require("../../styles/AppFonts");
45
+ const i18n_1 = require("../../i18n");
46
+ const NetworkIcons_1 = require("../NetworkIcons");
47
+ const TouchableScale_1 = __importDefault(require("../TouchableScale"));
48
+ exports.DEFAULT_CRASH_FILTERS = {
49
+ types: new Set(['all']),
50
+ timeWindow: 'all',
51
+ platform: 'all',
52
+ engine: 'all',
53
+ sortBy: 'time_desc',
54
+ };
55
+ const isCrashFiltersDefault = (f) => (f.types.has('all') || f.types.size === 0) &&
56
+ f.timeWindow === 'all' &&
57
+ f.platform === 'all' &&
58
+ f.engine === 'all' &&
59
+ f.sortBy === 'time_desc';
60
+ exports.isCrashFiltersDefault = isCrashFiltersDefault;
61
+ const CrashFilterModal = ({ visible, onClose, filters, onApply, searchQuery = '', }) => {
62
+ const { t } = (0, i18n_1.useTranslation)();
63
+ const { crashRecords } = (0, InspectorContext_1.useInspector)();
64
+ const [draft, setDraft] = (0, react_1.useState)(() => ({
65
+ ...filters,
66
+ types: new Set(filters.types),
67
+ }));
68
+ (0, react_1.useEffect)(() => {
69
+ if (visible) {
70
+ setDraft({
71
+ ...filters,
72
+ types: new Set(filters.types),
73
+ });
74
+ }
75
+ // eslint-disable-next-line react-hooks/exhaustive-deps
76
+ }, [visible]);
77
+ const toggleType = (key) => {
78
+ setDraft(prev => {
79
+ const next = new Set(prev.types);
80
+ if (key === 'all') {
81
+ return { ...prev, types: new Set(['all']) };
82
+ }
83
+ next.delete('all');
84
+ if (next.has(key)) {
85
+ next.delete(key);
86
+ if (next.size === 0)
87
+ next.add('all');
88
+ }
89
+ else {
90
+ next.add(key);
91
+ }
92
+ return { ...prev, types: next };
93
+ });
94
+ };
95
+ const setTimeWindow = (tw) => {
96
+ setDraft(prev => ({ ...prev, timeWindow: tw }));
97
+ };
98
+ const setPlatform = (p) => {
99
+ setDraft(prev => ({ ...prev, platform: p }));
100
+ };
101
+ const setEngine = (e) => {
102
+ setDraft(prev => ({ ...prev, engine: e }));
103
+ };
104
+ const setSort = (sortBy) => {
105
+ setDraft(prev => ({ ...prev, sortBy }));
106
+ };
107
+ const handleResetDraft = () => {
108
+ setDraft({
109
+ ...exports.DEFAULT_CRASH_FILTERS,
110
+ types: new Set(exports.DEFAULT_CRASH_FILTERS.types),
111
+ });
112
+ };
113
+ const handleApply = () => {
114
+ onApply({
115
+ ...draft,
116
+ types: new Set(draft.types),
117
+ });
118
+ onClose();
119
+ };
120
+ const handleDiscard = () => {
121
+ setDraft({
122
+ ...filters,
123
+ types: new Set(filters.types),
124
+ });
125
+ onClose();
126
+ };
127
+ const draftMatchCount = (0, react_1.useMemo)(() => {
128
+ let crashes = crashRecords;
129
+ if (searchQuery.trim().length > 0) {
130
+ const q = searchQuery.toLowerCase();
131
+ crashes = crashes.filter(c => (c.message || '').toLowerCase().includes(q) ||
132
+ (c.name || '').toLowerCase().includes(q) ||
133
+ (c.stack || '').toLowerCase().includes(q));
134
+ }
135
+ if (draft.types.size > 0 && !draft.types.has('all')) {
136
+ crashes = crashes.filter(c => {
137
+ if (draft.types.has('fatal') && c.isFatal)
138
+ return true;
139
+ if (draft.types.has('js') && c.type === 'js')
140
+ return true;
141
+ if (draft.types.has('promise') && c.type === 'promise')
142
+ return true;
143
+ if (draft.types.has('render') && c.type === 'render')
144
+ return true;
145
+ if (draft.types.has('native') && c.type === 'native')
146
+ return true;
147
+ return false;
148
+ });
149
+ }
150
+ if (draft.timeWindow !== 'all') {
151
+ const now = Date.now();
152
+ const cutoff = draft.timeWindow === '15m'
153
+ ? now - 15 * 60 * 1000
154
+ : draft.timeWindow === '1h'
155
+ ? now - 60 * 60 * 1000
156
+ : draft.timeWindow === '24h'
157
+ ? now - 24 * 60 * 60 * 1000
158
+ : now - 7 * 24 * 60 * 60 * 1000;
159
+ crashes = crashes.filter(c => c.timestamp >= cutoff);
160
+ }
161
+ if (draft.platform !== 'all') {
162
+ crashes = crashes.filter(c => (c.deviceInfo?.platform || '').toLowerCase() === draft.platform);
163
+ }
164
+ if (draft.engine !== 'all') {
165
+ crashes = crashes.filter(c => {
166
+ const isHermes = c.deviceInfo?.isHermes;
167
+ if (draft.engine === 'hermes')
168
+ return isHermes === true;
169
+ return isHermes === false || isHermes === undefined;
170
+ });
171
+ }
172
+ return crashes.length;
173
+ }, [crashRecords, searchQuery, draft]);
174
+ const isAllTypes = draft.types.has('all') || draft.types.size === 0;
175
+ const typeChips = [
176
+ { key: 'all', label: t('crash.filterTypeAll'), Icon: NetworkIcons_1.FilterIcon, color: AppColors_1.AppColors.grayTextStrong },
177
+ { key: 'fatal', label: t('crash.filterFatal'), Icon: NetworkIcons_1.SkullIcon, color: AppColors_1.AppColors.errorColor },
178
+ { key: 'js', label: t('crash.filterJsError'), Icon: NetworkIcons_1.JsIcon, color: AppColors_1.AppColors.sky600 },
179
+ { key: 'promise', label: t('crash.filterPromise'), Icon: NetworkIcons_1.HourglassIcon, color: AppColors_1.AppColors.darkOrange },
180
+ { key: 'render', label: t('crash.filterRender'), Icon: NetworkIcons_1.LayoutIcon, color: AppColors_1.AppColors.purple },
181
+ { key: 'native', label: t('crash.filterNative'), Icon: NetworkIcons_1.ChipIcon, color: AppColors_1.AppColors.amber700 },
182
+ ];
183
+ return (<react_native_1.Modal visible={visible} transparent animationType="fade" onRequestClose={handleDiscard}>
184
+ <react_native_1.View style={styles.backdrop}>
185
+ <react_native_1.Pressable style={react_native_1.StyleSheet.absoluteFill} onPress={handleDiscard}/>
186
+
187
+ <react_native_1.View style={styles.modalCard}>
188
+ <react_native_1.View style={styles.header}>
189
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
190
+ <react_native_1.View style={styles.headerIconBox}>
191
+ <NetworkIcons_1.FilterIcon color={AppColors_1.AppColors.brandPurple} size={15}/>
192
+ </react_native_1.View>
193
+ <react_native_1.View>
194
+ <react_native_1.Text style={styles.headerTitle}>{t('crash.filterTitle')}</react_native_1.Text>
195
+ <react_native_1.Text style={styles.headerSubtitle}>
196
+ {t('crash.filterSubtitle')}
197
+ </react_native_1.Text>
198
+ </react_native_1.View>
199
+ </react_native_1.View>
200
+
201
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
202
+ <react_native_1.TouchableOpacity onPress={handleResetDraft} hitSlop={8} style={styles.resetBtn}>
203
+ <react_native_1.Text style={styles.resetBtnText}>{t('crash.filterReset')}</react_native_1.Text>
204
+ </react_native_1.TouchableOpacity>
205
+
206
+ <react_native_1.TouchableOpacity onPress={handleDiscard} hitSlop={10} style={styles.closeBtn}>
207
+ <NetworkIcons_1.CloseWhite />
208
+ </react_native_1.TouchableOpacity>
209
+ </react_native_1.View>
210
+ </react_native_1.View>
211
+
212
+ <react_native_1.ScrollView style={{ maxHeight: 460 }} contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={true}>
213
+ <react_native_1.View style={styles.section}>
214
+ <react_native_1.View style={styles.sectionHeaderRow}>
215
+ <NetworkIcons_1.SkullIcon color={AppColors_1.AppColors.grayTextWeak} size={12}/>
216
+ <react_native_1.Text style={styles.sectionTitle}>
217
+ {t('crash.filterTypeSection')}
218
+ </react_native_1.Text>
219
+ </react_native_1.View>
220
+ <react_native_1.View style={styles.chipsWrap}>
221
+ {typeChips.map(chip => {
222
+ const isSelected = chip.key === 'all' ? isAllTypes : draft.types.has(chip.key);
223
+ return (<TouchableScale_1.default key={chip.key} onPress={() => toggleType(chip.key)} style={[
224
+ styles.filterPill,
225
+ isSelected && styles.filterPillActive,
226
+ ]}>
227
+ {chip.key === 'all' && isSelected && (<NetworkIcons_1.CheckIcon color={AppColors_1.AppColors.white} size={12}/>)}
228
+ <chip.Icon size={12} color={isSelected ? AppColors_1.AppColors.white : chip.color}/>
229
+ <react_native_1.Text style={[
230
+ styles.filterPillText,
231
+ isSelected && styles.filterPillTextActive,
232
+ ]}>
233
+ {chip.label}
234
+ </react_native_1.Text>
235
+ </TouchableScale_1.default>);
236
+ })}
237
+ </react_native_1.View>
238
+ </react_native_1.View>
239
+
240
+ <react_native_1.View style={styles.section}>
241
+ <react_native_1.View style={styles.sectionHeaderRow}>
242
+ <NetworkIcons_1.ClockIcon color={AppColors_1.AppColors.grayTextWeak} size={12}/>
243
+ <react_native_1.Text style={styles.sectionTitle}>
244
+ {t('crash.filterTimeSection')}
245
+ </react_native_1.Text>
246
+ </react_native_1.View>
247
+ <react_native_1.View style={styles.chipsWrap}>
248
+ {[
249
+ { key: 'all', label: t('crash.filterTimeAll') },
250
+ { key: '15m', label: t('crash.filterTime15m') },
251
+ { key: '1h', label: t('crash.filterTime1h') },
252
+ { key: '24h', label: t('crash.filterTime24h') },
253
+ { key: '7d', label: t('crash.filterTime7d') },
254
+ ].map(item => (<TouchableScale_1.default key={item.key} onPress={() => setTimeWindow(item.key)} style={[
255
+ styles.filterPill,
256
+ draft.timeWindow === item.key && styles.filterPillActive,
257
+ ]}>
258
+ <react_native_1.Text style={[
259
+ styles.filterPillText,
260
+ draft.timeWindow === item.key &&
261
+ styles.filterPillTextActive,
262
+ ]}>
263
+ {item.label}
264
+ </react_native_1.Text>
265
+ </TouchableScale_1.default>))}
266
+ </react_native_1.View>
267
+ </react_native_1.View>
268
+
269
+ <react_native_1.View style={styles.section}>
270
+ <react_native_1.View style={styles.sectionHeaderRow}>
271
+ <NetworkIcons_1.AppleIcon color={AppColors_1.AppColors.grayTextWeak} size={12}/>
272
+ <react_native_1.Text style={styles.sectionTitle}>
273
+ {t('crash.filterPlatformSection')}
274
+ </react_native_1.Text>
275
+ </react_native_1.View>
276
+ <react_native_1.View style={styles.chipsWrap}>
277
+ <TouchableScale_1.default onPress={() => setPlatform('all')} style={[
278
+ styles.filterPill,
279
+ draft.platform === 'all' && styles.filterPillActive,
280
+ ]}>
281
+ <react_native_1.Text style={[
282
+ styles.filterPillText,
283
+ draft.platform === 'all' && styles.filterPillTextActive,
284
+ ]}>
285
+ {t('crash.filterPlatformAll')}
286
+ </react_native_1.Text>
287
+ </TouchableScale_1.default>
288
+
289
+ <TouchableScale_1.default onPress={() => setPlatform('ios')} style={[
290
+ styles.filterPill,
291
+ draft.platform === 'ios' && styles.filterPillActive,
292
+ ]}>
293
+ <NetworkIcons_1.AppleIcon size={12} color={draft.platform === 'ios'
294
+ ? AppColors_1.AppColors.white
295
+ : AppColors_1.AppColors.grayTextStrong}/>
296
+ <react_native_1.Text style={[
297
+ styles.filterPillText,
298
+ draft.platform === 'ios' && styles.filterPillTextActive,
299
+ ]}>
300
+ {t('crash.filterPlatformIos')}
301
+ </react_native_1.Text>
302
+ </TouchableScale_1.default>
303
+
304
+ <TouchableScale_1.default onPress={() => setPlatform('android')} style={[
305
+ styles.filterPill,
306
+ draft.platform === 'android' && styles.filterPillActive,
307
+ ]}>
308
+ <NetworkIcons_1.AndroidIcon size={12} color={draft.platform === 'android'
309
+ ? AppColors_1.AppColors.white
310
+ : AppColors_1.AppColors.greenColor}/>
311
+ <react_native_1.Text style={[
312
+ styles.filterPillText,
313
+ draft.platform === 'android' && styles.filterPillTextActive,
314
+ ]}>
315
+ {t('crash.filterPlatformAndroid')}
316
+ </react_native_1.Text>
317
+ </TouchableScale_1.default>
318
+ </react_native_1.View>
319
+ </react_native_1.View>
320
+
321
+ <react_native_1.View style={styles.section}>
322
+ <react_native_1.View style={styles.sectionHeaderRow}>
323
+ <NetworkIcons_1.BoltIcon color={AppColors_1.AppColors.grayTextWeak} size={12}/>
324
+ <react_native_1.Text style={styles.sectionTitle}>
325
+ {t('crash.filterEngineSection')}
326
+ </react_native_1.Text>
327
+ </react_native_1.View>
328
+ <react_native_1.View style={styles.chipsWrap}>
329
+ <TouchableScale_1.default onPress={() => setEngine('all')} style={[
330
+ styles.filterPill,
331
+ draft.engine === 'all' && styles.filterPillActive,
332
+ ]}>
333
+ <react_native_1.Text style={[
334
+ styles.filterPillText,
335
+ draft.engine === 'all' && styles.filterPillTextActive,
336
+ ]}>
337
+ {t('crash.filterEngineAll')}
338
+ </react_native_1.Text>
339
+ </TouchableScale_1.default>
340
+
341
+ <TouchableScale_1.default onPress={() => setEngine('hermes')} style={[
342
+ styles.filterPill,
343
+ draft.engine === 'hermes' && styles.filterPillActive,
344
+ ]}>
345
+ <NetworkIcons_1.BoltIcon size={12} color={draft.engine === 'hermes'
346
+ ? AppColors_1.AppColors.white
347
+ : AppColors_1.AppColors.brandPurple}/>
348
+ <react_native_1.Text style={[
349
+ styles.filterPillText,
350
+ draft.engine === 'hermes' && styles.filterPillTextActive,
351
+ ]}>
352
+ {t('crash.filterEngineHermes')}
353
+ </react_native_1.Text>
354
+ </TouchableScale_1.default>
355
+
356
+ <TouchableScale_1.default onPress={() => setEngine('jsc')} style={[
357
+ styles.filterPill,
358
+ draft.engine === 'jsc' && styles.filterPillActive,
359
+ ]}>
360
+ <react_native_1.Text style={[
361
+ styles.filterPillText,
362
+ draft.engine === 'jsc' && styles.filterPillTextActive,
363
+ ]}>
364
+ {t('crash.jsc')}
365
+ </react_native_1.Text>
366
+ </TouchableScale_1.default>
367
+ </react_native_1.View>
368
+ </react_native_1.View>
369
+
370
+ <react_native_1.View style={styles.section}>
371
+ <react_native_1.View style={styles.sectionHeaderRow}>
372
+ <NetworkIcons_1.ClockIcon color={AppColors_1.AppColors.grayTextWeak} size={12}/>
373
+ <react_native_1.Text style={styles.sectionTitle}>
374
+ {t('crash.filterSortSection')}
375
+ </react_native_1.Text>
376
+ </react_native_1.View>
377
+ <react_native_1.View style={styles.chipsWrap}>
378
+ <TouchableScale_1.default onPress={() => setSort('time_desc')} style={[
379
+ styles.filterPill,
380
+ draft.sortBy === 'time_desc' && styles.filterPillActive,
381
+ ]}>
382
+ <NetworkIcons_1.ClockIcon size={12} color={draft.sortBy === 'time_desc'
383
+ ? AppColors_1.AppColors.white
384
+ : AppColors_1.AppColors.grayText}/>
385
+ <react_native_1.Text style={[
386
+ styles.filterPillText,
387
+ draft.sortBy === 'time_desc' &&
388
+ styles.filterPillTextActive,
389
+ ]}>
390
+ {t('crash.filterSortNewest')}
391
+ </react_native_1.Text>
392
+ </TouchableScale_1.default>
393
+
394
+ <TouchableScale_1.default onPress={() => setSort('time_asc')} style={[
395
+ styles.filterPill,
396
+ draft.sortBy === 'time_asc' && styles.filterPillActive,
397
+ ]}>
398
+ <NetworkIcons_1.HourglassIcon size={12} color={draft.sortBy === 'time_asc'
399
+ ? AppColors_1.AppColors.white
400
+ : AppColors_1.AppColors.grayText}/>
401
+ <react_native_1.Text style={[
402
+ styles.filterPillText,
403
+ draft.sortBy === 'time_asc' &&
404
+ styles.filterPillTextActive,
405
+ ]}>
406
+ {t('crash.filterSortOldest')}
407
+ </react_native_1.Text>
408
+ </TouchableScale_1.default>
409
+ </react_native_1.View>
410
+ </react_native_1.View>
411
+ </react_native_1.ScrollView>
412
+
413
+ <react_native_1.View style={styles.footer}>
414
+ <react_native_1.TouchableOpacity onPress={handleDiscard} style={styles.discardBtn} activeOpacity={0.8}>
415
+ <react_native_1.Text style={styles.discardBtnText}>
416
+ {t('crash.filterDiscard')}
417
+ </react_native_1.Text>
418
+ </react_native_1.TouchableOpacity>
419
+
420
+ <react_native_1.TouchableOpacity onPress={handleApply} style={styles.applyBtn} activeOpacity={0.85}>
421
+ <react_native_1.Text style={styles.applyBtnText}>
422
+ {t('crash.filterApply', { count: draftMatchCount })}
423
+ </react_native_1.Text>
424
+ </react_native_1.TouchableOpacity>
425
+ </react_native_1.View>
426
+ </react_native_1.View>
427
+ </react_native_1.View>
428
+ </react_native_1.Modal>);
429
+ };
430
+ const styles = react_native_1.StyleSheet.create({
431
+ backdrop: {
432
+ flex: 1,
433
+ backgroundColor: `${AppColors_1.AppColors.primaryBlack}85`,
434
+ justifyContent: 'center',
435
+ alignItems: 'center',
436
+ padding: 16,
437
+ },
438
+ modalCard: {
439
+ width: '100%',
440
+ maxWidth: 440,
441
+ backgroundColor: AppColors_1.AppColors.white,
442
+ borderRadius: 14,
443
+ borderWidth: 1,
444
+ borderColor: AppColors_1.AppColors.dividerColor,
445
+ shadowColor: AppColors_1.AppColors.black,
446
+ shadowOpacity: 0.18,
447
+ shadowRadius: 12,
448
+ shadowOffset: { width: 0, height: 4 },
449
+ elevation: 8,
450
+ overflow: 'hidden',
451
+ },
452
+ header: {
453
+ flexDirection: 'row',
454
+ alignItems: 'center',
455
+ justifyContent: 'space-between',
456
+ paddingHorizontal: 14,
457
+ paddingVertical: 12,
458
+ borderBottomWidth: 1,
459
+ borderBottomColor: AppColors_1.AppColors.dividerColor,
460
+ backgroundColor: `${AppColors_1.AppColors.brandPurple}08`,
461
+ },
462
+ headerIconBox: {
463
+ width: 28,
464
+ height: 28,
465
+ borderRadius: 7,
466
+ backgroundColor: `${AppColors_1.AppColors.brandPurple}18`,
467
+ alignItems: 'center',
468
+ justifyContent: 'center',
469
+ },
470
+ headerTitle: {
471
+ fontFamily: AppFonts_1.AppFonts.interBold,
472
+ fontSize: 13.5,
473
+ color: AppColors_1.AppColors.primaryBlack,
474
+ },
475
+ headerSubtitle: {
476
+ fontFamily: AppFonts_1.AppFonts.interMedium,
477
+ fontSize: 10,
478
+ color: AppColors_1.AppColors.grayTextWeak,
479
+ },
480
+ resetBtn: {
481
+ paddingHorizontal: 8,
482
+ paddingVertical: 4,
483
+ borderRadius: 5,
484
+ backgroundColor: `${AppColors_1.AppColors.darkOrange}14`,
485
+ borderWidth: 1,
486
+ borderColor: `${AppColors_1.AppColors.darkOrange}30`,
487
+ },
488
+ resetBtnText: {
489
+ fontFamily: AppFonts_1.AppFonts.interBold,
490
+ fontSize: 10,
491
+ color: AppColors_1.AppColors.darkOrange,
492
+ },
493
+ closeBtn: {
494
+ width: 24,
495
+ height: 24,
496
+ borderRadius: 12,
497
+ backgroundColor: `${AppColors_1.AppColors.primaryBlack}18`,
498
+ alignItems: 'center',
499
+ justifyContent: 'center',
500
+ },
501
+ scrollContent: {
502
+ padding: 14,
503
+ gap: 14,
504
+ },
505
+ section: {
506
+ gap: 7,
507
+ },
508
+ sectionHeaderRow: {
509
+ flexDirection: 'row',
510
+ alignItems: 'center',
511
+ gap: 5,
512
+ },
513
+ sectionTitle: {
514
+ fontFamily: AppFonts_1.AppFonts.interBold,
515
+ fontSize: 9.5,
516
+ color: AppColors_1.AppColors.grayTextWeak,
517
+ letterSpacing: 0.5,
518
+ },
519
+ chipsWrap: {
520
+ flexDirection: 'row',
521
+ flexWrap: 'wrap',
522
+ gap: 6.5,
523
+ },
524
+ filterPill: {
525
+ flexDirection: 'row',
526
+ alignItems: 'center',
527
+ gap: 5,
528
+ paddingHorizontal: 9,
529
+ paddingVertical: 5.5,
530
+ borderRadius: 7,
531
+ backgroundColor: AppColors_1.AppColors.grayBackground,
532
+ borderWidth: 1,
533
+ borderColor: AppColors_1.AppColors.dividerColor,
534
+ },
535
+ filterPillActive: {
536
+ backgroundColor: AppColors_1.AppColors.brandPurple,
537
+ borderColor: AppColors_1.AppColors.brandPurple,
538
+ shadowColor: AppColors_1.AppColors.brandPurple,
539
+ shadowOffset: { width: 0, height: 1 },
540
+ shadowOpacity: 0.25,
541
+ shadowRadius: 3,
542
+ elevation: 2,
543
+ },
544
+ filterPillText: {
545
+ fontFamily: AppFonts_1.AppFonts.interMedium,
546
+ fontSize: 11,
547
+ color: AppColors_1.AppColors.primaryBlack,
548
+ },
549
+ filterPillTextActive: {
550
+ color: AppColors_1.AppColors.white,
551
+ fontFamily: AppFonts_1.AppFonts.interBold,
552
+ },
553
+ footer: {
554
+ flexDirection: 'row',
555
+ alignItems: 'center',
556
+ gap: 10,
557
+ paddingHorizontal: 14,
558
+ paddingVertical: 12,
559
+ borderTopWidth: 1,
560
+ borderTopColor: AppColors_1.AppColors.dividerColor,
561
+ backgroundColor: AppColors_1.AppColors.grayBackground,
562
+ },
563
+ discardBtn: {
564
+ flex: 1,
565
+ paddingVertical: 9.5,
566
+ borderRadius: 8,
567
+ borderWidth: 1,
568
+ borderColor: AppColors_1.AppColors.dividerColor,
569
+ backgroundColor: AppColors_1.AppColors.white,
570
+ alignItems: 'center',
571
+ justifyContent: 'center',
572
+ },
573
+ discardBtnText: {
574
+ fontFamily: AppFonts_1.AppFonts.interBold,
575
+ fontSize: 12.5,
576
+ color: AppColors_1.AppColors.grayTextStrong,
577
+ },
578
+ applyBtn: {
579
+ flex: 2,
580
+ backgroundColor: AppColors_1.AppColors.brandPurple,
581
+ paddingVertical: 9.5,
582
+ borderRadius: 8,
583
+ alignItems: 'center',
584
+ justifyContent: 'center',
585
+ shadowColor: AppColors_1.AppColors.brandPurple,
586
+ shadowOffset: { width: 0, height: 1 },
587
+ shadowOpacity: 0.25,
588
+ shadowRadius: 3,
589
+ elevation: 2,
590
+ },
591
+ applyBtnText: {
592
+ fontFamily: AppFonts_1.AppFonts.interBold,
593
+ fontSize: 12.5,
594
+ color: AppColors_1.AppColors.white,
595
+ },
596
+ });
597
+ exports.default = CrashFilterModal;
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ declare const CrashTab: React.MemoExoticComponent<() => React.JSX.Element>;
3
+ export default CrashTab;