@mushi-mushi/react-native 0.22.0 → 0.24.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/README.md +19 -2
- package/dist/index.cjs +1269 -479
- package/dist/index.d.cts +170 -7
- package/dist/index.d.ts +170 -7
- package/dist/index.js +1118 -323
- package/package.json +3 -9
package/dist/index.cjs
CHANGED
|
@@ -48,12 +48,184 @@ __export(index_exports, {
|
|
|
48
48
|
module.exports = __toCommonJS(index_exports);
|
|
49
49
|
|
|
50
50
|
// src/provider.tsx
|
|
51
|
-
var
|
|
52
|
-
var
|
|
53
|
-
var
|
|
51
|
+
var import_react5 = require("react");
|
|
52
|
+
var import_react_native6 = require("react-native");
|
|
53
|
+
var import_core10 = require("@mushi-mushi/core");
|
|
54
|
+
var import_reporter_channels2 = require("@mushi-mushi/core/reporter-channels");
|
|
55
|
+
var import_reporter_ui3 = require("@mushi-mushi/core/reporter-ui");
|
|
54
56
|
|
|
55
|
-
// src/
|
|
57
|
+
// src/storage/reporter-flags.ts
|
|
58
|
+
async function readReporterFlag(projectId, flag) {
|
|
59
|
+
try {
|
|
60
|
+
const store = (await import("@react-native-async-storage/async-storage")).default;
|
|
61
|
+
return await store.getItem(`@mushi:${flag}:${projectId}`);
|
|
62
|
+
} catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function writeReporterFlag(projectId, flag, value) {
|
|
67
|
+
try {
|
|
68
|
+
const store = (await import("@react-native-async-storage/async-storage")).default;
|
|
69
|
+
await store.setItem(`@mushi:${flag}:${projectId}`, value);
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/components/MushiUpdateToast.tsx
|
|
75
|
+
var import_react = require("react");
|
|
76
|
+
var import_react_native = require("react-native");
|
|
77
|
+
var import_core2 = require("@mushi-mushi/core");
|
|
78
|
+
var import_reporter_ui = require("@mushi-mushi/core/reporter-ui");
|
|
79
|
+
|
|
80
|
+
// src/theme.ts
|
|
56
81
|
var import_core = require("@mushi-mushi/core");
|
|
82
|
+
function hexToRgb(hex) {
|
|
83
|
+
const m = /^#([0-9a-f]{3}|[0-9a-f]{6})([0-9a-f]{2})?$/i.exec(hex.trim());
|
|
84
|
+
if (!m) return null;
|
|
85
|
+
const h = m[1].length === 3 ? m[1].split("").map((c) => c + c).join("") : m[1];
|
|
86
|
+
return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16));
|
|
87
|
+
}
|
|
88
|
+
function luminance([r, g, b]) {
|
|
89
|
+
const lin = (c) => {
|
|
90
|
+
const s5 = c / 255;
|
|
91
|
+
return s5 <= 0.03928 ? s5 / 12.92 : ((s5 + 0.055) / 1.055) ** 2.4;
|
|
92
|
+
};
|
|
93
|
+
return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
|
|
94
|
+
}
|
|
95
|
+
function contrastingText(bg, fallback) {
|
|
96
|
+
const rgb = hexToRgb(bg);
|
|
97
|
+
if (!rgb) return fallback;
|
|
98
|
+
const l = luminance(rgb);
|
|
99
|
+
return 1.05 / (l + 0.05) >= (l + 0.05) / 0.05 ? "#FFFFFF" : "#000000";
|
|
100
|
+
}
|
|
101
|
+
function resolveRNTheme(dark, override = {}) {
|
|
102
|
+
const pal = (0, import_core.mushiPalette)(dark ? "dark" : "light");
|
|
103
|
+
const base = {
|
|
104
|
+
bg: dark ? pal.paper : pal.paperRaised,
|
|
105
|
+
fg: pal.ink,
|
|
106
|
+
muted: pal.inkMuted,
|
|
107
|
+
surface: dark ? pal.paperRaised : pal.paper,
|
|
108
|
+
border: pal.ruleStrong,
|
|
109
|
+
accent: pal.ink,
|
|
110
|
+
accentFg: dark ? pal.paper : pal.paperRaised,
|
|
111
|
+
success: pal.ok,
|
|
112
|
+
error: pal.danger,
|
|
113
|
+
fontFamily: void 0,
|
|
114
|
+
radius: import_core.MUSHI_RADIUS.card
|
|
115
|
+
};
|
|
116
|
+
const defined = Object.fromEntries(
|
|
117
|
+
Object.entries(override).filter(([, v]) => v !== void 0 && v !== null && v !== "")
|
|
118
|
+
);
|
|
119
|
+
const merged = { ...base, ...defined };
|
|
120
|
+
if (defined.accent && !defined.accentFg) merged.accentFg = contrastingText(defined.accent, base.accentFg);
|
|
121
|
+
return {
|
|
122
|
+
...merged,
|
|
123
|
+
backdrop: dark ? "rgba(0,0,0,0.6)" : "rgba(0,0,0,0.35)",
|
|
124
|
+
disabled: dark ? import_core.MUSHI_CONTROL_DISABLED.dark : import_core.MUSHI_CONTROL_DISABLED.light,
|
|
125
|
+
disabledFg: pal.inkFaint
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// src/components/MushiUpdateToast.tsx
|
|
130
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
131
|
+
var AUTO_HIDE_MS = 8e3;
|
|
132
|
+
var MushiUpdateToast = ({ message, onView, onDismiss, theme }) => {
|
|
133
|
+
const colors = resolveRNTheme((0, import_react_native.useColorScheme)() === "dark", theme);
|
|
134
|
+
const copy = (0, import_reporter_ui.reporterCopy)(
|
|
135
|
+
(0, import_reporter_ui.resolveReporterLocale)(typeof navigator !== "undefined" ? navigator.language : void 0)
|
|
136
|
+
).ui;
|
|
137
|
+
const font = colors.fontFamily ? { fontFamily: colors.fontFamily } : null;
|
|
138
|
+
(0, import_react.useEffect)(() => {
|
|
139
|
+
const timer = setTimeout(onDismiss, AUTO_HIDE_MS);
|
|
140
|
+
return () => clearTimeout(timer);
|
|
141
|
+
}, [onDismiss]);
|
|
142
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
143
|
+
import_react_native.View,
|
|
144
|
+
{
|
|
145
|
+
style: [s.toast, { backgroundColor: colors.bg, borderColor: colors.border, borderRadius: colors.radius }],
|
|
146
|
+
accessibilityLiveRegion: "polite",
|
|
147
|
+
accessibilityRole: "alert",
|
|
148
|
+
children: [
|
|
149
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: [s.text, { color: colors.fg }, font], numberOfLines: 2, children: message }),
|
|
150
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
151
|
+
import_react_native.TouchableOpacity,
|
|
152
|
+
{
|
|
153
|
+
onPress: onView,
|
|
154
|
+
accessibilityRole: "button",
|
|
155
|
+
style: [s.btn, { backgroundColor: colors.accent, borderRadius: colors.radius }],
|
|
156
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: [{ color: colors.accentFg, fontWeight: "600" }, font], children: copy.view })
|
|
157
|
+
}
|
|
158
|
+
),
|
|
159
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
160
|
+
import_react_native.TouchableOpacity,
|
|
161
|
+
{
|
|
162
|
+
onPress: onDismiss,
|
|
163
|
+
accessibilityRole: "button",
|
|
164
|
+
accessibilityLabel: copy.done,
|
|
165
|
+
hitSlop: { top: 10, bottom: 10, left: 10, right: 10 },
|
|
166
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: [{ color: colors.muted, fontSize: 18 }, font], children: "\xD7" })
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
var s = import_react_native.StyleSheet.create({
|
|
174
|
+
toast: {
|
|
175
|
+
position: "absolute",
|
|
176
|
+
left: 16,
|
|
177
|
+
right: 16,
|
|
178
|
+
bottom: 32,
|
|
179
|
+
flexDirection: "row",
|
|
180
|
+
alignItems: "center",
|
|
181
|
+
gap: 10,
|
|
182
|
+
paddingVertical: 10,
|
|
183
|
+
paddingHorizontal: 14,
|
|
184
|
+
borderWidth: import_react_native.StyleSheet.hairlineWidth,
|
|
185
|
+
shadowColor: import_core2.MUSHI_SHADOW_INK,
|
|
186
|
+
shadowOpacity: 0.18,
|
|
187
|
+
shadowRadius: 12,
|
|
188
|
+
shadowOffset: { width: 0, height: 6 },
|
|
189
|
+
elevation: 6
|
|
190
|
+
},
|
|
191
|
+
text: { flex: 1, fontSize: 14 },
|
|
192
|
+
btn: { paddingHorizontal: 12, paddingVertical: 6 }
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// src/optional-modules.ts
|
|
196
|
+
function unwrap(mod, key) {
|
|
197
|
+
if (!mod || typeof mod !== "object" && typeof mod !== "function") return null;
|
|
198
|
+
if (key in mod) return mod;
|
|
199
|
+
const def = mod.default;
|
|
200
|
+
return def && (typeof def === "object" || typeof def === "function") && key in def ? def : null;
|
|
201
|
+
}
|
|
202
|
+
function resolveNetInfo(injected) {
|
|
203
|
+
if (injected) return unwrap(injected, "addEventListener");
|
|
204
|
+
try {
|
|
205
|
+
return unwrap(require("@react-native-community/netinfo"), "addEventListener");
|
|
206
|
+
} catch {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function resolveViewShot(injected) {
|
|
211
|
+
if (injected) return unwrap(injected, "captureScreen");
|
|
212
|
+
try {
|
|
213
|
+
return unwrap(require("react-native-view-shot"), "captureScreen");
|
|
214
|
+
} catch {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function resolveExpoSensors(injected) {
|
|
219
|
+
if (injected) return unwrap(injected, "Accelerometer");
|
|
220
|
+
try {
|
|
221
|
+
return unwrap(require("expo-sensors"), "Accelerometer");
|
|
222
|
+
} catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/capture/console-capture.ts
|
|
228
|
+
var import_core3 = require("@mushi-mushi/core");
|
|
57
229
|
function setupConsoleCapture(maxEntries = 100) {
|
|
58
230
|
const entries = [];
|
|
59
231
|
const methods = ["log", "warn", "error"];
|
|
@@ -64,7 +236,7 @@ function setupConsoleCapture(maxEntries = 100) {
|
|
|
64
236
|
console[method] = (...args) => {
|
|
65
237
|
entries.push({
|
|
66
238
|
level: method,
|
|
67
|
-
message: (0,
|
|
239
|
+
message: (0, import_core3.scrubPii)(args.map(String).join(" ").slice(0, 500)),
|
|
68
240
|
timestamp: Date.now()
|
|
69
241
|
});
|
|
70
242
|
if (entries.length > maxEntries) entries.shift();
|
|
@@ -140,28 +312,28 @@ function stripQueryParams(url) {
|
|
|
140
312
|
}
|
|
141
313
|
|
|
142
314
|
// src/capture/device-info.ts
|
|
143
|
-
var
|
|
315
|
+
var import_react_native2 = require("react-native");
|
|
144
316
|
function getDeviceInfo() {
|
|
145
|
-
const { width, height } =
|
|
317
|
+
const { width, height } = import_react_native2.Dimensions.get("window");
|
|
146
318
|
const locale = (
|
|
147
319
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
148
|
-
|
|
149
|
-
|
|
320
|
+
import_react_native2.NativeModules.SettingsManager?.settings?.AppleLocale ?? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
321
|
+
import_react_native2.NativeModules.I18nManager?.localeIdentifier ?? "en"
|
|
150
322
|
);
|
|
151
323
|
let timezone = "UTC";
|
|
152
324
|
try {
|
|
153
325
|
timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
154
326
|
} catch {
|
|
155
327
|
}
|
|
156
|
-
const appVersion =
|
|
328
|
+
const appVersion = import_react_native2.NativeModules.RNDeviceInfo?.appVersion ?? import_react_native2.NativeModules.RNBuildConfig?.VERSION_NAME ?? String(import_react_native2.Platform.Version);
|
|
157
329
|
return {
|
|
158
|
-
platform:
|
|
159
|
-
osVersion:
|
|
330
|
+
platform: import_react_native2.Platform.OS,
|
|
331
|
+
osVersion: import_react_native2.Platform.Version,
|
|
160
332
|
screenWidth: width,
|
|
161
333
|
screenHeight: height,
|
|
162
|
-
pixelRatio:
|
|
163
|
-
fontScale:
|
|
164
|
-
systemName:
|
|
334
|
+
pixelRatio: import_react_native2.PixelRatio.get(),
|
|
335
|
+
fontScale: import_react_native2.PixelRatio.getFontScale(),
|
|
336
|
+
systemName: import_react_native2.Platform.OS,
|
|
165
337
|
locale,
|
|
166
338
|
timezone,
|
|
167
339
|
appVersion
|
|
@@ -193,7 +365,7 @@ function getDeviceFingerprintHash(device) {
|
|
|
193
365
|
}
|
|
194
366
|
|
|
195
367
|
// src/storage/async-storage-queue.ts
|
|
196
|
-
var
|
|
368
|
+
var import_core4 = require("@mushi-mushi/core");
|
|
197
369
|
|
|
198
370
|
// src/storage/secure-storage.ts
|
|
199
371
|
var REPORTER_TOKEN_KEY = "@mushi:reporter_token";
|
|
@@ -393,19 +565,19 @@ var AsyncStorageQueue = class {
|
|
|
393
565
|
scrubReportPii(report) {
|
|
394
566
|
const next = { ...report };
|
|
395
567
|
if (typeof next.description === "string") {
|
|
396
|
-
next.description = (0,
|
|
568
|
+
next.description = (0, import_core4.scrubPii)(next.description);
|
|
397
569
|
}
|
|
398
570
|
if (typeof next.summary === "string") {
|
|
399
|
-
next.summary = (0,
|
|
571
|
+
next.summary = (0, import_core4.scrubPii)(next.summary);
|
|
400
572
|
}
|
|
401
573
|
if (Array.isArray(next.breadcrumbs)) {
|
|
402
574
|
next.breadcrumbs = next.breadcrumbs.map(
|
|
403
|
-
(b) => typeof b?.message === "string" ? { ...b, message: (0,
|
|
575
|
+
(b) => typeof b?.message === "string" ? { ...b, message: (0, import_core4.scrubPii)(b.message) } : b
|
|
404
576
|
);
|
|
405
577
|
}
|
|
406
578
|
if (Array.isArray(next.consoleLogs)) {
|
|
407
579
|
next.consoleLogs = next.consoleLogs.map(
|
|
408
|
-
(entry) => typeof entry?.message === "string" ? { ...entry, message: (0,
|
|
580
|
+
(entry) => typeof entry?.message === "string" ? { ...entry, message: (0, import_core4.scrubPii)(entry.message) } : entry
|
|
409
581
|
);
|
|
410
582
|
}
|
|
411
583
|
return next;
|
|
@@ -447,7 +619,7 @@ async function sendWithRetry(item, ctx, attempt = 0) {
|
|
|
447
619
|
try {
|
|
448
620
|
const res = await fetch(`${ctx.apiEndpoint}/v1/reports`, {
|
|
449
621
|
method: "POST",
|
|
450
|
-
headers: (0,
|
|
622
|
+
headers: (0, import_core4.buildSdkIngestHeaders)({
|
|
451
623
|
apiKey: ctx.apiKey,
|
|
452
624
|
projectId: ctx.projectId,
|
|
453
625
|
sdkPackage: ctx.sdkPackage,
|
|
@@ -456,7 +628,7 @@ async function sendWithRetry(item, ctx, attempt = 0) {
|
|
|
456
628
|
internalKind
|
|
457
629
|
}),
|
|
458
630
|
body: JSON.stringify(item.report),
|
|
459
|
-
[
|
|
631
|
+
[import_core4.MUSHI_INTERNAL_INIT_MARKER]: internalKind
|
|
460
632
|
});
|
|
461
633
|
if ((res.status === 429 || res.status >= 500) && attempt < 3) {
|
|
462
634
|
await sleep(delay);
|
|
@@ -476,51 +648,111 @@ function sleep(ms) {
|
|
|
476
648
|
}
|
|
477
649
|
|
|
478
650
|
// src/components/MushiBottomSheet.tsx
|
|
479
|
-
var
|
|
480
|
-
var
|
|
481
|
-
var
|
|
651
|
+
var import_react2 = require("react");
|
|
652
|
+
var import_react_native3 = require("react-native");
|
|
653
|
+
var import_core5 = require("@mushi-mushi/core");
|
|
654
|
+
var import_reporter_ui2 = require("@mushi-mushi/core/reporter-ui");
|
|
482
655
|
var import_i18n = require("@mushi-mushi/web/i18n");
|
|
483
656
|
|
|
484
|
-
// src/reporter-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
657
|
+
// src/reporter-thread.ts
|
|
658
|
+
var import_reporter_channels = require("@mushi-mushi/core/reporter-channels");
|
|
659
|
+
var THREAD_LOAD_TIMEOUT_MS = 12e3;
|
|
660
|
+
function settleWithin(p, ms) {
|
|
661
|
+
return new Promise((resolve) => {
|
|
662
|
+
const timer = setTimeout(() => resolve(null), ms);
|
|
663
|
+
p.then(
|
|
664
|
+
(value) => {
|
|
665
|
+
clearTimeout(timer);
|
|
666
|
+
resolve(value);
|
|
667
|
+
},
|
|
668
|
+
() => {
|
|
669
|
+
clearTimeout(timer);
|
|
670
|
+
resolve(null);
|
|
671
|
+
}
|
|
672
|
+
);
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
function loadReporterThread(load, reportId, timeoutMs = THREAD_LOAD_TIMEOUT_MS) {
|
|
676
|
+
let started;
|
|
677
|
+
try {
|
|
678
|
+
started = load(reportId);
|
|
679
|
+
} catch {
|
|
680
|
+
return Promise.resolve(null);
|
|
681
|
+
}
|
|
682
|
+
return settleWithin(started, timeoutMs);
|
|
683
|
+
}
|
|
684
|
+
async function markReporterReportRead(client, reporterToken, reportId) {
|
|
685
|
+
try {
|
|
686
|
+
const v2 = await (0, import_reporter_channels.reporterChannels)(client).markReportRead(reportId, reporterToken);
|
|
687
|
+
if (v2.ok) return v2.data?.marked_read ?? 0;
|
|
688
|
+
if (v2.error?.status !== 404) return 0;
|
|
689
|
+
const res = await client.listNotifications(reporterToken, { limit: 100 });
|
|
690
|
+
if (!res.ok) return 0;
|
|
691
|
+
const rows = res.data?.notifications ?? [];
|
|
692
|
+
const ids = rows.filter((n) => !n.read_at && n.payload?.reportId === reportId).map((n) => String(n.id));
|
|
693
|
+
let marked = 0;
|
|
694
|
+
for (const id of ids) {
|
|
695
|
+
const r = await client.markNotificationRead(id, reporterToken);
|
|
696
|
+
if (r.ok) marked++;
|
|
697
|
+
}
|
|
698
|
+
return marked;
|
|
699
|
+
} catch {
|
|
700
|
+
return 0;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
function createReporterUpdateHub(fetchUpdates) {
|
|
704
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
705
|
+
async function emit() {
|
|
706
|
+
let updates = null;
|
|
707
|
+
try {
|
|
708
|
+
updates = await fetchUpdates();
|
|
709
|
+
} catch {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
if (updates) {
|
|
713
|
+
for (const cb of listeners) {
|
|
714
|
+
try {
|
|
715
|
+
cb(updates);
|
|
716
|
+
} catch {
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return updates;
|
|
507
721
|
}
|
|
722
|
+
return {
|
|
723
|
+
subscribe(cb) {
|
|
724
|
+
listeners.add(cb);
|
|
725
|
+
void emit();
|
|
726
|
+
return () => {
|
|
727
|
+
listeners.delete(cb);
|
|
728
|
+
};
|
|
729
|
+
},
|
|
730
|
+
emit,
|
|
731
|
+
listenerCount: () => listeners.size
|
|
732
|
+
};
|
|
508
733
|
}
|
|
509
734
|
|
|
510
735
|
// src/components/MushiBottomSheet.tsx
|
|
511
|
-
var
|
|
512
|
-
var { height: SCREEN_HEIGHT } =
|
|
736
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
737
|
+
var { height: SCREEN_HEIGHT } = import_react_native3.Dimensions.get("window");
|
|
513
738
|
var SHEET_HEIGHT = SCREEN_HEIGHT * 0.55;
|
|
514
739
|
var DISMISS_THRESHOLD = 80;
|
|
515
|
-
var CATEGORY_KEYS = ["bug", "slow", "visual", "confusing", "other"];
|
|
516
740
|
var CATEGORY_EMOJI = {
|
|
517
741
|
bug: "\u{1F41B}",
|
|
518
742
|
slow: "\u{1F422}",
|
|
519
743
|
visual: "\u{1F3A8}",
|
|
520
744
|
confusing: "\u{1F615}",
|
|
521
|
-
|
|
745
|
+
idea: "\u{1F4A1}"
|
|
522
746
|
};
|
|
523
|
-
|
|
747
|
+
function commentsToTimeline(comments) {
|
|
748
|
+
return comments.map((c) => ({
|
|
749
|
+
kind: c.author_kind === "reporter" ? "reporter_comment" : "comment",
|
|
750
|
+
at: c.created_at,
|
|
751
|
+
text: c.body,
|
|
752
|
+
body: c.body,
|
|
753
|
+
comment_id: c.id
|
|
754
|
+
}));
|
|
755
|
+
}
|
|
524
756
|
var MushiBottomSheet = ({
|
|
525
757
|
visible,
|
|
526
758
|
onClose,
|
|
@@ -529,77 +761,176 @@ var MushiBottomSheet = ({
|
|
|
529
761
|
onClearScreenshot,
|
|
530
762
|
screenshotSensitiveHint,
|
|
531
763
|
assistantEnabled = false,
|
|
532
|
-
assistantLabel =
|
|
764
|
+
assistantLabel = import_core5.MUSHI_COPY.assistantTab,
|
|
533
765
|
assistantGreeting,
|
|
534
766
|
assistantSuggestions = [],
|
|
535
|
-
inboxPollIntervalMs = 0
|
|
767
|
+
inboxPollIntervalMs = 0,
|
|
768
|
+
theme,
|
|
769
|
+
minDescriptionLength,
|
|
770
|
+
emailOptIn = true
|
|
536
771
|
}) => {
|
|
537
772
|
const mushi = useMushiContext();
|
|
538
773
|
const t = (0, import_i18n.getLocale)();
|
|
774
|
+
const locale = (0, import_reporter_ui2.resolveReporterLocale)(
|
|
775
|
+
typeof navigator !== "undefined" ? navigator.language : void 0
|
|
776
|
+
);
|
|
777
|
+
const rc = (0, import_reporter_ui2.reporterCopy)(locale);
|
|
539
778
|
const greeting = assistantGreeting ?? t.assistant.defaultGreeting;
|
|
540
|
-
const scheme = (0,
|
|
779
|
+
const scheme = (0, import_react_native3.useColorScheme)();
|
|
541
780
|
const dark = scheme === "dark";
|
|
542
|
-
const translateY = (0,
|
|
543
|
-
const backdropOpacity = (0,
|
|
544
|
-
const [category, setCategory] = (0,
|
|
545
|
-
const [description, setDescription] = (0,
|
|
546
|
-
const [phase, setPhase] = (0,
|
|
547
|
-
const [submitError, setSubmitError] = (0,
|
|
548
|
-
const [sheetTab, setSheetTab] = (0,
|
|
549
|
-
const [inboxReports, setInboxReports] = (0,
|
|
550
|
-
const [
|
|
551
|
-
const [
|
|
552
|
-
const [
|
|
553
|
-
const
|
|
554
|
-
const [
|
|
555
|
-
const [
|
|
556
|
-
const [
|
|
557
|
-
const [
|
|
558
|
-
const [
|
|
559
|
-
const [
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
781
|
+
const translateY = (0, import_react2.useRef)(new import_react_native3.Animated.Value(SHEET_HEIGHT)).current;
|
|
782
|
+
const backdropOpacity = (0, import_react2.useRef)(new import_react_native3.Animated.Value(0)).current;
|
|
783
|
+
const [category, setCategory] = (0, import_react2.useState)(null);
|
|
784
|
+
const [description, setDescription] = (0, import_react2.useState)("");
|
|
785
|
+
const [phase, setPhase] = (0, import_react2.useState)("form");
|
|
786
|
+
const [submitError, setSubmitError] = (0, import_react2.useState)(null);
|
|
787
|
+
const [sheetTab, setSheetTab] = (0, import_react2.useState)("report");
|
|
788
|
+
const [inboxReports, setInboxReports] = (0, import_react2.useState)([]);
|
|
789
|
+
const [inboxLoaded, setInboxLoaded] = (0, import_react2.useState)(false);
|
|
790
|
+
const [inboxLoading, setInboxLoading] = (0, import_react2.useState)(false);
|
|
791
|
+
const [selectedReportId, setSelectedReportId] = (0, import_react2.useState)(null);
|
|
792
|
+
const selectedRef = (0, import_react2.useRef)(null);
|
|
793
|
+
const [threadItems, setThreadItems] = (0, import_react2.useState)([]);
|
|
794
|
+
const [threadStatus, setThreadStatus] = (0, import_react2.useState)("idle");
|
|
795
|
+
const [replyText, setReplyText] = (0, import_react2.useState)("");
|
|
796
|
+
const [replyState, setReplyState] = (0, import_react2.useState)("idle");
|
|
797
|
+
const [feedbackSending, setFeedbackSending] = (0, import_react2.useState)(false);
|
|
798
|
+
const [feedbackError, setFeedbackError] = (0, import_react2.useState)(false);
|
|
799
|
+
const [assistantInput, setAssistantInput] = (0, import_react2.useState)("");
|
|
800
|
+
const [assistantThreadId, setAssistantThreadId] = (0, import_react2.useState)(null);
|
|
801
|
+
const [assistantSending, setAssistantSending] = (0, import_react2.useState)(false);
|
|
802
|
+
const [assistantError, setAssistantError] = (0, import_react2.useState)(null);
|
|
803
|
+
const [assistantTurns, setAssistantTurns] = (0, import_react2.useState)([]);
|
|
804
|
+
const [screenshotAttached, setScreenshotAttached] = (0, import_react2.useState)(true);
|
|
805
|
+
const [emailState, setEmailState] = (0, import_react2.useState)("hidden");
|
|
806
|
+
const [emailInput, setEmailInput] = (0, import_react2.useState)("");
|
|
807
|
+
const [reduceMotion, setReduceMotion] = (0, import_react2.useState)(false);
|
|
808
|
+
(0, import_react2.useEffect)(() => {
|
|
809
|
+
let alive = true;
|
|
810
|
+
import_react_native3.AccessibilityInfo?.isReduceMotionEnabled?.().then((on) => alive && setReduceMotion(Boolean(on))).catch(() => void 0);
|
|
811
|
+
const sub = import_react_native3.AccessibilityInfo?.addEventListener?.("reduceMotionChanged", (on) => setReduceMotion(Boolean(on)));
|
|
812
|
+
return () => {
|
|
813
|
+
alive = false;
|
|
814
|
+
sub?.remove?.();
|
|
815
|
+
};
|
|
816
|
+
}, []);
|
|
817
|
+
const loadInbox = (0, import_react2.useCallback)(
|
|
818
|
+
async (opts = {}) => {
|
|
819
|
+
if (!mushi?.listMyReports) return;
|
|
820
|
+
if (!opts.silent) setInboxLoading(true);
|
|
821
|
+
try {
|
|
822
|
+
const rows = await mushi.listMyReports();
|
|
823
|
+
setInboxReports(rows.filter((r) => !(0, import_reporter_ui2.reporterStatus)(r, locale).hidden));
|
|
824
|
+
setInboxLoaded(true);
|
|
825
|
+
} catch {
|
|
826
|
+
} finally {
|
|
827
|
+
if (!opts.silent) setInboxLoading(false);
|
|
828
|
+
}
|
|
829
|
+
},
|
|
830
|
+
[mushi, locale]
|
|
831
|
+
);
|
|
832
|
+
(0, import_react2.useEffect)(() => {
|
|
571
833
|
if (visible) {
|
|
572
834
|
setScreenshotAttached(true);
|
|
573
835
|
setSheetTab(preferredTab);
|
|
574
836
|
if (preferredTab === "inbox") void loadInbox();
|
|
575
837
|
}
|
|
576
838
|
}, [visible, preferredTab, loadInbox]);
|
|
577
|
-
(0,
|
|
839
|
+
(0, import_react2.useEffect)(() => {
|
|
578
840
|
if (!visible || sheetTab !== "inbox" || inboxPollIntervalMs <= 0) return;
|
|
579
841
|
const timer = setInterval(() => {
|
|
580
|
-
void loadInbox();
|
|
842
|
+
void loadInbox({ silent: true });
|
|
581
843
|
}, inboxPollIntervalMs);
|
|
582
844
|
return () => clearInterval(timer);
|
|
583
845
|
}, [visible, sheetTab, inboxPollIntervalMs, loadInbox]);
|
|
584
|
-
const openThread = (0,
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
846
|
+
const openThread = (0, import_react2.useCallback)(
|
|
847
|
+
async (reportId, opts = {}) => {
|
|
848
|
+
selectedRef.current = reportId;
|
|
849
|
+
setSelectedReportId(reportId);
|
|
850
|
+
if (!opts.silent) {
|
|
851
|
+
setThreadItems([]);
|
|
852
|
+
setThreadStatus("loading");
|
|
853
|
+
setReplyState("idle");
|
|
854
|
+
setFeedbackError(false);
|
|
855
|
+
}
|
|
856
|
+
let items = null;
|
|
857
|
+
const detail = mushi?.loadMyReportDetail ? await settleWithin(mushi.loadMyReportDetail(reportId), THREAD_LOAD_TIMEOUT_MS) : "unsupported";
|
|
858
|
+
if (detail && detail !== "unsupported") {
|
|
859
|
+
items = detail.timeline;
|
|
860
|
+
} else if (detail === "unsupported") {
|
|
861
|
+
const load = mushi?.loadMyThread ?? mushi?.listMyComments;
|
|
862
|
+
const comments = load ? await loadReporterThread(load, reportId) : null;
|
|
863
|
+
items = comments ? commentsToTimeline(comments) : null;
|
|
864
|
+
}
|
|
865
|
+
if (selectedRef.current !== reportId) return;
|
|
866
|
+
if (items === null) {
|
|
867
|
+
setThreadStatus((prev) => opts.silent && prev === "ready" ? "ready" : "error");
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
setThreadItems(items);
|
|
871
|
+
setThreadStatus("ready");
|
|
872
|
+
if (!opts.silent && mushi?.markReportRead) {
|
|
873
|
+
mushi.markReportRead(reportId).then((marked) => {
|
|
874
|
+
if (marked > 0) {
|
|
875
|
+
setInboxReports((rows) => rows.map((r) => r.id === reportId ? { ...r, unread_count: 0 } : r));
|
|
876
|
+
}
|
|
877
|
+
}).catch(() => void 0);
|
|
878
|
+
}
|
|
879
|
+
},
|
|
880
|
+
[mushi]
|
|
881
|
+
);
|
|
882
|
+
const closeThread = (0, import_react2.useCallback)(() => {
|
|
883
|
+
selectedRef.current = null;
|
|
884
|
+
setSelectedReportId(null);
|
|
885
|
+
setThreadStatus("idle");
|
|
886
|
+
}, []);
|
|
887
|
+
const sendReply = (0, import_react2.useCallback)(async () => {
|
|
888
|
+
const body = replyText.trim();
|
|
889
|
+
const reportId = selectedReportId;
|
|
890
|
+
if (!mushi?.replyToReport || !reportId || !body || replyState === "sending") return;
|
|
891
|
+
setReplyState("sending");
|
|
892
|
+
try {
|
|
893
|
+
const comment = await settleWithin(mushi.replyToReport(reportId, body), THREAD_LOAD_TIMEOUT_MS);
|
|
894
|
+
if (!comment) {
|
|
895
|
+
setReplyState("failed");
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
setReplyText("");
|
|
899
|
+
setReplyState("idle");
|
|
900
|
+
if (selectedRef.current === reportId) {
|
|
901
|
+
setThreadItems((prev) => [
|
|
902
|
+
...prev,
|
|
903
|
+
{ kind: "reporter_comment", at: comment.created_at, text: comment.body, body: comment.body, comment_id: comment.id }
|
|
904
|
+
]);
|
|
905
|
+
void openThread(reportId, { silent: true });
|
|
906
|
+
}
|
|
907
|
+
} catch {
|
|
908
|
+
setReplyState("failed");
|
|
909
|
+
}
|
|
910
|
+
}, [mushi, selectedReportId, replyText, replyState, openThread]);
|
|
911
|
+
const submitFeedback = (0, import_react2.useCallback)(
|
|
912
|
+
async (signal) => {
|
|
913
|
+
const reportId = selectedReportId;
|
|
914
|
+
if (!mushi?.submitFeedbackSignal || !reportId || feedbackSending) return;
|
|
915
|
+
setFeedbackSending(true);
|
|
916
|
+
setFeedbackError(false);
|
|
917
|
+
try {
|
|
918
|
+
const outcome = await mushi.submitFeedbackSignal(reportId, signal);
|
|
919
|
+
if (!outcome) {
|
|
920
|
+
setFeedbackError(true);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
await loadInbox({ silent: true });
|
|
924
|
+
await openThread(reportId, { silent: true });
|
|
925
|
+
} catch {
|
|
926
|
+
setFeedbackError(true);
|
|
927
|
+
} finally {
|
|
928
|
+
setFeedbackSending(false);
|
|
929
|
+
}
|
|
930
|
+
},
|
|
931
|
+
[mushi, selectedReportId, feedbackSending, loadInbox, openThread]
|
|
932
|
+
);
|
|
933
|
+
const sendAssistant = (0, import_react2.useCallback)(async (message) => {
|
|
603
934
|
if (!mushi?.askAssistant || !message.trim() || assistantSending) return;
|
|
604
935
|
const trimmed = message.trim();
|
|
605
936
|
setAssistantInput("");
|
|
@@ -624,37 +955,44 @@ var MushiBottomSheet = ({
|
|
|
624
955
|
setAssistantSending(false);
|
|
625
956
|
}
|
|
626
957
|
}, [mushi, assistantSending, assistantThreadId]);
|
|
627
|
-
const resetForm = (0,
|
|
958
|
+
const resetForm = (0, import_react2.useCallback)(() => {
|
|
628
959
|
setCategory(null);
|
|
629
960
|
setDescription("");
|
|
630
961
|
setPhase("form");
|
|
962
|
+
setEmailState("hidden");
|
|
963
|
+
setEmailInput("");
|
|
631
964
|
}, []);
|
|
632
|
-
const animateIn = (0,
|
|
633
|
-
|
|
634
|
-
|
|
965
|
+
const animateIn = (0, import_react2.useCallback)(() => {
|
|
966
|
+
if (reduceMotion) {
|
|
967
|
+
translateY.setValue(0);
|
|
968
|
+
backdropOpacity.setValue(1);
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
import_react_native3.Animated.parallel([
|
|
972
|
+
import_react_native3.Animated.spring(translateY, {
|
|
635
973
|
toValue: 0,
|
|
636
974
|
useNativeDriver: true,
|
|
637
975
|
damping: 20,
|
|
638
976
|
stiffness: 200
|
|
639
977
|
}),
|
|
640
|
-
|
|
978
|
+
import_react_native3.Animated.timing(backdropOpacity, {
|
|
641
979
|
toValue: 1,
|
|
642
980
|
duration: 250,
|
|
643
981
|
useNativeDriver: true
|
|
644
982
|
})
|
|
645
983
|
]).start();
|
|
646
|
-
}, [translateY, backdropOpacity]);
|
|
647
|
-
const animateOut = (0,
|
|
984
|
+
}, [translateY, backdropOpacity, reduceMotion]);
|
|
985
|
+
const animateOut = (0, import_react2.useCallback)(
|
|
648
986
|
(cb) => {
|
|
649
|
-
|
|
650
|
-
|
|
987
|
+
import_react_native3.Animated.parallel([
|
|
988
|
+
import_react_native3.Animated.timing(translateY, {
|
|
651
989
|
toValue: SHEET_HEIGHT,
|
|
652
|
-
duration: 220,
|
|
990
|
+
duration: reduceMotion ? 0 : 220,
|
|
653
991
|
useNativeDriver: true
|
|
654
992
|
}),
|
|
655
|
-
|
|
993
|
+
import_react_native3.Animated.timing(backdropOpacity, {
|
|
656
994
|
toValue: 0,
|
|
657
|
-
duration: 200,
|
|
995
|
+
duration: reduceMotion ? 0 : 200,
|
|
658
996
|
useNativeDriver: true
|
|
659
997
|
})
|
|
660
998
|
]).start(() => {
|
|
@@ -662,16 +1000,16 @@ var MushiBottomSheet = ({
|
|
|
662
1000
|
cb?.();
|
|
663
1001
|
});
|
|
664
1002
|
},
|
|
665
|
-
[translateY, backdropOpacity, resetForm]
|
|
1003
|
+
[translateY, backdropOpacity, resetForm, reduceMotion]
|
|
666
1004
|
);
|
|
667
|
-
(0,
|
|
1005
|
+
(0, import_react2.useEffect)(() => {
|
|
668
1006
|
if (visible) animateIn();
|
|
669
1007
|
}, [visible, animateIn]);
|
|
670
|
-
const handleClose = (0,
|
|
1008
|
+
const handleClose = (0, import_react2.useCallback)(() => {
|
|
671
1009
|
animateOut(onClose);
|
|
672
1010
|
}, [animateOut, onClose]);
|
|
673
|
-
const panResponder = (0,
|
|
674
|
-
|
|
1011
|
+
const panResponder = (0, import_react2.useRef)(
|
|
1012
|
+
import_react_native3.PanResponder.create({
|
|
675
1013
|
onStartShouldSetPanResponder: () => true,
|
|
676
1014
|
onMoveShouldSetPanResponder: (_, g) => g.dy > 4,
|
|
677
1015
|
onPanResponderMove: (_, g) => {
|
|
@@ -681,7 +1019,7 @@ var MushiBottomSheet = ({
|
|
|
681
1019
|
if (g.dy > DISMISS_THRESHOLD) {
|
|
682
1020
|
handleClose();
|
|
683
1021
|
} else {
|
|
684
|
-
|
|
1022
|
+
import_react_native3.Animated.spring(translateY, {
|
|
685
1023
|
toValue: 0,
|
|
686
1024
|
useNativeDriver: true,
|
|
687
1025
|
damping: 20,
|
|
@@ -691,20 +1029,43 @@ var MushiBottomSheet = ({
|
|
|
691
1029
|
}
|
|
692
1030
|
})
|
|
693
1031
|
).current;
|
|
694
|
-
const
|
|
695
|
-
if (!
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
1032
|
+
const offerEmail = (0, import_react2.useCallback)(async () => {
|
|
1033
|
+
if (!emailOptIn || !mushi?.getNotificationPrefs) return;
|
|
1034
|
+
try {
|
|
1035
|
+
const prefs = await mushi.getNotificationPrefs();
|
|
1036
|
+
if (prefs?.available.email && !prefs.email_verified && !prefs.email_pending) {
|
|
1037
|
+
setEmailInput(mushi.emailPrefill?.() ?? "");
|
|
1038
|
+
setEmailState("offer");
|
|
1039
|
+
}
|
|
1040
|
+
} catch {
|
|
1041
|
+
}
|
|
1042
|
+
}, [emailOptIn, mushi]);
|
|
1043
|
+
const submitEmail = (0, import_react2.useCallback)(async () => {
|
|
1044
|
+
const email = emailInput.trim();
|
|
1045
|
+
if (!(0, import_reporter_ui2.isPlausibleReporterEmail)(email)) {
|
|
1046
|
+
setEmailState("invalid");
|
|
699
1047
|
return;
|
|
700
1048
|
}
|
|
1049
|
+
if (!mushi?.setNotificationPrefs) return;
|
|
1050
|
+
setEmailState("sending");
|
|
1051
|
+
try {
|
|
1052
|
+
const res = await mushi.setNotificationPrefs({ email });
|
|
1053
|
+
setEmailState(res.ok ? "sent" : res.error?.status === 422 ? "invalid" : "failed");
|
|
1054
|
+
} catch {
|
|
1055
|
+
setEmailState("failed");
|
|
1056
|
+
}
|
|
1057
|
+
}, [emailInput, mushi]);
|
|
1058
|
+
const handleSubmit = async () => {
|
|
1059
|
+
const hasAttachment = Boolean(screenshotDataUrl && screenshotAttached);
|
|
1060
|
+
if (!mushi || !(0, import_reporter_ui2.reporterCanSend)(description, hasAttachment, minDescriptionLength)) return;
|
|
1061
|
+
const trimmed = description.trim();
|
|
701
1062
|
setSubmitError(null);
|
|
702
1063
|
setPhase("sending");
|
|
703
1064
|
try {
|
|
704
1065
|
const outcome = await mushi.submitReport({
|
|
705
|
-
category,
|
|
1066
|
+
...(0, import_reporter_ui2.reporterChipToReport)(category),
|
|
706
1067
|
description: trimmed,
|
|
707
|
-
screenshotDataUrl:
|
|
1068
|
+
screenshotDataUrl: hasAttachment ? screenshotDataUrl : void 0
|
|
708
1069
|
});
|
|
709
1070
|
if (!outcome.ok) {
|
|
710
1071
|
if (outcome.failureKind === "credentials" || outcome.failureKind === "quota") {
|
|
@@ -722,174 +1083,465 @@ var MushiBottomSheet = ({
|
|
|
722
1083
|
return;
|
|
723
1084
|
}
|
|
724
1085
|
setPhase("sent");
|
|
725
|
-
|
|
1086
|
+
void offerEmail();
|
|
726
1087
|
} catch {
|
|
727
1088
|
setSubmitError("Something went wrong. Please try again.");
|
|
728
1089
|
setPhase("error");
|
|
729
1090
|
}
|
|
730
1091
|
};
|
|
731
1092
|
const activeScreenshot = screenshotDataUrl && screenshotAttached ? screenshotDataUrl : null;
|
|
732
|
-
const
|
|
733
|
-
const
|
|
1093
|
+
const colors = resolveRNTheme(dark, theme);
|
|
1094
|
+
const font = colors.fontFamily ? { fontFamily: colors.fontFamily } : null;
|
|
1095
|
+
const radius = colors.radius;
|
|
734
1096
|
const sheetTabs = ["report", "inbox", ...assistantEnabled ? ["assistant"] : []];
|
|
735
|
-
const canSubmit =
|
|
736
|
-
|
|
737
|
-
|
|
1097
|
+
const canSubmit = (0, import_reporter_ui2.reporterCanSend)(description, Boolean(activeScreenshot), minDescriptionLength) && (phase === "form" || phase === "error");
|
|
1098
|
+
const submitHint = canSubmit || phase === "sending" ? void 0 : rc.ui.addWords;
|
|
1099
|
+
const selectedReport = selectedReportId ? inboxReports.find((r) => r.id === selectedReportId) ?? null : null;
|
|
1100
|
+
const selectedView = selectedReport ? (0, import_reporter_ui2.reporterStatus)(selectedReport, locale) : null;
|
|
1101
|
+
const headerTitle = sheetTab === "assistant" ? assistantLabel : sheetTab === "inbox" ? t.flows.reports.title : t.widget.title;
|
|
1102
|
+
const renderThread = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
1103
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1104
|
+
import_react_native3.TouchableOpacity,
|
|
1105
|
+
{
|
|
1106
|
+
onPress: closeThread,
|
|
1107
|
+
accessibilityRole: "button",
|
|
1108
|
+
accessibilityLabel: t.widget.back,
|
|
1109
|
+
hitSlop: { top: 8, bottom: 8, left: 8, right: 8 },
|
|
1110
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.Text, { style: [{ color: colors.accent, marginBottom: 8 }, font], children: [
|
|
1111
|
+
"\u2190 ",
|
|
1112
|
+
t.widget.back
|
|
1113
|
+
] })
|
|
1114
|
+
}
|
|
1115
|
+
),
|
|
1116
|
+
selectedReport && selectedView ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: [s2.headerCard, { backgroundColor: colors.surface, borderRadius: radius, borderColor: colors.border }], children: [
|
|
1117
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.pill, { color: colors.fg, borderColor: colors.border }, font], children: selectedView.label }),
|
|
1118
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.muted, fontSize: 12, marginTop: 4 }, font], children: selectedView.detail }),
|
|
1119
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.fg, marginTop: 8 }, font], numberOfLines: 4, children: selectedReport.title ?? selectedReport.summary ?? selectedReport.description ?? "" })
|
|
1120
|
+
] }) : null,
|
|
1121
|
+
threadStatus === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.View, { accessibilityLabel: t.flows.reports.loading, accessibilityLiveRegion: "polite", children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.View, { style: [s2.skeleton, { backgroundColor: colors.surface, width: `${90 - i * 18}%` }] }, i)) }) : threadStatus === "error" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: s2.errorRow, accessibilityLiveRegion: "polite", children: [
|
|
1122
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.error, flex: 1 }, font], accessibilityRole: "alert", children: rc.ui.loadError }),
|
|
1123
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1124
|
+
import_react_native3.TouchableOpacity,
|
|
1125
|
+
{
|
|
1126
|
+
onPress: () => selectedReportId && void openThread(selectedReportId),
|
|
1127
|
+
accessibilityRole: "button",
|
|
1128
|
+
style: [s2.retryBtn, { borderColor: colors.border, borderRadius: radius }],
|
|
1129
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.fg, fontWeight: "600" }, font], children: rc.ui.retry })
|
|
1130
|
+
}
|
|
1131
|
+
)
|
|
1132
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
1133
|
+
threadItems.map(
|
|
1134
|
+
(item, idx) => (0, import_reporter_ui2.isReporterConversation)(item) ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1135
|
+
import_react_native3.View,
|
|
1136
|
+
{
|
|
1137
|
+
style: [
|
|
1138
|
+
s2.threadBubble,
|
|
1139
|
+
{
|
|
1140
|
+
backgroundColor: colors.surface,
|
|
1141
|
+
alignSelf: item.kind === "reporter_comment" ? "flex-end" : "flex-start"
|
|
1142
|
+
}
|
|
1143
|
+
],
|
|
1144
|
+
children: [
|
|
1145
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.muted, fontSize: 11 }, font], children: item.kind === "reporter_comment" ? rc.ui.you : rc.ui.developer }),
|
|
1146
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.fg }, font], children: (0, import_reporter_ui2.reporterTimelineEntryText)(item, locale) })
|
|
1147
|
+
]
|
|
1148
|
+
},
|
|
1149
|
+
`${item.kind}-${item.comment_id ?? idx}`
|
|
1150
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1151
|
+
import_react_native3.Text,
|
|
1152
|
+
{
|
|
1153
|
+
style: [{ color: colors.muted, fontSize: 12, textAlign: "center", marginVertical: 6 }, font],
|
|
1154
|
+
children: (0, import_reporter_ui2.reporterTimelineEntryText)(item, locale)
|
|
1155
|
+
},
|
|
1156
|
+
`${item.kind}-${item.at}-${idx}`
|
|
1157
|
+
)
|
|
1158
|
+
),
|
|
1159
|
+
!threadItems.some(import_reporter_ui2.isReporterConversation) ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.muted, marginBottom: 8 }, font], children: rc.ui.noReplies }) : null
|
|
1160
|
+
] }),
|
|
1161
|
+
replyState === "sending" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: [s2.threadBubble, { backgroundColor: colors.surface, alignSelf: "flex-end", opacity: 0.7 }], children: [
|
|
1162
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.muted, fontSize: 11 }, font], children: rc.ui.sending }),
|
|
1163
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.fg }, font], children: replyText.trim() })
|
|
1164
|
+
] }) : null,
|
|
1165
|
+
selectedView?.canVerify ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: s2.verifyRow, children: [
|
|
1166
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1167
|
+
import_react_native3.TouchableOpacity,
|
|
1168
|
+
{
|
|
1169
|
+
style: [s2.verifyBtn, { backgroundColor: colors.accent, borderRadius: radius }],
|
|
1170
|
+
onPress: () => void submitFeedback("confirms"),
|
|
1171
|
+
disabled: feedbackSending,
|
|
1172
|
+
accessibilityRole: "button",
|
|
1173
|
+
accessibilityState: { disabled: feedbackSending, busy: feedbackSending },
|
|
1174
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.submitText, { color: colors.accentFg }, font], children: rc.ui.yes })
|
|
1175
|
+
}
|
|
1176
|
+
),
|
|
1177
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1178
|
+
import_react_native3.TouchableOpacity,
|
|
1179
|
+
{
|
|
1180
|
+
style: [s2.verifyBtn, { backgroundColor: colors.border, borderRadius: radius }],
|
|
1181
|
+
onPress: () => void submitFeedback("not_fixed"),
|
|
1182
|
+
disabled: feedbackSending,
|
|
1183
|
+
accessibilityRole: "button",
|
|
1184
|
+
accessibilityState: { disabled: feedbackSending, busy: feedbackSending },
|
|
1185
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.submitText, { color: colors.fg }, font], children: rc.ui.notYet })
|
|
1186
|
+
}
|
|
1187
|
+
)
|
|
1188
|
+
] }) : null,
|
|
1189
|
+
feedbackError ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.error, marginBottom: 8 }, font], accessibilityRole: "alert", children: rc.ui.sendFailed }) : null,
|
|
1190
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: s2.composer, children: [
|
|
1191
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1192
|
+
import_react_native3.TextInput,
|
|
1193
|
+
{
|
|
1194
|
+
style: [
|
|
1195
|
+
s2.input,
|
|
1196
|
+
s2.composerInput,
|
|
1197
|
+
{ backgroundColor: colors.surface, color: colors.fg, borderColor: colors.border, borderRadius: radius },
|
|
1198
|
+
font
|
|
1199
|
+
],
|
|
1200
|
+
placeholder: t.flows.thread.replyPlaceholder,
|
|
1201
|
+
placeholderTextColor: colors.muted,
|
|
1202
|
+
value: replyText,
|
|
1203
|
+
onChangeText: (text) => {
|
|
1204
|
+
setReplyText(text.slice(0, 2e3));
|
|
1205
|
+
if (replyState === "failed") setReplyState("idle");
|
|
1206
|
+
},
|
|
1207
|
+
multiline: true,
|
|
1208
|
+
maxLength: 2e3,
|
|
1209
|
+
editable: replyState !== "sending",
|
|
1210
|
+
accessibilityLabel: t.flows.thread.replyPlaceholder
|
|
1211
|
+
}
|
|
1212
|
+
),
|
|
1213
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1214
|
+
import_react_native3.TouchableOpacity,
|
|
1215
|
+
{
|
|
1216
|
+
style: [
|
|
1217
|
+
s2.sendBtn,
|
|
1218
|
+
{
|
|
1219
|
+
backgroundColor: replyText.trim() && replyState !== "sending" ? colors.accent : colors.disabled,
|
|
1220
|
+
borderRadius: radius
|
|
1221
|
+
}
|
|
1222
|
+
],
|
|
1223
|
+
onPress: () => void sendReply(),
|
|
1224
|
+
disabled: !replyText.trim() || replyState === "sending",
|
|
1225
|
+
accessibilityRole: "button",
|
|
1226
|
+
accessibilityLabel: replyState === "failed" ? rc.ui.retry : t.flows.thread.send,
|
|
1227
|
+
accessibilityState: { disabled: !replyText.trim() || replyState === "sending", busy: replyState === "sending" },
|
|
1228
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1229
|
+
import_react_native3.Text,
|
|
1230
|
+
{
|
|
1231
|
+
style: [
|
|
1232
|
+
s2.submitText,
|
|
1233
|
+
{ color: replyText.trim() && replyState !== "sending" ? colors.accentFg : colors.disabledFg },
|
|
1234
|
+
font
|
|
1235
|
+
],
|
|
1236
|
+
children: replyState === "failed" ? rc.ui.retry : t.flows.thread.send
|
|
1237
|
+
}
|
|
1238
|
+
)
|
|
1239
|
+
}
|
|
1240
|
+
)
|
|
1241
|
+
] }),
|
|
1242
|
+
replyState === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.Text, { style: [{ color: colors.error, marginTop: 6, fontSize: 12 }, font], accessibilityRole: "alert", children: [
|
|
1243
|
+
rc.ui.sendFailed,
|
|
1244
|
+
" \xB7 ",
|
|
1245
|
+
rc.ui.retry
|
|
1246
|
+
] }) : null
|
|
1247
|
+
] });
|
|
1248
|
+
const renderInboxList = () => {
|
|
1249
|
+
if (inboxLoading && !inboxLoaded) {
|
|
1250
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.muted }, font], children: t.flows.reports.loading });
|
|
1251
|
+
}
|
|
1252
|
+
if (inboxReports.length === 0) {
|
|
1253
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.muted }, font], children: rc.ui.empty });
|
|
1254
|
+
}
|
|
1255
|
+
return inboxReports.map((r) => {
|
|
1256
|
+
const view = (0, import_reporter_ui2.reporterStatus)(r, locale);
|
|
1257
|
+
const unread = (r.unread_count ?? 0) > 0;
|
|
1258
|
+
const title = (r.title ?? r.summary ?? r.description ?? "Report").slice(0, 80);
|
|
1259
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1260
|
+
import_react_native3.TouchableOpacity,
|
|
1261
|
+
{
|
|
1262
|
+
style: [s2.inboxRow, { borderColor: colors.border }],
|
|
1263
|
+
onPress: () => void openThread(r.id),
|
|
1264
|
+
accessibilityRole: "button",
|
|
1265
|
+
accessibilityLabel: `${view.label}. ${title}${unread ? `. ${r.unread_count} new` : ""}`,
|
|
1266
|
+
children: [
|
|
1267
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.fg, fontWeight: unread ? "700" : "600" }, font], numberOfLines: 1, children: title }),
|
|
1268
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.Text, { style: [{ color: unread ? colors.fg : colors.muted, fontSize: 12, marginTop: 2 }, font], numberOfLines: 1, children: [
|
|
1269
|
+
view.label,
|
|
1270
|
+
view.othersNote ? ` \xB7 ${view.othersNote}` : ""
|
|
1271
|
+
] }),
|
|
1272
|
+
unread && r.last_event_preview ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.fg, fontSize: 12, fontWeight: "700", marginTop: 2 }, font], numberOfLines: 1, children: r.last_event_preview }) : null
|
|
1273
|
+
]
|
|
1274
|
+
},
|
|
1275
|
+
r.id
|
|
1276
|
+
);
|
|
1277
|
+
});
|
|
1278
|
+
};
|
|
1279
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Modal, { visible, transparent: true, animationType: "none", statusBarTranslucent: true, onRequestClose: handleClose, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1280
|
+
import_react_native3.KeyboardAvoidingView,
|
|
738
1281
|
{
|
|
739
|
-
style:
|
|
740
|
-
behavior:
|
|
741
|
-
keyboardVerticalOffset:
|
|
1282
|
+
style: s2.flex,
|
|
1283
|
+
behavior: import_react_native3.Platform.OS === "ios" ? "padding" : "height",
|
|
1284
|
+
keyboardVerticalOffset: import_react_native3.Platform.OS === "ios" ? 0 : 20,
|
|
742
1285
|
children: [
|
|
743
|
-
/* @__PURE__ */ (0,
|
|
744
|
-
|
|
1286
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1287
|
+
import_react_native3.Animated.View,
|
|
745
1288
|
{
|
|
746
|
-
style: [
|
|
747
|
-
children: /* @__PURE__ */ (0,
|
|
1289
|
+
style: [s2.backdrop, { backgroundColor: colors.backdrop, opacity: backdropOpacity }],
|
|
1290
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1291
|
+
import_react_native3.TouchableOpacity,
|
|
1292
|
+
{
|
|
1293
|
+
style: s2.flex,
|
|
1294
|
+
activeOpacity: 1,
|
|
1295
|
+
onPress: handleClose,
|
|
1296
|
+
accessibilityRole: "button",
|
|
1297
|
+
accessibilityLabel: t.widget.close
|
|
1298
|
+
}
|
|
1299
|
+
)
|
|
748
1300
|
}
|
|
749
1301
|
),
|
|
750
|
-
/* @__PURE__ */ (0,
|
|
751
|
-
|
|
1302
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1303
|
+
import_react_native3.Animated.View,
|
|
752
1304
|
{
|
|
1305
|
+
accessibilityViewIsModal: true,
|
|
1306
|
+
onAccessibilityEscape: handleClose,
|
|
753
1307
|
style: [
|
|
754
|
-
|
|
1308
|
+
s2.sheet,
|
|
755
1309
|
{ backgroundColor: colors.bg, transform: [{ translateY }] }
|
|
756
1310
|
],
|
|
757
1311
|
children: [
|
|
758
|
-
/* @__PURE__ */ (0,
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
1312
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1313
|
+
import_react_native3.View,
|
|
1314
|
+
{
|
|
1315
|
+
...panResponder.panHandlers,
|
|
1316
|
+
style: s2.handleArea,
|
|
1317
|
+
accessible: true,
|
|
1318
|
+
accessibilityRole: "adjustable",
|
|
1319
|
+
accessibilityLabel: t.widget.close,
|
|
1320
|
+
accessibilityActions: [{ name: "decrement", label: t.widget.close }, { name: "escape" }],
|
|
1321
|
+
onAccessibilityAction: (e) => {
|
|
1322
|
+
if (e.nativeEvent.actionName === "decrement" || e.nativeEvent.actionName === "escape") handleClose();
|
|
1323
|
+
},
|
|
1324
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.View, { style: [s2.handle, { backgroundColor: colors.muted }] })
|
|
1325
|
+
}
|
|
1326
|
+
),
|
|
1327
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.View, { style: [s2.header, { borderBottomColor: colors.border }], children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { accessibilityRole: "header", style: [s2.headerTitle, { color: colors.fg }, font], children: headerTitle }) }),
|
|
1328
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.View, { style: [s2.tabRow, { borderBottomColor: colors.border }], accessibilityRole: "tablist", children: sheetTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1329
|
+
import_react_native3.TouchableOpacity,
|
|
765
1330
|
{
|
|
766
1331
|
onPress: () => {
|
|
767
1332
|
setSheetTab(tab);
|
|
768
|
-
if (tab === "inbox") void loadInbox();
|
|
769
|
-
else
|
|
1333
|
+
if (tab === "inbox") void loadInbox({ silent: inboxLoaded });
|
|
1334
|
+
else closeThread();
|
|
770
1335
|
},
|
|
771
|
-
style: [
|
|
772
|
-
|
|
1336
|
+
style: [s2.tabBtn, sheetTab === tab && { borderBottomColor: colors.accent }],
|
|
1337
|
+
accessibilityRole: "tab",
|
|
1338
|
+
accessibilityState: { selected: sheetTab === tab },
|
|
1339
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.tabLabel, { color: sheetTab === tab ? colors.fg : colors.muted }, font], children: tab === "report" ? t.widget.trigger : tab === "inbox" ? t.flows.reports.title : assistantLabel })
|
|
773
1340
|
},
|
|
774
1341
|
tab
|
|
775
1342
|
)) }),
|
|
776
|
-
sheetTab === "assistant" ? /* @__PURE__ */ (0,
|
|
777
|
-
assistantTurns.length === 0 ? /* @__PURE__ */ (0,
|
|
778
|
-
/* @__PURE__ */ (0,
|
|
779
|
-
assistantSuggestions.map((chip) => /* @__PURE__ */ (0,
|
|
780
|
-
|
|
1343
|
+
sheetTab === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.ScrollView, { style: s2.body, keyboardShouldPersistTaps: "handled", children: [
|
|
1344
|
+
assistantTurns.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
1345
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.muted, marginBottom: 12, lineHeight: 20 }, font], children: greeting }),
|
|
1346
|
+
assistantSuggestions.map((chip) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1347
|
+
import_react_native3.TouchableOpacity,
|
|
781
1348
|
{
|
|
782
|
-
style: [
|
|
1349
|
+
style: [s2.assistantChip, { borderColor: colors.border, backgroundColor: colors.surface }],
|
|
783
1350
|
onPress: () => void sendAssistant(chip),
|
|
784
|
-
|
|
1351
|
+
accessibilityRole: "button",
|
|
1352
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.fg, fontSize: 13 }, font], children: chip })
|
|
785
1353
|
},
|
|
786
1354
|
chip
|
|
787
1355
|
))
|
|
788
|
-
] }) : assistantTurns.map((turn, idx) => /* @__PURE__ */ (0,
|
|
789
|
-
|
|
1356
|
+
] }) : assistantTurns.map((turn, idx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1357
|
+
import_react_native3.View,
|
|
790
1358
|
{
|
|
791
1359
|
style: [
|
|
792
|
-
|
|
1360
|
+
s2.assistantBubble,
|
|
793
1361
|
{
|
|
794
1362
|
alignSelf: turn.role === "user" ? "flex-end" : "flex-start",
|
|
795
|
-
backgroundColor: turn.role === "user" ? colors.accent : colors.
|
|
1363
|
+
backgroundColor: turn.role === "user" ? colors.accent : colors.surface
|
|
796
1364
|
}
|
|
797
1365
|
],
|
|
798
1366
|
children: [
|
|
799
|
-
/* @__PURE__ */ (0,
|
|
800
|
-
turn.options?.map((opt) => /* @__PURE__ */ (0,
|
|
1367
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: turn.role === "user" ? colors.accentFg : colors.fg }, font], children: turn.text }),
|
|
1368
|
+
turn.options?.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1369
|
+
import_react_native3.TouchableOpacity,
|
|
1370
|
+
{
|
|
1371
|
+
onPress: () => void sendAssistant(opt),
|
|
1372
|
+
style: { marginTop: 8 },
|
|
1373
|
+
accessibilityRole: "button",
|
|
1374
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.accent, fontSize: 12 }, font], children: opt })
|
|
1375
|
+
},
|
|
1376
|
+
opt
|
|
1377
|
+
))
|
|
801
1378
|
]
|
|
802
1379
|
},
|
|
803
1380
|
`${turn.role}-${idx}`
|
|
804
1381
|
)),
|
|
805
|
-
assistantSending ? /* @__PURE__ */ (0,
|
|
806
|
-
assistantError ? /* @__PURE__ */ (0,
|
|
807
|
-
/* @__PURE__ */ (0,
|
|
808
|
-
/* @__PURE__ */ (0,
|
|
809
|
-
|
|
1382
|
+
assistantSending ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.muted, marginTop: 8 }, font], accessibilityLiveRegion: "polite", children: t.assistant.thinking }) : null,
|
|
1383
|
+
assistantError ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.error, marginTop: 8 }, font], accessibilityRole: "alert", children: assistantError }) : null,
|
|
1384
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: s2.assistantComposer, children: [
|
|
1385
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1386
|
+
import_react_native3.TextInput,
|
|
810
1387
|
{
|
|
811
|
-
style: [
|
|
1388
|
+
style: [s2.input, { flex: 1, minHeight: 44, backgroundColor: colors.surface, color: colors.fg, borderColor: colors.border, borderRadius: radius }, font],
|
|
812
1389
|
placeholder: t.assistant.inputPlaceholder,
|
|
813
|
-
placeholderTextColor: colors.
|
|
1390
|
+
placeholderTextColor: colors.muted,
|
|
814
1391
|
value: assistantInput,
|
|
815
1392
|
onChangeText: setAssistantInput,
|
|
816
1393
|
editable: !assistantSending
|
|
817
1394
|
}
|
|
818
1395
|
),
|
|
819
|
-
/* @__PURE__ */ (0,
|
|
820
|
-
|
|
1396
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1397
|
+
import_react_native3.TouchableOpacity,
|
|
821
1398
|
{
|
|
822
|
-
style: [
|
|
1399
|
+
style: [s2.submitBtn, { backgroundColor: colors.accent, paddingHorizontal: 16, borderRadius: radius }],
|
|
823
1400
|
onPress: () => void sendAssistant(assistantInput),
|
|
824
1401
|
disabled: !assistantInput.trim() || assistantSending,
|
|
825
|
-
|
|
1402
|
+
accessibilityRole: "button",
|
|
1403
|
+
accessibilityLabel: t.flows.thread.send,
|
|
1404
|
+
accessibilityState: { disabled: !assistantInput.trim() || assistantSending, busy: assistantSending },
|
|
1405
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.submitText, { color: colors.accentFg }], children: "\u2191" })
|
|
826
1406
|
}
|
|
827
1407
|
)
|
|
828
1408
|
] })
|
|
829
|
-
] }) : sheetTab === "inbox" ? /* @__PURE__ */ (0,
|
|
830
|
-
/* @__PURE__ */ (0,
|
|
831
|
-
"\
|
|
832
|
-
|
|
833
|
-
] }) }),
|
|
834
|
-
threadComments.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native2.View, { style: [s.threadBubble, { backgroundColor: colors.card }], children: [
|
|
835
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: { color: colors.sub, fontSize: 11 }, children: c.author_kind }),
|
|
836
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: { color: colors.text }, children: c.body })
|
|
837
|
-
] }, c.id)),
|
|
838
|
-
VERIFIABLE_STATUSES.has(
|
|
839
|
-
inboxReports.find((r) => r.id === selectedReportId)?.status ?? ""
|
|
840
|
-
) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native2.View, { style: s.verifyRow, children: [
|
|
841
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.TouchableOpacity, { style: [s.verifyBtn, { backgroundColor: colors.accent }], onPress: () => submitFeedback("confirms"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: [s.submitText, { color: colors.accentInk }], children: t.flows.thread.confirmFixed }) }),
|
|
842
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.TouchableOpacity, { style: [s.verifyBtn, { backgroundColor: colors.border }], onPress: () => submitFeedback("not_fixed"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: [s.submitText, { color: colors.text }], children: t.flows.thread.notFixed }) })
|
|
1409
|
+
] }) : sheetTab === "inbox" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.ScrollView, { style: s2.body, keyboardShouldPersistTaps: "handled", contentContainerStyle: { paddingBottom: 24 }, children: selectedReportId ? renderThread() : renderInboxList() }) : phase === "sent" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.ScrollView, { style: s2.body, keyboardShouldPersistTaps: "handled", contentContainerStyle: { paddingBottom: 24 }, children: [
|
|
1410
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: s2.sentWrap, accessibilityLiveRegion: "polite", children: [
|
|
1411
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.sentEmoji], importantForAccessibility: "no", children: "\u2705" }),
|
|
1412
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.sentText, { color: colors.fg }, font], children: rc.ui.receipt })
|
|
843
1413
|
] }),
|
|
844
|
-
/* @__PURE__ */ (0,
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1414
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: s2.verifyRow, children: [
|
|
1415
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1416
|
+
import_react_native3.TouchableOpacity,
|
|
1417
|
+
{
|
|
1418
|
+
style: [s2.verifyBtn, { backgroundColor: colors.accent, borderRadius: radius }],
|
|
1419
|
+
onPress: () => {
|
|
1420
|
+
resetForm();
|
|
1421
|
+
setSheetTab("inbox");
|
|
1422
|
+
void loadInbox();
|
|
1423
|
+
},
|
|
1424
|
+
accessibilityRole: "button",
|
|
1425
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.submitText, { color: colors.accentFg }, font], children: rc.ui.trackIt })
|
|
1426
|
+
}
|
|
1427
|
+
),
|
|
1428
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1429
|
+
import_react_native3.TouchableOpacity,
|
|
1430
|
+
{
|
|
1431
|
+
style: [s2.verifyBtn, { backgroundColor: colors.border, borderRadius: radius }],
|
|
1432
|
+
onPress: handleClose,
|
|
1433
|
+
accessibilityRole: "button",
|
|
1434
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.submitText, { color: colors.fg }, font], children: rc.ui.done })
|
|
1435
|
+
}
|
|
1436
|
+
)
|
|
1437
|
+
] }),
|
|
1438
|
+
emailState === "sent" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.fg, marginTop: 12 }, font], accessibilityLiveRegion: "polite", children: rc.ui.emailCheckInbox }) : emailState !== "hidden" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: { marginTop: 12 }, children: [
|
|
1439
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.fg, marginBottom: 6 }, font], children: rc.ui.emailOptIn }),
|
|
1440
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: s2.composer, children: [
|
|
1441
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1442
|
+
import_react_native3.TextInput,
|
|
1443
|
+
{
|
|
1444
|
+
style: [
|
|
1445
|
+
s2.input,
|
|
1446
|
+
s2.composerInput,
|
|
1447
|
+
{ backgroundColor: colors.surface, color: colors.fg, borderColor: colors.border, borderRadius: radius },
|
|
1448
|
+
font
|
|
1449
|
+
],
|
|
1450
|
+
value: emailInput,
|
|
1451
|
+
onChangeText: (v) => {
|
|
1452
|
+
setEmailInput(v.slice(0, 254));
|
|
1453
|
+
if (emailState === "invalid" || emailState === "failed") setEmailState("offer");
|
|
1454
|
+
},
|
|
1455
|
+
placeholder: rc.ui.emailPlaceholder,
|
|
1456
|
+
placeholderTextColor: colors.muted,
|
|
1457
|
+
keyboardType: "email-address",
|
|
1458
|
+
autoCapitalize: "none",
|
|
1459
|
+
autoComplete: "email",
|
|
1460
|
+
textContentType: "emailAddress",
|
|
1461
|
+
editable: emailState !== "sending",
|
|
1462
|
+
accessibilityLabel: rc.ui.emailOptIn
|
|
1463
|
+
}
|
|
1464
|
+
),
|
|
1465
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1466
|
+
import_react_native3.TouchableOpacity,
|
|
1467
|
+
{
|
|
1468
|
+
style: [s2.sendBtn, { backgroundColor: emailInput.trim() ? colors.accent : colors.disabled, borderRadius: radius }],
|
|
1469
|
+
onPress: () => void submitEmail(),
|
|
1470
|
+
disabled: !emailInput.trim() || emailState === "sending",
|
|
1471
|
+
accessibilityRole: "button",
|
|
1472
|
+
accessibilityState: { disabled: !emailInput.trim() || emailState === "sending", busy: emailState === "sending" },
|
|
1473
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.submitText, { color: emailInput.trim() ? colors.accentFg : colors.disabledFg }, font], children: emailState === "sending" ? rc.ui.sending : rc.ui.emailSubmit })
|
|
1474
|
+
}
|
|
1475
|
+
)
|
|
1476
|
+
] }),
|
|
1477
|
+
emailState === "invalid" || emailState === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.error, marginTop: 6, fontSize: 12 }, font], accessibilityRole: "alert", children: emailState === "invalid" ? rc.ui.emailInvalid : rc.ui.emailFailed }) : null
|
|
1478
|
+
] }) : null
|
|
1479
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1480
|
+
import_react_native3.ScrollView,
|
|
863
1481
|
{
|
|
864
|
-
style:
|
|
1482
|
+
style: s2.body,
|
|
865
1483
|
keyboardShouldPersistTaps: "handled",
|
|
866
1484
|
contentContainerStyle: { paddingBottom: 24 },
|
|
867
1485
|
children: [
|
|
868
|
-
/* @__PURE__ */ (0,
|
|
869
|
-
|
|
1486
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1487
|
+
import_react_native3.TextInput,
|
|
1488
|
+
{
|
|
1489
|
+
style: [
|
|
1490
|
+
s2.input,
|
|
1491
|
+
{
|
|
1492
|
+
backgroundColor: colors.surface,
|
|
1493
|
+
color: colors.fg,
|
|
1494
|
+
borderColor: colors.border,
|
|
1495
|
+
borderRadius: radius
|
|
1496
|
+
},
|
|
1497
|
+
font
|
|
1498
|
+
],
|
|
1499
|
+
placeholder: t.step3.descriptionPlaceholder,
|
|
1500
|
+
placeholderTextColor: colors.muted,
|
|
1501
|
+
accessibilityLabel: t.step3.descriptionPlaceholder,
|
|
1502
|
+
multiline: true,
|
|
1503
|
+
textAlignVertical: "top",
|
|
1504
|
+
value: description,
|
|
1505
|
+
onChangeText: (text) => {
|
|
1506
|
+
setDescription(text.slice(0, 4e3));
|
|
1507
|
+
if (submitError) setSubmitError(null);
|
|
1508
|
+
if (phase === "error") setPhase("form");
|
|
1509
|
+
},
|
|
1510
|
+
editable: phase === "form" || phase === "error",
|
|
1511
|
+
maxLength: 4e3
|
|
1512
|
+
}
|
|
1513
|
+
),
|
|
1514
|
+
submitError ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.error, marginTop: 6, fontSize: 12 }, font], accessibilityRole: "alert", children: submitError }) : null,
|
|
1515
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.View, { style: [s2.catRow, { marginTop: 12 }], accessibilityRole: "radiogroup", children: import_reporter_ui2.REPORTER_CATEGORIES.map((key) => {
|
|
870
1516
|
const active = category === key;
|
|
871
|
-
|
|
872
|
-
|
|
1517
|
+
const label = (0, import_reporter_ui2.reporterCategoryLabel)(key, locale);
|
|
1518
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1519
|
+
import_react_native3.TouchableOpacity,
|
|
873
1520
|
{
|
|
874
|
-
onPress: () => setCategory(key),
|
|
1521
|
+
onPress: () => setCategory(active ? null : key),
|
|
875
1522
|
activeOpacity: 0.7,
|
|
1523
|
+
accessibilityRole: "radio",
|
|
1524
|
+
accessibilityState: { selected: active, checked: active },
|
|
1525
|
+
accessibilityLabel: label,
|
|
876
1526
|
style: [
|
|
877
|
-
|
|
1527
|
+
s2.catBtn,
|
|
878
1528
|
{
|
|
879
|
-
backgroundColor: active ? colors.accent : colors.
|
|
1529
|
+
backgroundColor: active ? colors.accent : colors.surface,
|
|
880
1530
|
borderColor: active ? colors.accent : colors.border
|
|
881
1531
|
}
|
|
882
1532
|
],
|
|
883
1533
|
children: [
|
|
884
|
-
/* @__PURE__ */ (0,
|
|
885
|
-
/* @__PURE__ */ (0,
|
|
886
|
-
|
|
1534
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: s2.catEmoji, importantForAccessibility: "no", children: CATEGORY_EMOJI[key] }),
|
|
1535
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1536
|
+
import_react_native3.Text,
|
|
887
1537
|
{
|
|
1538
|
+
numberOfLines: 1,
|
|
888
1539
|
style: [
|
|
889
|
-
|
|
890
|
-
{ color: active ? colors.
|
|
1540
|
+
s2.catLabel,
|
|
1541
|
+
{ color: active ? colors.accentFg : colors.fg },
|
|
1542
|
+
font
|
|
891
1543
|
],
|
|
892
|
-
children:
|
|
1544
|
+
children: label
|
|
893
1545
|
}
|
|
894
1546
|
)
|
|
895
1547
|
]
|
|
@@ -897,69 +1549,49 @@ var MushiBottomSheet = ({
|
|
|
897
1549
|
key
|
|
898
1550
|
);
|
|
899
1551
|
}) }),
|
|
900
|
-
/* @__PURE__ */ (0,
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
style: [
|
|
904
|
-
s.input,
|
|
905
|
-
{
|
|
906
|
-
backgroundColor: colors.card,
|
|
907
|
-
color: colors.text,
|
|
908
|
-
borderColor: colors.border
|
|
909
|
-
}
|
|
910
|
-
],
|
|
911
|
-
placeholder: t.step3.descriptionPlaceholder,
|
|
912
|
-
placeholderTextColor: colors.sub,
|
|
913
|
-
multiline: true,
|
|
914
|
-
textAlignVertical: "top",
|
|
915
|
-
value: description,
|
|
916
|
-
onChangeText: (text) => {
|
|
917
|
-
setDescription(text.slice(0, 4e3));
|
|
918
|
-
if (submitError) setSubmitError(null);
|
|
919
|
-
if (phase === "error") setPhase("form");
|
|
920
|
-
},
|
|
921
|
-
editable: phase === "form" || phase === "error",
|
|
922
|
-
maxLength: 4e3
|
|
923
|
-
}
|
|
924
|
-
),
|
|
925
|
-
submitError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: { color: colors.accent, marginTop: 6, fontSize: 12 }, children: submitError }) : null,
|
|
926
|
-
activeScreenshot ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_native2.View, { style: s.screenshotRow, children: [
|
|
927
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
928
|
-
import_react_native2.Image,
|
|
1552
|
+
activeScreenshot ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: s2.screenshotRow, children: [
|
|
1553
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1554
|
+
import_react_native3.Image,
|
|
929
1555
|
{
|
|
930
1556
|
source: { uri: activeScreenshot },
|
|
931
|
-
style: [
|
|
1557
|
+
style: [s2.screenshotThumb, { backgroundColor: colors.fg }],
|
|
932
1558
|
accessibilityLabel: t.step3.screenshotPreviewAlt
|
|
933
1559
|
}
|
|
934
1560
|
),
|
|
935
|
-
/* @__PURE__ */ (0,
|
|
936
|
-
/* @__PURE__ */ (0,
|
|
937
|
-
/* @__PURE__ */ (0,
|
|
1561
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native3.View, { style: s2.screenshotMeta, children: [
|
|
1562
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.screenshotLabel, { color: colors.fg }, font], children: t.step3.screenshotAttached.replace(" \u2713", "") }),
|
|
1563
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.screenshotSub, { color: colors.muted }, font], children: screenshotSensitiveHint && screenshotSensitiveHint.trim() ? `\u26A0 ${screenshotSensitiveHint}` : t.step3.screenshotSensitiveHint.split("\u2014")[0]?.trim() ?? t.step3.screenshotSensitiveHint })
|
|
938
1564
|
] }),
|
|
939
|
-
/* @__PURE__ */ (0,
|
|
940
|
-
|
|
1565
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1566
|
+
import_react_native3.TouchableOpacity,
|
|
941
1567
|
{
|
|
942
1568
|
onPress: () => {
|
|
943
1569
|
setScreenshotAttached(false);
|
|
944
1570
|
onClearScreenshot?.();
|
|
945
1571
|
},
|
|
946
1572
|
hitSlop: { top: 8, bottom: 8, left: 8, right: 8 },
|
|
1573
|
+
accessibilityRole: "button",
|
|
947
1574
|
accessibilityLabel: t.widget.close,
|
|
948
|
-
children: /* @__PURE__ */ (0,
|
|
1575
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.screenshotRemove, { color: colors.muted }], children: "\u2715" })
|
|
949
1576
|
}
|
|
950
1577
|
)
|
|
951
1578
|
] }) : null,
|
|
952
|
-
/* @__PURE__ */ (0,
|
|
953
|
-
|
|
1579
|
+
submitHint ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [{ color: colors.muted, fontSize: 12, marginBottom: 6 }, font], children: submitHint }) : null,
|
|
1580
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1581
|
+
import_react_native3.TouchableOpacity,
|
|
954
1582
|
{
|
|
955
1583
|
onPress: handleSubmit,
|
|
956
1584
|
disabled: !canSubmit,
|
|
957
1585
|
activeOpacity: 0.8,
|
|
1586
|
+
accessibilityRole: "button",
|
|
1587
|
+
accessibilityLabel: phase === "sending" ? t.widget.submitting : t.widget.submit,
|
|
1588
|
+
accessibilityHint: submitHint,
|
|
1589
|
+
accessibilityState: { disabled: !canSubmit, busy: phase === "sending" },
|
|
958
1590
|
style: [
|
|
959
|
-
|
|
960
|
-
{ backgroundColor: canSubmit ? colors.accent : colors.disabled }
|
|
1591
|
+
s2.submitBtn,
|
|
1592
|
+
{ backgroundColor: canSubmit ? colors.accent : colors.disabled, borderRadius: radius }
|
|
961
1593
|
],
|
|
962
|
-
children: /* @__PURE__ */ (0,
|
|
1594
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.Text, { style: [s2.submitText, { color: canSubmit ? colors.accentFg : colors.disabledFg }, font], children: phase === "sending" ? t.widget.submitting : t.widget.submit })
|
|
963
1595
|
}
|
|
964
1596
|
)
|
|
965
1597
|
]
|
|
@@ -972,7 +1604,7 @@ var MushiBottomSheet = ({
|
|
|
972
1604
|
}
|
|
973
1605
|
) });
|
|
974
1606
|
};
|
|
975
|
-
var
|
|
1607
|
+
var s2 = import_react_native3.StyleSheet.create({
|
|
976
1608
|
flex: { flex: 1 },
|
|
977
1609
|
backdrop: {
|
|
978
1610
|
// Inlined StyleSheet.absoluteFillObject — RN 0.86 dropped it from the
|
|
@@ -989,10 +1621,10 @@ var s = import_react_native2.StyleSheet.create({
|
|
|
989
1621
|
left: 0,
|
|
990
1622
|
right: 0,
|
|
991
1623
|
minHeight: SHEET_HEIGHT,
|
|
992
|
-
borderTopLeftRadius:
|
|
993
|
-
borderTopRightRadius:
|
|
994
|
-
paddingBottom:
|
|
995
|
-
shadowColor:
|
|
1624
|
+
borderTopLeftRadius: import_core5.MUSHI_RADIUS.sheet,
|
|
1625
|
+
borderTopRightRadius: import_core5.MUSHI_RADIUS.sheet,
|
|
1626
|
+
paddingBottom: import_react_native3.Platform.OS === "ios" ? 34 : import_core5.MUSHI_SPACING.roomy,
|
|
1627
|
+
shadowColor: import_core5.MUSHI_SHADOW_INK,
|
|
996
1628
|
shadowOffset: { width: 0, height: -3 },
|
|
997
1629
|
shadowOpacity: 0.12,
|
|
998
1630
|
shadowRadius: 8,
|
|
@@ -1000,88 +1632,72 @@ var s = import_react_native2.StyleSheet.create({
|
|
|
1000
1632
|
},
|
|
1001
1633
|
handleArea: {
|
|
1002
1634
|
alignItems: "center",
|
|
1003
|
-
paddingVertical:
|
|
1635
|
+
paddingVertical: import_core5.MUSHI_SPACING.comfy
|
|
1004
1636
|
},
|
|
1005
1637
|
handle: {
|
|
1006
1638
|
width: 40,
|
|
1007
1639
|
height: 5,
|
|
1008
|
-
borderRadius:
|
|
1640
|
+
borderRadius: import_core5.MUSHI_RADIUS.control,
|
|
1009
1641
|
opacity: 0.5
|
|
1010
1642
|
},
|
|
1011
|
-
|
|
1012
|
-
paddingHorizontal:
|
|
1013
|
-
|
|
1014
|
-
paddingBottom: import_core3.MUSHI_SPACING.comfy,
|
|
1015
|
-
borderBottomWidth: 1.5
|
|
1643
|
+
header: {
|
|
1644
|
+
paddingHorizontal: import_core5.MUSHI_SPACING.lounge,
|
|
1645
|
+
paddingBottom: import_core5.MUSHI_SPACING.comfy
|
|
1016
1646
|
},
|
|
1017
|
-
|
|
1018
|
-
fontSize: import_core3.MUSHI_TYPE.sizeLabel,
|
|
1019
|
-
fontWeight: "700",
|
|
1020
|
-
letterSpacing: 1.2,
|
|
1021
|
-
marginBottom: 2,
|
|
1022
|
-
opacity: 0.7
|
|
1023
|
-
},
|
|
1024
|
-
brandTitle: {
|
|
1647
|
+
headerTitle: {
|
|
1025
1648
|
fontSize: 18,
|
|
1026
|
-
fontWeight: "
|
|
1649
|
+
fontWeight: "700",
|
|
1027
1650
|
letterSpacing: -0.2
|
|
1028
1651
|
},
|
|
1029
1652
|
body: {
|
|
1030
|
-
paddingHorizontal:
|
|
1031
|
-
paddingTop:
|
|
1032
|
-
},
|
|
1033
|
-
title: {
|
|
1034
|
-
fontSize: 20,
|
|
1035
|
-
fontWeight: "700",
|
|
1036
|
-
marginBottom: import_core3.MUSHI_SPACING.roomy
|
|
1653
|
+
paddingHorizontal: import_core5.MUSHI_SPACING.lounge,
|
|
1654
|
+
paddingTop: import_core5.MUSHI_SPACING.tight
|
|
1037
1655
|
},
|
|
1038
1656
|
stepLabel: {
|
|
1039
|
-
fontSize:
|
|
1657
|
+
fontSize: import_core5.MUSHI_TYPE.sizeLabel,
|
|
1040
1658
|
fontWeight: "600",
|
|
1041
1659
|
letterSpacing: 0.2,
|
|
1042
|
-
marginTop:
|
|
1043
|
-
marginBottom:
|
|
1660
|
+
marginTop: import_core5.MUSHI_SPACING.snug,
|
|
1661
|
+
marginBottom: import_core5.MUSHI_SPACING.comfy,
|
|
1044
1662
|
textTransform: "uppercase"
|
|
1045
1663
|
},
|
|
1046
1664
|
catRow: {
|
|
1047
1665
|
flexDirection: "row",
|
|
1048
|
-
|
|
1049
|
-
|
|
1666
|
+
flexWrap: "wrap",
|
|
1667
|
+
gap: 8,
|
|
1668
|
+
marginBottom: import_core5.MUSHI_SPACING.roomy
|
|
1050
1669
|
},
|
|
1051
1670
|
catBtn: {
|
|
1671
|
+
flexDirection: "row",
|
|
1052
1672
|
alignItems: "center",
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1673
|
+
gap: 6,
|
|
1674
|
+
paddingVertical: 8,
|
|
1675
|
+
paddingHorizontal: 12,
|
|
1676
|
+
borderRadius: 999,
|
|
1056
1677
|
borderWidth: 1,
|
|
1057
|
-
flex: 1,
|
|
1058
|
-
marginHorizontal: 3,
|
|
1059
1678
|
minHeight: 44
|
|
1060
1679
|
},
|
|
1061
1680
|
catEmoji: {
|
|
1062
|
-
fontSize:
|
|
1063
|
-
marginBottom: import_core3.MUSHI_SPACING.tight
|
|
1681
|
+
fontSize: 16
|
|
1064
1682
|
},
|
|
1065
1683
|
catLabel: {
|
|
1066
|
-
fontSize:
|
|
1684
|
+
fontSize: 14,
|
|
1067
1685
|
fontWeight: "600"
|
|
1068
1686
|
},
|
|
1069
1687
|
input: {
|
|
1070
1688
|
borderWidth: 1,
|
|
1071
|
-
borderRadius: import_core3.MUSHI_RADIUS.card,
|
|
1072
1689
|
padding: 14,
|
|
1073
1690
|
fontSize: 15,
|
|
1074
1691
|
minHeight: 100,
|
|
1075
|
-
marginBottom:
|
|
1692
|
+
marginBottom: import_core5.MUSHI_SPACING.roomy
|
|
1076
1693
|
},
|
|
1077
1694
|
submitBtn: {
|
|
1078
|
-
borderRadius: import_core3.MUSHI_RADIUS.card,
|
|
1079
1695
|
paddingVertical: 14,
|
|
1080
1696
|
alignItems: "center",
|
|
1081
1697
|
minHeight: 44
|
|
1082
1698
|
},
|
|
1083
1699
|
submitText: {
|
|
1084
|
-
color:
|
|
1700
|
+
color: import_core5.MUSHI_INVERSE,
|
|
1085
1701
|
fontSize: 16,
|
|
1086
1702
|
fontWeight: "700"
|
|
1087
1703
|
},
|
|
@@ -1132,7 +1748,7 @@ var s = import_react_native2.StyleSheet.create({
|
|
|
1132
1748
|
},
|
|
1133
1749
|
tabRow: {
|
|
1134
1750
|
flexDirection: "row",
|
|
1135
|
-
borderBottomWidth:
|
|
1751
|
+
borderBottomWidth: import_react_native3.StyleSheet.hairlineWidth,
|
|
1136
1752
|
marginBottom: 8
|
|
1137
1753
|
},
|
|
1138
1754
|
tabBtn: {
|
|
@@ -1140,7 +1756,8 @@ var s = import_react_native2.StyleSheet.create({
|
|
|
1140
1756
|
alignItems: "center",
|
|
1141
1757
|
paddingVertical: 10,
|
|
1142
1758
|
borderBottomWidth: 2,
|
|
1143
|
-
borderBottomColor: "transparent"
|
|
1759
|
+
borderBottomColor: "transparent",
|
|
1760
|
+
minHeight: 44
|
|
1144
1761
|
},
|
|
1145
1762
|
tabLabel: {
|
|
1146
1763
|
fontSize: 14,
|
|
@@ -1148,12 +1765,47 @@ var s = import_react_native2.StyleSheet.create({
|
|
|
1148
1765
|
},
|
|
1149
1766
|
inboxRow: {
|
|
1150
1767
|
paddingVertical: 12,
|
|
1151
|
-
|
|
1768
|
+
minHeight: 56,
|
|
1769
|
+
borderBottomWidth: import_react_native3.StyleSheet.hairlineWidth
|
|
1770
|
+
},
|
|
1771
|
+
headerCard: {
|
|
1772
|
+
borderWidth: import_react_native3.StyleSheet.hairlineWidth,
|
|
1773
|
+
padding: 12,
|
|
1774
|
+
marginBottom: 12
|
|
1775
|
+
},
|
|
1776
|
+
pill: {
|
|
1777
|
+
alignSelf: "flex-start",
|
|
1778
|
+
borderWidth: 1,
|
|
1779
|
+
borderRadius: 999,
|
|
1780
|
+
paddingHorizontal: 8,
|
|
1781
|
+
paddingVertical: 2,
|
|
1782
|
+
fontSize: 12,
|
|
1783
|
+
fontWeight: "600",
|
|
1784
|
+
overflow: "hidden"
|
|
1785
|
+
},
|
|
1786
|
+
skeleton: {
|
|
1787
|
+
height: 14,
|
|
1788
|
+
borderRadius: 6,
|
|
1789
|
+
marginBottom: 10
|
|
1790
|
+
},
|
|
1791
|
+
errorRow: {
|
|
1792
|
+
flexDirection: "row",
|
|
1793
|
+
alignItems: "center",
|
|
1794
|
+
gap: 8,
|
|
1795
|
+
marginBottom: 12
|
|
1796
|
+
},
|
|
1797
|
+
retryBtn: {
|
|
1798
|
+
borderWidth: 1,
|
|
1799
|
+
paddingHorizontal: 12,
|
|
1800
|
+
paddingVertical: 8,
|
|
1801
|
+
minHeight: 44,
|
|
1802
|
+
justifyContent: "center"
|
|
1152
1803
|
},
|
|
1153
1804
|
threadBubble: {
|
|
1154
1805
|
borderRadius: 10,
|
|
1155
1806
|
padding: 10,
|
|
1156
|
-
marginBottom: 8
|
|
1807
|
+
marginBottom: 8,
|
|
1808
|
+
maxWidth: "88%"
|
|
1157
1809
|
},
|
|
1158
1810
|
verifyRow: {
|
|
1159
1811
|
flexDirection: "row",
|
|
@@ -1162,9 +1814,26 @@ var s = import_react_native2.StyleSheet.create({
|
|
|
1162
1814
|
},
|
|
1163
1815
|
verifyBtn: {
|
|
1164
1816
|
flex: 1,
|
|
1165
|
-
borderRadius: 10,
|
|
1166
1817
|
paddingVertical: 10,
|
|
1167
|
-
alignItems: "center"
|
|
1818
|
+
alignItems: "center",
|
|
1819
|
+
minHeight: 44
|
|
1820
|
+
},
|
|
1821
|
+
composer: {
|
|
1822
|
+
flexDirection: "row",
|
|
1823
|
+
alignItems: "flex-end",
|
|
1824
|
+
gap: 8
|
|
1825
|
+
},
|
|
1826
|
+
composerInput: {
|
|
1827
|
+
flex: 1,
|
|
1828
|
+
minHeight: 44,
|
|
1829
|
+
maxHeight: 120,
|
|
1830
|
+
marginBottom: 0
|
|
1831
|
+
},
|
|
1832
|
+
sendBtn: {
|
|
1833
|
+
paddingHorizontal: 16,
|
|
1834
|
+
paddingVertical: 12,
|
|
1835
|
+
minHeight: 44,
|
|
1836
|
+
justifyContent: "center"
|
|
1168
1837
|
},
|
|
1169
1838
|
assistantBubble: {
|
|
1170
1839
|
maxWidth: "88%",
|
|
@@ -1190,14 +1859,14 @@ var s = import_react_native2.StyleSheet.create({
|
|
|
1190
1859
|
});
|
|
1191
1860
|
|
|
1192
1861
|
// src/components/MushiFloatingButton.tsx
|
|
1193
|
-
var
|
|
1194
|
-
var
|
|
1195
|
-
var
|
|
1196
|
-
var
|
|
1197
|
-
var FAB_SIZE =
|
|
1862
|
+
var import_react3 = require("react");
|
|
1863
|
+
var import_react_native4 = require("react-native");
|
|
1864
|
+
var import_core6 = require("@mushi-mushi/core");
|
|
1865
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1866
|
+
var FAB_SIZE = import_core6.MUSHI_GEOMETRY.fabSize;
|
|
1198
1867
|
var EDGE_PADDING = 12;
|
|
1199
1868
|
function getScreenBounds() {
|
|
1200
|
-
const { width, height } =
|
|
1869
|
+
const { width, height } = import_react_native4.Dimensions.get("window");
|
|
1201
1870
|
return { width, height };
|
|
1202
1871
|
}
|
|
1203
1872
|
function clamp(value, min, max) {
|
|
@@ -1228,25 +1897,25 @@ var MushiFloatingButton = ({
|
|
|
1228
1897
|
config,
|
|
1229
1898
|
accent
|
|
1230
1899
|
}) => {
|
|
1231
|
-
const scheme = (0,
|
|
1900
|
+
const scheme = (0, import_react_native4.useColorScheme)();
|
|
1232
1901
|
const dark = scheme === "dark";
|
|
1233
1902
|
const draggable = config?.draggable ?? false;
|
|
1234
1903
|
const snapToEdge = config?.snapToEdge ?? true;
|
|
1235
1904
|
const persist = config?.persist ?? false;
|
|
1236
|
-
const baseBottom = inset?.bottom ?? (
|
|
1905
|
+
const baseBottom = inset?.bottom ?? (import_react_native4.Platform.OS === "ios" ? 50 : 28);
|
|
1237
1906
|
const baseX = position === "bottom-left" ? inset?.left ?? EDGE_PADDING : (() => {
|
|
1238
1907
|
const { width } = getScreenBounds();
|
|
1239
1908
|
return width - FAB_SIZE - (inset?.right ?? EDGE_PADDING);
|
|
1240
1909
|
})();
|
|
1241
|
-
const scale = (0,
|
|
1242
|
-
const posX = (0,
|
|
1243
|
-
const posY = (0,
|
|
1244
|
-
const currentX = (0,
|
|
1245
|
-
const currentY = (0,
|
|
1246
|
-
const isDragging = (0,
|
|
1247
|
-
const pal = (0,
|
|
1910
|
+
const scale = (0, import_react3.useRef)(new import_react_native4.Animated.Value(1)).current;
|
|
1911
|
+
const posX = (0, import_react3.useRef)(new import_react_native4.Animated.Value(baseX)).current;
|
|
1912
|
+
const posY = (0, import_react3.useRef)(new import_react_native4.Animated.Value(0)).current;
|
|
1913
|
+
const currentX = (0, import_react3.useRef)(baseX);
|
|
1914
|
+
const currentY = (0, import_react3.useRef)(0);
|
|
1915
|
+
const isDragging = (0, import_react3.useRef)(false);
|
|
1916
|
+
const pal = (0, import_core6.mushiPalette)(dark ? "dark" : "light");
|
|
1248
1917
|
const accentColor = accent ?? pal.accent;
|
|
1249
|
-
(0,
|
|
1918
|
+
(0, import_react3.useEffect)(() => {
|
|
1250
1919
|
if (!persist) return;
|
|
1251
1920
|
void loadStoredPos().then((pos) => {
|
|
1252
1921
|
if (!pos) return;
|
|
@@ -1256,7 +1925,7 @@ var MushiFloatingButton = ({
|
|
|
1256
1925
|
posY.setValue(pos.y);
|
|
1257
1926
|
});
|
|
1258
1927
|
}, [persist, posX, posY]);
|
|
1259
|
-
const snapAndPersist = (0,
|
|
1928
|
+
const snapAndPersist = (0, import_react3.useCallback)(
|
|
1260
1929
|
(x, y) => {
|
|
1261
1930
|
const { width, height } = getScreenBounds();
|
|
1262
1931
|
let snappedX = x;
|
|
@@ -1268,13 +1937,13 @@ var MushiFloatingButton = ({
|
|
|
1268
1937
|
const clampedY = clamp(y, -(height - FAB_SIZE - baseBottom - 60), 0);
|
|
1269
1938
|
currentX.current = clampedX;
|
|
1270
1939
|
currentY.current = clampedY;
|
|
1271
|
-
|
|
1940
|
+
import_react_native4.Animated.spring(posX, {
|
|
1272
1941
|
toValue: clampedX,
|
|
1273
1942
|
useNativeDriver: false,
|
|
1274
1943
|
damping: 18,
|
|
1275
1944
|
stiffness: 220
|
|
1276
1945
|
}).start();
|
|
1277
|
-
|
|
1946
|
+
import_react_native4.Animated.spring(posY, {
|
|
1278
1947
|
toValue: clampedY,
|
|
1279
1948
|
useNativeDriver: false,
|
|
1280
1949
|
damping: 18,
|
|
@@ -1284,8 +1953,8 @@ var MushiFloatingButton = ({
|
|
|
1284
1953
|
},
|
|
1285
1954
|
[snapToEdge, persist, posX, posY, baseBottom]
|
|
1286
1955
|
);
|
|
1287
|
-
const panResponder = (0,
|
|
1288
|
-
draggable ?
|
|
1956
|
+
const panResponder = (0, import_react3.useRef)(
|
|
1957
|
+
draggable ? import_react_native4.PanResponder.create({
|
|
1289
1958
|
onStartShouldSetPanResponder: () => true,
|
|
1290
1959
|
onMoveShouldSetPanResponder: (_, g) => Math.abs(g.dx) > 6 || Math.abs(g.dy) > 6,
|
|
1291
1960
|
onPanResponderGrant: () => {
|
|
@@ -1318,19 +1987,19 @@ var MushiFloatingButton = ({
|
|
|
1318
1987
|
}
|
|
1319
1988
|
isDragging.current = false;
|
|
1320
1989
|
}
|
|
1321
|
-
}) :
|
|
1990
|
+
}) : import_react_native4.PanResponder.create({})
|
|
1322
1991
|
).current;
|
|
1323
|
-
const handlePressIn = (0,
|
|
1992
|
+
const handlePressIn = (0, import_react3.useCallback)(() => {
|
|
1324
1993
|
if (isDragging.current) return;
|
|
1325
|
-
|
|
1994
|
+
import_react_native4.Animated.spring(scale, {
|
|
1326
1995
|
toValue: 0.85,
|
|
1327
1996
|
useNativeDriver: true,
|
|
1328
1997
|
damping: 15,
|
|
1329
1998
|
stiffness: 300
|
|
1330
1999
|
}).start();
|
|
1331
2000
|
}, [scale]);
|
|
1332
|
-
const handlePressOut = (0,
|
|
1333
|
-
|
|
2001
|
+
const handlePressOut = (0, import_react3.useCallback)(() => {
|
|
2002
|
+
import_react_native4.Animated.spring(scale, {
|
|
1334
2003
|
toValue: 1,
|
|
1335
2004
|
useNativeDriver: true,
|
|
1336
2005
|
damping: 10,
|
|
@@ -1338,15 +2007,15 @@ var MushiFloatingButton = ({
|
|
|
1338
2007
|
}).start();
|
|
1339
2008
|
if (!isDragging.current) onPress();
|
|
1340
2009
|
}, [scale, onPress]);
|
|
1341
|
-
return /* @__PURE__ */ (0,
|
|
1342
|
-
|
|
2010
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2011
|
+
import_react_native4.Animated.View,
|
|
1343
2012
|
{
|
|
1344
2013
|
...draggable ? panResponder.panHandlers : {},
|
|
1345
2014
|
style: [
|
|
1346
|
-
|
|
2015
|
+
s3.fab,
|
|
1347
2016
|
{
|
|
1348
2017
|
left: posX,
|
|
1349
|
-
bottom:
|
|
2018
|
+
bottom: import_react_native4.Animated.add(new import_react_native4.Animated.Value(baseBottom), import_react_native4.Animated.multiply(posY, -1)),
|
|
1350
2019
|
backgroundColor: accentColor,
|
|
1351
2020
|
shadowColor: dark ? accentColor : pal.ink,
|
|
1352
2021
|
transform: [{ scale }]
|
|
@@ -1355,11 +2024,11 @@ var MushiFloatingButton = ({
|
|
|
1355
2024
|
onStartShouldSetResponder: () => !draggable,
|
|
1356
2025
|
onResponderGrant: handlePressIn,
|
|
1357
2026
|
onResponderRelease: handlePressOut,
|
|
1358
|
-
children: /* @__PURE__ */ (0,
|
|
2027
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native4.Animated.Text, { style: s3.emoji, children: import_core6.MUSHI_COPY.triggerText })
|
|
1359
2028
|
}
|
|
1360
2029
|
);
|
|
1361
2030
|
};
|
|
1362
|
-
var
|
|
2031
|
+
var s3 = import_react_native4.StyleSheet.create({
|
|
1363
2032
|
fab: {
|
|
1364
2033
|
position: "absolute",
|
|
1365
2034
|
width: FAB_SIZE,
|
|
@@ -1379,93 +2048,93 @@ var s2 = import_react_native3.StyleSheet.create({
|
|
|
1379
2048
|
});
|
|
1380
2049
|
|
|
1381
2050
|
// src/components/MushiBanner.tsx
|
|
1382
|
-
var
|
|
1383
|
-
var
|
|
1384
|
-
var
|
|
1385
|
-
var
|
|
1386
|
-
var MUSHI_BANNER_DEFAULT_HEIGHT =
|
|
2051
|
+
var import_react4 = require("react");
|
|
2052
|
+
var import_react_native5 = require("react-native");
|
|
2053
|
+
var import_core7 = require("@mushi-mushi/core");
|
|
2054
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
2055
|
+
var MUSHI_BANNER_DEFAULT_HEIGHT = import_core7.MUSHI_GEOMETRY.bannerHeight;
|
|
1387
2056
|
var MushiBanner = ({
|
|
1388
2057
|
onPress,
|
|
1389
2058
|
onDismiss,
|
|
1390
|
-
message =
|
|
1391
|
-
label =
|
|
2059
|
+
message = import_core7.MUSHI_COPY.bannerMessage,
|
|
2060
|
+
label = import_core7.MUSHI_COPY.bannerLabel,
|
|
1392
2061
|
onLayoutHeight
|
|
1393
2062
|
}) => {
|
|
1394
|
-
const handleLayout = (0,
|
|
2063
|
+
const handleLayout = (0, import_react4.useCallback)(
|
|
1395
2064
|
(e) => {
|
|
1396
2065
|
onLayoutHeight?.(e.nativeEvent.layout.height);
|
|
1397
2066
|
},
|
|
1398
2067
|
[onLayoutHeight]
|
|
1399
2068
|
);
|
|
1400
|
-
return /* @__PURE__ */ (0,
|
|
1401
|
-
|
|
2069
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
2070
|
+
import_react_native5.View,
|
|
1402
2071
|
{
|
|
1403
|
-
style:
|
|
2072
|
+
style: s4.wrap,
|
|
1404
2073
|
onLayout: handleLayout,
|
|
1405
2074
|
accessibilityRole: "header",
|
|
1406
2075
|
accessibilityLabel: "Mushi feedback banner",
|
|
1407
2076
|
children: [
|
|
1408
|
-
/* @__PURE__ */ (0,
|
|
1409
|
-
/* @__PURE__ */ (0,
|
|
1410
|
-
/* @__PURE__ */ (0,
|
|
1411
|
-
/* @__PURE__ */ (0,
|
|
2077
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native5.TouchableOpacity, { style: s4.main, onPress, activeOpacity: 0.85, children: [
|
|
2078
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native5.Text, { style: s4.eyebrow, children: label.toUpperCase() }),
|
|
2079
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native5.Text, { style: s4.message, numberOfLines: 2, children: message }),
|
|
2080
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native5.Text, { style: s4.cta, children: import_core7.MUSHI_COPY.bugCta })
|
|
1412
2081
|
] }),
|
|
1413
|
-
onDismiss ? /* @__PURE__ */ (0,
|
|
1414
|
-
|
|
2082
|
+
onDismiss ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
2083
|
+
import_react_native5.TouchableOpacity,
|
|
1415
2084
|
{
|
|
1416
|
-
style:
|
|
2085
|
+
style: s4.dismiss,
|
|
1417
2086
|
onPress: onDismiss,
|
|
1418
2087
|
accessibilityRole: "button",
|
|
1419
2088
|
accessibilityLabel: "Dismiss banner",
|
|
1420
2089
|
hitSlop: { top: 8, bottom: 8, left: 8, right: 8 },
|
|
1421
|
-
children: /* @__PURE__ */ (0,
|
|
2090
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native5.Text, { style: s4.dismissText, children: "\xD7" })
|
|
1422
2091
|
}
|
|
1423
2092
|
) : null
|
|
1424
2093
|
]
|
|
1425
2094
|
}
|
|
1426
2095
|
);
|
|
1427
2096
|
};
|
|
1428
|
-
var
|
|
2097
|
+
var s4 = import_react_native5.StyleSheet.create({
|
|
1429
2098
|
wrap: {
|
|
1430
2099
|
position: "absolute",
|
|
1431
2100
|
left: 0,
|
|
1432
2101
|
right: 0,
|
|
1433
2102
|
bottom: 0,
|
|
1434
|
-
minHeight:
|
|
2103
|
+
minHeight: import_core7.MUSHI_GEOMETRY.bannerHeight,
|
|
1435
2104
|
flexDirection: "row",
|
|
1436
2105
|
alignItems: "stretch",
|
|
1437
|
-
backgroundColor:
|
|
2106
|
+
backgroundColor: import_core7.MUSHI_BANNER_NEON.bg,
|
|
1438
2107
|
borderTopWidth: 1,
|
|
1439
|
-
borderTopColor:
|
|
1440
|
-
zIndex:
|
|
2108
|
+
borderTopColor: import_core7.MUSHI_BANNER_NEON.border,
|
|
2109
|
+
zIndex: import_core7.MUSHI_Z.banner,
|
|
1441
2110
|
elevation: 8,
|
|
1442
|
-
paddingBottom:
|
|
2111
|
+
paddingBottom: import_react_native5.Platform.OS === "ios" ? 0 : 0
|
|
1443
2112
|
},
|
|
1444
2113
|
main: {
|
|
1445
2114
|
flex: 1,
|
|
1446
2115
|
flexDirection: "row",
|
|
1447
2116
|
alignItems: "center",
|
|
1448
|
-
gap:
|
|
1449
|
-
paddingHorizontal:
|
|
1450
|
-
paddingVertical:
|
|
2117
|
+
gap: import_core7.MUSHI_SPACING.snug,
|
|
2118
|
+
paddingHorizontal: import_core7.MUSHI_SPACING.comfy,
|
|
2119
|
+
paddingVertical: import_core7.MUSHI_SPACING.snug,
|
|
1451
2120
|
minHeight: 44
|
|
1452
2121
|
},
|
|
1453
2122
|
eyebrow: {
|
|
1454
|
-
fontSize:
|
|
2123
|
+
fontSize: import_core7.MUSHI_TYPE.sizeLabel,
|
|
1455
2124
|
fontWeight: "700",
|
|
1456
2125
|
letterSpacing: 1.2,
|
|
1457
|
-
color:
|
|
2126
|
+
color: import_core7.MUSHI_BANNER_NEON.fg
|
|
1458
2127
|
},
|
|
1459
2128
|
message: {
|
|
1460
2129
|
flex: 1,
|
|
1461
2130
|
fontSize: 12,
|
|
1462
2131
|
fontWeight: "500",
|
|
1463
|
-
color:
|
|
2132
|
+
color: import_core7.MUSHI_BANNER_NEON.fg
|
|
1464
2133
|
},
|
|
1465
2134
|
cta: {
|
|
1466
2135
|
fontSize: 12,
|
|
1467
2136
|
fontWeight: "700",
|
|
1468
|
-
color:
|
|
2137
|
+
color: import_core7.MUSHI_BANNER_NEON.fg,
|
|
1469
2138
|
textDecorationLine: "underline"
|
|
1470
2139
|
},
|
|
1471
2140
|
dismiss: {
|
|
@@ -1475,13 +2144,13 @@ var s3 = import_react_native4.StyleSheet.create({
|
|
|
1475
2144
|
dismissText: {
|
|
1476
2145
|
fontSize: 20,
|
|
1477
2146
|
lineHeight: 22,
|
|
1478
|
-
color:
|
|
2147
|
+
color: import_core7.MUSHI_BANNER_NEON.fg,
|
|
1479
2148
|
opacity: 0.7
|
|
1480
2149
|
}
|
|
1481
2150
|
});
|
|
1482
2151
|
|
|
1483
2152
|
// src/analytics/event-tracker.ts
|
|
1484
|
-
var
|
|
2153
|
+
var import_core8 = require("@mushi-mushi/core");
|
|
1485
2154
|
var CONSENT_BUFFER_MAX = 50;
|
|
1486
2155
|
var SPILL_MAX = 200;
|
|
1487
2156
|
var SPILL_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -1545,7 +2214,7 @@ function createRNEventTracker(opts) {
|
|
|
1545
2214
|
const client = resolveClient();
|
|
1546
2215
|
if (!client) return;
|
|
1547
2216
|
flushing = true;
|
|
1548
|
-
const batch = buffer.splice(0,
|
|
2217
|
+
const batch = buffer.splice(0, import_core8.EVENT_PROPERTY_LIMITS.maxServerBatch);
|
|
1549
2218
|
if (pendingIdentify) {
|
|
1550
2219
|
batch.unshift({ name: "identify", ts: (/* @__PURE__ */ new Date()).toISOString(), properties: {} });
|
|
1551
2220
|
pendingIdentify = false;
|
|
@@ -1569,7 +2238,7 @@ function createRNEventTracker(opts) {
|
|
|
1569
2238
|
} finally {
|
|
1570
2239
|
flushing = false;
|
|
1571
2240
|
}
|
|
1572
|
-
if (buffer.length >=
|
|
2241
|
+
if (buffer.length >= import_core8.EVENT_PROPERTY_LIMITS.maxClientBatch) void flushNow();
|
|
1573
2242
|
}
|
|
1574
2243
|
function enqueue(ev) {
|
|
1575
2244
|
if (consent === "denied") return;
|
|
@@ -1578,7 +2247,7 @@ function createRNEventTracker(opts) {
|
|
|
1578
2247
|
return;
|
|
1579
2248
|
}
|
|
1580
2249
|
buffer.push(ev);
|
|
1581
|
-
if (buffer.length >=
|
|
2250
|
+
if (buffer.length >= import_core8.EVENT_PROPERTY_LIMITS.maxClientBatch) void flushNow();
|
|
1582
2251
|
}
|
|
1583
2252
|
const ready = enabled ? (async () => {
|
|
1584
2253
|
storage = await loadStorage();
|
|
@@ -1618,17 +2287,17 @@ function createRNEventTracker(opts) {
|
|
|
1618
2287
|
ready,
|
|
1619
2288
|
track(name, properties, trackOpts) {
|
|
1620
2289
|
if (!enabled || destroyed) return false;
|
|
1621
|
-
if (!(0,
|
|
1622
|
-
const { properties: props } = (0,
|
|
2290
|
+
if (!(0, import_core8.isValidEventName)(name)) return false;
|
|
2291
|
+
const { properties: props } = (0, import_core8.sanitizeEventProperties)(properties, { allowlist, scrub: opts.scrub });
|
|
1623
2292
|
let merged = props;
|
|
1624
2293
|
if (trackOpts?.reserved) {
|
|
1625
|
-
const { properties: res } = (0,
|
|
2294
|
+
const { properties: res } = (0, import_core8.sanitizeEventProperties)(trackOpts.reserved, { allowReserved: true, scrub: opts.scrub });
|
|
1626
2295
|
merged = { ...props, ...res };
|
|
1627
2296
|
}
|
|
1628
2297
|
merged.$surface = SURFACE;
|
|
1629
2298
|
const route = opts.getRoute?.();
|
|
1630
2299
|
if (route && merged.$route === void 0) merged.$route = route;
|
|
1631
|
-
if (!(0,
|
|
2300
|
+
if (!(0, import_core8.propertiesWithinByteLimit)(merged)) return false;
|
|
1632
2301
|
enqueue({
|
|
1633
2302
|
name,
|
|
1634
2303
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -1697,13 +2366,13 @@ function createRNEventTracker(opts) {
|
|
|
1697
2366
|
}
|
|
1698
2367
|
|
|
1699
2368
|
// src/analytics/session-tracker.ts
|
|
1700
|
-
var
|
|
2369
|
+
var import_core9 = require("@mushi-mushi/core");
|
|
1701
2370
|
var HEARTBEAT_INTERVAL_MS = 6e4;
|
|
1702
2371
|
var RESUME_WINDOW_MS = 30 * 6e4;
|
|
1703
2372
|
function createRNSessionTracker(opts) {
|
|
1704
2373
|
const clock = opts.now ?? Date.now;
|
|
1705
2374
|
const resolveClient = () => typeof opts.client === "function" ? opts.client() : opts.client;
|
|
1706
|
-
let sessionId = opts.sessionId ?? `sess_${(0,
|
|
2375
|
+
let sessionId = opts.sessionId ?? `sess_${(0, import_core9.newUuid)()}`;
|
|
1707
2376
|
let pageViewCount = 0;
|
|
1708
2377
|
let started = false;
|
|
1709
2378
|
let allowed = false;
|
|
@@ -1771,7 +2440,7 @@ function createRNSessionTracker(opts) {
|
|
|
1771
2440
|
backgroundedAt = null;
|
|
1772
2441
|
if (!allowed) return;
|
|
1773
2442
|
if (!started || away >= RESUME_WINDOW_MS) {
|
|
1774
|
-
sessionId = `sess_${(0,
|
|
2443
|
+
sessionId = `sess_${(0, import_core9.newUuid)()}`;
|
|
1775
2444
|
startSession();
|
|
1776
2445
|
} else {
|
|
1777
2446
|
send("session_heartbeat", { route: route() });
|
|
@@ -1800,14 +2469,14 @@ function createRNSessionTracker(opts) {
|
|
|
1800
2469
|
|
|
1801
2470
|
// src/version.ts
|
|
1802
2471
|
var MUSHI_SDK_PACKAGE = "@mushi-mushi/react-native";
|
|
1803
|
-
var MUSHI_SDK_VERSION = "0.
|
|
2472
|
+
var MUSHI_SDK_VERSION = "0.24.0" ? "0.24.0" : "0.17.0";
|
|
1804
2473
|
|
|
1805
2474
|
// src/provider.tsx
|
|
1806
|
-
var
|
|
1807
|
-
var MushiContext = (0,
|
|
2475
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
2476
|
+
var MushiContext = (0, import_react5.createContext)(null);
|
|
1808
2477
|
function nativeUserAgent() {
|
|
1809
|
-
const os =
|
|
1810
|
-
const version =
|
|
2478
|
+
const os = import_react_native6.Platform?.OS ?? "native";
|
|
2479
|
+
const version = import_react_native6.Platform?.Version;
|
|
1811
2480
|
return `${MUSHI_SDK_PACKAGE}/${MUSHI_SDK_VERSION} (${version != null ? `${os} ${String(version)}` : os})`;
|
|
1812
2481
|
}
|
|
1813
2482
|
var DEFAULT_SCREENSHOT_HINT = "Check the preview \u2014 remove it if any private info (balances, personal details) is visible.";
|
|
@@ -1848,28 +2517,28 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1848
2517
|
);
|
|
1849
2518
|
}
|
|
1850
2519
|
}
|
|
1851
|
-
const consoleRef = (0,
|
|
1852
|
-
const networkRef = (0,
|
|
1853
|
-
const queueRef = (0,
|
|
1854
|
-
const apiClientRef = (0,
|
|
1855
|
-
const sessionIdRef = (0,
|
|
1856
|
-
const breadcrumbsRef = (0,
|
|
1857
|
-
(0,
|
|
2520
|
+
const consoleRef = (0, import_react5.useRef)(null);
|
|
2521
|
+
const networkRef = (0, import_react5.useRef)(null);
|
|
2522
|
+
const queueRef = (0, import_react5.useRef)(null);
|
|
2523
|
+
const apiClientRef = (0, import_react5.useRef)(null);
|
|
2524
|
+
const sessionIdRef = (0, import_react5.useRef)(`sess_${(0, import_core10.newUuid)()}`);
|
|
2525
|
+
const breadcrumbsRef = (0, import_react5.useRef)(
|
|
2526
|
+
(0, import_core10.createBreadcrumbBuffer)({ max: config.capture?.maxBreadcrumbs ?? 50 })
|
|
1858
2527
|
);
|
|
1859
|
-
const [sheetVisible, setSheetVisible] = (0,
|
|
1860
|
-
const [sheetPreferredTab, setSheetPreferredTab] = (0,
|
|
1861
|
-
const [bannerDismissed, setBannerDismissed] = (0,
|
|
1862
|
-
const [sheetScreenshot, setSheetScreenshot] = (0,
|
|
1863
|
-
const reporterTokenRef = (0,
|
|
2528
|
+
const [sheetVisible, setSheetVisible] = (0, import_react5.useState)(false);
|
|
2529
|
+
const [sheetPreferredTab, setSheetPreferredTab] = (0, import_react5.useState)("report");
|
|
2530
|
+
const [bannerDismissed, setBannerDismissed] = (0, import_react5.useState)(false);
|
|
2531
|
+
const [sheetScreenshot, setSheetScreenshot] = (0, import_react5.useState)(null);
|
|
2532
|
+
const reporterTokenRef = (0, import_react5.useRef)(`rn-${config.projectId}-anon`);
|
|
1864
2533
|
let resolveReporterTokenReady = () => {
|
|
1865
2534
|
};
|
|
1866
|
-
const reporterTokenReadyRef = (0,
|
|
2535
|
+
const reporterTokenReadyRef = (0, import_react5.useRef)(
|
|
1867
2536
|
new Promise((resolve) => {
|
|
1868
2537
|
resolveReporterTokenReady = resolve;
|
|
1869
2538
|
})
|
|
1870
2539
|
);
|
|
1871
|
-
const userTokenRef = (0,
|
|
1872
|
-
const pageContextRef = (0,
|
|
2540
|
+
const userTokenRef = (0, import_react5.useRef)(null);
|
|
2541
|
+
const pageContextRef = (0, import_react5.useRef)(null);
|
|
1873
2542
|
const rawEndpoint = config.apiEndpoint ?? config.endpoint;
|
|
1874
2543
|
if (rawEndpoint !== void 0 && rawEndpoint.trim() === "") {
|
|
1875
2544
|
throw new Error(
|
|
@@ -1879,15 +2548,15 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1879
2548
|
if (config.endpoint && !config.apiEndpoint && __DEV__) {
|
|
1880
2549
|
console.warn("[MushiProvider] `endpoint` is deprecated \u2014 use `apiEndpoint` instead.");
|
|
1881
2550
|
}
|
|
1882
|
-
const apiEndpoint = rawEndpoint ??
|
|
1883
|
-
(0,
|
|
2551
|
+
const apiEndpoint = rawEndpoint ?? import_core10.DEFAULT_API_ENDPOINT;
|
|
2552
|
+
(0, import_react5.useEffect)(() => {
|
|
1884
2553
|
if (config.capture?.console !== false) {
|
|
1885
2554
|
consoleRef.current = setupConsoleCapture(config.capture?.maxConsoleEntries);
|
|
1886
2555
|
}
|
|
1887
2556
|
if (config.capture?.network !== false) {
|
|
1888
2557
|
networkRef.current = setupNetworkCapture(config.capture?.maxNetworkEntries, apiEndpoint);
|
|
1889
2558
|
}
|
|
1890
|
-
apiClientRef.current = (0,
|
|
2559
|
+
apiClientRef.current = (0, import_core10.createApiClient)({
|
|
1891
2560
|
projectId: config.projectId,
|
|
1892
2561
|
apiKey: config.apiKey,
|
|
1893
2562
|
apiEndpoint,
|
|
@@ -1924,14 +2593,14 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1924
2593
|
if (existing && existing.startsWith("mushi_")) {
|
|
1925
2594
|
reporterTokenRef.current = existing;
|
|
1926
2595
|
} else {
|
|
1927
|
-
const fresh = `mushi_${(0,
|
|
2596
|
+
const fresh = `mushi_${(0, import_core10.newUuid)()}`;
|
|
1928
2597
|
reporterTokenRef.current = fresh;
|
|
1929
2598
|
await saveReporterToken(fresh, secureStorage);
|
|
1930
2599
|
}
|
|
1931
2600
|
resolveReporterTokenReady();
|
|
1932
2601
|
} catch {
|
|
1933
2602
|
if (reporterTokenRef.current === `rn-${config.projectId}-anon`) {
|
|
1934
|
-
reporterTokenRef.current = `mushi_${(0,
|
|
2603
|
+
reporterTokenRef.current = `mushi_${(0, import_core10.newUuid)()}`;
|
|
1935
2604
|
}
|
|
1936
2605
|
resolveReporterTokenReady();
|
|
1937
2606
|
}
|
|
@@ -1942,11 +2611,10 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1942
2611
|
networkRef.current?.restore();
|
|
1943
2612
|
};
|
|
1944
2613
|
}, []);
|
|
1945
|
-
(0,
|
|
2614
|
+
(0, import_react5.useEffect)(() => {
|
|
1946
2615
|
let unsubscribe;
|
|
1947
2616
|
try {
|
|
1948
|
-
|
|
1949
|
-
unsubscribe = NetInfo.addEventListener((state) => {
|
|
2617
|
+
unsubscribe = resolveNetInfo(config.netInfo)?.addEventListener((state) => {
|
|
1950
2618
|
if (state.isConnected && state.isInternetReachable) {
|
|
1951
2619
|
queueRef.current?.flush().catch(() => {
|
|
1952
2620
|
});
|
|
@@ -1955,8 +2623,8 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1955
2623
|
} catch {
|
|
1956
2624
|
}
|
|
1957
2625
|
return () => unsubscribe?.();
|
|
1958
|
-
}, []);
|
|
1959
|
-
const open = (0,
|
|
2626
|
+
}, [config.netInfo]);
|
|
2627
|
+
const open = (0, import_react5.useCallback)(() => {
|
|
1960
2628
|
setSheetPreferredTab("report");
|
|
1961
2629
|
breadcrumbsRef.current.add({
|
|
1962
2630
|
category: "lifecycle",
|
|
@@ -1969,11 +2637,7 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1969
2637
|
setSheetVisible(true);
|
|
1970
2638
|
return;
|
|
1971
2639
|
}
|
|
1972
|
-
|
|
1973
|
-
try {
|
|
1974
|
-
vshot = require("react-native-view-shot");
|
|
1975
|
-
} catch {
|
|
1976
|
-
}
|
|
2640
|
+
const vshot = resolveViewShot(config.viewShot);
|
|
1977
2641
|
if (!vshot) {
|
|
1978
2642
|
setSheetScreenshot(null);
|
|
1979
2643
|
setSheetVisible(true);
|
|
@@ -1986,16 +2650,16 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
1986
2650
|
}).finally(() => {
|
|
1987
2651
|
setSheetVisible(true);
|
|
1988
2652
|
});
|
|
1989
|
-
}, [config.capture?.screenshot]);
|
|
1990
|
-
const openAssistant = (0,
|
|
2653
|
+
}, [config.capture?.screenshot, config.viewShot]);
|
|
2654
|
+
const openAssistant = (0, import_react5.useCallback)(() => {
|
|
1991
2655
|
if (!config.assistant?.enabled) return;
|
|
1992
2656
|
setSheetPreferredTab("assistant");
|
|
1993
2657
|
setSheetScreenshot(null);
|
|
1994
2658
|
setSheetVisible(true);
|
|
1995
2659
|
}, [config.assistant?.enabled]);
|
|
1996
|
-
const close = (0,
|
|
1997
|
-
const attachTo = (0,
|
|
1998
|
-
(0,
|
|
2660
|
+
const close = (0, import_react5.useCallback)(() => setSheetVisible(false), []);
|
|
2661
|
+
const attachTo = (0, import_react5.useCallback)(() => ({ onPress: open }), [open]);
|
|
2662
|
+
(0, import_react5.useEffect)(() => {
|
|
1999
2663
|
const trigger2 = config.widget?.trigger ?? "button";
|
|
2000
2664
|
if (trigger2 !== "shake" && trigger2 !== "both") return;
|
|
2001
2665
|
let disposed = false;
|
|
@@ -2003,8 +2667,8 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2003
2667
|
let lastShake = 0;
|
|
2004
2668
|
const threshold = config.widget?.shakeThreshold ?? 2.7;
|
|
2005
2669
|
try {
|
|
2006
|
-
const mod =
|
|
2007
|
-
if (!disposed) {
|
|
2670
|
+
const mod = resolveExpoSensors(config.expoSensors);
|
|
2671
|
+
if (mod && !disposed) {
|
|
2008
2672
|
mod.Accelerometer.setUpdateInterval(120);
|
|
2009
2673
|
const sub = mod.Accelerometer.addListener((evt) => {
|
|
2010
2674
|
const g = Math.sqrt(evt.x * evt.x + evt.y * evt.y + evt.z * evt.z);
|
|
@@ -2022,8 +2686,8 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2022
2686
|
disposed = true;
|
|
2023
2687
|
cleanup?.();
|
|
2024
2688
|
};
|
|
2025
|
-
}, [config.widget?.trigger, config.widget?.shakeThreshold, open]);
|
|
2026
|
-
const submitReport = (0,
|
|
2689
|
+
}, [config.widget?.trigger, config.widget?.shakeThreshold, config.expoSensors, open]);
|
|
2690
|
+
const submitReport = (0, import_react5.useCallback)(
|
|
2027
2691
|
async (data) => {
|
|
2028
2692
|
await reporterTokenReadyRef.current;
|
|
2029
2693
|
const deviceInfo = getDeviceInfo();
|
|
@@ -2051,7 +2715,7 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2051
2715
|
const merged = Object.keys(metadataRef.current).length > 0 ? { ...contextMeta, ...metadataRef.current } : Object.keys(contextMeta).length > 0 ? contextMeta : void 0;
|
|
2052
2716
|
const breadcrumbs = breadcrumbsRef.current.getAll().map((c) => ({
|
|
2053
2717
|
...c,
|
|
2054
|
-
message: (0,
|
|
2718
|
+
message: (0, import_core10.scrubPii)(c.message)
|
|
2055
2719
|
}));
|
|
2056
2720
|
const timeline = breadcrumbs.map((c) => ({
|
|
2057
2721
|
ts: c.timestamp,
|
|
@@ -2060,10 +2724,11 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2060
2724
|
}));
|
|
2061
2725
|
const fingerprintHash = getDeviceFingerprintHash(deviceInfo);
|
|
2062
2726
|
const report = {
|
|
2063
|
-
id: (0,
|
|
2727
|
+
id: (0, import_core10.newUuid)(),
|
|
2064
2728
|
projectId: config.projectId,
|
|
2065
2729
|
category: data.category,
|
|
2066
|
-
|
|
2730
|
+
...data.userCategory ? { userCategory: data.userCategory } : {},
|
|
2731
|
+
description: (0, import_core10.scrubPii)(data.description),
|
|
2067
2732
|
environment: {
|
|
2068
2733
|
userAgent: deviceInfo.systemName ?? "ReactNative",
|
|
2069
2734
|
platform: deviceInfo.platform ?? "mobile",
|
|
@@ -2076,7 +2741,7 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2076
2741
|
},
|
|
2077
2742
|
consoleLogs: (consoleRef.current?.getEntries() ?? []).map((entry) => ({
|
|
2078
2743
|
...entry,
|
|
2079
|
-
message: (0,
|
|
2744
|
+
message: (0, import_core10.scrubPii)(entry.message)
|
|
2080
2745
|
})),
|
|
2081
2746
|
networkLogs: networkRef.current?.getEntries().map((entry) => ({
|
|
2082
2747
|
...entry,
|
|
@@ -2106,7 +2771,7 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2106
2771
|
const code = result.error?.code ?? "";
|
|
2107
2772
|
const isCredentialError = code === "HTTP_401" || code === "HTTP_403" || code.includes("UNAUTHORIZED") || code.includes("FORBIDDEN") || code.includes("INVALID_API_KEY");
|
|
2108
2773
|
if (isCredentialError) {
|
|
2109
|
-
const where = apiEndpoint.replace(/\/$/, "") ===
|
|
2774
|
+
const where = apiEndpoint.replace(/\/$/, "") === import_core10.DEFAULT_API_ENDPOINT ? "Get the correct values at: https://kensaur.us/mushi-mushi/admin/projects" : "Get the correct values from your Mushi console's Projects page.";
|
|
2110
2775
|
console.error(
|
|
2111
2776
|
'[Mushi] Credentials rejected. Check your projectId and apiKey (must have "report:write" scope). ' + where
|
|
2112
2777
|
);
|
|
@@ -2122,17 +2787,18 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2122
2787
|
};
|
|
2123
2788
|
} else {
|
|
2124
2789
|
config.callbacks?.onReportSubmitted?.(report.id);
|
|
2790
|
+
void writeReporterFlag(config.projectId, "has_reports", "1");
|
|
2125
2791
|
return { ok: true };
|
|
2126
2792
|
}
|
|
2127
2793
|
},
|
|
2128
2794
|
[config.projectId, config.callbacks, apiEndpoint]
|
|
2129
2795
|
);
|
|
2130
|
-
const userRef = (0,
|
|
2131
|
-
const metadataRef = (0,
|
|
2132
|
-
const screenRef = (0,
|
|
2133
|
-
const activityQueueRef = (0,
|
|
2134
|
-
const rewardsFlushRef = (0,
|
|
2135
|
-
(0,
|
|
2796
|
+
const userRef = (0, import_react5.useRef)(null);
|
|
2797
|
+
const metadataRef = (0, import_react5.useRef)({});
|
|
2798
|
+
const screenRef = (0, import_react5.useRef)(null);
|
|
2799
|
+
const activityQueueRef = (0, import_react5.useRef)([]);
|
|
2800
|
+
const rewardsFlushRef = (0, import_react5.useRef)(null);
|
|
2801
|
+
(0, import_react5.useEffect)(() => {
|
|
2136
2802
|
const rewards = config.rewards;
|
|
2137
2803
|
if (!rewards?.enabled || !rewards.trackActivity) return;
|
|
2138
2804
|
const flushMs = Math.max(3e4, rewards.flushIntervalMs ?? 3e5);
|
|
@@ -2152,9 +2818,9 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2152
2818
|
if (rewardsFlushRef.current) clearInterval(rewardsFlushRef.current);
|
|
2153
2819
|
};
|
|
2154
2820
|
}, [config.rewards?.enabled, config.rewards?.trackActivity, config.rewards?.flushIntervalMs]);
|
|
2155
|
-
const eventTrackerRef = (0,
|
|
2156
|
-
const sessionTrackerRef = (0,
|
|
2157
|
-
(0,
|
|
2821
|
+
const eventTrackerRef = (0, import_react5.useRef)(null);
|
|
2822
|
+
const sessionTrackerRef = (0, import_react5.useRef)(null);
|
|
2823
|
+
(0, import_react5.useEffect)(() => {
|
|
2158
2824
|
const getRoute = () => screenRef.current?.route ?? screenRef.current?.name ?? null;
|
|
2159
2825
|
const tracker = createRNEventTracker({
|
|
2160
2826
|
projectId: config.projectId,
|
|
@@ -2167,7 +2833,7 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2167
2833
|
getRoute,
|
|
2168
2834
|
sdkVersion: MUSHI_SDK_VERSION,
|
|
2169
2835
|
config: config.analytics,
|
|
2170
|
-
scrub:
|
|
2836
|
+
scrub: import_core10.scrubPii
|
|
2171
2837
|
});
|
|
2172
2838
|
eventTrackerRef.current = tracker;
|
|
2173
2839
|
const sessions = config.trackSessions === false ? null : createRNSessionTracker({
|
|
@@ -2185,8 +2851,8 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2185
2851
|
sessionTrackerRef.current = sessions;
|
|
2186
2852
|
let sub;
|
|
2187
2853
|
try {
|
|
2188
|
-
if (typeof
|
|
2189
|
-
sub =
|
|
2854
|
+
if (typeof import_react_native6.AppState?.addEventListener === "function") {
|
|
2855
|
+
sub = import_react_native6.AppState.addEventListener("change", (state) => {
|
|
2190
2856
|
sessions?.onAppStateChange(state);
|
|
2191
2857
|
if (state !== "active") tracker.flush().catch(() => {
|
|
2192
2858
|
});
|
|
@@ -2209,7 +2875,71 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2209
2875
|
config.analytics?.flushIntervalMs,
|
|
2210
2876
|
config.trackSessions
|
|
2211
2877
|
]);
|
|
2212
|
-
const
|
|
2878
|
+
const reporterUpdatesRef = (0, import_react5.useRef)(
|
|
2879
|
+
createReporterUpdateHub(async () => {
|
|
2880
|
+
const client = apiClientRef.current;
|
|
2881
|
+
if (!client) return null;
|
|
2882
|
+
await reporterTokenReadyRef.current;
|
|
2883
|
+
const res = await (0, import_reporter_channels2.reporterChannels)(client).getUpdates(reporterTokenRef.current);
|
|
2884
|
+
return res.ok ? res.data ?? null : null;
|
|
2885
|
+
})
|
|
2886
|
+
);
|
|
2887
|
+
(0, import_react5.useEffect)(() => {
|
|
2888
|
+
let sub;
|
|
2889
|
+
try {
|
|
2890
|
+
if (typeof import_react_native6.AppState?.addEventListener === "function") {
|
|
2891
|
+
sub = import_react_native6.AppState.addEventListener("change", (state) => {
|
|
2892
|
+
if (state === "active" && reporterUpdatesRef.current.listenerCount() > 0) void reporterUpdatesRef.current.emit();
|
|
2893
|
+
});
|
|
2894
|
+
}
|
|
2895
|
+
} catch {
|
|
2896
|
+
}
|
|
2897
|
+
return () => sub?.remove();
|
|
2898
|
+
}, []);
|
|
2899
|
+
const [toast, setToast] = (0, import_react5.useState)(null);
|
|
2900
|
+
const toastShownRef = (0, import_react5.useRef)(false);
|
|
2901
|
+
const toastEnabled = config.notifications?.toast !== false;
|
|
2902
|
+
const maybeShowToast = (0, import_react5.useCallback)(async () => {
|
|
2903
|
+
if (!toastEnabled || toastShownRef.current) return;
|
|
2904
|
+
if (await readReporterFlag(config.projectId, "has_reports") !== "1") return;
|
|
2905
|
+
const updates = await reporterUpdatesRef.current.emit();
|
|
2906
|
+
if (!updates) return;
|
|
2907
|
+
const now = Date.now();
|
|
2908
|
+
const show = (0, import_reporter_ui3.reporterShouldShowToast)({
|
|
2909
|
+
enabled: toastEnabled,
|
|
2910
|
+
unreadTotal: updates.unread_total,
|
|
2911
|
+
shownThisSession: toastShownRef.current,
|
|
2912
|
+
lastShownAt: Number(await readReporterFlag(config.projectId, "toast_shown_at")) || null,
|
|
2913
|
+
now
|
|
2914
|
+
});
|
|
2915
|
+
if (!show) return;
|
|
2916
|
+
toastShownRef.current = true;
|
|
2917
|
+
void writeReporterFlag(config.projectId, "toast_shown_at", String(now));
|
|
2918
|
+
const locale = (0, import_reporter_ui3.resolveReporterLocale)(
|
|
2919
|
+
typeof navigator !== "undefined" ? navigator.language : void 0
|
|
2920
|
+
);
|
|
2921
|
+
setToast({ message: (0, import_reporter_ui3.reporterToastMessage)(updates, locale), reportId: updates.latest[0]?.report_id ?? null });
|
|
2922
|
+
}, [toastEnabled, config.projectId]);
|
|
2923
|
+
(0, import_react5.useEffect)(() => {
|
|
2924
|
+
void reporterTokenReadyRef.current.then(() => maybeShowToast());
|
|
2925
|
+
let sub;
|
|
2926
|
+
try {
|
|
2927
|
+
if (typeof import_react_native6.AppState?.addEventListener === "function") {
|
|
2928
|
+
sub = import_react_native6.AppState.addEventListener("change", (state) => {
|
|
2929
|
+
if (state === "active") void maybeShowToast();
|
|
2930
|
+
});
|
|
2931
|
+
}
|
|
2932
|
+
} catch {
|
|
2933
|
+
}
|
|
2934
|
+
return () => sub?.remove();
|
|
2935
|
+
}, [maybeShowToast]);
|
|
2936
|
+
const openMyReports = (0, import_react5.useCallback)(() => {
|
|
2937
|
+
setToast(null);
|
|
2938
|
+
setSheetScreenshot(null);
|
|
2939
|
+
setSheetPreferredTab("inbox");
|
|
2940
|
+
setSheetVisible(true);
|
|
2941
|
+
}, []);
|
|
2942
|
+
const instance = (0, import_react5.useMemo)(
|
|
2213
2943
|
() => ({
|
|
2214
2944
|
open,
|
|
2215
2945
|
close,
|
|
@@ -2240,7 +2970,7 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2240
2970
|
identifyWithToken(token) {
|
|
2241
2971
|
userTokenRef.current = token && typeof token === "string" ? token : null;
|
|
2242
2972
|
if (userTokenRef.current) {
|
|
2243
|
-
const claims = (0,
|
|
2973
|
+
const claims = (0, import_core10.parseIdentityToken)(userTokenRef.current);
|
|
2244
2974
|
if (claims?.sub) {
|
|
2245
2975
|
userRef.current = {
|
|
2246
2976
|
id: claims.sub,
|
|
@@ -2295,6 +3025,54 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2295
3025
|
const res = await client.listReporterComments(reportId, reporterTokenRef.current);
|
|
2296
3026
|
return res.ok ? res.data.comments ?? [] : [];
|
|
2297
3027
|
},
|
|
3028
|
+
async loadMyThread(reportId) {
|
|
3029
|
+
const client = apiClientRef.current;
|
|
3030
|
+
if (!client) return null;
|
|
3031
|
+
try {
|
|
3032
|
+
await reporterTokenReadyRef.current;
|
|
3033
|
+
const res = await client.listReporterComments(reportId, reporterTokenRef.current);
|
|
3034
|
+
return res.ok ? res.data?.comments ?? [] : null;
|
|
3035
|
+
} catch {
|
|
3036
|
+
return null;
|
|
3037
|
+
}
|
|
3038
|
+
},
|
|
3039
|
+
async markReportRead(reportId) {
|
|
3040
|
+
const client = apiClientRef.current;
|
|
3041
|
+
if (!client) return 0;
|
|
3042
|
+
await reporterTokenReadyRef.current;
|
|
3043
|
+
return markReporterReportRead(client, reporterTokenRef.current, reportId);
|
|
3044
|
+
},
|
|
3045
|
+
getReporterUpdates: () => reporterUpdatesRef.current.emit(),
|
|
3046
|
+
async loadMyReportDetail(reportId) {
|
|
3047
|
+
const client = apiClientRef.current;
|
|
3048
|
+
if (!client) return null;
|
|
3049
|
+
try {
|
|
3050
|
+
await reporterTokenReadyRef.current;
|
|
3051
|
+
const res = await (0, import_reporter_channels2.reporterChannels)(client).getReport(reportId, reporterTokenRef.current);
|
|
3052
|
+
if (res.ok) return res.data ?? null;
|
|
3053
|
+
return res.error?.status === 404 && res.error.code !== "NOT_FOUND" ? "unsupported" : null;
|
|
3054
|
+
} catch {
|
|
3055
|
+
return null;
|
|
3056
|
+
}
|
|
3057
|
+
},
|
|
3058
|
+
emailPrefill() {
|
|
3059
|
+
return config.notifications?.emailFromIdentity ? userRef.current?.email ?? null : null;
|
|
3060
|
+
},
|
|
3061
|
+
openMyReports,
|
|
3062
|
+
onReporterUpdate: (cb) => reporterUpdatesRef.current.subscribe(cb),
|
|
3063
|
+
async getNotificationPrefs() {
|
|
3064
|
+
const client = apiClientRef.current;
|
|
3065
|
+
if (!client) return null;
|
|
3066
|
+
await reporterTokenReadyRef.current;
|
|
3067
|
+
const res = await (0, import_reporter_channels2.reporterChannels)(client).getPrefs(reporterTokenRef.current);
|
|
3068
|
+
return res.ok ? res.data ?? null : null;
|
|
3069
|
+
},
|
|
3070
|
+
async setNotificationPrefs(update) {
|
|
3071
|
+
const client = apiClientRef.current;
|
|
3072
|
+
if (!client) return { ok: false, error: { code: "NOT_INITIALIZED", message: "Mushi is not ready yet" } };
|
|
3073
|
+
await reporterTokenReadyRef.current;
|
|
3074
|
+
return (0, import_reporter_channels2.reporterChannels)(client).setPrefs(reporterTokenRef.current, update);
|
|
3075
|
+
},
|
|
2298
3076
|
async replyToReport(reportId, body) {
|
|
2299
3077
|
const client = apiClientRef.current;
|
|
2300
3078
|
if (!client) return null;
|
|
@@ -2365,14 +3143,14 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2365
3143
|
return res.ok ? res.data : null;
|
|
2366
3144
|
}
|
|
2367
3145
|
}),
|
|
2368
|
-
[open, close, attachTo, submitReport, openAssistant, config.rewards?.enabled, config.assistant?.enabled]
|
|
3146
|
+
[open, close, attachTo, submitReport, openAssistant, openMyReports, config.rewards?.enabled, config.assistant?.enabled, config.notifications?.emailFromIdentity]
|
|
2369
3147
|
);
|
|
2370
3148
|
const trigger = config.widget?.trigger ?? "button";
|
|
2371
3149
|
const showBanner = trigger === "banner" && !bannerDismissed;
|
|
2372
3150
|
const showFab = !showBanner && (trigger === "button" || trigger === "both" || trigger === "auto" || trigger === "edge-tab");
|
|
2373
|
-
return /* @__PURE__ */ (0,
|
|
3151
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(MushiContext.Provider, { value: instance, children: [
|
|
2374
3152
|
children,
|
|
2375
|
-
showBanner ? /* @__PURE__ */ (0,
|
|
3153
|
+
showBanner ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2376
3154
|
MushiBanner,
|
|
2377
3155
|
{
|
|
2378
3156
|
onPress: open,
|
|
@@ -2381,7 +3159,7 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2381
3159
|
label: config.widget?.banner?.label
|
|
2382
3160
|
}
|
|
2383
3161
|
) : null,
|
|
2384
|
-
showFab && /* @__PURE__ */ (0,
|
|
3162
|
+
showFab && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2385
3163
|
MushiFloatingButton,
|
|
2386
3164
|
{
|
|
2387
3165
|
onPress: open,
|
|
@@ -2389,7 +3167,7 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2389
3167
|
inset: config.widget?.inset
|
|
2390
3168
|
}
|
|
2391
3169
|
),
|
|
2392
|
-
/* @__PURE__ */ (0,
|
|
3170
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2393
3171
|
MushiBottomSheet,
|
|
2394
3172
|
{
|
|
2395
3173
|
visible: sheetVisible,
|
|
@@ -2402,13 +3180,25 @@ function MushiProvider({ children, config: configProp, ...barePropConfig }) {
|
|
|
2402
3180
|
assistantLabel: config.assistant?.label,
|
|
2403
3181
|
assistantGreeting: config.assistant?.greeting,
|
|
2404
3182
|
assistantSuggestions: config.assistant?.suggestions,
|
|
2405
|
-
inboxPollIntervalMs: config.widget?.inboxPollIntervalMs ?? 0
|
|
3183
|
+
inboxPollIntervalMs: config.widget?.inboxPollIntervalMs ?? 0,
|
|
3184
|
+
theme: config.widget?.theme,
|
|
3185
|
+
minDescriptionLength: config.widget?.minDescriptionLength,
|
|
3186
|
+
emailOptIn: config.notifications?.email !== false
|
|
3187
|
+
}
|
|
3188
|
+
),
|
|
3189
|
+
toast ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
3190
|
+
MushiUpdateToast,
|
|
3191
|
+
{
|
|
3192
|
+
message: toast.message,
|
|
3193
|
+
onView: openMyReports,
|
|
3194
|
+
onDismiss: () => setToast(null),
|
|
3195
|
+
theme: config.widget?.theme
|
|
2406
3196
|
}
|
|
2407
|
-
)
|
|
3197
|
+
) : null
|
|
2408
3198
|
] });
|
|
2409
3199
|
}
|
|
2410
3200
|
function useMushiContext() {
|
|
2411
|
-
return (0,
|
|
3201
|
+
return (0, import_react5.useContext)(MushiContext);
|
|
2412
3202
|
}
|
|
2413
3203
|
|
|
2414
3204
|
// src/hooks/useMushi.ts
|
|
@@ -2417,10 +3207,10 @@ function useMushi() {
|
|
|
2417
3207
|
}
|
|
2418
3208
|
|
|
2419
3209
|
// src/hooks/useMushiReport.ts
|
|
2420
|
-
var
|
|
3210
|
+
var import_react6 = require("react");
|
|
2421
3211
|
function useMushiReport() {
|
|
2422
3212
|
const mushi = useMushiContext();
|
|
2423
|
-
const submitReport = (0,
|
|
3213
|
+
const submitReport = (0, import_react6.useCallback)(
|
|
2424
3214
|
async (data) => {
|
|
2425
3215
|
if (!mushi) throw new Error("MushiProvider not found");
|
|
2426
3216
|
await mushi.submitReport(data);
|
|
@@ -2431,22 +3221,22 @@ function useMushiReport() {
|
|
|
2431
3221
|
}
|
|
2432
3222
|
|
|
2433
3223
|
// src/hooks/useMushiWidget.ts
|
|
2434
|
-
var
|
|
3224
|
+
var import_react7 = require("react");
|
|
2435
3225
|
function useMushiWidget() {
|
|
2436
3226
|
const mushi = useMushiContext();
|
|
2437
3227
|
return {
|
|
2438
|
-
open: (0,
|
|
2439
|
-
close: (0,
|
|
3228
|
+
open: (0, import_react7.useCallback)(() => mushi?.open(), [mushi]),
|
|
3229
|
+
close: (0, import_react7.useCallback)(() => mushi?.close(), [mushi])
|
|
2440
3230
|
};
|
|
2441
3231
|
}
|
|
2442
3232
|
|
|
2443
3233
|
// src/components/MushiTrigger.tsx
|
|
2444
|
-
var
|
|
3234
|
+
var import_react8 = require("react");
|
|
2445
3235
|
function MushiTrigger({ children }) {
|
|
2446
3236
|
const mushi = useMushiContext();
|
|
2447
|
-
if (!(0,
|
|
3237
|
+
if (!(0, import_react8.isValidElement)(children)) return children;
|
|
2448
3238
|
const originalOnPress = children.props.onPress;
|
|
2449
|
-
return (0,
|
|
3239
|
+
return (0, import_react8.cloneElement)(children, {
|
|
2450
3240
|
onPress: () => {
|
|
2451
3241
|
originalOnPress?.();
|
|
2452
3242
|
mushi?.open();
|
|
@@ -2455,10 +3245,10 @@ function MushiTrigger({ children }) {
|
|
|
2455
3245
|
}
|
|
2456
3246
|
|
|
2457
3247
|
// src/capture/navigation-capture.ts
|
|
2458
|
-
var
|
|
3248
|
+
var import_react9 = require("react");
|
|
2459
3249
|
function useNavigationCapture(navigationRef) {
|
|
2460
|
-
const historyRef = (0,
|
|
2461
|
-
(0,
|
|
3250
|
+
const historyRef = (0, import_react9.useRef)([]);
|
|
3251
|
+
(0, import_react9.useEffect)(() => {
|
|
2462
3252
|
if (!navigationRef?.addListener || !navigationRef?.getCurrentRoute) return;
|
|
2463
3253
|
const unsubscribe = navigationRef.addListener("state", () => {
|
|
2464
3254
|
const route = navigationRef.getCurrentRoute?.();
|