yaver-feedback-react-native 0.8.13 → 0.9.2
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/app.plugin.js +126 -9
- package/dist/BlackBox.js +9 -1
- package/dist/DeployPanel.js +65 -0
- package/dist/Discovery.js +13 -2
- package/dist/FeedbackModal.js +477 -66
- package/dist/MachinePickerScreen.js +14 -5
- package/dist/P2PClient.d.ts +94 -1
- package/dist/P2PClient.js +281 -2
- package/dist/ShakeDetector.js +2 -0
- package/dist/VibeChatScreen.d.ts +6 -1
- package/dist/VibeChatScreen.js +204 -1
- package/dist/YaverFeedback.d.ts +98 -0
- package/dist/YaverFeedback.js +509 -46
- package/dist/__tests__/BlackBox.relayPassword.test.d.ts +1 -0
- package/dist/__tests__/BlackBox.relayPassword.test.js +105 -0
- package/dist/__tests__/BlackBoxAutoStart.test.d.ts +1 -0
- package/dist/__tests__/BlackBoxAutoStart.test.js +91 -0
- package/dist/__tests__/BlackBoxAutoStartColdStart.test.d.ts +1 -0
- package/dist/__tests__/BlackBoxAutoStartColdStart.test.js +156 -0
- package/dist/__tests__/BrowserLaneIcon.test.d.ts +3 -0
- package/dist/__tests__/BrowserLaneIcon.test.js +80 -0
- package/dist/__tests__/P2PClient.test.js +2 -2
- package/dist/__tests__/ReportIdentity.test.d.ts +1 -0
- package/dist/__tests__/ReportIdentity.test.js +168 -0
- package/dist/__tests__/SDKToken.test.js +1 -1
- package/dist/__tests__/ShakeToggle.test.d.ts +1 -0
- package/dist/__tests__/ShakeToggle.test.js +121 -0
- package/dist/__tests__/pickTargetDevice.test.d.ts +1 -0
- package/dist/__tests__/pickTargetDevice.test.js +89 -0
- package/dist/__tests__/reloadActions.test.d.ts +1 -0
- package/dist/__tests__/reloadActions.test.js +129 -0
- package/dist/__tests__/reloadActionsParity.test.d.ts +1 -0
- package/dist/__tests__/reloadActionsParity.test.js +42 -0
- package/dist/__tests__/types.test.js +5 -5
- package/dist/_core/device.d.ts +19 -9
- package/dist/_core/device.js +20 -14
- package/dist/capture.d.ts +6 -0
- package/dist/capture.js +53 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +11 -1
- package/dist/reloadActions.d.ts +88 -0
- package/dist/reloadActions.js +200 -0
- package/dist/storeShots.d.ts +67 -0
- package/dist/storeShots.js +137 -0
- package/dist/types.d.ts +215 -3
- package/dist/voice.d.ts +61 -0
- package/dist/voice.js +246 -0
- package/package.json +14 -3
- package/src/BlackBox.ts +10 -1
- package/src/DeployPanel.tsx +74 -0
- package/src/Discovery.ts +13 -2
- package/src/FeedbackModal.tsx +563 -87
- package/src/MachinePickerScreen.tsx +12 -3
- package/src/P2PClient.ts +314 -2
- package/src/ShakeDetector.ts +1 -0
- package/src/VibeChatScreen.tsx +219 -0
- package/src/YaverFeedback.ts +498 -46
- package/src/__tests__/BlackBox.relayPassword.test.ts +129 -0
- package/src/__tests__/BlackBoxAutoStart.test.ts +111 -0
- package/src/__tests__/BlackBoxAutoStartColdStart.test.ts +191 -0
- package/src/__tests__/BrowserLaneIcon.test.ts +85 -0
- package/src/__tests__/P2PClient.test.ts +2 -2
- package/src/__tests__/ReportIdentity.test.ts +203 -0
- package/src/__tests__/SDKToken.test.ts +1 -1
- package/src/__tests__/ShakeToggle.test.ts +153 -0
- package/src/__tests__/pickTargetDevice.test.ts +101 -0
- package/src/__tests__/reloadActions.test.ts +171 -0
- package/src/__tests__/reloadActionsParity.test.ts +49 -0
- package/src/__tests__/types.test.ts +5 -5
- package/src/_core/device.ts +20 -14
- package/src/capture.ts +51 -0
- package/src/index.ts +21 -0
- package/src/reloadActions.ts +273 -0
- package/src/storeShots.ts +189 -0
- package/src/types.ts +217 -3
- package/src/voice.ts +270 -0
package/app.plugin.js
CHANGED
|
@@ -387,14 +387,135 @@ function withYaverAndroidHotReload(config) {
|
|
|
387
387
|
},
|
|
388
388
|
]);
|
|
389
389
|
|
|
390
|
-
// Patch MainApplication to register the package and use hot bundle
|
|
390
|
+
// Patch MainApplication to register the package and use hot bundle.
|
|
391
|
+
//
|
|
392
|
+
// MainApplication is Kotlin on React Native 0.73+ (every current Expo
|
|
393
|
+
// template) and Java before that. The two need genuinely different code —
|
|
394
|
+
// `new Foo()`, `final`, and `@Override` are all syntax errors in Kotlin —
|
|
395
|
+
// so branch on the language Expo reports rather than assuming.
|
|
391
396
|
config = withMainApplication(config, (config) => {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
if (contents.includes("YaverHotReload")) {
|
|
397
|
+
if (config.modResults.contents.includes("YaverHotReload")) {
|
|
395
398
|
return config;
|
|
396
399
|
}
|
|
400
|
+
config.modResults.contents =
|
|
401
|
+
config.modResults.language === "kt"
|
|
402
|
+
? patchMainApplicationKotlin(config.modResults.contents)
|
|
403
|
+
: patchMainApplicationJava(config.modResults.contents);
|
|
404
|
+
return config;
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
return config;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Insert `snippet` immediately before the closing brace of the method whose
|
|
412
|
+
* signature contains `anchor`, by matching braces from the method's opening
|
|
413
|
+
* one.
|
|
414
|
+
*
|
|
415
|
+
* The boot guard has to run at the END of onCreate: it touches
|
|
416
|
+
* reactNativeHost, and reaching that before super.onCreate() and
|
|
417
|
+
* SoLoader.init() would initialise React before its native libraries are
|
|
418
|
+
* loaded. Returns the contents unchanged if the anchor isn't found — a
|
|
419
|
+
* missing safety net is survivable, a corrupted MainApplication is not.
|
|
420
|
+
*/
|
|
421
|
+
function insertAtEndOfMethod(contents, anchor, snippet) {
|
|
422
|
+
const anchorIdx = contents.indexOf(anchor);
|
|
423
|
+
if (anchorIdx === -1) return contents;
|
|
424
|
+
const open = contents.indexOf("{", anchorIdx);
|
|
425
|
+
if (open === -1) return contents;
|
|
426
|
+
|
|
427
|
+
let depth = 0;
|
|
428
|
+
for (let i = open; i < contents.length; i++) {
|
|
429
|
+
const ch = contents[i];
|
|
430
|
+
if (ch === "{") depth++;
|
|
431
|
+
else if (ch === "}") {
|
|
432
|
+
depth--;
|
|
433
|
+
if (depth === 0) {
|
|
434
|
+
return contents.slice(0, i) + snippet + contents.slice(i);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return contents;
|
|
439
|
+
}
|
|
397
440
|
|
|
441
|
+
/** Kotlin MainApplication (React Native 0.73+). */
|
|
442
|
+
function patchMainApplicationKotlin(contents) {
|
|
443
|
+
// Imports. Kotlin takes no semicolons.
|
|
444
|
+
contents = contents.replace(
|
|
445
|
+
"import com.facebook.react.ReactApplication",
|
|
446
|
+
"import com.facebook.react.ReactApplication\nimport io.yaver.feedback.YaverHotReloadModule\nimport io.yaver.feedback.YaverHotReloadPackage"
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
// Register the package. Anchor on `return packages` rather than on
|
|
450
|
+
// `packages.add(` — the template's only occurrence of that is inside a
|
|
451
|
+
// commented-out example line.
|
|
452
|
+
contents = contents.replace(
|
|
453
|
+
/(\n([ \t]*)return packages\n)/,
|
|
454
|
+
"\n$2packages.add(YaverHotReloadPackage())\n$1"
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
// Load a hot-pushed bundle when one is present. Inserted before
|
|
458
|
+
// getJSMainModuleName, which every Expo template defines.
|
|
459
|
+
if (!contents.includes("getJSBundleFile")) {
|
|
460
|
+
contents = contents.replace(
|
|
461
|
+
/(\n([ \t]*)override fun getJSMainModuleName\(\))/,
|
|
462
|
+
`
|
|
463
|
+
$2override fun getJSBundleFile(): String? {
|
|
464
|
+
$2 // Yaver Feedback SDK: load hot-reloaded bundle if available
|
|
465
|
+
$2 val hotBundle = YaverHotReloadModule.getSavedBundleFile(application.applicationContext)
|
|
466
|
+
$2 return hotBundle?.absolutePath ?: super.getJSBundleFile()
|
|
467
|
+
$2}
|
|
468
|
+
$1`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Crash-revert safety net: clear the boot-attempt counter once the React
|
|
473
|
+
// context initialises (bundle loaded successfully), AND via a 10-s fallback
|
|
474
|
+
// in case that listener never fires (e.g. an infinite loop in the root
|
|
475
|
+
// component). If neither fires, YaverHotReloadModule.getSavedBundleFile()
|
|
476
|
+
// reverts to the APK-bundled bundle after 3 failed cold starts. Parity with
|
|
477
|
+
// YaverHotReload.swift on iOS.
|
|
478
|
+
if (!contents.includes("yaverHotReloadBootListener")) {
|
|
479
|
+
contents = insertAtEndOfMethod(
|
|
480
|
+
contents,
|
|
481
|
+
"override fun onCreate()",
|
|
482
|
+
`
|
|
483
|
+
// Yaver Feedback SDK hot-reload crash-revert safety net
|
|
484
|
+
val yaverHotReloadCtx: android.content.Context = applicationContext
|
|
485
|
+
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed(
|
|
486
|
+
{ YaverHotReloadModule.markBootSuccessful(yaverHotReloadCtx) },
|
|
487
|
+
10000
|
|
488
|
+
)
|
|
489
|
+
try {
|
|
490
|
+
// Anonymous object, not a lambda: SAM conversion needs a Java interface
|
|
491
|
+
// or a Kotlin \`fun interface\`, and as of RN 0.81
|
|
492
|
+
// com.facebook.react.ReactInstanceEventListener is a plain Kotlin
|
|
493
|
+
// interface (ReactInstanceEventListener.kt). A lambda there fails
|
|
494
|
+
// :app:compileReleaseKotlin with "Argument type mismatch: actual type is
|
|
495
|
+
// 'Function0<Unit>'" — i.e. the host app cannot build a release AAB at
|
|
496
|
+
// all. The Java path below already did this correctly.
|
|
497
|
+
reactNativeHost.reactInstanceManager.addReactInstanceEventListener(
|
|
498
|
+
object : com.facebook.react.ReactInstanceEventListener {
|
|
499
|
+
override fun onReactContextInitialized(
|
|
500
|
+
context: com.facebook.react.bridge.ReactContext
|
|
501
|
+
) {
|
|
502
|
+
YaverHotReloadModule.markBootSuccessful(yaverHotReloadCtx)
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
)
|
|
506
|
+
} catch (yaverHotReloadBootListener: Throwable) {
|
|
507
|
+
// Bridgeless / New Architecture does not expose reactInstanceManager;
|
|
508
|
+
// the 10-s fallback above still covers us.
|
|
509
|
+
}
|
|
510
|
+
`
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return contents;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** Java MainApplication (React Native < 0.73). */
|
|
518
|
+
function patchMainApplicationJava(contents) {
|
|
398
519
|
// Add import
|
|
399
520
|
contents = contents.replace(
|
|
400
521
|
"import com.facebook.react.ReactApplication",
|
|
@@ -468,11 +589,7 @@ function withYaverAndroidHotReload(config) {
|
|
|
468
589
|
contents.slice(0, insertionPoint) + bootGuard + contents.slice(insertionPoint);
|
|
469
590
|
}
|
|
470
591
|
|
|
471
|
-
|
|
472
|
-
return config;
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
return config;
|
|
592
|
+
return contents;
|
|
476
593
|
}
|
|
477
594
|
|
|
478
595
|
function withYaverFeedback(config, props) {
|
package/dist/BlackBox.js
CHANGED
|
@@ -3,6 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BlackBox = void 0;
|
|
4
4
|
const react_native_1 = require("react-native");
|
|
5
5
|
const YaverFeedback_1 = require("./YaverFeedback");
|
|
6
|
+
function unrefTimer(timer) {
|
|
7
|
+
const maybeNodeTimer = timer;
|
|
8
|
+
if (typeof maybeNodeTimer.unref === 'function') {
|
|
9
|
+
maybeNodeTimer.unref();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
6
12
|
class BlackBox {
|
|
7
13
|
/**
|
|
8
14
|
* Start the black box stream. Call after `YaverFeedback.init()`.
|
|
@@ -18,7 +24,7 @@ class BlackBox {
|
|
|
18
24
|
}
|
|
19
25
|
BlackBox.baseUrl = feedbackConfig.agentUrl.replace(/\/$/, '');
|
|
20
26
|
BlackBox.authToken = feedbackConfig.authToken ?? null;
|
|
21
|
-
BlackBox.relayPassword =
|
|
27
|
+
BlackBox.relayPassword = YaverFeedback_1.YaverFeedback.getRelayPassword();
|
|
22
28
|
BlackBox.deviceId = config?.deviceId ?? BlackBox.generateDeviceId();
|
|
23
29
|
BlackBox.appName = config?.appName ?? '';
|
|
24
30
|
BlackBox.flushInterval = config?.flushInterval ?? 2000;
|
|
@@ -29,6 +35,7 @@ class BlackBox {
|
|
|
29
35
|
if (BlackBox.flushTimer)
|
|
30
36
|
clearInterval(BlackBox.flushTimer);
|
|
31
37
|
BlackBox.flushTimer = setInterval(() => BlackBox.flush(), BlackBox.flushInterval);
|
|
38
|
+
unrefTimer(BlackBox.flushTimer);
|
|
32
39
|
// Log the session start
|
|
33
40
|
BlackBox.push({
|
|
34
41
|
type: 'lifecycle',
|
|
@@ -343,6 +350,7 @@ class BlackBox {
|
|
|
343
350
|
if (BlackBox.started)
|
|
344
351
|
BlackBox.connectSSE();
|
|
345
352
|
}, 5000);
|
|
353
|
+
unrefTimer(BlackBox.sseReconnectTimer);
|
|
346
354
|
}
|
|
347
355
|
// ─── Internal ────────────────────────────────────────────────────
|
|
348
356
|
static push(event) {
|
package/dist/DeployPanel.js
CHANGED
|
@@ -151,6 +151,45 @@ const DeployPanel = ({ onClose }) => {
|
|
|
151
151
|
</react_native_1.Text>
|
|
152
152
|
</react_native_1.Pressable>);
|
|
153
153
|
};
|
|
154
|
+
// First-class App Store screenshots: walk the app's routes on THIS
|
|
155
|
+
// device, screenshot each, upload to the agent (which runs the ASC
|
|
156
|
+
// backend). Closes the overlay first so the captures are clean (no
|
|
157
|
+
// modal in frame), then reports via an alert.
|
|
158
|
+
const runStoreShots = () => {
|
|
159
|
+
const cfg = YaverFeedback_1.YaverFeedback.getConfig();
|
|
160
|
+
const ss = cfg?.storeShots;
|
|
161
|
+
if (!ss?.routes?.length) {
|
|
162
|
+
setStatus('Set config.storeShots.routes (and a navigationRef) to enable.');
|
|
163
|
+
setStatusTone('error');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const app = resolveAppSlug();
|
|
167
|
+
onClose();
|
|
168
|
+
// Defer so the overlay is fully dismissed before the first capture.
|
|
169
|
+
setTimeout(async () => {
|
|
170
|
+
try {
|
|
171
|
+
const res = await YaverFeedback_1.YaverFeedback.captureStoreScreenshots({
|
|
172
|
+
app,
|
|
173
|
+
routes: ss.routes,
|
|
174
|
+
navigationRef: ss.navigationRef,
|
|
175
|
+
screens: ss.screens,
|
|
176
|
+
submit: ss.submit,
|
|
177
|
+
});
|
|
178
|
+
const msg = res.ok
|
|
179
|
+
? res.submitted
|
|
180
|
+
? 'Submitted for App Store review 🎉'
|
|
181
|
+
: res.staged
|
|
182
|
+
? `Uploaded ${res.uploaded} screenshots — staged. One tap left in App Store Connect.`
|
|
183
|
+
: `Uploaded ${res.uploaded} App Store screenshots.`
|
|
184
|
+
: res.message || 'Capture failed.';
|
|
185
|
+
react_native_1.Alert.alert('App Store screenshots', msg);
|
|
186
|
+
}
|
|
187
|
+
catch (e) {
|
|
188
|
+
react_native_1.Alert.alert('App Store screenshots', e?.message ?? 'Capture failed.');
|
|
189
|
+
}
|
|
190
|
+
}, 500);
|
|
191
|
+
};
|
|
192
|
+
const storeShotsEnabled = (YaverFeedback_1.YaverFeedback.getConfig()?.storeShots?.routes?.length ?? 0) > 0;
|
|
154
193
|
const triggerDeploy = async (machine) => {
|
|
155
194
|
if (!options)
|
|
156
195
|
return;
|
|
@@ -233,6 +272,13 @@ const DeployPanel = ({ onClose }) => {
|
|
|
233
272
|
<react_native_1.ActivityIndicator color="rgba(255,255,255,0.6)"/>
|
|
234
273
|
</react_native_1.View>) : error ? (<react_native_1.Text style={styles.error}>{error}</react_native_1.Text>) : options ? (<react_native_1.ScrollView style={styles.list}>{options.devices.map(machineRow)}</react_native_1.ScrollView>) : null}
|
|
235
274
|
|
|
275
|
+
{storeShotsEnabled && (<react_native_1.Pressable onPress={runStoreShots} disabled={shipping} style={({ pressed }) => [styles.shotsBtn, pressed && styles.rowPressed]}>
|
|
276
|
+
<react_native_1.Text style={styles.shotsBtnText}>📸 App Store screenshots</react_native_1.Text>
|
|
277
|
+
<react_native_1.Text style={styles.shotsBtnSub}>
|
|
278
|
+
capture this app on-device + upload to App Store Connect
|
|
279
|
+
</react_native_1.Text>
|
|
280
|
+
</react_native_1.Pressable>)}
|
|
281
|
+
|
|
236
282
|
{status && (<react_native_1.Text style={[
|
|
237
283
|
styles.status,
|
|
238
284
|
statusTone === 'success' && styles.statusSuccess,
|
|
@@ -339,6 +385,25 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
339
385
|
rowMetaWarning: {
|
|
340
386
|
color: 'rgb(255,178,115)',
|
|
341
387
|
},
|
|
388
|
+
shotsBtn: {
|
|
389
|
+
marginTop: 12,
|
|
390
|
+
paddingVertical: 12,
|
|
391
|
+
paddingHorizontal: 14,
|
|
392
|
+
borderRadius: 10,
|
|
393
|
+
backgroundColor: 'rgba(124,109,255,0.16)',
|
|
394
|
+
borderWidth: 1,
|
|
395
|
+
borderColor: 'rgba(124,109,255,0.4)',
|
|
396
|
+
},
|
|
397
|
+
shotsBtnText: {
|
|
398
|
+
color: '#fff',
|
|
399
|
+
fontSize: 14,
|
|
400
|
+
fontWeight: '600',
|
|
401
|
+
},
|
|
402
|
+
shotsBtnSub: {
|
|
403
|
+
color: 'rgba(255,255,255,0.55)',
|
|
404
|
+
fontSize: 12,
|
|
405
|
+
marginTop: 2,
|
|
406
|
+
},
|
|
342
407
|
status: {
|
|
343
408
|
color: 'rgba(255,255,255,0.55)',
|
|
344
409
|
fontSize: 12,
|
package/dist/Discovery.js
CHANGED
|
@@ -34,6 +34,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.YaverDiscovery = void 0;
|
|
37
|
+
function unrefTimer(timer) {
|
|
38
|
+
const maybeNodeTimer = timer;
|
|
39
|
+
if (typeof maybeNodeTimer.unref === 'function') {
|
|
40
|
+
maybeNodeTimer.unref();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
37
43
|
function getAsyncStorage() {
|
|
38
44
|
try {
|
|
39
45
|
const mod = require('@react-native-async-storage/async-storage');
|
|
@@ -333,14 +339,15 @@ class YaverDiscovery {
|
|
|
333
339
|
static async probe(url) {
|
|
334
340
|
const base = url.replace(/\/$/, '');
|
|
335
341
|
const start = Date.now();
|
|
342
|
+
let timeoutId = null;
|
|
336
343
|
try {
|
|
337
344
|
const controller = new AbortController();
|
|
338
|
-
|
|
345
|
+
timeoutId = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
|
|
346
|
+
unrefTimer(timeoutId);
|
|
339
347
|
const response = await fetch(`${base}/health`, {
|
|
340
348
|
method: 'GET',
|
|
341
349
|
signal: controller.signal,
|
|
342
350
|
});
|
|
343
|
-
clearTimeout(timeoutId);
|
|
344
351
|
if (!response.ok)
|
|
345
352
|
return null;
|
|
346
353
|
const latency = Date.now() - start;
|
|
@@ -359,6 +366,10 @@ class YaverDiscovery {
|
|
|
359
366
|
catch {
|
|
360
367
|
return null;
|
|
361
368
|
}
|
|
369
|
+
finally {
|
|
370
|
+
if (timeoutId)
|
|
371
|
+
clearTimeout(timeoutId);
|
|
372
|
+
}
|
|
362
373
|
}
|
|
363
374
|
static async connect(url) {
|
|
364
375
|
const result = await YaverDiscovery.probe(url);
|