@bbearai/react-native 0.3.8 → 0.3.10
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/README.md +139 -92
- package/dist/index.js +87 -17
- package/dist/index.mjs +97 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ function App() {
|
|
|
34
34
|
getCurrentUser: async () => ({
|
|
35
35
|
id: user.id,
|
|
36
36
|
email: user.email,
|
|
37
|
+
name: user.name, // Optional, shown on reports
|
|
37
38
|
}),
|
|
38
39
|
}}
|
|
39
40
|
>
|
|
@@ -44,6 +45,138 @@ function App() {
|
|
|
44
45
|
}
|
|
45
46
|
```
|
|
46
47
|
|
|
48
|
+
## Configuration Options
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<BugBearProvider
|
|
52
|
+
config={{
|
|
53
|
+
// Required
|
|
54
|
+
projectId: 'your-project-id',
|
|
55
|
+
getCurrentUser: async () => ({ id: user.id, email: user.email }),
|
|
56
|
+
|
|
57
|
+
// Optional — rich context for bug reports
|
|
58
|
+
getAppContext: () => ({
|
|
59
|
+
currentRoute: currentScreenName, // Current screen name
|
|
60
|
+
userRole: currentUser?.role, // 'owner', 'manager', 'guest', etc.
|
|
61
|
+
propertyId: selectedProperty?.id, // App-specific context
|
|
62
|
+
custom: { theme: 'dark' }, // Any additional data
|
|
63
|
+
}),
|
|
64
|
+
|
|
65
|
+
// Optional — callbacks
|
|
66
|
+
dashboardUrl: 'https://app.bugbear.ai', // Web dashboard link in widget
|
|
67
|
+
onNavigate: (route) => navigation.navigate(route), // Deep link handler for test cases
|
|
68
|
+
onReportSubmitted: (report) => { ... }, // After report submission
|
|
69
|
+
getNavigationHistory: () => [...recentScreens], // Custom nav tracking
|
|
70
|
+
|
|
71
|
+
// Optional — self-hosted
|
|
72
|
+
supabaseUrl: '...',
|
|
73
|
+
supabaseAnonKey: '...',
|
|
74
|
+
}}
|
|
75
|
+
enabled={isAuthReady} // Delay init until auth is ready (default: true)
|
|
76
|
+
>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Why use `getAppContext`?
|
|
80
|
+
|
|
81
|
+
When a tester reports a bug, BugBear attaches the app context to the report. This tells the developer fixing the bug:
|
|
82
|
+
- **Which screen** the bug is on
|
|
83
|
+
- **What role** the user has (critical for role-dependent bugs)
|
|
84
|
+
- **App-specific state** like selected property, active filters, etc.
|
|
85
|
+
|
|
86
|
+
The bug report form also includes a **"Which screen?"** field so testers can specify the affected screen manually.
|
|
87
|
+
|
|
88
|
+
## Automatic Context Capture
|
|
89
|
+
|
|
90
|
+
BugBearProvider automatically captures debugging context with zero configuration:
|
|
91
|
+
|
|
92
|
+
| Data | Details |
|
|
93
|
+
|------|---------|
|
|
94
|
+
| **Console logs** | Last 50 `console.log/warn/error/info` calls |
|
|
95
|
+
| **Network requests** | Last 20 `fetch()` calls with method, URL, status, duration |
|
|
96
|
+
| **Failed response bodies** | First 500 chars of 4xx/5xx response bodies |
|
|
97
|
+
| **Performance** | Page load time, memory usage |
|
|
98
|
+
| **Environment** | Language, timezone, online status |
|
|
99
|
+
|
|
100
|
+
This data is attached to every bug report and available to the MCP server's `get_report_context` tool for AI-assisted debugging.
|
|
101
|
+
|
|
102
|
+
> **Note:** For React Native, navigation history is not auto-captured (no `pushState`). Use `getNavigationHistory` or React Navigation integration below for screen tracking.
|
|
103
|
+
|
|
104
|
+
## Navigation Tracking
|
|
105
|
+
|
|
106
|
+
### React Navigation integration
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
import { useNavigationContainerRef } from '@react-navigation/native';
|
|
110
|
+
|
|
111
|
+
function App() {
|
|
112
|
+
const navigationRef = useNavigationContainerRef();
|
|
113
|
+
const routeHistory = useRef<string[]>([]);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<BugBearProvider
|
|
117
|
+
config={{
|
|
118
|
+
projectId: 'your-project-id',
|
|
119
|
+
getCurrentUser: async () => ({ id: user.id, email: user.email }),
|
|
120
|
+
getNavigationHistory: () => routeHistory.current,
|
|
121
|
+
getAppContext: () => ({
|
|
122
|
+
currentRoute: navigationRef.getCurrentRoute()?.name || 'unknown',
|
|
123
|
+
userRole: currentUser?.role,
|
|
124
|
+
}),
|
|
125
|
+
}}
|
|
126
|
+
>
|
|
127
|
+
<NavigationContainer
|
|
128
|
+
ref={navigationRef}
|
|
129
|
+
onStateChange={() => {
|
|
130
|
+
const name = navigationRef.getCurrentRoute()?.name;
|
|
131
|
+
if (name) {
|
|
132
|
+
routeHistory.current.push(name);
|
|
133
|
+
// Keep last 20
|
|
134
|
+
if (routeHistory.current.length > 20) routeHistory.current.shift();
|
|
135
|
+
}
|
|
136
|
+
}}
|
|
137
|
+
>
|
|
138
|
+
{/* Your navigation */}
|
|
139
|
+
</NavigationContainer>
|
|
140
|
+
<BugBearButton />
|
|
141
|
+
</BugBearProvider>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Widget Architecture
|
|
147
|
+
|
|
148
|
+
The widget uses a navigation stack pattern with 10 screens:
|
|
149
|
+
|
|
150
|
+
| Screen | Purpose |
|
|
151
|
+
|--------|---------|
|
|
152
|
+
| **Home** | Smart hero banner + 2x2 action grid + progress bar + web dashboard link |
|
|
153
|
+
| **Test Detail** | One-test-at-a-time execution with pass/fail/skip actions |
|
|
154
|
+
| **Test List** | All assignments grouped by folder with filter bar |
|
|
155
|
+
| **Test Feedback** | Star rating + quality flags after pass/fail |
|
|
156
|
+
| **Report** | Bug/feedback submission with type, severity, description, affected screen |
|
|
157
|
+
| **Report Success** | Confirmation with auto-return to home |
|
|
158
|
+
| **Message List** | Thread list with unread badges + compose button |
|
|
159
|
+
| **Thread Detail** | Chat bubbles + reply composer |
|
|
160
|
+
| **Compose Message** | New thread form with subject + message |
|
|
161
|
+
| **Profile** | Tester info, stats, and editable fields |
|
|
162
|
+
|
|
163
|
+
## Button Configuration
|
|
164
|
+
|
|
165
|
+
The BugBear button is draggable by default with edge-snapping behavior:
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
<BugBearButton
|
|
169
|
+
draggable={true} // Enable/disable dragging (default: true)
|
|
170
|
+
position="bottom-right" // Initial position: 'bottom-right' | 'bottom-left'
|
|
171
|
+
initialX={100} // Custom initial X position (optional)
|
|
172
|
+
initialY={500} // Custom initial Y position (optional)
|
|
173
|
+
minY={100} // Minimum Y from top (default: 100)
|
|
174
|
+
maxYOffset={160} // Max Y offset from bottom (default: 160)
|
|
175
|
+
/>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The button automatically snaps to the nearest screen edge when released.
|
|
179
|
+
|
|
47
180
|
## Monorepo Setup
|
|
48
181
|
|
|
49
182
|
When using BugBear in a monorepo (e.g., with Turborepo, Nx, or Yarn Workspaces), you may encounter React duplicate instance errors like:
|
|
@@ -105,90 +238,6 @@ If you're using Yarn workspaces, you can also configure hoisting to ensure a sin
|
|
|
105
238
|
}
|
|
106
239
|
```
|
|
107
240
|
|
|
108
|
-
## Configuration Options
|
|
109
|
-
|
|
110
|
-
```tsx
|
|
111
|
-
<BugBearProvider
|
|
112
|
-
config={{
|
|
113
|
-
projectId: 'your-project-id', // Required: Your BugBear project ID
|
|
114
|
-
getCurrentUser: async () => {...}, // Required: Return current user info
|
|
115
|
-
dashboardUrl: 'https://app.bugbear.ai', // Optional: Web dashboard link in widget
|
|
116
|
-
getNavigationHistory: () => [...], // Optional: Custom navigation tracking
|
|
117
|
-
onNavigate: (route) => {...}, // Optional: Deep link handler for test cases
|
|
118
|
-
onReportSubmitted: (report) => {...}, // Optional: Callback after report submission
|
|
119
|
-
}}
|
|
120
|
-
>
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Widget Architecture
|
|
124
|
-
|
|
125
|
-
The widget uses a navigation stack pattern with 10 screens:
|
|
126
|
-
|
|
127
|
-
| Screen | Purpose |
|
|
128
|
-
|--------|---------|
|
|
129
|
-
| **Home** | Smart hero banner + 2x2 action grid + progress bar + web dashboard link |
|
|
130
|
-
| **Test Detail** | One-test-at-a-time execution with pass/fail/skip actions |
|
|
131
|
-
| **Test List** | All assignments grouped by folder with filter bar |
|
|
132
|
-
| **Test Feedback** | Star rating + quality flags after pass/fail |
|
|
133
|
-
| **Report** | Bug/feedback submission with type, severity, description |
|
|
134
|
-
| **Report Success** | Confirmation with auto-return to home |
|
|
135
|
-
| **Message List** | Thread list with unread badges + compose button |
|
|
136
|
-
| **Thread Detail** | Chat bubbles + reply composer |
|
|
137
|
-
| **Compose Message** | New thread form with subject + message |
|
|
138
|
-
| **Profile** | Tester info, stats, and editable fields |
|
|
139
|
-
|
|
140
|
-
## Navigation Tracking
|
|
141
|
-
|
|
142
|
-
BugBear can automatically track navigation history for better bug context. If you're using React Navigation:
|
|
143
|
-
|
|
144
|
-
```tsx
|
|
145
|
-
import { useNavigationContainerRef } from '@react-navigation/native';
|
|
146
|
-
|
|
147
|
-
function App() {
|
|
148
|
-
const navigationRef = useNavigationContainerRef();
|
|
149
|
-
const routeNameRef = useRef<string>();
|
|
150
|
-
|
|
151
|
-
return (
|
|
152
|
-
<BugBearProvider
|
|
153
|
-
projectId="your-project-id"
|
|
154
|
-
getCurrentUser={...}
|
|
155
|
-
getNavigationHistory={() => routeNameRef.current ? [routeNameRef.current] : []}
|
|
156
|
-
>
|
|
157
|
-
<NavigationContainer
|
|
158
|
-
ref={navigationRef}
|
|
159
|
-
onReady={() => {
|
|
160
|
-
routeNameRef.current = navigationRef.getCurrentRoute()?.name;
|
|
161
|
-
}}
|
|
162
|
-
onStateChange={() => {
|
|
163
|
-
const currentRouteName = navigationRef.getCurrentRoute()?.name;
|
|
164
|
-
routeNameRef.current = currentRouteName;
|
|
165
|
-
}}
|
|
166
|
-
>
|
|
167
|
-
{/* Your navigation */}
|
|
168
|
-
</NavigationContainer>
|
|
169
|
-
<BugBearWidget />
|
|
170
|
-
</BugBearProvider>
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
## Button Configuration
|
|
176
|
-
|
|
177
|
-
The BugBear button is draggable by default with edge-snapping behavior:
|
|
178
|
-
|
|
179
|
-
```tsx
|
|
180
|
-
<BugBearButton
|
|
181
|
-
draggable={true} // Enable/disable dragging (default: true)
|
|
182
|
-
position="bottom-right" // Initial position: 'bottom-right' | 'bottom-left'
|
|
183
|
-
initialX={100} // Custom initial X position (optional)
|
|
184
|
-
initialY={500} // Custom initial Y position (optional)
|
|
185
|
-
minY={100} // Minimum Y from top (default: 100)
|
|
186
|
-
maxYOffset={160} // Max Y offset from bottom (default: 160)
|
|
187
|
-
/>
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
The button automatically snaps to the nearest screen edge when released.
|
|
191
|
-
|
|
192
241
|
## Troubleshooting
|
|
193
242
|
|
|
194
243
|
### Widget not showing
|
|
@@ -203,10 +252,12 @@ If you're using your own Supabase instance, provide the credentials:
|
|
|
203
252
|
|
|
204
253
|
```tsx
|
|
205
254
|
<BugBearProvider
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
255
|
+
config={{
|
|
256
|
+
projectId: 'your-project-id',
|
|
257
|
+
supabaseUrl: 'https://your-instance.supabase.co',
|
|
258
|
+
supabaseAnonKey: 'your-anon-key',
|
|
259
|
+
getCurrentUser: async () => ({ id: user.id, email: user.email }),
|
|
260
|
+
}}
|
|
210
261
|
>
|
|
211
262
|
```
|
|
212
263
|
|
|
@@ -216,15 +267,11 @@ If you're using your own Supabase instance, provide the credentials:
|
|
|
216
267
|
|
|
217
268
|
The BugBear button renders within the normal React Native view hierarchy using absolute positioning. When your app displays modals, bottom sheets, or the keyboard, the button may be hidden behind them.
|
|
218
269
|
|
|
219
|
-
This is a platform limitation - React Native doesn't provide a built-in way to render views above all modals without using a Modal component (which would block touch events on the underlying app).
|
|
220
|
-
|
|
221
270
|
**Workarounds:**
|
|
222
271
|
- Accept that the button won't be accessible when modals are open
|
|
223
272
|
- Consider dismissing the modal to access BugBear
|
|
224
273
|
- For testing flows that involve modals, provide an alternative way to trigger the BugBear widget
|
|
225
274
|
|
|
226
|
-
We're investigating solutions using `react-native-portal` or similar libraries for future releases.
|
|
227
|
-
|
|
228
275
|
## License
|
|
229
276
|
|
|
230
277
|
MIT
|
package/dist/index.js
CHANGED
|
@@ -11982,19 +11982,40 @@ var BugBearClient = class {
|
|
|
11982
11982
|
return { success: false, error: message };
|
|
11983
11983
|
}
|
|
11984
11984
|
}
|
|
11985
|
+
/**
|
|
11986
|
+
* Pass a test assignment — convenience wrapper around updateAssignmentStatus
|
|
11987
|
+
*/
|
|
11988
|
+
async passAssignment(assignmentId) {
|
|
11989
|
+
return this.updateAssignmentStatus(assignmentId, "passed");
|
|
11990
|
+
}
|
|
11991
|
+
/**
|
|
11992
|
+
* Fail a test assignment — convenience wrapper around updateAssignmentStatus
|
|
11993
|
+
*/
|
|
11994
|
+
async failAssignment(assignmentId) {
|
|
11995
|
+
return this.updateAssignmentStatus(assignmentId, "failed");
|
|
11996
|
+
}
|
|
11985
11997
|
/**
|
|
11986
11998
|
* Skip a test assignment with a required reason
|
|
11987
11999
|
* Marks the assignment as 'skipped' and records why it was skipped
|
|
11988
12000
|
*/
|
|
11989
12001
|
async skipAssignment(assignmentId, reason, notes) {
|
|
12002
|
+
let actualReason;
|
|
12003
|
+
let actualNotes;
|
|
12004
|
+
if (typeof reason === "object") {
|
|
12005
|
+
actualReason = reason.reason;
|
|
12006
|
+
actualNotes = reason.notes;
|
|
12007
|
+
} else {
|
|
12008
|
+
actualReason = reason;
|
|
12009
|
+
actualNotes = notes;
|
|
12010
|
+
}
|
|
11990
12011
|
try {
|
|
11991
12012
|
const updateData = {
|
|
11992
12013
|
status: "skipped",
|
|
11993
|
-
skip_reason:
|
|
12014
|
+
skip_reason: actualReason,
|
|
11994
12015
|
completed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
11995
12016
|
};
|
|
11996
|
-
if (
|
|
11997
|
-
updateData.notes =
|
|
12017
|
+
if (actualNotes) {
|
|
12018
|
+
updateData.notes = actualNotes;
|
|
11998
12019
|
}
|
|
11999
12020
|
const { error } = await this.supabase.from("test_assignments").update(updateData).eq("id", assignmentId);
|
|
12000
12021
|
if (error) {
|
|
@@ -12634,6 +12655,7 @@ var BugBearClient = class {
|
|
|
12634
12655
|
return false;
|
|
12635
12656
|
}
|
|
12636
12657
|
await this.supabase.from("discussion_threads").update({ last_message_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", threadId);
|
|
12658
|
+
await this.markThreadAsRead(threadId);
|
|
12637
12659
|
return true;
|
|
12638
12660
|
} catch (err) {
|
|
12639
12661
|
console.error("BugBear: Error sending message", err);
|
|
@@ -13478,7 +13500,7 @@ function HomeScreen({ nav }) {
|
|
|
13478
13500
|
import_react_native3.TouchableOpacity,
|
|
13479
13501
|
{
|
|
13480
13502
|
style: styles.actionCard,
|
|
13481
|
-
onPress: () => nav.push({ name: "
|
|
13503
|
+
onPress: () => nav.push({ name: "TEST_LIST" }),
|
|
13482
13504
|
activeOpacity: 0.7
|
|
13483
13505
|
},
|
|
13484
13506
|
/* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.actionIcon }, "\u2705"),
|
|
@@ -13746,12 +13768,14 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
13746
13768
|
const currentIndex = displayedAssignment ? assignments.indexOf(displayedAssignment) : -1;
|
|
13747
13769
|
const handlePass = (0, import_react4.useCallback)(async () => {
|
|
13748
13770
|
if (!client || !displayedAssignment) return;
|
|
13771
|
+
import_react_native4.Keyboard.dismiss();
|
|
13749
13772
|
await client.passAssignment(displayedAssignment.id);
|
|
13750
13773
|
await refreshAssignments();
|
|
13751
13774
|
nav.replace({ name: "TEST_FEEDBACK", status: "passed", assignmentId: displayedAssignment.id });
|
|
13752
13775
|
}, [client, displayedAssignment, refreshAssignments, nav]);
|
|
13753
13776
|
const handleFail = (0, import_react4.useCallback)(async () => {
|
|
13754
13777
|
if (!client || !displayedAssignment) return;
|
|
13778
|
+
import_react_native4.Keyboard.dismiss();
|
|
13755
13779
|
await client.failAssignment(displayedAssignment.id);
|
|
13756
13780
|
await refreshAssignments();
|
|
13757
13781
|
nav.replace({
|
|
@@ -13765,6 +13789,7 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
13765
13789
|
}, [client, displayedAssignment, refreshAssignments, nav]);
|
|
13766
13790
|
const handleSkip = (0, import_react4.useCallback)(async () => {
|
|
13767
13791
|
if (!client || !displayedAssignment || !selectedSkipReason) return;
|
|
13792
|
+
import_react_native4.Keyboard.dismiss();
|
|
13768
13793
|
setSkipping(true);
|
|
13769
13794
|
await client.skipAssignment(displayedAssignment.id, {
|
|
13770
13795
|
reason: selectedSkipReason,
|
|
@@ -13775,11 +13800,14 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
13775
13800
|
setSelectedSkipReason(null);
|
|
13776
13801
|
setSkipNotes("");
|
|
13777
13802
|
setSkipping(false);
|
|
13778
|
-
const
|
|
13803
|
+
const currentIdx = assignments.indexOf(displayedAssignment);
|
|
13804
|
+
const pending = assignments.filter(
|
|
13779
13805
|
(a) => (a.status === "pending" || a.status === "in_progress") && a.id !== displayedAssignment.id
|
|
13780
13806
|
);
|
|
13781
|
-
if (
|
|
13782
|
-
|
|
13807
|
+
if (pending.length > 0) {
|
|
13808
|
+
const nextAfterCurrent = pending.find((a) => assignments.indexOf(a) > currentIdx);
|
|
13809
|
+
const nextTest = nextAfterCurrent || pending[0];
|
|
13810
|
+
nav.replace({ name: "TEST_DETAIL", testId: nextTest.id });
|
|
13783
13811
|
} else {
|
|
13784
13812
|
nav.reset();
|
|
13785
13813
|
}
|
|
@@ -13833,7 +13861,9 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
13833
13861
|
{
|
|
13834
13862
|
style: styles2.navigateButton,
|
|
13835
13863
|
onPress: () => {
|
|
13864
|
+
import_react_native4.Keyboard.dismiss();
|
|
13836
13865
|
onNavigate(testCase.targetRoute);
|
|
13866
|
+
nav.closeWidget?.();
|
|
13837
13867
|
}
|
|
13838
13868
|
},
|
|
13839
13869
|
/* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.navigateText }, "\u{1F9ED} Go to test location")
|
|
@@ -13975,6 +14005,9 @@ function TestListScreen({ nav }) {
|
|
|
13975
14005
|
const { assignments, currentAssignment, refreshAssignments } = useBugBear();
|
|
13976
14006
|
const [filter, setFilter] = (0, import_react5.useState)("all");
|
|
13977
14007
|
const [collapsedFolders, setCollapsedFolders] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
|
|
14008
|
+
(0, import_react5.useEffect)(() => {
|
|
14009
|
+
refreshAssignments();
|
|
14010
|
+
}, []);
|
|
13978
14011
|
const groupedAssignments = (0, import_react5.useMemo)(() => {
|
|
13979
14012
|
const groups = /* @__PURE__ */ new Map();
|
|
13980
14013
|
for (const assignment of assignments) {
|
|
@@ -14307,9 +14340,12 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
|
|
|
14307
14340
|
if (status === "failed") {
|
|
14308
14341
|
nav.replace({ name: "REPORT", prefill: { type: "test_fail", assignmentId, testCaseId: assignment?.testCase.id } });
|
|
14309
14342
|
} else {
|
|
14310
|
-
const
|
|
14311
|
-
|
|
14312
|
-
|
|
14343
|
+
const currentIdx = assignments.findIndex((a) => a.id === assignmentId);
|
|
14344
|
+
const pending = assignments.filter((a) => (a.status === "pending" || a.status === "in_progress") && a.id !== assignmentId);
|
|
14345
|
+
if (pending.length > 0) {
|
|
14346
|
+
const nextAfterCurrent = pending.find((a) => assignments.indexOf(a) > currentIdx);
|
|
14347
|
+
const nextTest = nextAfterCurrent || pending[0];
|
|
14348
|
+
nav.replace({ name: "TEST_DETAIL", testId: nextTest.id });
|
|
14313
14349
|
} else {
|
|
14314
14350
|
nav.reset();
|
|
14315
14351
|
}
|
|
@@ -14319,9 +14355,12 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
|
|
|
14319
14355
|
if (status === "failed") {
|
|
14320
14356
|
nav.replace({ name: "REPORT", prefill: { type: "test_fail", assignmentId, testCaseId: assignment?.testCase.id } });
|
|
14321
14357
|
} else {
|
|
14322
|
-
const
|
|
14323
|
-
|
|
14324
|
-
|
|
14358
|
+
const currentIdx = assignments.findIndex((a) => a.id === assignmentId);
|
|
14359
|
+
const pending = assignments.filter((a) => (a.status === "pending" || a.status === "in_progress") && a.id !== assignmentId);
|
|
14360
|
+
if (pending.length > 0) {
|
|
14361
|
+
const nextAfterCurrent = pending.find((a) => assignments.indexOf(a) > currentIdx);
|
|
14362
|
+
const nextTest = nextAfterCurrent || pending[0];
|
|
14363
|
+
nav.replace({ name: "TEST_DETAIL", testId: nextTest.id });
|
|
14325
14364
|
} else {
|
|
14326
14365
|
nav.reset();
|
|
14327
14366
|
}
|
|
@@ -14974,10 +15013,29 @@ function BugBearButton({
|
|
|
14974
15013
|
}
|
|
14975
15014
|
};
|
|
14976
15015
|
const handleClose = () => {
|
|
15016
|
+
import_react_native15.Keyboard.dismiss();
|
|
14977
15017
|
setModalVisible(false);
|
|
14978
|
-
reset();
|
|
14979
15018
|
};
|
|
14980
|
-
const nav = {
|
|
15019
|
+
const nav = {
|
|
15020
|
+
push: (screen) => {
|
|
15021
|
+
import_react_native15.Keyboard.dismiss();
|
|
15022
|
+
push(screen);
|
|
15023
|
+
},
|
|
15024
|
+
pop: () => {
|
|
15025
|
+
import_react_native15.Keyboard.dismiss();
|
|
15026
|
+
pop();
|
|
15027
|
+
},
|
|
15028
|
+
replace: (screen) => {
|
|
15029
|
+
import_react_native15.Keyboard.dismiss();
|
|
15030
|
+
replace(screen);
|
|
15031
|
+
},
|
|
15032
|
+
reset: () => {
|
|
15033
|
+
import_react_native15.Keyboard.dismiss();
|
|
15034
|
+
reset();
|
|
15035
|
+
},
|
|
15036
|
+
canGoBack,
|
|
15037
|
+
closeWidget: handleClose
|
|
15038
|
+
};
|
|
14981
15039
|
const renderScreen = () => {
|
|
14982
15040
|
switch (currentScreen.name) {
|
|
14983
15041
|
case "HOME":
|
|
@@ -15034,7 +15092,7 @@ function BugBearButton({
|
|
|
15034
15092
|
behavior: import_react_native15.Platform.OS === "ios" ? "padding" : "height",
|
|
15035
15093
|
style: styles13.modalOverlay
|
|
15036
15094
|
},
|
|
15037
|
-
/* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.modalContainer }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.header }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.headerLeft }, canGoBack ? /* @__PURE__ */ import_react16.default.createElement(import_react_native15.TouchableOpacity, { onPress: pop, style: styles13.backButton }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.backText }, "\u2190 Back")) : /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.headerTitleRow }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ import_react16.default.createElement(import_react_native15.TouchableOpacity, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.TouchableOpacity, { onPress: handleClose, style: styles13.closeButton }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.closeText }, "\u2715"))), /* @__PURE__ */ import_react16.default.createElement(
|
|
15095
|
+
/* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.modalContainer }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.header }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.headerLeft }, canGoBack ? /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.headerNavRow }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.TouchableOpacity, { onPress: () => nav.pop(), style: styles13.backButton }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.backText }, "\u2190 Back")), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.TouchableOpacity, { onPress: () => nav.reset(), style: styles13.homeButton }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.homeText }, "\u{1F3E0}"))) : /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.headerTitleRow }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ import_react16.default.createElement(import_react_native15.TouchableOpacity, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.TouchableOpacity, { onPress: handleClose, style: styles13.closeButton }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.closeText }, "\u2715"))), /* @__PURE__ */ import_react16.default.createElement(
|
|
15038
15096
|
import_react_native15.ScrollView,
|
|
15039
15097
|
{
|
|
15040
15098
|
style: styles13.content,
|
|
@@ -15135,15 +15193,27 @@ var styles13 = import_react_native15.StyleSheet.create({
|
|
|
15135
15193
|
flex: 1,
|
|
15136
15194
|
textAlign: "center"
|
|
15137
15195
|
},
|
|
15196
|
+
headerNavRow: {
|
|
15197
|
+
flexDirection: "row",
|
|
15198
|
+
alignItems: "center",
|
|
15199
|
+
gap: 8
|
|
15200
|
+
},
|
|
15138
15201
|
backButton: {
|
|
15139
15202
|
paddingVertical: 2,
|
|
15140
|
-
paddingRight:
|
|
15203
|
+
paddingRight: 4
|
|
15141
15204
|
},
|
|
15142
15205
|
backText: {
|
|
15143
15206
|
fontSize: 15,
|
|
15144
15207
|
color: colors.blue,
|
|
15145
15208
|
fontWeight: "500"
|
|
15146
15209
|
},
|
|
15210
|
+
homeButton: {
|
|
15211
|
+
paddingVertical: 2,
|
|
15212
|
+
paddingHorizontal: 6
|
|
15213
|
+
},
|
|
15214
|
+
homeText: {
|
|
15215
|
+
fontSize: 16
|
|
15216
|
+
},
|
|
15147
15217
|
closeButton: {
|
|
15148
15218
|
width: 32,
|
|
15149
15219
|
height: 32,
|
package/dist/index.mjs
CHANGED
|
@@ -11951,19 +11951,40 @@ var BugBearClient = class {
|
|
|
11951
11951
|
return { success: false, error: message };
|
|
11952
11952
|
}
|
|
11953
11953
|
}
|
|
11954
|
+
/**
|
|
11955
|
+
* Pass a test assignment — convenience wrapper around updateAssignmentStatus
|
|
11956
|
+
*/
|
|
11957
|
+
async passAssignment(assignmentId) {
|
|
11958
|
+
return this.updateAssignmentStatus(assignmentId, "passed");
|
|
11959
|
+
}
|
|
11960
|
+
/**
|
|
11961
|
+
* Fail a test assignment — convenience wrapper around updateAssignmentStatus
|
|
11962
|
+
*/
|
|
11963
|
+
async failAssignment(assignmentId) {
|
|
11964
|
+
return this.updateAssignmentStatus(assignmentId, "failed");
|
|
11965
|
+
}
|
|
11954
11966
|
/**
|
|
11955
11967
|
* Skip a test assignment with a required reason
|
|
11956
11968
|
* Marks the assignment as 'skipped' and records why it was skipped
|
|
11957
11969
|
*/
|
|
11958
11970
|
async skipAssignment(assignmentId, reason, notes) {
|
|
11971
|
+
let actualReason;
|
|
11972
|
+
let actualNotes;
|
|
11973
|
+
if (typeof reason === "object") {
|
|
11974
|
+
actualReason = reason.reason;
|
|
11975
|
+
actualNotes = reason.notes;
|
|
11976
|
+
} else {
|
|
11977
|
+
actualReason = reason;
|
|
11978
|
+
actualNotes = notes;
|
|
11979
|
+
}
|
|
11959
11980
|
try {
|
|
11960
11981
|
const updateData = {
|
|
11961
11982
|
status: "skipped",
|
|
11962
|
-
skip_reason:
|
|
11983
|
+
skip_reason: actualReason,
|
|
11963
11984
|
completed_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
11964
11985
|
};
|
|
11965
|
-
if (
|
|
11966
|
-
updateData.notes =
|
|
11986
|
+
if (actualNotes) {
|
|
11987
|
+
updateData.notes = actualNotes;
|
|
11967
11988
|
}
|
|
11968
11989
|
const { error } = await this.supabase.from("test_assignments").update(updateData).eq("id", assignmentId);
|
|
11969
11990
|
if (error) {
|
|
@@ -12603,6 +12624,7 @@ var BugBearClient = class {
|
|
|
12603
12624
|
return false;
|
|
12604
12625
|
}
|
|
12605
12626
|
await this.supabase.from("discussion_threads").update({ last_message_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", threadId);
|
|
12627
|
+
await this.markThreadAsRead(threadId);
|
|
12606
12628
|
return true;
|
|
12607
12629
|
} catch (err) {
|
|
12608
12630
|
console.error("BugBear: Error sending message", err);
|
|
@@ -13204,7 +13226,8 @@ import {
|
|
|
13204
13226
|
Platform as Platform4,
|
|
13205
13227
|
PanResponder,
|
|
13206
13228
|
Animated,
|
|
13207
|
-
ActivityIndicator as ActivityIndicator2
|
|
13229
|
+
ActivityIndicator as ActivityIndicator2,
|
|
13230
|
+
Keyboard as Keyboard2
|
|
13208
13231
|
} from "react-native";
|
|
13209
13232
|
|
|
13210
13233
|
// src/widget/logo.ts
|
|
@@ -13461,7 +13484,7 @@ function HomeScreen({ nav }) {
|
|
|
13461
13484
|
TouchableOpacity,
|
|
13462
13485
|
{
|
|
13463
13486
|
style: styles.actionCard,
|
|
13464
|
-
onPress: () => nav.push({ name: "
|
|
13487
|
+
onPress: () => nav.push({ name: "TEST_LIST" }),
|
|
13465
13488
|
activeOpacity: 0.7
|
|
13466
13489
|
},
|
|
13467
13490
|
/* @__PURE__ */ React2.createElement(Text, { style: styles.actionIcon }, "\u2705"),
|
|
@@ -13694,7 +13717,7 @@ var styles = StyleSheet2.create({
|
|
|
13694
13717
|
|
|
13695
13718
|
// src/widget/screens/TestDetailScreen.tsx
|
|
13696
13719
|
import React3, { useState as useState2, useEffect as useEffect3, useCallback as useCallback2 } from "react";
|
|
13697
|
-
import { View as View2, Text as Text2, TouchableOpacity as TouchableOpacity2, StyleSheet as StyleSheet3, Modal, TextInput } from "react-native";
|
|
13720
|
+
import { View as View2, Text as Text2, TouchableOpacity as TouchableOpacity2, StyleSheet as StyleSheet3, Modal, TextInput, Keyboard } from "react-native";
|
|
13698
13721
|
function TestDetailScreen({ testId, nav }) {
|
|
13699
13722
|
const { client, assignments, currentAssignment, refreshAssignments, getDeviceInfo, onNavigate } = useBugBear();
|
|
13700
13723
|
const displayedAssignment = testId ? assignments.find((a) => a.id === testId) || currentAssignment : currentAssignment;
|
|
@@ -13729,12 +13752,14 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
13729
13752
|
const currentIndex = displayedAssignment ? assignments.indexOf(displayedAssignment) : -1;
|
|
13730
13753
|
const handlePass = useCallback2(async () => {
|
|
13731
13754
|
if (!client || !displayedAssignment) return;
|
|
13755
|
+
Keyboard.dismiss();
|
|
13732
13756
|
await client.passAssignment(displayedAssignment.id);
|
|
13733
13757
|
await refreshAssignments();
|
|
13734
13758
|
nav.replace({ name: "TEST_FEEDBACK", status: "passed", assignmentId: displayedAssignment.id });
|
|
13735
13759
|
}, [client, displayedAssignment, refreshAssignments, nav]);
|
|
13736
13760
|
const handleFail = useCallback2(async () => {
|
|
13737
13761
|
if (!client || !displayedAssignment) return;
|
|
13762
|
+
Keyboard.dismiss();
|
|
13738
13763
|
await client.failAssignment(displayedAssignment.id);
|
|
13739
13764
|
await refreshAssignments();
|
|
13740
13765
|
nav.replace({
|
|
@@ -13748,6 +13773,7 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
13748
13773
|
}, [client, displayedAssignment, refreshAssignments, nav]);
|
|
13749
13774
|
const handleSkip = useCallback2(async () => {
|
|
13750
13775
|
if (!client || !displayedAssignment || !selectedSkipReason) return;
|
|
13776
|
+
Keyboard.dismiss();
|
|
13751
13777
|
setSkipping(true);
|
|
13752
13778
|
await client.skipAssignment(displayedAssignment.id, {
|
|
13753
13779
|
reason: selectedSkipReason,
|
|
@@ -13758,11 +13784,14 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
13758
13784
|
setSelectedSkipReason(null);
|
|
13759
13785
|
setSkipNotes("");
|
|
13760
13786
|
setSkipping(false);
|
|
13761
|
-
const
|
|
13787
|
+
const currentIdx = assignments.indexOf(displayedAssignment);
|
|
13788
|
+
const pending = assignments.filter(
|
|
13762
13789
|
(a) => (a.status === "pending" || a.status === "in_progress") && a.id !== displayedAssignment.id
|
|
13763
13790
|
);
|
|
13764
|
-
if (
|
|
13765
|
-
|
|
13791
|
+
if (pending.length > 0) {
|
|
13792
|
+
const nextAfterCurrent = pending.find((a) => assignments.indexOf(a) > currentIdx);
|
|
13793
|
+
const nextTest = nextAfterCurrent || pending[0];
|
|
13794
|
+
nav.replace({ name: "TEST_DETAIL", testId: nextTest.id });
|
|
13766
13795
|
} else {
|
|
13767
13796
|
nav.reset();
|
|
13768
13797
|
}
|
|
@@ -13816,7 +13845,9 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
13816
13845
|
{
|
|
13817
13846
|
style: styles2.navigateButton,
|
|
13818
13847
|
onPress: () => {
|
|
13848
|
+
Keyboard.dismiss();
|
|
13819
13849
|
onNavigate(testCase.targetRoute);
|
|
13850
|
+
nav.closeWidget?.();
|
|
13820
13851
|
}
|
|
13821
13852
|
},
|
|
13822
13853
|
/* @__PURE__ */ React3.createElement(Text2, { style: styles2.navigateText }, "\u{1F9ED} Go to test location")
|
|
@@ -13952,12 +13983,15 @@ var styles2 = StyleSheet3.create({
|
|
|
13952
13983
|
});
|
|
13953
13984
|
|
|
13954
13985
|
// src/widget/screens/TestListScreen.tsx
|
|
13955
|
-
import React4, { useState as useState3, useMemo as useMemo2, useCallback as useCallback3 } from "react";
|
|
13986
|
+
import React4, { useState as useState3, useMemo as useMemo2, useCallback as useCallback3, useEffect as useEffect4 } from "react";
|
|
13956
13987
|
import { View as View3, Text as Text3, TouchableOpacity as TouchableOpacity3, StyleSheet as StyleSheet4 } from "react-native";
|
|
13957
13988
|
function TestListScreen({ nav }) {
|
|
13958
13989
|
const { assignments, currentAssignment, refreshAssignments } = useBugBear();
|
|
13959
13990
|
const [filter, setFilter] = useState3("all");
|
|
13960
13991
|
const [collapsedFolders, setCollapsedFolders] = useState3(/* @__PURE__ */ new Set());
|
|
13992
|
+
useEffect4(() => {
|
|
13993
|
+
refreshAssignments();
|
|
13994
|
+
}, []);
|
|
13961
13995
|
const groupedAssignments = useMemo2(() => {
|
|
13962
13996
|
const groups = /* @__PURE__ */ new Map();
|
|
13963
13997
|
for (const assignment of assignments) {
|
|
@@ -14290,9 +14324,12 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
|
|
|
14290
14324
|
if (status === "failed") {
|
|
14291
14325
|
nav.replace({ name: "REPORT", prefill: { type: "test_fail", assignmentId, testCaseId: assignment?.testCase.id } });
|
|
14292
14326
|
} else {
|
|
14293
|
-
const
|
|
14294
|
-
|
|
14295
|
-
|
|
14327
|
+
const currentIdx = assignments.findIndex((a) => a.id === assignmentId);
|
|
14328
|
+
const pending = assignments.filter((a) => (a.status === "pending" || a.status === "in_progress") && a.id !== assignmentId);
|
|
14329
|
+
if (pending.length > 0) {
|
|
14330
|
+
const nextAfterCurrent = pending.find((a) => assignments.indexOf(a) > currentIdx);
|
|
14331
|
+
const nextTest = nextAfterCurrent || pending[0];
|
|
14332
|
+
nav.replace({ name: "TEST_DETAIL", testId: nextTest.id });
|
|
14296
14333
|
} else {
|
|
14297
14334
|
nav.reset();
|
|
14298
14335
|
}
|
|
@@ -14302,9 +14339,12 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
|
|
|
14302
14339
|
if (status === "failed") {
|
|
14303
14340
|
nav.replace({ name: "REPORT", prefill: { type: "test_fail", assignmentId, testCaseId: assignment?.testCase.id } });
|
|
14304
14341
|
} else {
|
|
14305
|
-
const
|
|
14306
|
-
|
|
14307
|
-
|
|
14342
|
+
const currentIdx = assignments.findIndex((a) => a.id === assignmentId);
|
|
14343
|
+
const pending = assignments.filter((a) => (a.status === "pending" || a.status === "in_progress") && a.id !== assignmentId);
|
|
14344
|
+
if (pending.length > 0) {
|
|
14345
|
+
const nextAfterCurrent = pending.find((a) => assignments.indexOf(a) > currentIdx);
|
|
14346
|
+
const nextTest = nextAfterCurrent || pending[0];
|
|
14347
|
+
nav.replace({ name: "TEST_DETAIL", testId: nextTest.id });
|
|
14308
14348
|
} else {
|
|
14309
14349
|
nav.reset();
|
|
14310
14350
|
}
|
|
@@ -14490,10 +14530,10 @@ var styles7 = StyleSheet8.create({
|
|
|
14490
14530
|
});
|
|
14491
14531
|
|
|
14492
14532
|
// src/widget/screens/ReportSuccessScreen.tsx
|
|
14493
|
-
import React9, { useEffect as
|
|
14533
|
+
import React9, { useEffect as useEffect5 } from "react";
|
|
14494
14534
|
import { View as View8, Text as Text8, StyleSheet as StyleSheet9 } from "react-native";
|
|
14495
14535
|
function ReportSuccessScreen({ nav }) {
|
|
14496
|
-
|
|
14536
|
+
useEffect5(() => {
|
|
14497
14537
|
const timer = setTimeout(() => nav.reset(), 2e3);
|
|
14498
14538
|
return () => clearTimeout(timer);
|
|
14499
14539
|
}, [nav]);
|
|
@@ -14552,7 +14592,7 @@ var styles9 = StyleSheet10.create({
|
|
|
14552
14592
|
});
|
|
14553
14593
|
|
|
14554
14594
|
// src/widget/screens/ThreadDetailScreen.tsx
|
|
14555
|
-
import React11, { useState as useState7, useEffect as
|
|
14595
|
+
import React11, { useState as useState7, useEffect as useEffect6 } from "react";
|
|
14556
14596
|
import { View as View10, Text as Text10, TouchableOpacity as TouchableOpacity9, TextInput as TextInput4, StyleSheet as StyleSheet11, Image as Image2 } from "react-native";
|
|
14557
14597
|
function ThreadDetailScreen({ thread, nav }) {
|
|
14558
14598
|
const { getThreadMessages, sendMessage, markAsRead, uploadImage } = useBugBear();
|
|
@@ -14562,7 +14602,7 @@ function ThreadDetailScreen({ thread, nav }) {
|
|
|
14562
14602
|
const [sending, setSending] = useState7(false);
|
|
14563
14603
|
const [sendError, setSendError] = useState7(false);
|
|
14564
14604
|
const replyImages = useImageAttachments(uploadImage, 3, "discussion-attachments");
|
|
14565
|
-
|
|
14605
|
+
useEffect6(() => {
|
|
14566
14606
|
(async () => {
|
|
14567
14607
|
setLoading(true);
|
|
14568
14608
|
const msgs = await getThreadMessages(thread.id);
|
|
@@ -14731,7 +14771,7 @@ var styles11 = StyleSheet12.create({
|
|
|
14731
14771
|
});
|
|
14732
14772
|
|
|
14733
14773
|
// src/widget/screens/ProfileScreen.tsx
|
|
14734
|
-
import React13, { useState as useState9, useEffect as
|
|
14774
|
+
import React13, { useState as useState9, useEffect as useEffect7 } from "react";
|
|
14735
14775
|
import { View as View12, Text as Text12, TouchableOpacity as TouchableOpacity11, TextInput as TextInput6, StyleSheet as StyleSheet13 } from "react-native";
|
|
14736
14776
|
function ProfileScreen({ nav }) {
|
|
14737
14777
|
const { testerInfo, assignments, updateTesterProfile, refreshTesterInfo } = useBugBear();
|
|
@@ -14744,7 +14784,7 @@ function ProfileScreen({ nav }) {
|
|
|
14744
14784
|
const [saved, setSaved] = useState9(false);
|
|
14745
14785
|
const [showDetails, setShowDetails] = useState9(false);
|
|
14746
14786
|
const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
|
|
14747
|
-
|
|
14787
|
+
useEffect7(() => {
|
|
14748
14788
|
if (testerInfo) {
|
|
14749
14789
|
setName(testerInfo.name);
|
|
14750
14790
|
setAdditionalEmails(testerInfo.additionalEmails || []);
|
|
@@ -14957,10 +14997,29 @@ function BugBearButton({
|
|
|
14957
14997
|
}
|
|
14958
14998
|
};
|
|
14959
14999
|
const handleClose = () => {
|
|
15000
|
+
Keyboard2.dismiss();
|
|
14960
15001
|
setModalVisible(false);
|
|
14961
|
-
reset();
|
|
14962
15002
|
};
|
|
14963
|
-
const nav = {
|
|
15003
|
+
const nav = {
|
|
15004
|
+
push: (screen) => {
|
|
15005
|
+
Keyboard2.dismiss();
|
|
15006
|
+
push(screen);
|
|
15007
|
+
},
|
|
15008
|
+
pop: () => {
|
|
15009
|
+
Keyboard2.dismiss();
|
|
15010
|
+
pop();
|
|
15011
|
+
},
|
|
15012
|
+
replace: (screen) => {
|
|
15013
|
+
Keyboard2.dismiss();
|
|
15014
|
+
replace(screen);
|
|
15015
|
+
},
|
|
15016
|
+
reset: () => {
|
|
15017
|
+
Keyboard2.dismiss();
|
|
15018
|
+
reset();
|
|
15019
|
+
},
|
|
15020
|
+
canGoBack,
|
|
15021
|
+
closeWidget: handleClose
|
|
15022
|
+
};
|
|
14964
15023
|
const renderScreen = () => {
|
|
14965
15024
|
switch (currentScreen.name) {
|
|
14966
15025
|
case "HOME":
|
|
@@ -15017,7 +15076,7 @@ function BugBearButton({
|
|
|
15017
15076
|
behavior: Platform4.OS === "ios" ? "padding" : "height",
|
|
15018
15077
|
style: styles13.modalOverlay
|
|
15019
15078
|
},
|
|
15020
|
-
/* @__PURE__ */ React14.createElement(View13, { style: styles13.modalContainer }, /* @__PURE__ */ React14.createElement(View13, { style: styles13.header }, /* @__PURE__ */ React14.createElement(View13, { style: styles13.headerLeft }, canGoBack ? /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: pop, style: styles13.backButton }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.backText }, "\u2190 Back")) : /* @__PURE__ */ React14.createElement(View13, { style: styles13.headerTitleRow }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ React14.createElement(Text13, { style: styles13.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: handleClose, style: styles13.closeButton }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.closeText }, "\u2715"))), /* @__PURE__ */ React14.createElement(
|
|
15079
|
+
/* @__PURE__ */ React14.createElement(View13, { style: styles13.modalContainer }, /* @__PURE__ */ React14.createElement(View13, { style: styles13.header }, /* @__PURE__ */ React14.createElement(View13, { style: styles13.headerLeft }, canGoBack ? /* @__PURE__ */ React14.createElement(View13, { style: styles13.headerNavRow }, /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: () => nav.pop(), style: styles13.backButton }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.backText }, "\u2190 Back")), /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: () => nav.reset(), style: styles13.homeButton }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.homeText }, "\u{1F3E0}"))) : /* @__PURE__ */ React14.createElement(View13, { style: styles13.headerTitleRow }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ React14.createElement(Text13, { style: styles13.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: handleClose, style: styles13.closeButton }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.closeText }, "\u2715"))), /* @__PURE__ */ React14.createElement(
|
|
15021
15080
|
ScrollView2,
|
|
15022
15081
|
{
|
|
15023
15082
|
style: styles13.content,
|
|
@@ -15118,15 +15177,27 @@ var styles13 = StyleSheet14.create({
|
|
|
15118
15177
|
flex: 1,
|
|
15119
15178
|
textAlign: "center"
|
|
15120
15179
|
},
|
|
15180
|
+
headerNavRow: {
|
|
15181
|
+
flexDirection: "row",
|
|
15182
|
+
alignItems: "center",
|
|
15183
|
+
gap: 8
|
|
15184
|
+
},
|
|
15121
15185
|
backButton: {
|
|
15122
15186
|
paddingVertical: 2,
|
|
15123
|
-
paddingRight:
|
|
15187
|
+
paddingRight: 4
|
|
15124
15188
|
},
|
|
15125
15189
|
backText: {
|
|
15126
15190
|
fontSize: 15,
|
|
15127
15191
|
color: colors.blue,
|
|
15128
15192
|
fontWeight: "500"
|
|
15129
15193
|
},
|
|
15194
|
+
homeButton: {
|
|
15195
|
+
paddingVertical: 2,
|
|
15196
|
+
paddingHorizontal: 6
|
|
15197
|
+
},
|
|
15198
|
+
homeText: {
|
|
15199
|
+
fontSize: 16
|
|
15200
|
+
},
|
|
15130
15201
|
closeButton: {
|
|
15131
15202
|
width: 32,
|
|
15132
15203
|
height: 32,
|