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.
Files changed (98) hide show
  1. package/lib/components/ConfigDialog.d.ts +9 -0
  2. package/lib/components/ConfigDialog.d.ts.map +1 -0
  3. package/lib/components/ConfigDialog.js +83 -0
  4. package/lib/components/DraggableBall.d.ts +3 -0
  5. package/lib/components/DraggableBall.d.ts.map +1 -0
  6. package/lib/components/DraggableBall.js +136 -0
  7. package/lib/dreaction.d.ts +29 -0
  8. package/lib/dreaction.d.ts.map +1 -0
  9. package/lib/dreaction.js +134 -0
  10. package/lib/helpers/getHost.d.ts +9 -0
  11. package/lib/helpers/getHost.d.ts.map +1 -0
  12. package/lib/helpers/getHost.js +31 -0
  13. package/lib/helpers/getReactNativeDimensions.d.ts +3 -0
  14. package/lib/helpers/getReactNativeDimensions.d.ts.map +1 -0
  15. package/lib/helpers/getReactNativeDimensions.js +18 -0
  16. package/lib/helpers/getReactNativeDimensions.test.d.ts +2 -0
  17. package/lib/helpers/getReactNativeDimensions.test.d.ts.map +1 -0
  18. package/lib/helpers/getReactNativeDimensions.test.js +20 -0
  19. package/lib/helpers/getReactNativeDimensionsWithDimensions.d.ts +12 -0
  20. package/lib/helpers/getReactNativeDimensionsWithDimensions.d.ts.map +1 -0
  21. package/lib/helpers/getReactNativeDimensionsWithDimensions.js +31 -0
  22. package/lib/helpers/getReactNativePlatformConstants.d.ts +13 -0
  23. package/lib/helpers/getReactNativePlatformConstants.d.ts.map +1 -0
  24. package/lib/helpers/getReactNativePlatformConstants.js +37 -0
  25. package/lib/helpers/getReactNativeVersion.d.ts +2 -0
  26. package/lib/helpers/getReactNativeVersion.d.ts.map +1 -0
  27. package/lib/helpers/getReactNativeVersion.js +8 -0
  28. package/lib/helpers/getReactNativeVersion.test.d.ts +2 -0
  29. package/lib/helpers/getReactNativeVersion.test.d.ts.map +1 -0
  30. package/lib/helpers/getReactNativeVersion.test.js +19 -0
  31. package/lib/helpers/getReactNativeVersionWithModules.d.ts +3 -0
  32. package/lib/helpers/getReactNativeVersionWithModules.d.ts.map +1 -0
  33. package/lib/helpers/getReactNativeVersionWithModules.js +32 -0
  34. package/lib/helpers/parseErrorStack.d.ts +5 -0
  35. package/lib/helpers/parseErrorStack.d.ts.map +1 -0
  36. package/lib/helpers/parseErrorStack.js +2 -0
  37. package/lib/helpers/parseURL.d.ts +9 -0
  38. package/lib/helpers/parseURL.d.ts.map +1 -0
  39. package/lib/helpers/parseURL.js +21 -0
  40. package/lib/helpers/parseURL.test.d.ts +2 -0
  41. package/lib/helpers/parseURL.test.d.ts.map +1 -0
  42. package/lib/helpers/parseURL.test.js +61 -0
  43. package/lib/helpers/symbolicateStackTrace.d.ts +18 -0
  44. package/lib/helpers/symbolicateStackTrace.d.ts.map +1 -0
  45. package/lib/helpers/symbolicateStackTrace.js +2 -0
  46. package/lib/index.d.ts +5 -0
  47. package/lib/index.d.ts.map +1 -0
  48. package/lib/index.js +20 -0
  49. package/lib/plugins/asyncStorage.d.ts +12 -0
  50. package/lib/plugins/asyncStorage.d.ts.map +1 -0
  51. package/lib/plugins/asyncStorage.js +173 -0
  52. package/lib/plugins/devTools.d.ts +3 -0
  53. package/lib/plugins/devTools.d.ts.map +1 -0
  54. package/lib/plugins/devTools.js +24 -0
  55. package/lib/plugins/networking.d.ts +10 -0
  56. package/lib/plugins/networking.d.ts.map +1 -0
  57. package/lib/plugins/networking.js +139 -0
  58. package/lib/plugins/openInEditor.d.ts +9 -0
  59. package/lib/plugins/openInEditor.d.ts.map +1 -0
  60. package/lib/plugins/openInEditor.js +21 -0
  61. package/lib/plugins/overlay/index.d.ts +3 -0
  62. package/lib/plugins/overlay/index.d.ts.map +1 -0
  63. package/lib/plugins/overlay/index.js +29 -0
  64. package/lib/plugins/overlay/overlay.d.ts +161 -0
  65. package/lib/plugins/overlay/overlay.d.ts.map +1 -0
  66. package/lib/plugins/overlay/overlay.js +99 -0
  67. package/lib/plugins/storybook/index.d.ts +6 -0
  68. package/lib/plugins/storybook/index.d.ts.map +1 -0
  69. package/lib/plugins/storybook/index.js +27 -0
  70. package/lib/plugins/storybook/storybook.d.ts +22 -0
  71. package/lib/plugins/storybook/storybook.d.ts.map +1 -0
  72. package/lib/plugins/storybook/storybook.js +31 -0
  73. package/lib/plugins/trackGlobalErrors.d.ts +32 -0
  74. package/lib/plugins/trackGlobalErrors.d.ts.map +1 -0
  75. package/lib/plugins/trackGlobalErrors.js +100 -0
  76. package/lib/plugins/trackGlobalLogs.d.ts +9 -0
  77. package/lib/plugins/trackGlobalLogs.d.ts.map +1 -0
  78. package/lib/plugins/trackGlobalLogs.js +31 -0
  79. package/package.json +29 -0
  80. package/src/components/ConfigDialog.tsx +79 -0
  81. package/src/components/DraggableBall.tsx +139 -0
  82. package/src/dreaction.ts +221 -0
  83. package/src/helpers/getHost.ts +30 -0
  84. package/src/helpers/getReactNativeDimensions.ts +20 -0
  85. package/src/helpers/getReactNativeDimensionsWithDimensions.ts +45 -0
  86. package/src/helpers/getReactNativePlatformConstants.ts +52 -0
  87. package/src/helpers/getReactNativeVersion.ts +6 -0
  88. package/src/helpers/getReactNativeVersionWithModules.ts +37 -0
  89. package/src/helpers/parseErrorStack.ts +13 -0
  90. package/src/helpers/parseURL.ts +19 -0
  91. package/src/helpers/symbolicateStackTrace.ts +28 -0
  92. package/src/index.ts +6 -0
  93. package/src/plugins/asyncStorage.ts +222 -0
  94. package/src/plugins/devTools.ts +30 -0
  95. package/src/plugins/networking.ts +172 -0
  96. package/src/plugins/openInEditor.ts +30 -0
  97. package/src/plugins/trackGlobalErrors.ts +156 -0
  98. 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
+ });
@@ -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,6 @@
1
+ import { getReactNativeVersionWithModules } from './getReactNativeVersionWithModules';
2
+ import { Platform } from 'react-native';
3
+
4
+ export default function getReactNativeVersion(): string | null {
5
+ return getReactNativeVersionWithModules(Platform.constants);
6
+ }
@@ -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>;