yaver-feedback-react-native 0.8.1 → 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/README.md +2 -2
- package/dist/AuthOverlay.js +33 -3
- package/dist/Discovery.js +2 -0
- package/dist/FeedbackModal.js +438 -215
- 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 +22 -3
- package/dist/P2PClient.js +45 -2
- package/dist/QuickActionIcon.js +16 -4
- package/dist/YaverFeedback.d.ts +7 -0
- package/dist/YaverFeedback.js +68 -4
- package/dist/__tests__/P2PClient.test.js +30 -0
- package/dist/__tests__/YaverFeedback.test.js +39 -0
- package/dist/auth.d.ts +52 -0
- package/dist/auth.js +75 -0
- package/dist/index.d.ts +7 -6
- package/dist/index.js +10 -5
- package/dist/preferences.d.ts +11 -0
- package/dist/preferences.js +82 -0
- package/dist/types.d.ts +7 -0
- package/package.json +6 -4
- package/src/AuthOverlay.tsx +47 -2
- package/src/Discovery.ts +2 -0
- package/src/FeedbackModal.tsx +507 -258
- package/src/GuestOnboardingScreen.tsx +307 -0
- package/src/LoginScreen.tsx +21 -2
- package/src/MachinePickerScreen.tsx +3 -1
- package/src/P2PClient.ts +69 -3
- package/src/QuickActionIcon.tsx +25 -5
- package/src/YaverFeedback.ts +79 -4
- package/src/__tests__/P2PClient.test.ts +40 -0
- package/src/__tests__/YaverFeedback.test.ts +42 -0
- package/src/auth.ts +137 -0
- package/src/index.ts +14 -4
- package/src/preferences.ts +96 -0
- package/src/types.ts +7 -0
|
@@ -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
|
@@ -4,6 +4,14 @@ export interface FeedbackEvent {
|
|
|
4
4
|
timestamp: string;
|
|
5
5
|
data: any;
|
|
6
6
|
}
|
|
7
|
+
export interface ReloadAck {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
mode: 'dev' | 'bundle';
|
|
10
|
+
acknowledged: boolean;
|
|
11
|
+
message: string;
|
|
12
|
+
nativeChangesDetected?: boolean;
|
|
13
|
+
changeClass?: string;
|
|
14
|
+
}
|
|
7
15
|
/**
|
|
8
16
|
* Lightweight P2P HTTP client for communicating with a Yaver agent.
|
|
9
17
|
*
|
|
@@ -69,9 +77,7 @@ export declare class P2PClient {
|
|
|
69
77
|
projectName?: string;
|
|
70
78
|
bundleId?: string;
|
|
71
79
|
projectPath?: string;
|
|
72
|
-
}): Promise<
|
|
73
|
-
ok: boolean;
|
|
74
|
-
}>;
|
|
80
|
+
}): Promise<ReloadAck>;
|
|
75
81
|
/**
|
|
76
82
|
* Open a vibing session on the connected agent. Vibing is the Yaver
|
|
77
83
|
* interactive coding-agent flow — `/vibing/execute` creates a task with
|
|
@@ -90,6 +96,19 @@ export declare class P2PClient {
|
|
|
90
96
|
}): Promise<{
|
|
91
97
|
taskId: string;
|
|
92
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
|
+
}>;
|
|
93
112
|
/**
|
|
94
113
|
* After uploading a feedback bundle with `uploadFeedback`, call this
|
|
95
114
|
* with the returned report id to create a fix task on the agent. The
|
package/dist/P2PClient.js
CHANGED
|
@@ -291,7 +291,18 @@ class P2PClient {
|
|
|
291
291
|
headers: { Authorization: `Bearer ${this.authToken}` },
|
|
292
292
|
});
|
|
293
293
|
if (primary.ok) {
|
|
294
|
-
|
|
294
|
+
const payload = await primary.json().catch(() => ({}));
|
|
295
|
+
const nativeChangesDetected = payload.nativeChangesDetected === true;
|
|
296
|
+
return {
|
|
297
|
+
ok: true,
|
|
298
|
+
mode: 'dev',
|
|
299
|
+
acknowledged: true,
|
|
300
|
+
nativeChangesDetected,
|
|
301
|
+
changeClass: typeof payload.changeClass === 'string' ? payload.changeClass : undefined,
|
|
302
|
+
message: nativeChangesDetected
|
|
303
|
+
? 'Reload accepted, but native changes need a rebuild.'
|
|
304
|
+
: 'Hot reload request accepted.',
|
|
305
|
+
};
|
|
295
306
|
}
|
|
296
307
|
// Dev mode failed — fall through to bundle rebuild below rather
|
|
297
308
|
// than surfacing the raw error, so the user never has to know
|
|
@@ -321,7 +332,17 @@ class P2PClient {
|
|
|
321
332
|
const text = await res.text().catch(() => '');
|
|
322
333
|
throw new Error(friendlyReloadError(res.status, text));
|
|
323
334
|
}
|
|
324
|
-
|
|
335
|
+
const payload = await res.json().catch(() => ({}));
|
|
336
|
+
return {
|
|
337
|
+
ok: true,
|
|
338
|
+
mode: 'bundle',
|
|
339
|
+
acknowledged: true,
|
|
340
|
+
changeClass: typeof payload.changeClass === 'string' ? payload.changeClass : undefined,
|
|
341
|
+
nativeChangesDetected: payload.nativeChangesDetected === true,
|
|
342
|
+
message: typeof payload.message === 'string' && payload.message.trim()
|
|
343
|
+
? payload.message
|
|
344
|
+
: 'Reload request acknowledged. Agent is rebuilding the bundle.',
|
|
345
|
+
};
|
|
325
346
|
}
|
|
326
347
|
/**
|
|
327
348
|
* Open a vibing session on the connected agent. Vibing is the Yaver
|
|
@@ -362,6 +383,28 @@ class P2PClient {
|
|
|
362
383
|
}
|
|
363
384
|
return response.json();
|
|
364
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
|
+
}
|
|
365
408
|
/**
|
|
366
409
|
* After uploading a feedback bundle with `uploadFeedback`, call this
|
|
367
410
|
* with the returned report id to create a fix task on the agent. The
|
package/dist/QuickActionIcon.js
CHANGED
|
@@ -111,6 +111,7 @@ const QuickActionIcon = ({ color: colorProp, backgroundColor: backgroundColorPro
|
|
|
111
111
|
const dragStart = (0, react_1.useRef)(null);
|
|
112
112
|
const didDrag = (0, react_1.useRef)(false);
|
|
113
113
|
const [userDisabled, setUserDisabled] = (0, react_1.useState)(null);
|
|
114
|
+
const [colorPreset, setColorPreset] = (0, react_1.useState)(null);
|
|
114
115
|
const [shakenThisSession, setShakenThisSession] = (0, react_1.useState)(false);
|
|
115
116
|
const [menuOpen, setMenuOpen] = (0, react_1.useState)(false);
|
|
116
117
|
const [hostSuppressed] = (0, react_1.useState)(() => isRunningInsideYaverHost());
|
|
@@ -123,6 +124,10 @@ const QuickActionIcon = ({ color: colorProp, backgroundColor: backgroundColorPro
|
|
|
123
124
|
if (alive)
|
|
124
125
|
setUserDisabled(v);
|
|
125
126
|
});
|
|
127
|
+
(0, preferences_1.getQuickIconColorPreset)().then((v) => {
|
|
128
|
+
if (alive)
|
|
129
|
+
setColorPreset(v);
|
|
130
|
+
});
|
|
126
131
|
return () => {
|
|
127
132
|
alive = false;
|
|
128
133
|
};
|
|
@@ -146,9 +151,15 @@ const QuickActionIcon = ({ color: colorProp, backgroundColor: backgroundColorPro
|
|
|
146
151
|
void (0, preferences_1.setQuickIconDisabled)(true);
|
|
147
152
|
setMenuOpen(false);
|
|
148
153
|
});
|
|
154
|
+
const colorSub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:quickIconColorChange', (next) => {
|
|
155
|
+
const preset = next?.preset ?? null;
|
|
156
|
+
setColorPreset(preset);
|
|
157
|
+
void (0, preferences_1.setQuickIconColorPreset)(preset);
|
|
158
|
+
});
|
|
149
159
|
return () => {
|
|
150
160
|
showSub.remove();
|
|
151
161
|
hideSub.remove();
|
|
162
|
+
colorSub.remove();
|
|
152
163
|
};
|
|
153
164
|
}, []);
|
|
154
165
|
const panResponder = (0, react_1.useRef)(react_native_1.PanResponder.create({
|
|
@@ -204,6 +215,7 @@ const QuickActionIcon = ({ color: colorProp, backgroundColor: backgroundColorPro
|
|
|
204
215
|
return null;
|
|
205
216
|
if (!YaverFeedback_1.YaverFeedback.isEnabled())
|
|
206
217
|
return null;
|
|
218
|
+
const presetColors = colorPreset ? preferences_1.QUICK_ICON_COLOR_PRESETS[colorPreset] : null;
|
|
207
219
|
const visualSize = size;
|
|
208
220
|
const radius = visualSize / 2;
|
|
209
221
|
return (<react_native_1.Animated.View pointerEvents="box-none" style={[
|
|
@@ -232,16 +244,16 @@ const QuickActionIcon = ({ color: colorProp, backgroundColor: backgroundColorPro
|
|
|
232
244
|
width: visualSize,
|
|
233
245
|
height: visualSize,
|
|
234
246
|
borderRadius: radius,
|
|
235
|
-
backgroundColor,
|
|
236
|
-
borderColor,
|
|
237
|
-
shadowColor,
|
|
247
|
+
backgroundColor: presetColors?.backgroundColor ?? backgroundColor,
|
|
248
|
+
borderColor: presetColors?.borderColor ?? borderColor,
|
|
249
|
+
shadowColor: presetColors?.shadowColor ?? shadowColor,
|
|
238
250
|
opacity: pressed ? 0.85 : 1,
|
|
239
251
|
},
|
|
240
252
|
]}>
|
|
241
253
|
<react_native_1.Text style={[
|
|
242
254
|
styles.iconLabel,
|
|
243
255
|
{
|
|
244
|
-
color: foregroundColor,
|
|
256
|
+
color: presetColors?.foregroundColor ?? foregroundColor,
|
|
245
257
|
fontSize: Math.round(visualSize * 0.5),
|
|
246
258
|
},
|
|
247
259
|
]}>
|
package/dist/YaverFeedback.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { FeedbackConfig, CapturedError } from './types';
|
|
2
2
|
import { P2PClient } from './P2PClient';
|
|
3
|
+
import { QuickIconColorPreset } from './preferences';
|
|
3
4
|
/**
|
|
4
5
|
* Main entry point for the Yaver Feedback SDK.
|
|
5
6
|
* Call `YaverFeedback.init()` once at app startup.
|
|
6
7
|
*/
|
|
7
8
|
export declare class YaverFeedback {
|
|
9
|
+
private static resolveP2PAuthToken;
|
|
10
|
+
private static rebuildP2PClient;
|
|
8
11
|
/**
|
|
9
12
|
* Initialize the feedback SDK with the given configuration.
|
|
10
13
|
* Typically called in your app's root component or entry file.
|
|
@@ -60,6 +63,8 @@ export declare class YaverFeedback {
|
|
|
60
63
|
* newly-selected machine.
|
|
61
64
|
*/
|
|
62
65
|
static setPreferredDevice(deviceId: string): Promise<void>;
|
|
66
|
+
/** Resolve the currently selected remote machine from the authenticated device list. */
|
|
67
|
+
static getSelectedRemoteDevice(): Promise<import("./auth").RemoteDevice | null>;
|
|
63
68
|
/**
|
|
64
69
|
* Sign out: clear cached token + device, tear down the P2P client. The
|
|
65
70
|
* SDK stays enabled; the next feedback trigger will re-prompt for login.
|
|
@@ -223,6 +228,8 @@ export declare class YaverFeedback {
|
|
|
223
228
|
* about the programmatic API.
|
|
224
229
|
*/
|
|
225
230
|
static isQuickIconHidden(): Promise<boolean>;
|
|
231
|
+
static setQuickIconColorPreset(preset: QuickIconColorPreset | null): Promise<void>;
|
|
232
|
+
static getQuickIconColorPreset(): Promise<QuickIconColorPreset | null>;
|
|
226
233
|
/** Clear the persisted "user hid the icon" flag. */
|
|
227
234
|
static resetQuickIconPreference(): Promise<void>;
|
|
228
235
|
/** Tear down the SDK (stop shake detector, clear state). */
|