shakebug-react-native 2.4.14 → 2.4.16

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 (32) hide show
  1. package/README.md +6 -0
  2. package/android/src/main/java/com/shakebug/ConnectivityReceiver.java +12 -7
  3. package/android/src/main/java/com/shakebug/DrawingView.java +13 -3
  4. package/android/src/main/java/com/shakebug/RecordScreenModule.java +6 -3
  5. package/android/src/main/java/com/shakebug/ViewShot.java +1 -0
  6. package/ios/CustomScreenShot/ShakebugSDKCustomScreenCapture.m +6 -2
  7. package/ios/ScreenShot/ShakebugCapture.m +3 -1
  8. package/ios/SketchCanvas/DrawingView.m +14 -1
  9. package/package.json +1 -1
  10. package/shakebug-react-native-2.4.15.tgz +0 -0
  11. package/src/APIs/MatchKey.js +1 -1
  12. package/src/Assets/images/shake.png +0 -0
  13. package/src/Assets/images/undo.png +0 -0
  14. package/src/Assets/images/undo@2x.png +0 -0
  15. package/src/Assets/images/undo@3x.png +0 -0
  16. package/src/Componanats/CustomAlert.js +8 -4
  17. package/src/Componanats/Icons.js +278 -0
  18. package/src/Componanats/KeyboardAvoidingWrapper.js +31 -39
  19. package/src/Componanats/LoadingModal.js +7 -4
  20. package/src/Componanats/NavigationContext.js +8 -1
  21. package/src/Componanats/ReviewModal.js +18 -11
  22. package/src/Componanats/SafeAreaHelper.js +70 -0
  23. package/src/Componanats/ShakeFunction.js +655 -364
  24. package/src/Componanats/ShakebugBottomSheet.js +729 -0
  25. package/src/Componanats/Theme.js +96 -0
  26. package/src/Componanats/ZoomableImage.js +350 -0
  27. package/src/Componanats/localization.js +111 -82
  28. package/src/Screens/AddDetailsScreen.js +800 -439
  29. package/src/Screens/FirstScreen.js +950 -540
  30. package/src/Screens/Shakebug.js +78 -44
  31. package/src/Screens/TrimVideoScreen.js +40 -7
  32. package/src/index.tsx +16 -7
@@ -1,257 +1,325 @@
1
- import { View, Text, Platform, Image, ImageBackground, NativeModules, DeviceEventEmitter, NativeEventEmitter, StatusBar, PanResponder, Animated, AppState, Dimensions, BackHandler, LogBox, StyleSheet, SafeAreaView } from 'react-native'
2
- import React, { useRef, useEffect, useState } from 'react'
1
+ import {
2
+ View,
3
+ Text,
4
+ Platform,
5
+ Image,
6
+ NativeModules,
7
+ DeviceEventEmitter,
8
+ NativeEventEmitter,
9
+ StatusBar,
10
+ PanResponder,
11
+ Animated,
12
+ AppState,
13
+ Dimensions,
14
+ LogBox,
15
+ StyleSheet,
16
+ Alert,
17
+ PermissionsAndroid,
18
+ Linking,
19
+ } from 'react-native';
20
+ import React, { useRef, useEffect, useState } from 'react';
3
21
  import { SensorTypes, setUpdateIntervalForType, accelerometer } from '../../Sensor/src/index';
4
22
  import { ShakebugCapture, captureScreen } from '../index';
5
23
  import { TouchableOpacity } from 'react-native';
6
24
  import RecordScreen from '../../RecordScreen/src/index';
7
25
  import { ClearCanvas, RNDrawingView } from '../Screens/StrockView';
8
- import DeviceInfo, { getBaseOs } from '../../DeviceInfo';
26
+ import DeviceInfo from '../../DeviceInfo';
9
27
  import { useNavigation } from './NavigationContext';
10
-
11
- console.ignoredYellowBox = ["Warning: Each", "Warning: Failed", "Possible", "WARN"];
12
-
13
- import { logger, consoleTransport } from "react-native-logs";
14
- import { ActivityIndicator } from 'react-native';
28
+ import { logger, consoleTransport } from 'react-native-logs';
15
29
  import { useGlobalState } from './GlobalStateContext';
16
30
  import LoadingModal from './LoadingModal';
31
+ import CustomeAlert from './CustomAlert';
32
+ import ShakebugBottomSheet from './ShakebugBottomSheet';
33
+ import { getLocalizedString, Localization } from './localization';
34
+ import { launchImageLibrary } from '../../ImageOrVedioPicker/src/index';
17
35
 
18
- LogBox.ignoreAllLogs()
36
+ console.ignoredYellowBox = ['Warning: Each', 'Warning: Failed', 'Possible', 'WARN'];
37
+ LogBox.ignoreAllLogs();
19
38
 
20
39
  let intervalId = null;
21
- const ShakeFunction = ({ children, themeColor, showTutorialScreen, SDKScreenTitle, allowToReportBugByShaking, allowToReportBugByScreenCapture, allowCrashReport }) => {
22
40
 
23
- const { ShakebugSDKScreenShotDetector, ShakebugSDKSharedPreferencesModule, ShakebugSDKCustomScreenCapture} = NativeModules;
41
+ const ShakeFunction = ({
42
+ children,
43
+ themeColor = '#272B32',
44
+ showTutorialScreen,
45
+ SDKScreenTitle,
46
+ allowToReportBugByShaking,
47
+ allowToReportBugByScreenCapture,
48
+ allowCrashReport,
49
+ }) => {
50
+ const {
51
+ ShakebugSDKScreenShotDetector,
52
+ ShakebugSDKSharedPreferencesModule,
53
+ ShakebugSDKCustomScreenCapture,
54
+ RNCameraPermission,
55
+ } = NativeModules;
24
56
 
25
57
  const { navigate, focusedScreen, goToScreenAtIndex } = useNavigation();
26
58
 
27
- let isFocused = focusedScreen == "ShakebugHome";
28
- const [isLoading, setIsLoading] = useState(false)
29
- // const [isScrenShot, setIsScreenShot] = useState(false)
30
-
31
- const { setIsRecordingStart, setIsScreenShot, isScrenShot, isShakebugOpen } = useGlobalState()
32
-
33
- if (Platform.OS == 'android') {
34
- ShakebugSDKScreenShotDetector.startObserving();
35
-
36
- useEffect(() => {
37
- const subscription = DeviceEventEmitter.addListener('ScreenshotTaken', async () => {
38
- if (allowToReportBugByScreenCapture == true) {
39
- if (!global.isScreenShotDetected && global.application_id != "" && global.application_id != undefined) {
40
- OpenShakebug()
41
- }
42
- }
43
- })
59
+ let isFocused = focusedScreen === 'ShakebugHome';
60
+ const [isLoading, setIsLoading] = useState(false);
61
+ const [isBottomSheetOpen, setIsBottomSheetOpen] = useState(false);
62
+ const [capturedScreenshot, setCapturedScreenshot] = useState(null);
63
+ const [isAlert, setIsAlert] = useState(false);
64
+ const [isAlertText, setIsAlertText] = useState('');
44
65
 
45
- return () => subscription.remove()
46
- }, []);
47
- }
48
- else {
49
- const screenShotEmitter = new NativeEventEmitter(ShakebugSDKScreenShotDetector);
50
- useEffect(() => {
51
- const subscription = screenShotEmitter.addListener(
52
- 'ScreenshotTaken',
53
- (event) => {
54
- if (allowToReportBugByScreenCapture == true) {
55
- // setIsScreenShot(true)
56
- if (!global.isScreenShotDetected && global.application_id != "" && global.application_id != undefined) {
57
- OpenShakebug()
58
- }
59
- }
60
- }
61
- );
66
+ const KEY_SHAKE_DISABLED_UNTIL = 'SHAKE_DISABLED_UNTIL';
67
+ const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
68
+ const [isShakeDisabled, setIsShakeDisabled] = useState(false);
69
+ const shakeDisabledUntilRef = useRef(0);
62
70
 
63
- return () => subscription.remove();
64
- }, []);
65
- }
71
+ const { setIsRecordingStart, setIsScreenShot, isScrenShot } = useGlobalState();
66
72
 
67
73
  const ref = useRef();
68
- let canvasRef = useRef()
69
- const [isShow, setIsShow] = useState()
70
- const [time, setTime] = useState("00:00")
71
- const [img, setImg] = useState("")
72
- const [isDraw, setIsDraw] = useState(false)
74
+ let canvasRef = useRef();
75
+ const [isShow, setIsShow] = useState(false);
76
+ const [time, setTime] = useState('00:00');
77
+ const [isDraw, setIsDraw] = useState(false);
73
78
  const startTimeRef = useRef(null);
74
79
  const appState = useRef(AppState.currentState);
75
- const [triggered, setTriggred] = useState(global.isTrigger);
76
- const [restrictedView, setRestrictedView] = useState([])
77
80
 
78
81
  const defaultConfig = {
79
- levels: {
80
- debug: 0,
81
- info: 1,
82
- warn: 2,
83
- error: 3,
84
- },
85
- severity: "debug",
82
+ levels: { debug: 0, info: 1, warn: 2, error: 3 },
83
+ severity: 'debug',
86
84
  transport: consoleTransport,
87
85
  transportOptions: {
88
- colors: {
89
- info: "blueBright",
90
- warn: "yellowBright",
91
- error: "redBright",
92
- },
86
+ colors: { info: 'blueBright', warn: 'yellowBright', error: 'redBright' },
93
87
  },
94
88
  async: true,
95
- dateFormat: "time",
89
+ dateFormat: 'time',
96
90
  printLevel: false,
97
91
  printDate: false,
98
92
  fixedExtLvlLength: false,
99
- enabled: global.makeLogEnabled == true ? true : false,
93
+ enabled: global.makeLogEnabled === true,
100
94
  };
101
95
 
102
-
103
- var log = logger.createLogger(defaultConfig);
96
+ const log = logger.createLogger(defaultConfig);
104
97
 
105
98
  const isBugReportingEnabled = () => {
106
99
  return global.allowBugReport !== false && allowToReportBugByShaking !== false;
107
100
  };
108
101
 
102
+ const checkShakeDisabledStatus = async () => {
103
+ try {
104
+ if (ShakebugSDKSharedPreferencesModule && ShakebugSDKSharedPreferencesModule.getString) {
105
+ const stored = await ShakebugSDKSharedPreferencesModule.getString(KEY_SHAKE_DISABLED_UNTIL);
106
+ if (stored) {
107
+ const timestamp = parseInt(stored, 10);
108
+ if (!isNaN(timestamp) && timestamp > 0) {
109
+ if (Date.now() < timestamp) {
110
+ shakeDisabledUntilRef.current = timestamp;
111
+ setIsShakeDisabled(true);
112
+ return;
113
+ } else {
114
+ // 7 days elapsed, re-enable
115
+ await ShakebugSDKSharedPreferencesModule.setString(KEY_SHAKE_DISABLED_UNTIL, '');
116
+ }
117
+ }
118
+ }
119
+ }
120
+ } catch (e) {
121
+ log.warn('Failed to check shake disabled status', e);
122
+ }
123
+ shakeDisabledUntilRef.current = 0;
124
+ setIsShakeDisabled(false);
125
+ };
126
+
127
+ useEffect(() => {
128
+ checkShakeDisabledStatus();
129
+ }, []);
130
+
131
+ // Hardware screenshot detection
132
+ const allowScreenCaptureRef = useRef(allowToReportBugByScreenCapture);
133
+ const lastScreenshotTimeRef = useRef(0);
134
+
135
+ useEffect(() => {
136
+ allowScreenCaptureRef.current = allowToReportBugByScreenCapture;
137
+ }, [allowToReportBugByScreenCapture]);
138
+
139
+ useEffect(() => {
140
+ try {
141
+ if (ShakebugSDKScreenShotDetector && typeof ShakebugSDKScreenShotDetector.startObserving === 'function') {
142
+ ShakebugSDKScreenShotDetector.startObserving();
143
+ }
144
+ } catch (e) {
145
+ log.warn('Failed to start screenshot observer', e);
146
+ }
147
+
148
+ const onScreenshot = () => {
149
+ const now = Date.now();
150
+ if (now - lastScreenshotTimeRef.current < 1200) {
151
+ return;
152
+ }
153
+ lastScreenshotTimeRef.current = now;
154
+
155
+ log.info('ScreenshotTaken event detected', {
156
+ allowScreenCapture: allowScreenCaptureRef.current,
157
+ isScreenShotDetected: global.isScreenShotDetected,
158
+ isBottomSheetOpen,
159
+ isRecording: global.isRecording,
160
+ });
161
+
162
+ if (allowScreenCaptureRef.current === true) {
163
+ if (!global.isScreenShotDetected && !isBottomSheetOpen && !global.isRecording && !global.isShakebugSDKOpen) {
164
+ OpenShakebug(true);
165
+ }
166
+ }
167
+ };
168
+
169
+ let subscription = null;
170
+ if (Platform.OS === 'android') {
171
+ subscription = DeviceEventEmitter.addListener('ScreenshotTaken', onScreenshot);
172
+ } else if (ShakebugSDKScreenShotDetector) {
173
+ const screenShotEmitter = new NativeEventEmitter(ShakebugSDKScreenShotDetector);
174
+ subscription = screenShotEmitter.addListener('ScreenshotTaken', onScreenshot);
175
+ }
176
+
177
+ return () => {
178
+ if (subscription && typeof subscription.remove === 'function') {
179
+ subscription.remove();
180
+ }
181
+ try {
182
+ if (ShakebugSDKScreenShotDetector && typeof ShakebugSDKScreenShotDetector.stopObserving === 'function') {
183
+ ShakebugSDKScreenShotDetector.stopObserving();
184
+ }
185
+ } catch (e) {}
186
+ };
187
+ }, [isBottomSheetOpen, allowToReportBugByScreenCapture]);
188
+
189
+ // Manual trigger listener
109
190
  useEffect(() => {
110
191
  const triggerSubscription = DeviceEventEmitter.addListener('TriggerBugReportingEvent', () => {
111
- log.info("TriggerBugReportingEvent received. global.isShakebugOpen:", global.isShakebugOpen);
112
- if (!global.isShakebugOpen) {
113
- setTriggred(true);
114
- global.isShakebugOpen = true;
115
- setTimeout(() => {
116
- OpenShakebug(true); // Pass true to bypass shake checks
117
- }, 200);
192
+ log.info('TriggerBugReportingEvent received');
193
+ global.isShakebugOpen = false;
194
+ if (!isBottomSheetOpen && !global.isRecording) {
195
+ OpenShakebug(true);
118
196
  }
119
197
  });
120
198
  return () => triggerSubscription.remove();
121
- }, []);
199
+ }, [isBottomSheetOpen]);
122
200
 
123
201
  setUpdateIntervalForType(SensorTypes.accelerometer, 200);
124
202
 
125
- let iscrashSaveed = ""
126
203
  const BASE_URL = global.ShakebugSDKEndPointURL;
127
204
  const API_KEY = global.ShakebugSDKEndPointURLheaderKey;
128
205
 
129
-
206
+ // Check tutorial screen
130
207
  useEffect(() => {
131
208
  async function fetchIsFirstTime() {
132
209
  let showTutorialScreenFirstTime = showTutorialScreen;
133
210
  const isVisited = await ShakebugSDKSharedPreferencesModule.getString('isVisited');
134
- if (isVisited == "" || isVisited == undefined) {
135
- ShakebugSDKSharedPreferencesModule.setString("isVisited", "true")
211
+ if (isVisited === '' || isVisited === undefined) {
212
+ ShakebugSDKSharedPreferencesModule.setString('isVisited', 'true');
136
213
  }
137
214
 
138
- if (showTutorialScreenFirstTime == true || showTutorialScreenFirstTime == undefined) {
215
+ if (showTutorialScreenFirstTime === true || showTutorialScreenFirstTime === undefined) {
139
216
  setTimeout(() => {
140
- if ((isVisited != "true" || isVisited == "" || isVisited == undefined)) {
141
- navigate("TutorialScreen")
217
+ if (isVisited !== 'true' || isVisited === '' || isVisited === undefined) {
218
+ navigate('TutorialScreen');
142
219
  }
143
- }, 200)
220
+ }, 200);
144
221
  }
145
222
  }
146
223
  fetchIsFirstTime();
147
224
  }, []);
148
225
 
149
-
226
+ // Check saved crash report
150
227
  useEffect(() => {
151
- async function fetchIsFirstTime() {
152
- iscrashSaveed = await ShakebugSDKSharedPreferencesModule.getString('iscrashSaveed')
153
- const Custom_User_Id = await ShakebugSDKSharedPreferencesModule.getString('custom_user_id')
154
-
155
- if (iscrashSaveed.length > 10 && allowCrashReport) {
156
-
157
- let data = JSON.parse(iscrashSaveed)
158
-
159
- const formData = new FormData();
160
- formData.append("method", "addIssue");
161
- formData.append("description", data["desc"]);
162
- formData.append("applicationsid", data["application_id"]);
163
- formData.append("session_id", data["session_id"]);
164
- formData.append("batteryLevel", data["batteryLevel"]);
165
- formData.append("image", "");
166
- formData.append("memory", data["memory"]);
167
- formData.append("email", "");
168
- formData.append("bugtypes", "crashed");
169
- formData.append("devicemodel", `${data["architecture"]} -- ${data["model"]}`);
170
- formData.append("osversion", data["osversion"]);
171
- formData.append("internettype", data["internetType"]);
172
- formData.append("created", data["created"]);
173
- formData.append("isVideo", 0);
174
- formData.append("appversion", data["appversion"]);
175
- formData.append("custom_user_id", Custom_User_Id || "")
176
- formData.append("sdk_version", global.sdk_version)
177
-
228
+ async function fetchCrashData() {
229
+ const iscrashSaveed = await ShakebugSDKSharedPreferencesModule.getString('iscrashSaveed');
230
+ const Custom_User_Id = await ShakebugSDKSharedPreferencesModule.getString('custom_user_id');
178
231
 
232
+ if (iscrashSaveed && iscrashSaveed.length > 10 && allowCrashReport) {
179
233
  try {
234
+ let data = JSON.parse(iscrashSaveed);
235
+ const formData = new FormData();
236
+ formData.append('method', 'addIssue');
237
+ formData.append('description', data['desc']);
238
+ formData.append('applicationsid', data['application_id']);
239
+ formData.append('session_id', data['session_id']);
240
+ formData.append('batteryLevel', data['batteryLevel']);
241
+ formData.append('image', '');
242
+ formData.append('memory', data['memory']);
243
+ formData.append('email', '');
244
+ formData.append('bugtypes', 'crashed');
245
+ formData.append('devicemodel', `${data['architecture']} -- ${data['model']}`);
246
+ formData.append('osversion', data['osversion']);
247
+ formData.append('internettype', data['internetType']);
248
+ formData.append('created', data['created']);
249
+ formData.append('isVideo', 0);
250
+ formData.append('appversion', data['appversion']);
251
+ formData.append('custom_user_id', Custom_User_Id || '');
252
+ formData.append('sdk_version', global.sdk_version);
253
+
180
254
  const response = await fetch(BASE_URL, {
181
255
  method: 'POST',
182
- headers: {
183
- 'API-KEY': API_KEY
184
- },
256
+ headers: { 'API-KEY': API_KEY },
185
257
  body: formData,
186
258
  });
187
259
 
188
- const data = await response.json();
189
-
190
- if (data.code === 200 && data.success === true) {
191
- log.info(data.msg)
192
- ShakebugSDKSharedPreferencesModule.setString("iscrashSaveed", JSON.stringify({}))
193
- } else {
194
-
260
+ const resData = await response.json();
261
+ if (resData.code === 200 && resData.success === true) {
262
+ log.info(resData.msg);
263
+ ShakebugSDKSharedPreferencesModule.setString('iscrashSaveed', JSON.stringify({}));
195
264
  }
196
- }
197
- catch (e) {
198
- log.error("e", e)
265
+ } catch (e) {
266
+ log.error('Crash report send error', e);
199
267
  }
200
268
  }
201
-
202
269
  }
203
- fetchIsFirstTime();
270
+ fetchCrashData();
204
271
  }, []);
205
272
 
206
-
273
+ // Stop screen recording
207
274
  const Stop = () => {
208
- RecordScreen.stopRecording().then(res => {
209
- // setIsScreenShot(true)
210
- global.isMic = false;
211
- global.isRecordingStart = false;
212
- global.IsBackground = "true"
213
- setIsShow(false)
214
- setIsDraw(false)
215
- global.isDraw = false;
216
- global.isShow = false;
217
- startTimeRef.current = null
218
- clearInterval(intervalId);
219
- setIsLoading(false)
220
- global.isRecording = false;
221
- navigate("AddDetailsScreen", { media: res.result.outputURL, mediaType: 'Recordvideo', themeColor: themeColor, SDKScreenTitle: `${SDKScreenTitle}` })
222
- })
223
- .catch(e => {
224
- log.warn("e", e)
275
+ RecordScreen.stopRecording()
276
+ .then((res) => {
277
+ global.isMic = false;
278
+ global.isRecordingStart = false;
279
+ global.IsBackground = 'true';
280
+ setIsShow(false);
281
+ setIsDraw(false);
282
+ global.isDraw = false;
283
+ global.isShow = false;
284
+ startTimeRef.current = null;
285
+ clearInterval(intervalId);
286
+ setIsLoading(false);
287
+ global.isRecording = false;
288
+ navigate('AddDetailsScreen', {
289
+ media: res.result.outputURL,
290
+ mediaType: 'Recordvideo',
291
+ themeColor: themeColor,
292
+ SDKScreenTitle: `${SDKScreenTitle}`,
293
+ });
225
294
  })
226
- }
295
+ .catch((e) => {
296
+ log.warn('Stop recording error', e);
297
+ });
298
+ };
227
299
 
228
300
  useEffect(() => {
229
301
  const handleAppStateChange = (nextAppState) => {
230
- // if (nextAppState == 'background') {
231
- // if (Platform.OS == 'ios' && global.isRecording) {
232
- // Stop()
233
- // }
234
- // }
302
+ if (nextAppState === 'background' || nextAppState === 'inactive') {
303
+ if (global.isRecording) {
304
+ Stop();
305
+ }
306
+ }
235
307
 
236
308
  if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
237
- // Calculate the elapsed time when the app comes back to the foreground
309
+ checkShakeDisabledStatus();
238
310
  if (startTimeRef.current && global.isRecording) {
239
311
  const elapsed = Math.floor((Date.now() - startTimeRef.current) / 1000);
240
312
  updateTimer(elapsed);
241
313
  }
242
-
243
314
  }
244
315
  appState.current = nextAppState;
245
316
  };
246
317
 
247
318
  const AppStateListener = AppState.addEventListener('change', handleAppStateChange);
248
-
249
- // Start the interval initially
250
319
  startTimer();
251
320
 
252
321
  return () => {
253
- // AppState.removeEventListener('change', handleAppStateChange);
254
- AppStateListener.remove()
322
+ AppStateListener.remove();
255
323
  clearInterval(intervalId);
256
324
  };
257
325
  }, []);
@@ -274,27 +342,26 @@ const ShakeFunction = ({ children, themeColor, showTutorialScreen, SDKScreenTitl
274
342
  const sec = parseInt(elapsed % 60);
275
343
  const durationLimit = parseInt(global.videoDuration) || 20;
276
344
 
277
- if (elapsed == durationLimit) {
345
+ if (elapsed === durationLimit) {
278
346
  clearInterval(intervalId);
279
347
  setTimeout(() => {
280
- Stop()
281
- }, 1000)
348
+ Stop();
349
+ }, 1000);
282
350
  }
283
351
 
284
352
  if (elapsed > durationLimit) {
285
- RecordScreen.stopRecording()
286
- .then(() => {
287
- global.isMic = false;
288
- global.isRecordingStart = false;
289
- global.IsBackground = "true"
290
- global.isRecording = false;
291
- setIsShow(false)
292
- setIsDraw(false)
293
- global.isDraw = false;
294
- global.isShow = false;
295
- startTimeRef.current = null
296
- clearInterval(intervalId);
297
- })
353
+ RecordScreen.stopRecording().then(() => {
354
+ global.isMic = false;
355
+ global.isRecordingStart = false;
356
+ global.IsBackground = 'true';
357
+ global.isRecording = false;
358
+ setIsShow(false);
359
+ setIsDraw(false);
360
+ global.isDraw = false;
361
+ global.isShow = false;
362
+ startTimeRef.current = null;
363
+ clearInterval(intervalId);
364
+ });
298
365
  }
299
366
 
300
367
  const minStr = min < 10 ? `0${min}` : min;
@@ -302,206 +369,404 @@ const ShakeFunction = ({ children, themeColor, showTutorialScreen, SDKScreenTitl
302
369
  setTime(`${minStr}:${secStr}`);
303
370
  };
304
371
 
372
+ // Accelerometer subscription for shake detection
305
373
  useEffect(() => {
306
- let xdata = 0;
307
- let ydata = 0;
308
- let zdata = 0;
309
-
310
- DeviceInfo.isEmulator().then(res => {
311
- if (!res && isRecording == false && isBugReportingEnabled()) {
312
- const subscription = accelerometer.subscribe(async ({ x, y, z, timestamp }) => {
374
+ DeviceInfo.isEmulator().then((res) => {
375
+ if (!res && isRecordingStart === false && isBugReportingEnabled()) {
376
+ const subscription = accelerometer.subscribe(async ({ x, y, z }) => {
377
+ // Check if shake is temporarily disabled for 7 days
378
+ if (shakeDisabledUntilRef.current > 0) {
379
+ if (Date.now() < shakeDisabledUntilRef.current) {
380
+ return;
381
+ } else {
382
+ shakeDisabledUntilRef.current = 0;
383
+ setIsShakeDisabled(false);
384
+ if (ShakebugSDKSharedPreferencesModule && ShakebugSDKSharedPreferencesModule.setString) {
385
+ ShakebugSDKSharedPreferencesModule.setString(KEY_SHAKE_DISABLED_UNTIL, '');
386
+ }
387
+ }
388
+ }
313
389
 
314
390
  let calculate = Math.sqrt(x * x + y * y + z * z);
315
391
 
316
- if (global.isTrigger == true && global.isShakebugOpen == false && isBugReportingEnabled()) {
317
- setTriggred(true)
318
- global.isShakebugOpen = true;
319
- setTimeout(() => {
320
- OpenShakebug()
321
- }, 200)
392
+ if (global.isTrigger === true && global.isShakebugOpen === false) {
393
+ global.isTrigger = false;
394
+ OpenShakebug(true);
322
395
  }
323
396
 
324
- if (Platform.OS == 'android' && global.isRecording == false && global.isTrigger == false && isBugReportingEnabled()) {
397
+ if (
398
+ Platform.OS === 'android' &&
399
+ global.isRecording === false &&
400
+ global.isTrigger === false &&
401
+ isBugReportingEnabled()
402
+ ) {
325
403
  if (calculate.toFixed(2) >= 18) {
326
- if (global.isAndroidValid == true && global.application_id != "" && global.application_id != undefined && global.isTrigger == false) {
327
- setIsScreenShot(true)
328
- let ImgUrl = null;
329
- try {
330
- ImgUrl = ref.current ? await ref.current.capture() : null;
331
- } catch (e) {
332
- log.error("Failed to capture screenshot on shake", e);
333
- }
334
- if (!isShow) {
335
- if (ImgUrl != undefined || ImgUrl != null) {
336
- subscription.unsubscribe();
337
- global.IsBackground = false
338
- global.isShakebugSDKOpen = true
339
- // navigate('TestScreen');
340
- navigate("FirstScreen", { img: `${ImgUrl}`, themeColor: `${themeColor}`, SDKScreenTitle: `${SDKScreenTitle}` })
341
- }
342
- else {
343
- setIsScreenShot(false)
344
- }
345
- }
346
- xdata = 0;
347
- ydata = 0;
348
- zdata = 0;
404
+ if (
405
+ global.isAndroidValid === true &&
406
+ global.application_id &&
407
+ !isBottomSheetOpen
408
+ ) {
409
+ captureAndOpenBottomSheet();
349
410
  }
350
411
  }
351
- }
352
- else if (Platform.OS == 'ios' && global.isRecording == false && global.isTrigger == false && isBugReportingEnabled()) {
353
- if (calculate.toFixed(2) >= 2.00) {
354
- if (global.isIOSValid == true && global.application_id != "" && global.application_id != undefined) {
355
- setIsScreenShot(true)
356
-
357
- let ImgUrl = null;
358
- try {
359
- ImgUrl = ref.current ? await ref.current.capture() : null;
360
- } catch (e) {
361
- log.error("Failed to capture screenshot on iOS shake", e);
362
- }
363
- try {
364
- const filePath = await ShakebugSDKCustomScreenCapture.captureScreen();
365
- ImgUrl = filePath
366
- }
367
- catch (e) {
368
-
369
- }
370
- if (ImgUrl != undefined || ImgUrl != null) {
371
- subscription.unsubscribe();
372
- global.isShakebugSDKOpen = true
373
- navigate("FirstScreen", { img: `${ImgUrl}`, themeColor: `${themeColor}`, SDKScreenTitle: `${SDKScreenTitle}` })
374
- }
375
- else {
376
- setIsScreenShot(false)
377
- }
378
-
379
- xdata = 0;
380
- ydata = 0;
381
- zdata = 0;
412
+ } else if (
413
+ Platform.OS === 'ios' &&
414
+ global.isRecording === false &&
415
+ global.isTrigger === false &&
416
+ isBugReportingEnabled()
417
+ ) {
418
+ if (calculate.toFixed(2) >= 2.0) {
419
+ if (
420
+ global.isIOSValid === true &&
421
+ global.application_id &&
422
+ !isBottomSheetOpen
423
+ ) {
424
+ captureAndOpenBottomSheet();
382
425
  }
383
426
  }
384
427
  }
385
- })
428
+ });
386
429
 
387
- if (isFocused == false) {
388
- subscription.unsubscribe()
389
- xdata = 0;
390
- ydata = 0;
391
- zdata = 0;
392
- setTime("00:00")
430
+ if (isFocused === false) {
431
+ subscription.unsubscribe();
432
+ setTime('00:00');
393
433
  pan.setValue({ x: SCREEN_WIDTH * 0.55, y: SCREEN_HEIGHT * 0.85 });
394
434
  }
395
435
  }
396
- else {
397
- setInterval(() => {
398
- if (global.isTrigger == true && global.isShakebugOpen == false && isBugReportingEnabled()) {
399
- global.isShakebugOpen = true;
400
- global.isTrigger = true;
401
- OpenShakebug()
402
- }
403
- }, 1000)
404
- }
405
- })
406
-
407
- }, [isFocused])
436
+ });
437
+ }, [isFocused, isBottomSheetOpen]);
408
438
 
409
- const OpenShakebug = async (bypassShakeCheck = false) => {
410
- if (!bypassShakeCheck && !isBugReportingEnabled()) {
411
- log.warn("OpenShakebug aborted: Bug reporting is disabled.");
439
+ // Capture screenshot and present Bottom Sheet
440
+ const captureAndOpenBottomSheet = async () => {
441
+ if (isBottomSheetOpen || global.isRecording) {
442
+ global.isShakebugOpen = false;
412
443
  return;
413
444
  }
414
- setIsScreenShot(true)
415
- if (global.application_id != "" && global.application_id != undefined && global.session_id != "" && global.session_id != undefined) {
416
- let ImgUrl = null;
445
+
446
+ setIsScreenShot(true);
447
+ let ImgUrl = null;
448
+ try {
449
+ if (ref.current && typeof ref.current.capture === 'function') {
450
+ ImgUrl = await ref.current.capture();
451
+ }
452
+ } catch (e) {
453
+ log.error('Failed to capture screenshot with ref', e);
454
+ }
455
+
456
+ if (!ImgUrl && Platform.OS === 'ios') {
457
+ try {
458
+ const filePath = await ShakebugSDKCustomScreenCapture.captureScreen();
459
+ if (filePath) ImgUrl = filePath;
460
+ } catch (e) {}
461
+ }
462
+
463
+ if (!ImgUrl) {
417
464
  try {
418
- ImgUrl = ref.current ? await ref.current.capture() : null;
419
- } catch (e) {
420
- log.error("Failed to capture screenshot in OpenShakebug", e);
465
+ const screenUri = await captureScreen({ format: 'jpg', quality: 1 });
466
+ if (screenUri) ImgUrl = screenUri;
467
+ } catch (e) {}
468
+ }
469
+
470
+ setIsScreenShot(false);
471
+
472
+ if (!isShow) {
473
+ setCapturedScreenshot(ImgUrl || null);
474
+ setIsBottomSheetOpen(true);
475
+ global.isShakebugSDKOpen = true;
476
+ } else {
477
+ global.isShakebugOpen = false;
478
+ }
479
+ };
480
+
481
+ const OpenShakebug = async (bypassShakeCheck = false) => {
482
+ if (!bypassShakeCheck) {
483
+ if (global.allowBugReport === false || allowToReportBugByShaking === false) {
484
+ log.warn('OpenShakebug aborted: Shaking bug reporting is disabled.');
485
+ global.isShakebugOpen = false;
486
+ return;
421
487
  }
422
- if (!isShow) {
423
- if (ImgUrl != undefined || ImgUrl != null) {
424
- global.IsBackground = false
425
- global.isScreenShotDetected = true;
426
- setTriggred(false)
427
- global.isShakebugSDKOpen = true
428
- navigate("FirstScreen", { img: `${ImgUrl}`, themeColor: `${themeColor}`, SDKScreenTitle: `${SDKScreenTitle}` })
429
- } else {
430
- log.warn("OpenShakebug aborted: Screenshot capture returned null.");
431
- }
432
- } else {
433
- log.warn("OpenShakebug aborted: isShow is true (currently recording).");
488
+ if (shakeDisabledUntilRef.current > 0 && Date.now() < shakeDisabledUntilRef.current) {
489
+ log.warn('OpenShakebug aborted: Shake temporarily disabled by user.');
490
+ global.isShakebugOpen = false;
491
+ return;
434
492
  }
435
493
  } else {
436
- log.warn("OpenShakebug aborted: Missing application_id or session_id. App_Id:", global.application_id, "Session_Id:", global.session_id);
494
+ if (global.allowBugReport === false) {
495
+ log.warn('OpenShakebug aborted: Bug reporting is globally disabled.');
496
+ global.isShakebugOpen = false;
497
+ return;
498
+ }
437
499
  }
438
- }
439
500
 
440
- useEffect(() => {
441
- if (isScrenShot) {
442
- setIsScreenShot(false)
501
+ if (isShow || isBottomSheetOpen || global.isRecording) {
502
+ global.isShakebugOpen = false;
503
+ return;
443
504
  }
444
- setTimeout(() => {
445
- if (global.isRecordingStart == true && global.isRecording != true) {
446
- setIsLoading(true)
447
- try {
448
- RecordScreen.startRecording({
449
- mic: global.isMic,
450
- fps: 30,
451
- bitrate: 1024000,
452
- }).then(res => {
453
- setIsLoading(false)
454
- if (res == 'started') {
455
- setIsRecordingStart(true)
456
- if (focusedScreen != "ShakebugHome") {
457
- goToScreenAtIndex(0)
458
- }
459
- global.isRecording = true;
460
- StatusBar.setHidden(false)
461
- setIsShow(global.isRecording)
462
- global.minutes = 0;
463
- global.Seconds = 0;
464
- startTimeRef.current = null
465
- startTimer()
466
- }
467
- else {
468
- global.isMic = false;
469
- global.isRecordingStart = false;
470
- OpenShakebug()
505
+
506
+ global.isShakebugOpen = true;
507
+ await captureAndOpenBottomSheet();
508
+ };
509
+
510
+ // Bottom Sheet Handlers
511
+ const handleReportIssue = () => {
512
+ setIsBottomSheetOpen(false);
513
+ global.IsBackground = 'false';
514
+ global.isScreenShotDetected = true;
515
+ global.isShakebugSDKOpen = true;
516
+ if (capturedScreenshot) {
517
+ navigate('FirstScreen', {
518
+ img: `${capturedScreenshot}`,
519
+ themeColor: `${themeColor}`,
520
+ SDKScreenTitle: `${SDKScreenTitle}`,
521
+ });
522
+ } else {
523
+ navigate('AddDetailsScreen', {
524
+ media: '',
525
+ mediaType: 'img',
526
+ themeColor: `${themeColor}`,
527
+ SDKScreenTitle: `${SDKScreenTitle}`,
528
+ });
529
+ }
530
+ };
531
+
532
+ const startRecordingProcess = (withMic = false) => {
533
+ setIsBottomSheetOpen(false);
534
+ setIsLoading(true);
535
+ global.isMic = withMic;
536
+ global.isRecordingStart = true;
537
+ try {
538
+ RecordScreen.startRecording({
539
+ mic: withMic,
540
+ fps: 30,
541
+ bitrate: 1024000,
542
+ })
543
+ .then((res) => {
544
+ setIsLoading(false);
545
+ if (res === 'started') {
546
+ setIsRecordingStart(true);
547
+ if (focusedScreen !== 'ShakebugHome') {
548
+ goToScreenAtIndex(0);
471
549
  }
472
- })
473
- .catch(e => {
474
- log.error("e", e)
475
- setIsLoading(false)
476
- })
477
- }
478
- catch (e) {
479
- log.error("error", e)
480
- setIsLoading(false)
550
+ global.isRecording = true;
551
+ StatusBar.setHidden(false);
552
+ setIsShow(true);
553
+ global.minutes = 0;
554
+ global.Seconds = 0;
555
+ startTimeRef.current = null;
556
+ startTimer();
557
+ } else {
558
+ global.isMic = false;
559
+ global.isRecordingStart = false;
560
+ setIsAlertText('Unable to start screen recording. Please try again.');
561
+ setIsAlert(true);
562
+ }
563
+ })
564
+ .catch((e) => {
565
+ log.error('RecordScreen error', e);
566
+ setIsLoading(false);
567
+ });
568
+ } catch (e) {
569
+ log.error('RecordScreen start error', e);
570
+ setIsLoading(false);
571
+ }
572
+ };
573
+
574
+ const handleRecordIssue = () => {
575
+ if (global.AllowVideo === false) {
576
+ setIsAlertText(
577
+ `You have reached your max video limitation. \n\n Used Videos: ${global.used_video} \nAllowed Videos: ${global.total_video}\n\nPlease visit https://www.shakebug.com website and upgrade your plan.`
578
+ );
579
+ setIsAlert(true);
580
+ return;
581
+ }
582
+
583
+ DeviceInfo.isEmulator().then((res) => {
584
+ if (res === true && Platform.OS === 'ios') {
585
+ setIsAlertText('Only real device is supported for recording video');
586
+ setIsAlert(true);
587
+ return;
588
+ }
589
+
590
+ Alert.alert(
591
+ Localization[global.ShakebugLanguage]?.RecordAudio || 'Do you want to record Audio?',
592
+ '',
593
+ [
594
+ {
595
+ text: Localization[global.ShakebugLanguage]?.No || 'No',
596
+ style: 'cancel',
597
+ onPress: () => {
598
+ startRecordingProcess(false);
599
+ },
600
+ },
601
+ {
602
+ text: Localization[global.ShakebugLanguage]?.Yes || 'YES',
603
+ onPress: () => {
604
+ if (Platform.OS === 'android') {
605
+ PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.RECORD_AUDIO).then(
606
+ (permRes) => {
607
+ if (permRes === PermissionsAndroid.RESULTS.GRANTED) {
608
+ startRecordingProcess(true);
609
+ } else {
610
+ startRecordingProcess(false);
611
+ }
612
+ }
613
+ );
614
+ } else {
615
+ if (RNCameraPermission && RNCameraPermission.checkCameraPermissionStatus) {
616
+ RNCameraPermission.checkCameraPermissionStatus().then((permRes) => {
617
+ if (permRes === 'granted') {
618
+ startRecordingProcess(true);
619
+ } else if (permRes === 'denied') {
620
+ Alert.alert(
621
+ '',
622
+ Localization[global.ShakebugLanguage]?.AudioPermission ||
623
+ 'Audio permission required',
624
+ [
625
+ {
626
+ text: Localization[global.ShakebugLanguage]?.No || 'No',
627
+ style: 'cancel',
628
+ onPress: () => startRecordingProcess(false),
629
+ },
630
+ {
631
+ text: Localization[global.ShakebugLanguage]?.Yes || 'YES',
632
+ onPress: () => {
633
+ Linking.openSettings();
634
+ startRecordingProcess(false);
635
+ },
636
+ },
637
+ ]
638
+ );
639
+ } else {
640
+ startRecordingProcess(false);
641
+ }
642
+ });
643
+ } else {
644
+ startRecordingProcess(true);
645
+ }
646
+ }
647
+ },
648
+ },
649
+ ]
650
+ );
651
+ });
652
+ };
653
+
654
+ const handleChooseMedia = () => {
655
+ setIsBottomSheetOpen(false);
656
+ setIsLoading(true);
657
+
658
+ const options = {
659
+ mediaType: 'mixed',
660
+ includeBase64: false,
661
+ selectionLimit: 1,
662
+ };
663
+
664
+ launchImageLibrary(options, async (response) => {
665
+ setIsLoading(false);
666
+ if (response.didCancel) {
667
+ handleDismissBottomSheet();
668
+ return;
669
+ }
670
+ if (response.error || response.errorCode) {
671
+ handleDismissBottomSheet();
672
+ return;
673
+ }
674
+ const asset = response.assets?.[0];
675
+ const mediaUri = (await response.uri) || asset?.uri;
676
+ if (!mediaUri) {
677
+ handleDismissBottomSheet();
678
+ return;
679
+ }
680
+ const mediaType = asset?.type || '';
681
+ const isVideo =
682
+ mediaType.startsWith('video') ||
683
+ mediaUri.toLowerCase().endsWith('.mp4') ||
684
+ mediaUri.toLowerCase().endsWith('.mov') ||
685
+ mediaUri.toLowerCase().endsWith('.m4v');
686
+
687
+ if (isVideo) {
688
+ if (global.AllowVideo === false) {
689
+ setIsAlertText(
690
+ `You have reached your max video limitation. \n\n Used Videos: ${global.used_video} \nAllowed Videos: ${global.total_video}\n\nPlease visit https://www.shakebug.com website and upgrade your plan.`
691
+ );
692
+ setIsAlert(true);
693
+ return;
481
694
  }
695
+ global.IsBackground = 'false';
696
+ global.isShakebugSDKOpen = true;
697
+ navigate('TrimVideoScreen', {
698
+ video: `${mediaUri}`,
699
+ Type: 'video',
700
+ themeColor: themeColor,
701
+ SDKScreenTitle: `${SDKScreenTitle}`,
702
+ UpdateStatusBar: () => StatusBar.setHidden(false),
703
+ updateParent: () => {},
704
+ });
705
+ } else {
706
+ global.IsBackground = 'false';
707
+ global.isShakebugSDKOpen = true;
708
+ navigate('FirstScreen', {
709
+ img: `${mediaUri}`,
710
+ themeColor: themeColor,
711
+ SDKScreenTitle: `${SDKScreenTitle}`,
712
+ });
482
713
  }
483
- }, 1000)
714
+ });
715
+ };
484
716
 
485
- }, [isFocused])
717
+ const handleDismissBottomSheet = () => {
718
+ setIsBottomSheetOpen(false);
719
+ setCapturedScreenshot(null);
720
+ global.isTrigger = false;
721
+ global.isShakebugOpen = false;
722
+ global.isScreenShotDetected = false;
723
+ global.isScreeenShotTaken = false;
724
+ global.isShakebugSDKOpen = false;
725
+ setIsScreenShot(false);
726
+ setIsRecordingStart(false);
727
+ };
486
728
 
729
+ const handleDisableShake = async () => {
730
+ const disabledUntil = Date.now() + SEVEN_DAYS_MS;
731
+ shakeDisabledUntilRef.current = disabledUntil;
732
+ setIsShakeDisabled(true);
733
+ try {
734
+ if (ShakebugSDKSharedPreferencesModule && ShakebugSDKSharedPreferencesModule.setString) {
735
+ await ShakebugSDKSharedPreferencesModule.setString(
736
+ KEY_SHAKE_DISABLED_UNTIL,
737
+ disabledUntil.toString()
738
+ );
739
+ }
740
+ } catch (e) {
741
+ log.warn('Failed to save SHAKE_DISABLED_UNTIL', e);
742
+ }
743
+ };
487
744
 
488
- const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
745
+ const handleEnableShake = async () => {
746
+ shakeDisabledUntilRef.current = 0;
747
+ setIsShakeDisabled(false);
748
+ try {
749
+ if (ShakebugSDKSharedPreferencesModule && ShakebugSDKSharedPreferencesModule.setString) {
750
+ await ShakebugSDKSharedPreferencesModule.setString(KEY_SHAKE_DISABLED_UNTIL, '');
751
+ }
752
+ } catch (e) {
753
+ log.warn('Failed to clear SHAKE_DISABLED_UNTIL', e);
754
+ }
755
+ };
489
756
 
757
+ const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
490
758
  const pan = useRef(new Animated.ValueXY()).current;
491
-
492
759
  const BOUNDARY_X_MIN = 0;
493
- const BOUNDARY_X_MAX = Dimensions.get('window').width * 0.6; // Set your max boundary for x
760
+ const BOUNDARY_X_MAX = Dimensions.get('window').width * 0.6;
494
761
  const BOUNDARY_Y_MIN = 0;
495
- const BOUNDARY_Y_MAX = Dimensions.get('window').height * 0.98; // Set your max boundary for y
762
+ const BOUNDARY_Y_MAX = Dimensions.get('window').height * 0.98;
496
763
 
497
764
  const panResponder = useRef(
498
765
  PanResponder.create({
499
766
  onStartShouldSetPanResponder: () => true,
500
767
  onPanResponderMove: (event, gestureState) => {
501
- // Calculate the new x and y values within the boundaries
502
768
  let newX = Math.max(BOUNDARY_X_MIN, Math.min(BOUNDARY_X_MAX, gestureState.dx + pan.x._offset));
503
769
  let newY = Math.max(BOUNDARY_Y_MIN, Math.min(BOUNDARY_Y_MAX, gestureState.dy + pan.y._offset));
504
-
505
770
  pan.setValue({ x: newX - pan.x._offset, y: newY - pan.y._offset });
506
771
  },
507
772
  onPanResponderRelease: () => {
@@ -510,25 +775,17 @@ const ShakeFunction = ({ children, themeColor, showTutorialScreen, SDKScreenTitl
510
775
  onPanResponderGrant: () => {
511
776
  pan.setOffset({
512
777
  x: pan.x._value,
513
- y: pan.y._value
778
+ y: pan.y._value,
514
779
  });
515
780
  pan.setValue({ x: SCREEN_WIDTH * 0.55, y: SCREEN_HEIGHT * 0.8 });
516
- }
781
+ },
517
782
  })
518
783
  ).current;
519
784
 
520
785
  return (
521
- <View
522
- style={{
523
- width: '100%',
524
- height: '100%'
525
- }}
526
- >
527
-
528
- <ShakebugCapture ref={ref} options={{ format: "jpg", quality: 1, }} style={{ flex: 1 }}>
529
- <View style={{ flex: 1 }}>
530
- {children}
531
- </View>
786
+ <View style={{ width: '100%', height: '100%' }}>
787
+ <ShakebugCapture ref={ref} options={{ format: 'jpg', quality: 1 }} style={{ flex: 1 }}>
788
+ <View style={{ flex: 1 }}>{children}</View>
532
789
  {isDraw && (
533
790
  <RNDrawingView
534
791
  ref={canvasRef}
@@ -540,17 +797,15 @@ const ShakeFunction = ({ children, themeColor, showTutorialScreen, SDKScreenTitl
540
797
  bottom: 0,
541
798
  width: '100%',
542
799
  height: '100%',
543
- zIndex: 999
800
+ zIndex: 999,
544
801
  }}
545
802
  color={'red'}
546
- strokeWidth={Platform.OS == 'android' ? 5 : 3}
803
+ strokeWidth={Platform.OS === 'android' ? 5 : 3}
547
804
  />
548
805
  )}
549
-
550
-
551
806
  </ShakebugCapture>
552
807
 
553
- <LoadingModal visible={isLoading} color={themeColor} />
808
+ {/* Floating Screen Recording Widget */}
554
809
  {isShow ? (
555
810
  <Animated.View
556
811
  {...panResponder.panHandlers}
@@ -567,45 +822,81 @@ const ShakeFunction = ({ children, themeColor, showTutorialScreen, SDKScreenTitl
567
822
  position: 'absolute',
568
823
  backgroundColor: themeColor,
569
824
  flexDirection: 'row',
570
- zIndex: 1
825
+ zIndex: 100,
571
826
  }}
572
827
  >
573
828
  <TouchableOpacity
574
829
  onPress={() => Stop()}
575
830
  hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
576
831
  >
577
- <Image source={require("../Assets/images/stopButton.png")} style={{ height: 30, width: 30 }} />
832
+ <Image
833
+ source={require('../Assets/images/stopButton.png')}
834
+ style={{ height: 30, width: 30 }}
835
+ />
578
836
  </TouchableOpacity>
579
837
 
580
- <Text style={{ color: "white", fontSize: 18, marginRight: 5, marginLeft: 5, fontWeight: '700' }}>{time}</Text>
838
+ <Text style={{ color: 'white', fontSize: 18, marginRight: 5, marginLeft: 5, fontWeight: '700' }}>
839
+ {time}
840
+ </Text>
581
841
 
582
- {isDraw == false ? (
842
+ {isDraw === false ? (
583
843
  <TouchableOpacity
584
844
  onPress={() => {
585
- setIsDraw(true)
586
- global.isDraw = true
845
+ setIsDraw(true);
846
+ global.isDraw = true;
587
847
  }}
588
848
  hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
589
849
  >
590
- <Image source={require("../Assets/images/OpenSketch.png")} style={{ height: 25, width: 25 }} />
850
+ <Image
851
+ source={require('../Assets/images/OpenSketch.png')}
852
+ style={{ height: 25, width: 25 }}
853
+ />
591
854
  </TouchableOpacity>
592
855
  ) : (
593
856
  <TouchableOpacity
594
857
  onPress={() => {
595
- setIsDraw(false)
596
- global.isDraw = false
597
- ClearCanvas(canvasRef)
858
+ setIsDraw(false);
859
+ global.isDraw = false;
860
+ ClearCanvas(canvasRef);
598
861
  }}
599
862
  hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
600
863
  >
601
- <Image source={require("../Assets/images/close.png")} style={{ height: 30, width: 30 }} />
864
+ <Image
865
+ source={require('../Assets/images/close.png')}
866
+ style={{ height: 30, width: 30 }}
867
+ />
602
868
  </TouchableOpacity>
603
869
  )}
604
870
  </Animated.View>
605
871
  ) : null}
872
+
873
+ {/* Bottom Sheet on Shake / Trigger */}
874
+ <ShakebugBottomSheet
875
+ visible={isBottomSheetOpen}
876
+ onClose={handleDismissBottomSheet}
877
+ onReportIssue={handleReportIssue}
878
+ onRecordIssue={handleRecordIssue}
879
+ onChooseMedia={handleChooseMedia}
880
+ themeColor={themeColor}
881
+ isShakeDisabled={isShakeDisabled}
882
+ onDisableShake={handleDisableShake}
883
+ onEnableShake={handleEnableShake}
884
+ screenshot={capturedScreenshot}
885
+ />
886
+
887
+ <CustomeAlert
888
+ isVisible={isAlert}
889
+ text={isAlertText}
890
+ color={themeColor}
891
+ OnPrees={() => {
892
+ setIsAlert(false);
893
+ setIsAlertText('');
894
+ }}
895
+ />
896
+
897
+ <LoadingModal visible={isLoading} color={themeColor} />
606
898
  </View>
607
899
  );
608
-
609
- }
900
+ };
610
901
 
611
902
  export default ShakeFunction;