@eohjsc/react-native-smart-city 0.5.5 → 0.5.7-rc1
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.
- package/package.json +2 -1
- package/src/commons/ActionGroup/OnOffTemplate/OnOffButtonTemplate.js +27 -3
- package/src/commons/ActionGroup/OnOffTemplate/index.js +47 -32
- package/src/commons/ActionGroup/__test__/index.test.js +40 -1
- package/src/commons/ActionTemplate/OnOffButtonAction.js +8 -2
- package/src/commons/ActionTemplate/OneButtonAction.js +5 -1
- package/src/configs/API.js +6 -0
- package/src/configs/AccessibilityLabel.js +7 -0
- package/src/configs/Constants.js +5 -0
- package/src/navigations/UnitStack.js +6 -0
- package/src/screens/Automate/AddNewAction/ChooseAction.js +1 -89
- package/src/screens/Automate/AddNewAction/NewActionWrapper.js +10 -2
- package/src/screens/Automate/AddNewAction/RenderActionItem.js +92 -0
- package/src/screens/Automate/AddNewAction/SetupConfigCondition.js +23 -11
- package/src/screens/Automate/AddNewAction/SetupScriptDelay.js +2 -2
- package/src/screens/Automate/AddNewAction/Styles/SetupSensorStyles.js +3 -3
- package/src/screens/Automate/AddNewAction/__test__/SetupConfigCondition.test.js +10 -17
- package/src/screens/Automate/EditActionsList/Styles/indexStyles.js +32 -6
- package/src/screens/Automate/EditActionsList/UpdateActionScript.js +141 -0
- package/src/screens/Automate/EditActionsList/UpdateDelayScript.js +94 -0
- package/src/screens/Automate/EditActionsList/UpdateNotifyScript.js +115 -0
- package/src/screens/Automate/EditActionsList/__tests__/UpdateActionScript.test.js +174 -0
- package/src/screens/Automate/EditActionsList/__tests__/UpdateDelayScript.test.js +119 -0
- package/src/screens/Automate/EditActionsList/__tests__/UpdateNotifyScript.test.js +138 -0
- package/src/screens/Automate/EditActionsList/__tests__/index.test.js +121 -50
- package/src/screens/Automate/EditActionsList/index.js +276 -167
- package/src/screens/Automate/ScriptDetail/index.js +11 -6
- package/src/screens/ChangePosition/index.js +1 -1
- package/src/screens/ChangePosition/styles.js +4 -0
- package/src/utils/I18n/translations/en.js +17 -4
- package/src/utils/I18n/translations/vi.js +17 -4
- package/src/utils/Route/index.js +2 -0
|
@@ -9,11 +9,12 @@ export default StyleSheet.create({
|
|
|
9
9
|
conditionActive: {
|
|
10
10
|
color: Colors.Black,
|
|
11
11
|
},
|
|
12
|
+
inputValue: { marginBottom: 10 },
|
|
12
13
|
numberInput: {
|
|
13
14
|
borderRadius: 5,
|
|
14
15
|
borderWidth: 1,
|
|
15
|
-
borderColor: Colors.Black,
|
|
16
16
|
height: 38,
|
|
17
|
+
marginTop: 10,
|
|
17
18
|
},
|
|
18
19
|
boxDevices: {
|
|
19
20
|
flexWrap: 'wrap',
|
|
@@ -37,10 +38,9 @@ export default StyleSheet.create({
|
|
|
37
38
|
},
|
|
38
39
|
modalContent: {
|
|
39
40
|
paddingHorizontal: 16,
|
|
40
|
-
|
|
41
|
+
paddingVertical: 16,
|
|
41
42
|
backgroundColor: Colors.White,
|
|
42
43
|
width: '100%',
|
|
43
44
|
borderRadius: 10,
|
|
44
|
-
paddingBottom: 16,
|
|
45
45
|
},
|
|
46
46
|
});
|
|
@@ -121,15 +121,9 @@ describe('Test SetupConfigCondition', () => {
|
|
|
121
121
|
|
|
122
122
|
const gridItems = instance.findAllByType(GridItem);
|
|
123
123
|
|
|
124
|
-
expect(gridItems[0].findByType(Text).props.children).toEqual(
|
|
125
|
-
|
|
126
|
-
);
|
|
127
|
-
expect(gridItems[1].findByType(Text).props.children).toEqual(
|
|
128
|
-
'Case 2 (2 >= "config name" < 1)'
|
|
129
|
-
);
|
|
130
|
-
expect(gridItems[2].findByType(Text).props.children).toEqual(
|
|
131
|
-
'Case 3 (2 >= "config name" < 1)'
|
|
132
|
-
);
|
|
124
|
+
expect(gridItems[0].findByType(Text).props.children).toEqual('Case 1');
|
|
125
|
+
expect(gridItems[1].findByType(Text).props.children).toEqual('Case 2');
|
|
126
|
+
expect(gridItems[2].findByType(Text).props.children).toEqual('Case 3');
|
|
133
127
|
});
|
|
134
128
|
|
|
135
129
|
it('render boolean value evaluation', async () => {
|
|
@@ -152,12 +146,8 @@ describe('Test SetupConfigCondition', () => {
|
|
|
152
146
|
|
|
153
147
|
const gridItems = instance.findAllByType(GridItem);
|
|
154
148
|
expect(gridItems).toHaveLength(3);
|
|
155
|
-
expect(gridItems[0].findByType(Text).props.children).toEqual(
|
|
156
|
-
|
|
157
|
-
);
|
|
158
|
-
expect(gridItems[1].findByType(Text).props.children).toEqual(
|
|
159
|
-
'Case On ("config name" = 1)'
|
|
160
|
-
);
|
|
149
|
+
expect(gridItems[0].findByType(Text).props.children).toEqual('Case Off');
|
|
150
|
+
expect(gridItems[1].findByType(Text).props.children).toEqual('Case On');
|
|
161
151
|
expect(gridItems[2].findByType(Text).props.children).toEqual(
|
|
162
152
|
'Customize conditions'
|
|
163
153
|
);
|
|
@@ -284,11 +274,14 @@ describe('Test SetupConfigCondition', () => {
|
|
|
284
274
|
if (message === null) {
|
|
285
275
|
expect(mockNavigate).toBeCalled();
|
|
286
276
|
} else {
|
|
287
|
-
expect(spyToastError).toBeCalledWith(message);
|
|
277
|
+
expect(spyToastError).toBeCalledWith(message, '', 3000);
|
|
288
278
|
}
|
|
289
279
|
};
|
|
290
280
|
|
|
291
281
|
it('Test render when have input not number', async () => {
|
|
292
|
-
await testConditionValue(
|
|
282
|
+
await testConditionValue(
|
|
283
|
+
'abc',
|
|
284
|
+
'Please enter a number and select a condition'
|
|
285
|
+
);
|
|
293
286
|
});
|
|
294
287
|
});
|
|
@@ -11,12 +11,12 @@ export default StyleSheet.create({
|
|
|
11
11
|
},
|
|
12
12
|
wrapContent: {
|
|
13
13
|
flex: 1,
|
|
14
|
-
paddingHorizontal:
|
|
14
|
+
paddingHorizontal: 20,
|
|
15
15
|
},
|
|
16
16
|
wrapItem: {
|
|
17
17
|
flexDirection: 'row',
|
|
18
18
|
height: 100,
|
|
19
|
-
marginBottom:
|
|
19
|
+
marginBottom: 20,
|
|
20
20
|
},
|
|
21
21
|
isDragging: {
|
|
22
22
|
borderBottomWidth: 1,
|
|
@@ -32,7 +32,6 @@ export default StyleSheet.create({
|
|
|
32
32
|
borderColor: Colors.Gray4,
|
|
33
33
|
},
|
|
34
34
|
rightItem: {
|
|
35
|
-
position: 'relative',
|
|
36
35
|
flex: 1,
|
|
37
36
|
marginLeft: 4,
|
|
38
37
|
borderRadius: 4,
|
|
@@ -62,7 +61,7 @@ export default StyleSheet.create({
|
|
|
62
61
|
flex: 1,
|
|
63
62
|
},
|
|
64
63
|
containerStyle: {
|
|
65
|
-
marginVertical:
|
|
64
|
+
marginVertical: 20,
|
|
66
65
|
},
|
|
67
66
|
wrapBottom: {
|
|
68
67
|
height: 80,
|
|
@@ -96,7 +95,34 @@ export default StyleSheet.create({
|
|
|
96
95
|
flexDirection: 'row',
|
|
97
96
|
width: '45%',
|
|
98
97
|
},
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
popoverStyle: {
|
|
99
|
+
width: '100%',
|
|
100
|
+
backgroundColor: Colors.White,
|
|
101
|
+
borderRadius: 10,
|
|
102
|
+
},
|
|
103
|
+
modalHeader: {
|
|
104
|
+
paddingLeft: 16,
|
|
105
|
+
paddingRight: 16,
|
|
106
|
+
},
|
|
107
|
+
inputNumber: {
|
|
108
|
+
borderWidth: 1,
|
|
109
|
+
borderColor: Colors.Gray4,
|
|
110
|
+
borderStyle: 'solid',
|
|
111
|
+
borderRadius: 10,
|
|
112
|
+
marginBottom: 10,
|
|
113
|
+
},
|
|
114
|
+
textTitle: {
|
|
115
|
+
borderWidth: 1,
|
|
116
|
+
borderColor: Colors.Gray4,
|
|
117
|
+
borderStyle: 'solid',
|
|
118
|
+
borderRadius: 10,
|
|
119
|
+
},
|
|
120
|
+
textMessage: {
|
|
121
|
+
height: 200,
|
|
122
|
+
textAlignVertical: 'top',
|
|
123
|
+
borderWidth: 1,
|
|
124
|
+
borderColor: Colors.Gray4,
|
|
125
|
+
borderStyle: 'solid',
|
|
126
|
+
borderRadius: 10,
|
|
101
127
|
},
|
|
102
128
|
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import { useNavigation } from '@react-navigation/native';
|
|
4
|
+
|
|
5
|
+
import NewActionWrapper from '../AddNewAction/NewActionWrapper';
|
|
6
|
+
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
7
|
+
import { axiosGet, axiosPut } from '../../../utils/Apis/axios';
|
|
8
|
+
import { API } from '../../../configs';
|
|
9
|
+
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
10
|
+
import Routes from '../../../utils/Route';
|
|
11
|
+
import RenderActionItem from '../AddNewAction/RenderActionItem';
|
|
12
|
+
import { useBackendPermission } from '../../../utils/Permission/backend';
|
|
13
|
+
import AccessibilityLabel from '../../../configs/AccessibilityLabel';
|
|
14
|
+
|
|
15
|
+
const UpdateActionScript = ({ route }) => {
|
|
16
|
+
const t = useTranslations();
|
|
17
|
+
const { navigate } = useNavigation();
|
|
18
|
+
const {
|
|
19
|
+
unitId,
|
|
20
|
+
device,
|
|
21
|
+
automateId,
|
|
22
|
+
numberActionAdded,
|
|
23
|
+
scriptItemId,
|
|
24
|
+
closeScreen,
|
|
25
|
+
} = route.params;
|
|
26
|
+
const permissions = useBackendPermission();
|
|
27
|
+
const { max_actions_per_automation } = permissions || {};
|
|
28
|
+
const [data, setData] = useState([]);
|
|
29
|
+
const [actions, setActions] = useState([]);
|
|
30
|
+
const [processing, setProcessing] = useState(false);
|
|
31
|
+
|
|
32
|
+
const numberActionCanAdd = useMemo(() => {
|
|
33
|
+
return max_actions_per_automation - numberActionAdded;
|
|
34
|
+
}, [max_actions_per_automation, numberActionAdded]);
|
|
35
|
+
|
|
36
|
+
const fetchData = useCallback(async () => {
|
|
37
|
+
const { success, data: automateData } = await axiosGet(
|
|
38
|
+
API.DEVICE.DISPLAY_ACTIONS(device.id)
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
success && setData(automateData);
|
|
42
|
+
}, [device.id]);
|
|
43
|
+
|
|
44
|
+
const onSave = useCallback(async () => {
|
|
45
|
+
if (actions.length > numberActionCanAdd) {
|
|
46
|
+
ToastBottomHelper.error(
|
|
47
|
+
`${t('you_can_only_add_more', { number: numberActionCanAdd })} ${t(
|
|
48
|
+
numberActionCanAdd > 1 ? 'actions' : 'action'
|
|
49
|
+
)}`,
|
|
50
|
+
null,
|
|
51
|
+
3000
|
|
52
|
+
);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
setProcessing(true);
|
|
56
|
+
let list_action = [...actions];
|
|
57
|
+
list_action = list_action.map((item) => ({
|
|
58
|
+
action: item.action,
|
|
59
|
+
data: item.data,
|
|
60
|
+
}));
|
|
61
|
+
const { success } = await axiosPut(
|
|
62
|
+
API.AUTOMATE.UPDATE_SCRIPT_ACTION(automateId),
|
|
63
|
+
{
|
|
64
|
+
id: scriptItemId,
|
|
65
|
+
list_action,
|
|
66
|
+
unit: unitId,
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
if (success) {
|
|
71
|
+
const { success: success1, data: automateData } = await axiosGet(
|
|
72
|
+
API.AUTOMATE.SCRIPT_ITEMS(automateId)
|
|
73
|
+
);
|
|
74
|
+
setProcessing(false);
|
|
75
|
+
if (success1) {
|
|
76
|
+
ToastBottomHelper.success(t('update_successfully'));
|
|
77
|
+
navigate(Routes.EditActionsList, {
|
|
78
|
+
data: automateData.script_items,
|
|
79
|
+
id: automateId,
|
|
80
|
+
unitId: unitId,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
setProcessing(false);
|
|
85
|
+
}
|
|
86
|
+
}, [
|
|
87
|
+
actions,
|
|
88
|
+
numberActionCanAdd,
|
|
89
|
+
automateId,
|
|
90
|
+
scriptItemId,
|
|
91
|
+
unitId,
|
|
92
|
+
t,
|
|
93
|
+
navigate,
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
const handleOnSelectAction = (action) => {
|
|
97
|
+
setActions((prevActions) => {
|
|
98
|
+
const index = prevActions.findIndex(
|
|
99
|
+
(item) => item.index === action.index
|
|
100
|
+
);
|
|
101
|
+
if (index !== -1) {
|
|
102
|
+
const newActions = [...prevActions];
|
|
103
|
+
newActions[index] = action;
|
|
104
|
+
return newActions;
|
|
105
|
+
} else {
|
|
106
|
+
return [...prevActions, action];
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
fetchData();
|
|
113
|
+
}, [fetchData]);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<NewActionWrapper
|
|
117
|
+
closeScreen={closeScreen}
|
|
118
|
+
canNext={actions.length}
|
|
119
|
+
onNext={onSave}
|
|
120
|
+
nextTitle={t('update_now')}
|
|
121
|
+
name={t('choose_action')}
|
|
122
|
+
accessibilityLabel={AccessibilityLabel.BUTTON_UPDATE_SCRIPT_ITEM}
|
|
123
|
+
showLoading={processing}
|
|
124
|
+
>
|
|
125
|
+
<View>
|
|
126
|
+
{!!data.length &&
|
|
127
|
+
data.map((item, index) => (
|
|
128
|
+
<RenderActionItem
|
|
129
|
+
key={`action_item_${index}`}
|
|
130
|
+
device={device}
|
|
131
|
+
item={item}
|
|
132
|
+
index={index}
|
|
133
|
+
handleOnSelectAction={handleOnSelectAction}
|
|
134
|
+
t={t}
|
|
135
|
+
/>
|
|
136
|
+
))}
|
|
137
|
+
</View>
|
|
138
|
+
</NewActionWrapper>
|
|
139
|
+
);
|
|
140
|
+
};
|
|
141
|
+
export default UpdateActionScript;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import _TextInput from '../../../commons/Form/TextInput';
|
|
3
|
+
import styles from './Styles/indexStyles';
|
|
4
|
+
import AccessibilityLabel from '../../../configs/AccessibilityLabel';
|
|
5
|
+
import { View } from 'react-native';
|
|
6
|
+
import { axiosPut } from '../../../utils/Apis/axios';
|
|
7
|
+
import { API } from '../../../configs';
|
|
8
|
+
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
9
|
+
import BottomButtonView from '../../../commons/BottomButtonView';
|
|
10
|
+
|
|
11
|
+
const UpdateDelayScript = ({
|
|
12
|
+
automateId,
|
|
13
|
+
scriptItemId,
|
|
14
|
+
delay_script,
|
|
15
|
+
t,
|
|
16
|
+
onClosePopup,
|
|
17
|
+
actionsList,
|
|
18
|
+
setActionList,
|
|
19
|
+
updateIndex,
|
|
20
|
+
setNeedRefresh,
|
|
21
|
+
}) => {
|
|
22
|
+
const { delay } = delay_script;
|
|
23
|
+
const [newValue, setNewValue] = useState(String(delay));
|
|
24
|
+
|
|
25
|
+
const onPressSave = useCallback(async () => {
|
|
26
|
+
const { success } = await axiosPut(
|
|
27
|
+
API.AUTOMATE.UPDATE_SCRIPT_DELAY(automateId),
|
|
28
|
+
{
|
|
29
|
+
id: scriptItemId,
|
|
30
|
+
delay: newValue,
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
if (success) {
|
|
34
|
+
actionsList[updateIndex].delay_script.delay = newValue;
|
|
35
|
+
setActionList(actionsList);
|
|
36
|
+
onClosePopup();
|
|
37
|
+
ToastBottomHelper.success(t('update_successfully'));
|
|
38
|
+
setNeedRefresh((prev) => !prev);
|
|
39
|
+
}
|
|
40
|
+
}, [
|
|
41
|
+
actionsList,
|
|
42
|
+
automateId,
|
|
43
|
+
newValue,
|
|
44
|
+
onClosePopup,
|
|
45
|
+
scriptItemId,
|
|
46
|
+
setActionList,
|
|
47
|
+
setNeedRefresh,
|
|
48
|
+
t,
|
|
49
|
+
updateIndex,
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
const onChangeDelay = (value) => {
|
|
53
|
+
setNewValue(value);
|
|
54
|
+
};
|
|
55
|
+
const canSave = useMemo(() => {
|
|
56
|
+
const value = parseInt(newValue, 10);
|
|
57
|
+
if (
|
|
58
|
+
!Number.isInteger(value) ||
|
|
59
|
+
value === delay ||
|
|
60
|
+
value <= 0 ||
|
|
61
|
+
value > 3600
|
|
62
|
+
) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}, [delay, newValue]);
|
|
67
|
+
return (
|
|
68
|
+
<>
|
|
69
|
+
<_TextInput
|
|
70
|
+
label={t('update_waiting_time')}
|
|
71
|
+
placeholder={t('maximum_3600_seconds')}
|
|
72
|
+
keyboardType="numeric"
|
|
73
|
+
textInputStyle={styles.inputNumber}
|
|
74
|
+
value={newValue}
|
|
75
|
+
onChange={onChangeDelay}
|
|
76
|
+
maxLength={4}
|
|
77
|
+
accessibilityLabel={AccessibilityLabel.AUTOMATE_INPUT_DELAY}
|
|
78
|
+
autoFocus
|
|
79
|
+
/>
|
|
80
|
+
|
|
81
|
+
<View style={styles.wrapBottom}>
|
|
82
|
+
<BottomButtonView
|
|
83
|
+
disableKeyBoardAnimated
|
|
84
|
+
mainTitle={t('update_now')}
|
|
85
|
+
onPressMain={onPressSave}
|
|
86
|
+
typeMain={canSave ? 'primary' : 'disabled'}
|
|
87
|
+
accessibilityLabel={AccessibilityLabel.BUTTON_SAVE_EDIT_ACTION_LIST}
|
|
88
|
+
/>
|
|
89
|
+
</View>
|
|
90
|
+
</>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export default UpdateDelayScript;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import _TextInput from '../../../commons/Form/TextInput';
|
|
3
|
+
import styles from './Styles/indexStyles';
|
|
4
|
+
import AccessibilityLabel from '../../../configs/AccessibilityLabel';
|
|
5
|
+
import { Keyboard, TouchableWithoutFeedback, View } from 'react-native';
|
|
6
|
+
import { axiosPut } from '../../../utils/Apis/axios';
|
|
7
|
+
import { API } from '../../../configs';
|
|
8
|
+
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
9
|
+
import BottomButtonView from '../../../commons/BottomButtonView';
|
|
10
|
+
|
|
11
|
+
const UpdateNotifyScript = ({
|
|
12
|
+
unitId,
|
|
13
|
+
automateId,
|
|
14
|
+
scriptItemId,
|
|
15
|
+
notify_script,
|
|
16
|
+
t,
|
|
17
|
+
onClosePopup,
|
|
18
|
+
actionsList,
|
|
19
|
+
setActionList,
|
|
20
|
+
updateIndex,
|
|
21
|
+
setNeedRefresh,
|
|
22
|
+
}) => {
|
|
23
|
+
const { title, message } = notify_script;
|
|
24
|
+
const [newValue, setNewValue] = useState({
|
|
25
|
+
newTitle: title,
|
|
26
|
+
newMessage: message,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const onPressSave = useCallback(async () => {
|
|
30
|
+
const { success } = await axiosPut(
|
|
31
|
+
API.AUTOMATE.UPDATE_SCRIPT_NOTIFY(automateId),
|
|
32
|
+
{
|
|
33
|
+
id: scriptItemId,
|
|
34
|
+
unit: unitId,
|
|
35
|
+
title: newValue.newTitle,
|
|
36
|
+
message: newValue.newMessage,
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
if (success) {
|
|
40
|
+
actionsList[updateIndex].notify_script = {
|
|
41
|
+
title: newValue.newTitle,
|
|
42
|
+
message: newValue.newMessage,
|
|
43
|
+
};
|
|
44
|
+
setActionList(actionsList);
|
|
45
|
+
onClosePopup();
|
|
46
|
+
ToastBottomHelper.success(t('update_successfully'));
|
|
47
|
+
setNeedRefresh((prev) => !prev);
|
|
48
|
+
}
|
|
49
|
+
}, [
|
|
50
|
+
actionsList,
|
|
51
|
+
automateId,
|
|
52
|
+
newValue.newMessage,
|
|
53
|
+
newValue.newTitle,
|
|
54
|
+
onClosePopup,
|
|
55
|
+
scriptItemId,
|
|
56
|
+
setActionList,
|
|
57
|
+
setNeedRefresh,
|
|
58
|
+
t,
|
|
59
|
+
unitId,
|
|
60
|
+
updateIndex,
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const onChangeTitle = (value) => {
|
|
64
|
+
setNewValue((prev) => ({ ...prev, newTitle: value }));
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const onChangeMessage = (value) => {
|
|
68
|
+
setNewValue((prev) => ({ ...prev, newMessage: value }));
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const canSave = useMemo(() => {
|
|
72
|
+
const { newMessage, newTitle } = newValue;
|
|
73
|
+
if (newMessage === message && newTitle === title) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return !!newMessage && !!newTitle;
|
|
77
|
+
}, [message, newValue, title]);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
|
|
81
|
+
<View>
|
|
82
|
+
<_TextInput
|
|
83
|
+
label={t('update_title_notification')}
|
|
84
|
+
placeholder={t('title_notification')}
|
|
85
|
+
onChange={onChangeTitle}
|
|
86
|
+
textInputStyle={styles.textTitle}
|
|
87
|
+
value={newValue.newTitle}
|
|
88
|
+
accessibilityLabel={AccessibilityLabel.AUTOMATE_TITLE_NOTIFY}
|
|
89
|
+
autoFocus
|
|
90
|
+
/>
|
|
91
|
+
<_TextInput
|
|
92
|
+
label={t('update_message_notification')}
|
|
93
|
+
placeholder={t('message_notification')}
|
|
94
|
+
onChange={onChangeMessage}
|
|
95
|
+
textInputStyle={styles.textMessage}
|
|
96
|
+
value={newValue.newMessage}
|
|
97
|
+
accessibilityLabel={AccessibilityLabel.AUTOMATE_MESSAGE_NOTIFY}
|
|
98
|
+
multiline={true}
|
|
99
|
+
maxLength={255}
|
|
100
|
+
/>
|
|
101
|
+
<View style={styles.wrapBottom}>
|
|
102
|
+
<BottomButtonView
|
|
103
|
+
mainTitle={t('update_now')}
|
|
104
|
+
onPressMain={onPressSave}
|
|
105
|
+
typeMain={canSave ? 'primary' : 'disabled'}
|
|
106
|
+
accessibilityLabel={AccessibilityLabel.BUTTON_SAVE_EDIT_ACTION_LIST}
|
|
107
|
+
disableKeyBoardAnimated
|
|
108
|
+
/>
|
|
109
|
+
</View>
|
|
110
|
+
</View>
|
|
111
|
+
</TouchableWithoutFeedback>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export default UpdateNotifyScript;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer, { act } from 'react-test-renderer';
|
|
3
|
+
import MockAdapter from 'axios-mock-adapter';
|
|
4
|
+
|
|
5
|
+
import { SCProvider } from '../../../../context';
|
|
6
|
+
import { mockSCStore } from '../../../../context/mockStore';
|
|
7
|
+
import API from '../../../../configs/API';
|
|
8
|
+
import { AccessibilityLabel } from '../../../../configs/Constants';
|
|
9
|
+
|
|
10
|
+
import api from '../../../../utils/Apis/axios';
|
|
11
|
+
import { ToastBottomHelper } from '../../../../utils/Utils';
|
|
12
|
+
import UpdateActionScript from '../UpdateActionScript';
|
|
13
|
+
import { TouchableOpacity } from 'react-native';
|
|
14
|
+
import { useRoute } from '@react-navigation/native';
|
|
15
|
+
|
|
16
|
+
const mock = new MockAdapter(api.axiosInstance);
|
|
17
|
+
const mockedNavigate = jest.fn();
|
|
18
|
+
const mockedDispatch = jest.fn();
|
|
19
|
+
const mockedGoBack = jest.fn();
|
|
20
|
+
const mockedPermission = jest.fn();
|
|
21
|
+
|
|
22
|
+
jest.mock('@react-navigation/native', () => {
|
|
23
|
+
return {
|
|
24
|
+
...jest.requireActual('@react-navigation/native'),
|
|
25
|
+
useNavigation: () => ({
|
|
26
|
+
navigate: mockedNavigate,
|
|
27
|
+
dispatch: mockedDispatch,
|
|
28
|
+
goBack: mockedGoBack,
|
|
29
|
+
}),
|
|
30
|
+
useRoute: jest.fn(),
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
jest.mock('../../../../utils/Permission/backend', () => ({
|
|
34
|
+
useBackendPermission: () => ({
|
|
35
|
+
max_actions_per_automation: 1,
|
|
36
|
+
}),
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
let response = [
|
|
40
|
+
{
|
|
41
|
+
template: 'on_off_button_action_template',
|
|
42
|
+
title: 'title',
|
|
43
|
+
configuration: {
|
|
44
|
+
text_on: 'text_on',
|
|
45
|
+
text_off: 'text_off',
|
|
46
|
+
action_on: 'action_on',
|
|
47
|
+
action_off: 'action_off',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
template: 'one_button_action_template',
|
|
52
|
+
configuration: { text: 'text', action: 'action' },
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
const wrapComponent = (route) => (
|
|
56
|
+
<SCProvider initState={mockSCStore({})}>
|
|
57
|
+
<UpdateActionScript route={route} />
|
|
58
|
+
</SCProvider>
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
describe('Test UpdateActionScript', () => {
|
|
62
|
+
let tree;
|
|
63
|
+
let route = {
|
|
64
|
+
params: {
|
|
65
|
+
unitId: 1,
|
|
66
|
+
automateId: 1,
|
|
67
|
+
scriptItemId: 1,
|
|
68
|
+
numberActionAdded: 0,
|
|
69
|
+
device: { id: 1 },
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
useRoute.mockReturnValue({
|
|
74
|
+
params: {},
|
|
75
|
+
});
|
|
76
|
+
mockedNavigate.mockClear();
|
|
77
|
+
mockedPermission.mockClear();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('test update action script', async () => {
|
|
81
|
+
mock.onGet(API.DEVICE.DISPLAY_ACTIONS(1)).reply(200, response);
|
|
82
|
+
mock.onPut(API.AUTOMATE.UPDATE_SCRIPT_ACTION(1)).reply(200);
|
|
83
|
+
mock.onGet(API.AUTOMATE.SCRIPT_ITEMS(1)).reply(200, { script_items: [] });
|
|
84
|
+
await act(async () => {
|
|
85
|
+
tree = await renderer.create(wrapComponent(route));
|
|
86
|
+
});
|
|
87
|
+
const instance = tree.root;
|
|
88
|
+
const buttonOn = instance.findAll(
|
|
89
|
+
(el) =>
|
|
90
|
+
el.props.accessibilityLabel === AccessibilityLabel.ON_BUTTON &&
|
|
91
|
+
el.type === TouchableOpacity
|
|
92
|
+
);
|
|
93
|
+
await act(async () => {
|
|
94
|
+
buttonOn[0].props.onPress();
|
|
95
|
+
});
|
|
96
|
+
const buttonSave = instance.find(
|
|
97
|
+
(el) =>
|
|
98
|
+
el.props.accessibilityLabel ===
|
|
99
|
+
AccessibilityLabel.BUTTON_UPDATE_SCRIPT_ITEM
|
|
100
|
+
);
|
|
101
|
+
await act(async () => {
|
|
102
|
+
buttonSave.props.onNext();
|
|
103
|
+
});
|
|
104
|
+
expect(mockedNavigate).toHaveBeenCalled();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('test update action script fail', async () => {
|
|
108
|
+
mock.onGet(API.DEVICE.DISPLAY_ACTIONS(1)).reply(200, response);
|
|
109
|
+
mock.onPut(API.AUTOMATE.UPDATE_SCRIPT_ACTION(1)).reply(400);
|
|
110
|
+
await act(async () => {
|
|
111
|
+
tree = await renderer.create(wrapComponent(route));
|
|
112
|
+
});
|
|
113
|
+
const instance = tree.root;
|
|
114
|
+
const buttonOn = instance.findAll(
|
|
115
|
+
(el) =>
|
|
116
|
+
el.props.accessibilityLabel === AccessibilityLabel.ON_BUTTON &&
|
|
117
|
+
el.type === TouchableOpacity
|
|
118
|
+
);
|
|
119
|
+
await act(async () => {
|
|
120
|
+
buttonOn[0].props.onPress();
|
|
121
|
+
});
|
|
122
|
+
const buttonSave = instance.find(
|
|
123
|
+
(el) =>
|
|
124
|
+
el.props.accessibilityLabel ===
|
|
125
|
+
AccessibilityLabel.BUTTON_UPDATE_SCRIPT_ITEM
|
|
126
|
+
);
|
|
127
|
+
await act(async () => {
|
|
128
|
+
buttonSave.props.onNext();
|
|
129
|
+
});
|
|
130
|
+
expect(mockedNavigate).not.toHaveBeenCalled();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('test can not update because adding too many times allowed', async () => {
|
|
134
|
+
mock.onGet(API.DEVICE.DISPLAY_ACTIONS(1)).reply(200, response);
|
|
135
|
+
const spyToast = jest.spyOn(ToastBottomHelper, 'error');
|
|
136
|
+
await act(async () => {
|
|
137
|
+
tree = await renderer.create(wrapComponent(route));
|
|
138
|
+
});
|
|
139
|
+
const instance = tree.root;
|
|
140
|
+
const buttonOn = instance.findAll(
|
|
141
|
+
(el) =>
|
|
142
|
+
el.props.accessibilityLabel === AccessibilityLabel.ON_BUTTON &&
|
|
143
|
+
el.type === TouchableOpacity
|
|
144
|
+
);
|
|
145
|
+
await act(async () => {
|
|
146
|
+
buttonOn[0].props.onPress();
|
|
147
|
+
});
|
|
148
|
+
const buttonOff = instance.findAll(
|
|
149
|
+
(el) =>
|
|
150
|
+
el.props.accessibilityLabel === AccessibilityLabel.OFF_BUTTON &&
|
|
151
|
+
el.type === TouchableOpacity
|
|
152
|
+
);
|
|
153
|
+
await act(async () => {
|
|
154
|
+
buttonOff[0].props.onPress();
|
|
155
|
+
});
|
|
156
|
+
const buttonOne = instance.findAll(
|
|
157
|
+
(el) =>
|
|
158
|
+
el.props.accessibilityLabel === AccessibilityLabel.ONE_BUTTON &&
|
|
159
|
+
el.type === TouchableOpacity
|
|
160
|
+
);
|
|
161
|
+
await act(async () => {
|
|
162
|
+
buttonOne[0].props.onPress();
|
|
163
|
+
});
|
|
164
|
+
const buttonSave = instance.find(
|
|
165
|
+
(el) =>
|
|
166
|
+
el.props.accessibilityLabel ===
|
|
167
|
+
AccessibilityLabel.BUTTON_UPDATE_SCRIPT_ITEM
|
|
168
|
+
);
|
|
169
|
+
await act(async () => {
|
|
170
|
+
buttonSave.props.onNext();
|
|
171
|
+
});
|
|
172
|
+
expect(spyToast).toBeCalled();
|
|
173
|
+
});
|
|
174
|
+
});
|