@eohjsc/react-native-smart-city 0.7.7 → 0.7.9

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 (90) hide show
  1. package/assets/images/AddNewDevice/add-scan-device-icon.svg +13 -0
  2. package/assets/images/Email.svg +9 -0
  3. package/assets/images/lan.svg +3 -0
  4. package/assets/images/wifi-open.svg +3 -0
  5. package/package.json +4 -3
  6. package/src/commons/ActionGroup/StatesGridActionTemplate.js +7 -3
  7. package/src/commons/ActionGroup/__test__/StatesGridActionTemplate.test.js +7 -3
  8. package/src/commons/AlertAction/index.js +1 -0
  9. package/src/commons/Auth/AccountItem.js +17 -3
  10. package/src/commons/Auth/AccountList.js +3 -7
  11. package/src/commons/ConnectWifi/__test__/ConnectWifi.test.js +373 -0
  12. package/src/commons/ConnectWifi/index.js +201 -0
  13. package/src/commons/ConnectWifi/styles.js +69 -0
  14. package/src/commons/Device/LabelValue/__test__/LabelValue.test.js +74 -0
  15. package/src/commons/Device/LabelValue/index.js +49 -0
  16. package/src/commons/Device/LabelValue/styles.js +33 -0
  17. package/src/commons/Form/TextInputPassword.js +1 -1
  18. package/src/commons/OneTapTemplate/StatesGridActionTemplate.js +6 -2
  19. package/src/configs/API.js +12 -0
  20. package/src/configs/AccessibilityLabel.js +7 -0
  21. package/src/configs/Constants.js +1 -0
  22. package/src/hooks/Common/index.js +2 -2
  23. package/src/hooks/Common/useBlockBack.js +36 -0
  24. package/src/hooks/useMqtt.js +10 -5
  25. package/src/navigations/AddGatewayStack.js +2 -0
  26. package/src/navigations/AllGatewayStack.js +4 -0
  27. package/src/navigations/Main.js +2 -2
  28. package/src/navigations/UnitStack.js +32 -0
  29. package/src/screens/AddNewGateway/ConnectingWifiDevice.js +7 -6
  30. package/src/screens/AddNewGateway/ScanDeviceLocal.js +267 -0
  31. package/src/screens/AddNewGateway/ScanDeviceLocalStyles.js +58 -0
  32. package/src/screens/AddNewGateway/SelectDeviceSubUnit.js +10 -2
  33. package/src/screens/AddNewGateway/SelectDeviceType.js +19 -2
  34. package/src/screens/AddNewGateway/__test__/ScanDeviceLocal.test.js +475 -0
  35. package/src/screens/AddNewGateway/__test__/SelectDeviceType.test.js +2 -2
  36. package/src/screens/AddNewGateway/configs/API.js +8 -0
  37. package/src/screens/AddNewGateway/hooks/useConnectDevice.js +59 -0
  38. package/src/screens/AllGateway/GatewayInfo/__test__/index.test.js +58 -1
  39. package/src/screens/AllGateway/GatewayInfo/index.js +8 -6
  40. package/src/screens/AllGateway/GatewayWifi/__test__/index.test.js +319 -0
  41. package/src/screens/AllGateway/GatewayWifi/index.js +107 -0
  42. package/src/screens/AllGateway/Successfully/__test__/index.test.js +77 -0
  43. package/src/screens/AllGateway/Successfully/index.js +66 -0
  44. package/src/screens/AllGateway/Successfully/styles.js +35 -0
  45. package/src/screens/AllGateway/components/Information/index.js +17 -1
  46. package/src/screens/AllGateway/components/RowItem/index.js +12 -1
  47. package/src/screens/AllGateway/hooks/__test__/index.test.js +18 -0
  48. package/src/screens/AllGateway/hooks/useGateway.js +13 -0
  49. package/src/screens/Automate/AddNewAction/SetupConfigCondition.js +3 -3
  50. package/src/screens/Automate/AddNewAction/SetupScriptEmail.js +79 -0
  51. package/src/screens/Automate/AddNewAction/SetupScriptReceiverEmail.js +166 -0
  52. package/src/screens/Automate/AddNewAction/Styles/SetupScriptEmailStyles.js +37 -0
  53. package/src/screens/Automate/AddNewAction/Styles/SetupScriptReceiverEmailStyles.js +79 -0
  54. package/src/screens/Automate/AddNewAction/__test__/ChooseAction.test.js +1 -1
  55. package/src/screens/Automate/AddNewAction/__test__/SetupConfigCondition.test.js +13 -5
  56. package/src/screens/Automate/AddNewAction/__test__/SetupScriptEmail.test.js +76 -0
  57. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverEmail.test.js +105 -0
  58. package/src/screens/Automate/EditActionsList/Styles/UpdateReceiverEmailScriptStyles.js +78 -0
  59. package/src/screens/Automate/EditActionsList/UpdateEmailScript.js +80 -0
  60. package/src/screens/Automate/EditActionsList/UpdateReceiverEmailScript.js +179 -0
  61. package/src/screens/Automate/EditActionsList/__tests__/UpdateEmailScript.test.js +81 -0
  62. package/src/screens/Automate/EditActionsList/__tests__/UpdateReceiverEmailScript.test.js +83 -0
  63. package/src/screens/Automate/EditActionsList/__tests__/index.test.js +38 -5
  64. package/src/screens/Automate/EditActionsList/index.js +59 -2
  65. package/src/screens/Automate/ScriptDetail/Components/AddActionScript.js +20 -0
  66. package/src/screens/Automate/ScriptDetail/Styles/indexStyles.js +5 -3
  67. package/src/screens/Automate/ScriptDetail/__test__/index.test.js +127 -21
  68. package/src/screens/Automate/ScriptDetail/index.js +57 -14
  69. package/src/screens/Device/__test__/sensorDisplayItem.test.js +22 -0
  70. package/src/screens/Device/components/SensorDisplayItem.js +10 -0
  71. package/src/screens/SharedUnit/index.js +2 -2
  72. package/src/screens/Sharing/SelectUser.js +47 -47
  73. package/src/screens/Sharing/__test__/SelectUser.test.js +57 -103
  74. package/src/screens/SubUnit/ManageSubUnit.js +94 -90
  75. package/src/screens/SubUnit/ManageSubUnitStyles.js +4 -6
  76. package/src/screens/SubUnit/RearrageSubUnit.js +90 -0
  77. package/src/screens/SubUnit/RearrrageSubUnitStyle.js +65 -0
  78. package/src/screens/SubUnit/__test__/ManageSubUnit.test.js +35 -19
  79. package/src/screens/SubUnit/__test__/RearrangeSubUnit.test.js +129 -0
  80. package/src/screens/SubUnit/hooks/__test__/useManageSubUnit.test.js +6 -7
  81. package/src/screens/SubUnit/hooks/useManageSubUnit.js +8 -16
  82. package/src/screens/Unit/Detail.js +2 -6
  83. package/src/screens/Unit/ManageUnit.js +1 -1
  84. package/src/utils/Functions/__test__/ShortEmail.test.js +5 -0
  85. package/src/utils/I18n/translations/en.js +46 -8
  86. package/src/utils/I18n/translations/vi.js +37 -4
  87. package/src/utils/Route/index.js +7 -0
  88. package/src/commons/Auth/__test__/AccountItem.test.js +0 -31
  89. package/src/hooks/Common/useBlockBackAndroid.js +0 -21
  90. package/src/screens/SubUnit/DetailStyles.js +0 -46
@@ -8,6 +8,7 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler';
8
8
  import Close from '../../../../assets/images/Close.svg';
9
9
  import Delay from '../../../../assets/images/Delay.svg';
10
10
  import Notify from '../../../../assets/images/Notify.svg';
11
+ import Email from '../../../../assets/images/Email.svg';
11
12
  import Rearrange from '../../../../assets/images/Rearrange.svg';
12
13
  import { FullLoading } from '../../../commons';
13
14
  import FImage from '../../../commons/FImage';
@@ -23,6 +24,7 @@ import { ToastBottomHelper } from '../../../utils/Utils';
23
24
  import styles from './Styles/indexStyles';
24
25
  import UpdateDelayScript from './UpdateDelayScript';
25
26
  import UpdateNotifyScript from './UpdateNotifyScript';
27
+ import UpdateEmailScript from './UpdateEmailScript';
26
28
 
27
29
  const EditActionsList = () => {
28
30
  const t = useTranslations();
@@ -138,7 +140,7 @@ const EditActionsList = () => {
138
140
  ({ item, getIndex, drag, isActive }) => {
139
141
  const index = getIndex();
140
142
  const paddedIndex = (index + 1).toString().padStart(2, '0');
141
- const { action_script, notify_script, delay_script } = item;
143
+ const { action_script, notify_script, delay_script, email_script } = item;
142
144
  if (action_script) {
143
145
  const {
144
146
  sensor_icon_kit,
@@ -236,6 +238,37 @@ const EditActionsList = () => {
236
238
  />
237
239
  );
238
240
  }
241
+ if (email_script) {
242
+ const { title, message, str_emails } = email_script;
243
+
244
+ return (
245
+ <CommonItem
246
+ paddedIndex={paddedIndex}
247
+ icon={
248
+ <View style={styles.iconItem}>
249
+ <Email />
250
+ </View>
251
+ }
252
+ content={
253
+ <>
254
+ <Text numberOfLines={1} type="H4" color={Colors.Gray9} semibold>
255
+ {title}
256
+ </Text>
257
+ <Text numberOfLines={1} type="H4" color={Colors.Gray9}>
258
+ {message}
259
+ </Text>
260
+ <Text numberOfLines={1} type="H4" color={Colors.Gray9}>
261
+ {str_emails}
262
+ </Text>
263
+ </>
264
+ }
265
+ onPress={() => onPressRemove(item)}
266
+ onPressUpdate={() => onShowPopupUpdate(item, index)}
267
+ isActive={isActive}
268
+ drag={drag}
269
+ />
270
+ );
271
+ }
239
272
  },
240
273
  // eslint-disable-next-line react-hooks/exhaustive-deps
241
274
  [needRefresh]
@@ -246,6 +279,7 @@ const EditActionsList = () => {
246
279
  action_script,
247
280
  notify_script,
248
281
  delay_script,
282
+ email_script,
249
283
  id: scriptItemId,
250
284
  } = scriptItem;
251
285
 
@@ -294,6 +328,18 @@ const EditActionsList = () => {
294
328
  />
295
329
  );
296
330
  }
331
+ if (email_script) {
332
+ return (
333
+ <UpdateEmailScript
334
+ unitId={unitId}
335
+ automateId={id}
336
+ scriptItemId={scriptItemId}
337
+ email_script={email_script}
338
+ t={t}
339
+ onClosePopup={onClosePopup}
340
+ />
341
+ );
342
+ }
297
343
  }, [actionsList, id, navigate, scriptItem, t, unitId, updateIndex]);
298
344
 
299
345
  const onDragEnd = useCallback(
@@ -309,7 +355,8 @@ const EditActionsList = () => {
309
355
  );
310
356
 
311
357
  const renderMessageRemove = useMemo(() => {
312
- const { action_script, notify_script, delay_script } = itemRemove;
358
+ const { action_script, notify_script, delay_script, email_script } =
359
+ itemRemove;
313
360
  if (action_script) {
314
361
  return (
315
362
  <ParsedText
@@ -353,6 +400,16 @@ const EditActionsList = () => {
353
400
  </ParsedText>
354
401
  );
355
402
  }
403
+ if (email_script) {
404
+ return (
405
+ <ParsedText
406
+ style={styles.messageDelete}
407
+ childrenProps={{ allowFontScaling: false }}
408
+ >
409
+ {t('message_delete_notify', { title: email_script.title })}
410
+ </ParsedText>
411
+ );
412
+ }
356
413
  }, [itemRemove, t]);
357
414
 
358
415
  return (
@@ -10,6 +10,7 @@ import { useBackendPermission } from '../../../../utils/Permission/backend';
10
10
  import Event from '../../../../../assets/images/Event.svg';
11
11
  import Notify from '../../../../../assets/images/Notify.svg';
12
12
  import Delay from '../../../../../assets/images/Delay.svg';
13
+ import Email from '../../../../../assets/images/Email.svg';
13
14
  import { TouchableOpacity } from 'react-native';
14
15
  import { IconOutline } from '@ant-design/icons-react-native';
15
16
  import { Text } from '../../../../commons';
@@ -79,6 +80,25 @@ const AddActionScript = memo(
79
80
  });
80
81
  },
81
82
  },
83
+ {
84
+ id: 'send_email',
85
+ text: t('send_email'),
86
+ image: <Email />,
87
+ onClick: () => {
88
+ setIsVisible(false);
89
+ if (unit) {
90
+ navigate(Routes.SetupScriptEmail, {
91
+ automate,
92
+ unitId: unit,
93
+ });
94
+ } else {
95
+ navigate(Routes.SelectUnit, {
96
+ automate,
97
+ routeName: Routes.SetupScriptEmail,
98
+ });
99
+ }
100
+ },
101
+ },
82
102
  ];
83
103
  }, [
84
104
  automate,
@@ -101,13 +101,15 @@ export default StyleSheet.create({
101
101
  },
102
102
  rightItem: {
103
103
  flex: 1,
104
- marginLeft: 4,
105
- borderRadius: 4,
106
104
  borderColor: Colors.Gray4,
107
105
  borderWidth: 1,
108
- flexDirection: 'row',
106
+ borderRadius: 4,
109
107
  paddingVertical: 15,
110
108
  paddingHorizontal: 16,
109
+ marginLeft: 4,
110
+ },
111
+ insideItemRight: {
112
+ flexDirection: 'row',
111
113
  },
112
114
  rightItemAdd: {
113
115
  flex: 1,
@@ -38,10 +38,13 @@ describe('Test ScriptDetail', () => {
38
38
  let tree;
39
39
 
40
40
  const mockGoBack = useNavigation().goBack;
41
+ const mockAddListener = useNavigation().addListener;
41
42
  const mockedNavigate = useNavigation().navigate;
42
43
  beforeEach(() => {
43
44
  mockGoBack.mockClear();
45
+ mockAddListener.mockClear();
44
46
  mockedNavigate.mockClear();
47
+ global.mockedNavigate.mockClear();
45
48
  mock.reset();
46
49
  route = {
47
50
  params: {
@@ -91,6 +94,16 @@ describe('Test ScriptDetail', () => {
91
94
  notify_script: null,
92
95
  delay_script: { delay: 10 },
93
96
  },
97
+ {
98
+ action_script: null,
99
+ notify_script: null,
100
+ email_script: {
101
+ title: 'title',
102
+ message: 'message',
103
+ unit_id: 60,
104
+ str_emails: 'email0@eoh.io',
105
+ },
106
+ },
94
107
  ],
95
108
  };
96
109
  });
@@ -165,12 +178,12 @@ describe('Test ScriptDetail', () => {
165
178
  await act(async () => {
166
179
  await menu.props.onPress();
167
180
  });
168
- expect(spyToastError).toBeCalledWith(
181
+ expect(spyToastError).toHaveBeenCalledWith(
169
182
  getTranslate('en', 'only_owner_has_permission_to_edit_this_script')
170
183
  );
171
184
  });
172
185
 
173
- it('test cannot change type script', async () => {
186
+ it('test cannot edit condition because not owner', async () => {
174
187
  route.params.preAutomate.can_edit = false;
175
188
  const spyToastError = jest.spyOn(ToastBottomHelper, 'error');
176
189
 
@@ -194,14 +207,46 @@ describe('Test ScriptDetail', () => {
194
207
 
195
208
  await act(async () => {
196
209
  menuActionMore.props.onItemClick({ doAction: jest.fn() });
197
- menuActionMore.props.listMenuItem[1].doAction();
210
+ menuActionMore.props.listMenuItem[0].doAction();
198
211
  });
199
212
 
200
- expect(spyToastError).toBeCalledWith(
213
+ expect(spyToastError).toHaveBeenCalledWith(
201
214
  getTranslate('en', 'only_owner_has_permission_to_edit_this_script')
202
215
  );
203
216
  });
204
217
 
218
+ it('test cannot edit condition because script disabled', async () => {
219
+ route.params.preAutomate.script.enable = false;
220
+ const spyToastError = jest.spyOn(ToastBottomHelper, 'error');
221
+
222
+ await act(async () => {
223
+ tree = await create(wrapComponent(route));
224
+ });
225
+ const instance = tree.root;
226
+ const button = instance.find(
227
+ (el) =>
228
+ el.props.accessibilityLabel === AccessibilityLabel.ICON_MENU_POPUP &&
229
+ el.type === TouchableOpacity
230
+ );
231
+ await act(async () => {
232
+ await button.props.onPress();
233
+ });
234
+
235
+ const menuActionMore = instance.find(
236
+ (el) =>
237
+ el.props.accessibilityLabel === AccessibilityLabel.MENU_POPPER_MORE_2
238
+ );
239
+
240
+ await act(async () => {
241
+ menuActionMore.props.onItemClick({ doAction: jest.fn() });
242
+ menuActionMore.props.listMenuItem[0].doAction();
243
+ });
244
+
245
+ expect(spyToastError).toBeCalledWith(
246
+ getTranslate('en', 'this_script_has_been_disabled')
247
+ );
248
+ });
249
+
205
250
  it('test delete script', async () => {
206
251
  await act(async () => {
207
252
  tree = await create(wrapComponent(route));
@@ -322,7 +367,7 @@ describe('Test ScriptDetail', () => {
322
367
  AccessibilityLabel.AUTOMATE_LIST_SCRIPT_ACTION &&
323
368
  el.type === TouchableOpacity
324
369
  );
325
- expect(listScriptActions).toHaveLength(3);
370
+ expect(listScriptActions).toHaveLength(4);
326
371
  await act(async () => {
327
372
  await listScriptActions[0].props.onPress();
328
373
  });
@@ -360,6 +405,18 @@ describe('Test ScriptDetail', () => {
360
405
  automate: route.params.preAutomate,
361
406
  }
362
407
  );
408
+ mockedNavigate.mockClear();
409
+ await act(async () => {
410
+ await listScriptActions[3].props.onPress();
411
+ });
412
+
413
+ expect(global.mockedNavigate).toHaveBeenCalledWith(
414
+ Routes.SetupScriptEmail,
415
+ {
416
+ automate: route.params.preAutomate,
417
+ unitId: route.params.preAutomate.unit,
418
+ }
419
+ );
363
420
  });
364
421
 
365
422
  it('test press disable script', async () => {
@@ -418,8 +475,8 @@ describe('Test ScriptDetail', () => {
418
475
  await act(async () => {
419
476
  await button.props.onPress();
420
477
  });
421
- expect(global.mockedNavigate).not.toBeCalled();
422
- expect(spyToastError).toBeCalledWith(
478
+ expect(global.mockedNavigate).not.toHaveBeenCalled();
479
+ expect(spyToastError).toHaveBeenCalledWith(
423
480
  getTranslate('en', 'reach_max_actions_per_automation', { length: 0 }),
424
481
  '',
425
482
  7000
@@ -500,21 +557,24 @@ describe('Test ScriptDetail', () => {
500
557
  _testGoToActivityLog(3, AUTOMATE_TYPE.VALUE_CHANGE, 'automate', undefined);
501
558
 
502
559
  it('Test render textCondition value change >', async () => {
560
+ const automate = {
561
+ can_edit: true,
562
+ type: AUTOMATE_TYPE.VALUE_CHANGE,
563
+ value_change: {
564
+ config_name: 'Light Value',
565
+ value: 3,
566
+ condition: '>',
567
+ end_device_id: 1,
568
+ unit_id: 1,
569
+ },
570
+ script: {
571
+ name: 'name',
572
+ enable: true,
573
+ },
574
+ };
503
575
  route.params = {
504
576
  ...route.params,
505
- preAutomate: {
506
- can_edit: true,
507
- type: AUTOMATE_TYPE.VALUE_CHANGE,
508
- value_change: {
509
- config_name: 'Light Value',
510
- value: 3,
511
- condition: '>',
512
- },
513
- script: {
514
- name: 'name',
515
- enable: true,
516
- },
517
- },
577
+ preAutomate: automate,
518
578
  };
519
579
  await act(async () => {
520
580
  tree = await create(wrapComponent(route));
@@ -534,9 +594,29 @@ describe('Test ScriptDetail', () => {
534
594
  );
535
595
  await act(async () => {
536
596
  menuActionMore.props.onItemClick({ doAction: jest.fn() });
597
+ menuActionMore.props.listMenuItem[2].doAction();
598
+ });
599
+ expect(global.mockedNavigate).toBeCalledWith(Routes.AddUnknownTypeSmart, {
600
+ automate,
601
+ closeScreen: undefined,
602
+ });
603
+ global.mockedNavigate.mockClear();
604
+ expect(menuActionMore.props.listMenuItem[0].text).toEqual('Device display');
605
+ await act(async () => {
537
606
  menuActionMore.props.listMenuItem[0].doAction();
538
607
  });
539
- expect(global.mockedNavigate).toBeCalled();
608
+ expect(global.mockedNavigate).toBeCalledWith(Routes.DeviceDetail, {
609
+ sensorId: 1,
610
+ unitId: 1,
611
+ });
612
+ global.mockedNavigate.mockClear();
613
+ expect(menuActionMore.props.listMenuItem[1].text).toEqual('Unit');
614
+ await act(async () => {
615
+ menuActionMore.props.listMenuItem[1].doAction();
616
+ });
617
+ expect(global.mockedNavigate).toBeCalledWith(Routes.UnitDetail, {
618
+ unitId: 1,
619
+ });
540
620
  });
541
621
 
542
622
  it('Test render textCondition value change =', async () => {
@@ -623,4 +703,30 @@ describe('Test ScriptDetail', () => {
623
703
  'Mon, Tue, Thu, Sat at 19:00'
624
704
  );
625
705
  });
706
+
707
+ it('test navigate to UnitDetail on event beforeRemove', async () => {
708
+ route.params.closeScreen = Routes.UnitDetail;
709
+ route.params.preAutomate.unit = 2;
710
+
711
+ await act(async () => {
712
+ await create(wrapComponent(route));
713
+ });
714
+
715
+ // beforeRemove test when swipe to back
716
+ const navigation = useNavigation();
717
+ const beforeRemoveEvent = { preventDefault: jest.fn() };
718
+ const beforeRemoveListener = navigation.addListener.mock.calls.find(
719
+ ([event]) => event === 'beforeRemove'
720
+ )[1];
721
+
722
+ await act(async () => {
723
+ beforeRemoveListener(beforeRemoveEvent);
724
+ });
725
+
726
+ expect(mockAddListener).toHaveBeenCalled();
727
+ expect(beforeRemoveEvent.preventDefault).toHaveBeenCalled();
728
+ expect(mockedNavigate).toHaveBeenCalledWith(Routes.UnitDetail, {
729
+ unitId: 2,
730
+ });
731
+ });
626
732
  });
@@ -14,6 +14,7 @@ import { useIsFocused, useNavigation } from '@react-navigation/native';
14
14
  import Add from '../../../../assets/images/Add.svg';
15
15
  import Delay from '../../../../assets/images/Delay.svg';
16
16
  import Notify from '../../../../assets/images/Notify.svg';
17
+ import Email from '../../../../assets/images/Email.svg';
17
18
  import IconComponent from '../../../commons/IconComponent';
18
19
  import MenuActionMore from '../../../commons/MenuActionMore';
19
20
  import WrapHeaderScrollable from '../../../commons/Sharing/WrapHeaderScrollable';
@@ -22,7 +23,7 @@ import withPreventDoubleClick from '../../../commons/WithPreventDoubleClick';
22
23
  import { API, Colors } from '../../../configs';
23
24
  import { AccessibilityLabel, AUTOMATE_TYPE } from '../../../configs/Constants';
24
25
  import Images from '../../../configs/Images';
25
- import { usePopover } from '../../../hooks/Common';
26
+ import { useBlockBack, usePopover } from '../../../hooks/Common';
26
27
  import { useTranslations } from '../../../hooks/Common/useTranslations';
27
28
  import { axiosGet, axiosPost } from '../../../utils/Apis/axios';
28
29
  import { useBackendPermission } from '../../../utils/Permission/backend';
@@ -127,7 +128,16 @@ const ScriptDetail = ({ route }) => {
127
128
  const items = [
128
129
  { text: t('edit_condition'), doAction: () => handleUpdateAutomate() },
129
130
  ];
130
- if (value_change) {
131
+ if (unit_id) {
132
+ items.unshift({
133
+ text: t('text_unit'),
134
+ doAction: () =>
135
+ navigate(Routes.UnitDetail, {
136
+ unitId: unit_id,
137
+ }),
138
+ });
139
+ }
140
+ if (end_device_id) {
131
141
  items.unshift({
132
142
  text: t('device_display'),
133
143
  doAction: () =>
@@ -138,7 +148,7 @@ const ScriptDetail = ({ route }) => {
138
148
  });
139
149
  }
140
150
  return items;
141
- }, [t, handleUpdateAutomate, value_change, navigate, unit_id, end_device_id]);
151
+ }, [t, handleUpdateAutomate, navigate, unit_id, end_device_id]);
142
152
 
143
153
  const handleShowMenuAction = useCallback(() => {
144
154
  if (!can_edit) {
@@ -219,6 +229,8 @@ const ScriptDetail = ({ route }) => {
219
229
  [automateId]
220
230
  );
221
231
 
232
+ useBlockBack(handleGoBack);
233
+
222
234
  useEffect(() => {
223
235
  if (isFocused) {
224
236
  fetchAutomate();
@@ -385,7 +397,7 @@ const ScriptDetail = ({ route }) => {
385
397
  const Item = ({ item, index, enableScript, t }) => {
386
398
  const color = enableScript ? Colors.Gray9 : Colors.Gray7;
387
399
  const paddedIndex = (index + 1).toString().padStart(2, '0');
388
- const { action_script, notify_script, delay_script } = item;
400
+ const { action_script, notify_script, delay_script, email_script } = item;
389
401
  if (action_script) {
390
402
  const {
391
403
  sensor_icon_kit,
@@ -444,16 +456,18 @@ const Item = ({ item, index, enableScript, t }) => {
444
456
  </Text>
445
457
  </View>
446
458
  <View style={styles.rightItem}>
447
- <View style={styles.iconEndDevice}>
448
- <Notify />
449
- </View>
450
- <View style={styles.contentItem}>
451
- <Text numberOfLines={1} type="H4" color={color} semibold>
452
- {title}
453
- </Text>
454
- <Text numberOfLines={1} type="H4" color={color}>
455
- {message}
456
- </Text>
459
+ <View style={styles.insideItemRight}>
460
+ <View style={styles.iconEndDevice}>
461
+ <Notify />
462
+ </View>
463
+ <View style={styles.contentItem}>
464
+ <Text numberOfLines={1} type="H4" color={color} semibold>
465
+ {title}
466
+ </Text>
467
+ <Text numberOfLines={1} type="H4" color={color}>
468
+ {message}
469
+ </Text>
470
+ </View>
457
471
  </View>
458
472
  </View>
459
473
  </View>
@@ -482,6 +496,35 @@ const Item = ({ item, index, enableScript, t }) => {
482
496
  </View>
483
497
  </View>
484
498
  );
499
+ } else if (email_script) {
500
+ const { title, message, str_emails } = email_script;
501
+ return (
502
+ <View style={styles.wrapItem}>
503
+ <View style={styles.leftItem}>
504
+ <Text color={color} type="H4" semibold>
505
+ {paddedIndex}
506
+ </Text>
507
+ </View>
508
+ <View style={styles.rightItem}>
509
+ <View style={styles.insideItemRight}>
510
+ <View style={styles.iconEndDevice}>
511
+ <Email />
512
+ </View>
513
+ <View style={styles.contentItem}>
514
+ <Text numberOfLines={1} type="H4" color={color} semibold>
515
+ {title}
516
+ </Text>
517
+ <Text numberOfLines={1} type="H4" color={color}>
518
+ {message}
519
+ </Text>
520
+ </View>
521
+ </View>
522
+ <Text numberOfLines={1} type="H4" color={color}>
523
+ {str_emails}
524
+ </Text>
525
+ </View>
526
+ </View>
527
+ );
485
528
  }
486
529
  };
487
530
 
@@ -17,6 +17,7 @@ import DeviceAlertStatus from '../../../commons/Device/DeviceAlertStatus';
17
17
  import MockAdapter from 'axios-mock-adapter';
18
18
  import api from '../../../utils/Apis/axios';
19
19
  import CurrentRainSensor from '../../../commons/Device/RainningSensor/CurrentRainSensor';
20
+ import LabelValue from '../../../commons/Device/LabelValue';
20
21
 
21
22
  jest.mock('../../../iot/states', () => ({
22
23
  useConfigGlobalState: () => [{}, null],
@@ -410,4 +411,25 @@ describe('Test SensorDisplayItem', () => {
410
411
  isWidgetOrder: undefined,
411
412
  });
412
413
  });
414
+ it('test render LabelValue', async () => {
415
+ const item = {
416
+ id: 10452,
417
+ order: 0,
418
+ template: 'LabelValue',
419
+ configuration: {},
420
+ is_configuration_ready: true,
421
+ };
422
+
423
+ const sensor = {
424
+ name: 'Sensor name',
425
+ is_managed_by_backend: false,
426
+ };
427
+
428
+ await act(async () => {
429
+ tree = await renderer.create(wrapComponent({ item, sensor }));
430
+ });
431
+ const instance = tree.root;
432
+ const displayItems = instance.findAllByType(LabelValue);
433
+ expect(displayItems).toHaveLength(1);
434
+ });
413
435
  });
@@ -8,6 +8,7 @@ import FlatListItems from '../../../commons/Device/FlatListItems';
8
8
  import FooterInfo from '../../../commons/Device/FooterInfo';
9
9
  import PMSensorIndicator from '../../../commons/Device/PMSensor/PMSensorIndicator';
10
10
  import ProgressBar from '../../../commons/Device/ProgressBar';
11
+ import LabelValue from '../../../commons/Device/LabelValue';
11
12
  import CurrentRainSensor from '../../../commons/Device/RainningSensor/CurrentRainSensor';
12
13
  import ListQualityIndicator from '../../../commons/Device/WaterQualitySensor/ListQualityIndicator';
13
14
  import Compass from '../../../commons/Device/WindDirection/Compass';
@@ -216,6 +217,15 @@ export const SensorDisplayItem = ({
216
217
  isWidgetOrder={isWidgetOrder}
217
218
  />
218
219
  );
220
+ // use the same method to get data for circle_mini
221
+ case 'LabelValue':
222
+ return (
223
+ <LabelValue
224
+ data={getDataCircleMini(item)}
225
+ item={item}
226
+ isWidgetOrder={isWidgetOrder}
227
+ />
228
+ );
219
229
  case 'value':
220
230
  switch (type || template) {
221
231
  case 'circle':
@@ -7,7 +7,7 @@ import { useTranslations } from '../../hooks/Common/useTranslations';
7
7
  import { API } from '../../configs';
8
8
  import Text from '../../commons/Text';
9
9
  import SharedUnit from '../../commons/Unit/SharedUnit';
10
- import { useBlockBackAndroid } from '../../hooks/Common';
10
+ import { useBlockBack } from '../../hooks/Common';
11
11
  import { AccessibilityLabel } from '../../configs/Constants';
12
12
 
13
13
  import styles from './styles';
@@ -17,7 +17,7 @@ import { ModalCustom } from '../../commons/Modal';
17
17
  import useTitleHeader from '../../hooks/Common/useTitleHeader';
18
18
 
19
19
  const Shared = () => {
20
- useBlockBackAndroid();
20
+ useBlockBack();
21
21
  const t = useTranslations();
22
22
  useTitleHeader(t('text_shared_with_me'));
23
23
  const navigation = useNavigation();