@bbearai/react-native 0.5.7 → 0.6.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.mjs CHANGED
@@ -9,14 +9,14 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
9
9
  import React, { createContext, useContext, useEffect, useState, useCallback, useRef } from "react";
10
10
 
11
11
  // ../../node_modules/tslib/tslib.es6.mjs
12
- function __rest(s, e) {
12
+ function __rest(s2, e) {
13
13
  var t = {};
14
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
15
- t[p] = s[p];
16
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
17
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
18
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
19
- t[p[i]] = s[p[i]];
14
+ for (var p in s2) if (Object.prototype.hasOwnProperty.call(s2, p) && e.indexOf(p) < 0)
15
+ t[p] = s2[p];
16
+ if (s2 != null && typeof Object.getOwnPropertySymbols === "function")
17
+ for (var i = 0, p = Object.getOwnPropertySymbols(s2); i < p.length; i++) {
18
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s2, p[i]))
19
+ t[p[i]] = s2[p[i]];
20
20
  }
21
21
  return t;
22
22
  }
@@ -831,9 +831,9 @@ var PostgrestFilterBuilder = class extends PostgrestTransformBuilder {
831
831
  * @param values - The values array to filter with
832
832
  */
833
833
  in(column, values) {
834
- const cleanedValues = Array.from(new Set(values)).map((s) => {
835
- if (typeof s === "string" && PostgrestReservedCharsRegexp.test(s)) return `"${s}"`;
836
- else return `${s}`;
834
+ const cleanedValues = Array.from(new Set(values)).map((s2) => {
835
+ if (typeof s2 === "string" && PostgrestReservedCharsRegexp.test(s2)) return `"${s2}"`;
836
+ else return `${s2}`;
837
837
  }).join(",");
838
838
  this.url.searchParams.append(column, `in.(${cleanedValues})`);
839
839
  return this;
@@ -845,9 +845,9 @@ var PostgrestFilterBuilder = class extends PostgrestTransformBuilder {
845
845
  * @param values - The values array to filter with
846
846
  */
847
847
  notIn(column, values) {
848
- const cleanedValues = Array.from(new Set(values)).map((s) => {
849
- if (typeof s === "string" && PostgrestReservedCharsRegexp.test(s)) return `"${s}"`;
850
- else return `${s}`;
848
+ const cleanedValues = Array.from(new Set(values)).map((s2) => {
849
+ if (typeof s2 === "string" && PostgrestReservedCharsRegexp.test(s2)) return `"${s2}"`;
850
+ else return `${s2}`;
851
851
  }).join(",");
852
852
  this.url.searchParams.append(column, `not.in.(${cleanedValues})`);
853
853
  return this;
@@ -11602,31 +11602,306 @@ function captureError(error, errorInfo) {
11602
11602
  componentStack: errorInfo?.componentStack
11603
11603
  };
11604
11604
  }
11605
+ var LocalStorageAdapter = class {
11606
+ constructor() {
11607
+ this.fallback = /* @__PURE__ */ new Map();
11608
+ }
11609
+ get isAvailable() {
11610
+ try {
11611
+ const key = "__bugbear_test__";
11612
+ localStorage.setItem(key, "1");
11613
+ localStorage.removeItem(key);
11614
+ return true;
11615
+ } catch {
11616
+ return false;
11617
+ }
11618
+ }
11619
+ async getItem(key) {
11620
+ if (this.isAvailable) return localStorage.getItem(key);
11621
+ return this.fallback.get(key) ?? null;
11622
+ }
11623
+ async setItem(key, value) {
11624
+ if (this.isAvailable) {
11625
+ localStorage.setItem(key, value);
11626
+ } else {
11627
+ this.fallback.set(key, value);
11628
+ }
11629
+ }
11630
+ async removeItem(key) {
11631
+ if (this.isAvailable) {
11632
+ localStorage.removeItem(key);
11633
+ } else {
11634
+ this.fallback.delete(key);
11635
+ }
11636
+ }
11637
+ };
11638
+ var OfflineQueue = class {
11639
+ constructor(config) {
11640
+ this.items = [];
11641
+ this.storageKey = "bugbear_offline_queue";
11642
+ this.flushing = false;
11643
+ this.handlers = /* @__PURE__ */ new Map();
11644
+ this.maxItems = config.maxItems ?? 50;
11645
+ this.maxRetries = config.maxRetries ?? 5;
11646
+ this.storage = config.storage ?? new LocalStorageAdapter();
11647
+ }
11648
+ // ── Flush handler registration ──────────────────────────────
11649
+ /** Register a handler that replays a queued operation. */
11650
+ registerHandler(type, handler) {
11651
+ this.handlers.set(type, handler);
11652
+ }
11653
+ // ── Change listener ─────────────────────────────────────────
11654
+ /** Subscribe to queue count changes (for UI badges). */
11655
+ onChange(callback) {
11656
+ this.listener = callback;
11657
+ }
11658
+ notify() {
11659
+ this.listener?.(this.items.length);
11660
+ }
11661
+ // ── Persistence ─────────────────────────────────────────────
11662
+ /** Load queue from persistent storage. Call once after construction. */
11663
+ async load() {
11664
+ try {
11665
+ const raw = await this.storage.getItem(this.storageKey);
11666
+ if (raw) {
11667
+ this.items = JSON.parse(raw);
11668
+ }
11669
+ } catch {
11670
+ this.items = [];
11671
+ }
11672
+ this.notify();
11673
+ }
11674
+ async save() {
11675
+ try {
11676
+ await this.storage.setItem(this.storageKey, JSON.stringify(this.items));
11677
+ } catch (err) {
11678
+ console.error("BugBear: Failed to persist offline queue", err);
11679
+ }
11680
+ this.notify();
11681
+ }
11682
+ // ── Enqueue ─────────────────────────────────────────────────
11683
+ /** Add a failed operation to the queue. Returns the item ID. */
11684
+ async enqueue(type, payload) {
11685
+ if (this.items.length >= this.maxItems) {
11686
+ this.items.shift();
11687
+ }
11688
+ const id = `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
11689
+ this.items.push({ id, type, payload, createdAt: Date.now(), retries: 0 });
11690
+ await this.save();
11691
+ return id;
11692
+ }
11693
+ // ── Accessors ───────────────────────────────────────────────
11694
+ /** Number of items waiting to be flushed. */
11695
+ get count() {
11696
+ return this.items.length;
11697
+ }
11698
+ /** Read-only snapshot of pending items. */
11699
+ get pending() {
11700
+ return [...this.items];
11701
+ }
11702
+ /** Whether a flush is currently in progress. */
11703
+ get isFlushing() {
11704
+ return this.flushing;
11705
+ }
11706
+ // ── Flush ───────────────────────────────────────────────────
11707
+ /**
11708
+ * Process all queued items in FIFO order.
11709
+ * Stops early if a network error is encountered (still offline).
11710
+ */
11711
+ async flush() {
11712
+ if (this.flushing || this.items.length === 0) {
11713
+ return { flushed: 0, failed: 0 };
11714
+ }
11715
+ this.flushing = true;
11716
+ let flushed = 0;
11717
+ let failed = 0;
11718
+ const snapshot = [...this.items];
11719
+ for (const item of snapshot) {
11720
+ const handler = this.handlers.get(item.type);
11721
+ if (!handler) {
11722
+ failed++;
11723
+ continue;
11724
+ }
11725
+ try {
11726
+ const result = await handler(item.payload);
11727
+ if (result.success) {
11728
+ this.items = this.items.filter((i) => i.id !== item.id);
11729
+ flushed++;
11730
+ } else if (isNetworkError(result.error)) {
11731
+ break;
11732
+ } else {
11733
+ const idx = this.items.findIndex((i) => i.id === item.id);
11734
+ if (idx !== -1) {
11735
+ this.items[idx].retries++;
11736
+ if (this.items[idx].retries >= this.maxRetries) {
11737
+ this.items.splice(idx, 1);
11738
+ }
11739
+ }
11740
+ failed++;
11741
+ }
11742
+ } catch {
11743
+ break;
11744
+ }
11745
+ }
11746
+ await this.save();
11747
+ this.flushing = false;
11748
+ return { flushed, failed };
11749
+ }
11750
+ // ── Clear ───────────────────────────────────────────────────
11751
+ /** Drop all queued items. */
11752
+ async clear() {
11753
+ this.items = [];
11754
+ await this.save();
11755
+ }
11756
+ };
11757
+ function isNetworkError(error) {
11758
+ if (!error) return false;
11759
+ const msg = error.toLowerCase();
11760
+ return msg.includes("failed to fetch") || msg.includes("networkerror") || msg.includes("network request failed") || msg.includes("timeout") || msg.includes("econnrefused") || msg.includes("enotfound") || msg.includes("load failed") || // Safari
11761
+ msg.includes("the internet connection appears to be offline") || msg.includes("a]server with the specified hostname could not be found");
11762
+ }
11605
11763
  var formatPgError = (e) => {
11606
11764
  if (!e || typeof e !== "object") return { raw: e };
11607
11765
  const { message, code, details, hint } = e;
11608
11766
  return { message, code, details, hint };
11609
11767
  };
11610
- var DEFAULT_SUPABASE_URL = "https://kyxgzjnqgvapvlnvqawz.supabase.co";
11611
- var getEnvVar = (key) => {
11612
- try {
11613
- if (typeof process !== "undefined" && process.env) {
11614
- return process.env[key];
11615
- }
11616
- } catch {
11617
- }
11618
- return void 0;
11619
- };
11620
- var HOSTED_BUGBEAR_ANON_KEY = getEnvVar("BUGBEAR_ANON_KEY") || getEnvVar("NEXT_PUBLIC_BUGBEAR_ANON_KEY") || "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imt5eGd6am5xZ3ZhcHZsbnZxYXd6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkyNjgwNDIsImV4cCI6MjA4NDg0NDA0Mn0.NUkAlCHLFjeRoisbmNUVoGb4R6uQ8xs5LAEIX1BWTwU";
11621
11768
  var BugBearClient = class {
11622
11769
  constructor(config) {
11623
11770
  this.navigationHistory = [];
11624
11771
  this.reportSubmitInFlight = false;
11772
+ this._queue = null;
11773
+ this.realtimeChannels = [];
11774
+ if (!config.supabaseUrl) {
11775
+ throw new Error("BugBear: supabaseUrl is required. Get it from your BugBear project settings.");
11776
+ }
11777
+ if (!config.supabaseAnonKey) {
11778
+ throw new Error("BugBear: supabaseAnonKey is required. Get it from your BugBear project settings.");
11779
+ }
11625
11780
  this.config = config;
11626
- this.supabase = createClient(
11627
- config.supabaseUrl || DEFAULT_SUPABASE_URL,
11628
- config.supabaseAnonKey || HOSTED_BUGBEAR_ANON_KEY
11629
- );
11781
+ this.supabase = createClient(config.supabaseUrl, config.supabaseAnonKey);
11782
+ if (config.offlineQueue?.enabled) {
11783
+ this._queue = new OfflineQueue({
11784
+ enabled: true,
11785
+ maxItems: config.offlineQueue.maxItems,
11786
+ maxRetries: config.offlineQueue.maxRetries
11787
+ });
11788
+ this.registerQueueHandlers();
11789
+ }
11790
+ }
11791
+ // ── Offline Queue ─────────────────────────────────────────
11792
+ /**
11793
+ * Access the offline queue (if enabled).
11794
+ * Use this to check queue.count, subscribe to changes, or trigger flush.
11795
+ */
11796
+ get queue() {
11797
+ return this._queue;
11798
+ }
11799
+ /**
11800
+ * Initialize the offline queue with a platform-specific storage adapter.
11801
+ * Must be called after construction for React Native (which supplies AsyncStorage).
11802
+ * Web callers can skip this — LocalStorageAdapter is the default.
11803
+ */
11804
+ async initQueue(storage) {
11805
+ if (!this._queue) return;
11806
+ if (storage) {
11807
+ this._queue = new OfflineQueue({
11808
+ enabled: true,
11809
+ maxItems: this.config.offlineQueue?.maxItems,
11810
+ maxRetries: this.config.offlineQueue?.maxRetries,
11811
+ storage
11812
+ });
11813
+ this.registerQueueHandlers();
11814
+ }
11815
+ await this._queue.load();
11816
+ }
11817
+ registerQueueHandlers() {
11818
+ if (!this._queue) return;
11819
+ this._queue.registerHandler("report", async (payload) => {
11820
+ const { error } = await this.supabase.from("reports").insert(payload).select("id").single();
11821
+ if (error) return { success: false, error: error.message };
11822
+ return { success: true };
11823
+ });
11824
+ this._queue.registerHandler("message", async (payload) => {
11825
+ const { error } = await this.supabase.from("discussion_messages").insert(payload);
11826
+ if (error) return { success: false, error: error.message };
11827
+ return { success: true };
11828
+ });
11829
+ this._queue.registerHandler("feedback", async (payload) => {
11830
+ const { error } = await this.supabase.from("test_feedback").insert(payload);
11831
+ if (error) return { success: false, error: error.message };
11832
+ return { success: true };
11833
+ });
11834
+ }
11835
+ // ── Realtime Subscriptions ─────────────────────────────────
11836
+ /** Whether realtime is enabled in config. */
11837
+ get realtimeEnabled() {
11838
+ return !!this.config.realtime?.enabled;
11839
+ }
11840
+ /**
11841
+ * Subscribe to postgres_changes on relevant tables.
11842
+ * Each callback fires when the corresponding table has changes —
11843
+ * the provider should call its refresh function in response.
11844
+ * Returns a cleanup function that unsubscribes all channels.
11845
+ */
11846
+ subscribeToChanges(callbacks) {
11847
+ this.unsubscribeAll();
11848
+ const projectId = this.config.projectId;
11849
+ const debounce = (fn, ms = 500) => {
11850
+ let timer;
11851
+ return () => {
11852
+ clearTimeout(timer);
11853
+ timer = setTimeout(fn, ms);
11854
+ };
11855
+ };
11856
+ if (callbacks.onAssignmentChange) {
11857
+ const debouncedCb = debounce(callbacks.onAssignmentChange);
11858
+ const channel = this.supabase.channel("bugbear-assignments").on("postgres_changes", {
11859
+ event: "*",
11860
+ schema: "public",
11861
+ table: "test_assignments",
11862
+ filter: `project_id=eq.${projectId}`
11863
+ }, debouncedCb).subscribe((status) => {
11864
+ if (status === "CHANNEL_ERROR") {
11865
+ console.warn("BugBear: Realtime subscription failed for test_assignments");
11866
+ }
11867
+ });
11868
+ this.realtimeChannels.push(channel);
11869
+ }
11870
+ if (callbacks.onMessageChange) {
11871
+ const debouncedCb = debounce(callbacks.onMessageChange);
11872
+ const channel = this.supabase.channel("bugbear-messages").on("postgres_changes", {
11873
+ event: "INSERT",
11874
+ schema: "public",
11875
+ table: "discussion_messages"
11876
+ }, debouncedCb).subscribe((status) => {
11877
+ if (status === "CHANNEL_ERROR") {
11878
+ console.warn("BugBear: Realtime subscription failed for discussion_messages");
11879
+ }
11880
+ });
11881
+ this.realtimeChannels.push(channel);
11882
+ }
11883
+ if (callbacks.onReportChange) {
11884
+ const debouncedCb = debounce(callbacks.onReportChange);
11885
+ const channel = this.supabase.channel("bugbear-reports").on("postgres_changes", {
11886
+ event: "UPDATE",
11887
+ schema: "public",
11888
+ table: "reports",
11889
+ filter: `project_id=eq.${projectId}`
11890
+ }, debouncedCb).subscribe((status) => {
11891
+ if (status === "CHANNEL_ERROR") {
11892
+ console.warn("BugBear: Realtime subscription failed for reports");
11893
+ }
11894
+ });
11895
+ this.realtimeChannels.push(channel);
11896
+ }
11897
+ return () => this.unsubscribeAll();
11898
+ }
11899
+ /** Remove all active Realtime channels. */
11900
+ unsubscribeAll() {
11901
+ for (const channel of this.realtimeChannels) {
11902
+ this.supabase.removeChannel(channel);
11903
+ }
11904
+ this.realtimeChannels = [];
11630
11905
  }
11631
11906
  /**
11632
11907
  * Track navigation for context.
@@ -11686,6 +11961,7 @@ var BugBearClient = class {
11686
11961
  return { success: false, error: "A report is already being submitted" };
11687
11962
  }
11688
11963
  this.reportSubmitInFlight = true;
11964
+ let fullReport;
11689
11965
  try {
11690
11966
  const validationError = this.validateReport(report);
11691
11967
  if (validationError) {
@@ -11702,7 +11978,7 @@ var BugBearClient = class {
11702
11978
  return { success: false, error: "User not authenticated" };
11703
11979
  }
11704
11980
  const testerInfo = await this.getTesterInfo();
11705
- const fullReport = {
11981
+ fullReport = {
11706
11982
  project_id: this.config.projectId,
11707
11983
  reporter_id: userInfo.id,
11708
11984
  // User ID from host app (required)
@@ -11729,6 +12005,10 @@ var BugBearClient = class {
11729
12005
  };
11730
12006
  const { data, error } = await this.supabase.from("reports").insert(fullReport).select("id").single();
11731
12007
  if (error) {
12008
+ if (this._queue && isNetworkError(error.message)) {
12009
+ await this._queue.enqueue("report", fullReport);
12010
+ return { success: false, queued: true, error: "Queued \u2014 will send when online" };
12011
+ }
11732
12012
  console.error("BugBear: Failed to submit report", error.message);
11733
12013
  return { success: false, error: error.message };
11734
12014
  }
@@ -11738,6 +12018,10 @@ var BugBearClient = class {
11738
12018
  return { success: true, reportId: data.id };
11739
12019
  } catch (err) {
11740
12020
  const message = err instanceof Error ? err.message : "Unknown error";
12021
+ if (this._queue && fullReport && isNetworkError(message)) {
12022
+ await this._queue.enqueue("report", fullReport);
12023
+ return { success: false, queued: true, error: "Queued \u2014 will send when online" };
12024
+ }
11741
12025
  return { success: false, error: message };
11742
12026
  } finally {
11743
12027
  this.reportSubmitInFlight = false;
@@ -11747,10 +12031,13 @@ var BugBearClient = class {
11747
12031
  * Get assigned tests for current user
11748
12032
  * First looks up the tester by email, then fetches their assignments
11749
12033
  */
11750
- async getAssignedTests() {
12034
+ async getAssignedTests(options) {
11751
12035
  try {
11752
12036
  const testerInfo = await this.getTesterInfo();
11753
12037
  if (!testerInfo) return [];
12038
+ const pageSize = Math.min(options?.pageSize ?? 100, 100);
12039
+ const from = (options?.page ?? 0) * pageSize;
12040
+ const to = from + pageSize - 1;
11754
12041
  const { data, error } = await this.supabase.from("test_assignments").select(`
11755
12042
  id,
11756
12043
  status,
@@ -11791,7 +12078,7 @@ var BugBearClient = class {
11791
12078
  login_hint
11792
12079
  )
11793
12080
  )
11794
- `).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).limit(100);
12081
+ `).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).range(from, to);
11795
12082
  if (error) {
11796
12083
  console.error("BugBear: Failed to fetch assignments", formatPgError(error));
11797
12084
  return [];
@@ -12050,6 +12337,7 @@ var BugBearClient = class {
12050
12337
  * This empowers testers to shape better tests over time
12051
12338
  */
12052
12339
  async submitTestFeedback(options) {
12340
+ let feedbackPayload;
12053
12341
  try {
12054
12342
  const testerInfo = await this.getTesterInfo();
12055
12343
  if (!testerInfo) {
@@ -12069,7 +12357,7 @@ var BugBearClient = class {
12069
12357
  return { success: false, error: `${name} must be between 1 and 5` };
12070
12358
  }
12071
12359
  }
12072
- const { error: feedbackError } = await this.supabase.from("test_feedback").insert({
12360
+ feedbackPayload = {
12073
12361
  project_id: this.config.projectId,
12074
12362
  test_case_id: testCaseId,
12075
12363
  assignment_id: assignmentId || null,
@@ -12087,8 +12375,13 @@ var BugBearClient = class {
12087
12375
  platform: this.getDeviceInfo().platform,
12088
12376
  time_to_complete_seconds: timeToCompleteSeconds || null,
12089
12377
  screenshot_urls: screenshotUrls || []
12090
- });
12378
+ };
12379
+ const { error: feedbackError } = await this.supabase.from("test_feedback").insert(feedbackPayload);
12091
12380
  if (feedbackError) {
12381
+ if (this._queue && isNetworkError(feedbackError.message)) {
12382
+ await this._queue.enqueue("feedback", feedbackPayload);
12383
+ return { success: false, queued: true, error: "Queued \u2014 will send when online" };
12384
+ }
12092
12385
  console.error("BugBear: Failed to submit feedback", feedbackError);
12093
12386
  return { success: false, error: feedbackError.message };
12094
12387
  }
@@ -12105,6 +12398,10 @@ var BugBearClient = class {
12105
12398
  return { success: true };
12106
12399
  } catch (err) {
12107
12400
  const message = err instanceof Error ? err.message : "Unknown error";
12401
+ if (this._queue && feedbackPayload && isNetworkError(message)) {
12402
+ await this._queue.enqueue("feedback", feedbackPayload);
12403
+ return { success: false, queued: true, error: "Queued \u2014 will send when online" };
12404
+ }
12108
12405
  console.error("BugBear: Error submitting feedback", err);
12109
12406
  return { success: false, error: message };
12110
12407
  }
@@ -12739,6 +13036,7 @@ var BugBearClient = class {
12739
13036
  * Send a message to a thread
12740
13037
  */
12741
13038
  async sendMessage(threadId, content, attachments) {
13039
+ let insertData;
12742
13040
  try {
12743
13041
  const testerInfo = await this.getTesterInfo();
12744
13042
  if (!testerInfo) {
@@ -12750,7 +13048,7 @@ var BugBearClient = class {
12750
13048
  console.error("BugBear: Rate limit exceeded for messages");
12751
13049
  return false;
12752
13050
  }
12753
- const insertData = {
13051
+ insertData = {
12754
13052
  thread_id: threadId,
12755
13053
  sender_type: "tester",
12756
13054
  sender_tester_id: testerInfo.id,
@@ -12765,12 +13063,21 @@ var BugBearClient = class {
12765
13063
  }
12766
13064
  const { error } = await this.supabase.from("discussion_messages").insert(insertData);
12767
13065
  if (error) {
13066
+ if (this._queue && isNetworkError(error.message)) {
13067
+ await this._queue.enqueue("message", insertData);
13068
+ return false;
13069
+ }
12768
13070
  console.error("BugBear: Failed to send message", formatPgError(error));
12769
13071
  return false;
12770
13072
  }
12771
13073
  await this.markThreadAsRead(threadId);
12772
13074
  return true;
12773
13075
  } catch (err) {
13076
+ const message = err instanceof Error ? err.message : "Unknown error";
13077
+ if (this._queue && insertData && isNetworkError(message)) {
13078
+ await this._queue.enqueue("message", insertData);
13079
+ return false;
13080
+ }
12774
13081
  console.error("BugBear: Error sending message", err);
12775
13082
  return false;
12776
13083
  }
@@ -12957,7 +13264,7 @@ var BugBearClient = class {
12957
13264
  console.error("BugBear: Failed to fetch session history", formatPgError(error));
12958
13265
  return [];
12959
13266
  }
12960
- return (data || []).map((s) => this.transformSession(s));
13267
+ return (data || []).map((s2) => this.transformSession(s2));
12961
13268
  } catch (err) {
12962
13269
  console.error("BugBear: Error fetching session history", err);
12963
13270
  return [];
@@ -13105,7 +13412,7 @@ function createBugBear(config) {
13105
13412
  }
13106
13413
 
13107
13414
  // src/BugBearProvider.tsx
13108
- import { Platform, Dimensions } from "react-native";
13415
+ import { Platform, Dimensions, AppState } from "react-native";
13109
13416
  var BugBearContext = createContext({
13110
13417
  client: null,
13111
13418
  isTester: false,
@@ -13146,6 +13453,7 @@ var BugBearContext = createContext({
13146
13453
  issueCounts: { open: 0, done: 0, reopened: 0 },
13147
13454
  refreshIssueCounts: async () => {
13148
13455
  },
13456
+ queuedCount: 0,
13149
13457
  dashboardUrl: void 0,
13150
13458
  onError: void 0
13151
13459
  });
@@ -13162,6 +13470,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13162
13470
  const [threads, setThreads] = useState([]);
13163
13471
  const [unreadCount, setUnreadCount] = useState(0);
13164
13472
  const [issueCounts, setIssueCounts] = useState({ open: 0, done: 0, reopened: 0 });
13473
+ const [queuedCount, setQueuedCount] = useState(0);
13165
13474
  const [activeSession, setActiveSession] = useState(null);
13166
13475
  const [sessionFindings, setSessionFindings] = useState([]);
13167
13476
  const hasInitialized = useRef(false);
@@ -13321,18 +13630,46 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13321
13630
  hasInitialized.current = true;
13322
13631
  contextCapture.startCapture();
13323
13632
  const newClient = createBugBear(config);
13633
+ if (newClient.queue) {
13634
+ newClient.queue.onChange(setQueuedCount);
13635
+ newClient.initQueue();
13636
+ }
13324
13637
  setClient(newClient);
13325
13638
  initializeBugBear(newClient);
13326
13639
  }
13327
13640
  }, [enabled, config, initializeBugBear]);
13641
+ useEffect(() => {
13642
+ if (!client?.queue) return;
13643
+ const subscription = AppState.addEventListener("change", (state) => {
13644
+ if (state === "active" && client.queue && client.queue.count > 0) {
13645
+ client.queue.flush();
13646
+ }
13647
+ });
13648
+ if (client.queue.count > 0) {
13649
+ client.queue.flush();
13650
+ }
13651
+ return () => subscription.remove();
13652
+ }, [client]);
13328
13653
  useEffect(() => {
13329
13654
  if (!client || !isTester || !isQAEnabled) return;
13655
+ let unsubscribe;
13656
+ if (client.realtimeEnabled) {
13657
+ unsubscribe = client.subscribeToChanges({
13658
+ onAssignmentChange: refreshAssignments,
13659
+ onMessageChange: refreshThreads,
13660
+ onReportChange: refreshIssueCounts
13661
+ });
13662
+ }
13663
+ const pollInterval = client.realtimeEnabled ? 12e4 : 3e4;
13330
13664
  const interval = setInterval(() => {
13331
13665
  refreshThreads();
13332
13666
  refreshIssueCounts();
13333
- }, 3e4);
13334
- return () => clearInterval(interval);
13335
- }, [client, isTester, isQAEnabled, refreshThreads, refreshIssueCounts]);
13667
+ }, pollInterval);
13668
+ return () => {
13669
+ clearInterval(interval);
13670
+ unsubscribe?.();
13671
+ };
13672
+ }, [client, isTester, isQAEnabled, refreshThreads, refreshIssueCounts, refreshAssignments]);
13336
13673
  const currentAssignment = assignments.find(
13337
13674
  (a) => a.status === "in_progress"
13338
13675
  ) || assignments.find(
@@ -13375,6 +13712,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13375
13712
  // Issue tracking
13376
13713
  issueCounts,
13377
13714
  refreshIssueCounts,
13715
+ queuedCount,
13378
13716
  dashboardUrl: config.dashboardUrl,
13379
13717
  onError: config.onError
13380
13718
  }
@@ -13384,21 +13722,21 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13384
13722
  }
13385
13723
 
13386
13724
  // src/BugBearButton.tsx
13387
- import React17, { useState as useState12, useRef as useRef3 } from "react";
13725
+ import React18, { useState as useState12, useRef as useRef4 } from "react";
13388
13726
  import {
13389
- View as View16,
13727
+ View as View17,
13390
13728
  Text as Text16,
13391
13729
  Image as Image4,
13392
13730
  TouchableOpacity as TouchableOpacity15,
13393
13731
  Modal as Modal3,
13394
13732
  ScrollView as ScrollView3,
13395
- StyleSheet as StyleSheet17,
13733
+ StyleSheet as StyleSheet18,
13396
13734
  Dimensions as Dimensions2,
13397
13735
  KeyboardAvoidingView,
13398
13736
  Platform as Platform4,
13399
13737
  PanResponder,
13400
- Animated,
13401
- ActivityIndicator as ActivityIndicator3,
13738
+ Animated as Animated2,
13739
+ ActivityIndicator as ActivityIndicator2,
13402
13740
  Keyboard as Keyboard2
13403
13741
  } from "react-native";
13404
13742
 
@@ -13544,9 +13882,9 @@ var shared = StyleSheet.create({
13544
13882
  function formatElapsedTime(seconds) {
13545
13883
  const h = Math.floor(seconds / 3600);
13546
13884
  const m = Math.floor(seconds % 3600 / 60);
13547
- const s = seconds % 60;
13548
- if (h > 0) return `${h}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
13549
- return `${m}:${s.toString().padStart(2, "0")}`;
13885
+ const s2 = seconds % 60;
13886
+ if (h > 0) return `${h}:${m.toString().padStart(2, "0")}:${s2.toString().padStart(2, "0")}`;
13887
+ return `${m}:${s2.toString().padStart(2, "0")}`;
13550
13888
  }
13551
13889
  function formatRelativeTime(dateString) {
13552
13890
  const date = new Date(dateString);
@@ -13618,11 +13956,90 @@ var templateInfo = {
13618
13956
  };
13619
13957
 
13620
13958
  // src/widget/screens/HomeScreen.tsx
13621
- import React2, { useEffect as useEffect2 } from "react";
13622
- import { View, Text, TouchableOpacity, StyleSheet as StyleSheet2, Linking } from "react-native";
13623
- function HomeScreen({ nav }) {
13624
- const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, dashboardUrl } = useBugBear();
13959
+ import React3, { useEffect as useEffect3 } from "react";
13960
+ import { View as View2, Text, TouchableOpacity, StyleSheet as StyleSheet3, Linking } from "react-native";
13961
+
13962
+ // src/widget/Skeleton.tsx
13963
+ import React2, { useEffect as useEffect2, useRef as useRef2 } from "react";
13964
+ import { View, Animated, StyleSheet as StyleSheet2 } from "react-native";
13965
+ function usePulse(delay = 0) {
13966
+ const opacity = useRef2(new Animated.Value(0.6)).current;
13625
13967
  useEffect2(() => {
13968
+ const timeout = setTimeout(() => {
13969
+ Animated.loop(
13970
+ Animated.sequence([
13971
+ Animated.timing(opacity, { toValue: 0.25, duration: 750, useNativeDriver: true }),
13972
+ Animated.timing(opacity, { toValue: 0.6, duration: 750, useNativeDriver: true })
13973
+ ])
13974
+ ).start();
13975
+ }, delay);
13976
+ return () => clearTimeout(timeout);
13977
+ }, [opacity, delay]);
13978
+ return opacity;
13979
+ }
13980
+ function Bar({ width = "100%", height = 12, radius = 6, delay = 0 }) {
13981
+ const opacity = usePulse(delay);
13982
+ return /* @__PURE__ */ React2.createElement(
13983
+ Animated.View,
13984
+ {
13985
+ style: {
13986
+ width,
13987
+ height,
13988
+ borderRadius: radius,
13989
+ backgroundColor: colors.border,
13990
+ opacity
13991
+ }
13992
+ }
13993
+ );
13994
+ }
13995
+ function Circle({ size = 20, delay = 0 }) {
13996
+ const opacity = usePulse(delay);
13997
+ return /* @__PURE__ */ React2.createElement(
13998
+ Animated.View,
13999
+ {
14000
+ style: {
14001
+ width: size,
14002
+ height: size,
14003
+ borderRadius: size / 2,
14004
+ backgroundColor: colors.border,
14005
+ opacity
14006
+ }
14007
+ }
14008
+ );
14009
+ }
14010
+ function HomeScreenSkeleton() {
14011
+ return /* @__PURE__ */ React2.createElement(View, null, /* @__PURE__ */ React2.createElement(Bar, { width: "100%", height: 100, radius: 16 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 20 } }), /* @__PURE__ */ React2.createElement(View, { style: s.actionGrid }, [0, 1, 2, 3].map((i) => /* @__PURE__ */ React2.createElement(View, { key: i, style: s.actionCard }, /* @__PURE__ */ React2.createElement(Circle, { size: 28, delay: i * 80 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 8 } }), /* @__PURE__ */ React2.createElement(Bar, { width: 60, height: 10, delay: i * 80 })))), /* @__PURE__ */ React2.createElement(View, { style: s.issueGrid }, [0, 1, 2].map((i) => /* @__PURE__ */ React2.createElement(View, { key: i, style: s.issueCard }, /* @__PURE__ */ React2.createElement(Bar, { width: 30, height: 18, delay: i * 100 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 6 } }), /* @__PURE__ */ React2.createElement(Bar, { width: 40, height: 8, delay: i * 100 })))), /* @__PURE__ */ React2.createElement(Bar, { width: "100%", height: 6, radius: 3 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 8 } }), /* @__PURE__ */ React2.createElement(View, { style: { alignItems: "center" } }, /* @__PURE__ */ React2.createElement(Bar, { width: 120, height: 10 })));
14012
+ }
14013
+ function TestItemSkeleton({ delay = 0 }) {
14014
+ return /* @__PURE__ */ React2.createElement(View, { style: s.testItem }, /* @__PURE__ */ React2.createElement(Circle, { size: 18, delay }), /* @__PURE__ */ React2.createElement(View, { style: { flex: 1, marginLeft: 10 } }, /* @__PURE__ */ React2.createElement(Bar, { width: "70%", height: 11, delay }), /* @__PURE__ */ React2.createElement(View, { style: { height: 4 } }), /* @__PURE__ */ React2.createElement(Bar, { width: "45%", height: 8, delay })), /* @__PURE__ */ React2.createElement(Bar, { width: 50, height: 18, radius: 6, delay }));
14015
+ }
14016
+ function TestListScreenSkeleton() {
14017
+ return /* @__PURE__ */ React2.createElement(View, null, /* @__PURE__ */ React2.createElement(View, { style: s.filterRow }, [55, 50, 50, 70].map((w, i) => /* @__PURE__ */ React2.createElement(Bar, { key: i, width: w, height: 28, radius: 8, delay: i * 50 }))), /* @__PURE__ */ React2.createElement(Bar, { width: "100%", height: 36, radius: 8 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 10 } }), [0, 1].map((g) => /* @__PURE__ */ React2.createElement(View, { key: g, style: { marginBottom: 12 } }, /* @__PURE__ */ React2.createElement(View, { style: s.folderHeader }, /* @__PURE__ */ React2.createElement(Bar, { width: 12, height: 10 }), /* @__PURE__ */ React2.createElement(Bar, { width: "40%", height: 12 }), /* @__PURE__ */ React2.createElement(View, { style: { flex: 1 } }), /* @__PURE__ */ React2.createElement(Bar, { width: 40, height: 4, radius: 2 }), /* @__PURE__ */ React2.createElement(Bar, { width: 24, height: 10 })), [0, 1, 2].map((i) => /* @__PURE__ */ React2.createElement(TestItemSkeleton, { key: i, delay: (g * 3 + i) * 80 })))));
14018
+ }
14019
+ function IssueListScreenSkeleton() {
14020
+ return /* @__PURE__ */ React2.createElement(View, null, [0, 1, 2, 3].map((i) => /* @__PURE__ */ React2.createElement(View, { key: i, style: s.issueRow }, /* @__PURE__ */ React2.createElement(View, { style: s.issueRowTop }, /* @__PURE__ */ React2.createElement(Circle, { size: 8, delay: i * 100 }), /* @__PURE__ */ React2.createElement(Bar, { width: "65%", height: 11, delay: i * 100 })), /* @__PURE__ */ React2.createElement(View, { style: s.issueRowBottom }, /* @__PURE__ */ React2.createElement(Bar, { width: "40%", height: 8, delay: i * 100 }), /* @__PURE__ */ React2.createElement(Bar, { width: 40, height: 8, delay: i * 100 })))));
14021
+ }
14022
+ function MessageListScreenSkeleton() {
14023
+ return /* @__PURE__ */ React2.createElement(View, null, /* @__PURE__ */ React2.createElement(Bar, { width: "100%", height: 44, radius: 12 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 16 } }), [0, 1, 2, 3].map((i) => /* @__PURE__ */ React2.createElement(View, { key: i, style: s.threadRow }, /* @__PURE__ */ React2.createElement(Circle, { size: 20, delay: i * 100 }), /* @__PURE__ */ React2.createElement(View, { style: { flex: 1, marginLeft: 10 } }, /* @__PURE__ */ React2.createElement(Bar, { width: "55%", height: 11, delay: i * 100 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 5 } }), /* @__PURE__ */ React2.createElement(Bar, { width: "80%", height: 9, delay: i * 100 })), /* @__PURE__ */ React2.createElement(Bar, { width: 30, height: 8, delay: i * 100 }))));
14024
+ }
14025
+ var s = StyleSheet2.create({
14026
+ actionGrid: { flexDirection: "row", flexWrap: "wrap", gap: 12, marginBottom: 20 },
14027
+ actionCard: { width: "47%", backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 12, padding: 16, alignItems: "center" },
14028
+ issueGrid: { flexDirection: "row", gap: 10, marginBottom: 20 },
14029
+ issueCard: { flex: 1, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 10, paddingVertical: 12, paddingHorizontal: 8, alignItems: "center" },
14030
+ filterRow: { flexDirection: "row", gap: 8, marginBottom: 8 },
14031
+ folderHeader: { flexDirection: "row", alignItems: "center", gap: 8, paddingVertical: 8, paddingHorizontal: 4 },
14032
+ testItem: { flexDirection: "row", alignItems: "center", paddingVertical: 10, paddingHorizontal: 12, borderRadius: 8, marginBottom: 4, backgroundColor: colors.card },
14033
+ issueRow: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 14, marginBottom: 8 },
14034
+ issueRowTop: { flexDirection: "row", alignItems: "center", gap: 8 },
14035
+ issueRowBottom: { flexDirection: "row", justifyContent: "space-between", marginTop: 8 },
14036
+ threadRow: { flexDirection: "row", alignItems: "flex-start", padding: 12, borderRadius: 10, marginBottom: 4, backgroundColor: colors.card }
14037
+ });
14038
+
14039
+ // src/widget/screens/HomeScreen.tsx
14040
+ function HomeScreen({ nav }) {
14041
+ const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, dashboardUrl, isLoading } = useBugBear();
14042
+ useEffect3(() => {
13626
14043
  refreshAssignments();
13627
14044
  refreshThreads();
13628
14045
  refreshIssueCounts();
@@ -13632,103 +14049,104 @@ function HomeScreen({ nav }) {
13632
14049
  const retestCount = pendingAssignments.filter((a) => a.isVerification).length;
13633
14050
  const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
13634
14051
  const totalTests = assignments.length;
13635
- return /* @__PURE__ */ React2.createElement(View, null, pendingCount > 0 ? /* @__PURE__ */ React2.createElement(
14052
+ if (isLoading) return /* @__PURE__ */ React3.createElement(HomeScreenSkeleton, null);
14053
+ return /* @__PURE__ */ React3.createElement(View2, null, pendingCount > 0 ? /* @__PURE__ */ React3.createElement(
13636
14054
  TouchableOpacity,
13637
14055
  {
13638
14056
  style: [styles.heroBanner, styles.heroBannerTests],
13639
14057
  onPress: () => nav.push({ name: "TEST_DETAIL" }),
13640
14058
  activeOpacity: 0.8
13641
14059
  },
13642
- /* @__PURE__ */ React2.createElement(Text, { style: styles.heroCount }, pendingCount),
13643
- /* @__PURE__ */ React2.createElement(Text, { style: styles.heroLabel }, "test", pendingCount !== 1 ? "s" : "", " waiting"),
13644
- retestCount > 0 && /* @__PURE__ */ React2.createElement(View, { style: styles.retestPill }, /* @__PURE__ */ React2.createElement(Text, { style: styles.retestPillText }, "\u{1F504} ", retestCount, " retest", retestCount !== 1 ? "s" : "")),
13645
- /* @__PURE__ */ React2.createElement(Text, { style: styles.heroAction }, "Start Testing \u2192")
13646
- ) : unreadCount > 0 ? /* @__PURE__ */ React2.createElement(
14060
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.heroCount }, pendingCount),
14061
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.heroLabel }, "test", pendingCount !== 1 ? "s" : "", " waiting"),
14062
+ retestCount > 0 && /* @__PURE__ */ React3.createElement(View2, { style: styles.retestPill }, /* @__PURE__ */ React3.createElement(Text, { style: styles.retestPillText }, "\u{1F504} ", retestCount, " retest", retestCount !== 1 ? "s" : "")),
14063
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.heroAction }, "Start Testing \u2192")
14064
+ ) : unreadCount > 0 ? /* @__PURE__ */ React3.createElement(
13647
14065
  TouchableOpacity,
13648
14066
  {
13649
14067
  style: [styles.heroBanner, styles.heroBannerMessages],
13650
14068
  onPress: () => nav.push({ name: "MESSAGE_LIST" }),
13651
14069
  activeOpacity: 0.8
13652
14070
  },
13653
- /* @__PURE__ */ React2.createElement(Text, { style: styles.heroCount }, unreadCount),
13654
- /* @__PURE__ */ React2.createElement(Text, { style: styles.heroLabel }, "unread message", unreadCount !== 1 ? "s" : ""),
13655
- /* @__PURE__ */ React2.createElement(Text, { style: styles.heroAction }, "View Messages \u2192")
13656
- ) : /* @__PURE__ */ React2.createElement(View, { style: [styles.heroBanner, styles.heroBannerClear] }, /* @__PURE__ */ React2.createElement(Text, { style: styles.heroClearEmoji }, "\u2705"), /* @__PURE__ */ React2.createElement(Text, { style: styles.heroClearTitle }, "All caught up!"), totalTests > 0 && /* @__PURE__ */ React2.createElement(Text, { style: styles.heroClearSub }, completedCount, "/", totalTests, " tests completed")), /* @__PURE__ */ React2.createElement(View, { style: styles.actionGrid }, /* @__PURE__ */ React2.createElement(
14071
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.heroCount }, unreadCount),
14072
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.heroLabel }, "unread message", unreadCount !== 1 ? "s" : ""),
14073
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.heroAction }, "View Messages \u2192")
14074
+ ) : /* @__PURE__ */ React3.createElement(View2, { style: [styles.heroBanner, styles.heroBannerClear] }, /* @__PURE__ */ React3.createElement(Text, { style: styles.heroClearEmoji }, "\u2705"), /* @__PURE__ */ React3.createElement(Text, { style: styles.heroClearTitle }, "All caught up!"), totalTests > 0 && /* @__PURE__ */ React3.createElement(Text, { style: styles.heroClearSub }, completedCount, "/", totalTests, " tests completed")), /* @__PURE__ */ React3.createElement(View2, { style: styles.actionGrid }, /* @__PURE__ */ React3.createElement(
13657
14075
  TouchableOpacity,
13658
14076
  {
13659
14077
  style: styles.actionCard,
13660
14078
  onPress: () => nav.push({ name: "TEST_LIST" }),
13661
14079
  activeOpacity: 0.7
13662
14080
  },
13663
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionIcon }, "\u2705"),
13664
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionLabel }, "Tests"),
13665
- pendingCount > 0 && /* @__PURE__ */ React2.createElement(View, { style: styles.actionBadge }, /* @__PURE__ */ React2.createElement(Text, { style: styles.actionBadgeText }, pendingCount))
13666
- ), /* @__PURE__ */ React2.createElement(
14081
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionIcon }, "\u2705"),
14082
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionLabel }, "Tests"),
14083
+ pendingCount > 0 && /* @__PURE__ */ React3.createElement(View2, { style: styles.actionBadge }, /* @__PURE__ */ React3.createElement(Text, { style: styles.actionBadgeText }, pendingCount))
14084
+ ), /* @__PURE__ */ React3.createElement(
13667
14085
  TouchableOpacity,
13668
14086
  {
13669
14087
  style: styles.actionCard,
13670
14088
  onPress: () => nav.push({ name: "REPORT", prefill: { type: "bug" } }),
13671
14089
  activeOpacity: 0.7
13672
14090
  },
13673
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionIcon }, "\u{1F41B}"),
13674
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionLabel }, "Report Bug")
13675
- ), /* @__PURE__ */ React2.createElement(
14091
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionIcon }, "\u{1F41B}"),
14092
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionLabel }, "Report Bug")
14093
+ ), /* @__PURE__ */ React3.createElement(
13676
14094
  TouchableOpacity,
13677
14095
  {
13678
14096
  style: styles.actionCard,
13679
14097
  onPress: () => nav.push({ name: "REPORT", prefill: { type: "feedback" } }),
13680
14098
  activeOpacity: 0.7
13681
14099
  },
13682
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionIcon }, "\u{1F4A1}"),
13683
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionLabel }, "Feedback")
13684
- ), /* @__PURE__ */ React2.createElement(
14100
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionIcon }, "\u{1F4A1}"),
14101
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionLabel }, "Feedback")
14102
+ ), /* @__PURE__ */ React3.createElement(
13685
14103
  TouchableOpacity,
13686
14104
  {
13687
14105
  style: styles.actionCard,
13688
14106
  onPress: () => nav.push({ name: "MESSAGE_LIST" }),
13689
14107
  activeOpacity: 0.7
13690
14108
  },
13691
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionIcon }, "\u{1F4AC}"),
13692
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionLabel }, "Messages"),
13693
- unreadCount > 0 && /* @__PURE__ */ React2.createElement(View, { style: [styles.actionBadge, styles.actionBadgeMsg] }, /* @__PURE__ */ React2.createElement(Text, { style: styles.actionBadgeText }, unreadCount))
13694
- )), /* @__PURE__ */ React2.createElement(View, { style: styles.issueGrid }, /* @__PURE__ */ React2.createElement(
14109
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionIcon }, "\u{1F4AC}"),
14110
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionLabel }, "Messages"),
14111
+ unreadCount > 0 && /* @__PURE__ */ React3.createElement(View2, { style: [styles.actionBadge, styles.actionBadgeMsg] }, /* @__PURE__ */ React3.createElement(Text, { style: styles.actionBadgeText }, unreadCount))
14112
+ )), /* @__PURE__ */ React3.createElement(View2, { style: styles.issueGrid }, /* @__PURE__ */ React3.createElement(
13695
14113
  TouchableOpacity,
13696
14114
  {
13697
14115
  style: [styles.issueCard, styles.issueCardOpen],
13698
14116
  onPress: () => nav.push({ name: "ISSUE_LIST", category: "open" }),
13699
14117
  activeOpacity: 0.7
13700
14118
  },
13701
- /* @__PURE__ */ React2.createElement(Text, { style: styles.issueCountOpen }, issueCounts.open),
13702
- /* @__PURE__ */ React2.createElement(Text, { style: styles.issueLabel }, "Open")
13703
- ), /* @__PURE__ */ React2.createElement(
14119
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.issueCountOpen }, issueCounts.open),
14120
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.issueLabel }, "Open")
14121
+ ), /* @__PURE__ */ React3.createElement(
13704
14122
  TouchableOpacity,
13705
14123
  {
13706
14124
  style: [styles.issueCard, styles.issueCardDone],
13707
14125
  onPress: () => nav.push({ name: "ISSUE_LIST", category: "done" }),
13708
14126
  activeOpacity: 0.7
13709
14127
  },
13710
- /* @__PURE__ */ React2.createElement(Text, { style: styles.issueCountDone }, issueCounts.done),
13711
- /* @__PURE__ */ React2.createElement(Text, { style: styles.issueLabel }, "Done")
13712
- ), /* @__PURE__ */ React2.createElement(
14128
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.issueCountDone }, issueCounts.done),
14129
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.issueLabel }, "Done")
14130
+ ), /* @__PURE__ */ React3.createElement(
13713
14131
  TouchableOpacity,
13714
14132
  {
13715
14133
  style: [styles.issueCard, styles.issueCardReopened],
13716
14134
  onPress: () => nav.push({ name: "ISSUE_LIST", category: "reopened" }),
13717
14135
  activeOpacity: 0.7
13718
14136
  },
13719
- /* @__PURE__ */ React2.createElement(Text, { style: styles.issueCountReopened }, issueCounts.reopened),
13720
- /* @__PURE__ */ React2.createElement(Text, { style: styles.issueLabel }, "Reopened")
13721
- )), totalTests > 0 && /* @__PURE__ */ React2.createElement(View, { style: styles.progressSection }, /* @__PURE__ */ React2.createElement(View, { style: styles.progressBar }, /* @__PURE__ */ React2.createElement(View, { style: [styles.progressFill, { width: `${Math.round(completedCount / totalTests * 100)}%` }] })), /* @__PURE__ */ React2.createElement(Text, { style: styles.progressText }, completedCount, "/", totalTests, " tests completed")), dashboardUrl && /* @__PURE__ */ React2.createElement(
14137
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.issueCountReopened }, issueCounts.reopened),
14138
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.issueLabel }, "Reopened")
14139
+ )), totalTests > 0 && /* @__PURE__ */ React3.createElement(View2, { style: styles.progressSection }, /* @__PURE__ */ React3.createElement(View2, { style: styles.progressBar }, /* @__PURE__ */ React3.createElement(View2, { style: [styles.progressFill, { width: `${Math.round(completedCount / totalTests * 100)}%` }] })), /* @__PURE__ */ React3.createElement(Text, { style: styles.progressText }, completedCount, "/", totalTests, " tests completed")), dashboardUrl && /* @__PURE__ */ React3.createElement(
13722
14140
  TouchableOpacity,
13723
14141
  {
13724
14142
  style: styles.webAppLink,
13725
14143
  onPress: () => Linking.openURL(dashboardUrl),
13726
14144
  activeOpacity: 0.7
13727
14145
  },
13728
- /* @__PURE__ */ React2.createElement(Text, { style: styles.webAppIcon }, "\u{1F310}"),
13729
- /* @__PURE__ */ React2.createElement(View, { style: styles.webAppTextWrap }, /* @__PURE__ */ React2.createElement(Text, { style: styles.webAppTitle }, "Open Web Dashboard"), /* @__PURE__ */ React2.createElement(Text, { style: styles.webAppSub }, "View analytics, history & more")),
13730
- /* @__PURE__ */ React2.createElement(Text, { style: styles.webAppArrow }, "\u2192")
13731
- ), /* @__PURE__ */ React2.createElement(
14146
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.webAppIcon }, "\u{1F310}"),
14147
+ /* @__PURE__ */ React3.createElement(View2, { style: styles.webAppTextWrap }, /* @__PURE__ */ React3.createElement(Text, { style: styles.webAppTitle }, "Open Web Dashboard"), /* @__PURE__ */ React3.createElement(Text, { style: styles.webAppSub }, "View analytics, history & more")),
14148
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.webAppArrow }, "\u2192")
14149
+ ), /* @__PURE__ */ React3.createElement(
13732
14150
  TouchableOpacity,
13733
14151
  {
13734
14152
  style: styles.refreshButton,
@@ -13738,10 +14156,10 @@ function HomeScreen({ nav }) {
13738
14156
  refreshIssueCounts();
13739
14157
  }
13740
14158
  },
13741
- /* @__PURE__ */ React2.createElement(Text, { style: styles.refreshText }, "\u21BB Refresh")
14159
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.refreshText }, "\u21BB Refresh")
13742
14160
  ));
13743
14161
  }
13744
- var styles = StyleSheet2.create({
14162
+ var styles = StyleSheet3.create({
13745
14163
  heroBanner: {
13746
14164
  borderRadius: 16,
13747
14165
  padding: 24,
@@ -13965,8 +14383,8 @@ var styles = StyleSheet2.create({
13965
14383
  });
13966
14384
 
13967
14385
  // src/widget/screens/TestDetailScreen.tsx
13968
- import React3, { useState as useState2, useEffect as useEffect3, useCallback as useCallback2 } from "react";
13969
- import { View as View2, Text as Text2, TouchableOpacity as TouchableOpacity2, StyleSheet as StyleSheet3, Modal, TextInput, Keyboard } from "react-native";
14386
+ import React4, { useState as useState2, useEffect as useEffect4, useCallback as useCallback2 } from "react";
14387
+ import { View as View3, Text as Text2, TouchableOpacity as TouchableOpacity2, StyleSheet as StyleSheet4, Modal, TextInput, Keyboard } from "react-native";
13970
14388
  function TestDetailScreen({ testId, nav }) {
13971
14389
  const { client, assignments, currentAssignment, refreshAssignments, getDeviceInfo, onNavigate } = useBugBear();
13972
14390
  const displayedAssignment = testId ? assignments.find((a) => a.id === testId) || currentAssignment : currentAssignment;
@@ -13979,12 +14397,12 @@ function TestDetailScreen({ testId, nav }) {
13979
14397
  const [skipNotes, setSkipNotes] = useState2("");
13980
14398
  const [skipping, setSkipping] = useState2(false);
13981
14399
  const [isSubmitting, setIsSubmitting] = useState2(false);
13982
- useEffect3(() => {
14400
+ useEffect4(() => {
13983
14401
  setCriteriaResults({});
13984
14402
  setShowSteps(true);
13985
14403
  setShowDetails(false);
13986
14404
  }, [displayedAssignment?.id]);
13987
- useEffect3(() => {
14405
+ useEffect4(() => {
13988
14406
  const active = displayedAssignment?.status === "in_progress" ? displayedAssignment : null;
13989
14407
  if (!active?.startedAt) {
13990
14408
  setAssignmentElapsedTime(0);
@@ -14060,14 +14478,14 @@ function TestDetailScreen({ testId, nav }) {
14060
14478
  }
14061
14479
  }, [client, displayedAssignment, selectedSkipReason, skipNotes, refreshAssignments, assignments, nav]);
14062
14480
  if (!displayedAssignment) {
14063
- return /* @__PURE__ */ React3.createElement(View2, { style: shared.emptyState }, /* @__PURE__ */ React3.createElement(Text2, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ React3.createElement(Text2, { style: shared.emptyTitle }, "No tests assigned"), /* @__PURE__ */ React3.createElement(Text2, { style: shared.emptySubtitle }, "Check back later for new tests"), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { style: [shared.primaryButton, { marginTop: 20, paddingHorizontal: 24 }], onPress: () => nav.reset() }, /* @__PURE__ */ React3.createElement(Text2, { style: shared.primaryButtonText }, "Go Home")));
14481
+ return /* @__PURE__ */ React4.createElement(View3, { style: shared.emptyState }, /* @__PURE__ */ React4.createElement(Text2, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ React4.createElement(Text2, { style: shared.emptyTitle }, "No tests assigned"), /* @__PURE__ */ React4.createElement(Text2, { style: shared.emptySubtitle }, "Check back later for new tests"), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [shared.primaryButton, { marginTop: 20, paddingHorizontal: 24 }], onPress: () => nav.reset() }, /* @__PURE__ */ React4.createElement(Text2, { style: shared.primaryButtonText }, "Go Home")));
14064
14482
  }
14065
14483
  const testCase = displayedAssignment.testCase;
14066
14484
  const template = testCase.track?.testTemplate || "steps";
14067
14485
  const steps = testCase.steps;
14068
14486
  const info = templateInfo[template] || templateInfo.steps;
14069
14487
  const rubricMode = testCase.track?.rubricMode || "pass_fail";
14070
- return /* @__PURE__ */ React3.createElement(View2, { style: styles2.container }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.topRow }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.positionInfo }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.positionText }, "Test ", currentIndex + 1, " of ", allTests.length), displayedAssignment.status === "in_progress" && assignmentElapsedTime > 0 && /* @__PURE__ */ React3.createElement(View2, { style: styles2.timerBadge }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.timerText }, formatElapsedTime(assignmentElapsedTime)))), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { onPress: () => nav.push({ name: "TEST_LIST" }) }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.viewAllLink }, "View All \u2192"))), displayedAssignment.isVerification && /* @__PURE__ */ React3.createElement(View2, { style: styles2.retestBanner }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.retestIcon }, "\u{1F504}"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.retestLabel }, "Retest"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.retestSub }, "\u2014 Verify bug fix")), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.testTitle }, testCase.title), testCase.key && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.testKey }, testCase.key), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { onPress: () => setShowSteps(!showSteps), style: styles2.sectionHeader }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.sectionHeaderText }, showSteps ? "\u25BC" : "\u25B6", " ", info.icon, " ", template === "freeform" ? "Instructions" : `${steps.length} ${template === "checklist" ? "items" : template === "rubric" ? "criteria" : "steps"}`)), showSteps && /* @__PURE__ */ React3.createElement(View2, { style: styles2.templateContent }, template === "steps" && steps.map((step, idx) => /* @__PURE__ */ React3.createElement(View2, { key: idx, style: styles2.step }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.stepNumber }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.stepNumberText }, step.stepNumber)), /* @__PURE__ */ React3.createElement(View2, { style: styles2.stepBody }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.stepAction }, step.action), step.expectedResult && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.stepExpected }, "\u2192 ", step.expectedResult)))), template === "checklist" && /* @__PURE__ */ React3.createElement(React3.Fragment, null, steps.map((step, idx) => /* @__PURE__ */ React3.createElement(
14488
+ return /* @__PURE__ */ React4.createElement(View3, { style: styles2.container }, /* @__PURE__ */ React4.createElement(View3, { style: styles2.topRow }, /* @__PURE__ */ React4.createElement(View3, { style: styles2.positionInfo }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.positionText }, "Test ", currentIndex + 1, " of ", allTests.length), displayedAssignment.status === "in_progress" && assignmentElapsedTime > 0 && /* @__PURE__ */ React4.createElement(View3, { style: styles2.timerBadge }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.timerText }, formatElapsedTime(assignmentElapsedTime)))), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { onPress: () => nav.push({ name: "TEST_LIST" }) }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.viewAllLink }, "View All \u2192"))), displayedAssignment.isVerification && /* @__PURE__ */ React4.createElement(View3, { style: styles2.retestBanner }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.retestIcon }, "\u{1F504}"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.retestLabel }, "Retest"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.retestSub }, "\u2014 Verify bug fix")), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.testTitle }, testCase.title), testCase.key && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.testKey }, testCase.key), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { onPress: () => setShowSteps(!showSteps), style: styles2.sectionHeader }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.sectionHeaderText }, showSteps ? "\u25BC" : "\u25B6", " ", info.icon, " ", template === "freeform" ? "Instructions" : `${steps.length} ${template === "checklist" ? "items" : template === "rubric" ? "criteria" : "steps"}`)), showSteps && /* @__PURE__ */ React4.createElement(View3, { style: styles2.templateContent }, template === "steps" && steps.map((step, idx) => /* @__PURE__ */ React4.createElement(View3, { key: idx, style: styles2.step }, /* @__PURE__ */ React4.createElement(View3, { style: styles2.stepNumber }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.stepNumberText }, step.stepNumber)), /* @__PURE__ */ React4.createElement(View3, { style: styles2.stepBody }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.stepAction }, step.action), step.expectedResult && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.stepExpected }, "\u2192 ", step.expectedResult)))), template === "checklist" && /* @__PURE__ */ React4.createElement(React4.Fragment, null, steps.map((step, idx) => /* @__PURE__ */ React4.createElement(
14071
14489
  TouchableOpacity2,
14072
14490
  {
14073
14491
  key: idx,
@@ -14079,31 +14497,31 @@ function TestDetailScreen({ testId, nav }) {
14079
14497
  }),
14080
14498
  style: [styles2.checklistItem, criteriaResults[idx] === true && styles2.checklistItemChecked]
14081
14499
  },
14082
- /* @__PURE__ */ React3.createElement(View2, { style: [styles2.checkbox, criteriaResults[idx] === true && styles2.checkboxChecked] }, criteriaResults[idx] === true && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.checkmark }, "\u2713")),
14083
- /* @__PURE__ */ React3.createElement(Text2, { style: [styles2.checklistText, criteriaResults[idx] === true && styles2.checklistTextDone] }, step.action)
14084
- )), Object.keys(criteriaResults).length > 0 && /* @__PURE__ */ React3.createElement(TouchableOpacity2, { onPress: () => setCriteriaResults({}), style: styles2.resetRow }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.resetText }, "\u21BA Reset"))), template === "rubric" && steps.map((step, idx) => /* @__PURE__ */ React3.createElement(View2, { key: idx, style: styles2.rubricItem }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.rubricTitle }, idx + 1, ". ", step.action), step.expectedResult && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.rubricExpected }, step.expectedResult), rubricMode === "pass_fail" ? /* @__PURE__ */ React3.createElement(View2, { style: styles2.passFailRow }, /* @__PURE__ */ React3.createElement(
14500
+ /* @__PURE__ */ React4.createElement(View3, { style: [styles2.checkbox, criteriaResults[idx] === true && styles2.checkboxChecked] }, criteriaResults[idx] === true && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.checkmark }, "\u2713")),
14501
+ /* @__PURE__ */ React4.createElement(Text2, { style: [styles2.checklistText, criteriaResults[idx] === true && styles2.checklistTextDone] }, step.action)
14502
+ )), Object.keys(criteriaResults).length > 0 && /* @__PURE__ */ React4.createElement(TouchableOpacity2, { onPress: () => setCriteriaResults({}), style: styles2.resetRow }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.resetText }, "\u21BA Reset"))), template === "rubric" && steps.map((step, idx) => /* @__PURE__ */ React4.createElement(View3, { key: idx, style: styles2.rubricItem }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.rubricTitle }, idx + 1, ". ", step.action), step.expectedResult && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.rubricExpected }, step.expectedResult), rubricMode === "pass_fail" ? /* @__PURE__ */ React4.createElement(View3, { style: styles2.passFailRow }, /* @__PURE__ */ React4.createElement(
14085
14503
  TouchableOpacity2,
14086
14504
  {
14087
14505
  onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: true })),
14088
14506
  style: [styles2.pfButton, criteriaResults[idx] === true && styles2.pfButtonPass]
14089
14507
  },
14090
- /* @__PURE__ */ React3.createElement(Text2, { style: [styles2.pfButtonText, criteriaResults[idx] === true && styles2.pfButtonTextActive] }, "\u2713 Pass")
14091
- ), /* @__PURE__ */ React3.createElement(
14508
+ /* @__PURE__ */ React4.createElement(Text2, { style: [styles2.pfButtonText, criteriaResults[idx] === true && styles2.pfButtonTextActive] }, "\u2713 Pass")
14509
+ ), /* @__PURE__ */ React4.createElement(
14092
14510
  TouchableOpacity2,
14093
14511
  {
14094
14512
  onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: false })),
14095
14513
  style: [styles2.pfButton, criteriaResults[idx] === false && styles2.pfButtonFail]
14096
14514
  },
14097
- /* @__PURE__ */ React3.createElement(Text2, { style: [styles2.pfButtonText, criteriaResults[idx] === false && styles2.pfButtonTextActive] }, "\u2717 Fail")
14098
- )) : /* @__PURE__ */ React3.createElement(View2, { style: styles2.ratingRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ React3.createElement(
14515
+ /* @__PURE__ */ React4.createElement(Text2, { style: [styles2.pfButtonText, criteriaResults[idx] === false && styles2.pfButtonTextActive] }, "\u2717 Fail")
14516
+ )) : /* @__PURE__ */ React4.createElement(View3, { style: styles2.ratingRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ React4.createElement(
14099
14517
  TouchableOpacity2,
14100
14518
  {
14101
14519
  key: n,
14102
14520
  onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: n })),
14103
14521
  style: [styles2.ratingBtn, criteriaResults[idx] === n && styles2.ratingBtnActive]
14104
14522
  },
14105
- /* @__PURE__ */ React3.createElement(Text2, { style: [styles2.ratingBtnText, criteriaResults[idx] === n && styles2.ratingBtnTextActive] }, n)
14106
- ))))), template === "freeform" && /* @__PURE__ */ React3.createElement(View2, { style: styles2.freeformBox }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.freeformText }, "Review the area and note:"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.freeformBullet }, "\u2022 What works well"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.freeformBullet }, "\u2022 Issues or concerns"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.freeformBullet }, "\u2022 Suggestions"))), testCase.expectedResult && /* @__PURE__ */ React3.createElement(View2, { style: styles2.expectedBox }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.expectedLabel }, "\u2705 Expected Result"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.expectedText }, testCase.expectedResult)), testCase.targetRoute && onNavigate && /* @__PURE__ */ React3.createElement(
14523
+ /* @__PURE__ */ React4.createElement(Text2, { style: [styles2.ratingBtnText, criteriaResults[idx] === n && styles2.ratingBtnTextActive] }, n)
14524
+ ))))), template === "freeform" && /* @__PURE__ */ React4.createElement(View3, { style: styles2.freeformBox }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.freeformText }, "Review the area and note:"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.freeformBullet }, "\u2022 What works well"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.freeformBullet }, "\u2022 Issues or concerns"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.freeformBullet }, "\u2022 Suggestions"))), testCase.expectedResult && /* @__PURE__ */ React4.createElement(View3, { style: styles2.expectedBox }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.expectedLabel }, "\u2705 Expected Result"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.expectedText }, testCase.expectedResult)), testCase.targetRoute && onNavigate && /* @__PURE__ */ React4.createElement(
14107
14525
  TouchableOpacity2,
14108
14526
  {
14109
14527
  style: styles2.navigateButton,
@@ -14113,21 +14531,21 @@ function TestDetailScreen({ testId, nav }) {
14113
14531
  nav.closeWidget?.();
14114
14532
  }
14115
14533
  },
14116
- /* @__PURE__ */ React3.createElement(Text2, { style: styles2.navigateText }, "\u{1F9ED} Go to test location")
14117
- ), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { onPress: () => setShowDetails(!showDetails), style: styles2.detailsToggle }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ React3.createElement(View2, { style: styles2.detailsSection }, testCase.key && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.detailMeta }, testCase.key, " \xB7 ", testCase.priority, " \xB7 ", info.name), testCase.description && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.detailDesc }, testCase.description), testCase.group && /* @__PURE__ */ React3.createElement(View2, { style: styles2.folderProgress }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.folderName }, "\u{1F4C1} ", testCase.group.name))), /* @__PURE__ */ React3.createElement(View2, { style: styles2.actionButtons }, /* @__PURE__ */ React3.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }], onPress: handleFail, disabled: isSubmitting }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.failBtnText }, isSubmitting ? "Failing..." : "Fail")), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.skipBtn, isSubmitting && { opacity: 0.5 }], onPress: () => setShowSkipModal(true), disabled: isSubmitting }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.skipBtnText }, "Skip")), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }], onPress: handlePass, disabled: isSubmitting }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.passBtnText }, isSubmitting ? "Passing..." : "Pass"))), /* @__PURE__ */ React3.createElement(Modal, { visible: showSkipModal, transparent: true, animationType: "fade" }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.modalOverlay }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.modalContent }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.modalTitle }, "Skip this test?"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.modalSubtitle }, "Select a reason:"), [
14534
+ /* @__PURE__ */ React4.createElement(Text2, { style: styles2.navigateText }, "\u{1F9ED} Go to test location")
14535
+ ), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { onPress: () => setShowDetails(!showDetails), style: styles2.detailsToggle }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ React4.createElement(View3, { style: styles2.detailsSection }, testCase.key && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.detailMeta }, testCase.key, " \xB7 ", testCase.priority, " \xB7 ", info.name), testCase.description && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.detailDesc }, testCase.description), testCase.group && /* @__PURE__ */ React4.createElement(View3, { style: styles2.folderProgress }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.folderName }, "\u{1F4C1} ", testCase.group.name))), /* @__PURE__ */ React4.createElement(View3, { style: styles2.actionButtons }, /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }], onPress: handleFail, disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.failBtnText }, isSubmitting ? "Failing..." : "Fail")), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.skipBtn, isSubmitting && { opacity: 0.5 }], onPress: () => setShowSkipModal(true), disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.skipBtnText }, "Skip")), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }], onPress: handlePass, disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.passBtnText }, isSubmitting ? "Passing..." : "Pass"))), /* @__PURE__ */ React4.createElement(Modal, { visible: showSkipModal, transparent: true, animationType: "fade" }, /* @__PURE__ */ React4.createElement(View3, { style: styles2.modalOverlay }, /* @__PURE__ */ React4.createElement(View3, { style: styles2.modalContent }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.modalTitle }, "Skip this test?"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.modalSubtitle }, "Select a reason:"), [
14118
14536
  { reason: "blocked", label: "\u{1F6AB} Blocked by a bug" },
14119
14537
  { reason: "not_ready", label: "\u{1F6A7} Feature not ready" },
14120
14538
  { reason: "dependency", label: "\u{1F517} Needs another test first" },
14121
14539
  { reason: "other", label: "\u{1F4DD} Other reason" }
14122
- ].map(({ reason, label }) => /* @__PURE__ */ React3.createElement(
14540
+ ].map(({ reason, label }) => /* @__PURE__ */ React4.createElement(
14123
14541
  TouchableOpacity2,
14124
14542
  {
14125
14543
  key: reason,
14126
14544
  style: [styles2.skipOption, selectedSkipReason === reason && styles2.skipOptionActive],
14127
14545
  onPress: () => setSelectedSkipReason(reason)
14128
14546
  },
14129
- /* @__PURE__ */ React3.createElement(Text2, { style: [styles2.skipOptionText, selectedSkipReason === reason && styles2.skipOptionTextActive] }, label)
14130
- )), /* @__PURE__ */ React3.createElement(
14547
+ /* @__PURE__ */ React4.createElement(Text2, { style: [styles2.skipOptionText, selectedSkipReason === reason && styles2.skipOptionTextActive] }, label)
14548
+ )), /* @__PURE__ */ React4.createElement(
14131
14549
  TextInput,
14132
14550
  {
14133
14551
  style: styles2.skipNotes,
@@ -14137,21 +14555,21 @@ function TestDetailScreen({ testId, nav }) {
14137
14555
  placeholderTextColor: colors.textMuted,
14138
14556
  multiline: true
14139
14557
  }
14140
- ), /* @__PURE__ */ React3.createElement(View2, { style: styles2.skipActions }, /* @__PURE__ */ React3.createElement(TouchableOpacity2, { style: styles2.skipCancel, onPress: () => {
14558
+ ), /* @__PURE__ */ React4.createElement(View3, { style: styles2.skipActions }, /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: styles2.skipCancel, onPress: () => {
14141
14559
  setShowSkipModal(false);
14142
14560
  setSelectedSkipReason(null);
14143
14561
  setSkipNotes("");
14144
- } }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.skipCancelText }, "Cancel")), /* @__PURE__ */ React3.createElement(
14562
+ } }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.skipCancelText }, "Cancel")), /* @__PURE__ */ React4.createElement(
14145
14563
  TouchableOpacity2,
14146
14564
  {
14147
14565
  style: [styles2.skipConfirm, !selectedSkipReason && { opacity: 0.4 }],
14148
14566
  onPress: handleSkip,
14149
14567
  disabled: !selectedSkipReason || skipping
14150
14568
  },
14151
- /* @__PURE__ */ React3.createElement(Text2, { style: styles2.skipConfirmText }, skipping ? "Skipping..." : "Skip Test")
14569
+ /* @__PURE__ */ React4.createElement(Text2, { style: styles2.skipConfirmText }, skipping ? "Skipping..." : "Skip Test")
14152
14570
  ))))));
14153
14571
  }
14154
- var styles2 = StyleSheet3.create({
14572
+ var styles2 = StyleSheet4.create({
14155
14573
  container: { paddingBottom: 16 },
14156
14574
  retestBanner: { flexDirection: "row", alignItems: "center", gap: 6, backgroundColor: "#422006", borderWidth: 1, borderColor: "#854d0e", borderRadius: 8, paddingVertical: 6, paddingHorizontal: 10, marginBottom: 10 },
14157
14575
  retestIcon: { fontSize: 14 },
@@ -14246,14 +14664,17 @@ var styles2 = StyleSheet3.create({
14246
14664
  });
14247
14665
 
14248
14666
  // src/widget/screens/TestListScreen.tsx
14249
- import React4, { useState as useState3, useMemo as useMemo2, useCallback as useCallback3, useEffect as useEffect4 } from "react";
14250
- import { View as View3, Text as Text3, TouchableOpacity as TouchableOpacity3, StyleSheet as StyleSheet4, ScrollView } from "react-native";
14667
+ import React5, { useState as useState3, useMemo as useMemo2, useCallback as useCallback3, useEffect as useEffect5 } from "react";
14668
+ import { View as View4, Text as Text3, TouchableOpacity as TouchableOpacity3, StyleSheet as StyleSheet5, ScrollView, TextInput as TextInput2 } from "react-native";
14251
14669
  function TestListScreen({ nav }) {
14252
- const { assignments, currentAssignment, refreshAssignments } = useBugBear();
14670
+ const { assignments, currentAssignment, refreshAssignments, isLoading } = useBugBear();
14253
14671
  const [filter, setFilter] = useState3("all");
14254
14672
  const [roleFilter, setRoleFilter] = useState3(null);
14673
+ const [trackFilter, setTrackFilter] = useState3(null);
14674
+ const [searchQuery, setSearchQuery] = useState3("");
14675
+ const [sortMode, setSortMode] = useState3("priority");
14255
14676
  const [collapsedFolders, setCollapsedFolders] = useState3(/* @__PURE__ */ new Set());
14256
- useEffect4(() => {
14677
+ useEffect5(() => {
14257
14678
  refreshAssignments();
14258
14679
  }, []);
14259
14680
  const availableRoles = useMemo2(() => {
@@ -14263,6 +14684,13 @@ function TestListScreen({ nav }) {
14263
14684
  }
14264
14685
  return Array.from(roleMap.values());
14265
14686
  }, [assignments]);
14687
+ const availableTracks = useMemo2(() => {
14688
+ const trackMap = /* @__PURE__ */ new Map();
14689
+ for (const a of assignments) {
14690
+ if (a.testCase.track) trackMap.set(a.testCase.track.id, a.testCase.track);
14691
+ }
14692
+ return Array.from(trackMap.values());
14693
+ }, [assignments]);
14266
14694
  const selectedRole = availableRoles.find((r) => r.id === roleFilter);
14267
14695
  const groupedAssignments = useMemo2(() => {
14268
14696
  const groups = /* @__PURE__ */ new Map();
@@ -14286,6 +14714,8 @@ function TestListScreen({ nav }) {
14286
14714
  folder.assignments.sort((a, b) => {
14287
14715
  if (a.isVerification && !b.isVerification) return -1;
14288
14716
  if (!a.isVerification && b.isVerification) return 1;
14717
+ if (sortMode === "alpha") return a.testCase.title.localeCompare(b.testCase.title);
14718
+ if (sortMode === "recent") return 0;
14289
14719
  const sd = (statusOrder[a.status] ?? 5) - (statusOrder[b.status] ?? 5);
14290
14720
  if (sd !== 0) return sd;
14291
14721
  return (priorityOrder[a.testCase.priority] ?? 4) - (priorityOrder[b.testCase.priority] ?? 4);
@@ -14297,7 +14727,7 @@ function TestListScreen({ nav }) {
14297
14727
  if (!b.group) return -1;
14298
14728
  return a.group.sortOrder - b.group.sortOrder;
14299
14729
  });
14300
- }, [assignments]);
14730
+ }, [assignments, sortMode]);
14301
14731
  const toggleFolder = useCallback3((id) => {
14302
14732
  setCollapsedFolders((prev) => {
14303
14733
  const next = new Set(prev);
@@ -14306,28 +14736,36 @@ function TestListScreen({ nav }) {
14306
14736
  return next;
14307
14737
  });
14308
14738
  }, []);
14309
- const filterAssignment = (a) => {
14739
+ const filterAssignment = useCallback3((a) => {
14310
14740
  if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
14741
+ if (trackFilter && a.testCase.track?.id !== trackFilter) return false;
14742
+ if (searchQuery) {
14743
+ const q = searchQuery.toLowerCase();
14744
+ const titleMatch = a.testCase.title.toLowerCase().includes(q);
14745
+ const keyMatch = a.testCase.testKey.toLowerCase().includes(q);
14746
+ if (!titleMatch && !keyMatch) return false;
14747
+ }
14311
14748
  if (filter === "pending") return a.status === "pending" || a.status === "in_progress";
14312
14749
  if (filter === "done") return a.status === "passed";
14313
14750
  if (filter === "reopened") return a.status === "failed";
14314
14751
  return true;
14315
- };
14316
- return /* @__PURE__ */ React4.createElement(View3, null, /* @__PURE__ */ React4.createElement(View3, { style: styles3.filterBar }, [
14752
+ }, [roleFilter, trackFilter, searchQuery, filter]);
14753
+ if (isLoading) return /* @__PURE__ */ React5.createElement(TestListScreenSkeleton, null);
14754
+ return /* @__PURE__ */ React5.createElement(View4, null, /* @__PURE__ */ React5.createElement(View4, { style: styles3.filterBar }, [
14317
14755
  { key: "all", label: "All", count: assignments.length },
14318
14756
  { key: "pending", label: "To Do", count: assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length },
14319
14757
  { key: "done", label: "Done", count: assignments.filter((a) => a.status === "passed").length },
14320
14758
  { key: "reopened", label: "Re Opened", count: assignments.filter((a) => a.status === "failed").length }
14321
- ].map((f) => /* @__PURE__ */ React4.createElement(TouchableOpacity3, { key: f.key, style: [styles3.filterBtn, filter === f.key && styles3.filterBtnActive], onPress: () => setFilter(f.key) }, /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.filterBtnText, filter === f.key && styles3.filterBtnTextActive] }, f.label, " (", f.count, ")")))), availableRoles.length >= 2 && /* @__PURE__ */ React4.createElement(View3, { style: styles3.roleSection }, /* @__PURE__ */ React4.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.roleBar }, /* @__PURE__ */ React4.createElement(
14759
+ ].map((f) => /* @__PURE__ */ React5.createElement(TouchableOpacity3, { key: f.key, style: [styles3.filterBtn, filter === f.key && styles3.filterBtnActive], onPress: () => setFilter(f.key) }, /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.filterBtnText, filter === f.key && styles3.filterBtnTextActive] }, f.label, " (", f.count, ")")))), availableRoles.length >= 2 && /* @__PURE__ */ React5.createElement(View4, { style: styles3.roleSection }, /* @__PURE__ */ React5.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.roleBar }, /* @__PURE__ */ React5.createElement(
14322
14760
  TouchableOpacity3,
14323
14761
  {
14324
14762
  style: [styles3.roleBtn, !roleFilter && styles3.roleBtnActive],
14325
14763
  onPress: () => setRoleFilter(null)
14326
14764
  },
14327
- /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.roleBtnText, !roleFilter && styles3.roleBtnTextActive] }, "All Roles")
14765
+ /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.roleBtnText, !roleFilter && styles3.roleBtnTextActive] }, "All Roles")
14328
14766
  ), availableRoles.map((role) => {
14329
14767
  const isActive = roleFilter === role.id;
14330
- return /* @__PURE__ */ React4.createElement(
14768
+ return /* @__PURE__ */ React5.createElement(
14331
14769
  TouchableOpacity3,
14332
14770
  {
14333
14771
  key: role.id,
@@ -14337,33 +14775,72 @@ function TestListScreen({ nav }) {
14337
14775
  ],
14338
14776
  onPress: () => setRoleFilter(isActive ? null : role.id)
14339
14777
  },
14340
- /* @__PURE__ */ React4.createElement(View3, { style: [styles3.roleDot, { backgroundColor: role.color }] }),
14341
- /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.roleBtnText, isActive && { color: role.color, fontWeight: "600" }] }, role.name)
14778
+ /* @__PURE__ */ React5.createElement(View4, { style: [styles3.roleDot, { backgroundColor: role.color }] }),
14779
+ /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.roleBtnText, isActive && { color: role.color, fontWeight: "600" }] }, role.name)
14342
14780
  );
14343
- })), selectedRole?.loginHint && /* @__PURE__ */ React4.createElement(View3, { style: [styles3.loginHint, { backgroundColor: selectedRole.color + "10", borderColor: selectedRole.color + "30" }] }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.loginHintText }, "Log in as: ", selectedRole.loginHint))), groupedAssignments.map((folder) => {
14781
+ })), selectedRole?.loginHint && /* @__PURE__ */ React5.createElement(View4, { style: [styles3.loginHint, { backgroundColor: selectedRole.color + "10", borderColor: selectedRole.color + "30" }] }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.loginHintText }, "Log in as: ", selectedRole.loginHint))), /* @__PURE__ */ React5.createElement(View4, { style: styles3.searchContainer }, /* @__PURE__ */ React5.createElement(
14782
+ TextInput2,
14783
+ {
14784
+ value: searchQuery,
14785
+ onChangeText: setSearchQuery,
14786
+ placeholder: "Search tests...",
14787
+ placeholderTextColor: colors.textMuted,
14788
+ style: styles3.searchInput
14789
+ }
14790
+ )), /* @__PURE__ */ React5.createElement(View4, { style: styles3.trackSortRow }, availableTracks.length >= 2 && /* @__PURE__ */ React5.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: { flex: 1 } }, /* @__PURE__ */ React5.createElement(
14791
+ TouchableOpacity3,
14792
+ {
14793
+ style: [styles3.trackBtn, !trackFilter && styles3.trackBtnActive],
14794
+ onPress: () => setTrackFilter(null)
14795
+ },
14796
+ /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.trackBtnText, !trackFilter && styles3.trackBtnTextActive] }, "All Tracks")
14797
+ ), availableTracks.map((track) => {
14798
+ const isActive = trackFilter === track.id;
14799
+ return /* @__PURE__ */ React5.createElement(
14800
+ TouchableOpacity3,
14801
+ {
14802
+ key: track.id,
14803
+ style: [styles3.trackBtn, isActive && { backgroundColor: track.color + "20", borderColor: track.color + "60", borderWidth: 1 }],
14804
+ onPress: () => setTrackFilter(isActive ? null : track.id)
14805
+ },
14806
+ /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.trackBtnText, isActive && { color: track.color, fontWeight: "600" }] }, track.icon, " ", track.name)
14807
+ );
14808
+ })), /* @__PURE__ */ React5.createElement(View4, { style: styles3.sortGroup }, [
14809
+ { key: "priority", label: "\u2195" },
14810
+ { key: "recent", label: "\u{1F550}" },
14811
+ { key: "alpha", label: "AZ" }
14812
+ ].map((s2) => /* @__PURE__ */ React5.createElement(
14813
+ TouchableOpacity3,
14814
+ {
14815
+ key: s2.key,
14816
+ style: [styles3.sortBtn, sortMode === s2.key && styles3.sortBtnActive],
14817
+ onPress: () => setSortMode(s2.key)
14818
+ },
14819
+ /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.sortBtnText, sortMode === s2.key && styles3.sortBtnTextActive] }, s2.label)
14820
+ )))), groupedAssignments.map((folder) => {
14344
14821
  const folderId = folder.group?.id || "ungrouped";
14345
14822
  const isCollapsed = collapsedFolders.has(folderId);
14346
14823
  const filtered = folder.assignments.filter(filterAssignment);
14347
14824
  if (filtered.length === 0 && filter !== "all") return null;
14348
- return /* @__PURE__ */ React4.createElement(View3, { key: folderId, style: styles3.folder }, /* @__PURE__ */ React4.createElement(TouchableOpacity3, { style: styles3.folderHeader, onPress: () => toggleFolder(folderId) }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderToggle }, isCollapsed ? "\u25B6" : "\u25BC"), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderName, numberOfLines: 1 }, folder.group?.name || "Ungrouped"), /* @__PURE__ */ React4.createElement(View3, { style: styles3.folderProgress }, /* @__PURE__ */ React4.createElement(View3, { style: [styles3.folderProgressFill, { width: `${folder.stats.total > 0 ? Math.round((folder.stats.passed + folder.stats.failed) / folder.stats.total * 100) : 0}%` }] })), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderCount }, folder.stats.passed + folder.stats.failed, "/", folder.stats.total)), !isCollapsed && filtered.map((assignment) => {
14825
+ return /* @__PURE__ */ React5.createElement(View4, { key: folderId, style: styles3.folder }, /* @__PURE__ */ React5.createElement(TouchableOpacity3, { style: styles3.folderHeader, onPress: () => toggleFolder(folderId) }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.folderToggle }, isCollapsed ? "\u25B6" : "\u25BC"), /* @__PURE__ */ React5.createElement(Text3, { style: styles3.folderName, numberOfLines: 1 }, folder.group?.name || "Ungrouped"), /* @__PURE__ */ React5.createElement(View4, { style: styles3.folderProgress }, /* @__PURE__ */ React5.createElement(View4, { style: [styles3.folderProgressFill, { width: `${folder.stats.total > 0 ? Math.round((folder.stats.passed + folder.stats.failed) / folder.stats.total * 100) : 0}%` }] })), /* @__PURE__ */ React5.createElement(Text3, { style: styles3.folderCount }, folder.stats.passed + folder.stats.failed, "/", folder.stats.total)), !isCollapsed && filtered.map((assignment) => {
14349
14826
  const badge = getStatusBadge(assignment.status);
14350
14827
  const isCurrent = currentAssignment?.id === assignment.id;
14351
- return /* @__PURE__ */ React4.createElement(
14828
+ return /* @__PURE__ */ React5.createElement(
14352
14829
  TouchableOpacity3,
14353
14830
  {
14354
14831
  key: assignment.id,
14355
14832
  style: [styles3.testItem, isCurrent && styles3.testItemCurrent],
14356
14833
  onPress: () => nav.push({ name: "TEST_DETAIL", testId: assignment.id })
14357
14834
  },
14358
- /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testBadge }, badge.icon),
14359
- /* @__PURE__ */ React4.createElement(View3, { style: styles3.testInfo }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testTitle, numberOfLines: 1 }, assignment.testCase.title), /* @__PURE__ */ React4.createElement(View3, { style: styles3.testMetaRow }, assignment.isVerification && /* @__PURE__ */ React4.createElement(View3, { style: styles3.retestTag }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.retestTagText }, "Retest")), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testMeta }, assignment.testCase.testKey, " \xB7 ", assignment.testCase.priority), assignment.testCase.role && /* @__PURE__ */ React4.createElement(View3, { style: styles3.roleBadgeRow }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testMeta }, " \xB7 "), /* @__PURE__ */ React4.createElement(View3, { style: [styles3.roleBadgeDot, { backgroundColor: assignment.testCase.role.color }] }), /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.testMeta, { color: assignment.testCase.role.color, fontWeight: "500" }] }, assignment.testCase.role.name)))),
14360
- /* @__PURE__ */ React4.createElement(View3, { style: [
14835
+ /* @__PURE__ */ React5.createElement(Text3, { style: styles3.testBadge }, badge.icon),
14836
+ /* @__PURE__ */ React5.createElement(View4, { style: styles3.testInfo }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.testTitle, numberOfLines: 1 }, assignment.testCase.title), /* @__PURE__ */ React5.createElement(View4, { style: styles3.testMetaRow }, assignment.isVerification && /* @__PURE__ */ React5.createElement(View4, { style: styles3.retestTag }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.retestTagText }, "Retest")), /* @__PURE__ */ React5.createElement(Text3, { style: styles3.testMeta }, assignment.testCase.testKey, " \xB7 ", assignment.testCase.priority), assignment.testCase.role && /* @__PURE__ */ React5.createElement(View4, { style: styles3.roleBadgeRow }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.testMeta }, " \xB7 "), /* @__PURE__ */ React5.createElement(View4, { style: [styles3.roleBadgeDot, { backgroundColor: assignment.testCase.role.color }] }), /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.testMeta, { color: assignment.testCase.role.color, fontWeight: "500" }] }, assignment.testCase.role.name)))),
14837
+ /* @__PURE__ */ React5.createElement(View4, { style: [
14361
14838
  styles3.statusPill,
14362
14839
  {
14363
14840
  backgroundColor: assignment.status === "passed" ? "#14532d" : assignment.status === "failed" ? "#450a0a" : assignment.status === "in_progress" ? "#172554" : "#27272a",
14364
14841
  borderColor: assignment.status === "passed" ? "#166534" : assignment.status === "failed" ? "#7f1d1d" : assignment.status === "in_progress" ? "#1e3a5f" : "#3f3f46"
14365
14842
  }
14366
- ] }, /* @__PURE__ */ React4.createElement(Text3, { style: [
14843
+ ] }, /* @__PURE__ */ React5.createElement(Text3, { style: [
14367
14844
  styles3.statusPillText,
14368
14845
  {
14369
14846
  color: assignment.status === "passed" ? "#4ade80" : assignment.status === "failed" ? "#f87171" : assignment.status === "in_progress" ? "#60a5fa" : "#d4d4d8"
@@ -14371,9 +14848,9 @@ function TestListScreen({ nav }) {
14371
14848
  ] }, badge.label))
14372
14849
  );
14373
14850
  }));
14374
- }), /* @__PURE__ */ React4.createElement(TouchableOpacity3, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.refreshText }, "\u21BB", " Refresh")));
14851
+ }), /* @__PURE__ */ React5.createElement(TouchableOpacity3, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.refreshText }, "\u21BB", " Refresh")));
14375
14852
  }
14376
- var styles3 = StyleSheet4.create({
14853
+ var styles3 = StyleSheet5.create({
14377
14854
  filterBar: { flexDirection: "row", gap: 8, marginBottom: 8 },
14378
14855
  filterBtn: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 8, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
14379
14856
  filterBtnActive: { backgroundColor: colors.blue, borderColor: colors.blue },
@@ -14408,13 +14885,25 @@ var styles3 = StyleSheet4.create({
14408
14885
  testMeta: { fontSize: 11, color: colors.textDim },
14409
14886
  statusPill: { paddingHorizontal: 8, paddingVertical: 3, borderRadius: 6, borderWidth: 1, marginLeft: 8 },
14410
14887
  statusPillText: { fontSize: 10, fontWeight: "600" },
14888
+ searchContainer: { marginBottom: 8 },
14889
+ searchInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 8, paddingHorizontal: 12, paddingVertical: 8, fontSize: 13, color: colors.textPrimary },
14890
+ trackSortRow: { flexDirection: "row", alignItems: "center", marginBottom: 10, gap: 8 },
14891
+ trackBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
14892
+ trackBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
14893
+ trackBtnText: { fontSize: 11, color: colors.textMuted },
14894
+ trackBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
14895
+ sortGroup: { flexDirection: "row", gap: 2 },
14896
+ sortBtn: { paddingHorizontal: 8, paddingVertical: 4, borderRadius: 6, borderWidth: 1, borderColor: "transparent" },
14897
+ sortBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
14898
+ sortBtnText: { fontSize: 11, color: colors.textMuted },
14899
+ sortBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
14411
14900
  refreshBtn: { alignItems: "center", paddingVertical: 12 },
14412
14901
  refreshText: { fontSize: 13, color: colors.blue }
14413
14902
  });
14414
14903
 
14415
14904
  // src/widget/screens/TestFeedbackScreen.tsx
14416
- import React7, { useState as useState5 } from "react";
14417
- import { View as View6, Text as Text6, TouchableOpacity as TouchableOpacity6, TextInput as TextInput2, StyleSheet as StyleSheet7 } from "react-native";
14905
+ import React8, { useState as useState5 } from "react";
14906
+ import { View as View7, Text as Text6, TouchableOpacity as TouchableOpacity6, TextInput as TextInput3, StyleSheet as StyleSheet8 } from "react-native";
14418
14907
 
14419
14908
  // src/widget/useImageAttachments.ts
14420
14909
  import { useState as useState4, useCallback as useCallback4 } from "react";
@@ -14514,17 +15003,17 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
14514
15003
  }
14515
15004
 
14516
15005
  // src/widget/ImagePickerButtons.tsx
14517
- import React6 from "react";
14518
- import { View as View5, Text as Text5, TouchableOpacity as TouchableOpacity5, StyleSheet as StyleSheet6 } from "react-native";
15006
+ import React7 from "react";
15007
+ import { View as View6, Text as Text5, TouchableOpacity as TouchableOpacity5, StyleSheet as StyleSheet7 } from "react-native";
14519
15008
 
14520
15009
  // src/widget/ImagePreviewStrip.tsx
14521
- import React5 from "react";
14522
- import { View as View4, Text as Text4, TouchableOpacity as TouchableOpacity4, ScrollView as ScrollView2, Image, ActivityIndicator, StyleSheet as StyleSheet5 } from "react-native";
15010
+ import React6 from "react";
15011
+ import { View as View5, Text as Text4, TouchableOpacity as TouchableOpacity4, ScrollView as ScrollView2, Image, ActivityIndicator, StyleSheet as StyleSheet6 } from "react-native";
14523
15012
  function ImagePreviewStrip({ images, onRemove }) {
14524
15013
  if (images.length === 0) return null;
14525
- return /* @__PURE__ */ React5.createElement(ScrollView2, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles4.strip }, images.map((img) => /* @__PURE__ */ React5.createElement(View4, { key: img.id, style: styles4.thumbContainer }, /* @__PURE__ */ React5.createElement(Image, { source: { uri: img.localUri }, style: styles4.thumb }), img.status === "uploading" && /* @__PURE__ */ React5.createElement(View4, { style: styles4.thumbOverlay }, /* @__PURE__ */ React5.createElement(ActivityIndicator, { size: "small", color: "#fff" })), img.status === "error" && /* @__PURE__ */ React5.createElement(View4, { style: [styles4.thumbOverlay, styles4.thumbOverlayError] }, /* @__PURE__ */ React5.createElement(Text4, { style: styles4.thumbErrorText }, "!")), /* @__PURE__ */ React5.createElement(TouchableOpacity4, { style: styles4.thumbRemove, onPress: () => onRemove(img.id) }, /* @__PURE__ */ React5.createElement(Text4, { style: styles4.thumbRemoveText }, "\u2715")))));
15014
+ return /* @__PURE__ */ React6.createElement(ScrollView2, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles4.strip }, images.map((img) => /* @__PURE__ */ React6.createElement(View5, { key: img.id, style: styles4.thumbContainer }, /* @__PURE__ */ React6.createElement(Image, { source: { uri: img.localUri }, style: styles4.thumb }), img.status === "uploading" && /* @__PURE__ */ React6.createElement(View5, { style: styles4.thumbOverlay }, /* @__PURE__ */ React6.createElement(ActivityIndicator, { size: "small", color: "#fff" })), img.status === "error" && /* @__PURE__ */ React6.createElement(View5, { style: [styles4.thumbOverlay, styles4.thumbOverlayError] }, /* @__PURE__ */ React6.createElement(Text4, { style: styles4.thumbErrorText }, "!")), /* @__PURE__ */ React6.createElement(TouchableOpacity4, { style: styles4.thumbRemove, onPress: () => onRemove(img.id) }, /* @__PURE__ */ React6.createElement(Text4, { style: styles4.thumbRemoveText }, "\u2715")))));
14526
15015
  }
14527
- var styles4 = StyleSheet5.create({
15016
+ var styles4 = StyleSheet6.create({
14528
15017
  strip: {
14529
15018
  flexDirection: "row",
14530
15019
  marginTop: 4
@@ -14543,7 +15032,7 @@ var styles4 = StyleSheet5.create({
14543
15032
  borderRadius: 8
14544
15033
  },
14545
15034
  thumbOverlay: {
14546
- ...StyleSheet5.absoluteFillObject,
15035
+ ...StyleSheet6.absoluteFillObject,
14547
15036
  backgroundColor: "rgba(0,0,0,0.5)",
14548
15037
  justifyContent: "center",
14549
15038
  alignItems: "center",
@@ -14578,25 +15067,25 @@ var styles4 = StyleSheet5.create({
14578
15067
  // src/widget/ImagePickerButtons.tsx
14579
15068
  function ImagePickerButtons({ images, maxImages, onPickGallery, onPickCamera, onRemove, label }) {
14580
15069
  if (!IMAGE_PICKER_AVAILABLE) return null;
14581
- return /* @__PURE__ */ React6.createElement(View5, { style: styles5.section }, label && /* @__PURE__ */ React6.createElement(Text5, { style: styles5.label }, label), /* @__PURE__ */ React6.createElement(View5, { style: styles5.buttonRow }, /* @__PURE__ */ React6.createElement(
15070
+ return /* @__PURE__ */ React7.createElement(View6, { style: styles5.section }, label && /* @__PURE__ */ React7.createElement(Text5, { style: styles5.label }, label), /* @__PURE__ */ React7.createElement(View6, { style: styles5.buttonRow }, /* @__PURE__ */ React7.createElement(
14582
15071
  TouchableOpacity5,
14583
15072
  {
14584
15073
  style: styles5.pickButton,
14585
15074
  onPress: onPickGallery,
14586
15075
  disabled: images.length >= maxImages
14587
15076
  },
14588
- /* @__PURE__ */ React6.createElement(Text5, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Gallery")
14589
- ), /* @__PURE__ */ React6.createElement(
15077
+ /* @__PURE__ */ React7.createElement(Text5, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Gallery")
15078
+ ), /* @__PURE__ */ React7.createElement(
14590
15079
  TouchableOpacity5,
14591
15080
  {
14592
15081
  style: styles5.pickButton,
14593
15082
  onPress: onPickCamera,
14594
15083
  disabled: images.length >= maxImages
14595
15084
  },
14596
- /* @__PURE__ */ React6.createElement(Text5, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Camera")
14597
- ), /* @__PURE__ */ React6.createElement(Text5, { style: styles5.countText }, images.length, "/", maxImages)), /* @__PURE__ */ React6.createElement(ImagePreviewStrip, { images, onRemove }));
15085
+ /* @__PURE__ */ React7.createElement(Text5, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Camera")
15086
+ ), /* @__PURE__ */ React7.createElement(Text5, { style: styles5.countText }, images.length, "/", maxImages)), /* @__PURE__ */ React7.createElement(ImagePreviewStrip, { images, onRemove }));
14598
15087
  }
14599
- var styles5 = StyleSheet6.create({
15088
+ var styles5 = StyleSheet7.create({
14600
15089
  section: {
14601
15090
  marginTop: 12,
14602
15091
  marginBottom: 4
@@ -14697,22 +15186,22 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
14697
15186
  }
14698
15187
  }
14699
15188
  };
14700
- return /* @__PURE__ */ React7.createElement(View6, { style: styles6.container }, /* @__PURE__ */ React7.createElement(Text6, { style: styles6.header }, status === "passed" ? "\u2705 Test Passed!" : "\u274C Test Failed"), /* @__PURE__ */ React7.createElement(Text6, { style: styles6.subheader }, "Rate this test case"), /* @__PURE__ */ React7.createElement(View6, { style: styles6.starRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ React7.createElement(TouchableOpacity6, { key: n, onPress: () => setRating(n), style: styles6.starButton }, /* @__PURE__ */ React7.createElement(Text6, { style: [styles6.star, n <= rating && styles6.starActive] }, n <= rating ? "\u2605" : "\u2606")))), showFlags && /* @__PURE__ */ React7.createElement(View6, { style: styles6.flagsSection }, /* @__PURE__ */ React7.createElement(Text6, { style: styles6.flagsLabel }, "What could be improved?"), [
15189
+ return /* @__PURE__ */ React8.createElement(View7, { style: styles6.container }, /* @__PURE__ */ React8.createElement(Text6, { style: styles6.header }, status === "passed" ? "\u2705 Test Passed!" : "\u274C Test Failed"), /* @__PURE__ */ React8.createElement(Text6, { style: styles6.subheader }, "Rate this test case"), /* @__PURE__ */ React8.createElement(View7, { style: styles6.starRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ React8.createElement(TouchableOpacity6, { key: n, onPress: () => setRating(n), style: styles6.starButton }, /* @__PURE__ */ React8.createElement(Text6, { style: [styles6.star, n <= rating && styles6.starActive] }, n <= rating ? "\u2605" : "\u2606")))), showFlags && /* @__PURE__ */ React8.createElement(View7, { style: styles6.flagsSection }, /* @__PURE__ */ React8.createElement(Text6, { style: styles6.flagsLabel }, "What could be improved?"), [
14701
15190
  { key: "isOutdated", label: "Test is outdated" },
14702
15191
  { key: "needsMoreDetail", label: "Needs more detail" },
14703
15192
  { key: "stepsUnclear", label: "Steps are unclear" },
14704
15193
  { key: "expectedResultUnclear", label: "Expected result unclear" }
14705
- ].map(({ key, label }) => /* @__PURE__ */ React7.createElement(
15194
+ ].map(({ key, label }) => /* @__PURE__ */ React8.createElement(
14706
15195
  TouchableOpacity6,
14707
15196
  {
14708
15197
  key,
14709
15198
  style: [styles6.flagItem, flags[key] && styles6.flagItemActive],
14710
15199
  onPress: () => setFlags((prev) => ({ ...prev, [key]: !prev[key] }))
14711
15200
  },
14712
- /* @__PURE__ */ React7.createElement(View6, { style: [styles6.flagCheck, flags[key] && styles6.flagCheckActive] }, flags[key] && /* @__PURE__ */ React7.createElement(Text6, { style: styles6.flagCheckmark }, "\u2713")),
14713
- /* @__PURE__ */ React7.createElement(Text6, { style: [styles6.flagText, flags[key] && styles6.flagTextActive] }, label)
14714
- ))), /* @__PURE__ */ React7.createElement(
14715
- TextInput2,
15201
+ /* @__PURE__ */ React8.createElement(View7, { style: [styles6.flagCheck, flags[key] && styles6.flagCheckActive] }, flags[key] && /* @__PURE__ */ React8.createElement(Text6, { style: styles6.flagCheckmark }, "\u2713")),
15202
+ /* @__PURE__ */ React8.createElement(Text6, { style: [styles6.flagText, flags[key] && styles6.flagTextActive] }, label)
15203
+ ))), /* @__PURE__ */ React8.createElement(
15204
+ TextInput3,
14716
15205
  {
14717
15206
  style: styles6.noteInput,
14718
15207
  value: note,
@@ -14721,7 +15210,7 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
14721
15210
  placeholderTextColor: colors.textMuted,
14722
15211
  multiline: true
14723
15212
  }
14724
- ), /* @__PURE__ */ React7.createElement(
15213
+ ), /* @__PURE__ */ React8.createElement(
14725
15214
  ImagePickerButtons,
14726
15215
  {
14727
15216
  images: images.images,
@@ -14731,9 +15220,9 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
14731
15220
  onRemove: images.removeImage,
14732
15221
  label: "Screenshots (optional)"
14733
15222
  }
14734
- ), /* @__PURE__ */ React7.createElement(View6, { style: styles6.actions }, /* @__PURE__ */ React7.createElement(TouchableOpacity6, { style: styles6.skipButton, onPress: handleSkip }, /* @__PURE__ */ React7.createElement(Text6, { style: styles6.skipText }, "Skip")), /* @__PURE__ */ React7.createElement(TouchableOpacity6, { style: [shared.primaryButton, { flex: 2, opacity: submitting || images.isUploading ? 0.5 : 1 }], onPress: handleSubmit, disabled: submitting || images.isUploading }, /* @__PURE__ */ React7.createElement(Text6, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : submitting ? "Submitting..." : "Submit"))));
15223
+ ), /* @__PURE__ */ React8.createElement(View7, { style: styles6.actions }, /* @__PURE__ */ React8.createElement(TouchableOpacity6, { style: styles6.skipButton, onPress: handleSkip }, /* @__PURE__ */ React8.createElement(Text6, { style: styles6.skipText }, "Skip")), /* @__PURE__ */ React8.createElement(TouchableOpacity6, { style: [shared.primaryButton, { flex: 2, opacity: submitting || images.isUploading ? 0.5 : 1 }], onPress: handleSubmit, disabled: submitting || images.isUploading }, /* @__PURE__ */ React8.createElement(Text6, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : submitting ? "Submitting..." : "Submit"))));
14735
15224
  }
14736
- var styles6 = StyleSheet7.create({
15225
+ var styles6 = StyleSheet8.create({
14737
15226
  container: { paddingTop: 8 },
14738
15227
  header: { fontSize: 22, fontWeight: "700", color: colors.textPrimary, textAlign: "center", marginBottom: 4 },
14739
15228
  subheader: { fontSize: 14, color: colors.textMuted, textAlign: "center", marginBottom: 20 },
@@ -14757,12 +15246,12 @@ var styles6 = StyleSheet7.create({
14757
15246
  });
14758
15247
 
14759
15248
  // src/widget/screens/ReportScreen.tsx
14760
- import React9, { useState as useState7, useRef as useRef2, useEffect as useEffect5 } from "react";
14761
- import { View as View8, Text as Text8, TouchableOpacity as TouchableOpacity8, TextInput as TextInput3, StyleSheet as StyleSheet9 } from "react-native";
15249
+ import React10, { useState as useState7, useRef as useRef3, useEffect as useEffect6 } from "react";
15250
+ import { View as View9, Text as Text8, TouchableOpacity as TouchableOpacity8, TextInput as TextInput4, StyleSheet as StyleSheet10 } from "react-native";
14762
15251
 
14763
15252
  // src/widget/CategoryPicker.tsx
14764
- import React8, { useState as useState6 } from "react";
14765
- import { View as View7, Text as Text7, TouchableOpacity as TouchableOpacity7, Modal as Modal2, StyleSheet as StyleSheet8 } from "react-native";
15253
+ import React9, { useState as useState6 } from "react";
15254
+ import { View as View8, Text as Text7, TouchableOpacity as TouchableOpacity7, Modal as Modal2, StyleSheet as StyleSheet9 } from "react-native";
14766
15255
  var categoryOptions = [
14767
15256
  { value: "ui_ux", label: "UI/UX", icon: "\u{1F3A8}" },
14768
15257
  { value: "functional", label: "Functional", icon: "\u2699\uFE0F" },
@@ -14777,15 +15266,15 @@ function CategoryPicker({ value, onChange, optional = true }) {
14777
15266
  onChange(category);
14778
15267
  setModalVisible(false);
14779
15268
  };
14780
- return /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(
15269
+ return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(
14781
15270
  TouchableOpacity7,
14782
15271
  {
14783
15272
  style: styles7.trigger,
14784
15273
  onPress: () => setModalVisible(true)
14785
15274
  },
14786
- /* @__PURE__ */ React8.createElement(Text7, { style: selectedOption ? styles7.triggerTextSelected : styles7.triggerTextPlaceholder }, selectedOption ? `${selectedOption.icon} ${selectedOption.label}` : optional ? "Select category (optional)" : "Select category"),
14787
- /* @__PURE__ */ React8.createElement(Text7, { style: styles7.chevron }, "\u25BC")
14788
- ), /* @__PURE__ */ React8.createElement(
15275
+ /* @__PURE__ */ React9.createElement(Text7, { style: selectedOption ? styles7.triggerTextSelected : styles7.triggerTextPlaceholder }, selectedOption ? `${selectedOption.icon} ${selectedOption.label}` : optional ? "Select category (optional)" : "Select category"),
15276
+ /* @__PURE__ */ React9.createElement(Text7, { style: styles7.chevron }, "\u25BC")
15277
+ ), /* @__PURE__ */ React9.createElement(
14789
15278
  Modal2,
14790
15279
  {
14791
15280
  visible: modalVisible,
@@ -14793,42 +15282,42 @@ function CategoryPicker({ value, onChange, optional = true }) {
14793
15282
  animationType: "fade",
14794
15283
  onRequestClose: () => setModalVisible(false)
14795
15284
  },
14796
- /* @__PURE__ */ React8.createElement(
15285
+ /* @__PURE__ */ React9.createElement(
14797
15286
  TouchableOpacity7,
14798
15287
  {
14799
15288
  style: styles7.overlay,
14800
15289
  activeOpacity: 1,
14801
15290
  onPress: () => setModalVisible(false)
14802
15291
  },
14803
- /* @__PURE__ */ React8.createElement(View7, { style: styles7.modal, onStartShouldSetResponder: () => true }, /* @__PURE__ */ React8.createElement(Text7, { style: styles7.modalTitle }, "Select Category"), optional && /* @__PURE__ */ React8.createElement(
15292
+ /* @__PURE__ */ React9.createElement(View8, { style: styles7.modal, onStartShouldSetResponder: () => true }, /* @__PURE__ */ React9.createElement(Text7, { style: styles7.modalTitle }, "Select Category"), optional && /* @__PURE__ */ React9.createElement(
14804
15293
  TouchableOpacity7,
14805
15294
  {
14806
15295
  style: [styles7.option, !value && styles7.optionSelected],
14807
15296
  onPress: () => handleSelect(null)
14808
15297
  },
14809
- /* @__PURE__ */ React8.createElement(Text7, { style: styles7.optionText }, "\u2014 None \u2014")
14810
- ), categoryOptions.map(({ value: optValue, label, icon }) => /* @__PURE__ */ React8.createElement(
15298
+ /* @__PURE__ */ React9.createElement(Text7, { style: styles7.optionText }, "\u2014 None \u2014")
15299
+ ), categoryOptions.map(({ value: optValue, label, icon }) => /* @__PURE__ */ React9.createElement(
14811
15300
  TouchableOpacity7,
14812
15301
  {
14813
15302
  key: optValue,
14814
15303
  style: [styles7.option, value === optValue && styles7.optionSelected],
14815
15304
  onPress: () => handleSelect(optValue)
14816
15305
  },
14817
- /* @__PURE__ */ React8.createElement(Text7, { style: styles7.optionIcon }, icon),
14818
- /* @__PURE__ */ React8.createElement(Text7, { style: styles7.optionText }, label),
14819
- value === optValue && /* @__PURE__ */ React8.createElement(Text7, { style: styles7.checkmark }, "\u2713")
14820
- )), /* @__PURE__ */ React8.createElement(
15306
+ /* @__PURE__ */ React9.createElement(Text7, { style: styles7.optionIcon }, icon),
15307
+ /* @__PURE__ */ React9.createElement(Text7, { style: styles7.optionText }, label),
15308
+ value === optValue && /* @__PURE__ */ React9.createElement(Text7, { style: styles7.checkmark }, "\u2713")
15309
+ )), /* @__PURE__ */ React9.createElement(
14821
15310
  TouchableOpacity7,
14822
15311
  {
14823
15312
  style: styles7.cancelButton,
14824
15313
  onPress: () => setModalVisible(false)
14825
15314
  },
14826
- /* @__PURE__ */ React8.createElement(Text7, { style: styles7.cancelText }, "Cancel")
15315
+ /* @__PURE__ */ React9.createElement(Text7, { style: styles7.cancelText }, "Cancel")
14827
15316
  ))
14828
15317
  )
14829
15318
  ));
14830
15319
  }
14831
- var styles7 = StyleSheet8.create({
15320
+ var styles7 = StyleSheet9.create({
14832
15321
  trigger: {
14833
15322
  flexDirection: "row",
14834
15323
  alignItems: "center",
@@ -14925,11 +15414,11 @@ function ReportScreen({ nav, prefill }) {
14925
15414
  const [affectedScreen, setAffectedScreen] = useState7("");
14926
15415
  const [submitting, setSubmitting] = useState7(false);
14927
15416
  const [error, setError] = useState7(null);
14928
- const submittingRef = useRef2(false);
15417
+ const submittingRef = useRef3(false);
14929
15418
  const images = useImageAttachments(uploadImage, 5, "screenshots");
14930
15419
  const isRetestFailure = prefill?.type === "test_fail";
14931
15420
  const isBugType = reportType === "bug" || reportType === "test_fail";
14932
- useEffect5(() => {
15421
+ useEffect6(() => {
14933
15422
  if (reportType === "feedback" || reportType === "suggestion") {
14934
15423
  setCategory("other");
14935
15424
  } else {
@@ -14978,21 +15467,21 @@ function ReportScreen({ nav, prefill }) {
14978
15467
  submittingRef.current = false;
14979
15468
  }
14980
15469
  };
14981
- return /* @__PURE__ */ React9.createElement(View8, null, isRetestFailure ? /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(View8, { style: styles8.retestBanner }, /* @__PURE__ */ React9.createElement(Text8, { style: styles8.retestIcon }, "\u{1F504}"), /* @__PURE__ */ React9.createElement(View8, null, /* @__PURE__ */ React9.createElement(Text8, { style: styles8.retestTitle }, "Bug Still Present"), /* @__PURE__ */ React9.createElement(Text8, { style: styles8.retestSubtitle }, "The fix did not resolve this issue"))), /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "Severity"), /* @__PURE__ */ React9.createElement(View8, { style: styles8.severityRow }, [
15470
+ return /* @__PURE__ */ React10.createElement(View9, null, isRetestFailure ? /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(View9, { style: styles8.retestBanner }, /* @__PURE__ */ React10.createElement(Text8, { style: styles8.retestIcon }, "\u{1F504}"), /* @__PURE__ */ React10.createElement(View9, null, /* @__PURE__ */ React10.createElement(Text8, { style: styles8.retestTitle }, "Bug Still Present"), /* @__PURE__ */ React10.createElement(Text8, { style: styles8.retestSubtitle }, "The fix did not resolve this issue"))), /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "Severity"), /* @__PURE__ */ React10.createElement(View9, { style: styles8.severityRow }, [
14982
15471
  { sev: "critical", color: "#ef4444" },
14983
15472
  { sev: "high", color: "#f97316" },
14984
15473
  { sev: "medium", color: "#eab308" },
14985
15474
  { sev: "low", color: "#6b7280" }
14986
- ].map(({ sev, color }) => /* @__PURE__ */ React9.createElement(
15475
+ ].map(({ sev, color }) => /* @__PURE__ */ React10.createElement(
14987
15476
  TouchableOpacity8,
14988
15477
  {
14989
15478
  key: sev,
14990
15479
  style: [styles8.sevButton, severity === sev && { backgroundColor: `${color}30`, borderColor: color }],
14991
15480
  onPress: () => setSeverity(sev)
14992
15481
  },
14993
- /* @__PURE__ */ React9.createElement(Text8, { style: [styles8.sevText, severity === sev && { color }] }, sev)
14994
- )))), /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ React9.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "What went wrong?"), /* @__PURE__ */ React9.createElement(
14995
- TextInput3,
15482
+ /* @__PURE__ */ React10.createElement(Text8, { style: [styles8.sevText, severity === sev && { color }] }, sev)
15483
+ )))), /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ React10.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "What went wrong?"), /* @__PURE__ */ React10.createElement(
15484
+ TextInput4,
14996
15485
  {
14997
15486
  style: styles8.descInput,
14998
15487
  value: description,
@@ -15003,7 +15492,7 @@ function ReportScreen({ nav, prefill }) {
15003
15492
  numberOfLines: 4,
15004
15493
  textAlignVertical: "top"
15005
15494
  }
15006
- )), /* @__PURE__ */ React9.createElement(
15495
+ )), /* @__PURE__ */ React10.createElement(
15007
15496
  ImagePickerButtons,
15008
15497
  {
15009
15498
  images: images.images,
@@ -15013,42 +15502,42 @@ function ReportScreen({ nav, prefill }) {
15013
15502
  onRemove: images.removeImage,
15014
15503
  label: "Attachments (optional)"
15015
15504
  }
15016
- ), error && /* @__PURE__ */ React9.createElement(View8, { style: styles8.errorBanner }, /* @__PURE__ */ React9.createElement(Text8, { style: styles8.errorText }, error)), /* @__PURE__ */ React9.createElement(
15505
+ ), error && /* @__PURE__ */ React10.createElement(View9, { style: styles8.errorBanner }, /* @__PURE__ */ React10.createElement(Text8, { style: styles8.errorText }, error)), /* @__PURE__ */ React10.createElement(
15017
15506
  TouchableOpacity8,
15018
15507
  {
15019
15508
  style: [shared.primaryButton, styles8.retestSubmitButton, (!description.trim() || submitting || images.isUploading) && shared.primaryButtonDisabled, { marginTop: 20 }],
15020
15509
  onPress: handleSubmit,
15021
15510
  disabled: !description.trim() || submitting || images.isUploading
15022
15511
  },
15023
- /* @__PURE__ */ React9.createElement(Text8, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Failed Retest")
15024
- )) : /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "What are you reporting?"), /* @__PURE__ */ React9.createElement(View8, { style: styles8.typeRow }, [
15512
+ /* @__PURE__ */ React10.createElement(Text8, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Failed Retest")
15513
+ )) : /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "What are you reporting?"), /* @__PURE__ */ React10.createElement(View9, { style: styles8.typeRow }, [
15025
15514
  { type: "bug", label: "Bug", icon: "\u{1F41B}" },
15026
15515
  { type: "feedback", label: "Feedback", icon: "\u{1F4A1}" },
15027
15516
  { type: "suggestion", label: "Idea", icon: "\u2728" }
15028
- ].map(({ type, label, icon }) => /* @__PURE__ */ React9.createElement(
15517
+ ].map(({ type, label, icon }) => /* @__PURE__ */ React10.createElement(
15029
15518
  TouchableOpacity8,
15030
15519
  {
15031
15520
  key: type,
15032
15521
  style: [styles8.typeCard, reportType === type && styles8.typeCardActive],
15033
15522
  onPress: () => setReportType(type)
15034
15523
  },
15035
- /* @__PURE__ */ React9.createElement(Text8, { style: styles8.typeIcon }, icon),
15036
- /* @__PURE__ */ React9.createElement(Text8, { style: [styles8.typeLabel, reportType === type && styles8.typeLabelActive] }, label)
15037
- ))), isBugType && /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "Severity"), /* @__PURE__ */ React9.createElement(View8, { style: styles8.severityRow }, [
15524
+ /* @__PURE__ */ React10.createElement(Text8, { style: styles8.typeIcon }, icon),
15525
+ /* @__PURE__ */ React10.createElement(Text8, { style: [styles8.typeLabel, reportType === type && styles8.typeLabelActive] }, label)
15526
+ ))), isBugType && /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "Severity"), /* @__PURE__ */ React10.createElement(View9, { style: styles8.severityRow }, [
15038
15527
  { sev: "critical", color: "#ef4444" },
15039
15528
  { sev: "high", color: "#f97316" },
15040
15529
  { sev: "medium", color: "#eab308" },
15041
15530
  { sev: "low", color: "#6b7280" }
15042
- ].map(({ sev, color }) => /* @__PURE__ */ React9.createElement(
15531
+ ].map(({ sev, color }) => /* @__PURE__ */ React10.createElement(
15043
15532
  TouchableOpacity8,
15044
15533
  {
15045
15534
  key: sev,
15046
15535
  style: [styles8.sevButton, severity === sev && { backgroundColor: `${color}30`, borderColor: color }],
15047
15536
  onPress: () => setSeverity(sev)
15048
15537
  },
15049
- /* @__PURE__ */ React9.createElement(Text8, { style: [styles8.sevText, severity === sev && { color }] }, sev)
15050
- )))), isBugType && /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ React9.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "What happened?"), /* @__PURE__ */ React9.createElement(
15051
- TextInput3,
15538
+ /* @__PURE__ */ React10.createElement(Text8, { style: [styles8.sevText, severity === sev && { color }] }, sev)
15539
+ )))), isBugType && /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ React10.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "What happened?"), /* @__PURE__ */ React10.createElement(
15540
+ TextInput4,
15052
15541
  {
15053
15542
  style: styles8.descInput,
15054
15543
  value: description,
@@ -15059,8 +15548,8 @@ function ReportScreen({ nav, prefill }) {
15059
15548
  numberOfLines: 4,
15060
15549
  textAlignVertical: "top"
15061
15550
  }
15062
- )), isBugType && /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "Which screen?"), /* @__PURE__ */ React9.createElement(
15063
- TextInput3,
15551
+ )), isBugType && /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "Which screen?"), /* @__PURE__ */ React10.createElement(
15552
+ TextInput4,
15064
15553
  {
15065
15554
  style: styles8.screenInput,
15066
15555
  value: affectedScreen,
@@ -15068,7 +15557,7 @@ function ReportScreen({ nav, prefill }) {
15068
15557
  placeholder: "e.g. Reservations, Settings...",
15069
15558
  placeholderTextColor: colors.textMuted
15070
15559
  }
15071
- ), /* @__PURE__ */ React9.createElement(Text8, { style: styles8.screenHint }, "Which screen or area was the bug on? (optional)")), /* @__PURE__ */ React9.createElement(
15560
+ ), /* @__PURE__ */ React10.createElement(Text8, { style: styles8.screenHint }, "Which screen or area was the bug on? (optional)")), /* @__PURE__ */ React10.createElement(
15072
15561
  ImagePickerButtons,
15073
15562
  {
15074
15563
  images: images.images,
@@ -15078,17 +15567,17 @@ function ReportScreen({ nav, prefill }) {
15078
15567
  onRemove: images.removeImage,
15079
15568
  label: "Screenshots (optional)"
15080
15569
  }
15081
- ), error && /* @__PURE__ */ React9.createElement(View8, { style: styles8.errorBanner }, /* @__PURE__ */ React9.createElement(Text8, { style: styles8.errorText }, error)), /* @__PURE__ */ React9.createElement(
15570
+ ), error && /* @__PURE__ */ React10.createElement(View9, { style: styles8.errorBanner }, /* @__PURE__ */ React10.createElement(Text8, { style: styles8.errorText }, error)), /* @__PURE__ */ React10.createElement(
15082
15571
  TouchableOpacity8,
15083
15572
  {
15084
15573
  style: [shared.primaryButton, (!description.trim() || submitting || images.isUploading) && shared.primaryButtonDisabled, { marginTop: 20 }],
15085
15574
  onPress: handleSubmit,
15086
15575
  disabled: !description.trim() || submitting || images.isUploading
15087
15576
  },
15088
- /* @__PURE__ */ React9.createElement(Text8, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Report")
15577
+ /* @__PURE__ */ React10.createElement(Text8, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Report")
15089
15578
  )));
15090
15579
  }
15091
- var styles8 = StyleSheet9.create({
15580
+ var styles8 = StyleSheet10.create({
15092
15581
  typeRow: { flexDirection: "row", gap: 10, marginBottom: 20 },
15093
15582
  typeCard: { flex: 1, alignItems: "center", paddingVertical: 16, borderRadius: 12, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
15094
15583
  typeCardActive: { borderColor: colors.blue, backgroundColor: "#172554" },
@@ -15112,16 +15601,16 @@ var styles8 = StyleSheet9.create({
15112
15601
  });
15113
15602
 
15114
15603
  // src/widget/screens/ReportSuccessScreen.tsx
15115
- import React10, { useEffect as useEffect6 } from "react";
15116
- import { View as View9, Text as Text9, StyleSheet as StyleSheet10 } from "react-native";
15604
+ import React11, { useEffect as useEffect7 } from "react";
15605
+ import { View as View10, Text as Text9, StyleSheet as StyleSheet11 } from "react-native";
15117
15606
  function ReportSuccessScreen({ nav }) {
15118
- useEffect6(() => {
15607
+ useEffect7(() => {
15119
15608
  const timer = setTimeout(() => nav.reset(), 2e3);
15120
15609
  return () => clearTimeout(timer);
15121
15610
  }, [nav]);
15122
- return /* @__PURE__ */ React10.createElement(View9, { style: styles9.container }, /* @__PURE__ */ React10.createElement(Text9, { style: styles9.emoji }, "\u{1F389}"), /* @__PURE__ */ React10.createElement(Text9, { style: styles9.title }, "Report submitted!"), /* @__PURE__ */ React10.createElement(Text9, { style: styles9.subtitle }, "Thank you for your feedback"));
15611
+ return /* @__PURE__ */ React11.createElement(View10, { style: styles9.container }, /* @__PURE__ */ React11.createElement(Text9, { style: styles9.emoji }, "\u{1F389}"), /* @__PURE__ */ React11.createElement(Text9, { style: styles9.title }, "Report submitted!"), /* @__PURE__ */ React11.createElement(Text9, { style: styles9.subtitle }, "Thank you for your feedback"));
15123
15612
  }
15124
- var styles9 = StyleSheet10.create({
15613
+ var styles9 = StyleSheet11.create({
15125
15614
  container: { alignItems: "center", paddingVertical: 60 },
15126
15615
  emoji: { fontSize: 48, marginBottom: 16 },
15127
15616
  title: { fontSize: 22, fontWeight: "700", color: colors.textPrimary, marginBottom: 6 },
@@ -15129,29 +15618,30 @@ var styles9 = StyleSheet10.create({
15129
15618
  });
15130
15619
 
15131
15620
  // src/widget/screens/MessageListScreen.tsx
15132
- import React11 from "react";
15133
- import { View as View10, Text as Text10, TouchableOpacity as TouchableOpacity9, StyleSheet as StyleSheet11 } from "react-native";
15621
+ import React12 from "react";
15622
+ import { View as View11, Text as Text10, TouchableOpacity as TouchableOpacity9, StyleSheet as StyleSheet12 } from "react-native";
15134
15623
  function MessageListScreen({ nav }) {
15135
- const { threads, unreadCount, refreshThreads } = useBugBear();
15136
- return /* @__PURE__ */ React11.createElement(View10, null, /* @__PURE__ */ React11.createElement(
15624
+ const { threads, unreadCount, refreshThreads, isLoading } = useBugBear();
15625
+ if (isLoading) return /* @__PURE__ */ React12.createElement(MessageListScreenSkeleton, null);
15626
+ return /* @__PURE__ */ React12.createElement(View11, null, /* @__PURE__ */ React12.createElement(
15137
15627
  TouchableOpacity9,
15138
15628
  {
15139
15629
  style: styles10.newMsgButton,
15140
15630
  onPress: () => nav.push({ name: "COMPOSE_MESSAGE" })
15141
15631
  },
15142
- /* @__PURE__ */ React11.createElement(Text10, { style: styles10.newMsgText }, "\u2709\uFE0F New Message")
15143
- ), threads.length === 0 ? /* @__PURE__ */ React11.createElement(View10, { style: shared.emptyState }, /* @__PURE__ */ React11.createElement(Text10, { style: shared.emptyEmoji }, "\u{1F4AC}"), /* @__PURE__ */ React11.createElement(Text10, { style: shared.emptyTitle }, "No messages yet"), /* @__PURE__ */ React11.createElement(Text10, { style: shared.emptySubtitle }, "Start a conversation or wait for messages from admins")) : /* @__PURE__ */ React11.createElement(View10, null, threads.map((thread) => /* @__PURE__ */ React11.createElement(
15632
+ /* @__PURE__ */ React12.createElement(Text10, { style: styles10.newMsgText }, "\u2709\uFE0F New Message")
15633
+ ), threads.length === 0 ? /* @__PURE__ */ React12.createElement(View11, { 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(View11, null, threads.map((thread) => /* @__PURE__ */ React12.createElement(
15144
15634
  TouchableOpacity9,
15145
15635
  {
15146
15636
  key: thread.id,
15147
15637
  style: [styles10.threadItem, thread.unreadCount > 0 && styles10.threadItemUnread],
15148
15638
  onPress: () => nav.push({ name: "THREAD_DETAIL", thread })
15149
15639
  },
15150
- /* @__PURE__ */ React11.createElement(View10, { style: styles10.threadLeft }, /* @__PURE__ */ React11.createElement(Text10, { style: styles10.threadIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ React11.createElement(View10, { style: styles10.threadInfo }, /* @__PURE__ */ React11.createElement(View10, { style: styles10.threadTitleRow }, thread.isPinned && /* @__PURE__ */ React11.createElement(Text10, { style: styles10.pinIcon }, "\u{1F4CC}"), /* @__PURE__ */ React11.createElement(Text10, { style: styles10.threadSubject, numberOfLines: 1 }, thread.subject || "No subject")), thread.lastMessage && /* @__PURE__ */ React11.createElement(Text10, { style: styles10.threadPreview, numberOfLines: 1 }, thread.lastMessage.senderName, ": ", thread.lastMessage.content))),
15151
- /* @__PURE__ */ React11.createElement(View10, { style: styles10.threadRight }, /* @__PURE__ */ React11.createElement(Text10, { style: styles10.threadTime }, formatRelativeTime(thread.lastMessageAt)), thread.unreadCount > 0 && /* @__PURE__ */ React11.createElement(View10, { style: styles10.unreadBadge }, /* @__PURE__ */ React11.createElement(Text10, { style: styles10.unreadText }, thread.unreadCount)), thread.priority !== "normal" && /* @__PURE__ */ React11.createElement(View10, { style: [styles10.priorityDot, { backgroundColor: getPriorityColor(thread.priority) }] }))
15152
- ))), /* @__PURE__ */ React11.createElement(View10, { style: styles10.footer }, /* @__PURE__ */ React11.createElement(Text10, { style: styles10.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ React11.createElement(TouchableOpacity9, { onPress: refreshThreads }, /* @__PURE__ */ React11.createElement(Text10, { style: styles10.refreshText }, "\u21BB Refresh"))));
15640
+ /* @__PURE__ */ React12.createElement(View11, { style: styles10.threadLeft }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.threadIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ React12.createElement(View11, { style: styles10.threadInfo }, /* @__PURE__ */ React12.createElement(View11, { style: styles10.threadTitleRow }, thread.isPinned && /* @__PURE__ */ React12.createElement(Text10, { style: styles10.pinIcon }, "\u{1F4CC}"), /* @__PURE__ */ React12.createElement(Text10, { style: styles10.threadSubject, numberOfLines: 1 }, thread.subject || "No subject")), thread.lastMessage && /* @__PURE__ */ React12.createElement(Text10, { style: styles10.threadPreview, numberOfLines: 1 }, thread.lastMessage.senderName, ": ", thread.lastMessage.content))),
15641
+ /* @__PURE__ */ React12.createElement(View11, { style: styles10.threadRight }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.threadTime }, formatRelativeTime(thread.lastMessageAt)), thread.unreadCount > 0 && /* @__PURE__ */ React12.createElement(View11, { style: styles10.unreadBadge }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.unreadText }, thread.unreadCount)), thread.priority !== "normal" && /* @__PURE__ */ React12.createElement(View11, { style: [styles10.priorityDot, { backgroundColor: getPriorityColor(thread.priority) }] }))
15642
+ ))), /* @__PURE__ */ React12.createElement(View11, { style: styles10.footer }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ React12.createElement(TouchableOpacity9, { onPress: refreshThreads }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.refreshText }, "\u21BB Refresh"))));
15153
15643
  }
15154
- var styles10 = StyleSheet11.create({
15644
+ var styles10 = StyleSheet12.create({
15155
15645
  newMsgButton: { backgroundColor: colors.blue, paddingVertical: 12, borderRadius: 12, alignItems: "center", marginBottom: 16 },
15156
15646
  newMsgText: { fontSize: 15, fontWeight: "600", color: "#fff" },
15157
15647
  threadItem: { flexDirection: "row", justifyContent: "space-between", paddingVertical: 12, paddingHorizontal: 12, borderRadius: 10, marginBottom: 4, backgroundColor: colors.card },
@@ -15174,8 +15664,8 @@ var styles10 = StyleSheet11.create({
15174
15664
  });
15175
15665
 
15176
15666
  // src/widget/screens/ThreadDetailScreen.tsx
15177
- import React12, { useState as useState8, useEffect as useEffect7 } from "react";
15178
- import { View as View11, Text as Text11, TouchableOpacity as TouchableOpacity10, TextInput as TextInput4, StyleSheet as StyleSheet12, Image as Image2 } from "react-native";
15667
+ import React13, { useState as useState8, useEffect as useEffect8 } from "react";
15668
+ import { View as View12, Text as Text11, TouchableOpacity as TouchableOpacity10, TextInput as TextInput5, StyleSheet as StyleSheet13, Image as Image2 } from "react-native";
15179
15669
  function ThreadDetailScreen({ thread, nav }) {
15180
15670
  const { getThreadMessages, sendMessage, markAsRead, uploadImage } = useBugBear();
15181
15671
  const [messages, setMessages] = useState8([]);
@@ -15184,7 +15674,7 @@ function ThreadDetailScreen({ thread, nav }) {
15184
15674
  const [sending, setSending] = useState8(false);
15185
15675
  const [sendError, setSendError] = useState8(false);
15186
15676
  const replyImages = useImageAttachments(uploadImage, 3, "discussion-attachments");
15187
- useEffect7(() => {
15677
+ useEffect8(() => {
15188
15678
  let cancelled = false;
15189
15679
  setLoading(true);
15190
15680
  (async () => {
@@ -15229,18 +15719,18 @@ function ThreadDetailScreen({ thread, nav }) {
15229
15719
  }
15230
15720
  setSending(false);
15231
15721
  };
15232
- return /* @__PURE__ */ React12.createElement(View11, { style: styles11.container }, /* @__PURE__ */ React12.createElement(View11, { style: styles11.header }, /* @__PURE__ */ React12.createElement(Text11, { style: styles11.headerIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ React12.createElement(Text11, { style: styles11.headerSubject, numberOfLines: 2 }, thread.subject || "No subject")), loading ? /* @__PURE__ */ React12.createElement(View11, { style: styles11.loadingContainer }, /* @__PURE__ */ React12.createElement(Text11, { style: styles11.loadingText }, "Loading messages...")) : /* @__PURE__ */ React12.createElement(View11, { style: styles11.messagesContainer }, messages.map((msg) => /* @__PURE__ */ React12.createElement(
15233
- View11,
15722
+ return /* @__PURE__ */ React13.createElement(View12, { style: styles11.container }, /* @__PURE__ */ React13.createElement(View12, { style: styles11.header }, /* @__PURE__ */ React13.createElement(Text11, { style: styles11.headerIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ React13.createElement(Text11, { style: styles11.headerSubject, numberOfLines: 2 }, thread.subject || "No subject")), loading ? /* @__PURE__ */ React13.createElement(View12, { style: styles11.loadingContainer }, /* @__PURE__ */ React13.createElement(Text11, { style: styles11.loadingText }, "Loading messages...")) : /* @__PURE__ */ React13.createElement(View12, { style: styles11.messagesContainer }, messages.map((msg) => /* @__PURE__ */ React13.createElement(
15723
+ View12,
15234
15724
  {
15235
15725
  key: msg.id,
15236
15726
  style: [styles11.bubble, msg.senderType === "tester" ? styles11.bubbleTester : styles11.bubbleAdmin]
15237
15727
  },
15238
- /* @__PURE__ */ React12.createElement(Text11, { style: [styles11.sender, msg.senderType === "tester" && styles11.senderTester] }, msg.senderType === "tester" ? "You" : msg.senderName),
15239
- /* @__PURE__ */ React12.createElement(Text11, { style: [styles11.content, msg.senderType === "tester" && styles11.contentTester] }, msg.content),
15240
- msg.attachments && msg.attachments.length > 0 && /* @__PURE__ */ React12.createElement(View11, { style: styles11.attachments }, msg.attachments.filter((a) => a.type === "image").map((att, idx) => /* @__PURE__ */ React12.createElement(Image2, { key: idx, source: { uri: att.url }, style: styles11.attachmentImage, resizeMode: "cover" }))),
15241
- /* @__PURE__ */ React12.createElement(Text11, { style: [styles11.time, msg.senderType === "tester" && styles11.timeTester] }, formatMessageTime(msg.createdAt))
15242
- ))), sendError && /* @__PURE__ */ React12.createElement(View11, { style: styles11.errorBar }, /* @__PURE__ */ React12.createElement(Text11, { style: styles11.errorText }, "Failed to send. Tap Send to retry.")), replyImages.images.length > 0 && /* @__PURE__ */ React12.createElement(View11, { style: styles11.replyPreview }, /* @__PURE__ */ React12.createElement(ImagePreviewStrip, { images: replyImages.images, onRemove: replyImages.removeImage })), /* @__PURE__ */ React12.createElement(View11, { style: styles11.composer }, IMAGE_PICKER_AVAILABLE && /* @__PURE__ */ React12.createElement(TouchableOpacity10, { style: styles11.attachBtn, onPress: replyImages.pickFromGallery, disabled: replyImages.images.length >= 3 }, /* @__PURE__ */ React12.createElement(Text11, { style: styles11.attachBtnText }, "\u{1F4CE}")), /* @__PURE__ */ React12.createElement(
15243
- TextInput4,
15728
+ /* @__PURE__ */ React13.createElement(Text11, { style: [styles11.sender, msg.senderType === "tester" && styles11.senderTester] }, msg.senderType === "tester" ? "You" : msg.senderName),
15729
+ /* @__PURE__ */ React13.createElement(Text11, { style: [styles11.content, msg.senderType === "tester" && styles11.contentTester] }, msg.content),
15730
+ msg.attachments && msg.attachments.length > 0 && /* @__PURE__ */ React13.createElement(View12, { style: styles11.attachments }, msg.attachments.filter((a) => a.type === "image").map((att, idx) => /* @__PURE__ */ React13.createElement(Image2, { key: idx, source: { uri: att.url }, style: styles11.attachmentImage, resizeMode: "cover" }))),
15731
+ /* @__PURE__ */ React13.createElement(Text11, { style: [styles11.time, msg.senderType === "tester" && styles11.timeTester] }, formatMessageTime(msg.createdAt))
15732
+ ))), sendError && /* @__PURE__ */ React13.createElement(View12, { style: styles11.errorBar }, /* @__PURE__ */ React13.createElement(Text11, { style: styles11.errorText }, "Failed to send. Tap Send to retry.")), replyImages.images.length > 0 && /* @__PURE__ */ React13.createElement(View12, { style: styles11.replyPreview }, /* @__PURE__ */ React13.createElement(ImagePreviewStrip, { images: replyImages.images, onRemove: replyImages.removeImage })), /* @__PURE__ */ React13.createElement(View12, { style: styles11.composer }, IMAGE_PICKER_AVAILABLE && /* @__PURE__ */ React13.createElement(TouchableOpacity10, { style: styles11.attachBtn, onPress: replyImages.pickFromGallery, disabled: replyImages.images.length >= 3 }, /* @__PURE__ */ React13.createElement(Text11, { style: styles11.attachBtnText }, "\u{1F4CE}")), /* @__PURE__ */ React13.createElement(
15733
+ TextInput5,
15244
15734
  {
15245
15735
  style: styles11.replyInput,
15246
15736
  value: replyText,
@@ -15250,17 +15740,17 @@ function ThreadDetailScreen({ thread, nav }) {
15250
15740
  multiline: true,
15251
15741
  maxLength: 1e3
15252
15742
  }
15253
- ), /* @__PURE__ */ React12.createElement(
15743
+ ), /* @__PURE__ */ React13.createElement(
15254
15744
  TouchableOpacity10,
15255
15745
  {
15256
15746
  style: [styles11.sendBtn, (!replyText.trim() && replyImages.images.length === 0 || sending || replyImages.isUploading) && styles11.sendBtnDisabled],
15257
15747
  onPress: handleSend,
15258
15748
  disabled: !replyText.trim() && replyImages.images.length === 0 || sending || replyImages.isUploading
15259
15749
  },
15260
- /* @__PURE__ */ React12.createElement(Text11, { style: styles11.sendBtnText }, sending ? "..." : "Send")
15750
+ /* @__PURE__ */ React13.createElement(Text11, { style: styles11.sendBtnText }, sending ? "..." : "Send")
15261
15751
  )));
15262
15752
  }
15263
- var styles11 = StyleSheet12.create({
15753
+ var styles11 = StyleSheet13.create({
15264
15754
  container: { flex: 1 },
15265
15755
  header: { flexDirection: "row", alignItems: "center", gap: 8, marginBottom: 16, paddingBottom: 12, borderBottomWidth: 1, borderBottomColor: colors.border },
15266
15756
  headerIcon: { fontSize: 20 },
@@ -15292,8 +15782,8 @@ var styles11 = StyleSheet12.create({
15292
15782
  });
15293
15783
 
15294
15784
  // src/widget/screens/ComposeMessageScreen.tsx
15295
- import React13, { useState as useState9 } from "react";
15296
- import { View as View12, Text as Text12, TextInput as TextInput5, TouchableOpacity as TouchableOpacity11, StyleSheet as StyleSheet13 } from "react-native";
15785
+ import React14, { useState as useState9 } from "react";
15786
+ import { View as View13, Text as Text12, TextInput as TextInput6, TouchableOpacity as TouchableOpacity11, StyleSheet as StyleSheet14 } from "react-native";
15297
15787
  function ComposeMessageScreen({ nav }) {
15298
15788
  const { createThread, uploadImage } = useBugBear();
15299
15789
  const [subject, setSubject] = useState9("");
@@ -15314,8 +15804,8 @@ function ComposeMessageScreen({ nav }) {
15314
15804
  nav.pop();
15315
15805
  }
15316
15806
  };
15317
- return /* @__PURE__ */ React13.createElement(View12, null, /* @__PURE__ */ React13.createElement(View12, { style: styles12.header }, /* @__PURE__ */ React13.createElement(Text12, { style: styles12.title }, "New Message"), /* @__PURE__ */ React13.createElement(Text12, { style: styles12.subtitle }, "Send a message to the QA team")), /* @__PURE__ */ React13.createElement(View12, { style: styles12.form }, /* @__PURE__ */ React13.createElement(Text12, { style: shared.label }, "Subject"), /* @__PURE__ */ React13.createElement(
15318
- TextInput5,
15807
+ return /* @__PURE__ */ React14.createElement(View13, null, /* @__PURE__ */ React14.createElement(View13, { style: styles12.header }, /* @__PURE__ */ React14.createElement(Text12, { style: styles12.title }, "New Message"), /* @__PURE__ */ React14.createElement(Text12, { style: styles12.subtitle }, "Send a message to the QA team")), /* @__PURE__ */ React14.createElement(View13, { style: styles12.form }, /* @__PURE__ */ React14.createElement(Text12, { style: shared.label }, "Subject"), /* @__PURE__ */ React14.createElement(
15808
+ TextInput6,
15319
15809
  {
15320
15810
  style: styles12.subjectInput,
15321
15811
  value: subject,
@@ -15324,8 +15814,8 @@ function ComposeMessageScreen({ nav }) {
15324
15814
  placeholderTextColor: colors.textMuted,
15325
15815
  maxLength: 100
15326
15816
  }
15327
- ), /* @__PURE__ */ React13.createElement(Text12, { style: [shared.label, { marginTop: 16 }] }, "Message"), /* @__PURE__ */ React13.createElement(
15328
- TextInput5,
15817
+ ), /* @__PURE__ */ React14.createElement(Text12, { style: [shared.label, { marginTop: 16 }] }, "Message"), /* @__PURE__ */ React14.createElement(
15818
+ TextInput6,
15329
15819
  {
15330
15820
  style: styles12.messageInput,
15331
15821
  value: message,
@@ -15337,7 +15827,7 @@ function ComposeMessageScreen({ nav }) {
15337
15827
  textAlignVertical: "top",
15338
15828
  maxLength: 2e3
15339
15829
  }
15340
- ), /* @__PURE__ */ React13.createElement(
15830
+ ), /* @__PURE__ */ React14.createElement(
15341
15831
  ImagePickerButtons,
15342
15832
  {
15343
15833
  images: images.images,
@@ -15346,17 +15836,17 @@ function ComposeMessageScreen({ nav }) {
15346
15836
  onPickCamera: images.pickFromCamera,
15347
15837
  onRemove: images.removeImage
15348
15838
  }
15349
- ), /* @__PURE__ */ React13.createElement(
15839
+ ), /* @__PURE__ */ React14.createElement(
15350
15840
  TouchableOpacity11,
15351
15841
  {
15352
15842
  style: [shared.primaryButton, (!subject.trim() || !message.trim() || sending || images.isUploading) && shared.primaryButtonDisabled, { marginTop: 20 }],
15353
15843
  onPress: handleSend,
15354
15844
  disabled: !subject.trim() || !message.trim() || sending || images.isUploading
15355
15845
  },
15356
- /* @__PURE__ */ React13.createElement(Text12, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : sending ? "Sending..." : "Send Message")
15846
+ /* @__PURE__ */ React14.createElement(Text12, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : sending ? "Sending..." : "Send Message")
15357
15847
  )));
15358
15848
  }
15359
- var styles12 = StyleSheet13.create({
15849
+ var styles12 = StyleSheet14.create({
15360
15850
  header: { marginBottom: 20 },
15361
15851
  title: { fontSize: 20, fontWeight: "600", color: colors.textPrimary, marginBottom: 4 },
15362
15852
  subtitle: { fontSize: 14, color: colors.textMuted },
@@ -15366,8 +15856,8 @@ var styles12 = StyleSheet13.create({
15366
15856
  });
15367
15857
 
15368
15858
  // src/widget/screens/ProfileScreen.tsx
15369
- import React14, { useState as useState10, useEffect as useEffect8 } from "react";
15370
- import { View as View13, Text as Text13, TouchableOpacity as TouchableOpacity12, TextInput as TextInput6, StyleSheet as StyleSheet14 } from "react-native";
15859
+ import React15, { useState as useState10, useEffect as useEffect9 } from "react";
15860
+ import { View as View14, Text as Text13, TouchableOpacity as TouchableOpacity12, TextInput as TextInput7, StyleSheet as StyleSheet15 } from "react-native";
15371
15861
  function ProfileScreen({ nav }) {
15372
15862
  const { testerInfo, assignments, updateTesterProfile, refreshTesterInfo } = useBugBear();
15373
15863
  const [editing, setEditing] = useState10(false);
@@ -15379,7 +15869,7 @@ function ProfileScreen({ nav }) {
15379
15869
  const [saved, setSaved] = useState10(false);
15380
15870
  const [showDetails, setShowDetails] = useState10(false);
15381
15871
  const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
15382
- useEffect8(() => {
15872
+ useEffect9(() => {
15383
15873
  if (testerInfo) {
15384
15874
  setName(testerInfo.name);
15385
15875
  setAdditionalEmails(testerInfo.additionalEmails || []);
@@ -15414,17 +15904,17 @@ function ProfileScreen({ nav }) {
15414
15904
  }
15415
15905
  };
15416
15906
  if (saved) {
15417
- return /* @__PURE__ */ React14.createElement(View13, { style: shared.emptyState }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ React14.createElement(Text13, { style: shared.emptyTitle }, "Profile saved!"));
15907
+ return /* @__PURE__ */ React15.createElement(View14, { style: shared.emptyState }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ React15.createElement(Text13, { style: shared.emptyTitle }, "Profile saved!"));
15418
15908
  }
15419
15909
  if (!testerInfo) {
15420
- return /* @__PURE__ */ React14.createElement(View13, { style: shared.emptyState }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.emptyEmoji }, "\u{1F464}"), /* @__PURE__ */ React14.createElement(Text13, { style: shared.emptyTitle }, "No profile found"));
15910
+ return /* @__PURE__ */ React15.createElement(View14, { style: shared.emptyState }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.emptyEmoji }, "\u{1F464}"), /* @__PURE__ */ React15.createElement(Text13, { style: shared.emptyTitle }, "No profile found"));
15421
15911
  }
15422
15912
  if (editing) {
15423
- return /* @__PURE__ */ React14.createElement(View13, null, /* @__PURE__ */ React14.createElement(View13, { style: styles13.editHeader }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.editTitle }, "Edit Profile"), /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: () => {
15913
+ return /* @__PURE__ */ React15.createElement(View14, null, /* @__PURE__ */ React15.createElement(View14, { style: styles13.editHeader }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.editTitle }, "Edit Profile"), /* @__PURE__ */ React15.createElement(TouchableOpacity12, { onPress: () => {
15424
15914
  setEditing(false);
15425
15915
  setNewEmailInput("");
15426
- } }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.cancelText }, "Cancel"))), /* @__PURE__ */ React14.createElement(View13, { style: styles13.field }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.label }, "Name"), /* @__PURE__ */ React14.createElement(TextInput6, { style: styles13.input, value: name, onChangeText: setName, placeholder: "Your name", placeholderTextColor: colors.textMuted })), /* @__PURE__ */ React14.createElement(View13, { style: styles13.field }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.label }, "Primary Email"), /* @__PURE__ */ React14.createElement(Text13, { style: styles13.emailFixed }, testerInfo.email)), /* @__PURE__ */ React14.createElement(View13, { style: styles13.field }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.label }, "Additional Emails"), additionalEmails.map((email) => /* @__PURE__ */ React14.createElement(View13, { key: email, style: styles13.emailRow }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.emailText }, email), /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: () => setAdditionalEmails(additionalEmails.filter((e) => e !== email)) }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.removeEmail }, "\u2715")))), /* @__PURE__ */ React14.createElement(View13, { style: styles13.addEmailRow }, /* @__PURE__ */ React14.createElement(
15427
- TextInput6,
15916
+ } }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.cancelText }, "Cancel"))), /* @__PURE__ */ React15.createElement(View14, { style: styles13.field }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.label }, "Name"), /* @__PURE__ */ React15.createElement(TextInput7, { style: styles13.input, value: name, onChangeText: setName, placeholder: "Your name", placeholderTextColor: colors.textMuted })), /* @__PURE__ */ React15.createElement(View14, { style: styles13.field }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.label }, "Primary Email"), /* @__PURE__ */ React15.createElement(Text13, { style: styles13.emailFixed }, testerInfo.email)), /* @__PURE__ */ React15.createElement(View14, { style: styles13.field }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.label }, "Additional Emails"), additionalEmails.map((email) => /* @__PURE__ */ React15.createElement(View14, { key: email, style: styles13.emailRow }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.emailText }, email), /* @__PURE__ */ React15.createElement(TouchableOpacity12, { onPress: () => setAdditionalEmails(additionalEmails.filter((e) => e !== email)) }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.removeEmail }, "\u2715")))), /* @__PURE__ */ React15.createElement(View14, { style: styles13.addEmailRow }, /* @__PURE__ */ React15.createElement(
15917
+ TextInput7,
15428
15918
  {
15429
15919
  style: [styles13.input, { flex: 1, marginRight: 8 }],
15430
15920
  value: newEmailInput,
@@ -15434,26 +15924,26 @@ function ProfileScreen({ nav }) {
15434
15924
  keyboardType: "email-address",
15435
15925
  autoCapitalize: "none"
15436
15926
  }
15437
- ), /* @__PURE__ */ React14.createElement(TouchableOpacity12, { style: styles13.addButton, onPress: handleAddEmail }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.addButtonText }, "Add")))), /* @__PURE__ */ React14.createElement(View13, { style: styles13.field }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.label }, "Testing Platforms"), /* @__PURE__ */ React14.createElement(View13, { style: styles13.platformRow }, [{ key: "ios", label: "\u{1F4F1} iOS" }, { key: "android", label: "\u{1F916} Android" }, { key: "web", label: "\u{1F310} Web" }].map(({ key, label }) => /* @__PURE__ */ React14.createElement(
15927
+ ), /* @__PURE__ */ React15.createElement(TouchableOpacity12, { style: styles13.addButton, onPress: handleAddEmail }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.addButtonText }, "Add")))), /* @__PURE__ */ React15.createElement(View14, { style: styles13.field }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.label }, "Testing Platforms"), /* @__PURE__ */ React15.createElement(View14, { style: styles13.platformRow }, [{ key: "ios", label: "\u{1F4F1} iOS" }, { key: "android", label: "\u{1F916} Android" }, { key: "web", label: "\u{1F310} Web" }].map(({ key, label }) => /* @__PURE__ */ React15.createElement(
15438
15928
  TouchableOpacity12,
15439
15929
  {
15440
15930
  key,
15441
15931
  style: [styles13.platformBtn, platforms.includes(key) && styles13.platformBtnActive],
15442
15932
  onPress: () => setPlatforms((prev) => prev.includes(key) ? prev.filter((p) => p !== key) : [...prev, key])
15443
15933
  },
15444
- /* @__PURE__ */ React14.createElement(Text13, { style: [styles13.platformText, platforms.includes(key) && styles13.platformTextActive] }, label)
15445
- )))), /* @__PURE__ */ React14.createElement(TouchableOpacity12, { style: [shared.primaryButton, { marginTop: 20 }], onPress: handleSave, disabled: saving }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.primaryButtonText }, saving ? "Saving..." : "Save Profile")));
15934
+ /* @__PURE__ */ React15.createElement(Text13, { style: [styles13.platformText, platforms.includes(key) && styles13.platformTextActive] }, label)
15935
+ )))), /* @__PURE__ */ React15.createElement(TouchableOpacity12, { style: [shared.primaryButton, { marginTop: 20 }], onPress: handleSave, disabled: saving }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.primaryButtonText }, saving ? "Saving..." : "Save Profile")));
15446
15936
  }
15447
- return /* @__PURE__ */ React14.createElement(View13, null, /* @__PURE__ */ React14.createElement(View13, { style: styles13.profileCard }, /* @__PURE__ */ React14.createElement(View13, { style: styles13.avatar }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.avatarText }, testerInfo.name.charAt(0).toUpperCase())), /* @__PURE__ */ React14.createElement(Text13, { style: styles13.profileName }, testerInfo.name), /* @__PURE__ */ React14.createElement(Text13, { style: styles13.profileEmail }, testerInfo.email)), /* @__PURE__ */ React14.createElement(View13, { style: styles13.statsRow }, /* @__PURE__ */ React14.createElement(View13, { style: styles13.statItem }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.statNumber }, completedCount), /* @__PURE__ */ React14.createElement(Text13, { style: styles13.statLabel }, "Completed")), /* @__PURE__ */ React14.createElement(View13, { style: styles13.statDivider }), /* @__PURE__ */ React14.createElement(View13, { style: styles13.statItem }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.statNumber }, assignments.length), /* @__PURE__ */ React14.createElement(Text13, { style: styles13.statLabel }, "Total Assigned"))), /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: () => setShowDetails(!showDetails), style: styles13.detailsToggle }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ React14.createElement(View13, { style: styles13.detailsSection }, additionalEmails.length > 0 && /* @__PURE__ */ React14.createElement(View13, { style: styles13.detailBlock }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.detailLabel }, "Additional Emails"), additionalEmails.map((e) => /* @__PURE__ */ React14.createElement(Text13, { key: e, style: styles13.detailValue }, e))), platforms.length > 0 && /* @__PURE__ */ React14.createElement(View13, { style: styles13.detailBlock }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.detailLabel }, "Platforms"), /* @__PURE__ */ React14.createElement(View13, { style: styles13.platformTags }, platforms.map((p) => /* @__PURE__ */ React14.createElement(View13, { key: p, style: styles13.platformTag }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.platformTagText }, p === "ios" ? "\u{1F4F1} iOS" : p === "android" ? "\u{1F916} Android" : "\u{1F310} Web")))))), /* @__PURE__ */ React14.createElement(
15937
+ return /* @__PURE__ */ React15.createElement(View14, null, /* @__PURE__ */ React15.createElement(View14, { style: styles13.profileCard }, /* @__PURE__ */ React15.createElement(View14, { style: styles13.avatar }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.avatarText }, testerInfo.name.charAt(0).toUpperCase())), /* @__PURE__ */ React15.createElement(Text13, { style: styles13.profileName }, testerInfo.name), /* @__PURE__ */ React15.createElement(Text13, { style: styles13.profileEmail }, testerInfo.email)), /* @__PURE__ */ React15.createElement(View14, { style: styles13.statsRow }, /* @__PURE__ */ React15.createElement(View14, { style: styles13.statItem }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.statNumber }, completedCount), /* @__PURE__ */ React15.createElement(Text13, { style: styles13.statLabel }, "Completed")), /* @__PURE__ */ React15.createElement(View14, { style: styles13.statDivider }), /* @__PURE__ */ React15.createElement(View14, { style: styles13.statItem }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.statNumber }, assignments.length), /* @__PURE__ */ React15.createElement(Text13, { style: styles13.statLabel }, "Total Assigned"))), /* @__PURE__ */ React15.createElement(TouchableOpacity12, { onPress: () => setShowDetails(!showDetails), style: styles13.detailsToggle }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ React15.createElement(View14, { style: styles13.detailsSection }, additionalEmails.length > 0 && /* @__PURE__ */ React15.createElement(View14, { style: styles13.detailBlock }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.detailLabel }, "Additional Emails"), additionalEmails.map((e) => /* @__PURE__ */ React15.createElement(Text13, { key: e, style: styles13.detailValue }, e))), platforms.length > 0 && /* @__PURE__ */ React15.createElement(View14, { style: styles13.detailBlock }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.detailLabel }, "Platforms"), /* @__PURE__ */ React15.createElement(View14, { style: styles13.platformTags }, platforms.map((p) => /* @__PURE__ */ React15.createElement(View14, { key: p, style: styles13.platformTag }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.platformTagText }, p === "ios" ? "\u{1F4F1} iOS" : p === "android" ? "\u{1F916} Android" : "\u{1F310} Web")))))), /* @__PURE__ */ React15.createElement(
15448
15938
  TouchableOpacity12,
15449
15939
  {
15450
15940
  style: [shared.primaryButton, { marginTop: 20 }],
15451
15941
  onPress: () => setEditing(true)
15452
15942
  },
15453
- /* @__PURE__ */ React14.createElement(Text13, { style: shared.primaryButtonText }, "Edit Profile")
15943
+ /* @__PURE__ */ React15.createElement(Text13, { style: shared.primaryButtonText }, "Edit Profile")
15454
15944
  ));
15455
15945
  }
15456
- var styles13 = StyleSheet14.create({
15946
+ var styles13 = StyleSheet15.create({
15457
15947
  profileCard: { alignItems: "center", backgroundColor: colors.card, borderRadius: 16, padding: 24, marginBottom: 16 },
15458
15948
  avatar: { width: 64, height: 64, borderRadius: 32, backgroundColor: colors.blue, justifyContent: "center", alignItems: "center", marginBottom: 12 },
15459
15949
  avatarText: { fontSize: 28, fontWeight: "700", color: "#fff" },
@@ -15494,8 +15984,8 @@ var styles13 = StyleSheet14.create({
15494
15984
  });
15495
15985
 
15496
15986
  // src/widget/screens/IssueListScreen.tsx
15497
- import React15, { useState as useState11, useEffect as useEffect9 } from "react";
15498
- import { View as View14, Text as Text14, TouchableOpacity as TouchableOpacity13, StyleSheet as StyleSheet15, ActivityIndicator as ActivityIndicator2 } from "react-native";
15987
+ import React16, { useState as useState11, useEffect as useEffect10 } from "react";
15988
+ import { View as View15, Text as Text14, TouchableOpacity as TouchableOpacity13, StyleSheet as StyleSheet16 } from "react-native";
15499
15989
  var CATEGORY_CONFIG = {
15500
15990
  open: { label: "Open Issues", accent: "#f97316", emptyIcon: "\u2705", emptyText: "No open issues" },
15501
15991
  done: { label: "Done", accent: "#22c55e", emptyIcon: "\u{1F389}", emptyText: "No completed issues yet" },
@@ -15512,7 +16002,7 @@ function IssueListScreen({ nav, category }) {
15512
16002
  const [issues, setIssues] = useState11([]);
15513
16003
  const [loading, setLoading] = useState11(true);
15514
16004
  const config = CATEGORY_CONFIG[category];
15515
- useEffect9(() => {
16005
+ useEffect10(() => {
15516
16006
  let cancelled = false;
15517
16007
  setLoading(true);
15518
16008
  (async () => {
@@ -15538,12 +16028,12 @@ function IssueListScreen({ nav, category }) {
15538
16028
  };
15539
16029
  }, [client, category]);
15540
16030
  if (loading) {
15541
- return /* @__PURE__ */ React15.createElement(View14, { style: styles14.emptyContainer }, /* @__PURE__ */ React15.createElement(ActivityIndicator2, { size: "small", color: colors.textMuted }), /* @__PURE__ */ React15.createElement(Text14, { style: styles14.emptyText }, "Loading..."));
16031
+ return /* @__PURE__ */ React16.createElement(IssueListScreenSkeleton, null);
15542
16032
  }
15543
16033
  if (issues.length === 0) {
15544
- return /* @__PURE__ */ React15.createElement(View14, { style: styles14.emptyContainer }, /* @__PURE__ */ React15.createElement(Text14, { style: styles14.emptyIcon }, config.emptyIcon), /* @__PURE__ */ React15.createElement(Text14, { style: styles14.emptyText }, config.emptyText));
16034
+ return /* @__PURE__ */ React16.createElement(View15, { style: styles14.emptyContainer }, /* @__PURE__ */ React16.createElement(Text14, { style: styles14.emptyIcon }, config.emptyIcon), /* @__PURE__ */ React16.createElement(Text14, { style: styles14.emptyText }, config.emptyText));
15545
16035
  }
15546
- return /* @__PURE__ */ React15.createElement(View14, null, issues.map((issue) => /* @__PURE__ */ React15.createElement(
16036
+ return /* @__PURE__ */ React16.createElement(View15, null, issues.map((issue) => /* @__PURE__ */ React16.createElement(
15547
16037
  TouchableOpacity13,
15548
16038
  {
15549
16039
  key: issue.id,
@@ -15551,13 +16041,13 @@ function IssueListScreen({ nav, category }) {
15551
16041
  onPress: () => nav.push({ name: "ISSUE_DETAIL", issue }),
15552
16042
  activeOpacity: 0.7
15553
16043
  },
15554
- /* @__PURE__ */ React15.createElement(View14, { style: styles14.topRow }, issue.severity && /* @__PURE__ */ React15.createElement(View14, { style: [styles14.severityDot, { backgroundColor: SEVERITY_COLORS[issue.severity] || colors.textDim }] }), /* @__PURE__ */ React15.createElement(Text14, { style: styles14.issueTitle, numberOfLines: 1 }, issue.title)),
15555
- /* @__PURE__ */ React15.createElement(View14, { style: styles14.bottomRow }, issue.route && /* @__PURE__ */ React15.createElement(Text14, { style: styles14.routeText, numberOfLines: 1 }, issue.route), /* @__PURE__ */ React15.createElement(Text14, { style: styles14.timeText }, formatRelativeTime(issue.updatedAt))),
15556
- category === "done" && issue.verifiedByName && /* @__PURE__ */ React15.createElement(View14, { style: styles14.verifiedBadge }, /* @__PURE__ */ React15.createElement(Text14, { style: styles14.verifiedBadgeText }, "\u2714", " Verified by ", issue.verifiedByName)),
15557
- category === "reopened" && issue.originalBugTitle && /* @__PURE__ */ React15.createElement(View14, { style: styles14.reopenedBadge }, /* @__PURE__ */ React15.createElement(Text14, { style: styles14.reopenedBadgeText, numberOfLines: 1 }, "\u{1F504}", " Retest of: ", issue.originalBugTitle))
16044
+ /* @__PURE__ */ React16.createElement(View15, { style: styles14.topRow }, issue.severity && /* @__PURE__ */ React16.createElement(View15, { style: [styles14.severityDot, { backgroundColor: SEVERITY_COLORS[issue.severity] || colors.textDim }] }), /* @__PURE__ */ React16.createElement(Text14, { style: styles14.issueTitle, numberOfLines: 1 }, issue.title)),
16045
+ /* @__PURE__ */ React16.createElement(View15, { style: styles14.bottomRow }, issue.route && /* @__PURE__ */ React16.createElement(Text14, { style: styles14.routeText, numberOfLines: 1 }, issue.route), /* @__PURE__ */ React16.createElement(Text14, { style: styles14.timeText }, formatRelativeTime(issue.updatedAt))),
16046
+ category === "done" && issue.verifiedByName && /* @__PURE__ */ React16.createElement(View15, { style: styles14.verifiedBadge }, /* @__PURE__ */ React16.createElement(Text14, { style: styles14.verifiedBadgeText }, "\u2714", " Verified by ", issue.verifiedByName)),
16047
+ category === "reopened" && issue.originalBugTitle && /* @__PURE__ */ React16.createElement(View15, { style: styles14.reopenedBadge }, /* @__PURE__ */ React16.createElement(Text14, { style: styles14.reopenedBadgeText, numberOfLines: 1 }, "\u{1F504}", " Retest of: ", issue.originalBugTitle))
15558
16048
  )));
15559
16049
  }
15560
- var styles14 = StyleSheet15.create({
16050
+ var styles14 = StyleSheet16.create({
15561
16051
  emptyContainer: {
15562
16052
  alignItems: "center",
15563
16053
  paddingVertical: 40
@@ -15648,8 +16138,8 @@ var styles14 = StyleSheet15.create({
15648
16138
  });
15649
16139
 
15650
16140
  // src/widget/screens/IssueDetailScreen.tsx
15651
- import React16 from "react";
15652
- import { View as View15, Text as Text15, Image as Image3, StyleSheet as StyleSheet16, Linking as Linking2, TouchableOpacity as TouchableOpacity14 } from "react-native";
16141
+ import React17 from "react";
16142
+ import { View as View16, Text as Text15, Image as Image3, StyleSheet as StyleSheet17, Linking as Linking2, TouchableOpacity as TouchableOpacity14 } from "react-native";
15653
16143
  var STATUS_LABELS = {
15654
16144
  new: { label: "New", bg: "#1e3a5f", color: "#60a5fa" },
15655
16145
  triaging: { label: "Triaging", bg: "#1e3a5f", color: "#60a5fa" },
@@ -15673,9 +16163,9 @@ var SEVERITY_CONFIG = {
15673
16163
  function IssueDetailScreen({ nav, issue }) {
15674
16164
  const statusConfig = STATUS_LABELS[issue.status] || { label: issue.status, bg: "#27272a", color: "#a1a1aa" };
15675
16165
  const severityConfig = issue.severity ? SEVERITY_CONFIG[issue.severity] : null;
15676
- return /* @__PURE__ */ React16.createElement(View15, null, /* @__PURE__ */ React16.createElement(View15, { style: styles15.badgeRow }, /* @__PURE__ */ React16.createElement(View15, { style: [styles15.badge, { backgroundColor: statusConfig.bg }] }, /* @__PURE__ */ React16.createElement(Text15, { style: [styles15.badgeText, { color: statusConfig.color }] }, statusConfig.label)), severityConfig && /* @__PURE__ */ React16.createElement(View15, { style: [styles15.badge, { backgroundColor: severityConfig.bg }] }, /* @__PURE__ */ React16.createElement(Text15, { style: [styles15.badgeText, { color: severityConfig.color }] }, severityConfig.label))), /* @__PURE__ */ React16.createElement(Text15, { style: styles15.title }, issue.title), issue.route && /* @__PURE__ */ React16.createElement(Text15, { style: styles15.route }, issue.route), issue.description && /* @__PURE__ */ React16.createElement(View15, { style: styles15.descriptionCard }, /* @__PURE__ */ React16.createElement(Text15, { style: styles15.descriptionText }, issue.description)), issue.verifiedByName && /* @__PURE__ */ React16.createElement(View15, { style: styles15.verifiedCard }, /* @__PURE__ */ React16.createElement(View15, { style: styles15.verifiedHeader }, /* @__PURE__ */ React16.createElement(Text15, { style: styles15.verifiedIcon }, "\u2705"), /* @__PURE__ */ React16.createElement(Text15, { style: styles15.verifiedTitle }, "Retesting Proof")), /* @__PURE__ */ React16.createElement(Text15, { style: styles15.verifiedBody }, "Verified by ", issue.verifiedByName, issue.verifiedAt && ` on ${new Date(issue.verifiedAt).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" })}`)), issue.originalBugTitle && /* @__PURE__ */ React16.createElement(View15, { style: styles15.originalBugCard }, /* @__PURE__ */ React16.createElement(View15, { style: styles15.originalBugHeader }, /* @__PURE__ */ React16.createElement(Text15, { style: styles15.originalBugIcon }, "\u{1F504}"), /* @__PURE__ */ React16.createElement(Text15, { style: styles15.originalBugTitle }, "Original Bug")), /* @__PURE__ */ React16.createElement(Text15, { style: styles15.originalBugBody }, "Retest of: ", issue.originalBugTitle)), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ React16.createElement(View15, { style: styles15.screenshotSection }, /* @__PURE__ */ React16.createElement(Text15, { style: styles15.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ React16.createElement(View15, { style: styles15.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ React16.createElement(TouchableOpacity14, { key: i, onPress: () => Linking2.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ React16.createElement(Image3, { source: { uri: url }, style: styles15.screenshotThumb }))))), /* @__PURE__ */ React16.createElement(View15, { style: styles15.metaSection }, issue.reporterName && /* @__PURE__ */ React16.createElement(Text15, { style: styles15.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ React16.createElement(Text15, { style: styles15.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))));
16166
+ return /* @__PURE__ */ React17.createElement(View16, null, /* @__PURE__ */ React17.createElement(View16, { style: styles15.badgeRow }, /* @__PURE__ */ React17.createElement(View16, { style: [styles15.badge, { backgroundColor: statusConfig.bg }] }, /* @__PURE__ */ React17.createElement(Text15, { style: [styles15.badgeText, { color: statusConfig.color }] }, statusConfig.label)), severityConfig && /* @__PURE__ */ React17.createElement(View16, { style: [styles15.badge, { backgroundColor: severityConfig.bg }] }, /* @__PURE__ */ React17.createElement(Text15, { style: [styles15.badgeText, { color: severityConfig.color }] }, severityConfig.label))), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.title }, issue.title), issue.route && /* @__PURE__ */ React17.createElement(Text15, { style: styles15.route }, issue.route), issue.description && /* @__PURE__ */ React17.createElement(View16, { style: styles15.descriptionCard }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.descriptionText }, issue.description)), issue.verifiedByName && /* @__PURE__ */ React17.createElement(View16, { style: styles15.verifiedCard }, /* @__PURE__ */ React17.createElement(View16, { style: styles15.verifiedHeader }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.verifiedIcon }, "\u2705"), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.verifiedTitle }, "Retesting Proof")), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.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(View16, { style: styles15.originalBugCard }, /* @__PURE__ */ React17.createElement(View16, { style: styles15.originalBugHeader }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.originalBugIcon }, "\u{1F504}"), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.originalBugTitle }, "Original Bug")), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.originalBugBody }, "Retest of: ", issue.originalBugTitle)), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ React17.createElement(View16, { style: styles15.screenshotSection }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ React17.createElement(View16, { style: styles15.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ React17.createElement(TouchableOpacity14, { key: i, onPress: () => Linking2.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ React17.createElement(Image3, { source: { uri: url }, style: styles15.screenshotThumb }))))), /* @__PURE__ */ React17.createElement(View16, { style: styles15.metaSection }, issue.reporterName && /* @__PURE__ */ React17.createElement(Text15, { style: styles15.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))));
15677
16167
  }
15678
- var styles15 = StyleSheet16.create({
16168
+ var styles15 = StyleSheet17.create({
15679
16169
  badgeRow: {
15680
16170
  flexDirection: "row",
15681
16171
  gap: 8,
@@ -15831,9 +16321,9 @@ function BugBearButton({
15831
16321
  return { x, y };
15832
16322
  };
15833
16323
  const initialPos = getInitialPosition();
15834
- const pan = useRef3(new Animated.ValueXY(initialPos)).current;
15835
- const isDragging = useRef3(false);
15836
- const panResponder = useRef3(
16324
+ const pan = useRef4(new Animated2.ValueXY(initialPos)).current;
16325
+ const isDragging = useRef4(false);
16326
+ const panResponder = useRef4(
15837
16327
  PanResponder.create({
15838
16328
  onStartShouldSetPanResponder: () => draggable,
15839
16329
  onMoveShouldSetPanResponder: (_, gs) => draggable && (Math.abs(gs.dx) > 5 || Math.abs(gs.dy) > 5),
@@ -15849,7 +16339,7 @@ function BugBearButton({
15849
16339
  if (Math.abs(gs.dx) > 5 || Math.abs(gs.dy) > 5) {
15850
16340
  isDragging.current = true;
15851
16341
  }
15852
- Animated.event(
16342
+ Animated2.event(
15853
16343
  [null, { dx: pan.x, dy: pan.y }],
15854
16344
  { useNativeDriver: false }
15855
16345
  )(_, gs);
@@ -15862,7 +16352,7 @@ function BugBearButton({
15862
16352
  const margin = 16;
15863
16353
  const snapX = currentX < screenWidth / 2 ? margin : screenWidth - buttonSize - margin;
15864
16354
  const snapY = Math.max(minY, Math.min(currentY, screenHeight - maxYOffset));
15865
- Animated.spring(pan, {
16355
+ Animated2.spring(pan, {
15866
16356
  toValue: { x: snapX, y: snapY },
15867
16357
  useNativeDriver: false,
15868
16358
  friction: 7,
@@ -15935,50 +16425,50 @@ function BugBearButton({
15935
16425
  const renderScreen = () => {
15936
16426
  switch (currentScreen.name) {
15937
16427
  case "HOME":
15938
- return /* @__PURE__ */ React17.createElement(HomeScreen, { nav });
16428
+ return /* @__PURE__ */ React18.createElement(HomeScreen, { nav });
15939
16429
  case "TEST_DETAIL":
15940
- return /* @__PURE__ */ React17.createElement(TestDetailScreen, { testId: currentScreen.testId, nav });
16430
+ return /* @__PURE__ */ React18.createElement(TestDetailScreen, { testId: currentScreen.testId, nav });
15941
16431
  case "TEST_LIST":
15942
- return /* @__PURE__ */ React17.createElement(TestListScreen, { nav });
16432
+ return /* @__PURE__ */ React18.createElement(TestListScreen, { nav });
15943
16433
  case "TEST_FEEDBACK":
15944
- return /* @__PURE__ */ React17.createElement(TestFeedbackScreen, { status: currentScreen.status, assignmentId: currentScreen.assignmentId, nav });
16434
+ return /* @__PURE__ */ React18.createElement(TestFeedbackScreen, { status: currentScreen.status, assignmentId: currentScreen.assignmentId, nav });
15945
16435
  case "REPORT":
15946
- return /* @__PURE__ */ React17.createElement(ReportScreen, { nav, prefill: currentScreen.prefill });
16436
+ return /* @__PURE__ */ React18.createElement(ReportScreen, { nav, prefill: currentScreen.prefill });
15947
16437
  case "REPORT_SUCCESS":
15948
- return /* @__PURE__ */ React17.createElement(ReportSuccessScreen, { nav });
16438
+ return /* @__PURE__ */ React18.createElement(ReportSuccessScreen, { nav });
15949
16439
  case "MESSAGE_LIST":
15950
- return /* @__PURE__ */ React17.createElement(MessageListScreen, { nav });
16440
+ return /* @__PURE__ */ React18.createElement(MessageListScreen, { nav });
15951
16441
  case "THREAD_DETAIL":
15952
- return /* @__PURE__ */ React17.createElement(ThreadDetailScreen, { thread: currentScreen.thread, nav });
16442
+ return /* @__PURE__ */ React18.createElement(ThreadDetailScreen, { thread: currentScreen.thread, nav });
15953
16443
  case "COMPOSE_MESSAGE":
15954
- return /* @__PURE__ */ React17.createElement(ComposeMessageScreen, { nav });
16444
+ return /* @__PURE__ */ React18.createElement(ComposeMessageScreen, { nav });
15955
16445
  case "ISSUE_LIST":
15956
- return /* @__PURE__ */ React17.createElement(IssueListScreen, { nav, category: currentScreen.category });
16446
+ return /* @__PURE__ */ React18.createElement(IssueListScreen, { nav, category: currentScreen.category });
15957
16447
  case "ISSUE_DETAIL":
15958
- return /* @__PURE__ */ React17.createElement(IssueDetailScreen, { nav, issue: currentScreen.issue });
16448
+ return /* @__PURE__ */ React18.createElement(IssueDetailScreen, { nav, issue: currentScreen.issue });
15959
16449
  case "PROFILE":
15960
- return /* @__PURE__ */ React17.createElement(ProfileScreen, { nav });
16450
+ return /* @__PURE__ */ React18.createElement(ProfileScreen, { nav });
15961
16451
  default:
15962
- return /* @__PURE__ */ React17.createElement(HomeScreen, { nav });
16452
+ return /* @__PURE__ */ React18.createElement(HomeScreen, { nav });
15963
16453
  }
15964
16454
  };
15965
- return /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement(
15966
- Animated.View,
16455
+ return /* @__PURE__ */ React18.createElement(React18.Fragment, null, /* @__PURE__ */ React18.createElement(
16456
+ Animated2.View,
15967
16457
  {
15968
16458
  style: [styles16.fabContainer, { transform: pan.getTranslateTransform() }, buttonStyle],
15969
16459
  ...panResponder.panHandlers
15970
16460
  },
15971
- /* @__PURE__ */ React17.createElement(
16461
+ /* @__PURE__ */ React18.createElement(
15972
16462
  TouchableOpacity15,
15973
16463
  {
15974
16464
  style: styles16.fab,
15975
16465
  onPress: () => setModalVisible(true),
15976
16466
  activeOpacity: draggable ? 1 : 0.7
15977
16467
  },
15978
- /* @__PURE__ */ React17.createElement(Image4, { source: { uri: BUGBEAR_LOGO_BASE64 }, style: styles16.fabIcon }),
15979
- badgeCount > 0 && /* @__PURE__ */ React17.createElement(View16, { style: styles16.badge }, /* @__PURE__ */ React17.createElement(Text16, { style: styles16.badgeText }, badgeCount > 9 ? "9+" : badgeCount))
16468
+ /* @__PURE__ */ React18.createElement(Image4, { source: { uri: BUGBEAR_LOGO_BASE64 }, style: styles16.fabIcon }),
16469
+ badgeCount > 0 && /* @__PURE__ */ React18.createElement(View17, { style: styles16.badge }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.badgeText }, badgeCount > 9 ? "9+" : badgeCount))
15980
16470
  )
15981
- ), /* @__PURE__ */ React17.createElement(
16471
+ ), /* @__PURE__ */ React18.createElement(
15982
16472
  Modal3,
15983
16473
  {
15984
16474
  visible: modalVisible,
@@ -15986,13 +16476,13 @@ function BugBearButton({
15986
16476
  transparent: true,
15987
16477
  onRequestClose: handleClose
15988
16478
  },
15989
- /* @__PURE__ */ React17.createElement(
16479
+ /* @__PURE__ */ React18.createElement(
15990
16480
  KeyboardAvoidingView,
15991
16481
  {
15992
16482
  behavior: Platform4.OS === "ios" ? "padding" : "height",
15993
16483
  style: styles16.modalOverlay
15994
16484
  },
15995
- /* @__PURE__ */ React17.createElement(View16, { style: styles16.modalContainer }, /* @__PURE__ */ React17.createElement(View16, { style: styles16.header }, /* @__PURE__ */ React17.createElement(View16, { style: styles16.headerLeft }, canGoBack ? /* @__PURE__ */ React17.createElement(View16, { style: styles16.headerNavRow }, /* @__PURE__ */ React17.createElement(TouchableOpacity15, { onPress: () => nav.pop(), style: styles16.backButton }, /* @__PURE__ */ React17.createElement(Text16, { style: styles16.backText }, "\u2190 Back")), /* @__PURE__ */ React17.createElement(TouchableOpacity15, { onPress: () => nav.reset(), style: styles16.homeButton }, /* @__PURE__ */ React17.createElement(Text16, { style: styles16.homeText }, "\u{1F3E0}"))) : /* @__PURE__ */ React17.createElement(View16, { style: styles16.headerTitleRow }, /* @__PURE__ */ React17.createElement(Text16, { style: styles16.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ React17.createElement(TouchableOpacity15, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ React17.createElement(Text16, { style: styles16.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ React17.createElement(Text16, { style: styles16.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ React17.createElement(TouchableOpacity15, { onPress: handleClose, style: styles16.closeButton }, /* @__PURE__ */ React17.createElement(Text16, { style: styles16.closeText }, "\u2715"))), /* @__PURE__ */ React17.createElement(
16485
+ /* @__PURE__ */ React18.createElement(View17, { style: styles16.modalContainer }, /* @__PURE__ */ React18.createElement(View17, { style: styles16.header }, /* @__PURE__ */ React18.createElement(View17, { style: styles16.headerLeft }, canGoBack ? /* @__PURE__ */ React18.createElement(View17, { style: styles16.headerNavRow }, /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: () => nav.pop(), style: styles16.backButton }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.backText }, "\u2190 Back")), /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: () => nav.reset(), style: styles16.homeButton }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.homeText }, "\u{1F3E0}"))) : /* @__PURE__ */ React18.createElement(View17, { style: styles16.headerTitleRow }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ React18.createElement(Text16, { style: styles16.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: handleClose, style: styles16.closeButton }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.closeText }, "\u2715"))), /* @__PURE__ */ React18.createElement(
15996
16486
  ScrollView3,
15997
16487
  {
15998
16488
  style: styles16.content,
@@ -16000,12 +16490,12 @@ function BugBearButton({
16000
16490
  keyboardShouldPersistTaps: "handled",
16001
16491
  showsVerticalScrollIndicator: false
16002
16492
  },
16003
- isLoading ? /* @__PURE__ */ React17.createElement(View16, { style: styles16.loadingContainer }, /* @__PURE__ */ React17.createElement(ActivityIndicator3, { size: "large", color: colors.blue }), /* @__PURE__ */ React17.createElement(Text16, { style: styles16.loadingText }, "Loading...")) : renderScreen()
16493
+ isLoading ? /* @__PURE__ */ React18.createElement(View17, { style: styles16.loadingContainer }, /* @__PURE__ */ React18.createElement(ActivityIndicator2, { size: "large", color: colors.blue }), /* @__PURE__ */ React18.createElement(Text16, { style: styles16.loadingText }, "Loading...")) : renderScreen()
16004
16494
  ))
16005
16495
  )
16006
16496
  ));
16007
16497
  }
16008
- var styles16 = StyleSheet17.create({
16498
+ var styles16 = StyleSheet18.create({
16009
16499
  // FAB
16010
16500
  fabContainer: {
16011
16501
  position: "absolute",
@@ -16147,8 +16637,8 @@ var styles16 = StyleSheet17.create({
16147
16637
  });
16148
16638
 
16149
16639
  // src/BugBearErrorBoundary.tsx
16150
- import React18, { Component } from "react";
16151
- import { View as View17, Text as Text17, TouchableOpacity as TouchableOpacity16, StyleSheet as StyleSheet18 } from "react-native";
16640
+ import React19, { Component } from "react";
16641
+ import { View as View18, Text as Text17, TouchableOpacity as TouchableOpacity16, StyleSheet as StyleSheet19 } from "react-native";
16152
16642
  var BugBearErrorBoundary = class extends Component {
16153
16643
  constructor(props) {
16154
16644
  super(props);
@@ -16193,7 +16683,7 @@ var BugBearErrorBoundary = class extends Component {
16193
16683
  if (fallback) {
16194
16684
  return fallback;
16195
16685
  }
16196
- return /* @__PURE__ */ React18.createElement(View17, { style: styles17.container }, /* @__PURE__ */ React18.createElement(Text17, { style: styles17.title }, "Something went wrong"), /* @__PURE__ */ React18.createElement(Text17, { style: styles17.message }, error.message), /* @__PURE__ */ React18.createElement(TouchableOpacity16, { style: styles17.button, onPress: this.reset }, /* @__PURE__ */ React18.createElement(Text17, { style: styles17.buttonText }, "Try Again")), /* @__PURE__ */ React18.createElement(Text17, { style: styles17.caption }, "The error has been captured by BugBear"));
16686
+ return /* @__PURE__ */ React19.createElement(View18, { style: styles17.container }, /* @__PURE__ */ React19.createElement(Text17, { style: styles17.title }, "Something went wrong"), /* @__PURE__ */ React19.createElement(Text17, { style: styles17.message }, error.message), /* @__PURE__ */ React19.createElement(TouchableOpacity16, { style: styles17.button, onPress: this.reset }, /* @__PURE__ */ React19.createElement(Text17, { style: styles17.buttonText }, "Try Again")), /* @__PURE__ */ React19.createElement(Text17, { style: styles17.caption }, "The error has been captured by BugBear"));
16197
16687
  }
16198
16688
  return children;
16199
16689
  }
@@ -16204,7 +16694,7 @@ function useErrorContext() {
16204
16694
  getEnhancedContext: () => contextCapture.getEnhancedContext()
16205
16695
  };
16206
16696
  }
16207
- var styles17 = StyleSheet18.create({
16697
+ var styles17 = StyleSheet19.create({
16208
16698
  container: {
16209
16699
  padding: 20,
16210
16700
  margin: 20,