@eohjsc/react-native-smart-city 0.3.3 → 0.3.4
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/package.json +3 -1
- package/src/commons/ActionGroup/ColorPickerTemplate.js +36 -23
- package/src/commons/ActionGroup/OnOffTemplate/OnOffButtonTemplate.js +8 -2
- package/src/commons/ActionGroup/SliderRangeTemplate.js +5 -1
- package/src/commons/ConnectingProcess/index.js +3 -0
- package/src/commons/Device/ItemDevice.js +4 -1
- package/src/commons/Device/WaterQualitySensor/QualityIndicatorsItem.js +7 -2
- package/src/commons/Form/CurrencyInput.js +15 -1
- package/src/commons/Form/TextInputPassword.js +1 -1
- package/src/commons/HeaderAni/index.js +6 -1
- package/src/commons/MediaPlayerDetail/index.js +11 -2
- package/src/commons/Sharing/MemberList.js +10 -2
- package/src/commons/Sharing/WrapHeaderScrollable.js +2 -0
- package/src/commons/SubUnit/__test__/ShortDetail.test.js +1 -1
- package/src/configs/Constants.js +19 -0
- package/src/configs/SCConfig.js +2 -0
- package/src/navigations/UnitStack.js +3 -20
- package/src/navigations/UnitStackStyles.js +21 -0
- package/src/screens/AddNewDevice/__test__/AddNewDevice.test.js +8 -1
- package/src/screens/AddNewDevice/__test__/ConnectingDevices.test.js +1 -1
- package/src/screens/Device/EditDevice/index.js +15 -13
- package/src/screens/SubUnit/AddSubUnit.js +23 -17
- package/src/screens/SubUnit/EditSubUnit.js +15 -13
- package/src/screens/SubUnit/__test__/AddSubUnit.test.js +9 -23
- package/src/screens/SyncLGDevice/__test__/AddLGDevice.test.js +8 -1
- package/src/screens/Unit/SelectAddress.js +7 -1
- package/src/screens/Unit/Station/index.js +3 -0
- package/src/screens/Unit/__test__/SelectAddress.test.js +80 -3
- package/src/screens/Unit/components/MyUnitDevice/index.js +4 -4
- package/src/screens/Unit/components/__test__/MyUnitDevice.test.js +2 -2
- package/src/utils/Apis/axios.js +9 -1
- package/src/utils/I18n/translations/en.json +3 -1
- package/src/utils/I18n/translations/vi.json +3 -1
- package/src/utils/Setting/Location.js +30 -0
- package/src/utils/__test__/Utils.test.js +12 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eohjsc/react-native-smart-city",
|
|
3
3
|
"title": "React Native Smart Home",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.04",
|
|
5
5
|
"description": "TODO",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"files": [
|
|
@@ -146,6 +146,8 @@
|
|
|
146
146
|
"react-hooks-global-state": "^1.0.1",
|
|
147
147
|
"react-i18next": "^11.8.12",
|
|
148
148
|
"react-native-alert-async": "^1.0.5",
|
|
149
|
+
"react-native-android-keyboard-adjust": "^1.2.0",
|
|
150
|
+
"react-native-android-location-enabler": "^1.2.2",
|
|
149
151
|
"react-native-android-wifi": "^0.0.41",
|
|
150
152
|
"react-native-appearance": "^0.3.4",
|
|
151
153
|
"react-native-base64": "^0.1.0",
|
|
@@ -1,38 +1,51 @@
|
|
|
1
|
-
import React, { memo,
|
|
1
|
+
import React, { memo, useState, useEffect } from 'react';
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
import styles from './ColorPickerTemplateStyles';
|
|
4
4
|
import ColorPicker from 'react-native-wheel-color-picker';
|
|
5
5
|
import { watchMultiConfigs } from '../../iot/Monitor';
|
|
6
6
|
import { useConfigGlobalState } from '../../iot/states';
|
|
7
7
|
|
|
8
|
+
let isFirstTime = true;
|
|
9
|
+
|
|
10
|
+
const WheelColorPicker = ({ valueColor, onChangeColor }) => {
|
|
11
|
+
return (
|
|
12
|
+
<ColorPicker
|
|
13
|
+
style={styles.colorPicker}
|
|
14
|
+
sliderHidden={true}
|
|
15
|
+
swatches={false}
|
|
16
|
+
color={valueColor}
|
|
17
|
+
onColorChangeComplete={onChangeColor}
|
|
18
|
+
thumbSize={16}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
8
23
|
const ColorPickerTemplate = memo(({ actionGroup, doAction, sensor }) => {
|
|
9
24
|
const { configuration } = actionGroup;
|
|
10
25
|
const [valueColorComplete, setValueColorComplete] = useState('');
|
|
11
|
-
const [isFirstColor, setIsFirstColor] = useState(false);
|
|
12
26
|
const [configValues] = useConfigGlobalState('configValues');
|
|
13
27
|
|
|
14
|
-
const onChangeColor =
|
|
15
|
-
(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
const onChangeColor = (color) => {
|
|
29
|
+
const to = setTimeout(() => {
|
|
30
|
+
isFirstTime = false;
|
|
31
|
+
clearTimeout(to);
|
|
32
|
+
}, 1000);
|
|
33
|
+
|
|
34
|
+
!isFirstTime &&
|
|
20
35
|
doAction(
|
|
21
36
|
configuration?.action_color_data,
|
|
22
37
|
JSON.stringify({ value: color })
|
|
23
38
|
);
|
|
24
|
-
|
|
25
|
-
[configuration?.action_color_data, doAction, isFirstColor]
|
|
26
|
-
);
|
|
27
|
-
const valueColor = useMemo(() => {
|
|
28
|
-
return valueColorComplete || '';
|
|
29
|
-
}, [valueColorComplete]);
|
|
39
|
+
};
|
|
30
40
|
|
|
31
41
|
useEffect(() => {
|
|
32
42
|
const { config } = configuration;
|
|
33
43
|
const configValue = configValues[config];
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
if (configValue && isFirstTime) {
|
|
45
|
+
setValueColorComplete(`#${configValue?.toString(16)}`);
|
|
46
|
+
}
|
|
47
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
48
|
+
}, [configValues]);
|
|
36
49
|
|
|
37
50
|
useEffect(() => {
|
|
38
51
|
if (sensor?.is_managed_by_backend && sensor.device_type !== 'GOOGLE_HOME') {
|
|
@@ -40,15 +53,15 @@ const ColorPickerTemplate = memo(({ actionGroup, doAction, sensor }) => {
|
|
|
40
53
|
}
|
|
41
54
|
}, [sensor, configuration.config]);
|
|
42
55
|
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
return () => (isFirstTime = true);
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
43
60
|
return (
|
|
44
61
|
<View style={styles.viewPickColor}>
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
swatches={false}
|
|
49
|
-
color={valueColor}
|
|
50
|
-
onColorChangeComplete={onChangeColor}
|
|
51
|
-
thumbSize={16}
|
|
62
|
+
<WheelColorPicker
|
|
63
|
+
valueColor={valueColorComplete}
|
|
64
|
+
onChangeColor={onChangeColor}
|
|
52
65
|
/>
|
|
53
66
|
</View>
|
|
54
67
|
);
|
|
@@ -5,15 +5,20 @@ import React, { memo } from 'react';
|
|
|
5
5
|
import { TouchableOpacity, View } from 'react-native';
|
|
6
6
|
import { Colors } from '../../../configs';
|
|
7
7
|
import styles from './OnOffButtonTemplateStyle';
|
|
8
|
+
import { TESTID } from '../../../configs/Constants';
|
|
8
9
|
|
|
9
10
|
const OnOffButtonTemplate = memo(
|
|
10
11
|
({ isOn, triggerAction, actionGroup, isLight = false }) => {
|
|
11
|
-
const { configuration } = actionGroup;
|
|
12
|
+
const { configuration, id } = actionGroup;
|
|
12
13
|
|
|
13
14
|
return (
|
|
14
15
|
<>
|
|
15
16
|
<View style={styles.barrierControlContainer}>
|
|
16
|
-
<TouchableOpacity
|
|
17
|
+
<TouchableOpacity
|
|
18
|
+
style={styles.bigCircle}
|
|
19
|
+
onPress={triggerAction}
|
|
20
|
+
testID={`${TESTID.ON_OFF_BUTTON}-${id}`}
|
|
21
|
+
>
|
|
17
22
|
<View style={styles.smallCircle}>
|
|
18
23
|
<Icon
|
|
19
24
|
name={isOn ? configuration.icon_on : configuration.icon_off}
|
|
@@ -25,6 +30,7 @@ const OnOffButtonTemplate = memo(
|
|
|
25
30
|
styles.textBig,
|
|
26
31
|
{ color: isOn ? Colors.Gray8 : Colors.Gray6 },
|
|
27
32
|
]}
|
|
33
|
+
testID={`${TESTID.SENSOR_STATUS}-${id}`}
|
|
28
34
|
>
|
|
29
35
|
{isOn ? configuration.text_on : configuration.text_off}
|
|
30
36
|
</Text>
|
|
@@ -33,7 +33,11 @@ const SliderRangeTemplate = memo(({ actionGroup, doAction, sensor }) => {
|
|
|
33
33
|
useEffect(() => {
|
|
34
34
|
const { config } = configuration;
|
|
35
35
|
const configValue = configValues[config];
|
|
36
|
-
|
|
36
|
+
let valueTemp = configValue;
|
|
37
|
+
if (configValue > 0) {
|
|
38
|
+
valueTemp = Math.round((configValue / 254) * 100);
|
|
39
|
+
}
|
|
40
|
+
setValueBrightness(valueTemp);
|
|
37
41
|
}, [configuration.config, configValues, configuration]);
|
|
38
42
|
|
|
39
43
|
useEffect(() => {
|
|
@@ -45,6 +45,7 @@ const ConnectingProcess = ({ route }) => {
|
|
|
45
45
|
);
|
|
46
46
|
if (success) {
|
|
47
47
|
setSensor(data);
|
|
48
|
+
setNewName(data?.name);
|
|
48
49
|
} else {
|
|
49
50
|
ToastBottomHelper.error(JSON.stringify(data));
|
|
50
51
|
goBack();
|
|
@@ -64,6 +65,7 @@ const ConnectingProcess = ({ route }) => {
|
|
|
64
65
|
});
|
|
65
66
|
if (success) {
|
|
66
67
|
setSensor(data);
|
|
68
|
+
setNewName(data?.name);
|
|
67
69
|
} else {
|
|
68
70
|
ToastBottomHelper.error(JSON.stringify(data));
|
|
69
71
|
goBack();
|
|
@@ -80,6 +82,7 @@ const ConnectingProcess = ({ route }) => {
|
|
|
80
82
|
);
|
|
81
83
|
if (success) {
|
|
82
84
|
setSensor({ name: gateway?.model });
|
|
85
|
+
setNewName(gateway?.model);
|
|
83
86
|
} else {
|
|
84
87
|
ToastBottomHelper.error(JSON.stringify(data));
|
|
85
88
|
goBack();
|
|
@@ -72,7 +72,10 @@ const ItemDevice = memo(
|
|
|
72
72
|
const textConnected = isConnected ? t('connected') : t('disconnected');
|
|
73
73
|
|
|
74
74
|
return (
|
|
75
|
-
<TouchableWithoutFeedback
|
|
75
|
+
<TouchableWithoutFeedback
|
|
76
|
+
onPress={goToSensorDisplay}
|
|
77
|
+
testID={`${TESTID.SENSOR_NAME}-${sensor?.id}`}
|
|
78
|
+
>
|
|
76
79
|
<View
|
|
77
80
|
style={[styles.container, wrapStyle, { borderColor }]}
|
|
78
81
|
testID={TESTID.SUB_UNIT_DEVICES}
|
|
@@ -37,7 +37,7 @@ const QualityIndicatorItem = memo(
|
|
|
37
37
|
>
|
|
38
38
|
<IconOutline
|
|
39
39
|
name={'info-circle'}
|
|
40
|
-
size={
|
|
40
|
+
size={18}
|
|
41
41
|
color={Colors.Gray8}
|
|
42
42
|
/>
|
|
43
43
|
</TouchableOpacity>
|
|
@@ -92,6 +92,11 @@ const styles = StyleSheet.create({
|
|
|
92
92
|
marginTop: 8,
|
|
93
93
|
},
|
|
94
94
|
iconInfo: {
|
|
95
|
-
|
|
95
|
+
width: 40,
|
|
96
|
+
height: 40,
|
|
97
|
+
justifyContent: 'center',
|
|
98
|
+
alignItems: 'center',
|
|
99
|
+
marginRight: -10,
|
|
100
|
+
marginTop: -2,
|
|
96
101
|
},
|
|
97
102
|
});
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useState,
|
|
3
|
+
useRef,
|
|
4
|
+
useCallback,
|
|
5
|
+
useMemo,
|
|
6
|
+
useEffect,
|
|
7
|
+
} from 'react';
|
|
2
8
|
import {
|
|
3
9
|
View,
|
|
4
10
|
TextInput,
|
|
@@ -6,6 +12,7 @@ import {
|
|
|
6
12
|
TouchableWithoutFeedback,
|
|
7
13
|
Platform,
|
|
8
14
|
} from 'react-native';
|
|
15
|
+
import AndroidKeyboardAdjust from 'react-native-android-keyboard-adjust';
|
|
9
16
|
import Text from '../Text';
|
|
10
17
|
import { Colors } from '../../configs';
|
|
11
18
|
|
|
@@ -125,6 +132,13 @@ const CurrencyInput = ({
|
|
|
125
132
|
return textInputValue.toString().length < 2 ? -12 : 0;
|
|
126
133
|
}, [textInputValue]);
|
|
127
134
|
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
const isAndroid = Platform.OS === 'android';
|
|
137
|
+
isAndroid && AndroidKeyboardAdjust.setAdjustResize();
|
|
138
|
+
|
|
139
|
+
return () => isAndroid && AndroidKeyboardAdjust.setAdjustPan();
|
|
140
|
+
}, []);
|
|
141
|
+
|
|
128
142
|
return (
|
|
129
143
|
<TouchableWithoutFeedback onPress={focusInput}>
|
|
130
144
|
<View style={styles.wrap}>
|
|
@@ -6,6 +6,7 @@ import { getStatusBarHeight } from 'react-native-iphone-x-helper';
|
|
|
6
6
|
|
|
7
7
|
import Text from '../../commons/Text';
|
|
8
8
|
import { Colors, Constants } from '../../configs';
|
|
9
|
+
import { TESTID } from '../../configs/Constants';
|
|
9
10
|
|
|
10
11
|
const screenHeight = Constants.height;
|
|
11
12
|
const default_height = 44;
|
|
@@ -89,7 +90,11 @@ const HeaderAni = memo(
|
|
|
89
90
|
},
|
|
90
91
|
]}
|
|
91
92
|
>
|
|
92
|
-
<TouchableOpacity
|
|
93
|
+
<TouchableOpacity
|
|
94
|
+
style={styles.btnBack}
|
|
95
|
+
onPress={onPressLeft}
|
|
96
|
+
testID={TESTID.ICON_BACK}
|
|
97
|
+
>
|
|
93
98
|
<Icon name={'left'} size={27} color={Colors.Gray9} />
|
|
94
99
|
</TouchableOpacity>
|
|
95
100
|
<View styles={styles.wrapRightComponent}>{rightComponent}</View>
|
|
@@ -6,7 +6,14 @@ import React, {
|
|
|
6
6
|
useContext,
|
|
7
7
|
useMemo,
|
|
8
8
|
} from 'react';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
Image,
|
|
11
|
+
View,
|
|
12
|
+
StyleSheet,
|
|
13
|
+
Text,
|
|
14
|
+
TouchableOpacity,
|
|
15
|
+
Platform,
|
|
16
|
+
} from 'react-native';
|
|
10
17
|
import { VLCPlayer } from 'react-native-vlc-media-player';
|
|
11
18
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
12
19
|
|
|
@@ -68,7 +75,9 @@ const MediaPlayerDetail = ({
|
|
|
68
75
|
newHeight = 224;
|
|
69
76
|
break;
|
|
70
77
|
case 4:
|
|
71
|
-
newWidth =
|
|
78
|
+
newWidth =
|
|
79
|
+
Constants.width /
|
|
80
|
+
(Platform.OS === 'ios' && Constants.width < 400 ? 1.5 : 2);
|
|
72
81
|
newHeight = 112;
|
|
73
82
|
break;
|
|
74
83
|
case 6:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { memo } from 'react';
|
|
2
2
|
import { View, StyleSheet } from 'react-native';
|
|
3
3
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
4
|
-
import { Colors } from '../../configs';
|
|
4
|
+
import { Colors, Constants } from '../../configs';
|
|
5
5
|
import Text from '../../commons/Text';
|
|
6
6
|
import RowMember from './RowMember';
|
|
7
7
|
|
|
@@ -26,7 +26,9 @@ const MemberList = ({
|
|
|
26
26
|
/>
|
|
27
27
|
))}
|
|
28
28
|
{!dataMember.length && (
|
|
29
|
-
<
|
|
29
|
+
<View style={styles.viewEmpty}>
|
|
30
|
+
<Text style={styles.textCenter}>{t('no_member')}</Text>
|
|
31
|
+
</View>
|
|
30
32
|
)}
|
|
31
33
|
</View>
|
|
32
34
|
);
|
|
@@ -37,4 +39,10 @@ const styles = StyleSheet.create({
|
|
|
37
39
|
box: {
|
|
38
40
|
backgroundColor: Colors.White,
|
|
39
41
|
},
|
|
42
|
+
viewEmpty: {
|
|
43
|
+
justifyContent: 'center',
|
|
44
|
+
alignItems: 'center',
|
|
45
|
+
height: Constants.height - 200,
|
|
46
|
+
backgroundColor: Colors.White,
|
|
47
|
+
},
|
|
40
48
|
});
|
|
@@ -13,6 +13,7 @@ import { isIphoneX } from 'react-native-iphone-x-helper';
|
|
|
13
13
|
import { Colors, Theme } from '../../configs';
|
|
14
14
|
import HeaderAni, { heightHeader } from '../../commons/HeaderAni';
|
|
15
15
|
import Text from '../../commons/Text';
|
|
16
|
+
import { TESTID } from '../../configs/Constants';
|
|
16
17
|
|
|
17
18
|
const WrapHeaderScrollable = ({
|
|
18
19
|
children,
|
|
@@ -59,6 +60,7 @@ const WrapHeaderScrollable = ({
|
|
|
59
60
|
onLeft={onGoBack}
|
|
60
61
|
/>
|
|
61
62
|
<Animated.ScrollView
|
|
63
|
+
testID={TESTID.ANIMATED_SCROLL}
|
|
62
64
|
scrollEventThrottle={16}
|
|
63
65
|
onScroll={Animated.event(
|
|
64
66
|
[{ nativeEvent: { contentOffset: { y: animatedScrollYValue } } }],
|
|
@@ -151,7 +151,7 @@ describe('test ShortDetail Subunit', () => {
|
|
|
151
151
|
(item) =>
|
|
152
152
|
item.props.testID === TESTID.SUB_UNIT_DEVICES && item.type === View
|
|
153
153
|
);
|
|
154
|
-
expect(itemDevice.length).toBe(
|
|
154
|
+
expect(itemDevice.length).toBe(0);
|
|
155
155
|
});
|
|
156
156
|
|
|
157
157
|
test('render ShortDetail add new device', async () => {
|
package/src/configs/Constants.js
CHANGED
|
@@ -288,6 +288,8 @@ export const TESTID = {
|
|
|
288
288
|
SUB_UNIT_TEXT_DROPDOWN: 'SUB_UNIT_TEXT_DROPDOWN',
|
|
289
289
|
SUB_UNIT_GO_DETAIL: 'SUB_UNIT_GO_DETAIL',
|
|
290
290
|
VIEW_SUB_UNIT_AUTOMATE: 'VIEW_SUB_UNIT_AUTOMATE',
|
|
291
|
+
SUB_UNIT_NAME: 'SUB_UNIT_NAME',
|
|
292
|
+
ANIMATED_SCROLL: 'ANIMATED_SCROLL',
|
|
291
293
|
ADD_SUB_UNIT: 'ADD_SUB_UNIT',
|
|
292
294
|
|
|
293
295
|
// NavBar
|
|
@@ -392,6 +394,7 @@ export const TESTID = {
|
|
|
392
394
|
ITEM_TEXT_ERROR: 'ITEM_TEXT_ERROR',
|
|
393
395
|
SEARCH_BAR_INPUT: 'SEARCH_BAR_INPUT',
|
|
394
396
|
GO_DETAIL: 'GO_DETAIL',
|
|
397
|
+
NAV_LIST: 'NAV_LIST',
|
|
395
398
|
|
|
396
399
|
// Automate
|
|
397
400
|
AUTOMATE_SCRIPT_ACTION: 'AUTOMATE_SCRIPT_ACTION',
|
|
@@ -447,8 +450,13 @@ export const TESTID = {
|
|
|
447
450
|
ITEM_QUICK_ACTION_PRESS: 'ITEM_QUICK_ACTION_PRESS',
|
|
448
451
|
TIME_COUNT_DOWN_TEXT: 'TIME_COUNT_DOWN_TEXT',
|
|
449
452
|
ACTION_ITEM: 'ACTION_ITEM',
|
|
453
|
+
|
|
450
454
|
// Sensor Item
|
|
451
455
|
TEXT_SENSOR_ITEM: 'TEXT_SENSOR_ITEM',
|
|
456
|
+
SENSOR_NAME: 'SENSOR_NAME',
|
|
457
|
+
ON_OFF_BUTTON: 'ON_OFF_BUTTON',
|
|
458
|
+
SENSOR_STATUS: 'SENSOR_STATUS',
|
|
459
|
+
ICON_BACK: 'ICON_BACK',
|
|
452
460
|
|
|
453
461
|
// DeviceInfo
|
|
454
462
|
DEVICE_INFO_BATTERY: 'DEVICE_INFO_BATTERY',
|
|
@@ -564,6 +572,7 @@ export const TESTID = {
|
|
|
564
572
|
ICON_ADD_STAR_SHARED_UNIT: 'ICON_ADD_STAR_SHARED_UNIT',
|
|
565
573
|
TOUCH_SHARED_UNIT: 'TOUCH_SHARED_UNIT',
|
|
566
574
|
SHARED_UNIT_OWN_BY: 'SHARED_UNIT_OWN_BY',
|
|
575
|
+
ICON_UNIT: 'ICON_UNIT',
|
|
567
576
|
|
|
568
577
|
// Select subunit
|
|
569
578
|
|
|
@@ -809,3 +818,13 @@ export const SENSOR_TYPE = {
|
|
|
809
818
|
SOS: 'sos',
|
|
810
819
|
FILTER_WATER: 'filter_water',
|
|
811
820
|
};
|
|
821
|
+
|
|
822
|
+
export const PROBLEM_CODE = {
|
|
823
|
+
CLIENT_ERROR: 'CLIENT_ERROR',
|
|
824
|
+
SERVER_ERROR: 'SERVER_ERROR',
|
|
825
|
+
TIMEOUT_ERROR: 'TIMEOUT_ERROR',
|
|
826
|
+
CONNECTION_ERROR: 'CONNECTION_ERROR',
|
|
827
|
+
NETWORK_ERROR: 'NETWORK_ERROR',
|
|
828
|
+
UNKNOWN_ERROR: 'UNKNOWN_ERROR',
|
|
829
|
+
CANCEL_ERROR: 'CANCEL_ERROR',
|
|
830
|
+
};
|
package/src/configs/SCConfig.js
CHANGED
|
@@ -107,11 +107,13 @@ export class SCConfig {
|
|
|
107
107
|
static VCONNEX_REDIRECT_URI_APP = SCDefaultConfig.VCONNEX_REDIRECT_URI_APP;
|
|
108
108
|
static pusherAppKey = SCDefaultConfig.pusherAppKey;
|
|
109
109
|
static pusherAppCluste = SCDefaultConfig.pusherAppCluster;
|
|
110
|
+
static language = 'en';
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
export const initSCConfig = (config) => {
|
|
113
114
|
api.setBaseURL(config.apiRoot ?? SCDefaultConfig.apiRoot);
|
|
114
115
|
LocaleConfig.defaultLocale = config.language;
|
|
116
|
+
SCConfig.language = config.language ?? SCConfig.language;
|
|
115
117
|
SCConfig.apiRoot = config.apiRoot ?? SCDefaultConfig.apiRoot;
|
|
116
118
|
SCConfig.GOOGLE_MAP_API_KEY =
|
|
117
119
|
config.GOOGLE_MAP_API_KEY ?? SCDefaultConfig.GOOGLE_MAP_API_KEY;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { memo, useContext, useEffect } from 'react';
|
|
2
|
-
import { View
|
|
2
|
+
import { View } from 'react-native';
|
|
3
3
|
import { IconOutline } from '@ant-design/icons-react-native';
|
|
4
4
|
import { createStackNavigator } from '@react-navigation/stack';
|
|
5
5
|
import { BleManager } from 'react-native-ble-plx';
|
|
@@ -52,10 +52,10 @@ import ConfirmUnitDeletion from '../screens/ConfirmUnitDeletion';
|
|
|
52
52
|
import InfoMemberUnit from '../screens/Sharing/InfoMemberUnit';
|
|
53
53
|
import EnterPassword from '../screens/EnterPassword';
|
|
54
54
|
import { HanetCameraStack } from './HanetCameraStack';
|
|
55
|
-
|
|
56
55
|
import { axiosGet } from '../utils/Apis/axios';
|
|
57
56
|
import { API } from '../configs';
|
|
58
57
|
import SideMenuDetail from '../screens/SideMenuDetail';
|
|
58
|
+
import { styles } from './UnitStackStyles';
|
|
59
59
|
|
|
60
60
|
const Stack = createStackNavigator();
|
|
61
61
|
|
|
@@ -173,12 +173,9 @@ export const UnitStack = memo((props) => {
|
|
|
173
173
|
component={ChooseLocation}
|
|
174
174
|
options={{
|
|
175
175
|
headerShown: true,
|
|
176
|
-
headerTitleStyle: {
|
|
177
|
-
...styles.headerLocation,
|
|
178
|
-
},
|
|
179
176
|
headerTitle: () => (
|
|
180
177
|
<View style={styles.headerLocation}>
|
|
181
|
-
<Text
|
|
178
|
+
<Text color={Colors.Gray9} style={styles.headerTitle} bold>
|
|
182
179
|
{t('choose_on_map')}
|
|
183
180
|
</Text>
|
|
184
181
|
|
|
@@ -406,17 +403,3 @@ export const UnitStack = memo((props) => {
|
|
|
406
403
|
</Stack.Navigator>
|
|
407
404
|
);
|
|
408
405
|
});
|
|
409
|
-
|
|
410
|
-
const styles = StyleSheet.create({
|
|
411
|
-
icLeft: {
|
|
412
|
-
marginLeft: Device.isIOS ? 8 : 0,
|
|
413
|
-
},
|
|
414
|
-
headerLocation: {
|
|
415
|
-
alignItems: 'center',
|
|
416
|
-
justifyContent: 'center',
|
|
417
|
-
},
|
|
418
|
-
headerContent: {
|
|
419
|
-
textAlign: 'center',
|
|
420
|
-
paddingBottom: 8,
|
|
421
|
-
},
|
|
422
|
-
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { Device } from '../configs';
|
|
3
|
+
|
|
4
|
+
export const styles = StyleSheet.create({
|
|
5
|
+
icLeft: {
|
|
6
|
+
marginLeft: Device.isIOS ? 8 : 0,
|
|
7
|
+
},
|
|
8
|
+
headerLocation: {
|
|
9
|
+
alignItems: 'center',
|
|
10
|
+
justifyContent: 'center',
|
|
11
|
+
width: 290,
|
|
12
|
+
},
|
|
13
|
+
headerTitle: {
|
|
14
|
+
fontSize: 20,
|
|
15
|
+
lineHeight: 28,
|
|
16
|
+
},
|
|
17
|
+
headerContent: {
|
|
18
|
+
textAlign: 'center',
|
|
19
|
+
paddingBottom: 4,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
@@ -53,6 +53,7 @@ describe('Test AddNewDevice', () => {
|
|
|
53
53
|
unit_id: 1,
|
|
54
54
|
},
|
|
55
55
|
};
|
|
56
|
+
mock.resetHistory();
|
|
56
57
|
});
|
|
57
58
|
|
|
58
59
|
const getText = (instance, id) => {
|
|
@@ -103,7 +104,13 @@ describe('Test AddNewDevice', () => {
|
|
|
103
104
|
});
|
|
104
105
|
const instance = tree.root;
|
|
105
106
|
const groupCheckBox = instance.findByType(GroupCheckBox);
|
|
106
|
-
expect(groupCheckBox.props.data).toEqual([
|
|
107
|
+
expect(groupCheckBox.props.data).toEqual([
|
|
108
|
+
{
|
|
109
|
+
id: 2,
|
|
110
|
+
name: 'Station name',
|
|
111
|
+
title: 'Station name',
|
|
112
|
+
},
|
|
113
|
+
]);
|
|
107
114
|
});
|
|
108
115
|
|
|
109
116
|
test('ViewButtonBottom', async () => {
|
|
@@ -103,7 +103,7 @@ describe('Test ConnectingDevices', () => {
|
|
|
103
103
|
await jest.runOnlyPendingTimers();
|
|
104
104
|
});
|
|
105
105
|
expect(setInterval).toHaveBeenCalled();
|
|
106
|
-
expect(mockedNavigate).
|
|
106
|
+
expect(mockedNavigate).toHaveBeenCalledWith(Routes.ConnectDevices, {
|
|
107
107
|
new_sensor: { id: 1 },
|
|
108
108
|
});
|
|
109
109
|
});
|
|
@@ -104,20 +104,22 @@ const EditDevice = memo(() => {
|
|
|
104
104
|
/>
|
|
105
105
|
</View>
|
|
106
106
|
</TouchableOpacity>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<Text
|
|
113
|
-
type={'H4'}
|
|
114
|
-
semibold
|
|
115
|
-
color={Colors.Red}
|
|
116
|
-
style={styles.removeBorderBottom}
|
|
107
|
+
{!stateAlertAction.visible && (
|
|
108
|
+
<TouchableOpacity
|
|
109
|
+
style={styles.removeButton}
|
|
110
|
+
onPress={onShowDelete(sensor?.name)}
|
|
111
|
+
testID={TESTID.DEVICE_SHOW_REMOVE}
|
|
117
112
|
>
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
<Text
|
|
114
|
+
type={'H4'}
|
|
115
|
+
semibold
|
|
116
|
+
color={Colors.Red}
|
|
117
|
+
style={styles.removeBorderBottom}
|
|
118
|
+
>
|
|
119
|
+
{t('remove_device')}
|
|
120
|
+
</Text>
|
|
121
|
+
</TouchableOpacity>
|
|
122
|
+
)}
|
|
121
123
|
</View>
|
|
122
124
|
<AlertAction
|
|
123
125
|
visible={stateAlertAction.visible}
|
|
@@ -25,11 +25,12 @@ import { ToastBottomHelper } from '../../utils/Utils';
|
|
|
25
25
|
import { TESTID } from '../../configs/Constants';
|
|
26
26
|
import styles from './AddSubUnitStyles';
|
|
27
27
|
import useKeyboardShow from '../../hooks/Common/useKeyboardShow';
|
|
28
|
+
import { replace } from '../../navigations/utils';
|
|
28
29
|
|
|
29
30
|
const AddSubUnit = ({ route }) => {
|
|
30
31
|
const t = useTranslations();
|
|
31
32
|
const { dismissKeyboard } = useKeyboardShow();
|
|
32
|
-
const { navigate, goBack } = useNavigation();
|
|
33
|
+
const { navigate, goBack, dispatch } = useNavigation();
|
|
33
34
|
const { unit, addType, isAddUnit, location = '' } = route?.params;
|
|
34
35
|
const [roomName, setRoomName] = useState('');
|
|
35
36
|
const [wallpaper, setWallpaper] = useState('');
|
|
@@ -62,13 +63,15 @@ const AddSubUnit = ({ route }) => {
|
|
|
62
63
|
}
|
|
63
64
|
);
|
|
64
65
|
if (success) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
dispatch(
|
|
67
|
+
replace(Routes.UnitStack, {
|
|
68
|
+
screen: Routes.UnitDetail,
|
|
69
|
+
params: {
|
|
70
|
+
unitId: data?.id,
|
|
71
|
+
routeName: Routes.DashboardStack,
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
);
|
|
72
75
|
cleanData();
|
|
73
76
|
} else {
|
|
74
77
|
awaitCreate.current = false;
|
|
@@ -98,15 +101,17 @@ const AddSubUnit = ({ route }) => {
|
|
|
98
101
|
});
|
|
99
102
|
return;
|
|
100
103
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
104
|
+
dispatch(
|
|
105
|
+
replace(Routes.UnitStack, {
|
|
106
|
+
screen: Routes.UnitDetail,
|
|
107
|
+
params: {
|
|
108
|
+
unitId: unit.id,
|
|
109
|
+
unitData: unit,
|
|
110
|
+
isAddSubUnit: true,
|
|
111
|
+
routeName: Routes.DashboardStack,
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
);
|
|
110
115
|
cleanData();
|
|
111
116
|
} else {
|
|
112
117
|
awaitCreate.current = false;
|
|
@@ -123,6 +128,7 @@ const AddSubUnit = ({ route }) => {
|
|
|
123
128
|
t,
|
|
124
129
|
unit,
|
|
125
130
|
addType,
|
|
131
|
+
dispatch,
|
|
126
132
|
goBack,
|
|
127
133
|
route.params,
|
|
128
134
|
]);
|
|
@@ -228,20 +228,22 @@ const EditSubUnit = ({ route }) => {
|
|
|
228
228
|
setImageUrl={setImageUrl}
|
|
229
229
|
optionsCapture={options}
|
|
230
230
|
/>
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
<Text
|
|
237
|
-
type={'H4'}
|
|
238
|
-
semibold
|
|
239
|
-
color={Colors.Red}
|
|
240
|
-
style={styles.removeText}
|
|
231
|
+
{!showEdit && (
|
|
232
|
+
<TouchableOpacity
|
|
233
|
+
testID={TESTID.MANAGE_SUB_UNIT_REMOVE_BUTTON}
|
|
234
|
+
onPress={onPressRemove}
|
|
235
|
+
style={styles.removeButton}
|
|
241
236
|
>
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
237
|
+
<Text
|
|
238
|
+
type={'H4'}
|
|
239
|
+
semibold
|
|
240
|
+
color={Colors.Red}
|
|
241
|
+
style={styles.removeText}
|
|
242
|
+
>
|
|
243
|
+
{t('remove_sub_unit')}
|
|
244
|
+
</Text>
|
|
245
|
+
</TouchableOpacity>
|
|
246
|
+
)}
|
|
245
247
|
</View>
|
|
246
248
|
</View>
|
|
247
249
|
<ModalCustom
|
|
@@ -36,13 +36,20 @@ jest.mock('react-redux', () => {
|
|
|
36
36
|
};
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
const mockNavigationDispatch = jest.fn();
|
|
40
|
+
const mockReplace = jest.fn();
|
|
41
|
+
|
|
39
42
|
jest.mock('@react-navigation/native', () => {
|
|
40
43
|
return {
|
|
41
44
|
...jest.requireActual('@react-navigation/native'),
|
|
42
45
|
useNavigation: () => ({
|
|
43
46
|
navigate: mockedNavigate,
|
|
44
47
|
goBack: mockedGoBack,
|
|
48
|
+
dispatch: mockNavigationDispatch,
|
|
45
49
|
}),
|
|
50
|
+
StackActions: {
|
|
51
|
+
replace: () => mockReplace,
|
|
52
|
+
},
|
|
46
53
|
};
|
|
47
54
|
});
|
|
48
55
|
|
|
@@ -63,6 +70,7 @@ describe('Test AddSubUnit', () => {
|
|
|
63
70
|
afterEach(() => {
|
|
64
71
|
mockedDispatch.mockClear();
|
|
65
72
|
mockedNavigate.mockClear();
|
|
73
|
+
mockNavigationDispatch.mockClear();
|
|
66
74
|
Toast.show.mockClear();
|
|
67
75
|
});
|
|
68
76
|
let tree;
|
|
@@ -150,15 +158,6 @@ describe('Test AddSubUnit', () => {
|
|
|
150
158
|
await act(async () => {
|
|
151
159
|
await viewButtonBottom.props.onRightClick();
|
|
152
160
|
});
|
|
153
|
-
expect(mockedNavigate).not.toHaveBeenCalledWith(Routes.UnitStack, {
|
|
154
|
-
screen: Routes.UnitDetail,
|
|
155
|
-
params: {
|
|
156
|
-
unitId: route.params.unit.id,
|
|
157
|
-
unitData: route.params.unit,
|
|
158
|
-
routeName: 'DashboardStack',
|
|
159
|
-
isAddSubUnit: true,
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
161
|
expect(Toast.show).toHaveBeenCalledWith({
|
|
163
162
|
type: 'error',
|
|
164
163
|
position: 'bottom',
|
|
@@ -186,13 +185,6 @@ describe('Test AddSubUnit', () => {
|
|
|
186
185
|
});
|
|
187
186
|
|
|
188
187
|
test('test create Unit', async () => {
|
|
189
|
-
const response = {
|
|
190
|
-
success: true,
|
|
191
|
-
status: 200,
|
|
192
|
-
data: {
|
|
193
|
-
id: 2,
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
188
|
route.params = {
|
|
197
189
|
...route.params,
|
|
198
190
|
location: 'Unit address',
|
|
@@ -211,13 +203,7 @@ describe('Test AddSubUnit', () => {
|
|
|
211
203
|
await act(async () => {
|
|
212
204
|
await viewButtonBottom.props.onRightClick();
|
|
213
205
|
});
|
|
214
|
-
expect(
|
|
215
|
-
screen: Routes.UnitDetail,
|
|
216
|
-
params: {
|
|
217
|
-
unitId: response.data.id,
|
|
218
|
-
routeName: Routes.DashboardStack,
|
|
219
|
-
},
|
|
220
|
-
});
|
|
206
|
+
expect(mockNavigationDispatch).toBeCalledWith(mockReplace);
|
|
221
207
|
});
|
|
222
208
|
|
|
223
209
|
test('test choose Location', async () => {
|
|
@@ -53,6 +53,7 @@ describe('Test Add LG Device', () => {
|
|
|
53
53
|
backend_url: 'https://doamin.com',
|
|
54
54
|
},
|
|
55
55
|
};
|
|
56
|
+
mock.resetHistory();
|
|
56
57
|
});
|
|
57
58
|
|
|
58
59
|
const getText = (instance, id) => {
|
|
@@ -105,7 +106,13 @@ describe('Test Add LG Device', () => {
|
|
|
105
106
|
});
|
|
106
107
|
const instance = tree.root;
|
|
107
108
|
const groupCheckBox = instance.findByType(GroupCheckBox);
|
|
108
|
-
expect(groupCheckBox.props.data).toEqual([
|
|
109
|
+
expect(groupCheckBox.props.data).toEqual([
|
|
110
|
+
{
|
|
111
|
+
id: 2,
|
|
112
|
+
name: 'Station name',
|
|
113
|
+
title: 'Station name',
|
|
114
|
+
},
|
|
115
|
+
]);
|
|
109
116
|
});
|
|
110
117
|
|
|
111
118
|
test('ViewButtonBottom', async () => {
|
|
@@ -4,6 +4,7 @@ import MapView, { Marker, Circle, PROVIDER_GOOGLE } from 'react-native-maps';
|
|
|
4
4
|
import { useNavigation } from '@react-navigation/native';
|
|
5
5
|
import { IconOutline, IconFill } from '@ant-design/icons-react-native';
|
|
6
6
|
import { check, RESULTS } from 'react-native-permissions';
|
|
7
|
+
import { openPromptEnableLocation } from '../../utils/Setting/Location';
|
|
7
8
|
|
|
8
9
|
import BottomButtonView from '../../commons/BottomButtonView';
|
|
9
10
|
import SearchBarLocation from '../../commons/SearchLocation';
|
|
@@ -151,9 +152,14 @@ const SelectAddress = memo(({ route }) => {
|
|
|
151
152
|
t('location_rationale_title'),
|
|
152
153
|
t('location_require_message')
|
|
153
154
|
);
|
|
155
|
+
} else if (error.code === GEOLOCATION_ERROR.POSITION_UNAVAILABLE) {
|
|
156
|
+
const locationEnabaled = await openPromptEnableLocation();
|
|
157
|
+
process.env.NODE_ENV !== 'test' &&
|
|
158
|
+
locationEnabaled &&
|
|
159
|
+
getCurrentPosition();
|
|
154
160
|
}
|
|
155
161
|
}
|
|
156
|
-
// { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 } enable on emulator
|
|
162
|
+
// { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 } // enable on emulator
|
|
157
163
|
);
|
|
158
164
|
}, [t]);
|
|
159
165
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { memo, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { View, Text, TouchableOpacity, FlatList } from 'react-native';
|
|
3
|
+
import { TESTID } from '../../../configs/Constants';
|
|
3
4
|
import styles from './StationStyles';
|
|
4
5
|
|
|
5
6
|
const Station = ({ listStation = [], onSnapToItem, indexStation }) => {
|
|
@@ -23,6 +24,7 @@ const Station = ({ listStation = [], onSnapToItem, indexStation }) => {
|
|
|
23
24
|
key={index}
|
|
24
25
|
style={styles.wrapTitle}
|
|
25
26
|
onPress={handleOnSnapToItem(item, index)}
|
|
27
|
+
testID={`${TESTID.SUB_UNIT_NAME}-${item?.station?.id}`}
|
|
26
28
|
>
|
|
27
29
|
<Text
|
|
28
30
|
style={[styles.title, index === indexStation && styles.titleActive]}
|
|
@@ -71,6 +73,7 @@ const Station = ({ listStation = [], onSnapToItem, indexStation }) => {
|
|
|
71
73
|
renderItem={renderItem}
|
|
72
74
|
showsHorizontalScrollIndicator={false}
|
|
73
75
|
scrollIndicatorInsets={{ right: 1 }}
|
|
76
|
+
testID={TESTID.NAV_LIST}
|
|
74
77
|
/>
|
|
75
78
|
</View>
|
|
76
79
|
);
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { Alert } from 'react-native';
|
|
2
3
|
import { act, create } from 'react-test-renderer';
|
|
3
4
|
import MockAdapter from 'axios-mock-adapter';
|
|
4
5
|
import RNP from 'react-native-permissions';
|
|
6
|
+
import RNAndroidLocationEnabler from 'react-native-android-location-enabler';
|
|
5
7
|
|
|
6
8
|
import { SCProvider } from '../../../context';
|
|
7
9
|
import { mockSCStore } from '../../../context/mockStore';
|
|
@@ -62,6 +64,13 @@ jest.mock('react-native-maps', () => {
|
|
|
62
64
|
};
|
|
63
65
|
});
|
|
64
66
|
|
|
67
|
+
jest.mock('react-native-android-location-enabler', () => {
|
|
68
|
+
return {
|
|
69
|
+
...jest.requireActual('react-native-android-location-enabler'),
|
|
70
|
+
promptForEnableLocationIfNeeded: jest.fn(),
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
65
74
|
jest.mock('../../../utils/Permission/common');
|
|
66
75
|
|
|
67
76
|
const position = {
|
|
@@ -82,11 +91,12 @@ jest.mock('react-native-permissions', () => {
|
|
|
82
91
|
});
|
|
83
92
|
|
|
84
93
|
describe('Test SelectAddress', () => {
|
|
85
|
-
let tree;
|
|
86
|
-
let route;
|
|
94
|
+
let tree, route, Platform;
|
|
87
95
|
const mockUpdateLocation = jest.fn();
|
|
88
96
|
|
|
89
97
|
beforeAll(() => {
|
|
98
|
+
Platform = require('react-native').Platform;
|
|
99
|
+
RNAndroidLocationEnabler.promptForEnableLocationIfNeeded.mockClear();
|
|
90
100
|
RNP.check.mockClear();
|
|
91
101
|
route = {
|
|
92
102
|
params: {
|
|
@@ -209,7 +219,74 @@ describe('Test SelectAddress', () => {
|
|
|
209
219
|
OpenSetting.mockClear();
|
|
210
220
|
});
|
|
211
221
|
|
|
212
|
-
test('test get current failed
|
|
222
|
+
test('test get current location failed location not enabled android', async () => {
|
|
223
|
+
Platform.OS = 'android';
|
|
224
|
+
await act(async () => {
|
|
225
|
+
tree = await create(wrapComponent(route));
|
|
226
|
+
});
|
|
227
|
+
const instance = tree.root;
|
|
228
|
+
const button = instance.find(
|
|
229
|
+
(el) => el.props.testID === TESTID.BUTTON_YOUR_LOCATION
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
global.navigator.geolocation = {
|
|
233
|
+
getCurrentPosition: (onSuccess, onError, options) => {
|
|
234
|
+
onError({ code: GEOLOCATION_ERROR.POSITION_UNAVAILABLE });
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// cancel
|
|
239
|
+
RNAndroidLocationEnabler.promptForEnableLocationIfNeeded.mockImplementationOnce(
|
|
240
|
+
async () => {
|
|
241
|
+
throw new Error();
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
await act(async () => {
|
|
245
|
+
await button.props.onPress();
|
|
246
|
+
});
|
|
247
|
+
expect(
|
|
248
|
+
RNAndroidLocationEnabler.promptForEnableLocationIfNeeded
|
|
249
|
+
).toBeCalled();
|
|
250
|
+
|
|
251
|
+
RNAndroidLocationEnabler.promptForEnableLocationIfNeeded.mockClear();
|
|
252
|
+
|
|
253
|
+
// enabled
|
|
254
|
+
RNAndroidLocationEnabler.promptForEnableLocationIfNeeded.mockImplementationOnce(
|
|
255
|
+
async () => true
|
|
256
|
+
);
|
|
257
|
+
await act(async () => {
|
|
258
|
+
await button.props.onPress();
|
|
259
|
+
});
|
|
260
|
+
expect(
|
|
261
|
+
RNAndroidLocationEnabler.promptForEnableLocationIfNeeded
|
|
262
|
+
).toBeCalled();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test('test get current location failed location not enabled ios', async () => {
|
|
266
|
+
Platform.OS = 'ios';
|
|
267
|
+
jest.spyOn(Alert, 'alert');
|
|
268
|
+
|
|
269
|
+
await act(async () => {
|
|
270
|
+
tree = await create(wrapComponent(route));
|
|
271
|
+
});
|
|
272
|
+
const instance = tree.root;
|
|
273
|
+
const button = instance.find(
|
|
274
|
+
(el) => el.props.testID === TESTID.BUTTON_YOUR_LOCATION
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
global.navigator.geolocation = {
|
|
278
|
+
getCurrentPosition: (onSuccess, onError, options) => {
|
|
279
|
+
onError({ code: GEOLOCATION_ERROR.POSITION_UNAVAILABLE });
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
await act(async () => {
|
|
284
|
+
await button.props.onPress();
|
|
285
|
+
});
|
|
286
|
+
// expect AlertAsync is called
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test('test get current location failed error not handle', async () => {
|
|
213
290
|
await act(async () => {
|
|
214
291
|
tree = await create(wrapComponent(route));
|
|
215
292
|
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import React, { useState, useCallback } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { StyleSheet, View, TouchableOpacity } from 'react-native';
|
|
3
3
|
import { useNavigation } from '@react-navigation/native';
|
|
4
4
|
|
|
5
5
|
import ItemQuickAction from '../../../../commons/Action/ItemQuickAction';
|
|
6
6
|
import Text from '../../../../commons/Text';
|
|
7
7
|
import Routes from '../../../../utils/Route';
|
|
8
|
-
|
|
9
|
-
import
|
|
8
|
+
import { Colors } from '../../../../configs';
|
|
9
|
+
import FImage from '../../../../commons/FImage';
|
|
10
10
|
|
|
11
11
|
const MyUnitDevice = ({ sensor, unit }) => {
|
|
12
12
|
const [status, setStatus] = useState(sensor.status);
|
|
@@ -26,7 +26,7 @@ const MyUnitDevice = ({ sensor, unit }) => {
|
|
|
26
26
|
<View style={styles.item}>
|
|
27
27
|
<TouchableOpacity style={styles.flex1} onPress={goToSensorDisplay}>
|
|
28
28
|
<View style={styles.rowCenter}>
|
|
29
|
-
<
|
|
29
|
+
<FImage style={styles.image} source={{ uri: sensor?.icon_kit }} />
|
|
30
30
|
<View style={styles.marginTop3}>
|
|
31
31
|
<Text semibold style={styles.nameDevice}>
|
|
32
32
|
{sensor.name}
|
|
@@ -43,7 +43,7 @@ describe('Test MyUnitDevice', () => {
|
|
|
43
43
|
});
|
|
44
44
|
const instance = tree.root;
|
|
45
45
|
const Views = instance.findAllByType(View);
|
|
46
|
-
expect(Views).toHaveLength(
|
|
46
|
+
expect(Views).toHaveLength(12);
|
|
47
47
|
|
|
48
48
|
const touches = instance.findAllByType(TouchableOpacity);
|
|
49
49
|
expect(touches).toHaveLength(1);
|
|
@@ -60,6 +60,6 @@ describe('Test MyUnitDevice', () => {
|
|
|
60
60
|
});
|
|
61
61
|
const instance = tree.root;
|
|
62
62
|
const Views = instance.findAllByType(View);
|
|
63
|
-
expect(Views).toHaveLength(
|
|
63
|
+
expect(Views).toHaveLength(12);
|
|
64
64
|
});
|
|
65
65
|
});
|
package/src/utils/Apis/axios.js
CHANGED
|
@@ -2,6 +2,9 @@ import { create } from 'apisauce';
|
|
|
2
2
|
import { getData, storeData } from '../Storage';
|
|
3
3
|
import { ToastBottomHelper } from '../Utils';
|
|
4
4
|
import NetInfo from '@react-native-community/netinfo';
|
|
5
|
+
import { PROBLEM_CODE } from '../../configs/Constants';
|
|
6
|
+
import { getTranslate } from '../I18n';
|
|
7
|
+
import { SCConfig } from '../../configs';
|
|
5
8
|
|
|
6
9
|
const api = create({
|
|
7
10
|
headers: {
|
|
@@ -39,6 +42,11 @@ const parseErrorResponse = async (error) => {
|
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
44
|
if (!hideError) {
|
|
45
|
+
switch (error.problem) {
|
|
46
|
+
case PROBLEM_CODE.SERVER_ERROR:
|
|
47
|
+
message = getTranslate(SCConfig.language, 'server_error');
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
42
50
|
ToastBottomHelper.error(message);
|
|
43
51
|
}
|
|
44
52
|
}
|
|
@@ -102,7 +110,7 @@ export async function axiosGet(URL, config = {}, cache = false) {
|
|
|
102
110
|
return await parseErrorResponse(error);
|
|
103
111
|
}
|
|
104
112
|
const { data, problem } = response;
|
|
105
|
-
if (problem
|
|
113
|
+
if (problem) {
|
|
106
114
|
if (cache) {
|
|
107
115
|
return (
|
|
108
116
|
(await axiosCache(URL, 500)) || (await parseErrorResponse(response))
|
|
@@ -989,5 +989,7 @@
|
|
|
989
989
|
"This {name} was removed!" : "This {name} was removed!",
|
|
990
990
|
"camera_request_permission": "Camera request permission",
|
|
991
991
|
"camera_request_permission_des": "To use camera, please unlock camera permission",
|
|
992
|
-
"location_require_message": "EoH needs your location permission to get current location."
|
|
992
|
+
"location_require_message": "EoH needs your location permission to get current location.",
|
|
993
|
+
"turn_on_location_service": "Turn on location service for your phone",
|
|
994
|
+
"turn_on_location_service_des": "To continue, let your device turn on location by turning on location service in Settings"
|
|
993
995
|
}
|
|
@@ -990,5 +990,7 @@
|
|
|
990
990
|
"location_perm_denied": "Ứng dụng không cho phép truy cập vị trí.",
|
|
991
991
|
"camera_request_permission": "Quyền yêu cầu máy ảnh",
|
|
992
992
|
"camera_request_permission_des": "Để sử dụng máy ảnh, vui lòng mở khóa quyền đối với máy ảnh",
|
|
993
|
-
"location_require_message": "Eoh cần quyền truy cập vị trí của bạn để lấy vị trí hiện tại."
|
|
993
|
+
"location_require_message": "Eoh cần quyền truy cập vị trí của bạn để lấy vị trí hiện tại.",
|
|
994
|
+
"turn_on_location_service": "Bật dịch vụ vị trí cho điện thoại của bạn",
|
|
995
|
+
"turn_on_location_service_des": "Để tiếp tục, bật dịch vụ vị trí cho điện thoại của bạn trong Cài Đặt"
|
|
994
996
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
import RNAndroidLocationEnabler from 'react-native-android-location-enabler';
|
|
3
|
+
import AlertAsync from 'react-native-alert-async';
|
|
4
|
+
import t from '../../hooks/Common/useTranslations';
|
|
5
|
+
|
|
6
|
+
export const openPromptEnableLocation = async () => {
|
|
7
|
+
if (Platform.OS === 'android') {
|
|
8
|
+
try {
|
|
9
|
+
await RNAndroidLocationEnabler.promptForEnableLocationIfNeeded({
|
|
10
|
+
interval: 10000,
|
|
11
|
+
fastInterval: 5000,
|
|
12
|
+
});
|
|
13
|
+
return true;
|
|
14
|
+
} catch (err) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
} else {
|
|
18
|
+
await AlertAsync(
|
|
19
|
+
t('turn_on_location_service'),
|
|
20
|
+
t('turn_on_location_service_des'),
|
|
21
|
+
[
|
|
22
|
+
{
|
|
23
|
+
text: t('ok'),
|
|
24
|
+
onPress: () => {},
|
|
25
|
+
},
|
|
26
|
+
]
|
|
27
|
+
);
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { validateEmail, formatMoney } from '../Utils';
|
|
2
|
+
|
|
3
|
+
describe('Test utils', () => {
|
|
4
|
+
test('test validateEmail', () => {
|
|
5
|
+
const result = validateEmail('eoh.2020@gmail.com');
|
|
6
|
+
expect(result).toEqual(true);
|
|
7
|
+
});
|
|
8
|
+
test('test formatMoney', () => {
|
|
9
|
+
const result = formatMoney(10000);
|
|
10
|
+
expect(result).toEqual('10.000 đ');
|
|
11
|
+
});
|
|
12
|
+
});
|