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/AuthOverlay.js
CHANGED
|
@@ -38,6 +38,7 @@ const react_1 = __importStar(require("react"));
|
|
|
38
38
|
const react_native_1 = require("react-native");
|
|
39
39
|
const LoginScreen_1 = require("./LoginScreen");
|
|
40
40
|
const MachinePickerScreen_1 = require("./MachinePickerScreen");
|
|
41
|
+
const GuestOnboardingScreen_1 = require("./GuestOnboardingScreen");
|
|
41
42
|
const YaverFeedback_1 = require("./YaverFeedback");
|
|
42
43
|
const auth_1 = require("./auth");
|
|
43
44
|
/**
|
|
@@ -56,8 +57,10 @@ const auth_1 = require("./auth");
|
|
|
56
57
|
*/
|
|
57
58
|
const AuthOverlay = () => {
|
|
58
59
|
const [loginVisible, setLoginVisible] = (0, react_1.useState)(false);
|
|
60
|
+
const [guestVisible, setGuestVisible] = (0, react_1.useState)(false);
|
|
59
61
|
const [pickerVisible, setPickerVisible] = (0, react_1.useState)(false);
|
|
60
62
|
const [token, setToken] = (0, react_1.useState)(null);
|
|
63
|
+
const [pendingInviteCode, setPendingInviteCode] = (0, react_1.useState)(null);
|
|
61
64
|
(0, react_1.useEffect)(() => {
|
|
62
65
|
let mounted = true;
|
|
63
66
|
(async () => {
|
|
@@ -79,26 +82,53 @@ const AuthOverlay = () => {
|
|
|
79
82
|
pickerSub.remove();
|
|
80
83
|
};
|
|
81
84
|
}, []);
|
|
82
|
-
const
|
|
85
|
+
const continueAfterAuth = async (newToken, inviteCode) => {
|
|
83
86
|
setToken(newToken);
|
|
84
87
|
await YaverFeedback_1.YaverFeedback.setAuthToken(newToken);
|
|
88
|
+
const devices = await (0, auth_1.listReachableDevices)(newToken).catch(() => ({ owned: [], shared: [] }));
|
|
85
89
|
setLoginVisible(false);
|
|
90
|
+
const cleanedInviteCode = (inviteCode ?? '').trim().toUpperCase();
|
|
91
|
+
if (cleanedInviteCode) {
|
|
92
|
+
setPendingInviteCode(cleanedInviteCode);
|
|
93
|
+
setGuestVisible(true);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (devices.owned.length === 0 && devices.shared.length === 0) {
|
|
97
|
+
setGuestVisible(true);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
86
100
|
setPickerVisible(true);
|
|
87
101
|
};
|
|
102
|
+
const handleLoggedIn = async (newToken, opts) => {
|
|
103
|
+
await continueAfterAuth(newToken, opts?.inviteCode);
|
|
104
|
+
};
|
|
88
105
|
const handleDevicePicked = async (device) => {
|
|
89
106
|
await YaverFeedback_1.YaverFeedback.setPreferredDevice(device.deviceId);
|
|
90
107
|
setPickerVisible(false);
|
|
108
|
+
setGuestVisible(false);
|
|
91
109
|
// Continue straight into the feedback flow the user originally triggered.
|
|
92
110
|
react_native_1.DeviceEventEmitter.emit('yaverFeedback:startReport');
|
|
93
111
|
};
|
|
94
112
|
return (<>
|
|
95
113
|
<react_native_1.Modal visible={loginVisible} animationType="slide" presentationStyle="fullScreen" onRequestClose={() => setLoginVisible(false)}>
|
|
96
|
-
<LoginScreen_1.YaverLoginScreen onLoggedIn={handleLoggedIn} onCancel={() => setLoginVisible(false)}/>
|
|
114
|
+
<LoginScreen_1.YaverLoginScreen onLoggedIn={handleLoggedIn} onCancel={() => setLoginVisible(false)} initialInviteCode={pendingInviteCode ?? YaverFeedback_1.YaverFeedback.getConfig()?.guestInviteCode}/>
|
|
97
115
|
</react_native_1.Modal>
|
|
98
116
|
|
|
99
117
|
<react_native_1.Modal visible={pickerVisible && !!token} animationType="slide" presentationStyle="fullScreen" onRequestClose={() => setPickerVisible(false)}>
|
|
100
118
|
{token && (<MachinePickerScreen_1.YaverMachinePickerScreen token={token} currentDeviceId={YaverFeedback_1.YaverFeedback.getConfig()?.preferredDeviceId} onPick={handleDevicePicked} onCancel={() => setPickerVisible(false)}/>)}
|
|
101
119
|
</react_native_1.Modal>
|
|
120
|
+
|
|
121
|
+
<react_native_1.Modal visible={guestVisible && !!token} animationType="slide" presentationStyle="fullScreen" onRequestClose={() => setGuestVisible(false)}>
|
|
122
|
+
{token && (<GuestOnboardingScreen_1.YaverGuestOnboardingScreen token={token} initialInviteCode={pendingInviteCode ?? YaverFeedback_1.YaverFeedback.getConfig()?.guestInviteCode} onContinue={() => {
|
|
123
|
+
setGuestVisible(false);
|
|
124
|
+
setPendingInviteCode(null);
|
|
125
|
+
setPickerVisible(true);
|
|
126
|
+
}} onCancel={() => {
|
|
127
|
+
setGuestVisible(false);
|
|
128
|
+
setPendingInviteCode(null);
|
|
129
|
+
setPickerVisible(true);
|
|
130
|
+
}}/>)}
|
|
131
|
+
</react_native_1.Modal>
|
|
102
132
|
</>);
|
|
103
133
|
};
|
|
104
134
|
exports.AuthOverlay = AuthOverlay;
|
package/dist/Discovery.js
CHANGED
|
@@ -174,8 +174,10 @@ class YaverDiscovery {
|
|
|
174
174
|
runnerDown: !!d.runnerDown,
|
|
175
175
|
lastHeartbeat: d.lastHeartbeat ?? 0,
|
|
176
176
|
isGuest: !!d.isGuest,
|
|
177
|
+
hostUserId: d.hostUserId,
|
|
177
178
|
hostName: d.hostName,
|
|
178
179
|
hostEmail: d.hostEmail,
|
|
180
|
+
hostUserIdString: d.hostUserIdString,
|
|
179
181
|
accessScope: d.accessScope ?? 'owner',
|
|
180
182
|
quicHost: d.quicHost ?? d.host ?? '',
|
|
181
183
|
quicPort: d.quicPort ?? 0,
|
package/dist/FeedbackModal.js
CHANGED
|
@@ -437,8 +437,29 @@ const FeedbackModal = () => {
|
|
|
437
437
|
// user types what they want, hits Send, sees the task id back. If
|
|
438
438
|
// left blank, we default to "pick the next small improvement"
|
|
439
439
|
// so a one-tap workflow still works for lazy days.
|
|
440
|
-
const handleVibingButton = (0, react_1.useCallback)(() => {
|
|
440
|
+
const handleVibingButton = (0, react_1.useCallback)(async () => {
|
|
441
441
|
if (!showVibeInput) {
|
|
442
|
+
const client = YaverFeedback_1.YaverFeedback.getP2PClient();
|
|
443
|
+
if (!client) {
|
|
444
|
+
setError('Not connected to the agent yet.');
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
setError(null);
|
|
448
|
+
try {
|
|
449
|
+
const eligibility = await client.getVibingEligibility();
|
|
450
|
+
if (!eligibility.canVibe) {
|
|
451
|
+
const message = eligibility.guidance && eligibility.guidance.trim()
|
|
452
|
+
? `${eligibility.reason ?? 'Vibe coding is unavailable.'} ${eligibility.guidance}`
|
|
453
|
+
: eligibility.reason ?? 'Vibe coding is unavailable.';
|
|
454
|
+
setError(message);
|
|
455
|
+
setToast('Vibe coding unavailable for this project.');
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
catch (err) {
|
|
460
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
442
463
|
setShowVibeInput(true);
|
|
443
464
|
return;
|
|
444
465
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface YaverGuestOnboardingScreenProps {
|
|
3
|
+
token: string;
|
|
4
|
+
initialInviteCode?: string;
|
|
5
|
+
onContinue: () => void;
|
|
6
|
+
onCancel?: () => void;
|
|
7
|
+
}
|
|
8
|
+
export declare const YaverGuestOnboardingScreen: React.FC<YaverGuestOnboardingScreenProps>;
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.YaverGuestOnboardingScreen = void 0;
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const react_native_1 = require("react-native");
|
|
39
|
+
const auth_1 = require("./auth");
|
|
40
|
+
const YaverGuestOnboardingScreen = ({ token, initialInviteCode, onContinue, onCancel, }) => {
|
|
41
|
+
const [code, setCode] = (0, react_1.useState)((initialInviteCode ?? '').toUpperCase());
|
|
42
|
+
const [preview, setPreview] = (0, react_1.useState)(null);
|
|
43
|
+
const [hosts, setHosts] = (0, react_1.useState)({ pending: [], active: [] });
|
|
44
|
+
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
45
|
+
const [busy, setBusy] = (0, react_1.useState)(false);
|
|
46
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
47
|
+
const cleanedCode = (0, react_1.useMemo)(() => code.toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 6), [code]);
|
|
48
|
+
const loadHosts = (0, react_1.useCallback)(async () => {
|
|
49
|
+
setLoading(true);
|
|
50
|
+
setError(null);
|
|
51
|
+
try {
|
|
52
|
+
const result = await (0, auth_1.fetchGuestHosts)(token);
|
|
53
|
+
setHosts(result);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
setLoading(false);
|
|
60
|
+
}
|
|
61
|
+
}, [token]);
|
|
62
|
+
(0, react_1.useEffect)(() => {
|
|
63
|
+
void loadHosts();
|
|
64
|
+
}, [loadHosts]);
|
|
65
|
+
(0, react_1.useEffect)(() => {
|
|
66
|
+
let cancelled = false;
|
|
67
|
+
if (cleanedCode.length !== 6) {
|
|
68
|
+
setPreview(null);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
(async () => {
|
|
72
|
+
try {
|
|
73
|
+
const result = await (0, auth_1.findInviteByCode)(token, cleanedCode);
|
|
74
|
+
if (!cancelled) {
|
|
75
|
+
setPreview(result);
|
|
76
|
+
if (!result) {
|
|
77
|
+
setError('Invite code not found or expired.');
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
setError(null);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
if (!cancelled) {
|
|
86
|
+
setPreview(null);
|
|
87
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
})();
|
|
91
|
+
return () => {
|
|
92
|
+
cancelled = true;
|
|
93
|
+
};
|
|
94
|
+
}, [cleanedCode, token]);
|
|
95
|
+
const handleAcceptCode = (0, react_1.useCallback)(async () => {
|
|
96
|
+
if (cleanedCode.length !== 6) {
|
|
97
|
+
setError('Enter the 6-character invite code from the host.');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
setBusy(true);
|
|
101
|
+
setError(null);
|
|
102
|
+
try {
|
|
103
|
+
await (0, auth_1.acceptGuestByCode)(token, cleanedCode, preview?.proposedDeviceIds);
|
|
104
|
+
await loadHosts();
|
|
105
|
+
onContinue();
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
setBusy(false);
|
|
112
|
+
}
|
|
113
|
+
}, [cleanedCode, loadHosts, onContinue, preview?.proposedDeviceIds, token]);
|
|
114
|
+
const handleAcceptPending = (0, react_1.useCallback)(async (hostUserId) => {
|
|
115
|
+
setBusy(true);
|
|
116
|
+
setError(null);
|
|
117
|
+
try {
|
|
118
|
+
await (0, auth_1.acceptGuestInvitation)(token, hostUserId);
|
|
119
|
+
await loadHosts();
|
|
120
|
+
onContinue();
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
setBusy(false);
|
|
127
|
+
}
|
|
128
|
+
}, [loadHosts, onContinue, token]);
|
|
129
|
+
return (<react_native_1.SafeAreaView style={styles.container}>
|
|
130
|
+
<react_native_1.ScrollView contentContainerStyle={styles.content} keyboardShouldPersistTaps="handled">
|
|
131
|
+
<react_native_1.View style={styles.header}>
|
|
132
|
+
<react_native_1.View>
|
|
133
|
+
<react_native_1.Text style={styles.title}>Guest Access</react_native_1.Text>
|
|
134
|
+
<react_native_1.Text style={styles.subtitle}>
|
|
135
|
+
Join a host's repo-scoped Feedback SDK access without leaving the app.
|
|
136
|
+
</react_native_1.Text>
|
|
137
|
+
</react_native_1.View>
|
|
138
|
+
{onCancel && (<react_native_1.Pressable onPress={onCancel} style={styles.skipBtn}>
|
|
139
|
+
<react_native_1.Text style={styles.skipText}>Skip</react_native_1.Text>
|
|
140
|
+
</react_native_1.Pressable>)}
|
|
141
|
+
</react_native_1.View>
|
|
142
|
+
|
|
143
|
+
<react_native_1.View style={styles.card}>
|
|
144
|
+
<react_native_1.Text style={styles.cardTitle}>Have an invite code?</react_native_1.Text>
|
|
145
|
+
<react_native_1.Text style={styles.cardText}>
|
|
146
|
+
Enter the 6-character code from the host. After redemption you can pick the shared machine directly here.
|
|
147
|
+
</react_native_1.Text>
|
|
148
|
+
<react_native_1.TextInput style={styles.input} value={cleanedCode} onChangeText={setCode} placeholder="ABC123" placeholderTextColor="#667085" autoCapitalize="characters" autoCorrect={false} maxLength={6}/>
|
|
149
|
+
{preview && (<react_native_1.View style={styles.previewBox}>
|
|
150
|
+
<react_native_1.Text style={styles.previewTitle}>{preview.hostName}</react_native_1.Text>
|
|
151
|
+
<react_native_1.Text style={styles.previewMeta}>{preview.hostEmail}</react_native_1.Text>
|
|
152
|
+
<react_native_1.Text style={styles.previewMeta}>
|
|
153
|
+
{preview.hostDevices.length > 0
|
|
154
|
+
? `${preview.hostDevices.length} host machine${preview.hostDevices.length === 1 ? '' : 's'} shared`
|
|
155
|
+
: 'Host machine list will appear after acceptance'}
|
|
156
|
+
</react_native_1.Text>
|
|
157
|
+
</react_native_1.View>)}
|
|
158
|
+
<react_native_1.Pressable onPress={() => void handleAcceptCode()} disabled={busy || cleanedCode.length !== 6} style={({ pressed }) => [
|
|
159
|
+
styles.primaryBtn,
|
|
160
|
+
(pressed || busy || cleanedCode.length !== 6) && styles.primaryBtnPressed,
|
|
161
|
+
]}>
|
|
162
|
+
{busy ? <react_native_1.ActivityIndicator color="#fff"/> : <react_native_1.Text style={styles.primaryBtnText}>Redeem Code</react_native_1.Text>}
|
|
163
|
+
</react_native_1.Pressable>
|
|
164
|
+
</react_native_1.View>
|
|
165
|
+
|
|
166
|
+
<react_native_1.View style={styles.card}>
|
|
167
|
+
<react_native_1.Text style={styles.cardTitle}>Pending host invites</react_native_1.Text>
|
|
168
|
+
<react_native_1.Text style={styles.cardText}>
|
|
169
|
+
If a host invited your email directly, it should appear here after you sign up.
|
|
170
|
+
</react_native_1.Text>
|
|
171
|
+
{loading ? (<react_native_1.ActivityIndicator color="#98a2b3" style={{ marginTop: 12 }}/>) : hosts.pending.length > 0 ? (hosts.pending.map((invite) => (<react_native_1.View key={`${invite.hostUserId}:${invite.createdAt}`} style={styles.inviteRow}>
|
|
172
|
+
<react_native_1.View style={{ flex: 1 }}>
|
|
173
|
+
<react_native_1.Text style={styles.inviteName}>{invite.hostName}</react_native_1.Text>
|
|
174
|
+
<react_native_1.Text style={styles.inviteMeta}>{invite.hostEmail}</react_native_1.Text>
|
|
175
|
+
</react_native_1.View>
|
|
176
|
+
<react_native_1.Pressable onPress={() => void handleAcceptPending(invite.hostUserId)} disabled={busy} style={({ pressed }) => [
|
|
177
|
+
styles.secondaryBtn,
|
|
178
|
+
(pressed || busy) && styles.secondaryBtnPressed,
|
|
179
|
+
]}>
|
|
180
|
+
<react_native_1.Text style={styles.secondaryBtnText}>Accept</react_native_1.Text>
|
|
181
|
+
</react_native_1.Pressable>
|
|
182
|
+
</react_native_1.View>))) : (<react_native_1.Text style={styles.emptyText}>No pending host invites on this account yet.</react_native_1.Text>)}
|
|
183
|
+
</react_native_1.View>
|
|
184
|
+
|
|
185
|
+
<react_native_1.View style={styles.cardMuted}>
|
|
186
|
+
<react_native_1.Text style={styles.cardTitle}>No machine yet?</react_native_1.Text>
|
|
187
|
+
<react_native_1.Text style={styles.cardText}>
|
|
188
|
+
That is fine. You can still sign up here and redeem the host's guest access. Shared machines will appear once the host has one running. Later you can install Yaver on your own machine too.
|
|
189
|
+
</react_native_1.Text>
|
|
190
|
+
</react_native_1.View>
|
|
191
|
+
|
|
192
|
+
{error ? <react_native_1.Text style={styles.error}>{error}</react_native_1.Text> : null}
|
|
193
|
+
|
|
194
|
+
<react_native_1.Pressable onPress={onContinue} style={({ pressed }) => [styles.linkBtn, pressed && { opacity: 0.7 }]}>
|
|
195
|
+
<react_native_1.Text style={styles.linkBtnText}>Continue to machine picker</react_native_1.Text>
|
|
196
|
+
</react_native_1.Pressable>
|
|
197
|
+
</react_native_1.ScrollView>
|
|
198
|
+
</react_native_1.SafeAreaView>);
|
|
199
|
+
};
|
|
200
|
+
exports.YaverGuestOnboardingScreen = YaverGuestOnboardingScreen;
|
|
201
|
+
const styles = react_native_1.StyleSheet.create({
|
|
202
|
+
container: { flex: 1, backgroundColor: '#0f172a' },
|
|
203
|
+
content: { padding: 20, gap: 16 },
|
|
204
|
+
header: {
|
|
205
|
+
flexDirection: 'row',
|
|
206
|
+
justifyContent: 'space-between',
|
|
207
|
+
alignItems: 'flex-start',
|
|
208
|
+
marginBottom: 8,
|
|
209
|
+
},
|
|
210
|
+
title: { color: '#f8fafc', fontSize: 24, fontWeight: '700' },
|
|
211
|
+
subtitle: { color: '#94a3b8', fontSize: 14, marginTop: 6, maxWidth: 280 },
|
|
212
|
+
skipBtn: { paddingVertical: 6, paddingHorizontal: 10 },
|
|
213
|
+
skipText: { color: '#cbd5e1', fontSize: 13, fontWeight: '600' },
|
|
214
|
+
card: {
|
|
215
|
+
backgroundColor: 'rgba(15,23,42,0.82)',
|
|
216
|
+
borderWidth: 1,
|
|
217
|
+
borderColor: 'rgba(148,163,184,0.18)',
|
|
218
|
+
borderRadius: 16,
|
|
219
|
+
padding: 16,
|
|
220
|
+
},
|
|
221
|
+
cardMuted: {
|
|
222
|
+
backgroundColor: 'rgba(30,41,59,0.9)',
|
|
223
|
+
borderRadius: 16,
|
|
224
|
+
padding: 16,
|
|
225
|
+
},
|
|
226
|
+
cardTitle: { color: '#e2e8f0', fontSize: 16, fontWeight: '700' },
|
|
227
|
+
cardText: { color: '#94a3b8', fontSize: 13, lineHeight: 20, marginTop: 8 },
|
|
228
|
+
input: {
|
|
229
|
+
marginTop: 14,
|
|
230
|
+
borderWidth: 1,
|
|
231
|
+
borderColor: 'rgba(148,163,184,0.28)',
|
|
232
|
+
borderRadius: 12,
|
|
233
|
+
paddingHorizontal: 14,
|
|
234
|
+
paddingVertical: 12,
|
|
235
|
+
backgroundColor: 'rgba(2,6,23,0.88)',
|
|
236
|
+
color: '#f8fafc',
|
|
237
|
+
fontSize: 18,
|
|
238
|
+
letterSpacing: 3,
|
|
239
|
+
textAlign: 'center',
|
|
240
|
+
},
|
|
241
|
+
previewBox: {
|
|
242
|
+
marginTop: 12,
|
|
243
|
+
padding: 12,
|
|
244
|
+
borderRadius: 12,
|
|
245
|
+
backgroundColor: 'rgba(30,41,59,0.75)',
|
|
246
|
+
},
|
|
247
|
+
previewTitle: { color: '#f8fafc', fontSize: 15, fontWeight: '600' },
|
|
248
|
+
previewMeta: { color: '#94a3b8', fontSize: 12, marginTop: 4 },
|
|
249
|
+
primaryBtn: {
|
|
250
|
+
marginTop: 14,
|
|
251
|
+
backgroundColor: '#2563eb',
|
|
252
|
+
borderRadius: 12,
|
|
253
|
+
minHeight: 46,
|
|
254
|
+
alignItems: 'center',
|
|
255
|
+
justifyContent: 'center',
|
|
256
|
+
},
|
|
257
|
+
primaryBtnPressed: { opacity: 0.6 },
|
|
258
|
+
primaryBtnText: { color: '#fff', fontSize: 15, fontWeight: '700' },
|
|
259
|
+
inviteRow: {
|
|
260
|
+
flexDirection: 'row',
|
|
261
|
+
alignItems: 'center',
|
|
262
|
+
gap: 12,
|
|
263
|
+
marginTop: 12,
|
|
264
|
+
paddingTop: 12,
|
|
265
|
+
borderTopWidth: 1,
|
|
266
|
+
borderTopColor: 'rgba(148,163,184,0.12)',
|
|
267
|
+
},
|
|
268
|
+
inviteName: { color: '#f8fafc', fontSize: 14, fontWeight: '600' },
|
|
269
|
+
inviteMeta: { color: '#94a3b8', fontSize: 12, marginTop: 4 },
|
|
270
|
+
secondaryBtn: {
|
|
271
|
+
paddingHorizontal: 12,
|
|
272
|
+
paddingVertical: 10,
|
|
273
|
+
borderRadius: 10,
|
|
274
|
+
backgroundColor: 'rgba(37,99,235,0.16)',
|
|
275
|
+
},
|
|
276
|
+
secondaryBtnPressed: { opacity: 0.6 },
|
|
277
|
+
secondaryBtnText: { color: '#bfdbfe', fontSize: 13, fontWeight: '700' },
|
|
278
|
+
emptyText: { color: '#64748b', fontSize: 13, marginTop: 12 },
|
|
279
|
+
error: { color: '#f87171', fontSize: 13, marginTop: 4 },
|
|
280
|
+
linkBtn: { alignItems: 'center', paddingVertical: 8 },
|
|
281
|
+
linkBtnText: { color: '#cbd5e1', fontSize: 14, fontWeight: '600' },
|
|
282
|
+
});
|
package/dist/LoginScreen.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
export interface YaverLoginScreenProps {
|
|
3
3
|
/** Invoked once a session token is issued and the user is loaded. */
|
|
4
|
-
onLoggedIn: (token: string
|
|
4
|
+
onLoggedIn: (token: string, opts?: {
|
|
5
|
+
inviteCode?: string;
|
|
6
|
+
}) => void;
|
|
5
7
|
/** Optional cancel button shown in header. */
|
|
6
8
|
onCancel?: () => void;
|
|
9
|
+
/** Optional prefilled guest invite code from config / deep link. */
|
|
10
|
+
initialInviteCode?: string;
|
|
7
11
|
}
|
|
8
12
|
/**
|
|
9
13
|
* Full-screen in-SDK login. Mirrors the Yaver mobile app login UX: native
|
package/dist/LoginScreen.js
CHANGED
|
@@ -91,7 +91,7 @@ const iconStyles = react_native_1.StyleSheet.create({
|
|
|
91
91
|
* Apple Sign-In on iOS, in-app browser OAuth for Google/GitHub/GitLab/
|
|
92
92
|
* Microsoft (no codes, no leaving the app), and inline email/password.
|
|
93
93
|
*/
|
|
94
|
-
const YaverLoginScreen = ({ onLoggedIn, onCancel, }) => {
|
|
94
|
+
const YaverLoginScreen = ({ onLoggedIn, onCancel, initialInviteCode, }) => {
|
|
95
95
|
const [busyProvider, setBusyProvider] = (0, react_1.useState)(null);
|
|
96
96
|
const [showEmailForm, setShowEmailForm] = (0, react_1.useState)(false);
|
|
97
97
|
const [isSignUp, setIsSignUp] = (0, react_1.useState)(false);
|
|
@@ -99,6 +99,7 @@ const YaverLoginScreen = ({ onLoggedIn, onCancel, }) => {
|
|
|
99
99
|
const [email, setEmail] = (0, react_1.useState)('');
|
|
100
100
|
const [password, setPassword] = (0, react_1.useState)('');
|
|
101
101
|
const [confirmPassword, setConfirmPassword] = (0, react_1.useState)('');
|
|
102
|
+
const [inviteCode, setInviteCode] = (0, react_1.useState)((initialInviteCode ?? '').toUpperCase());
|
|
102
103
|
const [emailBusy, setEmailBusy] = (0, react_1.useState)(false);
|
|
103
104
|
const [emailError, setEmailError] = (0, react_1.useState)('');
|
|
104
105
|
const finish = async (token) => {
|
|
@@ -106,7 +107,8 @@ const YaverLoginScreen = ({ onLoggedIn, onCancel, }) => {
|
|
|
106
107
|
await (0, auth_1.saveToken)(token);
|
|
107
108
|
if (user)
|
|
108
109
|
await (0, auth_1.saveUser)(user);
|
|
109
|
-
|
|
110
|
+
const cleanedInviteCode = inviteCode.toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 6);
|
|
111
|
+
onLoggedIn(token, cleanedInviteCode ? { inviteCode: cleanedInviteCode } : undefined);
|
|
110
112
|
};
|
|
111
113
|
const handleApple = async () => {
|
|
112
114
|
setBusyProvider('apple');
|
|
@@ -212,6 +214,7 @@ const YaverLoginScreen = ({ onLoggedIn, onCancel, }) => {
|
|
|
212
214
|
<react_native_1.TextInput style={styles.input} placeholder="Email" placeholderTextColor="#666" value={email} onChangeText={setEmail} keyboardType="email-address" autoCapitalize="none" autoCorrect={false}/>
|
|
213
215
|
<react_native_1.TextInput style={styles.input} placeholder="Password" placeholderTextColor="#666" value={password} onChangeText={setPassword} secureTextEntry autoCapitalize="none"/>
|
|
214
216
|
{isSignUp && (<react_native_1.TextInput style={styles.input} placeholder="Confirm Password" placeholderTextColor="#666" value={confirmPassword} onChangeText={setConfirmPassword} secureTextEntry/>)}
|
|
217
|
+
{isSignUp && (<react_native_1.TextInput style={styles.input} placeholder="Invite Code (optional)" placeholderTextColor="#666" value={inviteCode} onChangeText={(value) => setInviteCode(value.toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 6))} autoCapitalize="characters" autoCorrect={false} maxLength={6}/>)}
|
|
215
218
|
|
|
216
219
|
{emailError ? (<react_native_1.Text style={styles.errorText}>{emailError}</react_native_1.Text>) : null}
|
|
217
220
|
|
|
@@ -61,7 +61,7 @@ const YaverMachinePickerScreen = ({ token, currentDeviceId, onPick, onCancel, })
|
|
|
61
61
|
const result = await (0, auth_1.listReachableDevices)(token);
|
|
62
62
|
setList(result);
|
|
63
63
|
if (result.owned.length === 0 && result.shared.length === 0) {
|
|
64
|
-
setError('
|
|
64
|
+
setError('No machines found yet. If you do not have your own computer, redeem a host invite code first. Otherwise run `yaver auth` + `yaver serve` on your machine.');
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
catch (err) {
|
package/dist/P2PClient.d.ts
CHANGED
|
@@ -96,6 +96,19 @@ export declare class P2PClient {
|
|
|
96
96
|
}): Promise<{
|
|
97
97
|
taskId: string;
|
|
98
98
|
}>;
|
|
99
|
+
getVibingEligibility(opts?: {
|
|
100
|
+
projectName?: string;
|
|
101
|
+
bundleId?: string;
|
|
102
|
+
projectPath?: string;
|
|
103
|
+
}): Promise<{
|
|
104
|
+
canVibe: boolean;
|
|
105
|
+
reason?: string;
|
|
106
|
+
guidance?: string;
|
|
107
|
+
projectName?: string;
|
|
108
|
+
projectPath?: string;
|
|
109
|
+
provider?: string;
|
|
110
|
+
repoFullName?: string;
|
|
111
|
+
}>;
|
|
99
112
|
/**
|
|
100
113
|
* After uploading a feedback bundle with `uploadFeedback`, call this
|
|
101
114
|
* with the returned report id to create a fix task on the agent. The
|
package/dist/P2PClient.js
CHANGED
|
@@ -383,6 +383,28 @@ class P2PClient {
|
|
|
383
383
|
}
|
|
384
384
|
return response.json();
|
|
385
385
|
}
|
|
386
|
+
async getVibingEligibility(opts) {
|
|
387
|
+
const identity = resolveAppIdentity(opts);
|
|
388
|
+
const params = new URLSearchParams();
|
|
389
|
+
if (identity.projectName ?? opts?.projectName) {
|
|
390
|
+
params.set('projectName', identity.projectName ?? opts?.projectName ?? '');
|
|
391
|
+
}
|
|
392
|
+
if (identity.bundleId ?? opts?.bundleId) {
|
|
393
|
+
params.set('bundleId', identity.bundleId ?? opts?.bundleId ?? '');
|
|
394
|
+
}
|
|
395
|
+
if (identity.projectPath ?? opts?.projectPath) {
|
|
396
|
+
params.set('projectPath', identity.projectPath ?? opts?.projectPath ?? '');
|
|
397
|
+
}
|
|
398
|
+
const response = await fetch(`${this.baseUrl}/vibing/eligibility?${params.toString()}`, {
|
|
399
|
+
method: 'GET',
|
|
400
|
+
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
401
|
+
});
|
|
402
|
+
if (!response.ok) {
|
|
403
|
+
const text = await response.text().catch(() => '');
|
|
404
|
+
throw new Error(`[P2PClient] Vibing eligibility failed (${response.status}): ${text}`);
|
|
405
|
+
}
|
|
406
|
+
return response.json();
|
|
407
|
+
}
|
|
386
408
|
/**
|
|
387
409
|
* After uploading a feedback bundle with `uploadFeedback`, call this
|
|
388
410
|
* with the returned report id to create a fix task on the agent. The
|
package/dist/YaverFeedback.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { QuickIconColorPreset } from './preferences';
|
|
|
6
6
|
* Call `YaverFeedback.init()` once at app startup.
|
|
7
7
|
*/
|
|
8
8
|
export declare class YaverFeedback {
|
|
9
|
+
private static resolveP2PAuthToken;
|
|
10
|
+
private static rebuildP2PClient;
|
|
9
11
|
/**
|
|
10
12
|
* Initialize the feedback SDK with the given configuration.
|
|
11
13
|
* Typically called in your app's root component or entry file.
|
package/dist/YaverFeedback.js
CHANGED
|
@@ -34,6 +34,7 @@ let config = null;
|
|
|
34
34
|
let enabled = false;
|
|
35
35
|
let p2pClient = null;
|
|
36
36
|
let shakeDetector = null;
|
|
37
|
+
let p2pAuthToken = null;
|
|
37
38
|
/** Ring buffer of captured errors. */
|
|
38
39
|
let errorBuffer = [];
|
|
39
40
|
let maxErrors = 5;
|
|
@@ -57,6 +58,41 @@ const flagCache = new Map();
|
|
|
57
58
|
* Call `YaverFeedback.init()` once at app startup.
|
|
58
59
|
*/
|
|
59
60
|
class YaverFeedback {
|
|
61
|
+
static async resolveP2PAuthToken() {
|
|
62
|
+
if (!config?.authToken)
|
|
63
|
+
return null;
|
|
64
|
+
if (!config.preferredDeviceId)
|
|
65
|
+
return config.authToken;
|
|
66
|
+
const devices = await (0, auth_1.listReachableDevices)(config.authToken);
|
|
67
|
+
const all = [...devices.owned, ...devices.shared];
|
|
68
|
+
const selected = all.find((device) => device.deviceId === config?.preferredDeviceId);
|
|
69
|
+
if (!selected || !selected.isGuest || selected.accessScope !== 'shared-scoped') {
|
|
70
|
+
return config.authToken;
|
|
71
|
+
}
|
|
72
|
+
if (!selected.hostUserId) {
|
|
73
|
+
return config.authToken;
|
|
74
|
+
}
|
|
75
|
+
const delegated = await (0, auth_1.mintGuestSdkToken)(config.authToken, selected.hostUserId, selected.deviceId);
|
|
76
|
+
return delegated.token;
|
|
77
|
+
}
|
|
78
|
+
static async rebuildP2PClient(agentUrl) {
|
|
79
|
+
if (!config)
|
|
80
|
+
return;
|
|
81
|
+
const effectiveUrl = agentUrl ?? config.agentUrl;
|
|
82
|
+
if (!effectiveUrl) {
|
|
83
|
+
p2pClient = null;
|
|
84
|
+
p2pAuthToken = null;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const token = await YaverFeedback.resolveP2PAuthToken();
|
|
88
|
+
if (!token) {
|
|
89
|
+
p2pClient = null;
|
|
90
|
+
p2pAuthToken = null;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
p2pAuthToken = token;
|
|
94
|
+
p2pClient = new P2PClient_1.P2PClient(effectiveUrl, token);
|
|
95
|
+
}
|
|
60
96
|
/**
|
|
61
97
|
* Initialize the feedback SDK with the given configuration.
|
|
62
98
|
* Typically called in your app's root component or entry file.
|
|
@@ -107,7 +143,11 @@ class YaverFeedback {
|
|
|
107
143
|
}
|
|
108
144
|
// Create P2P client if we have a URL
|
|
109
145
|
if (config.agentUrl) {
|
|
146
|
+
p2pAuthToken = config.authToken ?? null;
|
|
110
147
|
p2pClient = new P2PClient_1.P2PClient(config.agentUrl, config.authToken ?? '');
|
|
148
|
+
if (config.authToken) {
|
|
149
|
+
void YaverFeedback.rebuildP2PClient(config.agentUrl);
|
|
150
|
+
}
|
|
111
151
|
}
|
|
112
152
|
else {
|
|
113
153
|
p2pClient = null;
|
|
@@ -228,7 +268,7 @@ class YaverFeedback {
|
|
|
228
268
|
});
|
|
229
269
|
if (result && config) {
|
|
230
270
|
config.agentUrl = result.url;
|
|
231
|
-
|
|
271
|
+
await YaverFeedback.rebuildP2PClient(result.url);
|
|
232
272
|
}
|
|
233
273
|
}
|
|
234
274
|
catch {
|
|
@@ -258,7 +298,7 @@ class YaverFeedback {
|
|
|
258
298
|
if (!result)
|
|
259
299
|
return false;
|
|
260
300
|
config.agentUrl = result.url;
|
|
261
|
-
|
|
301
|
+
await YaverFeedback.rebuildP2PClient(result.url);
|
|
262
302
|
return true;
|
|
263
303
|
}
|
|
264
304
|
catch {
|
|
@@ -304,7 +344,7 @@ class YaverFeedback {
|
|
|
304
344
|
return;
|
|
305
345
|
config.authToken = token;
|
|
306
346
|
if (config.agentUrl) {
|
|
307
|
-
|
|
347
|
+
await YaverFeedback.rebuildP2PClient(config.agentUrl);
|
|
308
348
|
}
|
|
309
349
|
else {
|
|
310
350
|
await YaverFeedback.discoverAgent();
|
|
@@ -344,6 +384,7 @@ class YaverFeedback {
|
|
|
344
384
|
config.preferredDeviceId = deviceId;
|
|
345
385
|
config.agentUrl = undefined;
|
|
346
386
|
p2pClient = null;
|
|
387
|
+
p2pAuthToken = null;
|
|
347
388
|
await YaverFeedback.discoverAgent();
|
|
348
389
|
}
|
|
349
390
|
/** Resolve the currently selected remote machine from the authenticated device list. */
|
|
@@ -368,6 +409,7 @@ class YaverFeedback {
|
|
|
368
409
|
config.agentUrl = undefined;
|
|
369
410
|
}
|
|
370
411
|
p2pClient = null;
|
|
412
|
+
p2pAuthToken = null;
|
|
371
413
|
}
|
|
372
414
|
/**
|
|
373
415
|
* Manually trigger the feedback collection flow.
|
|
@@ -404,7 +446,7 @@ class YaverFeedback {
|
|
|
404
446
|
});
|
|
405
447
|
if (result) {
|
|
406
448
|
config.agentUrl = result.url;
|
|
407
|
-
|
|
449
|
+
await YaverFeedback.rebuildP2PClient(result.url);
|
|
408
450
|
}
|
|
409
451
|
else if (config.autoLogin !== false && !config.preferredDeviceId) {
|
|
410
452
|
// No agent discovered and no device picked yet — prompt the user
|