@eohjsc/react-native-smart-city 0.3.1 → 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 +4 -2
- package/react-native-smart-city.podspec +1 -0
- 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/DeviceItem/DeviceItem.js +7 -3
- package/src/commons/ConnectingProcess/DeviceItem/DeviceItemStyles.js +8 -11
- package/src/commons/ConnectingProcess/__test__/DeviceItem.test.js +3 -2
- package/src/commons/ConnectingProcess/index.js +64 -18
- 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 +16 -7
- package/src/commons/Sharing/MemberList.js +10 -2
- package/src/commons/Sharing/WrapHeaderScrollable.js +2 -0
- package/src/commons/SubUnit/ShortDetail.js +24 -7
- package/src/commons/SubUnit/__test__/ShortDetail.test.js +9 -2
- package/src/configs/API.js +4 -0
- package/src/configs/Constants.js +19 -0
- package/src/configs/SCConfig.js +2 -0
- package/src/hooks/Common/useSensorsStatus.js +11 -8
- package/src/hooks/useReceiveNotifications.js +1 -1
- 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/screens/UnitSummary/components/WaterQuality/Item/index.js +10 -2
- package/src/utils/Apis/axios.js +9 -1
- package/src/utils/I18n/translations/en.json +5 -1
- package/src/utils/I18n/translations/vi.json +5 -1
- package/src/utils/Permission/common.js +29 -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": [
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"@ant-design/icons-react-native": "^2.2.1",
|
|
101
101
|
"@ant-design/react-native": "^4.0.5",
|
|
102
102
|
"@babel/helper-environment-visitor": "^7.16.7",
|
|
103
|
-
"@eohjsc/highcharts": "^1.0.
|
|
103
|
+
"@eohjsc/highcharts": "^1.0.9",
|
|
104
104
|
"@eohjsc/react-native-keyboard-aware-scroll-view": "^0.9.5",
|
|
105
105
|
"@formatjs/intl-getcanonicallocales": "^1.4.5",
|
|
106
106
|
"@formatjs/intl-numberformat": "^5.6.2",
|
|
@@ -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(() => {
|
|
@@ -2,13 +2,17 @@ import React, { memo } from 'react';
|
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
import styles from './DeviceItemStyles';
|
|
4
4
|
import FImage from '../../FImage';
|
|
5
|
-
import
|
|
5
|
+
import _TextInput from '../../Form/TextInput';
|
|
6
6
|
|
|
7
|
-
const DeviceItem = memo(({ icon, name }) => {
|
|
7
|
+
const DeviceItem = memo(({ icon, name, setNewName }) => {
|
|
8
8
|
return (
|
|
9
9
|
<View style={styles.container}>
|
|
10
10
|
{!!icon && <FImage source={{ uri: icon }} style={styles.iconSensor} />}
|
|
11
|
-
<
|
|
11
|
+
<_TextInput
|
|
12
|
+
value={name}
|
|
13
|
+
textInputStyle={styles.textItem}
|
|
14
|
+
onChange={setNewName}
|
|
15
|
+
/>
|
|
12
16
|
</View>
|
|
13
17
|
);
|
|
14
18
|
});
|
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
const marginItem = 12;
|
|
5
|
-
const marginHorizontal = 5;
|
|
6
|
-
const widthItem = (Constants.width - marginHorizontal * 2 - marginItem) / 2;
|
|
7
|
-
const heightItem = (widthItem / 166) * 60;
|
|
2
|
+
import { Colors } from '../../../configs';
|
|
8
3
|
|
|
9
4
|
export default StyleSheet.create({
|
|
10
5
|
container: {
|
|
11
|
-
paddingHorizontal: 15,
|
|
12
6
|
borderRadius: 10,
|
|
13
7
|
width: 250,
|
|
14
|
-
height: heightItem,
|
|
15
8
|
borderWidth: 1,
|
|
16
9
|
borderColor: Colors.Gray4,
|
|
17
10
|
backgroundColor: Colors.White,
|
|
18
11
|
justifyContent: 'center',
|
|
19
|
-
alignItems: 'center',
|
|
20
|
-
marginBottom: 16,
|
|
21
12
|
flexDirection: 'row',
|
|
22
13
|
shadowColor: Colors.Shadow,
|
|
23
14
|
shadowOffset: {
|
|
@@ -36,7 +27,13 @@ export default StyleSheet.create({
|
|
|
36
27
|
resizeMode: 'contain',
|
|
37
28
|
},
|
|
38
29
|
textItem: {
|
|
39
|
-
|
|
30
|
+
borderWidth: 0,
|
|
31
|
+
borderBottomWidth: 1,
|
|
32
|
+
borderBottomColor: Colors.Primary,
|
|
33
|
+
textAlign: 'center',
|
|
34
|
+
marginTop: -10,
|
|
35
|
+
padding: 5,
|
|
40
36
|
fontSize: 16,
|
|
37
|
+
height: 50,
|
|
41
38
|
},
|
|
42
39
|
});
|
|
@@ -2,7 +2,8 @@ import React from 'react';
|
|
|
2
2
|
import renderer, { act } from 'react-test-renderer';
|
|
3
3
|
import DeviceItem from '../DeviceItem/DeviceItem';
|
|
4
4
|
import FImage from '../../FImage';
|
|
5
|
-
import
|
|
5
|
+
import _TextInput from '../../Form/TextInput';
|
|
6
|
+
|
|
6
7
|
describe('Test DeviceItem button', () => {
|
|
7
8
|
let tree;
|
|
8
9
|
test('create DeviceItem button', () => {
|
|
@@ -11,7 +12,7 @@ describe('Test DeviceItem button', () => {
|
|
|
11
12
|
});
|
|
12
13
|
const instance = tree.root;
|
|
13
14
|
const image = instance.findAllByType(FImage);
|
|
14
|
-
const text = instance.findAllByType(
|
|
15
|
+
const text = instance.findAllByType(_TextInput);
|
|
15
16
|
expect(image).toHaveLength(0);
|
|
16
17
|
expect(text).toHaveLength(1);
|
|
17
18
|
});
|
|
@@ -6,7 +6,7 @@ import { useNavigation } from '@react-navigation/native';
|
|
|
6
6
|
import ImageSuccessfully from '../../Images/Common/SuccessfullyConnected.svg';
|
|
7
7
|
|
|
8
8
|
import Text from '../Text';
|
|
9
|
-
import { axiosPost } from '../../utils/Apis/axios';
|
|
9
|
+
import { axiosPatch, axiosPost } from '../../utils/Apis/axios';
|
|
10
10
|
import { API, Colors } from '../../configs';
|
|
11
11
|
import styles from './styles';
|
|
12
12
|
import DeviceItem from './DeviceItem/DeviceItem';
|
|
@@ -32,25 +32,27 @@ const ConnectingProcess = ({ route }) => {
|
|
|
32
32
|
const [isLoading, setIsLoading] = useState(true);
|
|
33
33
|
const [sensor, setSensor] = useState(null);
|
|
34
34
|
const user = useSCContextSelector((state) => state?.auth?.account?.user);
|
|
35
|
+
const [newName, setNewName] = useState('');
|
|
35
36
|
|
|
36
37
|
const ConnectingDevice = useCallback(async () => {
|
|
37
38
|
setIsLoading(true);
|
|
38
39
|
switch (devicePrefixName) {
|
|
39
|
-
case 'SENSOR':
|
|
40
|
-
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
40
|
+
case 'SENSOR': {
|
|
41
|
+
const body = { imei: scan_sensor_data?.imei, chip: gateway?.id };
|
|
42
|
+
const { success, data } = await axiosPost(
|
|
43
|
+
API.SUB_UNIT.SENSOR_SCAN(station.id),
|
|
44
|
+
body
|
|
45
|
+
);
|
|
46
|
+
if (success) {
|
|
47
|
+
setSensor(data);
|
|
48
|
+
setNewName(data?.name);
|
|
49
|
+
} else {
|
|
50
|
+
ToastBottomHelper.error(JSON.stringify(data));
|
|
51
|
+
goBack();
|
|
52
52
|
}
|
|
53
|
+
|
|
53
54
|
break;
|
|
55
|
+
}
|
|
54
56
|
case 'ROBOT': {
|
|
55
57
|
const { success, data } = await axiosPost(API.UNIT.CHIP_SCAN(unit.id), {
|
|
56
58
|
imei: gateway?.imei,
|
|
@@ -63,6 +65,7 @@ const ConnectingProcess = ({ route }) => {
|
|
|
63
65
|
});
|
|
64
66
|
if (success) {
|
|
65
67
|
setSensor(data);
|
|
68
|
+
setNewName(data?.name);
|
|
66
69
|
} else {
|
|
67
70
|
ToastBottomHelper.error(JSON.stringify(data));
|
|
68
71
|
goBack();
|
|
@@ -79,6 +82,7 @@ const ConnectingProcess = ({ route }) => {
|
|
|
79
82
|
);
|
|
80
83
|
if (success) {
|
|
81
84
|
setSensor({ name: gateway?.model });
|
|
85
|
+
setNewName(gateway?.model);
|
|
82
86
|
} else {
|
|
83
87
|
ToastBottomHelper.error(JSON.stringify(data));
|
|
84
88
|
goBack();
|
|
@@ -114,12 +118,46 @@ const ConnectingProcess = ({ route }) => {
|
|
|
114
118
|
station?.name !== undefined ? '- ' + station?.name : ''
|
|
115
119
|
}`}
|
|
116
120
|
</Text>
|
|
117
|
-
<DeviceItem
|
|
121
|
+
<DeviceItem
|
|
122
|
+
icon={sensor?.icon_kit}
|
|
123
|
+
name={newName}
|
|
124
|
+
setNewName={setNewName}
|
|
125
|
+
/>
|
|
118
126
|
</View>
|
|
119
127
|
);
|
|
120
|
-
}, [
|
|
128
|
+
}, [newName, sensor?.icon_kit, station?.name, t, unit?.name, unit_name]);
|
|
129
|
+
|
|
130
|
+
const handleDone = useCallback(async () => {
|
|
131
|
+
let result, message;
|
|
132
|
+
switch (devicePrefixName) {
|
|
133
|
+
case 'SENSOR': {
|
|
134
|
+
const { success, data } = await axiosPatch(
|
|
135
|
+
API.SENSOR.SENSOR_DETAIL(sensor?.id),
|
|
136
|
+
{
|
|
137
|
+
name: newName,
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
result = success;
|
|
141
|
+
message = data;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
case 'ROBOT':
|
|
145
|
+
case 'LITE': {
|
|
146
|
+
const { success, data } = await axiosPatch(
|
|
147
|
+
API.CHIP.CHIP_DETAIL(sensor?.id),
|
|
148
|
+
{
|
|
149
|
+
name: newName,
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
result = success;
|
|
153
|
+
message = data;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
121
157
|
|
|
122
|
-
|
|
158
|
+
if (!result) {
|
|
159
|
+
ToastBottomHelper.error(JSON.stringify(message));
|
|
160
|
+
}
|
|
123
161
|
navigate(Routes.UnitStack, {
|
|
124
162
|
screen: Routes.UnitDetail,
|
|
125
163
|
params: {
|
|
@@ -129,7 +167,15 @@ const ConnectingProcess = ({ route }) => {
|
|
|
129
167
|
stationId: station?.id,
|
|
130
168
|
},
|
|
131
169
|
});
|
|
132
|
-
}, [
|
|
170
|
+
}, [
|
|
171
|
+
devicePrefixName,
|
|
172
|
+
navigate,
|
|
173
|
+
newName,
|
|
174
|
+
sensor?.id,
|
|
175
|
+
station?.id,
|
|
176
|
+
unit,
|
|
177
|
+
unit_id,
|
|
178
|
+
]);
|
|
133
179
|
|
|
134
180
|
useEffect(() => {
|
|
135
181
|
ConnectingDevice();
|
|
@@ -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:
|
|
@@ -91,9 +100,9 @@ const MediaPlayerDetail = ({
|
|
|
91
100
|
<VLCPlayer
|
|
92
101
|
autoAspectRatio={true}
|
|
93
102
|
videoAspectRatio={
|
|
94
|
-
|
|
95
|
-
? `${width}:${height}`
|
|
96
|
-
: `${
|
|
103
|
+
amount
|
|
104
|
+
? `${getWidthHeight().width}:${getWidthHeight().height}`
|
|
105
|
+
: `${width}:${height}`
|
|
97
106
|
}
|
|
98
107
|
source={{
|
|
99
108
|
initType: 2,
|
|
@@ -114,8 +123,8 @@ const MediaPlayerDetail = ({
|
|
|
114
123
|
styles.player,
|
|
115
124
|
style,
|
|
116
125
|
{
|
|
117
|
-
width:
|
|
118
|
-
height:
|
|
126
|
+
width: amount ? getWidthHeight().width : width,
|
|
127
|
+
height: amount ? getWidthHeight().height : height,
|
|
119
128
|
},
|
|
120
129
|
]}
|
|
121
130
|
isLive={true}
|
|
@@ -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 } } }],
|
|
@@ -15,6 +15,12 @@ import { standardizeCameraScreenSize } from '../../utils/Utils';
|
|
|
15
15
|
import Routes from '../../utils/Route';
|
|
16
16
|
import FastImage from 'react-native-fast-image';
|
|
17
17
|
import MediaPlayerDetail from '../MediaPlayerDetail';
|
|
18
|
+
import {
|
|
19
|
+
keyPermission,
|
|
20
|
+
OpenSetting,
|
|
21
|
+
permitPermissionFunction,
|
|
22
|
+
} from '../../utils/Permission/common';
|
|
23
|
+
import { RESULTS } from 'react-native-permissions';
|
|
18
24
|
|
|
19
25
|
const { standardizeWidth, standardizeHeight } = standardizeCameraScreenSize(
|
|
20
26
|
Device.screenWidth - 32
|
|
@@ -71,13 +77,24 @@ const ShortDetailSubUnit = ({ unit, station }) => {
|
|
|
71
77
|
};
|
|
72
78
|
|
|
73
79
|
const handleOnAddNew = () => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
permitPermissionFunction(keyPermission.CAMERA, (res) => {
|
|
81
|
+
if (res === RESULTS.GRANTED) {
|
|
82
|
+
navigate(Routes.AddDeviceStack, {
|
|
83
|
+
screen: Routes.ScanSensorQR,
|
|
84
|
+
params: {
|
|
85
|
+
station_id: station?.id,
|
|
86
|
+
unit_id: unit.id,
|
|
87
|
+
unit_name: unit.name,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (res === RESULTS.BLOCKED) {
|
|
93
|
+
OpenSetting(
|
|
94
|
+
t('camera_request_permission'),
|
|
95
|
+
t('camera_request_permission_des')
|
|
96
|
+
);
|
|
97
|
+
}
|
|
81
98
|
});
|
|
82
99
|
};
|
|
83
100
|
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Image, View } from 'react-native';
|
|
3
3
|
import { act, create } from 'react-test-renderer';
|
|
4
|
+
import mock from 'react-native-permissions/mock';
|
|
4
5
|
import { TESTID } from '../../../configs/Constants';
|
|
5
6
|
import { SCProvider } from '../../../context';
|
|
6
7
|
import { mockSCStore } from '../../../context/mockStore';
|
|
7
8
|
import ShortDetailSubUnit from '../ShortDetail';
|
|
8
9
|
import ItemAddNew from '../../Device/ItemAddNew';
|
|
9
10
|
import Routes from '../../../utils/Route';
|
|
11
|
+
import { keyPermission } from '../../../utils/Permission/common';
|
|
10
12
|
|
|
11
13
|
const wrapComponent = (props) => (
|
|
12
14
|
<SCProvider initState={mockSCStore({})}>
|
|
@@ -21,6 +23,10 @@ jest.mock('react', () => {
|
|
|
21
23
|
};
|
|
22
24
|
});
|
|
23
25
|
|
|
26
|
+
jest.mock('react-native-permissions', () => {
|
|
27
|
+
return mock;
|
|
28
|
+
});
|
|
29
|
+
|
|
24
30
|
const mockedNavigate = jest.fn();
|
|
25
31
|
jest.mock('@react-navigation/native', () => {
|
|
26
32
|
return {
|
|
@@ -145,10 +151,10 @@ describe('test ShortDetail Subunit', () => {
|
|
|
145
151
|
(item) =>
|
|
146
152
|
item.props.testID === TESTID.SUB_UNIT_DEVICES && item.type === View
|
|
147
153
|
);
|
|
148
|
-
expect(itemDevice.length).toBe(
|
|
154
|
+
expect(itemDevice.length).toBe(0);
|
|
149
155
|
});
|
|
150
156
|
|
|
151
|
-
test('render ShortDetail add new device', () => {
|
|
157
|
+
test('render ShortDetail add new device', async () => {
|
|
152
158
|
act(() => {
|
|
153
159
|
tree = create(wrapComponent(props));
|
|
154
160
|
});
|
|
@@ -157,6 +163,7 @@ describe('test ShortDetail Subunit', () => {
|
|
|
157
163
|
act(() => {
|
|
158
164
|
buttonAddNew.props.onAddNew();
|
|
159
165
|
});
|
|
166
|
+
await mock.request(keyPermission.CAMERA);
|
|
160
167
|
expect(mockedNavigate).toHaveBeenCalledWith(Routes.AddDeviceStack, {
|
|
161
168
|
screen: Routes.ScanSensorQR,
|
|
162
169
|
params: {
|