@eohjsc/react-native-smart-city 0.7.14 → 0.7.16

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 (33) hide show
  1. package/assets/images/Sms.svg +9 -0
  2. package/package.json +1 -1
  3. package/src/commons/Automate/ItemConditionScriptDetailStyles.js +1 -0
  4. package/src/commons/Widgets/IFrame/IFrame.js +2 -2
  5. package/src/commons/Widgets/IFrame/IFrameStyles.js +5 -0
  6. package/src/configs/API.js +8 -2
  7. package/src/hooks/Common/useBlockBack.js +36 -21
  8. package/src/hooks/Common/useDevicesStatus.js +16 -13
  9. package/src/navigations/UnitStack.js +24 -0
  10. package/src/screens/AddNewGateway/ScanDeviceLocal.js +14 -16
  11. package/src/screens/AddNewGateway/ScanDeviceLocalStyles.js +6 -1
  12. package/src/screens/AddNewGateway/__test__/ScanDeviceLocal.test.js +4 -13
  13. package/src/screens/Automate/AddNewAction/SetupScriptReceiverSms.js +167 -0
  14. package/src/screens/Automate/AddNewAction/SetupScriptSms.js +73 -0
  15. package/src/screens/Automate/AddNewAction/Styles/SetupScriptEmailStyles.js +5 -0
  16. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverSms.test.js +105 -0
  17. package/src/screens/Automate/AddNewAction/__test__/SetupScriptSms.test.js +70 -0
  18. package/src/screens/Automate/EditActionsList/UpdateReceiverSmsScript.js +178 -0
  19. package/src/screens/Automate/EditActionsList/UpdateSmsScript.js +66 -0
  20. package/src/screens/Automate/EditActionsList/__tests__/UpdateReceiverSmsScript.test.js +82 -0
  21. package/src/screens/Automate/EditActionsList/__tests__/UpdateSmsScript.test.js +71 -0
  22. package/src/screens/Automate/EditActionsList/index.js +50 -1
  23. package/src/screens/Automate/ScriptDetail/Components/AddActionScript.js +52 -19
  24. package/src/screens/Automate/ScriptDetail/Styles/indexStyles.js +39 -2
  25. package/src/screens/Automate/ScriptDetail/__test__/index.test.js +106 -38
  26. package/src/screens/Automate/ScriptDetail/index.js +227 -10
  27. package/src/screens/SharedUnit/__test__/ShareUnit.test.js +22 -1
  28. package/src/screens/Sharing/UnitMemberList.js +1 -1
  29. package/src/screens/Sharing/__test__/UnitMemberList.test.js +10 -0
  30. package/src/screens/Unit/__test__/Detail.test.js +1 -1
  31. package/src/utils/I18n/translations/en.js +10 -0
  32. package/src/utils/I18n/translations/vi.js +10 -0
  33. package/src/utils/Route/index.js +3 -0
@@ -10,11 +10,16 @@ import { PopoverMode } from 'react-native-popover-view';
10
10
  import { IconFill, IconOutline } from '@ant-design/icons-react-native';
11
11
  import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
12
12
 
13
- import { useIsFocused, useNavigation } from '@react-navigation/native';
13
+ import {
14
+ CommonActions,
15
+ useIsFocused,
16
+ useNavigation,
17
+ } from '@react-navigation/native';
14
18
  import Add from '../../../../assets/images/Add.svg';
15
19
  import Delay from '../../../../assets/images/Delay.svg';
16
20
  import Notify from '../../../../assets/images/Notify.svg';
17
21
  import Email from '../../../../assets/images/Email.svg';
22
+ import Sms from '../../../../assets/images/Sms.svg';
18
23
  import IconComponent from '../../../commons/IconComponent';
19
24
  import MenuActionMore from '../../../commons/MenuActionMore';
20
25
  import WrapHeaderScrollable from '../../../commons/Sharing/WrapHeaderScrollable';
@@ -35,11 +40,13 @@ import RenameScript from './Components/RenameScript';
35
40
  import { useStarredScript } from './hooks/useStarredScript';
36
41
  import styles from './Styles/indexStyles';
37
42
  import ItemConditionScriptDetail from '../../../commons/Automate/ItemConditionScriptDetail';
43
+ import { ModalCustom } from '../../../commons/Modal';
44
+ import { Card } from '../../../commons/CardShadow';
38
45
 
39
46
  const PreventDoubleTouch = withPreventDoubleClick(TouchableOpacity);
40
47
 
41
48
  const ScriptDetail = ({ route }) => {
42
- const { navigate, goBack } = useNavigation();
49
+ const { dispatch, navigate, goBack } = useNavigation();
43
50
  const { params = {} } = route;
44
51
  const refMenuAction = useRef();
45
52
  const { childRef, showingPopover, showPopoverWithRef, hidePopover } =
@@ -56,14 +63,16 @@ const ScriptDetail = ({ route }) => {
56
63
  preAutomate = {}, // pre-loaded automate data
57
64
  newActionsList, // updated actions list
58
65
  closeScreen,
66
+ automate: currentAutomate, // when edit and close
59
67
  } = params;
60
- const [automate, setAutomate] = useState(preAutomate);
68
+ const [automate, setAutomate] = useState(currentAutomate || preAutomate);
61
69
  const isFocused = useIsFocused();
62
70
  const [data, setData] = useState([]);
63
71
  const [isShowRename, setIsShowRename] = useState(false);
64
72
  const [isShowDelete, setIsShowDelete] = useState(false);
65
73
  const [isShowAddAction, setIsShowAddAction] = useState(false);
66
-
74
+ const [isShowLocalControl, setIsShowLocalControl] = useState(false);
75
+ const [listChipShared, setListChipShared] = useState([]);
67
76
  const {
68
77
  script,
69
78
  type,
@@ -74,7 +83,18 @@ const ScriptDetail = ({ route }) => {
74
83
  value_change,
75
84
  } = automate;
76
85
  const { end_device_id, unit_id } = value_change || {};
77
- const { enable } = script || {};
86
+ const {
87
+ enable,
88
+ chip_local_control,
89
+ is_local_control,
90
+ chip_id_local_control,
91
+ } = script || {};
92
+
93
+ const [local_control, setLocalControl] = useState({
94
+ is_local_control,
95
+ chip_local_control,
96
+ chip_id_local_control,
97
+ });
78
98
  const [enableScript, setEnableScript] = useState(enable);
79
99
  const refMenuAction1 = useRef();
80
100
  const onShowActivityLog = useCallback(() => {
@@ -92,6 +112,42 @@ const ScriptDetail = ({ route }) => {
92
112
  });
93
113
  }, [navigate, automateId, type, unit]);
94
114
 
115
+ const handleNavigate = useCallback(
116
+ (screen, screenParams) => {
117
+ dispatch((state) => {
118
+ const index = state.routes.findIndex((r) => r.name === closeScreen);
119
+ let routes = [];
120
+ if (!closeScreen) {
121
+ routes = state.routes;
122
+ } else if (index >= 0) {
123
+ routes = state.routes.slice(0, index + 1);
124
+ }
125
+ const hasScriptDetail = routes.some(
126
+ (r) => r.name === Routes.ScriptDetail
127
+ );
128
+
129
+ if (!hasScriptDetail) {
130
+ routes.push({
131
+ name: Routes.ScriptDetail,
132
+ params: route.params,
133
+ });
134
+ }
135
+
136
+ routes.push({
137
+ name: screen,
138
+ params: screenParams,
139
+ });
140
+
141
+ return CommonActions.reset({
142
+ ...state,
143
+ routes: routes,
144
+ index: routes.length - 1,
145
+ });
146
+ });
147
+ },
148
+ [closeScreen, dispatch, route.params]
149
+ );
150
+
95
151
  const handleUpdateAutomate = useCallback(async () => {
96
152
  if (!can_edit) {
97
153
  ToastBottomHelper.error(
@@ -103,11 +159,13 @@ const ScriptDetail = ({ route }) => {
103
159
  ToastBottomHelper.error(t('this_script_has_been_disabled'));
104
160
  return;
105
161
  }
106
- navigate(Routes.AddUnknownTypeSmart, {
162
+
163
+ // Reset stack and navigate to AddUnknownTypeSmart
164
+ handleNavigate(Routes.AddUnknownTypeSmart, {
107
165
  automate,
108
166
  closeScreen: route.name,
109
167
  });
110
- }, [automate, can_edit, enableScript, navigate, route.name, t]);
168
+ }, [automate, can_edit, enableScript, route.name, t, handleNavigate]);
111
169
 
112
170
  const listMenuItem = useMemo(
113
171
  () => [
@@ -273,6 +331,83 @@ const ScriptDetail = ({ route }) => {
273
331
  showPopoverWithRef1(refMenuAction1);
274
332
  }, [showPopoverWithRef1, refMenuAction1]);
275
333
 
334
+ const onCloseLocalControl = () => setIsShowLocalControl(false);
335
+
336
+ const fetchChipShared = useCallback(async () => {
337
+ const { success, data: listChip } = await axiosGet(
338
+ API.DEV_MODE.GATEWAY.SHARED(),
339
+ {
340
+ unit: unit_id,
341
+ },
342
+ true
343
+ );
344
+ if (success) {
345
+ setListChipShared(listChip);
346
+ }
347
+ }, [unit_id]);
348
+
349
+ const onShowLocalControl = useCallback(async () => {
350
+ setIsShowLocalControl(true);
351
+ }, []);
352
+
353
+ useEffect(() => {
354
+ fetchChipShared();
355
+ }, [fetchChipShared]);
356
+
357
+ const onPressSelectChip = useCallback(
358
+ async (enable_local_control, item) => {
359
+ const { success } = await axiosPost(
360
+ API.AUTOMATE.ENABLE_LOCAL_CONTROL(automateId),
361
+ {
362
+ is_local_control: enable_local_control,
363
+ chip_local_control: item.id,
364
+ }
365
+ );
366
+ setIsShowLocalControl(false);
367
+ if (success) {
368
+ setLocalControl({
369
+ is_local_control: enable_local_control,
370
+ chip_local_control: item.name,
371
+ chip_id_local_control: item.id,
372
+ });
373
+ }
374
+ },
375
+ [automateId]
376
+ );
377
+
378
+ const renderLocalControl = useMemo(() => {
379
+ if (!!can_edit && type !== AUTOMATE_TYPE.ONE_TAP) {
380
+ return (
381
+ <View style={styles.row}>
382
+ <Text type="H3" semibold style={styles.width40}>
383
+ {t('local_control')}
384
+ </Text>
385
+ <Card style={styles.card}>
386
+ <TouchableOpacity
387
+ style={styles.localControl}
388
+ onPress={onShowLocalControl}
389
+ >
390
+ <Text numberOfLines={1}>
391
+ {local_control.is_local_control
392
+ ? local_control.chip_local_control
393
+ : t('disable')}
394
+ </Text>
395
+
396
+ <IconOutline name="down" size={15} style={styles.marginLeft5} />
397
+ </TouchableOpacity>
398
+ </Card>
399
+ </View>
400
+ );
401
+ }
402
+ }, [
403
+ can_edit,
404
+ local_control.chip_local_control,
405
+ local_control.is_local_control,
406
+ onShowLocalControl,
407
+ t,
408
+ type,
409
+ ]);
410
+
276
411
  return (
277
412
  <View style={styles.wrap}>
278
413
  <WrapHeaderScrollable
@@ -290,6 +425,7 @@ const ScriptDetail = ({ route }) => {
290
425
  <Switch value={enableScript} onValueChange={onChangeSwitch} />
291
426
  </View>
292
427
  )}
428
+ {renderLocalControl}
293
429
  <Text type="H3" semibold>
294
430
  {t('how_to_start')}
295
431
  </Text>
@@ -334,6 +470,7 @@ const ScriptDetail = ({ route }) => {
334
470
  index={index}
335
471
  enableScript={enableScript}
336
472
  t={t}
473
+ local_control={local_control}
337
474
  />
338
475
  ))}
339
476
  {!!can_edit && !!enableScript && (
@@ -378,6 +515,8 @@ const ScriptDetail = ({ route }) => {
378
515
  numberActionAdded={data.length}
379
516
  isVisible={isShowAddAction}
380
517
  setIsVisible={setIsShowAddAction}
518
+ type={type}
519
+ navigate={handleNavigate}
381
520
  />
382
521
  <RenameScript
383
522
  automate={automate}
@@ -390,14 +529,60 @@ const ScriptDetail = ({ route }) => {
390
529
  setIsVisible={setIsShowDelete}
391
530
  isVisible={isShowDelete}
392
531
  />
532
+ <ModalCustom
533
+ isVisible={isShowLocalControl}
534
+ onBackButtonPress={onCloseLocalControl}
535
+ onBackdropPress={onCloseLocalControl}
536
+ >
537
+ <View style={styles.popoverStyle}>
538
+ <TouchableOpacity
539
+ style={styles.textDisable}
540
+ onPress={() => {
541
+ onPressSelectChip(false, {
542
+ id: local_control.chip_id_local_control,
543
+ name: local_control.chip_local_control,
544
+ });
545
+ }}
546
+ >
547
+ <Text>{t('disable')}</Text>
548
+ {!local_control.is_local_control && (
549
+ <IconOutline style={styles.checked} name={'check'} size={20} />
550
+ )}
551
+ </TouchableOpacity>
552
+ {listChipShared.map((item) => (
553
+ <TouchableOpacity
554
+ style={styles.listChip}
555
+ onPress={() => {
556
+ onPressSelectChip(true, item);
557
+ }}
558
+ >
559
+ <Text>{item.name}</Text>
560
+ {local_control.chip_id_local_control === item.id &&
561
+ local_control.is_local_control && (
562
+ <IconOutline
563
+ style={styles.checked}
564
+ name={'check'}
565
+ size={20}
566
+ />
567
+ )}
568
+ </TouchableOpacity>
569
+ ))}
570
+ </View>
571
+ </ModalCustom>
393
572
  </View>
394
573
  );
395
574
  };
396
575
 
397
- const Item = ({ item, index, enableScript, t }) => {
398
- const color = enableScript ? Colors.Gray9 : Colors.Gray7;
576
+ const Item = ({ item, index, enableScript, t, local_control }) => {
577
+ let color = enableScript ? Colors.Gray9 : Colors.Gray7;
399
578
  const paddedIndex = (index + 1).toString().padStart(2, '0');
400
- const { action_script, notify_script, delay_script, email_script } = item;
579
+ const {
580
+ action_script,
581
+ notify_script,
582
+ delay_script,
583
+ email_script,
584
+ sms_script,
585
+ } = item;
401
586
  if (action_script) {
402
587
  const {
403
588
  sensor_icon_kit,
@@ -528,6 +713,38 @@ const Item = ({ item, index, enableScript, t }) => {
528
713
  </View>
529
714
  </View>
530
715
  );
716
+ } else if (sms_script) {
717
+ const { message, str_phone_numbers } = sms_script;
718
+ color = local_control.is_local_control ? color : Colors.Gray7;
719
+ return (
720
+ <View style={styles.wrapItem}>
721
+ <View style={styles.leftItem}>
722
+ <Text color={color} type="H4" semibold>
723
+ {paddedIndex}
724
+ </Text>
725
+ </View>
726
+ <View style={styles.rightItem}>
727
+ <View style={styles.insideItemRight}>
728
+ <View style={styles.iconEndDevice}>
729
+ <Sms />
730
+ </View>
731
+ <View style={styles.contentItem}>
732
+ <Text numberOfLines={1} type="H4" color={color} semibold>
733
+ {message}
734
+ </Text>
735
+ <Text numberOfLines={1} type="H4" color={color}>
736
+ {str_phone_numbers}
737
+ </Text>
738
+ </View>
739
+ </View>
740
+ {!local_control.is_local_control && (
741
+ <Text numberOfLines={1} type="H4" color={Colors.Red}>
742
+ {t('only_in_local_control')}
743
+ </Text>
744
+ )}
745
+ </View>
746
+ </View>
747
+ );
531
748
  }
532
749
  };
533
750
 
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { create, act } from 'react-test-renderer';
3
- import { TouchableOpacity } from 'react-native';
3
+ import { FlatList, RefreshControl, TouchableOpacity } from 'react-native';
4
4
  import Modal from 'react-native-modal';
5
5
  import MockAdapter from 'axios-mock-adapter';
6
6
 
@@ -67,4 +67,25 @@ describe('test SharedUnit', () => {
67
67
  });
68
68
  expect(tabHeader.props.textFilter).toEqual(t('text_latest_date'));
69
69
  });
70
+
71
+ it('should trigger refresh when pulling down', async () => {
72
+ mock
73
+ .onGet(API.UNIT.FILTER_SHARED_UNITS())
74
+ .reply(200, [{ id: 1, is_star: false, name: 'Unit 1' }]);
75
+
76
+ await act(async () => {
77
+ tree = await create(wrapComponent());
78
+ });
79
+
80
+ const instance = tree.root;
81
+ const flatList = instance.findByType(FlatList);
82
+
83
+ const refreshControl = flatList.findByType(RefreshControl);
84
+ expect(refreshControl.props.refreshing).toBe(false);
85
+
86
+ await act(async () => {
87
+ refreshControl.props.onRefresh();
88
+ });
89
+ expect(mock.history.get.length).toBeGreaterThan(0);
90
+ });
70
91
  });
@@ -46,7 +46,7 @@ const UnitMemberList = ({ route }) => {
46
46
  return;
47
47
  }
48
48
 
49
- if (permissions?.max_members_per_unit <= dataMembers.length) {
49
+ if (permissions?.max_members_per_unit < dataMembers.length) {
50
50
  ToastBottomHelper.error(
51
51
  t('reach_max_members_per_unit', {
52
52
  length: permissions?.max_members_per_unit,
@@ -81,6 +81,16 @@ describe('test MemberList', () => {
81
81
  });
82
82
 
83
83
  it('add new member but reach limit', async () => {
84
+ const dataMember = [
85
+ {
86
+ id: 1,
87
+ name: 'owner',
88
+ avatar: 'https://image1.jpg',
89
+ phone_number: '0933123456',
90
+ },
91
+ ];
92
+ mockAxios.onGet(API.SHARE.UNITS_MEMBERS(1)).reply(200, dataMember);
93
+
84
94
  let tree;
85
95
  await act(async () => {
86
96
  tree = await create(
@@ -59,7 +59,7 @@ describe('Test UnitDetail', () => {
59
59
 
60
60
  const detailUnitApiUrl = API.UNIT.UNIT_DETAIL(1);
61
61
  const summaryUnitApiUrl = API.UNIT.UNIT_SUMMARY(1);
62
- const sensorStatusApiUrl = API.UNIT.SENSORS_STATUS(1);
62
+ const sensorStatusApiUrl = API.UNIT.END_DEVICES_STATUS(1);
63
63
  const getAutomates = API.UNIT.AUTOMATE(1);
64
64
 
65
65
  let tree;
@@ -727,10 +727,13 @@ export default {
727
727
  update_title_notification: 'Update title',
728
728
  update_message_notification: 'Update warning content',
729
729
  email_content: 'E-mail content',
730
+ sms_content: 'SMS content',
730
731
  email_to: 'E-mail to',
732
+ sms_to: 'SMS to',
731
733
  subject: 'Subject',
732
734
  message: 'Message...',
733
735
  no_email: 'No email address',
736
+ no_phone_number: 'No phone number',
734
737
  update_title_email: 'Update title',
735
738
  update_message_email: 'Update warning content',
736
739
  update_action: 'Update action',
@@ -1068,9 +1071,16 @@ export default {
1068
1071
  automate: 'Automate',
1069
1072
  smart: 'Smart',
1070
1073
  enable_this_script: 'Enable this script',
1074
+ local_control: 'Local control',
1075
+ choose_gateway: 'Choose gateway',
1071
1076
  this_script_has_been_disabled: 'This script has been disabled',
1077
+ disable: 'Disable',
1072
1078
  control_device: 'Control device',
1073
1079
  send_email: 'Send e-mail',
1080
+ sms_alarm: 'SMS Alarm',
1081
+ message_sms:
1082
+ 'Warning content: "There is a fire alarm from the 1st floor, please check."',
1083
+ only_in_local_control: 'Operates only in local control mode',
1074
1084
  send_app_notification: 'Send app notification',
1075
1085
  delay_the_action: 'Delay the action',
1076
1086
  email_the_action: 'Send e-mail',
@@ -750,10 +750,13 @@ export default {
750
750
  update_title_notification: 'Cập nhật tiêu đề',
751
751
  update_message_notification: 'Cập nhật nội dung cảnh báo',
752
752
  email_content: 'Nội dung E-mail',
753
+ sms_content: 'Nội dung SMS',
753
754
  email_to: 'Gửi E-mail tới',
755
+ sms_to: 'SMS tới',
754
756
  subject: 'Tiêu đè',
755
757
  message: 'Tin nhắn...',
756
758
  no_email: 'Không có địa chỉ email',
759
+ no_phone_number: 'Không có số điện thoại',
757
760
  update_action: 'Cập nhật hành động',
758
761
  updated_action_order: 'Đã cập nhật thứ tự hành động',
759
762
  message_notification:
@@ -1078,9 +1081,16 @@ export default {
1078
1081
  automate: 'Tự động',
1079
1082
  smart: 'Thông minh',
1080
1083
  enable_this_script: 'Kích hoạt kịch bản này',
1084
+ local_control: 'Điều khiển cục bộ',
1085
+ choose_gateway: 'Chọn gateway',
1081
1086
  this_script_has_been_disabled: 'Kịch bản này đã bị vô hiệu hoá',
1087
+ disable: 'Vô hiệu hóa',
1082
1088
  control_device: 'Thiết bị điều khiển',
1083
1089
  send_email: 'Gửi e-mail',
1090
+ sms_alarm: 'Cảnh báo SMS',
1091
+ message_sms:
1092
+ 'Nội dung cảnh báo: “Đang có tín hiệu báo cháy từ lầu 1, vui lòng kiểm tra.”',
1093
+ only_in_local_control: 'Chỉ hoạt động ở chế độ điều khiển cục bộ',
1084
1094
  send_app_notification: 'Gửi thông báo qua ứng dụng',
1085
1095
  delay_the_action: 'Trì hoãn hành động',
1086
1096
  wait: 'Chờ đợi',
@@ -139,8 +139,11 @@ const Routes = {
139
139
  SetupScriptNotify: 'SetupScriptNotify',
140
140
  SetupScriptDelay: 'SetupScriptDelay',
141
141
  SetupScriptEmail: 'SetupScriptEmail',
142
+ SetupScriptSms: 'SetupScriptSms',
142
143
  SetupScriptReceiverEmail: 'SetupScriptReceiverEmail',
143
144
  UpdateReceiverEmailScript: 'UpdateReceiverEmailScript',
145
+ SetupScriptReceiverSms: 'SetupScriptReceiverSms',
146
+ UpdateReceiverSmsScript: 'UpdateReceiverSmsScript',
144
147
  ScenarioName: 'ScenarioName',
145
148
  ValueChangeName: 'ValueChangeName',
146
149
  AllCamera: 'AllCamera',