react-native-debug-toolkit 3.2.1 → 3.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -2
- package/README.zh-CN.md +13 -2
- package/android/build.gradle +34 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/reactnativedebugtoolkit/DebugToolkitDevConnectModule.java +154 -0
- package/android/src/main/java/com/reactnativedebugtoolkit/ReactNativeDebugToolkitPackage.java +25 -0
- package/ios/DebugToolkitDevConnect.mm +81 -0
- package/lib/commonjs/features/devConnect/DevConnectQrScanner.js +115 -70
- package/lib/commonjs/features/devConnect/DevConnectQrScanner.js.map +1 -1
- package/lib/commonjs/features/devConnect/DevConnectTab.js +232 -161
- package/lib/commonjs/features/devConnect/DevConnectTab.js.map +1 -1
- package/lib/commonjs/features/devConnect/devConnectPreferences.js +35 -5
- package/lib/commonjs/features/devConnect/devConnectPreferences.js.map +1 -1
- package/lib/commonjs/features/devConnect/devConnectUtils.js +99 -15
- package/lib/commonjs/features/devConnect/devConnectUtils.js.map +1 -1
- package/lib/commonjs/features/devConnect/index.js +39 -2
- package/lib/commonjs/features/devConnect/index.js.map +1 -1
- package/lib/commonjs/features/devConnect/nativeDevConnect.js +108 -0
- package/lib/commonjs/features/devConnect/nativeDevConnect.js.map +1 -0
- package/lib/commonjs/features/devConnect/platformDetect.js +7 -11
- package/lib/commonjs/features/devConnect/platformDetect.js.map +1 -1
- package/lib/commonjs/utils/debugPreferences.js +43 -6
- package/lib/commonjs/utils/debugPreferences.js.map +1 -1
- package/lib/module/features/devConnect/DevConnectQrScanner.js +116 -71
- package/lib/module/features/devConnect/DevConnectQrScanner.js.map +1 -1
- package/lib/module/features/devConnect/DevConnectTab.js +235 -164
- package/lib/module/features/devConnect/DevConnectTab.js.map +1 -1
- package/lib/module/features/devConnect/devConnectPreferences.js +33 -6
- package/lib/module/features/devConnect/devConnectPreferences.js.map +1 -1
- package/lib/module/features/devConnect/devConnectUtils.js +94 -15
- package/lib/module/features/devConnect/devConnectUtils.js.map +1 -1
- package/lib/module/features/devConnect/index.js +11 -3
- package/lib/module/features/devConnect/index.js.map +1 -1
- package/lib/module/features/devConnect/nativeDevConnect.js +102 -0
- package/lib/module/features/devConnect/nativeDevConnect.js.map +1 -0
- package/lib/module/features/devConnect/platformDetect.js +8 -12
- package/lib/module/features/devConnect/platformDetect.js.map +1 -1
- package/lib/module/utils/debugPreferences.js +43 -6
- package/lib/module/utils/debugPreferences.js.map +1 -1
- package/lib/typescript/src/features/devConnect/DevConnectQrScanner.d.ts +3 -2
- package/lib/typescript/src/features/devConnect/DevConnectQrScanner.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/DevConnectTab.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/devConnectPreferences.d.ts +6 -0
- package/lib/typescript/src/features/devConnect/devConnectPreferences.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/devConnectUtils.d.ts +18 -1
- package/lib/typescript/src/features/devConnect/devConnectUtils.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/index.d.ts +2 -2
- package/lib/typescript/src/features/devConnect/index.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/nativeDevConnect.d.ts +17 -0
- package/lib/typescript/src/features/devConnect/nativeDevConnect.d.ts.map +1 -0
- package/lib/typescript/src/features/devConnect/platformDetect.d.ts.map +1 -1
- package/lib/typescript/src/features/devConnect/types.d.ts +3 -0
- package/lib/typescript/src/features/devConnect/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/debugPreferences.d.ts +2 -0
- package/lib/typescript/src/utils/debugPreferences.d.ts.map +1 -1
- package/package.json +4 -1
- package/react-native-debug-toolkit.podspec +18 -0
- package/src/features/devConnect/DevConnectQrScanner.tsx +101 -60
- package/src/features/devConnect/DevConnectTab.tsx +227 -105
- package/src/features/devConnect/devConnectPreferences.ts +50 -5
- package/src/features/devConnect/devConnectUtils.ts +122 -15
- package/src/features/devConnect/index.ts +13 -0
- package/src/features/devConnect/nativeDevConnect.ts +126 -0
- package/src/features/devConnect/platformDetect.ts +8 -13
- package/src/features/devConnect/types.ts +3 -0
- package/src/utils/debugPreferences.ts +49 -4
package/README.md
CHANGED
|
@@ -26,6 +26,15 @@ RN App -> Debug Panel -> local daemon -> Web Console / HTTP API / MCP
|
|
|
26
26
|
npm install react-native-debug-toolkit
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
Install the native part and rebuild the app:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
cd ios && pod install
|
|
33
|
+
# Android: Gradle autolinking runs on the next build
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Expo Go cannot load this native module. Use a development build, prebuild, or bare React Native app.
|
|
37
|
+
|
|
29
38
|
Optional dependencies:
|
|
30
39
|
|
|
31
40
|
```bash
|
|
@@ -64,9 +73,11 @@ Open the Web Console:
|
|
|
64
73
|
http://127.0.0.1:3799/console
|
|
65
74
|
```
|
|
66
75
|
|
|
67
|
-
In the app, open Debug Panel -> `DevConnect` -> `Send Once` or `Start Live Sync
|
|
76
|
+
In the app, open Debug Panel -> `DevConnect` -> `Send Once` or `Start Live Sync` for desktop logs.
|
|
77
|
+
|
|
78
|
+
DevConnect auto-detects simulator/emulator and uses local host settings automatically. On real devices, enter your computer IP to connect.
|
|
68
79
|
|
|
69
|
-
|
|
80
|
+
For Remote JS Bundle, run Metro on your computer, enter computer IP and Metro port in `DevConnect`, then tap `Use Metro Bundle`. DevConnect writes React Native's native dev-server host setting and reloads the app. The IP and ports are persisted through AsyncStorage when installed, or through the native module after rebuild.
|
|
70
81
|
|
|
71
82
|
QR scan is optional. Install `react-native-camera-kit` or `expo-camera` in the app to enable the scan button. The app must request camera permission before scanning.
|
|
72
83
|
|
package/README.zh-CN.md
CHANGED
|
@@ -26,6 +26,15 @@ RN App -> Debug Panel -> local daemon -> Web Console / HTTP API / MCP
|
|
|
26
26
|
npm install react-native-debug-toolkit
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
安装原生部分并重新构建 App:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
cd ios && pod install
|
|
33
|
+
# Android:下次构建时 Gradle autolinking 生效
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Expo Go 不能加载这个原生模块。Expo 项目需用 development build、prebuild,或 bare React Native。
|
|
37
|
+
|
|
29
38
|
可选依赖:
|
|
30
39
|
|
|
31
40
|
```bash
|
|
@@ -64,9 +73,11 @@ npm exec debug-toolkit --daemon-only
|
|
|
64
73
|
http://127.0.0.1:3799/console
|
|
65
74
|
```
|
|
66
75
|
|
|
67
|
-
App 内打开 Debug Panel -> `DevConnect` -> `Send Once` 或 `Start Live Sync
|
|
76
|
+
App 内打开 Debug Panel -> `DevConnect` -> `Send Once` 或 `Start Live Sync` 同步桌面日志。
|
|
77
|
+
|
|
78
|
+
DevConnect 自动识别模拟器/真机,模拟器下自动使用本机 Metro/daemon 地址。真机需输入电脑 IP 地址。
|
|
68
79
|
|
|
69
|
-
DevConnect
|
|
80
|
+
Remote JS Bundle:先在电脑启动 Metro,在 `DevConnect` 输入电脑 IP 和 Metro 端口,然后点 `Use Metro Bundle`。DevConnect 会写入 React Native 原生 dev-server host 设置并 reload App。IP 和端口会通过 AsyncStorage 持久化;如果没装 AsyncStorage,则在重建后通过本库原生模块持久化。
|
|
70
81
|
|
|
71
82
|
扫码是可选能力。App 安装 `react-native-camera-kit` 或 `expo-camera` 后,DevConnect 才显示扫码按钮。App 仍需自己配置相机权限文案,并在使用扫码前申请相机权限。
|
|
72
83
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
repositories {
|
|
3
|
+
google()
|
|
4
|
+
mavenCentral()
|
|
5
|
+
}
|
|
6
|
+
dependencies {
|
|
7
|
+
classpath("com.android.tools.build:gradle:7.4.2")
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
def safeExtGet(prop, fallback) {
|
|
12
|
+
return rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
apply plugin: 'com.android.library'
|
|
16
|
+
|
|
17
|
+
android {
|
|
18
|
+
namespace 'com.reactnativedebugtoolkit'
|
|
19
|
+
compileSdkVersion safeExtGet('compileSdkVersion', 35)
|
|
20
|
+
|
|
21
|
+
defaultConfig {
|
|
22
|
+
minSdkVersion safeExtGet('minSdkVersion', 23)
|
|
23
|
+
targetSdkVersion safeExtGet('targetSdkVersion', 35)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
repositories {
|
|
28
|
+
google()
|
|
29
|
+
mavenCentral()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
dependencies {
|
|
33
|
+
implementation 'com.facebook.react:react-android'
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
package com.reactnativedebugtoolkit;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.content.SharedPreferences;
|
|
5
|
+
import android.preference.PreferenceManager;
|
|
6
|
+
|
|
7
|
+
import androidx.annotation.NonNull;
|
|
8
|
+
import androidx.annotation.Nullable;
|
|
9
|
+
|
|
10
|
+
import com.facebook.react.bridge.Arguments;
|
|
11
|
+
import com.facebook.react.bridge.Promise;
|
|
12
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
13
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
14
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
15
|
+
import com.facebook.react.bridge.UiThreadUtil;
|
|
16
|
+
import com.facebook.react.bridge.WritableMap;
|
|
17
|
+
|
|
18
|
+
import java.lang.reflect.Method;
|
|
19
|
+
|
|
20
|
+
public class DebugToolkitDevConnectModule extends ReactContextBaseJavaModule {
|
|
21
|
+
private static final String MODULE_NAME = "DebugToolkitDevConnect";
|
|
22
|
+
private static final String DEBUG_SERVER_HOST_KEY = "debug_http_host";
|
|
23
|
+
private static final String APPLY_RELOAD_REASON = "DebugToolkit DevConnect Metro host changed";
|
|
24
|
+
private static final String RESET_RELOAD_REASON = "DebugToolkit DevConnect Metro host reset";
|
|
25
|
+
|
|
26
|
+
public DebugToolkitDevConnectModule(ReactApplicationContext reactContext) {
|
|
27
|
+
super(reactContext);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@NonNull
|
|
31
|
+
@Override
|
|
32
|
+
public String getName() {
|
|
33
|
+
return MODULE_NAME;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private SharedPreferences getPreferences() {
|
|
37
|
+
return PreferenceManager.getDefaultSharedPreferences(getReactApplicationContext());
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@Nullable
|
|
41
|
+
private Object callGetter(Object target, String methodName) {
|
|
42
|
+
try {
|
|
43
|
+
Method method = target.getClass().getMethod(methodName);
|
|
44
|
+
return method.invoke(target);
|
|
45
|
+
} catch (Exception ignored) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private boolean triggerDevSupportReload(@Nullable Object devSupportManager) throws Exception {
|
|
51
|
+
if (devSupportManager == null) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
Object enabled = callGetter(devSupportManager, "getDevSupportEnabled");
|
|
56
|
+
if (enabled instanceof Boolean && !((Boolean) enabled)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
Method reloadMethod = devSupportManager.getClass().getMethod("handleReloadJS");
|
|
61
|
+
reloadMethod.invoke(devSupportManager);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private boolean reloadFromReactHost(Context applicationContext, String reason) throws Exception {
|
|
66
|
+
Object reactHost = callGetter(applicationContext, "getReactHost");
|
|
67
|
+
if (reactHost == null) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (triggerDevSupportReload(callGetter(reactHost, "getDevSupportManager"))) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
Method reloadMethod = reactHost.getClass().getMethod("reload", String.class);
|
|
76
|
+
reloadMethod.invoke(reactHost, reason);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private boolean reloadFromReactNativeHost(Context applicationContext) throws Exception {
|
|
81
|
+
Object reactNativeHost = callGetter(applicationContext, "getReactNativeHost");
|
|
82
|
+
Object instanceManager = reactNativeHost == null
|
|
83
|
+
? null
|
|
84
|
+
: callGetter(reactNativeHost, "getReactInstanceManager");
|
|
85
|
+
Object devSupportManager = instanceManager == null
|
|
86
|
+
? null
|
|
87
|
+
: callGetter(instanceManager, "getDevSupportManager");
|
|
88
|
+
return triggerDevSupportReload(devSupportManager);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private void resolveAfterReload(String reason, @Nullable WritableMap result, Promise promise) {
|
|
92
|
+
UiThreadUtil.runOnUiThread(() -> {
|
|
93
|
+
try {
|
|
94
|
+
Context applicationContext = getReactApplicationContext().getApplicationContext();
|
|
95
|
+
boolean reloaded = reloadFromReactHost(applicationContext, reason)
|
|
96
|
+
|| reloadFromReactNativeHost(applicationContext);
|
|
97
|
+
if (!reloaded) {
|
|
98
|
+
promise.reject("reload_unavailable", "Unable to trigger React Native reload after updating Metro host.");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
promise.resolve(result);
|
|
102
|
+
} catch (Exception error) {
|
|
103
|
+
promise.reject("reload_failed", "Unable to trigger React Native reload after updating Metro host.", error);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@ReactMethod
|
|
109
|
+
public void getMetroHost(Promise promise) {
|
|
110
|
+
@Nullable String host = getPreferences().getString(DEBUG_SERVER_HOST_KEY, null);
|
|
111
|
+
promise.resolve(host);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@ReactMethod
|
|
115
|
+
public void applyMetroHost(String hostPort, Promise promise) {
|
|
116
|
+
if (hostPort == null || hostPort.length() == 0) {
|
|
117
|
+
promise.reject("invalid_host", "Metro host cannot be empty.");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
boolean stored = getPreferences().edit().putString(DEBUG_SERVER_HOST_KEY, hostPort).commit();
|
|
122
|
+
if (!stored) {
|
|
123
|
+
promise.reject("storage_failed", "Unable to persist Metro host.");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
WritableMap result = Arguments.createMap();
|
|
128
|
+
result.putString("hostPort", hostPort);
|
|
129
|
+
resolveAfterReload(APPLY_RELOAD_REASON, result, promise);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@ReactMethod
|
|
133
|
+
public void resetMetroHost(Promise promise) {
|
|
134
|
+
boolean stored = getPreferences().edit().remove(DEBUG_SERVER_HOST_KEY).commit();
|
|
135
|
+
if (!stored) {
|
|
136
|
+
promise.reject("storage_failed", "Unable to reset Metro host.");
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
resolveAfterReload(RESET_RELOAD_REASON, null, promise);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@ReactMethod
|
|
144
|
+
public void getPreference(String key, Promise promise) {
|
|
145
|
+
@Nullable String value = getPreferences().getString(key, null);
|
|
146
|
+
promise.resolve(value);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@ReactMethod
|
|
150
|
+
public void setPreference(String key, String value, Promise promise) {
|
|
151
|
+
getPreferences().edit().putString(key, value).apply();
|
|
152
|
+
promise.resolve(null);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package com.reactnativedebugtoolkit;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
|
|
5
|
+
import com.facebook.react.ReactPackage;
|
|
6
|
+
import com.facebook.react.bridge.NativeModule;
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
8
|
+
import com.facebook.react.uimanager.ViewManager;
|
|
9
|
+
|
|
10
|
+
import java.util.Collections;
|
|
11
|
+
import java.util.List;
|
|
12
|
+
|
|
13
|
+
public class ReactNativeDebugToolkitPackage implements ReactPackage {
|
|
14
|
+
@NonNull
|
|
15
|
+
@Override
|
|
16
|
+
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
|
|
17
|
+
return Collections.<NativeModule>singletonList(new DebugToolkitDevConnectModule(reactContext));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@NonNull
|
|
21
|
+
@Override
|
|
22
|
+
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
|
|
23
|
+
return Collections.emptyList();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
#import <React/RCTBridgeModule.h>
|
|
3
|
+
#import <React/RCTBundleURLProvider.h>
|
|
4
|
+
#import <React/RCTReloadCommand.h>
|
|
5
|
+
|
|
6
|
+
static NSString *const DebugToolkitRCTJsLocationKey = @"RCT_jsLocation";
|
|
7
|
+
static NSString *const DebugToolkitBundleRoot = @"index";
|
|
8
|
+
|
|
9
|
+
static void DebugToolkitResolveAfterReload(NSString *reason, id result, RCTPromiseResolveBlock resolve)
|
|
10
|
+
{
|
|
11
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
12
|
+
NSURL *bundleURL = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:DebugToolkitBundleRoot];
|
|
13
|
+
if (bundleURL) {
|
|
14
|
+
RCTReloadCommandSetBundleURL(bundleURL);
|
|
15
|
+
}
|
|
16
|
+
RCTTriggerReloadCommandListeners(reason);
|
|
17
|
+
resolve(result ?: [NSNull null]);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@interface DebugToolkitDevConnect : NSObject <RCTBridgeModule>
|
|
22
|
+
@end
|
|
23
|
+
|
|
24
|
+
@implementation DebugToolkitDevConnect
|
|
25
|
+
|
|
26
|
+
RCT_EXPORT_MODULE(DebugToolkitDevConnect)
|
|
27
|
+
|
|
28
|
+
+ (BOOL)requiresMainQueueSetup
|
|
29
|
+
{
|
|
30
|
+
return NO;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
RCT_EXPORT_METHOD(getMetroHost:(RCTPromiseResolveBlock)resolve
|
|
34
|
+
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
35
|
+
{
|
|
36
|
+
NSString *host = [RCTBundleURLProvider sharedSettings].jsLocation;
|
|
37
|
+
resolve(host ?: [NSNull null]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
RCT_EXPORT_METHOD(applyMetroHost:(NSString *)hostPort
|
|
41
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
42
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
43
|
+
{
|
|
44
|
+
if (hostPort.length == 0) {
|
|
45
|
+
reject(@"invalid_host", @"Metro host cannot be empty.", nil);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
[RCTBundleURLProvider sharedSettings].jsLocation = hostPort;
|
|
50
|
+
DebugToolkitResolveAfterReload(@"DebugToolkit DevConnect Metro host changed", @{ @"hostPort" : hostPort }, resolve);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
RCT_EXPORT_METHOD(resetMetroHost:(RCTPromiseResolveBlock)resolve
|
|
54
|
+
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
55
|
+
{
|
|
56
|
+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
57
|
+
[defaults removeObjectForKey:DebugToolkitRCTJsLocationKey];
|
|
58
|
+
[defaults synchronize];
|
|
59
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:RCTBundleURLProviderUpdatedNotification object:nil];
|
|
60
|
+
DebugToolkitResolveAfterReload(@"DebugToolkit DevConnect Metro host reset", [NSNull null], resolve);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
RCT_EXPORT_METHOD(getPreference:(NSString *)key
|
|
64
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
65
|
+
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
66
|
+
{
|
|
67
|
+
NSString *value = [[NSUserDefaults standardUserDefaults] stringForKey:key];
|
|
68
|
+
resolve(value ?: [NSNull null]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
RCT_EXPORT_METHOD(setPreference:(NSString *)key
|
|
72
|
+
value:(NSString *)value
|
|
73
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
74
|
+
rejecter:(__unused RCTPromiseRejectBlock)reject)
|
|
75
|
+
{
|
|
76
|
+
[[NSUserDefaults standardUserDefaults] setObject:value forKey:key];
|
|
77
|
+
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
78
|
+
resolve([NSNull null]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@end
|
|
@@ -27,7 +27,9 @@ class CameraErrorBoundary extends _react.Component {
|
|
|
27
27
|
this.props.onCameraError(error.message || 'Camera failed to initialize.');
|
|
28
28
|
}
|
|
29
29
|
render() {
|
|
30
|
-
if (this.state.hasError)
|
|
30
|
+
if (this.state.hasError) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
31
33
|
return this.props.children;
|
|
32
34
|
}
|
|
33
35
|
}
|
|
@@ -37,7 +39,7 @@ class CameraErrorBoundary extends _react.Component {
|
|
|
37
39
|
function DevConnectQrScanner({
|
|
38
40
|
visible,
|
|
39
41
|
onClose,
|
|
40
|
-
|
|
42
|
+
onScanTarget
|
|
41
43
|
}) {
|
|
42
44
|
const scannedRef = (0, _react.useRef)(false);
|
|
43
45
|
const [error, setError] = (0, _react.useState)(null);
|
|
@@ -51,8 +53,12 @@ function DevConnectQrScanner({
|
|
|
51
53
|
}
|
|
52
54
|
}, [visible]);
|
|
53
55
|
const handleScanned = (0, _react.useCallback)(rawValue => {
|
|
54
|
-
if (scannedRef.current)
|
|
55
|
-
|
|
56
|
+
if (scannedRef.current) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (typeof rawValue !== 'string') {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
56
62
|
const parsed = (0, _devConnectUtils.parseMetroQrPayload)(rawValue);
|
|
57
63
|
if (!parsed) {
|
|
58
64
|
setError('QR code does not contain a supported Metro URL.');
|
|
@@ -60,9 +66,12 @@ function DevConnectQrScanner({
|
|
|
60
66
|
}
|
|
61
67
|
scannedRef.current = true;
|
|
62
68
|
setError(null);
|
|
63
|
-
|
|
69
|
+
onScanTarget({
|
|
70
|
+
computerHost: parsed.computerHost,
|
|
71
|
+
metroPort: parsed.metroPort
|
|
72
|
+
});
|
|
64
73
|
onClose();
|
|
65
|
-
}, [onClose,
|
|
74
|
+
}, [onClose, onScanTarget]);
|
|
66
75
|
const handleCameraKitRead = (0, _react.useCallback)(event => {
|
|
67
76
|
handleScanned(event.nativeEvent?.codeStringValue ?? '');
|
|
68
77
|
}, [handleScanned]);
|
|
@@ -72,63 +81,71 @@ function DevConnectQrScanner({
|
|
|
72
81
|
const handleCameraError = (0, _react.useCallback)(_msg => {
|
|
73
82
|
setCameraFailed(true);
|
|
74
83
|
}, []);
|
|
75
|
-
if (!visible || !scanner)
|
|
84
|
+
if (!visible || !scanner) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
76
87
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
|
|
77
88
|
visible: visible,
|
|
78
89
|
animationType: "slide",
|
|
90
|
+
presentationStyle: "fullScreen",
|
|
79
91
|
onRequestClose: onClose,
|
|
80
92
|
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
81
93
|
style: styles.container,
|
|
82
|
-
children: [
|
|
83
|
-
|
|
84
|
-
children:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
style: styles.
|
|
104
|
-
children:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
94
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
95
|
+
style: styles.previewLayer,
|
|
96
|
+
children: [!cameraFailed && /*#__PURE__*/(0, _jsxRuntime.jsx)(CameraErrorBoundary, {
|
|
97
|
+
onCameraError: handleCameraError,
|
|
98
|
+
children: scanner.kind === 'camera-kit' && scanner.CameraKit ? /*#__PURE__*/(0, _jsxRuntime.jsx)(scanner.CameraKit.Camera, {
|
|
99
|
+
style: styles.camera,
|
|
100
|
+
cameraType: scanner.CameraKit.CameraType?.Back,
|
|
101
|
+
scanBarcode: true,
|
|
102
|
+
onReadCode: handleCameraKitRead,
|
|
103
|
+
showFrame: true,
|
|
104
|
+
laserColor: _colors.Colors.primary,
|
|
105
|
+
frameColor: _colors.Colors.primary,
|
|
106
|
+
allowedBarcodeTypes: ['qr']
|
|
107
|
+
}) : scanner.kind === 'expo-camera' && scanner.ExpoCamera ? /*#__PURE__*/(0, _jsxRuntime.jsx)(scanner.ExpoCamera.Camera, {
|
|
108
|
+
style: styles.camera,
|
|
109
|
+
onBarCodeScanned: handleExpoScanned,
|
|
110
|
+
barCodeScannerSettings: {
|
|
111
|
+
barCodeTypes: ['qr']
|
|
112
|
+
}
|
|
113
|
+
}) : null
|
|
114
|
+
}), cameraFailed && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
115
|
+
style: styles.cameraFallback,
|
|
116
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
117
|
+
style: styles.cameraFallbackText,
|
|
118
|
+
children: "Camera unavailable."
|
|
119
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
120
|
+
style: styles.cameraFallbackHint,
|
|
121
|
+
children: "Please enter computer IP manually."
|
|
122
|
+
})]
|
|
108
123
|
})]
|
|
109
124
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
110
|
-
style: styles.
|
|
111
|
-
children: [
|
|
112
|
-
style: styles.
|
|
113
|
-
children:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
125
|
+
style: styles.topBar,
|
|
126
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
127
|
+
style: styles.titleGroup,
|
|
128
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
129
|
+
style: styles.title,
|
|
130
|
+
children: "Scan Metro QR"
|
|
131
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
132
|
+
style: styles.subtitle,
|
|
133
|
+
children: "Expo or Metro URL"
|
|
134
|
+
})]
|
|
117
135
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
118
136
|
style: styles.closeButton,
|
|
119
137
|
onPress: onClose,
|
|
120
|
-
activeOpacity: 0.
|
|
138
|
+
activeOpacity: 0.75,
|
|
121
139
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
122
140
|
style: styles.closeButtonText,
|
|
123
141
|
children: "Close"
|
|
124
142
|
})
|
|
125
143
|
})]
|
|
126
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.
|
|
127
|
-
style: styles.
|
|
128
|
-
onPress: onClose,
|
|
144
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
145
|
+
style: [styles.statusPill, error && styles.statusPillError],
|
|
129
146
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
130
|
-
style: styles.
|
|
131
|
-
children:
|
|
147
|
+
style: [styles.statusText, error && styles.statusTextError],
|
|
148
|
+
children: error ?? 'Point the camera at exp:// or http:// Metro URL.'
|
|
132
149
|
})
|
|
133
150
|
})]
|
|
134
151
|
})
|
|
@@ -139,11 +156,14 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
139
156
|
flex: 1,
|
|
140
157
|
backgroundColor: '#000'
|
|
141
158
|
},
|
|
159
|
+
previewLayer: {
|
|
160
|
+
..._reactNative.StyleSheet.absoluteFillObject
|
|
161
|
+
},
|
|
142
162
|
camera: {
|
|
143
|
-
|
|
163
|
+
..._reactNative.StyleSheet.absoluteFillObject
|
|
144
164
|
},
|
|
145
165
|
cameraFallback: {
|
|
146
|
-
|
|
166
|
+
..._reactNative.StyleSheet.absoluteFillObject,
|
|
147
167
|
justifyContent: 'center',
|
|
148
168
|
alignItems: 'center',
|
|
149
169
|
padding: 24
|
|
@@ -159,45 +179,70 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
159
179
|
color: 'rgba(255,255,255,0.6)',
|
|
160
180
|
textAlign: 'center'
|
|
161
181
|
},
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
182
|
+
topBar: {
|
|
183
|
+
position: 'absolute',
|
|
184
|
+
top: 44,
|
|
185
|
+
left: 16,
|
|
186
|
+
right: 16,
|
|
187
|
+
flexDirection: 'row',
|
|
188
|
+
alignItems: 'center',
|
|
189
|
+
justifyContent: 'space-between',
|
|
190
|
+
paddingLeft: 14,
|
|
191
|
+
paddingRight: 8,
|
|
192
|
+
paddingVertical: 8,
|
|
193
|
+
borderRadius: 12,
|
|
194
|
+
backgroundColor: 'rgba(0,0,0,0.62)'
|
|
165
195
|
},
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
marginBottom: 12
|
|
196
|
+
titleGroup: {
|
|
197
|
+
flex: 1,
|
|
198
|
+
paddingRight: 10
|
|
170
199
|
},
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
200
|
+
title: {
|
|
201
|
+
color: '#fff',
|
|
202
|
+
fontSize: 15,
|
|
203
|
+
fontWeight: '700'
|
|
204
|
+
},
|
|
205
|
+
subtitle: {
|
|
206
|
+
color: 'rgba(255,255,255,0.68)',
|
|
207
|
+
fontSize: 11,
|
|
208
|
+
marginTop: 2
|
|
175
209
|
},
|
|
176
210
|
closeButton: {
|
|
177
211
|
alignItems: 'center',
|
|
178
212
|
justifyContent: 'center',
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
213
|
+
paddingHorizontal: 12,
|
|
214
|
+
paddingVertical: 8,
|
|
215
|
+
borderRadius: 8,
|
|
216
|
+
backgroundColor: 'rgba(255,255,255,0.14)'
|
|
182
217
|
},
|
|
183
218
|
closeButtonText: {
|
|
184
219
|
color: '#fff',
|
|
185
220
|
fontSize: 14,
|
|
186
221
|
fontWeight: '600'
|
|
187
222
|
},
|
|
188
|
-
|
|
223
|
+
statusPill: {
|
|
189
224
|
position: 'absolute',
|
|
190
|
-
|
|
225
|
+
left: 16,
|
|
191
226
|
right: 16,
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
227
|
+
bottom: 30,
|
|
228
|
+
paddingHorizontal: 14,
|
|
229
|
+
paddingVertical: 11,
|
|
230
|
+
borderRadius: 12,
|
|
231
|
+
backgroundColor: 'rgba(0,0,0,0.62)'
|
|
196
232
|
},
|
|
197
|
-
|
|
233
|
+
statusPillError: {
|
|
234
|
+
backgroundColor: `${_colors.Colors.error}22`,
|
|
235
|
+
borderWidth: 1,
|
|
236
|
+
borderColor: `${_colors.Colors.error}66`
|
|
237
|
+
},
|
|
238
|
+
statusText: {
|
|
198
239
|
color: '#fff',
|
|
199
240
|
fontSize: 13,
|
|
200
|
-
|
|
241
|
+
textAlign: 'center',
|
|
242
|
+
lineHeight: 18
|
|
243
|
+
},
|
|
244
|
+
statusTextError: {
|
|
245
|
+
color: '#fff'
|
|
201
246
|
}
|
|
202
247
|
});
|
|
203
248
|
//# sourceMappingURL=DevConnectQrScanner.js.map
|