@eohjsc/react-native-smart-city 0.3.3 → 0.3.6
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/NumberUpDownActionTemplate.js +31 -12
- package/src/commons/ActionGroup/OnOffTemplate/OnOffButtonTemplate.js +8 -2
- package/src/commons/ActionGroup/OptionsDropdownActionTemplate.js +9 -7
- package/src/commons/ActionGroup/SliderRangeTemplate.js +5 -1
- package/src/commons/ActionGroup/__test__/NumberUpDownTemplate.test.js +83 -3
- package/src/commons/ActionGroup/__test__/OptionsDropdownTemplate.test.js +57 -1
- package/src/commons/ActionGroup/__test__/index.test.js +4 -7
- package/src/commons/ConnectingProcess/index.js +4 -1
- 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 +20 -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/AddNewGateway/PlugAndPlay/GatewayWifiList.js +39 -36
- package/src/screens/AddNewGateway/PlugAndPlay/__test__/GatewayWifiList.test.js +64 -10
- 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/src/commons/ActionGroup/__test__/NumberUpDownTemplateWithNullConfigValue.test.js +0 -60
|
@@ -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
|
+
});
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { TouchableOpacity } from 'react-native';
|
|
3
|
-
import { act, create } from 'react-test-renderer';
|
|
4
|
-
import Text from '../../../commons/Text';
|
|
5
|
-
import { watchMultiConfigs } from '../../../iot/Monitor';
|
|
6
|
-
import NumberUpDownActionTemplate from '../NumberUpDownActionTemplate';
|
|
7
|
-
|
|
8
|
-
jest.mock('../../../iot/Monitor');
|
|
9
|
-
|
|
10
|
-
jest.mock('../../../iot/states', () => ({
|
|
11
|
-
useConfigGlobalState: () => [{ 5: null }, null],
|
|
12
|
-
}));
|
|
13
|
-
|
|
14
|
-
describe('Test NumberUpDownActionTemplate', () => {
|
|
15
|
-
const action_data = {
|
|
16
|
-
color: '#00979D',
|
|
17
|
-
command_prefer_over_bluetooth: true,
|
|
18
|
-
command_prefer_over_googlehome: false,
|
|
19
|
-
command_prefer_over_internet: false,
|
|
20
|
-
googlehome_actions: [],
|
|
21
|
-
icon: 'caret-up',
|
|
22
|
-
id: 20,
|
|
23
|
-
key: '5ed1d4dc-a905-47cd-b0c9-f979644bd21a',
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
let actionGroup;
|
|
27
|
-
let wrapper;
|
|
28
|
-
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
watchMultiConfigs.mockClear();
|
|
31
|
-
actionGroup = {
|
|
32
|
-
configuration: {
|
|
33
|
-
config: 5,
|
|
34
|
-
action_data,
|
|
35
|
-
min_value: 16,
|
|
36
|
-
max_value: 30,
|
|
37
|
-
text_format: '{number} *C',
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test('render template', async () => {
|
|
43
|
-
const mockDoAction = jest.fn();
|
|
44
|
-
await act(async () => {
|
|
45
|
-
wrapper = await create(
|
|
46
|
-
<NumberUpDownActionTemplate
|
|
47
|
-
actionGroup={actionGroup}
|
|
48
|
-
doAction={mockDoAction}
|
|
49
|
-
sensor={{ is_managed_by_backend: true }}
|
|
50
|
-
/>
|
|
51
|
-
);
|
|
52
|
-
});
|
|
53
|
-
const instance = wrapper.root;
|
|
54
|
-
const text = instance.findByType(Text);
|
|
55
|
-
expect(text.props.children).toEqual('28 *C');
|
|
56
|
-
|
|
57
|
-
const touchs = instance.findAllByType(TouchableOpacity);
|
|
58
|
-
expect(touchs).toHaveLength(2);
|
|
59
|
-
});
|
|
60
|
-
});
|