yaver-feedback-react-native 0.9.7 → 0.9.8
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 +39 -7
- package/android/src/main/java/io/yaver/feedback/YaverDogfoodGestureModule.java +241 -0
- package/android/src/main/java/io/yaver/feedback/YaverHotReloadPackage.java +1 -0
- package/app.plugin.js +12 -2
- package/dist/AuthOverlay.js +13 -2
- package/dist/DogfoodQuickControls.d.ts +8 -0
- package/dist/DogfoodQuickControls.js +394 -0
- package/dist/DogfoodSessionUi.d.ts +50 -0
- package/dist/DogfoodSessionUi.js +135 -0
- package/dist/FeedbackModal.js +50 -22
- package/dist/YaverFeedback.d.ts +35 -1
- package/dist/YaverFeedback.js +302 -12
- package/dist/__tests__/NativeDogfoodShortcut.test.js +14 -0
- package/dist/__tests__/YaverFeedback.test.js +148 -0
- package/dist/auth.d.ts +15 -0
- package/dist/auth.js +41 -2
- package/dist/dogfoodPolicy.d.ts +22 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +8 -2
- package/dist/preferences.d.ts +13 -0
- package/dist/preferences.js +84 -0
- package/ios/YaverDogfoodGesture.m +14 -0
- package/ios/YaverDogfoodGesture.swift +175 -0
- package/package.json +1 -1
- package/src/AuthOverlay.tsx +20 -3
- package/src/DogfoodQuickControls.tsx +454 -0
- package/src/DogfoodSessionUi.tsx +213 -0
- package/src/FeedbackModal.tsx +62 -34
- package/src/YaverFeedback.ts +333 -12
- package/src/__tests__/NativeDogfoodShortcut.test.ts +15 -0
- package/src/__tests__/YaverFeedback.test.ts +155 -0
- package/src/auth.ts +53 -2
- package/src/dogfoodPolicy.ts +22 -1
- package/src/index.ts +13 -1
- package/src/preferences.ts +89 -0
package/README.md
CHANGED
|
@@ -113,8 +113,9 @@ still require the user's normal Yaver authentication.
|
|
|
113
113
|
|
|
114
114
|
### Device-enrolled Dogfood for apps without their own OAuth/backend
|
|
115
115
|
|
|
116
|
-
Mount `FeedbackModal` once and configure the ACL-backed dynamic app shortcut
|
|
117
|
-
|
|
116
|
+
Mount `FeedbackModal` once and configure the ACL-backed dynamic app shortcut
|
|
117
|
+
plus smart quick controls. Keep a native Settings/Profile row as the universal
|
|
118
|
+
enrollment and recovery route; no shake gesture is needed:
|
|
118
119
|
|
|
119
120
|
```tsx
|
|
120
121
|
YaverFeedback.init({
|
|
@@ -128,20 +129,51 @@ YaverFeedback.init({
|
|
|
128
129
|
label: 'Example',
|
|
129
130
|
framework: 'expo',
|
|
130
131
|
appShortcut: { label: 'Dogfood Example' },
|
|
132
|
+
controlGesture: {
|
|
133
|
+
durationMs: 900,
|
|
134
|
+
fallback: 'minimized-y',
|
|
135
|
+
},
|
|
131
136
|
// Optional host presentation ACL. Backend account + phone-key approval
|
|
132
137
|
// remains mandatory even when this returns true.
|
|
133
138
|
canShow: (access) => appUser.isAdmin && access.authorized,
|
|
134
139
|
},
|
|
135
140
|
});
|
|
141
|
+
|
|
142
|
+
// The host owns only placement of the entry point. The SDK owns OAuth,
|
|
143
|
+
// continuation, machine/project/runner/model/lane selection and live logs.
|
|
144
|
+
const onDogfoodPress = () => YaverFeedback.openDogfood();
|
|
136
145
|
```
|
|
137
146
|
|
|
138
147
|
For first-time setup, a signed-in user opens the app from Yaver's Dogfood app
|
|
139
148
|
catalog. The public deep link contains no token. The SDK runs stateful Yaver
|
|
140
149
|
OAuth, machine, coding runner/model, project, and Browser/Hermes/WebRTC setup.
|
|
150
|
+
OAuth returns directly to that setup in the currently running app; it does not
|
|
151
|
+
restart the host. A valid cached Yaver session and prior selections skip steps
|
|
152
|
+
that are already satisfied. Expo and React Native projects default to the
|
|
153
|
+
Browser lane, while every selection remains changeable from Session setup.
|
|
141
154
|
After the owner approves that exact account + app ID + phone key, iOS and
|
|
142
155
|
Android add the long-press shortcut. Signing out, revocation, or host ACL denial
|
|
143
156
|
removes it on the next foreground sync.
|
|
144
157
|
|
|
158
|
+
Every newly authorized standalone installation starts with a partially
|
|
159
|
+
edge-docked, fading Y, even when the phone supports three-finger input. Its
|
|
160
|
+
one-time `Dogfood ready` card explains the entry point and records
|
|
161
|
+
`controlOnboardingSeenAt` in Convex for the exact Yaver user + app +
|
|
162
|
+
installation. After Continue, Settings in the compact card can switch between
|
|
163
|
+
`Always show Y` and a three-finger hold; that preference is stored in the same
|
|
164
|
+
Convex row and cached locally for cold start. Settings also reopens the session
|
|
165
|
+
setup for machine, coding runner/model, and Browser/Hermes/WebRTC lane changes.
|
|
166
|
+
|
|
167
|
+
In three-finger mode, a hold opens one compact card whose primary actions are
|
|
168
|
+
exactly `Fast Reload` and `Chat`; nothing is rendered while the card is closed.
|
|
169
|
+
The native observer does not consume ordinary app touches. If the native module
|
|
170
|
+
is absent, the iOS Simulator cannot supply genuine three-finger input, or
|
|
171
|
+
VoiceOver/TalkBack owns multi-touch, the SDK keeps the draggable Y and omits the
|
|
172
|
+
irrelevant mode preference. Inside Yaver's own
|
|
173
|
+
container both are suppressed: the host already renders preview/lane controls
|
|
174
|
+
beside its chat/vibing surface. Tablets follow the same host-vs-standalone rule
|
|
175
|
+
as phones; viewport size never decides ownership.
|
|
176
|
+
|
|
145
177
|
The app creates a random installation ID and Ed25519 key inside SecureStore.
|
|
146
178
|
The ID is a public handle, not a credential: enrollment and each short session
|
|
147
179
|
require a server challenge signed by the private key. An owner approves,
|
|
@@ -753,11 +785,11 @@ YaverFeedback.init({
|
|
|
753
785
|
|
|
754
786
|
When a third-party app is loaded through Yaver's Hermes-push flow (so the
|
|
755
787
|
Yaver mobile app is the runtime container for your app), the SDK detects
|
|
756
|
-
that situation via the `YaverInfo` native module and
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
788
|
+
that situation via the `YaverInfo` native module and suppresses guest-owned
|
|
789
|
+
triggers and overlays: no three-finger listener, minimized Y, shake detector,
|
|
790
|
+
or duplicate quick controls. Yaver owns the workspace, with the app preview
|
|
791
|
+
(Browser/WebRTC/Hermes lane) beside the chat/vibing surface on wider screens
|
|
792
|
+
and in a responsive stacked layout on phones.
|
|
761
793
|
|
|
762
794
|
Standalone installs (TestFlight / App Store / Play from your own dev
|
|
763
795
|
account) are unaffected — the SDK behaves exactly as it did before.
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
package io.yaver.feedback;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.content.Context;
|
|
5
|
+
import android.os.Handler;
|
|
6
|
+
import android.os.Looper;
|
|
7
|
+
import android.view.HapticFeedbackConstants;
|
|
8
|
+
import android.view.MotionEvent;
|
|
9
|
+
import android.view.View;
|
|
10
|
+
import android.view.ViewConfiguration;
|
|
11
|
+
import android.view.Window;
|
|
12
|
+
import android.view.accessibility.AccessibilityManager;
|
|
13
|
+
|
|
14
|
+
import androidx.annotation.NonNull;
|
|
15
|
+
|
|
16
|
+
import com.facebook.react.bridge.Arguments;
|
|
17
|
+
import com.facebook.react.bridge.LifecycleEventListener;
|
|
18
|
+
import com.facebook.react.bridge.Promise;
|
|
19
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
20
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
21
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
22
|
+
import com.facebook.react.bridge.WritableMap;
|
|
23
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
24
|
+
|
|
25
|
+
import java.lang.reflect.InvocationTargetException;
|
|
26
|
+
import java.lang.reflect.Proxy;
|
|
27
|
+
import java.util.HashMap;
|
|
28
|
+
import java.util.Map;
|
|
29
|
+
|
|
30
|
+
/** Passive three-finger hold observer for the Dogfood two-action card. */
|
|
31
|
+
public final class YaverDogfoodGestureModule extends ReactContextBaseJavaModule
|
|
32
|
+
implements LifecycleEventListener,
|
|
33
|
+
AccessibilityManager.TouchExplorationStateChangeListener {
|
|
34
|
+
|
|
35
|
+
private static final String MODULE_NAME = "YaverDogfoodGesture";
|
|
36
|
+
private static final String TRIGGER_EVENT = "yaverDogfoodControlGesture";
|
|
37
|
+
private static final String CAPABILITY_EVENT = "yaverDogfoodControlCapability";
|
|
38
|
+
|
|
39
|
+
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
40
|
+
private final AccessibilityManager accessibilityManager;
|
|
41
|
+
private final Map<Integer, float[]> initialPoints = new HashMap<>();
|
|
42
|
+
private boolean requestedEnabled = false;
|
|
43
|
+
private long durationMs = 900;
|
|
44
|
+
private boolean fired = false;
|
|
45
|
+
private float touchSlop = 16f;
|
|
46
|
+
private Activity attachedActivity;
|
|
47
|
+
private Window attachedWindow;
|
|
48
|
+
private Window.Callback originalCallback;
|
|
49
|
+
private Window.Callback observingCallback;
|
|
50
|
+
|
|
51
|
+
private final Runnable fireGesture = () -> {
|
|
52
|
+
if (!requestedEnabled || fired || initialPoints.size() < 3 || accessibilityConflict()) return;
|
|
53
|
+
fired = true;
|
|
54
|
+
if (attachedWindow != null) {
|
|
55
|
+
View decor = attachedWindow.getDecorView();
|
|
56
|
+
decor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
|
57
|
+
}
|
|
58
|
+
WritableMap event = Arguments.createMap();
|
|
59
|
+
event.putString("source", "three-finger-hold");
|
|
60
|
+
emit(TRIGGER_EVENT, event);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
public YaverDogfoodGestureModule(ReactApplicationContext context) {
|
|
64
|
+
super(context);
|
|
65
|
+
accessibilityManager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
|
66
|
+
context.addLifecycleEventListener(this);
|
|
67
|
+
if (accessibilityManager != null) {
|
|
68
|
+
accessibilityManager.addTouchExplorationStateChangeListener(this);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@Override
|
|
73
|
+
@NonNull
|
|
74
|
+
public String getName() {
|
|
75
|
+
return MODULE_NAME;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@ReactMethod
|
|
79
|
+
public void getCapability(Promise promise) {
|
|
80
|
+
mainHandler.post(() -> promise.resolve(status()));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@ReactMethod
|
|
84
|
+
public void setEnabled(boolean enabled, double requestedDurationMs, Promise promise) {
|
|
85
|
+
mainHandler.post(() -> {
|
|
86
|
+
requestedEnabled = enabled;
|
|
87
|
+
durationMs = Math.max(650L, Math.min(2000L, Math.round(requestedDurationMs)));
|
|
88
|
+
reconcile();
|
|
89
|
+
promise.resolve(status());
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private boolean accessibilityConflict() {
|
|
94
|
+
return accessibilityManager != null && accessibilityManager.isTouchExplorationEnabled();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private WritableMap status() {
|
|
98
|
+
WritableMap result = Arguments.createMap();
|
|
99
|
+
boolean supported = !accessibilityConflict();
|
|
100
|
+
result.putBoolean("supported", supported);
|
|
101
|
+
result.putBoolean("enabled", requestedEnabled && supported && observingCallback != null);
|
|
102
|
+
result.putString("reason", !supported
|
|
103
|
+
? "accessibility-touch-exploration"
|
|
104
|
+
: (getCurrentActivity() == null ? "window-unavailable" : "supported"));
|
|
105
|
+
result.putString("platform", "android");
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private void reconcile() {
|
|
110
|
+
Activity current = getCurrentActivity();
|
|
111
|
+
if (!requestedEnabled || accessibilityConflict() || current == null) {
|
|
112
|
+
detach();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
Window window = current.getWindow();
|
|
116
|
+
if (attachedActivity == current && attachedWindow == window
|
|
117
|
+
&& window.getCallback() == observingCallback) return;
|
|
118
|
+
detach();
|
|
119
|
+
Window.Callback callback = window.getCallback();
|
|
120
|
+
if (callback == null) return;
|
|
121
|
+
touchSlop = ViewConfiguration.get(current).getScaledTouchSlop();
|
|
122
|
+
Window.Callback proxy = (Window.Callback) Proxy.newProxyInstance(
|
|
123
|
+
YaverDogfoodGestureModule.class.getClassLoader(),
|
|
124
|
+
new Class<?>[]{Window.Callback.class},
|
|
125
|
+
(ignored, method, args) -> {
|
|
126
|
+
if ("dispatchTouchEvent".equals(method.getName())
|
|
127
|
+
&& args != null && args.length == 1 && args[0] instanceof MotionEvent) {
|
|
128
|
+
observe((MotionEvent) args[0]);
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
return method.invoke(callback, args);
|
|
132
|
+
} catch (InvocationTargetException error) {
|
|
133
|
+
throw error.getCause();
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
attachedActivity = current;
|
|
137
|
+
attachedWindow = window;
|
|
138
|
+
originalCallback = callback;
|
|
139
|
+
observingCallback = proxy;
|
|
140
|
+
window.setCallback(proxy);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private void detach() {
|
|
144
|
+
cancelGesture();
|
|
145
|
+
if (attachedWindow != null && attachedWindow.getCallback() == observingCallback
|
|
146
|
+
&& originalCallback != null) {
|
|
147
|
+
attachedWindow.setCallback(originalCallback);
|
|
148
|
+
}
|
|
149
|
+
attachedActivity = null;
|
|
150
|
+
attachedWindow = null;
|
|
151
|
+
originalCallback = null;
|
|
152
|
+
observingCallback = null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Observe without consuming: the original Window.Callback always receives the event. */
|
|
156
|
+
private void observe(MotionEvent event) {
|
|
157
|
+
if (!requestedEnabled || accessibilityConflict()) return;
|
|
158
|
+
switch (event.getActionMasked()) {
|
|
159
|
+
case MotionEvent.ACTION_DOWN:
|
|
160
|
+
cancelGesture();
|
|
161
|
+
rememberPointers(event);
|
|
162
|
+
break;
|
|
163
|
+
case MotionEvent.ACTION_POINTER_DOWN:
|
|
164
|
+
rememberPointers(event);
|
|
165
|
+
if (event.getPointerCount() >= 3 && !fired) {
|
|
166
|
+
mainHandler.removeCallbacks(fireGesture);
|
|
167
|
+
mainHandler.postDelayed(fireGesture, durationMs);
|
|
168
|
+
}
|
|
169
|
+
break;
|
|
170
|
+
case MotionEvent.ACTION_MOVE:
|
|
171
|
+
if (movedTooFar(event)) cancelGesture();
|
|
172
|
+
break;
|
|
173
|
+
case MotionEvent.ACTION_POINTER_UP:
|
|
174
|
+
case MotionEvent.ACTION_UP:
|
|
175
|
+
case MotionEvent.ACTION_CANCEL:
|
|
176
|
+
cancelGesture();
|
|
177
|
+
break;
|
|
178
|
+
default:
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private void rememberPointers(MotionEvent event) {
|
|
184
|
+
for (int index = 0; index < event.getPointerCount(); index++) {
|
|
185
|
+
int id = event.getPointerId(index);
|
|
186
|
+
if (!initialPoints.containsKey(id)) {
|
|
187
|
+
initialPoints.put(id, new float[]{event.getX(index), event.getY(index)});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private boolean movedTooFar(MotionEvent event) {
|
|
193
|
+
for (int index = 0; index < event.getPointerCount(); index++) {
|
|
194
|
+
float[] start = initialPoints.get(event.getPointerId(index));
|
|
195
|
+
if (start == null) continue;
|
|
196
|
+
if (Math.abs(event.getX(index) - start[0]) > touchSlop
|
|
197
|
+
|| Math.abs(event.getY(index) - start[1]) > touchSlop) return true;
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private void cancelGesture() {
|
|
203
|
+
mainHandler.removeCallbacks(fireGesture);
|
|
204
|
+
initialPoints.clear();
|
|
205
|
+
fired = false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private void emit(String name, WritableMap body) {
|
|
209
|
+
try {
|
|
210
|
+
getReactApplicationContext()
|
|
211
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
212
|
+
.emit(name, body);
|
|
213
|
+
} catch (RuntimeException ignored) {
|
|
214
|
+
// No JS bridge/listener yet; a trigger must never break the host app.
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private void emitCapability() {
|
|
219
|
+
emit(CAPABILITY_EVENT, status());
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
@Override
|
|
223
|
+
public void onHostResume() {
|
|
224
|
+
mainHandler.post(() -> {
|
|
225
|
+
reconcile();
|
|
226
|
+
emitCapability();
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
@Override public void onHostPause() { cancelGesture(); }
|
|
231
|
+
|
|
232
|
+
@Override public void onHostDestroy() { detach(); }
|
|
233
|
+
|
|
234
|
+
@Override
|
|
235
|
+
public void onTouchExplorationStateChanged(boolean enabled) {
|
|
236
|
+
mainHandler.post(() -> {
|
|
237
|
+
reconcile();
|
|
238
|
+
emitCapability();
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -22,6 +22,7 @@ public class YaverHotReloadPackage implements ReactPackage {
|
|
|
22
22
|
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
|
|
23
23
|
List<NativeModule> modules = new ArrayList<>();
|
|
24
24
|
modules.add(new YaverHotReloadModule(reactContext));
|
|
25
|
+
modules.add(new YaverDogfoodGestureModule(reactContext));
|
|
25
26
|
return modules;
|
|
26
27
|
}
|
|
27
28
|
|
package/app.plugin.js
CHANGED
|
@@ -137,7 +137,12 @@ function withYaverHotReloadNativeModule(config) {
|
|
|
137
137
|
appName
|
|
138
138
|
);
|
|
139
139
|
|
|
140
|
-
const filesToCopy = [
|
|
140
|
+
const filesToCopy = [
|
|
141
|
+
"YaverHotReload.swift",
|
|
142
|
+
"YaverHotReload.m",
|
|
143
|
+
"YaverDogfoodGesture.swift",
|
|
144
|
+
"YaverDogfoodGesture.m",
|
|
145
|
+
];
|
|
141
146
|
for (const fileName of filesToCopy) {
|
|
142
147
|
const src = path.join(sdkIosDir, fileName);
|
|
143
148
|
const dst = path.join(targetDir, fileName);
|
|
@@ -175,7 +180,12 @@ function withYaverHotReloadNativeModule(config) {
|
|
|
175
180
|
const target = proj.getFirstTarget();
|
|
176
181
|
if (!target || !target.uuid) return config;
|
|
177
182
|
|
|
178
|
-
const filesToAdd = [
|
|
183
|
+
const filesToAdd = [
|
|
184
|
+
"YaverHotReload.swift",
|
|
185
|
+
"YaverHotReload.m",
|
|
186
|
+
"YaverDogfoodGesture.swift",
|
|
187
|
+
"YaverDogfoodGesture.m",
|
|
188
|
+
];
|
|
179
189
|
for (const fileName of filesToAdd) {
|
|
180
190
|
const relPath = `${appName}/${fileName}`;
|
|
181
191
|
// addSourceFile registers PBXFileReference + PBXBuildFile, adds
|
package/dist/AuthOverlay.js
CHANGED
|
@@ -61,6 +61,17 @@ const AuthOverlay = () => {
|
|
|
61
61
|
setLoginVisible(false);
|
|
62
62
|
setPickerVisible(false);
|
|
63
63
|
}, []);
|
|
64
|
+
const continueDogfood = (0, react_1.useCallback)(async () => {
|
|
65
|
+
try {
|
|
66
|
+
const state = await YaverFeedback_1.YaverFeedback.continueDogfoodOnboarding();
|
|
67
|
+
if (state.phase === 'denied' || state.phase === 'error') {
|
|
68
|
+
react_native_1.Alert.alert('Dogfood unavailable', state.error || 'This Yaver account or device is not enabled for this app.');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (cause) {
|
|
72
|
+
react_native_1.Alert.alert('Dogfood unavailable', cause instanceof Error ? cause.message : 'Yaver could not continue Dogfood setup.');
|
|
73
|
+
}
|
|
74
|
+
}, []);
|
|
64
75
|
(0, react_1.useEffect)(() => {
|
|
65
76
|
let mounted = true;
|
|
66
77
|
void (0, auth_1.getToken)().then((cached) => {
|
|
@@ -91,7 +102,7 @@ const AuthOverlay = () => {
|
|
|
91
102
|
await YaverFeedback_1.YaverFeedback.setAuthToken(newToken);
|
|
92
103
|
if (YaverFeedback_1.YaverFeedback.getDogfoodOnboarding()) {
|
|
93
104
|
closeAll();
|
|
94
|
-
await
|
|
105
|
+
await continueDogfood();
|
|
95
106
|
}
|
|
96
107
|
else {
|
|
97
108
|
openPicker();
|
|
@@ -101,7 +112,7 @@ const AuthOverlay = () => {
|
|
|
101
112
|
await YaverFeedback_1.YaverFeedback.setPreferredDevice(device.deviceId);
|
|
102
113
|
closeAll();
|
|
103
114
|
if (YaverFeedback_1.YaverFeedback.getDogfoodOnboarding()) {
|
|
104
|
-
await
|
|
115
|
+
await continueDogfood();
|
|
105
116
|
}
|
|
106
117
|
else {
|
|
107
118
|
react_native_1.DeviceEventEmitter.emit('yaverFeedback:startReport');
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* The standalone SDK's only persistent chrome. A newly authorized tester sees
|
|
4
|
+
* this edge-docked Y until first-run onboarding is completed in Convex. After
|
|
5
|
+
* that, capable devices default to a passive three-finger hold; unsupported
|
|
6
|
+
* devices keep the Y. Both entry points open exactly the same compact card.
|
|
7
|
+
*/
|
|
8
|
+
export declare const DogfoodQuickControls: React.FC;
|