@mindedsolutions/bug-reporter-sdk 0.3.0 → 0.3.2
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/components/FeatureBoardLink.d.ts +8 -0
- package/dist/components/FeatureBoardLink.js +28 -0
- package/dist/components/FeatureBoardModal.d.ts +1 -0
- package/dist/components/FeatureBoardModal.js +57 -0
- package/dist/context/BugReporterContext.d.ts +3 -0
- package/dist/context/BugReporterProvider.js +5 -1
- package/dist/hooks/useFeatureBoard.d.ts +6 -0
- package/dist/hooks/useFeatureBoard.js +16 -0
- package/dist/hooks/useScreenCapture.js +11 -1
- package/dist/i18n/en.js +2 -0
- package/dist/i18n/fr.js +2 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +5 -1
- package/dist/types.d.ts +8 -0
- package/package.json +7 -5
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ViewStyle, TextStyle } from 'react-native';
|
|
2
|
+
interface FeatureBoardLinkProps {
|
|
3
|
+
style?: ViewStyle;
|
|
4
|
+
textStyle?: TextStyle;
|
|
5
|
+
label?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function FeatureBoardLink({ style, textStyle, label }: FeatureBoardLinkProps): import("react/jsx-runtime").JSX.Element | null;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FeatureBoardLink = FeatureBoardLink;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_native_1 = require("react-native");
|
|
6
|
+
const useFeatureBoard_1 = require("../hooks/useFeatureBoard");
|
|
7
|
+
const useBugReporter_1 = require("../hooks/useBugReporter");
|
|
8
|
+
function FeatureBoardLink({ style, textStyle, label }) {
|
|
9
|
+
const { boardUrl, openFeatureBoard } = (0, useFeatureBoard_1.useFeatureBoard)();
|
|
10
|
+
const { translations } = (0, useBugReporter_1.useBugReporter)();
|
|
11
|
+
if (!boardUrl)
|
|
12
|
+
return null;
|
|
13
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { onPress: openFeatureBoard, style: [styles.button, style], activeOpacity: 0.8, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [styles.text, textStyle], children: label ?? translations.suggestFeature }) }));
|
|
14
|
+
}
|
|
15
|
+
const styles = react_native_1.StyleSheet.create({
|
|
16
|
+
button: {
|
|
17
|
+
backgroundColor: '#4f46e5',
|
|
18
|
+
paddingHorizontal: 20,
|
|
19
|
+
paddingVertical: 12,
|
|
20
|
+
borderRadius: 10,
|
|
21
|
+
alignItems: 'center',
|
|
22
|
+
},
|
|
23
|
+
text: {
|
|
24
|
+
color: '#ffffff',
|
|
25
|
+
fontSize: 15,
|
|
26
|
+
fontWeight: '600',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function FeatureBoardModal(): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FeatureBoardModal = FeatureBoardModal;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_native_1 = require("react-native");
|
|
6
|
+
const react_native_webview_1 = require("react-native-webview");
|
|
7
|
+
const useBugReporter_1 = require("../hooks/useBugReporter");
|
|
8
|
+
const useFeatureBoard_1 = require("../hooks/useFeatureBoard");
|
|
9
|
+
// Cast to work around React 18/19 JSX type incompatibility
|
|
10
|
+
const WebView = react_native_webview_1.WebView;
|
|
11
|
+
function FeatureBoardModal() {
|
|
12
|
+
const { translations, isBoardVisible, closeBoard } = (0, useBugReporter_1.useBugReporter)();
|
|
13
|
+
const { boardUrl } = (0, useFeatureBoard_1.useFeatureBoard)();
|
|
14
|
+
if (!boardUrl)
|
|
15
|
+
return null;
|
|
16
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.Modal, { visible: isBoardVisible, animationType: "slide", presentationStyle: "fullScreen", onRequestClose: closeBoard, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.container, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.safeTop }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.header, children: [(0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { onPress: closeBoard, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.closeText, children: translations.cancel }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.title, children: translations.featureBoard }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.placeholder })] }), (0, jsx_runtime_1.jsx)(WebView, { source: { uri: boardUrl }, style: styles.webview, startInLoadingState: true, renderLoading: () => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.loading, children: (0, jsx_runtime_1.jsx)(react_native_1.ActivityIndicator, { size: "large", color: "#6366f1" }) })) })] }) }));
|
|
17
|
+
}
|
|
18
|
+
const styles = react_native_1.StyleSheet.create({
|
|
19
|
+
container: {
|
|
20
|
+
flex: 1,
|
|
21
|
+
backgroundColor: '#fff',
|
|
22
|
+
},
|
|
23
|
+
safeTop: {
|
|
24
|
+
paddingTop: react_native_1.Platform.OS === 'android' ? react_native_1.StatusBar.currentHeight ?? 44 : 54,
|
|
25
|
+
backgroundColor: '#fff',
|
|
26
|
+
},
|
|
27
|
+
header: {
|
|
28
|
+
flexDirection: 'row',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
justifyContent: 'space-between',
|
|
31
|
+
paddingHorizontal: 16,
|
|
32
|
+
paddingVertical: 14,
|
|
33
|
+
borderBottomWidth: 1,
|
|
34
|
+
borderBottomColor: '#e5e7eb',
|
|
35
|
+
},
|
|
36
|
+
title: {
|
|
37
|
+
fontSize: 17,
|
|
38
|
+
fontWeight: '600',
|
|
39
|
+
color: '#111827',
|
|
40
|
+
},
|
|
41
|
+
closeText: {
|
|
42
|
+
fontSize: 16,
|
|
43
|
+
color: '#6b7280',
|
|
44
|
+
},
|
|
45
|
+
placeholder: {
|
|
46
|
+
width: 50,
|
|
47
|
+
},
|
|
48
|
+
webview: {
|
|
49
|
+
flex: 1,
|
|
50
|
+
},
|
|
51
|
+
loading: {
|
|
52
|
+
...react_native_1.StyleSheet.absoluteFillObject,
|
|
53
|
+
justifyContent: 'center',
|
|
54
|
+
alignItems: 'center',
|
|
55
|
+
backgroundColor: '#fff',
|
|
56
|
+
},
|
|
57
|
+
});
|
|
@@ -7,6 +7,9 @@ export interface BugReporterContextValue {
|
|
|
7
7
|
isModalVisible: boolean;
|
|
8
8
|
openModal: () => void;
|
|
9
9
|
closeModal: () => void;
|
|
10
|
+
isBoardVisible: boolean;
|
|
11
|
+
openBoard: () => void;
|
|
12
|
+
closeBoard: () => void;
|
|
10
13
|
viewRef: RefObject<View>;
|
|
11
14
|
}
|
|
12
15
|
export declare const BugReporterContext: import("react").Context<BugReporterContextValue | null>;
|
|
@@ -6,22 +6,26 @@ const react_1 = require("react");
|
|
|
6
6
|
const react_native_1 = require("react-native");
|
|
7
7
|
const BugReporterContext_1 = require("./BugReporterContext");
|
|
8
8
|
const ReportModal_1 = require("../components/ReportModal");
|
|
9
|
+
const FeatureBoardModal_1 = require("../components/FeatureBoardModal");
|
|
9
10
|
const FloatingButton_1 = require("../components/FloatingButton");
|
|
10
11
|
const useShakeDetection_1 = require("../hooks/useShakeDetection");
|
|
11
12
|
const i18n_1 = require("../i18n");
|
|
12
13
|
function BugReporterProvider({ config, children }) {
|
|
13
14
|
const [isModalVisible, setIsModalVisible] = (0, react_1.useState)(false);
|
|
15
|
+
const [isBoardVisible, setIsBoardVisible] = (0, react_1.useState)(false);
|
|
14
16
|
const viewRef = (0, react_1.useRef)(null);
|
|
15
17
|
const translations = (0, i18n_1.getTranslations)(config.locale);
|
|
16
18
|
const openModal = (0, react_1.useCallback)(() => setIsModalVisible(true), []);
|
|
17
19
|
const closeModal = (0, react_1.useCallback)(() => setIsModalVisible(false), []);
|
|
20
|
+
const openBoard = (0, react_1.useCallback)(() => setIsBoardVisible(true), []);
|
|
21
|
+
const closeBoard = (0, react_1.useCallback)(() => setIsBoardVisible(false), []);
|
|
18
22
|
const shakeEnabled = config.enableShake ?? !__DEV__;
|
|
19
23
|
(0, useShakeDetection_1.useShakeDetection)({
|
|
20
24
|
enabled: shakeEnabled,
|
|
21
25
|
threshold: config.shakeThreshold,
|
|
22
26
|
onShake: openModal,
|
|
23
27
|
});
|
|
24
|
-
return ((0, jsx_runtime_1.jsxs)(BugReporterContext_1.BugReporterContext.Provider, { value: { config, translations, isModalVisible, openModal, closeModal, viewRef }, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { ref: viewRef, collapsable: false, style: styles.container, children: children }), config.floatingButton !== false && (0, jsx_runtime_1.jsx)(FloatingButton_1.FloatingButton, { onPress: openModal }), (0, jsx_runtime_1.jsx)(ReportModal_1.ReportModal, {})] }));
|
|
28
|
+
return ((0, jsx_runtime_1.jsxs)(BugReporterContext_1.BugReporterContext.Provider, { value: { config, translations, isModalVisible, openModal, closeModal, isBoardVisible, openBoard, closeBoard, viewRef }, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { ref: viewRef, collapsable: false, style: styles.container, children: children }), config.floatingButton !== false && (0, jsx_runtime_1.jsx)(FloatingButton_1.FloatingButton, { onPress: openModal }), (0, jsx_runtime_1.jsx)(ReportModal_1.ReportModal, {}), (0, jsx_runtime_1.jsx)(FeatureBoardModal_1.FeatureBoardModal, {})] }));
|
|
25
29
|
}
|
|
26
30
|
const styles = react_native_1.StyleSheet.create({
|
|
27
31
|
container: { flex: 1 },
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useFeatureBoard = useFeatureBoard;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const useBugReporter_1 = require("./useBugReporter");
|
|
6
|
+
function useFeatureBoard() {
|
|
7
|
+
const { config, openBoard, closeBoard, isBoardVisible } = (0, useBugReporter_1.useBugReporter)();
|
|
8
|
+
const boardUrl = (0, react_1.useMemo)(() => {
|
|
9
|
+
if (!config.featureBoard)
|
|
10
|
+
return null;
|
|
11
|
+
const base = config.featureBoard.boardBaseUrl.replace(/\/$/, '');
|
|
12
|
+
const url = `${base}/${config.projectId}`;
|
|
13
|
+
return config.userId ? `${url}?voter_id=${encodeURIComponent(config.userId)}` : url;
|
|
14
|
+
}, [config.featureBoard, config.projectId, config.userId]);
|
|
15
|
+
return { boardUrl, openFeatureBoard: openBoard, closeFeatureBoard: closeBoard, isBoardVisible };
|
|
16
|
+
}
|
|
@@ -8,9 +8,19 @@ const useBugReporter_1 = require("./useBugReporter");
|
|
|
8
8
|
const constants_1 = require("../constants");
|
|
9
9
|
function base64ToArrayBuffer(base64) {
|
|
10
10
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
11
|
+
// Count padding from the original string before stripping
|
|
12
|
+
let padding = 0;
|
|
13
|
+
if (base64.endsWith('=='))
|
|
14
|
+
padding = 2;
|
|
15
|
+
else if (base64.endsWith('='))
|
|
16
|
+
padding = 1;
|
|
17
|
+
// Remove all non-base64 characters (including '=' padding)
|
|
11
18
|
const clean = base64.replace(/[^A-Za-z0-9+/]/g, '');
|
|
12
19
|
const len = clean.length;
|
|
13
|
-
|
|
20
|
+
// The byte length formula requires the padded base64 length (multiple of 4).
|
|
21
|
+
// Since clean has '=' stripped, we add padding back for the calculation.
|
|
22
|
+
const paddedLen = len + padding;
|
|
23
|
+
const byteLen = (paddedLen * 3) / 4 - padding;
|
|
14
24
|
const buffer = new ArrayBuffer(byteLen);
|
|
15
25
|
const bytes = new Uint8Array(buffer);
|
|
16
26
|
let p = 0;
|
package/dist/i18n/en.js
CHANGED
package/dist/i18n/fr.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { BugReporterProvider } from './context/BugReporterProvider';
|
|
2
2
|
export { useBugReporter } from './hooks/useBugReporter';
|
|
3
|
+
export { useFeatureBoard } from './hooks/useFeatureBoard';
|
|
3
4
|
export { FloatingButton } from './components/FloatingButton';
|
|
4
|
-
export
|
|
5
|
+
export { FeatureBoardLink } from './components/FeatureBoardLink';
|
|
6
|
+
export type { BugReporterConfig, BugReportPayload, BugCategory, BugSeverity, BugStatus, SupportedLocale, DeviceInfo, AppInfo, NetworkInfo, Translations, FeatureCategory, FeatureStatus, FeatureBoardConfig, } from './types';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FloatingButton = exports.useBugReporter = exports.BugReporterProvider = void 0;
|
|
3
|
+
exports.FeatureBoardLink = exports.FloatingButton = exports.useFeatureBoard = exports.useBugReporter = exports.BugReporterProvider = void 0;
|
|
4
4
|
var BugReporterProvider_1 = require("./context/BugReporterProvider");
|
|
5
5
|
Object.defineProperty(exports, "BugReporterProvider", { enumerable: true, get: function () { return BugReporterProvider_1.BugReporterProvider; } });
|
|
6
6
|
var useBugReporter_1 = require("./hooks/useBugReporter");
|
|
7
7
|
Object.defineProperty(exports, "useBugReporter", { enumerable: true, get: function () { return useBugReporter_1.useBugReporter; } });
|
|
8
|
+
var useFeatureBoard_1 = require("./hooks/useFeatureBoard");
|
|
9
|
+
Object.defineProperty(exports, "useFeatureBoard", { enumerable: true, get: function () { return useFeatureBoard_1.useFeatureBoard; } });
|
|
8
10
|
var FloatingButton_1 = require("./components/FloatingButton");
|
|
9
11
|
Object.defineProperty(exports, "FloatingButton", { enumerable: true, get: function () { return FloatingButton_1.FloatingButton; } });
|
|
12
|
+
var FeatureBoardLink_1 = require("./components/FeatureBoardLink");
|
|
13
|
+
Object.defineProperty(exports, "FeatureBoardLink", { enumerable: true, get: function () { return FeatureBoardLink_1.FeatureBoardLink; } });
|
package/dist/types.d.ts
CHANGED
|
@@ -2,6 +2,11 @@ export type BugCategory = 'Bug' | 'Crash' | 'UI' | 'Performance' | 'Feature Requ
|
|
|
2
2
|
export type BugSeverity = 'low' | 'medium' | 'high' | 'critical';
|
|
3
3
|
export type BugStatus = 'open' | 'in_progress' | 'resolved' | 'closed';
|
|
4
4
|
export type SupportedLocale = 'en' | 'fr';
|
|
5
|
+
export type FeatureCategory = 'UI/UX' | 'Performance' | 'New Feature' | 'Integration' | 'Improvement' | 'Other';
|
|
6
|
+
export type FeatureStatus = 'under_review' | 'planned' | 'in_progress' | 'completed' | 'declined';
|
|
7
|
+
export interface FeatureBoardConfig {
|
|
8
|
+
boardBaseUrl: string;
|
|
9
|
+
}
|
|
5
10
|
export interface DeviceInfo {
|
|
6
11
|
brand: string | null;
|
|
7
12
|
model: string | null;
|
|
@@ -51,6 +56,7 @@ export interface BugReporterConfig {
|
|
|
51
56
|
currentScreen?: string;
|
|
52
57
|
userId?: string;
|
|
53
58
|
customData?: Record<string, unknown>;
|
|
59
|
+
featureBoard?: FeatureBoardConfig;
|
|
54
60
|
}
|
|
55
61
|
export interface Translations {
|
|
56
62
|
reportBug: string;
|
|
@@ -70,4 +76,6 @@ export interface Translations {
|
|
|
70
76
|
severityMedium: string;
|
|
71
77
|
severityHigh: string;
|
|
72
78
|
severityCritical: string;
|
|
79
|
+
featureBoard: string;
|
|
80
|
+
suggestFeature: string;
|
|
73
81
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindedsolutions/bug-reporter-sdk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "In-app bug reporting and feature request SDK for React Native/Expo with shake detection, screenshot capture, feature board, and Supabase integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -21,13 +21,14 @@
|
|
|
21
21
|
"clean": "rm -rf dist"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
|
+
"expo-application": ">=5",
|
|
25
|
+
"expo-device": ">=6",
|
|
26
|
+
"expo-network": ">=6",
|
|
27
|
+
"expo-sensors": ">=13",
|
|
24
28
|
"react": ">=18",
|
|
25
29
|
"react-native": ">=0.72",
|
|
26
|
-
"expo-sensors": ">=13",
|
|
27
30
|
"react-native-view-shot": ">=3",
|
|
28
|
-
"
|
|
29
|
-
"expo-application": ">=5",
|
|
30
|
-
"expo-network": ">=6"
|
|
31
|
+
"react-native-webview": ">=13"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|
|
33
34
|
"@supabase/supabase-js": "^2.49.0"
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@types/react": "^18",
|
|
37
38
|
"@types/react-native": "^0.72",
|
|
39
|
+
"react-native-webview": "^13.16.1",
|
|
38
40
|
"typescript": "^5.5.0"
|
|
39
41
|
}
|
|
40
42
|
}
|