@bugpulse/react-native 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +185 -0
- package/dist/AnnotationCanvas.d.ts +11 -0
- package/dist/AnnotationCanvas.d.ts.map +1 -0
- package/dist/AnnotationCanvas.js +154 -0
- package/dist/AnnotationCanvas.js.map +1 -0
- package/dist/BugReportModal.d.ts +15 -0
- package/dist/BugReportModal.d.ts.map +1 -0
- package/dist/BugReportModal.js +303 -0
- package/dist/BugReportModal.js.map +1 -0
- package/dist/BugReportProvider.d.ts +19 -0
- package/dist/BugReportProvider.d.ts.map +1 -0
- package/dist/BugReportProvider.js +103 -0
- package/dist/BugReportProvider.js.map +1 -0
- package/dist/DeviceInfo.d.ts +3 -0
- package/dist/DeviceInfo.d.ts.map +1 -0
- package/dist/DeviceInfo.js +63 -0
- package/dist/DeviceInfo.js.map +1 -0
- package/dist/ErrorBoundary.d.ts +19 -0
- package/dist/ErrorBoundary.d.ts.map +1 -0
- package/dist/ErrorBoundary.js +79 -0
- package/dist/ErrorBoundary.js.map +1 -0
- package/dist/NavigationTracker.d.ts +7 -0
- package/dist/NavigationTracker.d.ts.map +1 -0
- package/dist/NavigationTracker.js +61 -0
- package/dist/NavigationTracker.js.map +1 -0
- package/dist/RingBuffer.d.ts +10 -0
- package/dist/RingBuffer.d.ts.map +1 -0
- package/dist/RingBuffer.js +26 -0
- package/dist/RingBuffer.js.map +1 -0
- package/dist/ScreenCapture.d.ts +3 -0
- package/dist/ScreenCapture.d.ts.map +1 -0
- package/dist/ScreenCapture.js +20 -0
- package/dist/ScreenCapture.js.map +1 -0
- package/dist/ShakeDetector.d.ts +5 -0
- package/dist/ShakeDetector.d.ts.map +1 -0
- package/dist/ShakeDetector.js +44 -0
- package/dist/ShakeDetector.js.map +1 -0
- package/dist/StateCapture.d.ts +13 -0
- package/dist/StateCapture.d.ts.map +1 -0
- package/dist/StateCapture.js +74 -0
- package/dist/StateCapture.js.map +1 -0
- package/dist/__tests__/DeviceInfo.test.d.ts +2 -0
- package/dist/__tests__/DeviceInfo.test.d.ts.map +1 -0
- package/dist/__tests__/DeviceInfo.test.js +63 -0
- package/dist/__tests__/DeviceInfo.test.js.map +1 -0
- package/dist/__tests__/ErrorBoundary.test.d.ts +2 -0
- package/dist/__tests__/ErrorBoundary.test.d.ts.map +1 -0
- package/dist/__tests__/ErrorBoundary.test.js +64 -0
- package/dist/__tests__/ErrorBoundary.test.js.map +1 -0
- package/dist/__tests__/NavigationTracker.test.d.ts +2 -0
- package/dist/__tests__/NavigationTracker.test.d.ts.map +1 -0
- package/dist/__tests__/NavigationTracker.test.js +54 -0
- package/dist/__tests__/NavigationTracker.test.js.map +1 -0
- package/dist/__tests__/RingBuffer.test.d.ts +2 -0
- package/dist/__tests__/RingBuffer.test.d.ts.map +1 -0
- package/dist/__tests__/RingBuffer.test.js +43 -0
- package/dist/__tests__/RingBuffer.test.js.map +1 -0
- package/dist/__tests__/ScreenCapture.test.d.ts +2 -0
- package/dist/__tests__/ScreenCapture.test.d.ts.map +1 -0
- package/dist/__tests__/ScreenCapture.test.js +27 -0
- package/dist/__tests__/ScreenCapture.test.js.map +1 -0
- package/dist/__tests__/ShakeDetector.test.d.ts +2 -0
- package/dist/__tests__/ShakeDetector.test.d.ts.map +1 -0
- package/dist/__tests__/ShakeDetector.test.js +65 -0
- package/dist/__tests__/ShakeDetector.test.js.map +1 -0
- package/dist/__tests__/StateCapture.test.d.ts +2 -0
- package/dist/__tests__/StateCapture.test.d.ts.map +1 -0
- package/dist/__tests__/StateCapture.test.js +128 -0
- package/dist/__tests__/StateCapture.test.js.map +1 -0
- package/dist/__tests__/__mocks__/expo-clipboard.d.ts +3 -0
- package/dist/__tests__/__mocks__/expo-clipboard.d.ts.map +1 -0
- package/dist/__tests__/__mocks__/expo-clipboard.js +6 -0
- package/dist/__tests__/__mocks__/expo-clipboard.js.map +1 -0
- package/dist/__tests__/__mocks__/expo-constants.d.ts +17 -0
- package/dist/__tests__/__mocks__/expo-constants.d.ts.map +1 -0
- package/dist/__tests__/__mocks__/expo-constants.js +8 -0
- package/dist/__tests__/__mocks__/expo-constants.js.map +1 -0
- package/dist/__tests__/__mocks__/expo-device.d.ts +2 -0
- package/dist/__tests__/__mocks__/expo-device.d.ts.map +1 -0
- package/dist/__tests__/__mocks__/expo-device.js +5 -0
- package/dist/__tests__/__mocks__/expo-device.js.map +1 -0
- package/dist/__tests__/__mocks__/expo-file-system-legacy.d.ts +8 -0
- package/dist/__tests__/__mocks__/expo-file-system-legacy.d.ts.map +1 -0
- package/dist/__tests__/__mocks__/expo-file-system-legacy.js +10 -0
- package/dist/__tests__/__mocks__/expo-file-system-legacy.js.map +1 -0
- package/dist/__tests__/__mocks__/expo-localization.d.ts +6 -0
- package/dist/__tests__/__mocks__/expo-localization.d.ts.map +1 -0
- package/dist/__tests__/__mocks__/expo-localization.js +7 -0
- package/dist/__tests__/__mocks__/expo-localization.js.map +1 -0
- package/dist/__tests__/__mocks__/expo-router.d.ts +5 -0
- package/dist/__tests__/__mocks__/expo-router.d.ts.map +1 -0
- package/dist/__tests__/__mocks__/expo-router.js +16 -0
- package/dist/__tests__/__mocks__/expo-router.js.map +1 -0
- package/dist/__tests__/__mocks__/expo-sensors.d.ts +17 -0
- package/dist/__tests__/__mocks__/expo-sensors.d.ts.map +1 -0
- package/dist/__tests__/__mocks__/expo-sensors.js +22 -0
- package/dist/__tests__/__mocks__/expo-sensors.js.map +1 -0
- package/dist/__tests__/__mocks__/react-native-gesture-handler.d.ts +11 -0
- package/dist/__tests__/__mocks__/react-native-gesture-handler.d.ts.map +1 -0
- package/dist/__tests__/__mocks__/react-native-gesture-handler.js +14 -0
- package/dist/__tests__/__mocks__/react-native-gesture-handler.js.map +1 -0
- package/dist/__tests__/__mocks__/react-native-svg.d.ts +4 -0
- package/dist/__tests__/__mocks__/react-native-svg.d.ts.map +1 -0
- package/dist/__tests__/__mocks__/react-native-svg.js +7 -0
- package/dist/__tests__/__mocks__/react-native-svg.js.map +1 -0
- package/dist/__tests__/__mocks__/react-native-view-shot.d.ts +2 -0
- package/dist/__tests__/__mocks__/react-native-view-shot.d.ts.map +1 -0
- package/dist/__tests__/__mocks__/react-native-view-shot.js +5 -0
- package/dist/__tests__/__mocks__/react-native-view-shot.js.map +1 -0
- package/dist/__tests__/__mocks__/react-native.d.ts +20 -0
- package/dist/__tests__/__mocks__/react-native.d.ts.map +1 -0
- package/dist/__tests__/__mocks__/react-native.js +14 -0
- package/dist/__tests__/__mocks__/react-native.js.map +1 -0
- package/dist/__tests__/fileToBase64.test.d.ts +2 -0
- package/dist/__tests__/fileToBase64.test.d.ts.map +1 -0
- package/dist/__tests__/fileToBase64.test.js +21 -0
- package/dist/__tests__/fileToBase64.test.js.map +1 -0
- package/dist/__tests__/integrations/slack.test.d.ts +2 -0
- package/dist/__tests__/integrations/slack.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/slack.test.js +123 -0
- package/dist/__tests__/integrations/slack.test.js.map +1 -0
- package/dist/__tests__/integrations/webhook.test.d.ts +2 -0
- package/dist/__tests__/integrations/webhook.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/webhook.test.js +98 -0
- package/dist/__tests__/integrations/webhook.test.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/index.d.ts +4 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +8 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/slack.d.ts +9 -0
- package/dist/integrations/slack.d.ts.map +1 -0
- package/dist/integrations/slack.js +136 -0
- package/dist/integrations/slack.js.map +1 -0
- package/dist/integrations/types.d.ts +53 -0
- package/dist/integrations/types.d.ts.map +1 -0
- package/dist/integrations/types.js +3 -0
- package/dist/integrations/types.js.map +1 -0
- package/dist/integrations/webhook.d.ts +8 -0
- package/dist/integrations/webhook.d.ts.map +1 -0
- package/dist/integrations/webhook.js +45 -0
- package/dist/integrations/webhook.js.map +1 -0
- package/dist/utils/fileToBase64.d.ts +2 -0
- package/dist/utils/fileToBase64.d.ts.map +1 -0
- package/dist/utils/fileToBase64.js +11 -0
- package/dist/utils/fileToBase64.js.map +1 -0
- package/package.json +77 -0
package/README.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# @bugpulse/react-native
|
|
2
|
+
|
|
3
|
+
Lightweight in-app bug reporting for React Native & Expo. Shake to report. Annotate screenshots. Auto-capture app state, navigation history, and JS errors. Send to Slack or any webhook.
|
|
4
|
+
|
|
5
|
+
## What makes this different
|
|
6
|
+
|
|
7
|
+
Every bug report automatically includes what cross-platform tools don't capture:
|
|
8
|
+
- **Zustand/Redux state snapshots** at the moment of the shake (not when the user hits submit)
|
|
9
|
+
- **Expo Router navigation history** (last 10 routes)
|
|
10
|
+
- **JS error boundary data** (last caught error + component stack)
|
|
11
|
+
- Device info, screenshot with annotation, and user description
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx expo install @bugpulse/react-native react-native-view-shot react-native-svg react-native-gesture-handler expo-sensors expo-device expo-constants
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { BugReportProvider, SlackIntegration } from '@bugpulse/react-native';
|
|
23
|
+
|
|
24
|
+
export default function App() {
|
|
25
|
+
return (
|
|
26
|
+
<BugReportProvider
|
|
27
|
+
integrations={[
|
|
28
|
+
SlackIntegration({
|
|
29
|
+
webhookUrl: 'https://hooks.slack.com/services/...',
|
|
30
|
+
imageUploadKey: 'your-imgbb-api-key',
|
|
31
|
+
}),
|
|
32
|
+
]}
|
|
33
|
+
>
|
|
34
|
+
<YourApp />
|
|
35
|
+
</BugReportProvider>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Shake your phone. That's it.
|
|
41
|
+
|
|
42
|
+
## RN-Specific Diagnostics
|
|
43
|
+
|
|
44
|
+
### State Capture (Zustand)
|
|
45
|
+
|
|
46
|
+
Track Zustand stores to include state snapshots in every bug report:
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { trackStore } from '@bugpulse/react-native';
|
|
50
|
+
import { useAppStore } from './stores/app';
|
|
51
|
+
|
|
52
|
+
// Call once at app startup
|
|
53
|
+
trackStore(useAppStore, { name: 'app' });
|
|
54
|
+
|
|
55
|
+
// Track multiple stores
|
|
56
|
+
trackStore(useCartStore, { name: 'cart' });
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
State is captured at shake time (frozen before the user annotates), so the report reflects the app state when the bug occurred, not when the user hit submit.
|
|
60
|
+
|
|
61
|
+
Call `untrackStore('app')` to stop tracking a store and free the subscription.
|
|
62
|
+
|
|
63
|
+
**Privacy note:** State snapshots are sent as-is. Do not track stores containing passwords, auth tokens, or PII. A redaction API is planned for a future release.
|
|
64
|
+
|
|
65
|
+
### Navigation History (Expo Router)
|
|
66
|
+
|
|
67
|
+
Auto-captures route changes when using Expo Router:
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { useNavigationTracker } from '@bugpulse/react-native';
|
|
71
|
+
|
|
72
|
+
// Add to your root layout
|
|
73
|
+
export default function RootLayout() {
|
|
74
|
+
useNavigationTracker();
|
|
75
|
+
|
|
76
|
+
return <Slot />;
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Each bug report includes the last 10 routes with pathnames and timestamps.
|
|
81
|
+
|
|
82
|
+
Not using Expo Router? Use the `screenNameProvider` prop on `BugReportProvider` to manually pass the current screen name.
|
|
83
|
+
|
|
84
|
+
### Error Boundary
|
|
85
|
+
|
|
86
|
+
Wrap your app (or specific subtrees) to capture JS errors:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { BugPulseErrorBoundary } from '@bugpulse/react-native';
|
|
90
|
+
|
|
91
|
+
export default function App() {
|
|
92
|
+
return (
|
|
93
|
+
<BugPulseErrorBoundary>
|
|
94
|
+
<BugReportProvider integrations={[...]}>
|
|
95
|
+
<YourApp />
|
|
96
|
+
</BugReportProvider>
|
|
97
|
+
</BugPulseErrorBoundary>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Caught errors are passively stored and attached to the next bug report. The boundary renders a minimal fallback on error.
|
|
103
|
+
|
|
104
|
+
## Integrations
|
|
105
|
+
|
|
106
|
+
### Slack
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
SlackIntegration({
|
|
110
|
+
webhookUrl: 'https://hooks.slack.com/services/...',
|
|
111
|
+
imageUploadKey: 'your-imgbb-api-key', // for screenshot uploads
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Slack messages include a truncated summary of diagnostics (last 3 state snapshots, last 5 routes, error info) to fit within webhook payload limits.
|
|
116
|
+
|
|
117
|
+
### Webhook
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
WebhookIntegration({
|
|
121
|
+
url: 'https://your-api.com/bugs',
|
|
122
|
+
headers: { Authorization: 'Bearer ...' },
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Webhook payloads include the full diagnostics object with all state snapshots, navigation history, and error data.
|
|
127
|
+
|
|
128
|
+
### Custom
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
const MyIntegration: Integration = {
|
|
132
|
+
name: 'my-integration',
|
|
133
|
+
async send(report) {
|
|
134
|
+
// report.diagnostics.stateSnapshots — array of { name, state, timestamp, truncated }
|
|
135
|
+
// report.diagnostics.navHistory — array of { pathname, segments, timestamp }
|
|
136
|
+
// report.diagnostics.lastError — { message, stack, componentStack, timestamp } | null
|
|
137
|
+
return { success: true };
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Props
|
|
143
|
+
|
|
144
|
+
| Prop | Type | Default | Description |
|
|
145
|
+
|------|------|---------|-------------|
|
|
146
|
+
| `integrations` | `Integration[]` | required | Where bug reports are sent |
|
|
147
|
+
| `metadata` | `Record<string, string>` or `() => Record` | `{}` | App context (user ID, plan, etc.) |
|
|
148
|
+
| `shakeThreshold` | `number` | `1.8` | Accelerometer sensitivity |
|
|
149
|
+
| `shakeEnabled` | `boolean` | `true` | Enable/disable shake trigger |
|
|
150
|
+
| `screenNameProvider` | `() => string` | auto-detect | Current screen name |
|
|
151
|
+
| `enabled` | `boolean` | `true` | Enable/disable the SDK entirely |
|
|
152
|
+
|
|
153
|
+
## Programmatic Trigger
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
import { useBugReport } from '@bugpulse/react-native';
|
|
157
|
+
|
|
158
|
+
function SettingsScreen() {
|
|
159
|
+
const { triggerBugReport } = useBugReport();
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<Button title="Report Bug" onPress={triggerBugReport} />
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## How It Works
|
|
168
|
+
|
|
169
|
+
1. User shakes phone (or triggers programmatically)
|
|
170
|
+
2. SDK freezes state snapshots and navigation history
|
|
171
|
+
3. SDK captures a screenshot
|
|
172
|
+
4. User annotates the screenshot (draw circles, arrows)
|
|
173
|
+
5. User adds a description
|
|
174
|
+
6. SDK collects device info, attaches diagnostics, and sends to your integrations
|
|
175
|
+
|
|
176
|
+
## Requirements
|
|
177
|
+
|
|
178
|
+
- Expo SDK 50+
|
|
179
|
+
- React Native 0.72+
|
|
180
|
+
- Dev build required for screenshots (Expo Go gets graceful degradation)
|
|
181
|
+
- Expo Router (optional, for auto navigation tracking)
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface AnnotationCanvasProps {
|
|
3
|
+
screenshotUri: string;
|
|
4
|
+
onAnnotationComplete: (annotatedUri: string) => void;
|
|
5
|
+
onSkip: () => void;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function AnnotationCanvas({ screenshotUri, onAnnotationComplete, onSkip, width, height, }: AnnotationCanvasProps): React.JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=AnnotationCanvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnnotationCanvas.d.ts","sourceRoot":"","sources":["../src/AnnotationCanvas.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAwC,MAAM,OAAO,CAAC;AAU7D,UAAU,qBAAqB;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAMD,wBAAgB,gBAAgB,CAAC,EAC/B,aAAa,EACb,oBAAoB,EACpB,MAAM,EACN,KAAK,EACL,MAAM,GACP,EAAE,qBAAqB,qBA2KvB"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.AnnotationCanvas = AnnotationCanvas;
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const react_native_1 = require("react-native");
|
|
39
|
+
const react_native_svg_1 = __importStar(require("react-native-svg"));
|
|
40
|
+
const react_native_gesture_handler_1 = require("react-native-gesture-handler");
|
|
41
|
+
const react_native_view_shot_1 = require("react-native-view-shot");
|
|
42
|
+
function AnnotationCanvas({ screenshotUri, onAnnotationComplete, onSkip, width, height, }) {
|
|
43
|
+
const [paths, setPaths] = (0, react_1.useState)([]);
|
|
44
|
+
const [currentPath, setCurrentPath] = (0, react_1.useState)('');
|
|
45
|
+
const currentPathRef = (0, react_1.useRef)('');
|
|
46
|
+
const compositeRef = (0, react_1.useRef)(null);
|
|
47
|
+
const [isSaving, setIsSaving] = (0, react_1.useState)(false);
|
|
48
|
+
const panGesture = react_native_gesture_handler_1.Gesture.Pan()
|
|
49
|
+
.onStart((event) => {
|
|
50
|
+
const newPath = `M${event.x},${event.y}`;
|
|
51
|
+
currentPathRef.current = newPath;
|
|
52
|
+
setCurrentPath(newPath);
|
|
53
|
+
})
|
|
54
|
+
.onUpdate((event) => {
|
|
55
|
+
const updated = `${currentPathRef.current} L${event.x},${event.y}`;
|
|
56
|
+
currentPathRef.current = updated;
|
|
57
|
+
setCurrentPath(updated);
|
|
58
|
+
})
|
|
59
|
+
.onEnd(() => {
|
|
60
|
+
const pathToSave = currentPathRef.current;
|
|
61
|
+
if (pathToSave) {
|
|
62
|
+
setPaths((prev) => [...prev, { d: pathToSave }]);
|
|
63
|
+
currentPathRef.current = '';
|
|
64
|
+
setCurrentPath('');
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
.minDistance(0);
|
|
68
|
+
const handleUndo = (0, react_1.useCallback)(() => {
|
|
69
|
+
setPaths((prev) => prev.slice(0, -1));
|
|
70
|
+
}, []);
|
|
71
|
+
const handleClear = (0, react_1.useCallback)(() => {
|
|
72
|
+
setPaths([]);
|
|
73
|
+
}, []);
|
|
74
|
+
const handleDone = (0, react_1.useCallback)(async () => {
|
|
75
|
+
if (isSaving)
|
|
76
|
+
return;
|
|
77
|
+
setIsSaving(true);
|
|
78
|
+
try {
|
|
79
|
+
const uri = await (0, react_native_view_shot_1.captureRef)(compositeRef, {
|
|
80
|
+
format: 'png',
|
|
81
|
+
quality: 0.9,
|
|
82
|
+
result: 'tmpfile',
|
|
83
|
+
});
|
|
84
|
+
onAnnotationComplete(uri);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
onSkip();
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
setIsSaving(false);
|
|
91
|
+
}
|
|
92
|
+
}, [isSaving, onAnnotationComplete, onSkip]);
|
|
93
|
+
return (<react_native_gesture_handler_1.GestureHandlerRootView style={{ flex: 1 }}>
|
|
94
|
+
<react_native_1.View style={{ flex: 1 }}>
|
|
95
|
+
<react_native_1.View ref={compositeRef} collapsable={false} style={{ width, height, position: 'relative' }}>
|
|
96
|
+
<react_native_1.Image source={{ uri: screenshotUri }} style={{ width, height }} resizeMode="contain"/>
|
|
97
|
+
<react_native_gesture_handler_1.GestureDetector gesture={panGesture}>
|
|
98
|
+
<react_native_svg_1.default style={{
|
|
99
|
+
position: 'absolute',
|
|
100
|
+
top: 0,
|
|
101
|
+
left: 0,
|
|
102
|
+
width,
|
|
103
|
+
height,
|
|
104
|
+
}}>
|
|
105
|
+
{paths.map((path, index) => (<react_native_svg_1.Path key={index} d={path.d} stroke="#FF3B30" strokeWidth={3} fill="none" strokeLinecap="round" strokeLinejoin="round"/>))}
|
|
106
|
+
{currentPath ? (<react_native_svg_1.Path d={currentPath} stroke="#FF3B30" strokeWidth={3} fill="none" strokeLinecap="round" strokeLinejoin="round"/>) : null}
|
|
107
|
+
</react_native_svg_1.default>
|
|
108
|
+
</react_native_gesture_handler_1.GestureDetector>
|
|
109
|
+
</react_native_1.View>
|
|
110
|
+
|
|
111
|
+
<react_native_1.View style={{
|
|
112
|
+
flexDirection: 'row',
|
|
113
|
+
justifyContent: 'space-between',
|
|
114
|
+
paddingHorizontal: 16,
|
|
115
|
+
paddingVertical: 12,
|
|
116
|
+
}}>
|
|
117
|
+
<react_native_1.View style={{ flexDirection: 'row', gap: 12 }}>
|
|
118
|
+
<react_native_1.TouchableOpacity onPress={handleUndo} disabled={paths.length === 0} accessibilityRole="button" accessibilityLabel="Undo last drawing">
|
|
119
|
+
<react_native_1.Text style={{
|
|
120
|
+
fontSize: 16,
|
|
121
|
+
color: paths.length === 0 ? '#ccc' : '#007AFF',
|
|
122
|
+
}}>
|
|
123
|
+
Undo
|
|
124
|
+
</react_native_1.Text>
|
|
125
|
+
</react_native_1.TouchableOpacity>
|
|
126
|
+
<react_native_1.TouchableOpacity onPress={handleClear} disabled={paths.length === 0} accessibilityRole="button" accessibilityLabel="Clear all drawings">
|
|
127
|
+
<react_native_1.Text style={{
|
|
128
|
+
fontSize: 16,
|
|
129
|
+
color: paths.length === 0 ? '#ccc' : '#FF3B30',
|
|
130
|
+
}}>
|
|
131
|
+
Clear
|
|
132
|
+
</react_native_1.Text>
|
|
133
|
+
</react_native_1.TouchableOpacity>
|
|
134
|
+
</react_native_1.View>
|
|
135
|
+
|
|
136
|
+
<react_native_1.View style={{ flexDirection: 'row', gap: 12 }}>
|
|
137
|
+
<react_native_1.TouchableOpacity onPress={onSkip} accessibilityRole="button" accessibilityLabel="Skip annotation">
|
|
138
|
+
<react_native_1.Text style={{ fontSize: 16, color: '#8E8E93' }}>Skip</react_native_1.Text>
|
|
139
|
+
</react_native_1.TouchableOpacity>
|
|
140
|
+
<react_native_1.TouchableOpacity onPress={handleDone} disabled={isSaving} accessibilityRole="button" accessibilityLabel="Done annotating">
|
|
141
|
+
<react_native_1.Text style={{
|
|
142
|
+
fontSize: 16,
|
|
143
|
+
fontWeight: '600',
|
|
144
|
+
color: isSaving ? '#ccc' : '#007AFF',
|
|
145
|
+
}}>
|
|
146
|
+
Done
|
|
147
|
+
</react_native_1.Text>
|
|
148
|
+
</react_native_1.TouchableOpacity>
|
|
149
|
+
</react_native_1.View>
|
|
150
|
+
</react_native_1.View>
|
|
151
|
+
</react_native_1.View>
|
|
152
|
+
</react_native_gesture_handler_1.GestureHandlerRootView>);
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=AnnotationCanvas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnnotationCanvas.js","sourceRoot":"","sources":["../src/AnnotationCanvas.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Integration, BugReport } from './integrations/types';
|
|
3
|
+
interface BugReportModalProps {
|
|
4
|
+
visible: boolean;
|
|
5
|
+
screenshotUri: string | null;
|
|
6
|
+
integrations: Integration[];
|
|
7
|
+
metadata: Record<string, string> | (() => Record<string, string>);
|
|
8
|
+
screenNameProvider: () => string;
|
|
9
|
+
onClose: () => void;
|
|
10
|
+
onSubmitSuccess?: () => void;
|
|
11
|
+
onError?: (error: Error, report: BugReport) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function BugReportModal({ visible, screenshotUri, integrations, metadata, screenNameProvider, onClose, onSubmitSuccess, onError, }: BugReportModalProps): React.JSX.Element;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=BugReportModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BugReportModal.d.ts","sourceRoot":"","sources":["../src/BugReportModal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAmBhE,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEnE,UAAU,mBAAmB;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClE,kBAAkB,EAAE,MAAM,MAAM,CAAC;IACjC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,KAAK,IAAI,CAAC;CACrD;AAMD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,kBAAkB,EAClB,OAAO,EACP,eAAe,EACf,OAAO,GACR,EAAE,mBAAmB,qBA+XrB"}
|