@eohjsc/react-native-smart-city 0.3.21 → 0.3.24

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 (104) hide show
  1. package/index.js +2 -0
  2. package/package.json +2 -1
  3. package/src/Images/Common/buttonLeftCurtain.png +0 -0
  4. package/src/Images/Common/buttonLeftCurtain@2x.png +0 -0
  5. package/src/Images/Common/buttonLeftCurtain@3x.png +0 -0
  6. package/src/commons/Action/ItemQuickAction.js +1 -12
  7. package/src/commons/Action/__test__/ItemQuickAction.test.js +1 -1
  8. package/src/commons/ActionGroup/ColorPickerTemplate.js +2 -9
  9. package/src/commons/ActionGroup/CurtainButtonTemplate.js +14 -21
  10. package/src/commons/ActionGroup/CurtainButtonTemplateStyle.js +5 -0
  11. package/src/commons/ActionGroup/NumberUpDownActionTemplate.js +12 -24
  12. package/src/commons/ActionGroup/OnOffSmartLock/OnOffSmartLock.js +10 -17
  13. package/src/commons/ActionGroup/OnOffTemplate/index.js +10 -35
  14. package/src/commons/ActionGroup/OneBigButtonTemplate.js +2 -3
  15. package/src/commons/ActionGroup/OptionsDropdownActionTemplate.js +7 -8
  16. package/src/commons/ActionGroup/SliderRangeTemplate.js +3 -10
  17. package/src/commons/ActionGroup/StatesGridActionTemplate.js +9 -24
  18. package/src/commons/ActionGroup/ThreeButtonTemplate.js +6 -9
  19. package/src/commons/ActionGroup/TimerActionTemplate.js +11 -4
  20. package/src/commons/ActionGroup/TwoButtonTemplate/index.js +18 -33
  21. package/src/commons/ActionGroup/__test__/CurtainButtonTemplate.test.js +3 -15
  22. package/src/commons/ActionGroup/__test__/NumberUpDownTemplate.test.js +38 -4
  23. package/src/commons/ActionGroup/__test__/OnOffButtonTemplate.test.js +7 -0
  24. package/src/commons/ActionGroup/__test__/OnOffSmartLock.test.js +17 -12
  25. package/src/commons/ActionGroup/__test__/OnOffTemplate.test.js +11 -16
  26. package/src/commons/ActionGroup/__test__/OneBigButtonTemplate.test.js +1 -1
  27. package/src/commons/ActionGroup/__test__/OptionsDropdownTemplate.test.js +11 -10
  28. package/src/commons/ActionGroup/__test__/StatesGridActionTemplate.test.js +8 -7
  29. package/src/commons/ActionGroup/__test__/TimerActionTemplate.test.js +8 -1
  30. package/src/commons/ActionGroup/__test__/TimerActionTemplateWithutConfigValue.test.js +7 -0
  31. package/src/commons/ActionGroup/__test__/TwoButtonTemplate.test.js +17 -2
  32. package/src/commons/ActionGroup/__test__/index.test.js +15 -18
  33. package/src/commons/ConnectingProcess/index.js +6 -25
  34. package/src/commons/Device/HistoryChart.js +8 -6
  35. package/src/commons/Device/ItemDevice.js +79 -35
  36. package/src/commons/MediaPlayerDetail/index.js +5 -0
  37. package/src/commons/RowItem/index.js +6 -2
  38. package/src/commons/SubUnit/Favorites/index.js +24 -6
  39. package/src/commons/SubUnit/ShortDetail.js +31 -5
  40. package/src/commons/SubUnit/__test__/Favorites.test.js +1 -0
  41. package/src/commons/SubUnit/__test__/ShortDetail.test.js +1 -0
  42. package/src/configs/API.js +9 -4
  43. package/src/configs/Constants.js +8 -2
  44. package/src/configs/SCConfig.js +4 -0
  45. package/src/context/actionType.ts +7 -5
  46. package/src/context/mockStore.ts +10 -3
  47. package/src/context/reducer.ts +29 -15
  48. package/src/hoc/index.js +3 -0
  49. package/src/hoc/withRemoteControl.js +10 -0
  50. package/src/hooks/Common/index.js +2 -2
  51. package/src/hooks/Common/useDevicesStatus.js +57 -0
  52. package/src/hooks/Common/useGGHomeDeviceConnected.js +3 -3
  53. package/src/hooks/IoT/__test__/useGGHomeConnection.test.js +1 -2
  54. package/src/hooks/IoT/__test__/useRemoteControl.test.js +9 -11
  55. package/src/hooks/IoT/index.js +9 -1
  56. package/src/hooks/IoT/useGGHomeConnection.js +0 -1
  57. package/src/hooks/IoT/useRemoteControl.js +2 -3
  58. package/src/hooks/IoT/useUnwatchLGDeviceConfigControl.js +29 -0
  59. package/src/hooks/IoT/useValueEvaluation.js +17 -4
  60. package/src/hooks/IoT/useWatchConfigs.js +34 -0
  61. package/src/iot/Monitor.js +13 -20
  62. package/src/iot/RemoteControl/GoogleHome.js +12 -13
  63. package/src/iot/RemoteControl/Internet.js +1 -8
  64. package/src/iot/RemoteControl/LG.js +1 -0
  65. package/src/iot/RemoteControl/__test__/GoogleHome.test.js +7 -2
  66. package/src/navigations/UnitStack.js +27 -3
  67. package/src/screens/AddNewAction/SelectAction.js +1 -1
  68. package/src/screens/AddNewAction/SetupSensor.js +4 -0
  69. package/src/screens/AddNewGateway/PlugAndPlay/ConnectWifiWarning.js +44 -78
  70. package/src/screens/AddNewGateway/PlugAndPlay/GatewayWifiList.js +15 -35
  71. package/src/screens/AddNewGateway/PlugAndPlay/__test__/GatewayWifiList.test.js +2 -0
  72. package/src/screens/AllCamera/__test__/index.test.js +1 -1
  73. package/src/screens/Device/__test__/detail.test.js +1 -54
  74. package/src/screens/Device/components/SensorConnectStatusViewHeader.js +18 -8
  75. package/src/screens/Device/components/SensorDisplayItem.js +2 -2
  76. package/src/screens/Device/detail.js +36 -30
  77. package/src/screens/Device/hooks/__test__/useEvaluateValue.test.js +102 -0
  78. package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +20 -0
  79. package/src/screens/Device/utils/index.js +45 -0
  80. package/src/screens/Device/utils/index.test.js +111 -0
  81. package/src/screens/EmergencyContacts/EmergencyContactsAddNew.js +35 -22
  82. package/src/screens/EmergencyContacts/EmergencyContactsSelectContacts.js +2 -1
  83. package/src/screens/EmergencyContacts/__test__/EmergencyContactAddNew.test.js +36 -2
  84. package/src/screens/MoveToAnotherSubUnit/__test__/index.test.js +0 -2
  85. package/src/screens/Notification/__test__/NotificationItem.test.js +84 -19
  86. package/src/screens/Notification/components/NotificationItem.js +64 -31
  87. package/src/screens/PlayBackCamera/index.js +22 -6
  88. package/src/screens/ScriptDetail/hooks/useStarredScript.js +2 -2
  89. package/src/screens/SubUnit/AddSubUnit.js +2 -1
  90. package/src/screens/Unit/AddMenu.js +4 -0
  91. package/src/screens/Unit/{SelectFavoritesDevices.js → SelectAddToFavorites.js} +81 -26
  92. package/src/screens/Unit/{SelectFavoritesDevicesStyles.js → SelectAddToFavoritesStyles.js} +0 -0
  93. package/src/screens/Unit/__test__/CheckSendEmail.test.js +12 -0
  94. package/src/screens/Unit/__test__/Detail.test.js +2 -3
  95. package/src/screens/Unit/__test__/SelectAddToFavorites.test.js +267 -0
  96. package/src/screens/Unit/components/AutomateScript/index.js +65 -0
  97. package/src/screens/Unit/components/AutomateScript/styles.js +48 -0
  98. package/src/screens/Unit/components/MyUnitDevice/index.js +4 -2
  99. package/src/screens/UnitSummary/components/3PPowerConsumption/index.js +4 -2
  100. package/src/utils/I18n/translations/en.json +7 -2
  101. package/src/utils/I18n/translations/vi.json +6 -1
  102. package/src/utils/Route/index.js +1 -1
  103. package/src/hooks/Common/useSensorsStatus.js +0 -62
  104. package/src/screens/Unit/__test__/SelectFavoritesDevices.test.js +0 -110
@@ -7,7 +7,11 @@ import styles from '../styles/NotificationItemStyles';
7
7
  import Text from '../../../commons/Text';
8
8
  import { Colors, API, Images } from '../../../configs';
9
9
  import IconComponent from '../../../commons/IconComponent';
10
- import { NOTIFICATION_TYPES, SENSOR_TYPE } from '../../../configs/Constants';
10
+ import {
11
+ NOTIFICATION_TYPES,
12
+ SENSOR_TYPE,
13
+ EMERGENCY_TYPE,
14
+ } from '../../../configs/Constants';
11
15
  import { useTranslations } from '../../../hooks/Common/useTranslations';
12
16
  import { axiosPost } from '../../../utils/Apis/axios';
13
17
  import Routes from '../../../utils/Route';
@@ -18,28 +22,14 @@ const NotificationItem = memo(({ item }) => {
18
22
  const { id, icon, created_at, is_read, params, content_code } = item;
19
23
  const [isRead, setIsRead] = useState(is_read);
20
24
  const timeFormat = moment(created_at).format('LT DD/MM/YYYY');
21
- const regex = useMemo(() => {
22
- return /\B'|'\B/g;
23
- }, []);
24
-
25
- const paramsJSON = useMemo(() => {
26
- if (typeof params === 'object') {
27
- return params;
28
- }
29
- // TODO return this later
30
- let stringParams = JSON.stringify(params);
31
- stringParams = stringParams.replace(regex, '"');
32
- stringParams = stringParams.substring(1, stringParams.length - 1);
33
- return JSON.parse(stringParams);
34
- }, [params, regex]);
35
25
 
36
26
  let arrParams = useMemo(() => {
37
27
  const values = [];
38
- Object.entries(paramsJSON).forEach(([key, value]) => {
28
+ Object.entries(params).forEach(([key, value]) => {
39
29
  values.push(value);
40
30
  });
41
31
  return values;
42
- }, [paramsJSON]);
32
+ }, [params]);
43
33
 
44
34
  const customColorText = (text, colorParams) => {
45
35
  return text.split('**').map((str, i) =>
@@ -56,9 +46,9 @@ const NotificationItem = memo(({ item }) => {
56
46
  };
57
47
 
58
48
  const renderItem = useCallback(() => {
59
- const booking_id = paramsJSON.booking_id && paramsJSON.booking_id;
60
- const unitId = paramsJSON?.unit_id;
61
- const sensorId = paramsJSON?.sensor_id;
49
+ const booking_id = params?.booking_id && params?.booking_id;
50
+ const unitId = params?.unit_id;
51
+ const sensorId = params?.sensor_id;
62
52
  switch (content_code) {
63
53
  case NOTIFICATION_TYPES.NOTIFY_INVITE_MEMBER:
64
54
  return {
@@ -177,7 +167,7 @@ const NotificationItem = memo(({ item }) => {
177
167
  redirect: () =>
178
168
  navigation.navigate(Routes.SmartParkingStack, {
179
169
  screen: Routes.SmartParkingBookingDetails,
180
- params: { id: paramsJSON.violated_booking_id || booking_id },
170
+ params: { id: params?.violated_booking_id || booking_id },
181
171
  }),
182
172
  };
183
173
  case NOTIFICATION_TYPES.MOVE_CAR_WITHOUT_PAY_VIOLATION:
@@ -213,7 +203,7 @@ const NotificationItem = memo(({ item }) => {
213
203
  redirect: () =>
214
204
  navigation.navigate(Routes.SmartParkingStack, {
215
205
  screen: Routes.SmartParkingBookingDetails,
216
- params: { id: paramsJSON.booking_id_new },
206
+ params: { id: params?.booking_id_new },
217
207
  }),
218
208
  };
219
209
  case NOTIFICATION_TYPES.STOP_VIOLATION_FREE_PARKING_ZONE:
@@ -229,7 +219,7 @@ const NotificationItem = memo(({ item }) => {
229
219
  }),
230
220
  };
231
221
  case NOTIFICATION_TYPES.NOTIFY_INDICATOR:
232
- switch (paramsJSON.sensor_type) {
222
+ switch (params?.sensor_type) {
233
223
  case SENSOR_TYPE.AIR_QUALITY:
234
224
  return {
235
225
  content: customColorText(
@@ -240,7 +230,7 @@ const NotificationItem = memo(({ item }) => {
240
230
  navigation.navigate(Routes.UnitStack, {
241
231
  screen: Routes.UnitSummary,
242
232
  params: {
243
- summaryId: paramsJSON.summary_id,
233
+ summaryId: params?.summary_id,
244
234
  unitId,
245
235
  },
246
236
  }),
@@ -256,7 +246,7 @@ const NotificationItem = memo(({ item }) => {
256
246
  navigation.navigate(Routes.UnitStack, {
257
247
  screen: Routes.UnitSummary,
258
248
  params: {
259
- summaryId: paramsJSON.summary_id,
249
+ summaryId: params?.summary_id,
260
250
  unitId,
261
251
  },
262
252
  }),
@@ -272,7 +262,7 @@ const NotificationItem = memo(({ item }) => {
272
262
  navigation.navigate(Routes.UnitStack, {
273
263
  screen: Routes.UnitSummary,
274
264
  params: {
275
- summaryId: paramsJSON.summary_id,
265
+ summaryId: params?.summary_id,
276
266
  unitId,
277
267
  },
278
268
  }),
@@ -288,7 +278,7 @@ const NotificationItem = memo(({ item }) => {
288
278
  navigation.navigate(Routes.UnitStack, {
289
279
  screen: Routes.UnitSummary,
290
280
  params: {
291
- summaryId: paramsJSON.summary_id,
281
+ summaryId: params?.summary_id,
292
282
  unitId,
293
283
  },
294
284
  }),
@@ -304,7 +294,7 @@ const NotificationItem = memo(({ item }) => {
304
294
  navigation.navigate(Routes.UnitStack, {
305
295
  screen: Routes.UnitSummary,
306
296
  params: {
307
- summaryId: paramsJSON.summary_id,
297
+ summaryId: params?.summary_id,
308
298
  unitId,
309
299
  },
310
300
  }),
@@ -387,7 +377,7 @@ const NotificationItem = memo(({ item }) => {
387
377
  case NOTIFICATION_TYPES.NOTIFY_REMOVE_UNIT:
388
378
  return {
389
379
  content: customColorText(
390
- paramsJSON?.unit_owner_name
380
+ params?.unit_owner_name
391
381
  ? t('text_notification_content_remove_unit_to_member')
392
382
  : t('text_notification_content_remove_unit_to_owner'),
393
383
  arrParams
@@ -482,7 +472,7 @@ const NotificationItem = memo(({ item }) => {
482
472
  screen: Routes.UnitDetail,
483
473
  params: {
484
474
  unitId,
485
- stationId: paramsJSON?.sub_unit_id,
475
+ stationId: params?.sub_unit_id,
486
476
  },
487
477
  });
488
478
  },
@@ -512,6 +502,49 @@ const NotificationItem = memo(({ item }) => {
512
502
  <IconComponent icon={'home'} style={styles.backgroundSummer} />
513
503
  ),
514
504
  };
505
+ case NOTIFICATION_TYPES.NOTIFY_EMERGENCY:
506
+ switch (params?.type) {
507
+ case EMERGENCY_TYPE.CREATED:
508
+ return {
509
+ content: customColorText(
510
+ t('text_notification_content_emergency'),
511
+ arrParams
512
+ ),
513
+ redirect: () =>
514
+ navigation.navigate(Routes.UnitStack, {
515
+ screen: Routes.DeviceDetail,
516
+ params: {
517
+ unitId,
518
+ sensorId,
519
+ },
520
+ }),
521
+ iconContent: <Image source={Images.logo} style={styles.logo} />,
522
+ };
523
+ case EMERGENCY_TYPE.RESOLVE:
524
+ return {
525
+ content: customColorText(
526
+ t('text_notification_content_emergency_resolve'),
527
+ arrParams
528
+ ),
529
+ redirect: () =>
530
+ navigation.navigate(Routes.UnitStack, {
531
+ screen: Routes.DeviceDetail,
532
+ params: {
533
+ unitId,
534
+ sensorId,
535
+ },
536
+ }),
537
+ iconContent: <Image source={Images.logo} style={styles.logo} />,
538
+ };
539
+ default:
540
+ return {
541
+ content: customColorText(
542
+ t('this_notification_will_be_updated_soon')
543
+ ),
544
+ redirect: () => null,
545
+ iconContent: <Image source={Images.logo} style={styles.logo} />,
546
+ };
547
+ }
515
548
  default:
516
549
  return {
517
550
  content: customColorText(t('this_notification_will_be_updated_soon')),
@@ -519,7 +552,7 @@ const NotificationItem = memo(({ item }) => {
519
552
  iconContent: <Image source={Images.logo} style={styles.logo} />,
520
553
  };
521
554
  }
522
- }, [arrParams, content_code, navigation, paramsJSON, t]);
555
+ }, [arrParams, content_code, navigation, params, t]);
523
556
 
524
557
  const { content, redirect, iconContent } = renderItem() || {};
525
558
 
@@ -62,6 +62,16 @@ const PlayBackCamera = () => {
62
62
  dateTemp = date;
63
63
  }, [selected]);
64
64
 
65
+ const getHourWithTimeZone = useCallback(
66
+ // eslint-disable-next-line no-shadow
67
+ (hour) => {
68
+ const hourWithTimezone =
69
+ parseInt(hour, 10) + parseInt(item?.configuration?.time_zone || 0, 10);
70
+ return hourWithTimezone < 10 ? '0' + hourWithTimezone : hourWithTimezone;
71
+ },
72
+ [item?.configuration?.time_zone]
73
+ );
74
+
65
75
  const onChangeValue = useCallback(
66
76
  (value, selectedTime) => {
67
77
  if (!isFirstTime) {
@@ -87,7 +97,7 @@ const PlayBackCamera = () => {
87
97
  setUri(
88
98
  `${playback.split('=')[0]}=${date[0]}${date[1]}${
89
99
  date[2]
90
- }T${h}${m}${s}Z`
100
+ }T${getHourWithTimeZone(h)}${m}${s}Z`
91
101
  );
92
102
  }
93
103
  const to = setTimeout(() => {
@@ -96,7 +106,13 @@ const PlayBackCamera = () => {
96
106
  }, 100);
97
107
  }
98
108
  },
99
- [arrHourTemp, item?.configuration, now]
109
+ [
110
+ arrHourTemp,
111
+ getHourWithTimeZone,
112
+ item?.configuration?.playback,
113
+ item?.configuration?.uri,
114
+ now,
115
+ ]
100
116
  );
101
117
 
102
118
  useEffect(() => {
@@ -111,9 +127,9 @@ const PlayBackCamera = () => {
111
127
  setUri(item?.configuration?.uri);
112
128
  } else {
113
129
  setUri(
114
- `${playback.split('=')[0]}=${date[0]}${date[1]}${date[2]}T${hour.h}${
115
- hour.m
116
- }${hour.s}Z`
130
+ `${playback.split('=')[0]}=${date[0]}${date[1]}${
131
+ date[2]
132
+ }T${getHourWithTimeZone(hour.h)}${hour.m}${hour.s}Z`
117
133
  );
118
134
  }
119
135
  const to = setTimeout(() => {
@@ -121,7 +137,7 @@ const PlayBackCamera = () => {
121
137
  clearTimeout(to);
122
138
  }, 100);
123
139
  // eslint-disable-next-line react-hooks/exhaustive-deps
124
- }, [selected]);
140
+ }, [selected, getHourWithTimeZone]);
125
141
 
126
142
  useEffect(() => {
127
143
  // eslint-disable-next-line react-hooks/rules-of-hooks
@@ -14,14 +14,14 @@ export const useStarredScript = (automate) => {
14
14
 
15
15
  const starScript = useCallback(async () => {
16
16
  const { success } = await axiosPost(API.AUTOMATE.STAR_SCRIPT(automate?.id));
17
- success && setAction(Action.STAR_SCRIPT, automate?.script?.id);
17
+ success && setAction(Action.STAR_SCRIPTS, [automate?.script?.id]);
18
18
  }, [automate, setAction]);
19
19
 
20
20
  const unstarScript = useCallback(async () => {
21
21
  const { success } = await axiosPost(
22
22
  API.AUTOMATE.UNSTAR_SCRIPT(automate?.id)
23
23
  );
24
- success && setAction(Action.UNSTAR_SCRIPT, automate?.script?.id);
24
+ success && setAction(Action.UNSTAR_SCRIPTS, [automate?.script?.id]);
25
25
  }, [automate, setAction]);
26
26
 
27
27
  return {
@@ -83,7 +83,7 @@ const AddSubUnit = ({ route }) => {
83
83
  awaitCreate.current = true;
84
84
  const dataObj = { name: roomName, background: wallpaper };
85
85
  const formData = createFormData(dataObj, ['background']);
86
- const { success } = await axiosPost(
86
+ const { success, data } = await axiosPost(
87
87
  API.SUB_UNIT.CREATE_SUB_UNIT(unit.id),
88
88
  formData,
89
89
  {
@@ -107,6 +107,7 @@ const AddSubUnit = ({ route }) => {
107
107
  params: {
108
108
  unitId: unit.id,
109
109
  unitData: unit,
110
+ stationId: data.id,
110
111
  isAddSubUnit: true,
111
112
  routeName: Routes.DashboardStack,
112
113
  },
@@ -66,6 +66,10 @@ const AddMenu = memo(({ unit, afterItemClick, showAdd, setHideAdd }) => {
66
66
  route: Routes.SmartAccount,
67
67
  text: t('name_smart_account'),
68
68
  image: <SmartAccount width={43} height={43} />, // TODO change icon
69
+ data: {
70
+ unit_id: unit?.id,
71
+ unit_name: unit?.name,
72
+ },
69
73
  type: Routes.SmartAccount,
70
74
  },
71
75
  ];
@@ -15,14 +15,15 @@ import NavBar from '../../commons/NavBar';
15
15
  import BottomButtonView from '../../commons/BottomButtonView';
16
16
  import { FullLoading } from '../../commons';
17
17
  import Device from '../AddNewAction/Device';
18
+ import AutomateScript from './components/AutomateScript';
18
19
  import { useTranslations } from '../../hooks/Common/useTranslations';
19
20
  import { SCContext } from '../../context';
20
21
  import { Action } from '../../context/actionType';
21
22
  import { axiosGet, axiosPost } from '../../utils/Apis/axios';
22
23
  import { API, Colors } from '../../configs';
23
- import styles from './SelectFavoritesDevicesStyles';
24
+ import styles from './SelectAddToFavoritesStyles';
24
25
 
25
- const SelectFavoritesDevices = memo(({ route }) => {
26
+ const SelectAddToFavorites = memo(({ route }) => {
26
27
  const t = useTranslations();
27
28
  const { goBack } = useNavigation();
28
29
  const { unitId } = route.params;
@@ -31,43 +32,69 @@ const SelectFavoritesDevices = memo(({ route }) => {
31
32
  const [listMenuItem, setListMenuItem] = useState([]);
32
33
  const [indexStation, setIndexStation] = useState(0);
33
34
  const [stations, setStations] = useState([]);
34
- const [selectedIds, setSelectedIds] = useState([]);
35
+ const [automates, setAutomates] = useState([]);
36
+
37
+ const [selectedDeviceIds, setSelectedDeviceIds] = useState([]);
38
+ const [selectedScriptIds, setSelectedScriptIds] = useState([]);
35
39
  const [loading, setLoading] = useState(true);
36
40
 
37
41
  const fetchData = useCallback(async () => {
38
42
  setLoading(true);
39
- const { success, data } = await axiosGet(API.UNIT.DEVICES(unitId));
40
- if (success) {
41
- const newData = data.filter((item) => item.devices.length > 0);
43
+ const { success: successDevice, data: dataDevice } = await axiosGet(
44
+ API.UNIT.DEVICES_NOT_FAVORITES(unitId)
45
+ );
46
+ const { success: successAutomate, data: dataAutomate } = await axiosGet(
47
+ API.UNIT.AUTOMATE_SCRIPTS_NOT_STARRED(unitId)
48
+ );
49
+ if (successDevice && successAutomate) {
50
+ const newData = dataDevice.filter((item) => item.devices.length > 0);
51
+ if (dataAutomate.length) {
52
+ newData.unshift({
53
+ isSmart: true,
54
+ name: t('smart'),
55
+ });
56
+ }
42
57
  const listMenu = newData.map((item, index) => ({
43
58
  text: item.name,
44
59
  station: item,
45
60
  index: index,
46
61
  }));
47
62
  setStations(newData);
63
+ setAutomates(dataAutomate);
48
64
  setListMenuItem(listMenu);
49
65
  setListStation(listMenu);
50
66
  }
51
67
  setLoading(false);
52
- }, [unitId]);
68
+ }, [unitId, t]);
53
69
 
54
- const addDevicesToFavorites = useCallback(async () => {
55
- if (selectedIds.length === 0) {
70
+ const addToFavorites = useCallback(async () => {
71
+ if (!selectedDeviceIds.length && !selectedScriptIds.length) {
56
72
  return;
57
73
  }
58
74
  setLoading(true);
59
- const { success } = await axiosPost(
60
- API.UNIT.ADD_DEVICES_TO_FAVORITES(unitId),
61
- {
62
- devices: selectedIds,
63
- }
64
- );
65
- if (success) {
66
- setAction(Action.ADD_DEVICES_TO_FAVORITES, selectedIds);
75
+
76
+ let response, success1, success2;
77
+
78
+ if (selectedDeviceIds.length) {
79
+ response = await axiosPost(API.UNIT.ADD_DEVICES_TO_FAVORITES(unitId), {
80
+ devices: selectedDeviceIds,
81
+ });
82
+ success1 = response.success;
83
+ }
84
+ if (selectedScriptIds.length) {
85
+ response = await axiosPost(API.UNIT.STAR_AUTOMATE_SCRIPTS(unitId), {
86
+ scripts: selectedScriptIds,
87
+ });
88
+ success2 = response.success;
89
+ }
90
+ success1 && setAction(Action.ADD_DEVICES_TO_FAVORITES, selectedDeviceIds);
91
+ success2 && setAction(Action.STAR_SCRIPTS, selectedScriptIds);
92
+ if (success1 || success2) {
67
93
  goBack();
68
94
  }
95
+
69
96
  setLoading(false);
70
- }, [unitId, selectedIds, setAction, goBack]);
97
+ }, [unitId, selectedDeviceIds, selectedScriptIds, setAction, goBack]);
71
98
 
72
99
  useEffect(() => {
73
100
  fetchData();
@@ -83,7 +110,7 @@ const SelectFavoritesDevices = memo(({ route }) => {
83
110
 
84
111
  const onSelectDevice = useCallback(
85
112
  (device) => {
86
- setSelectedIds((ids) => {
113
+ setSelectedDeviceIds((ids) => {
87
114
  const index = ids.indexOf(device.id);
88
115
  if (index !== -1) {
89
116
  return ids.filter((id) => id !== device.id);
@@ -91,7 +118,20 @@ const SelectFavoritesDevices = memo(({ route }) => {
91
118
  return [...ids, device.id];
92
119
  });
93
120
  },
94
- [setSelectedIds]
121
+ [setSelectedDeviceIds]
122
+ );
123
+
124
+ const onSelectAutomateScript = useCallback(
125
+ (automate) => {
126
+ setSelectedScriptIds((ids) => {
127
+ const index = ids.indexOf(automate?.script?.id);
128
+ if (index !== -1) {
129
+ return ids.filter((id) => id !== automate?.script?.id);
130
+ }
131
+ return [...ids, automate?.script?.id];
132
+ });
133
+ },
134
+ [setSelectedScriptIds]
95
135
  );
96
136
 
97
137
  const rightComponent = useMemo(
@@ -132,13 +172,24 @@ const SelectFavoritesDevices = memo(({ route }) => {
132
172
  )}
133
173
 
134
174
  <View style={styles.boxDevices}>
135
- {stations[indexStation]?.devices &&
136
- stations[indexStation].devices.map((device) => (
175
+ {stations[indexStation]?.isSmart &&
176
+ automates.map((automate, index) => (
177
+ <AutomateScript
178
+ key={`automate_script_${index}`}
179
+ automate={automate}
180
+ onPress={onSelectAutomateScript}
181
+ isSelected={selectedScriptIds.includes(automate?.script?.id)}
182
+ />
183
+ ))}
184
+ {!stations[indexStation]?.isSmart &&
185
+ stations[indexStation]?.devices &&
186
+ stations[indexStation].devices.map((device, index) => (
137
187
  <Device
188
+ key={`device_${index}`}
138
189
  svgMain={device.icon || 'sensor'}
139
190
  title={device.name}
140
191
  sensor={device}
141
- isSelectDevice={selectedIds.includes(device.id)}
192
+ isSelectDevice={selectedDeviceIds.includes(device.id)}
142
193
  onPress={onSelectDevice}
143
194
  />
144
195
  ))}
@@ -148,12 +199,16 @@ const SelectFavoritesDevices = memo(({ route }) => {
148
199
  <BottomButtonView
149
200
  style={styles.bottomButtonView}
150
201
  mainTitle={t('done')}
151
- onPressMain={addDevicesToFavorites}
152
- typeMain={selectedIds.length === 0 ? 'disabled' : 'primary'}
202
+ onPressMain={addToFavorites}
203
+ typeMain={
204
+ !selectedDeviceIds.length && !selectedScriptIds.length
205
+ ? 'disabled'
206
+ : 'primary'
207
+ }
153
208
  />
154
209
  {loading && <FullLoading />}
155
210
  </View>
156
211
  );
157
212
  });
158
213
 
159
- export default SelectFavoritesDevices;
214
+ export default SelectAddToFavorites;
@@ -45,6 +45,18 @@ jest.mock('react-native-onesignal', () => {
45
45
  };
46
46
  });
47
47
 
48
+ jest.mock('@react-navigation/native', () => {
49
+ return {
50
+ ...jest.requireActual('@react-navigation/native'),
51
+ useNavigation: () => ({
52
+ navigate: jest.fn(),
53
+ goBack: jest.fn(),
54
+ }),
55
+ useFocusEffect: jest.fn(),
56
+ useIsFocused: () => true,
57
+ };
58
+ });
59
+
48
60
  const wrapComponent = (route, unitData, account) => (
49
61
  <SCProvider initState={mockSCStore({})}>
50
62
  <UnitDetail
@@ -51,6 +51,7 @@ jest.mock('@react-navigation/native', () => {
51
51
  useNavigation: () => ({
52
52
  navigate: mockedNavigate,
53
53
  }),
54
+ useFocusEffect: jest.fn(),
54
55
  };
55
56
  });
56
57
 
@@ -252,7 +253,6 @@ describe('Test UnitDetail', () => {
252
253
  icon_kit: '',
253
254
  id: 73,
254
255
  is_managed_by_backend: true,
255
- is_other_device: false,
256
256
  name: 'Multi-Air Quality',
257
257
  quick_action: null,
258
258
  },
@@ -278,7 +278,6 @@ describe('Test UnitDetail', () => {
278
278
  icon_kit: '',
279
279
  id: 73,
280
280
  is_managed_by_backend: true,
281
- is_other_device: false,
282
281
  name: 'HR',
283
282
  quick_action: null,
284
283
  },
@@ -398,7 +397,7 @@ describe('Test UnitDetail', () => {
398
397
  el.type === TouchableOpacity
399
398
  );
400
399
 
401
- expect(goDetailButton).toHaveLength(2);
400
+ expect(goDetailButton).toHaveLength(1);
402
401
  await act(async () => {
403
402
  await goDetailButton[0].props.onPress();
404
403
  });