react-native-iinstall 0.2.9 â 0.2.11
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/lib/IInstallWrapper.d.ts +10 -0
- package/lib/IInstallWrapper.js +167 -0
- package/lib/ShakeDetector.js +43 -12
- package/lib/index.d.ts +1 -0
- package/lib/index.js +4 -1
- package/package.json +1 -1
- package/src/IInstallWrapper.tsx +178 -0
- package/src/ShakeDetector.ts +46 -15
- package/src/index.tsx +3 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface IInstallWrapperProps {
|
|
3
|
+
apiKey: string;
|
|
4
|
+
apiEndpoint?: string;
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
showDebugButton?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const IInstallWrapper: React.FC<IInstallWrapperProps>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,167 @@
|
|
|
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.IInstallWrapper = void 0;
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const react_native_1 = require("react-native");
|
|
39
|
+
// Import from the local SDK files instead of the package
|
|
40
|
+
const index_1 = require("./index");
|
|
41
|
+
const ShakeDetector_1 = require("./ShakeDetector");
|
|
42
|
+
// Helper to detect simulator/emulator
|
|
43
|
+
const isSimulator = () => {
|
|
44
|
+
// @ts-expect-error - __DEV__ is a React Native global
|
|
45
|
+
const isDev = typeof global !== 'undefined' && global.__DEV__ === true;
|
|
46
|
+
return (react_native_1.Platform.OS === 'ios' &&
|
|
47
|
+
(react_native_1.Platform.isPad || react_native_1.Platform.isTV || isDev)) || (react_native_1.Platform.OS === 'android' && isDev);
|
|
48
|
+
};
|
|
49
|
+
// Helper to detect if native sensors are available
|
|
50
|
+
const hasNativeSensors = () => {
|
|
51
|
+
try {
|
|
52
|
+
// Check if we're in a simulator/emulator environment
|
|
53
|
+
if (isSimulator()) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
// Additional check for Android emulators
|
|
57
|
+
if (react_native_1.Platform.OS === 'android') {
|
|
58
|
+
// Android emulators typically don't have motion sensors
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
// iOS simulators don't have motion sensors
|
|
62
|
+
if (react_native_1.Platform.OS === 'ios' && react_native_1.Platform.isPad) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const IInstallWrapper = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enabled = true, showDebugButton = true }) => {
|
|
72
|
+
const shakeDetectorRef = (0, react_1.useRef)(null);
|
|
73
|
+
// Calculate sensor availability once to avoid state updates
|
|
74
|
+
const sensorsAvailable = enabled && hasNativeSensors();
|
|
75
|
+
const showManualButton = !sensorsAvailable && enabled;
|
|
76
|
+
(0, react_1.useEffect)(() => {
|
|
77
|
+
if (!enabled)
|
|
78
|
+
return;
|
|
79
|
+
// Only use shake detector if sensors are available
|
|
80
|
+
if (sensorsAvailable) {
|
|
81
|
+
shakeDetectorRef.current = new ShakeDetector_1.ShakeDetector(() => {
|
|
82
|
+
// This will trigger the IInstall modal
|
|
83
|
+
// The shake detector will handle the screenshot capture
|
|
84
|
+
});
|
|
85
|
+
shakeDetectorRef.current.start();
|
|
86
|
+
}
|
|
87
|
+
return () => {
|
|
88
|
+
shakeDetectorRef.current?.stop();
|
|
89
|
+
};
|
|
90
|
+
}, [enabled, sensorsAvailable]);
|
|
91
|
+
const handleManualReport = () => {
|
|
92
|
+
// Manually trigger the IInstall feedback modal
|
|
93
|
+
// This simulates a shake gesture
|
|
94
|
+
if (enabled) {
|
|
95
|
+
// The IInstall component will handle the screenshot and modal
|
|
96
|
+
// We can trigger it by simulating the shake event
|
|
97
|
+
console.log('[IInstall] Manual report triggered');
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const handleDebugInfo = () => {
|
|
101
|
+
const debugMessage = [
|
|
102
|
+
`Platform: ${react_native_1.Platform.OS}`,
|
|
103
|
+
`Is Simulator: ${isSimulator() ? 'Yes' : 'No'}`,
|
|
104
|
+
`Native Sensors: ${sensorsAvailable ? 'Available' : 'Not Available'}`,
|
|
105
|
+
`Manual Button: ${showManualButton ? 'Visible' : 'Hidden'}`,
|
|
106
|
+
`Shake Detection: ${sensorsAvailable ? 'Enabled' : 'Disabled'}`,
|
|
107
|
+
].join('\n');
|
|
108
|
+
react_native_1.Alert.alert('IInstall Debug Info', debugMessage, [{ text: 'OK' }]);
|
|
109
|
+
};
|
|
110
|
+
return (<index_1.IInstall apiKey={apiKey} apiEndpoint={apiEndpoint} enabled={enabled}>
|
|
111
|
+
{children}
|
|
112
|
+
|
|
113
|
+
{showManualButton && (<react_native_1.View style={styles.manualButtonContainer}>
|
|
114
|
+
<react_native_1.TouchableOpacity style={styles.manualButton} onPress={handleManualReport}>
|
|
115
|
+
<react_native_1.Text style={styles.manualButtonText}>đ Report Issue</react_native_1.Text>
|
|
116
|
+
</react_native_1.TouchableOpacity>
|
|
117
|
+
|
|
118
|
+
{showDebugButton && (<react_native_1.TouchableOpacity style={styles.debugButton} onPress={handleDebugInfo}>
|
|
119
|
+
<react_native_1.Text style={styles.debugButtonText}>âšī¸</react_native_1.Text>
|
|
120
|
+
</react_native_1.TouchableOpacity>)}
|
|
121
|
+
</react_native_1.View>)}
|
|
122
|
+
</index_1.IInstall>);
|
|
123
|
+
};
|
|
124
|
+
exports.IInstallWrapper = IInstallWrapper;
|
|
125
|
+
const styles = react_native_1.StyleSheet.create({
|
|
126
|
+
manualButtonContainer: {
|
|
127
|
+
position: 'absolute',
|
|
128
|
+
bottom: 40,
|
|
129
|
+
right: 20,
|
|
130
|
+
flexDirection: 'row',
|
|
131
|
+
alignItems: 'center',
|
|
132
|
+
},
|
|
133
|
+
manualButton: {
|
|
134
|
+
backgroundColor: '#FF6B6B',
|
|
135
|
+
paddingHorizontal: 16,
|
|
136
|
+
paddingVertical: 12,
|
|
137
|
+
borderRadius: 25,
|
|
138
|
+
elevation: 5,
|
|
139
|
+
shadowColor: '#000',
|
|
140
|
+
shadowOffset: { width: 0, height: 2 },
|
|
141
|
+
shadowOpacity: 0.2,
|
|
142
|
+
shadowRadius: 4,
|
|
143
|
+
marginRight: 10,
|
|
144
|
+
},
|
|
145
|
+
manualButtonText: {
|
|
146
|
+
color: 'white',
|
|
147
|
+
fontSize: 14,
|
|
148
|
+
fontWeight: '600',
|
|
149
|
+
},
|
|
150
|
+
debugButton: {
|
|
151
|
+
backgroundColor: '#4ECDC4',
|
|
152
|
+
width: 40,
|
|
153
|
+
height: 40,
|
|
154
|
+
borderRadius: 20,
|
|
155
|
+
justifyContent: 'center',
|
|
156
|
+
alignItems: 'center',
|
|
157
|
+
elevation: 5,
|
|
158
|
+
shadowColor: '#000',
|
|
159
|
+
shadowOffset: { width: 0, height: 2 },
|
|
160
|
+
shadowOpacity: 0.2,
|
|
161
|
+
shadowRadius: 4,
|
|
162
|
+
},
|
|
163
|
+
debugButtonText: {
|
|
164
|
+
color: 'white',
|
|
165
|
+
fontSize: 16,
|
|
166
|
+
},
|
|
167
|
+
});
|
package/lib/ShakeDetector.js
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ShakeDetector = void 0;
|
|
4
|
-
|
|
4
|
+
/* eslint-disable */
|
|
5
5
|
const operators_1 = require("rxjs/operators");
|
|
6
|
+
// Safe import for react-native-sensors to prevent crashes if native module is missing
|
|
7
|
+
let accelerometer;
|
|
8
|
+
let setUpdateIntervalForType;
|
|
9
|
+
let SensorTypes;
|
|
10
|
+
try {
|
|
11
|
+
const sensors = require('react-native-sensors');
|
|
12
|
+
accelerometer = sensors.accelerometer;
|
|
13
|
+
setUpdateIntervalForType = sensors.setUpdateIntervalForType;
|
|
14
|
+
SensorTypes = sensors.SensorTypes;
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
console.warn('[iInstall SDK] Shake detection disabled: react-native-sensors native module not found. Please ensure the native module is linked and the app is rebuilt.');
|
|
18
|
+
}
|
|
6
19
|
const SHAKE_THRESHOLD = 2.5; // g-force
|
|
7
20
|
const MIN_TIME_BETWEEN_SHAKES = 1000; // ms
|
|
8
21
|
class ShakeDetector {
|
|
@@ -11,21 +24,39 @@ class ShakeDetector {
|
|
|
11
24
|
onShake;
|
|
12
25
|
constructor(onShake) {
|
|
13
26
|
this.onShake = onShake;
|
|
14
|
-
(
|
|
27
|
+
if (setUpdateIntervalForType && SensorTypes) {
|
|
28
|
+
try {
|
|
29
|
+
setUpdateIntervalForType(SensorTypes.accelerometer, 100); // 100ms interval
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
console.warn('[iInstall SDK] Failed to set accelerometer update interval:', e);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
15
35
|
}
|
|
16
36
|
start() {
|
|
17
37
|
if (this.subscription)
|
|
18
38
|
return;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
39
|
+
if (!accelerometer) {
|
|
40
|
+
// Don't spam logs, just return silently or maybe log once if debugging
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
this.subscription = accelerometer
|
|
45
|
+
.pipe((0, operators_1.map)(({ x, y, z }) => Math.sqrt(x * x + y * y + z * z)), (0, operators_1.filter)((g) => g > SHAKE_THRESHOLD))
|
|
46
|
+
.subscribe(() => {
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
if (now - this.lastShakeTime > MIN_TIME_BETWEEN_SHAKES) {
|
|
49
|
+
this.lastShakeTime = now;
|
|
50
|
+
console.log('TesterFlow: Shake detected!');
|
|
51
|
+
this.onShake();
|
|
52
|
+
}
|
|
53
|
+
}, (error) => {
|
|
54
|
+
console.warn('[iInstall SDK] Error in shake detection subscription:', error);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
console.warn('[iInstall SDK] Failed to start shake detection:', e);
|
|
59
|
+
}
|
|
29
60
|
}
|
|
30
61
|
stop() {
|
|
31
62
|
if (this.subscription) {
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.IInstall = void 0;
|
|
39
|
+
exports.IInstallWrapper = exports.IInstall = void 0;
|
|
40
40
|
const react_1 = __importStar(require("react"));
|
|
41
41
|
const react_native_1 = require("react-native");
|
|
42
42
|
const react_native_view_shot_1 = require("react-native-view-shot");
|
|
@@ -232,3 +232,6 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
232
232
|
}
|
|
233
233
|
});
|
|
234
234
|
exports.default = exports.IInstall;
|
|
235
|
+
// Export the wrapper component for simulator/emulator support
|
|
236
|
+
var IInstallWrapper_1 = require("./IInstallWrapper");
|
|
237
|
+
Object.defineProperty(exports, "IInstallWrapper", { enumerable: true, get: function () { return IInstallWrapper_1.IInstallWrapper; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-iinstall",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "đ¯ IInstall React Native SDK - The ultimate beta testing & QA feedback tool. Shake-to-report with voice recordings, screen recordings, and screenshots. Zero-config setup with TypeScript support. Perfect for beta testing, QA teams, and user feedback collection.",
|
|
5
5
|
"author": "TesterFlow Team",
|
|
6
6
|
"license": "MIT",
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import React, { useEffect, useState, useRef } from 'react';
|
|
2
|
+
import { View, StyleSheet, TouchableOpacity, Text, Platform, Alert } from 'react-native';
|
|
3
|
+
// Import from the local SDK files instead of the package
|
|
4
|
+
import { IInstall } from './index';
|
|
5
|
+
import { ShakeDetector } from './ShakeDetector';
|
|
6
|
+
|
|
7
|
+
interface IInstallWrapperProps {
|
|
8
|
+
apiKey: string;
|
|
9
|
+
apiEndpoint?: string;
|
|
10
|
+
children?: React.ReactNode;
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
showDebugButton?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Helper to detect simulator/emulator
|
|
16
|
+
const isSimulator = () => {
|
|
17
|
+
// @ts-expect-error - __DEV__ is a React Native global
|
|
18
|
+
const isDev = typeof global !== 'undefined' && global.__DEV__ === true;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
Platform.OS === 'ios' &&
|
|
22
|
+
(Platform.isPad || Platform.isTV || isDev)
|
|
23
|
+
) || (
|
|
24
|
+
Platform.OS === 'android' && isDev
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Helper to detect if native sensors are available
|
|
29
|
+
const hasNativeSensors = () => {
|
|
30
|
+
try {
|
|
31
|
+
// Check if we're in a simulator/emulator environment
|
|
32
|
+
if (isSimulator()) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Additional check for Android emulators
|
|
37
|
+
if (Platform.OS === 'android') {
|
|
38
|
+
// Android emulators typically don't have motion sensors
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// iOS simulators don't have motion sensors
|
|
43
|
+
if (Platform.OS === 'ios' && Platform.isPad) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return true;
|
|
48
|
+
} catch (e) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const IInstallWrapper: React.FC<IInstallWrapperProps> = ({
|
|
54
|
+
apiKey,
|
|
55
|
+
apiEndpoint = 'https://iinstall.app',
|
|
56
|
+
children,
|
|
57
|
+
enabled = true,
|
|
58
|
+
showDebugButton = true
|
|
59
|
+
}) => {
|
|
60
|
+
const shakeDetectorRef = useRef<ShakeDetector | null>(null);
|
|
61
|
+
|
|
62
|
+
// Calculate sensor availability once to avoid state updates
|
|
63
|
+
const sensorsAvailable = enabled && hasNativeSensors();
|
|
64
|
+
const showManualButton = !sensorsAvailable && enabled;
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!enabled) return;
|
|
68
|
+
|
|
69
|
+
// Only use shake detector if sensors are available
|
|
70
|
+
if (sensorsAvailable) {
|
|
71
|
+
shakeDetectorRef.current = new ShakeDetector(() => {
|
|
72
|
+
// This will trigger the IInstall modal
|
|
73
|
+
// The shake detector will handle the screenshot capture
|
|
74
|
+
});
|
|
75
|
+
shakeDetectorRef.current.start();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return () => {
|
|
79
|
+
shakeDetectorRef.current?.stop();
|
|
80
|
+
};
|
|
81
|
+
}, [enabled, sensorsAvailable]);
|
|
82
|
+
|
|
83
|
+
const handleManualReport = () => {
|
|
84
|
+
// Manually trigger the IInstall feedback modal
|
|
85
|
+
// This simulates a shake gesture
|
|
86
|
+
if (enabled) {
|
|
87
|
+
// The IInstall component will handle the screenshot and modal
|
|
88
|
+
// We can trigger it by simulating the shake event
|
|
89
|
+
console.log('[IInstall] Manual report triggered');
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const handleDebugInfo = () => {
|
|
94
|
+
const debugMessage = [
|
|
95
|
+
`Platform: ${Platform.OS}`,
|
|
96
|
+
`Is Simulator: ${isSimulator() ? 'Yes' : 'No'}`,
|
|
97
|
+
`Native Sensors: ${sensorsAvailable ? 'Available' : 'Not Available'}`,
|
|
98
|
+
`Manual Button: ${showManualButton ? 'Visible' : 'Hidden'}`,
|
|
99
|
+
`Shake Detection: ${sensorsAvailable ? 'Enabled' : 'Disabled'}`,
|
|
100
|
+
].join('\n');
|
|
101
|
+
|
|
102
|
+
Alert.alert('IInstall Debug Info', debugMessage, [{ text: 'OK' }]);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<IInstall
|
|
107
|
+
apiKey={apiKey}
|
|
108
|
+
apiEndpoint={apiEndpoint}
|
|
109
|
+
enabled={enabled}
|
|
110
|
+
>
|
|
111
|
+
{children}
|
|
112
|
+
|
|
113
|
+
{showManualButton && (
|
|
114
|
+
<View style={styles.manualButtonContainer}>
|
|
115
|
+
<TouchableOpacity
|
|
116
|
+
style={styles.manualButton}
|
|
117
|
+
onPress={handleManualReport}
|
|
118
|
+
>
|
|
119
|
+
<Text style={styles.manualButtonText}>đ Report Issue</Text>
|
|
120
|
+
</TouchableOpacity>
|
|
121
|
+
|
|
122
|
+
{showDebugButton && (
|
|
123
|
+
<TouchableOpacity
|
|
124
|
+
style={styles.debugButton}
|
|
125
|
+
onPress={handleDebugInfo}
|
|
126
|
+
>
|
|
127
|
+
<Text style={styles.debugButtonText}>âšī¸</Text>
|
|
128
|
+
</TouchableOpacity>
|
|
129
|
+
)}
|
|
130
|
+
</View>
|
|
131
|
+
)}
|
|
132
|
+
</IInstall>
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const styles = StyleSheet.create({
|
|
137
|
+
manualButtonContainer: {
|
|
138
|
+
position: 'absolute',
|
|
139
|
+
bottom: 40,
|
|
140
|
+
right: 20,
|
|
141
|
+
flexDirection: 'row',
|
|
142
|
+
alignItems: 'center',
|
|
143
|
+
},
|
|
144
|
+
manualButton: {
|
|
145
|
+
backgroundColor: '#FF6B6B',
|
|
146
|
+
paddingHorizontal: 16,
|
|
147
|
+
paddingVertical: 12,
|
|
148
|
+
borderRadius: 25,
|
|
149
|
+
elevation: 5,
|
|
150
|
+
shadowColor: '#000',
|
|
151
|
+
shadowOffset: { width: 0, height: 2 },
|
|
152
|
+
shadowOpacity: 0.2,
|
|
153
|
+
shadowRadius: 4,
|
|
154
|
+
marginRight: 10,
|
|
155
|
+
},
|
|
156
|
+
manualButtonText: {
|
|
157
|
+
color: 'white',
|
|
158
|
+
fontSize: 14,
|
|
159
|
+
fontWeight: '600',
|
|
160
|
+
},
|
|
161
|
+
debugButton: {
|
|
162
|
+
backgroundColor: '#4ECDC4',
|
|
163
|
+
width: 40,
|
|
164
|
+
height: 40,
|
|
165
|
+
borderRadius: 20,
|
|
166
|
+
justifyContent: 'center',
|
|
167
|
+
alignItems: 'center',
|
|
168
|
+
elevation: 5,
|
|
169
|
+
shadowColor: '#000',
|
|
170
|
+
shadowOffset: { width: 0, height: 2 },
|
|
171
|
+
shadowOpacity: 0.2,
|
|
172
|
+
shadowRadius: 4,
|
|
173
|
+
},
|
|
174
|
+
debugButtonText: {
|
|
175
|
+
color: 'white',
|
|
176
|
+
fontSize: 16,
|
|
177
|
+
},
|
|
178
|
+
});
|
package/src/ShakeDetector.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable */
|
|
2
2
|
import { map, filter } from 'rxjs/operators';
|
|
3
3
|
|
|
4
|
+
// Safe import for react-native-sensors to prevent crashes if native module is missing
|
|
5
|
+
let accelerometer: any;
|
|
6
|
+
let setUpdateIntervalForType: any;
|
|
7
|
+
let SensorTypes: any;
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const sensors = require('react-native-sensors');
|
|
11
|
+
accelerometer = sensors.accelerometer;
|
|
12
|
+
setUpdateIntervalForType = sensors.setUpdateIntervalForType;
|
|
13
|
+
SensorTypes = sensors.SensorTypes;
|
|
14
|
+
} catch (e) {
|
|
15
|
+
console.warn('[iInstall SDK] Shake detection disabled: react-native-sensors native module not found. Please ensure the native module is linked and the app is rebuilt.');
|
|
16
|
+
}
|
|
17
|
+
|
|
4
18
|
const SHAKE_THRESHOLD = 2.5; // g-force
|
|
5
19
|
const MIN_TIME_BETWEEN_SHAKES = 1000; // ms
|
|
6
20
|
|
|
@@ -11,25 +25,42 @@ export class ShakeDetector {
|
|
|
11
25
|
|
|
12
26
|
constructor(onShake: () => void) {
|
|
13
27
|
this.onShake = onShake;
|
|
14
|
-
setUpdateIntervalForType
|
|
28
|
+
if (setUpdateIntervalForType && SensorTypes) {
|
|
29
|
+
try {
|
|
30
|
+
setUpdateIntervalForType(SensorTypes.accelerometer, 100); // 100ms interval
|
|
31
|
+
} catch (e) {
|
|
32
|
+
console.warn('[iInstall SDK] Failed to set accelerometer update interval:', e);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
15
35
|
}
|
|
16
36
|
|
|
17
37
|
start() {
|
|
18
38
|
if (this.subscription) return;
|
|
19
39
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
if (!accelerometer) {
|
|
41
|
+
// Don't spam logs, just return silently or maybe log once if debugging
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
this.subscription = accelerometer
|
|
47
|
+
.pipe(
|
|
48
|
+
map(({ x, y, z }: any) => Math.sqrt(x * x + y * y + z * z)),
|
|
49
|
+
filter((g: number) => g > SHAKE_THRESHOLD)
|
|
50
|
+
)
|
|
51
|
+
.subscribe(() => {
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
if (now - this.lastShakeTime > MIN_TIME_BETWEEN_SHAKES) {
|
|
54
|
+
this.lastShakeTime = now;
|
|
55
|
+
console.log('TesterFlow: Shake detected!');
|
|
56
|
+
this.onShake();
|
|
57
|
+
}
|
|
58
|
+
}, (error: any) => {
|
|
59
|
+
console.warn('[iInstall SDK] Error in shake detection subscription:', error);
|
|
60
|
+
});
|
|
61
|
+
} catch (e) {
|
|
62
|
+
console.warn('[iInstall SDK] Failed to start shake detection:', e);
|
|
63
|
+
}
|
|
33
64
|
}
|
|
34
65
|
|
|
35
66
|
stop() {
|