@eohjsc/react-native-smart-city 0.7.18 → 0.7.20

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 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.7.18",
4
+ "version": "0.7.20",
5
5
  "description": "TODO",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -64,7 +64,9 @@ const SelectGateway = ({
64
64
  const { success, data } = await axiosGet(
65
65
  API.CHIP.GET_CHIPS_FROM_UNIT(unitId),
66
66
  {
67
- type,
67
+ params: {
68
+ type,
69
+ },
68
70
  }
69
71
  );
70
72
  if (success) {
@@ -30,12 +30,18 @@ import styles from './ScanDeviceLocalStyles';
30
30
 
31
31
  let zeroconf;
32
32
 
33
- const DeviceItem = ({ item, setSelectedDevice, selectedDevice }) => {
33
+ const DeviceItem = ({
34
+ item,
35
+ selectedDevice,
36
+ setSelectedDevice,
37
+ fetchDeviceInfo,
38
+ }) => {
34
39
  const handleSelectDevice = useCallback(
35
40
  (device) => {
36
41
  setSelectedDevice(device);
42
+ fetchDeviceInfo(device?.host);
37
43
  },
38
- [setSelectedDevice]
44
+ [setSelectedDevice, fetchDeviceInfo]
39
45
  );
40
46
 
41
47
  return (
@@ -69,9 +75,8 @@ const ScanDeviceLocal = ({ route }) => {
69
75
  const [selectedDevice, setSelectedDevice] = useState();
70
76
  const [isShowLoading, setIsShowLoading] = useState(false);
71
77
 
72
- const { deviceInfo, fetchDeviceInfo, sendConfigToDevice } = useConnectDevice(
73
- selectedDevice?.host
74
- );
78
+ const { deviceInfo, fetchDeviceInfo, sendConfigToDevice } =
79
+ useConnectDevice();
75
80
 
76
81
  const code = useMemo(() => {
77
82
  return uuidv4();
@@ -107,7 +112,7 @@ const ScanDeviceLocal = ({ route }) => {
107
112
 
108
113
  const onConnectingDevice = useCallback(async () => {
109
114
  setIsShowLoading(true);
110
- const success = await sendConfigToDevice({
115
+ const success = await sendConfigToDevice(selectedDevice?.host, {
111
116
  token: code,
112
117
  host: detailChipQr?.host,
113
118
  port: detailChipQr?.port,
@@ -126,6 +131,7 @@ const ScanDeviceLocal = ({ route }) => {
126
131
  code,
127
132
  subUnit,
128
133
  deviceInfo?.type,
134
+ selectedDevice?.host,
129
135
  detailChipQr,
130
136
  t,
131
137
  sendConfigToDevice,
@@ -138,12 +144,13 @@ const ScanDeviceLocal = ({ route }) => {
138
144
  return (
139
145
  <DeviceItem
140
146
  item={item}
141
- setSelectedDevice={setSelectedDevice}
142
147
  selectedDevice={selectedDevice}
148
+ setSelectedDevice={setSelectedDevice}
149
+ fetchDeviceInfo={fetchDeviceInfo}
143
150
  />
144
151
  );
145
152
  },
146
- [selectedDevice, setSelectedDevice]
153
+ [selectedDevice, setSelectedDevice, fetchDeviceInfo]
147
154
  );
148
155
 
149
156
  const handleGoBack = useCallback(() => {
@@ -202,12 +209,6 @@ const ScanDeviceLocal = ({ route }) => {
202
209
  };
203
210
  }, []);
204
211
 
205
- useEffect(() => {
206
- if (selectedDevice) {
207
- fetchDeviceInfo();
208
- }
209
- }, [selectedDevice, fetchDeviceInfo]);
210
-
211
212
  useEffect(() => {
212
213
  if (deviceInfo) {
213
214
  fetchChipQrDetail(deviceInfo?.secret);
@@ -93,7 +93,7 @@ describe('test ScanDeviceLocal', () => {
93
93
  await viewButtonBottom.props.onRightClick();
94
94
  });
95
95
 
96
- expect(deviceApi.baseURL).toBe(`http://${resolvedDevice.host}/api`);
96
+ expect(deviceApi.getBaseURL()).toBe(`http://${resolvedDevice.host}/api`);
97
97
 
98
98
  expect(zeroconfMock.on).toHaveBeenCalledWith(
99
99
  'resolved',
@@ -259,6 +259,56 @@ describe('test ScanDeviceLocal', () => {
259
259
 
260
260
  const viewButtonBottom = instance.findByType(ViewButtonBottom);
261
261
  expect(viewButtonBottom.props.rightDisabled).toBeTruthy();
262
+ expect(spyToastError).toHaveBeenCalledWith(
263
+ getTranslate('en', 'cannot_fetch_data_from_device')
264
+ );
265
+ });
266
+
267
+ it('test disable connect button when fetch chip qr info error', async () => {
268
+ const resolvedDevice = {
269
+ host: 'host-1.lan',
270
+ name: 'Device 1',
271
+ type: 'end_device',
272
+ };
273
+
274
+ mock.onGet(API.DEV_MODE.CHIP_QR_CODE.DETAIL_BY_SECRET()).reply(400);
275
+ mockDeviceApi.onGet(DEVICEAPI.BOARD.DETAIL()).reply(200, {
276
+ name: resolvedDevice.name,
277
+ type: resolvedDevice.type,
278
+ secret: '123456',
279
+ });
280
+
281
+ const route = { params: { unit: 1 } };
282
+ await act(async () => {
283
+ tree = await renderer.create(wrapComponent(route));
284
+ });
285
+ const instance = tree.root;
286
+
287
+ const resolvedCallback = zeroconfMock.on.mock.calls.find(
288
+ (call) => call[0] === 'resolved'
289
+ )[1];
290
+ await act(async () => {
291
+ await resolvedCallback(resolvedDevice);
292
+ });
293
+
294
+ const flatList = instance.findByType(FlatList);
295
+ expect(flatList.props.data).toEqual([resolvedDevice]);
296
+ await act(async () => {
297
+ await flatList.props.renderItem({ item: resolvedDevice });
298
+ });
299
+
300
+ // Select first device
301
+ const touchable = flatList.findAllByType(TouchableOpacity);
302
+ expect(touchable).toHaveLength(1);
303
+ await act(async () => {
304
+ await touchable[0].props.onPress();
305
+ });
306
+
307
+ const viewButtonBottom = instance.findByType(ViewButtonBottom);
308
+ expect(viewButtonBottom.props.rightDisabled).toBeTruthy();
309
+ expect(spyToastError).toHaveBeenCalledWith(
310
+ getTranslate('en', 'chip_qr_not_found')
311
+ );
262
312
  });
263
313
 
264
314
  it('test handle connecting device error', async () => {
@@ -1,7 +1,9 @@
1
1
  import { create } from 'apisauce';
2
- import { useState, useCallback, useEffect } from 'react';
2
+ import { useState, useCallback } from 'react';
3
3
 
4
4
  import { API } from '../configs/API';
5
+ import { ToastBottomHelper } from '../../../utils/Utils';
6
+ import t from '../../../hooks/Common/useTranslations';
5
7
 
6
8
  export const api = create({
7
9
  timeout: 10000,
@@ -10,15 +12,11 @@ export const api = create({
10
12
  },
11
13
  });
12
14
 
13
- export const useConnectDevice = (host) => {
15
+ export const useConnectDevice = () => {
14
16
  const [deviceInfo, setDeviceInfo] = useState();
15
17
  const [listWiFiDevice, setListWiFiDevice] = useState([]);
16
18
  const [lastError, setLastError] = useState();
17
19
 
18
- useEffect(() => {
19
- api.baseURL = `http://${host}/api`;
20
- }, [host]);
21
-
22
20
  const parseResponse = (response, setInfo) => {
23
21
  if (response.data?.status === 'error') {
24
22
  setLastError(response.data?.message);
@@ -26,23 +24,27 @@ export const useConnectDevice = (host) => {
26
24
  }
27
25
  if (!response.ok) {
28
26
  setLastError(response.problem);
27
+ setInfo && ToastBottomHelper.error(t('cannot_fetch_data_from_device'));
29
28
  return false;
30
29
  }
31
30
  setInfo && setInfo(response.data);
32
31
  return true;
33
32
  };
34
33
 
35
- const fetchDeviceInfo = useCallback(async () => {
34
+ const fetchDeviceInfo = useCallback(async (host) => {
35
+ api.setBaseURL(`http://${host}/api`);
36
36
  const response = await api.get(API.BOARD.DETAIL());
37
37
  return parseResponse(response, setDeviceInfo);
38
38
  }, []);
39
39
 
40
- const fetchListWiFiDevice = useCallback(async () => {
40
+ const fetchListWiFiDevice = useCallback(async (host) => {
41
+ api.setBaseURL(`http://${host}/api`);
41
42
  const response = await api.get(API.BOARD.SCAN_WIFI());
42
43
  return parseResponse(response, setListWiFiDevice);
43
44
  }, []);
44
45
 
45
- const sendConfigToDevice = useCallback(async (config) => {
46
+ const sendConfigToDevice = useCallback(async (host, config) => {
47
+ api.setBaseURL(`http://${host}/api`);
46
48
  const response = await api.post(API.BOARD.CONFIG(), config);
47
49
  return parseResponse(response);
48
50
  }, []);
@@ -179,6 +179,34 @@ describe('Test DeviceInternalDetail', () => {
179
179
  });
180
180
  });
181
181
 
182
+ it('test render DeviceInternalDetail onPress more and onPress Delete internal failure', async () => {
183
+ await act(async () => {
184
+ tree = await create(wrapComponent());
185
+ });
186
+ const instance = tree.root;
187
+ const detail = instance?.findByType(Detail);
188
+ await headerCustomOnPressMore(detail);
189
+ const menuActionMore = detail?.findByType(MenuActionMore);
190
+ expect(menuActionMore.props.isVisible).toEqual(true);
191
+ await act(async () => {
192
+ menuActionMore.props.listMenuItem[1].doAction();
193
+ });
194
+ const modal = instance.findByType(ModalPopupCT);
195
+
196
+ mock.onDelete(API.DEV_MODE.ARDUINO.DEVICE_DETAIL(1, 1)).reply(400);
197
+ await act(async () => {
198
+ await modal.props.onPressConfirm();
199
+ });
200
+ expect(global.mockedPop).not.toHaveBeenCalled();
201
+ expect(Toast.show).toHaveBeenCalledWith({
202
+ position: 'bottom',
203
+ text1: 'CLIENT_ERROR',
204
+ text2: undefined,
205
+ type: 'error',
206
+ visibilityTime: 1000,
207
+ });
208
+ });
209
+
182
210
  it('test render DeviceInternalDetail onPress TabPanel config write', async () => {
183
211
  mock
184
212
  .onGet(API.DEV_MODE.ARDUINO.CONFIG_PINS(1, 1))
@@ -137,7 +137,7 @@ describe('Test DeviceModbusDetail', () => {
137
137
  await flushPromises();
138
138
 
139
139
  const modal = instance.findByType(ModalPopupCT);
140
- mock.onDelete(API.DEV_MODE.ZIGBEE.DEVICE_DETAIL(1, 1)).reply(200);
140
+ mock.onDelete(API.DEV_MODE.MODBUS.DEVICE_DETAIL(1, 1)).reply(200);
141
141
  await act(async () => {
142
142
  await modal.props.onPressConfirm();
143
143
  });
@@ -85,6 +85,25 @@ describe('Test Gateway screen', () => {
85
85
  expect(flatList.props.data).toEqual([{ id: 1, name: 'device 1' }]);
86
86
  });
87
87
 
88
+ it('render Gateway onLoadMore', async () => {
89
+ await act(async () => {
90
+ tree = await create(wrapComponent());
91
+ });
92
+ const instance = tree.root;
93
+ const flatList = instance.findByType(FlatList);
94
+ expect(flatList.props.data).toEqual([]);
95
+ mock
96
+ .onGet(API.DEV_MODE.GATEWAY.LIST())
97
+ .reply(200, { results: [{ id: 1, name: 'device 1' }] });
98
+ await act(async () => {
99
+ await flatList.props.onMomentumScrollBegin();
100
+ });
101
+ await act(async () => {
102
+ await flatList.props.onEndReached();
103
+ });
104
+ expect(flatList.props.data).toEqual([{ id: 1, name: 'device 1' }]);
105
+ });
106
+
88
107
  it('Test render empty', async () => {
89
108
  await act(async () => {
90
109
  tree = await create(wrapComponent());
@@ -146,26 +146,29 @@ export const useGateway = () => {
146
146
  isModbus,
147
147
  numberGoBack = 1
148
148
  ) => {
149
- let success;
149
+ let response;
150
150
  if (isInternal) {
151
- success = await axiosDelete(
151
+ response = await axiosDelete(
152
152
  API.DEV_MODE.ARDUINO.DEVICE_DETAIL(gatewayId, deviceId)
153
153
  );
154
- success && setGatewayDevices((prev) => ({ ...prev, internal: [] }));
154
+ response.success &&
155
+ setGatewayDevices((prev) => ({ ...prev, internal: [] }));
155
156
  }
156
157
  if (isZigbee) {
157
- success = await axiosDelete(
158
+ response = await axiosDelete(
158
159
  API.DEV_MODE.ZIGBEE.DEVICE_DETAIL(gatewayId, deviceId)
159
160
  );
160
- success && setGatewayDevices((prev) => ({ ...prev, zigbee: [] }));
161
+ response.success &&
162
+ setGatewayDevices((prev) => ({ ...prev, zigbee: [] }));
161
163
  }
162
164
  if (isModbus) {
163
- success = await axiosDelete(
165
+ response = await axiosDelete(
164
166
  API.DEV_MODE.MODBUS.DEVICE_DETAIL(gatewayId, deviceId)
165
167
  );
166
- success && setGatewayDevices((prev) => ({ ...prev, modbus: [] }));
168
+ response.success &&
169
+ setGatewayDevices((prev) => ({ ...prev, modbus: [] }));
167
170
  }
168
- if (success) {
171
+ if (response.success) {
169
172
  ToastBottomHelper.success(t('delete_successfully'));
170
173
  navigation.pop(numberGoBack);
171
174
  }
@@ -259,7 +262,11 @@ export const useGateway = () => {
259
262
  params: { secret },
260
263
  }
261
264
  );
262
- success && setDetailChipQr(data);
265
+ if (success) {
266
+ setDetailChipQr(data);
267
+ } else {
268
+ ToastBottomHelper.error(t('chip_qr_not_found'));
269
+ }
263
270
  }, []);
264
271
 
265
272
  return {
@@ -190,6 +190,7 @@ export default StyleSheet.create({
190
190
  width: '100%',
191
191
  backgroundColor: Colors.White,
192
192
  borderRadius: 5,
193
+ maxHeight: 500,
193
194
  },
194
195
  localControl: {
195
196
  flexDirection: 'row',
@@ -158,17 +158,24 @@ describe('Test ScriptDetail', () => {
158
158
  tree = await create(wrapComponent(route));
159
159
  });
160
160
  const instance = tree.root;
161
+ const icon = instance.find(
162
+ (el) => el.props.accessibilityLabel === AccessibilityLabel.ICON_MORE
163
+ );
164
+
165
+ await act(async () => {
166
+ await icon.props.onPress();
167
+ });
161
168
  const menu = instance.find(
162
169
  (el) =>
163
170
  el.props.accessibilityLabel === AccessibilityLabel.MENU_POPPER_MORE
164
171
  );
172
+ expect(menu.props.isVisible).toBeTruthy();
165
173
  const alertAction = instance.findAllByType(AlertAction)[0];
166
174
  const rename = menu.props.listMenuItem[0];
167
175
 
168
176
  await act(async () => {
169
177
  await menu.props.onItemClick(rename);
170
178
  });
171
- expect(menu.props.isVisible).toBeFalsy();
172
179
  expect(alertAction.props.visible).toBeTruthy();
173
180
 
174
181
  const textInput = instance.findByType(_TextInput);
@@ -5,7 +5,14 @@ import React, {
5
5
  useRef,
6
6
  useState,
7
7
  } from 'react';
8
- import { Image, Platform, Switch, TouchableOpacity, View } from 'react-native';
8
+ import {
9
+ Image,
10
+ Platform,
11
+ ScrollView,
12
+ Switch,
13
+ TouchableOpacity,
14
+ View,
15
+ } from 'react-native';
9
16
  import { PopoverMode } from 'react-native-popover-view';
10
17
  import { IconFill, IconOutline } from '@ant-design/icons-react-native';
11
18
  import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
@@ -343,7 +350,9 @@ const ScriptDetail = ({ route }) => {
343
350
  const { success, data: listChip } = await axiosGet(
344
351
  API.DEV_MODE.GATEWAY.SHARED(),
345
352
  {
346
- unit: unit_id,
353
+ params: {
354
+ unit: unit_id,
355
+ },
347
356
  },
348
357
  true
349
358
  );
@@ -548,46 +557,48 @@ const ScriptDetail = ({ route }) => {
548
557
  onBackdropPress={onCloseLocalControl}
549
558
  >
550
559
  <View key={'localControl'} style={styles.popoverStyle}>
551
- <TouchableOpacity
552
- style={styles.textDisable}
553
- key={'listChip'}
554
- onPress={() => {
555
- onPressSelectChip(false, {
556
- id: local_control.chip_id_local_control,
557
- name: local_control.chip_local_control,
558
- });
559
- }}
560
- accessibilityLabel={
561
- AccessibilityLabel.AUTOMATE_DISABLE_LOCAL_CONTROL
562
- }
563
- >
564
- <Text>{t('disable')}</Text>
565
- {!local_control.is_local_control && (
566
- <IconOutline style={styles.checked} name={'check'} size={20} />
567
- )}
568
- </TouchableOpacity>
569
- {listChipShared.map((item, index) => (
560
+ <ScrollView>
570
561
  <TouchableOpacity
571
- key={'listChip' + index} // Add key fix warning
572
- style={styles.listChip}
562
+ style={styles.textDisable}
563
+ key={'listChip'}
573
564
  onPress={() => {
574
- onPressSelectChip(true, item);
565
+ onPressSelectChip(false, {
566
+ id: local_control.chip_id_local_control,
567
+ name: local_control.chip_local_control,
568
+ });
575
569
  }}
576
570
  accessibilityLabel={
577
- AccessibilityLabel.AUTOMATE_ENABLE_LOCAL_CONTROL
571
+ AccessibilityLabel.AUTOMATE_DISABLE_LOCAL_CONTROL
578
572
  }
579
573
  >
580
- <Text>{item.name}</Text>
581
- {local_control.chip_id_local_control === item.id &&
582
- local_control.is_local_control && (
583
- <IconOutline
584
- style={styles.checked}
585
- name={'check'}
586
- size={20}
587
- />
588
- )}
574
+ <Text>{t('disable')}</Text>
575
+ {!local_control.is_local_control && (
576
+ <IconOutline style={styles.checked} name={'check'} size={20} />
577
+ )}
589
578
  </TouchableOpacity>
590
- ))}
579
+ {listChipShared.map((item, index) => (
580
+ <TouchableOpacity
581
+ key={'listChip' + index} // Add key fix warning
582
+ style={styles.listChip}
583
+ onPress={() => {
584
+ onPressSelectChip(true, item);
585
+ }}
586
+ accessibilityLabel={
587
+ AccessibilityLabel.AUTOMATE_ENABLE_LOCAL_CONTROL
588
+ }
589
+ >
590
+ <Text>{item.name}</Text>
591
+ {local_control.chip_id_local_control === item.id &&
592
+ local_control.is_local_control && (
593
+ <IconOutline
594
+ style={styles.checked}
595
+ name={'check'}
596
+ size={20}
597
+ />
598
+ )}
599
+ </TouchableOpacity>
600
+ ))}
601
+ </ScrollView>
591
602
  </View>
592
603
  </ModalCustom>
593
604
  </View>
@@ -1536,4 +1536,7 @@ export default {
1536
1536
  'Press and hold the number and drag to rearrange the order of the Sub-Units',
1537
1537
  rearrange_sub_unit: 'Rearrange Sub-Unit',
1538
1538
  updated_sub_unit_order: 'Updated Sub-Units order successfully!',
1539
+ cannot_fetch_data_from_device:
1540
+ 'Cannot fetch data from device, Please try again',
1541
+ chip_qr_not_found: 'Chip not found in the system',
1539
1542
  };
@@ -1542,4 +1542,7 @@ export default {
1542
1542
  'Nhấn giữ vào số thứ tự và di chuyển để sắp xếp lại thứ tự các khu vực',
1543
1543
  rearrange_sub_unit: 'Sắp xếp lại các khu vực',
1544
1544
  updated_sub_unit_order: 'Đã cập nhật thứ tự các khu vực thành công!',
1545
+ cannot_fetch_data_from_device:
1546
+ 'Không thể lấy dữ liệu từ thiết bị, Vui lòng thử lại',
1547
+ chip_qr_not_found: 'Không tìm thấy chip trong hệ thống',
1545
1548
  };