@bbearai/react-native 0.6.3 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +302 -43
- package/dist/index.mjs +303 -44
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -11540,6 +11540,7 @@ var ContextCaptureManager = class {
|
|
|
11540
11540
|
this.networkRequests = [];
|
|
11541
11541
|
this.navigationHistory = [];
|
|
11542
11542
|
this.originalConsole = {};
|
|
11543
|
+
this.fetchHost = null;
|
|
11543
11544
|
this.isCapturing = false;
|
|
11544
11545
|
}
|
|
11545
11546
|
/**
|
|
@@ -11562,8 +11563,9 @@ var ContextCaptureManager = class {
|
|
|
11562
11563
|
if (this.originalConsole.warn) console.warn = this.originalConsole.warn;
|
|
11563
11564
|
if (this.originalConsole.error) console.error = this.originalConsole.error;
|
|
11564
11565
|
if (this.originalConsole.info) console.info = this.originalConsole.info;
|
|
11565
|
-
if (this.originalFetch &&
|
|
11566
|
-
|
|
11566
|
+
if (this.originalFetch && this.fetchHost) {
|
|
11567
|
+
this.fetchHost.fetch = this.originalFetch;
|
|
11568
|
+
this.fetchHost = null;
|
|
11567
11569
|
}
|
|
11568
11570
|
if (typeof window !== "undefined" && typeof history !== "undefined") {
|
|
11569
11571
|
if (this.originalPushState) {
|
|
@@ -11672,15 +11674,19 @@ var ContextCaptureManager = class {
|
|
|
11672
11674
|
});
|
|
11673
11675
|
}
|
|
11674
11676
|
captureFetch() {
|
|
11675
|
-
if (typeof
|
|
11676
|
-
|
|
11677
|
+
if (typeof fetch === "undefined") return;
|
|
11678
|
+
const host = typeof window !== "undefined" && typeof window.fetch === "function" ? window : typeof globalThis !== "undefined" && typeof globalThis.fetch === "function" ? globalThis : null;
|
|
11679
|
+
if (!host) return;
|
|
11680
|
+
const canCloneResponse = typeof document !== "undefined";
|
|
11681
|
+
this.fetchHost = host;
|
|
11682
|
+
this.originalFetch = host.fetch;
|
|
11677
11683
|
const self2 = this;
|
|
11678
|
-
|
|
11684
|
+
host.fetch = async function(input, init) {
|
|
11679
11685
|
const startTime = Date.now();
|
|
11680
11686
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
11681
11687
|
const method = init?.method || "GET";
|
|
11682
11688
|
try {
|
|
11683
|
-
const response = await self2.originalFetch.call(
|
|
11689
|
+
const response = await self2.originalFetch.call(host, input, init);
|
|
11684
11690
|
const requestEntry = {
|
|
11685
11691
|
method,
|
|
11686
11692
|
url: url.slice(0, 200),
|
|
@@ -11689,7 +11695,7 @@ var ContextCaptureManager = class {
|
|
|
11689
11695
|
duration: Date.now() - startTime,
|
|
11690
11696
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11691
11697
|
};
|
|
11692
|
-
if (response.status >= 400) {
|
|
11698
|
+
if (canCloneResponse && response.status >= 400) {
|
|
11693
11699
|
try {
|
|
11694
11700
|
const cloned = response.clone();
|
|
11695
11701
|
const body = await cloned.text();
|
|
@@ -11933,29 +11939,140 @@ var formatPgError = (e) => {
|
|
|
11933
11939
|
const { message, code, details, hint } = e;
|
|
11934
11940
|
return { message, code, details, hint };
|
|
11935
11941
|
};
|
|
11942
|
+
var DEFAULT_API_BASE_URL = "https://app.bugbear.ai";
|
|
11943
|
+
var CONFIG_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
11944
|
+
var CONFIG_CACHE_PREFIX = "bugbear_config_";
|
|
11936
11945
|
var BugBearClient = class {
|
|
11937
11946
|
constructor(config) {
|
|
11938
11947
|
this.navigationHistory = [];
|
|
11939
11948
|
this.reportSubmitInFlight = false;
|
|
11940
11949
|
this._queue = null;
|
|
11941
11950
|
this.realtimeChannels = [];
|
|
11942
|
-
|
|
11943
|
-
|
|
11944
|
-
}
|
|
11945
|
-
if (!config.supabaseAnonKey) {
|
|
11946
|
-
throw new Error("BugBear: supabaseAnonKey is required. Get it from your BugBear project settings.");
|
|
11947
|
-
}
|
|
11951
|
+
this.initialized = false;
|
|
11952
|
+
this.initError = null;
|
|
11948
11953
|
this.config = config;
|
|
11949
|
-
|
|
11950
|
-
|
|
11954
|
+
if (config.apiKey) {
|
|
11955
|
+
this.pendingInit = this.resolveFromApiKey(config.apiKey);
|
|
11956
|
+
} else if (config.supabaseUrl && config.supabaseAnonKey) {
|
|
11957
|
+
if (!config.projectId) {
|
|
11958
|
+
throw new Error(
|
|
11959
|
+
"BugBear: projectId is required when using explicit Supabase credentials. Tip: Use apiKey instead for simpler setup \u2014 it resolves everything automatically."
|
|
11960
|
+
);
|
|
11961
|
+
}
|
|
11962
|
+
this.supabase = createClient(config.supabaseUrl, config.supabaseAnonKey);
|
|
11963
|
+
this.initialized = true;
|
|
11964
|
+
this.pendingInit = Promise.resolve();
|
|
11965
|
+
this.initOfflineQueue();
|
|
11966
|
+
} else {
|
|
11967
|
+
throw new Error(
|
|
11968
|
+
"BugBear: Missing configuration. Provide either:\n \u2022 apiKey (recommended) \u2014 resolves everything automatically\n \u2022 projectId + supabaseUrl + supabaseAnonKey \u2014 explicit credentials\n\nGet your API key at https://app.bugbear.ai/settings/projects"
|
|
11969
|
+
);
|
|
11970
|
+
}
|
|
11971
|
+
}
|
|
11972
|
+
/** Whether the client is ready for requests. */
|
|
11973
|
+
get isReady() {
|
|
11974
|
+
return this.initialized;
|
|
11975
|
+
}
|
|
11976
|
+
/** Wait until the client is ready. Throws if initialization failed. */
|
|
11977
|
+
async ready() {
|
|
11978
|
+
await this.pendingInit;
|
|
11979
|
+
if (this.initError) throw this.initError;
|
|
11980
|
+
}
|
|
11981
|
+
/**
|
|
11982
|
+
* Resolve Supabase credentials from a BugBear API key.
|
|
11983
|
+
* Checks localStorage cache first, falls back to /api/v1/config.
|
|
11984
|
+
*/
|
|
11985
|
+
async resolveFromApiKey(apiKey) {
|
|
11986
|
+
try {
|
|
11987
|
+
const cached = this.readConfigCache(apiKey);
|
|
11988
|
+
if (cached) {
|
|
11989
|
+
this.applyResolvedConfig(cached);
|
|
11990
|
+
return;
|
|
11991
|
+
}
|
|
11992
|
+
const baseUrl = (this.config.apiBaseUrl || DEFAULT_API_BASE_URL).replace(/\/$/, "");
|
|
11993
|
+
const response = await fetch(`${baseUrl}/api/v1/config`, {
|
|
11994
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
11995
|
+
});
|
|
11996
|
+
if (!response.ok) {
|
|
11997
|
+
const body = await response.json().catch(() => ({}));
|
|
11998
|
+
const message = body.error || `HTTP ${response.status}`;
|
|
11999
|
+
throw new Error(
|
|
12000
|
+
`BugBear: Invalid API key \u2014 ${message}. Get yours at https://app.bugbear.ai/settings/projects`
|
|
12001
|
+
);
|
|
12002
|
+
}
|
|
12003
|
+
const data = await response.json();
|
|
12004
|
+
this.writeConfigCache(apiKey, data);
|
|
12005
|
+
this.applyResolvedConfig(data);
|
|
12006
|
+
} catch (err) {
|
|
12007
|
+
this.initError = err instanceof Error ? err : new Error(String(err));
|
|
12008
|
+
this.config.onError?.(this.initError, { context: "apikey_resolution_failed" });
|
|
12009
|
+
throw this.initError;
|
|
12010
|
+
}
|
|
12011
|
+
}
|
|
12012
|
+
/** Apply resolved credentials and create the Supabase client. */
|
|
12013
|
+
applyResolvedConfig(resolved) {
|
|
12014
|
+
this.config = {
|
|
12015
|
+
...this.config,
|
|
12016
|
+
projectId: resolved.projectId,
|
|
12017
|
+
supabaseUrl: resolved.supabaseUrl,
|
|
12018
|
+
supabaseAnonKey: resolved.supabaseAnonKey
|
|
12019
|
+
};
|
|
12020
|
+
this.supabase = createClient(resolved.supabaseUrl, resolved.supabaseAnonKey);
|
|
12021
|
+
this.initialized = true;
|
|
12022
|
+
this.initOfflineQueue();
|
|
12023
|
+
}
|
|
12024
|
+
/** Initialize offline queue if configured. Shared by both init paths. */
|
|
12025
|
+
initOfflineQueue() {
|
|
12026
|
+
if (this.config.offlineQueue?.enabled) {
|
|
11951
12027
|
this._queue = new OfflineQueue({
|
|
11952
12028
|
enabled: true,
|
|
11953
|
-
maxItems: config.offlineQueue.maxItems,
|
|
11954
|
-
maxRetries: config.offlineQueue.maxRetries
|
|
12029
|
+
maxItems: this.config.offlineQueue.maxItems,
|
|
12030
|
+
maxRetries: this.config.offlineQueue.maxRetries
|
|
11955
12031
|
});
|
|
11956
12032
|
this.registerQueueHandlers();
|
|
11957
12033
|
}
|
|
11958
12034
|
}
|
|
12035
|
+
/** Read cached config from localStorage if available and not expired. */
|
|
12036
|
+
readConfigCache(apiKey) {
|
|
12037
|
+
if (typeof localStorage === "undefined") return null;
|
|
12038
|
+
try {
|
|
12039
|
+
const key = CONFIG_CACHE_PREFIX + this.hashKey(apiKey);
|
|
12040
|
+
const raw = localStorage.getItem(key);
|
|
12041
|
+
if (!raw) return null;
|
|
12042
|
+
const cached = JSON.parse(raw);
|
|
12043
|
+
if (Date.now() - cached.cachedAt > CONFIG_CACHE_TTL_MS) {
|
|
12044
|
+
localStorage.removeItem(key);
|
|
12045
|
+
return null;
|
|
12046
|
+
}
|
|
12047
|
+
return cached;
|
|
12048
|
+
} catch {
|
|
12049
|
+
return null;
|
|
12050
|
+
}
|
|
12051
|
+
}
|
|
12052
|
+
/** Write resolved config to localStorage cache. */
|
|
12053
|
+
writeConfigCache(apiKey, data) {
|
|
12054
|
+
if (typeof localStorage === "undefined") return;
|
|
12055
|
+
try {
|
|
12056
|
+
const key = CONFIG_CACHE_PREFIX + this.hashKey(apiKey);
|
|
12057
|
+
const cached = { ...data, cachedAt: Date.now() };
|
|
12058
|
+
localStorage.setItem(key, JSON.stringify(cached));
|
|
12059
|
+
} catch {
|
|
12060
|
+
}
|
|
12061
|
+
}
|
|
12062
|
+
/** Simple string hash for cache keys — avoids storing raw API keys. */
|
|
12063
|
+
hashKey(apiKey) {
|
|
12064
|
+
let hash = 0;
|
|
12065
|
+
for (let i = 0; i < apiKey.length; i++) {
|
|
12066
|
+
hash = (hash << 5) - hash + apiKey.charCodeAt(i) | 0;
|
|
12067
|
+
}
|
|
12068
|
+
return hash.toString(36);
|
|
12069
|
+
}
|
|
12070
|
+
/** Ensure the client is initialized before making requests. */
|
|
12071
|
+
async ensureReady() {
|
|
12072
|
+
if (this.initialized) return;
|
|
12073
|
+
await this.pendingInit;
|
|
12074
|
+
if (this.initError) throw this.initError;
|
|
12075
|
+
}
|
|
11959
12076
|
// ── Offline Queue ─────────────────────────────────────────
|
|
11960
12077
|
/**
|
|
11961
12078
|
* Access the offline queue (if enabled).
|
|
@@ -12111,6 +12228,7 @@ var BugBearClient = class {
|
|
|
12111
12228
|
* Get current user info from host app or BugBear's own auth
|
|
12112
12229
|
*/
|
|
12113
12230
|
async getCurrentUserInfo() {
|
|
12231
|
+
await this.ensureReady();
|
|
12114
12232
|
if (this.config.getCurrentUser) {
|
|
12115
12233
|
return await this.config.getCurrentUser();
|
|
12116
12234
|
}
|
|
@@ -12330,6 +12448,7 @@ var BugBearClient = class {
|
|
|
12330
12448
|
*/
|
|
12331
12449
|
async getAssignment(assignmentId) {
|
|
12332
12450
|
try {
|
|
12451
|
+
await this.ensureReady();
|
|
12333
12452
|
const { data, error } = await this.supabase.from("test_assignments").select(`
|
|
12334
12453
|
id,
|
|
12335
12454
|
status,
|
|
@@ -12401,6 +12520,7 @@ var BugBearClient = class {
|
|
|
12401
12520
|
*/
|
|
12402
12521
|
async updateAssignmentStatus(assignmentId, status, options) {
|
|
12403
12522
|
try {
|
|
12523
|
+
await this.ensureReady();
|
|
12404
12524
|
const { data: currentAssignment, error: fetchError } = await this.supabase.from("test_assignments").select("status, started_at").eq("id", assignmentId).single();
|
|
12405
12525
|
if (fetchError || !currentAssignment) {
|
|
12406
12526
|
console.error("BugBear: Assignment not found", {
|
|
@@ -12487,6 +12607,7 @@ var BugBearClient = class {
|
|
|
12487
12607
|
*/
|
|
12488
12608
|
async reopenAssignment(assignmentId) {
|
|
12489
12609
|
try {
|
|
12610
|
+
await this.ensureReady();
|
|
12490
12611
|
const { data: current, error: fetchError } = await this.supabase.from("test_assignments").select("status").eq("id", assignmentId).single();
|
|
12491
12612
|
if (fetchError || !current) {
|
|
12492
12613
|
return { success: false, error: "Assignment not found" };
|
|
@@ -12526,6 +12647,7 @@ var BugBearClient = class {
|
|
|
12526
12647
|
actualNotes = notes;
|
|
12527
12648
|
}
|
|
12528
12649
|
try {
|
|
12650
|
+
await this.ensureReady();
|
|
12529
12651
|
const updateData = {
|
|
12530
12652
|
status: "skipped",
|
|
12531
12653
|
skip_reason: actualReason,
|
|
@@ -12695,6 +12817,7 @@ var BugBearClient = class {
|
|
|
12695
12817
|
*/
|
|
12696
12818
|
async getTesterInfo() {
|
|
12697
12819
|
try {
|
|
12820
|
+
await this.ensureReady();
|
|
12698
12821
|
const userInfo = await this.getCurrentUserInfo();
|
|
12699
12822
|
if (!userInfo?.email) return null;
|
|
12700
12823
|
if (!this.isValidEmail(userInfo.email)) {
|
|
@@ -12986,6 +13109,7 @@ var BugBearClient = class {
|
|
|
12986
13109
|
*/
|
|
12987
13110
|
async isQAEnabled() {
|
|
12988
13111
|
try {
|
|
13112
|
+
await this.ensureReady();
|
|
12989
13113
|
const { data, error } = await this.supabase.rpc("check_qa_enabled", {
|
|
12990
13114
|
p_project_id: this.config.projectId
|
|
12991
13115
|
});
|
|
@@ -13017,6 +13141,7 @@ var BugBearClient = class {
|
|
|
13017
13141
|
*/
|
|
13018
13142
|
async uploadScreenshot(file, filename, bucket = "screenshots") {
|
|
13019
13143
|
try {
|
|
13144
|
+
await this.ensureReady();
|
|
13020
13145
|
const contentType = file.type || "image/png";
|
|
13021
13146
|
const ext = contentType.includes("png") ? "png" : "jpg";
|
|
13022
13147
|
const name = filename || `screenshot-${Date.now()}.${ext}`;
|
|
@@ -13057,6 +13182,7 @@ var BugBearClient = class {
|
|
|
13057
13182
|
*/
|
|
13058
13183
|
async uploadImageFromUri(uri, filename, bucket = "screenshots") {
|
|
13059
13184
|
try {
|
|
13185
|
+
await this.ensureReady();
|
|
13060
13186
|
const response = await fetch(uri);
|
|
13061
13187
|
const blob = await response.blob();
|
|
13062
13188
|
const contentType = blob.type || "image/jpeg";
|
|
@@ -13151,6 +13277,7 @@ var BugBearClient = class {
|
|
|
13151
13277
|
*/
|
|
13152
13278
|
async getFixRequests(options) {
|
|
13153
13279
|
try {
|
|
13280
|
+
await this.ensureReady();
|
|
13154
13281
|
let query = this.supabase.from("fix_requests").select("*").eq("project_id", this.config.projectId).order("created_at", { ascending: false }).limit(options?.limit || 20);
|
|
13155
13282
|
if (options?.status) {
|
|
13156
13283
|
query = query.eq("status", options.status);
|
|
@@ -13222,6 +13349,7 @@ var BugBearClient = class {
|
|
|
13222
13349
|
*/
|
|
13223
13350
|
async getThreadMessages(threadId) {
|
|
13224
13351
|
try {
|
|
13352
|
+
await this.ensureReady();
|
|
13225
13353
|
const { data, error } = await this.supabase.from("discussion_messages").select(`
|
|
13226
13354
|
id,
|
|
13227
13355
|
thread_id,
|
|
@@ -13424,6 +13552,7 @@ var BugBearClient = class {
|
|
|
13424
13552
|
*/
|
|
13425
13553
|
async endSession(sessionId, options = {}) {
|
|
13426
13554
|
try {
|
|
13555
|
+
await this.ensureReady();
|
|
13427
13556
|
const { data, error } = await this.supabase.rpc("end_qa_session", {
|
|
13428
13557
|
p_session_id: sessionId,
|
|
13429
13558
|
p_notes: options.notes || null,
|
|
@@ -13461,6 +13590,7 @@ var BugBearClient = class {
|
|
|
13461
13590
|
*/
|
|
13462
13591
|
async getSession(sessionId) {
|
|
13463
13592
|
try {
|
|
13593
|
+
await this.ensureReady();
|
|
13464
13594
|
const { data, error } = await this.supabase.from("qa_sessions").select("*").eq("id", sessionId).single();
|
|
13465
13595
|
if (error || !data) return null;
|
|
13466
13596
|
return this.transformSession(data);
|
|
@@ -13492,6 +13622,7 @@ var BugBearClient = class {
|
|
|
13492
13622
|
*/
|
|
13493
13623
|
async addFinding(sessionId, options) {
|
|
13494
13624
|
try {
|
|
13625
|
+
await this.ensureReady();
|
|
13495
13626
|
const { data, error } = await this.supabase.rpc("add_session_finding", {
|
|
13496
13627
|
p_session_id: sessionId,
|
|
13497
13628
|
p_type: options.type,
|
|
@@ -13522,6 +13653,7 @@ var BugBearClient = class {
|
|
|
13522
13653
|
*/
|
|
13523
13654
|
async getSessionFindings(sessionId) {
|
|
13524
13655
|
try {
|
|
13656
|
+
await this.ensureReady();
|
|
13525
13657
|
const { data, error } = await this.supabase.from("qa_findings").select("*").eq("session_id", sessionId).order("created_at", { ascending: true }).limit(100);
|
|
13526
13658
|
if (error) {
|
|
13527
13659
|
console.error("BugBear: Failed to fetch findings", formatPgError(error));
|
|
@@ -13538,6 +13670,7 @@ var BugBearClient = class {
|
|
|
13538
13670
|
*/
|
|
13539
13671
|
async convertFindingToBug(findingId) {
|
|
13540
13672
|
try {
|
|
13673
|
+
await this.ensureReady();
|
|
13541
13674
|
const { data, error } = await this.supabase.rpc("convert_finding_to_bug", {
|
|
13542
13675
|
p_finding_id: findingId
|
|
13543
13676
|
});
|
|
@@ -13557,6 +13690,7 @@ var BugBearClient = class {
|
|
|
13557
13690
|
*/
|
|
13558
13691
|
async dismissFinding(findingId, reason) {
|
|
13559
13692
|
try {
|
|
13693
|
+
await this.ensureReady();
|
|
13560
13694
|
const { error } = await this.supabase.from("qa_findings").update({
|
|
13561
13695
|
dismissed: true,
|
|
13562
13696
|
dismissed_reason: reason || null,
|
|
@@ -15108,9 +15242,9 @@ function TestListScreen({ nav }) {
|
|
|
15108
15242
|
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.trackBtnText, isActive && { color: track.color, fontWeight: "600" }] }, track.icon, " ", track.name)
|
|
15109
15243
|
);
|
|
15110
15244
|
})), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.sortGroup }, [
|
|
15111
|
-
{ key: "priority", label: "\u2195" },
|
|
15112
|
-
{ key: "recent", label: "\u{1F550}" },
|
|
15113
|
-
{ key: "alpha", label: "
|
|
15245
|
+
{ key: "priority", label: "\u2195 Priority" },
|
|
15246
|
+
{ key: "recent", label: "\u{1F550} Recent" },
|
|
15247
|
+
{ key: "alpha", label: "A-Z" }
|
|
15114
15248
|
].map((s2) => /* @__PURE__ */ import_react6.default.createElement(
|
|
15115
15249
|
import_react_native6.TouchableOpacity,
|
|
15116
15250
|
{
|
|
@@ -16327,8 +16461,9 @@ var styles13 = import_react_native16.StyleSheet.create({
|
|
|
16327
16461
|
// src/widget/screens/IssueListScreen.tsx
|
|
16328
16462
|
var import_react18 = __toESM(require("react"));
|
|
16329
16463
|
var import_react_native17 = require("react-native");
|
|
16464
|
+
var CATEGORIES = ["open", "done", "reopened"];
|
|
16330
16465
|
var CATEGORY_CONFIG = {
|
|
16331
|
-
open: { label: "Open
|
|
16466
|
+
open: { label: "Open", accent: "#f97316", emptyIcon: "\u2705", emptyText: "No open issues" },
|
|
16332
16467
|
done: { label: "Done", accent: "#22c55e", emptyIcon: "\u{1F389}", emptyText: "No completed issues yet" },
|
|
16333
16468
|
reopened: { label: "Reopened", accent: "#ef4444", emptyIcon: "\u{1F44D}", emptyText: "No reopened issues" }
|
|
16334
16469
|
};
|
|
@@ -16338,11 +16473,20 @@ var SEVERITY_COLORS = {
|
|
|
16338
16473
|
medium: "#eab308",
|
|
16339
16474
|
low: "#71717a"
|
|
16340
16475
|
};
|
|
16476
|
+
var SEVERITY_ORDER = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
16341
16477
|
function IssueListScreen({ nav, category }) {
|
|
16342
16478
|
const { client } = useBugBear();
|
|
16479
|
+
const [activeCategory, setActiveCategory] = (0, import_react18.useState)(category);
|
|
16343
16480
|
const [issues, setIssues] = (0, import_react18.useState)([]);
|
|
16344
16481
|
const [loading, setLoading] = (0, import_react18.useState)(true);
|
|
16345
|
-
const
|
|
16482
|
+
const [counts, setCounts] = (0, import_react18.useState)(null);
|
|
16483
|
+
const [sortMode, setSortMode] = (0, import_react18.useState)("severity");
|
|
16484
|
+
const config = CATEGORY_CONFIG[activeCategory];
|
|
16485
|
+
(0, import_react18.useEffect)(() => {
|
|
16486
|
+
if (!client) return;
|
|
16487
|
+
client.getIssueCounts().then(setCounts).catch(() => {
|
|
16488
|
+
});
|
|
16489
|
+
}, [client]);
|
|
16346
16490
|
(0, import_react18.useEffect)(() => {
|
|
16347
16491
|
let cancelled = false;
|
|
16348
16492
|
setLoading(true);
|
|
@@ -16352,7 +16496,7 @@ function IssueListScreen({ nav, category }) {
|
|
|
16352
16496
|
return;
|
|
16353
16497
|
}
|
|
16354
16498
|
try {
|
|
16355
|
-
const data = await client.getIssues(
|
|
16499
|
+
const data = await client.getIssues(activeCategory);
|
|
16356
16500
|
if (!cancelled) {
|
|
16357
16501
|
setIssues(data);
|
|
16358
16502
|
}
|
|
@@ -16367,14 +16511,63 @@ function IssueListScreen({ nav, category }) {
|
|
|
16367
16511
|
return () => {
|
|
16368
16512
|
cancelled = true;
|
|
16369
16513
|
};
|
|
16370
|
-
}, [client,
|
|
16371
|
-
|
|
16372
|
-
|
|
16373
|
-
|
|
16374
|
-
|
|
16375
|
-
|
|
16376
|
-
|
|
16377
|
-
|
|
16514
|
+
}, [client, activeCategory]);
|
|
16515
|
+
const sortedIssues = (0, import_react18.useMemo)(() => {
|
|
16516
|
+
const sorted = [...issues];
|
|
16517
|
+
if (sortMode === "severity") {
|
|
16518
|
+
sorted.sort((a, b) => (SEVERITY_ORDER[a.severity || "low"] ?? 4) - (SEVERITY_ORDER[b.severity || "low"] ?? 4));
|
|
16519
|
+
} else {
|
|
16520
|
+
sorted.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
16521
|
+
}
|
|
16522
|
+
return sorted;
|
|
16523
|
+
}, [issues, sortMode]);
|
|
16524
|
+
return /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, null, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles14.tabBar }, CATEGORIES.map((cat) => {
|
|
16525
|
+
const catConfig = CATEGORY_CONFIG[cat];
|
|
16526
|
+
const isActive = activeCategory === cat;
|
|
16527
|
+
const count = counts?.[cat];
|
|
16528
|
+
return /* @__PURE__ */ import_react18.default.createElement(
|
|
16529
|
+
import_react_native17.TouchableOpacity,
|
|
16530
|
+
{
|
|
16531
|
+
key: cat,
|
|
16532
|
+
style: [
|
|
16533
|
+
styles14.tab,
|
|
16534
|
+
{ borderBottomColor: isActive ? catConfig.accent : "transparent" }
|
|
16535
|
+
],
|
|
16536
|
+
onPress: () => setActiveCategory(cat),
|
|
16537
|
+
activeOpacity: 0.7
|
|
16538
|
+
},
|
|
16539
|
+
/* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: [
|
|
16540
|
+
styles14.tabLabel,
|
|
16541
|
+
isActive && { fontWeight: "600", color: colors.textPrimary }
|
|
16542
|
+
] }, catConfig.label),
|
|
16543
|
+
count !== void 0 && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: [
|
|
16544
|
+
styles14.countBadge,
|
|
16545
|
+
{
|
|
16546
|
+
backgroundColor: isActive ? catConfig.accent + "18" : colors.card
|
|
16547
|
+
}
|
|
16548
|
+
] }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: [
|
|
16549
|
+
styles14.countText,
|
|
16550
|
+
{ color: isActive ? catConfig.accent : colors.textDim }
|
|
16551
|
+
] }, count))
|
|
16552
|
+
);
|
|
16553
|
+
})), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles14.sortRow }, [
|
|
16554
|
+
{ key: "severity", label: "Severity" },
|
|
16555
|
+
{ key: "recent", label: "Recent" }
|
|
16556
|
+
].map((s2) => /* @__PURE__ */ import_react18.default.createElement(
|
|
16557
|
+
import_react_native17.TouchableOpacity,
|
|
16558
|
+
{
|
|
16559
|
+
key: s2.key,
|
|
16560
|
+
style: [
|
|
16561
|
+
styles14.sortBtn,
|
|
16562
|
+
sortMode === s2.key && styles14.sortBtnActive
|
|
16563
|
+
],
|
|
16564
|
+
onPress: () => setSortMode(s2.key)
|
|
16565
|
+
},
|
|
16566
|
+
/* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: [
|
|
16567
|
+
styles14.sortBtnText,
|
|
16568
|
+
sortMode === s2.key && styles14.sortBtnTextActive
|
|
16569
|
+
] }, s2.label)
|
|
16570
|
+
))), loading ? /* @__PURE__ */ import_react18.default.createElement(IssueListScreenSkeleton, null) : sortedIssues.length === 0 ? /* @__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)) : sortedIssues.map((issue) => /* @__PURE__ */ import_react18.default.createElement(
|
|
16378
16571
|
import_react_native17.TouchableOpacity,
|
|
16379
16572
|
{
|
|
16380
16573
|
key: issue.id,
|
|
@@ -16384,11 +16577,69 @@ function IssueListScreen({ nav, category }) {
|
|
|
16384
16577
|
},
|
|
16385
16578
|
/* @__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)),
|
|
16386
16579
|
/* @__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))),
|
|
16387
|
-
|
|
16388
|
-
|
|
16580
|
+
activeCategory === "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)),
|
|
16581
|
+
activeCategory === "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))
|
|
16389
16582
|
)));
|
|
16390
16583
|
}
|
|
16391
16584
|
var styles14 = import_react_native17.StyleSheet.create({
|
|
16585
|
+
tabBar: {
|
|
16586
|
+
flexDirection: "row",
|
|
16587
|
+
borderBottomWidth: 1,
|
|
16588
|
+
borderBottomColor: colors.border,
|
|
16589
|
+
marginBottom: 8
|
|
16590
|
+
},
|
|
16591
|
+
tab: {
|
|
16592
|
+
flex: 1,
|
|
16593
|
+
flexDirection: "row",
|
|
16594
|
+
alignItems: "center",
|
|
16595
|
+
justifyContent: "center",
|
|
16596
|
+
gap: 6,
|
|
16597
|
+
paddingVertical: 8,
|
|
16598
|
+
paddingHorizontal: 4,
|
|
16599
|
+
borderBottomWidth: 2
|
|
16600
|
+
},
|
|
16601
|
+
tabLabel: {
|
|
16602
|
+
fontSize: 12,
|
|
16603
|
+
fontWeight: "400",
|
|
16604
|
+
color: colors.textMuted
|
|
16605
|
+
},
|
|
16606
|
+
countBadge: {
|
|
16607
|
+
borderRadius: 8,
|
|
16608
|
+
paddingHorizontal: 6,
|
|
16609
|
+
paddingVertical: 1,
|
|
16610
|
+
minWidth: 18,
|
|
16611
|
+
alignItems: "center"
|
|
16612
|
+
},
|
|
16613
|
+
countText: {
|
|
16614
|
+
fontSize: 10,
|
|
16615
|
+
fontWeight: "600"
|
|
16616
|
+
},
|
|
16617
|
+
sortRow: {
|
|
16618
|
+
flexDirection: "row",
|
|
16619
|
+
justifyContent: "flex-end",
|
|
16620
|
+
gap: 2,
|
|
16621
|
+
marginBottom: 8
|
|
16622
|
+
},
|
|
16623
|
+
sortBtn: {
|
|
16624
|
+
paddingHorizontal: 8,
|
|
16625
|
+
paddingVertical: 3,
|
|
16626
|
+
borderRadius: 6,
|
|
16627
|
+
borderWidth: 1,
|
|
16628
|
+
borderColor: "transparent"
|
|
16629
|
+
},
|
|
16630
|
+
sortBtnActive: {
|
|
16631
|
+
backgroundColor: colors.card,
|
|
16632
|
+
borderColor: colors.border
|
|
16633
|
+
},
|
|
16634
|
+
sortBtnText: {
|
|
16635
|
+
fontSize: 10,
|
|
16636
|
+
fontWeight: "400",
|
|
16637
|
+
color: colors.textMuted
|
|
16638
|
+
},
|
|
16639
|
+
sortBtnTextActive: {
|
|
16640
|
+
fontWeight: "600",
|
|
16641
|
+
color: colors.textPrimary
|
|
16642
|
+
},
|
|
16392
16643
|
emptyContainer: {
|
|
16393
16644
|
alignItems: "center",
|
|
16394
16645
|
paddingVertical: 40
|
|
@@ -16844,7 +17095,7 @@ function BugBearButton({
|
|
|
16844
17095
|
behavior: import_react_native19.Platform.OS === "ios" ? "padding" : "height",
|
|
16845
17096
|
style: styles16.modalOverlay
|
|
16846
17097
|
},
|
|
16847
|
-
/* @__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.
|
|
17098
|
+
/* @__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.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.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.View, { style: styles16.headerActions }, currentScreen.name !== "HOME" && /* @__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.homeIcon }, "\u2302")), /* @__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(
|
|
16848
17099
|
import_react_native19.ScrollView,
|
|
16849
17100
|
{
|
|
16850
17101
|
style: styles16.content,
|
|
@@ -16945,11 +17196,6 @@ var styles16 = import_react_native19.StyleSheet.create({
|
|
|
16945
17196
|
flex: 1,
|
|
16946
17197
|
textAlign: "center"
|
|
16947
17198
|
},
|
|
16948
|
-
headerNavRow: {
|
|
16949
|
-
flexDirection: "row",
|
|
16950
|
-
alignItems: "center",
|
|
16951
|
-
gap: 8
|
|
16952
|
-
},
|
|
16953
17199
|
backButton: {
|
|
16954
17200
|
paddingVertical: 2,
|
|
16955
17201
|
paddingRight: 4
|
|
@@ -16959,12 +17205,25 @@ var styles16 = import_react_native19.StyleSheet.create({
|
|
|
16959
17205
|
color: colors.blue,
|
|
16960
17206
|
fontWeight: "500"
|
|
16961
17207
|
},
|
|
17208
|
+
headerActions: {
|
|
17209
|
+
flexDirection: "row",
|
|
17210
|
+
alignItems: "center",
|
|
17211
|
+
gap: 6
|
|
17212
|
+
},
|
|
16962
17213
|
homeButton: {
|
|
16963
|
-
|
|
16964
|
-
|
|
17214
|
+
width: 28,
|
|
17215
|
+
height: 28,
|
|
17216
|
+
borderRadius: 14,
|
|
17217
|
+
backgroundColor: "#1e3a5f",
|
|
17218
|
+
borderWidth: 1,
|
|
17219
|
+
borderColor: "rgba(59, 130, 246, 0.25)",
|
|
17220
|
+
justifyContent: "center",
|
|
17221
|
+
alignItems: "center"
|
|
16965
17222
|
},
|
|
16966
|
-
|
|
16967
|
-
fontSize: 16
|
|
17223
|
+
homeIcon: {
|
|
17224
|
+
fontSize: 16,
|
|
17225
|
+
color: "#60a5fa",
|
|
17226
|
+
marginTop: -1
|
|
16968
17227
|
},
|
|
16969
17228
|
closeButton: {
|
|
16970
17229
|
width: 32,
|
package/dist/index.mjs
CHANGED
|
@@ -11507,6 +11507,7 @@ var ContextCaptureManager = class {
|
|
|
11507
11507
|
this.networkRequests = [];
|
|
11508
11508
|
this.navigationHistory = [];
|
|
11509
11509
|
this.originalConsole = {};
|
|
11510
|
+
this.fetchHost = null;
|
|
11510
11511
|
this.isCapturing = false;
|
|
11511
11512
|
}
|
|
11512
11513
|
/**
|
|
@@ -11529,8 +11530,9 @@ var ContextCaptureManager = class {
|
|
|
11529
11530
|
if (this.originalConsole.warn) console.warn = this.originalConsole.warn;
|
|
11530
11531
|
if (this.originalConsole.error) console.error = this.originalConsole.error;
|
|
11531
11532
|
if (this.originalConsole.info) console.info = this.originalConsole.info;
|
|
11532
|
-
if (this.originalFetch &&
|
|
11533
|
-
|
|
11533
|
+
if (this.originalFetch && this.fetchHost) {
|
|
11534
|
+
this.fetchHost.fetch = this.originalFetch;
|
|
11535
|
+
this.fetchHost = null;
|
|
11534
11536
|
}
|
|
11535
11537
|
if (typeof window !== "undefined" && typeof history !== "undefined") {
|
|
11536
11538
|
if (this.originalPushState) {
|
|
@@ -11639,15 +11641,19 @@ var ContextCaptureManager = class {
|
|
|
11639
11641
|
});
|
|
11640
11642
|
}
|
|
11641
11643
|
captureFetch() {
|
|
11642
|
-
if (typeof
|
|
11643
|
-
|
|
11644
|
+
if (typeof fetch === "undefined") return;
|
|
11645
|
+
const host = typeof window !== "undefined" && typeof window.fetch === "function" ? window : typeof globalThis !== "undefined" && typeof globalThis.fetch === "function" ? globalThis : null;
|
|
11646
|
+
if (!host) return;
|
|
11647
|
+
const canCloneResponse = typeof document !== "undefined";
|
|
11648
|
+
this.fetchHost = host;
|
|
11649
|
+
this.originalFetch = host.fetch;
|
|
11644
11650
|
const self2 = this;
|
|
11645
|
-
|
|
11651
|
+
host.fetch = async function(input, init) {
|
|
11646
11652
|
const startTime = Date.now();
|
|
11647
11653
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
11648
11654
|
const method = init?.method || "GET";
|
|
11649
11655
|
try {
|
|
11650
|
-
const response = await self2.originalFetch.call(
|
|
11656
|
+
const response = await self2.originalFetch.call(host, input, init);
|
|
11651
11657
|
const requestEntry = {
|
|
11652
11658
|
method,
|
|
11653
11659
|
url: url.slice(0, 200),
|
|
@@ -11656,7 +11662,7 @@ var ContextCaptureManager = class {
|
|
|
11656
11662
|
duration: Date.now() - startTime,
|
|
11657
11663
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11658
11664
|
};
|
|
11659
|
-
if (response.status >= 400) {
|
|
11665
|
+
if (canCloneResponse && response.status >= 400) {
|
|
11660
11666
|
try {
|
|
11661
11667
|
const cloned = response.clone();
|
|
11662
11668
|
const body = await cloned.text();
|
|
@@ -11900,29 +11906,140 @@ var formatPgError = (e) => {
|
|
|
11900
11906
|
const { message, code, details, hint } = e;
|
|
11901
11907
|
return { message, code, details, hint };
|
|
11902
11908
|
};
|
|
11909
|
+
var DEFAULT_API_BASE_URL = "https://app.bugbear.ai";
|
|
11910
|
+
var CONFIG_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
11911
|
+
var CONFIG_CACHE_PREFIX = "bugbear_config_";
|
|
11903
11912
|
var BugBearClient = class {
|
|
11904
11913
|
constructor(config) {
|
|
11905
11914
|
this.navigationHistory = [];
|
|
11906
11915
|
this.reportSubmitInFlight = false;
|
|
11907
11916
|
this._queue = null;
|
|
11908
11917
|
this.realtimeChannels = [];
|
|
11909
|
-
|
|
11910
|
-
|
|
11911
|
-
}
|
|
11912
|
-
if (!config.supabaseAnonKey) {
|
|
11913
|
-
throw new Error("BugBear: supabaseAnonKey is required. Get it from your BugBear project settings.");
|
|
11914
|
-
}
|
|
11918
|
+
this.initialized = false;
|
|
11919
|
+
this.initError = null;
|
|
11915
11920
|
this.config = config;
|
|
11916
|
-
|
|
11917
|
-
|
|
11921
|
+
if (config.apiKey) {
|
|
11922
|
+
this.pendingInit = this.resolveFromApiKey(config.apiKey);
|
|
11923
|
+
} else if (config.supabaseUrl && config.supabaseAnonKey) {
|
|
11924
|
+
if (!config.projectId) {
|
|
11925
|
+
throw new Error(
|
|
11926
|
+
"BugBear: projectId is required when using explicit Supabase credentials. Tip: Use apiKey instead for simpler setup \u2014 it resolves everything automatically."
|
|
11927
|
+
);
|
|
11928
|
+
}
|
|
11929
|
+
this.supabase = createClient(config.supabaseUrl, config.supabaseAnonKey);
|
|
11930
|
+
this.initialized = true;
|
|
11931
|
+
this.pendingInit = Promise.resolve();
|
|
11932
|
+
this.initOfflineQueue();
|
|
11933
|
+
} else {
|
|
11934
|
+
throw new Error(
|
|
11935
|
+
"BugBear: Missing configuration. Provide either:\n \u2022 apiKey (recommended) \u2014 resolves everything automatically\n \u2022 projectId + supabaseUrl + supabaseAnonKey \u2014 explicit credentials\n\nGet your API key at https://app.bugbear.ai/settings/projects"
|
|
11936
|
+
);
|
|
11937
|
+
}
|
|
11938
|
+
}
|
|
11939
|
+
/** Whether the client is ready for requests. */
|
|
11940
|
+
get isReady() {
|
|
11941
|
+
return this.initialized;
|
|
11942
|
+
}
|
|
11943
|
+
/** Wait until the client is ready. Throws if initialization failed. */
|
|
11944
|
+
async ready() {
|
|
11945
|
+
await this.pendingInit;
|
|
11946
|
+
if (this.initError) throw this.initError;
|
|
11947
|
+
}
|
|
11948
|
+
/**
|
|
11949
|
+
* Resolve Supabase credentials from a BugBear API key.
|
|
11950
|
+
* Checks localStorage cache first, falls back to /api/v1/config.
|
|
11951
|
+
*/
|
|
11952
|
+
async resolveFromApiKey(apiKey) {
|
|
11953
|
+
try {
|
|
11954
|
+
const cached = this.readConfigCache(apiKey);
|
|
11955
|
+
if (cached) {
|
|
11956
|
+
this.applyResolvedConfig(cached);
|
|
11957
|
+
return;
|
|
11958
|
+
}
|
|
11959
|
+
const baseUrl = (this.config.apiBaseUrl || DEFAULT_API_BASE_URL).replace(/\/$/, "");
|
|
11960
|
+
const response = await fetch(`${baseUrl}/api/v1/config`, {
|
|
11961
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
11962
|
+
});
|
|
11963
|
+
if (!response.ok) {
|
|
11964
|
+
const body = await response.json().catch(() => ({}));
|
|
11965
|
+
const message = body.error || `HTTP ${response.status}`;
|
|
11966
|
+
throw new Error(
|
|
11967
|
+
`BugBear: Invalid API key \u2014 ${message}. Get yours at https://app.bugbear.ai/settings/projects`
|
|
11968
|
+
);
|
|
11969
|
+
}
|
|
11970
|
+
const data = await response.json();
|
|
11971
|
+
this.writeConfigCache(apiKey, data);
|
|
11972
|
+
this.applyResolvedConfig(data);
|
|
11973
|
+
} catch (err) {
|
|
11974
|
+
this.initError = err instanceof Error ? err : new Error(String(err));
|
|
11975
|
+
this.config.onError?.(this.initError, { context: "apikey_resolution_failed" });
|
|
11976
|
+
throw this.initError;
|
|
11977
|
+
}
|
|
11978
|
+
}
|
|
11979
|
+
/** Apply resolved credentials and create the Supabase client. */
|
|
11980
|
+
applyResolvedConfig(resolved) {
|
|
11981
|
+
this.config = {
|
|
11982
|
+
...this.config,
|
|
11983
|
+
projectId: resolved.projectId,
|
|
11984
|
+
supabaseUrl: resolved.supabaseUrl,
|
|
11985
|
+
supabaseAnonKey: resolved.supabaseAnonKey
|
|
11986
|
+
};
|
|
11987
|
+
this.supabase = createClient(resolved.supabaseUrl, resolved.supabaseAnonKey);
|
|
11988
|
+
this.initialized = true;
|
|
11989
|
+
this.initOfflineQueue();
|
|
11990
|
+
}
|
|
11991
|
+
/** Initialize offline queue if configured. Shared by both init paths. */
|
|
11992
|
+
initOfflineQueue() {
|
|
11993
|
+
if (this.config.offlineQueue?.enabled) {
|
|
11918
11994
|
this._queue = new OfflineQueue({
|
|
11919
11995
|
enabled: true,
|
|
11920
|
-
maxItems: config.offlineQueue.maxItems,
|
|
11921
|
-
maxRetries: config.offlineQueue.maxRetries
|
|
11996
|
+
maxItems: this.config.offlineQueue.maxItems,
|
|
11997
|
+
maxRetries: this.config.offlineQueue.maxRetries
|
|
11922
11998
|
});
|
|
11923
11999
|
this.registerQueueHandlers();
|
|
11924
12000
|
}
|
|
11925
12001
|
}
|
|
12002
|
+
/** Read cached config from localStorage if available and not expired. */
|
|
12003
|
+
readConfigCache(apiKey) {
|
|
12004
|
+
if (typeof localStorage === "undefined") return null;
|
|
12005
|
+
try {
|
|
12006
|
+
const key = CONFIG_CACHE_PREFIX + this.hashKey(apiKey);
|
|
12007
|
+
const raw = localStorage.getItem(key);
|
|
12008
|
+
if (!raw) return null;
|
|
12009
|
+
const cached = JSON.parse(raw);
|
|
12010
|
+
if (Date.now() - cached.cachedAt > CONFIG_CACHE_TTL_MS) {
|
|
12011
|
+
localStorage.removeItem(key);
|
|
12012
|
+
return null;
|
|
12013
|
+
}
|
|
12014
|
+
return cached;
|
|
12015
|
+
} catch {
|
|
12016
|
+
return null;
|
|
12017
|
+
}
|
|
12018
|
+
}
|
|
12019
|
+
/** Write resolved config to localStorage cache. */
|
|
12020
|
+
writeConfigCache(apiKey, data) {
|
|
12021
|
+
if (typeof localStorage === "undefined") return;
|
|
12022
|
+
try {
|
|
12023
|
+
const key = CONFIG_CACHE_PREFIX + this.hashKey(apiKey);
|
|
12024
|
+
const cached = { ...data, cachedAt: Date.now() };
|
|
12025
|
+
localStorage.setItem(key, JSON.stringify(cached));
|
|
12026
|
+
} catch {
|
|
12027
|
+
}
|
|
12028
|
+
}
|
|
12029
|
+
/** Simple string hash for cache keys — avoids storing raw API keys. */
|
|
12030
|
+
hashKey(apiKey) {
|
|
12031
|
+
let hash = 0;
|
|
12032
|
+
for (let i = 0; i < apiKey.length; i++) {
|
|
12033
|
+
hash = (hash << 5) - hash + apiKey.charCodeAt(i) | 0;
|
|
12034
|
+
}
|
|
12035
|
+
return hash.toString(36);
|
|
12036
|
+
}
|
|
12037
|
+
/** Ensure the client is initialized before making requests. */
|
|
12038
|
+
async ensureReady() {
|
|
12039
|
+
if (this.initialized) return;
|
|
12040
|
+
await this.pendingInit;
|
|
12041
|
+
if (this.initError) throw this.initError;
|
|
12042
|
+
}
|
|
11926
12043
|
// ── Offline Queue ─────────────────────────────────────────
|
|
11927
12044
|
/**
|
|
11928
12045
|
* Access the offline queue (if enabled).
|
|
@@ -12078,6 +12195,7 @@ var BugBearClient = class {
|
|
|
12078
12195
|
* Get current user info from host app or BugBear's own auth
|
|
12079
12196
|
*/
|
|
12080
12197
|
async getCurrentUserInfo() {
|
|
12198
|
+
await this.ensureReady();
|
|
12081
12199
|
if (this.config.getCurrentUser) {
|
|
12082
12200
|
return await this.config.getCurrentUser();
|
|
12083
12201
|
}
|
|
@@ -12297,6 +12415,7 @@ var BugBearClient = class {
|
|
|
12297
12415
|
*/
|
|
12298
12416
|
async getAssignment(assignmentId) {
|
|
12299
12417
|
try {
|
|
12418
|
+
await this.ensureReady();
|
|
12300
12419
|
const { data, error } = await this.supabase.from("test_assignments").select(`
|
|
12301
12420
|
id,
|
|
12302
12421
|
status,
|
|
@@ -12368,6 +12487,7 @@ var BugBearClient = class {
|
|
|
12368
12487
|
*/
|
|
12369
12488
|
async updateAssignmentStatus(assignmentId, status, options) {
|
|
12370
12489
|
try {
|
|
12490
|
+
await this.ensureReady();
|
|
12371
12491
|
const { data: currentAssignment, error: fetchError } = await this.supabase.from("test_assignments").select("status, started_at").eq("id", assignmentId).single();
|
|
12372
12492
|
if (fetchError || !currentAssignment) {
|
|
12373
12493
|
console.error("BugBear: Assignment not found", {
|
|
@@ -12454,6 +12574,7 @@ var BugBearClient = class {
|
|
|
12454
12574
|
*/
|
|
12455
12575
|
async reopenAssignment(assignmentId) {
|
|
12456
12576
|
try {
|
|
12577
|
+
await this.ensureReady();
|
|
12457
12578
|
const { data: current, error: fetchError } = await this.supabase.from("test_assignments").select("status").eq("id", assignmentId).single();
|
|
12458
12579
|
if (fetchError || !current) {
|
|
12459
12580
|
return { success: false, error: "Assignment not found" };
|
|
@@ -12493,6 +12614,7 @@ var BugBearClient = class {
|
|
|
12493
12614
|
actualNotes = notes;
|
|
12494
12615
|
}
|
|
12495
12616
|
try {
|
|
12617
|
+
await this.ensureReady();
|
|
12496
12618
|
const updateData = {
|
|
12497
12619
|
status: "skipped",
|
|
12498
12620
|
skip_reason: actualReason,
|
|
@@ -12662,6 +12784,7 @@ var BugBearClient = class {
|
|
|
12662
12784
|
*/
|
|
12663
12785
|
async getTesterInfo() {
|
|
12664
12786
|
try {
|
|
12787
|
+
await this.ensureReady();
|
|
12665
12788
|
const userInfo = await this.getCurrentUserInfo();
|
|
12666
12789
|
if (!userInfo?.email) return null;
|
|
12667
12790
|
if (!this.isValidEmail(userInfo.email)) {
|
|
@@ -12953,6 +13076,7 @@ var BugBearClient = class {
|
|
|
12953
13076
|
*/
|
|
12954
13077
|
async isQAEnabled() {
|
|
12955
13078
|
try {
|
|
13079
|
+
await this.ensureReady();
|
|
12956
13080
|
const { data, error } = await this.supabase.rpc("check_qa_enabled", {
|
|
12957
13081
|
p_project_id: this.config.projectId
|
|
12958
13082
|
});
|
|
@@ -12984,6 +13108,7 @@ var BugBearClient = class {
|
|
|
12984
13108
|
*/
|
|
12985
13109
|
async uploadScreenshot(file, filename, bucket = "screenshots") {
|
|
12986
13110
|
try {
|
|
13111
|
+
await this.ensureReady();
|
|
12987
13112
|
const contentType = file.type || "image/png";
|
|
12988
13113
|
const ext = contentType.includes("png") ? "png" : "jpg";
|
|
12989
13114
|
const name = filename || `screenshot-${Date.now()}.${ext}`;
|
|
@@ -13024,6 +13149,7 @@ var BugBearClient = class {
|
|
|
13024
13149
|
*/
|
|
13025
13150
|
async uploadImageFromUri(uri, filename, bucket = "screenshots") {
|
|
13026
13151
|
try {
|
|
13152
|
+
await this.ensureReady();
|
|
13027
13153
|
const response = await fetch(uri);
|
|
13028
13154
|
const blob = await response.blob();
|
|
13029
13155
|
const contentType = blob.type || "image/jpeg";
|
|
@@ -13118,6 +13244,7 @@ var BugBearClient = class {
|
|
|
13118
13244
|
*/
|
|
13119
13245
|
async getFixRequests(options) {
|
|
13120
13246
|
try {
|
|
13247
|
+
await this.ensureReady();
|
|
13121
13248
|
let query = this.supabase.from("fix_requests").select("*").eq("project_id", this.config.projectId).order("created_at", { ascending: false }).limit(options?.limit || 20);
|
|
13122
13249
|
if (options?.status) {
|
|
13123
13250
|
query = query.eq("status", options.status);
|
|
@@ -13189,6 +13316,7 @@ var BugBearClient = class {
|
|
|
13189
13316
|
*/
|
|
13190
13317
|
async getThreadMessages(threadId) {
|
|
13191
13318
|
try {
|
|
13319
|
+
await this.ensureReady();
|
|
13192
13320
|
const { data, error } = await this.supabase.from("discussion_messages").select(`
|
|
13193
13321
|
id,
|
|
13194
13322
|
thread_id,
|
|
@@ -13391,6 +13519,7 @@ var BugBearClient = class {
|
|
|
13391
13519
|
*/
|
|
13392
13520
|
async endSession(sessionId, options = {}) {
|
|
13393
13521
|
try {
|
|
13522
|
+
await this.ensureReady();
|
|
13394
13523
|
const { data, error } = await this.supabase.rpc("end_qa_session", {
|
|
13395
13524
|
p_session_id: sessionId,
|
|
13396
13525
|
p_notes: options.notes || null,
|
|
@@ -13428,6 +13557,7 @@ var BugBearClient = class {
|
|
|
13428
13557
|
*/
|
|
13429
13558
|
async getSession(sessionId) {
|
|
13430
13559
|
try {
|
|
13560
|
+
await this.ensureReady();
|
|
13431
13561
|
const { data, error } = await this.supabase.from("qa_sessions").select("*").eq("id", sessionId).single();
|
|
13432
13562
|
if (error || !data) return null;
|
|
13433
13563
|
return this.transformSession(data);
|
|
@@ -13459,6 +13589,7 @@ var BugBearClient = class {
|
|
|
13459
13589
|
*/
|
|
13460
13590
|
async addFinding(sessionId, options) {
|
|
13461
13591
|
try {
|
|
13592
|
+
await this.ensureReady();
|
|
13462
13593
|
const { data, error } = await this.supabase.rpc("add_session_finding", {
|
|
13463
13594
|
p_session_id: sessionId,
|
|
13464
13595
|
p_type: options.type,
|
|
@@ -13489,6 +13620,7 @@ var BugBearClient = class {
|
|
|
13489
13620
|
*/
|
|
13490
13621
|
async getSessionFindings(sessionId) {
|
|
13491
13622
|
try {
|
|
13623
|
+
await this.ensureReady();
|
|
13492
13624
|
const { data, error } = await this.supabase.from("qa_findings").select("*").eq("session_id", sessionId).order("created_at", { ascending: true }).limit(100);
|
|
13493
13625
|
if (error) {
|
|
13494
13626
|
console.error("BugBear: Failed to fetch findings", formatPgError(error));
|
|
@@ -13505,6 +13637,7 @@ var BugBearClient = class {
|
|
|
13505
13637
|
*/
|
|
13506
13638
|
async convertFindingToBug(findingId) {
|
|
13507
13639
|
try {
|
|
13640
|
+
await this.ensureReady();
|
|
13508
13641
|
const { data, error } = await this.supabase.rpc("convert_finding_to_bug", {
|
|
13509
13642
|
p_finding_id: findingId
|
|
13510
13643
|
});
|
|
@@ -13524,6 +13657,7 @@ var BugBearClient = class {
|
|
|
13524
13657
|
*/
|
|
13525
13658
|
async dismissFinding(findingId, reason) {
|
|
13526
13659
|
try {
|
|
13660
|
+
await this.ensureReady();
|
|
13527
13661
|
const { error } = await this.supabase.from("qa_findings").update({
|
|
13528
13662
|
dismissed: true,
|
|
13529
13663
|
dismissed_reason: reason || null,
|
|
@@ -15090,9 +15224,9 @@ function TestListScreen({ nav }) {
|
|
|
15090
15224
|
/* @__PURE__ */ React5.createElement(Text3, { style: [styles3.trackBtnText, isActive && { color: track.color, fontWeight: "600" }] }, track.icon, " ", track.name)
|
|
15091
15225
|
);
|
|
15092
15226
|
})), /* @__PURE__ */ React5.createElement(View4, { style: styles3.sortGroup }, [
|
|
15093
|
-
{ key: "priority", label: "\u2195" },
|
|
15094
|
-
{ key: "recent", label: "\u{1F550}" },
|
|
15095
|
-
{ key: "alpha", label: "
|
|
15227
|
+
{ key: "priority", label: "\u2195 Priority" },
|
|
15228
|
+
{ key: "recent", label: "\u{1F550} Recent" },
|
|
15229
|
+
{ key: "alpha", label: "A-Z" }
|
|
15096
15230
|
].map((s2) => /* @__PURE__ */ React5.createElement(
|
|
15097
15231
|
TouchableOpacity3,
|
|
15098
15232
|
{
|
|
@@ -16307,10 +16441,11 @@ var styles13 = StyleSheet15.create({
|
|
|
16307
16441
|
});
|
|
16308
16442
|
|
|
16309
16443
|
// src/widget/screens/IssueListScreen.tsx
|
|
16310
|
-
import React16, { useState as useState11, useEffect as useEffect10 } from "react";
|
|
16444
|
+
import React16, { useState as useState11, useEffect as useEffect10, useMemo as useMemo3 } from "react";
|
|
16311
16445
|
import { View as View15, Text as Text14, TouchableOpacity as TouchableOpacity13, StyleSheet as StyleSheet16 } from "react-native";
|
|
16446
|
+
var CATEGORIES = ["open", "done", "reopened"];
|
|
16312
16447
|
var CATEGORY_CONFIG = {
|
|
16313
|
-
open: { label: "Open
|
|
16448
|
+
open: { label: "Open", accent: "#f97316", emptyIcon: "\u2705", emptyText: "No open issues" },
|
|
16314
16449
|
done: { label: "Done", accent: "#22c55e", emptyIcon: "\u{1F389}", emptyText: "No completed issues yet" },
|
|
16315
16450
|
reopened: { label: "Reopened", accent: "#ef4444", emptyIcon: "\u{1F44D}", emptyText: "No reopened issues" }
|
|
16316
16451
|
};
|
|
@@ -16320,11 +16455,20 @@ var SEVERITY_COLORS = {
|
|
|
16320
16455
|
medium: "#eab308",
|
|
16321
16456
|
low: "#71717a"
|
|
16322
16457
|
};
|
|
16458
|
+
var SEVERITY_ORDER = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
16323
16459
|
function IssueListScreen({ nav, category }) {
|
|
16324
16460
|
const { client } = useBugBear();
|
|
16461
|
+
const [activeCategory, setActiveCategory] = useState11(category);
|
|
16325
16462
|
const [issues, setIssues] = useState11([]);
|
|
16326
16463
|
const [loading, setLoading] = useState11(true);
|
|
16327
|
-
const
|
|
16464
|
+
const [counts, setCounts] = useState11(null);
|
|
16465
|
+
const [sortMode, setSortMode] = useState11("severity");
|
|
16466
|
+
const config = CATEGORY_CONFIG[activeCategory];
|
|
16467
|
+
useEffect10(() => {
|
|
16468
|
+
if (!client) return;
|
|
16469
|
+
client.getIssueCounts().then(setCounts).catch(() => {
|
|
16470
|
+
});
|
|
16471
|
+
}, [client]);
|
|
16328
16472
|
useEffect10(() => {
|
|
16329
16473
|
let cancelled = false;
|
|
16330
16474
|
setLoading(true);
|
|
@@ -16334,7 +16478,7 @@ function IssueListScreen({ nav, category }) {
|
|
|
16334
16478
|
return;
|
|
16335
16479
|
}
|
|
16336
16480
|
try {
|
|
16337
|
-
const data = await client.getIssues(
|
|
16481
|
+
const data = await client.getIssues(activeCategory);
|
|
16338
16482
|
if (!cancelled) {
|
|
16339
16483
|
setIssues(data);
|
|
16340
16484
|
}
|
|
@@ -16349,14 +16493,63 @@ function IssueListScreen({ nav, category }) {
|
|
|
16349
16493
|
return () => {
|
|
16350
16494
|
cancelled = true;
|
|
16351
16495
|
};
|
|
16352
|
-
}, [client,
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
|
|
16357
|
-
|
|
16358
|
-
|
|
16359
|
-
|
|
16496
|
+
}, [client, activeCategory]);
|
|
16497
|
+
const sortedIssues = useMemo3(() => {
|
|
16498
|
+
const sorted = [...issues];
|
|
16499
|
+
if (sortMode === "severity") {
|
|
16500
|
+
sorted.sort((a, b) => (SEVERITY_ORDER[a.severity || "low"] ?? 4) - (SEVERITY_ORDER[b.severity || "low"] ?? 4));
|
|
16501
|
+
} else {
|
|
16502
|
+
sorted.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
16503
|
+
}
|
|
16504
|
+
return sorted;
|
|
16505
|
+
}, [issues, sortMode]);
|
|
16506
|
+
return /* @__PURE__ */ React16.createElement(View15, null, /* @__PURE__ */ React16.createElement(View15, { style: styles14.tabBar }, CATEGORIES.map((cat) => {
|
|
16507
|
+
const catConfig = CATEGORY_CONFIG[cat];
|
|
16508
|
+
const isActive = activeCategory === cat;
|
|
16509
|
+
const count = counts?.[cat];
|
|
16510
|
+
return /* @__PURE__ */ React16.createElement(
|
|
16511
|
+
TouchableOpacity13,
|
|
16512
|
+
{
|
|
16513
|
+
key: cat,
|
|
16514
|
+
style: [
|
|
16515
|
+
styles14.tab,
|
|
16516
|
+
{ borderBottomColor: isActive ? catConfig.accent : "transparent" }
|
|
16517
|
+
],
|
|
16518
|
+
onPress: () => setActiveCategory(cat),
|
|
16519
|
+
activeOpacity: 0.7
|
|
16520
|
+
},
|
|
16521
|
+
/* @__PURE__ */ React16.createElement(Text14, { style: [
|
|
16522
|
+
styles14.tabLabel,
|
|
16523
|
+
isActive && { fontWeight: "600", color: colors.textPrimary }
|
|
16524
|
+
] }, catConfig.label),
|
|
16525
|
+
count !== void 0 && /* @__PURE__ */ React16.createElement(View15, { style: [
|
|
16526
|
+
styles14.countBadge,
|
|
16527
|
+
{
|
|
16528
|
+
backgroundColor: isActive ? catConfig.accent + "18" : colors.card
|
|
16529
|
+
}
|
|
16530
|
+
] }, /* @__PURE__ */ React16.createElement(Text14, { style: [
|
|
16531
|
+
styles14.countText,
|
|
16532
|
+
{ color: isActive ? catConfig.accent : colors.textDim }
|
|
16533
|
+
] }, count))
|
|
16534
|
+
);
|
|
16535
|
+
})), /* @__PURE__ */ React16.createElement(View15, { style: styles14.sortRow }, [
|
|
16536
|
+
{ key: "severity", label: "Severity" },
|
|
16537
|
+
{ key: "recent", label: "Recent" }
|
|
16538
|
+
].map((s2) => /* @__PURE__ */ React16.createElement(
|
|
16539
|
+
TouchableOpacity13,
|
|
16540
|
+
{
|
|
16541
|
+
key: s2.key,
|
|
16542
|
+
style: [
|
|
16543
|
+
styles14.sortBtn,
|
|
16544
|
+
sortMode === s2.key && styles14.sortBtnActive
|
|
16545
|
+
],
|
|
16546
|
+
onPress: () => setSortMode(s2.key)
|
|
16547
|
+
},
|
|
16548
|
+
/* @__PURE__ */ React16.createElement(Text14, { style: [
|
|
16549
|
+
styles14.sortBtnText,
|
|
16550
|
+
sortMode === s2.key && styles14.sortBtnTextActive
|
|
16551
|
+
] }, s2.label)
|
|
16552
|
+
))), loading ? /* @__PURE__ */ React16.createElement(IssueListScreenSkeleton, null) : sortedIssues.length === 0 ? /* @__PURE__ */ React16.createElement(View15, { style: styles14.emptyContainer }, /* @__PURE__ */ React16.createElement(Text14, { style: styles14.emptyIcon }, config.emptyIcon), /* @__PURE__ */ React16.createElement(Text14, { style: styles14.emptyText }, config.emptyText)) : sortedIssues.map((issue) => /* @__PURE__ */ React16.createElement(
|
|
16360
16553
|
TouchableOpacity13,
|
|
16361
16554
|
{
|
|
16362
16555
|
key: issue.id,
|
|
@@ -16366,11 +16559,69 @@ function IssueListScreen({ nav, category }) {
|
|
|
16366
16559
|
},
|
|
16367
16560
|
/* @__PURE__ */ React16.createElement(View15, { style: styles14.topRow }, issue.severity && /* @__PURE__ */ React16.createElement(View15, { style: [styles14.severityDot, { backgroundColor: SEVERITY_COLORS[issue.severity] || colors.textDim }] }), /* @__PURE__ */ React16.createElement(Text14, { style: styles14.issueTitle, numberOfLines: 1 }, issue.title)),
|
|
16368
16561
|
/* @__PURE__ */ React16.createElement(View15, { style: styles14.bottomRow }, issue.route && /* @__PURE__ */ React16.createElement(Text14, { style: styles14.routeText, numberOfLines: 1 }, issue.route), /* @__PURE__ */ React16.createElement(Text14, { style: styles14.timeText }, formatRelativeTime(issue.updatedAt))),
|
|
16369
|
-
|
|
16370
|
-
|
|
16562
|
+
activeCategory === "done" && issue.verifiedByName && /* @__PURE__ */ React16.createElement(View15, { style: styles14.verifiedBadge }, /* @__PURE__ */ React16.createElement(Text14, { style: styles14.verifiedBadgeText }, "\u2714", " Verified by ", issue.verifiedByName)),
|
|
16563
|
+
activeCategory === "reopened" && issue.originalBugTitle && /* @__PURE__ */ React16.createElement(View15, { style: styles14.reopenedBadge }, /* @__PURE__ */ React16.createElement(Text14, { style: styles14.reopenedBadgeText, numberOfLines: 1 }, "\u{1F504}", " Retest of: ", issue.originalBugTitle))
|
|
16371
16564
|
)));
|
|
16372
16565
|
}
|
|
16373
16566
|
var styles14 = StyleSheet16.create({
|
|
16567
|
+
tabBar: {
|
|
16568
|
+
flexDirection: "row",
|
|
16569
|
+
borderBottomWidth: 1,
|
|
16570
|
+
borderBottomColor: colors.border,
|
|
16571
|
+
marginBottom: 8
|
|
16572
|
+
},
|
|
16573
|
+
tab: {
|
|
16574
|
+
flex: 1,
|
|
16575
|
+
flexDirection: "row",
|
|
16576
|
+
alignItems: "center",
|
|
16577
|
+
justifyContent: "center",
|
|
16578
|
+
gap: 6,
|
|
16579
|
+
paddingVertical: 8,
|
|
16580
|
+
paddingHorizontal: 4,
|
|
16581
|
+
borderBottomWidth: 2
|
|
16582
|
+
},
|
|
16583
|
+
tabLabel: {
|
|
16584
|
+
fontSize: 12,
|
|
16585
|
+
fontWeight: "400",
|
|
16586
|
+
color: colors.textMuted
|
|
16587
|
+
},
|
|
16588
|
+
countBadge: {
|
|
16589
|
+
borderRadius: 8,
|
|
16590
|
+
paddingHorizontal: 6,
|
|
16591
|
+
paddingVertical: 1,
|
|
16592
|
+
minWidth: 18,
|
|
16593
|
+
alignItems: "center"
|
|
16594
|
+
},
|
|
16595
|
+
countText: {
|
|
16596
|
+
fontSize: 10,
|
|
16597
|
+
fontWeight: "600"
|
|
16598
|
+
},
|
|
16599
|
+
sortRow: {
|
|
16600
|
+
flexDirection: "row",
|
|
16601
|
+
justifyContent: "flex-end",
|
|
16602
|
+
gap: 2,
|
|
16603
|
+
marginBottom: 8
|
|
16604
|
+
},
|
|
16605
|
+
sortBtn: {
|
|
16606
|
+
paddingHorizontal: 8,
|
|
16607
|
+
paddingVertical: 3,
|
|
16608
|
+
borderRadius: 6,
|
|
16609
|
+
borderWidth: 1,
|
|
16610
|
+
borderColor: "transparent"
|
|
16611
|
+
},
|
|
16612
|
+
sortBtnActive: {
|
|
16613
|
+
backgroundColor: colors.card,
|
|
16614
|
+
borderColor: colors.border
|
|
16615
|
+
},
|
|
16616
|
+
sortBtnText: {
|
|
16617
|
+
fontSize: 10,
|
|
16618
|
+
fontWeight: "400",
|
|
16619
|
+
color: colors.textMuted
|
|
16620
|
+
},
|
|
16621
|
+
sortBtnTextActive: {
|
|
16622
|
+
fontWeight: "600",
|
|
16623
|
+
color: colors.textPrimary
|
|
16624
|
+
},
|
|
16374
16625
|
emptyContainer: {
|
|
16375
16626
|
alignItems: "center",
|
|
16376
16627
|
paddingVertical: 40
|
|
@@ -16826,7 +17077,7 @@ function BugBearButton({
|
|
|
16826
17077
|
behavior: Platform5.OS === "ios" ? "padding" : "height",
|
|
16827
17078
|
style: styles16.modalOverlay
|
|
16828
17079
|
},
|
|
16829
|
-
/* @__PURE__ */ React18.createElement(View17, { style: styles16.modalContainer }, /* @__PURE__ */ React18.createElement(View17, { style: styles16.header }, /* @__PURE__ */ React18.createElement(View17, { style: styles16.headerLeft }, canGoBack ? /* @__PURE__ */ React18.createElement(
|
|
17080
|
+
/* @__PURE__ */ React18.createElement(View17, { style: styles16.modalContainer }, /* @__PURE__ */ React18.createElement(View17, { style: styles16.header }, /* @__PURE__ */ React18.createElement(View17, { style: styles16.headerLeft }, canGoBack ? /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: () => nav.pop(), style: styles16.backButton }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.backText }, "\u2190 Back")) : /* @__PURE__ */ React18.createElement(View17, { style: styles16.headerTitleRow }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ React18.createElement(Text16, { style: styles16.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ React18.createElement(View17, { style: styles16.headerActions }, currentScreen.name !== "HOME" && /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: () => nav.reset(), style: styles16.homeButton }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.homeIcon }, "\u2302")), /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: handleClose, style: styles16.closeButton }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.closeText }, "\u2715")))), /* @__PURE__ */ React18.createElement(
|
|
16830
17081
|
ScrollView3,
|
|
16831
17082
|
{
|
|
16832
17083
|
style: styles16.content,
|
|
@@ -16927,11 +17178,6 @@ var styles16 = StyleSheet18.create({
|
|
|
16927
17178
|
flex: 1,
|
|
16928
17179
|
textAlign: "center"
|
|
16929
17180
|
},
|
|
16930
|
-
headerNavRow: {
|
|
16931
|
-
flexDirection: "row",
|
|
16932
|
-
alignItems: "center",
|
|
16933
|
-
gap: 8
|
|
16934
|
-
},
|
|
16935
17181
|
backButton: {
|
|
16936
17182
|
paddingVertical: 2,
|
|
16937
17183
|
paddingRight: 4
|
|
@@ -16941,12 +17187,25 @@ var styles16 = StyleSheet18.create({
|
|
|
16941
17187
|
color: colors.blue,
|
|
16942
17188
|
fontWeight: "500"
|
|
16943
17189
|
},
|
|
17190
|
+
headerActions: {
|
|
17191
|
+
flexDirection: "row",
|
|
17192
|
+
alignItems: "center",
|
|
17193
|
+
gap: 6
|
|
17194
|
+
},
|
|
16944
17195
|
homeButton: {
|
|
16945
|
-
|
|
16946
|
-
|
|
17196
|
+
width: 28,
|
|
17197
|
+
height: 28,
|
|
17198
|
+
borderRadius: 14,
|
|
17199
|
+
backgroundColor: "#1e3a5f",
|
|
17200
|
+
borderWidth: 1,
|
|
17201
|
+
borderColor: "rgba(59, 130, 246, 0.25)",
|
|
17202
|
+
justifyContent: "center",
|
|
17203
|
+
alignItems: "center"
|
|
16947
17204
|
},
|
|
16948
|
-
|
|
16949
|
-
fontSize: 16
|
|
17205
|
+
homeIcon: {
|
|
17206
|
+
fontSize: 16,
|
|
17207
|
+
color: "#60a5fa",
|
|
17208
|
+
marginTop: -1
|
|
16950
17209
|
},
|
|
16951
17210
|
closeButton: {
|
|
16952
17211
|
width: 32,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bbearai/react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "BugBear React Native components for mobile apps",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@bbearai/core": "^0.
|
|
54
|
+
"@bbearai/core": "^0.6.0",
|
|
55
55
|
"@eslint/js": "^9.39.2",
|
|
56
56
|
"@testing-library/jest-dom": "^6.9.1",
|
|
57
57
|
"@testing-library/react": "^16.3.2",
|