react-native-inapp-inspector 1.0.10 → 1.0.12
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.
- package/README.md +8 -0
- package/dist/commonjs/components/ReduxTreeView.d.ts +11 -0
- package/dist/commonjs/components/ReduxTreeView.js +228 -1
- package/dist/commonjs/customHooks/reduxLogger.d.ts +8 -0
- package/dist/commonjs/customHooks/reduxLogger.js +25 -2
- package/dist/commonjs/index.js +52 -5
- package/dist/esm/components/ReduxTreeView.d.ts +11 -0
- package/dist/esm/components/ReduxTreeView.js +226 -0
- package/dist/esm/customHooks/reduxLogger.d.ts +8 -0
- package/dist/esm/customHooks/reduxLogger.js +22 -1
- package/dist/esm/index.js +54 -7
- package/example/guidance/Video-WalkThrough.mp4 +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,6 +21,14 @@ A premium, self-contained, and interactive in-app debugger for React Native appl
|
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
+
## Video Walkthrough
|
|
25
|
+
|
|
26
|
+
Watch the library in action, demonstrating network inspection, Redux state tree analysis, WebView debugging, and console logging:
|
|
27
|
+
|
|
28
|
+
[🎬 Download or watch the Video Walkthrough](https://raw.githubusercontent.com/vengatmacuser/react-native-inapp-inspector/main/example/guidance/Video-WalkThrough.mp4)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
24
32
|
## Installation
|
|
25
33
|
|
|
26
34
|
Install the package as a development dependency in your React Native project:
|
|
@@ -4,3 +4,14 @@ export declare const ReduxTreeView: ({ state, lastActionMap, search, }: {
|
|
|
4
4
|
lastActionMap: Record<string, any>;
|
|
5
5
|
search?: string;
|
|
6
6
|
}) => React.JSX.Element;
|
|
7
|
+
export declare const ReduxActionTimeline: ({ history, onClear, search, }: {
|
|
8
|
+
history: Array<{
|
|
9
|
+
id: number;
|
|
10
|
+
type: string;
|
|
11
|
+
payload: any;
|
|
12
|
+
timestamp: string;
|
|
13
|
+
affectedSlices: string[];
|
|
14
|
+
}>;
|
|
15
|
+
onClear: () => void;
|
|
16
|
+
search?: string;
|
|
17
|
+
}) => React.JSX.Element;
|
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.ReduxTreeView = void 0;
|
|
36
|
+
exports.ReduxActionTimeline = exports.ReduxTreeView = void 0;
|
|
37
37
|
const react_1 = __importStar(require("react"));
|
|
38
38
|
const react_native_1 = require("react-native");
|
|
39
39
|
const AppColors_1 = require("../styles/AppColors");
|
|
@@ -210,6 +210,81 @@ const ReduxTreeView = ({ state, lastActionMap, search, }) => {
|
|
|
210
210
|
</react_native_1.View>);
|
|
211
211
|
};
|
|
212
212
|
exports.ReduxTreeView = ReduxTreeView;
|
|
213
|
+
const ReduxActionTimeline = ({ history, onClear, search, }) => {
|
|
214
|
+
const [expandedActionId, setExpandedActionId] = (0, react_1.useState)(null);
|
|
215
|
+
const toggleExpand = (id) => {
|
|
216
|
+
setExpandedActionId(prev => (prev === id ? null : id));
|
|
217
|
+
};
|
|
218
|
+
const filteredHistory = history.filter(action => {
|
|
219
|
+
if (!search)
|
|
220
|
+
return true;
|
|
221
|
+
const s = search.toLowerCase();
|
|
222
|
+
if (action.type.toLowerCase().includes(s))
|
|
223
|
+
return true;
|
|
224
|
+
if (action.affectedSlices.some(slice => slice.toLowerCase().includes(s)))
|
|
225
|
+
return true;
|
|
226
|
+
if (action.payload && typeof action.payload === 'object') {
|
|
227
|
+
return JSON.stringify(action.payload).toLowerCase().includes(s);
|
|
228
|
+
}
|
|
229
|
+
return false;
|
|
230
|
+
});
|
|
231
|
+
return (<react_native_1.View style={timelineStyles.container}>
|
|
232
|
+
<react_native_1.View style={timelineStyles.headerRow}>
|
|
233
|
+
<react_native_1.Text style={timelineStyles.headerTitle}>⚡ Dispatched Actions ({filteredHistory.length})</react_native_1.Text>
|
|
234
|
+
{history.length > 0 && (<react_native_1.Pressable onPress={onClear} style={timelineStyles.clearBtn}>
|
|
235
|
+
<react_native_1.Text style={timelineStyles.clearBtnText}>Clear Log</react_native_1.Text>
|
|
236
|
+
</react_native_1.Pressable>)}
|
|
237
|
+
</react_native_1.View>
|
|
238
|
+
|
|
239
|
+
{filteredHistory.length === 0 ? (<react_native_1.View style={timelineStyles.emptyContainer}>
|
|
240
|
+
<react_native_1.Text style={timelineStyles.emptyText}>
|
|
241
|
+
{history.length === 0
|
|
242
|
+
? 'No actions dispatched yet.\nDispatch actions in your application to see the timeline.'
|
|
243
|
+
: 'No matching actions found.'}
|
|
244
|
+
</react_native_1.Text>
|
|
245
|
+
</react_native_1.View>) : (<react_native_1.View style={timelineStyles.listContainer}>
|
|
246
|
+
{filteredHistory.map((item, index) => {
|
|
247
|
+
const isLast = index === filteredHistory.length - 1;
|
|
248
|
+
const isExpanded = expandedActionId === item.id;
|
|
249
|
+
return (<react_native_1.View key={item.id} style={timelineStyles.timelineItem}>
|
|
250
|
+
{/* Visual Line */}
|
|
251
|
+
<react_native_1.View style={[timelineStyles.verticalLine, isLast && { bottom: '50%' }]}/>
|
|
252
|
+
<react_native_1.View style={timelineStyles.circleIndicator}>
|
|
253
|
+
<react_native_1.View style={timelineStyles.circleInner}/>
|
|
254
|
+
</react_native_1.View>
|
|
255
|
+
|
|
256
|
+
{/* Card */}
|
|
257
|
+
<react_native_1.Pressable onPress={() => toggleExpand(item.id)} style={[
|
|
258
|
+
timelineStyles.card,
|
|
259
|
+
isExpanded && { borderColor: AppColors_1.AppColors.purple, backgroundColor: AppColors_1.AppColors.purpleShade50 },
|
|
260
|
+
]}>
|
|
261
|
+
<react_native_1.View style={timelineStyles.cardHeader}>
|
|
262
|
+
<react_native_1.View style={timelineStyles.typeBadge}>
|
|
263
|
+
<react_native_1.Text style={timelineStyles.typeText}>{item.type}</react_native_1.Text>
|
|
264
|
+
</react_native_1.View>
|
|
265
|
+
<react_native_1.Text style={timelineStyles.timestamp}>{item.timestamp}</react_native_1.Text>
|
|
266
|
+
</react_native_1.View>
|
|
267
|
+
|
|
268
|
+
{item.affectedSlices.length > 0 && (<react_native_1.View style={timelineStyles.slicesRow}>
|
|
269
|
+
<react_native_1.Text style={timelineStyles.slicesLabel}>Affected:</react_native_1.Text>
|
|
270
|
+
{item.affectedSlices.map(slice => (<react_native_1.View key={slice} style={timelineStyles.slicePill}>
|
|
271
|
+
<react_native_1.Text style={timelineStyles.sliceText}>{slice}</react_native_1.Text>
|
|
272
|
+
</react_native_1.View>))}
|
|
273
|
+
</react_native_1.View>)}
|
|
274
|
+
|
|
275
|
+
{isExpanded && (<react_native_1.View style={timelineStyles.payloadContainer}>
|
|
276
|
+
<react_native_1.Text style={timelineStyles.payloadTitle}>Payload</react_native_1.Text>
|
|
277
|
+
{item.payload !== null && typeof item.payload === 'object' ? (<ReduxValueNode name="action.payload" value={item.payload} level={0} search={search}/>) : (<react_native_1.Text style={timelineStyles.primitivePayload}>
|
|
278
|
+
{item.payload === null ? 'null' : String(item.payload)}
|
|
279
|
+
</react_native_1.Text>)}
|
|
280
|
+
</react_native_1.View>)}
|
|
281
|
+
</react_native_1.Pressable>
|
|
282
|
+
</react_native_1.View>);
|
|
283
|
+
})}
|
|
284
|
+
</react_native_1.View>)}
|
|
285
|
+
</react_native_1.View>);
|
|
286
|
+
};
|
|
287
|
+
exports.ReduxActionTimeline = ReduxActionTimeline;
|
|
213
288
|
const reduxValueStyles = react_native_1.StyleSheet.create({
|
|
214
289
|
treeNodeBlock: {
|
|
215
290
|
marginTop: 4,
|
|
@@ -261,6 +336,158 @@ const reduxValueStyles = react_native_1.StyleSheet.create({
|
|
|
261
336
|
color: AppColors_1.AppColors.grayTextWeak,
|
|
262
337
|
},
|
|
263
338
|
});
|
|
339
|
+
const timelineStyles = react_native_1.StyleSheet.create({
|
|
340
|
+
container: {
|
|
341
|
+
flex: 1,
|
|
342
|
+
},
|
|
343
|
+
headerRow: {
|
|
344
|
+
flexDirection: 'row',
|
|
345
|
+
alignItems: 'center',
|
|
346
|
+
justifyContent: 'space-between',
|
|
347
|
+
marginBottom: 12,
|
|
348
|
+
},
|
|
349
|
+
headerTitle: {
|
|
350
|
+
fontFamily: AppFonts_1.AppFonts.interBold,
|
|
351
|
+
fontSize: 14,
|
|
352
|
+
color: AppColors_1.AppColors.primaryBlack,
|
|
353
|
+
},
|
|
354
|
+
clearBtn: {
|
|
355
|
+
backgroundColor: 'rgba(239, 68, 68, 0.08)',
|
|
356
|
+
borderColor: 'rgba(239, 68, 68, 0.2)',
|
|
357
|
+
borderWidth: 1,
|
|
358
|
+
paddingHorizontal: 8,
|
|
359
|
+
paddingVertical: 4,
|
|
360
|
+
borderRadius: 6,
|
|
361
|
+
},
|
|
362
|
+
clearBtnText: {
|
|
363
|
+
fontFamily: AppFonts_1.AppFonts.interBold,
|
|
364
|
+
fontSize: 10,
|
|
365
|
+
color: '#EF4444',
|
|
366
|
+
},
|
|
367
|
+
emptyContainer: {
|
|
368
|
+
paddingVertical: 32,
|
|
369
|
+
alignItems: 'center',
|
|
370
|
+
justifyContent: 'center',
|
|
371
|
+
},
|
|
372
|
+
emptyText: {
|
|
373
|
+
fontFamily: AppFonts_1.AppFonts.interRegular,
|
|
374
|
+
fontSize: 12,
|
|
375
|
+
color: AppColors_1.AppColors.grayTextWeak,
|
|
376
|
+
textAlign: 'center',
|
|
377
|
+
lineHeight: 18,
|
|
378
|
+
},
|
|
379
|
+
listContainer: {
|
|
380
|
+
paddingLeft: 12,
|
|
381
|
+
},
|
|
382
|
+
timelineItem: {
|
|
383
|
+
position: 'relative',
|
|
384
|
+
paddingLeft: 20,
|
|
385
|
+
marginBottom: 12,
|
|
386
|
+
},
|
|
387
|
+
verticalLine: {
|
|
388
|
+
position: 'absolute',
|
|
389
|
+
left: 4,
|
|
390
|
+
top: 0,
|
|
391
|
+
bottom: -12,
|
|
392
|
+
width: 1,
|
|
393
|
+
backgroundColor: AppColors_1.AppColors.dividerColor,
|
|
394
|
+
},
|
|
395
|
+
circleIndicator: {
|
|
396
|
+
position: 'absolute',
|
|
397
|
+
left: 0,
|
|
398
|
+
top: 10,
|
|
399
|
+
width: 9,
|
|
400
|
+
height: 9,
|
|
401
|
+
borderRadius: 4.5,
|
|
402
|
+
backgroundColor: AppColors_1.AppColors.purpleShade50,
|
|
403
|
+
borderWidth: 1,
|
|
404
|
+
borderColor: AppColors_1.AppColors.purple,
|
|
405
|
+
alignItems: 'center',
|
|
406
|
+
justifyContent: 'center',
|
|
407
|
+
},
|
|
408
|
+
circleInner: {
|
|
409
|
+
width: 3,
|
|
410
|
+
height: 3,
|
|
411
|
+
borderRadius: 1.5,
|
|
412
|
+
backgroundColor: AppColors_1.AppColors.purple,
|
|
413
|
+
},
|
|
414
|
+
card: {
|
|
415
|
+
backgroundColor: AppColors_1.AppColors.primaryLight,
|
|
416
|
+
borderWidth: 1,
|
|
417
|
+
borderColor: AppColors_1.AppColors.grayBorderSecondary,
|
|
418
|
+
borderRadius: 8,
|
|
419
|
+
padding: 10,
|
|
420
|
+
},
|
|
421
|
+
cardHeader: {
|
|
422
|
+
flexDirection: 'row',
|
|
423
|
+
alignItems: 'center',
|
|
424
|
+
justifyContent: 'space-between',
|
|
425
|
+
gap: 12,
|
|
426
|
+
},
|
|
427
|
+
typeBadge: {
|
|
428
|
+
backgroundColor: 'rgba(104,75,155,0.08)',
|
|
429
|
+
borderColor: 'rgba(104,75,155,0.18)',
|
|
430
|
+
borderWidth: 1,
|
|
431
|
+
borderRadius: 6,
|
|
432
|
+
paddingHorizontal: 6,
|
|
433
|
+
paddingVertical: 2.5,
|
|
434
|
+
flexShrink: 1,
|
|
435
|
+
},
|
|
436
|
+
typeText: {
|
|
437
|
+
fontFamily: AppFonts_1.AppFonts.interBold,
|
|
438
|
+
fontSize: 11,
|
|
439
|
+
color: AppColors_1.AppColors.purple,
|
|
440
|
+
},
|
|
441
|
+
timestamp: {
|
|
442
|
+
fontFamily: AppFonts_1.AppFonts.interRegular,
|
|
443
|
+
fontSize: 10,
|
|
444
|
+
color: AppColors_1.AppColors.grayTextWeak,
|
|
445
|
+
},
|
|
446
|
+
slicesRow: {
|
|
447
|
+
flexDirection: 'row',
|
|
448
|
+
alignItems: 'center',
|
|
449
|
+
flexWrap: 'wrap',
|
|
450
|
+
gap: 4,
|
|
451
|
+
marginTop: 6,
|
|
452
|
+
},
|
|
453
|
+
slicesLabel: {
|
|
454
|
+
fontFamily: AppFonts_1.AppFonts.interMedium,
|
|
455
|
+
fontSize: 10,
|
|
456
|
+
color: AppColors_1.AppColors.grayTextWeak,
|
|
457
|
+
marginRight: 2,
|
|
458
|
+
},
|
|
459
|
+
slicePill: {
|
|
460
|
+
backgroundColor: AppColors_1.AppColors.grayBackground,
|
|
461
|
+
borderColor: AppColors_1.AppColors.dividerColor,
|
|
462
|
+
borderWidth: 1,
|
|
463
|
+
borderRadius: 4,
|
|
464
|
+
paddingHorizontal: 4,
|
|
465
|
+
paddingVertical: 1,
|
|
466
|
+
},
|
|
467
|
+
sliceText: {
|
|
468
|
+
fontFamily: AppFonts_1.AppFonts.interMedium,
|
|
469
|
+
fontSize: 9,
|
|
470
|
+
color: AppColors_1.AppColors.grayText,
|
|
471
|
+
},
|
|
472
|
+
payloadContainer: {
|
|
473
|
+
marginTop: 10,
|
|
474
|
+
borderTopWidth: 1,
|
|
475
|
+
borderTopColor: AppColors_1.AppColors.dividerColor,
|
|
476
|
+
paddingTop: 8,
|
|
477
|
+
},
|
|
478
|
+
payloadTitle: {
|
|
479
|
+
fontFamily: AppFonts_1.AppFonts.interBold,
|
|
480
|
+
fontSize: 10,
|
|
481
|
+
color: AppColors_1.AppColors.grayTextWeak,
|
|
482
|
+
textTransform: 'uppercase',
|
|
483
|
+
marginBottom: 4,
|
|
484
|
+
},
|
|
485
|
+
primitivePayload: {
|
|
486
|
+
fontFamily: AppFonts_1.AppFonts.interRegular,
|
|
487
|
+
fontSize: 11,
|
|
488
|
+
color: AppColors_1.AppColors.grayTextStrong,
|
|
489
|
+
},
|
|
490
|
+
});
|
|
264
491
|
const styles = react_native_1.StyleSheet.create({
|
|
265
492
|
container: {
|
|
266
493
|
flex: 1,
|
|
@@ -3,6 +3,14 @@ export declare const setReduxAutoRefresh: (val: boolean) => void;
|
|
|
3
3
|
export declare const getReduxAutoRefresh: () => boolean;
|
|
4
4
|
export declare const getLastActionForReducer: () => Record<string, any>;
|
|
5
5
|
export declare const clearLastActionForReducer: () => void;
|
|
6
|
+
export declare const getActionHistory: () => {
|
|
7
|
+
id: number;
|
|
8
|
+
type: string;
|
|
9
|
+
payload: any;
|
|
10
|
+
timestamp: string;
|
|
11
|
+
affectedSlices: string[];
|
|
12
|
+
}[];
|
|
13
|
+
export declare const clearActionHistory: () => void;
|
|
6
14
|
export declare const setReduxState: (state: any) => void;
|
|
7
15
|
export declare const subscribeReduxState: (cb: () => void) => () => void;
|
|
8
16
|
export declare const connectReduxStore: (store: any) => void;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.connectReduxStore = exports.subscribeReduxState = exports.setReduxState = exports.clearLastActionForReducer = exports.getLastActionForReducer = exports.getReduxAutoRefresh = exports.setReduxAutoRefresh = exports.getReduxState = void 0;
|
|
3
|
+
exports.connectReduxStore = exports.subscribeReduxState = exports.setReduxState = exports.clearActionHistory = exports.getActionHistory = exports.clearLastActionForReducer = exports.getLastActionForReducer = exports.getReduxAutoRefresh = exports.setReduxAutoRefresh = exports.getReduxState = void 0;
|
|
4
4
|
let currentReduxState = null;
|
|
5
5
|
const listeners = new Set();
|
|
6
6
|
let globalReduxAutoRefresh = true;
|
|
7
7
|
let lastActionForReducer = {};
|
|
8
|
+
let actionHistory = [];
|
|
8
9
|
const getReduxState = () => currentReduxState;
|
|
9
10
|
exports.getReduxState = getReduxState;
|
|
10
11
|
const setReduxAutoRefresh = (val) => {
|
|
@@ -20,6 +21,13 @@ const clearLastActionForReducer = () => {
|
|
|
20
21
|
listeners.forEach(cb => cb());
|
|
21
22
|
};
|
|
22
23
|
exports.clearLastActionForReducer = clearLastActionForReducer;
|
|
24
|
+
const getActionHistory = () => actionHistory;
|
|
25
|
+
exports.getActionHistory = getActionHistory;
|
|
26
|
+
const clearActionHistory = () => {
|
|
27
|
+
actionHistory = [];
|
|
28
|
+
listeners.forEach(cb => cb());
|
|
29
|
+
};
|
|
30
|
+
exports.clearActionHistory = clearActionHistory;
|
|
23
31
|
const setReduxState = (state) => {
|
|
24
32
|
currentReduxState = state;
|
|
25
33
|
listeners.forEach(cb => cb());
|
|
@@ -44,6 +52,7 @@ const connectReduxStore = (store) => {
|
|
|
44
52
|
const result = originalDispatch(action);
|
|
45
53
|
const nextState = store.getState();
|
|
46
54
|
// Map the dispatched action to state slices that actually changed
|
|
55
|
+
const affectedSlices = [];
|
|
47
56
|
if (prevState &&
|
|
48
57
|
nextState &&
|
|
49
58
|
typeof prevState === 'object' &&
|
|
@@ -52,13 +61,27 @@ const connectReduxStore = (store) => {
|
|
|
52
61
|
typeof action === 'object') {
|
|
53
62
|
Object.keys(nextState).forEach(key => {
|
|
54
63
|
if (prevState[key] !== nextState[key]) {
|
|
55
|
-
|
|
64
|
+
const actionObj = {
|
|
56
65
|
type: action.type || 'UNKNOWN_ACTION',
|
|
57
66
|
payload: action.payload !== undefined ? action.payload : null,
|
|
58
67
|
timestamp: new Date().toLocaleTimeString(),
|
|
59
68
|
};
|
|
69
|
+
lastActionForReducer[key] = actionObj;
|
|
70
|
+
affectedSlices.push(key);
|
|
60
71
|
}
|
|
61
72
|
});
|
|
73
|
+
// Push to history
|
|
74
|
+
actionHistory.unshift({
|
|
75
|
+
id: Date.now() + Math.random(),
|
|
76
|
+
type: action.type || 'UNKNOWN_ACTION',
|
|
77
|
+
payload: action.payload !== undefined ? action.payload : null,
|
|
78
|
+
timestamp: new Date().toLocaleTimeString(),
|
|
79
|
+
affectedSlices,
|
|
80
|
+
});
|
|
81
|
+
// Cap size at 50
|
|
82
|
+
if (actionHistory.length > 50) {
|
|
83
|
+
actionHistory.pop();
|
|
84
|
+
}
|
|
62
85
|
}
|
|
63
86
|
if (globalReduxAutoRefresh) {
|
|
64
87
|
(0, exports.setReduxState)(nextState);
|
package/dist/commonjs/index.js
CHANGED
|
@@ -158,6 +158,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
158
158
|
const [search, setSearch] = (0, react_1.useState)('');
|
|
159
159
|
const [detailSearch, setDetailSearch] = (0, react_1.useState)('');
|
|
160
160
|
const [reduxSearch, setReduxSearch] = (0, react_1.useState)('');
|
|
161
|
+
const [reduxActiveSubTab, setReduxActiveSubTab] = (0, react_1.useState)('timeline');
|
|
161
162
|
const [apiDetailActiveTab, setApiDetailActiveTab] = (0, react_1.useState)('response');
|
|
162
163
|
(0, react_1.useEffect)(() => {
|
|
163
164
|
if (enabled && visible) {
|
|
@@ -998,7 +999,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
998
999
|
borderWidth: 1,
|
|
999
1000
|
borderColor: 'rgba(255, 255, 255, 0.1)'
|
|
1000
1001
|
}}>
|
|
1001
|
-
<react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 10.5, color: '#FFFFFF' }}>v1.0.
|
|
1002
|
+
<react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 10.5, color: '#FFFFFF' }}>v1.0.12</react_native_1.Text>
|
|
1002
1003
|
</react_native_1.View>
|
|
1003
1004
|
</react_native_1.View>
|
|
1004
1005
|
</react_native_linear_gradient_1.default>
|
|
@@ -1732,6 +1733,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
1732
1733
|
}
|
|
1733
1734
|
// Build hierarchical tree: Store -> Reducers -> Action -> Data
|
|
1734
1735
|
const lastActionMap = (0, reduxLogger_1.getLastActionForReducer)();
|
|
1736
|
+
const actionHistory = (0, reduxLogger_1.getActionHistory)();
|
|
1735
1737
|
return (<react_native_1.ScrollView style={styles_1.default.detailScroll} contentContainerStyle={{ paddingBottom: 24 }}>
|
|
1736
1738
|
{/* Top Summary Card */}
|
|
1737
1739
|
<react_native_1.View style={{
|
|
@@ -1768,6 +1770,51 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
1768
1770
|
<CopyButton_1.default value={() => reduxState} label="Overall Store"/>
|
|
1769
1771
|
</react_native_1.View>
|
|
1770
1772
|
|
|
1773
|
+
{/* Tab View Selection Segments */}
|
|
1774
|
+
<react_native_1.View style={{
|
|
1775
|
+
flexDirection: 'row',
|
|
1776
|
+
backgroundColor: AppColors_1.AppColors.grayBackground,
|
|
1777
|
+
borderRadius: 10,
|
|
1778
|
+
padding: 3,
|
|
1779
|
+
marginHorizontal: 16,
|
|
1780
|
+
marginBottom: 12,
|
|
1781
|
+
borderWidth: 1,
|
|
1782
|
+
borderColor: AppColors_1.AppColors.dividerColor,
|
|
1783
|
+
}}>
|
|
1784
|
+
<react_native_1.TouchableOpacity onPress={() => setReduxActiveSubTab('timeline')} style={{
|
|
1785
|
+
flex: 1,
|
|
1786
|
+
paddingVertical: 6,
|
|
1787
|
+
alignItems: 'center',
|
|
1788
|
+
justifyContent: 'center',
|
|
1789
|
+
borderRadius: 8,
|
|
1790
|
+
backgroundColor: reduxActiveSubTab === 'timeline' ? AppColors_1.AppColors.purple : 'transparent',
|
|
1791
|
+
}}>
|
|
1792
|
+
<react_native_1.Text style={{
|
|
1793
|
+
fontFamily: AppFonts_1.AppFonts.interBold,
|
|
1794
|
+
fontSize: 11,
|
|
1795
|
+
color: reduxActiveSubTab === 'timeline' ? '#FFFFFF' : AppColors_1.AppColors.grayText,
|
|
1796
|
+
}}>
|
|
1797
|
+
⚡ Action Timeline
|
|
1798
|
+
</react_native_1.Text>
|
|
1799
|
+
</react_native_1.TouchableOpacity>
|
|
1800
|
+
<react_native_1.TouchableOpacity onPress={() => setReduxActiveSubTab('tree')} style={{
|
|
1801
|
+
flex: 1,
|
|
1802
|
+
paddingVertical: 6,
|
|
1803
|
+
alignItems: 'center',
|
|
1804
|
+
justifyContent: 'center',
|
|
1805
|
+
borderRadius: 8,
|
|
1806
|
+
backgroundColor: reduxActiveSubTab === 'tree' ? AppColors_1.AppColors.purple : 'transparent',
|
|
1807
|
+
}}>
|
|
1808
|
+
<react_native_1.Text style={{
|
|
1809
|
+
fontFamily: AppFonts_1.AppFonts.interBold,
|
|
1810
|
+
fontSize: 11,
|
|
1811
|
+
color: reduxActiveSubTab === 'tree' ? '#FFFFFF' : AppColors_1.AppColors.grayText,
|
|
1812
|
+
}}>
|
|
1813
|
+
🏪 Store Tree
|
|
1814
|
+
</react_native_1.Text>
|
|
1815
|
+
</react_native_1.TouchableOpacity>
|
|
1816
|
+
</react_native_1.View>
|
|
1817
|
+
|
|
1771
1818
|
{/* Search Bar */}
|
|
1772
1819
|
<react_native_1.View style={{
|
|
1773
1820
|
flexDirection: 'row',
|
|
@@ -1781,7 +1828,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
1781
1828
|
borderColor: AppColors_1.AppColors.dividerColor,
|
|
1782
1829
|
height: 36,
|
|
1783
1830
|
}}>
|
|
1784
|
-
<react_native_1.TextInput placeholder="Search Redux keys or values..." placeholderTextColor={AppColors_1.AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={{
|
|
1831
|
+
<react_native_1.TextInput placeholder={reduxActiveSubTab === 'timeline' ? "Search actions or payloads..." : "Search Redux keys or values..."} placeholderTextColor={AppColors_1.AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={{
|
|
1785
1832
|
flex: 1,
|
|
1786
1833
|
fontFamily: AppFonts_1.AppFonts.interRegular,
|
|
1787
1834
|
fontSize: 12,
|
|
@@ -1793,7 +1840,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
1793
1840
|
</react_native_1.Pressable>)}
|
|
1794
1841
|
</react_native_1.View>
|
|
1795
1842
|
|
|
1796
|
-
{/* Main
|
|
1843
|
+
{/* Main Content Card */}
|
|
1797
1844
|
<react_native_1.View style={{
|
|
1798
1845
|
backgroundColor: AppColors_1.AppColors.primaryLight,
|
|
1799
1846
|
borderRadius: 12,
|
|
@@ -1802,7 +1849,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
1802
1849
|
marginHorizontal: 16,
|
|
1803
1850
|
padding: 12,
|
|
1804
1851
|
}}>
|
|
1805
|
-
<ReduxTreeView_1.ReduxTreeView state={reduxState} lastActionMap={lastActionMap} search={reduxSearch}/>
|
|
1852
|
+
{reduxActiveSubTab === 'timeline' ? (<ReduxTreeView_1.ReduxActionTimeline history={actionHistory} onClear={reduxLogger_1.clearActionHistory} search={reduxSearch}/>) : (<ReduxTreeView_1.ReduxTreeView state={reduxState} lastActionMap={lastActionMap} search={reduxSearch}/>)}
|
|
1806
1853
|
</react_native_1.View>
|
|
1807
1854
|
</react_native_1.ScrollView>);
|
|
1808
1855
|
};
|
|
@@ -1877,7 +1924,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
1877
1924
|
<react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 5 }}>
|
|
1878
1925
|
<react_native_1.Animated.View style={{ width: 6, height: 6, borderRadius: 3, backgroundColor: '#4ADE80', opacity: activePulseAnim }}/>
|
|
1879
1926
|
<react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interMedium, fontSize: 10, color: 'rgba(255,255,255,0.78)', letterSpacing: 0.3 }}>
|
|
1880
|
-
Active • {react_native_1.Platform.OS === 'ios' ? 'iOS' : 'Android'} (v1.0.
|
|
1927
|
+
Active • {react_native_1.Platform.OS === 'ios' ? 'iOS' : 'Android'} (v1.0.12)
|
|
1881
1928
|
</react_native_1.Text>
|
|
1882
1929
|
</react_native_1.View>
|
|
1883
1930
|
</react_native_1.View>
|
|
@@ -4,3 +4,14 @@ export declare const ReduxTreeView: ({ state, lastActionMap, search, }: {
|
|
|
4
4
|
lastActionMap: Record<string, any>;
|
|
5
5
|
search?: string;
|
|
6
6
|
}) => React.JSX.Element;
|
|
7
|
+
export declare const ReduxActionTimeline: ({ history, onClear, search, }: {
|
|
8
|
+
history: Array<{
|
|
9
|
+
id: number;
|
|
10
|
+
type: string;
|
|
11
|
+
payload: any;
|
|
12
|
+
timestamp: string;
|
|
13
|
+
affectedSlices: string[];
|
|
14
|
+
}>;
|
|
15
|
+
onClear: () => void;
|
|
16
|
+
search?: string;
|
|
17
|
+
}) => React.JSX.Element;
|
|
@@ -173,6 +173,80 @@ export const ReduxTreeView = ({ state, lastActionMap, search, }) => {
|
|
|
173
173
|
</View>)}
|
|
174
174
|
</View>);
|
|
175
175
|
};
|
|
176
|
+
export const ReduxActionTimeline = ({ history, onClear, search, }) => {
|
|
177
|
+
const [expandedActionId, setExpandedActionId] = useState(null);
|
|
178
|
+
const toggleExpand = (id) => {
|
|
179
|
+
setExpandedActionId(prev => (prev === id ? null : id));
|
|
180
|
+
};
|
|
181
|
+
const filteredHistory = history.filter(action => {
|
|
182
|
+
if (!search)
|
|
183
|
+
return true;
|
|
184
|
+
const s = search.toLowerCase();
|
|
185
|
+
if (action.type.toLowerCase().includes(s))
|
|
186
|
+
return true;
|
|
187
|
+
if (action.affectedSlices.some(slice => slice.toLowerCase().includes(s)))
|
|
188
|
+
return true;
|
|
189
|
+
if (action.payload && typeof action.payload === 'object') {
|
|
190
|
+
return JSON.stringify(action.payload).toLowerCase().includes(s);
|
|
191
|
+
}
|
|
192
|
+
return false;
|
|
193
|
+
});
|
|
194
|
+
return (<View style={timelineStyles.container}>
|
|
195
|
+
<View style={timelineStyles.headerRow}>
|
|
196
|
+
<Text style={timelineStyles.headerTitle}>⚡ Dispatched Actions ({filteredHistory.length})</Text>
|
|
197
|
+
{history.length > 0 && (<Pressable onPress={onClear} style={timelineStyles.clearBtn}>
|
|
198
|
+
<Text style={timelineStyles.clearBtnText}>Clear Log</Text>
|
|
199
|
+
</Pressable>)}
|
|
200
|
+
</View>
|
|
201
|
+
|
|
202
|
+
{filteredHistory.length === 0 ? (<View style={timelineStyles.emptyContainer}>
|
|
203
|
+
<Text style={timelineStyles.emptyText}>
|
|
204
|
+
{history.length === 0
|
|
205
|
+
? 'No actions dispatched yet.\nDispatch actions in your application to see the timeline.'
|
|
206
|
+
: 'No matching actions found.'}
|
|
207
|
+
</Text>
|
|
208
|
+
</View>) : (<View style={timelineStyles.listContainer}>
|
|
209
|
+
{filteredHistory.map((item, index) => {
|
|
210
|
+
const isLast = index === filteredHistory.length - 1;
|
|
211
|
+
const isExpanded = expandedActionId === item.id;
|
|
212
|
+
return (<View key={item.id} style={timelineStyles.timelineItem}>
|
|
213
|
+
{/* Visual Line */}
|
|
214
|
+
<View style={[timelineStyles.verticalLine, isLast && { bottom: '50%' }]}/>
|
|
215
|
+
<View style={timelineStyles.circleIndicator}>
|
|
216
|
+
<View style={timelineStyles.circleInner}/>
|
|
217
|
+
</View>
|
|
218
|
+
|
|
219
|
+
{/* Card */}
|
|
220
|
+
<Pressable onPress={() => toggleExpand(item.id)} style={[
|
|
221
|
+
timelineStyles.card,
|
|
222
|
+
isExpanded && { borderColor: AppColors.purple, backgroundColor: AppColors.purpleShade50 },
|
|
223
|
+
]}>
|
|
224
|
+
<View style={timelineStyles.cardHeader}>
|
|
225
|
+
<View style={timelineStyles.typeBadge}>
|
|
226
|
+
<Text style={timelineStyles.typeText}>{item.type}</Text>
|
|
227
|
+
</View>
|
|
228
|
+
<Text style={timelineStyles.timestamp}>{item.timestamp}</Text>
|
|
229
|
+
</View>
|
|
230
|
+
|
|
231
|
+
{item.affectedSlices.length > 0 && (<View style={timelineStyles.slicesRow}>
|
|
232
|
+
<Text style={timelineStyles.slicesLabel}>Affected:</Text>
|
|
233
|
+
{item.affectedSlices.map(slice => (<View key={slice} style={timelineStyles.slicePill}>
|
|
234
|
+
<Text style={timelineStyles.sliceText}>{slice}</Text>
|
|
235
|
+
</View>))}
|
|
236
|
+
</View>)}
|
|
237
|
+
|
|
238
|
+
{isExpanded && (<View style={timelineStyles.payloadContainer}>
|
|
239
|
+
<Text style={timelineStyles.payloadTitle}>Payload</Text>
|
|
240
|
+
{item.payload !== null && typeof item.payload === 'object' ? (<ReduxValueNode name="action.payload" value={item.payload} level={0} search={search}/>) : (<Text style={timelineStyles.primitivePayload}>
|
|
241
|
+
{item.payload === null ? 'null' : String(item.payload)}
|
|
242
|
+
</Text>)}
|
|
243
|
+
</View>)}
|
|
244
|
+
</Pressable>
|
|
245
|
+
</View>);
|
|
246
|
+
})}
|
|
247
|
+
</View>)}
|
|
248
|
+
</View>);
|
|
249
|
+
};
|
|
176
250
|
const reduxValueStyles = StyleSheet.create({
|
|
177
251
|
treeNodeBlock: {
|
|
178
252
|
marginTop: 4,
|
|
@@ -224,6 +298,158 @@ const reduxValueStyles = StyleSheet.create({
|
|
|
224
298
|
color: AppColors.grayTextWeak,
|
|
225
299
|
},
|
|
226
300
|
});
|
|
301
|
+
const timelineStyles = StyleSheet.create({
|
|
302
|
+
container: {
|
|
303
|
+
flex: 1,
|
|
304
|
+
},
|
|
305
|
+
headerRow: {
|
|
306
|
+
flexDirection: 'row',
|
|
307
|
+
alignItems: 'center',
|
|
308
|
+
justifyContent: 'space-between',
|
|
309
|
+
marginBottom: 12,
|
|
310
|
+
},
|
|
311
|
+
headerTitle: {
|
|
312
|
+
fontFamily: AppFonts.interBold,
|
|
313
|
+
fontSize: 14,
|
|
314
|
+
color: AppColors.primaryBlack,
|
|
315
|
+
},
|
|
316
|
+
clearBtn: {
|
|
317
|
+
backgroundColor: 'rgba(239, 68, 68, 0.08)',
|
|
318
|
+
borderColor: 'rgba(239, 68, 68, 0.2)',
|
|
319
|
+
borderWidth: 1,
|
|
320
|
+
paddingHorizontal: 8,
|
|
321
|
+
paddingVertical: 4,
|
|
322
|
+
borderRadius: 6,
|
|
323
|
+
},
|
|
324
|
+
clearBtnText: {
|
|
325
|
+
fontFamily: AppFonts.interBold,
|
|
326
|
+
fontSize: 10,
|
|
327
|
+
color: '#EF4444',
|
|
328
|
+
},
|
|
329
|
+
emptyContainer: {
|
|
330
|
+
paddingVertical: 32,
|
|
331
|
+
alignItems: 'center',
|
|
332
|
+
justifyContent: 'center',
|
|
333
|
+
},
|
|
334
|
+
emptyText: {
|
|
335
|
+
fontFamily: AppFonts.interRegular,
|
|
336
|
+
fontSize: 12,
|
|
337
|
+
color: AppColors.grayTextWeak,
|
|
338
|
+
textAlign: 'center',
|
|
339
|
+
lineHeight: 18,
|
|
340
|
+
},
|
|
341
|
+
listContainer: {
|
|
342
|
+
paddingLeft: 12,
|
|
343
|
+
},
|
|
344
|
+
timelineItem: {
|
|
345
|
+
position: 'relative',
|
|
346
|
+
paddingLeft: 20,
|
|
347
|
+
marginBottom: 12,
|
|
348
|
+
},
|
|
349
|
+
verticalLine: {
|
|
350
|
+
position: 'absolute',
|
|
351
|
+
left: 4,
|
|
352
|
+
top: 0,
|
|
353
|
+
bottom: -12,
|
|
354
|
+
width: 1,
|
|
355
|
+
backgroundColor: AppColors.dividerColor,
|
|
356
|
+
},
|
|
357
|
+
circleIndicator: {
|
|
358
|
+
position: 'absolute',
|
|
359
|
+
left: 0,
|
|
360
|
+
top: 10,
|
|
361
|
+
width: 9,
|
|
362
|
+
height: 9,
|
|
363
|
+
borderRadius: 4.5,
|
|
364
|
+
backgroundColor: AppColors.purpleShade50,
|
|
365
|
+
borderWidth: 1,
|
|
366
|
+
borderColor: AppColors.purple,
|
|
367
|
+
alignItems: 'center',
|
|
368
|
+
justifyContent: 'center',
|
|
369
|
+
},
|
|
370
|
+
circleInner: {
|
|
371
|
+
width: 3,
|
|
372
|
+
height: 3,
|
|
373
|
+
borderRadius: 1.5,
|
|
374
|
+
backgroundColor: AppColors.purple,
|
|
375
|
+
},
|
|
376
|
+
card: {
|
|
377
|
+
backgroundColor: AppColors.primaryLight,
|
|
378
|
+
borderWidth: 1,
|
|
379
|
+
borderColor: AppColors.grayBorderSecondary,
|
|
380
|
+
borderRadius: 8,
|
|
381
|
+
padding: 10,
|
|
382
|
+
},
|
|
383
|
+
cardHeader: {
|
|
384
|
+
flexDirection: 'row',
|
|
385
|
+
alignItems: 'center',
|
|
386
|
+
justifyContent: 'space-between',
|
|
387
|
+
gap: 12,
|
|
388
|
+
},
|
|
389
|
+
typeBadge: {
|
|
390
|
+
backgroundColor: 'rgba(104,75,155,0.08)',
|
|
391
|
+
borderColor: 'rgba(104,75,155,0.18)',
|
|
392
|
+
borderWidth: 1,
|
|
393
|
+
borderRadius: 6,
|
|
394
|
+
paddingHorizontal: 6,
|
|
395
|
+
paddingVertical: 2.5,
|
|
396
|
+
flexShrink: 1,
|
|
397
|
+
},
|
|
398
|
+
typeText: {
|
|
399
|
+
fontFamily: AppFonts.interBold,
|
|
400
|
+
fontSize: 11,
|
|
401
|
+
color: AppColors.purple,
|
|
402
|
+
},
|
|
403
|
+
timestamp: {
|
|
404
|
+
fontFamily: AppFonts.interRegular,
|
|
405
|
+
fontSize: 10,
|
|
406
|
+
color: AppColors.grayTextWeak,
|
|
407
|
+
},
|
|
408
|
+
slicesRow: {
|
|
409
|
+
flexDirection: 'row',
|
|
410
|
+
alignItems: 'center',
|
|
411
|
+
flexWrap: 'wrap',
|
|
412
|
+
gap: 4,
|
|
413
|
+
marginTop: 6,
|
|
414
|
+
},
|
|
415
|
+
slicesLabel: {
|
|
416
|
+
fontFamily: AppFonts.interMedium,
|
|
417
|
+
fontSize: 10,
|
|
418
|
+
color: AppColors.grayTextWeak,
|
|
419
|
+
marginRight: 2,
|
|
420
|
+
},
|
|
421
|
+
slicePill: {
|
|
422
|
+
backgroundColor: AppColors.grayBackground,
|
|
423
|
+
borderColor: AppColors.dividerColor,
|
|
424
|
+
borderWidth: 1,
|
|
425
|
+
borderRadius: 4,
|
|
426
|
+
paddingHorizontal: 4,
|
|
427
|
+
paddingVertical: 1,
|
|
428
|
+
},
|
|
429
|
+
sliceText: {
|
|
430
|
+
fontFamily: AppFonts.interMedium,
|
|
431
|
+
fontSize: 9,
|
|
432
|
+
color: AppColors.grayText,
|
|
433
|
+
},
|
|
434
|
+
payloadContainer: {
|
|
435
|
+
marginTop: 10,
|
|
436
|
+
borderTopWidth: 1,
|
|
437
|
+
borderTopColor: AppColors.dividerColor,
|
|
438
|
+
paddingTop: 8,
|
|
439
|
+
},
|
|
440
|
+
payloadTitle: {
|
|
441
|
+
fontFamily: AppFonts.interBold,
|
|
442
|
+
fontSize: 10,
|
|
443
|
+
color: AppColors.grayTextWeak,
|
|
444
|
+
textTransform: 'uppercase',
|
|
445
|
+
marginBottom: 4,
|
|
446
|
+
},
|
|
447
|
+
primitivePayload: {
|
|
448
|
+
fontFamily: AppFonts.interRegular,
|
|
449
|
+
fontSize: 11,
|
|
450
|
+
color: AppColors.grayTextStrong,
|
|
451
|
+
},
|
|
452
|
+
});
|
|
227
453
|
const styles = StyleSheet.create({
|
|
228
454
|
container: {
|
|
229
455
|
flex: 1,
|
|
@@ -3,6 +3,14 @@ export declare const setReduxAutoRefresh: (val: boolean) => void;
|
|
|
3
3
|
export declare const getReduxAutoRefresh: () => boolean;
|
|
4
4
|
export declare const getLastActionForReducer: () => Record<string, any>;
|
|
5
5
|
export declare const clearLastActionForReducer: () => void;
|
|
6
|
+
export declare const getActionHistory: () => {
|
|
7
|
+
id: number;
|
|
8
|
+
type: string;
|
|
9
|
+
payload: any;
|
|
10
|
+
timestamp: string;
|
|
11
|
+
affectedSlices: string[];
|
|
12
|
+
}[];
|
|
13
|
+
export declare const clearActionHistory: () => void;
|
|
6
14
|
export declare const setReduxState: (state: any) => void;
|
|
7
15
|
export declare const subscribeReduxState: (cb: () => void) => () => void;
|
|
8
16
|
export declare const connectReduxStore: (store: any) => void;
|
|
@@ -2,6 +2,7 @@ let currentReduxState = null;
|
|
|
2
2
|
const listeners = new Set();
|
|
3
3
|
let globalReduxAutoRefresh = true;
|
|
4
4
|
let lastActionForReducer = {};
|
|
5
|
+
let actionHistory = [];
|
|
5
6
|
export const getReduxState = () => currentReduxState;
|
|
6
7
|
export const setReduxAutoRefresh = (val) => {
|
|
7
8
|
globalReduxAutoRefresh = val;
|
|
@@ -12,6 +13,11 @@ export const clearLastActionForReducer = () => {
|
|
|
12
13
|
lastActionForReducer = {};
|
|
13
14
|
listeners.forEach(cb => cb());
|
|
14
15
|
};
|
|
16
|
+
export const getActionHistory = () => actionHistory;
|
|
17
|
+
export const clearActionHistory = () => {
|
|
18
|
+
actionHistory = [];
|
|
19
|
+
listeners.forEach(cb => cb());
|
|
20
|
+
};
|
|
15
21
|
export const setReduxState = (state) => {
|
|
16
22
|
currentReduxState = state;
|
|
17
23
|
listeners.forEach(cb => cb());
|
|
@@ -34,6 +40,7 @@ export const connectReduxStore = (store) => {
|
|
|
34
40
|
const result = originalDispatch(action);
|
|
35
41
|
const nextState = store.getState();
|
|
36
42
|
// Map the dispatched action to state slices that actually changed
|
|
43
|
+
const affectedSlices = [];
|
|
37
44
|
if (prevState &&
|
|
38
45
|
nextState &&
|
|
39
46
|
typeof prevState === 'object' &&
|
|
@@ -42,13 +49,27 @@ export const connectReduxStore = (store) => {
|
|
|
42
49
|
typeof action === 'object') {
|
|
43
50
|
Object.keys(nextState).forEach(key => {
|
|
44
51
|
if (prevState[key] !== nextState[key]) {
|
|
45
|
-
|
|
52
|
+
const actionObj = {
|
|
46
53
|
type: action.type || 'UNKNOWN_ACTION',
|
|
47
54
|
payload: action.payload !== undefined ? action.payload : null,
|
|
48
55
|
timestamp: new Date().toLocaleTimeString(),
|
|
49
56
|
};
|
|
57
|
+
lastActionForReducer[key] = actionObj;
|
|
58
|
+
affectedSlices.push(key);
|
|
50
59
|
}
|
|
51
60
|
});
|
|
61
|
+
// Push to history
|
|
62
|
+
actionHistory.unshift({
|
|
63
|
+
id: Date.now() + Math.random(),
|
|
64
|
+
type: action.type || 'UNKNOWN_ACTION',
|
|
65
|
+
payload: action.payload !== undefined ? action.payload : null,
|
|
66
|
+
timestamp: new Date().toLocaleTimeString(),
|
|
67
|
+
affectedSlices,
|
|
68
|
+
});
|
|
69
|
+
// Cap size at 50
|
|
70
|
+
if (actionHistory.length > 50) {
|
|
71
|
+
actionHistory.pop();
|
|
72
|
+
}
|
|
52
73
|
}
|
|
53
74
|
if (globalReduxAutoRefresh) {
|
|
54
75
|
setReduxState(nextState);
|
package/dist/esm/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import CopyButton from './components/CopyButton';
|
|
|
11
11
|
import SectionHeader from './components/SectionHeader';
|
|
12
12
|
import EmptyState from './components/EmptyState';
|
|
13
13
|
import JsonViewer from './components/JsonViewer';
|
|
14
|
-
import { ReduxTreeView } from './components/ReduxTreeView';
|
|
14
|
+
import { ReduxTreeView, ReduxActionTimeline } from './components/ReduxTreeView';
|
|
15
15
|
import DomainHeader from './components/DomainHeader';
|
|
16
16
|
import DiffViewer from './components/DiffViewer';
|
|
17
17
|
import LogCard from './components/LogCard';
|
|
@@ -98,7 +98,7 @@ const previewInspectScript = `
|
|
|
98
98
|
})();
|
|
99
99
|
true;
|
|
100
100
|
`;
|
|
101
|
-
import { getReduxState, subscribeReduxState, setReduxAutoRefresh, getLastActionForReducer, } from './customHooks/reduxLogger';
|
|
101
|
+
import { getReduxState, subscribeReduxState, setReduxAutoRefresh, getLastActionForReducer, getActionHistory, clearActionHistory, } from './customHooks/reduxLogger';
|
|
102
102
|
import { METHOD_COLORS, STATUS_FILTERS } from './constants';
|
|
103
103
|
const NavigationTracker = ({ onStateChange }) => {
|
|
104
104
|
const navState = useNavigationState(state => state);
|
|
@@ -119,6 +119,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
119
119
|
const [search, setSearch] = useState('');
|
|
120
120
|
const [detailSearch, setDetailSearch] = useState('');
|
|
121
121
|
const [reduxSearch, setReduxSearch] = useState('');
|
|
122
|
+
const [reduxActiveSubTab, setReduxActiveSubTab] = useState('timeline');
|
|
122
123
|
const [apiDetailActiveTab, setApiDetailActiveTab] = useState('response');
|
|
123
124
|
useEffect(() => {
|
|
124
125
|
if (enabled && visible) {
|
|
@@ -959,7 +960,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
959
960
|
borderWidth: 1,
|
|
960
961
|
borderColor: 'rgba(255, 255, 255, 0.1)'
|
|
961
962
|
}}>
|
|
962
|
-
<Text style={{ fontFamily: AppFonts.interBold, fontSize: 10.5, color: '#FFFFFF' }}>v1.0.
|
|
963
|
+
<Text style={{ fontFamily: AppFonts.interBold, fontSize: 10.5, color: '#FFFFFF' }}>v1.0.12</Text>
|
|
963
964
|
</View>
|
|
964
965
|
</View>
|
|
965
966
|
</LinearGradient>
|
|
@@ -1693,6 +1694,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
1693
1694
|
}
|
|
1694
1695
|
// Build hierarchical tree: Store -> Reducers -> Action -> Data
|
|
1695
1696
|
const lastActionMap = getLastActionForReducer();
|
|
1697
|
+
const actionHistory = getActionHistory();
|
|
1696
1698
|
return (<ScrollView style={styles.detailScroll} contentContainerStyle={{ paddingBottom: 24 }}>
|
|
1697
1699
|
{/* Top Summary Card */}
|
|
1698
1700
|
<View style={{
|
|
@@ -1729,6 +1731,51 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
1729
1731
|
<CopyButton value={() => reduxState} label="Overall Store"/>
|
|
1730
1732
|
</View>
|
|
1731
1733
|
|
|
1734
|
+
{/* Tab View Selection Segments */}
|
|
1735
|
+
<View style={{
|
|
1736
|
+
flexDirection: 'row',
|
|
1737
|
+
backgroundColor: AppColors.grayBackground,
|
|
1738
|
+
borderRadius: 10,
|
|
1739
|
+
padding: 3,
|
|
1740
|
+
marginHorizontal: 16,
|
|
1741
|
+
marginBottom: 12,
|
|
1742
|
+
borderWidth: 1,
|
|
1743
|
+
borderColor: AppColors.dividerColor,
|
|
1744
|
+
}}>
|
|
1745
|
+
<TouchableOpacity onPress={() => setReduxActiveSubTab('timeline')} style={{
|
|
1746
|
+
flex: 1,
|
|
1747
|
+
paddingVertical: 6,
|
|
1748
|
+
alignItems: 'center',
|
|
1749
|
+
justifyContent: 'center',
|
|
1750
|
+
borderRadius: 8,
|
|
1751
|
+
backgroundColor: reduxActiveSubTab === 'timeline' ? AppColors.purple : 'transparent',
|
|
1752
|
+
}}>
|
|
1753
|
+
<Text style={{
|
|
1754
|
+
fontFamily: AppFonts.interBold,
|
|
1755
|
+
fontSize: 11,
|
|
1756
|
+
color: reduxActiveSubTab === 'timeline' ? '#FFFFFF' : AppColors.grayText,
|
|
1757
|
+
}}>
|
|
1758
|
+
⚡ Action Timeline
|
|
1759
|
+
</Text>
|
|
1760
|
+
</TouchableOpacity>
|
|
1761
|
+
<TouchableOpacity onPress={() => setReduxActiveSubTab('tree')} style={{
|
|
1762
|
+
flex: 1,
|
|
1763
|
+
paddingVertical: 6,
|
|
1764
|
+
alignItems: 'center',
|
|
1765
|
+
justifyContent: 'center',
|
|
1766
|
+
borderRadius: 8,
|
|
1767
|
+
backgroundColor: reduxActiveSubTab === 'tree' ? AppColors.purple : 'transparent',
|
|
1768
|
+
}}>
|
|
1769
|
+
<Text style={{
|
|
1770
|
+
fontFamily: AppFonts.interBold,
|
|
1771
|
+
fontSize: 11,
|
|
1772
|
+
color: reduxActiveSubTab === 'tree' ? '#FFFFFF' : AppColors.grayText,
|
|
1773
|
+
}}>
|
|
1774
|
+
🏪 Store Tree
|
|
1775
|
+
</Text>
|
|
1776
|
+
</TouchableOpacity>
|
|
1777
|
+
</View>
|
|
1778
|
+
|
|
1732
1779
|
{/* Search Bar */}
|
|
1733
1780
|
<View style={{
|
|
1734
1781
|
flexDirection: 'row',
|
|
@@ -1742,7 +1789,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
1742
1789
|
borderColor: AppColors.dividerColor,
|
|
1743
1790
|
height: 36,
|
|
1744
1791
|
}}>
|
|
1745
|
-
<TextInput placeholder="Search Redux keys or values..." placeholderTextColor={AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={{
|
|
1792
|
+
<TextInput placeholder={reduxActiveSubTab === 'timeline' ? "Search actions or payloads..." : "Search Redux keys or values..."} placeholderTextColor={AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={{
|
|
1746
1793
|
flex: 1,
|
|
1747
1794
|
fontFamily: AppFonts.interRegular,
|
|
1748
1795
|
fontSize: 12,
|
|
@@ -1754,7 +1801,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
1754
1801
|
</Pressable>)}
|
|
1755
1802
|
</View>
|
|
1756
1803
|
|
|
1757
|
-
{/* Main
|
|
1804
|
+
{/* Main Content Card */}
|
|
1758
1805
|
<View style={{
|
|
1759
1806
|
backgroundColor: AppColors.primaryLight,
|
|
1760
1807
|
borderRadius: 12,
|
|
@@ -1763,7 +1810,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
1763
1810
|
marginHorizontal: 16,
|
|
1764
1811
|
padding: 12,
|
|
1765
1812
|
}}>
|
|
1766
|
-
<ReduxTreeView state={reduxState} lastActionMap={lastActionMap} search={reduxSearch}/>
|
|
1813
|
+
{reduxActiveSubTab === 'timeline' ? (<ReduxActionTimeline history={actionHistory} onClear={clearActionHistory} search={reduxSearch}/>) : (<ReduxTreeView state={reduxState} lastActionMap={lastActionMap} search={reduxSearch}/>)}
|
|
1767
1814
|
</View>
|
|
1768
1815
|
</ScrollView>);
|
|
1769
1816
|
};
|
|
@@ -1838,7 +1885,7 @@ const NetworkInspector = ({ enabled = true }) => {
|
|
|
1838
1885
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 5 }}>
|
|
1839
1886
|
<Animated.View style={{ width: 6, height: 6, borderRadius: 3, backgroundColor: '#4ADE80', opacity: activePulseAnim }}/>
|
|
1840
1887
|
<Text style={{ fontFamily: AppFonts.interMedium, fontSize: 10, color: 'rgba(255,255,255,0.78)', letterSpacing: 0.3 }}>
|
|
1841
|
-
Active • {Platform.OS === 'ios' ? 'iOS' : 'Android'} (v1.0.
|
|
1888
|
+
Active • {Platform.OS === 'ios' ? 'iOS' : 'Android'} (v1.0.12)
|
|
1842
1889
|
</Text>
|
|
1843
1890
|
</View>
|
|
1844
1891
|
</View>
|
|
Binary file
|
package/package.json
CHANGED