@bbearai/react-native 0.8.3 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +438 -36
- package/dist/index.mjs +496 -94
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -12202,6 +12202,9 @@ var BugBearClient = class {
|
|
|
12202
12202
|
this.monitor = null;
|
|
12203
12203
|
this.initialized = false;
|
|
12204
12204
|
this.initError = null;
|
|
12205
|
+
this._presenceSessionId = null;
|
|
12206
|
+
this._presenceInterval = null;
|
|
12207
|
+
this._presencePaused = false;
|
|
12205
12208
|
this.config = config;
|
|
12206
12209
|
if (config.apiKey) {
|
|
12207
12210
|
this.pendingInit = this.resolveFromApiKey(config.apiKey);
|
|
@@ -12899,7 +12902,7 @@ var BugBearClient = class {
|
|
|
12899
12902
|
async updateAssignmentStatus(assignmentId, status, options) {
|
|
12900
12903
|
try {
|
|
12901
12904
|
await this.ensureReady();
|
|
12902
|
-
const { data: currentAssignment, error: fetchError } = await this.supabase.from("test_assignments").select("status, started_at").eq("id", assignmentId).single();
|
|
12905
|
+
const { data: currentAssignment, error: fetchError } = await this.supabase.from("test_assignments").select("status, started_at, tester_id, project_id").eq("id", assignmentId).single();
|
|
12903
12906
|
if (fetchError || !currentAssignment) {
|
|
12904
12907
|
console.error("BugBear: Assignment not found", {
|
|
12905
12908
|
message: fetchError?.message,
|
|
@@ -12920,6 +12923,19 @@ var BugBearClient = class {
|
|
|
12920
12923
|
const completedAt = /* @__PURE__ */ new Date();
|
|
12921
12924
|
durationSeconds = Math.round((completedAt.getTime() - startedAt.getTime()) / 1e3);
|
|
12922
12925
|
updateData.duration_seconds = durationSeconds;
|
|
12926
|
+
if (currentAssignment.tester_id && currentAssignment.project_id) {
|
|
12927
|
+
try {
|
|
12928
|
+
const { data: activeTime } = await this.supabase.rpc("compute_assignment_active_time", {
|
|
12929
|
+
p_tester_id: currentAssignment.tester_id,
|
|
12930
|
+
p_project_id: currentAssignment.project_id,
|
|
12931
|
+
p_started_at: currentAssignment.started_at,
|
|
12932
|
+
p_completed_at: updateData.completed_at
|
|
12933
|
+
});
|
|
12934
|
+
updateData.active_seconds = typeof activeTime === "number" ? activeTime : Math.min(durationSeconds, 1800);
|
|
12935
|
+
} catch {
|
|
12936
|
+
updateData.active_seconds = Math.min(durationSeconds, 1800);
|
|
12937
|
+
}
|
|
12938
|
+
}
|
|
12923
12939
|
}
|
|
12924
12940
|
}
|
|
12925
12941
|
if (options?.notes) {
|
|
@@ -12998,6 +13014,7 @@ var BugBearClient = class {
|
|
|
12998
13014
|
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12999
13015
|
completed_at: null,
|
|
13000
13016
|
duration_seconds: null,
|
|
13017
|
+
active_seconds: null,
|
|
13001
13018
|
skip_reason: null
|
|
13002
13019
|
}).eq("id", assignmentId).eq("status", current.status);
|
|
13003
13020
|
if (error) {
|
|
@@ -13847,6 +13864,7 @@ var BugBearClient = class {
|
|
|
13847
13864
|
lastMessageAt: row.last_message_at,
|
|
13848
13865
|
createdAt: row.created_at,
|
|
13849
13866
|
unreadCount: Number(row.unread_count) || 0,
|
|
13867
|
+
reporterName: row.reporter_name || void 0,
|
|
13850
13868
|
lastMessage: row.last_message_preview ? {
|
|
13851
13869
|
id: "",
|
|
13852
13870
|
threadId: row.thread_id,
|
|
@@ -14286,6 +14304,93 @@ var BugBearClient = class {
|
|
|
14286
14304
|
updatedAt: data.updated_at
|
|
14287
14305
|
};
|
|
14288
14306
|
}
|
|
14307
|
+
// ─── Passive Presence Tracking ──────────────────────────────
|
|
14308
|
+
/** Current presence session ID (null if not tracking). */
|
|
14309
|
+
get presenceSessionId() {
|
|
14310
|
+
return this._presenceSessionId;
|
|
14311
|
+
}
|
|
14312
|
+
/**
|
|
14313
|
+
* Start passive presence tracking for this tester.
|
|
14314
|
+
* Idempotent — reuses an existing active session if one exists.
|
|
14315
|
+
*/
|
|
14316
|
+
async startPresence(platform) {
|
|
14317
|
+
try {
|
|
14318
|
+
await this.ensureReady();
|
|
14319
|
+
const testerInfo = await this.getTesterInfo();
|
|
14320
|
+
if (!testerInfo) return null;
|
|
14321
|
+
const { data, error } = await this.supabase.rpc("upsert_tester_presence", {
|
|
14322
|
+
p_project_id: this.config.projectId,
|
|
14323
|
+
p_tester_id: testerInfo.id,
|
|
14324
|
+
p_platform: platform
|
|
14325
|
+
});
|
|
14326
|
+
if (error) {
|
|
14327
|
+
console.error("BugBear: Failed to start presence", formatPgError(error));
|
|
14328
|
+
return null;
|
|
14329
|
+
}
|
|
14330
|
+
this._presenceSessionId = data;
|
|
14331
|
+
this._presencePaused = false;
|
|
14332
|
+
this.startPresenceHeartbeat();
|
|
14333
|
+
return data;
|
|
14334
|
+
} catch (err) {
|
|
14335
|
+
console.error("BugBear: Error starting presence", err);
|
|
14336
|
+
return null;
|
|
14337
|
+
}
|
|
14338
|
+
}
|
|
14339
|
+
/** Gracefully end the current presence session. */
|
|
14340
|
+
async endPresence() {
|
|
14341
|
+
this.stopPresenceHeartbeat();
|
|
14342
|
+
if (!this._presenceSessionId) return;
|
|
14343
|
+
try {
|
|
14344
|
+
await this.supabase.rpc("end_tester_presence", {
|
|
14345
|
+
p_session_id: this._presenceSessionId
|
|
14346
|
+
});
|
|
14347
|
+
} catch {
|
|
14348
|
+
}
|
|
14349
|
+
this._presenceSessionId = null;
|
|
14350
|
+
}
|
|
14351
|
+
/** Pause heartbeat (tab hidden / app backgrounded). Sends one final beat. */
|
|
14352
|
+
pausePresence() {
|
|
14353
|
+
this._presencePaused = true;
|
|
14354
|
+
this.heartbeatPresence();
|
|
14355
|
+
}
|
|
14356
|
+
/** Resume heartbeat after pause. Restarts if session was cleaned up. */
|
|
14357
|
+
async resumePresence() {
|
|
14358
|
+
if (!this._presenceSessionId) return;
|
|
14359
|
+
this._presencePaused = false;
|
|
14360
|
+
try {
|
|
14361
|
+
const { data } = await this.supabase.rpc("heartbeat_tester_presence", {
|
|
14362
|
+
p_session_id: this._presenceSessionId
|
|
14363
|
+
});
|
|
14364
|
+
if (!data) {
|
|
14365
|
+
this._presenceSessionId = null;
|
|
14366
|
+
}
|
|
14367
|
+
} catch {
|
|
14368
|
+
this._presenceSessionId = null;
|
|
14369
|
+
}
|
|
14370
|
+
}
|
|
14371
|
+
async heartbeatPresence() {
|
|
14372
|
+
if (!this._presenceSessionId || this._presencePaused) return;
|
|
14373
|
+
try {
|
|
14374
|
+
const { data, error } = await this.supabase.rpc("heartbeat_tester_presence", {
|
|
14375
|
+
p_session_id: this._presenceSessionId
|
|
14376
|
+
});
|
|
14377
|
+
if (error || data === false) {
|
|
14378
|
+
this.stopPresenceHeartbeat();
|
|
14379
|
+
this._presenceSessionId = null;
|
|
14380
|
+
}
|
|
14381
|
+
} catch {
|
|
14382
|
+
}
|
|
14383
|
+
}
|
|
14384
|
+
startPresenceHeartbeat() {
|
|
14385
|
+
this.stopPresenceHeartbeat();
|
|
14386
|
+
this._presenceInterval = setInterval(() => this.heartbeatPresence(), 6e4);
|
|
14387
|
+
}
|
|
14388
|
+
stopPresenceHeartbeat() {
|
|
14389
|
+
if (this._presenceInterval) {
|
|
14390
|
+
clearInterval(this._presenceInterval);
|
|
14391
|
+
this._presenceInterval = null;
|
|
14392
|
+
}
|
|
14393
|
+
}
|
|
14289
14394
|
};
|
|
14290
14395
|
function createBugBear(config) {
|
|
14291
14396
|
return new BugBearClient(config);
|
|
@@ -14420,6 +14525,9 @@ function setActiveColors(palette) {
|
|
|
14420
14525
|
colors = palette;
|
|
14421
14526
|
shared = createSharedStyles();
|
|
14422
14527
|
}
|
|
14528
|
+
function withAlpha(hex, alpha) {
|
|
14529
|
+
return hex + Math.round(alpha * 255).toString(16).padStart(2, "0");
|
|
14530
|
+
}
|
|
14423
14531
|
function createSharedStyles() {
|
|
14424
14532
|
return StyleSheet.create({
|
|
14425
14533
|
card: {
|
|
@@ -14922,6 +15030,28 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
14922
15030
|
}
|
|
14923
15031
|
return () => subscription.remove();
|
|
14924
15032
|
}, [client]);
|
|
15033
|
+
useEffect(() => {
|
|
15034
|
+
if (!client || !isTester) return;
|
|
15035
|
+
let mounted = true;
|
|
15036
|
+
const platform = Platform2.OS === "ios" ? "ios" : "android";
|
|
15037
|
+
client.startPresence(platform);
|
|
15038
|
+
const subscription = AppState.addEventListener("change", (nextState) => {
|
|
15039
|
+
if (nextState === "active") {
|
|
15040
|
+
client.resumePresence().then(() => {
|
|
15041
|
+
if (mounted && !client.presenceSessionId) {
|
|
15042
|
+
client.startPresence(platform);
|
|
15043
|
+
}
|
|
15044
|
+
});
|
|
15045
|
+
} else if (nextState === "background" || nextState === "inactive") {
|
|
15046
|
+
client.pausePresence();
|
|
15047
|
+
}
|
|
15048
|
+
});
|
|
15049
|
+
return () => {
|
|
15050
|
+
mounted = false;
|
|
15051
|
+
subscription.remove();
|
|
15052
|
+
client.endPresence();
|
|
15053
|
+
};
|
|
15054
|
+
}, [client, isTester]);
|
|
14925
15055
|
useEffect(() => {
|
|
14926
15056
|
if (!client || !isTester) return;
|
|
14927
15057
|
if (widgetMode === "qa" && !isQAEnabled) return;
|
|
@@ -15017,14 +15147,13 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
15017
15147
|
}
|
|
15018
15148
|
|
|
15019
15149
|
// src/BugBearButton.tsx
|
|
15020
|
-
import React21, { useState as
|
|
15150
|
+
import React21, { useState as useState18, useRef as useRef6, useEffect as useEffect12, useMemo as useMemo16 } from "react";
|
|
15021
15151
|
import {
|
|
15022
15152
|
View as View21,
|
|
15023
15153
|
Text as Text19,
|
|
15024
15154
|
Image as Image4,
|
|
15025
15155
|
TouchableOpacity as TouchableOpacity18,
|
|
15026
|
-
|
|
15027
|
-
ScrollView as ScrollView3,
|
|
15156
|
+
ScrollView as ScrollView4,
|
|
15028
15157
|
StyleSheet as StyleSheet21,
|
|
15029
15158
|
Dimensions as Dimensions2,
|
|
15030
15159
|
KeyboardAvoidingView,
|
|
@@ -15032,7 +15161,8 @@ import {
|
|
|
15032
15161
|
PanResponder,
|
|
15033
15162
|
Animated as Animated2,
|
|
15034
15163
|
ActivityIndicator as ActivityIndicator3,
|
|
15035
|
-
Keyboard as Keyboard4
|
|
15164
|
+
Keyboard as Keyboard4,
|
|
15165
|
+
BackHandler
|
|
15036
15166
|
} from "react-native";
|
|
15037
15167
|
|
|
15038
15168
|
// src/widget/logo.ts
|
|
@@ -16934,6 +17064,8 @@ function ReportScreen({ nav, prefill, autoCaptureUri, onAutoCaptureConsumed }) {
|
|
|
16934
17064
|
const [severity, setSeverity] = useState8("medium");
|
|
16935
17065
|
const [category, setCategory] = useState8(null);
|
|
16936
17066
|
const [description, setDescription] = useState8("");
|
|
17067
|
+
const [myIssues, setMyIssues] = useState8([]);
|
|
17068
|
+
const [similarReports, setSimilarReports] = useState8([]);
|
|
16937
17069
|
const [affectedScreen, setAffectedScreen] = useState8("");
|
|
16938
17070
|
const [submitting, setSubmitting] = useState8(false);
|
|
16939
17071
|
const [error, setError] = useState8(null);
|
|
@@ -16949,6 +17081,41 @@ function ReportScreen({ nav, prefill, autoCaptureUri, onAutoCaptureConsumed }) {
|
|
|
16949
17081
|
}, [autoCaptureUri, onAutoCaptureConsumed]);
|
|
16950
17082
|
const isRetestFailure = prefill?.type === "test_fail";
|
|
16951
17083
|
const isBugType = reportType === "bug" || reportType === "test_fail";
|
|
17084
|
+
useEffect6(() => {
|
|
17085
|
+
if (!client?.getIssues) return;
|
|
17086
|
+
let cancelled = false;
|
|
17087
|
+
const load = async () => {
|
|
17088
|
+
try {
|
|
17089
|
+
const [open, done] = await Promise.all([
|
|
17090
|
+
client.getIssues("open"),
|
|
17091
|
+
client.getIssues("done")
|
|
17092
|
+
]);
|
|
17093
|
+
if (!cancelled) setMyIssues([...open, ...done]);
|
|
17094
|
+
} catch {
|
|
17095
|
+
}
|
|
17096
|
+
};
|
|
17097
|
+
load();
|
|
17098
|
+
return () => {
|
|
17099
|
+
cancelled = true;
|
|
17100
|
+
};
|
|
17101
|
+
}, [client]);
|
|
17102
|
+
useEffect6(() => {
|
|
17103
|
+
if (description.length < 10 || myIssues.length === 0) {
|
|
17104
|
+
setSimilarReports([]);
|
|
17105
|
+
return;
|
|
17106
|
+
}
|
|
17107
|
+
const words = description.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
|
|
17108
|
+
if (words.length === 0) {
|
|
17109
|
+
setSimilarReports([]);
|
|
17110
|
+
return;
|
|
17111
|
+
}
|
|
17112
|
+
const scored = myIssues.map((issue) => {
|
|
17113
|
+
const text = `${issue.title || ""} ${issue.description || ""}`.toLowerCase();
|
|
17114
|
+
const matches = words.filter((w) => text.includes(w)).length;
|
|
17115
|
+
return { issue, score: matches / words.length };
|
|
17116
|
+
}).filter((s2) => s2.score >= 0.3).sort((a, b) => b.score - a.score).slice(0, 3);
|
|
17117
|
+
setSimilarReports(scored.map((s2) => s2.issue));
|
|
17118
|
+
}, [description, myIssues]);
|
|
16952
17119
|
useEffect6(() => {
|
|
16953
17120
|
if (reportType === "feedback" || reportType === "suggestion") {
|
|
16954
17121
|
setCategory("other");
|
|
@@ -17023,7 +17190,41 @@ function ReportScreen({ nav, prefill, autoCaptureUri, onAutoCaptureConsumed }) {
|
|
|
17023
17190
|
numberOfLines: 4,
|
|
17024
17191
|
textAlignVertical: "top"
|
|
17025
17192
|
}
|
|
17026
|
-
)
|
|
17193
|
+
), similarReports.length > 0 && /* @__PURE__ */ React10.createElement(View10, { style: {
|
|
17194
|
+
marginTop: 8,
|
|
17195
|
+
padding: 12,
|
|
17196
|
+
borderRadius: 8,
|
|
17197
|
+
backgroundColor: withAlpha(colors.orange, 0.1),
|
|
17198
|
+
borderWidth: 1,
|
|
17199
|
+
borderColor: withAlpha(colors.orange, 0.3)
|
|
17200
|
+
} }, /* @__PURE__ */ React10.createElement(Text8, { style: { fontSize: 12, fontWeight: "600", color: colors.orange, marginBottom: 8 } }, "Similar reports you've already filed:"), similarReports.map((issue) => /* @__PURE__ */ React10.createElement(
|
|
17201
|
+
TouchableOpacity8,
|
|
17202
|
+
{
|
|
17203
|
+
key: issue.id,
|
|
17204
|
+
onPress: () => nav.push({ name: "ISSUE_DETAIL", issue }),
|
|
17205
|
+
style: {
|
|
17206
|
+
flexDirection: "row",
|
|
17207
|
+
alignItems: "center",
|
|
17208
|
+
gap: 8,
|
|
17209
|
+
paddingVertical: 6
|
|
17210
|
+
}
|
|
17211
|
+
},
|
|
17212
|
+
/* @__PURE__ */ React10.createElement(View10, { style: {
|
|
17213
|
+
width: 8,
|
|
17214
|
+
height: 8,
|
|
17215
|
+
borderRadius: 4,
|
|
17216
|
+
backgroundColor: issue.severity === "critical" ? colors.red : issue.severity === "high" ? colors.orange : colors.yellow
|
|
17217
|
+
} }),
|
|
17218
|
+
/* @__PURE__ */ React10.createElement(
|
|
17219
|
+
Text8,
|
|
17220
|
+
{
|
|
17221
|
+
numberOfLines: 1,
|
|
17222
|
+
style: { fontSize: 12, color: colors.textPrimary, flex: 1 }
|
|
17223
|
+
},
|
|
17224
|
+
issue.title
|
|
17225
|
+
),
|
|
17226
|
+
/* @__PURE__ */ React10.createElement(Text8, { style: { fontSize: 11, color: colors.textMuted } }, issue.status)
|
|
17227
|
+
)))), /* @__PURE__ */ React10.createElement(
|
|
17027
17228
|
ImagePickerButtons,
|
|
17028
17229
|
{
|
|
17029
17230
|
images: images.images,
|
|
@@ -17079,7 +17280,41 @@ function ReportScreen({ nav, prefill, autoCaptureUri, onAutoCaptureConsumed }) {
|
|
|
17079
17280
|
numberOfLines: 4,
|
|
17080
17281
|
textAlignVertical: "top"
|
|
17081
17282
|
}
|
|
17082
|
-
)
|
|
17283
|
+
), similarReports.length > 0 && /* @__PURE__ */ React10.createElement(View10, { style: {
|
|
17284
|
+
marginTop: 8,
|
|
17285
|
+
padding: 12,
|
|
17286
|
+
borderRadius: 8,
|
|
17287
|
+
backgroundColor: withAlpha(colors.orange, 0.1),
|
|
17288
|
+
borderWidth: 1,
|
|
17289
|
+
borderColor: withAlpha(colors.orange, 0.3)
|
|
17290
|
+
} }, /* @__PURE__ */ React10.createElement(Text8, { style: { fontSize: 12, fontWeight: "600", color: colors.orange, marginBottom: 8 } }, "Similar reports you've already filed:"), similarReports.map((issue) => /* @__PURE__ */ React10.createElement(
|
|
17291
|
+
TouchableOpacity8,
|
|
17292
|
+
{
|
|
17293
|
+
key: issue.id,
|
|
17294
|
+
onPress: () => nav.push({ name: "ISSUE_DETAIL", issue }),
|
|
17295
|
+
style: {
|
|
17296
|
+
flexDirection: "row",
|
|
17297
|
+
alignItems: "center",
|
|
17298
|
+
gap: 8,
|
|
17299
|
+
paddingVertical: 6
|
|
17300
|
+
}
|
|
17301
|
+
},
|
|
17302
|
+
/* @__PURE__ */ React10.createElement(View10, { style: {
|
|
17303
|
+
width: 8,
|
|
17304
|
+
height: 8,
|
|
17305
|
+
borderRadius: 4,
|
|
17306
|
+
backgroundColor: issue.severity === "critical" ? colors.red : issue.severity === "high" ? colors.orange : colors.yellow
|
|
17307
|
+
} }),
|
|
17308
|
+
/* @__PURE__ */ React10.createElement(
|
|
17309
|
+
Text8,
|
|
17310
|
+
{
|
|
17311
|
+
numberOfLines: 1,
|
|
17312
|
+
style: { fontSize: 12, color: colors.textPrimary, flex: 1 }
|
|
17313
|
+
},
|
|
17314
|
+
issue.title
|
|
17315
|
+
),
|
|
17316
|
+
/* @__PURE__ */ React10.createElement(Text8, { style: { fontSize: 11, color: colors.textMuted } }, issue.status)
|
|
17317
|
+
)))), isBugType && /* @__PURE__ */ React10.createElement(View10, { style: styles5.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "Which screen?"), /* @__PURE__ */ React10.createElement(
|
|
17083
17318
|
TextInput4,
|
|
17084
17319
|
{
|
|
17085
17320
|
style: styles5.screenInput,
|
|
@@ -17155,11 +17390,24 @@ function createStyles6() {
|
|
|
17155
17390
|
}
|
|
17156
17391
|
|
|
17157
17392
|
// src/widget/screens/MessageListScreen.tsx
|
|
17158
|
-
import React12, { useMemo as useMemo7 } from "react";
|
|
17159
|
-
import { View as View12, Text as Text10, TouchableOpacity as TouchableOpacity9, StyleSheet as StyleSheet12, Linking as Linking3 } from "react-native";
|
|
17393
|
+
import React12, { useMemo as useMemo7, useState as useState9 } from "react";
|
|
17394
|
+
import { View as View12, Text as Text10, TouchableOpacity as TouchableOpacity9, ScrollView as ScrollView3, StyleSheet as StyleSheet12, Linking as Linking3 } from "react-native";
|
|
17160
17395
|
function MessageListScreen({ nav }) {
|
|
17161
17396
|
const { threads, unreadCount, refreshThreads, dashboardUrl, isLoading, widgetColorScheme } = useBugBear();
|
|
17162
17397
|
const styles5 = useMemo7(() => createStyles7(), [widgetColorScheme]);
|
|
17398
|
+
const [activeFilter, setActiveFilter] = useState9("all");
|
|
17399
|
+
const filteredThreads = threads.filter((thread) => {
|
|
17400
|
+
if (activeFilter === "all") return true;
|
|
17401
|
+
if (activeFilter === "unread") return thread.unreadCount > 0;
|
|
17402
|
+
return thread.threadType === activeFilter;
|
|
17403
|
+
});
|
|
17404
|
+
const filterChips = [
|
|
17405
|
+
{ key: "all", label: "All" },
|
|
17406
|
+
{ key: "report", label: "Bug Reports" },
|
|
17407
|
+
{ key: "direct", label: "Direct" },
|
|
17408
|
+
{ key: "announcement", label: "Announcements" },
|
|
17409
|
+
{ key: "unread", label: "Unread", count: threads.filter((t) => t.unreadCount > 0).length }
|
|
17410
|
+
];
|
|
17163
17411
|
if (isLoading) return /* @__PURE__ */ React12.createElement(MessageListScreenSkeleton, null);
|
|
17164
17412
|
return /* @__PURE__ */ React12.createElement(View12, null, /* @__PURE__ */ React12.createElement(
|
|
17165
17413
|
TouchableOpacity9,
|
|
@@ -17168,14 +17416,54 @@ function MessageListScreen({ nav }) {
|
|
|
17168
17416
|
onPress: () => nav.push({ name: "COMPOSE_MESSAGE" })
|
|
17169
17417
|
},
|
|
17170
17418
|
/* @__PURE__ */ React12.createElement(Text10, { style: styles5.newMsgText }, "\u2709\uFE0F New Message")
|
|
17171
|
-
),
|
|
17419
|
+
), /* @__PURE__ */ React12.createElement(
|
|
17420
|
+
ScrollView3,
|
|
17421
|
+
{
|
|
17422
|
+
horizontal: true,
|
|
17423
|
+
showsHorizontalScrollIndicator: false,
|
|
17424
|
+
style: { paddingBottom: 12 },
|
|
17425
|
+
contentContainerStyle: { paddingHorizontal: 16, gap: 8 }
|
|
17426
|
+
},
|
|
17427
|
+
filterChips.map((chip) => /* @__PURE__ */ React12.createElement(
|
|
17428
|
+
TouchableOpacity9,
|
|
17429
|
+
{
|
|
17430
|
+
key: chip.key,
|
|
17431
|
+
onPress: () => setActiveFilter(chip.key),
|
|
17432
|
+
style: {
|
|
17433
|
+
paddingVertical: 6,
|
|
17434
|
+
paddingHorizontal: 12,
|
|
17435
|
+
borderRadius: 16,
|
|
17436
|
+
borderWidth: 1,
|
|
17437
|
+
borderColor: activeFilter === chip.key ? colors.blue : colors.border,
|
|
17438
|
+
backgroundColor: activeFilter === chip.key ? withAlpha(colors.blue, 0.15) : "transparent",
|
|
17439
|
+
flexDirection: "row",
|
|
17440
|
+
alignItems: "center",
|
|
17441
|
+
gap: 4
|
|
17442
|
+
}
|
|
17443
|
+
},
|
|
17444
|
+
/* @__PURE__ */ React12.createElement(Text10, { style: {
|
|
17445
|
+
fontSize: 12,
|
|
17446
|
+
fontWeight: "500",
|
|
17447
|
+
color: activeFilter === chip.key ? colors.blue : colors.textSecondary
|
|
17448
|
+
} }, chip.label),
|
|
17449
|
+
chip.count !== void 0 && chip.count > 0 && /* @__PURE__ */ React12.createElement(View12, { style: {
|
|
17450
|
+
backgroundColor: colors.blue,
|
|
17451
|
+
borderRadius: 8,
|
|
17452
|
+
minWidth: 16,
|
|
17453
|
+
height: 16,
|
|
17454
|
+
justifyContent: "center",
|
|
17455
|
+
alignItems: "center",
|
|
17456
|
+
paddingHorizontal: 4
|
|
17457
|
+
} }, /* @__PURE__ */ React12.createElement(Text10, { style: { fontSize: 10, fontWeight: "bold", color: colors.onPrimary } }, chip.count))
|
|
17458
|
+
))
|
|
17459
|
+
), filteredThreads.length === 0 ? /* @__PURE__ */ React12.createElement(View12, { style: shared.emptyState }, /* @__PURE__ */ React12.createElement(Text10, { style: shared.emptyEmoji }, "\u{1F4AC}"), /* @__PURE__ */ React12.createElement(Text10, { style: shared.emptyTitle }, "No messages yet"), /* @__PURE__ */ React12.createElement(Text10, { style: shared.emptySubtitle }, "Start a conversation or wait for messages from admins")) : /* @__PURE__ */ React12.createElement(View12, null, filteredThreads.map((thread) => /* @__PURE__ */ React12.createElement(
|
|
17172
17460
|
TouchableOpacity9,
|
|
17173
17461
|
{
|
|
17174
17462
|
key: thread.id,
|
|
17175
17463
|
style: [styles5.threadItem, thread.unreadCount > 0 && styles5.threadItemUnread],
|
|
17176
17464
|
onPress: () => nav.push({ name: "THREAD_DETAIL", thread })
|
|
17177
17465
|
},
|
|
17178
|
-
/* @__PURE__ */ React12.createElement(View12, { style: styles5.threadLeft }, /* @__PURE__ */ React12.createElement(Text10, { style: styles5.threadIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ React12.createElement(View12, { style: styles5.threadInfo }, /* @__PURE__ */ React12.createElement(View12, { style: styles5.threadTitleRow }, thread.isPinned && /* @__PURE__ */ React12.createElement(Text10, { style: styles5.pinIcon }, "\u{1F4CC}"), /* @__PURE__ */ React12.createElement(Text10, { style: styles5.threadSubject, numberOfLines: 1 }, thread.subject || "No subject")), thread.lastMessage && /* @__PURE__ */ React12.createElement(Text10, { style: styles5.threadPreview, numberOfLines: 1 }, thread.lastMessage.senderName, ": ", thread.lastMessage.content))),
|
|
17466
|
+
/* @__PURE__ */ React12.createElement(View12, { style: styles5.threadLeft }, /* @__PURE__ */ React12.createElement(Text10, { style: styles5.threadIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ React12.createElement(View12, { style: styles5.threadInfo }, /* @__PURE__ */ React12.createElement(View12, { style: styles5.threadTitleRow }, thread.isPinned && /* @__PURE__ */ React12.createElement(Text10, { style: styles5.pinIcon }, "\u{1F4CC}"), /* @__PURE__ */ React12.createElement(Text10, { style: styles5.threadSubject, numberOfLines: 1 }, thread.subject || "No subject")), thread.threadType === "report" && thread.reporterName && /* @__PURE__ */ React12.createElement(Text10, { style: { fontSize: 11, color: colors.textMuted } }, "Reported by: ", thread.reporterName), thread.lastMessage && /* @__PURE__ */ React12.createElement(Text10, { style: styles5.threadPreview, numberOfLines: 1 }, thread.lastMessage.senderName, ": ", thread.lastMessage.content))),
|
|
17179
17467
|
/* @__PURE__ */ React12.createElement(View12, { style: styles5.threadRight }, /* @__PURE__ */ React12.createElement(Text10, { style: styles5.threadTime }, formatRelativeTime(thread.lastMessageAt)), thread.unreadCount > 0 && /* @__PURE__ */ React12.createElement(View12, { style: styles5.unreadBadge }, /* @__PURE__ */ React12.createElement(Text10, { style: styles5.unreadText }, thread.unreadCount)), thread.priority !== "normal" && /* @__PURE__ */ React12.createElement(View12, { style: [styles5.priorityDot, { backgroundColor: getPriorityColor(thread.priority) }] }))
|
|
17180
17468
|
))), dashboardUrl && /* @__PURE__ */ React12.createElement(
|
|
17181
17469
|
TouchableOpacity9,
|
|
@@ -17185,7 +17473,7 @@ function MessageListScreen({ nav }) {
|
|
|
17185
17473
|
activeOpacity: 0.7
|
|
17186
17474
|
},
|
|
17187
17475
|
/* @__PURE__ */ React12.createElement(Text10, { style: styles5.dashboardLinkText }, "\u{1F310}", " View on Dashboard ", "\u2192")
|
|
17188
|
-
), /* @__PURE__ */ React12.createElement(View12, { style: styles5.footer }, /* @__PURE__ */ React12.createElement(Text10, { style: styles5.footerText },
|
|
17476
|
+
), /* @__PURE__ */ React12.createElement(View12, { style: styles5.footer }, /* @__PURE__ */ React12.createElement(Text10, { style: styles5.footerText }, filteredThreads.length, " thread", filteredThreads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ React12.createElement(TouchableOpacity9, { onPress: refreshThreads }, /* @__PURE__ */ React12.createElement(Text10, { style: styles5.refreshText }, "\u21BB Refresh"))));
|
|
17189
17477
|
}
|
|
17190
17478
|
function createStyles7() {
|
|
17191
17479
|
return StyleSheet12.create({
|
|
@@ -17214,16 +17502,16 @@ function createStyles7() {
|
|
|
17214
17502
|
}
|
|
17215
17503
|
|
|
17216
17504
|
// src/widget/screens/ThreadDetailScreen.tsx
|
|
17217
|
-
import React13, { useState as
|
|
17505
|
+
import React13, { useState as useState10, useEffect as useEffect8, useMemo as useMemo8 } from "react";
|
|
17218
17506
|
import { View as View13, Text as Text11, TouchableOpacity as TouchableOpacity10, TextInput as TextInput5, StyleSheet as StyleSheet13, Image as Image2 } from "react-native";
|
|
17219
17507
|
function ThreadDetailScreen({ thread, nav }) {
|
|
17220
17508
|
const { getThreadMessages, sendMessage, markAsRead, uploadImage, widgetColorScheme } = useBugBear();
|
|
17221
17509
|
const styles5 = useMemo8(() => createStyles8(), [widgetColorScheme]);
|
|
17222
|
-
const [messages, setMessages] =
|
|
17223
|
-
const [loading, setLoading] =
|
|
17224
|
-
const [replyText, setReplyText] =
|
|
17225
|
-
const [sending, setSending] =
|
|
17226
|
-
const [sendError, setSendError] =
|
|
17510
|
+
const [messages, setMessages] = useState10([]);
|
|
17511
|
+
const [loading, setLoading] = useState10(true);
|
|
17512
|
+
const [replyText, setReplyText] = useState10("");
|
|
17513
|
+
const [sending, setSending] = useState10(false);
|
|
17514
|
+
const [sendError, setSendError] = useState10(false);
|
|
17227
17515
|
const replyImages = useImageAttachments(uploadImage, 3, "discussion-attachments");
|
|
17228
17516
|
useEffect8(() => {
|
|
17229
17517
|
let cancelled = false;
|
|
@@ -17335,14 +17623,14 @@ function createStyles8() {
|
|
|
17335
17623
|
}
|
|
17336
17624
|
|
|
17337
17625
|
// src/widget/screens/ComposeMessageScreen.tsx
|
|
17338
|
-
import React14, { useState as
|
|
17626
|
+
import React14, { useState as useState11, useMemo as useMemo9 } from "react";
|
|
17339
17627
|
import { View as View14, Text as Text12, TextInput as TextInput6, TouchableOpacity as TouchableOpacity11, StyleSheet as StyleSheet14 } from "react-native";
|
|
17340
17628
|
function ComposeMessageScreen({ nav }) {
|
|
17341
17629
|
const { createThread, uploadImage, widgetColorScheme } = useBugBear();
|
|
17342
17630
|
const styles5 = useMemo9(() => createStyles9(), [widgetColorScheme]);
|
|
17343
|
-
const [subject, setSubject] =
|
|
17344
|
-
const [message, setMessage] =
|
|
17345
|
-
const [sending, setSending] =
|
|
17631
|
+
const [subject, setSubject] = useState11("");
|
|
17632
|
+
const [message, setMessage] = useState11("");
|
|
17633
|
+
const [sending, setSending] = useState11(false);
|
|
17346
17634
|
const images = useImageAttachments(uploadImage, 3, "discussion-attachments");
|
|
17347
17635
|
const handleSend = async () => {
|
|
17348
17636
|
if (!subject.trim() || !message.trim() || sending || images.isUploading) return;
|
|
@@ -17412,19 +17700,19 @@ function createStyles9() {
|
|
|
17412
17700
|
}
|
|
17413
17701
|
|
|
17414
17702
|
// src/widget/screens/ProfileScreen.tsx
|
|
17415
|
-
import React15, { useState as
|
|
17703
|
+
import React15, { useState as useState12, useEffect as useEffect9, useMemo as useMemo10 } from "react";
|
|
17416
17704
|
import { View as View15, Text as Text13, TouchableOpacity as TouchableOpacity12, TextInput as TextInput7, StyleSheet as StyleSheet15 } from "react-native";
|
|
17417
17705
|
function ProfileScreen({ nav }) {
|
|
17418
17706
|
const { testerInfo, assignments, updateTesterProfile, refreshTesterInfo, widgetColorScheme } = useBugBear();
|
|
17419
17707
|
const styles5 = useMemo10(() => createStyles10(), [widgetColorScheme]);
|
|
17420
|
-
const [editing, setEditing] =
|
|
17421
|
-
const [name, setName] =
|
|
17422
|
-
const [additionalEmails, setAdditionalEmails] =
|
|
17423
|
-
const [newEmailInput, setNewEmailInput] =
|
|
17424
|
-
const [platforms, setPlatforms] =
|
|
17425
|
-
const [saving, setSaving] =
|
|
17426
|
-
const [saved, setSaved] =
|
|
17427
|
-
const [showDetails, setShowDetails] =
|
|
17708
|
+
const [editing, setEditing] = useState12(false);
|
|
17709
|
+
const [name, setName] = useState12(testerInfo?.name || "");
|
|
17710
|
+
const [additionalEmails, setAdditionalEmails] = useState12(testerInfo?.additionalEmails || []);
|
|
17711
|
+
const [newEmailInput, setNewEmailInput] = useState12("");
|
|
17712
|
+
const [platforms, setPlatforms] = useState12(testerInfo?.platforms || []);
|
|
17713
|
+
const [saving, setSaving] = useState12(false);
|
|
17714
|
+
const [saved, setSaved] = useState12(false);
|
|
17715
|
+
const [showDetails, setShowDetails] = useState12(false);
|
|
17428
17716
|
const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
|
|
17429
17717
|
useEffect9(() => {
|
|
17430
17718
|
if (testerInfo) {
|
|
@@ -17543,8 +17831,8 @@ function createStyles10() {
|
|
|
17543
17831
|
}
|
|
17544
17832
|
|
|
17545
17833
|
// src/widget/screens/IssueListScreen.tsx
|
|
17546
|
-
import React16, { useState as
|
|
17547
|
-
import { View as View16, Text as Text14, TouchableOpacity as TouchableOpacity13, StyleSheet as StyleSheet16 } from "react-native";
|
|
17834
|
+
import React16, { useState as useState13, useEffect as useEffect10, useMemo as useMemo11 } from "react";
|
|
17835
|
+
import { View as View16, Text as Text14, TextInput as TextInput8, TouchableOpacity as TouchableOpacity13, StyleSheet as StyleSheet16 } from "react-native";
|
|
17548
17836
|
var CATEGORIES = ["open", "done", "reopened"];
|
|
17549
17837
|
var SEVERITY_ORDER2 = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
17550
17838
|
function IssueListScreen({ nav, category }) {
|
|
@@ -17561,12 +17849,22 @@ function IssueListScreen({ nav, category }) {
|
|
|
17561
17849
|
medium: colors.yellow,
|
|
17562
17850
|
low: colors.textMuted
|
|
17563
17851
|
}), [widgetColorScheme]);
|
|
17564
|
-
const [activeCategory, setActiveCategory] =
|
|
17565
|
-
const [issues, setIssues] =
|
|
17566
|
-
const [loading, setLoading] =
|
|
17567
|
-
const [counts, setCounts] =
|
|
17568
|
-
const [sortMode, setSortMode] =
|
|
17852
|
+
const [activeCategory, setActiveCategory] = useState13(category);
|
|
17853
|
+
const [issues, setIssues] = useState13([]);
|
|
17854
|
+
const [loading, setLoading] = useState13(true);
|
|
17855
|
+
const [counts, setCounts] = useState13(null);
|
|
17856
|
+
const [sortMode, setSortMode] = useState13("severity");
|
|
17857
|
+
const [searchQuery, setSearchQuery] = useState13("");
|
|
17858
|
+
const [debouncedQuery, setDebouncedQuery] = useState13("");
|
|
17569
17859
|
const config = CATEGORY_CONFIG[activeCategory];
|
|
17860
|
+
useEffect10(() => {
|
|
17861
|
+
const timer = setTimeout(() => setDebouncedQuery(searchQuery), 300);
|
|
17862
|
+
return () => clearTimeout(timer);
|
|
17863
|
+
}, [searchQuery]);
|
|
17864
|
+
useEffect10(() => {
|
|
17865
|
+
setSearchQuery("");
|
|
17866
|
+
setDebouncedQuery("");
|
|
17867
|
+
}, [activeCategory]);
|
|
17570
17868
|
useEffect10(() => {
|
|
17571
17869
|
if (!client) return;
|
|
17572
17870
|
client.getIssueCounts().then(setCounts).catch(() => {
|
|
@@ -17606,6 +17904,9 @@ function IssueListScreen({ nav, category }) {
|
|
|
17606
17904
|
}
|
|
17607
17905
|
return sorted;
|
|
17608
17906
|
}, [issues, sortMode]);
|
|
17907
|
+
const searchFilteredIssues = debouncedQuery ? sortedIssues.filter(
|
|
17908
|
+
(issue) => (issue.title || "").toLowerCase().includes(debouncedQuery.toLowerCase()) || (issue.description || "").toLowerCase().includes(debouncedQuery.toLowerCase())
|
|
17909
|
+
) : sortedIssues;
|
|
17609
17910
|
return /* @__PURE__ */ React16.createElement(View16, null, /* @__PURE__ */ React16.createElement(View16, { style: styles5.tabBar }, CATEGORIES.map((cat) => {
|
|
17610
17911
|
const catConfig = CATEGORY_CONFIG[cat];
|
|
17611
17912
|
const isActive = activeCategory === cat;
|
|
@@ -17652,7 +17953,25 @@ function IssueListScreen({ nav, category }) {
|
|
|
17652
17953
|
styles5.sortBtnText,
|
|
17653
17954
|
sortMode === s2.key && styles5.sortBtnTextActive
|
|
17654
17955
|
] }, s2.label)
|
|
17655
|
-
))),
|
|
17956
|
+
))), /* @__PURE__ */ React16.createElement(View16, { style: { paddingHorizontal: 16, paddingBottom: 12 } }, /* @__PURE__ */ React16.createElement(
|
|
17957
|
+
TextInput8,
|
|
17958
|
+
{
|
|
17959
|
+
value: searchQuery,
|
|
17960
|
+
onChangeText: setSearchQuery,
|
|
17961
|
+
placeholder: "Search my reports...",
|
|
17962
|
+
placeholderTextColor: colors.textMuted,
|
|
17963
|
+
style: {
|
|
17964
|
+
padding: 8,
|
|
17965
|
+
paddingHorizontal: 12,
|
|
17966
|
+
borderRadius: 8,
|
|
17967
|
+
borderWidth: 1,
|
|
17968
|
+
borderColor: colors.border,
|
|
17969
|
+
backgroundColor: colors.card,
|
|
17970
|
+
color: colors.textPrimary,
|
|
17971
|
+
fontSize: 13
|
|
17972
|
+
}
|
|
17973
|
+
}
|
|
17974
|
+
)), loading ? /* @__PURE__ */ React16.createElement(IssueListScreenSkeleton, null) : searchFilteredIssues.length === 0 ? /* @__PURE__ */ React16.createElement(View16, { style: styles5.emptyContainer }, /* @__PURE__ */ React16.createElement(Text14, { style: styles5.emptyIcon }, debouncedQuery ? "\u{1F50D}" : config.emptyIcon), /* @__PURE__ */ React16.createElement(Text14, { style: styles5.emptyText }, debouncedQuery ? "No matching issues" : config.emptyText)) : searchFilteredIssues.map((issue) => /* @__PURE__ */ React16.createElement(
|
|
17656
17975
|
TouchableOpacity13,
|
|
17657
17976
|
{
|
|
17658
17977
|
key: issue.id,
|
|
@@ -17817,17 +18136,17 @@ function createStyles11() {
|
|
|
17817
18136
|
}
|
|
17818
18137
|
|
|
17819
18138
|
// src/widget/screens/IssueDetailScreen.tsx
|
|
17820
|
-
import React17, { useMemo as useMemo12, useState as
|
|
17821
|
-
import { View as View17, Text as Text15, Image as Image3, StyleSheet as StyleSheet17, Linking as Linking4, TouchableOpacity as TouchableOpacity14, TextInput as
|
|
18139
|
+
import React17, { useMemo as useMemo12, useState as useState14, useCallback as useCallback5 } from "react";
|
|
18140
|
+
import { View as View17, Text as Text15, Image as Image3, StyleSheet as StyleSheet17, Linking as Linking4, TouchableOpacity as TouchableOpacity14, TextInput as TextInput9, ActivityIndicator as ActivityIndicator2 } from "react-native";
|
|
17822
18141
|
var DONE_STATUSES = ["verified", "resolved", "closed", "reviewed"];
|
|
17823
18142
|
function IssueDetailScreen({ nav, issue }) {
|
|
17824
18143
|
const { dashboardUrl, widgetColorScheme, reopenReport } = useBugBear();
|
|
17825
18144
|
const styles5 = useMemo12(() => createStyles12(), [widgetColorScheme]);
|
|
17826
|
-
const [showReopenForm, setShowReopenForm] =
|
|
17827
|
-
const [reopenReason, setReopenReason] =
|
|
17828
|
-
const [isSubmitting, setIsSubmitting] =
|
|
17829
|
-
const [reopenError, setReopenError] =
|
|
17830
|
-
const [wasReopened, setWasReopened] =
|
|
18145
|
+
const [showReopenForm, setShowReopenForm] = useState14(false);
|
|
18146
|
+
const [reopenReason, setReopenReason] = useState14("");
|
|
18147
|
+
const [isSubmitting, setIsSubmitting] = useState14(false);
|
|
18148
|
+
const [reopenError, setReopenError] = useState14(null);
|
|
18149
|
+
const [wasReopened, setWasReopened] = useState14(false);
|
|
17831
18150
|
const STATUS_LABELS = useMemo12(() => ({
|
|
17832
18151
|
new: { label: "New", bg: colors.blueDark, color: colors.blueLight },
|
|
17833
18152
|
triaging: { label: "Triaging", bg: colors.blueDark, color: colors.blueLight },
|
|
@@ -17873,7 +18192,7 @@ function IssueDetailScreen({ nav, issue }) {
|
|
|
17873
18192
|
},
|
|
17874
18193
|
/* @__PURE__ */ React17.createElement(Text15, { style: styles5.reopenButtonText }, "\u{1F504}", " Not Fixed \u2014 Reopen Issue")
|
|
17875
18194
|
), showReopenForm && !wasReopened && /* @__PURE__ */ React17.createElement(View17, { style: styles5.reopenForm }, /* @__PURE__ */ React17.createElement(Text15, { style: styles5.reopenFormTitle }, "Why isn't this fixed?"), /* @__PURE__ */ React17.createElement(
|
|
17876
|
-
|
|
18195
|
+
TextInput9,
|
|
17877
18196
|
{
|
|
17878
18197
|
value: reopenReason,
|
|
17879
18198
|
onChangeText: setReopenReason,
|
|
@@ -18171,18 +18490,20 @@ function createStyles12() {
|
|
|
18171
18490
|
}
|
|
18172
18491
|
|
|
18173
18492
|
// src/widget/screens/SessionStartScreen.tsx
|
|
18174
|
-
import React18, { useState as
|
|
18175
|
-
import { View as View18, Text as Text16, TextInput as
|
|
18493
|
+
import React18, { useState as useState15, useMemo as useMemo13 } from "react";
|
|
18494
|
+
import { View as View18, Text as Text16, TextInput as TextInput10, TouchableOpacity as TouchableOpacity15, StyleSheet as StyleSheet18, Keyboard as Keyboard2 } from "react-native";
|
|
18176
18495
|
function SessionStartScreen({ nav }) {
|
|
18177
18496
|
const { startSession, assignments, widgetColorScheme } = useBugBear();
|
|
18178
18497
|
const styles5 = useMemo13(() => createStyles13(), [widgetColorScheme]);
|
|
18179
|
-
const [focusArea, setFocusArea] =
|
|
18180
|
-
const [isStarting, setIsStarting] =
|
|
18498
|
+
const [focusArea, setFocusArea] = useState15("");
|
|
18499
|
+
const [isStarting, setIsStarting] = useState15(false);
|
|
18500
|
+
const [error, setError] = useState15(null);
|
|
18181
18501
|
const trackNames = Array.from(new Set(
|
|
18182
18502
|
assignments.filter((a) => a.testCase.track?.name).map((a) => a.testCase.track.name)
|
|
18183
18503
|
)).slice(0, 6);
|
|
18184
18504
|
const handleStart = async () => {
|
|
18185
18505
|
if (isStarting) return;
|
|
18506
|
+
setError(null);
|
|
18186
18507
|
Keyboard2.dismiss();
|
|
18187
18508
|
setIsStarting(true);
|
|
18188
18509
|
try {
|
|
@@ -18191,13 +18512,17 @@ function SessionStartScreen({ nav }) {
|
|
|
18191
18512
|
});
|
|
18192
18513
|
if (result.success) {
|
|
18193
18514
|
nav.replace({ name: "SESSION_ACTIVE" });
|
|
18515
|
+
} else {
|
|
18516
|
+
const msg = result.error || "Failed to start session. Please try again.";
|
|
18517
|
+
console.warn("BugBear: Session start failed:", msg);
|
|
18518
|
+
setError(msg);
|
|
18194
18519
|
}
|
|
18195
18520
|
} finally {
|
|
18196
18521
|
setIsStarting(false);
|
|
18197
18522
|
}
|
|
18198
18523
|
};
|
|
18199
18524
|
return /* @__PURE__ */ React18.createElement(View18, null, /* @__PURE__ */ React18.createElement(View18, { style: styles5.header }, /* @__PURE__ */ React18.createElement(Text16, { style: styles5.headerIcon }, "\u{1F50D}"), /* @__PURE__ */ React18.createElement(Text16, { style: styles5.headerDesc }, "Start an exploratory QA session. Log findings as you go \u2014 bugs, concerns, suggestions, or questions.")), /* @__PURE__ */ React18.createElement(View18, { style: styles5.inputSection }, /* @__PURE__ */ React18.createElement(Text16, { style: styles5.label }, "What are you testing?"), /* @__PURE__ */ React18.createElement(
|
|
18200
|
-
|
|
18525
|
+
TextInput10,
|
|
18201
18526
|
{
|
|
18202
18527
|
value: focusArea,
|
|
18203
18528
|
onChangeText: setFocusArea,
|
|
@@ -18216,7 +18541,7 @@ function SessionStartScreen({ nav }) {
|
|
|
18216
18541
|
activeOpacity: 0.7
|
|
18217
18542
|
},
|
|
18218
18543
|
/* @__PURE__ */ React18.createElement(Text16, { style: [styles5.chipText, focusArea === name && styles5.chipTextActive] }, name)
|
|
18219
|
-
)))), /* @__PURE__ */ React18.createElement(
|
|
18544
|
+
)))), error && /* @__PURE__ */ React18.createElement(View18, { style: styles5.errorBox }, /* @__PURE__ */ React18.createElement(Text16, { style: styles5.errorText }, error)), /* @__PURE__ */ React18.createElement(
|
|
18220
18545
|
TouchableOpacity15,
|
|
18221
18546
|
{
|
|
18222
18547
|
style: [styles5.startBtn, isStarting && styles5.startBtnDisabled],
|
|
@@ -18295,6 +18620,20 @@ function createStyles13() {
|
|
|
18295
18620
|
chipTextActive: {
|
|
18296
18621
|
color: colors.blueLight
|
|
18297
18622
|
},
|
|
18623
|
+
errorBox: {
|
|
18624
|
+
marginBottom: 12,
|
|
18625
|
+
paddingVertical: 10,
|
|
18626
|
+
paddingHorizontal: 12,
|
|
18627
|
+
backgroundColor: "rgba(239, 68, 68, 0.1)",
|
|
18628
|
+
borderWidth: 1,
|
|
18629
|
+
borderColor: "rgba(239, 68, 68, 0.3)",
|
|
18630
|
+
borderRadius: 8
|
|
18631
|
+
},
|
|
18632
|
+
errorText: {
|
|
18633
|
+
fontSize: 13,
|
|
18634
|
+
color: "#f87171",
|
|
18635
|
+
lineHeight: 18
|
|
18636
|
+
},
|
|
18298
18637
|
startBtn: {
|
|
18299
18638
|
paddingVertical: 14,
|
|
18300
18639
|
backgroundColor: colors.blueSurface,
|
|
@@ -18315,7 +18654,7 @@ function createStyles13() {
|
|
|
18315
18654
|
}
|
|
18316
18655
|
|
|
18317
18656
|
// src/widget/screens/SessionActiveScreen.tsx
|
|
18318
|
-
import React19, { useState as
|
|
18657
|
+
import React19, { useState as useState16, useEffect as useEffect11, useRef as useRef5, useMemo as useMemo14 } from "react";
|
|
18319
18658
|
import { View as View19, Text as Text17, TouchableOpacity as TouchableOpacity16, StyleSheet as StyleSheet19 } from "react-native";
|
|
18320
18659
|
function SessionActiveScreen({ nav }) {
|
|
18321
18660
|
const { activeSession, sessionFindings, endSession, refreshSession, widgetColorScheme } = useBugBear();
|
|
@@ -18326,8 +18665,8 @@ function SessionActiveScreen({ nav }) {
|
|
|
18326
18665
|
suggestion: { icon: "\u{1F4A1}", label: "Suggestion", color: colors.blue },
|
|
18327
18666
|
question: { icon: "\u2753", label: "Question", color: colors.violet }
|
|
18328
18667
|
}), [widgetColorScheme]);
|
|
18329
|
-
const [isEnding, setIsEnding] =
|
|
18330
|
-
const [elapsed, setElapsed] =
|
|
18668
|
+
const [isEnding, setIsEnding] = useState16(false);
|
|
18669
|
+
const [elapsed, setElapsed] = useState16(0);
|
|
18331
18670
|
const timerRef = useRef5(null);
|
|
18332
18671
|
useEffect11(() => {
|
|
18333
18672
|
refreshSession();
|
|
@@ -18552,8 +18891,8 @@ function createStyles14() {
|
|
|
18552
18891
|
}
|
|
18553
18892
|
|
|
18554
18893
|
// src/widget/screens/SessionFindingScreen.tsx
|
|
18555
|
-
import React20, { useState as
|
|
18556
|
-
import { View as View20, Text as Text18, TextInput as
|
|
18894
|
+
import React20, { useState as useState17, useMemo as useMemo15 } from "react";
|
|
18895
|
+
import { View as View20, Text as Text18, TextInput as TextInput11, TouchableOpacity as TouchableOpacity17, StyleSheet as StyleSheet20, Keyboard as Keyboard3 } from "react-native";
|
|
18557
18896
|
var FINDING_TYPES = [
|
|
18558
18897
|
{ value: "bug", icon: "\u{1F41B}", label: "Bug" },
|
|
18559
18898
|
{ value: "concern", icon: "\u26A0\uFE0F", label: "Concern" },
|
|
@@ -18570,11 +18909,11 @@ function SessionFindingScreen({ nav }) {
|
|
|
18570
18909
|
{ value: "low", label: "Low", color: colors.textMuted },
|
|
18571
18910
|
{ value: "observation", label: "Note", color: colors.textDim }
|
|
18572
18911
|
], [widgetColorScheme]);
|
|
18573
|
-
const [type, setType] =
|
|
18574
|
-
const [severity, setSeverity] =
|
|
18575
|
-
const [title, setTitle] =
|
|
18576
|
-
const [description, setDescription] =
|
|
18577
|
-
const [isSubmitting, setIsSubmitting] =
|
|
18912
|
+
const [type, setType] = useState17("bug");
|
|
18913
|
+
const [severity, setSeverity] = useState17("medium");
|
|
18914
|
+
const [title, setTitle] = useState17("");
|
|
18915
|
+
const [description, setDescription] = useState17("");
|
|
18916
|
+
const [isSubmitting, setIsSubmitting] = useState17(false);
|
|
18578
18917
|
const handleSubmit = async () => {
|
|
18579
18918
|
if (!title.trim() || isSubmitting) return;
|
|
18580
18919
|
Keyboard3.dismiss();
|
|
@@ -18617,7 +18956,7 @@ function SessionFindingScreen({ nav }) {
|
|
|
18617
18956
|
},
|
|
18618
18957
|
/* @__PURE__ */ React20.createElement(Text18, { style: [styles5.severityText, severity === s2.value && { color: s2.color }] }, s2.label)
|
|
18619
18958
|
)))), /* @__PURE__ */ React20.createElement(View20, { style: styles5.inputSection }, /* @__PURE__ */ React20.createElement(
|
|
18620
|
-
|
|
18959
|
+
TextInput11,
|
|
18621
18960
|
{
|
|
18622
18961
|
value: title,
|
|
18623
18962
|
onChangeText: setTitle,
|
|
@@ -18627,7 +18966,7 @@ function SessionFindingScreen({ nav }) {
|
|
|
18627
18966
|
returnKeyType: "next"
|
|
18628
18967
|
}
|
|
18629
18968
|
)), /* @__PURE__ */ React20.createElement(View20, { style: styles5.inputSection }, /* @__PURE__ */ React20.createElement(
|
|
18630
|
-
|
|
18969
|
+
TextInput11,
|
|
18631
18970
|
{
|
|
18632
18971
|
value: description,
|
|
18633
18972
|
onChangeText: setDescription,
|
|
@@ -18772,14 +19111,31 @@ function BugBearButton({
|
|
|
18772
19111
|
}) {
|
|
18773
19112
|
const { shouldShowWidget, testerInfo, isLoading, unreadCount, assignments, widgetMode, widgetColorScheme } = useBugBear();
|
|
18774
19113
|
const { currentScreen, canGoBack, push, pop, replace, reset } = useNavigation();
|
|
18775
|
-
const [
|
|
19114
|
+
const [panelVisible, setPanelVisible] = useState18(false);
|
|
19115
|
+
const panelAnim = useRef6(new Animated2.Value(0)).current;
|
|
18776
19116
|
const styles5 = useMemo16(() => createStyles16(), [widgetColorScheme]);
|
|
18777
19117
|
const screenCaptureRef = useRef6(null);
|
|
18778
|
-
const
|
|
19118
|
+
const openPanel = () => {
|
|
18779
19119
|
captureAppScreen().then((uri) => {
|
|
18780
19120
|
screenCaptureRef.current = uri;
|
|
18781
19121
|
});
|
|
18782
|
-
|
|
19122
|
+
setPanelVisible(true);
|
|
19123
|
+
Animated2.spring(panelAnim, {
|
|
19124
|
+
toValue: 1,
|
|
19125
|
+
useNativeDriver: true,
|
|
19126
|
+
friction: 8,
|
|
19127
|
+
tension: 65
|
|
19128
|
+
}).start();
|
|
19129
|
+
};
|
|
19130
|
+
const closePanel = () => {
|
|
19131
|
+
Keyboard4.dismiss();
|
|
19132
|
+
Animated2.timing(panelAnim, {
|
|
19133
|
+
toValue: 0,
|
|
19134
|
+
duration: 250,
|
|
19135
|
+
useNativeDriver: true
|
|
19136
|
+
}).start(() => {
|
|
19137
|
+
setPanelVisible(false);
|
|
19138
|
+
});
|
|
18783
19139
|
};
|
|
18784
19140
|
const getInitialPosition = () => {
|
|
18785
19141
|
const buttonSize = 56;
|
|
@@ -18830,7 +19186,7 @@ function BugBearButton({
|
|
|
18830
19186
|
tension: 40
|
|
18831
19187
|
}).start();
|
|
18832
19188
|
if (!isDragging.current && Math.abs(gs.dx) < 5 && Math.abs(gs.dy) < 5) {
|
|
18833
|
-
|
|
19189
|
+
openPanel();
|
|
18834
19190
|
}
|
|
18835
19191
|
isDragging.current = false;
|
|
18836
19192
|
}
|
|
@@ -18838,6 +19194,19 @@ function BugBearButton({
|
|
|
18838
19194
|
).current;
|
|
18839
19195
|
const pendingTests = widgetMode === "qa" ? assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length : 0;
|
|
18840
19196
|
const badgeCount = pendingTests + unreadCount;
|
|
19197
|
+
useEffect12(() => {
|
|
19198
|
+
if (!panelVisible || Platform5.OS !== "android") return;
|
|
19199
|
+
const handler = BackHandler.addEventListener("hardwareBackPress", () => {
|
|
19200
|
+
if (canGoBack) {
|
|
19201
|
+
Keyboard4.dismiss();
|
|
19202
|
+
pop();
|
|
19203
|
+
} else {
|
|
19204
|
+
closePanel();
|
|
19205
|
+
}
|
|
19206
|
+
return true;
|
|
19207
|
+
});
|
|
19208
|
+
return () => handler.remove();
|
|
19209
|
+
}, [panelVisible, canGoBack]);
|
|
18841
19210
|
if (!shouldShowWidget) return null;
|
|
18842
19211
|
const getHeaderTitle = () => {
|
|
18843
19212
|
switch (currentScreen.name) {
|
|
@@ -18876,8 +19245,7 @@ function BugBearButton({
|
|
|
18876
19245
|
}
|
|
18877
19246
|
};
|
|
18878
19247
|
const handleClose = () => {
|
|
18879
|
-
|
|
18880
|
-
setModalVisible(false);
|
|
19248
|
+
closePanel();
|
|
18881
19249
|
};
|
|
18882
19250
|
const nav = {
|
|
18883
19251
|
push: (screen) => {
|
|
@@ -18949,7 +19317,7 @@ function BugBearButton({
|
|
|
18949
19317
|
return /* @__PURE__ */ React21.createElement(HomeScreen, { nav });
|
|
18950
19318
|
}
|
|
18951
19319
|
};
|
|
18952
|
-
return /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement(
|
|
19320
|
+
return /* @__PURE__ */ React21.createElement(React21.Fragment, null, !panelVisible && /* @__PURE__ */ React21.createElement(
|
|
18953
19321
|
Animated2.View,
|
|
18954
19322
|
{
|
|
18955
19323
|
style: [styles5.fabContainer, { transform: pan.getTranslateTransform() }, buttonStyle],
|
|
@@ -18959,28 +19327,38 @@ function BugBearButton({
|
|
|
18959
19327
|
TouchableOpacity18,
|
|
18960
19328
|
{
|
|
18961
19329
|
style: styles5.fab,
|
|
18962
|
-
onPress:
|
|
19330
|
+
onPress: openPanel,
|
|
18963
19331
|
activeOpacity: draggable ? 1 : 0.7
|
|
18964
19332
|
},
|
|
18965
19333
|
/* @__PURE__ */ React21.createElement(Image4, { source: { uri: BUGBEAR_LOGO_BASE64 }, style: styles5.fabIcon }),
|
|
18966
19334
|
badgeCount > 0 && /* @__PURE__ */ React21.createElement(View21, { style: styles5.badge }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.badgeText }, badgeCount > 9 ? "9+" : badgeCount))
|
|
18967
19335
|
)
|
|
18968
|
-
), /* @__PURE__ */ React21.createElement(
|
|
18969
|
-
|
|
19336
|
+
), panelVisible && /* @__PURE__ */ React21.createElement(View21, { style: styles5.panelWrapper, pointerEvents: "box-none" }, /* @__PURE__ */ React21.createElement(
|
|
19337
|
+
KeyboardAvoidingView,
|
|
18970
19338
|
{
|
|
18971
|
-
|
|
18972
|
-
|
|
18973
|
-
|
|
18974
|
-
onRequestClose: handleClose
|
|
19339
|
+
behavior: Platform5.OS === "ios" ? "padding" : "height",
|
|
19340
|
+
style: styles5.panelKeyboardAvoid,
|
|
19341
|
+
pointerEvents: "box-none"
|
|
18975
19342
|
},
|
|
18976
19343
|
/* @__PURE__ */ React21.createElement(
|
|
18977
|
-
|
|
19344
|
+
Animated2.View,
|
|
18978
19345
|
{
|
|
18979
|
-
|
|
18980
|
-
|
|
19346
|
+
style: [
|
|
19347
|
+
styles5.panelContainer,
|
|
19348
|
+
{
|
|
19349
|
+
transform: [{
|
|
19350
|
+
translateY: panelAnim.interpolate({
|
|
19351
|
+
inputRange: [0, 1],
|
|
19352
|
+
outputRange: [screenHeight, 0]
|
|
19353
|
+
})
|
|
19354
|
+
}]
|
|
19355
|
+
}
|
|
19356
|
+
]
|
|
18981
19357
|
},
|
|
18982
|
-
/* @__PURE__ */ React21.createElement(View21, { style: styles5.
|
|
18983
|
-
|
|
19358
|
+
/* @__PURE__ */ React21.createElement(View21, { style: styles5.panelHandle }, /* @__PURE__ */ React21.createElement(View21, { style: styles5.panelHandleBar })),
|
|
19359
|
+
/* @__PURE__ */ React21.createElement(View21, { style: styles5.header }, /* @__PURE__ */ React21.createElement(View21, { style: styles5.headerLeft }, canGoBack ? /* @__PURE__ */ React21.createElement(TouchableOpacity18, { onPress: () => nav.pop(), style: styles5.backButton }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.backText }, "\u2190 Back")) : /* @__PURE__ */ React21.createElement(View21, { style: styles5.headerTitleRow }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ React21.createElement(TouchableOpacity18, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ React21.createElement(Text19, { style: styles5.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ React21.createElement(View21, { style: styles5.headerActions }, currentScreen.name !== "HOME" && /* @__PURE__ */ React21.createElement(TouchableOpacity18, { onPress: () => nav.reset(), style: styles5.homeButton }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.homeIcon }, "\u2302")), /* @__PURE__ */ React21.createElement(TouchableOpacity18, { onPress: handleClose, style: styles5.closeButton }, /* @__PURE__ */ React21.createElement(Text19, { style: styles5.closeText }, "\u2715")))),
|
|
19360
|
+
/* @__PURE__ */ React21.createElement(
|
|
19361
|
+
ScrollView4,
|
|
18984
19362
|
{
|
|
18985
19363
|
style: styles5.content,
|
|
18986
19364
|
contentContainerStyle: styles5.contentContainer,
|
|
@@ -18988,9 +19366,9 @@ function BugBearButton({
|
|
|
18988
19366
|
showsVerticalScrollIndicator: false
|
|
18989
19367
|
},
|
|
18990
19368
|
isLoading ? /* @__PURE__ */ React21.createElement(View21, { style: styles5.loadingContainer }, /* @__PURE__ */ React21.createElement(ActivityIndicator3, { size: "large", color: colors.blue }), /* @__PURE__ */ React21.createElement(Text19, { style: styles5.loadingText }, "Loading...")) : renderScreen()
|
|
18991
|
-
)
|
|
19369
|
+
)
|
|
18992
19370
|
)
|
|
18993
|
-
));
|
|
19371
|
+
)));
|
|
18994
19372
|
}
|
|
18995
19373
|
function createStyles16() {
|
|
18996
19374
|
return StyleSheet21.create({
|
|
@@ -19034,18 +19412,42 @@ function createStyles16() {
|
|
|
19034
19412
|
fontSize: 11,
|
|
19035
19413
|
fontWeight: "700"
|
|
19036
19414
|
},
|
|
19037
|
-
// Modal
|
|
19038
|
-
|
|
19415
|
+
// Panel (replaces Modal — no backdrop, app stays interactive)
|
|
19416
|
+
panelWrapper: {
|
|
19417
|
+
position: "absolute",
|
|
19418
|
+
top: 0,
|
|
19419
|
+
left: 0,
|
|
19420
|
+
right: 0,
|
|
19421
|
+
bottom: 0,
|
|
19422
|
+
zIndex: 9998,
|
|
19423
|
+
justifyContent: "flex-end"
|
|
19424
|
+
},
|
|
19425
|
+
panelKeyboardAvoid: {
|
|
19039
19426
|
flex: 1,
|
|
19040
|
-
justifyContent: "flex-end"
|
|
19041
|
-
backgroundColor: "rgba(0,0,0,0.5)"
|
|
19427
|
+
justifyContent: "flex-end"
|
|
19042
19428
|
},
|
|
19043
|
-
|
|
19429
|
+
panelContainer: {
|
|
19044
19430
|
backgroundColor: colors.bg,
|
|
19045
19431
|
borderTopLeftRadius: 20,
|
|
19046
19432
|
borderTopRightRadius: 20,
|
|
19047
|
-
maxHeight: "
|
|
19048
|
-
minHeight: "
|
|
19433
|
+
maxHeight: "70%",
|
|
19434
|
+
minHeight: "40%",
|
|
19435
|
+
shadowColor: colors.onBright,
|
|
19436
|
+
shadowOffset: { width: 0, height: -4 },
|
|
19437
|
+
shadowOpacity: 0.25,
|
|
19438
|
+
shadowRadius: 12,
|
|
19439
|
+
elevation: 16
|
|
19440
|
+
},
|
|
19441
|
+
panelHandle: {
|
|
19442
|
+
alignItems: "center",
|
|
19443
|
+
paddingTop: 8,
|
|
19444
|
+
paddingBottom: 4
|
|
19445
|
+
},
|
|
19446
|
+
panelHandleBar: {
|
|
19447
|
+
width: 36,
|
|
19448
|
+
height: 4,
|
|
19449
|
+
borderRadius: 2,
|
|
19450
|
+
backgroundColor: colors.border
|
|
19049
19451
|
},
|
|
19050
19452
|
// Header
|
|
19051
19453
|
header: {
|
|
@@ -19128,7 +19530,7 @@ function createStyles16() {
|
|
|
19128
19530
|
},
|
|
19129
19531
|
contentContainer: {
|
|
19130
19532
|
padding: 16,
|
|
19131
|
-
paddingBottom:
|
|
19533
|
+
paddingBottom: Platform5.OS === "ios" ? 50 : 28
|
|
19132
19534
|
},
|
|
19133
19535
|
// Loading
|
|
19134
19536
|
loadingContainer: {
|