@bbearai/react-native 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +372 -36
- package/dist/index.mjs +395 -59
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -12197,6 +12197,7 @@ var BugBearClient = class {
|
|
|
12197
12197
|
this.navigationHistory = [];
|
|
12198
12198
|
this.reportSubmitInFlight = false;
|
|
12199
12199
|
this._queue = null;
|
|
12200
|
+
this._sessionStorage = new LocalStorageAdapter();
|
|
12200
12201
|
this.realtimeChannels = [];
|
|
12201
12202
|
this.monitor = null;
|
|
12202
12203
|
this.initialized = false;
|
|
@@ -12274,6 +12275,10 @@ var BugBearClient = class {
|
|
|
12274
12275
|
this.initOfflineQueue();
|
|
12275
12276
|
this.initMonitoring();
|
|
12276
12277
|
}
|
|
12278
|
+
/** Cache key scoped to the active project. */
|
|
12279
|
+
get sessionCacheKey() {
|
|
12280
|
+
return `bugbear_session_${this.config.projectId ?? "unknown"}`;
|
|
12281
|
+
}
|
|
12277
12282
|
/** Initialize offline queue if configured. Shared by both init paths. */
|
|
12278
12283
|
initOfflineQueue() {
|
|
12279
12284
|
if (this.config.offlineQueue?.enabled) {
|
|
@@ -12337,6 +12342,26 @@ var BugBearClient = class {
|
|
|
12337
12342
|
await this.pendingInit;
|
|
12338
12343
|
if (this.initError) throw this.initError;
|
|
12339
12344
|
}
|
|
12345
|
+
/**
|
|
12346
|
+
* Fire-and-forget call to a dashboard notification endpoint.
|
|
12347
|
+
* Only works when apiKey is configured (needed for API auth).
|
|
12348
|
+
* Failures are silently ignored — notifications are best-effort.
|
|
12349
|
+
*/
|
|
12350
|
+
async notifyDashboard(path, body) {
|
|
12351
|
+
if (!this.config.apiKey) return;
|
|
12352
|
+
try {
|
|
12353
|
+
const baseUrl = (this.config.apiBaseUrl || DEFAULT_API_BASE_URL).replace(/\/$/, "");
|
|
12354
|
+
await fetch(`${baseUrl}/api/v1/notifications/${path}`, {
|
|
12355
|
+
method: "POST",
|
|
12356
|
+
headers: {
|
|
12357
|
+
"Content-Type": "application/json",
|
|
12358
|
+
"Authorization": `Bearer ${this.config.apiKey}`
|
|
12359
|
+
},
|
|
12360
|
+
body: JSON.stringify(body)
|
|
12361
|
+
});
|
|
12362
|
+
} catch {
|
|
12363
|
+
}
|
|
12364
|
+
}
|
|
12340
12365
|
// ── Offline Queue ─────────────────────────────────────────
|
|
12341
12366
|
/**
|
|
12342
12367
|
* Access the offline queue (if enabled).
|
|
@@ -12363,6 +12388,48 @@ var BugBearClient = class {
|
|
|
12363
12388
|
}
|
|
12364
12389
|
await this._queue.load();
|
|
12365
12390
|
}
|
|
12391
|
+
// ── Session Cache ──────────────────────────────────────────
|
|
12392
|
+
/**
|
|
12393
|
+
* Swap the session cache storage adapter (for React Native — pass AsyncStorage).
|
|
12394
|
+
* Must be called before getCachedSession() for persistence across app kills.
|
|
12395
|
+
* Web callers don't need this — LocalStorageAdapter is the default.
|
|
12396
|
+
*/
|
|
12397
|
+
setSessionStorage(adapter) {
|
|
12398
|
+
this._sessionStorage = adapter;
|
|
12399
|
+
}
|
|
12400
|
+
/**
|
|
12401
|
+
* Cache the active QA session locally for instant restore on app restart.
|
|
12402
|
+
* Pass null to clear the cache (e.g. after ending a session).
|
|
12403
|
+
*/
|
|
12404
|
+
async cacheSession(session) {
|
|
12405
|
+
try {
|
|
12406
|
+
if (session) {
|
|
12407
|
+
await this._sessionStorage.setItem(this.sessionCacheKey, JSON.stringify(session));
|
|
12408
|
+
} else {
|
|
12409
|
+
await this._sessionStorage.removeItem(this.sessionCacheKey);
|
|
12410
|
+
}
|
|
12411
|
+
} catch {
|
|
12412
|
+
}
|
|
12413
|
+
}
|
|
12414
|
+
/**
|
|
12415
|
+
* Retrieve the cached QA session. Returns null if no cache, if stale (>24h),
|
|
12416
|
+
* or if parsing fails. The DB fetch in initializeBugBear() is the source of truth.
|
|
12417
|
+
*/
|
|
12418
|
+
async getCachedSession() {
|
|
12419
|
+
try {
|
|
12420
|
+
const raw = await this._sessionStorage.getItem(this.sessionCacheKey);
|
|
12421
|
+
if (!raw) return null;
|
|
12422
|
+
const session = JSON.parse(raw);
|
|
12423
|
+
const age = Date.now() - new Date(session.startedAt).getTime();
|
|
12424
|
+
if (age > 24 * 60 * 60 * 1e3) {
|
|
12425
|
+
await this._sessionStorage.removeItem(this.sessionCacheKey);
|
|
12426
|
+
return null;
|
|
12427
|
+
}
|
|
12428
|
+
return session;
|
|
12429
|
+
} catch {
|
|
12430
|
+
return null;
|
|
12431
|
+
}
|
|
12432
|
+
}
|
|
12366
12433
|
registerQueueHandlers() {
|
|
12367
12434
|
if (!this._queue) return;
|
|
12368
12435
|
this._queue.registerHandler("report", async (payload) => {
|
|
@@ -12380,6 +12447,11 @@ var BugBearClient = class {
|
|
|
12380
12447
|
if (error) return { success: false, error: error.message };
|
|
12381
12448
|
return { success: true };
|
|
12382
12449
|
});
|
|
12450
|
+
this._queue.registerHandler("email_capture", async (payload) => {
|
|
12451
|
+
const { error } = await this.supabase.from("email_captures").insert(payload).select("id").single();
|
|
12452
|
+
if (error) return { success: false, error: error.message };
|
|
12453
|
+
return { success: true };
|
|
12454
|
+
});
|
|
12383
12455
|
}
|
|
12384
12456
|
// ── Realtime Subscriptions ─────────────────────────────────
|
|
12385
12457
|
/** Whether realtime is enabled in config. */
|
|
@@ -12567,6 +12639,8 @@ var BugBearClient = class {
|
|
|
12567
12639
|
if (this.config.onReportSubmitted) {
|
|
12568
12640
|
this.config.onReportSubmitted(report);
|
|
12569
12641
|
}
|
|
12642
|
+
this.notifyDashboard("report", { reportId: data.id }).catch(() => {
|
|
12643
|
+
});
|
|
12570
12644
|
return { success: true, reportId: data.id };
|
|
12571
12645
|
} catch (err) {
|
|
12572
12646
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -12579,6 +12653,44 @@ var BugBearClient = class {
|
|
|
12579
12653
|
this.reportSubmitInFlight = false;
|
|
12580
12654
|
}
|
|
12581
12655
|
}
|
|
12656
|
+
/**
|
|
12657
|
+
* Capture an email for QA testing.
|
|
12658
|
+
* Called by the email interceptor — not typically called directly.
|
|
12659
|
+
*/
|
|
12660
|
+
async captureEmail(payload) {
|
|
12661
|
+
try {
|
|
12662
|
+
await this.ready();
|
|
12663
|
+
if (!payload.subject || !payload.to || payload.to.length === 0) {
|
|
12664
|
+
return { success: false, error: "subject and to are required" };
|
|
12665
|
+
}
|
|
12666
|
+
const record = {
|
|
12667
|
+
project_id: this.config.projectId,
|
|
12668
|
+
to_addresses: payload.to,
|
|
12669
|
+
from_address: payload.from || null,
|
|
12670
|
+
subject: payload.subject,
|
|
12671
|
+
html_content: payload.html || null,
|
|
12672
|
+
text_content: payload.text || null,
|
|
12673
|
+
template_id: payload.templateId || null,
|
|
12674
|
+
metadata: payload.metadata || {},
|
|
12675
|
+
capture_mode: payload.captureMode,
|
|
12676
|
+
was_delivered: payload.wasDelivered,
|
|
12677
|
+
delivery_status: payload.wasDelivered ? "sent" : "pending"
|
|
12678
|
+
};
|
|
12679
|
+
const { data, error } = await this.supabase.from("email_captures").insert(record).select("id").single();
|
|
12680
|
+
if (error) {
|
|
12681
|
+
if (this._queue && isNetworkError(error.message)) {
|
|
12682
|
+
await this._queue.enqueue("email_capture", record);
|
|
12683
|
+
return { success: false, queued: true, error: "Queued \u2014 will send when online" };
|
|
12684
|
+
}
|
|
12685
|
+
console.error("BugBear: Failed to capture email", error.message);
|
|
12686
|
+
return { success: false, error: error.message };
|
|
12687
|
+
}
|
|
12688
|
+
return { success: true, captureId: data.id };
|
|
12689
|
+
} catch (err) {
|
|
12690
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
12691
|
+
return { success: false, error: message };
|
|
12692
|
+
}
|
|
12693
|
+
}
|
|
12582
12694
|
/**
|
|
12583
12695
|
* Get assigned tests for current user
|
|
12584
12696
|
* First looks up the tester by email, then fetches their assignments
|
|
@@ -13200,6 +13312,32 @@ var BugBearClient = class {
|
|
|
13200
13312
|
return [];
|
|
13201
13313
|
}
|
|
13202
13314
|
}
|
|
13315
|
+
/**
|
|
13316
|
+
* Reopen a done issue that the tester believes isn't actually fixed.
|
|
13317
|
+
* Transitions the report from a done status back to 'confirmed'.
|
|
13318
|
+
*/
|
|
13319
|
+
async reopenReport(reportId, reason) {
|
|
13320
|
+
try {
|
|
13321
|
+
const testerInfo = await this.getTesterInfo();
|
|
13322
|
+
if (!testerInfo) return { success: false, error: "Not authenticated as tester" };
|
|
13323
|
+
const { data, error } = await this.supabase.rpc("reopen_report", {
|
|
13324
|
+
p_report_id: reportId,
|
|
13325
|
+
p_tester_id: testerInfo.id,
|
|
13326
|
+
p_reason: reason
|
|
13327
|
+
});
|
|
13328
|
+
if (error) {
|
|
13329
|
+
console.error("BugBear: Failed to reopen report", formatPgError(error));
|
|
13330
|
+
return { success: false, error: error.message };
|
|
13331
|
+
}
|
|
13332
|
+
if (!data?.success) {
|
|
13333
|
+
return { success: false, error: data?.error || "Failed to reopen report" };
|
|
13334
|
+
}
|
|
13335
|
+
return { success: true };
|
|
13336
|
+
} catch (err) {
|
|
13337
|
+
console.error("BugBear: Error reopening report", err);
|
|
13338
|
+
return { success: false, error: "Unexpected error" };
|
|
13339
|
+
}
|
|
13340
|
+
}
|
|
13203
13341
|
/**
|
|
13204
13342
|
* Basic email format validation (defense in depth)
|
|
13205
13343
|
*/
|
|
@@ -13786,7 +13924,7 @@ var BugBearClient = class {
|
|
|
13786
13924
|
insertData.attachments = safeAttachments;
|
|
13787
13925
|
}
|
|
13788
13926
|
}
|
|
13789
|
-
const { error } = await this.supabase.from("discussion_messages").insert(insertData);
|
|
13927
|
+
const { data: msgData, error } = await this.supabase.from("discussion_messages").insert(insertData).select("id").single();
|
|
13790
13928
|
if (error) {
|
|
13791
13929
|
if (this._queue && isNetworkError(error.message)) {
|
|
13792
13930
|
await this._queue.enqueue("message", insertData);
|
|
@@ -13795,6 +13933,10 @@ var BugBearClient = class {
|
|
|
13795
13933
|
console.error("BugBear: Failed to send message", formatPgError(error));
|
|
13796
13934
|
return false;
|
|
13797
13935
|
}
|
|
13936
|
+
if (msgData?.id) {
|
|
13937
|
+
this.notifyDashboard("message", { threadId, messageId: msgData.id }).catch(() => {
|
|
13938
|
+
});
|
|
13939
|
+
}
|
|
13798
13940
|
await this.markThreadAsRead(threadId);
|
|
13799
13941
|
return true;
|
|
13800
13942
|
} catch (err) {
|
|
@@ -13920,6 +14062,7 @@ var BugBearClient = class {
|
|
|
13920
14062
|
if (!session) {
|
|
13921
14063
|
return { success: false, error: "Session created but could not be fetched" };
|
|
13922
14064
|
}
|
|
14065
|
+
await this.cacheSession(session);
|
|
13923
14066
|
return { success: true, session };
|
|
13924
14067
|
} catch (err) {
|
|
13925
14068
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -13943,6 +14086,7 @@ var BugBearClient = class {
|
|
|
13943
14086
|
return { success: false, error: error.message };
|
|
13944
14087
|
}
|
|
13945
14088
|
const session = this.transformSession(data);
|
|
14089
|
+
await this.cacheSession(null);
|
|
13946
14090
|
return { success: true, session };
|
|
13947
14091
|
} catch (err) {
|
|
13948
14092
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -13958,8 +14102,13 @@ var BugBearClient = class {
|
|
|
13958
14102
|
const testerInfo = await this.getTesterInfo();
|
|
13959
14103
|
if (!testerInfo) return null;
|
|
13960
14104
|
const { data, error } = await this.supabase.from("qa_sessions").select("*").eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).eq("status", "active").order("started_at", { ascending: false }).limit(1).maybeSingle();
|
|
13961
|
-
if (error || !data)
|
|
13962
|
-
|
|
14105
|
+
if (error || !data) {
|
|
14106
|
+
await this.cacheSession(null);
|
|
14107
|
+
return null;
|
|
14108
|
+
}
|
|
14109
|
+
const session = this.transformSession(data);
|
|
14110
|
+
await this.cacheSession(session);
|
|
14111
|
+
return session;
|
|
13963
14112
|
} catch (err) {
|
|
13964
14113
|
console.error("BugBear: Error fetching active session", err);
|
|
13965
14114
|
return null;
|
|
@@ -14477,6 +14626,7 @@ var BugBearContext = createContext({
|
|
|
14477
14626
|
issueCounts: { open: 0, done: 0, reopened: 0 },
|
|
14478
14627
|
refreshIssueCounts: async () => {
|
|
14479
14628
|
},
|
|
14629
|
+
reopenReport: async () => ({ success: false }),
|
|
14480
14630
|
queuedCount: 0,
|
|
14481
14631
|
dashboardUrl: void 0,
|
|
14482
14632
|
onError: void 0
|
|
@@ -14618,6 +14768,14 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
14618
14768
|
const counts = await client.getIssueCounts();
|
|
14619
14769
|
setIssueCounts(counts);
|
|
14620
14770
|
}, [client]);
|
|
14771
|
+
const reopenReport = useCallback(async (reportId, reason) => {
|
|
14772
|
+
if (!client) return { success: false, error: "Client not initialized" };
|
|
14773
|
+
const result = await client.reopenReport(reportId, reason);
|
|
14774
|
+
if (result.success) {
|
|
14775
|
+
await refreshIssueCounts();
|
|
14776
|
+
}
|
|
14777
|
+
return result;
|
|
14778
|
+
}, [client, refreshIssueCounts]);
|
|
14621
14779
|
const initializeBugBear = useCallback(async (bugBearClient) => {
|
|
14622
14780
|
setIsLoading(true);
|
|
14623
14781
|
try {
|
|
@@ -14703,6 +14861,10 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
14703
14861
|
setClient(newClient);
|
|
14704
14862
|
(async () => {
|
|
14705
14863
|
try {
|
|
14864
|
+
const cachedSession = await newClient.getCachedSession();
|
|
14865
|
+
if (cachedSession) {
|
|
14866
|
+
setActiveSession(cachedSession);
|
|
14867
|
+
}
|
|
14706
14868
|
await initializeBugBear(newClient);
|
|
14707
14869
|
if (newClient.monitor && config.monitoring) {
|
|
14708
14870
|
const getCurrentRoute = () => contextCapture.getCurrentRoute();
|
|
@@ -14837,6 +14999,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
14837
14999
|
// Issue tracking
|
|
14838
15000
|
issueCounts,
|
|
14839
15001
|
refreshIssueCounts,
|
|
15002
|
+
reopenReport,
|
|
14840
15003
|
queuedCount,
|
|
14841
15004
|
dashboardUrl: config.dashboardUrl,
|
|
14842
15005
|
onError: config.onError
|
|
@@ -14854,7 +15017,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
14854
15017
|
}
|
|
14855
15018
|
|
|
14856
15019
|
// src/BugBearButton.tsx
|
|
14857
|
-
import React21, { useState as
|
|
15020
|
+
import React21, { useState as useState17, useRef as useRef6, useMemo as useMemo16 } from "react";
|
|
14858
15021
|
import {
|
|
14859
15022
|
View as View21,
|
|
14860
15023
|
Text as Text19,
|
|
@@ -14868,7 +15031,7 @@ import {
|
|
|
14868
15031
|
Platform as Platform5,
|
|
14869
15032
|
PanResponder,
|
|
14870
15033
|
Animated as Animated2,
|
|
14871
|
-
ActivityIndicator as
|
|
15034
|
+
ActivityIndicator as ActivityIndicator3,
|
|
14872
15035
|
Keyboard as Keyboard4
|
|
14873
15036
|
} from "react-native";
|
|
14874
15037
|
|
|
@@ -15583,6 +15746,17 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
15583
15746
|
setShowSteps(true);
|
|
15584
15747
|
setShowDetails(false);
|
|
15585
15748
|
}, [displayedAssignment?.id]);
|
|
15749
|
+
useEffect4(() => {
|
|
15750
|
+
if (!client || !displayedAssignment || displayedAssignment.status !== "pending") return;
|
|
15751
|
+
let cancelled = false;
|
|
15752
|
+
(async () => {
|
|
15753
|
+
await client.updateAssignmentStatus(displayedAssignment.id, "in_progress");
|
|
15754
|
+
if (!cancelled) await refreshAssignments();
|
|
15755
|
+
})();
|
|
15756
|
+
return () => {
|
|
15757
|
+
cancelled = true;
|
|
15758
|
+
};
|
|
15759
|
+
}, [client, displayedAssignment?.id, displayedAssignment?.status, refreshAssignments]);
|
|
15586
15760
|
useEffect4(() => {
|
|
15587
15761
|
const active = displayedAssignment?.status === "in_progress" ? displayedAssignment : null;
|
|
15588
15762
|
if (!active?.startedAt) {
|
|
@@ -15633,17 +15807,6 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
15633
15807
|
setIsSubmitting(false);
|
|
15634
15808
|
}
|
|
15635
15809
|
}, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
|
|
15636
|
-
const handleStart = useCallback2(async () => {
|
|
15637
|
-
if (!client || !displayedAssignment || isSubmitting) return;
|
|
15638
|
-
Keyboard.dismiss();
|
|
15639
|
-
setIsSubmitting(true);
|
|
15640
|
-
try {
|
|
15641
|
-
await client.updateAssignmentStatus(displayedAssignment.id, "in_progress");
|
|
15642
|
-
await refreshAssignments();
|
|
15643
|
-
} finally {
|
|
15644
|
-
setIsSubmitting(false);
|
|
15645
|
-
}
|
|
15646
|
-
}, [client, displayedAssignment, refreshAssignments, isSubmitting]);
|
|
15647
15810
|
const handleReopen = useCallback2(async () => {
|
|
15648
15811
|
if (!client || !displayedAssignment || isSubmitting) return;
|
|
15649
15812
|
Keyboard.dismiss();
|
|
@@ -15789,15 +15952,7 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
15789
15952
|
disabled: isSubmitting
|
|
15790
15953
|
},
|
|
15791
15954
|
/* @__PURE__ */ React4.createElement(Text2, { style: styles5.passBtnText }, "Change to Pass")
|
|
15792
|
-
))) : displayedAssignment.
|
|
15793
|
-
TouchableOpacity2,
|
|
15794
|
-
{
|
|
15795
|
-
style: [styles5.startBtn, isSubmitting && { opacity: 0.5 }],
|
|
15796
|
-
onPress: handleStart,
|
|
15797
|
-
disabled: isSubmitting
|
|
15798
|
-
},
|
|
15799
|
-
/* @__PURE__ */ React4.createElement(Text2, { style: styles5.startBtnText }, isSubmitting ? "Starting..." : displayedAssignment.isVerification ? "\u{1F50D} Start Verification" : "\u25B6 Start Test")
|
|
15800
|
-
) : /* @__PURE__ */ React4.createElement(View4, { style: styles5.actionButtons }, /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles5.actionBtn, styles5.failBtn, isSubmitting && { opacity: 0.5 }], onPress: handleFail, disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles5.failBtnText }, isSubmitting ? displayedAssignment.isVerification ? "Reporting..." : "Failing..." : displayedAssignment.isVerification ? "\u2717 Still Broken" : "Fail")), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles5.actionBtn, styles5.skipBtn, isSubmitting && { opacity: 0.5 }], onPress: () => setShowSkipModal(true), disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles5.skipBtnText }, "Skip")), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles5.actionBtn, styles5.passBtn, isSubmitting && { opacity: 0.5 }], onPress: handlePass, disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles5.passBtnText }, isSubmitting ? displayedAssignment.isVerification ? "Verifying..." : "Passing..." : displayedAssignment.isVerification ? "\u2713 Fix Verified" : "Pass"))), /* @__PURE__ */ React4.createElement(Modal, { visible: showSkipModal, transparent: true, animationType: "fade" }, /* @__PURE__ */ React4.createElement(View4, { style: styles5.modalOverlay }, /* @__PURE__ */ React4.createElement(View4, { style: styles5.modalContent }, /* @__PURE__ */ React4.createElement(Text2, { style: styles5.modalTitle }, "Skip this test?"), /* @__PURE__ */ React4.createElement(Text2, { style: styles5.modalSubtitle }, "Select a reason:"), [
|
|
15955
|
+
))) : /* @__PURE__ */ React4.createElement(View4, { style: styles5.actionButtons }, /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles5.actionBtn, styles5.failBtn, isSubmitting && { opacity: 0.5 }], onPress: handleFail, disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles5.failBtnText }, isSubmitting ? displayedAssignment.isVerification ? "Reporting..." : "Failing..." : displayedAssignment.isVerification ? "\u2717 Still Broken" : "Fail")), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles5.actionBtn, styles5.skipBtn, isSubmitting && { opacity: 0.5 }], onPress: () => setShowSkipModal(true), disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles5.skipBtnText }, "Skip")), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles5.actionBtn, styles5.passBtn, isSubmitting && { opacity: 0.5 }], onPress: handlePass, disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles5.passBtnText }, isSubmitting ? displayedAssignment.isVerification ? "Verifying..." : "Passing..." : displayedAssignment.isVerification ? "\u2713 Fix Verified" : "Pass"))), /* @__PURE__ */ React4.createElement(Modal, { visible: showSkipModal, transparent: true, animationType: "fade" }, /* @__PURE__ */ React4.createElement(View4, { style: styles5.modalOverlay }, /* @__PURE__ */ React4.createElement(View4, { style: styles5.modalContent }, /* @__PURE__ */ React4.createElement(Text2, { style: styles5.modalTitle }, "Skip this test?"), /* @__PURE__ */ React4.createElement(Text2, { style: styles5.modalSubtitle }, "Select a reason:"), [
|
|
15801
15956
|
{ reason: "blocked", label: "\u{1F6AB} Blocked by a bug" },
|
|
15802
15957
|
{ reason: "not_ready", label: "\u{1F6A7} Feature not ready" },
|
|
15803
15958
|
{ reason: "dependency", label: "\u{1F517} Needs another test first" },
|
|
@@ -15911,9 +16066,6 @@ function createStyles2() {
|
|
|
15911
16066
|
completedLabel: { fontSize: 13, fontWeight: "600", color: colors.textSecondary },
|
|
15912
16067
|
reopenBtn: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.blue },
|
|
15913
16068
|
reopenBtnText: { fontSize: 14, fontWeight: "600", color: colors.blue },
|
|
15914
|
-
// Start Test button
|
|
15915
|
-
startBtn: { paddingVertical: 16, borderRadius: 12, alignItems: "center", backgroundColor: colors.blueSurface, borderWidth: 1, borderColor: colors.blueAccent, marginTop: 8 },
|
|
15916
|
-
startBtnText: { fontSize: 16, fontWeight: "600", color: colors.blueText },
|
|
15917
16069
|
// Action buttons
|
|
15918
16070
|
actionButtons: { flexDirection: "row", gap: 10, marginTop: 8 },
|
|
15919
16071
|
actionBtn: { flex: 1, paddingVertical: 14, borderRadius: 12, alignItems: "center" },
|
|
@@ -16026,17 +16178,23 @@ function TestListScreen({ nav }) {
|
|
|
16026
16178
|
const keyMatch = a.testCase.testKey.toLowerCase().includes(q);
|
|
16027
16179
|
if (!titleMatch && !keyMatch) return false;
|
|
16028
16180
|
}
|
|
16029
|
-
if (filter === "
|
|
16030
|
-
|
|
16031
|
-
|
|
16181
|
+
if (filter === "todo") {
|
|
16182
|
+
return !a.isVerification && (a.status === "pending" || a.status === "in_progress" || a.status === "failed");
|
|
16183
|
+
}
|
|
16184
|
+
if (filter === "retest") {
|
|
16185
|
+
return !!a.isVerification && (a.status === "pending" || a.status === "in_progress");
|
|
16186
|
+
}
|
|
16187
|
+
if (filter === "done") {
|
|
16188
|
+
return a.status === "passed" || a.status === "skipped" || a.status === "blocked";
|
|
16189
|
+
}
|
|
16032
16190
|
return true;
|
|
16033
16191
|
}, [platformFilter, roleFilter, trackFilter, searchQuery, filter]);
|
|
16034
16192
|
if (isLoading) return /* @__PURE__ */ React5.createElement(TestListScreenSkeleton, null);
|
|
16035
16193
|
return /* @__PURE__ */ React5.createElement(View5, null, /* @__PURE__ */ React5.createElement(View5, { style: styles5.filterBar }, [
|
|
16036
16194
|
{ key: "all", label: "All", count: assignments.length },
|
|
16037
|
-
{ key: "
|
|
16038
|
-
{ key: "
|
|
16039
|
-
{ key: "
|
|
16195
|
+
{ key: "todo", label: "To Do", count: assignments.filter((a) => !a.isVerification && (a.status === "pending" || a.status === "in_progress" || a.status === "failed")).length },
|
|
16196
|
+
{ key: "retest", label: "Retest", count: assignments.filter((a) => !!a.isVerification && (a.status === "pending" || a.status === "in_progress")).length },
|
|
16197
|
+
{ key: "done", label: "Done", count: assignments.filter((a) => a.status === "passed" || a.status === "skipped" || a.status === "blocked").length }
|
|
16040
16198
|
].map((f) => /* @__PURE__ */ React5.createElement(TouchableOpacity3, { key: f.key, style: [styles5.filterBtn, filter === f.key && styles5.filterBtnActive], onPress: () => setFilter(f.key) }, /* @__PURE__ */ React5.createElement(Text3, { style: [styles5.filterBtnText, filter === f.key && styles5.filterBtnTextActive] }, f.label, " (", f.count, ")")))), availableRoles.length >= 2 && /* @__PURE__ */ React5.createElement(View5, { style: styles5.roleSection }, /* @__PURE__ */ React5.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles5.roleBar }, /* @__PURE__ */ React5.createElement(
|
|
16041
16199
|
TouchableOpacity3,
|
|
16042
16200
|
{
|
|
@@ -16139,7 +16297,13 @@ function TestListScreen({ nav }) {
|
|
|
16139
16297
|
onPress: () => nav.push({ name: "TEST_DETAIL", testId: assignment.id })
|
|
16140
16298
|
},
|
|
16141
16299
|
/* @__PURE__ */ React5.createElement(Text3, { style: styles5.testBadge }, badge.icon),
|
|
16142
|
-
/* @__PURE__ */ React5.createElement(View5, { style: styles5.testInfo }, /* @__PURE__ */ React5.createElement(Text3, { style: styles5.testTitle, numberOfLines: 1 }, assignment.testCase.title), /* @__PURE__ */ React5.createElement(View5, { style: styles5.testMetaRow }, assignment.isVerification && /* @__PURE__ */ React5.createElement(View5, { style:
|
|
16300
|
+
/* @__PURE__ */ React5.createElement(View5, { style: styles5.testInfo }, /* @__PURE__ */ React5.createElement(Text3, { style: styles5.testTitle, numberOfLines: 1 }, assignment.testCase.title), /* @__PURE__ */ React5.createElement(View5, { style: styles5.testMetaRow }, assignment.isVerification && /* @__PURE__ */ React5.createElement(View5, { style: [
|
|
16301
|
+
styles5.retestTag,
|
|
16302
|
+
assignment.status === "passed" && { backgroundColor: colors.greenDark, borderColor: colors.greenBorder }
|
|
16303
|
+
] }, /* @__PURE__ */ React5.createElement(Text3, { style: [
|
|
16304
|
+
styles5.retestTagText,
|
|
16305
|
+
assignment.status === "passed" && { color: colors.greenLight }
|
|
16306
|
+
] }, assignment.status === "passed" ? "Verified" : "Retest")), /* @__PURE__ */ React5.createElement(Text3, { style: styles5.testMeta }, assignment.testCase.testKey, " \xB7 ", assignment.testCase.priority), assignment.testCase.role && /* @__PURE__ */ React5.createElement(View5, { style: styles5.roleBadgeRow }, /* @__PURE__ */ React5.createElement(Text3, { style: styles5.testMeta }, " \xB7 "), /* @__PURE__ */ React5.createElement(View5, { style: [styles5.roleBadgeDot, { backgroundColor: assignment.testCase.role.color }] }), /* @__PURE__ */ React5.createElement(Text3, { style: [styles5.testMeta, { color: assignment.testCase.role.color, fontWeight: "500" }] }, assignment.testCase.role.name)))),
|
|
16143
16307
|
/* @__PURE__ */ React5.createElement(View5, { style: [
|
|
16144
16308
|
styles5.statusPill,
|
|
16145
16309
|
{
|
|
@@ -17653,11 +17817,17 @@ function createStyles11() {
|
|
|
17653
17817
|
}
|
|
17654
17818
|
|
|
17655
17819
|
// src/widget/screens/IssueDetailScreen.tsx
|
|
17656
|
-
import React17, { useMemo as useMemo12 } from "react";
|
|
17657
|
-
import { View as View17, Text as Text15, Image as Image3, StyleSheet as StyleSheet17, Linking as Linking4, TouchableOpacity as TouchableOpacity14 } from "react-native";
|
|
17820
|
+
import React17, { useMemo as useMemo12, useState as useState13, useCallback as useCallback5 } from "react";
|
|
17821
|
+
import { View as View17, Text as Text15, Image as Image3, StyleSheet as StyleSheet17, Linking as Linking4, TouchableOpacity as TouchableOpacity14, TextInput as TextInput8, ActivityIndicator as ActivityIndicator2 } from "react-native";
|
|
17822
|
+
var DONE_STATUSES = ["verified", "resolved", "closed", "reviewed"];
|
|
17658
17823
|
function IssueDetailScreen({ nav, issue }) {
|
|
17659
|
-
const { dashboardUrl, widgetColorScheme } = useBugBear();
|
|
17824
|
+
const { dashboardUrl, widgetColorScheme, reopenReport } = useBugBear();
|
|
17660
17825
|
const styles5 = useMemo12(() => createStyles12(), [widgetColorScheme]);
|
|
17826
|
+
const [showReopenForm, setShowReopenForm] = useState13(false);
|
|
17827
|
+
const [reopenReason, setReopenReason] = useState13("");
|
|
17828
|
+
const [isSubmitting, setIsSubmitting] = useState13(false);
|
|
17829
|
+
const [reopenError, setReopenError] = useState13(null);
|
|
17830
|
+
const [wasReopened, setWasReopened] = useState13(false);
|
|
17661
17831
|
const STATUS_LABELS = useMemo12(() => ({
|
|
17662
17832
|
new: { label: "New", bg: colors.blueDark, color: colors.blueLight },
|
|
17663
17833
|
triaging: { label: "Triaging", bg: colors.blueDark, color: colors.blueLight },
|
|
@@ -17680,7 +17850,68 @@ function IssueDetailScreen({ nav, issue }) {
|
|
|
17680
17850
|
}), [widgetColorScheme]);
|
|
17681
17851
|
const statusConfig = STATUS_LABELS[issue.status] || { label: issue.status, bg: colors.card, color: colors.textSecondary };
|
|
17682
17852
|
const severityConfig = issue.severity ? SEVERITY_CONFIG[issue.severity] : null;
|
|
17683
|
-
|
|
17853
|
+
const isDone = DONE_STATUSES.includes(issue.status);
|
|
17854
|
+
const handleReopen = useCallback5(async () => {
|
|
17855
|
+
if (!reopenReason.trim()) return;
|
|
17856
|
+
setIsSubmitting(true);
|
|
17857
|
+
setReopenError(null);
|
|
17858
|
+
const result = await reopenReport(issue.id, reopenReason.trim());
|
|
17859
|
+
setIsSubmitting(false);
|
|
17860
|
+
if (result.success) {
|
|
17861
|
+
setWasReopened(true);
|
|
17862
|
+
setShowReopenForm(false);
|
|
17863
|
+
} else {
|
|
17864
|
+
setReopenError(result.error || "Failed to reopen");
|
|
17865
|
+
}
|
|
17866
|
+
}, [reopenReason, reopenReport, issue.id]);
|
|
17867
|
+
return /* @__PURE__ */ React17.createElement(View17, null, /* @__PURE__ */ React17.createElement(View17, { style: styles5.badgeRow }, /* @__PURE__ */ React17.createElement(View17, { style: [styles5.badge, { backgroundColor: wasReopened ? colors.yellowDark : statusConfig.bg }] }, /* @__PURE__ */ React17.createElement(Text15, { style: [styles5.badgeText, { color: wasReopened ? colors.yellowLight : statusConfig.color }] }, wasReopened ? "Reopened" : statusConfig.label)), severityConfig && /* @__PURE__ */ React17.createElement(View17, { style: [styles5.badge, { backgroundColor: severityConfig.bg }] }, /* @__PURE__ */ React17.createElement(Text15, { style: [styles5.badgeText, { color: severityConfig.color }] }, severityConfig.label))), /* @__PURE__ */ React17.createElement(Text15, { style: styles5.title }, issue.title), issue.route && /* @__PURE__ */ React17.createElement(Text15, { style: styles5.route }, issue.route), issue.description && /* @__PURE__ */ React17.createElement(View17, { style: styles5.descriptionCard }, /* @__PURE__ */ React17.createElement(Text15, { style: styles5.descriptionText }, issue.description)), wasReopened && /* @__PURE__ */ React17.createElement(View17, { style: styles5.reopenedCard }, /* @__PURE__ */ React17.createElement(View17, { style: styles5.reopenedRow }, /* @__PURE__ */ React17.createElement(Text15, { style: styles5.reopenedIcon }, "\u{1F504}"), /* @__PURE__ */ React17.createElement(Text15, { style: styles5.reopenedText }, "Issue reopened \u2014 your team has been notified"))), issue.verifiedByName && !wasReopened && /* @__PURE__ */ React17.createElement(View17, { style: styles5.verifiedCard }, /* @__PURE__ */ React17.createElement(View17, { style: styles5.verifiedHeader }, /* @__PURE__ */ React17.createElement(Text15, { style: styles5.verifiedIcon }, "\u2705"), /* @__PURE__ */ React17.createElement(Text15, { style: styles5.verifiedTitle }, "Retesting Proof")), /* @__PURE__ */ React17.createElement(Text15, { style: styles5.verifiedBody }, "Verified by ", issue.verifiedByName, issue.verifiedAt && ` on ${new Date(issue.verifiedAt).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" })}`)), issue.originalBugTitle && /* @__PURE__ */ React17.createElement(View17, { style: styles5.originalBugCard }, /* @__PURE__ */ React17.createElement(View17, { style: styles5.originalBugHeader }, /* @__PURE__ */ React17.createElement(Text15, { style: styles5.originalBugIcon }, "\u{1F504}"), /* @__PURE__ */ React17.createElement(Text15, { style: styles5.originalBugTitleText }, "Original Bug")), /* @__PURE__ */ React17.createElement(Text15, { style: styles5.originalBugBody }, "Retest of: ", issue.originalBugTitle)), isDone && !wasReopened && !showReopenForm && /* @__PURE__ */ React17.createElement(
|
|
17868
|
+
TouchableOpacity14,
|
|
17869
|
+
{
|
|
17870
|
+
style: styles5.reopenButton,
|
|
17871
|
+
onPress: () => setShowReopenForm(true),
|
|
17872
|
+
activeOpacity: 0.7
|
|
17873
|
+
},
|
|
17874
|
+
/* @__PURE__ */ React17.createElement(Text15, { style: styles5.reopenButtonText }, "\u{1F504}", " Not Fixed \u2014 Reopen Issue")
|
|
17875
|
+
), 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
|
+
TextInput8,
|
|
17877
|
+
{
|
|
17878
|
+
value: reopenReason,
|
|
17879
|
+
onChangeText: setReopenReason,
|
|
17880
|
+
placeholder: "Describe what you're still seeing...",
|
|
17881
|
+
placeholderTextColor: colors.textMuted,
|
|
17882
|
+
style: styles5.reopenInput,
|
|
17883
|
+
multiline: true,
|
|
17884
|
+
autoFocus: true
|
|
17885
|
+
}
|
|
17886
|
+
), reopenError && /* @__PURE__ */ React17.createElement(Text15, { style: styles5.reopenErrorText }, reopenError), /* @__PURE__ */ React17.createElement(View17, { style: styles5.reopenActions }, /* @__PURE__ */ React17.createElement(
|
|
17887
|
+
TouchableOpacity14,
|
|
17888
|
+
{
|
|
17889
|
+
style: [
|
|
17890
|
+
styles5.reopenSubmitButton,
|
|
17891
|
+
(!reopenReason.trim() || isSubmitting) && styles5.reopenSubmitDisabled
|
|
17892
|
+
],
|
|
17893
|
+
onPress: handleReopen,
|
|
17894
|
+
disabled: isSubmitting || !reopenReason.trim(),
|
|
17895
|
+
activeOpacity: 0.7
|
|
17896
|
+
},
|
|
17897
|
+
isSubmitting ? /* @__PURE__ */ React17.createElement(ActivityIndicator2, { size: "small", color: "#fff" }) : /* @__PURE__ */ React17.createElement(Text15, { style: [
|
|
17898
|
+
styles5.reopenSubmitText,
|
|
17899
|
+
!reopenReason.trim() && styles5.reopenSubmitTextDisabled
|
|
17900
|
+
] }, "Reopen Issue")
|
|
17901
|
+
), /* @__PURE__ */ React17.createElement(
|
|
17902
|
+
TouchableOpacity14,
|
|
17903
|
+
{
|
|
17904
|
+
style: styles5.reopenCancelButton,
|
|
17905
|
+
onPress: () => {
|
|
17906
|
+
setShowReopenForm(false);
|
|
17907
|
+
setReopenReason("");
|
|
17908
|
+
setReopenError(null);
|
|
17909
|
+
},
|
|
17910
|
+
disabled: isSubmitting,
|
|
17911
|
+
activeOpacity: 0.7
|
|
17912
|
+
},
|
|
17913
|
+
/* @__PURE__ */ React17.createElement(Text15, { style: styles5.reopenCancelText }, "Cancel")
|
|
17914
|
+
))), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ React17.createElement(View17, { style: styles5.screenshotSection }, /* @__PURE__ */ React17.createElement(Text15, { style: styles5.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ React17.createElement(View17, { style: styles5.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ React17.createElement(TouchableOpacity14, { key: i, onPress: () => Linking4.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ React17.createElement(Image3, { source: { uri: url }, style: styles5.screenshotThumb }))))), /* @__PURE__ */ React17.createElement(View17, { style: styles5.metaSection }, issue.reporterName && /* @__PURE__ */ React17.createElement(Text15, { style: styles5.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ React17.createElement(Text15, { style: styles5.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))), dashboardUrl && /* @__PURE__ */ React17.createElement(
|
|
17684
17915
|
TouchableOpacity14,
|
|
17685
17916
|
{
|
|
17686
17917
|
style: styles5.dashboardLink,
|
|
@@ -17732,6 +17963,28 @@ function createStyles12() {
|
|
|
17732
17963
|
color: colors.textSecondary,
|
|
17733
17964
|
lineHeight: 19
|
|
17734
17965
|
},
|
|
17966
|
+
reopenedCard: {
|
|
17967
|
+
backgroundColor: colors.yellowDark,
|
|
17968
|
+
borderWidth: 1,
|
|
17969
|
+
borderColor: colors.yellowBorder,
|
|
17970
|
+
borderRadius: 8,
|
|
17971
|
+
padding: 12,
|
|
17972
|
+
marginBottom: 12
|
|
17973
|
+
},
|
|
17974
|
+
reopenedRow: {
|
|
17975
|
+
flexDirection: "row",
|
|
17976
|
+
alignItems: "center",
|
|
17977
|
+
gap: 8
|
|
17978
|
+
},
|
|
17979
|
+
reopenedIcon: {
|
|
17980
|
+
fontSize: 14
|
|
17981
|
+
},
|
|
17982
|
+
reopenedText: {
|
|
17983
|
+
fontSize: 13,
|
|
17984
|
+
fontWeight: "600",
|
|
17985
|
+
color: colors.yellowLight,
|
|
17986
|
+
flex: 1
|
|
17987
|
+
},
|
|
17735
17988
|
verifiedCard: {
|
|
17736
17989
|
backgroundColor: colors.greenDark,
|
|
17737
17990
|
borderWidth: 1,
|
|
@@ -17775,7 +18028,7 @@ function createStyles12() {
|
|
|
17775
18028
|
originalBugIcon: {
|
|
17776
18029
|
fontSize: 16
|
|
17777
18030
|
},
|
|
17778
|
-
|
|
18031
|
+
originalBugTitleText: {
|
|
17779
18032
|
fontSize: 13,
|
|
17780
18033
|
fontWeight: "600",
|
|
17781
18034
|
color: colors.yellowLight
|
|
@@ -17784,6 +18037,89 @@ function createStyles12() {
|
|
|
17784
18037
|
fontSize: 12,
|
|
17785
18038
|
color: colors.yellowSubtle
|
|
17786
18039
|
},
|
|
18040
|
+
reopenButton: {
|
|
18041
|
+
borderWidth: 1,
|
|
18042
|
+
borderColor: colors.orange,
|
|
18043
|
+
borderRadius: 8,
|
|
18044
|
+
paddingVertical: 10,
|
|
18045
|
+
paddingHorizontal: 16,
|
|
18046
|
+
alignItems: "center",
|
|
18047
|
+
marginBottom: 12
|
|
18048
|
+
},
|
|
18049
|
+
reopenButtonText: {
|
|
18050
|
+
fontSize: 13,
|
|
18051
|
+
fontWeight: "600",
|
|
18052
|
+
color: colors.orange
|
|
18053
|
+
},
|
|
18054
|
+
reopenForm: {
|
|
18055
|
+
backgroundColor: colors.card,
|
|
18056
|
+
borderWidth: 1,
|
|
18057
|
+
borderColor: colors.orange,
|
|
18058
|
+
borderRadius: 8,
|
|
18059
|
+
padding: 12,
|
|
18060
|
+
marginBottom: 12
|
|
18061
|
+
},
|
|
18062
|
+
reopenFormTitle: {
|
|
18063
|
+
fontSize: 13,
|
|
18064
|
+
fontWeight: "600",
|
|
18065
|
+
color: colors.orange,
|
|
18066
|
+
marginBottom: 8
|
|
18067
|
+
},
|
|
18068
|
+
reopenInput: {
|
|
18069
|
+
backgroundColor: colors.bg,
|
|
18070
|
+
borderWidth: 1,
|
|
18071
|
+
borderColor: colors.border,
|
|
18072
|
+
borderRadius: 6,
|
|
18073
|
+
padding: 8,
|
|
18074
|
+
color: colors.textPrimary,
|
|
18075
|
+
fontSize: 13,
|
|
18076
|
+
lineHeight: 18,
|
|
18077
|
+
minHeight: 60,
|
|
18078
|
+
textAlignVertical: "top"
|
|
18079
|
+
},
|
|
18080
|
+
reopenErrorText: {
|
|
18081
|
+
fontSize: 12,
|
|
18082
|
+
color: colors.red,
|
|
18083
|
+
marginTop: 6
|
|
18084
|
+
},
|
|
18085
|
+
reopenActions: {
|
|
18086
|
+
flexDirection: "row",
|
|
18087
|
+
gap: 8,
|
|
18088
|
+
marginTop: 8
|
|
18089
|
+
},
|
|
18090
|
+
reopenSubmitButton: {
|
|
18091
|
+
flex: 1,
|
|
18092
|
+
backgroundColor: colors.orange,
|
|
18093
|
+
borderRadius: 6,
|
|
18094
|
+
paddingVertical: 8,
|
|
18095
|
+
paddingHorizontal: 12,
|
|
18096
|
+
alignItems: "center",
|
|
18097
|
+
justifyContent: "center"
|
|
18098
|
+
},
|
|
18099
|
+
reopenSubmitDisabled: {
|
|
18100
|
+
backgroundColor: colors.card,
|
|
18101
|
+
opacity: 0.7
|
|
18102
|
+
},
|
|
18103
|
+
reopenSubmitText: {
|
|
18104
|
+
fontSize: 13,
|
|
18105
|
+
fontWeight: "600",
|
|
18106
|
+
color: "#fff"
|
|
18107
|
+
},
|
|
18108
|
+
reopenSubmitTextDisabled: {
|
|
18109
|
+
color: colors.textMuted
|
|
18110
|
+
},
|
|
18111
|
+
reopenCancelButton: {
|
|
18112
|
+
borderWidth: 1,
|
|
18113
|
+
borderColor: colors.border,
|
|
18114
|
+
borderRadius: 6,
|
|
18115
|
+
paddingVertical: 8,
|
|
18116
|
+
paddingHorizontal: 12,
|
|
18117
|
+
alignItems: "center"
|
|
18118
|
+
},
|
|
18119
|
+
reopenCancelText: {
|
|
18120
|
+
fontSize: 13,
|
|
18121
|
+
color: colors.textSecondary
|
|
18122
|
+
},
|
|
17787
18123
|
screenshotSection: {
|
|
17788
18124
|
marginBottom: 12
|
|
17789
18125
|
},
|
|
@@ -17835,13 +18171,13 @@ function createStyles12() {
|
|
|
17835
18171
|
}
|
|
17836
18172
|
|
|
17837
18173
|
// src/widget/screens/SessionStartScreen.tsx
|
|
17838
|
-
import React18, { useState as
|
|
17839
|
-
import { View as View18, Text as Text16, TextInput as
|
|
18174
|
+
import React18, { useState as useState14, useMemo as useMemo13 } from "react";
|
|
18175
|
+
import { View as View18, Text as Text16, TextInput as TextInput9, TouchableOpacity as TouchableOpacity15, StyleSheet as StyleSheet18, Keyboard as Keyboard2 } from "react-native";
|
|
17840
18176
|
function SessionStartScreen({ nav }) {
|
|
17841
18177
|
const { startSession, assignments, widgetColorScheme } = useBugBear();
|
|
17842
18178
|
const styles5 = useMemo13(() => createStyles13(), [widgetColorScheme]);
|
|
17843
|
-
const [focusArea, setFocusArea] =
|
|
17844
|
-
const [isStarting, setIsStarting] =
|
|
18179
|
+
const [focusArea, setFocusArea] = useState14("");
|
|
18180
|
+
const [isStarting, setIsStarting] = useState14(false);
|
|
17845
18181
|
const trackNames = Array.from(new Set(
|
|
17846
18182
|
assignments.filter((a) => a.testCase.track?.name).map((a) => a.testCase.track.name)
|
|
17847
18183
|
)).slice(0, 6);
|
|
@@ -17861,7 +18197,7 @@ function SessionStartScreen({ nav }) {
|
|
|
17861
18197
|
}
|
|
17862
18198
|
};
|
|
17863
18199
|
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(
|
|
17864
|
-
|
|
18200
|
+
TextInput9,
|
|
17865
18201
|
{
|
|
17866
18202
|
value: focusArea,
|
|
17867
18203
|
onChangeText: setFocusArea,
|
|
@@ -17979,7 +18315,7 @@ function createStyles13() {
|
|
|
17979
18315
|
}
|
|
17980
18316
|
|
|
17981
18317
|
// src/widget/screens/SessionActiveScreen.tsx
|
|
17982
|
-
import React19, { useState as
|
|
18318
|
+
import React19, { useState as useState15, useEffect as useEffect11, useRef as useRef5, useMemo as useMemo14 } from "react";
|
|
17983
18319
|
import { View as View19, Text as Text17, TouchableOpacity as TouchableOpacity16, StyleSheet as StyleSheet19 } from "react-native";
|
|
17984
18320
|
function SessionActiveScreen({ nav }) {
|
|
17985
18321
|
const { activeSession, sessionFindings, endSession, refreshSession, widgetColorScheme } = useBugBear();
|
|
@@ -17990,8 +18326,8 @@ function SessionActiveScreen({ nav }) {
|
|
|
17990
18326
|
suggestion: { icon: "\u{1F4A1}", label: "Suggestion", color: colors.blue },
|
|
17991
18327
|
question: { icon: "\u2753", label: "Question", color: colors.violet }
|
|
17992
18328
|
}), [widgetColorScheme]);
|
|
17993
|
-
const [isEnding, setIsEnding] =
|
|
17994
|
-
const [elapsed, setElapsed] =
|
|
18329
|
+
const [isEnding, setIsEnding] = useState15(false);
|
|
18330
|
+
const [elapsed, setElapsed] = useState15(0);
|
|
17995
18331
|
const timerRef = useRef5(null);
|
|
17996
18332
|
useEffect11(() => {
|
|
17997
18333
|
refreshSession();
|
|
@@ -18216,8 +18552,8 @@ function createStyles14() {
|
|
|
18216
18552
|
}
|
|
18217
18553
|
|
|
18218
18554
|
// src/widget/screens/SessionFindingScreen.tsx
|
|
18219
|
-
import React20, { useState as
|
|
18220
|
-
import { View as View20, Text as Text18, TextInput as
|
|
18555
|
+
import React20, { useState as useState16, useMemo as useMemo15 } from "react";
|
|
18556
|
+
import { View as View20, Text as Text18, TextInput as TextInput10, TouchableOpacity as TouchableOpacity17, StyleSheet as StyleSheet20, Keyboard as Keyboard3 } from "react-native";
|
|
18221
18557
|
var FINDING_TYPES = [
|
|
18222
18558
|
{ value: "bug", icon: "\u{1F41B}", label: "Bug" },
|
|
18223
18559
|
{ value: "concern", icon: "\u26A0\uFE0F", label: "Concern" },
|
|
@@ -18234,11 +18570,11 @@ function SessionFindingScreen({ nav }) {
|
|
|
18234
18570
|
{ value: "low", label: "Low", color: colors.textMuted },
|
|
18235
18571
|
{ value: "observation", label: "Note", color: colors.textDim }
|
|
18236
18572
|
], [widgetColorScheme]);
|
|
18237
|
-
const [type, setType] =
|
|
18238
|
-
const [severity, setSeverity] =
|
|
18239
|
-
const [title, setTitle] =
|
|
18240
|
-
const [description, setDescription] =
|
|
18241
|
-
const [isSubmitting, setIsSubmitting] =
|
|
18573
|
+
const [type, setType] = useState16("bug");
|
|
18574
|
+
const [severity, setSeverity] = useState16("medium");
|
|
18575
|
+
const [title, setTitle] = useState16("");
|
|
18576
|
+
const [description, setDescription] = useState16("");
|
|
18577
|
+
const [isSubmitting, setIsSubmitting] = useState16(false);
|
|
18242
18578
|
const handleSubmit = async () => {
|
|
18243
18579
|
if (!title.trim() || isSubmitting) return;
|
|
18244
18580
|
Keyboard3.dismiss();
|
|
@@ -18281,7 +18617,7 @@ function SessionFindingScreen({ nav }) {
|
|
|
18281
18617
|
},
|
|
18282
18618
|
/* @__PURE__ */ React20.createElement(Text18, { style: [styles5.severityText, severity === s2.value && { color: s2.color }] }, s2.label)
|
|
18283
18619
|
)))), /* @__PURE__ */ React20.createElement(View20, { style: styles5.inputSection }, /* @__PURE__ */ React20.createElement(
|
|
18284
|
-
|
|
18620
|
+
TextInput10,
|
|
18285
18621
|
{
|
|
18286
18622
|
value: title,
|
|
18287
18623
|
onChangeText: setTitle,
|
|
@@ -18291,7 +18627,7 @@ function SessionFindingScreen({ nav }) {
|
|
|
18291
18627
|
returnKeyType: "next"
|
|
18292
18628
|
}
|
|
18293
18629
|
)), /* @__PURE__ */ React20.createElement(View20, { style: styles5.inputSection }, /* @__PURE__ */ React20.createElement(
|
|
18294
|
-
|
|
18630
|
+
TextInput10,
|
|
18295
18631
|
{
|
|
18296
18632
|
value: description,
|
|
18297
18633
|
onChangeText: setDescription,
|
|
@@ -18436,7 +18772,7 @@ function BugBearButton({
|
|
|
18436
18772
|
}) {
|
|
18437
18773
|
const { shouldShowWidget, testerInfo, isLoading, unreadCount, assignments, widgetMode, widgetColorScheme } = useBugBear();
|
|
18438
18774
|
const { currentScreen, canGoBack, push, pop, replace, reset } = useNavigation();
|
|
18439
|
-
const [modalVisible, setModalVisible] =
|
|
18775
|
+
const [modalVisible, setModalVisible] = useState17(false);
|
|
18440
18776
|
const styles5 = useMemo16(() => createStyles16(), [widgetColorScheme]);
|
|
18441
18777
|
const screenCaptureRef = useRef6(null);
|
|
18442
18778
|
const openModal = () => {
|
|
@@ -18651,7 +18987,7 @@ function BugBearButton({
|
|
|
18651
18987
|
keyboardShouldPersistTaps: "handled",
|
|
18652
18988
|
showsVerticalScrollIndicator: false
|
|
18653
18989
|
},
|
|
18654
|
-
isLoading ? /* @__PURE__ */ React21.createElement(View21, { style: styles5.loadingContainer }, /* @__PURE__ */ React21.createElement(
|
|
18990
|
+
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()
|
|
18655
18991
|
))
|
|
18656
18992
|
)
|
|
18657
18993
|
));
|