shakebug-react-native 2.4.15 → 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.
- package/README.md +6 -0
- package/android/src/main/java/com/shakebug/ConnectivityReceiver.java +12 -7
- package/android/src/main/java/com/shakebug/DrawingView.java +13 -3
- package/android/src/main/java/com/shakebug/RecordScreenModule.java +6 -3
- package/android/src/main/java/com/shakebug/ViewShot.java +1 -0
- package/ios/CustomScreenShot/ShakebugSDKCustomScreenCapture.m +6 -2
- package/ios/ScreenShot/ShakebugCapture.m +3 -1
- package/ios/SketchCanvas/DrawingView.m +14 -1
- package/package.json +1 -1
- package/shakebug-react-native-2.4.15.tgz +0 -0
- package/src/APIs/MatchKey.js +1 -1
- package/src/Assets/images/shake.png +0 -0
- package/src/Assets/images/undo.png +0 -0
- package/src/Assets/images/undo@2x.png +0 -0
- package/src/Assets/images/undo@3x.png +0 -0
- package/src/Componanats/CustomAlert.js +8 -4
- package/src/Componanats/Icons.js +278 -0
- package/src/Componanats/KeyboardAvoidingWrapper.js +31 -39
- package/src/Componanats/LoadingModal.js +7 -4
- package/src/Componanats/NavigationContext.js +8 -1
- package/src/Componanats/ReviewModal.js +18 -11
- package/src/Componanats/SafeAreaHelper.js +70 -0
- package/src/Componanats/ShakeFunction.js +655 -364
- package/src/Componanats/ShakebugBottomSheet.js +729 -0
- package/src/Componanats/Theme.js +96 -0
- package/src/Componanats/ZoomableImage.js +350 -0
- package/src/Componanats/localization.js +32 -3
- package/src/Screens/AddDetailsScreen.js +800 -439
- package/src/Screens/FirstScreen.js +950 -540
- package/src/Screens/Shakebug.js +78 -44
- package/src/Screens/TrimVideoScreen.js +40 -7
- package/src/index.tsx +16 -7
- package/shakebug-react-native-2.4.14.tgz +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { useColorScheme, Appearance } from 'react-native';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Determines whether Shakebug should render in dark mode.
|
|
5
|
+
* Priority:
|
|
6
|
+
* 1. Explicit developer override (global.ShakebugTheme / global.ShakebugDarkMode)
|
|
7
|
+
* 2. Automatically inferred from application / device theme (useColorScheme / Appearance)
|
|
8
|
+
*/
|
|
9
|
+
export const isShakebugDark = (colorScheme) => {
|
|
10
|
+
if (global.ShakebugTheme === 'dark' || global.ShakebugDarkMode === true) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
if (global.ShakebugTheme === 'light' || global.ShakebugDarkMode === false) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Automatically take theme from the application / system
|
|
18
|
+
const scheme = (colorScheme !== undefined && colorScheme !== null)
|
|
19
|
+
? colorScheme
|
|
20
|
+
: (Appearance && Appearance.getColorScheme ? Appearance.getColorScheme() : null);
|
|
21
|
+
|
|
22
|
+
return scheme === 'dark';
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const useTheme = (customHeaderColor) => {
|
|
26
|
+
// Automatically detects and follows application / system theme
|
|
27
|
+
const colorScheme = useColorScheme();
|
|
28
|
+
const isDark = isShakebugDark(colorScheme);
|
|
29
|
+
|
|
30
|
+
// Always respect the given Shakebug theme color, falling back to default
|
|
31
|
+
const brandColor = customHeaderColor || '#2E2E2E';
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
isDark,
|
|
35
|
+
theme: isDark ? 'dark' : 'light',
|
|
36
|
+
|
|
37
|
+
// Primary Brand / Header Color
|
|
38
|
+
brandColor,
|
|
39
|
+
headerBg: brandColor,
|
|
40
|
+
headerText: '#ffffff',
|
|
41
|
+
|
|
42
|
+
// Screen backgrounds (clean white in light mode, dark charcoal in dark mode)
|
|
43
|
+
backgroundColor: isDark ? '#21252C' : '#ffffff',
|
|
44
|
+
detailsBg: isDark ? '#21252C' : '#ffffff',
|
|
45
|
+
|
|
46
|
+
// Card and surface backgrounds
|
|
47
|
+
cardBg: isDark ? '#21252C' : '#ffffff',
|
|
48
|
+
surfaceBg: isDark ? '#21252C' : '#ffffff',
|
|
49
|
+
|
|
50
|
+
// Bottom Sheet background
|
|
51
|
+
sheetBg: isDark ? '#313A46' : '#ffffff',
|
|
52
|
+
sheetHandle: isDark ? '#313A46' : '#d1d5db',
|
|
53
|
+
|
|
54
|
+
// Text colors
|
|
55
|
+
textColor: isDark ? '#ffffff' : '#0f172a',
|
|
56
|
+
textSecondary: isDark ? '#8e9db0' : '#64748b',
|
|
57
|
+
titleColor: isDark ? '#ffffff' : '#0f172a',
|
|
58
|
+
|
|
59
|
+
// Borders
|
|
60
|
+
borderColor: isDark ? '#2c323d' : '#e2e8f0',
|
|
61
|
+
inputBorder: isDark ? '#363b46' : '#e2e8f0',
|
|
62
|
+
|
|
63
|
+
// Inputs
|
|
64
|
+
inputBg: isDark ? '#2b2f38' : '#ffffff',
|
|
65
|
+
placeholderColor: isDark ? '#8e9db0' : '#94a3b8',
|
|
66
|
+
|
|
67
|
+
// Action buttons (Replace Image / Replace Video)
|
|
68
|
+
buttonBg: isDark ? '#363a44' : '#edf2f7',
|
|
69
|
+
buttonBorder: isDark ? '#444955' : '#cbd5e1',
|
|
70
|
+
buttonText: isDark ? '#8e9db0' : '#64748b',
|
|
71
|
+
|
|
72
|
+
// Send button in header
|
|
73
|
+
sendButtonBg: '#ffffff',
|
|
74
|
+
sendButtonText: brandColor,
|
|
75
|
+
|
|
76
|
+
// Bottom sheet buttons
|
|
77
|
+
sheetPrimaryBtnBg: brandColor,
|
|
78
|
+
sheetPrimaryBtnText: '#ffffff',
|
|
79
|
+
sheetButtonBg: isDark ? '#313A46' : '#ffffff',
|
|
80
|
+
sheetButtonBorder: isDark ? '#444b57' : '#e2e8f0',
|
|
81
|
+
sheetButtonText: isDark ? '#ebeef1' : '#0f172a',
|
|
82
|
+
|
|
83
|
+
// Floating annotation bar (always dark floating pill for contrast against screenshots)
|
|
84
|
+
floatingBarBg: '#242b35',
|
|
85
|
+
floatingBarBorder: 'transparent',
|
|
86
|
+
|
|
87
|
+
// Confirmation Modal
|
|
88
|
+
confirmModalBg: isDark ? '#2b2f38' : '#ffffff',
|
|
89
|
+
confirmModalText: isDark ? '#ffffff' : '#111827',
|
|
90
|
+
confirmButtonBg: isDark ? '#3a404c' : '#e2e8f0',
|
|
91
|
+
confirmCancelText: isDark ? '#ffffff' : '#111827',
|
|
92
|
+
confirmDisableText: '#ef4444',
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export default useTheme;
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import React, { useRef, useEffect } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Image,
|
|
5
|
+
PanResponder,
|
|
6
|
+
Animated,
|
|
7
|
+
StyleSheet,
|
|
8
|
+
Dimensions,
|
|
9
|
+
Platform,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
|
|
12
|
+
const ZoomableImage = ({ uri, style, resizeMode = 'contain' }) => {
|
|
13
|
+
const scale = useRef(new Animated.Value(1)).current;
|
|
14
|
+
const pan = useRef(new Animated.ValueXY({ x: 0, y: 0 })).current;
|
|
15
|
+
|
|
16
|
+
// Track values synchronously for smooth gesture calculations
|
|
17
|
+
const currentScaleRef = useRef(1);
|
|
18
|
+
const currentPanRef = useRef({ x: 0, y: 0 });
|
|
19
|
+
|
|
20
|
+
const isPinchingRef = useRef(false);
|
|
21
|
+
const isPanningRef = useRef(false);
|
|
22
|
+
const initialDistanceRef = useRef(0);
|
|
23
|
+
const baseScaleRef = useRef(1);
|
|
24
|
+
const initialCenterRef = useRef({ x: 0, y: 0 });
|
|
25
|
+
const basePanRef = useRef({ x: 0, y: 0 });
|
|
26
|
+
const panStartTouchRef = useRef({ x: 0, y: 0 });
|
|
27
|
+
const lastTapRef = useRef(0);
|
|
28
|
+
|
|
29
|
+
const windowDim = Dimensions.get('window');
|
|
30
|
+
const layoutSizeRef = useRef({
|
|
31
|
+
width: windowDim.width,
|
|
32
|
+
height: 300,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const scaleId = scale.addListener(({ value }) => {
|
|
37
|
+
currentScaleRef.current = value;
|
|
38
|
+
});
|
|
39
|
+
const panId = pan.addListener(({ x, y }) => {
|
|
40
|
+
currentPanRef.current = { x, y };
|
|
41
|
+
});
|
|
42
|
+
return () => {
|
|
43
|
+
if (typeof scale.removeListener === 'function') {
|
|
44
|
+
scale.removeListener(scaleId);
|
|
45
|
+
}
|
|
46
|
+
if (typeof pan.removeListener === 'function') {
|
|
47
|
+
pan.removeListener(panId);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}, [scale, pan]);
|
|
51
|
+
|
|
52
|
+
const onLayout = (e) => {
|
|
53
|
+
const { width, height } = e.nativeEvent.layout;
|
|
54
|
+
if (width > 0 && height > 0) {
|
|
55
|
+
layoutSizeRef.current = { width, height };
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const getDistance = (touches) => {
|
|
60
|
+
if (!touches || touches.length < 2) return 0;
|
|
61
|
+
const dx = touches[0].pageX - touches[1].pageX;
|
|
62
|
+
const dy = touches[0].pageY - touches[1].pageY;
|
|
63
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const getCenter = (touches) => {
|
|
67
|
+
if (!touches || touches.length < 2) return { x: 0, y: 0 };
|
|
68
|
+
return {
|
|
69
|
+
x: (touches[0].pageX + touches[1].pageX) / 2,
|
|
70
|
+
y: (touches[0].pageY + touches[1].pageY) / 2,
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const handleRelease = () => {
|
|
75
|
+
isPinchingRef.current = false;
|
|
76
|
+
isPanningRef.current = false;
|
|
77
|
+
initialDistanceRef.current = 0;
|
|
78
|
+
|
|
79
|
+
if (currentScaleRef.current < 1.1) {
|
|
80
|
+
// Smoothly spring back to 1x and center (0, 0)
|
|
81
|
+
Animated.parallel([
|
|
82
|
+
Animated.spring(scale, {
|
|
83
|
+
toValue: 1,
|
|
84
|
+
friction: 7,
|
|
85
|
+
tension: 50,
|
|
86
|
+
useNativeDriver: true,
|
|
87
|
+
}),
|
|
88
|
+
Animated.spring(pan, {
|
|
89
|
+
toValue: { x: 0, y: 0 },
|
|
90
|
+
friction: 7,
|
|
91
|
+
tension: 50,
|
|
92
|
+
useNativeDriver: true,
|
|
93
|
+
}),
|
|
94
|
+
]).start();
|
|
95
|
+
currentScaleRef.current = 1;
|
|
96
|
+
currentPanRef.current = { x: 0, y: 0 };
|
|
97
|
+
} else {
|
|
98
|
+
// Clamping within bounds
|
|
99
|
+
const { width, height } = layoutSizeRef.current;
|
|
100
|
+
const effectiveScale = Math.min(currentScaleRef.current, 4.5);
|
|
101
|
+
const maxX = Math.max(0, ((effectiveScale - 1) * width) / 2);
|
|
102
|
+
const maxY = Math.max(0, ((effectiveScale - 1) * height) / 2);
|
|
103
|
+
|
|
104
|
+
const boundedX = Math.max(-maxX, Math.min(maxX, currentPanRef.current.x));
|
|
105
|
+
const boundedY = Math.max(-maxY, Math.min(maxY, currentPanRef.current.y));
|
|
106
|
+
|
|
107
|
+
const animations = [];
|
|
108
|
+
|
|
109
|
+
if (currentScaleRef.current > 4.5) {
|
|
110
|
+
animations.push(
|
|
111
|
+
Animated.spring(scale, {
|
|
112
|
+
toValue: 4.0,
|
|
113
|
+
friction: 7,
|
|
114
|
+
tension: 50,
|
|
115
|
+
useNativeDriver: true,
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
currentScaleRef.current = 4.0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (
|
|
122
|
+
Math.abs(boundedX - currentPanRef.current.x) > 0.5 ||
|
|
123
|
+
Math.abs(boundedY - currentPanRef.current.y) > 0.5
|
|
124
|
+
) {
|
|
125
|
+
animations.push(
|
|
126
|
+
Animated.spring(pan, {
|
|
127
|
+
toValue: { x: boundedX, y: boundedY },
|
|
128
|
+
friction: 7,
|
|
129
|
+
tension: 50,
|
|
130
|
+
useNativeDriver: true,
|
|
131
|
+
})
|
|
132
|
+
);
|
|
133
|
+
currentPanRef.current = { x: boundedX, y: boundedY };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (animations.length > 0) {
|
|
137
|
+
Animated.parallel(animations).start();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const panResponder = useRef(
|
|
143
|
+
PanResponder.create({
|
|
144
|
+
onStartShouldSetPanResponder: () => true,
|
|
145
|
+
onStartShouldSetPanResponderCapture: (evt) => {
|
|
146
|
+
return evt.nativeEvent.touches && evt.nativeEvent.touches.length > 1;
|
|
147
|
+
},
|
|
148
|
+
onMoveShouldSetPanResponder: (evt, gestureState) => {
|
|
149
|
+
const touches = evt.nativeEvent.touches;
|
|
150
|
+
return (
|
|
151
|
+
(touches && touches.length > 1) ||
|
|
152
|
+
(currentScaleRef.current > 1.05 &&
|
|
153
|
+
(Math.abs(gestureState.dx) > 2 || Math.abs(gestureState.dy) > 2))
|
|
154
|
+
);
|
|
155
|
+
},
|
|
156
|
+
onMoveShouldSetPanResponderCapture: (evt) => {
|
|
157
|
+
return evt.nativeEvent.touches && evt.nativeEvent.touches.length > 1;
|
|
158
|
+
},
|
|
159
|
+
onPanResponderTerminationRequest: (evt) => {
|
|
160
|
+
// Never allow parent ScrollViews to steal active multi-touch pinch or zoomed-in pan
|
|
161
|
+
if (
|
|
162
|
+
(evt.nativeEvent.touches && evt.nativeEvent.touches.length > 1) ||
|
|
163
|
+
currentScaleRef.current > 1.05
|
|
164
|
+
) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
return true;
|
|
168
|
+
},
|
|
169
|
+
onShouldBlockNativeResponder: () => true,
|
|
170
|
+
|
|
171
|
+
onPanResponderGrant: (evt) => {
|
|
172
|
+
const touches = evt.nativeEvent.touches;
|
|
173
|
+
const now = Date.now();
|
|
174
|
+
|
|
175
|
+
if (touches && touches.length >= 2) {
|
|
176
|
+
isPinchingRef.current = true;
|
|
177
|
+
isPanningRef.current = false;
|
|
178
|
+
initialDistanceRef.current = getDistance(touches);
|
|
179
|
+
baseScaleRef.current = currentScaleRef.current;
|
|
180
|
+
initialCenterRef.current = getCenter(touches);
|
|
181
|
+
basePanRef.current = { ...currentPanRef.current };
|
|
182
|
+
} else {
|
|
183
|
+
isPinchingRef.current = false;
|
|
184
|
+
isPanningRef.current = true;
|
|
185
|
+
if (touches && touches.length === 1) {
|
|
186
|
+
panStartTouchRef.current = {
|
|
187
|
+
x: touches[0].pageX,
|
|
188
|
+
y: touches[0].pageY,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
basePanRef.current = { ...currentPanRef.current };
|
|
192
|
+
|
|
193
|
+
// Double tap toggle: 1x <-> 2.2x
|
|
194
|
+
if (now - lastTapRef.current < 300) {
|
|
195
|
+
lastTapRef.current = 0;
|
|
196
|
+
if (currentScaleRef.current > 1.2) {
|
|
197
|
+
Animated.parallel([
|
|
198
|
+
Animated.spring(scale, {
|
|
199
|
+
toValue: 1,
|
|
200
|
+
friction: 7,
|
|
201
|
+
tension: 50,
|
|
202
|
+
useNativeDriver: true,
|
|
203
|
+
}),
|
|
204
|
+
Animated.spring(pan, {
|
|
205
|
+
toValue: { x: 0, y: 0 },
|
|
206
|
+
friction: 7,
|
|
207
|
+
tension: 50,
|
|
208
|
+
useNativeDriver: true,
|
|
209
|
+
}),
|
|
210
|
+
]).start();
|
|
211
|
+
currentScaleRef.current = 1;
|
|
212
|
+
currentPanRef.current = { x: 0, y: 0 };
|
|
213
|
+
} else {
|
|
214
|
+
Animated.spring(scale, {
|
|
215
|
+
toValue: 2.2,
|
|
216
|
+
friction: 7,
|
|
217
|
+
tension: 50,
|
|
218
|
+
useNativeDriver: true,
|
|
219
|
+
}).start();
|
|
220
|
+
currentScaleRef.current = 2.2;
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
lastTapRef.current = now;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
onPanResponderMove: (evt) => {
|
|
229
|
+
const touches = evt.nativeEvent.touches;
|
|
230
|
+
if (touches && touches.length >= 2) {
|
|
231
|
+
const currentDist = getDistance(touches);
|
|
232
|
+
const currentCenter = getCenter(touches);
|
|
233
|
+
|
|
234
|
+
if (!isPinchingRef.current || !initialDistanceRef.current) {
|
|
235
|
+
isPinchingRef.current = true;
|
|
236
|
+
isPanningRef.current = false;
|
|
237
|
+
initialDistanceRef.current = currentDist;
|
|
238
|
+
baseScaleRef.current = currentScaleRef.current;
|
|
239
|
+
initialCenterRef.current = currentCenter;
|
|
240
|
+
basePanRef.current = { ...currentPanRef.current };
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (initialDistanceRef.current > 0) {
|
|
245
|
+
const factor = currentDist / initialDistanceRef.current;
|
|
246
|
+
const targetScale = Math.max(
|
|
247
|
+
0.85,
|
|
248
|
+
Math.min(5.0, baseScaleRef.current * factor)
|
|
249
|
+
);
|
|
250
|
+
scale.setValue(targetScale);
|
|
251
|
+
currentScaleRef.current = targetScale;
|
|
252
|
+
|
|
253
|
+
const deltaX = currentCenter.x - initialCenterRef.current.x;
|
|
254
|
+
const deltaY = currentCenter.y - initialCenterRef.current.y;
|
|
255
|
+
const targetPanX = basePanRef.current.x + deltaX;
|
|
256
|
+
const targetPanY = basePanRef.current.y + deltaY;
|
|
257
|
+
pan.setValue({ x: targetPanX, y: targetPanY });
|
|
258
|
+
currentPanRef.current = { x: targetPanX, y: targetPanY };
|
|
259
|
+
}
|
|
260
|
+
} else if (touches && touches.length === 1) {
|
|
261
|
+
if (isPinchingRef.current) {
|
|
262
|
+
isPinchingRef.current = false;
|
|
263
|
+
isPanningRef.current = true;
|
|
264
|
+
initialDistanceRef.current = 0;
|
|
265
|
+
panStartTouchRef.current = {
|
|
266
|
+
x: touches[0].pageX,
|
|
267
|
+
y: touches[0].pageY,
|
|
268
|
+
};
|
|
269
|
+
basePanRef.current = { ...currentPanRef.current };
|
|
270
|
+
} else if (isPanningRef.current && currentScaleRef.current > 1.05) {
|
|
271
|
+
const deltaX = touches[0].pageX - panStartTouchRef.current.x;
|
|
272
|
+
const deltaY = touches[0].pageY - panStartTouchRef.current.y;
|
|
273
|
+
const targetPanX = basePanRef.current.x + deltaX;
|
|
274
|
+
const targetPanY = basePanRef.current.y + deltaY;
|
|
275
|
+
pan.setValue({ x: targetPanX, y: targetPanY });
|
|
276
|
+
currentPanRef.current = { x: targetPanX, y: targetPanY };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
onPanResponderRelease: handleRelease,
|
|
282
|
+
onPanResponderTerminate: handleRelease,
|
|
283
|
+
})
|
|
284
|
+
).current;
|
|
285
|
+
|
|
286
|
+
return (
|
|
287
|
+
<View
|
|
288
|
+
style={[styles.container, style]}
|
|
289
|
+
onLayout={onLayout}
|
|
290
|
+
collapsable={false}
|
|
291
|
+
>
|
|
292
|
+
<Animated.View
|
|
293
|
+
{...panResponder.panHandlers}
|
|
294
|
+
style={[
|
|
295
|
+
styles.imageWrapper,
|
|
296
|
+
{
|
|
297
|
+
transform: [
|
|
298
|
+
{ translateX: pan.x },
|
|
299
|
+
{ translateY: pan.y },
|
|
300
|
+
{ scale },
|
|
301
|
+
],
|
|
302
|
+
},
|
|
303
|
+
]}
|
|
304
|
+
collapsable={false}
|
|
305
|
+
>
|
|
306
|
+
{uri ? (
|
|
307
|
+
<Image
|
|
308
|
+
source={{
|
|
309
|
+
uri:
|
|
310
|
+
Platform.OS === 'android' &&
|
|
311
|
+
typeof uri === 'string' &&
|
|
312
|
+
!uri.startsWith('file://') &&
|
|
313
|
+
!uri.startsWith('content://') &&
|
|
314
|
+
!uri.startsWith('http')
|
|
315
|
+
? `file://${uri}`
|
|
316
|
+
: uri,
|
|
317
|
+
}}
|
|
318
|
+
style={styles.image}
|
|
319
|
+
resizeMode={resizeMode}
|
|
320
|
+
/>
|
|
321
|
+
) : null}
|
|
322
|
+
</Animated.View>
|
|
323
|
+
</View>
|
|
324
|
+
);
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const styles = StyleSheet.create({
|
|
328
|
+
container: {
|
|
329
|
+
flex: 1,
|
|
330
|
+
width: '100%',
|
|
331
|
+
height: '100%',
|
|
332
|
+
overflow: 'hidden',
|
|
333
|
+
justifyContent: 'center',
|
|
334
|
+
alignItems: 'center',
|
|
335
|
+
backgroundColor: 'transparent',
|
|
336
|
+
},
|
|
337
|
+
imageWrapper: {
|
|
338
|
+
width: '100%',
|
|
339
|
+
height: '100%',
|
|
340
|
+
justifyContent: 'center',
|
|
341
|
+
alignItems: 'center',
|
|
342
|
+
backgroundColor: 'transparent',
|
|
343
|
+
},
|
|
344
|
+
image: {
|
|
345
|
+
width: '100%',
|
|
346
|
+
height: '100%',
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
export default ZoomableImage;
|
|
@@ -2,7 +2,8 @@ export const Localization = {
|
|
|
2
2
|
"ENGLISH": {
|
|
3
3
|
InterNetConnetionError: "Please check your internet connection",
|
|
4
4
|
FirstScreenTitle: "Annotate your bug",
|
|
5
|
-
SecondScreenTitle: "Add
|
|
5
|
+
SecondScreenTitle: "Add details",
|
|
6
|
+
AddDetails: "Add details",
|
|
6
7
|
RecordVideo: "Record Video",
|
|
7
8
|
ChooseVideo: "Choose Video",
|
|
8
9
|
Done: "Done",
|
|
@@ -21,7 +22,24 @@ export const Localization = {
|
|
|
21
22
|
EmailError: "Please enter valid email address",
|
|
22
23
|
BugSubmited: "Thanks for your help!",
|
|
23
24
|
ReplaceImage: "Replace Image",
|
|
24
|
-
ReplaceVideo: "Replace Video"
|
|
25
|
+
ReplaceVideo: "Replace Video",
|
|
26
|
+
ShakeDetected: "Shake detected",
|
|
27
|
+
SomethingNotWorking: "Something not working?",
|
|
28
|
+
SavedScreenshot: "We saved a screenshot of this screen.",
|
|
29
|
+
ReportAnIssue: "Report an issue",
|
|
30
|
+
RecordIssueInstead: "Record the issue instead",
|
|
31
|
+
ChooseMedia: "Choose media",
|
|
32
|
+
NotNow: "Not now",
|
|
33
|
+
TurnOffShake: "Turn off shake",
|
|
34
|
+
TurnOnShake: "Turn on shake",
|
|
35
|
+
DisableShakeConfirm: "Are you sure you want to disable the Shake feature? It will be automatically enabled again after 7 days.",
|
|
36
|
+
Disable: "Disable",
|
|
37
|
+
Feedback: "Feedback",
|
|
38
|
+
Send: "Send",
|
|
39
|
+
AttachedMedia: "ATTACHED MEDIA",
|
|
40
|
+
Screenshot: "Screenshot",
|
|
41
|
+
PinchToZoom: "Pinch to zoom",
|
|
42
|
+
AnnotateYourBug: "Annotate your Bug",
|
|
25
43
|
},
|
|
26
44
|
"HINDI": {
|
|
27
45
|
InterNetConnetionError: "कृपया अपनी इंटरनेट कनेक्शन की जांच करें",
|
|
@@ -479,4 +497,15 @@ export const Localization = {
|
|
|
479
497
|
ReplaceImage: "చిత్రం మార్చండి",
|
|
480
498
|
ReplaceVideo: "వీడియోను మార్చండి"
|
|
481
499
|
}
|
|
482
|
-
}
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
export const getLocalizedString = (key, lang = global.ShakebugLanguage) => {
|
|
503
|
+
const selectedLang = lang && Localization[lang] ? lang : "ENGLISH";
|
|
504
|
+
if (Localization[selectedLang] && Localization[selectedLang][key] !== undefined) {
|
|
505
|
+
return Localization[selectedLang][key];
|
|
506
|
+
}
|
|
507
|
+
if (Localization["ENGLISH"] && Localization["ENGLISH"][key] !== undefined) {
|
|
508
|
+
return Localization["ENGLISH"][key];
|
|
509
|
+
}
|
|
510
|
+
return key;
|
|
511
|
+
};
|