yaver-feedback-react-native 0.8.12 → 0.9.0
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/app.plugin.js +13 -0
- package/dist/DeployPanel.d.ts +7 -0
- package/dist/DeployPanel.js +354 -0
- package/dist/FeedbackModal.js +408 -63
- package/dist/FloatingButton.js +25 -3
- package/dist/MachinePickerScreen.js +14 -5
- package/dist/P2PClient.d.ts +102 -1
- package/dist/P2PClient.js +313 -0
- package/dist/VibeChatScreen.d.ts +25 -0
- package/dist/VibeChatScreen.js +531 -0
- package/dist/_core/buildFeedbackPrompt.d.ts +13 -0
- package/dist/_core/buildFeedbackPrompt.js +77 -0
- package/dist/capture.d.ts +20 -0
- package/dist/capture.js +82 -0
- package/dist/preferences.d.ts +4 -0
- package/dist/preferences.js +62 -0
- package/dist/types.d.ts +66 -2
- package/dist/voice.d.ts +61 -0
- package/dist/voice.js +246 -0
- package/package.json +26 -3
- package/src/DeployPanel.tsx +403 -0
- package/src/FeedbackModal.tsx +510 -76
- package/src/FloatingButton.tsx +27 -3
- package/src/MachinePickerScreen.tsx +12 -3
- package/src/P2PClient.ts +326 -1
- package/src/VibeChatScreen.tsx +581 -0
- package/src/_core/buildFeedbackPrompt.ts +102 -0
- package/src/capture.ts +82 -0
- package/src/preferences.ts +56 -0
- package/src/types.ts +62 -2
- package/src/voice.ts +270 -0
package/app.plugin.js
CHANGED
|
@@ -51,9 +51,22 @@ function withYaverFeedbackAndroid(config) {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
const permissions = manifest["uses-permission"];
|
|
54
|
+
// Camera + mic for screenshot/voice. The rest are required to
|
|
55
|
+
// make `react-native-record-screen` actually start on modern
|
|
56
|
+
// Android — without FOREGROUND_SERVICE_MEDIA_PROJECTION
|
|
57
|
+
// (API 34+, mandatory) startRecording throws SecurityException,
|
|
58
|
+
// and without POST_NOTIFICATIONS (API 33+) the recording
|
|
59
|
+
// notification fails to post which some OEMs use as a signal
|
|
60
|
+
// to kill the projection a few seconds in. Inject all five
|
|
61
|
+
// unconditionally — listing them does NOT trigger any user
|
|
62
|
+
// prompt; the actual runtime dialogs only fire if the app
|
|
63
|
+
// calls startVideoRecording().
|
|
54
64
|
const requiredPermissions = [
|
|
55
65
|
"android.permission.CAMERA",
|
|
56
66
|
"android.permission.RECORD_AUDIO",
|
|
67
|
+
"android.permission.FOREGROUND_SERVICE",
|
|
68
|
+
"android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION",
|
|
69
|
+
"android.permission.POST_NOTIFICATIONS",
|
|
57
70
|
];
|
|
58
71
|
|
|
59
72
|
for (const perm of requiredPermissions) {
|
|
@@ -0,0 +1,354 @@
|
|
|
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.DeployPanel = void 0;
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const react_native_1 = require("react-native");
|
|
39
|
+
const YaverFeedback_1 = require("./YaverFeedback");
|
|
40
|
+
const TARGET_LABELS = {
|
|
41
|
+
testflight: 'TestFlight',
|
|
42
|
+
playstore: 'Play Store',
|
|
43
|
+
};
|
|
44
|
+
const DeployPanel = ({ onClose }) => {
|
|
45
|
+
const [options, setOptions] = (0, react_1.useState)(null);
|
|
46
|
+
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
47
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
48
|
+
const [status, setStatus] = (0, react_1.useState)(null);
|
|
49
|
+
const [statusTone, setStatusTone] = (0, react_1.useState)('progress');
|
|
50
|
+
const [selected, setSelected] = (0, react_1.useState)('both');
|
|
51
|
+
const [shipping, setShipping] = (0, react_1.useState)(false);
|
|
52
|
+
const resolveAppSlug = (0, react_1.useCallback)(() => {
|
|
53
|
+
const cfg = YaverFeedback_1.YaverFeedback.getConfig();
|
|
54
|
+
const explicit = cfg?.deployAppSlug;
|
|
55
|
+
if (explicit && explicit.trim().length > 0)
|
|
56
|
+
return explicit.trim();
|
|
57
|
+
// Best-effort fallback: bundleId's last dot-segment. iOS gives us
|
|
58
|
+
// `io.yaver.sfmg`; Android gives the same shape. The agent's
|
|
59
|
+
// workspace manifest typically names apps after the project basename
|
|
60
|
+
// which is usually the same word, but the user can override via
|
|
61
|
+
// config.deployAppSlug if it isn't.
|
|
62
|
+
const bundleId = cfg?.bundleId;
|
|
63
|
+
if (bundleId) {
|
|
64
|
+
const tail = bundleId.split('.').pop();
|
|
65
|
+
if (tail)
|
|
66
|
+
return tail;
|
|
67
|
+
}
|
|
68
|
+
return 'main';
|
|
69
|
+
}, []);
|
|
70
|
+
const baseAuthHeaders = (0, react_1.useCallback)(() => {
|
|
71
|
+
const cfg = YaverFeedback_1.YaverFeedback.getConfig();
|
|
72
|
+
const headers = {};
|
|
73
|
+
if (cfg?.authToken)
|
|
74
|
+
headers.Authorization = `Bearer ${cfg.authToken}`;
|
|
75
|
+
const relay = YaverFeedback_1.YaverFeedback.getRelayPassword();
|
|
76
|
+
if (relay)
|
|
77
|
+
headers['X-Relay-Password'] = relay;
|
|
78
|
+
return headers;
|
|
79
|
+
}, []);
|
|
80
|
+
const fetchOptions = (0, react_1.useCallback)(async () => {
|
|
81
|
+
setLoading(true);
|
|
82
|
+
setError(null);
|
|
83
|
+
const cfg = YaverFeedback_1.YaverFeedback.getConfig();
|
|
84
|
+
if (!cfg?.agentUrl) {
|
|
85
|
+
setError('Not connected to a Yaver agent yet.');
|
|
86
|
+
setLoading(false);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const app = resolveAppSlug();
|
|
90
|
+
const url = `${cfg.agentUrl.replace(/\/$/, '')}/fleet/deploy-options?app=${encodeURIComponent(app)}`;
|
|
91
|
+
try {
|
|
92
|
+
const resp = await fetch(url, { headers: baseAuthHeaders() });
|
|
93
|
+
if (!resp.ok) {
|
|
94
|
+
const text = await resp.text().catch(() => '');
|
|
95
|
+
throw new Error(`fetch failed (${resp.status}): ${text || resp.statusText}`);
|
|
96
|
+
}
|
|
97
|
+
const json = (await resp.json());
|
|
98
|
+
setOptions(json);
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
setLoading(false);
|
|
105
|
+
}
|
|
106
|
+
}, [baseAuthHeaders, resolveAppSlug]);
|
|
107
|
+
(0, react_1.useEffect)(() => {
|
|
108
|
+
void fetchOptions();
|
|
109
|
+
}, [fetchOptions]);
|
|
110
|
+
const pickedTargets = () => {
|
|
111
|
+
switch (selected) {
|
|
112
|
+
case 'testflight':
|
|
113
|
+
return ['testflight'];
|
|
114
|
+
case 'playstore':
|
|
115
|
+
return ['playstore'];
|
|
116
|
+
default:
|
|
117
|
+
return ['testflight', 'playstore'];
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const machineRow = (d) => {
|
|
121
|
+
const targets = pickedTargets();
|
|
122
|
+
const blockers = [];
|
|
123
|
+
let allOK = true;
|
|
124
|
+
for (const t of targets) {
|
|
125
|
+
const cap = d.capabilities.find((c) => c.target === t);
|
|
126
|
+
if (!cap) {
|
|
127
|
+
allOK = false;
|
|
128
|
+
blockers.push(`${TARGET_LABELS[t] ?? t}: no capability data`);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (!cap.ok) {
|
|
132
|
+
allOK = false;
|
|
133
|
+
if (cap.reason)
|
|
134
|
+
blockers.push(`${TARGET_LABELS[t] ?? t}: ${cap.reason}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!d.probed && allOK) {
|
|
138
|
+
allOK = false;
|
|
139
|
+
blockers.push(d.probeError || "couldn't reach this machine");
|
|
140
|
+
}
|
|
141
|
+
const label = (d.alias && d.alias.length > 0 ? d.alias : d.name) +
|
|
142
|
+
(d.isLocal ? ' (this phone’s primary)' : '');
|
|
143
|
+
return (<react_native_1.Pressable key={d.deviceId} disabled={!allOK || shipping} onPress={() => triggerDeploy(d.deviceId)} style={({ pressed }) => [
|
|
144
|
+
styles.row,
|
|
145
|
+
!allOK && styles.rowDisabled,
|
|
146
|
+
pressed && allOK && styles.rowPressed,
|
|
147
|
+
]}>
|
|
148
|
+
<react_native_1.Text style={styles.rowName}>{label}</react_native_1.Text>
|
|
149
|
+
<react_native_1.Text style={[styles.rowMeta, !allOK && styles.rowMetaWarning]}>
|
|
150
|
+
{d.platform} {'·'} {allOK ? 'ready' : blockers.join(' · ')}
|
|
151
|
+
</react_native_1.Text>
|
|
152
|
+
</react_native_1.Pressable>);
|
|
153
|
+
};
|
|
154
|
+
const triggerDeploy = async (machine) => {
|
|
155
|
+
if (!options)
|
|
156
|
+
return;
|
|
157
|
+
setShipping(true);
|
|
158
|
+
setStatus(`starting deploy on ${prettyMachineName(machine)}…`);
|
|
159
|
+
setStatusTone('progress');
|
|
160
|
+
const cfg = YaverFeedback_1.YaverFeedback.getConfig();
|
|
161
|
+
if (!cfg?.agentUrl) {
|
|
162
|
+
setStatus('Not connected to a Yaver agent yet.');
|
|
163
|
+
setStatusTone('error');
|
|
164
|
+
setShipping(false);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const targets = pickedTargets();
|
|
168
|
+
const body = {
|
|
169
|
+
app: options.app,
|
|
170
|
+
machine,
|
|
171
|
+
};
|
|
172
|
+
if (targets.length === 1) {
|
|
173
|
+
body.target = targets[0];
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
body.targets = targets;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const resp = await fetch(`${cfg.agentUrl.replace(/\/$/, '')}/deploy/ship`, {
|
|
180
|
+
method: 'POST',
|
|
181
|
+
headers: { ...baseAuthHeaders(), 'Content-Type': 'application/json' },
|
|
182
|
+
body: JSON.stringify(body),
|
|
183
|
+
});
|
|
184
|
+
if (!resp.ok) {
|
|
185
|
+
const text = await resp.text().catch(() => '');
|
|
186
|
+
throw new Error(`ship failed (${resp.status}): ${text || resp.statusText}`);
|
|
187
|
+
}
|
|
188
|
+
setStatus('deploy started — track progress in the desktop / web tab');
|
|
189
|
+
setStatusTone('success');
|
|
190
|
+
// Auto-close shortly so the user can keep using their app. Keep
|
|
191
|
+
// this in sync with the iOS / Android pane delays.
|
|
192
|
+
setTimeout(() => onClose(), 1600);
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
setStatus(err instanceof Error ? err.message : String(err));
|
|
196
|
+
setStatusTone('error');
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
setShipping(false);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
const prettyMachineName = (id) => {
|
|
203
|
+
const d = options?.devices.find((x) => x.deviceId === id);
|
|
204
|
+
if (!d)
|
|
205
|
+
return id;
|
|
206
|
+
return d.alias && d.alias.length > 0 ? d.alias : d.name;
|
|
207
|
+
};
|
|
208
|
+
const targetButton = (value, label) => (<react_native_1.Pressable key={value} onPress={() => setSelected(value)} style={[styles.segBtn, selected === value && styles.segBtnSelected]}>
|
|
209
|
+
<react_native_1.Text style={[styles.segText, selected === value && styles.segTextSelected]}>{label}</react_native_1.Text>
|
|
210
|
+
</react_native_1.Pressable>);
|
|
211
|
+
return (<react_native_1.View style={styles.container}>
|
|
212
|
+
<react_native_1.View style={styles.headerRow}>
|
|
213
|
+
<react_native_1.Text style={styles.title}>Deploy</react_native_1.Text>
|
|
214
|
+
<react_native_1.Pressable onPress={onClose} hitSlop={10}>
|
|
215
|
+
<react_native_1.Text style={styles.closeIcon}>✕</react_native_1.Text>
|
|
216
|
+
</react_native_1.Pressable>
|
|
217
|
+
</react_native_1.View>
|
|
218
|
+
<react_native_1.Text style={styles.subtitle}>
|
|
219
|
+
{loading
|
|
220
|
+
? 'loading machines…'
|
|
221
|
+
: options
|
|
222
|
+
? `${options.devices.length} machine${options.devices.length === 1 ? '' : 's'} — pick a target, then tap to deploy`
|
|
223
|
+
: 'no data yet'}
|
|
224
|
+
</react_native_1.Text>
|
|
225
|
+
|
|
226
|
+
<react_native_1.View style={styles.segment}>
|
|
227
|
+
{targetButton('testflight', 'TestFlight')}
|
|
228
|
+
{targetButton('playstore', 'Play Store')}
|
|
229
|
+
{targetButton('both', 'Both')}
|
|
230
|
+
</react_native_1.View>
|
|
231
|
+
|
|
232
|
+
{loading ? (<react_native_1.View style={styles.loading}>
|
|
233
|
+
<react_native_1.ActivityIndicator color="rgba(255,255,255,0.6)"/>
|
|
234
|
+
</react_native_1.View>) : error ? (<react_native_1.Text style={styles.error}>{error}</react_native_1.Text>) : options ? (<react_native_1.ScrollView style={styles.list}>{options.devices.map(machineRow)}</react_native_1.ScrollView>) : null}
|
|
235
|
+
|
|
236
|
+
{status && (<react_native_1.Text style={[
|
|
237
|
+
styles.status,
|
|
238
|
+
statusTone === 'success' && styles.statusSuccess,
|
|
239
|
+
statusTone === 'error' && styles.statusError,
|
|
240
|
+
]}>
|
|
241
|
+
{status}
|
|
242
|
+
</react_native_1.Text>)}
|
|
243
|
+
</react_native_1.View>);
|
|
244
|
+
};
|
|
245
|
+
exports.DeployPanel = DeployPanel;
|
|
246
|
+
const styles = react_native_1.StyleSheet.create({
|
|
247
|
+
container: {
|
|
248
|
+
backgroundColor: 'rgba(14,12,28,0.92)',
|
|
249
|
+
borderRadius: 16,
|
|
250
|
+
paddingHorizontal: 16,
|
|
251
|
+
paddingTop: 14,
|
|
252
|
+
paddingBottom: 18,
|
|
253
|
+
marginVertical: 8,
|
|
254
|
+
borderWidth: 1,
|
|
255
|
+
borderColor: 'rgba(255,255,255,0.08)',
|
|
256
|
+
},
|
|
257
|
+
headerRow: {
|
|
258
|
+
flexDirection: 'row',
|
|
259
|
+
alignItems: 'center',
|
|
260
|
+
justifyContent: 'space-between',
|
|
261
|
+
},
|
|
262
|
+
title: {
|
|
263
|
+
color: '#fff',
|
|
264
|
+
fontSize: 16,
|
|
265
|
+
fontWeight: '600',
|
|
266
|
+
},
|
|
267
|
+
closeIcon: {
|
|
268
|
+
color: 'rgba(255,255,255,0.55)',
|
|
269
|
+
fontSize: 18,
|
|
270
|
+
paddingHorizontal: 4,
|
|
271
|
+
},
|
|
272
|
+
subtitle: {
|
|
273
|
+
color: 'rgba(255,255,255,0.55)',
|
|
274
|
+
fontSize: 12,
|
|
275
|
+
marginTop: 2,
|
|
276
|
+
},
|
|
277
|
+
segment: {
|
|
278
|
+
flexDirection: 'row',
|
|
279
|
+
backgroundColor: 'rgba(255,255,255,0.08)',
|
|
280
|
+
borderRadius: 10,
|
|
281
|
+
padding: 3,
|
|
282
|
+
marginTop: 14,
|
|
283
|
+
},
|
|
284
|
+
segBtn: {
|
|
285
|
+
flex: 1,
|
|
286
|
+
alignItems: 'center',
|
|
287
|
+
paddingVertical: 7,
|
|
288
|
+
borderRadius: 8,
|
|
289
|
+
},
|
|
290
|
+
segBtnSelected: {
|
|
291
|
+
backgroundColor: 'rgba(127,140,247,0.65)',
|
|
292
|
+
},
|
|
293
|
+
segText: {
|
|
294
|
+
color: 'rgba(255,255,255,0.65)',
|
|
295
|
+
fontSize: 13,
|
|
296
|
+
fontWeight: '500',
|
|
297
|
+
},
|
|
298
|
+
segTextSelected: {
|
|
299
|
+
color: '#fff',
|
|
300
|
+
fontWeight: '600',
|
|
301
|
+
},
|
|
302
|
+
loading: {
|
|
303
|
+
paddingVertical: 32,
|
|
304
|
+
alignItems: 'center',
|
|
305
|
+
},
|
|
306
|
+
error: {
|
|
307
|
+
color: 'rgb(255,115,115)',
|
|
308
|
+
fontSize: 12,
|
|
309
|
+
marginTop: 14,
|
|
310
|
+
},
|
|
311
|
+
list: {
|
|
312
|
+
marginTop: 14,
|
|
313
|
+
maxHeight: 260,
|
|
314
|
+
},
|
|
315
|
+
row: {
|
|
316
|
+
backgroundColor: 'rgba(255,255,255,0.06)',
|
|
317
|
+
borderRadius: 12,
|
|
318
|
+
paddingHorizontal: 14,
|
|
319
|
+
paddingVertical: 12,
|
|
320
|
+
marginBottom: 8,
|
|
321
|
+
},
|
|
322
|
+
rowPressed: {
|
|
323
|
+
backgroundColor: 'rgba(255,255,255,0.10)',
|
|
324
|
+
},
|
|
325
|
+
rowDisabled: {
|
|
326
|
+
opacity: 0.55,
|
|
327
|
+
backgroundColor: 'rgba(255,255,255,0.03)',
|
|
328
|
+
},
|
|
329
|
+
rowName: {
|
|
330
|
+
color: '#fff',
|
|
331
|
+
fontSize: 15,
|
|
332
|
+
fontWeight: '600',
|
|
333
|
+
},
|
|
334
|
+
rowMeta: {
|
|
335
|
+
color: 'rgba(255,255,255,0.55)',
|
|
336
|
+
fontSize: 12,
|
|
337
|
+
marginTop: 2,
|
|
338
|
+
},
|
|
339
|
+
rowMetaWarning: {
|
|
340
|
+
color: 'rgb(255,178,115)',
|
|
341
|
+
},
|
|
342
|
+
status: {
|
|
343
|
+
color: 'rgba(255,255,255,0.55)',
|
|
344
|
+
fontSize: 12,
|
|
345
|
+
marginTop: 12,
|
|
346
|
+
textAlign: 'center',
|
|
347
|
+
},
|
|
348
|
+
statusSuccess: {
|
|
349
|
+
color: 'rgb(34,197,94)',
|
|
350
|
+
},
|
|
351
|
+
statusError: {
|
|
352
|
+
color: 'rgb(255,115,115)',
|
|
353
|
+
},
|
|
354
|
+
});
|