react-native-iinstall 0.2.14 → 0.2.15
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/INTEGRATION_GUIDE.md +12 -7
- package/README.md +1 -1
- package/RELEASE_MANIFEST.json +4 -4
- package/lib/FeedbackModal.js +34 -36
- package/lib/index.js +13 -12
- package/lib/{src/nativeModules.js → nativeModules.js} +8 -7
- package/package.json +3 -2
- package/src/nativeModules.ts +15 -10
- package/lib/scripts/check-release-manifest.d.mts +0 -2
- package/lib/scripts/check-release-manifest.mjs +0 -38
- package/lib/scripts/sync-release-manifest.d.mts +0 -2
- package/lib/scripts/sync-release-manifest.mjs +0 -38
- package/lib/src/FeedbackModal.d.ts +0 -12
- package/lib/src/FeedbackModal.js +0 -393
- package/lib/src/IInstallWrapper.d.ts +0 -16
- package/lib/src/IInstallWrapper.js +0 -18
- package/lib/src/ShakeDetector.d.ts +0 -8
- package/lib/src/ShakeDetector.js +0 -68
- package/lib/src/index.d.ts +0 -17
- package/lib/src/index.js +0 -349
- package/lib/src/pushRegistration.d.ts +0 -22
- package/lib/src/pushRegistration.js +0 -70
- /package/lib/{src/nativeModules.d.ts → nativeModules.d.ts} +0 -0
package/INTEGRATION_GUIDE.md
CHANGED
|
@@ -46,16 +46,20 @@ npm install react-native-iinstall
|
|
|
46
46
|
yarn add react-native-iinstall
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
Install required native peer dependencies at app root:
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
```bash
|
|
52
|
+
npm install react-native-sensors react-native-view-shot react-native-device-info react-native-audio-recorder-player react-native-record-screen
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Updating to Latest Version (v0.2.14)
|
|
52
56
|
|
|
53
57
|
If you're using an older version, update to get the latest audio/video improvements:
|
|
54
58
|
|
|
55
59
|
```bash
|
|
56
|
-
npm install react-native-iinstall@0.2.
|
|
60
|
+
npm install react-native-iinstall@0.2.14
|
|
57
61
|
# OR
|
|
58
|
-
yarn add react-native-iinstall@0.2.
|
|
62
|
+
yarn add react-native-iinstall@0.2.14
|
|
59
63
|
```
|
|
60
64
|
|
|
61
65
|
**Key improvements in v0.2.7:**
|
|
@@ -136,7 +140,7 @@ import { registerPushToken, unregisterPushToken } from 'react-native-iinstall';
|
|
|
136
140
|
|
|
137
141
|
---
|
|
138
142
|
|
|
139
|
-
## 6. Audio & Video Feedback (v0.2.
|
|
143
|
+
## 6. Audio & Video Feedback (v0.2.14+)
|
|
140
144
|
|
|
141
145
|
The SDK now supports **Audio Feedback** (Voice Notes) and **Screen Recording** with enhanced compatibility and reliability.
|
|
142
146
|
|
|
@@ -151,6 +155,7 @@ The SDK now supports **Audio Feedback** (Voice Notes) and **Screen Recording** w
|
|
|
151
155
|
These features require native modules. If you haven't already, run:
|
|
152
156
|
|
|
153
157
|
```bash
|
|
158
|
+
npm install react-native-sensors react-native-view-shot react-native-device-info react-native-audio-recorder-player react-native-record-screen
|
|
154
159
|
cd ios && pod install
|
|
155
160
|
```
|
|
156
161
|
|
|
@@ -219,7 +224,7 @@ A: The app will work fine, but features like "Shake to Report" and automatic ses
|
|
|
219
224
|
A: Ensure your project icon URL is valid. If the image fails to load, the dashboard now automatically falls back to showing the project's initial letter on a styled background.
|
|
220
225
|
|
|
221
226
|
**Q: Audio feedback shows 0:00 duration in dashboard?**
|
|
222
|
-
A: Update to SDK v0.2.
|
|
227
|
+
A: Update to SDK v0.2.14+ which includes the audio codec compatibility fixes. Audio files are encoded as AAC for browser playback.
|
|
223
228
|
|
|
224
229
|
**Q: Screen recording sometimes doesn't capture?**
|
|
225
|
-
A: Update to SDK v0.2.
|
|
230
|
+
A: Update to SDK v0.2.14+ which includes enhanced URI extraction and better error handling for screen recordings across different devices.
|
package/README.md
CHANGED
|
@@ -176,7 +176,7 @@ await unregisterPushToken({
|
|
|
176
176
|
- **Shake not working?** Test on real device or enable "Shake" in simulator
|
|
177
177
|
- **Network errors?** Verify `apiEndpoint` is base URL only (not `/api/sdk/issue`)
|
|
178
178
|
- **Permissions denied?** Check platform-specific setup in integration guide
|
|
179
|
-
- **Audio issues?** Ensure SDK v0.2.
|
|
179
|
+
- **Audio issues?** Ensure SDK v0.2.14+ and rebuild after native peer install
|
|
180
180
|
- **Native module is null/undefined?** Reinstall peer deps in app root, run `cd ios && pod install`, then rebuild the app
|
|
181
181
|
|
|
182
182
|
### Getting Help
|
package/RELEASE_MANIFEST.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.2.
|
|
2
|
+
"version": "0.2.15",
|
|
3
3
|
"highlights": [
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
4
|
+
"Native modules now declared as peer dependencies for reliable app-root autolinking",
|
|
5
|
+
"SDK now lazy-loads native modules and degrades gracefully instead of hard crashing",
|
|
6
|
+
"Updated integration docs with explicit peer dependency install commands"
|
|
7
7
|
]
|
|
8
8
|
}
|
package/lib/FeedbackModal.js
CHANGED
|
@@ -32,15 +32,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
36
|
exports.FeedbackModal = void 0;
|
|
40
37
|
const react_1 = __importStar(require("react"));
|
|
41
38
|
const react_native_1 = require("react-native");
|
|
42
|
-
const
|
|
43
|
-
const react_native_audio_recorder_player_1 = __importStar(require("react-native-audio-recorder-player"));
|
|
39
|
+
const nativeModules_1 = require("./nativeModules");
|
|
44
40
|
const FeedbackModal = ({ visible, onClose, screenshotUri, videoUri, onStartRecording, apiKey, apiEndpoint }) => {
|
|
45
41
|
const [description, setDescription] = (0, react_1.useState)('');
|
|
46
42
|
const [isSending, setIsSending] = (0, react_1.useState)(false);
|
|
@@ -48,16 +44,21 @@ const FeedbackModal = ({ visible, onClose, screenshotUri, videoUri, onStartRecor
|
|
|
48
44
|
const [isRecordingAudio, setIsRecordingAudio] = (0, react_1.useState)(false);
|
|
49
45
|
const [audioUri, setAudioUri] = (0, react_1.useState)(null);
|
|
50
46
|
const [audioDuration, setAudioDuration] = (0, react_1.useState)('00:00');
|
|
51
|
-
const
|
|
47
|
+
const audioRecordingAvailable = (0, nativeModules_1.hasAudioRecordingSupport)();
|
|
48
|
+
const audioRecorderPlayer = (0, react_1.useRef)((0, nativeModules_1.createAudioRecorderPlayer)()).current;
|
|
52
49
|
(0, react_1.useEffect)(() => {
|
|
53
50
|
return () => {
|
|
54
51
|
// Cleanup
|
|
55
|
-
if (isRecordingAudio) {
|
|
56
|
-
audioRecorderPlayer.stopRecorder();
|
|
52
|
+
if (isRecordingAudio && audioRecorderPlayer) {
|
|
53
|
+
audioRecorderPlayer.stopRecorder().catch(() => undefined);
|
|
57
54
|
}
|
|
58
55
|
};
|
|
59
|
-
}, [isRecordingAudio]);
|
|
56
|
+
}, [audioRecorderPlayer, isRecordingAudio]);
|
|
60
57
|
const onStartAudioRecord = async () => {
|
|
58
|
+
if (!audioRecorderPlayer || !audioRecordingAvailable) {
|
|
59
|
+
react_native_1.Alert.alert('Audio unavailable', 'Install react-native-audio-recorder-player in your app root, then rebuild the app.');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
61
62
|
if (react_native_1.Platform.OS === 'android') {
|
|
62
63
|
try {
|
|
63
64
|
const grants = await react_native_1.PermissionsAndroid.requestMultiple([
|
|
@@ -77,20 +78,7 @@ const FeedbackModal = ({ visible, onClose, screenshotUri, videoUri, onStartRecor
|
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
try {
|
|
80
|
-
const
|
|
81
|
-
AVFormatIDKeyIOS: react_native_audio_recorder_player_1.AVEncodingOption.aac,
|
|
82
|
-
AVEncoderAudioQualityKeyIOS: react_native_audio_recorder_player_1.AVEncoderAudioQualityIOSType.high,
|
|
83
|
-
AVSampleRateKeyIOS: 44100,
|
|
84
|
-
AVNumberOfChannelsKeyIOS: 1,
|
|
85
|
-
AVEncoderBitRateKeyIOS: 128000,
|
|
86
|
-
AudioEncoderAndroid: react_native_audio_recorder_player_1.AudioEncoderAndroidType.AAC,
|
|
87
|
-
AudioSourceAndroid: react_native_audio_recorder_player_1.AudioSourceAndroidType.MIC,
|
|
88
|
-
OutputFormatAndroid: react_native_audio_recorder_player_1.OutputFormatAndroidType.MPEG_4,
|
|
89
|
-
AudioEncodingBitRateAndroid: 128000,
|
|
90
|
-
AudioSamplingRateAndroid: 44100,
|
|
91
|
-
AudioChannelsAndroid: 1,
|
|
92
|
-
};
|
|
93
|
-
const result = await audioRecorderPlayer.startRecorder(undefined, audioSet);
|
|
81
|
+
const result = await audioRecorderPlayer.startRecorder(undefined, (0, nativeModules_1.getAudioRecordingPreset)());
|
|
94
82
|
audioRecorderPlayer.addRecordBackListener((e) => {
|
|
95
83
|
setAudioDuration(audioRecorderPlayer.mmssss(Math.floor(e.currentPosition)));
|
|
96
84
|
return;
|
|
@@ -104,6 +92,9 @@ const FeedbackModal = ({ visible, onClose, screenshotUri, videoUri, onStartRecor
|
|
|
104
92
|
}
|
|
105
93
|
};
|
|
106
94
|
const onStopAudioRecord = async () => {
|
|
95
|
+
if (!audioRecorderPlayer) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
107
98
|
try {
|
|
108
99
|
const result = await audioRecorderPlayer.stopRecorder();
|
|
109
100
|
audioRecorderPlayer.removeRecordBackListener();
|
|
@@ -120,7 +111,7 @@ const FeedbackModal = ({ visible, onClose, screenshotUri, videoUri, onStartRecor
|
|
|
120
111
|
setAudioDuration('00:00');
|
|
121
112
|
};
|
|
122
113
|
const handleClose = async () => {
|
|
123
|
-
if (isRecordingAudio) {
|
|
114
|
+
if (isRecordingAudio && audioRecorderPlayer) {
|
|
124
115
|
try {
|
|
125
116
|
await audioRecorderPlayer.stopRecorder();
|
|
126
117
|
}
|
|
@@ -181,20 +172,13 @@ const FeedbackModal = ({ visible, onClose, screenshotUri, videoUri, onStartRecor
|
|
|
181
172
|
});
|
|
182
173
|
}
|
|
183
174
|
// 4. Metadata
|
|
184
|
-
const metadata =
|
|
185
|
-
device: react_native_device_info_1.default.getModel(),
|
|
186
|
-
systemName: react_native_device_info_1.default.getSystemName(),
|
|
187
|
-
systemVersion: react_native_device_info_1.default.getSystemVersion(),
|
|
188
|
-
appVersion: react_native_device_info_1.default.getVersion(),
|
|
189
|
-
buildNumber: react_native_device_info_1.default.getBuildNumber(),
|
|
190
|
-
brand: react_native_device_info_1.default.getBrand(),
|
|
191
|
-
isEmulator: await react_native_device_info_1.default.isEmulator(),
|
|
192
|
-
};
|
|
175
|
+
const metadata = await (0, nativeModules_1.getDeviceMetadata)();
|
|
193
176
|
formData.append('metadata', JSON.stringify(metadata));
|
|
194
177
|
// 5. Other fields
|
|
195
178
|
formData.append('description', description);
|
|
196
179
|
formData.append('apiKey', apiKey);
|
|
197
|
-
|
|
180
|
+
const deviceUdid = await (0, nativeModules_1.getDeviceUniqueId)();
|
|
181
|
+
formData.append('udid', deviceUdid || 'unknown');
|
|
198
182
|
// 6. Send
|
|
199
183
|
const response = await fetch(`${apiEndpoint}/api/feedback`, {
|
|
200
184
|
method: 'POST',
|
|
@@ -257,9 +241,20 @@ const FeedbackModal = ({ visible, onClose, screenshotUri, videoUri, onStartRecor
|
|
|
257
241
|
{/* Media Actions Row */}
|
|
258
242
|
<react_native_1.View style={styles.actionsRow}>
|
|
259
243
|
{/* Audio Recorder */}
|
|
260
|
-
<react_native_1.TouchableOpacity style={[
|
|
244
|
+
<react_native_1.TouchableOpacity style={[
|
|
245
|
+
styles.actionBtn,
|
|
246
|
+
isRecordingAudio ? styles.recordingBtn : undefined,
|
|
247
|
+
audioUri ? styles.hasAudioBtn : undefined,
|
|
248
|
+
!audioRecordingAvailable ? styles.disabledActionBtn : undefined,
|
|
249
|
+
]} onPress={isRecordingAudio ? onStopAudioRecord : onStartAudioRecord} disabled={!audioRecordingAvailable}>
|
|
261
250
|
<react_native_1.Text style={[styles.actionBtnText, (isRecordingAudio || audioUri) ? { color: '#FFF' } : undefined]}>
|
|
262
|
-
{
|
|
251
|
+
{!audioRecordingAvailable
|
|
252
|
+
? '🎙 Audio Unavailable'
|
|
253
|
+
: isRecordingAudio
|
|
254
|
+
? `Stop (${audioDuration})`
|
|
255
|
+
: audioUri
|
|
256
|
+
? 'Re-record Audio'
|
|
257
|
+
: '🎙 Record Audio'}
|
|
263
258
|
</react_native_1.Text>
|
|
264
259
|
</react_native_1.TouchableOpacity>
|
|
265
260
|
|
|
@@ -372,6 +367,9 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
372
367
|
hasAudioBtn: {
|
|
373
368
|
backgroundColor: '#4CAF50',
|
|
374
369
|
},
|
|
370
|
+
disabledActionBtn: {
|
|
371
|
+
opacity: 0.45,
|
|
372
|
+
},
|
|
375
373
|
actionBtnText: {
|
|
376
374
|
fontSize: 14,
|
|
377
375
|
fontWeight: '600',
|
package/lib/index.js
CHANGED
|
@@ -32,19 +32,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
36
|
exports.unregisterPushToken = exports.registerPushToken = exports.IInstallWrapper = exports.IInstall = void 0;
|
|
40
37
|
const react_1 = __importStar(require("react"));
|
|
41
38
|
const react_native_1 = require("react-native");
|
|
42
|
-
const react_native_view_shot_1 = require("react-native-view-shot");
|
|
43
39
|
const ShakeDetector_1 = require("./ShakeDetector");
|
|
44
40
|
const FeedbackModal_1 = require("./FeedbackModal");
|
|
45
|
-
const react_native_record_screen_1 = __importDefault(require("react-native-record-screen"));
|
|
46
|
-
const react_native_device_info_1 = __importDefault(require("react-native-device-info"));
|
|
47
41
|
const pushRegistration_1 = require("./pushRegistration");
|
|
42
|
+
const nativeModules_1 = require("./nativeModules");
|
|
48
43
|
const IInstall = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enabled = true, showFloatingButtonOnEmulator = true, floatingButtonLabel = 'Report Issue', pushToken, autoRegisterPushToken = true, projectId, onPushTokenRegisterError, }) => {
|
|
49
44
|
const [modalVisible, setModalVisible] = (0, react_1.useState)(false);
|
|
50
45
|
const [screenshotUri, setScreenshotUri] = (0, react_1.useState)(null);
|
|
@@ -62,7 +57,7 @@ const IInstall = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enab
|
|
|
62
57
|
}, [isRecording, modalVisible]);
|
|
63
58
|
(0, react_1.useEffect)(() => {
|
|
64
59
|
let mounted = true;
|
|
65
|
-
|
|
60
|
+
(0, nativeModules_1.isEmulatorDevice)()
|
|
66
61
|
.then((value) => {
|
|
67
62
|
if (mounted) {
|
|
68
63
|
setIsEmulator(value);
|
|
@@ -82,7 +77,7 @@ const IInstall = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enab
|
|
|
82
77
|
return;
|
|
83
78
|
try {
|
|
84
79
|
// Capture screenshot
|
|
85
|
-
const uri = await (0,
|
|
80
|
+
const uri = await (0, nativeModules_1.captureScreenImage)({
|
|
86
81
|
format: 'png',
|
|
87
82
|
quality: 0.8,
|
|
88
83
|
});
|
|
@@ -117,7 +112,7 @@ const IInstall = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enab
|
|
|
117
112
|
return;
|
|
118
113
|
let cancelled = false;
|
|
119
114
|
const syncPushToken = async () => {
|
|
120
|
-
const deviceUdid = await
|
|
115
|
+
const deviceUdid = await (0, nativeModules_1.getDeviceUniqueId)();
|
|
121
116
|
const result = await (0, pushRegistration_1.registerPushToken)({
|
|
122
117
|
token: pushToken,
|
|
123
118
|
apiKey,
|
|
@@ -167,6 +162,7 @@ const IInstall = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enab
|
|
|
167
162
|
showFloatingButtonOnEmulator &&
|
|
168
163
|
!modalVisible &&
|
|
169
164
|
!isRecording;
|
|
165
|
+
const screenRecordingAvailable = (0, nativeModules_1.hasScreenRecordingSupport)();
|
|
170
166
|
const normalizeVideoUri = (value) => {
|
|
171
167
|
if (react_native_1.Platform.OS === 'ios' && value.startsWith('/')) {
|
|
172
168
|
return `file://${value}`;
|
|
@@ -211,10 +207,15 @@ const IInstall = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enab
|
|
|
211
207
|
return null;
|
|
212
208
|
};
|
|
213
209
|
const handleStartRecording = async () => {
|
|
210
|
+
if (!screenRecordingAvailable) {
|
|
211
|
+
console.warn('IInstall: Screen recording unavailable. Install react-native-record-screen in your app and rebuild.');
|
|
212
|
+
setModalVisible(true);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
214
215
|
setModalVisible(false);
|
|
215
216
|
setIsRecording(true);
|
|
216
217
|
try {
|
|
217
|
-
await
|
|
218
|
+
await (0, nativeModules_1.startScreenRecording)({ mic: true });
|
|
218
219
|
}
|
|
219
220
|
catch (e) {
|
|
220
221
|
console.error('Failed to start recording', e);
|
|
@@ -225,7 +226,7 @@ const IInstall = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enab
|
|
|
225
226
|
const handleStopRecording = async () => {
|
|
226
227
|
try {
|
|
227
228
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
228
|
-
const res = await
|
|
229
|
+
const res = await (0, nativeModules_1.stopScreenRecording)();
|
|
229
230
|
if (res) {
|
|
230
231
|
const nextVideoUri = resolveRecordedVideoUri(res);
|
|
231
232
|
if (nextVideoUri) {
|
|
@@ -272,7 +273,7 @@ const IInstall = ({ apiKey, apiEndpoint = 'https://iinstall.app', children, enab
|
|
|
272
273
|
setModalVisible(false);
|
|
273
274
|
setVideoUri(null); // Clear video on close
|
|
274
275
|
setScreenshotUri(null);
|
|
275
|
-
}} screenshotUri={screenshotUri} videoUri={videoUri} onStartRecording={handleStartRecording} apiKey={apiKey} apiEndpoint={apiEndpoint}/>
|
|
276
|
+
}} screenshotUri={screenshotUri} videoUri={videoUri} onStartRecording={screenRecordingAvailable ? handleStartRecording : undefined} apiKey={apiKey} apiEndpoint={apiEndpoint}/>
|
|
276
277
|
</react_native_1.View>);
|
|
277
278
|
};
|
|
278
279
|
exports.IInstall = IInstall;
|
|
@@ -194,12 +194,12 @@ async function captureScreenImage(options) {
|
|
|
194
194
|
}
|
|
195
195
|
function hasAudioRecordingSupport() {
|
|
196
196
|
const moduleRef = getAudioRecorderModule();
|
|
197
|
-
const recorderCtor = moduleRef?.default;
|
|
197
|
+
const recorderCtor = typeof moduleRef === 'function' ? moduleRef : moduleRef?.default;
|
|
198
198
|
return typeof recorderCtor === 'function';
|
|
199
199
|
}
|
|
200
200
|
function createAudioRecorderPlayer() {
|
|
201
201
|
const moduleRef = getAudioRecorderModule();
|
|
202
|
-
const recorderCtor = moduleRef?.default;
|
|
202
|
+
const recorderCtor = typeof moduleRef === 'function' ? moduleRef : moduleRef?.default;
|
|
203
203
|
if (typeof recorderCtor !== 'function') {
|
|
204
204
|
warnOnce('missing:react-native-audio-recorder-player:ctor', '[iInstall SDK] Audio recording unavailable: react-native-audio-recorder-player is not linked correctly.');
|
|
205
205
|
return null;
|
|
@@ -215,15 +215,16 @@ function createAudioRecorderPlayer() {
|
|
|
215
215
|
}
|
|
216
216
|
function getAudioRecordingPreset() {
|
|
217
217
|
const moduleRef = getAudioRecorderModule();
|
|
218
|
+
const namedExports = typeof moduleRef === 'object' && moduleRef !== null ? moduleRef : undefined;
|
|
218
219
|
return {
|
|
219
|
-
AVFormatIDKeyIOS:
|
|
220
|
-
AVEncoderAudioQualityKeyIOS:
|
|
220
|
+
AVFormatIDKeyIOS: namedExports?.AVEncodingOption?.aac ?? 'aac',
|
|
221
|
+
AVEncoderAudioQualityKeyIOS: namedExports?.AVEncoderAudioQualityIOSType?.high ?? 96,
|
|
221
222
|
AVSampleRateKeyIOS: 44100,
|
|
222
223
|
AVNumberOfChannelsKeyIOS: 1,
|
|
223
224
|
AVEncoderBitRateKeyIOS: 128000,
|
|
224
|
-
AudioEncoderAndroid:
|
|
225
|
-
AudioSourceAndroid:
|
|
226
|
-
OutputFormatAndroid:
|
|
225
|
+
AudioEncoderAndroid: namedExports?.AudioEncoderAndroidType?.AAC ?? 3,
|
|
226
|
+
AudioSourceAndroid: namedExports?.AudioSourceAndroidType?.MIC ?? 1,
|
|
227
|
+
OutputFormatAndroid: namedExports?.OutputFormatAndroidType?.MPEG_4 ?? 2,
|
|
227
228
|
AudioEncodingBitRateAndroid: 128000,
|
|
228
229
|
AudioSamplingRateAndroid: 44100,
|
|
229
230
|
AudioChannelsAndroid: 1,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-iinstall",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
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",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
],
|
|
40
40
|
"scripts": {
|
|
41
41
|
"prebuild": "npm run prepare:release",
|
|
42
|
-
"build": "
|
|
42
|
+
"clean:build": "node -e \"require('fs').rmSync('lib',{ recursive: true, force: true })\"",
|
|
43
|
+
"build": "npm run clean:build && tsc",
|
|
43
44
|
"sync:release-manifest": "node scripts/sync-release-manifest.mjs",
|
|
44
45
|
"check:release-manifest": "node scripts/check-release-manifest.mjs",
|
|
45
46
|
"prepare:release": "npm run sync:release-manifest && npm run check:release-manifest",
|
package/src/nativeModules.ts
CHANGED
|
@@ -35,6 +35,8 @@ type AudioRecorderModule = {
|
|
|
35
35
|
OutputFormatAndroidType?: { MPEG_4?: number };
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
+
type AudioRecorderModuleValue = AudioRecorderModule | (new () => AudioRecorderPlayerLike);
|
|
39
|
+
|
|
38
40
|
type RecordBackEvent = {
|
|
39
41
|
currentPosition: number;
|
|
40
42
|
};
|
|
@@ -93,7 +95,7 @@ function loadRawModule<T>(moduleName: string): T | null {
|
|
|
93
95
|
let cachedDeviceInfoModule: DeviceInfoModule | null | undefined;
|
|
94
96
|
let cachedCaptureScreen: ((options?: CaptureScreenOptions) => Promise<string>) | null | undefined;
|
|
95
97
|
let cachedRecordScreenModule: RecordScreenModule | null | undefined;
|
|
96
|
-
let cachedAudioModule:
|
|
98
|
+
let cachedAudioModule: AudioRecorderModuleValue | null | undefined;
|
|
97
99
|
|
|
98
100
|
function getDeviceInfoModule(): DeviceInfoModule | null {
|
|
99
101
|
if (cachedDeviceInfoModule !== undefined) {
|
|
@@ -128,11 +130,11 @@ function getRecordScreenModule(): RecordScreenModule | null {
|
|
|
128
130
|
return cachedRecordScreenModule;
|
|
129
131
|
}
|
|
130
132
|
|
|
131
|
-
function getAudioRecorderModule():
|
|
133
|
+
function getAudioRecorderModule(): AudioRecorderModuleValue | null {
|
|
132
134
|
if (cachedAudioModule !== undefined) {
|
|
133
135
|
return cachedAudioModule;
|
|
134
136
|
}
|
|
135
|
-
cachedAudioModule = loadRawModule<
|
|
137
|
+
cachedAudioModule = loadRawModule<AudioRecorderModuleValue>('react-native-audio-recorder-player');
|
|
136
138
|
return cachedAudioModule;
|
|
137
139
|
}
|
|
138
140
|
|
|
@@ -263,13 +265,15 @@ export async function captureScreenImage(options?: CaptureScreenOptions): Promis
|
|
|
263
265
|
|
|
264
266
|
export function hasAudioRecordingSupport(): boolean {
|
|
265
267
|
const moduleRef = getAudioRecorderModule();
|
|
266
|
-
const recorderCtor =
|
|
268
|
+
const recorderCtor =
|
|
269
|
+
typeof moduleRef === 'function' ? moduleRef : moduleRef?.default;
|
|
267
270
|
return typeof recorderCtor === 'function';
|
|
268
271
|
}
|
|
269
272
|
|
|
270
273
|
export function createAudioRecorderPlayer(): AudioRecorderPlayerLike | null {
|
|
271
274
|
const moduleRef = getAudioRecorderModule();
|
|
272
|
-
const recorderCtor =
|
|
275
|
+
const recorderCtor =
|
|
276
|
+
typeof moduleRef === 'function' ? moduleRef : moduleRef?.default;
|
|
273
277
|
if (typeof recorderCtor !== 'function') {
|
|
274
278
|
warnOnce(
|
|
275
279
|
'missing:react-native-audio-recorder-player:ctor',
|
|
@@ -292,15 +296,16 @@ export function createAudioRecorderPlayer(): AudioRecorderPlayerLike | null {
|
|
|
292
296
|
|
|
293
297
|
export function getAudioRecordingPreset(): Record<string, string | number> {
|
|
294
298
|
const moduleRef = getAudioRecorderModule();
|
|
299
|
+
const namedExports = typeof moduleRef === 'object' && moduleRef !== null ? moduleRef : undefined;
|
|
295
300
|
return {
|
|
296
|
-
AVFormatIDKeyIOS:
|
|
297
|
-
AVEncoderAudioQualityKeyIOS:
|
|
301
|
+
AVFormatIDKeyIOS: namedExports?.AVEncodingOption?.aac ?? 'aac',
|
|
302
|
+
AVEncoderAudioQualityKeyIOS: namedExports?.AVEncoderAudioQualityIOSType?.high ?? 96,
|
|
298
303
|
AVSampleRateKeyIOS: 44100,
|
|
299
304
|
AVNumberOfChannelsKeyIOS: 1,
|
|
300
305
|
AVEncoderBitRateKeyIOS: 128000,
|
|
301
|
-
AudioEncoderAndroid:
|
|
302
|
-
AudioSourceAndroid:
|
|
303
|
-
OutputFormatAndroid:
|
|
306
|
+
AudioEncoderAndroid: namedExports?.AudioEncoderAndroidType?.AAC ?? 3,
|
|
307
|
+
AudioSourceAndroid: namedExports?.AudioSourceAndroidType?.MIC ?? 1,
|
|
308
|
+
OutputFormatAndroid: namedExports?.OutputFormatAndroidType?.MPEG_4 ?? 2,
|
|
304
309
|
AudioEncodingBitRateAndroid: 128000,
|
|
305
310
|
AudioSamplingRateAndroid: 44100,
|
|
306
311
|
AudioChannelsAndroid: 1,
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
const cwd = process.cwd();
|
|
6
|
-
const packagePath = path.join(cwd, 'package.json');
|
|
7
|
-
const manifestPath = path.join(cwd, 'RELEASE_MANIFEST.json');
|
|
8
|
-
function readJson(filePath) {
|
|
9
|
-
const raw = fs.readFileSync(filePath, 'utf8');
|
|
10
|
-
return JSON.parse(raw);
|
|
11
|
-
}
|
|
12
|
-
try {
|
|
13
|
-
if (!fs.existsSync(manifestPath)) {
|
|
14
|
-
console.error('RELEASE_MANIFEST.json not found');
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
const pkg = readJson(packagePath);
|
|
18
|
-
const manifest = readJson(manifestPath);
|
|
19
|
-
const packageVersion = String(pkg?.version || '').trim();
|
|
20
|
-
const manifestVersion = String(manifest?.version || '').trim();
|
|
21
|
-
if (!packageVersion) {
|
|
22
|
-
console.error('SDK package version missing in package.json');
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
if (!manifestVersion) {
|
|
26
|
-
console.error('RELEASE_MANIFEST version missing');
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
if (packageVersion !== manifestVersion) {
|
|
30
|
-
console.error(`Release mismatch: package.json=${packageVersion}, RELEASE_MANIFEST.json=${manifestVersion}`);
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
console.log(`Release manifest check OK: react-native-iinstall@${packageVersion}`);
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
console.error('Failed to verify RELEASE_MANIFEST:', error);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
const cwd = process.cwd();
|
|
6
|
-
const packagePath = path.join(cwd, 'package.json');
|
|
7
|
-
const manifestPath = path.join(cwd, 'RELEASE_MANIFEST.json');
|
|
8
|
-
function readJson(filePath) {
|
|
9
|
-
const raw = fs.readFileSync(filePath, 'utf8');
|
|
10
|
-
return JSON.parse(raw);
|
|
11
|
-
}
|
|
12
|
-
function writeJson(filePath, value) {
|
|
13
|
-
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
14
|
-
}
|
|
15
|
-
try {
|
|
16
|
-
const pkg = readJson(packagePath);
|
|
17
|
-
const packageVersion = String(pkg?.version || '').trim();
|
|
18
|
-
if (!packageVersion) {
|
|
19
|
-
console.error('SDK package version missing in package.json');
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
22
|
-
let manifest = {
|
|
23
|
-
version: packageVersion,
|
|
24
|
-
highlights: [],
|
|
25
|
-
};
|
|
26
|
-
if (fs.existsSync(manifestPath)) {
|
|
27
|
-
manifest = {
|
|
28
|
-
...readJson(manifestPath),
|
|
29
|
-
version: packageVersion,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
writeJson(manifestPath, manifest);
|
|
33
|
-
console.log(`Synced RELEASE_MANIFEST -> react-native-iinstall@${packageVersion}`);
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
console.error('Failed to sync RELEASE_MANIFEST:', error);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
interface FeedbackModalProps {
|
|
3
|
-
visible: boolean;
|
|
4
|
-
onClose: () => void;
|
|
5
|
-
screenshotUri: string | null;
|
|
6
|
-
videoUri?: string | null;
|
|
7
|
-
onStartRecording?: () => void;
|
|
8
|
-
apiKey: string;
|
|
9
|
-
apiEndpoint: string;
|
|
10
|
-
}
|
|
11
|
-
export declare const FeedbackModal: React.FC<FeedbackModalProps>;
|
|
12
|
-
export {};
|