dreaction-react-native 1.0.0
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/lib/components/ConfigDialog.d.ts +9 -0
- package/lib/components/ConfigDialog.d.ts.map +1 -0
- package/lib/components/ConfigDialog.js +83 -0
- package/lib/components/DraggableBall.d.ts +3 -0
- package/lib/components/DraggableBall.d.ts.map +1 -0
- package/lib/components/DraggableBall.js +136 -0
- package/lib/dreaction.d.ts +29 -0
- package/lib/dreaction.d.ts.map +1 -0
- package/lib/dreaction.js +134 -0
- package/lib/helpers/getHost.d.ts +9 -0
- package/lib/helpers/getHost.d.ts.map +1 -0
- package/lib/helpers/getHost.js +31 -0
- package/lib/helpers/getReactNativeDimensions.d.ts +3 -0
- package/lib/helpers/getReactNativeDimensions.d.ts.map +1 -0
- package/lib/helpers/getReactNativeDimensions.js +18 -0
- package/lib/helpers/getReactNativeDimensions.test.d.ts +2 -0
- package/lib/helpers/getReactNativeDimensions.test.d.ts.map +1 -0
- package/lib/helpers/getReactNativeDimensions.test.js +20 -0
- package/lib/helpers/getReactNativeDimensionsWithDimensions.d.ts +12 -0
- package/lib/helpers/getReactNativeDimensionsWithDimensions.d.ts.map +1 -0
- package/lib/helpers/getReactNativeDimensionsWithDimensions.js +31 -0
- package/lib/helpers/getReactNativePlatformConstants.d.ts +13 -0
- package/lib/helpers/getReactNativePlatformConstants.d.ts.map +1 -0
- package/lib/helpers/getReactNativePlatformConstants.js +37 -0
- package/lib/helpers/getReactNativeVersion.d.ts +2 -0
- package/lib/helpers/getReactNativeVersion.d.ts.map +1 -0
- package/lib/helpers/getReactNativeVersion.js +8 -0
- package/lib/helpers/getReactNativeVersion.test.d.ts +2 -0
- package/lib/helpers/getReactNativeVersion.test.d.ts.map +1 -0
- package/lib/helpers/getReactNativeVersion.test.js +19 -0
- package/lib/helpers/getReactNativeVersionWithModules.d.ts +3 -0
- package/lib/helpers/getReactNativeVersionWithModules.d.ts.map +1 -0
- package/lib/helpers/getReactNativeVersionWithModules.js +32 -0
- package/lib/helpers/parseErrorStack.d.ts +5 -0
- package/lib/helpers/parseErrorStack.d.ts.map +1 -0
- package/lib/helpers/parseErrorStack.js +2 -0
- package/lib/helpers/parseURL.d.ts +9 -0
- package/lib/helpers/parseURL.d.ts.map +1 -0
- package/lib/helpers/parseURL.js +21 -0
- package/lib/helpers/parseURL.test.d.ts +2 -0
- package/lib/helpers/parseURL.test.d.ts.map +1 -0
- package/lib/helpers/parseURL.test.js +61 -0
- package/lib/helpers/symbolicateStackTrace.d.ts +18 -0
- package/lib/helpers/symbolicateStackTrace.d.ts.map +1 -0
- package/lib/helpers/symbolicateStackTrace.js +2 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +20 -0
- package/lib/plugins/asyncStorage.d.ts +12 -0
- package/lib/plugins/asyncStorage.d.ts.map +1 -0
- package/lib/plugins/asyncStorage.js +173 -0
- package/lib/plugins/devTools.d.ts +3 -0
- package/lib/plugins/devTools.d.ts.map +1 -0
- package/lib/plugins/devTools.js +24 -0
- package/lib/plugins/networking.d.ts +10 -0
- package/lib/plugins/networking.d.ts.map +1 -0
- package/lib/plugins/networking.js +139 -0
- package/lib/plugins/openInEditor.d.ts +9 -0
- package/lib/plugins/openInEditor.d.ts.map +1 -0
- package/lib/plugins/openInEditor.js +21 -0
- package/lib/plugins/overlay/index.d.ts +3 -0
- package/lib/plugins/overlay/index.d.ts.map +1 -0
- package/lib/plugins/overlay/index.js +29 -0
- package/lib/plugins/overlay/overlay.d.ts +161 -0
- package/lib/plugins/overlay/overlay.d.ts.map +1 -0
- package/lib/plugins/overlay/overlay.js +99 -0
- package/lib/plugins/storybook/index.d.ts +6 -0
- package/lib/plugins/storybook/index.d.ts.map +1 -0
- package/lib/plugins/storybook/index.js +27 -0
- package/lib/plugins/storybook/storybook.d.ts +22 -0
- package/lib/plugins/storybook/storybook.d.ts.map +1 -0
- package/lib/plugins/storybook/storybook.js +31 -0
- package/lib/plugins/trackGlobalErrors.d.ts +32 -0
- package/lib/plugins/trackGlobalErrors.d.ts.map +1 -0
- package/lib/plugins/trackGlobalErrors.js +100 -0
- package/lib/plugins/trackGlobalLogs.d.ts +9 -0
- package/lib/plugins/trackGlobalLogs.d.ts.map +1 -0
- package/lib/plugins/trackGlobalLogs.js +31 -0
- package/package.json +29 -0
- package/src/components/ConfigDialog.tsx +79 -0
- package/src/components/DraggableBall.tsx +139 -0
- package/src/dreaction.ts +221 -0
- package/src/helpers/getHost.ts +30 -0
- package/src/helpers/getReactNativeDimensions.ts +20 -0
- package/src/helpers/getReactNativeDimensionsWithDimensions.ts +45 -0
- package/src/helpers/getReactNativePlatformConstants.ts +52 -0
- package/src/helpers/getReactNativeVersion.ts +6 -0
- package/src/helpers/getReactNativeVersionWithModules.ts +37 -0
- package/src/helpers/parseErrorStack.ts +13 -0
- package/src/helpers/parseURL.ts +19 -0
- package/src/helpers/symbolicateStackTrace.ts +28 -0
- package/src/index.ts +6 -0
- package/src/plugins/asyncStorage.ts +222 -0
- package/src/plugins/devTools.ts +30 -0
- package/src/plugins/networking.ts +172 -0
- package/src/plugins/openInEditor.ts +30 -0
- package/src/plugins/trackGlobalErrors.ts +156 -0
- package/src/plugins/trackGlobalLogs.ts +42 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { View, Text, TextInput, Button, Modal, StyleSheet } from 'react-native';
|
|
3
|
+
import { getHost } from '../helpers/getHost';
|
|
4
|
+
|
|
5
|
+
interface ConfigDialogProps {
|
|
6
|
+
visible: boolean;
|
|
7
|
+
onConfirm: (value: string) => void;
|
|
8
|
+
onCancel: () => void;
|
|
9
|
+
}
|
|
10
|
+
export const ConfigDialog: React.FC<ConfigDialogProps> = React.memo((props) => {
|
|
11
|
+
const { visible, onConfirm, onCancel } = props;
|
|
12
|
+
const [inputValue, setInputValue] = useState(getHost());
|
|
13
|
+
|
|
14
|
+
const handleConfirm = () => {
|
|
15
|
+
onConfirm(inputValue);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<Modal
|
|
20
|
+
animationType="fade"
|
|
21
|
+
transparent={true}
|
|
22
|
+
visible={visible}
|
|
23
|
+
onRequestClose={() => onCancel()}
|
|
24
|
+
>
|
|
25
|
+
<View style={styles.modalOverlay}>
|
|
26
|
+
<View style={styles.modalContent}>
|
|
27
|
+
<Text style={styles.title}>DReaction Desktop Application Url</Text>
|
|
28
|
+
<TextInput
|
|
29
|
+
style={styles.input}
|
|
30
|
+
value={inputValue}
|
|
31
|
+
keyboardType="url"
|
|
32
|
+
onChangeText={setInputValue}
|
|
33
|
+
placeholder={getHost()}
|
|
34
|
+
/>
|
|
35
|
+
<View style={styles.buttonContainer}>
|
|
36
|
+
<Button title="Cancel" onPress={onCancel} />
|
|
37
|
+
<Button title="Confirm" onPress={handleConfirm} />
|
|
38
|
+
</View>
|
|
39
|
+
</View>
|
|
40
|
+
</View>
|
|
41
|
+
</Modal>
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
ConfigDialog.displayName = 'ConfigDialog';
|
|
45
|
+
|
|
46
|
+
const styles = StyleSheet.create({
|
|
47
|
+
container: {
|
|
48
|
+
flex: 1,
|
|
49
|
+
justifyContent: 'center',
|
|
50
|
+
alignItems: 'center',
|
|
51
|
+
},
|
|
52
|
+
modalOverlay: {
|
|
53
|
+
flex: 1,
|
|
54
|
+
justifyContent: 'center',
|
|
55
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
56
|
+
},
|
|
57
|
+
modalContent: {
|
|
58
|
+
backgroundColor: 'white',
|
|
59
|
+
marginHorizontal: 20,
|
|
60
|
+
borderRadius: 10,
|
|
61
|
+
padding: 20,
|
|
62
|
+
},
|
|
63
|
+
title: {
|
|
64
|
+
fontSize: 18,
|
|
65
|
+
marginBottom: 10,
|
|
66
|
+
textAlign: 'center',
|
|
67
|
+
},
|
|
68
|
+
input: {
|
|
69
|
+
borderWidth: 1,
|
|
70
|
+
borderColor: '#ccc',
|
|
71
|
+
borderRadius: 5,
|
|
72
|
+
padding: 10,
|
|
73
|
+
marginBottom: 20,
|
|
74
|
+
},
|
|
75
|
+
buttonContainer: {
|
|
76
|
+
flexDirection: 'row',
|
|
77
|
+
justifyContent: 'space-around',
|
|
78
|
+
},
|
|
79
|
+
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import React, { useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Animated,
|
|
4
|
+
PanResponder,
|
|
5
|
+
Dimensions,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
Image,
|
|
8
|
+
Alert,
|
|
9
|
+
} from 'react-native';
|
|
10
|
+
import { ConfigDialog } from './ConfigDialog';
|
|
11
|
+
import { dreaction } from '../dreaction';
|
|
12
|
+
import { getHost } from '../helpers/getHost';
|
|
13
|
+
|
|
14
|
+
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
|
|
15
|
+
const BALL_SIZE = 60;
|
|
16
|
+
const CLICK_THRESHOLD = 5;
|
|
17
|
+
|
|
18
|
+
const initialPosition = {
|
|
19
|
+
x: SCREEN_WIDTH - BALL_SIZE,
|
|
20
|
+
y: SCREEN_HEIGHT - BALL_SIZE - 100,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const DraggableBall: React.FC = React.memo(() => {
|
|
24
|
+
const position = useRef(new Animated.ValueXY(initialPosition)).current;
|
|
25
|
+
const lastPosition = useRef(initialPosition);
|
|
26
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
27
|
+
const [modalVisible, setModalVisible] = useState(false);
|
|
28
|
+
|
|
29
|
+
const handleClick = (value: string) => {
|
|
30
|
+
try {
|
|
31
|
+
const host = getHost(value);
|
|
32
|
+
|
|
33
|
+
dreaction
|
|
34
|
+
.configure({
|
|
35
|
+
host,
|
|
36
|
+
})
|
|
37
|
+
.close()
|
|
38
|
+
.connect();
|
|
39
|
+
|
|
40
|
+
setModalVisible(false);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
Alert.alert('Connected Failed', 'Please check your url');
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const panResponder = useRef(
|
|
47
|
+
PanResponder.create({
|
|
48
|
+
onStartShouldSetPanResponder: () => true,
|
|
49
|
+
onPanResponderGrant: () => {
|
|
50
|
+
setIsDragging(true);
|
|
51
|
+
position.stopAnimation();
|
|
52
|
+
},
|
|
53
|
+
onPanResponderMove: (_, gestureState) => {
|
|
54
|
+
// 使用 lastPosition 计算位置
|
|
55
|
+
position.setValue({
|
|
56
|
+
x: lastPosition.current.x + gestureState.dx,
|
|
57
|
+
y: lastPosition.current.y + gestureState.dy,
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
onPanResponderRelease: (_, gestureState) => {
|
|
61
|
+
setIsDragging(false);
|
|
62
|
+
|
|
63
|
+
lastPosition.current = {
|
|
64
|
+
x: lastPosition.current.x + gestureState.dx,
|
|
65
|
+
y: lastPosition.current.y + gestureState.dy,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (
|
|
69
|
+
Math.abs(gestureState.dx) < CLICK_THRESHOLD &&
|
|
70
|
+
Math.abs(gestureState.dy) < CLICK_THRESHOLD
|
|
71
|
+
) {
|
|
72
|
+
setModalVisible(true);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const { moveX, moveY } = gestureState;
|
|
77
|
+
|
|
78
|
+
// Calculate adsorption to the nearest edge
|
|
79
|
+
let newX = moveX <= SCREEN_WIDTH / 2 ? 0 : SCREEN_WIDTH - BALL_SIZE; // Adsorb to the left or right
|
|
80
|
+
let newY = Math.min(
|
|
81
|
+
Math.max(moveY, 0), // Limit the top boundary
|
|
82
|
+
SCREEN_HEIGHT - BALL_SIZE // Limit the bottom boundary
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Animation
|
|
86
|
+
Animated.spring(position, {
|
|
87
|
+
toValue: { x: newX, y: newY },
|
|
88
|
+
useNativeDriver: false,
|
|
89
|
+
friction: 5,
|
|
90
|
+
}).start(() => {
|
|
91
|
+
lastPosition.current = { x: newX, y: newY };
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
).current;
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<>
|
|
99
|
+
<Animated.View
|
|
100
|
+
style={[
|
|
101
|
+
styles.ball,
|
|
102
|
+
{
|
|
103
|
+
transform: [{ translateX: position.x }, { translateY: position.y }],
|
|
104
|
+
backgroundColor: isDragging ? '#eee' : '#fff',
|
|
105
|
+
},
|
|
106
|
+
]}
|
|
107
|
+
{...panResponder.panHandlers}
|
|
108
|
+
>
|
|
109
|
+
<Image style={styles.icon} source={require('../../assets/icon.png')} />
|
|
110
|
+
</Animated.View>
|
|
111
|
+
|
|
112
|
+
<ConfigDialog
|
|
113
|
+
visible={modalVisible}
|
|
114
|
+
onCancel={() => setModalVisible(false)}
|
|
115
|
+
onConfirm={handleClick}
|
|
116
|
+
/>
|
|
117
|
+
</>
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
DraggableBall.displayName = 'DraggableBall';
|
|
121
|
+
|
|
122
|
+
const styles = StyleSheet.create({
|
|
123
|
+
ball: {
|
|
124
|
+
width: BALL_SIZE,
|
|
125
|
+
height: BALL_SIZE,
|
|
126
|
+
borderRadius: BALL_SIZE / 2,
|
|
127
|
+
position: 'absolute',
|
|
128
|
+
overflow: 'hidden',
|
|
129
|
+
borderWidth: 1,
|
|
130
|
+
borderColor: '#eee',
|
|
131
|
+
justifyContent: 'center',
|
|
132
|
+
alignItems: 'center',
|
|
133
|
+
backgroundColor: '#fff',
|
|
134
|
+
},
|
|
135
|
+
icon: {
|
|
136
|
+
width: BALL_SIZE * 0.8,
|
|
137
|
+
height: BALL_SIZE * 0.8,
|
|
138
|
+
},
|
|
139
|
+
});
|
package/src/dreaction.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import { createClient } from 'dreaction-client-core';
|
|
3
|
+
import type {
|
|
4
|
+
ClientOptions,
|
|
5
|
+
InferFeaturesFromPlugins,
|
|
6
|
+
PluginCreator,
|
|
7
|
+
Reactotron,
|
|
8
|
+
DReactionCore,
|
|
9
|
+
} from 'dreaction-client-core';
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
import type { AsyncStorageStatic } from '@react-native-async-storage/async-storage';
|
|
12
|
+
import getReactNativeVersion from './helpers/getReactNativeVersion';
|
|
13
|
+
import getReactNativeDimensions from './helpers/getReactNativeDimensions';
|
|
14
|
+
import asyncStorage, { AsyncStorageOptions } from './plugins/asyncStorage';
|
|
15
|
+
import openInEditor, { OpenInEditorOptions } from './plugins/openInEditor';
|
|
16
|
+
import trackGlobalErrors, {
|
|
17
|
+
TrackGlobalErrorsOptions,
|
|
18
|
+
} from './plugins/trackGlobalErrors';
|
|
19
|
+
import networking, { NetworkingOptions } from './plugins/networking';
|
|
20
|
+
import devTools from './plugins/devTools';
|
|
21
|
+
import trackGlobalLogs from './plugins/trackGlobalLogs';
|
|
22
|
+
import getReactNativePlatformConstants from './helpers/getReactNativePlatformConstants';
|
|
23
|
+
import { DataWatchPayload } from 'dreaction-protocol';
|
|
24
|
+
import { useEffect } from 'react';
|
|
25
|
+
import { getHost } from './helpers/getHost';
|
|
26
|
+
|
|
27
|
+
export type { ClientOptions };
|
|
28
|
+
|
|
29
|
+
const DREACTION_ASYNC_CLIENT_ID = '@REACTOTRON/clientId';
|
|
30
|
+
|
|
31
|
+
let tempClientId: string | null = null;
|
|
32
|
+
|
|
33
|
+
export const reactNativeCorePlugins = [
|
|
34
|
+
asyncStorage(),
|
|
35
|
+
trackGlobalErrors(),
|
|
36
|
+
trackGlobalLogs(),
|
|
37
|
+
openInEditor(),
|
|
38
|
+
networking(),
|
|
39
|
+
devTools(),
|
|
40
|
+
] satisfies PluginCreator<DReactionCore>[];
|
|
41
|
+
|
|
42
|
+
export interface UseReactNativeOptions {
|
|
43
|
+
errors?: TrackGlobalErrorsOptions | boolean;
|
|
44
|
+
log?: boolean;
|
|
45
|
+
editor?: OpenInEditorOptions | boolean;
|
|
46
|
+
asyncStorage?: AsyncStorageOptions | boolean;
|
|
47
|
+
networking?: NetworkingOptions | boolean;
|
|
48
|
+
devTools?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type ReactNativePluginFeatures = InferFeaturesFromPlugins<
|
|
52
|
+
DReactionCore,
|
|
53
|
+
typeof reactNativeCorePlugins
|
|
54
|
+
>;
|
|
55
|
+
|
|
56
|
+
export interface ReactotronReactNative
|
|
57
|
+
extends Reactotron,
|
|
58
|
+
// @ts-ignore
|
|
59
|
+
ReactNativePluginFeatures {
|
|
60
|
+
useReactNative: (options?: UseReactNativeOptions) => this;
|
|
61
|
+
asyncStorageHandler?: AsyncStorageStatic;
|
|
62
|
+
setAsyncStorageHandler: (asyncStorage: AsyncStorageStatic) => this;
|
|
63
|
+
registerDataWatcher: (
|
|
64
|
+
name: string,
|
|
65
|
+
type: DataWatchPayload['type']
|
|
66
|
+
) => {
|
|
67
|
+
updateDebugValue: (data: unknown) => void;
|
|
68
|
+
useDebugDataWatch: (target: unknown) => void;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const {
|
|
73
|
+
osRelease,
|
|
74
|
+
model,
|
|
75
|
+
serverHost,
|
|
76
|
+
forceTouch,
|
|
77
|
+
interfaceIdiom,
|
|
78
|
+
systemName,
|
|
79
|
+
uiMode,
|
|
80
|
+
serial,
|
|
81
|
+
} = getReactNativePlatformConstants();
|
|
82
|
+
|
|
83
|
+
const DEFAULTS: ClientOptions<ReactotronReactNative> = {
|
|
84
|
+
createSocket: (path: string) => new WebSocket(path), // eslint-disable-line
|
|
85
|
+
host: getHost('localhost'),
|
|
86
|
+
port: 9600,
|
|
87
|
+
name: 'React Native App',
|
|
88
|
+
environment: process.env.NODE_ENV || (__DEV__ ? 'development' : 'production'),
|
|
89
|
+
client: {
|
|
90
|
+
dreactionLibraryName: 'dreaction-react-native',
|
|
91
|
+
dreactionLibraryVersion: 'DREACTION_REACT_NATIVE_VERSION',
|
|
92
|
+
platform: Platform.OS,
|
|
93
|
+
platformVersion: Platform.Version,
|
|
94
|
+
osRelease,
|
|
95
|
+
model,
|
|
96
|
+
serverHost,
|
|
97
|
+
forceTouch,
|
|
98
|
+
interfaceIdiom,
|
|
99
|
+
systemName,
|
|
100
|
+
uiMode,
|
|
101
|
+
serial,
|
|
102
|
+
reactNativeVersion: getReactNativeVersion()!,
|
|
103
|
+
...getReactNativeDimensions(),
|
|
104
|
+
},
|
|
105
|
+
/* eslint-disable @typescript-eslint/no-use-before-define */
|
|
106
|
+
getClientId: async (name: string = '') => {
|
|
107
|
+
if (dreaction.asyncStorageHandler) {
|
|
108
|
+
return (await dreaction.asyncStorageHandler.getItem(
|
|
109
|
+
DREACTION_ASYNC_CLIENT_ID
|
|
110
|
+
))!;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Generate clientId based on the device info
|
|
114
|
+
const { screenWidth, screenHeight, screenScale } =
|
|
115
|
+
getReactNativeDimensions()!;
|
|
116
|
+
|
|
117
|
+
// Accounting for screen rotation
|
|
118
|
+
const dimensions = [screenWidth, screenHeight].sort().join('-');
|
|
119
|
+
|
|
120
|
+
const additionalInfo = Platform.select({
|
|
121
|
+
ios: systemName,
|
|
122
|
+
android: model,
|
|
123
|
+
default: '',
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
tempClientId = [
|
|
127
|
+
name,
|
|
128
|
+
Platform.OS,
|
|
129
|
+
Platform.Version,
|
|
130
|
+
additionalInfo,
|
|
131
|
+
dimensions,
|
|
132
|
+
screenScale,
|
|
133
|
+
]
|
|
134
|
+
.filter(Boolean)
|
|
135
|
+
.join('-');
|
|
136
|
+
|
|
137
|
+
return tempClientId;
|
|
138
|
+
},
|
|
139
|
+
setClientId: async (clientId: string) => {
|
|
140
|
+
if (dreaction.asyncStorageHandler) {
|
|
141
|
+
return dreaction.asyncStorageHandler.setItem(
|
|
142
|
+
DREACTION_ASYNC_CLIENT_ID,
|
|
143
|
+
clientId
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
tempClientId = clientId;
|
|
148
|
+
},
|
|
149
|
+
proxyHack: true,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const dreaction = createClient<ReactotronReactNative>(DEFAULTS);
|
|
153
|
+
|
|
154
|
+
function getPluginOptions<T>(options?: T | boolean): T | null {
|
|
155
|
+
return typeof options === 'object' ? options : null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
dreaction.useReactNative = (options: UseReactNativeOptions = {}) => {
|
|
159
|
+
if (options.errors !== false) {
|
|
160
|
+
dreaction.use(
|
|
161
|
+
trackGlobalErrors(getPluginOptions(options.errors as any)) as any
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (options.log !== false) {
|
|
166
|
+
dreaction.use(trackGlobalLogs() as any);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (options.editor !== false) {
|
|
170
|
+
dreaction.use(openInEditor(getPluginOptions(options.editor as any)));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (options.asyncStorage !== false) {
|
|
174
|
+
dreaction.use(
|
|
175
|
+
asyncStorage(getPluginOptions(options.asyncStorage) as any) as any
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (options.networking !== false) {
|
|
180
|
+
dreaction.use(
|
|
181
|
+
networking(getPluginOptions(options.networking) as any) as any
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (options.devTools !== false) {
|
|
186
|
+
dreaction.use(devTools());
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return dreaction;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
dreaction.setAsyncStorageHandler = (asyncStorage: AsyncStorageStatic) => {
|
|
193
|
+
dreaction.asyncStorageHandler = asyncStorage;
|
|
194
|
+
|
|
195
|
+
return dreaction;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
dreaction.registerDataWatcher = (
|
|
199
|
+
name: string,
|
|
200
|
+
type: DataWatchPayload['type']
|
|
201
|
+
) => {
|
|
202
|
+
if (!__DEV__) {
|
|
203
|
+
return {
|
|
204
|
+
updateDebugValue: () => {},
|
|
205
|
+
useDebugDataWatch: () => {},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const updateDebugValue = (data: unknown) => {
|
|
210
|
+
dreaction.send('dataWatch', { name, type, data });
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
updateDebugValue,
|
|
215
|
+
useDebugDataWatch: (target: unknown) => {
|
|
216
|
+
useEffect(() => {
|
|
217
|
+
updateDebugValue(target);
|
|
218
|
+
}, [target]);
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// @ts-ignore
|
|
2
|
+
import NativeSourceCode from 'react-native/Libraries/NativeModules/specs/NativeSourceCode';
|
|
3
|
+
import { getHostFromUrl } from './parseURL';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Most of the time, host should be 'localhost'.
|
|
7
|
+
* But sometimes, it's not. So we need to figure out what it is.
|
|
8
|
+
* @see https://github.com/infinitered/dreaction/issues/1107
|
|
9
|
+
*
|
|
10
|
+
* On an Android emulator, if you want to connect any servers of local, you will need run adb reverse on your terminal. This function gets the localhost IP of host machine directly to bypass this.
|
|
11
|
+
*/
|
|
12
|
+
export const getHost = (defaultHost = 'localhost') => {
|
|
13
|
+
try {
|
|
14
|
+
// RN Reference: https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/specs/modules/NativeSourceCode.js
|
|
15
|
+
const scriptURL = NativeSourceCode.getConstants().scriptURL;
|
|
16
|
+
|
|
17
|
+
if (typeof scriptURL !== 'string') {
|
|
18
|
+
throw new Error('Invalid non-string URL');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return getHostFromUrl(scriptURL);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.warn(
|
|
24
|
+
`getHost: "${
|
|
25
|
+
(error as any).message
|
|
26
|
+
}" for scriptURL - Falling back to ${defaultHost}`
|
|
27
|
+
);
|
|
28
|
+
return defaultHost;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Dimensions } from 'react-native';
|
|
2
|
+
import {
|
|
3
|
+
AppDimensions,
|
|
4
|
+
getReactNativeDimensionsWithDimensions,
|
|
5
|
+
} from './getReactNativeDimensionsWithDimensions';
|
|
6
|
+
|
|
7
|
+
export default function getReactNativeDimensions(): AppDimensions | null {
|
|
8
|
+
let screen = null;
|
|
9
|
+
let win = null;
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
screen = Dimensions.get('screen');
|
|
13
|
+
} catch {}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
win = Dimensions.get('window');
|
|
17
|
+
} catch {}
|
|
18
|
+
|
|
19
|
+
return getReactNativeDimensionsWithDimensions(screen, win);
|
|
20
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface AppDimensions {
|
|
2
|
+
screenWidth?: number
|
|
3
|
+
screenHeight?: number
|
|
4
|
+
screenScale?: number
|
|
5
|
+
screenFontScale?: number
|
|
6
|
+
windowWidth?: number
|
|
7
|
+
windowHeight?: number
|
|
8
|
+
windowScale?: number
|
|
9
|
+
windowFontScale?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getReactNativeDimensionsWithDimensions(
|
|
13
|
+
screen: any,
|
|
14
|
+
win: any
|
|
15
|
+
): AppDimensions | null {
|
|
16
|
+
try {
|
|
17
|
+
let screenInfo = {}
|
|
18
|
+
let windowInfo = {}
|
|
19
|
+
|
|
20
|
+
if (screen) {
|
|
21
|
+
screenInfo = {
|
|
22
|
+
screenWidth: Math.ceil(screen.width),
|
|
23
|
+
screenHeight: Math.ceil(screen.height),
|
|
24
|
+
screenScale: screen.scale,
|
|
25
|
+
screenFontScale: screen.fontScale,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (win) {
|
|
30
|
+
windowInfo = {
|
|
31
|
+
windowWidth: Math.ceil(win.width),
|
|
32
|
+
windowHeight: Math.ceil(win.height),
|
|
33
|
+
windowScale: win.scale,
|
|
34
|
+
windowFontScale: win.fontScale,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
...screenInfo,
|
|
40
|
+
...windowInfo,
|
|
41
|
+
}
|
|
42
|
+
} catch (e) {}
|
|
43
|
+
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Platform,
|
|
3
|
+
PlatformIOSStatic,
|
|
4
|
+
PlatformAndroidStatic,
|
|
5
|
+
} from 'react-native';
|
|
6
|
+
|
|
7
|
+
interface PlatformConstants {
|
|
8
|
+
osRelease: string;
|
|
9
|
+
model: string;
|
|
10
|
+
serverHost: string;
|
|
11
|
+
uiMode: string;
|
|
12
|
+
serial: string;
|
|
13
|
+
forceTouch: boolean;
|
|
14
|
+
interfaceIdiom: string;
|
|
15
|
+
systemName: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default function getReactNativePlatformConstants(): PlatformConstants {
|
|
19
|
+
const defaults: PlatformConstants = {
|
|
20
|
+
osRelease: '',
|
|
21
|
+
model: '',
|
|
22
|
+
serverHost: '',
|
|
23
|
+
uiMode: '',
|
|
24
|
+
serial: '',
|
|
25
|
+
forceTouch: false,
|
|
26
|
+
interfaceIdiom: '',
|
|
27
|
+
systemName: '',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
if (Platform.OS === 'android') {
|
|
31
|
+
const constants = Platform.constants as PlatformAndroidStatic['constants'];
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
...defaults,
|
|
35
|
+
osRelease: constants.Release,
|
|
36
|
+
model: constants.Model,
|
|
37
|
+
serverHost: constants.ServerHost!,
|
|
38
|
+
uiMode: constants.uiMode,
|
|
39
|
+
serial: constants.Serial,
|
|
40
|
+
};
|
|
41
|
+
} else if (Platform.OS === 'ios') {
|
|
42
|
+
const constants = Platform.constants as PlatformIOSStatic['constants'];
|
|
43
|
+
return {
|
|
44
|
+
...defaults,
|
|
45
|
+
forceTouch: constants.forceTouchAvailable || false,
|
|
46
|
+
interfaceIdiom: constants.interfaceIdiom,
|
|
47
|
+
systemName: constants.systemName,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return defaults;
|
|
52
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { PlatformConstants } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export function getReactNativeVersionWithModules(
|
|
4
|
+
constants: PlatformConstants
|
|
5
|
+
): string | null {
|
|
6
|
+
try {
|
|
7
|
+
// dodge some bullets
|
|
8
|
+
if (!constants) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
if (!constants.reactNativeVersion) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// grab the raw numbers
|
|
16
|
+
const major = constants.reactNativeVersion.major;
|
|
17
|
+
const minor = constants.reactNativeVersion.minor;
|
|
18
|
+
const patch = constants.reactNativeVersion.patch;
|
|
19
|
+
const prerelease = constants.reactNativeVersion.prerelease;
|
|
20
|
+
|
|
21
|
+
// check the major or jet
|
|
22
|
+
if (typeof major !== 'number') {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// assemble!
|
|
27
|
+
const vParts = [];
|
|
28
|
+
vParts.push(`${major}.${minor}.${patch}`);
|
|
29
|
+
if (prerelease) {
|
|
30
|
+
vParts.push(`-${prerelease}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return vParts.join('');
|
|
34
|
+
} catch {}
|
|
35
|
+
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// eslint-disable-next-line import/namespace
|
|
2
|
+
// @ts-ignore
|
|
3
|
+
import type { StackFrame } from 'react-native/Libraries/Core/NativeExceptionsManager';
|
|
4
|
+
// @ts-ignore
|
|
5
|
+
import type { HermesParsedStack } from 'react-native/Libraries/Core/Devtools/parseHermesStack';
|
|
6
|
+
|
|
7
|
+
// Fixing react-native/Libraries/Core/Devtools/symbolicateStackTrace
|
|
8
|
+
// since the native type definitions are wrong lol
|
|
9
|
+
|
|
10
|
+
/** @see https://github.com/facebook/react-native/blob/v0.72.1/packages/react-native/Libraries/Core/Devtools/parseErrorStack.js#L41-L57 */
|
|
11
|
+
export type ParseErrorStackFn = <T extends any[]>(
|
|
12
|
+
errorStack?: string | T | HermesParsedStack
|
|
13
|
+
) => Array<StackFrame>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Given a valid http(s) URL, the host for the given URL
|
|
3
|
+
* is returned.
|
|
4
|
+
*
|
|
5
|
+
* @param url {string} URL to extract the host from
|
|
6
|
+
* @returns {string} host of given URL or throws
|
|
7
|
+
*/
|
|
8
|
+
// Using a capture group to extract the hostname from a URL
|
|
9
|
+
export function getHostFromUrl(url: string) {
|
|
10
|
+
// Group 1: http(s)://
|
|
11
|
+
// Group 2: host
|
|
12
|
+
// Group 3: port
|
|
13
|
+
// Group 4: rest
|
|
14
|
+
const host = url.match(/^(?:https?:\/\/)?(\[[^\]]+\]|[^/:\s]+)(?::\d+)?(?:[/?#]|$)/)?.[1]
|
|
15
|
+
|
|
16
|
+
if (typeof host !== "string") throw new Error("Invalid URL - host not found")
|
|
17
|
+
|
|
18
|
+
return host
|
|
19
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// The actual type definitions are wrong, so we are fixing them lol
|
|
2
|
+
// eslint-disable-next-line import/namespace
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
import type { StackFrame } from 'react-native/Libraries/Core/NativeExceptionsManager';
|
|
5
|
+
|
|
6
|
+
/** @see https://github.com/facebook/react-native/blob/v0.72.1/packages/react-native/Libraries/Core/Devtools/symbolicateStackTrace.js#L17-L25 */
|
|
7
|
+
export type CodeFrame = Readonly<{
|
|
8
|
+
content: string;
|
|
9
|
+
location:
|
|
10
|
+
| {
|
|
11
|
+
row: number;
|
|
12
|
+
column: number;
|
|
13
|
+
}
|
|
14
|
+
| null
|
|
15
|
+
| undefined;
|
|
16
|
+
fileName: string;
|
|
17
|
+
}>;
|
|
18
|
+
|
|
19
|
+
/** @see https://github.com/facebook/react-native/blob/v0.72.1/packages/react-native/Libraries/Core/Devtools/symbolicateStackTrace.js#L27-L30 */
|
|
20
|
+
export type SymbolicatedStackTrace = Readonly<{
|
|
21
|
+
stack: Array<StackFrame>;
|
|
22
|
+
codeFrame: CodeFrame | null | undefined;
|
|
23
|
+
}>;
|
|
24
|
+
|
|
25
|
+
/** @see https://github.com/facebook/react-native/blob/v0.72.1/packages/react-native/Libraries/Core/Devtools/symbolicateStackTrace.js#L32-L34 */
|
|
26
|
+
export type SymbolicateStackTraceFn = (
|
|
27
|
+
stack: Array<StackFrame>
|
|
28
|
+
) => Promise<SymbolicatedStackTrace>;
|