@bbearai/react-native 0.5.6 → 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.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +962 -431
- package/dist/index.mjs +849 -318
- package/package.json +12 -3
package/dist/index.js
CHANGED
|
@@ -42,14 +42,14 @@ module.exports = __toCommonJS(index_exports);
|
|
|
42
42
|
var import_react = __toESM(require("react"));
|
|
43
43
|
|
|
44
44
|
// ../../node_modules/tslib/tslib.es6.mjs
|
|
45
|
-
function __rest(
|
|
45
|
+
function __rest(s2, e) {
|
|
46
46
|
var t = {};
|
|
47
|
-
for (var p in
|
|
48
|
-
t[p] =
|
|
49
|
-
if (
|
|
50
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(
|
|
51
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(
|
|
52
|
-
t[p[i]] =
|
|
47
|
+
for (var p in s2) if (Object.prototype.hasOwnProperty.call(s2, p) && e.indexOf(p) < 0)
|
|
48
|
+
t[p] = s2[p];
|
|
49
|
+
if (s2 != null && typeof Object.getOwnPropertySymbols === "function")
|
|
50
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s2); i < p.length; i++) {
|
|
51
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s2, p[i]))
|
|
52
|
+
t[p[i]] = s2[p[i]];
|
|
53
53
|
}
|
|
54
54
|
return t;
|
|
55
55
|
}
|
|
@@ -864,9 +864,9 @@ var PostgrestFilterBuilder = class extends PostgrestTransformBuilder {
|
|
|
864
864
|
* @param values - The values array to filter with
|
|
865
865
|
*/
|
|
866
866
|
in(column, values) {
|
|
867
|
-
const cleanedValues = Array.from(new Set(values)).map((
|
|
868
|
-
if (typeof
|
|
869
|
-
else return `${
|
|
867
|
+
const cleanedValues = Array.from(new Set(values)).map((s2) => {
|
|
868
|
+
if (typeof s2 === "string" && PostgrestReservedCharsRegexp.test(s2)) return `"${s2}"`;
|
|
869
|
+
else return `${s2}`;
|
|
870
870
|
}).join(",");
|
|
871
871
|
this.url.searchParams.append(column, `in.(${cleanedValues})`);
|
|
872
872
|
return this;
|
|
@@ -878,9 +878,9 @@ var PostgrestFilterBuilder = class extends PostgrestTransformBuilder {
|
|
|
878
878
|
* @param values - The values array to filter with
|
|
879
879
|
*/
|
|
880
880
|
notIn(column, values) {
|
|
881
|
-
const cleanedValues = Array.from(new Set(values)).map((
|
|
882
|
-
if (typeof
|
|
883
|
-
else return `${
|
|
881
|
+
const cleanedValues = Array.from(new Set(values)).map((s2) => {
|
|
882
|
+
if (typeof s2 === "string" && PostgrestReservedCharsRegexp.test(s2)) return `"${s2}"`;
|
|
883
|
+
else return `${s2}`;
|
|
884
884
|
}).join(",");
|
|
885
885
|
this.url.searchParams.append(column, `not.in.(${cleanedValues})`);
|
|
886
886
|
return this;
|
|
@@ -11635,31 +11635,306 @@ function captureError(error, errorInfo) {
|
|
|
11635
11635
|
componentStack: errorInfo?.componentStack
|
|
11636
11636
|
};
|
|
11637
11637
|
}
|
|
11638
|
+
var LocalStorageAdapter = class {
|
|
11639
|
+
constructor() {
|
|
11640
|
+
this.fallback = /* @__PURE__ */ new Map();
|
|
11641
|
+
}
|
|
11642
|
+
get isAvailable() {
|
|
11643
|
+
try {
|
|
11644
|
+
const key = "__bugbear_test__";
|
|
11645
|
+
localStorage.setItem(key, "1");
|
|
11646
|
+
localStorage.removeItem(key);
|
|
11647
|
+
return true;
|
|
11648
|
+
} catch {
|
|
11649
|
+
return false;
|
|
11650
|
+
}
|
|
11651
|
+
}
|
|
11652
|
+
async getItem(key) {
|
|
11653
|
+
if (this.isAvailable) return localStorage.getItem(key);
|
|
11654
|
+
return this.fallback.get(key) ?? null;
|
|
11655
|
+
}
|
|
11656
|
+
async setItem(key, value) {
|
|
11657
|
+
if (this.isAvailable) {
|
|
11658
|
+
localStorage.setItem(key, value);
|
|
11659
|
+
} else {
|
|
11660
|
+
this.fallback.set(key, value);
|
|
11661
|
+
}
|
|
11662
|
+
}
|
|
11663
|
+
async removeItem(key) {
|
|
11664
|
+
if (this.isAvailable) {
|
|
11665
|
+
localStorage.removeItem(key);
|
|
11666
|
+
} else {
|
|
11667
|
+
this.fallback.delete(key);
|
|
11668
|
+
}
|
|
11669
|
+
}
|
|
11670
|
+
};
|
|
11671
|
+
var OfflineQueue = class {
|
|
11672
|
+
constructor(config) {
|
|
11673
|
+
this.items = [];
|
|
11674
|
+
this.storageKey = "bugbear_offline_queue";
|
|
11675
|
+
this.flushing = false;
|
|
11676
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
11677
|
+
this.maxItems = config.maxItems ?? 50;
|
|
11678
|
+
this.maxRetries = config.maxRetries ?? 5;
|
|
11679
|
+
this.storage = config.storage ?? new LocalStorageAdapter();
|
|
11680
|
+
}
|
|
11681
|
+
// ── Flush handler registration ──────────────────────────────
|
|
11682
|
+
/** Register a handler that replays a queued operation. */
|
|
11683
|
+
registerHandler(type, handler) {
|
|
11684
|
+
this.handlers.set(type, handler);
|
|
11685
|
+
}
|
|
11686
|
+
// ── Change listener ─────────────────────────────────────────
|
|
11687
|
+
/** Subscribe to queue count changes (for UI badges). */
|
|
11688
|
+
onChange(callback) {
|
|
11689
|
+
this.listener = callback;
|
|
11690
|
+
}
|
|
11691
|
+
notify() {
|
|
11692
|
+
this.listener?.(this.items.length);
|
|
11693
|
+
}
|
|
11694
|
+
// ── Persistence ─────────────────────────────────────────────
|
|
11695
|
+
/** Load queue from persistent storage. Call once after construction. */
|
|
11696
|
+
async load() {
|
|
11697
|
+
try {
|
|
11698
|
+
const raw = await this.storage.getItem(this.storageKey);
|
|
11699
|
+
if (raw) {
|
|
11700
|
+
this.items = JSON.parse(raw);
|
|
11701
|
+
}
|
|
11702
|
+
} catch {
|
|
11703
|
+
this.items = [];
|
|
11704
|
+
}
|
|
11705
|
+
this.notify();
|
|
11706
|
+
}
|
|
11707
|
+
async save() {
|
|
11708
|
+
try {
|
|
11709
|
+
await this.storage.setItem(this.storageKey, JSON.stringify(this.items));
|
|
11710
|
+
} catch (err) {
|
|
11711
|
+
console.error("BugBear: Failed to persist offline queue", err);
|
|
11712
|
+
}
|
|
11713
|
+
this.notify();
|
|
11714
|
+
}
|
|
11715
|
+
// ── Enqueue ─────────────────────────────────────────────────
|
|
11716
|
+
/** Add a failed operation to the queue. Returns the item ID. */
|
|
11717
|
+
async enqueue(type, payload) {
|
|
11718
|
+
if (this.items.length >= this.maxItems) {
|
|
11719
|
+
this.items.shift();
|
|
11720
|
+
}
|
|
11721
|
+
const id = `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
11722
|
+
this.items.push({ id, type, payload, createdAt: Date.now(), retries: 0 });
|
|
11723
|
+
await this.save();
|
|
11724
|
+
return id;
|
|
11725
|
+
}
|
|
11726
|
+
// ── Accessors ───────────────────────────────────────────────
|
|
11727
|
+
/** Number of items waiting to be flushed. */
|
|
11728
|
+
get count() {
|
|
11729
|
+
return this.items.length;
|
|
11730
|
+
}
|
|
11731
|
+
/** Read-only snapshot of pending items. */
|
|
11732
|
+
get pending() {
|
|
11733
|
+
return [...this.items];
|
|
11734
|
+
}
|
|
11735
|
+
/** Whether a flush is currently in progress. */
|
|
11736
|
+
get isFlushing() {
|
|
11737
|
+
return this.flushing;
|
|
11738
|
+
}
|
|
11739
|
+
// ── Flush ───────────────────────────────────────────────────
|
|
11740
|
+
/**
|
|
11741
|
+
* Process all queued items in FIFO order.
|
|
11742
|
+
* Stops early if a network error is encountered (still offline).
|
|
11743
|
+
*/
|
|
11744
|
+
async flush() {
|
|
11745
|
+
if (this.flushing || this.items.length === 0) {
|
|
11746
|
+
return { flushed: 0, failed: 0 };
|
|
11747
|
+
}
|
|
11748
|
+
this.flushing = true;
|
|
11749
|
+
let flushed = 0;
|
|
11750
|
+
let failed = 0;
|
|
11751
|
+
const snapshot = [...this.items];
|
|
11752
|
+
for (const item of snapshot) {
|
|
11753
|
+
const handler = this.handlers.get(item.type);
|
|
11754
|
+
if (!handler) {
|
|
11755
|
+
failed++;
|
|
11756
|
+
continue;
|
|
11757
|
+
}
|
|
11758
|
+
try {
|
|
11759
|
+
const result = await handler(item.payload);
|
|
11760
|
+
if (result.success) {
|
|
11761
|
+
this.items = this.items.filter((i) => i.id !== item.id);
|
|
11762
|
+
flushed++;
|
|
11763
|
+
} else if (isNetworkError(result.error)) {
|
|
11764
|
+
break;
|
|
11765
|
+
} else {
|
|
11766
|
+
const idx = this.items.findIndex((i) => i.id === item.id);
|
|
11767
|
+
if (idx !== -1) {
|
|
11768
|
+
this.items[idx].retries++;
|
|
11769
|
+
if (this.items[idx].retries >= this.maxRetries) {
|
|
11770
|
+
this.items.splice(idx, 1);
|
|
11771
|
+
}
|
|
11772
|
+
}
|
|
11773
|
+
failed++;
|
|
11774
|
+
}
|
|
11775
|
+
} catch {
|
|
11776
|
+
break;
|
|
11777
|
+
}
|
|
11778
|
+
}
|
|
11779
|
+
await this.save();
|
|
11780
|
+
this.flushing = false;
|
|
11781
|
+
return { flushed, failed };
|
|
11782
|
+
}
|
|
11783
|
+
// ── Clear ───────────────────────────────────────────────────
|
|
11784
|
+
/** Drop all queued items. */
|
|
11785
|
+
async clear() {
|
|
11786
|
+
this.items = [];
|
|
11787
|
+
await this.save();
|
|
11788
|
+
}
|
|
11789
|
+
};
|
|
11790
|
+
function isNetworkError(error) {
|
|
11791
|
+
if (!error) return false;
|
|
11792
|
+
const msg = error.toLowerCase();
|
|
11793
|
+
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
|
|
11794
|
+
msg.includes("the internet connection appears to be offline") || msg.includes("a]server with the specified hostname could not be found");
|
|
11795
|
+
}
|
|
11638
11796
|
var formatPgError = (e) => {
|
|
11639
11797
|
if (!e || typeof e !== "object") return { raw: e };
|
|
11640
11798
|
const { message, code, details, hint } = e;
|
|
11641
11799
|
return { message, code, details, hint };
|
|
11642
11800
|
};
|
|
11643
|
-
var DEFAULT_SUPABASE_URL = "https://kyxgzjnqgvapvlnvqawz.supabase.co";
|
|
11644
|
-
var getEnvVar = (key) => {
|
|
11645
|
-
try {
|
|
11646
|
-
if (typeof process !== "undefined" && process.env) {
|
|
11647
|
-
return process.env[key];
|
|
11648
|
-
}
|
|
11649
|
-
} catch {
|
|
11650
|
-
}
|
|
11651
|
-
return void 0;
|
|
11652
|
-
};
|
|
11653
|
-
var HOSTED_BUGBEAR_ANON_KEY = getEnvVar("BUGBEAR_ANON_KEY") || getEnvVar("NEXT_PUBLIC_BUGBEAR_ANON_KEY") || "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imt5eGd6am5xZ3ZhcHZsbnZxYXd6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkyNjgwNDIsImV4cCI6MjA4NDg0NDA0Mn0.NUkAlCHLFjeRoisbmNUVoGb4R6uQ8xs5LAEIX1BWTwU";
|
|
11654
11801
|
var BugBearClient = class {
|
|
11655
11802
|
constructor(config) {
|
|
11656
11803
|
this.navigationHistory = [];
|
|
11657
11804
|
this.reportSubmitInFlight = false;
|
|
11805
|
+
this._queue = null;
|
|
11806
|
+
this.realtimeChannels = [];
|
|
11807
|
+
if (!config.supabaseUrl) {
|
|
11808
|
+
throw new Error("BugBear: supabaseUrl is required. Get it from your BugBear project settings.");
|
|
11809
|
+
}
|
|
11810
|
+
if (!config.supabaseAnonKey) {
|
|
11811
|
+
throw new Error("BugBear: supabaseAnonKey is required. Get it from your BugBear project settings.");
|
|
11812
|
+
}
|
|
11658
11813
|
this.config = config;
|
|
11659
|
-
this.supabase = createClient(
|
|
11660
|
-
|
|
11661
|
-
|
|
11662
|
-
|
|
11814
|
+
this.supabase = createClient(config.supabaseUrl, config.supabaseAnonKey);
|
|
11815
|
+
if (config.offlineQueue?.enabled) {
|
|
11816
|
+
this._queue = new OfflineQueue({
|
|
11817
|
+
enabled: true,
|
|
11818
|
+
maxItems: config.offlineQueue.maxItems,
|
|
11819
|
+
maxRetries: config.offlineQueue.maxRetries
|
|
11820
|
+
});
|
|
11821
|
+
this.registerQueueHandlers();
|
|
11822
|
+
}
|
|
11823
|
+
}
|
|
11824
|
+
// ── Offline Queue ─────────────────────────────────────────
|
|
11825
|
+
/**
|
|
11826
|
+
* Access the offline queue (if enabled).
|
|
11827
|
+
* Use this to check queue.count, subscribe to changes, or trigger flush.
|
|
11828
|
+
*/
|
|
11829
|
+
get queue() {
|
|
11830
|
+
return this._queue;
|
|
11831
|
+
}
|
|
11832
|
+
/**
|
|
11833
|
+
* Initialize the offline queue with a platform-specific storage adapter.
|
|
11834
|
+
* Must be called after construction for React Native (which supplies AsyncStorage).
|
|
11835
|
+
* Web callers can skip this — LocalStorageAdapter is the default.
|
|
11836
|
+
*/
|
|
11837
|
+
async initQueue(storage) {
|
|
11838
|
+
if (!this._queue) return;
|
|
11839
|
+
if (storage) {
|
|
11840
|
+
this._queue = new OfflineQueue({
|
|
11841
|
+
enabled: true,
|
|
11842
|
+
maxItems: this.config.offlineQueue?.maxItems,
|
|
11843
|
+
maxRetries: this.config.offlineQueue?.maxRetries,
|
|
11844
|
+
storage
|
|
11845
|
+
});
|
|
11846
|
+
this.registerQueueHandlers();
|
|
11847
|
+
}
|
|
11848
|
+
await this._queue.load();
|
|
11849
|
+
}
|
|
11850
|
+
registerQueueHandlers() {
|
|
11851
|
+
if (!this._queue) return;
|
|
11852
|
+
this._queue.registerHandler("report", async (payload) => {
|
|
11853
|
+
const { error } = await this.supabase.from("reports").insert(payload).select("id").single();
|
|
11854
|
+
if (error) return { success: false, error: error.message };
|
|
11855
|
+
return { success: true };
|
|
11856
|
+
});
|
|
11857
|
+
this._queue.registerHandler("message", async (payload) => {
|
|
11858
|
+
const { error } = await this.supabase.from("discussion_messages").insert(payload);
|
|
11859
|
+
if (error) return { success: false, error: error.message };
|
|
11860
|
+
return { success: true };
|
|
11861
|
+
});
|
|
11862
|
+
this._queue.registerHandler("feedback", async (payload) => {
|
|
11863
|
+
const { error } = await this.supabase.from("test_feedback").insert(payload);
|
|
11864
|
+
if (error) return { success: false, error: error.message };
|
|
11865
|
+
return { success: true };
|
|
11866
|
+
});
|
|
11867
|
+
}
|
|
11868
|
+
// ── Realtime Subscriptions ─────────────────────────────────
|
|
11869
|
+
/** Whether realtime is enabled in config. */
|
|
11870
|
+
get realtimeEnabled() {
|
|
11871
|
+
return !!this.config.realtime?.enabled;
|
|
11872
|
+
}
|
|
11873
|
+
/**
|
|
11874
|
+
* Subscribe to postgres_changes on relevant tables.
|
|
11875
|
+
* Each callback fires when the corresponding table has changes —
|
|
11876
|
+
* the provider should call its refresh function in response.
|
|
11877
|
+
* Returns a cleanup function that unsubscribes all channels.
|
|
11878
|
+
*/
|
|
11879
|
+
subscribeToChanges(callbacks) {
|
|
11880
|
+
this.unsubscribeAll();
|
|
11881
|
+
const projectId = this.config.projectId;
|
|
11882
|
+
const debounce = (fn, ms = 500) => {
|
|
11883
|
+
let timer;
|
|
11884
|
+
return () => {
|
|
11885
|
+
clearTimeout(timer);
|
|
11886
|
+
timer = setTimeout(fn, ms);
|
|
11887
|
+
};
|
|
11888
|
+
};
|
|
11889
|
+
if (callbacks.onAssignmentChange) {
|
|
11890
|
+
const debouncedCb = debounce(callbacks.onAssignmentChange);
|
|
11891
|
+
const channel = this.supabase.channel("bugbear-assignments").on("postgres_changes", {
|
|
11892
|
+
event: "*",
|
|
11893
|
+
schema: "public",
|
|
11894
|
+
table: "test_assignments",
|
|
11895
|
+
filter: `project_id=eq.${projectId}`
|
|
11896
|
+
}, debouncedCb).subscribe((status) => {
|
|
11897
|
+
if (status === "CHANNEL_ERROR") {
|
|
11898
|
+
console.warn("BugBear: Realtime subscription failed for test_assignments");
|
|
11899
|
+
}
|
|
11900
|
+
});
|
|
11901
|
+
this.realtimeChannels.push(channel);
|
|
11902
|
+
}
|
|
11903
|
+
if (callbacks.onMessageChange) {
|
|
11904
|
+
const debouncedCb = debounce(callbacks.onMessageChange);
|
|
11905
|
+
const channel = this.supabase.channel("bugbear-messages").on("postgres_changes", {
|
|
11906
|
+
event: "INSERT",
|
|
11907
|
+
schema: "public",
|
|
11908
|
+
table: "discussion_messages"
|
|
11909
|
+
}, debouncedCb).subscribe((status) => {
|
|
11910
|
+
if (status === "CHANNEL_ERROR") {
|
|
11911
|
+
console.warn("BugBear: Realtime subscription failed for discussion_messages");
|
|
11912
|
+
}
|
|
11913
|
+
});
|
|
11914
|
+
this.realtimeChannels.push(channel);
|
|
11915
|
+
}
|
|
11916
|
+
if (callbacks.onReportChange) {
|
|
11917
|
+
const debouncedCb = debounce(callbacks.onReportChange);
|
|
11918
|
+
const channel = this.supabase.channel("bugbear-reports").on("postgres_changes", {
|
|
11919
|
+
event: "UPDATE",
|
|
11920
|
+
schema: "public",
|
|
11921
|
+
table: "reports",
|
|
11922
|
+
filter: `project_id=eq.${projectId}`
|
|
11923
|
+
}, debouncedCb).subscribe((status) => {
|
|
11924
|
+
if (status === "CHANNEL_ERROR") {
|
|
11925
|
+
console.warn("BugBear: Realtime subscription failed for reports");
|
|
11926
|
+
}
|
|
11927
|
+
});
|
|
11928
|
+
this.realtimeChannels.push(channel);
|
|
11929
|
+
}
|
|
11930
|
+
return () => this.unsubscribeAll();
|
|
11931
|
+
}
|
|
11932
|
+
/** Remove all active Realtime channels. */
|
|
11933
|
+
unsubscribeAll() {
|
|
11934
|
+
for (const channel of this.realtimeChannels) {
|
|
11935
|
+
this.supabase.removeChannel(channel);
|
|
11936
|
+
}
|
|
11937
|
+
this.realtimeChannels = [];
|
|
11663
11938
|
}
|
|
11664
11939
|
/**
|
|
11665
11940
|
* Track navigation for context.
|
|
@@ -11719,6 +11994,7 @@ var BugBearClient = class {
|
|
|
11719
11994
|
return { success: false, error: "A report is already being submitted" };
|
|
11720
11995
|
}
|
|
11721
11996
|
this.reportSubmitInFlight = true;
|
|
11997
|
+
let fullReport;
|
|
11722
11998
|
try {
|
|
11723
11999
|
const validationError = this.validateReport(report);
|
|
11724
12000
|
if (validationError) {
|
|
@@ -11735,7 +12011,7 @@ var BugBearClient = class {
|
|
|
11735
12011
|
return { success: false, error: "User not authenticated" };
|
|
11736
12012
|
}
|
|
11737
12013
|
const testerInfo = await this.getTesterInfo();
|
|
11738
|
-
|
|
12014
|
+
fullReport = {
|
|
11739
12015
|
project_id: this.config.projectId,
|
|
11740
12016
|
reporter_id: userInfo.id,
|
|
11741
12017
|
// User ID from host app (required)
|
|
@@ -11762,6 +12038,10 @@ var BugBearClient = class {
|
|
|
11762
12038
|
};
|
|
11763
12039
|
const { data, error } = await this.supabase.from("reports").insert(fullReport).select("id").single();
|
|
11764
12040
|
if (error) {
|
|
12041
|
+
if (this._queue && isNetworkError(error.message)) {
|
|
12042
|
+
await this._queue.enqueue("report", fullReport);
|
|
12043
|
+
return { success: false, queued: true, error: "Queued \u2014 will send when online" };
|
|
12044
|
+
}
|
|
11765
12045
|
console.error("BugBear: Failed to submit report", error.message);
|
|
11766
12046
|
return { success: false, error: error.message };
|
|
11767
12047
|
}
|
|
@@ -11771,6 +12051,10 @@ var BugBearClient = class {
|
|
|
11771
12051
|
return { success: true, reportId: data.id };
|
|
11772
12052
|
} catch (err) {
|
|
11773
12053
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
12054
|
+
if (this._queue && fullReport && isNetworkError(message)) {
|
|
12055
|
+
await this._queue.enqueue("report", fullReport);
|
|
12056
|
+
return { success: false, queued: true, error: "Queued \u2014 will send when online" };
|
|
12057
|
+
}
|
|
11774
12058
|
return { success: false, error: message };
|
|
11775
12059
|
} finally {
|
|
11776
12060
|
this.reportSubmitInFlight = false;
|
|
@@ -11780,10 +12064,13 @@ var BugBearClient = class {
|
|
|
11780
12064
|
* Get assigned tests for current user
|
|
11781
12065
|
* First looks up the tester by email, then fetches their assignments
|
|
11782
12066
|
*/
|
|
11783
|
-
async getAssignedTests() {
|
|
12067
|
+
async getAssignedTests(options) {
|
|
11784
12068
|
try {
|
|
11785
12069
|
const testerInfo = await this.getTesterInfo();
|
|
11786
12070
|
if (!testerInfo) return [];
|
|
12071
|
+
const pageSize = Math.min(options?.pageSize ?? 100, 100);
|
|
12072
|
+
const from = (options?.page ?? 0) * pageSize;
|
|
12073
|
+
const to = from + pageSize - 1;
|
|
11787
12074
|
const { data, error } = await this.supabase.from("test_assignments").select(`
|
|
11788
12075
|
id,
|
|
11789
12076
|
status,
|
|
@@ -11824,12 +12111,18 @@ var BugBearClient = class {
|
|
|
11824
12111
|
login_hint
|
|
11825
12112
|
)
|
|
11826
12113
|
)
|
|
11827
|
-
`).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).
|
|
12114
|
+
`).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).range(from, to);
|
|
11828
12115
|
if (error) {
|
|
11829
12116
|
console.error("BugBear: Failed to fetch assignments", formatPgError(error));
|
|
11830
12117
|
return [];
|
|
11831
12118
|
}
|
|
11832
|
-
const mapped = (data || []).
|
|
12119
|
+
const mapped = (data || []).filter((item) => {
|
|
12120
|
+
if (!item.test_case) {
|
|
12121
|
+
console.warn("BugBear: Assignment returned without test_case", { id: item.id });
|
|
12122
|
+
return false;
|
|
12123
|
+
}
|
|
12124
|
+
return true;
|
|
12125
|
+
}).map((item) => ({
|
|
11833
12126
|
id: item.id,
|
|
11834
12127
|
status: item.status,
|
|
11835
12128
|
startedAt: item.started_at,
|
|
@@ -12077,6 +12370,7 @@ var BugBearClient = class {
|
|
|
12077
12370
|
* This empowers testers to shape better tests over time
|
|
12078
12371
|
*/
|
|
12079
12372
|
async submitTestFeedback(options) {
|
|
12373
|
+
let feedbackPayload;
|
|
12080
12374
|
try {
|
|
12081
12375
|
const testerInfo = await this.getTesterInfo();
|
|
12082
12376
|
if (!testerInfo) {
|
|
@@ -12086,7 +12380,17 @@ var BugBearClient = class {
|
|
|
12086
12380
|
if (feedback.rating < 1 || feedback.rating > 5) {
|
|
12087
12381
|
return { success: false, error: "Rating must be between 1 and 5" };
|
|
12088
12382
|
}
|
|
12089
|
-
const
|
|
12383
|
+
const optionalRatings = [
|
|
12384
|
+
{ name: "clarityRating", value: feedback.clarityRating },
|
|
12385
|
+
{ name: "stepsRating", value: feedback.stepsRating },
|
|
12386
|
+
{ name: "relevanceRating", value: feedback.relevanceRating }
|
|
12387
|
+
];
|
|
12388
|
+
for (const { name, value } of optionalRatings) {
|
|
12389
|
+
if (value !== void 0 && value !== null && (value < 1 || value > 5)) {
|
|
12390
|
+
return { success: false, error: `${name} must be between 1 and 5` };
|
|
12391
|
+
}
|
|
12392
|
+
}
|
|
12393
|
+
feedbackPayload = {
|
|
12090
12394
|
project_id: this.config.projectId,
|
|
12091
12395
|
test_case_id: testCaseId,
|
|
12092
12396
|
assignment_id: assignmentId || null,
|
|
@@ -12104,8 +12408,13 @@ var BugBearClient = class {
|
|
|
12104
12408
|
platform: this.getDeviceInfo().platform,
|
|
12105
12409
|
time_to_complete_seconds: timeToCompleteSeconds || null,
|
|
12106
12410
|
screenshot_urls: screenshotUrls || []
|
|
12107
|
-
}
|
|
12411
|
+
};
|
|
12412
|
+
const { error: feedbackError } = await this.supabase.from("test_feedback").insert(feedbackPayload);
|
|
12108
12413
|
if (feedbackError) {
|
|
12414
|
+
if (this._queue && isNetworkError(feedbackError.message)) {
|
|
12415
|
+
await this._queue.enqueue("feedback", feedbackPayload);
|
|
12416
|
+
return { success: false, queued: true, error: "Queued \u2014 will send when online" };
|
|
12417
|
+
}
|
|
12109
12418
|
console.error("BugBear: Failed to submit feedback", feedbackError);
|
|
12110
12419
|
return { success: false, error: feedbackError.message };
|
|
12111
12420
|
}
|
|
@@ -12122,6 +12431,10 @@ var BugBearClient = class {
|
|
|
12122
12431
|
return { success: true };
|
|
12123
12432
|
} catch (err) {
|
|
12124
12433
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
12434
|
+
if (this._queue && feedbackPayload && isNetworkError(message)) {
|
|
12435
|
+
await this._queue.enqueue("feedback", feedbackPayload);
|
|
12436
|
+
return { success: false, queued: true, error: "Queued \u2014 will send when online" };
|
|
12437
|
+
}
|
|
12125
12438
|
console.error("BugBear: Error submitting feedback", err);
|
|
12126
12439
|
return { success: false, error: message };
|
|
12127
12440
|
}
|
|
@@ -12756,6 +13069,7 @@ var BugBearClient = class {
|
|
|
12756
13069
|
* Send a message to a thread
|
|
12757
13070
|
*/
|
|
12758
13071
|
async sendMessage(threadId, content, attachments) {
|
|
13072
|
+
let insertData;
|
|
12759
13073
|
try {
|
|
12760
13074
|
const testerInfo = await this.getTesterInfo();
|
|
12761
13075
|
if (!testerInfo) {
|
|
@@ -12767,7 +13081,7 @@ var BugBearClient = class {
|
|
|
12767
13081
|
console.error("BugBear: Rate limit exceeded for messages");
|
|
12768
13082
|
return false;
|
|
12769
13083
|
}
|
|
12770
|
-
|
|
13084
|
+
insertData = {
|
|
12771
13085
|
thread_id: threadId,
|
|
12772
13086
|
sender_type: "tester",
|
|
12773
13087
|
sender_tester_id: testerInfo.id,
|
|
@@ -12782,12 +13096,21 @@ var BugBearClient = class {
|
|
|
12782
13096
|
}
|
|
12783
13097
|
const { error } = await this.supabase.from("discussion_messages").insert(insertData);
|
|
12784
13098
|
if (error) {
|
|
13099
|
+
if (this._queue && isNetworkError(error.message)) {
|
|
13100
|
+
await this._queue.enqueue("message", insertData);
|
|
13101
|
+
return false;
|
|
13102
|
+
}
|
|
12785
13103
|
console.error("BugBear: Failed to send message", formatPgError(error));
|
|
12786
13104
|
return false;
|
|
12787
13105
|
}
|
|
12788
13106
|
await this.markThreadAsRead(threadId);
|
|
12789
13107
|
return true;
|
|
12790
13108
|
} catch (err) {
|
|
13109
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
13110
|
+
if (this._queue && insertData && isNetworkError(message)) {
|
|
13111
|
+
await this._queue.enqueue("message", insertData);
|
|
13112
|
+
return false;
|
|
13113
|
+
}
|
|
12791
13114
|
console.error("BugBear: Error sending message", err);
|
|
12792
13115
|
return false;
|
|
12793
13116
|
}
|
|
@@ -12974,7 +13297,7 @@ var BugBearClient = class {
|
|
|
12974
13297
|
console.error("BugBear: Failed to fetch session history", formatPgError(error));
|
|
12975
13298
|
return [];
|
|
12976
13299
|
}
|
|
12977
|
-
return (data || []).map((
|
|
13300
|
+
return (data || []).map((s2) => this.transformSession(s2));
|
|
12978
13301
|
} catch (err) {
|
|
12979
13302
|
console.error("BugBear: Error fetching session history", err);
|
|
12980
13303
|
return [];
|
|
@@ -13163,6 +13486,7 @@ var BugBearContext = (0, import_react.createContext)({
|
|
|
13163
13486
|
issueCounts: { open: 0, done: 0, reopened: 0 },
|
|
13164
13487
|
refreshIssueCounts: async () => {
|
|
13165
13488
|
},
|
|
13489
|
+
queuedCount: 0,
|
|
13166
13490
|
dashboardUrl: void 0,
|
|
13167
13491
|
onError: void 0
|
|
13168
13492
|
});
|
|
@@ -13179,6 +13503,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
13179
13503
|
const [threads, setThreads] = (0, import_react.useState)([]);
|
|
13180
13504
|
const [unreadCount, setUnreadCount] = (0, import_react.useState)(0);
|
|
13181
13505
|
const [issueCounts, setIssueCounts] = (0, import_react.useState)({ open: 0, done: 0, reopened: 0 });
|
|
13506
|
+
const [queuedCount, setQueuedCount] = (0, import_react.useState)(0);
|
|
13182
13507
|
const [activeSession, setActiveSession] = (0, import_react.useState)(null);
|
|
13183
13508
|
const [sessionFindings, setSessionFindings] = (0, import_react.useState)([]);
|
|
13184
13509
|
const hasInitialized = (0, import_react.useRef)(false);
|
|
@@ -13338,18 +13663,46 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
13338
13663
|
hasInitialized.current = true;
|
|
13339
13664
|
contextCapture.startCapture();
|
|
13340
13665
|
const newClient = createBugBear(config);
|
|
13666
|
+
if (newClient.queue) {
|
|
13667
|
+
newClient.queue.onChange(setQueuedCount);
|
|
13668
|
+
newClient.initQueue();
|
|
13669
|
+
}
|
|
13341
13670
|
setClient(newClient);
|
|
13342
13671
|
initializeBugBear(newClient);
|
|
13343
13672
|
}
|
|
13344
13673
|
}, [enabled, config, initializeBugBear]);
|
|
13674
|
+
(0, import_react.useEffect)(() => {
|
|
13675
|
+
if (!client?.queue) return;
|
|
13676
|
+
const subscription = import_react_native.AppState.addEventListener("change", (state) => {
|
|
13677
|
+
if (state === "active" && client.queue && client.queue.count > 0) {
|
|
13678
|
+
client.queue.flush();
|
|
13679
|
+
}
|
|
13680
|
+
});
|
|
13681
|
+
if (client.queue.count > 0) {
|
|
13682
|
+
client.queue.flush();
|
|
13683
|
+
}
|
|
13684
|
+
return () => subscription.remove();
|
|
13685
|
+
}, [client]);
|
|
13345
13686
|
(0, import_react.useEffect)(() => {
|
|
13346
13687
|
if (!client || !isTester || !isQAEnabled) return;
|
|
13688
|
+
let unsubscribe;
|
|
13689
|
+
if (client.realtimeEnabled) {
|
|
13690
|
+
unsubscribe = client.subscribeToChanges({
|
|
13691
|
+
onAssignmentChange: refreshAssignments,
|
|
13692
|
+
onMessageChange: refreshThreads,
|
|
13693
|
+
onReportChange: refreshIssueCounts
|
|
13694
|
+
});
|
|
13695
|
+
}
|
|
13696
|
+
const pollInterval = client.realtimeEnabled ? 12e4 : 3e4;
|
|
13347
13697
|
const interval = setInterval(() => {
|
|
13348
13698
|
refreshThreads();
|
|
13349
13699
|
refreshIssueCounts();
|
|
13350
|
-
},
|
|
13351
|
-
return () =>
|
|
13352
|
-
|
|
13700
|
+
}, pollInterval);
|
|
13701
|
+
return () => {
|
|
13702
|
+
clearInterval(interval);
|
|
13703
|
+
unsubscribe?.();
|
|
13704
|
+
};
|
|
13705
|
+
}, [client, isTester, isQAEnabled, refreshThreads, refreshIssueCounts, refreshAssignments]);
|
|
13353
13706
|
const currentAssignment = assignments.find(
|
|
13354
13707
|
(a) => a.status === "in_progress"
|
|
13355
13708
|
) || assignments.find(
|
|
@@ -13392,6 +13745,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
13392
13745
|
// Issue tracking
|
|
13393
13746
|
issueCounts,
|
|
13394
13747
|
refreshIssueCounts,
|
|
13748
|
+
queuedCount,
|
|
13395
13749
|
dashboardUrl: config.dashboardUrl,
|
|
13396
13750
|
onError: config.onError
|
|
13397
13751
|
}
|
|
@@ -13401,8 +13755,8 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
13401
13755
|
}
|
|
13402
13756
|
|
|
13403
13757
|
// src/BugBearButton.tsx
|
|
13404
|
-
var
|
|
13405
|
-
var
|
|
13758
|
+
var import_react20 = __toESM(require("react"));
|
|
13759
|
+
var import_react_native19 = require("react-native");
|
|
13406
13760
|
|
|
13407
13761
|
// src/widget/logo.ts
|
|
13408
13762
|
var BUGBEAR_LOGO_BASE64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAAAXNSR0IArs4c6QAAAJhlWElmTU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAIdpAAQAAAABAAAATgAAAAAAAABIAAAAAQAAAEgAAAABAASQBAACAAAAFAAAAISgAQADAAAAAQABAACgAgAEAAAAAQAAAGCgAwAEAAAAAQAAAGAAAAAAMjAyNjowMToyNCAxNjoyMTozOABbbVCuAAAACXBIWXMAAAsTAAALEwEAmpwYAAACo2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpJcHRjNHhtcEV4dD0iaHR0cDovL2lwdGMub3JnL3N0ZC9JcHRjNHhtcEV4dC8yMDA4LTAyLTI5LyIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIj4KICAgICAgICAgPElwdGM0eG1wRXh0OkRpZ2l0YWxTb3VyY2VUeXBlPmh0dHA6Ly9jdi5pcHRjLm9yZy9uZXdzY29kZXMvZGlnaXRhbHNvdXJjZXR5cGUvdHJhaW5lZEFsZ29yaXRobWljTWVkaWE8L0lwdGM0eG1wRXh0OkRpZ2l0YWxTb3VyY2VUeXBlPgogICAgICAgICA8SXB0YzR4bXBFeHQ6RGlnSW1hZ2VHVUlEPmZjNzJlN2Q2LTYyYTEtNDE1ZS04MjY5LWM2NjA4MjY0OWRiMDwvSXB0YzR4bXBFeHQ6RGlnSW1hZ2VHVUlEPgogICAgICAgICA8eG1wOkNyZWF0ZURhdGU+MjAyNi0wMS0yNFQxNjoyMTozODwveG1wOkNyZWF0ZURhdGU+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+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";
|
|
@@ -13546,9 +13900,9 @@ var shared = import_react_native2.StyleSheet.create({
|
|
|
13546
13900
|
function formatElapsedTime(seconds) {
|
|
13547
13901
|
const h = Math.floor(seconds / 3600);
|
|
13548
13902
|
const m = Math.floor(seconds % 3600 / 60);
|
|
13549
|
-
const
|
|
13550
|
-
if (h > 0) return `${h}:${m.toString().padStart(2, "0")}:${
|
|
13551
|
-
return `${m}:${
|
|
13903
|
+
const s2 = seconds % 60;
|
|
13904
|
+
if (h > 0) return `${h}:${m.toString().padStart(2, "0")}:${s2.toString().padStart(2, "0")}`;
|
|
13905
|
+
return `${m}:${s2.toString().padStart(2, "0")}`;
|
|
13552
13906
|
}
|
|
13553
13907
|
function formatRelativeTime(dateString) {
|
|
13554
13908
|
const date = new Date(dateString);
|
|
@@ -13620,11 +13974,90 @@ var templateInfo = {
|
|
|
13620
13974
|
};
|
|
13621
13975
|
|
|
13622
13976
|
// src/widget/screens/HomeScreen.tsx
|
|
13977
|
+
var import_react4 = __toESM(require("react"));
|
|
13978
|
+
var import_react_native4 = require("react-native");
|
|
13979
|
+
|
|
13980
|
+
// src/widget/Skeleton.tsx
|
|
13623
13981
|
var import_react3 = __toESM(require("react"));
|
|
13624
13982
|
var import_react_native3 = require("react-native");
|
|
13625
|
-
function
|
|
13626
|
-
const
|
|
13983
|
+
function usePulse(delay = 0) {
|
|
13984
|
+
const opacity = (0, import_react3.useRef)(new import_react_native3.Animated.Value(0.6)).current;
|
|
13627
13985
|
(0, import_react3.useEffect)(() => {
|
|
13986
|
+
const timeout = setTimeout(() => {
|
|
13987
|
+
import_react_native3.Animated.loop(
|
|
13988
|
+
import_react_native3.Animated.sequence([
|
|
13989
|
+
import_react_native3.Animated.timing(opacity, { toValue: 0.25, duration: 750, useNativeDriver: true }),
|
|
13990
|
+
import_react_native3.Animated.timing(opacity, { toValue: 0.6, duration: 750, useNativeDriver: true })
|
|
13991
|
+
])
|
|
13992
|
+
).start();
|
|
13993
|
+
}, delay);
|
|
13994
|
+
return () => clearTimeout(timeout);
|
|
13995
|
+
}, [opacity, delay]);
|
|
13996
|
+
return opacity;
|
|
13997
|
+
}
|
|
13998
|
+
function Bar({ width = "100%", height = 12, radius = 6, delay = 0 }) {
|
|
13999
|
+
const opacity = usePulse(delay);
|
|
14000
|
+
return /* @__PURE__ */ import_react3.default.createElement(
|
|
14001
|
+
import_react_native3.Animated.View,
|
|
14002
|
+
{
|
|
14003
|
+
style: {
|
|
14004
|
+
width,
|
|
14005
|
+
height,
|
|
14006
|
+
borderRadius: radius,
|
|
14007
|
+
backgroundColor: colors.border,
|
|
14008
|
+
opacity
|
|
14009
|
+
}
|
|
14010
|
+
}
|
|
14011
|
+
);
|
|
14012
|
+
}
|
|
14013
|
+
function Circle({ size = 20, delay = 0 }) {
|
|
14014
|
+
const opacity = usePulse(delay);
|
|
14015
|
+
return /* @__PURE__ */ import_react3.default.createElement(
|
|
14016
|
+
import_react_native3.Animated.View,
|
|
14017
|
+
{
|
|
14018
|
+
style: {
|
|
14019
|
+
width: size,
|
|
14020
|
+
height: size,
|
|
14021
|
+
borderRadius: size / 2,
|
|
14022
|
+
backgroundColor: colors.border,
|
|
14023
|
+
opacity
|
|
14024
|
+
}
|
|
14025
|
+
}
|
|
14026
|
+
);
|
|
14027
|
+
}
|
|
14028
|
+
function HomeScreenSkeleton() {
|
|
14029
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, null, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "100%", height: 100, radius: 16 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 20 } }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.actionGrid }, [0, 1, 2, 3].map((i) => /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { key: i, style: s.actionCard }, /* @__PURE__ */ import_react3.default.createElement(Circle, { size: 28, delay: i * 80 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 8 } }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 60, height: 10, delay: i * 80 })))), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.issueGrid }, [0, 1, 2].map((i) => /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { key: i, style: s.issueCard }, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 30, height: 18, delay: i * 100 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 6 } }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 40, height: 8, delay: i * 100 })))), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "100%", height: 6, radius: 3 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 8 } }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { alignItems: "center" } }, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 120, height: 10 })));
|
|
14030
|
+
}
|
|
14031
|
+
function TestItemSkeleton({ delay = 0 }) {
|
|
14032
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.testItem }, /* @__PURE__ */ import_react3.default.createElement(Circle, { size: 18, delay }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { flex: 1, marginLeft: 10 } }, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "70%", height: 11, delay }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 4 } }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "45%", height: 8, delay })), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 50, height: 18, radius: 6, delay }));
|
|
14033
|
+
}
|
|
14034
|
+
function TestListScreenSkeleton() {
|
|
14035
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, null, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.filterRow }, [55, 50, 50, 70].map((w, i) => /* @__PURE__ */ import_react3.default.createElement(Bar, { key: i, width: w, height: 28, radius: 8, delay: i * 50 }))), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "100%", height: 36, radius: 8 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 10 } }), [0, 1].map((g) => /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { key: g, style: { marginBottom: 12 } }, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.folderHeader }, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 12, height: 10 }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "40%", height: 12 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { flex: 1 } }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 40, height: 4, radius: 2 }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 24, height: 10 })), [0, 1, 2].map((i) => /* @__PURE__ */ import_react3.default.createElement(TestItemSkeleton, { key: i, delay: (g * 3 + i) * 80 })))));
|
|
14036
|
+
}
|
|
14037
|
+
function IssueListScreenSkeleton() {
|
|
14038
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, null, [0, 1, 2, 3].map((i) => /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { key: i, style: s.issueRow }, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.issueRowTop }, /* @__PURE__ */ import_react3.default.createElement(Circle, { size: 8, delay: i * 100 }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "65%", height: 11, delay: i * 100 })), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.issueRowBottom }, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "40%", height: 8, delay: i * 100 }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 40, height: 8, delay: i * 100 })))));
|
|
14039
|
+
}
|
|
14040
|
+
function MessageListScreenSkeleton() {
|
|
14041
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, null, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "100%", height: 44, radius: 12 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 16 } }), [0, 1, 2, 3].map((i) => /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { key: i, style: s.threadRow }, /* @__PURE__ */ import_react3.default.createElement(Circle, { size: 20, delay: i * 100 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { flex: 1, marginLeft: 10 } }, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "55%", height: 11, delay: i * 100 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 5 } }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "80%", height: 9, delay: i * 100 })), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 30, height: 8, delay: i * 100 }))));
|
|
14042
|
+
}
|
|
14043
|
+
var s = import_react_native3.StyleSheet.create({
|
|
14044
|
+
actionGrid: { flexDirection: "row", flexWrap: "wrap", gap: 12, marginBottom: 20 },
|
|
14045
|
+
actionCard: { width: "47%", backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 12, padding: 16, alignItems: "center" },
|
|
14046
|
+
issueGrid: { flexDirection: "row", gap: 10, marginBottom: 20 },
|
|
14047
|
+
issueCard: { flex: 1, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 10, paddingVertical: 12, paddingHorizontal: 8, alignItems: "center" },
|
|
14048
|
+
filterRow: { flexDirection: "row", gap: 8, marginBottom: 8 },
|
|
14049
|
+
folderHeader: { flexDirection: "row", alignItems: "center", gap: 8, paddingVertical: 8, paddingHorizontal: 4 },
|
|
14050
|
+
testItem: { flexDirection: "row", alignItems: "center", paddingVertical: 10, paddingHorizontal: 12, borderRadius: 8, marginBottom: 4, backgroundColor: colors.card },
|
|
14051
|
+
issueRow: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 14, marginBottom: 8 },
|
|
14052
|
+
issueRowTop: { flexDirection: "row", alignItems: "center", gap: 8 },
|
|
14053
|
+
issueRowBottom: { flexDirection: "row", justifyContent: "space-between", marginTop: 8 },
|
|
14054
|
+
threadRow: { flexDirection: "row", alignItems: "flex-start", padding: 12, borderRadius: 10, marginBottom: 4, backgroundColor: colors.card }
|
|
14055
|
+
});
|
|
14056
|
+
|
|
14057
|
+
// src/widget/screens/HomeScreen.tsx
|
|
14058
|
+
function HomeScreen({ nav }) {
|
|
14059
|
+
const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, dashboardUrl, isLoading } = useBugBear();
|
|
14060
|
+
(0, import_react4.useEffect)(() => {
|
|
13628
14061
|
refreshAssignments();
|
|
13629
14062
|
refreshThreads();
|
|
13630
14063
|
refreshIssueCounts();
|
|
@@ -13634,104 +14067,105 @@ function HomeScreen({ nav }) {
|
|
|
13634
14067
|
const retestCount = pendingAssignments.filter((a) => a.isVerification).length;
|
|
13635
14068
|
const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
|
|
13636
14069
|
const totalTests = assignments.length;
|
|
13637
|
-
return /* @__PURE__ */
|
|
13638
|
-
|
|
14070
|
+
if (isLoading) return /* @__PURE__ */ import_react4.default.createElement(HomeScreenSkeleton, null);
|
|
14071
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, null, pendingCount > 0 ? /* @__PURE__ */ import_react4.default.createElement(
|
|
14072
|
+
import_react_native4.TouchableOpacity,
|
|
13639
14073
|
{
|
|
13640
14074
|
style: [styles.heroBanner, styles.heroBannerTests],
|
|
13641
14075
|
onPress: () => nav.push({ name: "TEST_DETAIL" }),
|
|
13642
14076
|
activeOpacity: 0.8
|
|
13643
14077
|
},
|
|
13644
|
-
/* @__PURE__ */
|
|
13645
|
-
/* @__PURE__ */
|
|
13646
|
-
retestCount > 0 && /* @__PURE__ */
|
|
13647
|
-
/* @__PURE__ */
|
|
13648
|
-
) : unreadCount > 0 ? /* @__PURE__ */
|
|
13649
|
-
|
|
14078
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroCount }, pendingCount),
|
|
14079
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroLabel }, "test", pendingCount !== 1 ? "s" : "", " waiting"),
|
|
14080
|
+
retestCount > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.retestPill }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.retestPillText }, "\u{1F504} ", retestCount, " retest", retestCount !== 1 ? "s" : "")),
|
|
14081
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroAction }, "Start Testing \u2192")
|
|
14082
|
+
) : unreadCount > 0 ? /* @__PURE__ */ import_react4.default.createElement(
|
|
14083
|
+
import_react_native4.TouchableOpacity,
|
|
13650
14084
|
{
|
|
13651
14085
|
style: [styles.heroBanner, styles.heroBannerMessages],
|
|
13652
14086
|
onPress: () => nav.push({ name: "MESSAGE_LIST" }),
|
|
13653
14087
|
activeOpacity: 0.8
|
|
13654
14088
|
},
|
|
13655
|
-
/* @__PURE__ */
|
|
13656
|
-
/* @__PURE__ */
|
|
13657
|
-
/* @__PURE__ */
|
|
13658
|
-
) : /* @__PURE__ */
|
|
13659
|
-
|
|
14089
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroCount }, unreadCount),
|
|
14090
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroLabel }, "unread message", unreadCount !== 1 ? "s" : ""),
|
|
14091
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroAction }, "View Messages \u2192")
|
|
14092
|
+
) : /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: [styles.heroBanner, styles.heroBannerClear] }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroClearEmoji }, "\u2705"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroClearTitle }, "All caught up!"), totalTests > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroClearSub }, completedCount, "/", totalTests, " tests completed")), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.actionGrid }, /* @__PURE__ */ import_react4.default.createElement(
|
|
14093
|
+
import_react_native4.TouchableOpacity,
|
|
13660
14094
|
{
|
|
13661
14095
|
style: styles.actionCard,
|
|
13662
14096
|
onPress: () => nav.push({ name: "TEST_LIST" }),
|
|
13663
14097
|
activeOpacity: 0.7
|
|
13664
14098
|
},
|
|
13665
|
-
/* @__PURE__ */
|
|
13666
|
-
/* @__PURE__ */
|
|
13667
|
-
pendingCount > 0 && /* @__PURE__ */
|
|
13668
|
-
), /* @__PURE__ */
|
|
13669
|
-
|
|
14099
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionIcon }, "\u2705"),
|
|
14100
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionLabel }, "Tests"),
|
|
14101
|
+
pendingCount > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.actionBadge }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionBadgeText }, pendingCount))
|
|
14102
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
14103
|
+
import_react_native4.TouchableOpacity,
|
|
13670
14104
|
{
|
|
13671
14105
|
style: styles.actionCard,
|
|
13672
14106
|
onPress: () => nav.push({ name: "REPORT", prefill: { type: "bug" } }),
|
|
13673
14107
|
activeOpacity: 0.7
|
|
13674
14108
|
},
|
|
13675
|
-
/* @__PURE__ */
|
|
13676
|
-
/* @__PURE__ */
|
|
13677
|
-
), /* @__PURE__ */
|
|
13678
|
-
|
|
14109
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionIcon }, "\u{1F41B}"),
|
|
14110
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionLabel }, "Report Bug")
|
|
14111
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
14112
|
+
import_react_native4.TouchableOpacity,
|
|
13679
14113
|
{
|
|
13680
14114
|
style: styles.actionCard,
|
|
13681
14115
|
onPress: () => nav.push({ name: "REPORT", prefill: { type: "feedback" } }),
|
|
13682
14116
|
activeOpacity: 0.7
|
|
13683
14117
|
},
|
|
13684
|
-
/* @__PURE__ */
|
|
13685
|
-
/* @__PURE__ */
|
|
13686
|
-
), /* @__PURE__ */
|
|
13687
|
-
|
|
14118
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionIcon }, "\u{1F4A1}"),
|
|
14119
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionLabel }, "Feedback")
|
|
14120
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
14121
|
+
import_react_native4.TouchableOpacity,
|
|
13688
14122
|
{
|
|
13689
14123
|
style: styles.actionCard,
|
|
13690
14124
|
onPress: () => nav.push({ name: "MESSAGE_LIST" }),
|
|
13691
14125
|
activeOpacity: 0.7
|
|
13692
14126
|
},
|
|
13693
|
-
/* @__PURE__ */
|
|
13694
|
-
/* @__PURE__ */
|
|
13695
|
-
unreadCount > 0 && /* @__PURE__ */
|
|
13696
|
-
)), /* @__PURE__ */
|
|
13697
|
-
|
|
14127
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionIcon }, "\u{1F4AC}"),
|
|
14128
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionLabel }, "Messages"),
|
|
14129
|
+
unreadCount > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: [styles.actionBadge, styles.actionBadgeMsg] }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionBadgeText }, unreadCount))
|
|
14130
|
+
)), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.issueGrid }, /* @__PURE__ */ import_react4.default.createElement(
|
|
14131
|
+
import_react_native4.TouchableOpacity,
|
|
13698
14132
|
{
|
|
13699
14133
|
style: [styles.issueCard, styles.issueCardOpen],
|
|
13700
14134
|
onPress: () => nav.push({ name: "ISSUE_LIST", category: "open" }),
|
|
13701
14135
|
activeOpacity: 0.7
|
|
13702
14136
|
},
|
|
13703
|
-
/* @__PURE__ */
|
|
13704
|
-
/* @__PURE__ */
|
|
13705
|
-
), /* @__PURE__ */
|
|
13706
|
-
|
|
14137
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.issueCountOpen }, issueCounts.open),
|
|
14138
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.issueLabel }, "Open")
|
|
14139
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
14140
|
+
import_react_native4.TouchableOpacity,
|
|
13707
14141
|
{
|
|
13708
14142
|
style: [styles.issueCard, styles.issueCardDone],
|
|
13709
14143
|
onPress: () => nav.push({ name: "ISSUE_LIST", category: "done" }),
|
|
13710
14144
|
activeOpacity: 0.7
|
|
13711
14145
|
},
|
|
13712
|
-
/* @__PURE__ */
|
|
13713
|
-
/* @__PURE__ */
|
|
13714
|
-
), /* @__PURE__ */
|
|
13715
|
-
|
|
14146
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.issueCountDone }, issueCounts.done),
|
|
14147
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.issueLabel }, "Done")
|
|
14148
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
14149
|
+
import_react_native4.TouchableOpacity,
|
|
13716
14150
|
{
|
|
13717
14151
|
style: [styles.issueCard, styles.issueCardReopened],
|
|
13718
14152
|
onPress: () => nav.push({ name: "ISSUE_LIST", category: "reopened" }),
|
|
13719
14153
|
activeOpacity: 0.7
|
|
13720
14154
|
},
|
|
13721
|
-
/* @__PURE__ */
|
|
13722
|
-
/* @__PURE__ */
|
|
13723
|
-
)), totalTests > 0 && /* @__PURE__ */
|
|
13724
|
-
|
|
14155
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.issueCountReopened }, issueCounts.reopened),
|
|
14156
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.issueLabel }, "Reopened")
|
|
14157
|
+
)), totalTests > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.progressSection }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.progressBar }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: [styles.progressFill, { width: `${Math.round(completedCount / totalTests * 100)}%` }] })), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.progressText }, completedCount, "/", totalTests, " tests completed")), dashboardUrl && /* @__PURE__ */ import_react4.default.createElement(
|
|
14158
|
+
import_react_native4.TouchableOpacity,
|
|
13725
14159
|
{
|
|
13726
14160
|
style: styles.webAppLink,
|
|
13727
|
-
onPress: () =>
|
|
14161
|
+
onPress: () => import_react_native4.Linking.openURL(dashboardUrl),
|
|
13728
14162
|
activeOpacity: 0.7
|
|
13729
14163
|
},
|
|
13730
|
-
/* @__PURE__ */
|
|
13731
|
-
/* @__PURE__ */
|
|
13732
|
-
/* @__PURE__ */
|
|
13733
|
-
), /* @__PURE__ */
|
|
13734
|
-
|
|
14164
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.webAppIcon }, "\u{1F310}"),
|
|
14165
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.webAppTextWrap }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.webAppTitle }, "Open Web Dashboard"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.webAppSub }, "View analytics, history & more")),
|
|
14166
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.webAppArrow }, "\u2192")
|
|
14167
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
14168
|
+
import_react_native4.TouchableOpacity,
|
|
13735
14169
|
{
|
|
13736
14170
|
style: styles.refreshButton,
|
|
13737
14171
|
onPress: () => {
|
|
@@ -13740,10 +14174,10 @@ function HomeScreen({ nav }) {
|
|
|
13740
14174
|
refreshIssueCounts();
|
|
13741
14175
|
}
|
|
13742
14176
|
},
|
|
13743
|
-
/* @__PURE__ */
|
|
14177
|
+
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.refreshText }, "\u21BB Refresh")
|
|
13744
14178
|
));
|
|
13745
14179
|
}
|
|
13746
|
-
var styles =
|
|
14180
|
+
var styles = import_react_native4.StyleSheet.create({
|
|
13747
14181
|
heroBanner: {
|
|
13748
14182
|
borderRadius: 16,
|
|
13749
14183
|
padding: 24,
|
|
@@ -13967,26 +14401,26 @@ var styles = import_react_native3.StyleSheet.create({
|
|
|
13967
14401
|
});
|
|
13968
14402
|
|
|
13969
14403
|
// src/widget/screens/TestDetailScreen.tsx
|
|
13970
|
-
var
|
|
13971
|
-
var
|
|
14404
|
+
var import_react5 = __toESM(require("react"));
|
|
14405
|
+
var import_react_native5 = require("react-native");
|
|
13972
14406
|
function TestDetailScreen({ testId, nav }) {
|
|
13973
14407
|
const { client, assignments, currentAssignment, refreshAssignments, getDeviceInfo, onNavigate } = useBugBear();
|
|
13974
14408
|
const displayedAssignment = testId ? assignments.find((a) => a.id === testId) || currentAssignment : currentAssignment;
|
|
13975
|
-
const [showSteps, setShowSteps] = (0,
|
|
13976
|
-
const [showDetails, setShowDetails] = (0,
|
|
13977
|
-
const [criteriaResults, setCriteriaResults] = (0,
|
|
13978
|
-
const [assignmentElapsedTime, setAssignmentElapsedTime] = (0,
|
|
13979
|
-
const [showSkipModal, setShowSkipModal] = (0,
|
|
13980
|
-
const [selectedSkipReason, setSelectedSkipReason] = (0,
|
|
13981
|
-
const [skipNotes, setSkipNotes] = (0,
|
|
13982
|
-
const [skipping, setSkipping] = (0,
|
|
13983
|
-
const [isSubmitting, setIsSubmitting] = (0,
|
|
13984
|
-
(0,
|
|
14409
|
+
const [showSteps, setShowSteps] = (0, import_react5.useState)(true);
|
|
14410
|
+
const [showDetails, setShowDetails] = (0, import_react5.useState)(false);
|
|
14411
|
+
const [criteriaResults, setCriteriaResults] = (0, import_react5.useState)({});
|
|
14412
|
+
const [assignmentElapsedTime, setAssignmentElapsedTime] = (0, import_react5.useState)(0);
|
|
14413
|
+
const [showSkipModal, setShowSkipModal] = (0, import_react5.useState)(false);
|
|
14414
|
+
const [selectedSkipReason, setSelectedSkipReason] = (0, import_react5.useState)(null);
|
|
14415
|
+
const [skipNotes, setSkipNotes] = (0, import_react5.useState)("");
|
|
14416
|
+
const [skipping, setSkipping] = (0, import_react5.useState)(false);
|
|
14417
|
+
const [isSubmitting, setIsSubmitting] = (0, import_react5.useState)(false);
|
|
14418
|
+
(0, import_react5.useEffect)(() => {
|
|
13985
14419
|
setCriteriaResults({});
|
|
13986
14420
|
setShowSteps(true);
|
|
13987
14421
|
setShowDetails(false);
|
|
13988
14422
|
}, [displayedAssignment?.id]);
|
|
13989
|
-
(0,
|
|
14423
|
+
(0, import_react5.useEffect)(() => {
|
|
13990
14424
|
const active = displayedAssignment?.status === "in_progress" ? displayedAssignment : null;
|
|
13991
14425
|
if (!active?.startedAt) {
|
|
13992
14426
|
setAssignmentElapsedTime(0);
|
|
@@ -14002,9 +14436,9 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14002
14436
|
const pendingTests = assignments.filter((a) => a.status === "pending" || a.status === "in_progress");
|
|
14003
14437
|
const allTests = assignments;
|
|
14004
14438
|
const currentIndex = displayedAssignment ? assignments.indexOf(displayedAssignment) : -1;
|
|
14005
|
-
const handlePass = (0,
|
|
14439
|
+
const handlePass = (0, import_react5.useCallback)(async () => {
|
|
14006
14440
|
if (!client || !displayedAssignment || isSubmitting) return;
|
|
14007
|
-
|
|
14441
|
+
import_react_native5.Keyboard.dismiss();
|
|
14008
14442
|
setIsSubmitting(true);
|
|
14009
14443
|
try {
|
|
14010
14444
|
await client.passAssignment(displayedAssignment.id);
|
|
@@ -14014,9 +14448,9 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14014
14448
|
setIsSubmitting(false);
|
|
14015
14449
|
}
|
|
14016
14450
|
}, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
|
|
14017
|
-
const handleFail = (0,
|
|
14451
|
+
const handleFail = (0, import_react5.useCallback)(async () => {
|
|
14018
14452
|
if (!client || !displayedAssignment || isSubmitting) return;
|
|
14019
|
-
|
|
14453
|
+
import_react_native5.Keyboard.dismiss();
|
|
14020
14454
|
setIsSubmitting(true);
|
|
14021
14455
|
try {
|
|
14022
14456
|
const result = await client.failAssignment(displayedAssignment.id);
|
|
@@ -14036,9 +14470,9 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14036
14470
|
setIsSubmitting(false);
|
|
14037
14471
|
}
|
|
14038
14472
|
}, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
|
|
14039
|
-
const handleSkip = (0,
|
|
14473
|
+
const handleSkip = (0, import_react5.useCallback)(async () => {
|
|
14040
14474
|
if (!client || !displayedAssignment || !selectedSkipReason) return;
|
|
14041
|
-
|
|
14475
|
+
import_react_native5.Keyboard.dismiss();
|
|
14042
14476
|
setSkipping(true);
|
|
14043
14477
|
await client.skipAssignment(displayedAssignment.id, {
|
|
14044
14478
|
reason: selectedSkipReason,
|
|
@@ -14062,15 +14496,15 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14062
14496
|
}
|
|
14063
14497
|
}, [client, displayedAssignment, selectedSkipReason, skipNotes, refreshAssignments, assignments, nav]);
|
|
14064
14498
|
if (!displayedAssignment) {
|
|
14065
|
-
return /* @__PURE__ */
|
|
14499
|
+
return /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: shared.emptyState }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: shared.emptyTitle }, "No tests assigned"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: shared.emptySubtitle }, "Check back later for new tests"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: [shared.primaryButton, { marginTop: 20, paddingHorizontal: 24 }], onPress: () => nav.reset() }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: shared.primaryButtonText }, "Go Home")));
|
|
14066
14500
|
}
|
|
14067
14501
|
const testCase = displayedAssignment.testCase;
|
|
14068
14502
|
const template = testCase.track?.testTemplate || "steps";
|
|
14069
14503
|
const steps = testCase.steps;
|
|
14070
14504
|
const info = templateInfo[template] || templateInfo.steps;
|
|
14071
14505
|
const rubricMode = testCase.track?.rubricMode || "pass_fail";
|
|
14072
|
-
return /* @__PURE__ */
|
|
14073
|
-
|
|
14506
|
+
return /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.container }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.topRow }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.positionInfo }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.positionText }, "Test ", currentIndex + 1, " of ", allTests.length), displayedAssignment.status === "in_progress" && assignmentElapsedTime > 0 && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.timerBadge }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.timerText }, formatElapsedTime(assignmentElapsedTime)))), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { onPress: () => nav.push({ name: "TEST_LIST" }) }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.viewAllLink }, "View All \u2192"))), displayedAssignment.isVerification && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.retestBanner }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.retestIcon }, "\u{1F504}"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.retestLabel }, "Retest"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.retestSub }, "\u2014 Verify bug fix")), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.testTitle }, testCase.title), testCase.key && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.testKey }, testCase.key), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { onPress: () => setShowSteps(!showSteps), style: styles2.sectionHeader }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.sectionHeaderText }, showSteps ? "\u25BC" : "\u25B6", " ", info.icon, " ", template === "freeform" ? "Instructions" : `${steps.length} ${template === "checklist" ? "items" : template === "rubric" ? "criteria" : "steps"}`)), showSteps && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.templateContent }, template === "steps" && steps.map((step, idx) => /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { key: idx, style: styles2.step }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.stepNumber }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.stepNumberText }, step.stepNumber)), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.stepBody }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.stepAction }, step.action), step.expectedResult && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.stepExpected }, "\u2192 ", step.expectedResult)))), template === "checklist" && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, steps.map((step, idx) => /* @__PURE__ */ import_react5.default.createElement(
|
|
14507
|
+
import_react_native5.TouchableOpacity,
|
|
14074
14508
|
{
|
|
14075
14509
|
key: idx,
|
|
14076
14510
|
onPress: () => setCriteriaResults((prev) => {
|
|
@@ -14081,56 +14515,56 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14081
14515
|
}),
|
|
14082
14516
|
style: [styles2.checklistItem, criteriaResults[idx] === true && styles2.checklistItemChecked]
|
|
14083
14517
|
},
|
|
14084
|
-
/* @__PURE__ */
|
|
14085
|
-
/* @__PURE__ */
|
|
14086
|
-
)), Object.keys(criteriaResults).length > 0 && /* @__PURE__ */
|
|
14087
|
-
|
|
14518
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles2.checkbox, criteriaResults[idx] === true && styles2.checkboxChecked] }, criteriaResults[idx] === true && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.checkmark }, "\u2713")),
|
|
14519
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles2.checklistText, criteriaResults[idx] === true && styles2.checklistTextDone] }, step.action)
|
|
14520
|
+
)), Object.keys(criteriaResults).length > 0 && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { onPress: () => setCriteriaResults({}), style: styles2.resetRow }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.resetText }, "\u21BA Reset"))), template === "rubric" && steps.map((step, idx) => /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { key: idx, style: styles2.rubricItem }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.rubricTitle }, idx + 1, ". ", step.action), step.expectedResult && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.rubricExpected }, step.expectedResult), rubricMode === "pass_fail" ? /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.passFailRow }, /* @__PURE__ */ import_react5.default.createElement(
|
|
14521
|
+
import_react_native5.TouchableOpacity,
|
|
14088
14522
|
{
|
|
14089
14523
|
onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: true })),
|
|
14090
14524
|
style: [styles2.pfButton, criteriaResults[idx] === true && styles2.pfButtonPass]
|
|
14091
14525
|
},
|
|
14092
|
-
/* @__PURE__ */
|
|
14093
|
-
), /* @__PURE__ */
|
|
14094
|
-
|
|
14526
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles2.pfButtonText, criteriaResults[idx] === true && styles2.pfButtonTextActive] }, "\u2713 Pass")
|
|
14527
|
+
), /* @__PURE__ */ import_react5.default.createElement(
|
|
14528
|
+
import_react_native5.TouchableOpacity,
|
|
14095
14529
|
{
|
|
14096
14530
|
onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: false })),
|
|
14097
14531
|
style: [styles2.pfButton, criteriaResults[idx] === false && styles2.pfButtonFail]
|
|
14098
14532
|
},
|
|
14099
|
-
/* @__PURE__ */
|
|
14100
|
-
)) : /* @__PURE__ */
|
|
14101
|
-
|
|
14533
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles2.pfButtonText, criteriaResults[idx] === false && styles2.pfButtonTextActive] }, "\u2717 Fail")
|
|
14534
|
+
)) : /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.ratingRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ import_react5.default.createElement(
|
|
14535
|
+
import_react_native5.TouchableOpacity,
|
|
14102
14536
|
{
|
|
14103
14537
|
key: n,
|
|
14104
14538
|
onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: n })),
|
|
14105
14539
|
style: [styles2.ratingBtn, criteriaResults[idx] === n && styles2.ratingBtnActive]
|
|
14106
14540
|
},
|
|
14107
|
-
/* @__PURE__ */
|
|
14108
|
-
))))), template === "freeform" && /* @__PURE__ */
|
|
14109
|
-
|
|
14541
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles2.ratingBtnText, criteriaResults[idx] === n && styles2.ratingBtnTextActive] }, n)
|
|
14542
|
+
))))), template === "freeform" && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.freeformBox }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.freeformText }, "Review the area and note:"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.freeformBullet }, "\u2022 What works well"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.freeformBullet }, "\u2022 Issues or concerns"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.freeformBullet }, "\u2022 Suggestions"))), testCase.expectedResult && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.expectedBox }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.expectedLabel }, "\u2705 Expected Result"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.expectedText }, testCase.expectedResult)), testCase.targetRoute && onNavigate && /* @__PURE__ */ import_react5.default.createElement(
|
|
14543
|
+
import_react_native5.TouchableOpacity,
|
|
14110
14544
|
{
|
|
14111
14545
|
style: styles2.navigateButton,
|
|
14112
14546
|
onPress: () => {
|
|
14113
|
-
|
|
14547
|
+
import_react_native5.Keyboard.dismiss();
|
|
14114
14548
|
onNavigate(testCase.targetRoute);
|
|
14115
14549
|
nav.closeWidget?.();
|
|
14116
14550
|
}
|
|
14117
14551
|
},
|
|
14118
|
-
/* @__PURE__ */
|
|
14119
|
-
), /* @__PURE__ */
|
|
14552
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.navigateText }, "\u{1F9ED} Go to test location")
|
|
14553
|
+
), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { onPress: () => setShowDetails(!showDetails), style: styles2.detailsToggle }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.detailsSection }, testCase.key && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.detailMeta }, testCase.key, " \xB7 ", testCase.priority, " \xB7 ", info.name), testCase.description && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.detailDesc }, testCase.description), testCase.group && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.folderProgress }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.folderName }, "\u{1F4C1} ", testCase.group.name))), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.actionButtons }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }], onPress: handleFail, disabled: isSubmitting }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.failBtnText }, isSubmitting ? "Failing..." : "Fail")), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: [styles2.actionBtn, styles2.skipBtn, isSubmitting && { opacity: 0.5 }], onPress: () => setShowSkipModal(true), disabled: isSubmitting }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.skipBtnText }, "Skip")), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }], onPress: handlePass, disabled: isSubmitting }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.passBtnText }, isSubmitting ? "Passing..." : "Pass"))), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Modal, { visible: showSkipModal, transparent: true, animationType: "fade" }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.modalOverlay }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.modalContent }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.modalTitle }, "Skip this test?"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.modalSubtitle }, "Select a reason:"), [
|
|
14120
14554
|
{ reason: "blocked", label: "\u{1F6AB} Blocked by a bug" },
|
|
14121
14555
|
{ reason: "not_ready", label: "\u{1F6A7} Feature not ready" },
|
|
14122
14556
|
{ reason: "dependency", label: "\u{1F517} Needs another test first" },
|
|
14123
14557
|
{ reason: "other", label: "\u{1F4DD} Other reason" }
|
|
14124
|
-
].map(({ reason, label }) => /* @__PURE__ */
|
|
14125
|
-
|
|
14558
|
+
].map(({ reason, label }) => /* @__PURE__ */ import_react5.default.createElement(
|
|
14559
|
+
import_react_native5.TouchableOpacity,
|
|
14126
14560
|
{
|
|
14127
14561
|
key: reason,
|
|
14128
14562
|
style: [styles2.skipOption, selectedSkipReason === reason && styles2.skipOptionActive],
|
|
14129
14563
|
onPress: () => setSelectedSkipReason(reason)
|
|
14130
14564
|
},
|
|
14131
|
-
/* @__PURE__ */
|
|
14132
|
-
)), /* @__PURE__ */
|
|
14133
|
-
|
|
14565
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles2.skipOptionText, selectedSkipReason === reason && styles2.skipOptionTextActive] }, label)
|
|
14566
|
+
)), /* @__PURE__ */ import_react5.default.createElement(
|
|
14567
|
+
import_react_native5.TextInput,
|
|
14134
14568
|
{
|
|
14135
14569
|
style: styles2.skipNotes,
|
|
14136
14570
|
value: skipNotes,
|
|
@@ -14139,21 +14573,21 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14139
14573
|
placeholderTextColor: colors.textMuted,
|
|
14140
14574
|
multiline: true
|
|
14141
14575
|
}
|
|
14142
|
-
), /* @__PURE__ */
|
|
14576
|
+
), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.skipActions }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: styles2.skipCancel, onPress: () => {
|
|
14143
14577
|
setShowSkipModal(false);
|
|
14144
14578
|
setSelectedSkipReason(null);
|
|
14145
14579
|
setSkipNotes("");
|
|
14146
|
-
} }, /* @__PURE__ */
|
|
14147
|
-
|
|
14580
|
+
} }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.skipCancelText }, "Cancel")), /* @__PURE__ */ import_react5.default.createElement(
|
|
14581
|
+
import_react_native5.TouchableOpacity,
|
|
14148
14582
|
{
|
|
14149
14583
|
style: [styles2.skipConfirm, !selectedSkipReason && { opacity: 0.4 }],
|
|
14150
14584
|
onPress: handleSkip,
|
|
14151
14585
|
disabled: !selectedSkipReason || skipping
|
|
14152
14586
|
},
|
|
14153
|
-
/* @__PURE__ */
|
|
14587
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.skipConfirmText }, skipping ? "Skipping..." : "Skip Test")
|
|
14154
14588
|
))))));
|
|
14155
14589
|
}
|
|
14156
|
-
var styles2 =
|
|
14590
|
+
var styles2 = import_react_native5.StyleSheet.create({
|
|
14157
14591
|
container: { paddingBottom: 16 },
|
|
14158
14592
|
retestBanner: { flexDirection: "row", alignItems: "center", gap: 6, backgroundColor: "#422006", borderWidth: 1, borderColor: "#854d0e", borderRadius: 8, paddingVertical: 6, paddingHorizontal: 10, marginBottom: 10 },
|
|
14159
14593
|
retestIcon: { fontSize: 14 },
|
|
@@ -14248,25 +14682,35 @@ var styles2 = import_react_native4.StyleSheet.create({
|
|
|
14248
14682
|
});
|
|
14249
14683
|
|
|
14250
14684
|
// src/widget/screens/TestListScreen.tsx
|
|
14251
|
-
var
|
|
14252
|
-
var
|
|
14685
|
+
var import_react6 = __toESM(require("react"));
|
|
14686
|
+
var import_react_native6 = require("react-native");
|
|
14253
14687
|
function TestListScreen({ nav }) {
|
|
14254
|
-
const { assignments, currentAssignment, refreshAssignments } = useBugBear();
|
|
14255
|
-
const [filter, setFilter] = (0,
|
|
14256
|
-
const [roleFilter, setRoleFilter] = (0,
|
|
14257
|
-
const [
|
|
14258
|
-
(0,
|
|
14688
|
+
const { assignments, currentAssignment, refreshAssignments, isLoading } = useBugBear();
|
|
14689
|
+
const [filter, setFilter] = (0, import_react6.useState)("all");
|
|
14690
|
+
const [roleFilter, setRoleFilter] = (0, import_react6.useState)(null);
|
|
14691
|
+
const [trackFilter, setTrackFilter] = (0, import_react6.useState)(null);
|
|
14692
|
+
const [searchQuery, setSearchQuery] = (0, import_react6.useState)("");
|
|
14693
|
+
const [sortMode, setSortMode] = (0, import_react6.useState)("priority");
|
|
14694
|
+
const [collapsedFolders, setCollapsedFolders] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
|
|
14695
|
+
(0, import_react6.useEffect)(() => {
|
|
14259
14696
|
refreshAssignments();
|
|
14260
14697
|
}, []);
|
|
14261
|
-
const availableRoles = (0,
|
|
14698
|
+
const availableRoles = (0, import_react6.useMemo)(() => {
|
|
14262
14699
|
const roleMap = /* @__PURE__ */ new Map();
|
|
14263
14700
|
for (const a of assignments) {
|
|
14264
14701
|
if (a.testCase.role) roleMap.set(a.testCase.role.id, a.testCase.role);
|
|
14265
14702
|
}
|
|
14266
14703
|
return Array.from(roleMap.values());
|
|
14267
14704
|
}, [assignments]);
|
|
14705
|
+
const availableTracks = (0, import_react6.useMemo)(() => {
|
|
14706
|
+
const trackMap = /* @__PURE__ */ new Map();
|
|
14707
|
+
for (const a of assignments) {
|
|
14708
|
+
if (a.testCase.track) trackMap.set(a.testCase.track.id, a.testCase.track);
|
|
14709
|
+
}
|
|
14710
|
+
return Array.from(trackMap.values());
|
|
14711
|
+
}, [assignments]);
|
|
14268
14712
|
const selectedRole = availableRoles.find((r) => r.id === roleFilter);
|
|
14269
|
-
const groupedAssignments = (0,
|
|
14713
|
+
const groupedAssignments = (0, import_react6.useMemo)(() => {
|
|
14270
14714
|
const groups = /* @__PURE__ */ new Map();
|
|
14271
14715
|
for (const assignment of assignments) {
|
|
14272
14716
|
const groupId = assignment.testCase.group?.id || "ungrouped";
|
|
@@ -14288,6 +14732,8 @@ function TestListScreen({ nav }) {
|
|
|
14288
14732
|
folder.assignments.sort((a, b) => {
|
|
14289
14733
|
if (a.isVerification && !b.isVerification) return -1;
|
|
14290
14734
|
if (!a.isVerification && b.isVerification) return 1;
|
|
14735
|
+
if (sortMode === "alpha") return a.testCase.title.localeCompare(b.testCase.title);
|
|
14736
|
+
if (sortMode === "recent") return 0;
|
|
14291
14737
|
const sd = (statusOrder[a.status] ?? 5) - (statusOrder[b.status] ?? 5);
|
|
14292
14738
|
if (sd !== 0) return sd;
|
|
14293
14739
|
return (priorityOrder[a.testCase.priority] ?? 4) - (priorityOrder[b.testCase.priority] ?? 4);
|
|
@@ -14299,8 +14745,8 @@ function TestListScreen({ nav }) {
|
|
|
14299
14745
|
if (!b.group) return -1;
|
|
14300
14746
|
return a.group.sortOrder - b.group.sortOrder;
|
|
14301
14747
|
});
|
|
14302
|
-
}, [assignments]);
|
|
14303
|
-
const toggleFolder = (0,
|
|
14748
|
+
}, [assignments, sortMode]);
|
|
14749
|
+
const toggleFolder = (0, import_react6.useCallback)((id) => {
|
|
14304
14750
|
setCollapsedFolders((prev) => {
|
|
14305
14751
|
const next = new Set(prev);
|
|
14306
14752
|
if (next.has(id)) next.delete(id);
|
|
@@ -14308,29 +14754,37 @@ function TestListScreen({ nav }) {
|
|
|
14308
14754
|
return next;
|
|
14309
14755
|
});
|
|
14310
14756
|
}, []);
|
|
14311
|
-
const filterAssignment = (a) => {
|
|
14757
|
+
const filterAssignment = (0, import_react6.useCallback)((a) => {
|
|
14312
14758
|
if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
|
|
14759
|
+
if (trackFilter && a.testCase.track?.id !== trackFilter) return false;
|
|
14760
|
+
if (searchQuery) {
|
|
14761
|
+
const q = searchQuery.toLowerCase();
|
|
14762
|
+
const titleMatch = a.testCase.title.toLowerCase().includes(q);
|
|
14763
|
+
const keyMatch = a.testCase.testKey.toLowerCase().includes(q);
|
|
14764
|
+
if (!titleMatch && !keyMatch) return false;
|
|
14765
|
+
}
|
|
14313
14766
|
if (filter === "pending") return a.status === "pending" || a.status === "in_progress";
|
|
14314
14767
|
if (filter === "done") return a.status === "passed";
|
|
14315
14768
|
if (filter === "reopened") return a.status === "failed";
|
|
14316
14769
|
return true;
|
|
14317
|
-
};
|
|
14318
|
-
|
|
14770
|
+
}, [roleFilter, trackFilter, searchQuery, filter]);
|
|
14771
|
+
if (isLoading) return /* @__PURE__ */ import_react6.default.createElement(TestListScreenSkeleton, null);
|
|
14772
|
+
return /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, null, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.filterBar }, [
|
|
14319
14773
|
{ key: "all", label: "All", count: assignments.length },
|
|
14320
14774
|
{ key: "pending", label: "To Do", count: assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length },
|
|
14321
14775
|
{ key: "done", label: "Done", count: assignments.filter((a) => a.status === "passed").length },
|
|
14322
14776
|
{ key: "reopened", label: "Re Opened", count: assignments.filter((a) => a.status === "failed").length }
|
|
14323
|
-
].map((f) => /* @__PURE__ */
|
|
14324
|
-
|
|
14777
|
+
].map((f) => /* @__PURE__ */ import_react6.default.createElement(import_react_native6.TouchableOpacity, { key: f.key, style: [styles3.filterBtn, filter === f.key && styles3.filterBtnActive], onPress: () => setFilter(f.key) }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.filterBtnText, filter === f.key && styles3.filterBtnTextActive] }, f.label, " (", f.count, ")")))), availableRoles.length >= 2 && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.roleSection }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.roleBar }, /* @__PURE__ */ import_react6.default.createElement(
|
|
14778
|
+
import_react_native6.TouchableOpacity,
|
|
14325
14779
|
{
|
|
14326
14780
|
style: [styles3.roleBtn, !roleFilter && styles3.roleBtnActive],
|
|
14327
14781
|
onPress: () => setRoleFilter(null)
|
|
14328
14782
|
},
|
|
14329
|
-
/* @__PURE__ */
|
|
14783
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.roleBtnText, !roleFilter && styles3.roleBtnTextActive] }, "All Roles")
|
|
14330
14784
|
), availableRoles.map((role) => {
|
|
14331
14785
|
const isActive = roleFilter === role.id;
|
|
14332
|
-
return /* @__PURE__ */
|
|
14333
|
-
|
|
14786
|
+
return /* @__PURE__ */ import_react6.default.createElement(
|
|
14787
|
+
import_react_native6.TouchableOpacity,
|
|
14334
14788
|
{
|
|
14335
14789
|
key: role.id,
|
|
14336
14790
|
style: [
|
|
@@ -14339,33 +14793,72 @@ function TestListScreen({ nav }) {
|
|
|
14339
14793
|
],
|
|
14340
14794
|
onPress: () => setRoleFilter(isActive ? null : role.id)
|
|
14341
14795
|
},
|
|
14342
|
-
/* @__PURE__ */
|
|
14343
|
-
/* @__PURE__ */
|
|
14796
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: [styles3.roleDot, { backgroundColor: role.color }] }),
|
|
14797
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.roleBtnText, isActive && { color: role.color, fontWeight: "600" }] }, role.name)
|
|
14344
14798
|
);
|
|
14345
|
-
})), selectedRole?.loginHint && /* @__PURE__ */
|
|
14799
|
+
})), selectedRole?.loginHint && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: [styles3.loginHint, { backgroundColor: selectedRole.color + "10", borderColor: selectedRole.color + "30" }] }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.loginHintText }, "Log in as: ", selectedRole.loginHint))), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.searchContainer }, /* @__PURE__ */ import_react6.default.createElement(
|
|
14800
|
+
import_react_native6.TextInput,
|
|
14801
|
+
{
|
|
14802
|
+
value: searchQuery,
|
|
14803
|
+
onChangeText: setSearchQuery,
|
|
14804
|
+
placeholder: "Search tests...",
|
|
14805
|
+
placeholderTextColor: colors.textMuted,
|
|
14806
|
+
style: styles3.searchInput
|
|
14807
|
+
}
|
|
14808
|
+
)), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.trackSortRow }, availableTracks.length >= 2 && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: { flex: 1 } }, /* @__PURE__ */ import_react6.default.createElement(
|
|
14809
|
+
import_react_native6.TouchableOpacity,
|
|
14810
|
+
{
|
|
14811
|
+
style: [styles3.trackBtn, !trackFilter && styles3.trackBtnActive],
|
|
14812
|
+
onPress: () => setTrackFilter(null)
|
|
14813
|
+
},
|
|
14814
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.trackBtnText, !trackFilter && styles3.trackBtnTextActive] }, "All Tracks")
|
|
14815
|
+
), availableTracks.map((track) => {
|
|
14816
|
+
const isActive = trackFilter === track.id;
|
|
14817
|
+
return /* @__PURE__ */ import_react6.default.createElement(
|
|
14818
|
+
import_react_native6.TouchableOpacity,
|
|
14819
|
+
{
|
|
14820
|
+
key: track.id,
|
|
14821
|
+
style: [styles3.trackBtn, isActive && { backgroundColor: track.color + "20", borderColor: track.color + "60", borderWidth: 1 }],
|
|
14822
|
+
onPress: () => setTrackFilter(isActive ? null : track.id)
|
|
14823
|
+
},
|
|
14824
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.trackBtnText, isActive && { color: track.color, fontWeight: "600" }] }, track.icon, " ", track.name)
|
|
14825
|
+
);
|
|
14826
|
+
})), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.sortGroup }, [
|
|
14827
|
+
{ key: "priority", label: "\u2195" },
|
|
14828
|
+
{ key: "recent", label: "\u{1F550}" },
|
|
14829
|
+
{ key: "alpha", label: "AZ" }
|
|
14830
|
+
].map((s2) => /* @__PURE__ */ import_react6.default.createElement(
|
|
14831
|
+
import_react_native6.TouchableOpacity,
|
|
14832
|
+
{
|
|
14833
|
+
key: s2.key,
|
|
14834
|
+
style: [styles3.sortBtn, sortMode === s2.key && styles3.sortBtnActive],
|
|
14835
|
+
onPress: () => setSortMode(s2.key)
|
|
14836
|
+
},
|
|
14837
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.sortBtnText, sortMode === s2.key && styles3.sortBtnTextActive] }, s2.label)
|
|
14838
|
+
)))), groupedAssignments.map((folder) => {
|
|
14346
14839
|
const folderId = folder.group?.id || "ungrouped";
|
|
14347
14840
|
const isCollapsed = collapsedFolders.has(folderId);
|
|
14348
14841
|
const filtered = folder.assignments.filter(filterAssignment);
|
|
14349
14842
|
if (filtered.length === 0 && filter !== "all") return null;
|
|
14350
|
-
return /* @__PURE__ */
|
|
14843
|
+
return /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { key: folderId, style: styles3.folder }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.TouchableOpacity, { style: styles3.folderHeader, onPress: () => toggleFolder(folderId) }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.folderToggle }, isCollapsed ? "\u25B6" : "\u25BC"), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.folderName, numberOfLines: 1 }, folder.group?.name || "Ungrouped"), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.folderProgress }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: [styles3.folderProgressFill, { width: `${folder.stats.total > 0 ? Math.round((folder.stats.passed + folder.stats.failed) / folder.stats.total * 100) : 0}%` }] })), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.folderCount }, folder.stats.passed + folder.stats.failed, "/", folder.stats.total)), !isCollapsed && filtered.map((assignment) => {
|
|
14351
14844
|
const badge = getStatusBadge(assignment.status);
|
|
14352
14845
|
const isCurrent = currentAssignment?.id === assignment.id;
|
|
14353
|
-
return /* @__PURE__ */
|
|
14354
|
-
|
|
14846
|
+
return /* @__PURE__ */ import_react6.default.createElement(
|
|
14847
|
+
import_react_native6.TouchableOpacity,
|
|
14355
14848
|
{
|
|
14356
14849
|
key: assignment.id,
|
|
14357
14850
|
style: [styles3.testItem, isCurrent && styles3.testItemCurrent],
|
|
14358
14851
|
onPress: () => nav.push({ name: "TEST_DETAIL", testId: assignment.id })
|
|
14359
14852
|
},
|
|
14360
|
-
/* @__PURE__ */
|
|
14361
|
-
/* @__PURE__ */
|
|
14362
|
-
/* @__PURE__ */
|
|
14853
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.testBadge }, badge.icon),
|
|
14854
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.testInfo }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.testTitle, numberOfLines: 1 }, assignment.testCase.title), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.testMetaRow }, assignment.isVerification && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.retestTag }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.retestTagText }, "Retest")), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.testMeta }, assignment.testCase.testKey, " \xB7 ", assignment.testCase.priority), assignment.testCase.role && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.roleBadgeRow }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.testMeta }, " \xB7 "), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: [styles3.roleBadgeDot, { backgroundColor: assignment.testCase.role.color }] }), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.testMeta, { color: assignment.testCase.role.color, fontWeight: "500" }] }, assignment.testCase.role.name)))),
|
|
14855
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: [
|
|
14363
14856
|
styles3.statusPill,
|
|
14364
14857
|
{
|
|
14365
14858
|
backgroundColor: assignment.status === "passed" ? "#14532d" : assignment.status === "failed" ? "#450a0a" : assignment.status === "in_progress" ? "#172554" : "#27272a",
|
|
14366
14859
|
borderColor: assignment.status === "passed" ? "#166534" : assignment.status === "failed" ? "#7f1d1d" : assignment.status === "in_progress" ? "#1e3a5f" : "#3f3f46"
|
|
14367
14860
|
}
|
|
14368
|
-
] }, /* @__PURE__ */
|
|
14861
|
+
] }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [
|
|
14369
14862
|
styles3.statusPillText,
|
|
14370
14863
|
{
|
|
14371
14864
|
color: assignment.status === "passed" ? "#4ade80" : assignment.status === "failed" ? "#f87171" : assignment.status === "in_progress" ? "#60a5fa" : "#d4d4d8"
|
|
@@ -14373,9 +14866,9 @@ function TestListScreen({ nav }) {
|
|
|
14373
14866
|
] }, badge.label))
|
|
14374
14867
|
);
|
|
14375
14868
|
}));
|
|
14376
|
-
}), /* @__PURE__ */
|
|
14869
|
+
}), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.TouchableOpacity, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.refreshText }, "\u21BB", " Refresh")));
|
|
14377
14870
|
}
|
|
14378
|
-
var styles3 =
|
|
14871
|
+
var styles3 = import_react_native6.StyleSheet.create({
|
|
14379
14872
|
filterBar: { flexDirection: "row", gap: 8, marginBottom: 8 },
|
|
14380
14873
|
filterBtn: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 8, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
|
|
14381
14874
|
filterBtnActive: { backgroundColor: colors.blue, borderColor: colors.blue },
|
|
@@ -14410,16 +14903,28 @@ var styles3 = import_react_native5.StyleSheet.create({
|
|
|
14410
14903
|
testMeta: { fontSize: 11, color: colors.textDim },
|
|
14411
14904
|
statusPill: { paddingHorizontal: 8, paddingVertical: 3, borderRadius: 6, borderWidth: 1, marginLeft: 8 },
|
|
14412
14905
|
statusPillText: { fontSize: 10, fontWeight: "600" },
|
|
14906
|
+
searchContainer: { marginBottom: 8 },
|
|
14907
|
+
searchInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 8, paddingHorizontal: 12, paddingVertical: 8, fontSize: 13, color: colors.textPrimary },
|
|
14908
|
+
trackSortRow: { flexDirection: "row", alignItems: "center", marginBottom: 10, gap: 8 },
|
|
14909
|
+
trackBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
|
|
14910
|
+
trackBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
14911
|
+
trackBtnText: { fontSize: 11, color: colors.textMuted },
|
|
14912
|
+
trackBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
|
|
14913
|
+
sortGroup: { flexDirection: "row", gap: 2 },
|
|
14914
|
+
sortBtn: { paddingHorizontal: 8, paddingVertical: 4, borderRadius: 6, borderWidth: 1, borderColor: "transparent" },
|
|
14915
|
+
sortBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
14916
|
+
sortBtnText: { fontSize: 11, color: colors.textMuted },
|
|
14917
|
+
sortBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
|
|
14413
14918
|
refreshBtn: { alignItems: "center", paddingVertical: 12 },
|
|
14414
14919
|
refreshText: { fontSize: 13, color: colors.blue }
|
|
14415
14920
|
});
|
|
14416
14921
|
|
|
14417
14922
|
// src/widget/screens/TestFeedbackScreen.tsx
|
|
14418
|
-
var
|
|
14419
|
-
var
|
|
14923
|
+
var import_react10 = __toESM(require("react"));
|
|
14924
|
+
var import_react_native9 = require("react-native");
|
|
14420
14925
|
|
|
14421
14926
|
// src/widget/useImageAttachments.ts
|
|
14422
|
-
var
|
|
14927
|
+
var import_react7 = require("react");
|
|
14423
14928
|
var launchImageLibrary = null;
|
|
14424
14929
|
var launchCamera = null;
|
|
14425
14930
|
try {
|
|
@@ -14430,8 +14935,8 @@ try {
|
|
|
14430
14935
|
}
|
|
14431
14936
|
var IMAGE_PICKER_AVAILABLE = launchImageLibrary !== null;
|
|
14432
14937
|
function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
14433
|
-
const [images, setImages] = (0,
|
|
14434
|
-
const pickFromGallery = (0,
|
|
14938
|
+
const [images, setImages] = (0, import_react7.useState)([]);
|
|
14939
|
+
const pickFromGallery = (0, import_react7.useCallback)(async () => {
|
|
14435
14940
|
if (!launchImageLibrary || images.length >= maxImages) return;
|
|
14436
14941
|
launchImageLibrary(
|
|
14437
14942
|
{ mediaType: "photo", quality: 0.7, maxWidth: 1920, maxHeight: 1920, selectionLimit: maxImages - images.length },
|
|
@@ -14465,7 +14970,7 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
|
14465
14970
|
}
|
|
14466
14971
|
);
|
|
14467
14972
|
}, [images.length, maxImages, uploadFn, bucket]);
|
|
14468
|
-
const pickFromCamera = (0,
|
|
14973
|
+
const pickFromCamera = (0, import_react7.useCallback)(async () => {
|
|
14469
14974
|
if (!launchCamera || images.length >= maxImages) return;
|
|
14470
14975
|
launchCamera(
|
|
14471
14976
|
{ mediaType: "photo", quality: 0.7, maxWidth: 1920, maxHeight: 1920 },
|
|
@@ -14498,35 +15003,35 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
|
14498
15003
|
}
|
|
14499
15004
|
);
|
|
14500
15005
|
}, [images.length, maxImages, uploadFn, bucket]);
|
|
14501
|
-
const removeImage = (0,
|
|
15006
|
+
const removeImage = (0, import_react7.useCallback)((id) => {
|
|
14502
15007
|
setImages((prev) => prev.filter((img) => img.id !== id));
|
|
14503
15008
|
}, []);
|
|
14504
|
-
const clear = (0,
|
|
15009
|
+
const clear = (0, import_react7.useCallback)(() => {
|
|
14505
15010
|
setImages([]);
|
|
14506
15011
|
}, []);
|
|
14507
15012
|
const isUploading = images.some((img) => img.status === "uploading");
|
|
14508
15013
|
const hasError = images.some((img) => img.status === "error");
|
|
14509
|
-
const getAttachments = (0,
|
|
15014
|
+
const getAttachments = (0, import_react7.useCallback)(() => {
|
|
14510
15015
|
return images.filter((img) => img.status === "done" && img.remoteUrl).map((img) => ({ type: "image", url: img.remoteUrl, name: img.name }));
|
|
14511
15016
|
}, [images]);
|
|
14512
|
-
const getScreenshotUrls = (0,
|
|
15017
|
+
const getScreenshotUrls = (0, import_react7.useCallback)(() => {
|
|
14513
15018
|
return images.filter((img) => img.status === "done" && img.remoteUrl).map((img) => img.remoteUrl);
|
|
14514
15019
|
}, [images]);
|
|
14515
15020
|
return { images, pickFromGallery, pickFromCamera, removeImage, clear, isUploading, hasError, getAttachments, getScreenshotUrls };
|
|
14516
15021
|
}
|
|
14517
15022
|
|
|
14518
15023
|
// src/widget/ImagePickerButtons.tsx
|
|
14519
|
-
var
|
|
14520
|
-
var
|
|
15024
|
+
var import_react9 = __toESM(require("react"));
|
|
15025
|
+
var import_react_native8 = require("react-native");
|
|
14521
15026
|
|
|
14522
15027
|
// src/widget/ImagePreviewStrip.tsx
|
|
14523
|
-
var
|
|
14524
|
-
var
|
|
15028
|
+
var import_react8 = __toESM(require("react"));
|
|
15029
|
+
var import_react_native7 = require("react-native");
|
|
14525
15030
|
function ImagePreviewStrip({ images, onRemove }) {
|
|
14526
15031
|
if (images.length === 0) return null;
|
|
14527
|
-
return /* @__PURE__ */
|
|
15032
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_react_native7.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles4.strip }, images.map((img) => /* @__PURE__ */ import_react8.default.createElement(import_react_native7.View, { key: img.id, style: styles4.thumbContainer }, /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Image, { source: { uri: img.localUri }, style: styles4.thumb }), img.status === "uploading" && /* @__PURE__ */ import_react8.default.createElement(import_react_native7.View, { style: styles4.thumbOverlay }, /* @__PURE__ */ import_react8.default.createElement(import_react_native7.ActivityIndicator, { size: "small", color: "#fff" })), img.status === "error" && /* @__PURE__ */ import_react8.default.createElement(import_react_native7.View, { style: [styles4.thumbOverlay, styles4.thumbOverlayError] }, /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Text, { style: styles4.thumbErrorText }, "!")), /* @__PURE__ */ import_react8.default.createElement(import_react_native7.TouchableOpacity, { style: styles4.thumbRemove, onPress: () => onRemove(img.id) }, /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Text, { style: styles4.thumbRemoveText }, "\u2715")))));
|
|
14528
15033
|
}
|
|
14529
|
-
var styles4 =
|
|
15034
|
+
var styles4 = import_react_native7.StyleSheet.create({
|
|
14530
15035
|
strip: {
|
|
14531
15036
|
flexDirection: "row",
|
|
14532
15037
|
marginTop: 4
|
|
@@ -14545,7 +15050,7 @@ var styles4 = import_react_native6.StyleSheet.create({
|
|
|
14545
15050
|
borderRadius: 8
|
|
14546
15051
|
},
|
|
14547
15052
|
thumbOverlay: {
|
|
14548
|
-
...
|
|
15053
|
+
...import_react_native7.StyleSheet.absoluteFillObject,
|
|
14549
15054
|
backgroundColor: "rgba(0,0,0,0.5)",
|
|
14550
15055
|
justifyContent: "center",
|
|
14551
15056
|
alignItems: "center",
|
|
@@ -14580,25 +15085,25 @@ var styles4 = import_react_native6.StyleSheet.create({
|
|
|
14580
15085
|
// src/widget/ImagePickerButtons.tsx
|
|
14581
15086
|
function ImagePickerButtons({ images, maxImages, onPickGallery, onPickCamera, onRemove, label }) {
|
|
14582
15087
|
if (!IMAGE_PICKER_AVAILABLE) return null;
|
|
14583
|
-
return /* @__PURE__ */
|
|
14584
|
-
|
|
15088
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: styles5.section }, label && /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles5.label }, label), /* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: styles5.buttonRow }, /* @__PURE__ */ import_react9.default.createElement(
|
|
15089
|
+
import_react_native8.TouchableOpacity,
|
|
14585
15090
|
{
|
|
14586
15091
|
style: styles5.pickButton,
|
|
14587
15092
|
onPress: onPickGallery,
|
|
14588
15093
|
disabled: images.length >= maxImages
|
|
14589
15094
|
},
|
|
14590
|
-
/* @__PURE__ */
|
|
14591
|
-
), /* @__PURE__ */
|
|
14592
|
-
|
|
15095
|
+
/* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Gallery")
|
|
15096
|
+
), /* @__PURE__ */ import_react9.default.createElement(
|
|
15097
|
+
import_react_native8.TouchableOpacity,
|
|
14593
15098
|
{
|
|
14594
15099
|
style: styles5.pickButton,
|
|
14595
15100
|
onPress: onPickCamera,
|
|
14596
15101
|
disabled: images.length >= maxImages
|
|
14597
15102
|
},
|
|
14598
|
-
/* @__PURE__ */
|
|
14599
|
-
), /* @__PURE__ */
|
|
15103
|
+
/* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Camera")
|
|
15104
|
+
), /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles5.countText }, images.length, "/", maxImages)), /* @__PURE__ */ import_react9.default.createElement(ImagePreviewStrip, { images, onRemove }));
|
|
14600
15105
|
}
|
|
14601
|
-
var styles5 =
|
|
15106
|
+
var styles5 = import_react_native8.StyleSheet.create({
|
|
14602
15107
|
section: {
|
|
14603
15108
|
marginTop: 12,
|
|
14604
15109
|
marginBottom: 4
|
|
@@ -14641,13 +15146,14 @@ var styles5 = import_react_native7.StyleSheet.create({
|
|
|
14641
15146
|
function TestFeedbackScreen({ status, assignmentId, nav }) {
|
|
14642
15147
|
const { client, assignments, refreshAssignments, uploadImage } = useBugBear();
|
|
14643
15148
|
const images = useImageAttachments(uploadImage, 3, "screenshots");
|
|
14644
|
-
const [rating, setRating] = (0,
|
|
14645
|
-
const [note, setNote] = (0,
|
|
14646
|
-
const [flags, setFlags] = (0,
|
|
14647
|
-
const [submitting, setSubmitting] = (0,
|
|
15149
|
+
const [rating, setRating] = (0, import_react10.useState)(5);
|
|
15150
|
+
const [note, setNote] = (0, import_react10.useState)("");
|
|
15151
|
+
const [flags, setFlags] = (0, import_react10.useState)({ isOutdated: false, needsMoreDetail: false, stepsUnclear: false, expectedResultUnclear: false });
|
|
15152
|
+
const [submitting, setSubmitting] = (0, import_react10.useState)(false);
|
|
14648
15153
|
const assignment = assignments.find((a) => a.id === assignmentId);
|
|
14649
15154
|
const showFlags = rating < 4;
|
|
14650
15155
|
const handleSubmit = async () => {
|
|
15156
|
+
if (submitting || images.isUploading) return;
|
|
14651
15157
|
setSubmitting(true);
|
|
14652
15158
|
if (client && assignment) {
|
|
14653
15159
|
const screenshotUrls = images.getScreenshotUrls();
|
|
@@ -14698,22 +15204,22 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
|
|
|
14698
15204
|
}
|
|
14699
15205
|
}
|
|
14700
15206
|
};
|
|
14701
|
-
return /* @__PURE__ */
|
|
15207
|
+
return /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: styles6.container }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles6.header }, status === "passed" ? "\u2705 Test Passed!" : "\u274C Test Failed"), /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles6.subheader }, "Rate this test case"), /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: styles6.starRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ import_react10.default.createElement(import_react_native9.TouchableOpacity, { key: n, onPress: () => setRating(n), style: styles6.starButton }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: [styles6.star, n <= rating && styles6.starActive] }, n <= rating ? "\u2605" : "\u2606")))), showFlags && /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: styles6.flagsSection }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles6.flagsLabel }, "What could be improved?"), [
|
|
14702
15208
|
{ key: "isOutdated", label: "Test is outdated" },
|
|
14703
15209
|
{ key: "needsMoreDetail", label: "Needs more detail" },
|
|
14704
15210
|
{ key: "stepsUnclear", label: "Steps are unclear" },
|
|
14705
15211
|
{ key: "expectedResultUnclear", label: "Expected result unclear" }
|
|
14706
|
-
].map(({ key, label }) => /* @__PURE__ */
|
|
14707
|
-
|
|
15212
|
+
].map(({ key, label }) => /* @__PURE__ */ import_react10.default.createElement(
|
|
15213
|
+
import_react_native9.TouchableOpacity,
|
|
14708
15214
|
{
|
|
14709
15215
|
key,
|
|
14710
15216
|
style: [styles6.flagItem, flags[key] && styles6.flagItemActive],
|
|
14711
15217
|
onPress: () => setFlags((prev) => ({ ...prev, [key]: !prev[key] }))
|
|
14712
15218
|
},
|
|
14713
|
-
/* @__PURE__ */
|
|
14714
|
-
/* @__PURE__ */
|
|
14715
|
-
))), /* @__PURE__ */
|
|
14716
|
-
|
|
15219
|
+
/* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: [styles6.flagCheck, flags[key] && styles6.flagCheckActive] }, flags[key] && /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles6.flagCheckmark }, "\u2713")),
|
|
15220
|
+
/* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: [styles6.flagText, flags[key] && styles6.flagTextActive] }, label)
|
|
15221
|
+
))), /* @__PURE__ */ import_react10.default.createElement(
|
|
15222
|
+
import_react_native9.TextInput,
|
|
14717
15223
|
{
|
|
14718
15224
|
style: styles6.noteInput,
|
|
14719
15225
|
value: note,
|
|
@@ -14722,7 +15228,7 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
|
|
|
14722
15228
|
placeholderTextColor: colors.textMuted,
|
|
14723
15229
|
multiline: true
|
|
14724
15230
|
}
|
|
14725
|
-
), /* @__PURE__ */
|
|
15231
|
+
), /* @__PURE__ */ import_react10.default.createElement(
|
|
14726
15232
|
ImagePickerButtons,
|
|
14727
15233
|
{
|
|
14728
15234
|
images: images.images,
|
|
@@ -14732,9 +15238,9 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
|
|
|
14732
15238
|
onRemove: images.removeImage,
|
|
14733
15239
|
label: "Screenshots (optional)"
|
|
14734
15240
|
}
|
|
14735
|
-
), /* @__PURE__ */
|
|
15241
|
+
), /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: styles6.actions }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.TouchableOpacity, { style: styles6.skipButton, onPress: handleSkip }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles6.skipText }, "Skip")), /* @__PURE__ */ import_react10.default.createElement(import_react_native9.TouchableOpacity, { style: [shared.primaryButton, { flex: 2, opacity: submitting || images.isUploading ? 0.5 : 1 }], onPress: handleSubmit, disabled: submitting || images.isUploading }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : submitting ? "Submitting..." : "Submit"))));
|
|
14736
15242
|
}
|
|
14737
|
-
var styles6 =
|
|
15243
|
+
var styles6 = import_react_native9.StyleSheet.create({
|
|
14738
15244
|
container: { paddingTop: 8 },
|
|
14739
15245
|
header: { fontSize: 22, fontWeight: "700", color: colors.textPrimary, textAlign: "center", marginBottom: 4 },
|
|
14740
15246
|
subheader: { fontSize: 14, color: colors.textMuted, textAlign: "center", marginBottom: 20 },
|
|
@@ -14758,12 +15264,12 @@ var styles6 = import_react_native8.StyleSheet.create({
|
|
|
14758
15264
|
});
|
|
14759
15265
|
|
|
14760
15266
|
// src/widget/screens/ReportScreen.tsx
|
|
14761
|
-
var
|
|
14762
|
-
var
|
|
15267
|
+
var import_react12 = __toESM(require("react"));
|
|
15268
|
+
var import_react_native11 = require("react-native");
|
|
14763
15269
|
|
|
14764
15270
|
// src/widget/CategoryPicker.tsx
|
|
14765
|
-
var
|
|
14766
|
-
var
|
|
15271
|
+
var import_react11 = __toESM(require("react"));
|
|
15272
|
+
var import_react_native10 = require("react-native");
|
|
14767
15273
|
var categoryOptions = [
|
|
14768
15274
|
{ value: "ui_ux", label: "UI/UX", icon: "\u{1F3A8}" },
|
|
14769
15275
|
{ value: "functional", label: "Functional", icon: "\u2699\uFE0F" },
|
|
@@ -14772,64 +15278,64 @@ var categoryOptions = [
|
|
|
14772
15278
|
{ value: "other", label: "Other", icon: "\u{1F4DD}" }
|
|
14773
15279
|
];
|
|
14774
15280
|
function CategoryPicker({ value, onChange, optional = true }) {
|
|
14775
|
-
const [modalVisible, setModalVisible] = (0,
|
|
15281
|
+
const [modalVisible, setModalVisible] = (0, import_react11.useState)(false);
|
|
14776
15282
|
const selectedOption = value ? categoryOptions.find((o) => o.value === value) : null;
|
|
14777
15283
|
const handleSelect = (category) => {
|
|
14778
15284
|
onChange(category);
|
|
14779
15285
|
setModalVisible(false);
|
|
14780
15286
|
};
|
|
14781
|
-
return /* @__PURE__ */
|
|
14782
|
-
|
|
15287
|
+
return /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, /* @__PURE__ */ import_react11.default.createElement(
|
|
15288
|
+
import_react_native10.TouchableOpacity,
|
|
14783
15289
|
{
|
|
14784
15290
|
style: styles7.trigger,
|
|
14785
15291
|
onPress: () => setModalVisible(true)
|
|
14786
15292
|
},
|
|
14787
|
-
/* @__PURE__ */
|
|
14788
|
-
/* @__PURE__ */
|
|
14789
|
-
), /* @__PURE__ */
|
|
14790
|
-
|
|
15293
|
+
/* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: selectedOption ? styles7.triggerTextSelected : styles7.triggerTextPlaceholder }, selectedOption ? `${selectedOption.icon} ${selectedOption.label}` : optional ? "Select category (optional)" : "Select category"),
|
|
15294
|
+
/* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.chevron }, "\u25BC")
|
|
15295
|
+
), /* @__PURE__ */ import_react11.default.createElement(
|
|
15296
|
+
import_react_native10.Modal,
|
|
14791
15297
|
{
|
|
14792
15298
|
visible: modalVisible,
|
|
14793
15299
|
transparent: true,
|
|
14794
15300
|
animationType: "fade",
|
|
14795
15301
|
onRequestClose: () => setModalVisible(false)
|
|
14796
15302
|
},
|
|
14797
|
-
/* @__PURE__ */
|
|
14798
|
-
|
|
15303
|
+
/* @__PURE__ */ import_react11.default.createElement(
|
|
15304
|
+
import_react_native10.TouchableOpacity,
|
|
14799
15305
|
{
|
|
14800
15306
|
style: styles7.overlay,
|
|
14801
15307
|
activeOpacity: 1,
|
|
14802
15308
|
onPress: () => setModalVisible(false)
|
|
14803
15309
|
},
|
|
14804
|
-
/* @__PURE__ */
|
|
14805
|
-
|
|
15310
|
+
/* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles7.modal, onStartShouldSetResponder: () => true }, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.modalTitle }, "Select Category"), optional && /* @__PURE__ */ import_react11.default.createElement(
|
|
15311
|
+
import_react_native10.TouchableOpacity,
|
|
14806
15312
|
{
|
|
14807
15313
|
style: [styles7.option, !value && styles7.optionSelected],
|
|
14808
15314
|
onPress: () => handleSelect(null)
|
|
14809
15315
|
},
|
|
14810
|
-
/* @__PURE__ */
|
|
14811
|
-
), categoryOptions.map(({ value: optValue, label, icon }) => /* @__PURE__ */
|
|
14812
|
-
|
|
15316
|
+
/* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.optionText }, "\u2014 None \u2014")
|
|
15317
|
+
), categoryOptions.map(({ value: optValue, label, icon }) => /* @__PURE__ */ import_react11.default.createElement(
|
|
15318
|
+
import_react_native10.TouchableOpacity,
|
|
14813
15319
|
{
|
|
14814
15320
|
key: optValue,
|
|
14815
15321
|
style: [styles7.option, value === optValue && styles7.optionSelected],
|
|
14816
15322
|
onPress: () => handleSelect(optValue)
|
|
14817
15323
|
},
|
|
14818
|
-
/* @__PURE__ */
|
|
14819
|
-
/* @__PURE__ */
|
|
14820
|
-
value === optValue && /* @__PURE__ */
|
|
14821
|
-
)), /* @__PURE__ */
|
|
14822
|
-
|
|
15324
|
+
/* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.optionIcon }, icon),
|
|
15325
|
+
/* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.optionText }, label),
|
|
15326
|
+
value === optValue && /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.checkmark }, "\u2713")
|
|
15327
|
+
)), /* @__PURE__ */ import_react11.default.createElement(
|
|
15328
|
+
import_react_native10.TouchableOpacity,
|
|
14823
15329
|
{
|
|
14824
15330
|
style: styles7.cancelButton,
|
|
14825
15331
|
onPress: () => setModalVisible(false)
|
|
14826
15332
|
},
|
|
14827
|
-
/* @__PURE__ */
|
|
15333
|
+
/* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.cancelText }, "Cancel")
|
|
14828
15334
|
))
|
|
14829
15335
|
)
|
|
14830
15336
|
));
|
|
14831
15337
|
}
|
|
14832
|
-
var styles7 =
|
|
15338
|
+
var styles7 = import_react_native10.StyleSheet.create({
|
|
14833
15339
|
trigger: {
|
|
14834
15340
|
flexDirection: "row",
|
|
14835
15341
|
alignItems: "center",
|
|
@@ -14919,18 +15425,18 @@ var styles7 = import_react_native9.StyleSheet.create({
|
|
|
14919
15425
|
// src/widget/screens/ReportScreen.tsx
|
|
14920
15426
|
function ReportScreen({ nav, prefill }) {
|
|
14921
15427
|
const { client, getDeviceInfo, uploadImage, refreshAssignments } = useBugBear();
|
|
14922
|
-
const [reportType, setReportType] = (0,
|
|
14923
|
-
const [severity, setSeverity] = (0,
|
|
14924
|
-
const [category, setCategory] = (0,
|
|
14925
|
-
const [description, setDescription] = (0,
|
|
14926
|
-
const [affectedScreen, setAffectedScreen] = (0,
|
|
14927
|
-
const [submitting, setSubmitting] = (0,
|
|
14928
|
-
const [error, setError] = (0,
|
|
14929
|
-
const submittingRef = (0,
|
|
15428
|
+
const [reportType, setReportType] = (0, import_react12.useState)(prefill?.type || "bug");
|
|
15429
|
+
const [severity, setSeverity] = (0, import_react12.useState)("medium");
|
|
15430
|
+
const [category, setCategory] = (0, import_react12.useState)(null);
|
|
15431
|
+
const [description, setDescription] = (0, import_react12.useState)("");
|
|
15432
|
+
const [affectedScreen, setAffectedScreen] = (0, import_react12.useState)("");
|
|
15433
|
+
const [submitting, setSubmitting] = (0, import_react12.useState)(false);
|
|
15434
|
+
const [error, setError] = (0, import_react12.useState)(null);
|
|
15435
|
+
const submittingRef = (0, import_react12.useRef)(false);
|
|
14930
15436
|
const images = useImageAttachments(uploadImage, 5, "screenshots");
|
|
14931
15437
|
const isRetestFailure = prefill?.type === "test_fail";
|
|
14932
15438
|
const isBugType = reportType === "bug" || reportType === "test_fail";
|
|
14933
|
-
(0,
|
|
15439
|
+
(0, import_react12.useEffect)(() => {
|
|
14934
15440
|
if (reportType === "feedback" || reportType === "suggestion") {
|
|
14935
15441
|
setCategory("other");
|
|
14936
15442
|
} else {
|
|
@@ -14938,7 +15444,7 @@ function ReportScreen({ nav, prefill }) {
|
|
|
14938
15444
|
}
|
|
14939
15445
|
}, [reportType]);
|
|
14940
15446
|
const handleSubmit = async () => {
|
|
14941
|
-
if (!client || !description.trim()) return;
|
|
15447
|
+
if (!client || !description.trim() || images.isUploading) return;
|
|
14942
15448
|
if (submittingRef.current) return;
|
|
14943
15449
|
submittingRef.current = true;
|
|
14944
15450
|
setSubmitting(true);
|
|
@@ -14979,21 +15485,21 @@ function ReportScreen({ nav, prefill }) {
|
|
|
14979
15485
|
submittingRef.current = false;
|
|
14980
15486
|
}
|
|
14981
15487
|
};
|
|
14982
|
-
return /* @__PURE__ */
|
|
15488
|
+
return /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, null, isRetestFailure ? /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, null, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.retestBanner }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.retestIcon }, "\u{1F504}"), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, null, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.retestTitle }, "Bug Still Present"), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.retestSubtitle }, "The fix did not resolve this issue"))), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "Severity"), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.severityRow }, [
|
|
14983
15489
|
{ sev: "critical", color: "#ef4444" },
|
|
14984
15490
|
{ sev: "high", color: "#f97316" },
|
|
14985
15491
|
{ sev: "medium", color: "#eab308" },
|
|
14986
15492
|
{ sev: "low", color: "#6b7280" }
|
|
14987
|
-
].map(({ sev, color }) => /* @__PURE__ */
|
|
14988
|
-
|
|
15493
|
+
].map(({ sev, color }) => /* @__PURE__ */ import_react12.default.createElement(
|
|
15494
|
+
import_react_native11.TouchableOpacity,
|
|
14989
15495
|
{
|
|
14990
15496
|
key: sev,
|
|
14991
15497
|
style: [styles8.sevButton, severity === sev && { backgroundColor: `${color}30`, borderColor: color }],
|
|
14992
15498
|
onPress: () => setSeverity(sev)
|
|
14993
15499
|
},
|
|
14994
|
-
/* @__PURE__ */
|
|
14995
|
-
)))), /* @__PURE__ */
|
|
14996
|
-
|
|
15500
|
+
/* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: [styles8.sevText, severity === sev && { color }] }, sev)
|
|
15501
|
+
)))), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ import_react12.default.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "What went wrong?"), /* @__PURE__ */ import_react12.default.createElement(
|
|
15502
|
+
import_react_native11.TextInput,
|
|
14997
15503
|
{
|
|
14998
15504
|
style: styles8.descInput,
|
|
14999
15505
|
value: description,
|
|
@@ -15004,7 +15510,7 @@ function ReportScreen({ nav, prefill }) {
|
|
|
15004
15510
|
numberOfLines: 4,
|
|
15005
15511
|
textAlignVertical: "top"
|
|
15006
15512
|
}
|
|
15007
|
-
)), /* @__PURE__ */
|
|
15513
|
+
)), /* @__PURE__ */ import_react12.default.createElement(
|
|
15008
15514
|
ImagePickerButtons,
|
|
15009
15515
|
{
|
|
15010
15516
|
images: images.images,
|
|
@@ -15014,42 +15520,42 @@ function ReportScreen({ nav, prefill }) {
|
|
|
15014
15520
|
onRemove: images.removeImage,
|
|
15015
15521
|
label: "Attachments (optional)"
|
|
15016
15522
|
}
|
|
15017
|
-
), error && /* @__PURE__ */
|
|
15018
|
-
|
|
15523
|
+
), error && /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.errorBanner }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.errorText }, error)), /* @__PURE__ */ import_react12.default.createElement(
|
|
15524
|
+
import_react_native11.TouchableOpacity,
|
|
15019
15525
|
{
|
|
15020
15526
|
style: [shared.primaryButton, styles8.retestSubmitButton, (!description.trim() || submitting || images.isUploading) && shared.primaryButtonDisabled, { marginTop: 20 }],
|
|
15021
15527
|
onPress: handleSubmit,
|
|
15022
15528
|
disabled: !description.trim() || submitting || images.isUploading
|
|
15023
15529
|
},
|
|
15024
|
-
/* @__PURE__ */
|
|
15025
|
-
)) : /* @__PURE__ */
|
|
15530
|
+
/* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Failed Retest")
|
|
15531
|
+
)) : /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, null, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "What are you reporting?"), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.typeRow }, [
|
|
15026
15532
|
{ type: "bug", label: "Bug", icon: "\u{1F41B}" },
|
|
15027
15533
|
{ type: "feedback", label: "Feedback", icon: "\u{1F4A1}" },
|
|
15028
15534
|
{ type: "suggestion", label: "Idea", icon: "\u2728" }
|
|
15029
|
-
].map(({ type, label, icon }) => /* @__PURE__ */
|
|
15030
|
-
|
|
15535
|
+
].map(({ type, label, icon }) => /* @__PURE__ */ import_react12.default.createElement(
|
|
15536
|
+
import_react_native11.TouchableOpacity,
|
|
15031
15537
|
{
|
|
15032
15538
|
key: type,
|
|
15033
15539
|
style: [styles8.typeCard, reportType === type && styles8.typeCardActive],
|
|
15034
15540
|
onPress: () => setReportType(type)
|
|
15035
15541
|
},
|
|
15036
|
-
/* @__PURE__ */
|
|
15037
|
-
/* @__PURE__ */
|
|
15038
|
-
))), isBugType && /* @__PURE__ */
|
|
15542
|
+
/* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.typeIcon }, icon),
|
|
15543
|
+
/* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: [styles8.typeLabel, reportType === type && styles8.typeLabelActive] }, label)
|
|
15544
|
+
))), isBugType && /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "Severity"), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.severityRow }, [
|
|
15039
15545
|
{ sev: "critical", color: "#ef4444" },
|
|
15040
15546
|
{ sev: "high", color: "#f97316" },
|
|
15041
15547
|
{ sev: "medium", color: "#eab308" },
|
|
15042
15548
|
{ sev: "low", color: "#6b7280" }
|
|
15043
|
-
].map(({ sev, color }) => /* @__PURE__ */
|
|
15044
|
-
|
|
15549
|
+
].map(({ sev, color }) => /* @__PURE__ */ import_react12.default.createElement(
|
|
15550
|
+
import_react_native11.TouchableOpacity,
|
|
15045
15551
|
{
|
|
15046
15552
|
key: sev,
|
|
15047
15553
|
style: [styles8.sevButton, severity === sev && { backgroundColor: `${color}30`, borderColor: color }],
|
|
15048
15554
|
onPress: () => setSeverity(sev)
|
|
15049
15555
|
},
|
|
15050
|
-
/* @__PURE__ */
|
|
15051
|
-
)))), isBugType && /* @__PURE__ */
|
|
15052
|
-
|
|
15556
|
+
/* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: [styles8.sevText, severity === sev && { color }] }, sev)
|
|
15557
|
+
)))), isBugType && /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ import_react12.default.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "What happened?"), /* @__PURE__ */ import_react12.default.createElement(
|
|
15558
|
+
import_react_native11.TextInput,
|
|
15053
15559
|
{
|
|
15054
15560
|
style: styles8.descInput,
|
|
15055
15561
|
value: description,
|
|
@@ -15060,8 +15566,8 @@ function ReportScreen({ nav, prefill }) {
|
|
|
15060
15566
|
numberOfLines: 4,
|
|
15061
15567
|
textAlignVertical: "top"
|
|
15062
15568
|
}
|
|
15063
|
-
)), isBugType && /* @__PURE__ */
|
|
15064
|
-
|
|
15569
|
+
)), isBugType && /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "Which screen?"), /* @__PURE__ */ import_react12.default.createElement(
|
|
15570
|
+
import_react_native11.TextInput,
|
|
15065
15571
|
{
|
|
15066
15572
|
style: styles8.screenInput,
|
|
15067
15573
|
value: affectedScreen,
|
|
@@ -15069,7 +15575,7 @@ function ReportScreen({ nav, prefill }) {
|
|
|
15069
15575
|
placeholder: "e.g. Reservations, Settings...",
|
|
15070
15576
|
placeholderTextColor: colors.textMuted
|
|
15071
15577
|
}
|
|
15072
|
-
), /* @__PURE__ */
|
|
15578
|
+
), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.screenHint }, "Which screen or area was the bug on? (optional)")), /* @__PURE__ */ import_react12.default.createElement(
|
|
15073
15579
|
ImagePickerButtons,
|
|
15074
15580
|
{
|
|
15075
15581
|
images: images.images,
|
|
@@ -15079,17 +15585,17 @@ function ReportScreen({ nav, prefill }) {
|
|
|
15079
15585
|
onRemove: images.removeImage,
|
|
15080
15586
|
label: "Screenshots (optional)"
|
|
15081
15587
|
}
|
|
15082
|
-
), error && /* @__PURE__ */
|
|
15083
|
-
|
|
15588
|
+
), error && /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.errorBanner }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.errorText }, error)), /* @__PURE__ */ import_react12.default.createElement(
|
|
15589
|
+
import_react_native11.TouchableOpacity,
|
|
15084
15590
|
{
|
|
15085
15591
|
style: [shared.primaryButton, (!description.trim() || submitting || images.isUploading) && shared.primaryButtonDisabled, { marginTop: 20 }],
|
|
15086
15592
|
onPress: handleSubmit,
|
|
15087
15593
|
disabled: !description.trim() || submitting || images.isUploading
|
|
15088
15594
|
},
|
|
15089
|
-
/* @__PURE__ */
|
|
15595
|
+
/* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Report")
|
|
15090
15596
|
)));
|
|
15091
15597
|
}
|
|
15092
|
-
var styles8 =
|
|
15598
|
+
var styles8 = import_react_native11.StyleSheet.create({
|
|
15093
15599
|
typeRow: { flexDirection: "row", gap: 10, marginBottom: 20 },
|
|
15094
15600
|
typeCard: { flex: 1, alignItems: "center", paddingVertical: 16, borderRadius: 12, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
|
|
15095
15601
|
typeCardActive: { borderColor: colors.blue, backgroundColor: "#172554" },
|
|
@@ -15113,16 +15619,16 @@ var styles8 = import_react_native10.StyleSheet.create({
|
|
|
15113
15619
|
});
|
|
15114
15620
|
|
|
15115
15621
|
// src/widget/screens/ReportSuccessScreen.tsx
|
|
15116
|
-
var
|
|
15117
|
-
var
|
|
15622
|
+
var import_react13 = __toESM(require("react"));
|
|
15623
|
+
var import_react_native12 = require("react-native");
|
|
15118
15624
|
function ReportSuccessScreen({ nav }) {
|
|
15119
|
-
(0,
|
|
15625
|
+
(0, import_react13.useEffect)(() => {
|
|
15120
15626
|
const timer = setTimeout(() => nav.reset(), 2e3);
|
|
15121
15627
|
return () => clearTimeout(timer);
|
|
15122
15628
|
}, [nav]);
|
|
15123
|
-
return /* @__PURE__ */
|
|
15629
|
+
return /* @__PURE__ */ import_react13.default.createElement(import_react_native12.View, { style: styles9.container }, /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles9.emoji }, "\u{1F389}"), /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles9.title }, "Report submitted!"), /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles9.subtitle }, "Thank you for your feedback"));
|
|
15124
15630
|
}
|
|
15125
|
-
var styles9 =
|
|
15631
|
+
var styles9 = import_react_native12.StyleSheet.create({
|
|
15126
15632
|
container: { alignItems: "center", paddingVertical: 60 },
|
|
15127
15633
|
emoji: { fontSize: 48, marginBottom: 16 },
|
|
15128
15634
|
title: { fontSize: 22, fontWeight: "700", color: colors.textPrimary, marginBottom: 6 },
|
|
@@ -15130,29 +15636,30 @@ var styles9 = import_react_native11.StyleSheet.create({
|
|
|
15130
15636
|
});
|
|
15131
15637
|
|
|
15132
15638
|
// src/widget/screens/MessageListScreen.tsx
|
|
15133
|
-
var
|
|
15134
|
-
var
|
|
15639
|
+
var import_react14 = __toESM(require("react"));
|
|
15640
|
+
var import_react_native13 = require("react-native");
|
|
15135
15641
|
function MessageListScreen({ nav }) {
|
|
15136
|
-
const { threads, unreadCount, refreshThreads } = useBugBear();
|
|
15137
|
-
return /* @__PURE__ */
|
|
15138
|
-
|
|
15642
|
+
const { threads, unreadCount, refreshThreads, isLoading } = useBugBear();
|
|
15643
|
+
if (isLoading) return /* @__PURE__ */ import_react14.default.createElement(MessageListScreenSkeleton, null);
|
|
15644
|
+
return /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, null, /* @__PURE__ */ import_react14.default.createElement(
|
|
15645
|
+
import_react_native13.TouchableOpacity,
|
|
15139
15646
|
{
|
|
15140
15647
|
style: styles10.newMsgButton,
|
|
15141
15648
|
onPress: () => nav.push({ name: "COMPOSE_MESSAGE" })
|
|
15142
15649
|
},
|
|
15143
|
-
/* @__PURE__ */
|
|
15144
|
-
), threads.length === 0 ? /* @__PURE__ */
|
|
15145
|
-
|
|
15650
|
+
/* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.newMsgText }, "\u2709\uFE0F New Message")
|
|
15651
|
+
), threads.length === 0 ? /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: shared.emptyState }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: shared.emptyEmoji }, "\u{1F4AC}"), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: shared.emptyTitle }, "No messages yet"), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: shared.emptySubtitle }, "Start a conversation or wait for messages from admins")) : /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, null, threads.map((thread) => /* @__PURE__ */ import_react14.default.createElement(
|
|
15652
|
+
import_react_native13.TouchableOpacity,
|
|
15146
15653
|
{
|
|
15147
15654
|
key: thread.id,
|
|
15148
15655
|
style: [styles10.threadItem, thread.unreadCount > 0 && styles10.threadItemUnread],
|
|
15149
15656
|
onPress: () => nav.push({ name: "THREAD_DETAIL", thread })
|
|
15150
15657
|
},
|
|
15151
|
-
/* @__PURE__ */
|
|
15152
|
-
/* @__PURE__ */
|
|
15153
|
-
))), /* @__PURE__ */
|
|
15658
|
+
/* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.threadLeft }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.threadIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.threadInfo }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.threadTitleRow }, thread.isPinned && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.pinIcon }, "\u{1F4CC}"), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.threadSubject, numberOfLines: 1 }, thread.subject || "No subject")), thread.lastMessage && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.threadPreview, numberOfLines: 1 }, thread.lastMessage.senderName, ": ", thread.lastMessage.content))),
|
|
15659
|
+
/* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.threadRight }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.threadTime }, formatRelativeTime(thread.lastMessageAt)), thread.unreadCount > 0 && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.unreadBadge }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.unreadText }, thread.unreadCount)), thread.priority !== "normal" && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: [styles10.priorityDot, { backgroundColor: getPriorityColor(thread.priority) }] }))
|
|
15660
|
+
))), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.footer }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.TouchableOpacity, { onPress: refreshThreads }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.refreshText }, "\u21BB Refresh"))));
|
|
15154
15661
|
}
|
|
15155
|
-
var styles10 =
|
|
15662
|
+
var styles10 = import_react_native13.StyleSheet.create({
|
|
15156
15663
|
newMsgButton: { backgroundColor: colors.blue, paddingVertical: 12, borderRadius: 12, alignItems: "center", marginBottom: 16 },
|
|
15157
15664
|
newMsgText: { fontSize: 15, fontWeight: "600", color: "#fff" },
|
|
15158
15665
|
threadItem: { flexDirection: "row", justifyContent: "space-between", paddingVertical: 12, paddingHorizontal: 12, borderRadius: 10, marginBottom: 4, backgroundColor: colors.card },
|
|
@@ -15175,26 +15682,39 @@ var styles10 = import_react_native12.StyleSheet.create({
|
|
|
15175
15682
|
});
|
|
15176
15683
|
|
|
15177
15684
|
// src/widget/screens/ThreadDetailScreen.tsx
|
|
15178
|
-
var
|
|
15179
|
-
var
|
|
15685
|
+
var import_react15 = __toESM(require("react"));
|
|
15686
|
+
var import_react_native14 = require("react-native");
|
|
15180
15687
|
function ThreadDetailScreen({ thread, nav }) {
|
|
15181
15688
|
const { getThreadMessages, sendMessage, markAsRead, uploadImage } = useBugBear();
|
|
15182
|
-
const [messages, setMessages] = (0,
|
|
15183
|
-
const [loading, setLoading] = (0,
|
|
15184
|
-
const [replyText, setReplyText] = (0,
|
|
15185
|
-
const [sending, setSending] = (0,
|
|
15186
|
-
const [sendError, setSendError] = (0,
|
|
15689
|
+
const [messages, setMessages] = (0, import_react15.useState)([]);
|
|
15690
|
+
const [loading, setLoading] = (0, import_react15.useState)(true);
|
|
15691
|
+
const [replyText, setReplyText] = (0, import_react15.useState)("");
|
|
15692
|
+
const [sending, setSending] = (0, import_react15.useState)(false);
|
|
15693
|
+
const [sendError, setSendError] = (0, import_react15.useState)(false);
|
|
15187
15694
|
const replyImages = useImageAttachments(uploadImage, 3, "discussion-attachments");
|
|
15188
|
-
(0,
|
|
15695
|
+
(0, import_react15.useEffect)(() => {
|
|
15696
|
+
let cancelled = false;
|
|
15697
|
+
setLoading(true);
|
|
15189
15698
|
(async () => {
|
|
15190
|
-
|
|
15191
|
-
|
|
15192
|
-
|
|
15193
|
-
|
|
15194
|
-
|
|
15195
|
-
|
|
15699
|
+
try {
|
|
15700
|
+
const msgs = await getThreadMessages(thread.id);
|
|
15701
|
+
if (!cancelled) {
|
|
15702
|
+
setMessages(msgs);
|
|
15703
|
+
}
|
|
15704
|
+
if (thread.unreadCount > 0) {
|
|
15705
|
+
await markAsRead(thread.id);
|
|
15706
|
+
}
|
|
15707
|
+
} catch (err) {
|
|
15708
|
+
console.error("BugBear: Failed to load thread messages", err);
|
|
15709
|
+
} finally {
|
|
15710
|
+
if (!cancelled) {
|
|
15711
|
+
setLoading(false);
|
|
15712
|
+
}
|
|
15196
15713
|
}
|
|
15197
15714
|
})();
|
|
15715
|
+
return () => {
|
|
15716
|
+
cancelled = true;
|
|
15717
|
+
};
|
|
15198
15718
|
}, [thread.id]);
|
|
15199
15719
|
const handleSend = async () => {
|
|
15200
15720
|
if (!replyText.trim() && replyImages.images.length === 0 || sending || replyImages.isUploading) return;
|
|
@@ -15217,18 +15737,18 @@ function ThreadDetailScreen({ thread, nav }) {
|
|
|
15217
15737
|
}
|
|
15218
15738
|
setSending(false);
|
|
15219
15739
|
};
|
|
15220
|
-
return /* @__PURE__ */
|
|
15221
|
-
|
|
15740
|
+
return /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.container }, /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.header }, /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles11.headerIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles11.headerSubject, numberOfLines: 2 }, thread.subject || "No subject")), loading ? /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.loadingContainer }, /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles11.loadingText }, "Loading messages...")) : /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.messagesContainer }, messages.map((msg) => /* @__PURE__ */ import_react15.default.createElement(
|
|
15741
|
+
import_react_native14.View,
|
|
15222
15742
|
{
|
|
15223
15743
|
key: msg.id,
|
|
15224
15744
|
style: [styles11.bubble, msg.senderType === "tester" ? styles11.bubbleTester : styles11.bubbleAdmin]
|
|
15225
15745
|
},
|
|
15226
|
-
/* @__PURE__ */
|
|
15227
|
-
/* @__PURE__ */
|
|
15228
|
-
msg.attachments && msg.attachments.length > 0 && /* @__PURE__ */
|
|
15229
|
-
/* @__PURE__ */
|
|
15230
|
-
))), sendError && /* @__PURE__ */
|
|
15231
|
-
|
|
15746
|
+
/* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: [styles11.sender, msg.senderType === "tester" && styles11.senderTester] }, msg.senderType === "tester" ? "You" : msg.senderName),
|
|
15747
|
+
/* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: [styles11.content, msg.senderType === "tester" && styles11.contentTester] }, msg.content),
|
|
15748
|
+
msg.attachments && msg.attachments.length > 0 && /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.attachments }, msg.attachments.filter((a) => a.type === "image").map((att, idx) => /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Image, { key: idx, source: { uri: att.url }, style: styles11.attachmentImage, resizeMode: "cover" }))),
|
|
15749
|
+
/* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: [styles11.time, msg.senderType === "tester" && styles11.timeTester] }, formatMessageTime(msg.createdAt))
|
|
15750
|
+
))), sendError && /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.errorBar }, /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles11.errorText }, "Failed to send. Tap Send to retry.")), replyImages.images.length > 0 && /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.replyPreview }, /* @__PURE__ */ import_react15.default.createElement(ImagePreviewStrip, { images: replyImages.images, onRemove: replyImages.removeImage })), /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.composer }, IMAGE_PICKER_AVAILABLE && /* @__PURE__ */ import_react15.default.createElement(import_react_native14.TouchableOpacity, { style: styles11.attachBtn, onPress: replyImages.pickFromGallery, disabled: replyImages.images.length >= 3 }, /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles11.attachBtnText }, "\u{1F4CE}")), /* @__PURE__ */ import_react15.default.createElement(
|
|
15751
|
+
import_react_native14.TextInput,
|
|
15232
15752
|
{
|
|
15233
15753
|
style: styles11.replyInput,
|
|
15234
15754
|
value: replyText,
|
|
@@ -15238,17 +15758,17 @@ function ThreadDetailScreen({ thread, nav }) {
|
|
|
15238
15758
|
multiline: true,
|
|
15239
15759
|
maxLength: 1e3
|
|
15240
15760
|
}
|
|
15241
|
-
), /* @__PURE__ */
|
|
15242
|
-
|
|
15761
|
+
), /* @__PURE__ */ import_react15.default.createElement(
|
|
15762
|
+
import_react_native14.TouchableOpacity,
|
|
15243
15763
|
{
|
|
15244
|
-
style: [styles11.sendBtn, (!replyText.trim() || sending || replyImages.isUploading) && styles11.sendBtnDisabled],
|
|
15764
|
+
style: [styles11.sendBtn, (!replyText.trim() && replyImages.images.length === 0 || sending || replyImages.isUploading) && styles11.sendBtnDisabled],
|
|
15245
15765
|
onPress: handleSend,
|
|
15246
|
-
disabled: !replyText.trim() || sending || replyImages.isUploading
|
|
15766
|
+
disabled: !replyText.trim() && replyImages.images.length === 0 || sending || replyImages.isUploading
|
|
15247
15767
|
},
|
|
15248
|
-
/* @__PURE__ */
|
|
15768
|
+
/* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles11.sendBtnText }, sending ? "..." : "Send")
|
|
15249
15769
|
)));
|
|
15250
15770
|
}
|
|
15251
|
-
var styles11 =
|
|
15771
|
+
var styles11 = import_react_native14.StyleSheet.create({
|
|
15252
15772
|
container: { flex: 1 },
|
|
15253
15773
|
header: { flexDirection: "row", alignItems: "center", gap: 8, marginBottom: 16, paddingBottom: 12, borderBottomWidth: 1, borderBottomColor: colors.border },
|
|
15254
15774
|
headerIcon: { fontSize: 20 },
|
|
@@ -15280,16 +15800,16 @@ var styles11 = import_react_native13.StyleSheet.create({
|
|
|
15280
15800
|
});
|
|
15281
15801
|
|
|
15282
15802
|
// src/widget/screens/ComposeMessageScreen.tsx
|
|
15283
|
-
var
|
|
15284
|
-
var
|
|
15803
|
+
var import_react16 = __toESM(require("react"));
|
|
15804
|
+
var import_react_native15 = require("react-native");
|
|
15285
15805
|
function ComposeMessageScreen({ nav }) {
|
|
15286
15806
|
const { createThread, uploadImage } = useBugBear();
|
|
15287
|
-
const [subject, setSubject] = (0,
|
|
15288
|
-
const [message, setMessage] = (0,
|
|
15289
|
-
const [sending, setSending] = (0,
|
|
15807
|
+
const [subject, setSubject] = (0, import_react16.useState)("");
|
|
15808
|
+
const [message, setMessage] = (0, import_react16.useState)("");
|
|
15809
|
+
const [sending, setSending] = (0, import_react16.useState)(false);
|
|
15290
15810
|
const images = useImageAttachments(uploadImage, 3, "discussion-attachments");
|
|
15291
15811
|
const handleSend = async () => {
|
|
15292
|
-
if (!subject.trim() || !message.trim()) return;
|
|
15812
|
+
if (!subject.trim() || !message.trim() || sending || images.isUploading) return;
|
|
15293
15813
|
setSending(true);
|
|
15294
15814
|
const attachments = images.getAttachments();
|
|
15295
15815
|
const result = await createThread({
|
|
@@ -15302,8 +15822,8 @@ function ComposeMessageScreen({ nav }) {
|
|
|
15302
15822
|
nav.pop();
|
|
15303
15823
|
}
|
|
15304
15824
|
};
|
|
15305
|
-
return /* @__PURE__ */
|
|
15306
|
-
|
|
15825
|
+
return /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, null, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles12.header }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles12.title }, "New Message"), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles12.subtitle }, "Send a message to the QA team")), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles12.form }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.label }, "Subject"), /* @__PURE__ */ import_react16.default.createElement(
|
|
15826
|
+
import_react_native15.TextInput,
|
|
15307
15827
|
{
|
|
15308
15828
|
style: styles12.subjectInput,
|
|
15309
15829
|
value: subject,
|
|
@@ -15312,8 +15832,8 @@ function ComposeMessageScreen({ nav }) {
|
|
|
15312
15832
|
placeholderTextColor: colors.textMuted,
|
|
15313
15833
|
maxLength: 100
|
|
15314
15834
|
}
|
|
15315
|
-
), /* @__PURE__ */
|
|
15316
|
-
|
|
15835
|
+
), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: [shared.label, { marginTop: 16 }] }, "Message"), /* @__PURE__ */ import_react16.default.createElement(
|
|
15836
|
+
import_react_native15.TextInput,
|
|
15317
15837
|
{
|
|
15318
15838
|
style: styles12.messageInput,
|
|
15319
15839
|
value: message,
|
|
@@ -15325,7 +15845,7 @@ function ComposeMessageScreen({ nav }) {
|
|
|
15325
15845
|
textAlignVertical: "top",
|
|
15326
15846
|
maxLength: 2e3
|
|
15327
15847
|
}
|
|
15328
|
-
), /* @__PURE__ */
|
|
15848
|
+
), /* @__PURE__ */ import_react16.default.createElement(
|
|
15329
15849
|
ImagePickerButtons,
|
|
15330
15850
|
{
|
|
15331
15851
|
images: images.images,
|
|
@@ -15334,17 +15854,17 @@ function ComposeMessageScreen({ nav }) {
|
|
|
15334
15854
|
onPickCamera: images.pickFromCamera,
|
|
15335
15855
|
onRemove: images.removeImage
|
|
15336
15856
|
}
|
|
15337
|
-
), /* @__PURE__ */
|
|
15338
|
-
|
|
15857
|
+
), /* @__PURE__ */ import_react16.default.createElement(
|
|
15858
|
+
import_react_native15.TouchableOpacity,
|
|
15339
15859
|
{
|
|
15340
15860
|
style: [shared.primaryButton, (!subject.trim() || !message.trim() || sending || images.isUploading) && shared.primaryButtonDisabled, { marginTop: 20 }],
|
|
15341
15861
|
onPress: handleSend,
|
|
15342
15862
|
disabled: !subject.trim() || !message.trim() || sending || images.isUploading
|
|
15343
15863
|
},
|
|
15344
|
-
/* @__PURE__ */
|
|
15864
|
+
/* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : sending ? "Sending..." : "Send Message")
|
|
15345
15865
|
)));
|
|
15346
15866
|
}
|
|
15347
|
-
var styles12 =
|
|
15867
|
+
var styles12 = import_react_native15.StyleSheet.create({
|
|
15348
15868
|
header: { marginBottom: 20 },
|
|
15349
15869
|
title: { fontSize: 20, fontWeight: "600", color: colors.textPrimary, marginBottom: 4 },
|
|
15350
15870
|
subtitle: { fontSize: 14, color: colors.textMuted },
|
|
@@ -15354,20 +15874,20 @@ var styles12 = import_react_native14.StyleSheet.create({
|
|
|
15354
15874
|
});
|
|
15355
15875
|
|
|
15356
15876
|
// src/widget/screens/ProfileScreen.tsx
|
|
15357
|
-
var
|
|
15358
|
-
var
|
|
15877
|
+
var import_react17 = __toESM(require("react"));
|
|
15878
|
+
var import_react_native16 = require("react-native");
|
|
15359
15879
|
function ProfileScreen({ nav }) {
|
|
15360
15880
|
const { testerInfo, assignments, updateTesterProfile, refreshTesterInfo } = useBugBear();
|
|
15361
|
-
const [editing, setEditing] = (0,
|
|
15362
|
-
const [name, setName] = (0,
|
|
15363
|
-
const [additionalEmails, setAdditionalEmails] = (0,
|
|
15364
|
-
const [newEmailInput, setNewEmailInput] = (0,
|
|
15365
|
-
const [platforms, setPlatforms] = (0,
|
|
15366
|
-
const [saving, setSaving] = (0,
|
|
15367
|
-
const [saved, setSaved] = (0,
|
|
15368
|
-
const [showDetails, setShowDetails] = (0,
|
|
15881
|
+
const [editing, setEditing] = (0, import_react17.useState)(false);
|
|
15882
|
+
const [name, setName] = (0, import_react17.useState)(testerInfo?.name || "");
|
|
15883
|
+
const [additionalEmails, setAdditionalEmails] = (0, import_react17.useState)(testerInfo?.additionalEmails || []);
|
|
15884
|
+
const [newEmailInput, setNewEmailInput] = (0, import_react17.useState)("");
|
|
15885
|
+
const [platforms, setPlatforms] = (0, import_react17.useState)(testerInfo?.platforms || []);
|
|
15886
|
+
const [saving, setSaving] = (0, import_react17.useState)(false);
|
|
15887
|
+
const [saved, setSaved] = (0, import_react17.useState)(false);
|
|
15888
|
+
const [showDetails, setShowDetails] = (0, import_react17.useState)(false);
|
|
15369
15889
|
const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
|
|
15370
|
-
(0,
|
|
15890
|
+
(0, import_react17.useEffect)(() => {
|
|
15371
15891
|
if (testerInfo) {
|
|
15372
15892
|
setName(testerInfo.name);
|
|
15373
15893
|
setAdditionalEmails(testerInfo.additionalEmails || []);
|
|
@@ -15375,6 +15895,7 @@ function ProfileScreen({ nav }) {
|
|
|
15375
15895
|
}
|
|
15376
15896
|
}, [testerInfo]);
|
|
15377
15897
|
const handleSave = async () => {
|
|
15898
|
+
if (saving) return;
|
|
15378
15899
|
setSaving(true);
|
|
15379
15900
|
const updates = {
|
|
15380
15901
|
name: name.trim(),
|
|
@@ -15401,17 +15922,17 @@ function ProfileScreen({ nav }) {
|
|
|
15401
15922
|
}
|
|
15402
15923
|
};
|
|
15403
15924
|
if (saved) {
|
|
15404
|
-
return /* @__PURE__ */
|
|
15925
|
+
return /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: shared.emptyState }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.emptyTitle }, "Profile saved!"));
|
|
15405
15926
|
}
|
|
15406
15927
|
if (!testerInfo) {
|
|
15407
|
-
return /* @__PURE__ */
|
|
15928
|
+
return /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: shared.emptyState }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.emptyEmoji }, "\u{1F464}"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.emptyTitle }, "No profile found"));
|
|
15408
15929
|
}
|
|
15409
15930
|
if (editing) {
|
|
15410
|
-
return /* @__PURE__ */
|
|
15931
|
+
return /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, null, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.editHeader }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.editTitle }, "Edit Profile"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.TouchableOpacity, { onPress: () => {
|
|
15411
15932
|
setEditing(false);
|
|
15412
15933
|
setNewEmailInput("");
|
|
15413
|
-
} }, /* @__PURE__ */
|
|
15414
|
-
|
|
15934
|
+
} }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.cancelText }, "Cancel"))), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.field }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.label }, "Name"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.TextInput, { style: styles13.input, value: name, onChangeText: setName, placeholder: "Your name", placeholderTextColor: colors.textMuted })), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.field }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.label }, "Primary Email"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.emailFixed }, testerInfo.email)), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.field }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.label }, "Additional Emails"), additionalEmails.map((email) => /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { key: email, style: styles13.emailRow }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.emailText }, email), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.TouchableOpacity, { onPress: () => setAdditionalEmails(additionalEmails.filter((e) => e !== email)) }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.removeEmail }, "\u2715")))), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.addEmailRow }, /* @__PURE__ */ import_react17.default.createElement(
|
|
15935
|
+
import_react_native16.TextInput,
|
|
15415
15936
|
{
|
|
15416
15937
|
style: [styles13.input, { flex: 1, marginRight: 8 }],
|
|
15417
15938
|
value: newEmailInput,
|
|
@@ -15421,26 +15942,26 @@ function ProfileScreen({ nav }) {
|
|
|
15421
15942
|
keyboardType: "email-address",
|
|
15422
15943
|
autoCapitalize: "none"
|
|
15423
15944
|
}
|
|
15424
|
-
), /* @__PURE__ */
|
|
15425
|
-
|
|
15945
|
+
), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.TouchableOpacity, { style: styles13.addButton, onPress: handleAddEmail }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.addButtonText }, "Add")))), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.field }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.label }, "Testing Platforms"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { 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__ */ import_react17.default.createElement(
|
|
15946
|
+
import_react_native16.TouchableOpacity,
|
|
15426
15947
|
{
|
|
15427
15948
|
key,
|
|
15428
15949
|
style: [styles13.platformBtn, platforms.includes(key) && styles13.platformBtnActive],
|
|
15429
15950
|
onPress: () => setPlatforms((prev) => prev.includes(key) ? prev.filter((p) => p !== key) : [...prev, key])
|
|
15430
15951
|
},
|
|
15431
|
-
/* @__PURE__ */
|
|
15432
|
-
)))), /* @__PURE__ */
|
|
15952
|
+
/* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: [styles13.platformText, platforms.includes(key) && styles13.platformTextActive] }, label)
|
|
15953
|
+
)))), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.TouchableOpacity, { style: [shared.primaryButton, { marginTop: 20 }], onPress: handleSave, disabled: saving }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.primaryButtonText }, saving ? "Saving..." : "Save Profile")));
|
|
15433
15954
|
}
|
|
15434
|
-
return /* @__PURE__ */
|
|
15435
|
-
|
|
15955
|
+
return /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, null, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.profileCard }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.avatar }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.avatarText }, testerInfo.name.charAt(0).toUpperCase())), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.profileName }, testerInfo.name), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.profileEmail }, testerInfo.email)), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.statsRow }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.statItem }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.statNumber }, completedCount), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.statLabel }, "Completed")), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.statDivider }), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.statItem }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.statNumber }, assignments.length), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.statLabel }, "Total Assigned"))), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.TouchableOpacity, { onPress: () => setShowDetails(!showDetails), style: styles13.detailsToggle }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.detailsSection }, additionalEmails.length > 0 && /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.detailBlock }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.detailLabel }, "Additional Emails"), additionalEmails.map((e) => /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { key: e, style: styles13.detailValue }, e))), platforms.length > 0 && /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.detailBlock }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.detailLabel }, "Platforms"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.platformTags }, platforms.map((p) => /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { key: p, style: styles13.platformTag }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.platformTagText }, p === "ios" ? "\u{1F4F1} iOS" : p === "android" ? "\u{1F916} Android" : "\u{1F310} Web")))))), /* @__PURE__ */ import_react17.default.createElement(
|
|
15956
|
+
import_react_native16.TouchableOpacity,
|
|
15436
15957
|
{
|
|
15437
15958
|
style: [shared.primaryButton, { marginTop: 20 }],
|
|
15438
15959
|
onPress: () => setEditing(true)
|
|
15439
15960
|
},
|
|
15440
|
-
/* @__PURE__ */
|
|
15961
|
+
/* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.primaryButtonText }, "Edit Profile")
|
|
15441
15962
|
));
|
|
15442
15963
|
}
|
|
15443
|
-
var styles13 =
|
|
15964
|
+
var styles13 = import_react_native16.StyleSheet.create({
|
|
15444
15965
|
profileCard: { alignItems: "center", backgroundColor: colors.card, borderRadius: 16, padding: 24, marginBottom: 16 },
|
|
15445
15966
|
avatar: { width: 64, height: 64, borderRadius: 32, backgroundColor: colors.blue, justifyContent: "center", alignItems: "center", marginBottom: 12 },
|
|
15446
15967
|
avatarText: { fontSize: 28, fontWeight: "700", color: "#fff" },
|
|
@@ -15481,8 +16002,8 @@ var styles13 = import_react_native15.StyleSheet.create({
|
|
|
15481
16002
|
});
|
|
15482
16003
|
|
|
15483
16004
|
// src/widget/screens/IssueListScreen.tsx
|
|
15484
|
-
var
|
|
15485
|
-
var
|
|
16005
|
+
var import_react18 = __toESM(require("react"));
|
|
16006
|
+
var import_react_native17 = require("react-native");
|
|
15486
16007
|
var CATEGORY_CONFIG = {
|
|
15487
16008
|
open: { label: "Open Issues", accent: "#f97316", emptyIcon: "\u2705", emptyText: "No open issues" },
|
|
15488
16009
|
done: { label: "Done", accent: "#22c55e", emptyIcon: "\u{1F389}", emptyText: "No completed issues yet" },
|
|
@@ -15496,18 +16017,28 @@ var SEVERITY_COLORS = {
|
|
|
15496
16017
|
};
|
|
15497
16018
|
function IssueListScreen({ nav, category }) {
|
|
15498
16019
|
const { client } = useBugBear();
|
|
15499
|
-
const [issues, setIssues] = (0,
|
|
15500
|
-
const [loading, setLoading] = (0,
|
|
16020
|
+
const [issues, setIssues] = (0, import_react18.useState)([]);
|
|
16021
|
+
const [loading, setLoading] = (0, import_react18.useState)(true);
|
|
15501
16022
|
const config = CATEGORY_CONFIG[category];
|
|
15502
|
-
(0,
|
|
16023
|
+
(0, import_react18.useEffect)(() => {
|
|
15503
16024
|
let cancelled = false;
|
|
15504
16025
|
setLoading(true);
|
|
15505
16026
|
(async () => {
|
|
15506
|
-
if (!client)
|
|
15507
|
-
const data = await client.getIssues(category);
|
|
15508
|
-
if (!cancelled) {
|
|
15509
|
-
setIssues(data);
|
|
16027
|
+
if (!client) {
|
|
15510
16028
|
setLoading(false);
|
|
16029
|
+
return;
|
|
16030
|
+
}
|
|
16031
|
+
try {
|
|
16032
|
+
const data = await client.getIssues(category);
|
|
16033
|
+
if (!cancelled) {
|
|
16034
|
+
setIssues(data);
|
|
16035
|
+
}
|
|
16036
|
+
} catch (err) {
|
|
16037
|
+
console.error("BugBear: Failed to load issues", err);
|
|
16038
|
+
} finally {
|
|
16039
|
+
if (!cancelled) {
|
|
16040
|
+
setLoading(false);
|
|
16041
|
+
}
|
|
15511
16042
|
}
|
|
15512
16043
|
})();
|
|
15513
16044
|
return () => {
|
|
@@ -15515,26 +16046,26 @@ function IssueListScreen({ nav, category }) {
|
|
|
15515
16046
|
};
|
|
15516
16047
|
}, [client, category]);
|
|
15517
16048
|
if (loading) {
|
|
15518
|
-
return /* @__PURE__ */
|
|
16049
|
+
return /* @__PURE__ */ import_react18.default.createElement(IssueListScreenSkeleton, null);
|
|
15519
16050
|
}
|
|
15520
16051
|
if (issues.length === 0) {
|
|
15521
|
-
return /* @__PURE__ */
|
|
16052
|
+
return /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles14.emptyContainer }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.emptyIcon }, config.emptyIcon), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.emptyText }, config.emptyText));
|
|
15522
16053
|
}
|
|
15523
|
-
return /* @__PURE__ */
|
|
15524
|
-
|
|
16054
|
+
return /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, null, issues.map((issue) => /* @__PURE__ */ import_react18.default.createElement(
|
|
16055
|
+
import_react_native17.TouchableOpacity,
|
|
15525
16056
|
{
|
|
15526
16057
|
key: issue.id,
|
|
15527
16058
|
style: styles14.issueCard,
|
|
15528
16059
|
onPress: () => nav.push({ name: "ISSUE_DETAIL", issue }),
|
|
15529
16060
|
activeOpacity: 0.7
|
|
15530
16061
|
},
|
|
15531
|
-
/* @__PURE__ */
|
|
15532
|
-
/* @__PURE__ */
|
|
15533
|
-
category === "done" && issue.verifiedByName && /* @__PURE__ */
|
|
15534
|
-
category === "reopened" && issue.originalBugTitle && /* @__PURE__ */
|
|
16062
|
+
/* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles14.topRow }, issue.severity && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: [styles14.severityDot, { backgroundColor: SEVERITY_COLORS[issue.severity] || colors.textDim }] }), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.issueTitle, numberOfLines: 1 }, issue.title)),
|
|
16063
|
+
/* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles14.bottomRow }, issue.route && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.routeText, numberOfLines: 1 }, issue.route), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.timeText }, formatRelativeTime(issue.updatedAt))),
|
|
16064
|
+
category === "done" && issue.verifiedByName && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles14.verifiedBadge }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.verifiedBadgeText }, "\u2714", " Verified by ", issue.verifiedByName)),
|
|
16065
|
+
category === "reopened" && issue.originalBugTitle && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles14.reopenedBadge }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.reopenedBadgeText, numberOfLines: 1 }, "\u{1F504}", " Retest of: ", issue.originalBugTitle))
|
|
15535
16066
|
)));
|
|
15536
16067
|
}
|
|
15537
|
-
var styles14 =
|
|
16068
|
+
var styles14 = import_react_native17.StyleSheet.create({
|
|
15538
16069
|
emptyContainer: {
|
|
15539
16070
|
alignItems: "center",
|
|
15540
16071
|
paddingVertical: 40
|
|
@@ -15625,8 +16156,8 @@ var styles14 = import_react_native16.StyleSheet.create({
|
|
|
15625
16156
|
});
|
|
15626
16157
|
|
|
15627
16158
|
// src/widget/screens/IssueDetailScreen.tsx
|
|
15628
|
-
var
|
|
15629
|
-
var
|
|
16159
|
+
var import_react19 = __toESM(require("react"));
|
|
16160
|
+
var import_react_native18 = require("react-native");
|
|
15630
16161
|
var STATUS_LABELS = {
|
|
15631
16162
|
new: { label: "New", bg: "#1e3a5f", color: "#60a5fa" },
|
|
15632
16163
|
triaging: { label: "Triaging", bg: "#1e3a5f", color: "#60a5fa" },
|
|
@@ -15650,9 +16181,9 @@ var SEVERITY_CONFIG = {
|
|
|
15650
16181
|
function IssueDetailScreen({ nav, issue }) {
|
|
15651
16182
|
const statusConfig = STATUS_LABELS[issue.status] || { label: issue.status, bg: "#27272a", color: "#a1a1aa" };
|
|
15652
16183
|
const severityConfig = issue.severity ? SEVERITY_CONFIG[issue.severity] : null;
|
|
15653
|
-
return /* @__PURE__ */
|
|
16184
|
+
return /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, null, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.badgeRow }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: [styles15.badge, { backgroundColor: statusConfig.bg }] }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: [styles15.badgeText, { color: statusConfig.color }] }, statusConfig.label)), severityConfig && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: [styles15.badge, { backgroundColor: severityConfig.bg }] }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: [styles15.badgeText, { color: severityConfig.color }] }, severityConfig.label))), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.title }, issue.title), issue.route && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.route }, issue.route), issue.description && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.descriptionCard }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.descriptionText }, issue.description)), issue.verifiedByName && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.verifiedCard }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.verifiedHeader }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.verifiedIcon }, "\u2705"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.verifiedTitle }, "Retesting Proof")), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { 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__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.originalBugCard }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.originalBugHeader }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.originalBugIcon }, "\u{1F504}"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.originalBugTitle }, "Original Bug")), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.originalBugBody }, "Retest of: ", issue.originalBugTitle)), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.screenshotSection }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ import_react19.default.createElement(import_react_native18.TouchableOpacity, { key: i, onPress: () => import_react_native18.Linking.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Image, { source: { uri: url }, style: styles15.screenshotThumb }))))), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.metaSection }, issue.reporterName && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))));
|
|
15654
16185
|
}
|
|
15655
|
-
var styles15 =
|
|
16186
|
+
var styles15 = import_react_native18.StyleSheet.create({
|
|
15656
16187
|
badgeRow: {
|
|
15657
16188
|
flexDirection: "row",
|
|
15658
16189
|
gap: 8,
|
|
@@ -15783,8 +16314,8 @@ var styles15 = import_react_native17.StyleSheet.create({
|
|
|
15783
16314
|
});
|
|
15784
16315
|
|
|
15785
16316
|
// src/BugBearButton.tsx
|
|
15786
|
-
var screenWidth =
|
|
15787
|
-
var screenHeight =
|
|
16317
|
+
var screenWidth = import_react_native19.Dimensions.get("window").width;
|
|
16318
|
+
var screenHeight = import_react_native19.Dimensions.get("window").height;
|
|
15788
16319
|
function BugBearButton({
|
|
15789
16320
|
position = "bottom-right",
|
|
15790
16321
|
buttonStyle,
|
|
@@ -15796,7 +16327,7 @@ function BugBearButton({
|
|
|
15796
16327
|
}) {
|
|
15797
16328
|
const { shouldShowWidget, testerInfo, isLoading, unreadCount, assignments } = useBugBear();
|
|
15798
16329
|
const { currentScreen, canGoBack, push, pop, replace, reset } = useNavigation();
|
|
15799
|
-
const [modalVisible, setModalVisible] = (0,
|
|
16330
|
+
const [modalVisible, setModalVisible] = (0, import_react20.useState)(false);
|
|
15800
16331
|
const getInitialPosition = () => {
|
|
15801
16332
|
const buttonSize = 56;
|
|
15802
16333
|
const margin = 16;
|
|
@@ -15808,10 +16339,10 @@ function BugBearButton({
|
|
|
15808
16339
|
return { x, y };
|
|
15809
16340
|
};
|
|
15810
16341
|
const initialPos = getInitialPosition();
|
|
15811
|
-
const pan = (0,
|
|
15812
|
-
const isDragging = (0,
|
|
15813
|
-
const panResponder = (0,
|
|
15814
|
-
|
|
16342
|
+
const pan = (0, import_react20.useRef)(new import_react_native19.Animated.ValueXY(initialPos)).current;
|
|
16343
|
+
const isDragging = (0, import_react20.useRef)(false);
|
|
16344
|
+
const panResponder = (0, import_react20.useRef)(
|
|
16345
|
+
import_react_native19.PanResponder.create({
|
|
15815
16346
|
onStartShouldSetPanResponder: () => draggable,
|
|
15816
16347
|
onMoveShouldSetPanResponder: (_, gs) => draggable && (Math.abs(gs.dx) > 5 || Math.abs(gs.dy) > 5),
|
|
15817
16348
|
onPanResponderGrant: () => {
|
|
@@ -15826,7 +16357,7 @@ function BugBearButton({
|
|
|
15826
16357
|
if (Math.abs(gs.dx) > 5 || Math.abs(gs.dy) > 5) {
|
|
15827
16358
|
isDragging.current = true;
|
|
15828
16359
|
}
|
|
15829
|
-
|
|
16360
|
+
import_react_native19.Animated.event(
|
|
15830
16361
|
[null, { dx: pan.x, dy: pan.y }],
|
|
15831
16362
|
{ useNativeDriver: false }
|
|
15832
16363
|
)(_, gs);
|
|
@@ -15839,7 +16370,7 @@ function BugBearButton({
|
|
|
15839
16370
|
const margin = 16;
|
|
15840
16371
|
const snapX = currentX < screenWidth / 2 ? margin : screenWidth - buttonSize - margin;
|
|
15841
16372
|
const snapY = Math.max(minY, Math.min(currentY, screenHeight - maxYOffset));
|
|
15842
|
-
|
|
16373
|
+
import_react_native19.Animated.spring(pan, {
|
|
15843
16374
|
toValue: { x: snapX, y: snapY },
|
|
15844
16375
|
useNativeDriver: false,
|
|
15845
16376
|
friction: 7,
|
|
@@ -15886,24 +16417,24 @@ function BugBearButton({
|
|
|
15886
16417
|
}
|
|
15887
16418
|
};
|
|
15888
16419
|
const handleClose = () => {
|
|
15889
|
-
|
|
16420
|
+
import_react_native19.Keyboard.dismiss();
|
|
15890
16421
|
setModalVisible(false);
|
|
15891
16422
|
};
|
|
15892
16423
|
const nav = {
|
|
15893
16424
|
push: (screen) => {
|
|
15894
|
-
|
|
16425
|
+
import_react_native19.Keyboard.dismiss();
|
|
15895
16426
|
push(screen);
|
|
15896
16427
|
},
|
|
15897
16428
|
pop: () => {
|
|
15898
|
-
|
|
16429
|
+
import_react_native19.Keyboard.dismiss();
|
|
15899
16430
|
pop();
|
|
15900
16431
|
},
|
|
15901
16432
|
replace: (screen) => {
|
|
15902
|
-
|
|
16433
|
+
import_react_native19.Keyboard.dismiss();
|
|
15903
16434
|
replace(screen);
|
|
15904
16435
|
},
|
|
15905
16436
|
reset: () => {
|
|
15906
|
-
|
|
16437
|
+
import_react_native19.Keyboard.dismiss();
|
|
15907
16438
|
reset();
|
|
15908
16439
|
},
|
|
15909
16440
|
canGoBack,
|
|
@@ -15912,77 +16443,77 @@ function BugBearButton({
|
|
|
15912
16443
|
const renderScreen = () => {
|
|
15913
16444
|
switch (currentScreen.name) {
|
|
15914
16445
|
case "HOME":
|
|
15915
|
-
return /* @__PURE__ */
|
|
16446
|
+
return /* @__PURE__ */ import_react20.default.createElement(HomeScreen, { nav });
|
|
15916
16447
|
case "TEST_DETAIL":
|
|
15917
|
-
return /* @__PURE__ */
|
|
16448
|
+
return /* @__PURE__ */ import_react20.default.createElement(TestDetailScreen, { testId: currentScreen.testId, nav });
|
|
15918
16449
|
case "TEST_LIST":
|
|
15919
|
-
return /* @__PURE__ */
|
|
16450
|
+
return /* @__PURE__ */ import_react20.default.createElement(TestListScreen, { nav });
|
|
15920
16451
|
case "TEST_FEEDBACK":
|
|
15921
|
-
return /* @__PURE__ */
|
|
16452
|
+
return /* @__PURE__ */ import_react20.default.createElement(TestFeedbackScreen, { status: currentScreen.status, assignmentId: currentScreen.assignmentId, nav });
|
|
15922
16453
|
case "REPORT":
|
|
15923
|
-
return /* @__PURE__ */
|
|
16454
|
+
return /* @__PURE__ */ import_react20.default.createElement(ReportScreen, { nav, prefill: currentScreen.prefill });
|
|
15924
16455
|
case "REPORT_SUCCESS":
|
|
15925
|
-
return /* @__PURE__ */
|
|
16456
|
+
return /* @__PURE__ */ import_react20.default.createElement(ReportSuccessScreen, { nav });
|
|
15926
16457
|
case "MESSAGE_LIST":
|
|
15927
|
-
return /* @__PURE__ */
|
|
16458
|
+
return /* @__PURE__ */ import_react20.default.createElement(MessageListScreen, { nav });
|
|
15928
16459
|
case "THREAD_DETAIL":
|
|
15929
|
-
return /* @__PURE__ */
|
|
16460
|
+
return /* @__PURE__ */ import_react20.default.createElement(ThreadDetailScreen, { thread: currentScreen.thread, nav });
|
|
15930
16461
|
case "COMPOSE_MESSAGE":
|
|
15931
|
-
return /* @__PURE__ */
|
|
16462
|
+
return /* @__PURE__ */ import_react20.default.createElement(ComposeMessageScreen, { nav });
|
|
15932
16463
|
case "ISSUE_LIST":
|
|
15933
|
-
return /* @__PURE__ */
|
|
16464
|
+
return /* @__PURE__ */ import_react20.default.createElement(IssueListScreen, { nav, category: currentScreen.category });
|
|
15934
16465
|
case "ISSUE_DETAIL":
|
|
15935
|
-
return /* @__PURE__ */
|
|
16466
|
+
return /* @__PURE__ */ import_react20.default.createElement(IssueDetailScreen, { nav, issue: currentScreen.issue });
|
|
15936
16467
|
case "PROFILE":
|
|
15937
|
-
return /* @__PURE__ */
|
|
16468
|
+
return /* @__PURE__ */ import_react20.default.createElement(ProfileScreen, { nav });
|
|
15938
16469
|
default:
|
|
15939
|
-
return /* @__PURE__ */
|
|
16470
|
+
return /* @__PURE__ */ import_react20.default.createElement(HomeScreen, { nav });
|
|
15940
16471
|
}
|
|
15941
16472
|
};
|
|
15942
|
-
return /* @__PURE__ */
|
|
15943
|
-
|
|
16473
|
+
return /* @__PURE__ */ import_react20.default.createElement(import_react20.default.Fragment, null, /* @__PURE__ */ import_react20.default.createElement(
|
|
16474
|
+
import_react_native19.Animated.View,
|
|
15944
16475
|
{
|
|
15945
16476
|
style: [styles16.fabContainer, { transform: pan.getTranslateTransform() }, buttonStyle],
|
|
15946
16477
|
...panResponder.panHandlers
|
|
15947
16478
|
},
|
|
15948
|
-
/* @__PURE__ */
|
|
15949
|
-
|
|
16479
|
+
/* @__PURE__ */ import_react20.default.createElement(
|
|
16480
|
+
import_react_native19.TouchableOpacity,
|
|
15950
16481
|
{
|
|
15951
16482
|
style: styles16.fab,
|
|
15952
16483
|
onPress: () => setModalVisible(true),
|
|
15953
16484
|
activeOpacity: draggable ? 1 : 0.7
|
|
15954
16485
|
},
|
|
15955
|
-
/* @__PURE__ */
|
|
15956
|
-
badgeCount > 0 && /* @__PURE__ */
|
|
16486
|
+
/* @__PURE__ */ import_react20.default.createElement(import_react_native19.Image, { source: { uri: BUGBEAR_LOGO_BASE64 }, style: styles16.fabIcon }),
|
|
16487
|
+
badgeCount > 0 && /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.badge }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.badgeText }, badgeCount > 9 ? "9+" : badgeCount))
|
|
15957
16488
|
)
|
|
15958
|
-
), /* @__PURE__ */
|
|
15959
|
-
|
|
16489
|
+
), /* @__PURE__ */ import_react20.default.createElement(
|
|
16490
|
+
import_react_native19.Modal,
|
|
15960
16491
|
{
|
|
15961
16492
|
visible: modalVisible,
|
|
15962
16493
|
animationType: "slide",
|
|
15963
16494
|
transparent: true,
|
|
15964
16495
|
onRequestClose: handleClose
|
|
15965
16496
|
},
|
|
15966
|
-
/* @__PURE__ */
|
|
15967
|
-
|
|
16497
|
+
/* @__PURE__ */ import_react20.default.createElement(
|
|
16498
|
+
import_react_native19.KeyboardAvoidingView,
|
|
15968
16499
|
{
|
|
15969
|
-
behavior:
|
|
16500
|
+
behavior: import_react_native19.Platform.OS === "ios" ? "padding" : "height",
|
|
15970
16501
|
style: styles16.modalOverlay
|
|
15971
16502
|
},
|
|
15972
|
-
/* @__PURE__ */
|
|
15973
|
-
|
|
16503
|
+
/* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.modalContainer }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.header }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.headerLeft }, canGoBack ? /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.headerNavRow }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.TouchableOpacity, { onPress: () => nav.pop(), style: styles16.backButton }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.backText }, "\u2190 Back")), /* @__PURE__ */ import_react20.default.createElement(import_react_native19.TouchableOpacity, { onPress: () => nav.reset(), style: styles16.homeButton }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.homeText }, "\u{1F3E0}"))) : /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.headerTitleRow }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ import_react20.default.createElement(import_react_native19.TouchableOpacity, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.TouchableOpacity, { onPress: handleClose, style: styles16.closeButton }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.closeText }, "\u2715"))), /* @__PURE__ */ import_react20.default.createElement(
|
|
16504
|
+
import_react_native19.ScrollView,
|
|
15974
16505
|
{
|
|
15975
16506
|
style: styles16.content,
|
|
15976
16507
|
contentContainerStyle: styles16.contentContainer,
|
|
15977
16508
|
keyboardShouldPersistTaps: "handled",
|
|
15978
16509
|
showsVerticalScrollIndicator: false
|
|
15979
16510
|
},
|
|
15980
|
-
isLoading ? /* @__PURE__ */
|
|
16511
|
+
isLoading ? /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.loadingContainer }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.ActivityIndicator, { size: "large", color: colors.blue }), /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.loadingText }, "Loading...")) : renderScreen()
|
|
15981
16512
|
))
|
|
15982
16513
|
)
|
|
15983
16514
|
));
|
|
15984
16515
|
}
|
|
15985
|
-
var styles16 =
|
|
16516
|
+
var styles16 = import_react_native19.StyleSheet.create({
|
|
15986
16517
|
// FAB
|
|
15987
16518
|
fabContainer: {
|
|
15988
16519
|
position: "absolute",
|
|
@@ -16124,9 +16655,9 @@ var styles16 = import_react_native18.StyleSheet.create({
|
|
|
16124
16655
|
});
|
|
16125
16656
|
|
|
16126
16657
|
// src/BugBearErrorBoundary.tsx
|
|
16127
|
-
var
|
|
16128
|
-
var
|
|
16129
|
-
var BugBearErrorBoundary = class extends
|
|
16658
|
+
var import_react21 = __toESM(require("react"));
|
|
16659
|
+
var import_react_native20 = require("react-native");
|
|
16660
|
+
var BugBearErrorBoundary = class extends import_react21.Component {
|
|
16130
16661
|
constructor(props) {
|
|
16131
16662
|
super(props);
|
|
16132
16663
|
this.reset = () => {
|
|
@@ -16170,7 +16701,7 @@ var BugBearErrorBoundary = class extends import_react20.Component {
|
|
|
16170
16701
|
if (fallback) {
|
|
16171
16702
|
return fallback;
|
|
16172
16703
|
}
|
|
16173
|
-
return /* @__PURE__ */
|
|
16704
|
+
return /* @__PURE__ */ import_react21.default.createElement(import_react_native20.View, { style: styles17.container }, /* @__PURE__ */ import_react21.default.createElement(import_react_native20.Text, { style: styles17.title }, "Something went wrong"), /* @__PURE__ */ import_react21.default.createElement(import_react_native20.Text, { style: styles17.message }, error.message), /* @__PURE__ */ import_react21.default.createElement(import_react_native20.TouchableOpacity, { style: styles17.button, onPress: this.reset }, /* @__PURE__ */ import_react21.default.createElement(import_react_native20.Text, { style: styles17.buttonText }, "Try Again")), /* @__PURE__ */ import_react21.default.createElement(import_react_native20.Text, { style: styles17.caption }, "The error has been captured by BugBear"));
|
|
16174
16705
|
}
|
|
16175
16706
|
return children;
|
|
16176
16707
|
}
|
|
@@ -16181,7 +16712,7 @@ function useErrorContext() {
|
|
|
16181
16712
|
getEnhancedContext: () => contextCapture.getEnhancedContext()
|
|
16182
16713
|
};
|
|
16183
16714
|
}
|
|
16184
|
-
var styles17 =
|
|
16715
|
+
var styles17 = import_react_native20.StyleSheet.create({
|
|
16185
16716
|
container: {
|
|
16186
16717
|
padding: 20,
|
|
16187
16718
|
margin: 20,
|