yaver-feedback-react-native 0.9.7 → 0.9.9
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 +401 -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 +312 -12
- package/dist/__tests__/NativeDogfoodShortcut.test.js +14 -0
- package/dist/__tests__/YaverFeedback.test.js +173 -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 +461 -0
- package/src/DogfoodSessionUi.tsx +213 -0
- package/src/FeedbackModal.tsx +62 -34
- package/src/YaverFeedback.ts +343 -12
- package/src/__tests__/NativeDogfoodShortcut.test.ts +15 -0
- package/src/__tests__/YaverFeedback.test.ts +183 -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
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const react_native_1 = require("react-native");
|
|
4
4
|
const YaverFeedback_1 = require("../YaverFeedback");
|
|
5
|
+
const auth_1 = require("../auth");
|
|
5
6
|
// Mock react-native: DeviceEventEmitter for event dispatch + Platform so
|
|
6
7
|
// ShakeDetector.start() can branch on iOS without hitting a real RN runtime.
|
|
7
8
|
jest.mock('react-native', () => ({
|
|
@@ -15,6 +16,10 @@ jest.mock('react-native', () => ({
|
|
|
15
16
|
setDogfoodShortcut: jest.fn(async () => true),
|
|
16
17
|
consumeDogfoodShortcut: jest.fn(async () => false),
|
|
17
18
|
},
|
|
19
|
+
YaverDogfoodGesture: {
|
|
20
|
+
getCapability: jest.fn(async () => ({ supported: true, enabled: false, reason: 'supported', platform: 'ios' })),
|
|
21
|
+
setEnabled: jest.fn(async (next) => ({ supported: true, enabled: next, reason: 'supported', platform: 'ios' })),
|
|
22
|
+
},
|
|
18
23
|
},
|
|
19
24
|
AppState: { addEventListener: jest.fn(() => ({ remove: jest.fn() })) },
|
|
20
25
|
}));
|
|
@@ -32,8 +37,10 @@ jest.mock('../auth', () => ({
|
|
|
32
37
|
getDogfoodAccountAccess: jest.fn(async (_appId, token) => ({
|
|
33
38
|
authenticated: token === 'owner-token',
|
|
34
39
|
ownerAuthorized: token === 'owner-token',
|
|
40
|
+
accountAuthorized: token === 'owner-token',
|
|
35
41
|
installationAuthorized: token === 'owner-token',
|
|
36
42
|
})),
|
|
43
|
+
setDogfoodControlPreference: jest.fn(async () => true),
|
|
37
44
|
saveSelectedDeviceId: jest.fn(async () => { }),
|
|
38
45
|
clearToken: jest.fn(async () => { }),
|
|
39
46
|
clearSelectedDeviceId: jest.fn(async () => { }),
|
|
@@ -62,6 +69,25 @@ beforeEach(() => {
|
|
|
62
69
|
jest.restoreAllMocks();
|
|
63
70
|
jest.clearAllMocks();
|
|
64
71
|
});
|
|
72
|
+
function activeDogfood(controlGesture = true) {
|
|
73
|
+
return {
|
|
74
|
+
enabled: true,
|
|
75
|
+
appId: 'io.example.app',
|
|
76
|
+
installationId: 'phone-1',
|
|
77
|
+
installationStatus: 'active',
|
|
78
|
+
controlGesture,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function initActiveDogfood(options = {}) {
|
|
82
|
+
const controlGesture = options.controlGesture ?? true;
|
|
83
|
+
YaverFeedback_1.YaverFeedback.init({
|
|
84
|
+
authToken: options.authToken,
|
|
85
|
+
bundleId: 'io.example.app',
|
|
86
|
+
dogfood: { controlGesture },
|
|
87
|
+
});
|
|
88
|
+
Object.assign(YaverFeedback_1.YaverFeedback.getConfig().dogfood, activeDogfood(controlGesture));
|
|
89
|
+
jest.clearAllMocks();
|
|
90
|
+
}
|
|
65
91
|
describe('YaverFeedback', () => {
|
|
66
92
|
describe('Dogfood onboarding', () => {
|
|
67
93
|
it('starts with Yaver OAuth when the host has no session', async () => {
|
|
@@ -96,6 +122,17 @@ describe('YaverFeedback', () => {
|
|
|
96
122
|
unsubscribe();
|
|
97
123
|
expect(states[states.length - 1]).toBe('auth-required');
|
|
98
124
|
});
|
|
125
|
+
it('returns a visible structured error when access verification fails', async () => {
|
|
126
|
+
YaverFeedback_1.YaverFeedback.init({ enabled: true, authToken: 'owner-token' });
|
|
127
|
+
auth_1.getDogfoodAccountAccess.mockRejectedValueOnce(new Error('Access service unavailable'));
|
|
128
|
+
const state = await YaverFeedback_1.YaverFeedback.beginDogfoodOnboarding({ appId: 'io.example.app' });
|
|
129
|
+
expect(state).toEqual({
|
|
130
|
+
phase: 'error',
|
|
131
|
+
appId: 'io.example.app',
|
|
132
|
+
error: 'Access service unavailable',
|
|
133
|
+
});
|
|
134
|
+
expect(react_native_1.DeviceEventEmitter.emit).not.toHaveBeenCalledWith('yaverFeedback:startReport');
|
|
135
|
+
});
|
|
99
136
|
it('lets a host ACL hide its affordance without opening auth UI', async () => {
|
|
100
137
|
YaverFeedback_1.YaverFeedback.init({
|
|
101
138
|
enabled: true,
|
|
@@ -113,6 +150,7 @@ describe('YaverFeedback', () => {
|
|
|
113
150
|
appId: 'io.example.app',
|
|
114
151
|
yaverAuthenticated: true,
|
|
115
152
|
ownerAuthorized: false,
|
|
153
|
+
accountAuthorized: true,
|
|
116
154
|
installationId: 'phone-1',
|
|
117
155
|
deviceState: 'active',
|
|
118
156
|
authorized: true,
|
|
@@ -128,6 +166,7 @@ describe('YaverFeedback', () => {
|
|
|
128
166
|
appId: 'io.example.app',
|
|
129
167
|
yaverAuthenticated: false,
|
|
130
168
|
ownerAuthorized: false,
|
|
169
|
+
accountAuthorized: true,
|
|
131
170
|
installationId: 'phone-1',
|
|
132
171
|
deviceState: 'active',
|
|
133
172
|
authorized: false,
|
|
@@ -145,6 +184,7 @@ describe('YaverFeedback', () => {
|
|
|
145
184
|
appId: 'io.example.app',
|
|
146
185
|
yaverAuthenticated: true,
|
|
147
186
|
ownerAuthorized: false,
|
|
187
|
+
accountAuthorized: true,
|
|
148
188
|
installationId: 'phone-1',
|
|
149
189
|
deviceState: 'active',
|
|
150
190
|
authorized: true,
|
|
@@ -153,6 +193,139 @@ describe('YaverFeedback', () => {
|
|
|
153
193
|
expect(react_native_1.NativeModules.YaverHotReload.setDogfoodShortcut)
|
|
154
194
|
.toHaveBeenCalledWith(false, 'Dogfood');
|
|
155
195
|
});
|
|
196
|
+
it('keeps the Y visible until the authorized phone has completed onboarding', async () => {
|
|
197
|
+
initActiveDogfood();
|
|
198
|
+
jest.spyOn(YaverFeedback_1.YaverFeedback, 'getDogfoodAccess').mockResolvedValueOnce({
|
|
199
|
+
appId: 'io.example.app',
|
|
200
|
+
yaverAuthenticated: true,
|
|
201
|
+
ownerAuthorized: false,
|
|
202
|
+
accountAuthorized: true,
|
|
203
|
+
installationId: 'phone-1',
|
|
204
|
+
deviceState: 'active',
|
|
205
|
+
authorized: true,
|
|
206
|
+
});
|
|
207
|
+
const state = await YaverFeedback_1.YaverFeedback.syncDogfoodControlGesture();
|
|
208
|
+
expect(state).toMatchObject({
|
|
209
|
+
onboardingSeen: false,
|
|
210
|
+
presentation: 'minimized-y',
|
|
211
|
+
gestureSupported: true,
|
|
212
|
+
gestureEnabled: false,
|
|
213
|
+
fallbackVisible: true,
|
|
214
|
+
});
|
|
215
|
+
expect(react_native_1.NativeModules.YaverDogfoodGesture.setEnabled).toHaveBeenCalledWith(false, 900);
|
|
216
|
+
});
|
|
217
|
+
it('uses the invisible three-finger hold only after onboarding selects it', async () => {
|
|
218
|
+
initActiveDogfood();
|
|
219
|
+
jest.spyOn(YaverFeedback_1.YaverFeedback, 'getDogfoodAccess').mockResolvedValueOnce({
|
|
220
|
+
appId: 'io.example.app',
|
|
221
|
+
yaverAuthenticated: true,
|
|
222
|
+
ownerAuthorized: false,
|
|
223
|
+
accountAuthorized: true,
|
|
224
|
+
installationId: 'phone-1',
|
|
225
|
+
deviceState: 'active',
|
|
226
|
+
authorized: true,
|
|
227
|
+
controlPresentation: 'auto',
|
|
228
|
+
controlOnboardingSeen: true,
|
|
229
|
+
});
|
|
230
|
+
const state = await YaverFeedback_1.YaverFeedback.syncDogfoodControlGesture();
|
|
231
|
+
expect(state).toMatchObject({
|
|
232
|
+
onboardingSeen: true,
|
|
233
|
+
presentation: 'auto',
|
|
234
|
+
gestureSupported: true,
|
|
235
|
+
gestureEnabled: true,
|
|
236
|
+
fallbackVisible: false,
|
|
237
|
+
});
|
|
238
|
+
expect(react_native_1.NativeModules.YaverDogfoodGesture.setEnabled).toHaveBeenCalledWith(true, 900);
|
|
239
|
+
});
|
|
240
|
+
it('keeps the Y when native capability reports support but enabling the gesture fails', async () => {
|
|
241
|
+
initActiveDogfood();
|
|
242
|
+
jest.spyOn(YaverFeedback_1.YaverFeedback, 'getDogfoodAccess').mockResolvedValueOnce({
|
|
243
|
+
appId: 'io.example.app',
|
|
244
|
+
yaverAuthenticated: true,
|
|
245
|
+
ownerAuthorized: false,
|
|
246
|
+
accountAuthorized: true,
|
|
247
|
+
installationId: 'phone-1',
|
|
248
|
+
deviceState: 'active',
|
|
249
|
+
authorized: true,
|
|
250
|
+
controlPresentation: 'auto',
|
|
251
|
+
controlOnboardingSeen: true,
|
|
252
|
+
});
|
|
253
|
+
react_native_1.NativeModules.YaverDogfoodGesture.setEnabled.mockResolvedValueOnce({
|
|
254
|
+
supported: true,
|
|
255
|
+
enabled: false,
|
|
256
|
+
reason: 'supported',
|
|
257
|
+
platform: 'ios',
|
|
258
|
+
});
|
|
259
|
+
const state = await YaverFeedback_1.YaverFeedback.syncDogfoodControlGesture();
|
|
260
|
+
expect(state).toMatchObject({
|
|
261
|
+
gestureSupported: true,
|
|
262
|
+
gestureEnabled: false,
|
|
263
|
+
fallbackVisible: true,
|
|
264
|
+
reason: 'gesture-enable-failed',
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
it('persists first-run completion for the exact account app installation', async () => {
|
|
268
|
+
initActiveDogfood({ authToken: 'owner-token' });
|
|
269
|
+
jest.spyOn(YaverFeedback_1.YaverFeedback, 'getDogfoodAccess').mockResolvedValueOnce({
|
|
270
|
+
appId: 'io.example.app',
|
|
271
|
+
yaverAuthenticated: true,
|
|
272
|
+
ownerAuthorized: true,
|
|
273
|
+
accountAuthorized: true,
|
|
274
|
+
installationId: 'phone-1',
|
|
275
|
+
deviceState: 'active',
|
|
276
|
+
authorized: true,
|
|
277
|
+
controlOnboardingSeen: false,
|
|
278
|
+
});
|
|
279
|
+
const state = await YaverFeedback_1.YaverFeedback.setDogfoodControlPresentation('minimized-y');
|
|
280
|
+
expect(state).toMatchObject({ onboardingSeen: true, presentation: 'minimized-y', fallbackVisible: true });
|
|
281
|
+
expect(auth_1.setDogfoodControlPreference).toHaveBeenCalledWith(expect.objectContaining({
|
|
282
|
+
appId: 'io.example.app',
|
|
283
|
+
installationId: 'phone-1',
|
|
284
|
+
token: 'owner-token',
|
|
285
|
+
presentation: 'minimized-y',
|
|
286
|
+
controlOnboardingSeen: true,
|
|
287
|
+
}));
|
|
288
|
+
});
|
|
289
|
+
it('falls back to the minimized Y when accessibility owns multi-touch', async () => {
|
|
290
|
+
initActiveDogfood({ controlGesture: { durationMs: 1200 } });
|
|
291
|
+
jest.spyOn(YaverFeedback_1.YaverFeedback, 'getDogfoodAccess').mockResolvedValueOnce({
|
|
292
|
+
appId: 'io.example.app',
|
|
293
|
+
yaverAuthenticated: true,
|
|
294
|
+
ownerAuthorized: false,
|
|
295
|
+
accountAuthorized: true,
|
|
296
|
+
installationId: 'phone-1',
|
|
297
|
+
deviceState: 'active',
|
|
298
|
+
authorized: true,
|
|
299
|
+
});
|
|
300
|
+
react_native_1.NativeModules.YaverDogfoodGesture.getCapability.mockResolvedValueOnce({
|
|
301
|
+
supported: false,
|
|
302
|
+
enabled: false,
|
|
303
|
+
reason: 'accessibility-touch-exploration',
|
|
304
|
+
platform: 'ios',
|
|
305
|
+
});
|
|
306
|
+
const state = await YaverFeedback_1.YaverFeedback.syncDogfoodControlGesture();
|
|
307
|
+
expect(state).toMatchObject({ gestureSupported: false, gestureEnabled: false, fallbackVisible: true });
|
|
308
|
+
expect(react_native_1.NativeModules.YaverDogfoodGesture.setEnabled).toHaveBeenCalledWith(false, 1200);
|
|
309
|
+
});
|
|
310
|
+
it('suppresses guest controls inside the Yaver split-view container', async () => {
|
|
311
|
+
initActiveDogfood();
|
|
312
|
+
react_native_1.NativeModules.YaverInfo = { isYaver: true };
|
|
313
|
+
const state = await YaverFeedback_1.YaverFeedback.syncDogfoodControlGesture();
|
|
314
|
+
delete react_native_1.NativeModules.YaverInfo;
|
|
315
|
+
expect(state).toMatchObject({ gestureEnabled: false, fallbackVisible: false, reason: 'yaver-host-owns-controls' });
|
|
316
|
+
});
|
|
317
|
+
it('removes the gesture and fallback Y when Dogfood exits', async () => {
|
|
318
|
+
initActiveDogfood();
|
|
319
|
+
await YaverFeedback_1.YaverFeedback.exitDogfoodMode();
|
|
320
|
+
const state = await YaverFeedback_1.YaverFeedback.syncDogfoodControlGesture();
|
|
321
|
+
expect(state).toMatchObject({
|
|
322
|
+
authorized: false,
|
|
323
|
+
gestureEnabled: false,
|
|
324
|
+
fallbackVisible: false,
|
|
325
|
+
reason: 'dogfood-session-inactive',
|
|
326
|
+
});
|
|
327
|
+
expect(react_native_1.NativeModules.YaverDogfoodGesture.setEnabled).toHaveBeenCalledWith(false, 900);
|
|
328
|
+
});
|
|
156
329
|
});
|
|
157
330
|
describe('init()', () => {
|
|
158
331
|
it('sets config correctly with defaults', () => {
|
package/dist/auth.d.ts
CHANGED
|
@@ -52,8 +52,23 @@ export declare function clearSelectedDeviceId(): Promise<void>;
|
|
|
52
52
|
export declare function getDogfoodAccountAccess(appId: string, token: string, installationId?: string): Promise<{
|
|
53
53
|
authenticated: boolean;
|
|
54
54
|
ownerAuthorized: boolean;
|
|
55
|
+
accountAuthorized: boolean;
|
|
55
56
|
installationAuthorized: boolean;
|
|
57
|
+
controlPresentation?: 'auto' | 'minimized-y';
|
|
58
|
+
gestureSupported?: boolean;
|
|
59
|
+
gestureCapabilityReason?: string;
|
|
60
|
+
controlOnboardingSeen?: boolean;
|
|
56
61
|
}>;
|
|
62
|
+
export declare function setDogfoodControlPreference(input: {
|
|
63
|
+
appId: string;
|
|
64
|
+
installationId: string;
|
|
65
|
+
token: string;
|
|
66
|
+
presentation: 'auto' | 'minimized-y';
|
|
67
|
+
gestureSupported: boolean;
|
|
68
|
+
gestureCapabilityReason: string;
|
|
69
|
+
gesturePlatform: string;
|
|
70
|
+
controlOnboardingSeen?: boolean;
|
|
71
|
+
}): Promise<boolean>;
|
|
57
72
|
export declare function validateToken(token: string): Promise<User | null>;
|
|
58
73
|
/**
|
|
59
74
|
* Sign in with Apple using the native ASAuthorization flow. Requires
|
package/dist/auth.js
CHANGED
|
@@ -32,6 +32,7 @@ exports.getSelectedDeviceId = getSelectedDeviceId;
|
|
|
32
32
|
exports.saveSelectedDeviceId = saveSelectedDeviceId;
|
|
33
33
|
exports.clearSelectedDeviceId = clearSelectedDeviceId;
|
|
34
34
|
exports.getDogfoodAccountAccess = getDogfoodAccountAccess;
|
|
35
|
+
exports.setDogfoodControlPreference = setDogfoodControlPreference;
|
|
35
36
|
exports.validateToken = validateToken;
|
|
36
37
|
exports.signInWithApple = signInWithApple;
|
|
37
38
|
exports.signInWithOAuth = signInWithOAuth;
|
|
@@ -202,16 +203,54 @@ async function getDogfoodAccountAccess(appId, token, installationId) {
|
|
|
202
203
|
});
|
|
203
204
|
clearTimeout(timeout);
|
|
204
205
|
if (!response.ok)
|
|
205
|
-
return { authenticated: false, ownerAuthorized: false, installationAuthorized: false };
|
|
206
|
+
return { authenticated: false, ownerAuthorized: false, accountAuthorized: false, installationAuthorized: false };
|
|
206
207
|
const result = await response.json();
|
|
207
208
|
return {
|
|
208
209
|
authenticated: result?.authenticated === true,
|
|
209
210
|
ownerAuthorized: result?.ownerAuthorized === true,
|
|
211
|
+
accountAuthorized: result?.accountAuthorized === true,
|
|
210
212
|
installationAuthorized: result?.installationAuthorized === true,
|
|
213
|
+
controlPresentation: result?.controlPresentation === 'minimized-y' ? 'minimized-y'
|
|
214
|
+
: result?.controlPresentation === 'auto' ? 'auto' : undefined,
|
|
215
|
+
gestureSupported: typeof result?.gestureSupported === 'boolean' ? result.gestureSupported : undefined,
|
|
216
|
+
gestureCapabilityReason: typeof result?.gestureCapabilityReason === 'string'
|
|
217
|
+
? result.gestureCapabilityReason
|
|
218
|
+
: undefined,
|
|
219
|
+
controlOnboardingSeen: result?.controlOnboardingSeen === true,
|
|
211
220
|
};
|
|
212
221
|
}
|
|
213
222
|
catch {
|
|
214
|
-
return { authenticated: false, ownerAuthorized: false, installationAuthorized: false };
|
|
223
|
+
return { authenticated: false, ownerAuthorized: false, accountAuthorized: false, installationAuthorized: false };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async function setDogfoodControlPreference(input) {
|
|
227
|
+
const controller = new AbortController();
|
|
228
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
229
|
+
try {
|
|
230
|
+
const response = await fetch(`${convexSiteUrl}/dogfood/control-preference`, {
|
|
231
|
+
method: 'POST',
|
|
232
|
+
headers: {
|
|
233
|
+
Authorization: `Bearer ${input.token}`,
|
|
234
|
+
'Content-Type': 'application/json',
|
|
235
|
+
},
|
|
236
|
+
body: JSON.stringify({
|
|
237
|
+
appId: input.appId,
|
|
238
|
+
installationId: input.installationId,
|
|
239
|
+
presentation: input.presentation,
|
|
240
|
+
gestureSupported: input.gestureSupported,
|
|
241
|
+
gestureCapabilityReason: input.gestureCapabilityReason,
|
|
242
|
+
gesturePlatform: input.gesturePlatform,
|
|
243
|
+
controlOnboardingSeen: input.controlOnboardingSeen === true,
|
|
244
|
+
}),
|
|
245
|
+
signal: controller.signal,
|
|
246
|
+
});
|
|
247
|
+
return response.ok;
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
clearTimeout(timeout);
|
|
215
254
|
}
|
|
216
255
|
}
|
|
217
256
|
// ─── Token validation ──────────────────────────────────────────────────
|
package/dist/dogfoodPolicy.d.ts
CHANGED
|
@@ -4,10 +4,18 @@ export interface DogfoodAccessSnapshot {
|
|
|
4
4
|
yaverAuthenticated: boolean;
|
|
5
5
|
/** Backend-confirmed owner/maintainer of this exact appId. */
|
|
6
6
|
ownerAuthorized: boolean;
|
|
7
|
+
/** Backend-confirmed app assignment for this Yaver account. App owners are
|
|
8
|
+
* assigned implicitly; other accounts require an active owner grant. */
|
|
9
|
+
accountAuthorized: boolean;
|
|
7
10
|
installationId?: string;
|
|
8
11
|
deviceState: 'unknown' | 'unregistered' | 'pending' | 'active' | 'cancelled' | 'revoked' | 'superseded';
|
|
9
|
-
/** True only for
|
|
12
|
+
/** True only for an assigned account and this backend-approved device key. */
|
|
10
13
|
authorized: boolean;
|
|
14
|
+
/** OAuth-backed preference scoped to this user + app + installation. */
|
|
15
|
+
controlPresentation?: 'auto' | 'minimized-y';
|
|
16
|
+
gestureSupported?: boolean;
|
|
17
|
+
gestureCapabilityReason?: string;
|
|
18
|
+
controlOnboardingSeen?: boolean;
|
|
11
19
|
}
|
|
12
20
|
export interface DogfoodFlowSnapshot {
|
|
13
21
|
phase: 'idle' | 'denied' | 'auth-required' | 'machine-required' | 'opening' | 'error';
|
|
@@ -44,6 +52,19 @@ export interface SDKDogfoodConfig {
|
|
|
44
52
|
appShortcut?: boolean | {
|
|
45
53
|
label?: string;
|
|
46
54
|
};
|
|
55
|
+
/** Smart in-app quick controls. On supported standalone iOS/Android builds,
|
|
56
|
+
* a three-finger hold opens a two-action card with no persistent overlay.
|
|
57
|
+
* Unsupported/accessibility-conflicted devices may show a minimized,
|
|
58
|
+
* draggable Y fallback. Yaver host/container mode suppresses both. */
|
|
59
|
+
controlGesture?: boolean | {
|
|
60
|
+
/** Hold duration, clamped to 650–2000 ms. Default 900 ms. */
|
|
61
|
+
durationMs?: number;
|
|
62
|
+
/** Default `minimized-y`; use `none` when Settings is the only fallback. */
|
|
63
|
+
fallback?: 'minimized-y' | 'none';
|
|
64
|
+
/** Initial user-facing presentation before their persisted SDK preference
|
|
65
|
+
* exists. `auto` keeps pixels clear when the gesture works. */
|
|
66
|
+
defaultPresentation?: 'auto' | 'minimized-y';
|
|
67
|
+
};
|
|
47
68
|
}
|
|
48
69
|
export interface SDKDogfoodStatus {
|
|
49
70
|
active: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* ```
|
|
29
29
|
*/
|
|
30
30
|
export { YaverFeedback } from './YaverFeedback';
|
|
31
|
-
export type { DogfoodOnboardingOptions, DogfoodFlowPhase, DogfoodFlowState } from './YaverFeedback';
|
|
31
|
+
export type { DogfoodOnboardingOptions, DogfoodFlowPhase, DogfoodFlowState, DogfoodControlTriggerState, } from './YaverFeedback';
|
|
32
32
|
export { captureStoreScreenshots } from './storeShots';
|
|
33
33
|
export type { CaptureStoreScreenshotsOptions, CaptureStoreScreenshotsResult, StoreShotFrame, } from './storeShots';
|
|
34
34
|
export { BlackBox } from './BlackBox';
|
|
@@ -59,7 +59,10 @@ export { YaverDeviceDogfood } from './deviceDogfood';
|
|
|
59
59
|
export type { DeviceDogfoodOptions, DeviceDogfoodSession, DeviceDogfoodState } from './deviceDogfood';
|
|
60
60
|
export { DogfoodController, DogfoodRuntimeError, defaultDogfoodLane, dogfoodLaneOptions, dogfoodLogLinesFromDevEvent, runtimeLogLinesFromDevEvent, validateDogfoodProject, } from './DogfoodRuntime';
|
|
61
61
|
export type { DogfoodControllerOptions, DogfoodDriver, DogfoodFailure, DogfoodLane, DogfoodLaneOption, DogfoodLogLine, DogfoodPhase, DogfoodProject, DogfoodResult, DogfoodRunContext, DogfoodSnapshot, } from './DogfoodRuntime';
|
|
62
|
+
export { DogfoodLanePicker, DogfoodLiveConsole, DogfoodStatusRail } from './DogfoodSessionUi';
|
|
63
|
+
export type { DogfoodStatusStep, DogfoodStatusTone, DogfoodUiColors, } from './DogfoodSessionUi';
|
|
62
64
|
export { FeedbackModal } from './FeedbackModal';
|
|
65
|
+
export { DogfoodQuickControls } from './DogfoodQuickControls';
|
|
63
66
|
export { QuickActionIcon } from './QuickActionIcon';
|
|
64
67
|
export type { QuickActionIconProps } from './QuickActionIcon';
|
|
65
68
|
export { FixReport } from './FixReport';
|
package/dist/index.js
CHANGED
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
exports.
|
|
33
|
-
exports.uploadFeedback = exports.isVideoRecording = exports.stopVideoRecording = exports.startVideoRecording = exports.pickFeedbackFile = exports.captureScreenshot = exports.DEFAULT_OAUTH_REDIRECT = exports.DEFAULT_WEB_BASE_URL = exports.DEFAULT_CONVEX_SITE_URL = exports.listReachableDevices = exports.loginWithEmail = exports.signupWithEmail = exports.signInWithOAuth = exports.signInWithApple = exports.validateToken = exports.clearSelectedDeviceId = exports.saveSelectedDeviceId = exports.getDogfoodAccountAccess = exports.getSelectedDeviceId = void 0;
|
|
32
|
+
exports.getToken = exports.getWebBaseUrl = exports.getConvexSiteUrl = exports.configureAuthEndpoints = exports.setPreferredDogfoodLane = exports.getPreferredDogfoodLane = exports.clearQuickIconDisabled = exports.setQuickIconDisabled = exports.getQuickIconDisabled = exports.FixReport = exports.QuickActionIcon = exports.DogfoodQuickControls = exports.FeedbackModal = exports.DogfoodStatusRail = exports.DogfoodLiveConsole = exports.DogfoodLanePicker = exports.validateDogfoodProject = exports.runtimeLogLinesFromDevEvent = exports.dogfoodLogLinesFromDevEvent = exports.dogfoodLaneOptions = exports.defaultDogfoodLane = exports.DogfoodRuntimeError = exports.DogfoodController = exports.YaverDeviceDogfood = exports.resolveSDKDogfood = exports.isYaverModeBadgeHidden = exports.showYaverModeBadge = exports.hideYaverModeBadge = exports.YaverModeBadge = exports.FloatingButton = exports.ShakeDetector = exports.AuthOverlay = exports.PairDeviceModal = exports.YaverMachinePickerScreen = exports.YaverLoginScreen = exports.YaverConnectionScreen = exports.RELOAD_APP_PATH = exports.RELOAD_PATH = exports.describeReloadFailure = exports.reloadFrameworkFamily = exports.reloadRequest = exports.reloadActions = exports.createP2PDogfoodDriver = exports.P2PClient = exports.YaverDiscovery = exports.initExpo = exports.YaverUpdates = exports.BlackBox = exports.captureStoreScreenshots = exports.YaverFeedback = void 0;
|
|
33
|
+
exports.uploadFeedback = exports.isVideoRecording = exports.stopVideoRecording = exports.startVideoRecording = exports.pickFeedbackFile = exports.captureScreenshot = exports.DEFAULT_OAUTH_REDIRECT = exports.DEFAULT_WEB_BASE_URL = exports.DEFAULT_CONVEX_SITE_URL = exports.listReachableDevices = exports.loginWithEmail = exports.signupWithEmail = exports.signInWithOAuth = exports.signInWithApple = exports.validateToken = exports.clearSelectedDeviceId = exports.saveSelectedDeviceId = exports.getDogfoodAccountAccess = exports.getSelectedDeviceId = exports.saveUser = exports.getUser = exports.clearToken = exports.saveToken = void 0;
|
|
34
34
|
var YaverFeedback_1 = require("./YaverFeedback");
|
|
35
35
|
Object.defineProperty(exports, "YaverFeedback", { enumerable: true, get: function () { return YaverFeedback_1.YaverFeedback; } });
|
|
36
36
|
var storeShots_1 = require("./storeShots");
|
|
@@ -88,8 +88,14 @@ Object.defineProperty(exports, "dogfoodLaneOptions", { enumerable: true, get: fu
|
|
|
88
88
|
Object.defineProperty(exports, "dogfoodLogLinesFromDevEvent", { enumerable: true, get: function () { return DogfoodRuntime_1.dogfoodLogLinesFromDevEvent; } });
|
|
89
89
|
Object.defineProperty(exports, "runtimeLogLinesFromDevEvent", { enumerable: true, get: function () { return DogfoodRuntime_1.runtimeLogLinesFromDevEvent; } });
|
|
90
90
|
Object.defineProperty(exports, "validateDogfoodProject", { enumerable: true, get: function () { return DogfoodRuntime_1.validateDogfoodProject; } });
|
|
91
|
+
var DogfoodSessionUi_1 = require("./DogfoodSessionUi");
|
|
92
|
+
Object.defineProperty(exports, "DogfoodLanePicker", { enumerable: true, get: function () { return DogfoodSessionUi_1.DogfoodLanePicker; } });
|
|
93
|
+
Object.defineProperty(exports, "DogfoodLiveConsole", { enumerable: true, get: function () { return DogfoodSessionUi_1.DogfoodLiveConsole; } });
|
|
94
|
+
Object.defineProperty(exports, "DogfoodStatusRail", { enumerable: true, get: function () { return DogfoodSessionUi_1.DogfoodStatusRail; } });
|
|
91
95
|
var FeedbackModal_1 = require("./FeedbackModal");
|
|
92
96
|
Object.defineProperty(exports, "FeedbackModal", { enumerable: true, get: function () { return FeedbackModal_1.FeedbackModal; } });
|
|
97
|
+
var DogfoodQuickControls_1 = require("./DogfoodQuickControls");
|
|
98
|
+
Object.defineProperty(exports, "DogfoodQuickControls", { enumerable: true, get: function () { return DogfoodQuickControls_1.DogfoodQuickControls; } });
|
|
93
99
|
var QuickActionIcon_1 = require("./QuickActionIcon");
|
|
94
100
|
Object.defineProperty(exports, "QuickActionIcon", { enumerable: true, get: function () { return QuickActionIcon_1.QuickActionIcon; } });
|
|
95
101
|
var FixReport_1 = require("./FixReport");
|
package/dist/preferences.d.ts
CHANGED
|
@@ -12,6 +12,19 @@
|
|
|
12
12
|
* still works (it just can't remember the disable beyond the
|
|
13
13
|
* in-memory session).
|
|
14
14
|
*/
|
|
15
|
+
export type DogfoodControlPresentation = 'auto' | 'minimized-y';
|
|
16
|
+
export type DogfoodControlEdge = 'left' | 'right';
|
|
17
|
+
export interface DogfoodControlPosition {
|
|
18
|
+
edge: DogfoodControlEdge;
|
|
19
|
+
/** 0–1 within the safe vertical travel area. */
|
|
20
|
+
yRatio: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function getDogfoodControlPresentation(scope?: string): Promise<DogfoodControlPresentation | null>;
|
|
23
|
+
export declare function setDogfoodControlPresentation(value: DogfoodControlPresentation, scope?: string): Promise<void>;
|
|
24
|
+
export declare function getDogfoodControlPosition(orientation: 'portrait' | 'landscape', scope?: string): Promise<DogfoodControlPosition | null>;
|
|
25
|
+
export declare function setDogfoodControlPosition(orientation: 'portrait' | 'landscape', position: DogfoodControlPosition, scope?: string): Promise<void>;
|
|
26
|
+
export declare function getDogfoodControlOnboardingSeen(scope?: string): Promise<boolean>;
|
|
27
|
+
export declare function setDogfoodControlOnboardingSeen(seen: boolean, scope?: string): Promise<void>;
|
|
15
28
|
export type QuickIconColorPreset = 'orange' | 'lime' | 'cyan' | 'pink' | 'yellow' | 'slate';
|
|
16
29
|
export declare const QUICK_ICON_COLOR_PRESETS: Record<QuickIconColorPreset, {
|
|
17
30
|
label: string;
|
package/dist/preferences.js
CHANGED
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
*/
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.QUICK_ICON_COLOR_PRESETS = void 0;
|
|
18
|
+
exports.getDogfoodControlPresentation = getDogfoodControlPresentation;
|
|
19
|
+
exports.setDogfoodControlPresentation = setDogfoodControlPresentation;
|
|
20
|
+
exports.getDogfoodControlPosition = getDogfoodControlPosition;
|
|
21
|
+
exports.setDogfoodControlPosition = setDogfoodControlPosition;
|
|
22
|
+
exports.getDogfoodControlOnboardingSeen = getDogfoodControlOnboardingSeen;
|
|
23
|
+
exports.setDogfoodControlOnboardingSeen = setDogfoodControlOnboardingSeen;
|
|
18
24
|
exports.getQuickIconDisabled = getQuickIconDisabled;
|
|
19
25
|
exports.setQuickIconDisabled = setQuickIconDisabled;
|
|
20
26
|
exports.clearQuickIconDisabled = clearQuickIconDisabled;
|
|
@@ -36,6 +42,84 @@ catch {
|
|
|
36
42
|
}
|
|
37
43
|
const QUICK_ICON_DISABLED_KEY = 'yaver_feedback_quickicon_disabled';
|
|
38
44
|
const QUICK_ICON_COLOR_KEY = 'yaver_feedback_quickicon_color';
|
|
45
|
+
const DOGFOOD_CONTROL_PRESENTATION_KEY = 'yaver_dogfood_control_presentation';
|
|
46
|
+
const DOGFOOD_CONTROL_POSITION_PREFIX = 'yaver_dogfood_control_position_';
|
|
47
|
+
const DOGFOOD_CONTROL_ONBOARDING_PREFIX = 'yaver_dogfood_control_onboarding_';
|
|
48
|
+
function dogfoodPreferenceKey(base, scope) {
|
|
49
|
+
const normalized = String(scope || 'legacy').trim() || 'legacy';
|
|
50
|
+
return `${base}_${encodeURIComponent(normalized)}`;
|
|
51
|
+
}
|
|
52
|
+
async function getDogfoodControlPresentation(scope) {
|
|
53
|
+
if (!AsyncStorage)
|
|
54
|
+
return null;
|
|
55
|
+
try {
|
|
56
|
+
const value = await AsyncStorage.getItem(dogfoodPreferenceKey(DOGFOOD_CONTROL_PRESENTATION_KEY, scope));
|
|
57
|
+
return value === 'auto' || value === 'minimized-y' ? value : null;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function setDogfoodControlPresentation(value, scope) {
|
|
64
|
+
if (!AsyncStorage)
|
|
65
|
+
return;
|
|
66
|
+
try {
|
|
67
|
+
await AsyncStorage.setItem(dogfoodPreferenceKey(DOGFOOD_CONTROL_PRESENTATION_KEY, scope), value);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// best-effort
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function getDogfoodControlPosition(orientation, scope) {
|
|
74
|
+
if (!AsyncStorage)
|
|
75
|
+
return null;
|
|
76
|
+
try {
|
|
77
|
+
const raw = await AsyncStorage.getItem(dogfoodPreferenceKey(`${DOGFOOD_CONTROL_POSITION_PREFIX}${orientation}`, scope));
|
|
78
|
+
if (!raw)
|
|
79
|
+
return null;
|
|
80
|
+
const parsed = JSON.parse(raw);
|
|
81
|
+
if ((parsed.edge !== 'left' && parsed.edge !== 'right') || typeof parsed.yRatio !== 'number')
|
|
82
|
+
return null;
|
|
83
|
+
return { edge: parsed.edge, yRatio: Math.max(0, Math.min(1, parsed.yRatio)) };
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function setDogfoodControlPosition(orientation, position, scope) {
|
|
90
|
+
if (!AsyncStorage)
|
|
91
|
+
return;
|
|
92
|
+
try {
|
|
93
|
+
await AsyncStorage.setItem(dogfoodPreferenceKey(`${DOGFOOD_CONTROL_POSITION_PREFIX}${orientation}`, scope), JSON.stringify(position));
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// best-effort
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function getDogfoodControlOnboardingSeen(scope) {
|
|
100
|
+
if (!AsyncStorage)
|
|
101
|
+
return false;
|
|
102
|
+
try {
|
|
103
|
+
return await AsyncStorage.getItem(dogfoodPreferenceKey(DOGFOOD_CONTROL_ONBOARDING_PREFIX, scope)) === '1';
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function setDogfoodControlOnboardingSeen(seen, scope) {
|
|
110
|
+
if (!AsyncStorage)
|
|
111
|
+
return;
|
|
112
|
+
try {
|
|
113
|
+
const key = dogfoodPreferenceKey(DOGFOOD_CONTROL_ONBOARDING_PREFIX, scope);
|
|
114
|
+
if (seen)
|
|
115
|
+
await AsyncStorage.setItem(key, '1');
|
|
116
|
+
else
|
|
117
|
+
await AsyncStorage.removeItem(key);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// best-effort startup cache; Convex remains authoritative.
|
|
121
|
+
}
|
|
122
|
+
}
|
|
39
123
|
exports.QUICK_ICON_COLOR_PRESETS = {
|
|
40
124
|
orange: {
|
|
41
125
|
label: 'Orange',
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#import <React/RCTBridgeModule.h>
|
|
2
|
+
#import <React/RCTEventEmitter.h>
|
|
3
|
+
|
|
4
|
+
@interface RCT_EXTERN_MODULE(YaverDogfoodGesture, RCTEventEmitter)
|
|
5
|
+
|
|
6
|
+
RCT_EXTERN_METHOD(getCapability:(RCTPromiseResolveBlock)resolve
|
|
7
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
8
|
+
|
|
9
|
+
RCT_EXTERN_METHOD(setEnabled:(BOOL)enabled
|
|
10
|
+
durationMs:(double)durationMs
|
|
11
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
12
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
13
|
+
|
|
14
|
+
@end
|