yaver-feedback-react-native 0.8.2 → 0.8.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/dist/AuthOverlay.js +32 -2
- package/dist/Discovery.js +2 -0
- package/dist/FeedbackModal.js +22 -1
- package/dist/GuestOnboardingScreen.d.ts +8 -0
- package/dist/GuestOnboardingScreen.js +282 -0
- package/dist/LoginScreen.d.ts +5 -1
- package/dist/LoginScreen.js +5 -2
- package/dist/MachinePickerScreen.js +1 -1
- package/dist/P2PClient.d.ts +13 -0
- package/dist/P2PClient.js +22 -0
- package/dist/YaverFeedback.d.ts +2 -0
- package/dist/YaverFeedback.js +46 -4
- package/dist/auth.d.ts +52 -0
- package/dist/auth.js +75 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +7 -1
- package/dist/types.d.ts +7 -0
- package/package.json +1 -1
- package/src/AuthOverlay.tsx +46 -2
- package/src/Discovery.ts +2 -0
- package/src/FeedbackModal.tsx +22 -1
- package/src/GuestOnboardingScreen.tsx +307 -0
- package/src/LoginScreen.tsx +21 -2
- package/src/MachinePickerScreen.tsx +3 -1
- package/src/P2PClient.ts +33 -0
- package/src/YaverFeedback.ts +50 -4
- package/src/auth.ts +137 -0
- package/src/index.ts +11 -0
- package/src/types.ts +7 -0
package/dist/auth.d.ts
CHANGED
|
@@ -100,8 +100,10 @@ export interface RemoteDevice {
|
|
|
100
100
|
runnerDown: boolean;
|
|
101
101
|
lastHeartbeat: number;
|
|
102
102
|
isGuest: boolean;
|
|
103
|
+
hostUserId?: string;
|
|
103
104
|
hostName?: string;
|
|
104
105
|
hostEmail?: string;
|
|
106
|
+
hostUserIdString?: string;
|
|
105
107
|
accessScope: 'owner' | 'shared-scoped' | 'shared-legacy';
|
|
106
108
|
quicHost: string;
|
|
107
109
|
quicPort: number;
|
|
@@ -121,6 +123,44 @@ export interface DeviceList {
|
|
|
121
123
|
owned: RemoteDevice[];
|
|
122
124
|
shared: RemoteDevice[];
|
|
123
125
|
}
|
|
126
|
+
export interface GuestInvitation {
|
|
127
|
+
hostUserId: string;
|
|
128
|
+
hostName: string;
|
|
129
|
+
hostEmail: string;
|
|
130
|
+
hostUserIdString?: string;
|
|
131
|
+
createdAt: number;
|
|
132
|
+
expiresAt: number;
|
|
133
|
+
inviteCode?: string;
|
|
134
|
+
}
|
|
135
|
+
export interface ActiveGuestHost {
|
|
136
|
+
hostUserId: string;
|
|
137
|
+
hostName: string;
|
|
138
|
+
hostEmail: string;
|
|
139
|
+
grantedAt: number;
|
|
140
|
+
}
|
|
141
|
+
export interface GuestHostsResponse {
|
|
142
|
+
pending: GuestInvitation[];
|
|
143
|
+
active: ActiveGuestHost[];
|
|
144
|
+
}
|
|
145
|
+
export interface InvitationHostDevice {
|
|
146
|
+
deviceId: string;
|
|
147
|
+
name: string;
|
|
148
|
+
platform: string;
|
|
149
|
+
lastHeartbeat?: number;
|
|
150
|
+
proposed: boolean;
|
|
151
|
+
}
|
|
152
|
+
export interface InvitationPreview {
|
|
153
|
+
inviteCode: string;
|
|
154
|
+
hostUserId: string;
|
|
155
|
+
hostName: string;
|
|
156
|
+
hostEmail: string;
|
|
157
|
+
hostUserIdString?: string;
|
|
158
|
+
proposedDeviceIds?: string[];
|
|
159
|
+
hostDevices: InvitationHostDevice[];
|
|
160
|
+
invitedByUserId?: boolean;
|
|
161
|
+
expiresAt: number;
|
|
162
|
+
createdAt: number;
|
|
163
|
+
}
|
|
124
164
|
/**
|
|
125
165
|
* Fetch the set of remote dev machines this user can reach. Splits into
|
|
126
166
|
* owned (user is the host) vs shared (host invited them as a guest).
|
|
@@ -130,3 +170,15 @@ export interface DeviceList {
|
|
|
130
170
|
* raw list used to render as "Kvancs-MacBook-Air.local ×3" in the picker.
|
|
131
171
|
*/
|
|
132
172
|
export declare function listReachableDevices(token: string): Promise<DeviceList>;
|
|
173
|
+
export declare function mintGuestSdkToken(token: string, hostUserId: string, targetDeviceId: string): Promise<{
|
|
174
|
+
token: string;
|
|
175
|
+
expiresAt: number;
|
|
176
|
+
allowedProjects?: string[];
|
|
177
|
+
}>;
|
|
178
|
+
export declare function fetchGuestHosts(token: string): Promise<GuestHostsResponse>;
|
|
179
|
+
export declare function findInviteByCode(token: string, code: string): Promise<InvitationPreview | null>;
|
|
180
|
+
export declare function acceptGuestByCode(token: string, code: string, approvedDeviceIds?: string[]): Promise<{
|
|
181
|
+
hostName: string;
|
|
182
|
+
hostEmail: string;
|
|
183
|
+
}>;
|
|
184
|
+
export declare function acceptGuestInvitation(token: string, hostUserId: string, approvedDeviceIds?: string[]): Promise<void>;
|
package/dist/auth.js
CHANGED
|
@@ -37,6 +37,11 @@ exports.signInWithOAuth = signInWithOAuth;
|
|
|
37
37
|
exports.signupWithEmail = signupWithEmail;
|
|
38
38
|
exports.loginWithEmail = loginWithEmail;
|
|
39
39
|
exports.listReachableDevices = listReachableDevices;
|
|
40
|
+
exports.mintGuestSdkToken = mintGuestSdkToken;
|
|
41
|
+
exports.fetchGuestHosts = fetchGuestHosts;
|
|
42
|
+
exports.findInviteByCode = findInviteByCode;
|
|
43
|
+
exports.acceptGuestByCode = acceptGuestByCode;
|
|
44
|
+
exports.acceptGuestInvitation = acceptGuestInvitation;
|
|
40
45
|
// AsyncStorage is an optional peer dep — degrade gracefully if missing.
|
|
41
46
|
let AsyncStorage = null;
|
|
42
47
|
try {
|
|
@@ -379,8 +384,10 @@ async function listReachableDevices(token) {
|
|
|
379
384
|
runnerDown: !!d.runnerDown,
|
|
380
385
|
lastHeartbeat: d.lastHeartbeat ?? 0,
|
|
381
386
|
isGuest: !!d.isGuest,
|
|
387
|
+
hostUserId: d.hostUserId,
|
|
382
388
|
hostName: d.hostName,
|
|
383
389
|
hostEmail: d.hostEmail,
|
|
390
|
+
hostUserIdString: d.hostUserIdString,
|
|
384
391
|
accessScope: d.accessScope ?? 'owner',
|
|
385
392
|
quicHost: d.quicHost ?? d.host ?? '',
|
|
386
393
|
quicPort: d.quicPort ?? 0,
|
|
@@ -405,3 +412,71 @@ async function listReachableDevices(token) {
|
|
|
405
412
|
return { owned: [], shared: [] };
|
|
406
413
|
}
|
|
407
414
|
}
|
|
415
|
+
async function mintGuestSdkToken(token, hostUserId, targetDeviceId) {
|
|
416
|
+
const res = await fetch(`${convexSiteUrl}/guests/sdk-token`, {
|
|
417
|
+
method: 'POST',
|
|
418
|
+
headers: {
|
|
419
|
+
Authorization: `Bearer ${token}`,
|
|
420
|
+
'Content-Type': 'application/json',
|
|
421
|
+
},
|
|
422
|
+
body: JSON.stringify({ hostUserId, targetDeviceId }),
|
|
423
|
+
});
|
|
424
|
+
if (!res.ok) {
|
|
425
|
+
const data = await res.json().catch(() => ({}));
|
|
426
|
+
throw new Error(data.error || 'Failed to mint delegated SDK token');
|
|
427
|
+
}
|
|
428
|
+
return res.json();
|
|
429
|
+
}
|
|
430
|
+
async function fetchGuestHosts(token) {
|
|
431
|
+
const res = await fetch(`${convexSiteUrl}/guests/hosts`, {
|
|
432
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
433
|
+
});
|
|
434
|
+
if (!res.ok) {
|
|
435
|
+
const data = await res.json().catch(() => ({}));
|
|
436
|
+
throw new Error(data.error || 'Failed to fetch guest hosts');
|
|
437
|
+
}
|
|
438
|
+
return res.json();
|
|
439
|
+
}
|
|
440
|
+
async function findInviteByCode(token, code) {
|
|
441
|
+
const cleaned = code.toUpperCase().trim();
|
|
442
|
+
const res = await fetch(`${convexSiteUrl}/guests/find-by-code?code=${encodeURIComponent(cleaned)}`, { headers: { Authorization: `Bearer ${token}` } });
|
|
443
|
+
if (res.status === 404)
|
|
444
|
+
return null;
|
|
445
|
+
if (!res.ok) {
|
|
446
|
+
const data = await res.json().catch(() => ({}));
|
|
447
|
+
throw new Error(data.error || 'Failed to load invite');
|
|
448
|
+
}
|
|
449
|
+
return res.json();
|
|
450
|
+
}
|
|
451
|
+
async function acceptGuestByCode(token, code, approvedDeviceIds) {
|
|
452
|
+
const res = await fetch(`${convexSiteUrl}/guests/accept-code`, {
|
|
453
|
+
method: 'POST',
|
|
454
|
+
headers: {
|
|
455
|
+
Authorization: `Bearer ${token}`,
|
|
456
|
+
'Content-Type': 'application/json',
|
|
457
|
+
},
|
|
458
|
+
body: JSON.stringify({
|
|
459
|
+
code: code.toUpperCase().trim(),
|
|
460
|
+
approvedDeviceIds,
|
|
461
|
+
}),
|
|
462
|
+
});
|
|
463
|
+
if (!res.ok) {
|
|
464
|
+
const data = await res.json().catch(() => ({}));
|
|
465
|
+
throw new Error(data.error || 'Invalid invite code');
|
|
466
|
+
}
|
|
467
|
+
return res.json();
|
|
468
|
+
}
|
|
469
|
+
async function acceptGuestInvitation(token, hostUserId, approvedDeviceIds) {
|
|
470
|
+
const res = await fetch(`${convexSiteUrl}/guests/accept`, {
|
|
471
|
+
method: 'POST',
|
|
472
|
+
headers: {
|
|
473
|
+
Authorization: `Bearer ${token}`,
|
|
474
|
+
'Content-Type': 'application/json',
|
|
475
|
+
},
|
|
476
|
+
body: JSON.stringify({ hostUserId, approvedDeviceIds }),
|
|
477
|
+
});
|
|
478
|
+
if (!res.ok) {
|
|
479
|
+
const data = await res.json().catch(() => ({}));
|
|
480
|
+
throw new Error(data.error || 'Failed to accept invitation');
|
|
481
|
+
}
|
|
482
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -39,6 +39,8 @@ export { YaverLoginScreen } from './LoginScreen';
|
|
|
39
39
|
export type { YaverLoginScreenProps } from './LoginScreen';
|
|
40
40
|
export { YaverMachinePickerScreen } from './MachinePickerScreen';
|
|
41
41
|
export type { YaverMachinePickerProps } from './MachinePickerScreen';
|
|
42
|
+
export { YaverGuestOnboardingScreen } from './GuestOnboardingScreen';
|
|
43
|
+
export type { YaverGuestOnboardingScreenProps } from './GuestOnboardingScreen';
|
|
42
44
|
export { PairDeviceModal } from './PairDeviceModal';
|
|
43
45
|
export type { PairDeviceModalProps } from './PairDeviceModal';
|
|
44
46
|
export { AuthOverlay } from './AuthOverlay';
|
|
@@ -49,8 +51,8 @@ export { QuickActionIcon } from './QuickActionIcon';
|
|
|
49
51
|
export type { QuickActionIconProps } from './QuickActionIcon';
|
|
50
52
|
export { FixReport } from './FixReport';
|
|
51
53
|
export { getQuickIconDisabled, setQuickIconDisabled, clearQuickIconDisabled, } from './preferences';
|
|
52
|
-
export { configureAuthEndpoints, getConvexSiteUrl, getWebBaseUrl, getToken, saveToken, clearToken, getUser, saveUser, getSelectedDeviceId, saveSelectedDeviceId, clearSelectedDeviceId, validateToken, signInWithApple, signInWithOAuth, signupWithEmail, loginWithEmail, listReachableDevices, DEFAULT_CONVEX_SITE_URL, DEFAULT_WEB_BASE_URL, DEFAULT_OAUTH_REDIRECT, } from './auth';
|
|
53
|
-
export type { OAuthProvider, User, RemoteDevice, DeviceList, } from './auth';
|
|
54
|
+
export { configureAuthEndpoints, getConvexSiteUrl, getWebBaseUrl, getToken, saveToken, clearToken, getUser, saveUser, getSelectedDeviceId, saveSelectedDeviceId, clearSelectedDeviceId, validateToken, signInWithApple, signInWithOAuth, signupWithEmail, loginWithEmail, listReachableDevices, fetchGuestHosts, findInviteByCode, acceptGuestByCode, acceptGuestInvitation, DEFAULT_CONVEX_SITE_URL, DEFAULT_WEB_BASE_URL, DEFAULT_OAUTH_REDIRECT, } from './auth';
|
|
55
|
+
export type { OAuthProvider, User, RemoteDevice, DeviceList, GuestInvitation, ActiveGuestHost, GuestHostsResponse, InvitationHostDevice, InvitationPreview, } from './auth';
|
|
54
56
|
export { captureScreenshot, pickFeedbackFile, startVideoRecording, stopVideoRecording, isVideoRecording, } from './capture';
|
|
55
57
|
export { uploadFeedback } from './upload';
|
|
56
58
|
export type { FeedbackConfig, FeedbackBundle, FeedbackMetadata, DeviceInfo, AppInfo, TimelineEvent, FeedbackReport, FeedbackStreamEvent, VoiceCapability, CapturedError, TestFix, TestSession, } from './types';
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
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.getSelectedDeviceId = exports.saveUser = exports.getUser = exports.clearToken = exports.saveToken = exports.getToken = exports.getWebBaseUrl = exports.getConvexSiteUrl = exports.configureAuthEndpoints = exports.clearQuickIconDisabled = exports.setQuickIconDisabled = exports.getQuickIconDisabled = exports.FixReport = exports.QuickActionIcon = exports.FeedbackModal = exports.FloatingButton = exports.ShakeDetector = exports.AuthOverlay = exports.PairDeviceModal = exports.YaverMachinePickerScreen = exports.YaverLoginScreen = exports.YaverConnectionScreen = exports.P2PClient = exports.YaverDiscovery = exports.initExpo = exports.YaverUpdates = exports.BlackBox = exports.YaverFeedback = void 0;
|
|
32
|
+
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.acceptGuestInvitation = exports.acceptGuestByCode = exports.findInviteByCode = exports.fetchGuestHosts = exports.listReachableDevices = exports.loginWithEmail = exports.signupWithEmail = exports.signInWithOAuth = exports.signInWithApple = exports.validateToken = exports.clearSelectedDeviceId = exports.saveSelectedDeviceId = exports.getSelectedDeviceId = exports.saveUser = exports.getUser = exports.clearToken = exports.saveToken = exports.getToken = exports.getWebBaseUrl = exports.getConvexSiteUrl = exports.configureAuthEndpoints = exports.clearQuickIconDisabled = exports.setQuickIconDisabled = exports.getQuickIconDisabled = exports.FixReport = exports.QuickActionIcon = exports.FeedbackModal = exports.FloatingButton = exports.ShakeDetector = exports.AuthOverlay = exports.PairDeviceModal = exports.YaverGuestOnboardingScreen = exports.YaverMachinePickerScreen = exports.YaverLoginScreen = exports.YaverConnectionScreen = exports.P2PClient = exports.YaverDiscovery = exports.initExpo = exports.YaverUpdates = exports.BlackBox = exports.YaverFeedback = void 0;
|
|
33
33
|
var YaverFeedback_1 = require("./YaverFeedback");
|
|
34
34
|
Object.defineProperty(exports, "YaverFeedback", { enumerable: true, get: function () { return YaverFeedback_1.YaverFeedback; } });
|
|
35
35
|
var BlackBox_1 = require("./BlackBox");
|
|
@@ -48,6 +48,8 @@ var LoginScreen_1 = require("./LoginScreen");
|
|
|
48
48
|
Object.defineProperty(exports, "YaverLoginScreen", { enumerable: true, get: function () { return LoginScreen_1.YaverLoginScreen; } });
|
|
49
49
|
var MachinePickerScreen_1 = require("./MachinePickerScreen");
|
|
50
50
|
Object.defineProperty(exports, "YaverMachinePickerScreen", { enumerable: true, get: function () { return MachinePickerScreen_1.YaverMachinePickerScreen; } });
|
|
51
|
+
var GuestOnboardingScreen_1 = require("./GuestOnboardingScreen");
|
|
52
|
+
Object.defineProperty(exports, "YaverGuestOnboardingScreen", { enumerable: true, get: function () { return GuestOnboardingScreen_1.YaverGuestOnboardingScreen; } });
|
|
51
53
|
var PairDeviceModal_1 = require("./PairDeviceModal");
|
|
52
54
|
Object.defineProperty(exports, "PairDeviceModal", { enumerable: true, get: function () { return PairDeviceModal_1.PairDeviceModal; } });
|
|
53
55
|
var AuthOverlay_1 = require("./AuthOverlay");
|
|
@@ -84,6 +86,10 @@ Object.defineProperty(exports, "signInWithOAuth", { enumerable: true, get: funct
|
|
|
84
86
|
Object.defineProperty(exports, "signupWithEmail", { enumerable: true, get: function () { return auth_1.signupWithEmail; } });
|
|
85
87
|
Object.defineProperty(exports, "loginWithEmail", { enumerable: true, get: function () { return auth_1.loginWithEmail; } });
|
|
86
88
|
Object.defineProperty(exports, "listReachableDevices", { enumerable: true, get: function () { return auth_1.listReachableDevices; } });
|
|
89
|
+
Object.defineProperty(exports, "fetchGuestHosts", { enumerable: true, get: function () { return auth_1.fetchGuestHosts; } });
|
|
90
|
+
Object.defineProperty(exports, "findInviteByCode", { enumerable: true, get: function () { return auth_1.findInviteByCode; } });
|
|
91
|
+
Object.defineProperty(exports, "acceptGuestByCode", { enumerable: true, get: function () { return auth_1.acceptGuestByCode; } });
|
|
92
|
+
Object.defineProperty(exports, "acceptGuestInvitation", { enumerable: true, get: function () { return auth_1.acceptGuestInvitation; } });
|
|
87
93
|
Object.defineProperty(exports, "DEFAULT_CONVEX_SITE_URL", { enumerable: true, get: function () { return auth_1.DEFAULT_CONVEX_SITE_URL; } });
|
|
88
94
|
Object.defineProperty(exports, "DEFAULT_WEB_BASE_URL", { enumerable: true, get: function () { return auth_1.DEFAULT_WEB_BASE_URL; } });
|
|
89
95
|
Object.defineProperty(exports, "DEFAULT_OAUTH_REDIRECT", { enumerable: true, get: function () { return auth_1.DEFAULT_OAUTH_REDIRECT; } });
|
package/dist/types.d.ts
CHANGED
|
@@ -209,6 +209,13 @@ export interface FeedbackConfig {
|
|
|
209
209
|
* Default: false (preserve historical behavior).
|
|
210
210
|
*/
|
|
211
211
|
strictNativeAuth?: boolean;
|
|
212
|
+
/**
|
|
213
|
+
* Optional host invite code to prefill into the in-SDK guest onboarding
|
|
214
|
+
* flow. Useful when your app receives the code from a deep link, QR flow,
|
|
215
|
+
* or an out-of-band host handoff and you want the user to redeem it
|
|
216
|
+
* without typing.
|
|
217
|
+
*/
|
|
218
|
+
guestInviteCode?: string;
|
|
212
219
|
}
|
|
213
220
|
export interface FeedbackBundle {
|
|
214
221
|
metadata: FeedbackMetadata;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yaver-feedback-react-native",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Visual feedback SDK for Yaver — bug reports, screen recording, voice annotations, and local-first developer workflows",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
package/src/AuthOverlay.tsx
CHANGED
|
@@ -2,8 +2,9 @@ import React, { useEffect, useState } from 'react';
|
|
|
2
2
|
import { DeviceEventEmitter, Modal } from 'react-native';
|
|
3
3
|
import { YaverLoginScreen } from './LoginScreen';
|
|
4
4
|
import { YaverMachinePickerScreen } from './MachinePickerScreen';
|
|
5
|
+
import { YaverGuestOnboardingScreen } from './GuestOnboardingScreen';
|
|
5
6
|
import { YaverFeedback } from './YaverFeedback';
|
|
6
|
-
import { getToken, RemoteDevice } from './auth';
|
|
7
|
+
import { getToken, RemoteDevice, listReachableDevices } from './auth';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Presentation layer for the SDK's auth + machine-picker modals.
|
|
@@ -21,8 +22,10 @@ import { getToken, RemoteDevice } from './auth';
|
|
|
21
22
|
*/
|
|
22
23
|
export const AuthOverlay: React.FC = () => {
|
|
23
24
|
const [loginVisible, setLoginVisible] = useState(false);
|
|
25
|
+
const [guestVisible, setGuestVisible] = useState(false);
|
|
24
26
|
const [pickerVisible, setPickerVisible] = useState(false);
|
|
25
27
|
const [token, setToken] = useState<string | null>(null);
|
|
28
|
+
const [pendingInviteCode, setPendingInviteCode] = useState<string | null>(null);
|
|
26
29
|
|
|
27
30
|
useEffect(() => {
|
|
28
31
|
let mounted = true;
|
|
@@ -50,16 +53,32 @@ export const AuthOverlay: React.FC = () => {
|
|
|
50
53
|
};
|
|
51
54
|
}, []);
|
|
52
55
|
|
|
53
|
-
const
|
|
56
|
+
const continueAfterAuth = async (newToken: string, inviteCode?: string) => {
|
|
54
57
|
setToken(newToken);
|
|
55
58
|
await YaverFeedback.setAuthToken(newToken);
|
|
59
|
+
const devices = await listReachableDevices(newToken).catch(() => ({ owned: [], shared: [] }));
|
|
56
60
|
setLoginVisible(false);
|
|
61
|
+
const cleanedInviteCode = (inviteCode ?? '').trim().toUpperCase();
|
|
62
|
+
if (cleanedInviteCode) {
|
|
63
|
+
setPendingInviteCode(cleanedInviteCode);
|
|
64
|
+
setGuestVisible(true);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (devices.owned.length === 0 && devices.shared.length === 0) {
|
|
68
|
+
setGuestVisible(true);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
57
71
|
setPickerVisible(true);
|
|
58
72
|
};
|
|
59
73
|
|
|
74
|
+
const handleLoggedIn = async (newToken: string, opts?: { inviteCode?: string }) => {
|
|
75
|
+
await continueAfterAuth(newToken, opts?.inviteCode);
|
|
76
|
+
};
|
|
77
|
+
|
|
60
78
|
const handleDevicePicked = async (device: RemoteDevice) => {
|
|
61
79
|
await YaverFeedback.setPreferredDevice(device.deviceId);
|
|
62
80
|
setPickerVisible(false);
|
|
81
|
+
setGuestVisible(false);
|
|
63
82
|
// Continue straight into the feedback flow the user originally triggered.
|
|
64
83
|
DeviceEventEmitter.emit('yaverFeedback:startReport');
|
|
65
84
|
};
|
|
@@ -75,6 +94,7 @@ export const AuthOverlay: React.FC = () => {
|
|
|
75
94
|
<YaverLoginScreen
|
|
76
95
|
onLoggedIn={handleLoggedIn}
|
|
77
96
|
onCancel={() => setLoginVisible(false)}
|
|
97
|
+
initialInviteCode={pendingInviteCode ?? YaverFeedback.getConfig()?.guestInviteCode}
|
|
78
98
|
/>
|
|
79
99
|
</Modal>
|
|
80
100
|
|
|
@@ -93,6 +113,30 @@ export const AuthOverlay: React.FC = () => {
|
|
|
93
113
|
/>
|
|
94
114
|
)}
|
|
95
115
|
</Modal>
|
|
116
|
+
|
|
117
|
+
<Modal
|
|
118
|
+
visible={guestVisible && !!token}
|
|
119
|
+
animationType="slide"
|
|
120
|
+
presentationStyle="fullScreen"
|
|
121
|
+
onRequestClose={() => setGuestVisible(false)}
|
|
122
|
+
>
|
|
123
|
+
{token && (
|
|
124
|
+
<YaverGuestOnboardingScreen
|
|
125
|
+
token={token}
|
|
126
|
+
initialInviteCode={pendingInviteCode ?? YaverFeedback.getConfig()?.guestInviteCode}
|
|
127
|
+
onContinue={() => {
|
|
128
|
+
setGuestVisible(false);
|
|
129
|
+
setPendingInviteCode(null);
|
|
130
|
+
setPickerVisible(true);
|
|
131
|
+
}}
|
|
132
|
+
onCancel={() => {
|
|
133
|
+
setGuestVisible(false);
|
|
134
|
+
setPendingInviteCode(null);
|
|
135
|
+
setPickerVisible(true);
|
|
136
|
+
}}
|
|
137
|
+
/>
|
|
138
|
+
)}
|
|
139
|
+
</Modal>
|
|
96
140
|
</>
|
|
97
141
|
);
|
|
98
142
|
};
|
package/src/Discovery.ts
CHANGED
|
@@ -188,8 +188,10 @@ export class YaverDiscovery {
|
|
|
188
188
|
runnerDown: !!d.runnerDown,
|
|
189
189
|
lastHeartbeat: d.lastHeartbeat ?? 0,
|
|
190
190
|
isGuest: !!d.isGuest,
|
|
191
|
+
hostUserId: d.hostUserId,
|
|
191
192
|
hostName: d.hostName,
|
|
192
193
|
hostEmail: d.hostEmail,
|
|
194
|
+
hostUserIdString: d.hostUserIdString,
|
|
193
195
|
accessScope: d.accessScope ?? 'owner',
|
|
194
196
|
quicHost: d.quicHost ?? d.host ?? '',
|
|
195
197
|
quicPort: d.quicPort ?? 0,
|
package/src/FeedbackModal.tsx
CHANGED
|
@@ -480,8 +480,29 @@ export const FeedbackModal: React.FC = () => {
|
|
|
480
480
|
// user types what they want, hits Send, sees the task id back. If
|
|
481
481
|
// left blank, we default to "pick the next small improvement"
|
|
482
482
|
// so a one-tap workflow still works for lazy days.
|
|
483
|
-
const handleVibingButton = useCallback(() => {
|
|
483
|
+
const handleVibingButton = useCallback(async () => {
|
|
484
484
|
if (!showVibeInput) {
|
|
485
|
+
const client = YaverFeedback.getP2PClient();
|
|
486
|
+
if (!client) {
|
|
487
|
+
setError('Not connected to the agent yet.');
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
setError(null);
|
|
491
|
+
try {
|
|
492
|
+
const eligibility = await client.getVibingEligibility();
|
|
493
|
+
if (!eligibility.canVibe) {
|
|
494
|
+
const message =
|
|
495
|
+
eligibility.guidance && eligibility.guidance.trim()
|
|
496
|
+
? `${eligibility.reason ?? 'Vibe coding is unavailable.'} ${eligibility.guidance}`
|
|
497
|
+
: eligibility.reason ?? 'Vibe coding is unavailable.';
|
|
498
|
+
setError(message);
|
|
499
|
+
setToast('Vibe coding unavailable for this project.');
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
} catch (err: unknown) {
|
|
503
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
485
506
|
setShowVibeInput(true);
|
|
486
507
|
return;
|
|
487
508
|
}
|