@eohjsc/react-native-smart-city 0.3.92 → 0.3.94

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.
Files changed (87) hide show
  1. package/package.json +5 -1
  2. package/src/commons/ActionGroup/SliderRangeTemplate.js +2 -2
  3. package/src/commons/AlertAction/index.js +5 -0
  4. package/src/commons/BottomButtonView/index.js +22 -4
  5. package/src/commons/Button/index.js +5 -0
  6. package/src/commons/Device/ConnectedViewHeader.js +0 -1
  7. package/src/commons/Device/Emergency/EmergencyDetail.js +4 -2
  8. package/src/commons/Device/Emergency/__test__/EmergencyDetail.test.js +4 -2
  9. package/src/commons/Device/ProgressBar/index.js +3 -1
  10. package/src/commons/Device/ProgressBar/styles.js +1 -4
  11. package/src/commons/Device/WindSpeed/Anemometer/index.js +1 -1
  12. package/src/commons/Header/HeaderCustom.js +8 -18
  13. package/src/commons/SubUnit/OneTap/ItemOneTap.js +84 -129
  14. package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +74 -39
  15. package/src/commons/SubUnit/OneTap/index.js +24 -4
  16. package/src/commons/ViewButtonBottom/index.js +32 -4
  17. package/src/configs/API.js +4 -0
  18. package/src/configs/AccessibilityLabel.js +1 -0
  19. package/src/configs/BLE.js +1 -0
  20. package/src/context/actionType.ts +2 -1
  21. package/src/context/mockStore.ts +1 -0
  22. package/src/context/reducer.ts +12 -1
  23. package/src/hooks/Explore/useKeyboardAnimated.js +10 -4
  24. package/src/hooks/IoT/useBluetoothConnection.js +14 -26
  25. package/src/hooks/useMqtt.js +95 -0
  26. package/src/iot/Monitor.js +2 -1
  27. package/src/iot/RemoteControl/Bluetooth.js +56 -19
  28. package/src/iot/RemoteControl/__test__/Bluetooth.test.js +140 -0
  29. package/src/iot/mqtt.js +233 -0
  30. package/src/navigations/UnitStack.js +11 -3
  31. package/src/screens/AddLocationMaps/index.js +18 -16
  32. package/src/screens/AddLocationMaps/indexStyle.js +3 -0
  33. package/src/screens/AddNewGateway/ConnectingDevice.js +7 -0
  34. package/src/screens/AddNewGateway/RenameNewDevices.js +2 -2
  35. package/src/screens/AddNewGateway/SelectDeviceType.js +67 -9
  36. package/src/screens/AddNewGateway/__test__/ConnectingZigbeeDevice.test.js +35 -0
  37. package/src/screens/AddNewGateway/__test__/SelectDeviceType.test.js +183 -29
  38. package/src/screens/Automate/AddNewAction/NewActionWrapper.js +3 -6
  39. package/src/screens/Automate/AddNewAction/SetupConfigCondition.js +29 -29
  40. package/src/screens/Automate/AddNewAction/__test__/SetupSensor.test.js +45 -39
  41. package/src/screens/Automate/AddNewAutoSmart/AddAutomationTypeSmart.js +25 -0
  42. package/src/screens/{AddNewAutoSmart/index.js → Automate/AddNewAutoSmart/AddTypeSmart.js} +16 -51
  43. package/src/screens/Automate/AddNewAutoSmart/AddUnknownTypeSmart.js +29 -0
  44. package/src/screens/{AddNewAutoSmart → Automate/AddNewAutoSmart}/__test__/AddNewAutoSmart.test.js +11 -11
  45. package/src/screens/{AddNewAutoSmart → Automate/AddNewAutoSmart}/styles/AddNewAutoSmartStyles.js +1 -1
  46. package/src/screens/Automate/Components/InputNameStyles.js +1 -1
  47. package/src/screens/Automate/EditActionsList/__tests__/index.test.js +11 -25
  48. package/src/screens/Automate/EditActionsList/index.js +32 -33
  49. package/src/screens/Automate/MultiUnits.js +29 -19
  50. package/src/screens/Automate/ScriptDetail/__test__/index.test.js +78 -5
  51. package/src/screens/Automate/ScriptDetail/index.js +38 -10
  52. package/src/screens/Automate/Styles/MultiUnitsStyles.js +1 -1
  53. package/src/screens/Automate/__test__/MultiUnits.test.js +64 -7
  54. package/src/screens/Automate/__test__/index.test.js +45 -11
  55. package/src/screens/Automate/index.js +53 -38
  56. package/src/screens/Device/__test__/detail.test.js +1 -1
  57. package/src/screens/Device/__test__/mqttDetail.test.js +599 -0
  58. package/src/screens/Device/components/SensorDisplayItem.js +1 -7
  59. package/src/screens/Device/detail.js +64 -30
  60. package/src/screens/Device/hooks/__test__/useEvaluateValue.test.js +24 -1
  61. package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +13 -3
  62. package/src/screens/SelectUnit/__test__/index.test.js +8 -13
  63. package/src/screens/Sharing/InfoMemberUnit.js +2 -2
  64. package/src/screens/Sharing/MemberList.js +28 -7
  65. package/src/screens/Sharing/__test__/InfoMemberUnit.test.js +32 -18
  66. package/src/screens/Sharing/__test__/MemberList.test.js +37 -4
  67. package/src/screens/SmartAccount/__test__/SmartAccount.test.js +8 -4
  68. package/src/screens/SmartAccount/index.js +8 -9
  69. package/src/screens/SmartAccount/style.js +8 -7
  70. package/src/screens/Unit/AddMenu.js +42 -19
  71. package/src/screens/Unit/Detail.js +4 -19
  72. package/src/screens/Unit/Summaries.js +6 -17
  73. package/src/screens/Unit/__test__/AddMenu.test.js +68 -15
  74. package/src/screens/Unit/__test__/Summaries.test.js +2 -2
  75. package/src/screens/Unit/components/AutomateScript/index.js +1 -1
  76. package/src/utils/FactoryGateway.js +525 -0
  77. package/src/utils/I18n/translations/en.js +1409 -0
  78. package/src/utils/I18n/translations/vi.js +1411 -0
  79. package/src/utils/I18n/translations.ts +2 -2
  80. package/src/utils/Permission/backend.js +7 -0
  81. package/src/utils/Route/index.js +2 -1
  82. package/src/utils/Utils.js +11 -0
  83. package/src/commons/Device/SensorConnectedStatus.js +0 -56
  84. package/src/commons/Device/__test__/SensorConnectedStatus.test.js +0 -29
  85. package/src/screens/Sharing/__test__/MemberList2.test.js +0 -74
  86. package/src/utils/I18n/translations/en.json +0 -1138
  87. package/src/utils/I18n/translations/vi.json +0 -1136
@@ -16,11 +16,13 @@ import Routes from '../../../../utils/Route';
16
16
  import api from '../../../../utils/Apis/axios';
17
17
  import { API } from '../../../../configs';
18
18
  import { useNavigation } from '@react-navigation/native';
19
+ import { ToastBottomHelper } from '../../../../utils/Utils';
20
+ import { getTranslate } from '../../../../utils/I18n';
19
21
 
20
22
  const mock = new MockAdapter(api.axiosInstance);
21
23
 
22
- const wrapComponent = (data) => (
23
- <SCProvider initState={mockSCStore({})}>
24
+ const wrapComponent = (data, storeData = {}) => (
25
+ <SCProvider initState={mockSCStore(storeData)}>
24
26
  <SubUnitAutomate {...data} />
25
27
  </SCProvider>
26
28
  );
@@ -33,34 +35,36 @@ jest.mock('react-redux', () => {
33
35
  });
34
36
 
35
37
  let tree;
36
- let data = {
37
- isOwner: true,
38
- listAutomate: [
39
- {
40
- text: 'Scenario',
41
- data: [
42
- {
43
- id: 1,
44
- user: 6,
45
- type: 'one_tap',
46
- activate_at: '2021-09-17T05:30:00Z',
47
- script: {
48
- name: 'Joshua Ray',
49
- icon: '',
50
- icon_kit: '',
51
- },
52
- },
53
- ],
54
- type: AUTOMATE_TABS.SCENARIO,
55
- },
56
- ],
57
- };
38
+ let data;
58
39
 
59
40
  describe('test Item', () => {
60
41
  const mockedNavigate = useNavigation().navigate;
61
42
  beforeEach(() => {
62
43
  mockedNavigate.mockClear();
44
+ data = {
45
+ isOwner: true,
46
+ listAutomate: [
47
+ {
48
+ text: 'Scenario',
49
+ data: [
50
+ {
51
+ id: 1,
52
+ user: 6,
53
+ type: 'one_tap',
54
+ activate_at: '2021-09-17T05:30:00Z',
55
+ script: {
56
+ name: 'Joshua Ray',
57
+ icon: '',
58
+ icon_kit: '',
59
+ },
60
+ },
61
+ ],
62
+ type: AUTOMATE_TABS.SCENARIO,
63
+ },
64
+ ],
65
+ };
63
66
  });
67
+
64
68
  it('render SubUnitAutomate isOwner and handleOnAddNew', async () => {
65
69
  await act(async () => {
66
70
  tree = await create(wrapComponent(data));
@@ -78,7 +82,7 @@ describe('test Item', () => {
78
82
  await act(async () => {
79
83
  await item[0].props.onPress();
80
84
  });
81
- expect(mockedNavigate).toHaveBeenCalledWith(Routes.AddNewOneTap, {
85
+ expect(mockedNavigate).toHaveBeenCalledWith(Routes.ScenarioName, {
82
86
  automate: {
83
87
  type: 'one_tap',
84
88
  unit: undefined,
@@ -97,13 +101,8 @@ describe('test Item', () => {
97
101
  await goDetail[0].props.onPress();
98
102
  });
99
103
  expect(mockedNavigate).toHaveBeenCalledWith(Routes.ScriptDetail, {
100
- havePermission: true,
101
104
  id: 1,
102
- name: 'Joshua Ray',
103
- type: 'one_tap',
104
- unit: undefined,
105
- textCondition: null,
106
- automate: data.listAutomate[0].data[0],
105
+ preAutomate: data.listAutomate[0].data[0],
107
106
  });
108
107
 
109
108
  const handleScriptAction = instance.findAll(
@@ -189,15 +188,11 @@ describe('test Item', () => {
189
188
  await goDetail[0].props.onPress();
190
189
  });
191
190
  expect(mockedNavigate).toHaveBeenCalledWith(Routes.ScriptDetail, {
192
- havePermission: false,
193
191
  id: 1,
194
- name: 'Rain',
195
- type: 'value_change',
196
- unit: undefined,
197
- textCondition: 'Temperature higher than 29',
198
- automate: data.listAutomate[0].data[0],
192
+ preAutomate: data.listAutomate[0].data[0],
199
193
  });
200
194
  });
195
+
201
196
  it('render SubUnitAutomate script schedule and handleOnAddNew item automate', async () => {
202
197
  data.isOwner = false;
203
198
  data.type = 'schedule';
@@ -237,8 +232,8 @@ describe('test Item', () => {
237
232
  await act(async () => {
238
233
  await item[0].props.onPress();
239
234
  });
240
- expect(mockedNavigate).toHaveBeenCalledWith(Routes.AddNewAutoSmart, {
241
- automate: { type: AUTOMATE_TYPE.VALUE_CHANGE, unit: undefined },
235
+ expect(mockedNavigate).toHaveBeenCalledWith(Routes.AddAutomationTypeSmart, {
236
+ automate: { unit: undefined },
242
237
  closeScreen: undefined,
243
238
  });
244
239
  mockedNavigate.mockClear();
@@ -251,6 +246,46 @@ describe('test Item', () => {
251
246
  expect(goDetail).toHaveLength(1);
252
247
  });
253
248
 
249
+ it('add new automate when reach maximum', async () => {
250
+ data.isOwner = false;
251
+ data.type = 'schedule';
252
+ data.unit = { id: 1 };
253
+
254
+ await act(async () => {
255
+ tree = await create(
256
+ wrapComponent(data, {
257
+ auth: {
258
+ account: {
259
+ user: {
260
+ permissions: {
261
+ max_automations_per_unit: 0,
262
+ },
263
+ },
264
+ },
265
+ },
266
+ })
267
+ );
268
+ });
269
+
270
+ const instance = tree.root;
271
+ const item = instance.findAll(
272
+ (el) =>
273
+ el.props.accessibilityLabel ===
274
+ AccessibilityLabel.SUB_UNIT_ADD_DEVICE &&
275
+ el.type === TouchableWithoutFeedback
276
+ );
277
+
278
+ expect(item).toHaveLength(1);
279
+ const spyToastError = jest.spyOn(ToastBottomHelper, 'error');
280
+ await act(async () => {
281
+ await item[0].props.onPress();
282
+ });
283
+ expect(mockedNavigate).not.toBeCalled();
284
+ expect(spyToastError).toBeCalledWith(
285
+ getTranslate('en', 'reach_max_automations_per_unit')
286
+ );
287
+ });
288
+
254
289
  it('render click select option by automation', async () => {
255
290
  await act(async () => {
256
291
  tree = await create(wrapComponent(data));
@@ -1,4 +1,10 @@
1
- import React, { useCallback, useEffect, useRef, useState } from 'react';
1
+ import React, {
2
+ useCallback,
3
+ useEffect,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
2
8
  import { TouchableOpacity, View } from 'react-native';
3
9
 
4
10
  import { Section } from '../..';
@@ -15,17 +21,32 @@ import {
15
21
  import Text from '../../Text/index.js';
16
22
 
17
23
  import styles from './OneTapStyles.js';
24
+ import { ToastBottomHelper } from '../../../utils/Utils';
25
+ import { useBackendPermission } from '../../../utils/Permission/backend';
18
26
 
19
27
  const SubUnitAutomate = ({ isOwner, listAutomate, unit, wrapItemStyle }) => {
20
28
  const t = useTranslations();
21
29
  const { navigate } = useNavigation();
22
30
  const [automates, setAutomates] = useState(listAutomate[0]);
23
31
  const [indexAutomate, setIndexAutomate] = useState(0);
32
+ const permissions = useBackendPermission();
24
33
  const { name: currentScreen } = useRoute();
34
+
35
+ const totalAutomates = useMemo(
36
+ () => listAutomate.reduce((acc, item) => item.data.length + acc, 0),
37
+ [listAutomate]
38
+ );
39
+
25
40
  const handleOnAddNew = () => {
41
+ if (unit?.id) {
42
+ if (permissions.max_automations_per_unit <= totalAutomates) {
43
+ ToastBottomHelper.error(t('reach_max_automations_per_unit'));
44
+ return;
45
+ }
46
+ }
26
47
  switch (automates.type) {
27
48
  case AUTOMATE_TABS.SCENARIO:
28
- navigate(Routes.AddNewOneTap, {
49
+ navigate(Routes.ScenarioName, {
29
50
  automate: {
30
51
  type: AUTOMATE_TYPE.ONE_TAP,
31
52
  unit: unit?.id,
@@ -34,9 +55,8 @@ const SubUnitAutomate = ({ isOwner, listAutomate, unit, wrapItemStyle }) => {
34
55
  });
35
56
  break;
36
57
  case AUTOMATE_TABS.AUTOMATION:
37
- navigate(Routes.AddNewAutoSmart, {
58
+ navigate(Routes.AddAutomationTypeSmart, {
38
59
  automate: {
39
- type: AUTOMATE_TYPE.VALUE_CHANGE,
40
60
  unit: unit?.id,
41
61
  },
42
62
  closeScreen: currentScreen,
@@ -1,10 +1,17 @@
1
- import React, { useMemo } from 'react';
2
- import { StyleSheet, TouchableOpacity, View } from 'react-native';
1
+ import React, { useMemo, useEffect, useState } from 'react';
2
+ import {
3
+ StyleSheet,
4
+ TouchableOpacity,
5
+ Animated,
6
+ Platform,
7
+ Easing,
8
+ } from 'react-native';
3
9
 
4
10
  import { Colors } from '../../configs';
5
11
  import Text from '../../commons/Text';
6
12
  import { AccessibilityLabel } from '../../configs/Constants';
7
13
  import withPreventDoubleClick from '../WithPreventDoubleClick';
14
+ import useKeyboardAnimated from '../../hooks/Explore/useKeyboardAnimated';
8
15
 
9
16
  const PreventDoubleTouch = withPreventDoubleClick(TouchableOpacity);
10
17
 
@@ -23,15 +30,34 @@ const ViewButtonBottom = ({
23
30
  styleButtonRight,
24
31
  accessibilityLabelPrefix = '',
25
32
  isPreventDoubleTouch = false,
33
+ wrapStyle,
34
+ disableKeyBoardAnimated = false,
26
35
  }) => {
27
36
  const useTwoButton = leftTitle && rightTitle;
37
+ const transY = useKeyboardAnimated();
38
+ const [keyboardAnim] = useState(new Animated.Value(0));
28
39
 
29
40
  const RightButtonView = useMemo(() => {
30
41
  return isPreventDoubleTouch ? PreventDoubleTouch : TouchableOpacity;
31
42
  }, [isPreventDoubleTouch]);
32
43
 
44
+ useEffect(() => {
45
+ Animated.timing(keyboardAnim, {
46
+ toValue: transY,
47
+ duration: Platform.OS === 'ios' ? 220 : 0,
48
+ easing: Easing.linear(),
49
+ useNativeDriver: false,
50
+ }).start();
51
+ }, [keyboardAnim, transY]);
52
+
33
53
  return (
34
- <View style={styles.container}>
54
+ <Animated.View
55
+ style={[
56
+ styles.container,
57
+ wrapStyle,
58
+ !disableKeyBoardAnimated && { bottom: keyboardAnim },
59
+ ]}
60
+ >
35
61
  {leftTitle && (
36
62
  <TouchableOpacity
37
63
  style={[
@@ -76,7 +102,7 @@ const ViewButtonBottom = ({
76
102
  </Text>
77
103
  </RightButtonView>
78
104
  )}
79
- </View>
105
+ </Animated.View>
80
106
  );
81
107
  };
82
108
 
@@ -85,6 +111,8 @@ const styles = StyleSheet.create({
85
111
  flexDirection: 'row',
86
112
  justifyContent: 'center',
87
113
  paddingHorizontal: 16,
114
+ position: 'absolute',
115
+ bottom: 0,
88
116
  },
89
117
  button: {
90
118
  paddingVertical: 24,
@@ -35,6 +35,8 @@ const API = {
35
35
  `/property_manager/units/${id}/star_automate_scripts/`,
36
36
  },
37
37
  SUB_UNIT: {
38
+ END_DEVICES_STATUS: (stationId) =>
39
+ `/property_manager/iot_dashboard/stations_v2/${stationId}/end_devices_status/`,
38
40
  REMOVE_SUB_UNIT: (unitId, id) =>
39
41
  `/property_manager/${unitId}/sub_units/${id}/`,
40
42
  CREATE_SUB_UNIT: (unitId) => `/property_manager/${unitId}/sub_units/`,
@@ -49,6 +51,7 @@ const API = {
49
51
  ACCEPT_NEW_DEVICE: (id) =>
50
52
  `/iot/modules/zigbee/chips/${id}/device_permit_to_join/`,
51
53
  },
54
+ JSON_CONFIGURATION: '/chip_manager/chips/json_configuration/',
52
55
  },
53
56
  DEVICE: {
54
57
  SENSOR_DETAIL: (id) => `/property_manager/devices/${id}/`,
@@ -212,6 +215,7 @@ const API = {
212
215
  },
213
216
  GATEWAY: {
214
217
  LIST: () => '/chip_manager/developer_mode_chips/',
218
+ COUNT: () => '/chip_manager/developer_mode_chips/count/',
215
219
  DETAIL: (id) => `/chip_manager/developer_mode_chips/${id}/`,
216
220
  REBOOT: (id) => `/chip_manager/developer_mode_chips/${id}/reboot_chip/`,
217
221
  },
@@ -238,6 +238,7 @@ export default {
238
238
  BUTTON_EDIT_SCRIPT_ACTION: 'BUTTON_EDIT_SCRIPT_ACTION',
239
239
  BUTTON_ADD_SCRIPT_ACTION: 'BUTTON_ADD_SCRIPT_ACTION',
240
240
  ICON_CLOSE: 'ICON_CLOSE',
241
+ ICON_MORE: 'ICON_MORE',
241
242
  ICON_ARROW_RIGHT: 'ICON_ARROW_RIGHT',
242
243
 
243
244
  // Parking input maunaly spot
@@ -6,4 +6,5 @@ export default {
6
6
  BLE_RESPONSE_OK: 'OK',
7
7
  BLE_RESPONSE_FAILED: 'FAILED',
8
8
  BLE_LISTER_RESPONSE_CONTROL: 'LISTER_RESPONSE_CONTROL',
9
+ BLE_REQUEST_ONE_TIME_CODE: 'cf153c1f-bc0d-11ed-afa1-0242ac120002',
9
10
  };
@@ -36,11 +36,11 @@ export const Action = {
36
36
  IS_CHECK_CLEAR_CACHE_UNITS: 'IS_CHECK_CLEAR_CACHE_UNITS',
37
37
  DELETE_UNIT_SUCCESSFULLY: 'DELETE_UNIT_SUCCESSFULLY',
38
38
  RESET_DELETE_UNIT_ACTION: 'RESET_DELETE_UNIT_ACTION',
39
- // NOTE: DEV MODE
40
39
  SET_WIDGET_DRAGGING: 'SET_WIDGET_DRAGGING',
41
40
  SET_IS_EDITING_TEMPLATE: 'SET_IS_EDITING_TEMPLATE',
42
41
  SET_IS_IN_EDIT_TEMPLATE_SCREEN: 'SET_IS_IN_EDIT_TEMPLATE_SCREEN',
43
42
  LOGOUT: 'LOGOUT',
43
+ SET_APP_STATE_CHANGE: 'SET_APP_STATE_CHANGE',
44
44
  };
45
45
 
46
46
  export type AuthData = {
@@ -101,6 +101,7 @@ export type AppType = {
101
101
  isLockWhenPickColor: boolean;
102
102
  isNeedUpdateCache: boolean;
103
103
  isDeleteUnitSuccessFully: boolean;
104
+ appState: string;
104
105
  };
105
106
 
106
107
  export type IoTType = {
@@ -92,6 +92,7 @@ export const mockSCStore = (data: ContextData): ContextData => {
92
92
  isConnectWifiGateway: false,
93
93
  isNetworkConnected: true,
94
94
  isLockWhenPickColor: false,
95
+ appState: 'active',
95
96
  ...data.app,
96
97
  },
97
98
  unit: {
@@ -16,6 +16,7 @@ import {
16
16
  } from './actionType';
17
17
  import _ from 'lodash';
18
18
  import { STORAGE_KEY, removeMultiple } from '../utils/Storage.js';
19
+ import { AppState } from 'react-native';
19
20
 
20
21
  export type ContextData = {
21
22
  auth: AuthData;
@@ -71,6 +72,7 @@ export const initialState = {
71
72
  isEmergencyPopupScreen: false,
72
73
  isNeedUpdateCache: false,
73
74
  isDeleteUnitSuccessFully: false,
75
+ appState: AppState.currentState,
74
76
  },
75
77
  iot: {
76
78
  bluetooth: {
@@ -394,7 +396,7 @@ export const reducer = (currentState: ContextData, action: Action) => {
394
396
  ...currentState.iot,
395
397
  internet: {
396
398
  ...currentState.iot.internet,
397
- statuses: newStatuses,
399
+ statuses: JSON.parse(JSON.stringify(newStatuses)),
398
400
  },
399
401
  },
400
402
  };
@@ -527,6 +529,15 @@ export const reducer = (currentState: ContextData, action: Action) => {
527
529
  },
528
530
  };
529
531
 
532
+ case Action.SET_APP_STATE_CHANGE:
533
+ return {
534
+ ...currentState,
535
+ app: {
536
+ ...currentState.app,
537
+ appState: payload,
538
+ },
539
+ };
540
+
530
541
  default:
531
542
  return currentState;
532
543
  }
@@ -18,11 +18,17 @@ const useKeyboardAnimated = (tabHeight = 0) => {
18
18
  );
19
19
 
20
20
  useEffect(() => {
21
- Keyboard.addListener('keyboard' + action + 'Hide', _keyboardWillHide);
22
- Keyboard.addListener('keyboard' + action + 'Show', _keyboardWillShow);
21
+ const keyboardDidHide = Keyboard.addListener(
22
+ 'keyboard' + action + 'Hide',
23
+ _keyboardWillHide
24
+ );
25
+ const keyboardDidShow = Keyboard.addListener(
26
+ 'keyboard' + action + 'Show',
27
+ _keyboardWillShow
28
+ );
23
29
  return () => {
24
- Keyboard.removeListener('keyboard' + action + 'Show', _keyboardWillShow);
25
- Keyboard.removeListener('keyboard' + action + 'Hide', _keyboardWillHide);
30
+ keyboardDidHide?.remove();
31
+ keyboardDidShow?.remove();
26
32
  };
27
33
  }, [_keyboardWillHide, _keyboardWillShow, tabHeight]);
28
34
 
@@ -1,5 +1,5 @@
1
- import { useContext, useCallback, useRef, useEffect, useMemo } from 'react';
2
- import { AppState, Platform } from 'react-native';
1
+ import { useContext, useCallback, useEffect, useMemo } from 'react';
2
+ import { Platform } from 'react-native';
3
3
  import { getSystemVersion } from 'react-native-device-info';
4
4
 
5
5
  import { SCContext, useSCContextSelector } from '../../context';
@@ -20,7 +20,7 @@ const permissions = [
20
20
  // NOTE: fnCallback is used for Lavida when found device
21
21
  const useBluetoothConnection = (fnCallback) => {
22
22
  const t = useTranslations();
23
- const appState = useRef(AppState.currentState);
23
+ const appState = useSCContextSelector((state) => state.app.appState);
24
24
 
25
25
  const { setAction } = useContext(SCContext);
26
26
  const isEnabled = useSCContextSelector((state) => state.bluetooth.isEnabled);
@@ -51,33 +51,21 @@ const useBluetoothConnection = (fnCallback) => {
51
51
  [onDeviceFound]
52
52
  );
53
53
 
54
- useEffect(() => {
55
- const subscription = AppState.addEventListener(
56
- 'change',
57
- async (nextAppState) => {
58
- if (
59
- appState.current.match(/inactive|background/) &&
60
- nextAppState === 'active'
61
- ) {
62
- const results = await Permissions.checkMultiple(permissions);
63
- const granted = Object.values(results).every((result) =>
64
- [
65
- Permissions.RESULTS.GRANTED,
66
- Permissions.RESULTS.UNAVAILABLE,
67
- ].includes(result)
68
- );
69
- setAction(Action.SET_BLUETOOTH_PERM_GRANTED, granted);
70
- }
71
- appState.current = nextAppState;
72
- }
54
+ const requestPermission = useCallback(async () => {
55
+ const results = await Permissions.checkMultiple(permissions);
56
+ const granted = Object.values(results).every((result) =>
57
+ [Permissions.RESULTS.GRANTED, Permissions.RESULTS.UNAVAILABLE].includes(
58
+ result
59
+ )
73
60
  );
74
-
75
- return () => {
76
- subscription?.remove();
77
- };
61
+ setAction(Action.SET_BLUETOOTH_PERM_GRANTED, granted);
78
62
  // eslint-disable-next-line react-hooks/exhaustive-deps
79
63
  }, []);
80
64
 
65
+ useEffect(() => {
66
+ appState === 'active' && requestPermission();
67
+ }, [appState, requestPermission]);
68
+
81
69
  const requestPerm = useCallback(async () => {
82
70
  let results = await Permissions.requestMultiple(permissions);
83
71
  const blocked = Object.values(results).some(
@@ -0,0 +1,95 @@
1
+ import mqtt from 'precompiled-mqtt/dist/mqtt.browser';
2
+ import { useState, useEffect, useMemo, useRef } from 'react';
3
+
4
+ import API from '../configs/API';
5
+ import { axiosGet } from '../utils/Apis/axios';
6
+ import { handleMqttMessage } from '../iot/mqtt';
7
+
8
+ const useChipJsonConfiguration = (dashboardId) => {
9
+ const [chips, setChips] = useState([]);
10
+ // get chip data
11
+ useEffect(() => {
12
+ const getGateways = async () => {
13
+ const { success, data } = await axiosGet(API.CHIP.JSON_CONFIGURATION, {
14
+ params: {
15
+ unit: dashboardId,
16
+ },
17
+ });
18
+ if (success) {
19
+ setChips(data);
20
+ }
21
+ };
22
+ getGateways();
23
+ }, [dashboardId]);
24
+
25
+ return {
26
+ chips,
27
+ };
28
+ };
29
+
30
+ const connectChipMqtt = (chip) => {
31
+ const configById = {};
32
+ const { code, mqtt_server } = chip;
33
+ const { host, websocket_port } = mqtt_server;
34
+
35
+ const client = mqtt.connect(`wss://${host}:${websocket_port}`, {
36
+ username: code,
37
+ password: code,
38
+ });
39
+
40
+ (chip?.sensors).forEach((sensor) => {
41
+ (sensor?.configs).forEach((config) => {
42
+ configById[config.id] = config;
43
+ });
44
+ });
45
+
46
+ client.on('connect', () => {
47
+ client.subscribe(`eoh/chip/${code}/#`);
48
+ });
49
+ client.on('message', (topic, payload, packet) => {
50
+ const payloadObject = JSON.parse(payload.toString());
51
+ handleMqttMessage(topic, payloadObject, code, chip, configById);
52
+ });
53
+
54
+ return client;
55
+ };
56
+
57
+ export const useConnectChipMqtt = (chips) => {
58
+ const mqttChips = useMemo(
59
+ () => chips.filter((chip) => chip?.mqtt_server?.allow_frontend_connect),
60
+ [chips]
61
+ );
62
+
63
+ const mqttConfigs = useMemo(() => {
64
+ let configById = {};
65
+
66
+ mqttChips.forEach((chip) => {
67
+ chip?.sensors.forEach((sensor) => {
68
+ sensor?.configs.forEach((config) => {
69
+ configById[config.id] = config;
70
+ });
71
+ });
72
+ });
73
+
74
+ return configById;
75
+ }, [mqttChips]);
76
+
77
+ const mqttClientsRef = useRef([]);
78
+ useEffect(() => {
79
+ mqttChips.forEach((chip) => {
80
+ const client = connectChipMqtt(chip);
81
+ mqttClientsRef.current.push(client);
82
+ });
83
+
84
+ return () => {
85
+ mqttClientsRef.current.forEach((client) => {
86
+ client && client.end();
87
+ });
88
+ mqttClientsRef.current = [];
89
+ };
90
+ }, [mqttChips]);
91
+
92
+ return { mqttConfigs };
93
+ };
94
+
95
+ export default useChipJsonConfiguration;
@@ -70,7 +70,8 @@ let waitWatchConfigTimerId = null;
70
70
  let waitWatchConfigIds = [];
71
71
 
72
72
  export const watchMultiConfigs = async (configIds) => {
73
- waitWatchConfigIds = waitWatchConfigIds.concat(configIds);
73
+ const uniqueConfigIds = [...new Set(configIds)]?.filter(Boolean);
74
+ waitWatchConfigIds = waitWatchConfigIds.concat(uniqueConfigIds);
74
75
  if (!waitWatchConfigTimerId) {
75
76
  waitWatchConfigTimerId = setTimeout(async () => {
76
77
  await realWatchMultiConfigs(waitWatchConfigIds);