react-native-bug-reporter 1.0.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.
Files changed (152) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +217 -0
  3. package/android/build.gradle +49 -0
  4. package/android/src/main/AndroidManifest.xml +11 -0
  5. package/android/src/main/java/com/bugreporter/ScreenshotDetectorModule.kt +227 -0
  6. package/android/src/main/java/com/bugreporter/ScreenshotDetectorPackage.kt +20 -0
  7. package/ios/RNBugReporterScreenshot.m +11 -0
  8. package/ios/RNBugReporterScreenshot.swift +113 -0
  9. package/lib/commonjs/BugReporterProvider.js +139 -0
  10. package/lib/commonjs/BugReporterProvider.js.map +1 -0
  11. package/lib/commonjs/collectors/appInfo.js +21 -0
  12. package/lib/commonjs/collectors/appInfo.js.map +1 -0
  13. package/lib/commonjs/collectors/collectContext.js +45 -0
  14. package/lib/commonjs/collectors/collectContext.js.map +1 -0
  15. package/lib/commonjs/collectors/deviceInfo.js +41 -0
  16. package/lib/commonjs/collectors/deviceInfo.js.map +1 -0
  17. package/lib/commonjs/collectors/networkInfo.js +33 -0
  18. package/lib/commonjs/collectors/networkInfo.js.map +1 -0
  19. package/lib/commonjs/components/BugReportAdminScreen.js +225 -0
  20. package/lib/commonjs/components/BugReportAdminScreen.js.map +1 -0
  21. package/lib/commonjs/components/BugReportModal.js +341 -0
  22. package/lib/commonjs/components/BugReportModal.js.map +1 -0
  23. package/lib/commonjs/components/ScreenshotEditor.js +466 -0
  24. package/lib/commonjs/components/ScreenshotEditor.js.map +1 -0
  25. package/lib/commonjs/components/ScreenshotPreview.js +134 -0
  26. package/lib/commonjs/components/ScreenshotPreview.js.map +1 -0
  27. package/lib/commonjs/components/SeveritySelector.js +65 -0
  28. package/lib/commonjs/components/SeveritySelector.js.map +1 -0
  29. package/lib/commonjs/context/BugReporterContext.js +24 -0
  30. package/lib/commonjs/context/BugReporterContext.js.map +1 -0
  31. package/lib/commonjs/hooks/useScreenshotDetector.js +22 -0
  32. package/lib/commonjs/hooks/useScreenshotDetector.js.map +1 -0
  33. package/lib/commonjs/index.js +87 -0
  34. package/lib/commonjs/index.js.map +1 -0
  35. package/lib/commonjs/native/ScreenshotDetector.js +72 -0
  36. package/lib/commonjs/native/ScreenshotDetector.js.map +1 -0
  37. package/lib/commonjs/navigation/screenTracker.js +47 -0
  38. package/lib/commonjs/navigation/screenTracker.js.map +1 -0
  39. package/lib/commonjs/package.json +1 -0
  40. package/lib/commonjs/services/bugReportService.js +61 -0
  41. package/lib/commonjs/services/bugReportService.js.map +1 -0
  42. package/lib/commonjs/services/supabaseService.js +166 -0
  43. package/lib/commonjs/services/supabaseService.js.map +1 -0
  44. package/lib/commonjs/theme.js +28 -0
  45. package/lib/commonjs/theme.js.map +1 -0
  46. package/lib/commonjs/types.js +35 -0
  47. package/lib/commonjs/types.js.map +1 -0
  48. package/lib/commonjs/utils/logger.js +29 -0
  49. package/lib/commonjs/utils/logger.js.map +1 -0
  50. package/lib/module/BugReporterProvider.js +134 -0
  51. package/lib/module/BugReporterProvider.js.map +1 -0
  52. package/lib/module/collectors/appInfo.js +16 -0
  53. package/lib/module/collectors/appInfo.js.map +1 -0
  54. package/lib/module/collectors/collectContext.js +41 -0
  55. package/lib/module/collectors/collectContext.js.map +1 -0
  56. package/lib/module/collectors/deviceInfo.js +37 -0
  57. package/lib/module/collectors/deviceInfo.js.map +1 -0
  58. package/lib/module/collectors/networkInfo.js +29 -0
  59. package/lib/module/collectors/networkInfo.js.map +1 -0
  60. package/lib/module/components/BugReportAdminScreen.js +221 -0
  61. package/lib/module/components/BugReportAdminScreen.js.map +1 -0
  62. package/lib/module/components/BugReportModal.js +337 -0
  63. package/lib/module/components/BugReportModal.js.map +1 -0
  64. package/lib/module/components/ScreenshotEditor.js +461 -0
  65. package/lib/module/components/ScreenshotEditor.js.map +1 -0
  66. package/lib/module/components/ScreenshotPreview.js +130 -0
  67. package/lib/module/components/ScreenshotPreview.js.map +1 -0
  68. package/lib/module/components/SeveritySelector.js +61 -0
  69. package/lib/module/components/SeveritySelector.js.map +1 -0
  70. package/lib/module/context/BugReporterContext.js +19 -0
  71. package/lib/module/context/BugReporterContext.js.map +1 -0
  72. package/lib/module/hooks/useScreenshotDetector.js +18 -0
  73. package/lib/module/hooks/useScreenshotDetector.js.map +1 -0
  74. package/lib/module/index.js +32 -0
  75. package/lib/module/index.js.map +1 -0
  76. package/lib/module/native/ScreenshotDetector.js +68 -0
  77. package/lib/module/native/ScreenshotDetector.js.map +1 -0
  78. package/lib/module/navigation/screenTracker.js +41 -0
  79. package/lib/module/navigation/screenTracker.js.map +1 -0
  80. package/lib/module/services/bugReportService.js +57 -0
  81. package/lib/module/services/bugReportService.js.map +1 -0
  82. package/lib/module/services/supabaseService.js +159 -0
  83. package/lib/module/services/supabaseService.js.map +1 -0
  84. package/lib/module/theme.js +23 -0
  85. package/lib/module/theme.js.map +1 -0
  86. package/lib/module/types.js +31 -0
  87. package/lib/module/types.js.map +1 -0
  88. package/lib/module/utils/logger.js +25 -0
  89. package/lib/module/utils/logger.js.map +1 -0
  90. package/lib/typescript/src/BugReporterProvider.d.ts +18 -0
  91. package/lib/typescript/src/BugReporterProvider.d.ts.map +1 -0
  92. package/lib/typescript/src/collectors/appInfo.d.ts +6 -0
  93. package/lib/typescript/src/collectors/appInfo.d.ts.map +1 -0
  94. package/lib/typescript/src/collectors/collectContext.d.ts +7 -0
  95. package/lib/typescript/src/collectors/collectContext.d.ts.map +1 -0
  96. package/lib/typescript/src/collectors/deviceInfo.d.ts +7 -0
  97. package/lib/typescript/src/collectors/deviceInfo.d.ts.map +1 -0
  98. package/lib/typescript/src/collectors/networkInfo.d.ts +6 -0
  99. package/lib/typescript/src/collectors/networkInfo.d.ts.map +1 -0
  100. package/lib/typescript/src/components/BugReportAdminScreen.d.ts +11 -0
  101. package/lib/typescript/src/components/BugReportAdminScreen.d.ts.map +1 -0
  102. package/lib/typescript/src/components/BugReportModal.d.ts +20 -0
  103. package/lib/typescript/src/components/BugReportModal.d.ts.map +1 -0
  104. package/lib/typescript/src/components/ScreenshotEditor.d.ts +16 -0
  105. package/lib/typescript/src/components/ScreenshotEditor.d.ts.map +1 -0
  106. package/lib/typescript/src/components/ScreenshotPreview.d.ts +11 -0
  107. package/lib/typescript/src/components/ScreenshotPreview.d.ts.map +1 -0
  108. package/lib/typescript/src/components/SeveritySelector.d.ts +10 -0
  109. package/lib/typescript/src/components/SeveritySelector.d.ts.map +1 -0
  110. package/lib/typescript/src/context/BugReporterContext.d.ts +20 -0
  111. package/lib/typescript/src/context/BugReporterContext.d.ts.map +1 -0
  112. package/lib/typescript/src/hooks/useScreenshotDetector.d.ts +7 -0
  113. package/lib/typescript/src/hooks/useScreenshotDetector.d.ts.map +1 -0
  114. package/lib/typescript/src/index.d.ts +26 -0
  115. package/lib/typescript/src/index.d.ts.map +1 -0
  116. package/lib/typescript/src/native/ScreenshotDetector.d.ts +15 -0
  117. package/lib/typescript/src/native/ScreenshotDetector.d.ts.map +1 -0
  118. package/lib/typescript/src/navigation/screenTracker.d.ts +7 -0
  119. package/lib/typescript/src/navigation/screenTracker.d.ts.map +1 -0
  120. package/lib/typescript/src/services/bugReportService.d.ts +17 -0
  121. package/lib/typescript/src/services/bugReportService.d.ts.map +1 -0
  122. package/lib/typescript/src/services/supabaseService.d.ts +38 -0
  123. package/lib/typescript/src/services/supabaseService.d.ts.map +1 -0
  124. package/lib/typescript/src/theme.d.ts +7 -0
  125. package/lib/typescript/src/theme.d.ts.map +1 -0
  126. package/lib/typescript/src/types.d.ts +144 -0
  127. package/lib/typescript/src/types.d.ts.map +1 -0
  128. package/lib/typescript/src/utils/logger.d.ts +7 -0
  129. package/lib/typescript/src/utils/logger.d.ts.map +1 -0
  130. package/package.json +100 -0
  131. package/react-native-bug-reporter.podspec +22 -0
  132. package/react-native.config.js +18 -0
  133. package/src/BugReporterProvider.tsx +178 -0
  134. package/src/collectors/appInfo.ts +15 -0
  135. package/src/collectors/collectContext.ts +47 -0
  136. package/src/collectors/deviceInfo.ts +51 -0
  137. package/src/collectors/networkInfo.ts +31 -0
  138. package/src/components/BugReportAdminScreen.tsx +160 -0
  139. package/src/components/BugReportModal.tsx +315 -0
  140. package/src/components/ScreenshotEditor.tsx +410 -0
  141. package/src/components/ScreenshotPreview.tsx +98 -0
  142. package/src/components/SeveritySelector.tsx +59 -0
  143. package/src/context/BugReporterContext.ts +29 -0
  144. package/src/hooks/useScreenshotDetector.ts +20 -0
  145. package/src/index.ts +51 -0
  146. package/src/native/ScreenshotDetector.ts +87 -0
  147. package/src/navigation/screenTracker.ts +40 -0
  148. package/src/services/bugReportService.ts +81 -0
  149. package/src/services/supabaseService.ts +195 -0
  150. package/src/theme.ts +23 -0
  151. package/src/types.ts +156 -0
  152. package/src/utils/logger.ts +24 -0
@@ -0,0 +1,315 @@
1
+ import { useEffect, useState } from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ KeyboardAvoidingView,
5
+ Modal,
6
+ Platform,
7
+ ScrollView,
8
+ StyleSheet,
9
+ Text,
10
+ TextInput,
11
+ TouchableOpacity,
12
+ View,
13
+ } from 'react-native';
14
+ import type {
15
+ BugReportInput,
16
+ BugSeverity,
17
+ CollectedContext,
18
+ ScreenshotData,
19
+ SubmitState,
20
+ } from '../types';
21
+ import type { ResolvedTheme } from '../theme';
22
+ import { SeveritySelector } from './SeveritySelector';
23
+ import { ScreenshotPreview } from './ScreenshotPreview';
24
+
25
+ interface Props {
26
+ visible: boolean;
27
+ screenshot: ScreenshotData | null;
28
+ context: CollectedContext | null;
29
+ /** Show the "Automatically attached" summary. Defaults to true. */
30
+ showContext?: boolean;
31
+ submitState: SubmitState;
32
+ errorMessage?: string | null;
33
+ theme: ResolvedTheme;
34
+ onRemoveScreenshot: () => void;
35
+ /** Open the (top-level) screenshot annotation editor. */
36
+ onRequestEdit: () => void;
37
+ onSubmit: (input: BugReportInput) => void;
38
+ onClose: () => void;
39
+ }
40
+
41
+ const SUBMIT_LABELS: Partial<Record<SubmitState, string>> = {
42
+ collecting: 'Collecting…',
43
+ uploading: 'Uploading…',
44
+ saving: 'Saving…',
45
+ };
46
+
47
+ export function BugReportModal({
48
+ visible,
49
+ screenshot,
50
+ context,
51
+ showContext = true,
52
+ submitState,
53
+ errorMessage,
54
+ theme,
55
+ onRemoveScreenshot,
56
+ onRequestEdit,
57
+ onSubmit,
58
+ onClose,
59
+ }: Props) {
60
+ const [title, setTitle] = useState('');
61
+ const [description, setDescription] = useState('');
62
+ const [severity, setSeverity] = useState<BugSeverity>('medium');
63
+ const [touched, setTouched] = useState(false);
64
+
65
+ // Reset the form whenever the modal is freshly opened.
66
+ useEffect(() => {
67
+ if (visible) {
68
+ setTitle('');
69
+ setDescription('');
70
+ setSeverity('medium');
71
+ setTouched(false);
72
+ }
73
+ }, [visible]);
74
+
75
+ const busy =
76
+ submitState === 'collecting' ||
77
+ submitState === 'uploading' ||
78
+ submitState === 'saving';
79
+ const titleValid = title.trim().length > 0;
80
+
81
+ const handleSubmit = () => {
82
+ setTouched(true);
83
+ if (!titleValid || busy) return;
84
+ onSubmit({ title, description, severity });
85
+ };
86
+
87
+ return (
88
+ <Modal
89
+ visible={visible}
90
+ transparent
91
+ animationType="slide"
92
+ onRequestClose={busy ? undefined : onClose}>
93
+ <KeyboardAvoidingView
94
+ style={styles.backdrop}
95
+ behavior={Platform.OS === 'ios' ? 'padding' : undefined}>
96
+ <View style={[styles.sheet, { backgroundColor: theme.backgroundColor }]}>
97
+ <View style={styles.handle} />
98
+ <View style={styles.header}>
99
+ <Text style={[styles.heading, { color: theme.textColor }]}>
100
+ Report a bug
101
+ </Text>
102
+ <TouchableOpacity
103
+ onPress={onClose}
104
+ disabled={busy}
105
+ accessibilityRole="button"
106
+ accessibilityLabel="Close">
107
+ <Text style={[styles.close, { color: theme.mutedColor, opacity: busy ? 0.4 : 1 }]}>
108
+
109
+ </Text>
110
+ </TouchableOpacity>
111
+ </View>
112
+
113
+ <ScrollView
114
+ keyboardShouldPersistTaps="handled"
115
+ contentContainerStyle={styles.body}>
116
+ <ScreenshotPreview
117
+ screenshot={screenshot}
118
+ onRemove={onRemoveScreenshot}
119
+ onEdit={onRequestEdit}
120
+ theme={theme}
121
+ />
122
+
123
+ <Text style={[styles.label, { color: theme.textColor }]}>Title *</Text>
124
+ <TextInput
125
+ value={title}
126
+ onChangeText={setTitle}
127
+ placeholder="Short summary of the issue"
128
+ placeholderTextColor={theme.mutedColor}
129
+ editable={!busy}
130
+ style={[
131
+ styles.input,
132
+ {
133
+ color: theme.textColor,
134
+ borderColor:
135
+ touched && !titleValid ? theme.errorColor : theme.borderColor,
136
+ },
137
+ ]}
138
+ />
139
+ {touched && !titleValid && (
140
+ <Text style={[styles.errorText, { color: theme.errorColor }]}>
141
+ A title is required
142
+ </Text>
143
+ )}
144
+
145
+ <Text style={[styles.label, { color: theme.textColor }]}>Description</Text>
146
+ <TextInput
147
+ value={description}
148
+ onChangeText={setDescription}
149
+ placeholder="What happened? What did you expect?"
150
+ placeholderTextColor={theme.mutedColor}
151
+ editable={!busy}
152
+ multiline
153
+ numberOfLines={4}
154
+ style={[
155
+ styles.input,
156
+ styles.textArea,
157
+ { color: theme.textColor, borderColor: theme.borderColor },
158
+ ]}
159
+ />
160
+
161
+ <Text style={[styles.label, { color: theme.textColor }]}>Severity</Text>
162
+ <SeveritySelector value={severity} onChange={setSeverity} theme={theme} />
163
+
164
+ {showContext && context && (
165
+ <View style={[styles.context, { borderColor: theme.borderColor }]}>
166
+ <Text style={[styles.contextTitle, { color: theme.mutedColor }]}>
167
+ Automatically attached
168
+ </Text>
169
+ <ContextRow theme={theme} label="Screen" value={context.currentScreen || '—'} />
170
+ <ContextRow
171
+ theme={theme}
172
+ label="User"
173
+ value={context.user.name || context.user.email || 'Anonymous'}
174
+ />
175
+ <ContextRow
176
+ theme={theme}
177
+ label="App"
178
+ value={`${context.app.version} (${context.app.buildNumber})`}
179
+ />
180
+ <ContextRow
181
+ theme={theme}
182
+ label="Device"
183
+ value={`${context.device.brand} ${context.device.model} · ${context.device.systemName} ${context.device.systemVersion}`}
184
+ />
185
+ <ContextRow
186
+ theme={theme}
187
+ label="Network"
188
+ value={context.network.type}
189
+ />
190
+ </View>
191
+ )}
192
+
193
+ {errorMessage && (
194
+ <Text style={[styles.errorText, { color: theme.errorColor }]}>
195
+ {errorMessage}
196
+ </Text>
197
+ )}
198
+ </ScrollView>
199
+
200
+ <View style={[styles.footer, { borderColor: theme.borderColor }]}>
201
+ <TouchableOpacity
202
+ accessibilityRole="button"
203
+ onPress={handleSubmit}
204
+ disabled={busy}
205
+ style={[
206
+ styles.submit,
207
+ { backgroundColor: theme.primaryColor, opacity: busy ? 0.7 : 1 },
208
+ ]}>
209
+ {busy ? (
210
+ <View style={styles.submitBusy}>
211
+ <ActivityIndicator color="#fff" size="small" />
212
+ <Text style={styles.submitText}>
213
+ {SUBMIT_LABELS[submitState] || 'Submitting…'}
214
+ </Text>
215
+ </View>
216
+ ) : (
217
+ <Text style={styles.submitText}>Submit report</Text>
218
+ )}
219
+ </TouchableOpacity>
220
+ </View>
221
+ </View>
222
+ </KeyboardAvoidingView>
223
+ </Modal>
224
+ );
225
+ }
226
+
227
+ function ContextRow({
228
+ label,
229
+ value,
230
+ theme,
231
+ }: {
232
+ label: string;
233
+ value: string;
234
+ theme: ResolvedTheme;
235
+ }) {
236
+ return (
237
+ <View style={styles.contextRow}>
238
+ <Text style={[styles.contextLabel, { color: theme.mutedColor }]}>{label}</Text>
239
+ <Text style={[styles.contextValue, { color: theme.textColor }]} numberOfLines={1}>
240
+ {value}
241
+ </Text>
242
+ </View>
243
+ );
244
+ }
245
+
246
+ const styles = StyleSheet.create({
247
+ backdrop: {
248
+ flex: 1,
249
+ justifyContent: 'flex-end',
250
+ backgroundColor: 'rgba(0,0,0,0.45)',
251
+ },
252
+ sheet: {
253
+ maxHeight: '92%',
254
+ borderTopLeftRadius: 20,
255
+ borderTopRightRadius: 20,
256
+ paddingTop: 8,
257
+ },
258
+ handle: {
259
+ alignSelf: 'center',
260
+ width: 40,
261
+ height: 4,
262
+ borderRadius: 2,
263
+ backgroundColor: '#cbd5e1',
264
+ marginBottom: 8,
265
+ },
266
+ header: {
267
+ flexDirection: 'row',
268
+ alignItems: 'center',
269
+ justifyContent: 'space-between',
270
+ paddingHorizontal: 20,
271
+ paddingBottom: 8,
272
+ },
273
+ heading: { fontSize: 18, fontWeight: '700' },
274
+ close: { fontSize: 18, fontWeight: '600', padding: 4 },
275
+ body: { paddingHorizontal: 20, paddingBottom: 16, gap: 6 },
276
+ label: { fontSize: 13, fontWeight: '600', marginTop: 14, marginBottom: 6 },
277
+ input: {
278
+ borderWidth: 1,
279
+ borderRadius: 10,
280
+ paddingHorizontal: 12,
281
+ paddingVertical: Platform.OS === 'ios' ? 12 : 8,
282
+ fontSize: 15,
283
+ },
284
+ textArea: { height: 100, textAlignVertical: 'top' },
285
+ errorText: { fontSize: 12, marginTop: 4 },
286
+ context: {
287
+ marginTop: 18,
288
+ borderWidth: 1,
289
+ borderRadius: 12,
290
+ padding: 12,
291
+ gap: 6,
292
+ },
293
+ contextTitle: {
294
+ fontSize: 11,
295
+ fontWeight: '700',
296
+ textTransform: 'uppercase',
297
+ letterSpacing: 0.5,
298
+ marginBottom: 4,
299
+ },
300
+ contextRow: { flexDirection: 'row', justifyContent: 'space-between', gap: 12 },
301
+ contextLabel: { fontSize: 12 },
302
+ contextValue: { fontSize: 12, flex: 1, textAlign: 'right' },
303
+ footer: {
304
+ padding: 16,
305
+ paddingBottom: Platform.OS === 'ios' ? 28 : 16,
306
+ borderTopWidth: 1,
307
+ },
308
+ submit: {
309
+ borderRadius: 12,
310
+ paddingVertical: 15,
311
+ alignItems: 'center',
312
+ },
313
+ submitBusy: { flexDirection: 'row', alignItems: 'center', gap: 8 },
314
+ submitText: { color: '#fff', fontSize: 16, fontWeight: '700' },
315
+ });