@eohjsc/react-native-smart-city 0.2.96 → 0.2.97
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 +15 -3
- package/src/commons/{Connecting → Connecting}/__test__/Connecting.test.js +0 -0
- package/src/commons/{Connecting → Connecting}/index.js +0 -0
- package/src/commons/{Connecting → Connecting}/styles.js +0 -0
- package/src/commons/ConnectingProcess/index.js +1 -1
- package/src/commons/Device/HistoryChart.js +2 -2
- package/src/commons/PreventAccess/index.js +59 -0
- package/src/commons/PreventAccess/styles.js +33 -0
- package/src/iot/RemoteControl/GoogleHome.js +24 -11
- package/src/iot/RemoteControl/__test__/GoogleHome.test.js +32 -0
- package/src/screens/AddNewAction/SelectSensorDevices.js +6 -1
- package/src/screens/AddNewAction/__test__/SelectSensorDevices.test.js +34 -92
- package/src/screens/AddNewGateway/PlugAndPlay/ConnectWifiWarning.js +1 -1
- package/src/screens/Device/detail.js +16 -3
- package/src/screens/Unit/Detail.js +18 -5
- package/src/screens/Unit/__test__/Detail.test.js +25 -5
- package/src/screens/UnitSummary/components/3PPowerConsumption/index.js +1 -1
- package/src/utils/I18n/translations/en.json +4 -1
- package/src/utils/I18n/translations/vi.json +4 -1
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.2.
|
|
4
|
+
"version": "0.2.97",
|
|
5
5
|
"description": "TODO",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"files": [
|
|
@@ -81,10 +81,11 @@
|
|
|
81
81
|
"eslint-plugin-react": "^7.21.5",
|
|
82
82
|
"eslint-plugin-react-native": "^3.10.0",
|
|
83
83
|
"factory-girl": "^5.0.4",
|
|
84
|
+
"husky": "^8.0.1",
|
|
84
85
|
"jest": "^26.6.3",
|
|
85
86
|
"jest-circus": "^26.6.3",
|
|
86
87
|
"jetifier": "^1.6.6",
|
|
87
|
-
"lint-staged": "^
|
|
88
|
+
"lint-staged": "^12.4.1",
|
|
88
89
|
"metro-react-native-babel-preset": "^0.66.2",
|
|
89
90
|
"node-html-parser": "^2.0.2",
|
|
90
91
|
"react": "17.0.2",
|
|
@@ -97,6 +98,7 @@
|
|
|
97
98
|
"dependencies": {
|
|
98
99
|
"@ant-design/icons-react-native": "^2.2.1",
|
|
99
100
|
"@ant-design/react-native": "^4.0.5",
|
|
101
|
+
"@babel/helper-environment-visitor": "^7.16.7",
|
|
100
102
|
"@eohjsc/highcharts": "^1.0.8",
|
|
101
103
|
"@eohjsc/react-native-keyboard-aware-scroll-view": "^0.9.5",
|
|
102
104
|
"@formatjs/intl-getcanonicallocales": "^1.4.5",
|
|
@@ -209,5 +211,15 @@
|
|
|
209
211
|
"bugs": {
|
|
210
212
|
"url": "https://github.com/github_account/react-native-smart-city/issues"
|
|
211
213
|
},
|
|
212
|
-
"homepage": "https://github.com/github_account/react-native-smart-city#readme"
|
|
214
|
+
"homepage": "https://github.com/github_account/react-native-smart-city#readme",
|
|
215
|
+
"husky": {
|
|
216
|
+
"hooks": {
|
|
217
|
+
"pre-commit": "lint-staged"
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
"lint-staged": {
|
|
221
|
+
"*.{js, ts}": [
|
|
222
|
+
"yarn lint"
|
|
223
|
+
]
|
|
224
|
+
}
|
|
213
225
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -10,7 +10,7 @@ import { 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';
|
|
13
|
-
import Connecting from '../Connecting
|
|
13
|
+
import Connecting from '../Connecting';
|
|
14
14
|
import { useSCContextSelector } from '../../context';
|
|
15
15
|
|
|
16
16
|
const ConnectingProcess = ({ route }) => {
|
|
@@ -234,7 +234,7 @@ const HistoryChart = memo(
|
|
|
234
234
|
)}
|
|
235
235
|
<DateTimePickerModal
|
|
236
236
|
isVisible={eventPicker.showModalStart}
|
|
237
|
-
date={eventPicker.startTime.
|
|
237
|
+
date={eventPicker.startTime.valueOf()}
|
|
238
238
|
mode={formatType || 'datetime'}
|
|
239
239
|
onConfirm={onConfirmStart}
|
|
240
240
|
onCancel={onCancel}
|
|
@@ -242,7 +242,7 @@ const HistoryChart = memo(
|
|
|
242
242
|
/>
|
|
243
243
|
<DateTimePickerModal
|
|
244
244
|
isVisible={eventPicker.showModalEnd}
|
|
245
|
-
date={eventPicker.endTime.
|
|
245
|
+
date={eventPicker.endTime.valueOf()}
|
|
246
246
|
mode={formatType || 'datetime'}
|
|
247
247
|
onConfirm={onConfirmEnd}
|
|
248
248
|
onCancel={onCancel}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React, { memo, useCallback, useMemo } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import Modal from 'react-native-modal';
|
|
4
|
+
import { useNavigation } from '@react-navigation/native';
|
|
5
|
+
|
|
6
|
+
import t from '../../hooks/Common/useTranslations';
|
|
7
|
+
import Text from '../Text';
|
|
8
|
+
import { Colors } from '../../configs';
|
|
9
|
+
import { styles } from './styles';
|
|
10
|
+
|
|
11
|
+
const PreventAccess = memo(({ headerBodyText, visible, hidePreventAccess }) => {
|
|
12
|
+
const { goBack } = useNavigation();
|
|
13
|
+
|
|
14
|
+
const dataText = useMemo(() => {
|
|
15
|
+
return {
|
|
16
|
+
headerText: t('note'),
|
|
17
|
+
bodyText: t('This {name} was removed!', {
|
|
18
|
+
name: headerBodyText ? headerBodyText : '',
|
|
19
|
+
}),
|
|
20
|
+
endOfText: t('back'),
|
|
21
|
+
};
|
|
22
|
+
}, [headerBodyText]);
|
|
23
|
+
|
|
24
|
+
const handleBackPress = useCallback(() => {
|
|
25
|
+
hidePreventAccess();
|
|
26
|
+
goBack();
|
|
27
|
+
}, [goBack, hidePreventAccess]);
|
|
28
|
+
|
|
29
|
+
const handleDonePopup = useCallback(() => {
|
|
30
|
+
goBack();
|
|
31
|
+
hidePreventAccess();
|
|
32
|
+
}, [goBack, hidePreventAccess]);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Modal
|
|
36
|
+
isVisible={visible}
|
|
37
|
+
transparent={true}
|
|
38
|
+
onBackButtonPress={handleBackPress}
|
|
39
|
+
style={styles.container}
|
|
40
|
+
>
|
|
41
|
+
<View style={styles.popoverStyle}>
|
|
42
|
+
<View style={styles.actionTextWrap}>
|
|
43
|
+
<Text size={17} bold color={Colors.Gray9} style={styles.textHeader}>
|
|
44
|
+
{dataText?.headerText}
|
|
45
|
+
</Text>
|
|
46
|
+
<Text type="H4" color={Colors.Gray8} style={styles.textNotification}>
|
|
47
|
+
{dataText?.bodyText}
|
|
48
|
+
</Text>
|
|
49
|
+
</View>
|
|
50
|
+
<View style={styles.endOfText}>
|
|
51
|
+
<Text size={16} bold color={Colors.Primary} onPress={handleDonePopup}>
|
|
52
|
+
{dataText?.endOfText}
|
|
53
|
+
</Text>
|
|
54
|
+
</View>
|
|
55
|
+
</View>
|
|
56
|
+
</Modal>
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
export default PreventAccess;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
|
|
3
|
+
import { Colors } from '../../configs';
|
|
4
|
+
|
|
5
|
+
export const styles = StyleSheet.create({
|
|
6
|
+
container: {
|
|
7
|
+
flex: 1,
|
|
8
|
+
},
|
|
9
|
+
popoverStyle: {
|
|
10
|
+
flex: 1,
|
|
11
|
+
width: '100%',
|
|
12
|
+
backgroundColor: Colors.White,
|
|
13
|
+
position: 'absolute',
|
|
14
|
+
borderRadius: 10,
|
|
15
|
+
},
|
|
16
|
+
actionTextWrap: {
|
|
17
|
+
marginTop: 14,
|
|
18
|
+
marginHorizontal: 20,
|
|
19
|
+
},
|
|
20
|
+
textHeader: {
|
|
21
|
+
marginVertical: 10,
|
|
22
|
+
},
|
|
23
|
+
textNotification: {
|
|
24
|
+
lineHeight: 20,
|
|
25
|
+
},
|
|
26
|
+
endOfText: {
|
|
27
|
+
flexDirection: 'row',
|
|
28
|
+
justifyContent: 'flex-end',
|
|
29
|
+
marginTop: 5,
|
|
30
|
+
marginBottom: 20,
|
|
31
|
+
marginRight: 20,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
@@ -201,8 +201,22 @@ function getServiceName(message) {
|
|
|
201
201
|
return serviceSplit.join('_');
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
async function sendCommandSingleAction(connection, ghAction, data) {
|
|
205
|
+
const { message } = ghAction || {};
|
|
206
|
+
const name = getServiceName(message);
|
|
207
|
+
|
|
208
|
+
if (name && data) {
|
|
209
|
+
message.service_data[name] = data;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
await connection.sendMessagePromise(message);
|
|
213
|
+
}
|
|
214
|
+
|
|
204
215
|
export async function sendCommandOverGoogleHome(sensor, action, data) {
|
|
205
|
-
if (
|
|
216
|
+
if (
|
|
217
|
+
!(action.googlehome_actions && action.googlehome_actions.length) &&
|
|
218
|
+
!action.googlehome_action
|
|
219
|
+
) {
|
|
206
220
|
return;
|
|
207
221
|
}
|
|
208
222
|
|
|
@@ -212,20 +226,19 @@ export async function sendCommandOverGoogleHome(sensor, action, data) {
|
|
|
212
226
|
return;
|
|
213
227
|
}
|
|
214
228
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
229
|
+
if (action.googlehome_actions) {
|
|
230
|
+
for (let i = 0; i < action.googlehome_actions.length; i++) {
|
|
231
|
+
await sendCommandSingleAction(
|
|
232
|
+
connection,
|
|
233
|
+
action.googlehome_actions[i],
|
|
234
|
+
data
|
|
235
|
+
);
|
|
222
236
|
}
|
|
223
|
-
|
|
224
|
-
await connection.
|
|
237
|
+
} else if (action.googlehome_action) {
|
|
238
|
+
await sendCommandSingleAction(connection, action.googlehome_action, data);
|
|
225
239
|
}
|
|
226
240
|
|
|
227
241
|
const { success } = await axiosPost(API.SENSOR.ACTIVITY_LOG(), {
|
|
228
|
-
sensor_id: sensor.id,
|
|
229
242
|
action_id: action.id,
|
|
230
243
|
message: 'Trigger by user action with google home',
|
|
231
244
|
});
|
|
@@ -75,6 +75,9 @@ describe('Remote Control Google Home', () => {
|
|
|
75
75
|
message: 'message',
|
|
76
76
|
},
|
|
77
77
|
],
|
|
78
|
+
googlehome_action: {
|
|
79
|
+
message: 'message',
|
|
80
|
+
},
|
|
78
81
|
};
|
|
79
82
|
|
|
80
83
|
const response = {
|
|
@@ -223,6 +226,35 @@ describe('Remote Control Google Home', () => {
|
|
|
223
226
|
});
|
|
224
227
|
});
|
|
225
228
|
|
|
229
|
+
it('Send command over google home action googlehome_action one to one', async () => {
|
|
230
|
+
action.googlehome_actions = null;
|
|
231
|
+
action.googlehome_action.message = {
|
|
232
|
+
type: 'call_service',
|
|
233
|
+
domain: 'climate',
|
|
234
|
+
service: 'set_temperature',
|
|
235
|
+
service_data: {
|
|
236
|
+
temperature: 0,
|
|
237
|
+
entity_id: 'climate.dqsmart_0108f6cdde',
|
|
238
|
+
},
|
|
239
|
+
id: 20,
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
axios.post.mockImplementation(async () => {
|
|
243
|
+
return response;
|
|
244
|
+
});
|
|
245
|
+
await googleHomeConnect(options);
|
|
246
|
+
await sendCommandOverGoogleHome(sensor, action, 19);
|
|
247
|
+
|
|
248
|
+
action.googlehome_action.message.service_data.temperature = 19;
|
|
249
|
+
expect(connection.sendMessagePromise).toBeCalledWith(
|
|
250
|
+
action.googlehome_action.message
|
|
251
|
+
);
|
|
252
|
+
expect(axios.post).toHaveBeenCalledWith(API.SENSOR.ACTIVITY_LOG(), {
|
|
253
|
+
action_id: 1,
|
|
254
|
+
message: 'Trigger by user action with google home',
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
226
258
|
it('Send command over google home that not connected', async () => {
|
|
227
259
|
await googleHomeConnect(options);
|
|
228
260
|
await sendCommandOverGoogleHome({ chip_id: 2 }, action);
|
|
@@ -37,6 +37,7 @@ const SelectSensorDevices = memo(({ route }) => {
|
|
|
37
37
|
const [selectedDevice, setSelectedDevice] = useState();
|
|
38
38
|
const { navigate, dispatch, goBack } = useNavigation();
|
|
39
39
|
const isSelectSensor = title === AUTOMATE_SELECT.SELECT_SENSOR;
|
|
40
|
+
const [loading, setLoading] = useState(true);
|
|
40
41
|
|
|
41
42
|
const onSnapToItem = useCallback(
|
|
42
43
|
(item, index) => {
|
|
@@ -83,6 +84,7 @@ const SelectSensorDevices = memo(({ route }) => {
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
);
|
|
87
|
+
setLoading(false);
|
|
86
88
|
}, [automate.sensor_id, isSelectSensor, unit]);
|
|
87
89
|
|
|
88
90
|
useEffect(() => {
|
|
@@ -162,6 +164,9 @@ const SelectSensorDevices = memo(({ route }) => {
|
|
|
162
164
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
163
165
|
[route.params]
|
|
164
166
|
);
|
|
167
|
+
if (loading) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
165
170
|
|
|
166
171
|
return (
|
|
167
172
|
<View style={styles.wrap}>
|
|
@@ -175,7 +180,7 @@ const SelectSensorDevices = memo(({ route }) => {
|
|
|
175
180
|
{t(title)}
|
|
176
181
|
</Text>
|
|
177
182
|
|
|
178
|
-
{listStation ? (
|
|
183
|
+
{listStation.length ? (
|
|
179
184
|
<NavBar
|
|
180
185
|
listStation={listStation}
|
|
181
186
|
listMenuItem={listMenuItem}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import renderer, { act } from 'react-test-renderer';
|
|
3
3
|
import axios from 'axios';
|
|
4
4
|
|
|
@@ -25,12 +25,10 @@ jest.mock('@react-navigation/native', () => {
|
|
|
25
25
|
};
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
const mockSetState = jest.fn();
|
|
29
28
|
jest.mock('react', () => {
|
|
30
29
|
return {
|
|
31
30
|
...jest.requireActual('react'),
|
|
32
31
|
memo: (x) => x,
|
|
33
|
-
useState: jest.fn((init) => [init, mockSetState]),
|
|
34
32
|
};
|
|
35
33
|
});
|
|
36
34
|
|
|
@@ -49,28 +47,27 @@ describe('Test SelectSensorDevices', () => {
|
|
|
49
47
|
scriptName: 'scriptName test',
|
|
50
48
|
},
|
|
51
49
|
};
|
|
50
|
+
const response = {
|
|
51
|
+
status: 200,
|
|
52
|
+
success: true,
|
|
53
|
+
data: [
|
|
54
|
+
{
|
|
55
|
+
id: 1,
|
|
56
|
+
name: 'Device 1',
|
|
57
|
+
sensors: [
|
|
58
|
+
{ id: 1, name: 'sensor' },
|
|
59
|
+
{ id: 2, name: 'sensor' },
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
52
64
|
|
|
53
65
|
beforeEach(() => {
|
|
54
66
|
axios.get.mockClear();
|
|
55
67
|
mockedNavigate.mockClear();
|
|
56
|
-
mockSetState.mockClear();
|
|
57
68
|
});
|
|
58
69
|
|
|
59
70
|
test('fetch Device success', async () => {
|
|
60
|
-
const response = {
|
|
61
|
-
status: 200,
|
|
62
|
-
success: true,
|
|
63
|
-
data: [
|
|
64
|
-
{
|
|
65
|
-
id: 1,
|
|
66
|
-
name: 'Device 1',
|
|
67
|
-
sensors: [
|
|
68
|
-
{ id: 1, name: 'sensor' },
|
|
69
|
-
{ id: 2, name: 'sensor' },
|
|
70
|
-
],
|
|
71
|
-
},
|
|
72
|
-
],
|
|
73
|
-
};
|
|
74
71
|
axios.get.mockImplementation(async () => {
|
|
75
72
|
return response;
|
|
76
73
|
});
|
|
@@ -78,12 +75,17 @@ describe('Test SelectSensorDevices', () => {
|
|
|
78
75
|
await act(async () => {
|
|
79
76
|
tree = renderer.create(wrapComponent(route));
|
|
80
77
|
});
|
|
78
|
+
const instance = tree.root;
|
|
79
|
+
const navBar = instance.findByType(NavBar);
|
|
81
80
|
|
|
82
81
|
expect(axios.get).toHaveBeenCalledWith(API.UNIT.DEVICE_CONTROL(1), {});
|
|
83
|
-
expect(
|
|
82
|
+
expect(navBar.props.listStation).toHaveLength(1);
|
|
84
83
|
});
|
|
85
84
|
|
|
86
85
|
test('onPress continue', async () => {
|
|
86
|
+
axios.get.mockImplementation(async () => {
|
|
87
|
+
return response;
|
|
88
|
+
});
|
|
87
89
|
await act(async () => {
|
|
88
90
|
tree = renderer.create(wrapComponent(route));
|
|
89
91
|
});
|
|
@@ -97,23 +99,9 @@ describe('Test SelectSensorDevices', () => {
|
|
|
97
99
|
});
|
|
98
100
|
|
|
99
101
|
test('test onPressDevice', async () => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
name: 'Device 1',
|
|
104
|
-
sensors: [
|
|
105
|
-
{ id: 1, name: 'sensor' },
|
|
106
|
-
{ id: 2, name: 'sensor' },
|
|
107
|
-
],
|
|
108
|
-
},
|
|
109
|
-
];
|
|
110
|
-
const mockSetSelectedDevice = jest.fn();
|
|
111
|
-
useState.mockImplementationOnce((init) => [init, mockSetState]);
|
|
112
|
-
useState.mockImplementationOnce((init) => [init, mockSetState]);
|
|
113
|
-
useState.mockImplementationOnce((init) => [init, mockSetState]);
|
|
114
|
-
useState.mockImplementationOnce((init) => [data, mockSetState]);
|
|
115
|
-
useState.mockImplementationOnce((init) => [false, mockSetSelectedDevice]);
|
|
116
|
-
|
|
102
|
+
axios.get.mockImplementation(async () => {
|
|
103
|
+
return response;
|
|
104
|
+
});
|
|
117
105
|
await act(async () => {
|
|
118
106
|
tree = renderer.create(wrapComponent(route));
|
|
119
107
|
});
|
|
@@ -124,67 +112,18 @@ describe('Test SelectSensorDevices', () => {
|
|
|
124
112
|
act(() => {
|
|
125
113
|
device[0].props.onPress({ id: 1, name: 'sensor' });
|
|
126
114
|
});
|
|
127
|
-
expect(
|
|
128
|
-
id: 1,
|
|
129
|
-
name: 'sensor',
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
test('test onPressDevice false', async () => {
|
|
134
|
-
const data = [
|
|
135
|
-
{
|
|
136
|
-
id: 1,
|
|
137
|
-
name: 'Device 1',
|
|
138
|
-
sensors: [
|
|
139
|
-
{ id: 1, name: 'sensor' },
|
|
140
|
-
{ id: 2, name: 'sensor' },
|
|
141
|
-
],
|
|
142
|
-
},
|
|
143
|
-
];
|
|
144
|
-
const mockSetSelectedDevice = jest.fn();
|
|
145
|
-
useState.mockImplementationOnce((init) => [init, mockSetState]);
|
|
146
|
-
useState.mockImplementationOnce((init) => [init, mockSetState]);
|
|
147
|
-
useState.mockImplementationOnce((init) => [init, mockSetState]);
|
|
148
|
-
useState.mockImplementationOnce((init) => [data, mockSetState]);
|
|
149
|
-
useState.mockImplementationOnce((init) => [
|
|
150
|
-
{ id: 1, name: 'sensor' },
|
|
151
|
-
mockSetSelectedDevice,
|
|
152
|
-
]);
|
|
153
|
-
|
|
154
|
-
await act(async () => {
|
|
155
|
-
tree = renderer.create(wrapComponent(route));
|
|
156
|
-
});
|
|
157
|
-
const instance = tree.root;
|
|
158
|
-
const device = instance.findAllByType(Device);
|
|
159
|
-
expect(device).toHaveLength(2);
|
|
115
|
+
expect(device[0].props.isSelectDevice).toEqual(true);
|
|
160
116
|
|
|
161
117
|
act(() => {
|
|
162
118
|
device[0].props.onPress({ id: 1, name: 'sensor' });
|
|
163
119
|
});
|
|
164
|
-
expect(
|
|
120
|
+
expect(device[0].props.isSelectDevice).toEqual(false);
|
|
165
121
|
});
|
|
166
122
|
|
|
167
123
|
test('test onSnapItem', async () => {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
name: 'Device 1',
|
|
172
|
-
sensors: [
|
|
173
|
-
{ id: 1, name: 'sensor' },
|
|
174
|
-
{ id: 2, name: 'sensor' },
|
|
175
|
-
],
|
|
176
|
-
},
|
|
177
|
-
];
|
|
178
|
-
const listMenu = data.map((item, index) => ({
|
|
179
|
-
text: item.name,
|
|
180
|
-
station: item,
|
|
181
|
-
index: index,
|
|
182
|
-
}));
|
|
183
|
-
const mockSetIndexStation = jest.fn();
|
|
184
|
-
useState.mockImplementationOnce((init) => [listMenu, mockSetState]);
|
|
185
|
-
useState.mockImplementationOnce((init) => [init, mockSetState]);
|
|
186
|
-
useState.mockImplementationOnce((init) => [init, mockSetIndexStation]);
|
|
187
|
-
useState.mockImplementationOnce((init) => [data, mockSetState]);
|
|
124
|
+
axios.get.mockImplementation(async () => {
|
|
125
|
+
return response;
|
|
126
|
+
});
|
|
188
127
|
|
|
189
128
|
await act(async () => {
|
|
190
129
|
tree = renderer.create(wrapComponent(route));
|
|
@@ -195,10 +134,13 @@ describe('Test SelectSensorDevices', () => {
|
|
|
195
134
|
act(() => {
|
|
196
135
|
navBar.props.onSnapToItem({ id: 1, name: 'sensor' }, 0);
|
|
197
136
|
});
|
|
198
|
-
|
|
199
|
-
expect(mockSetIndexStation).toBeCalledWith(0);
|
|
137
|
+
expect(navBar.props.indexStation).toEqual(0);
|
|
200
138
|
});
|
|
139
|
+
|
|
201
140
|
test('test onPressClose user already has an automateId', async () => {
|
|
141
|
+
axios.get.mockImplementation(async () => {
|
|
142
|
+
return response;
|
|
143
|
+
});
|
|
202
144
|
await act(async () => {
|
|
203
145
|
tree = renderer.create(wrapComponent(route));
|
|
204
146
|
});
|
|
@@ -26,7 +26,7 @@ import Routes from '../../../utils/Route';
|
|
|
26
26
|
import { SCContext } from '../../../context';
|
|
27
27
|
import { Action } from '../../../context/actionType';
|
|
28
28
|
import { TESTID } from '../../../configs/Constants';
|
|
29
|
-
import Connecting from '../../../commons/Connecting
|
|
29
|
+
import Connecting from '../../../commons/Connecting';
|
|
30
30
|
|
|
31
31
|
const isIos = Platform.OS === 'ios';
|
|
32
32
|
const isAndroid = Platform.OS === 'android';
|
|
@@ -47,6 +47,7 @@ import { EmergencyCountdown } from './components/EmergencyCountdown';
|
|
|
47
47
|
import { SensorConnectStatusViewHeader } from './components/SensorConnectStatusViewHeader';
|
|
48
48
|
import { useDisconnectedDevice } from './hooks/useDisconnectedDevice';
|
|
49
49
|
import { Card } from '../../commons/CardShadow';
|
|
50
|
+
import PreventAccess from '../../commons/PreventAccess';
|
|
50
51
|
|
|
51
52
|
const DeviceDetail = ({ route }) => {
|
|
52
53
|
const t = useTranslations();
|
|
@@ -73,6 +74,8 @@ const DeviceDetail = ({ route }) => {
|
|
|
73
74
|
const { isOwner } = useIsOwnerOfUnit(unit?.user_id);
|
|
74
75
|
const [sensorName, setSensorName] = useState(sensor?.name);
|
|
75
76
|
const [lockShowing, acquireLockShowing, releaseLockShowing] = useBoolean();
|
|
77
|
+
const [showPreventAccess, setShowPreventAccess, setHidePreventAccess] =
|
|
78
|
+
useBoolean(false);
|
|
76
79
|
const [showWindDirection, setShowWindDirection] = useState(false);
|
|
77
80
|
const [loading, setLoading] = useState({
|
|
78
81
|
isConnected: true,
|
|
@@ -142,7 +145,9 @@ const DeviceDetail = ({ route }) => {
|
|
|
142
145
|
}, [currentUserId, unit]);
|
|
143
146
|
|
|
144
147
|
const fetchUnitDetail = useCallback(async () => {
|
|
145
|
-
const { success, data } = await axiosGet(API.UNIT.UNIT_DETAIL(unitId)
|
|
148
|
+
const { success, data } = await axiosGet(API.UNIT.UNIT_DETAIL(unitId), {
|
|
149
|
+
headers: { 'Cache-Control': 'no-cache' },
|
|
150
|
+
});
|
|
146
151
|
if (success) {
|
|
147
152
|
setUnit(data);
|
|
148
153
|
}
|
|
@@ -155,15 +160,17 @@ const DeviceDetail = ({ route }) => {
|
|
|
155
160
|
}, [fetchUnitDetail, unitId, unitData]);
|
|
156
161
|
|
|
157
162
|
const fetchSensorDetail = useCallback(async () => {
|
|
158
|
-
const { success, data } = await axiosGet(
|
|
163
|
+
const { success, data, resp_status } = await axiosGet(
|
|
159
164
|
API.SENSOR.SENSOR_DETAIL(sensorId)
|
|
160
165
|
);
|
|
161
166
|
if (success) {
|
|
162
167
|
setSensor(data);
|
|
163
168
|
setSensorName(data.name);
|
|
164
169
|
setStation(data.station);
|
|
170
|
+
} else if (resp_status === 404) {
|
|
171
|
+
setShowPreventAccess();
|
|
165
172
|
}
|
|
166
|
-
}, [sensorId]);
|
|
173
|
+
}, [sensorId, setShowPreventAccess]);
|
|
167
174
|
|
|
168
175
|
useEffect(() => {
|
|
169
176
|
if (sensorId && !sensorData) {
|
|
@@ -500,6 +507,7 @@ const DeviceDetail = ({ route }) => {
|
|
|
500
507
|
return isFlag;
|
|
501
508
|
}, [display]);
|
|
502
509
|
// replace isConnected=True to see template
|
|
510
|
+
|
|
503
511
|
const renderSensorConnected = () => {
|
|
504
512
|
return (
|
|
505
513
|
<SensorConnectStatusViewHeader
|
|
@@ -691,6 +699,11 @@ const DeviceDetail = ({ route }) => {
|
|
|
691
699
|
unit={unit}
|
|
692
700
|
station={sensor?.station}
|
|
693
701
|
/>
|
|
702
|
+
<PreventAccess
|
|
703
|
+
visible={showPreventAccess}
|
|
704
|
+
hidePreventAccess={setHidePreventAccess}
|
|
705
|
+
headerBodyText={'device'}
|
|
706
|
+
/>
|
|
694
707
|
</WrapHeaderScrollable>
|
|
695
708
|
{isShowEmergencyResolve && (
|
|
696
709
|
<BottomButtonView
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
watchNotificationData,
|
|
48
48
|
unwatchNotificationData,
|
|
49
49
|
} from '../../utils/Monitor';
|
|
50
|
+
import PreventAccess from '../../commons/PreventAccess';
|
|
50
51
|
|
|
51
52
|
const UnitDetail = ({ route }) => {
|
|
52
53
|
const t = useTranslations();
|
|
@@ -63,14 +64,17 @@ const UnitDetail = ({ route }) => {
|
|
|
63
64
|
|
|
64
65
|
const isFocused = useIsFocused();
|
|
65
66
|
const { stateData, setAction } = useContext(SCContext);
|
|
66
|
-
const { navigate } = useNavigation();
|
|
67
|
+
const { navigate, goBack } = useNavigation();
|
|
67
68
|
const RouterHardware = useCallback(
|
|
68
69
|
(routeHardware) => () => {
|
|
69
70
|
navigate(routeHardware);
|
|
70
71
|
},
|
|
71
72
|
[navigate]
|
|
72
73
|
);
|
|
73
|
-
|
|
74
|
+
|
|
75
|
+
useBlockBackAndroid(
|
|
76
|
+
isSuccessfullyConnected ? RouterHardware(Routes.Dashboard) : goBack
|
|
77
|
+
);
|
|
74
78
|
const user = useSCContextSelector((state) => state?.auth?.account?.user);
|
|
75
79
|
const isLavidaSource = useSCContextSelector(
|
|
76
80
|
(state) => state.app.isLavidaSource
|
|
@@ -84,6 +88,8 @@ const UnitDetail = ({ route }) => {
|
|
|
84
88
|
const [station, setStation] = useState({});
|
|
85
89
|
const [indexStation, setIndexStation] = useState(0);
|
|
86
90
|
const [showAdd, setShowAdd, setHideAdd] = useBoolean();
|
|
91
|
+
const [showPreventAccess, setShowPreventAccess, setHidePreventAccess] =
|
|
92
|
+
useBoolean(false);
|
|
87
93
|
const [isFullScreen, setIsFullScreen] = useState(false);
|
|
88
94
|
const [dataFullScreen, setDataFullScreen] = useState();
|
|
89
95
|
const appState = useRef(AppState.currentState);
|
|
@@ -122,16 +128,18 @@ const UnitDetail = ({ route }) => {
|
|
|
122
128
|
|
|
123
129
|
const fetchDetails = useCallback(async () => {
|
|
124
130
|
getAutomates();
|
|
125
|
-
const { success, data } = await axiosGet(
|
|
131
|
+
const { success, data, resp_status } = await axiosGet(
|
|
126
132
|
API.UNIT.UNIT_DETAIL(unitId),
|
|
127
|
-
{},
|
|
133
|
+
{ headers: { 'Cache-Control': 'no-cache' } },
|
|
128
134
|
true
|
|
129
135
|
);
|
|
130
136
|
if (success) {
|
|
131
137
|
prepareData(data);
|
|
132
138
|
setUnit(data);
|
|
139
|
+
} else if (resp_status === 404) {
|
|
140
|
+
setShowPreventAccess();
|
|
133
141
|
}
|
|
134
|
-
}, [
|
|
142
|
+
}, [getAutomates, unitId, prepareData, setShowPreventAccess]);
|
|
135
143
|
|
|
136
144
|
const getAutomates = useCallback(async () => {
|
|
137
145
|
await fetchWithCache(
|
|
@@ -395,6 +403,11 @@ const UnitDetail = ({ route }) => {
|
|
|
395
403
|
modalStyles={styles.modal}
|
|
396
404
|
onClose={onClose}
|
|
397
405
|
/>
|
|
406
|
+
<PreventAccess
|
|
407
|
+
visible={showPreventAccess}
|
|
408
|
+
hidePreventAccess={setHidePreventAccess}
|
|
409
|
+
headerBodyText={'unit'}
|
|
410
|
+
/>
|
|
398
411
|
</WrapParallaxScrollView>
|
|
399
412
|
);
|
|
400
413
|
};
|
|
@@ -96,7 +96,11 @@ describe('Test UnitDetail', () => {
|
|
|
96
96
|
await renderer.create(wrapComponent(route));
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
expect(axios.get).toHaveBeenCalledWith(detailUnitApiUrl, {
|
|
99
|
+
expect(axios.get).toHaveBeenCalledWith(detailUnitApiUrl, {
|
|
100
|
+
headers: {
|
|
101
|
+
'Cache-Control': 'no-cache',
|
|
102
|
+
},
|
|
103
|
+
});
|
|
100
104
|
});
|
|
101
105
|
|
|
102
106
|
test('fetch unit detail no network then load from cache', async () => {
|
|
@@ -118,7 +122,11 @@ describe('Test UnitDetail', () => {
|
|
|
118
122
|
await renderer.create(wrapComponent(route, account));
|
|
119
123
|
});
|
|
120
124
|
|
|
121
|
-
expect(axios.get).toHaveBeenCalledWith(detailUnitApiUrl, {
|
|
125
|
+
expect(axios.get).toHaveBeenCalledWith(detailUnitApiUrl, {
|
|
126
|
+
headers: {
|
|
127
|
+
'Cache-Control': 'no-cache',
|
|
128
|
+
},
|
|
129
|
+
});
|
|
122
130
|
});
|
|
123
131
|
|
|
124
132
|
test('fetch unit detail no network and fail load from cache', async () => {
|
|
@@ -129,7 +137,11 @@ describe('Test UnitDetail', () => {
|
|
|
129
137
|
await renderer.create(wrapComponent(route, account));
|
|
130
138
|
});
|
|
131
139
|
|
|
132
|
-
expect(axios.get).toHaveBeenCalledWith(detailUnitApiUrl, {
|
|
140
|
+
expect(axios.get).toHaveBeenCalledWith(detailUnitApiUrl, {
|
|
141
|
+
headers: {
|
|
142
|
+
'Cache-Control': 'no-cache',
|
|
143
|
+
},
|
|
144
|
+
});
|
|
133
145
|
});
|
|
134
146
|
|
|
135
147
|
test('fetch unit summary empty', async () => {
|
|
@@ -193,7 +205,11 @@ describe('Test UnitDetail', () => {
|
|
|
193
205
|
await renderer.create(wrapComponent(route, account));
|
|
194
206
|
});
|
|
195
207
|
|
|
196
|
-
expect(axios.get).not.toHaveBeenCalledWith(summaryUnitApiUrl, {
|
|
208
|
+
expect(axios.get).not.toHaveBeenCalledWith(summaryUnitApiUrl, {
|
|
209
|
+
headers: {
|
|
210
|
+
'Cache-Control': 'no-cache',
|
|
211
|
+
},
|
|
212
|
+
});
|
|
197
213
|
});
|
|
198
214
|
|
|
199
215
|
test('fetch unit detail when refresh', async () => {
|
|
@@ -206,7 +222,11 @@ describe('Test UnitDetail', () => {
|
|
|
206
222
|
await act(async () => {
|
|
207
223
|
refreshControl.props.onRefresh();
|
|
208
224
|
});
|
|
209
|
-
expect(axios.get).toHaveBeenCalledWith(detailUnitApiUrl, {
|
|
225
|
+
expect(axios.get).toHaveBeenCalledWith(detailUnitApiUrl, {
|
|
226
|
+
headers: {
|
|
227
|
+
'Cache-Control': 'no-cache',
|
|
228
|
+
},
|
|
229
|
+
});
|
|
210
230
|
});
|
|
211
231
|
|
|
212
232
|
test('when unit has google home action then connect to google home', async () => {
|
|
@@ -207,7 +207,7 @@ const ThreePhasePowerConsumption = memo(({ summaryDetail }) => {
|
|
|
207
207
|
params.append('date_from', moment(startDate).format('YYYY-MM-DD'));
|
|
208
208
|
params.append('date_to', moment(endDate).format('YYYY-MM-DD'));
|
|
209
209
|
const { success, data } = await axiosGet(
|
|
210
|
-
API.VALUE_CONSUME.DISPLAY_HISTORY,
|
|
210
|
+
API.VALUE_CONSUME.DISPLAY_HISTORY(),
|
|
211
211
|
{
|
|
212
212
|
params,
|
|
213
213
|
}
|
|
@@ -982,5 +982,8 @@
|
|
|
982
982
|
"saving_contact_successful" : "Saving contact successful",
|
|
983
983
|
"maxmium_contacts": "Maxmium contacts",
|
|
984
984
|
"enter_password": "Enter Your Password",
|
|
985
|
-
"you_need_to_enter_password": "You need to enter password to perform this action"
|
|
985
|
+
"you_need_to_enter_password": "You need to enter password to perform this action",
|
|
986
|
+
"note": "Note",
|
|
987
|
+
"back": "Back",
|
|
988
|
+
"This {name} was removed!" : "This {name} was removed!"
|
|
986
989
|
}
|
|
@@ -982,5 +982,8 @@
|
|
|
982
982
|
"search": "Tìm kiếm",
|
|
983
983
|
"connect_home_wifi" : "Kết nối Wifi của gia đình",
|
|
984
984
|
"saving_contact_successful" : "Lưu liên hệ thành công",
|
|
985
|
-
"maxmium_contacts": "Địa chỉ liên hệ tối đa"
|
|
985
|
+
"maxmium_contacts": "Địa chỉ liên hệ tối đa",
|
|
986
|
+
"note": "Lưu ý ",
|
|
987
|
+
"back": "Quay lại",
|
|
988
|
+
"This {name} was removed!" : "{Name} này đã bị xóa!"
|
|
986
989
|
}
|