@eohjsc/react-native-smart-city 0.4.87 → 0.4.89

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 (31) hide show
  1. package/assets/images/Delay.svg +14 -0
  2. package/package.json +1 -1
  3. package/src/commons/Action/ItemQuickAction.js +2 -2
  4. package/src/commons/Device/ItemDevice.js +14 -1
  5. package/src/commons/IconComponent/index.js +4 -4
  6. package/src/commons/Sharing/WrapHeaderScrollable.js +3 -5
  7. package/src/commons/UnitSummary/ConfigHistoryChart/index.js +1 -0
  8. package/src/configs/API.js +2 -0
  9. package/src/configs/AccessibilityLabel.js +1 -1
  10. package/src/navigations/UnitStack.js +8 -0
  11. package/src/screens/Automate/AddNewAction/SetupScriptDelay.js +81 -0
  12. package/src/screens/Automate/AddNewAction/Styles/SetupScriptDelayStyles.js +30 -0
  13. package/src/screens/Automate/AddNewAction/Styles/SetupScriptNotifyStyles.js +0 -21
  14. package/src/screens/Automate/AddNewAction/__test__/SetupScriptDelay.test.js +95 -0
  15. package/src/screens/Automate/AddNewAction/__test__/SetupScriptNotify.test.js +4 -3
  16. package/src/screens/Automate/EditActionsList/Styles/indexStyles.js +5 -3
  17. package/src/screens/Automate/EditActionsList/__tests__/index.test.js +9 -1
  18. package/src/screens/Automate/EditActionsList/index.js +36 -5
  19. package/src/screens/Automate/ScriptDetail/Components/AddActionScript.js +16 -1
  20. package/src/screens/Automate/ScriptDetail/Styles/indexStyles.js +3 -1
  21. package/src/screens/Automate/ScriptDetail/__test__/index.test.js +16 -1
  22. package/src/screens/Automate/ScriptDetail/index.js +36 -6
  23. package/src/screens/Notification/__test__/Notification.test.js +2 -1
  24. package/src/screens/Notification/components/NotificationItem.js +23 -12
  25. package/src/screens/Notification/index.js +0 -1
  26. package/src/screens/Notification/styles/NotificationItemStyles.js +8 -21
  27. package/src/screens/Notification/styles/indexStyles.js +0 -4
  28. package/src/screens/Unit/components/MyUnitDevice/index.js +1 -0
  29. package/src/utils/I18n/translations/en.js +4 -0
  30. package/src/utils/I18n/translations/vi.js +4 -0
  31. package/src/utils/Route/index.js +1 -0
@@ -0,0 +1,14 @@
1
+ <svg width="21" height="34" viewBox="0 0 21 34" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M0 0L0.0175004 10.2L7 17L0.0175004 23.817L0 34H21V23.8L14 17L21 10.217V0H0ZM17.5 24.65V30.6H3.5V24.65L10.5 17.85L17.5 24.65Z" fill="url(#paint0_linear_1913_17610)"/>
3
+ <path d="M7.35 30.26H13.65C13.4294 28.5299 12.1032 27.2 10.5 27.2C8.89685 27.2 7.57059 28.5299 7.35 30.26Z" fill="url(#paint1_linear_1913_17610)"/>
4
+ <defs>
5
+ <linearGradient id="paint0_linear_1913_17610" x1="0" y1="0" x2="30.3834" y2="18.787" gradientUnits="userSpaceOnUse">
6
+ <stop stop-color="#2B6B9F"/>
7
+ <stop offset="1" stop-color="#D5FFCB"/>
8
+ </linearGradient>
9
+ <linearGradient id="paint1_linear_1913_17610" x1="0" y1="0" x2="30.3834" y2="18.787" gradientUnits="userSpaceOnUse">
10
+ <stop stop-color="#2B6B9F"/>
11
+ <stop offset="1" stop-color="#D5FFCB"/>
12
+ </linearGradient>
13
+ </defs>
14
+ </svg>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eohjsc/react-native-smart-city",
3
3
  "title": "React Native Smart Home",
4
- "version": "0.4.87",
4
+ "version": "0.4.89",
5
5
  "description": "TODO",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -1,5 +1,5 @@
1
1
  import React, { memo, useCallback, useEffect, useState } from 'react';
2
- import { Platform, TouchableOpacity, View } from 'react-native';
2
+ import { TouchableOpacity, View } from 'react-native';
3
3
  import { AccessibilityLabel } from '../../configs/Constants';
4
4
  import { useSCContextSelector } from '../../context';
5
5
  import { useRemoteControl } from '../../hooks/IoT';
@@ -115,7 +115,7 @@ const ItemQuickAction = memo(({ sensor, wrapperStyle, setStatus }) => {
115
115
  <IconComponent
116
116
  icon={action?.icon_kit || action?.icon || 'PoweroffOutlined'}
117
117
  isOn={isOn}
118
- size={Platform.OS === 'ios' ? 20 : 30}
118
+ size={20}
119
119
  />
120
120
  </View>
121
121
  </TouchableOpacity>
@@ -117,7 +117,11 @@ const ItemDevice = memo(
117
117
  <IconComponent icon={sensor?.icon_kit || sensor?.icon} />
118
118
  </TouchableOpacity>
119
119
  {!!canRenderQuickAction && (
120
- <ItemQuickAction sensor={sensor} unit={unit} />
120
+ <ItemQuickAction
121
+ sensor={sensor}
122
+ unit={unit}
123
+ wrapperStyle={styles.quickAction}
124
+ />
121
125
  )}
122
126
  </View>
123
127
  <TouchableOpacity onPress={goToSensorDisplay}>
@@ -179,4 +183,13 @@ const styles = StyleSheet.create({
179
183
  height: 40,
180
184
  resizeMode: 'contain',
181
185
  },
186
+ quickAction: {
187
+ width: 32,
188
+ height: 32,
189
+ backgroundColor: Colors.Gray3,
190
+ borderRadius: 16,
191
+ justifyContent: 'center',
192
+ alignItems: 'center',
193
+ marginLeft: 5,
194
+ },
182
195
  });
@@ -16,8 +16,8 @@ const IconComponent = memo(
16
16
  colorInActive,
17
17
  }) => {
18
18
  let extraStyle = {
19
- width: size,
20
- height: size,
19
+ width: size + 10,
20
+ height: size + 10,
21
21
  };
22
22
  const displayIcon = useMemo(() => {
23
23
  let newIcon = `${icon}`;
@@ -66,7 +66,7 @@ const IconComponent = memo(
66
66
  name={displayIcon}
67
67
  color={colorIcon}
68
68
  size={size}
69
- style={[extraStyle, antIconStyle || style]}
69
+ style={[antIconStyle || style]}
70
70
  />
71
71
  );
72
72
  } else {
@@ -80,7 +80,7 @@ const IconComponent = memo(
80
80
  name={iconCT}
81
81
  color={colorIcon}
82
82
  size={size}
83
- style={[extraStyle, antIconStyle || style]}
83
+ style={[antIconStyle || style]}
84
84
  />
85
85
  );
86
86
  }
@@ -7,8 +7,6 @@ import {
7
7
  View,
8
8
  } from 'react-native';
9
9
  import { ActivityIndicator } from '@ant-design/react-native';
10
- import { isIphoneX } from 'react-native-iphone-x-helper';
11
-
12
10
  import { Colors, Theme } from '../../configs';
13
11
  import HeaderAni, { heightHeader } from '../../commons/HeaderAni';
14
12
  import Text from '../../commons/Text';
@@ -83,7 +81,7 @@ const WrapHeaderScrollable = ({
83
81
  />
84
82
  }
85
83
  contentInset={{
86
- top: heightHeader - (isIphoneX() ? 32 : 0),
84
+ top: heightHeader,
87
85
  }}
88
86
  contentOffset={{
89
87
  y: -heightHeader,
@@ -116,8 +114,8 @@ const styles = StyleSheet.create({
116
114
  scrollView: {
117
115
  flex: 1,
118
116
  paddingTop: Platform.select({
119
- ios: 15,
120
- android: heightHeader + 16,
117
+ ios: 0,
118
+ android: heightHeader,
121
119
  }),
122
120
  },
123
121
  contentContainerStyle: {
@@ -32,6 +32,7 @@ export const useFetchConfigHistory = (configs, setChartData) => {
32
32
  validConfigs.forEach((item) => {
33
33
  params.append('configs', item.id);
34
34
  });
35
+
35
36
  params.append(
36
37
  'date_from',
37
38
  startDate.subtract(7, 'hours').format('YYYY-MM-DDTHH:mm:ss')
@@ -100,6 +100,8 @@ const API = {
100
100
  `/property_manager/automate/${id}/add_script_action/`,
101
101
  ADD_SCRIPT_NOTIFY: (id) =>
102
102
  `/property_manager/automate/${id}/add_script_notify/`,
103
+ ADD_SCRIPT_DELAY: (id) =>
104
+ `/property_manager/automate/${id}/add_script_delay/`,
103
105
 
104
106
  FETCH_AUTOMATE: (automateId) => `/property_manager/automate/${automateId}/`,
105
107
  CREATE_AUTOMATE: () => '/property_manager/automate/',
@@ -244,7 +244,7 @@ export default {
244
244
  AUTOMATE_LIST_SCRIPT_ACTION: 'AUTOMATE_LIST_SCRIPT_ACTION',
245
245
  AUTOMATE_TITLE_NOTIFY: 'AUTOMATE_TITLE_NOTIFY',
246
246
  AUTOMATE_MESSAGE_NOTIFY: 'AUTOMATE_MESSAGE_NOTIFY',
247
-
247
+ AUTOMATE_INPUT_DELAY: 'AUTOMATE_INPUT_DELAY',
248
248
  // Parking input maunaly spot
249
249
  PARKING_SPOT_INFO_BUTTON: 'PARKING_SPOT_INFO_BUTTON',
250
250
  PARKING_SPOT_CONFIRM_SPOT: 'PARKING_SPOT_CONFIRM_SPOT',
@@ -67,6 +67,7 @@ import ScenarioName from '../screens/Automate/Scenario/ScenarioName';
67
67
  import ValueChangeName from '../screens/Automate/ValueChange/ValueChangeName';
68
68
  import AddAutomationTypeSmart from '../screens/Automate/AddNewAutoSmart/AddAutomationTypeSmart';
69
69
  import SetupScriptNotify from '../screens/Automate/AddNewAction/SetupScriptNotify';
70
+ import SetupScriptDelay from '../screens/Automate/AddNewAction/SetupScriptDelay';
70
71
 
71
72
  const Stack = createStackNavigator();
72
73
 
@@ -414,6 +415,13 @@ export const UnitStack = memo((props) => {
414
415
  headerShown: false,
415
416
  }}
416
417
  />
418
+ <Stack.Screen
419
+ name={Route.SetupScriptDelay}
420
+ component={SetupScriptDelay}
421
+ options={{
422
+ headerShown: false,
423
+ }}
424
+ />
417
425
  <Stack.Screen
418
426
  name={Route.AddAutomationTypeSmart}
419
427
  component={AddAutomationTypeSmart}
@@ -0,0 +1,81 @@
1
+ import React, { useCallback, useMemo, useState } from 'react';
2
+ import { View } from 'react-native';
3
+ import { useNavigation } from '@react-navigation/native';
4
+ import styles from './Styles/SetupScriptDelayStyles';
5
+ import { HeaderCustom } from '../../../commons';
6
+ import { useTranslations } from '../../../hooks/Common/useTranslations';
7
+
8
+ import _TextInput from '../../../commons/Form/TextInput';
9
+ import AccessibilityLabel from '../../../configs/AccessibilityLabel';
10
+ import BottomButtonView from '../../../commons/BottomButtonView';
11
+ import { axiosPost } from '../../../utils/Apis/axios';
12
+ import { API } from '../../../configs';
13
+ import { ToastBottomHelper } from '../../../utils/Utils';
14
+ import Routes from '../../../utils/Route';
15
+ import moment from 'moment';
16
+
17
+ const SetupScriptDelay = ({ route }) => {
18
+ const t = useTranslations();
19
+ const { goBack, navigate } = useNavigation();
20
+ const { automate = {} } = route?.params || {};
21
+ const { id: automateId } = automate;
22
+ const [delay, setDelay] = useState();
23
+
24
+ const onChangeTitle = (value) => {
25
+ setDelay(value);
26
+ };
27
+
28
+ const onNext = useCallback(async () => {
29
+ const { success } = await axiosPost(
30
+ API.AUTOMATE.ADD_SCRIPT_DELAY(automateId),
31
+ {
32
+ delay,
33
+ }
34
+ );
35
+ if (success) {
36
+ ToastBottomHelper.success(t('text_done'));
37
+ navigate({
38
+ name: Routes.ScriptDetail,
39
+ merge: true,
40
+ params: { saveAt: moment().valueOf() },
41
+ });
42
+ } else {
43
+ ToastBottomHelper.error(t('text_done'));
44
+ }
45
+ }, [automateId, navigate, delay, t]);
46
+
47
+ const canSave = useMemo(() => {
48
+ const value = parseInt(delay);
49
+ if (!Number.isInteger(value) || value <= 0 || value > 3600) {
50
+ return false;
51
+ }
52
+ return true;
53
+ }, [delay]);
54
+
55
+ return (
56
+ <View style={styles.wrap}>
57
+ <HeaderCustom isShowClose onClose={goBack} title={t('wait')} />
58
+ <View style={styles.container}>
59
+ <_TextInput
60
+ label={t('set_timeout_seconds')}
61
+ placeholder={t('maximum_3600_seconds')}
62
+ keyboardType="numeric"
63
+ textInputStyle={styles.inputNumber}
64
+ value={delay}
65
+ onChange={onChangeTitle}
66
+ maxLength={4}
67
+ accessibilityLabel={AccessibilityLabel.AUTOMATE_INPUT_DELAY}
68
+ autoFocus
69
+ />
70
+ <BottomButtonView
71
+ style={styles.bottomButtonView}
72
+ mainTitle={t('save')}
73
+ onPressMain={onNext}
74
+ typeMain={canSave ? 'primary' : 'disabled'}
75
+ />
76
+ </View>
77
+ </View>
78
+ );
79
+ };
80
+
81
+ export default SetupScriptDelay;
@@ -0,0 +1,30 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { Colors } from '../../../../configs';
3
+ import { getBottomSpace } from 'react-native-iphone-x-helper';
4
+
5
+ export default StyleSheet.create({
6
+ wrap: {
7
+ flex: 1,
8
+ backgroundColor: Colors.White,
9
+ },
10
+ container: {
11
+ flex: 1,
12
+ paddingHorizontal: 16,
13
+ paddingTop: 10,
14
+ paddingBottom: getBottomSpace() + 10,
15
+ },
16
+ inputNumber: {
17
+ borderWidth: 1,
18
+ borderColor: Colors.Gray4,
19
+ borderStyle: 'solid',
20
+ borderRadius: 10,
21
+ },
22
+ bottomButtonView: {
23
+ paddingTop: 24,
24
+ paddingBottom: 32,
25
+
26
+ backgroundColor: Colors.White,
27
+ borderColor: Colors.ShadownTransparent,
28
+ borderTopWidth: 1,
29
+ },
30
+ });
@@ -7,16 +7,6 @@ export default StyleSheet.create({
7
7
  flex: 1,
8
8
  backgroundColor: Colors.White,
9
9
  },
10
- titleCreate: {
11
- fontStyle: 'normal',
12
- fontWeight: '600',
13
- color: Colors.Gray9,
14
- },
15
- titleChoose: {
16
- fontStyle: 'normal',
17
- fontWeight: '400',
18
- color: Colors.Gray8,
19
- },
20
10
  container: {
21
11
  flex: 1,
22
12
  paddingHorizontal: 16,
@@ -39,19 +29,8 @@ export default StyleSheet.create({
39
29
  bottomButtonView: {
40
30
  paddingTop: 24,
41
31
  paddingBottom: 32,
42
-
43
32
  backgroundColor: Colors.White,
44
33
  borderColor: Colors.ShadownTransparent,
45
34
  borderTopWidth: 1,
46
35
  },
47
- bottomButton: {
48
- borderWidth: 0,
49
- borderColor: Colors.Gray4,
50
- paddingTop: 24,
51
- paddingBottom: 24,
52
- borderTopWidth: 1,
53
- borderStyle: 'solid',
54
- position: 'absolute',
55
- bottom: 0,
56
- },
57
36
  });
@@ -0,0 +1,95 @@
1
+ import React from 'react';
2
+ import renderer, { act } from 'react-test-renderer';
3
+ import MockAdapter from 'axios-mock-adapter';
4
+ import { SCProvider } from '../../../../context';
5
+ import { mockSCStore } from '../../../../context/mockStore';
6
+ import API from '../../../../configs/API';
7
+ import Routes from '../../../../utils/Route';
8
+ import api from '../../../../utils/Apis/axios';
9
+ import { useNavigation, useRoute } from '@react-navigation/native';
10
+ import _TextInput from '../../../../commons/Form/TextInput';
11
+ import BottomButtonView from '../../../../commons/BottomButtonView';
12
+ import { ToastBottomHelper } from '../../../../utils/Utils';
13
+ import SetupScriptDelay from '../SetupScriptDelay';
14
+
15
+ const mock = new MockAdapter(api.axiosInstance);
16
+
17
+ const wrapComponent = (route) => (
18
+ <SCProvider initState={mockSCStore({})}>
19
+ <SetupScriptDelay route={route} />
20
+ </SCProvider>
21
+ );
22
+
23
+ describe('Test SetupScriptDelay', () => {
24
+ const mockedNavigate = useNavigation().navigate;
25
+
26
+ let tree;
27
+ const route = {
28
+ params: {
29
+ unitId: 1,
30
+ automateId: 1,
31
+ scriptName: 'scriptName test',
32
+ automate: {
33
+ id: 1,
34
+ sensor_id: 1,
35
+ },
36
+ closeScreen: Routes.ScriptDetail,
37
+ },
38
+ };
39
+ beforeEach(() => {
40
+ mockedNavigate.mockClear();
41
+ useRoute.mockImplementation(() => route);
42
+ });
43
+
44
+ it('SetupScriptDelay onPress create script delay success', async () => {
45
+ const spyToast = jest.spyOn(ToastBottomHelper, 'success');
46
+ mock.onPost(API.AUTOMATE.ADD_SCRIPT_DELAY(1)).reply(200);
47
+ await act(async () => {
48
+ tree = await renderer.create(wrapComponent(route));
49
+ });
50
+ const instance = tree.root;
51
+ const inputs = instance.findAllByType(_TextInput);
52
+ expect(inputs).toHaveLength(1);
53
+ const button = instance.findByType(BottomButtonView);
54
+ await act(async () => {
55
+ inputs[0].props.onChange('u');
56
+ });
57
+ expect(button.props.typeMain).toEqual('disabled');
58
+ await act(async () => {
59
+ inputs[0].props.onChange('0');
60
+ });
61
+ expect(button.props.typeMain).toEqual('disabled');
62
+ await act(async () => {
63
+ inputs[0].props.onChange('3601');
64
+ });
65
+ expect(button.props.typeMain).toEqual('disabled');
66
+ await act(async () => {
67
+ inputs[0].props.onChange('10');
68
+ });
69
+ expect(button.props.typeMain).toEqual('primary');
70
+ await act(async () => {
71
+ button.props.onPressMain();
72
+ });
73
+ expect(spyToast).toBeCalled();
74
+ });
75
+
76
+ it('SetupScriptDelay onPress create script notify error', async () => {
77
+ const spyToast = jest.spyOn(ToastBottomHelper, 'error');
78
+ mock.onPost(API.AUTOMATE.ADD_SCRIPT_DELAY(1)).reply(400);
79
+ await act(async () => {
80
+ tree = await renderer.create(wrapComponent(route));
81
+ });
82
+ const instance = tree.root;
83
+ const inputs = instance.findAllByType(_TextInput);
84
+ expect(inputs).toHaveLength(1);
85
+
86
+ await act(async () => {
87
+ inputs[0].props.onChange('12');
88
+ });
89
+ const button = instance.findByType(BottomButtonView);
90
+ await act(async () => {
91
+ button.props.onPressMain();
92
+ });
93
+ expect(spyToast).toBeCalled();
94
+ });
95
+ });
@@ -20,7 +20,7 @@ const wrapComponent = (route) => (
20
20
  </SCProvider>
21
21
  );
22
22
 
23
- describe('Test SelectMonitorDevices', () => {
23
+ describe('Test SetupScriptNotify', () => {
24
24
  const mockedNavigate = useNavigation().navigate;
25
25
 
26
26
  let tree;
@@ -30,6 +30,7 @@ describe('Test SelectMonitorDevices', () => {
30
30
  automateId: 1,
31
31
  scriptName: 'scriptName test',
32
32
  automate: {
33
+ id: 1,
33
34
  sensor_id: 1,
34
35
  },
35
36
  closeScreen: Routes.ScriptDetail,
@@ -42,7 +43,7 @@ describe('Test SelectMonitorDevices', () => {
42
43
 
43
44
  it('SetupScriptNotify onPress create script notify success', async () => {
44
45
  const spyToast = jest.spyOn(ToastBottomHelper, 'success');
45
- mock.onPost(API.AUTOMATE.CREATE_SCRIPT_NOTIFY).reply(200);
46
+ mock.onPost(API.AUTOMATE.ADD_SCRIPT_NOTIFY(1)).reply(200);
46
47
  await act(async () => {
47
48
  tree = await renderer.create(wrapComponent(route));
48
49
  });
@@ -63,7 +64,7 @@ describe('Test SelectMonitorDevices', () => {
63
64
 
64
65
  it('SetupScriptNotify onPress create script notify error', async () => {
65
66
  const spyToast = jest.spyOn(ToastBottomHelper, 'error');
66
- mock.onPost(API.AUTOMATE.CREATE_SCRIPT_NOTIFY).reply(400);
67
+ mock.onPost(API.AUTOMATE.ADD_SCRIPT_NOTIFY(1)).reply(400);
67
68
  await act(async () => {
68
69
  tree = await renderer.create(wrapComponent(route));
69
70
  });
@@ -52,9 +52,11 @@ export default StyleSheet.create({
52
52
  borderWidth: 0,
53
53
  },
54
54
  iconItem: {
55
- width: 40,
56
- height: 40,
57
- marginRight: 24,
55
+ width: 35,
56
+ height: 35,
57
+ marginTop: 6,
58
+ marginRight: 15,
59
+ alignItems: 'center',
58
60
  },
59
61
  contentItem: {
60
62
  flex: 1,
@@ -34,11 +34,19 @@ describe('EditActionsList', () => {
34
34
  id: 1,
35
35
  action_script: { id: 1, sensor_name: 'abc', station_name: 'abc' },
36
36
  notify_script: null,
37
+ delay_script: null,
37
38
  },
38
39
  {
39
40
  id: 2,
40
41
  action_script: null,
41
42
  notify_script: { title: 'title' },
43
+ delay_script: null,
44
+ },
45
+ {
46
+ id: 3,
47
+ action_script: null,
48
+ notify_script: null,
49
+ delay_script: { delay: 10 },
42
50
  },
43
51
  ],
44
52
  id: 1,
@@ -106,7 +114,7 @@ describe('EditActionsList', () => {
106
114
  AccessibilityLabel.BUTTON_REMOVE_EDIT_ACTION_LIST &&
107
115
  el.type === TouchableOpacity
108
116
  );
109
- expect(buttonRemoves).toHaveLength(2);
117
+ expect(buttonRemoves).toHaveLength(3);
110
118
  });
111
119
 
112
120
  it('EditActionsList modal onPress remove', async () => {
@@ -12,6 +12,7 @@ import { API, Colors } from '../../../configs';
12
12
  import FImage from '../../../commons/FImage';
13
13
  import Rearrange from '../../../../assets/images/Rearrange.svg';
14
14
  import Notify from '../../../../assets/images/Notify.svg';
15
+ import Delay from '../../../../assets/images/Delay.svg';
15
16
  import Close from '../../../../assets/images/Close.svg';
16
17
  import { axiosDelete, axiosPut } from '../../../utils/Apis/axios';
17
18
  import { ModalBottom } from '../../../commons/Modal';
@@ -112,7 +113,7 @@ const EditActionsList = memo(() => {
112
113
  );
113
114
  const renderItem = useCallback(({ item, index, drag, isActive }) => {
114
115
  const paddedIndex = (index + 1).toString().padStart(2, '0');
115
- const { action_script, notify_script } = item;
116
+ const { action_script, notify_script, delay_script } = item;
116
117
  if (action_script) {
117
118
  const { sensor_icon_kit, station_name, sensor_name, action_name } =
118
119
  action_script;
@@ -155,19 +156,23 @@ const EditActionsList = memo(() => {
155
156
  }
156
157
 
157
158
  if (notify_script) {
158
- const { title } = notify_script;
159
+ const { title, message } = notify_script;
159
160
 
160
161
  return (
161
162
  <CommonItem
162
163
  paddedIndex={paddedIndex}
163
- icon={<Notify style={styles.iconItem} />}
164
+ icon={
165
+ <View style={styles.iconItem}>
166
+ <Notify />
167
+ </View>
168
+ }
164
169
  content={
165
170
  <>
166
171
  <Text numberOfLines={1} type="H4" color={Colors.Gray9} semibold>
167
- {'Send notification'}
172
+ {title}
168
173
  </Text>
169
174
  <Text numberOfLines={1} type="H4" color={Colors.Gray9}>
170
- {title}
175
+ {message}
171
176
  </Text>
172
177
  </>
173
178
  }
@@ -177,7 +182,33 @@ const EditActionsList = memo(() => {
177
182
  />
178
183
  );
179
184
  }
185
+ if (delay_script) {
186
+ const { delay } = delay_script;
180
187
 
188
+ return (
189
+ <CommonItem
190
+ paddedIndex={paddedIndex}
191
+ icon={
192
+ <View style={styles.iconItem}>
193
+ <Delay />
194
+ </View>
195
+ }
196
+ content={
197
+ <>
198
+ <Text numberOfLines={1} type="H4" color={Colors.Gray9} semibold>
199
+ {t('wait')}
200
+ </Text>
201
+ <Text numberOfLines={1} type="H4" color={Colors.Gray9}>
202
+ {t('wait_time_seconds', { delay })}
203
+ </Text>
204
+ </>
205
+ }
206
+ onPressRemove={() => onPressRemove(item)}
207
+ isActive={isActive}
208
+ drag={drag}
209
+ />
210
+ );
211
+ }
181
212
  // eslint-disable-next-line react-hooks/exhaustive-deps
182
213
  }, []);
183
214
 
@@ -9,6 +9,7 @@ import { useTranslations } from '../../../../hooks/Common/useTranslations';
9
9
  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
+ import Delay from '../../../../../assets/images/Delay.svg';
12
13
  import { TouchableOpacity } from 'react-native';
13
14
  import { IconOutline } from '@ant-design/icons-react-native';
14
15
  import { Text } from '../../../../commons';
@@ -59,6 +60,17 @@ const AddActionScript = memo(
59
60
  });
60
61
  },
61
62
  },
63
+ {
64
+ id: 'delay_the_action',
65
+ text: t('delay_the_action'),
66
+ image: <Delay />,
67
+ onClick: () => {
68
+ setIsVisible(false);
69
+ navigate(Routes.SetupScriptDelay, {
70
+ automate,
71
+ });
72
+ },
73
+ },
62
74
  ];
63
75
  }, [
64
76
  automate,
@@ -160,7 +172,10 @@ const styles = StyleSheet.create({
160
172
  justifyContent: 'center',
161
173
  },
162
174
  imageItem: {
163
- width: '15%',
175
+ width: '10%',
176
+ alignItems: 'center',
177
+ justifyContent: 'center',
178
+ marginRight: 20,
164
179
  },
165
180
  textItem: {
166
181
  width: '80%',
@@ -163,7 +163,9 @@ export default StyleSheet.create({
163
163
  flex: 1,
164
164
  },
165
165
  iconEndDevice: {
166
- marginTop: 5,
166
+ width: 35,
167
+ height: 35,
168
+ marginTop: 6,
167
169
  marginRight: 15,
168
170
  },
169
171
  });
@@ -81,10 +81,17 @@ describe('Test ScriptDetail', () => {
81
81
  action_name: 'action',
82
82
  },
83
83
  notify_script: null,
84
+ delay_script: null,
84
85
  },
85
86
  {
86
87
  action_script: null,
87
88
  notify_script: { title: 'title' },
89
+ delay_script: null,
90
+ },
91
+ {
92
+ action_script: null,
93
+ notify_script: null,
94
+ delay_script: { delay: 10 },
88
95
  },
89
96
  ],
90
97
  };
@@ -291,7 +298,7 @@ describe('Test ScriptDetail', () => {
291
298
  AccessibilityLabel.AUTOMATE_LIST_SCRIPT_ACTION &&
292
299
  el.type === TouchableOpacity
293
300
  );
294
- expect(listScriptActions).toHaveLength(2);
301
+ expect(listScriptActions).toHaveLength(3);
295
302
  await act(async () => {
296
303
  await listScriptActions[0].props.onPress();
297
304
  });
@@ -310,6 +317,14 @@ describe('Test ScriptDetail', () => {
310
317
  automate: route.params.preAutomate,
311
318
  unitId: route.params.preAutomate.unit,
312
319
  });
320
+ mockNavigate.mockClear();
321
+ await act(async () => {
322
+ await listScriptActions[2].props.onPress();
323
+ });
324
+
325
+ expect(mockNavigate).toHaveBeenCalledWith(Routes.SetupScriptDelay, {
326
+ automate: route.params.preAutomate,
327
+ });
313
328
  });
314
329
 
315
330
  it('test press disable script', async () => {
@@ -20,6 +20,7 @@ import { useStarredScript } from './hooks/useStarredScript';
20
20
  import MenuActionMore from '../../../commons/MenuActionMore';
21
21
  import Add from '../../../../assets/images/Add.svg';
22
22
  import Notify from '../../../../assets/images/Notify.svg';
23
+ import Delay from '../../../../assets/images/Delay.svg';
23
24
  import { useNavigation } from '@react-navigation/native';
24
25
  import { axiosGet, axiosPost } from '../../../utils/Apis/axios';
25
26
  import Routes from '../../../utils/Route';
@@ -116,7 +117,10 @@ const ScriptDetail = ({ route }) => {
116
117
  const { success, data: automateData } = await axiosGet(
117
118
  API.AUTOMATE.FETCH_AUTOMATE(id)
118
119
  );
119
- success && setAutomate(automateData);
120
+ if (success) {
121
+ setAutomate(automateData);
122
+ setEnableScript(automateData.script.enable);
123
+ }
120
124
  }, [id]);
121
125
 
122
126
  const onPressEdit = useCallback(() => {
@@ -310,7 +314,7 @@ const ScriptDetail = ({ route }) => {
310
314
  const Item = ({ item, index, enableScript, t }) => {
311
315
  const color = enableScript ? Colors.Gray9 : Colors.Gray7;
312
316
  const paddedIndex = (index + 1).toString().padStart(2, '0');
313
- const { action_script, notify_script } = item;
317
+ const { action_script, notify_script, delay_script } = item;
314
318
  if (action_script) {
315
319
  const {
316
320
  sensor_icon_kit,
@@ -359,7 +363,7 @@ const Item = ({ item, index, enableScript, t }) => {
359
363
  </View>
360
364
  );
361
365
  } else if (notify_script) {
362
- const { title } = notify_script;
366
+ const { title, message } = notify_script;
363
367
  return (
364
368
  <View style={styles.wrapItem}>
365
369
  <View style={styles.leftItem}>
@@ -368,13 +372,39 @@ const Item = ({ item, index, enableScript, t }) => {
368
372
  </Text>
369
373
  </View>
370
374
  <View style={styles.rightItem}>
371
- <Notify style={styles.iconEndDevice} />
375
+ <View style={styles.iconEndDevice}>
376
+ <Notify />
377
+ </View>
372
378
  <View style={styles.contentItem}>
373
379
  <Text numberOfLines={1} type="H4" color={color} semibold>
374
- {t('send_notification')}
380
+ {title}
375
381
  </Text>
376
382
  <Text numberOfLines={1} type="H4" color={color}>
377
- {title}
383
+ {message}
384
+ </Text>
385
+ </View>
386
+ </View>
387
+ </View>
388
+ );
389
+ } else if (delay_script) {
390
+ const { delay } = delay_script;
391
+ return (
392
+ <View style={styles.wrapItem}>
393
+ <View style={styles.leftItem}>
394
+ <Text color={color} type="H4" semibold>
395
+ {paddedIndex}
396
+ </Text>
397
+ </View>
398
+ <View style={styles.rightItem}>
399
+ <View style={styles.iconEndDevice}>
400
+ <Delay />
401
+ </View>
402
+ <View style={styles.contentItem}>
403
+ <Text numberOfLines={1} type="H4" color={color} semibold>
404
+ {t('wait')}
405
+ </Text>
406
+ <Text numberOfLines={1} type="H4" color={color}>
407
+ {t('wait_time_seconds', { delay })}
378
408
  </Text>
379
409
  </View>
380
410
  </View>
@@ -173,7 +173,8 @@ describe('test Notification', () => {
173
173
  created_at: '2021-10-07T08:57:09.370286Z',
174
174
  id: 12905,
175
175
  is_read: true,
176
- params: "{'unit_id': 1, 'content': 'EoH Office'}",
176
+ params:
177
+ "{'unit_id': 1, 'content': 'EoH Office', 'title': 'Nhiệt độ: (30.5°C > 30.0°C)', 'automate_id:1'}",
177
178
  type: 'NEWS',
178
179
  },
179
180
  ],
@@ -16,6 +16,7 @@ import { useTranslations } from '../../../hooks/Common/useTranslations';
16
16
  import { axiosPost } from '../../../utils/Apis/axios';
17
17
  import Routes from '../../../utils/Route';
18
18
  import styles from '../styles/NotificationItemStyles';
19
+ import Notify from '../../../../assets/images/Notify.svg';
19
20
 
20
21
  const NotificationItem = memo(({ item }) => {
21
22
  const t = useTranslations();
@@ -52,6 +53,8 @@ const NotificationItem = memo(({ item }) => {
52
53
  sub_unit_id,
53
54
  type,
54
55
  content,
56
+ automate_id,
57
+ title,
55
58
  } = params;
56
59
  switch (content_code) {
57
60
  case NOTIFICATION_TYPES.NOTIFY_INVITE_MEMBER:
@@ -430,34 +433,33 @@ const NotificationItem = memo(({ item }) => {
430
433
  };
431
434
  case NOTIFICATION_TYPES.TRIGGER_SCRIPT_NOTIFY:
432
435
  return {
433
- content: content,
436
+ content: title,
437
+ sub_content: content,
434
438
  redirect: () => {
435
439
  navigation.navigate(Routes.UnitStack, {
436
- screen: Routes.UnitDetail,
440
+ screen: Routes.ScriptDetail,
437
441
  params: {
438
- unitId: unit_id,
442
+ id: automate_id,
439
443
  },
440
444
  });
441
445
  },
442
- iconContent: (
443
- <IconComponent icon={'home'} style={styles.backgroundSummer} />
444
- ),
446
+ iconContent: <Notify />,
445
447
  };
446
448
  default:
447
449
  return null;
448
450
  }
449
451
  }, [
452
+ params,
450
453
  content_code,
451
454
  customColorText,
452
- icon,
453
- navigation,
454
- params,
455
455
  t,
456
- language,
457
456
  logo,
457
+ icon,
458
+ language,
459
+ navigation,
458
460
  ]);
459
461
 
460
- const { content, redirect, iconContent } = renderItem() || {};
462
+ const { content, redirect, iconContent, sub_content } = renderItem() || {};
461
463
 
462
464
  const onItemPress = useCallback(() => {
463
465
  if (!isRead) {
@@ -488,7 +490,16 @@ const NotificationItem = memo(({ item }) => {
488
490
  >
489
491
  {content}
490
492
  </Text>
491
- <Text color={Colors.Gray7} type="Label" style={[styles.time]}>
493
+ {sub_content && (
494
+ <Text
495
+ type="Body"
496
+ color={Colors.Gray9}
497
+ accessibilityLabel={AccessibilityLabel.CUSTOM_TEXT}
498
+ >
499
+ {sub_content}
500
+ </Text>
501
+ )}
502
+ <Text color={Colors.Gray7} type="Label">
492
503
  {timeFormat}
493
504
  </Text>
494
505
  </View>
@@ -99,7 +99,6 @@ const Notification = memo(() => {
99
99
  onLoadMore={handleOnLoadMore}
100
100
  onRefresh={onRefresh}
101
101
  disableLoadMore={page >= maxPageNotification}
102
- styleScrollView={styles.styleScrollView}
103
102
  >
104
103
  {notifications.map((item, index) => (
105
104
  <NotificationItem item={item} key={index} />
@@ -3,9 +3,9 @@ import { Colors } from '../../../configs';
3
3
 
4
4
  export default StyleSheet.create({
5
5
  container: {
6
- paddingHorizontal: 16,
7
- paddingTop: 16,
8
- paddingBottom: 16,
6
+ paddingHorizontal: 10,
7
+ paddingTop: 10,
8
+ paddingBottom: 10,
9
9
  flexDirection: 'row',
10
10
  backgroundColor: Colors.White,
11
11
  },
@@ -21,35 +21,22 @@ export default StyleSheet.create({
21
21
  justifyContent: 'center',
22
22
  alignItems: 'center',
23
23
  },
24
+ iconNotification: {
25
+ width: '46%',
26
+ },
24
27
  viewRight: {
25
28
  flexShrink: 1,
26
29
  },
27
- time: {
28
- marginTop: 12,
29
- },
30
- iconNotification: {
31
- width: '46%',
30
+ textNotification: {
31
+ lineHeight: 23,
32
32
  },
33
33
  backgroundSummer: {
34
34
  width: '46%',
35
35
  color: Colors.Summer,
36
36
  },
37
- border: {
38
- color: Colors.Primary,
39
- paddingTop: 8,
40
- paddingBottom: 8,
41
- paddingHorizontal: 16,
42
- borderColor: Colors.Primary,
43
- borderWidth: 1,
44
- borderRadius: 50,
45
- width: 120,
46
- },
47
37
  logo: {
48
38
  width: 20,
49
39
  height: 20,
50
40
  resizeMode: 'contain',
51
41
  },
52
- textNotification: {
53
- lineHeight: 23,
54
- },
55
42
  });
@@ -1,7 +1,6 @@
1
1
  import { StyleSheet } from 'react-native';
2
2
 
3
3
  import { Colors } from '../../../configs';
4
- import { getStatusBarHeight } from '../../../configs/Constants';
5
4
 
6
5
  export default StyleSheet.create({
7
6
  wrap: {
@@ -17,7 +16,4 @@ export default StyleSheet.create({
17
16
  iconPlus: {
18
17
  marginRight: 22,
19
18
  },
20
- styleScrollView: {
21
- marginTop: getStatusBarHeight(),
22
- },
23
19
  });
@@ -48,6 +48,7 @@ const MyUnitDevice = ({ device, unit }) => {
48
48
  <IconComponent
49
49
  icon={device?.icon_kit || device?.icon}
50
50
  style={styles.image}
51
+ size={20}
51
52
  />
52
53
  <View style={styles.marginTop3}>
53
54
  <Text numberOfLines={1} semibold style={styles.nameDevice}>
@@ -1052,6 +1052,10 @@ export default {
1052
1052
  send_email: 'Send e-mail',
1053
1053
  send_app_notification: 'Send app notification',
1054
1054
  delay_the_action: 'Delay the action',
1055
+ wait: 'Wait',
1056
+ set_timeout_seconds: 'Set timeout (seconds)',
1057
+ maximum_3600_seconds: 'Maximum 3600 seconds',
1058
+ wait_time_seconds: 'Wait {delay} seconds',
1055
1059
  end_device_not_support_script:
1056
1060
  // eslint-disable-next-line max-len
1057
1061
  'This device does not support installing "{not_support}" scripts, but you can use "{support}" scripts to perform the desired setup.',
@@ -1065,6 +1065,10 @@ export default {
1065
1065
  send_email: 'Gửi e-mail',
1066
1066
  send_app_notification: 'Gửi thông báo qua ứng dụng',
1067
1067
  delay_the_action: 'Trì hoãn hành động',
1068
+ wait: 'Chờ đợi',
1069
+ set_timeout_seconds: 'Cài đặt thời gian chờ (giây)',
1070
+ maximum_3600_seconds: 'Tối đa 3600 giây',
1071
+ wait_time_seconds: 'Chờ {delay} giây',
1068
1072
  smart_account: 'Tài khoản thông minh',
1069
1073
  delete_smart_account: 'Xóa tài khoản thông minh',
1070
1074
  end_device_not_support_script:
@@ -134,6 +134,7 @@ const Routes = {
134
134
  AddAutomationTypeSmart: 'AddAutomationTypeSmart',
135
135
  AddUnknownTypeSmart: 'AddUnknownTypeSmart',
136
136
  SetupScriptNotify: 'SetupScriptNotify',
137
+ SetupScriptDelay: 'SetupScriptDelay',
137
138
  ScenarioName: 'ScenarioName',
138
139
  ValueChangeName: 'ValueChangeName',
139
140
  AllCamera: 'AllCamera',