@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.
- package/assets/images/Sms.svg +9 -0
- package/package.json +1 -1
- package/src/commons/Automate/ItemConditionScriptDetailStyles.js +1 -0
- package/src/commons/Widgets/IFrame/IFrame.js +2 -2
- package/src/commons/Widgets/IFrame/IFrameStyles.js +5 -0
- package/src/configs/API.js +8 -2
- package/src/hooks/Common/useBlockBack.js +36 -21
- package/src/hooks/Common/useDevicesStatus.js +16 -13
- package/src/navigations/UnitStack.js +24 -0
- package/src/screens/AddNewGateway/ScanDeviceLocal.js +14 -16
- package/src/screens/AddNewGateway/ScanDeviceLocalStyles.js +6 -1
- package/src/screens/AddNewGateway/__test__/ScanDeviceLocal.test.js +4 -13
- package/src/screens/Automate/AddNewAction/SetupScriptReceiverSms.js +167 -0
- package/src/screens/Automate/AddNewAction/SetupScriptSms.js +73 -0
- package/src/screens/Automate/AddNewAction/Styles/SetupScriptEmailStyles.js +5 -0
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverSms.test.js +105 -0
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptSms.test.js +70 -0
- package/src/screens/Automate/EditActionsList/UpdateReceiverSmsScript.js +178 -0
- package/src/screens/Automate/EditActionsList/UpdateSmsScript.js +66 -0
- package/src/screens/Automate/EditActionsList/__tests__/UpdateReceiverSmsScript.test.js +82 -0
- package/src/screens/Automate/EditActionsList/__tests__/UpdateSmsScript.test.js +71 -0
- package/src/screens/Automate/EditActionsList/index.js +50 -1
- package/src/screens/Automate/ScriptDetail/Components/AddActionScript.js +52 -19
- package/src/screens/Automate/ScriptDetail/Styles/indexStyles.js +39 -2
- package/src/screens/Automate/ScriptDetail/__test__/index.test.js +106 -38
- package/src/screens/Automate/ScriptDetail/index.js +227 -10
- package/src/screens/SharedUnit/__test__/ShareUnit.test.js +22 -1
- package/src/screens/Sharing/UnitMemberList.js +1 -1
- package/src/screens/Sharing/__test__/UnitMemberList.test.js +10 -0
- package/src/screens/Unit/__test__/Detail.test.js +1 -1
- package/src/utils/I18n/translations/en.js +10 -0
- package/src/utils/I18n/translations/vi.js +10 -0
- package/src/utils/Route/index.js +3 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg width="34" height="34" viewBox="0 0 34 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M30.3334 0.333313H3.66671C1.83337 0.333313 0.350041 1.83331 0.350041 3.66665L0.333374 33.6666L7.00004 27H30.3334C32.1667 27 33.6667 25.5 33.6667 23.6666V3.66665C33.6667 1.83331 32.1667 0.333313 30.3334 0.333313ZM12 15.3333H8.66671V12H12V15.3333ZM18.6667 15.3333H15.3334V12H18.6667V15.3333ZM25.3334 15.3333H22V12H25.3334V15.3333Z" fill="url(#paint0_linear_3441_1581)"/>
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="paint0_linear_3441_1581" x1="0.333374" y1="0.333313" x2="33.6298" y2="33.6667" gradientUnits="userSpaceOnUse">
|
|
5
|
+
<stop stop-color="#2B6B9F"/>
|
|
6
|
+
<stop offset="1" stop-color="#D5FFCB"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
</defs>
|
|
9
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -29,13 +29,13 @@ const IFrame = memo(({ item = {} }) => {
|
|
|
29
29
|
}, [id, url]);
|
|
30
30
|
|
|
31
31
|
return (
|
|
32
|
-
<View>
|
|
32
|
+
<View style={styles.viewIframe}>
|
|
33
33
|
<TouchableOpacity style={styles.reloadButton} onClick={reload}>
|
|
34
34
|
<IconComponent size={20} icon="ReloadOutlined" />
|
|
35
35
|
</TouchableOpacity>
|
|
36
36
|
<WebView
|
|
37
37
|
source={{ uri: urlWithEnv }}
|
|
38
|
-
|
|
38
|
+
className={styles.iframe}
|
|
39
39
|
ref={ref}
|
|
40
40
|
title={title}
|
|
41
41
|
javaScriptEnabled
|
package/src/configs/API.js
CHANGED
|
@@ -21,7 +21,8 @@ const API = {
|
|
|
21
21
|
AUTOMATE: (id) => `/property_manager/units/${id}/automate/`,
|
|
22
22
|
DEVICE_CONTROL: (id) => `/property_manager/units/${id}/device_control/`,
|
|
23
23
|
DEVICE_SENSOR: (id) => `/property_manager/units/${id}/device_sensor/`,
|
|
24
|
-
|
|
24
|
+
END_DEVICES_STATUS: (id) =>
|
|
25
|
+
`/property_manager/units/${id}/end_devices_status/`,
|
|
25
26
|
CHANGE_OWNER: (id) => `/property_manager/units/${id}/change_owner/`,
|
|
26
27
|
FAVOURITE_DEVICES: (id) =>
|
|
27
28
|
`/property_manager/units/${id}/favourite_devices/`,
|
|
@@ -107,6 +108,8 @@ const API = {
|
|
|
107
108
|
`/property_manager/automate/${automateId}/update_script_action/`,
|
|
108
109
|
UPDATE_SCRIPT_EMAIL: (automateId) =>
|
|
109
110
|
`/property_manager/automate/${automateId}/update_script_email/`,
|
|
111
|
+
UPDATE_SCRIPT_SMS: (automateId) =>
|
|
112
|
+
`/property_manager/automate/${automateId}/update_script_sms/`,
|
|
110
113
|
ADD_SCRIPT_ACTION: (id) =>
|
|
111
114
|
`/property_manager/automate/${id}/add_script_action/`,
|
|
112
115
|
ADD_SCRIPT_NOTIFY: (id) =>
|
|
@@ -115,7 +118,7 @@ const API = {
|
|
|
115
118
|
`/property_manager/automate/${id}/add_script_delay/`,
|
|
116
119
|
ADD_SCRIPT_EMAIL: (id) =>
|
|
117
120
|
`/property_manager/automate/${id}/add_script_email/`,
|
|
118
|
-
|
|
121
|
+
ADD_SCRIPT_SMS: (id) => `/property_manager/automate/${id}/add_script_sms/`,
|
|
119
122
|
FETCH_AUTOMATE: (automateId) => `/property_manager/automate/${automateId}/`,
|
|
120
123
|
CREATE_AUTOMATE: () => '/property_manager/automate/',
|
|
121
124
|
UPDATE_AUTOMATE: (automateId) =>
|
|
@@ -130,6 +133,8 @@ const API = {
|
|
|
130
133
|
GET_MULTI_UNITS: () => '/property_manager/automate/multi_unit/',
|
|
131
134
|
ACTIVITY_LOG: (id) => `/property_manager/automate/${id}/logs/`,
|
|
132
135
|
STARRED_SCRIPTS: () => '/property_manager/automate/starred_scripts/',
|
|
136
|
+
ENABLE_LOCAL_CONTROL: (id) =>
|
|
137
|
+
`/property_manager/automate/${id}/enable_local_control/`,
|
|
133
138
|
},
|
|
134
139
|
HOME_ASSISTANT: {
|
|
135
140
|
CHECK_SEND_EMAIL: () =>
|
|
@@ -244,6 +249,7 @@ const API = {
|
|
|
244
249
|
COUNT: () => '/chip_manager/developer_mode_chips/count/',
|
|
245
250
|
DETAIL: (id) => `/chip_manager/developer_mode_chips/${id}/`,
|
|
246
251
|
REBOOT: (id) => `/chip_manager/developer_mode_chips/${id}/reboot_chip/`,
|
|
252
|
+
SHARED: () => '/property_manager/iot_dashboard/filters/chip_shared/',
|
|
247
253
|
},
|
|
248
254
|
ARDUINO: {
|
|
249
255
|
DETAIL: (id) => `/iot/modules/arduino/gateways/${id}/`,
|
|
@@ -1,36 +1,51 @@
|
|
|
1
1
|
import { useFocusEffect, useNavigation } from '@react-navigation/native';
|
|
2
2
|
import { useCallback, useRef } from 'react';
|
|
3
|
-
import { BackHandler } from 'react-native';
|
|
3
|
+
import { BackHandler, Platform } from 'react-native';
|
|
4
4
|
|
|
5
5
|
export const useBlockBack = (actionBack) => {
|
|
6
6
|
const navigation = useNavigation();
|
|
7
|
-
const
|
|
7
|
+
const isListening = useRef(false);
|
|
8
8
|
|
|
9
9
|
const blockBack = useCallback(() => {
|
|
10
10
|
actionBack && actionBack();
|
|
11
11
|
return true;
|
|
12
12
|
}, [actionBack]);
|
|
13
13
|
|
|
14
|
+
const blockBeforeRemove = useCallback(
|
|
15
|
+
(e) => {
|
|
16
|
+
e.preventDefault();
|
|
17
|
+
blockBack();
|
|
18
|
+
},
|
|
19
|
+
[blockBack]
|
|
20
|
+
);
|
|
21
|
+
|
|
14
22
|
useFocusEffect(
|
|
15
23
|
useCallback(() => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
if (isListening.current) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
isListening.current = true;
|
|
28
|
+
|
|
29
|
+
if (Platform.OS === 'ios') {
|
|
30
|
+
const unsubscribe = navigation.addListener(
|
|
31
|
+
'beforeRemove',
|
|
32
|
+
blockBeforeRemove
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return () => {
|
|
36
|
+
unsubscribe();
|
|
37
|
+
isListening.current = false;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (Platform.OS === 'android') {
|
|
42
|
+
BackHandler.addEventListener('hardwareBackPress', blockBack);
|
|
43
|
+
|
|
44
|
+
return () => {
|
|
45
|
+
BackHandler.removeEventListener('hardwareBackPress', blockBack);
|
|
46
|
+
isListening.current = false;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}, [blockBack, blockBeforeRemove, navigation])
|
|
35
50
|
);
|
|
36
51
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useContext, useEffect, useRef } from 'react';
|
|
2
2
|
import { useIsFocused } from '@react-navigation/native';
|
|
3
3
|
import { SCContext, useSCContextSelector } from '../../context';
|
|
4
|
-
import {
|
|
4
|
+
import { axiosPost } from '../../utils/Apis/axios';
|
|
5
5
|
import { API } from '../../configs';
|
|
6
6
|
import { Action } from '../../context/actionType';
|
|
7
7
|
|
|
@@ -13,7 +13,7 @@ const useDevicesStatus = (unit, devices) => {
|
|
|
13
13
|
(state) => state.app.isNetworkConnected
|
|
14
14
|
);
|
|
15
15
|
const isFocused = useIsFocused();
|
|
16
|
-
const hasFetched = useRef(
|
|
16
|
+
const hasFetched = useRef([]); // Track if data has been fetched
|
|
17
17
|
|
|
18
18
|
const getDevicesStatus = useCallback(
|
|
19
19
|
async (_unit, _devices) => {
|
|
@@ -21,24 +21,29 @@ const useDevicesStatus = (unit, devices) => {
|
|
|
21
21
|
clearTimeout(timeoutId);
|
|
22
22
|
timeoutId = null;
|
|
23
23
|
}
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
const end_device_ids = _devices.map((item) => item.id);
|
|
25
|
+
|
|
26
|
+
if (hasFetched.current.toString() === end_device_ids.toString()) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const { success, data } = await axiosPost(
|
|
30
|
+
API.UNIT.END_DEVICES_STATUS(_unit.id),
|
|
30
31
|
{
|
|
31
|
-
|
|
32
|
+
end_device_ids,
|
|
32
33
|
}
|
|
33
34
|
);
|
|
34
|
-
|
|
35
|
+
|
|
36
|
+
if (success) {
|
|
37
|
+
hasFetched.current = end_device_ids;
|
|
38
|
+
setAction(Action.SET_DEVICES_STATUS, data);
|
|
39
|
+
}
|
|
35
40
|
timeoutId = setTimeout(() => getDevicesStatus(_unit, _devices), 10000);
|
|
36
41
|
},
|
|
37
42
|
[setAction]
|
|
38
43
|
);
|
|
39
44
|
|
|
40
45
|
useEffect(() => {
|
|
41
|
-
if (!isFocused || !isNetworkConnected
|
|
46
|
+
if (!isFocused || !isNetworkConnected) {
|
|
42
47
|
return;
|
|
43
48
|
}
|
|
44
49
|
if (!devices?.length) {
|
|
@@ -53,7 +58,6 @@ const useDevicesStatus = (unit, devices) => {
|
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
getDevicesStatus(unit, devices);
|
|
56
|
-
hasFetched.current = true; // Mark as fetched
|
|
57
61
|
return () => {
|
|
58
62
|
if (timeoutId) {
|
|
59
63
|
clearTimeout(timeoutId);
|
|
@@ -65,7 +69,6 @@ const useDevicesStatus = (unit, devices) => {
|
|
|
65
69
|
useEffect(() => {
|
|
66
70
|
// Reset the hasFetched flag when the component is no longer focused
|
|
67
71
|
if (!isFocused) {
|
|
68
|
-
hasFetched.current = false;
|
|
69
72
|
if (timeoutId) {
|
|
70
73
|
clearTimeout(timeoutId); // Clear timeout when losing focus
|
|
71
74
|
timeoutId = null;
|
|
@@ -72,6 +72,9 @@ import { HanetCameraStack } from './HanetCameraStack';
|
|
|
72
72
|
import { styles } from './UnitStackStyles';
|
|
73
73
|
import { bleManager } from '../utils/bluetooth';
|
|
74
74
|
import UpdateReceiverEmailScript from '../screens/Automate/EditActionsList/UpdateReceiverEmailScript';
|
|
75
|
+
import SetupScriptSms from '../screens/Automate/AddNewAction/SetupScriptSms';
|
|
76
|
+
import SetupScriptReceiverSms from '../screens/Automate/AddNewAction/SetupScriptReceiverSms';
|
|
77
|
+
import UpdateReceiverSmsScript from '../screens/Automate/EditActionsList/UpdateReceiverSmsScript';
|
|
75
78
|
|
|
76
79
|
const Stack = createNativeStackNavigator();
|
|
77
80
|
|
|
@@ -446,6 +449,27 @@ export const UnitStack = memo((props) => {
|
|
|
446
449
|
headerShown: false,
|
|
447
450
|
}}
|
|
448
451
|
/>
|
|
452
|
+
<Stack.Screen
|
|
453
|
+
name={Route.SetupScriptSms}
|
|
454
|
+
component={SetupScriptSms}
|
|
455
|
+
options={{
|
|
456
|
+
headerShown: false,
|
|
457
|
+
}}
|
|
458
|
+
/>
|
|
459
|
+
<Stack.Screen
|
|
460
|
+
name={Route.SetupScriptReceiverSms}
|
|
461
|
+
component={SetupScriptReceiverSms}
|
|
462
|
+
options={{
|
|
463
|
+
headerShown: false,
|
|
464
|
+
}}
|
|
465
|
+
/>
|
|
466
|
+
<Stack.Screen
|
|
467
|
+
name={Route.UpdateReceiverSmsScript}
|
|
468
|
+
component={UpdateReceiverSmsScript}
|
|
469
|
+
options={{
|
|
470
|
+
headerShown: false,
|
|
471
|
+
}}
|
|
472
|
+
/>
|
|
449
473
|
<Stack.Screen
|
|
450
474
|
name={Route.AddAutomationTypeSmart}
|
|
451
475
|
component={AddAutomationTypeSmart}
|
|
@@ -221,23 +221,21 @@ const ScanDeviceLocal = ({ route }) => {
|
|
|
221
221
|
title={t('device_scaned')}
|
|
222
222
|
isShowSeparator
|
|
223
223
|
/>
|
|
224
|
-
|
|
224
|
+
|
|
225
|
+
<View style={styles.rowContainer}>
|
|
226
|
+
<Text style={styles.subTitle} type="Body">
|
|
227
|
+
{t('select_device_and_connect')}
|
|
228
|
+
</Text>
|
|
225
229
|
<ActivityIndicator style={styles.containerLoading} />
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
renderItem={renderItem}
|
|
236
|
-
extraData={deviceList}
|
|
237
|
-
numColumns={1}
|
|
238
|
-
/>
|
|
239
|
-
</>
|
|
240
|
-
)}
|
|
230
|
+
</View>
|
|
231
|
+
<FlatList
|
|
232
|
+
style={styles.listContainer}
|
|
233
|
+
keyExtractor={(item) => item?.host}
|
|
234
|
+
data={deviceList}
|
|
235
|
+
renderItem={renderItem}
|
|
236
|
+
extraData={deviceList}
|
|
237
|
+
numColumns={1}
|
|
238
|
+
/>
|
|
241
239
|
|
|
242
240
|
<ViewButtonBottom
|
|
243
241
|
leftTitle={t('cancel')}
|
|
@@ -6,6 +6,10 @@ export default StyleSheet.create({
|
|
|
6
6
|
flex: 1,
|
|
7
7
|
backgroundColor: Colors.Gray2,
|
|
8
8
|
},
|
|
9
|
+
rowContainer: {
|
|
10
|
+
flexDirection: 'row',
|
|
11
|
+
alignItems: 'center',
|
|
12
|
+
},
|
|
9
13
|
title: {
|
|
10
14
|
marginVertical: 16,
|
|
11
15
|
marginLeft: 16,
|
|
@@ -53,6 +57,7 @@ export default StyleSheet.create({
|
|
|
53
57
|
containerLoading: {
|
|
54
58
|
flex: 1,
|
|
55
59
|
justifyContent: 'center',
|
|
56
|
-
alignItems: '
|
|
60
|
+
alignItems: 'flex-start',
|
|
61
|
+
marginLeft: 16,
|
|
57
62
|
},
|
|
58
63
|
});
|
|
@@ -105,6 +105,9 @@ describe('test ScanDeviceLocal', () => {
|
|
|
105
105
|
);
|
|
106
106
|
expect(zeroconfMock.on).toHaveBeenCalledWith('error', expect.any(Function));
|
|
107
107
|
expect(zeroconfMock.scan).toHaveBeenCalledWith('plugandplay', 'tcp');
|
|
108
|
+
|
|
109
|
+
const activityIndicator = instance.findByType(ActivityIndicator);
|
|
110
|
+
expect(activityIndicator).toBeTruthy();
|
|
108
111
|
};
|
|
109
112
|
|
|
110
113
|
it('test connect gateway and navigate', async () => {
|
|
@@ -382,17 +385,6 @@ describe('test ScanDeviceLocal', () => {
|
|
|
382
385
|
expect(mockedGoBack).toHaveBeenCalled();
|
|
383
386
|
});
|
|
384
387
|
|
|
385
|
-
it('test renders loading indicator when device list is empty', async () => {
|
|
386
|
-
const route = { params: { unit: 1 } };
|
|
387
|
-
await act(async () => {
|
|
388
|
-
tree = await renderer.create(wrapComponent(route));
|
|
389
|
-
});
|
|
390
|
-
const instance = tree.root;
|
|
391
|
-
|
|
392
|
-
const activityIndicator = instance.findByType(ActivityIndicator);
|
|
393
|
-
expect(activityIndicator).toBeTruthy();
|
|
394
|
-
});
|
|
395
|
-
|
|
396
388
|
it('test renders device list when devices are available', async () => {
|
|
397
389
|
const route = { params: { unit: 1 } };
|
|
398
390
|
await act(async () => {
|
|
@@ -451,8 +443,7 @@ describe('test ScanDeviceLocal', () => {
|
|
|
451
443
|
await removeCallback('Device 1');
|
|
452
444
|
});
|
|
453
445
|
|
|
454
|
-
|
|
455
|
-
expect(flatListAfter.length).toBe(0);
|
|
446
|
+
expect(flatList.props.data).toEqual([]);
|
|
456
447
|
|
|
457
448
|
const activityIndicator = instance.findByType(ActivityIndicator);
|
|
458
449
|
expect(activityIndicator).toBeTruthy();
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState, useEffect, memo } from 'react';
|
|
2
|
+
import { FlatList, View } from 'react-native';
|
|
3
|
+
import { useNavigation } from '@react-navigation/native';
|
|
4
|
+
import styles from './Styles/SetupScriptReceiverEmailStyles';
|
|
5
|
+
import { CircleView, HeaderCustom, Text } from '../../../commons';
|
|
6
|
+
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
7
|
+
|
|
8
|
+
import BottomButtonView from '../../../commons/BottomButtonView';
|
|
9
|
+
import { axiosPost, axiosGet } from '../../../utils/Apis/axios';
|
|
10
|
+
import { API, Colors } from '../../../configs';
|
|
11
|
+
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
12
|
+
import Routes from '../../../utils/Route';
|
|
13
|
+
import moment from 'moment';
|
|
14
|
+
import CheckBox from '@react-native-community/checkbox';
|
|
15
|
+
import { useSCContextSelector } from '../../../context';
|
|
16
|
+
import { Image } from 'react-native';
|
|
17
|
+
|
|
18
|
+
const SetupScriptReceiverSms = ({ route }) => {
|
|
19
|
+
const t = useTranslations();
|
|
20
|
+
const { goBack, navigate } = useNavigation();
|
|
21
|
+
const { automate = {}, unitId, formData } = route.params || {};
|
|
22
|
+
const { id: automateId } = automate;
|
|
23
|
+
const [members, setMembers] = useState([]);
|
|
24
|
+
const [listUser, setListUser] = useState([]);
|
|
25
|
+
const currentUserId = useSCContextSelector(
|
|
26
|
+
(state) => state.auth.account.user.id
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const loadMembers = useCallback(async () => {
|
|
30
|
+
const { success, data } = await axiosGet(API.SHARE.UNITS_MEMBERS(unitId));
|
|
31
|
+
if (success) {
|
|
32
|
+
setMembers(data);
|
|
33
|
+
}
|
|
34
|
+
}, [unitId]);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
loadMembers();
|
|
38
|
+
}, [loadMembers]);
|
|
39
|
+
|
|
40
|
+
const onNext = useCallback(async () => {
|
|
41
|
+
formData.receiver = listUser;
|
|
42
|
+
const { success } = await axiosPost(
|
|
43
|
+
API.AUTOMATE.ADD_SCRIPT_SMS(automateId),
|
|
44
|
+
formData
|
|
45
|
+
);
|
|
46
|
+
if (success) {
|
|
47
|
+
ToastBottomHelper.success(t('text_done'));
|
|
48
|
+
navigate({
|
|
49
|
+
name: Routes.ScriptDetail,
|
|
50
|
+
merge: true,
|
|
51
|
+
params: { saveAt: moment().valueOf() },
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
ToastBottomHelper.error(t('error_please_try_later'));
|
|
55
|
+
}
|
|
56
|
+
}, [automateId, formData, listUser, navigate, t]);
|
|
57
|
+
|
|
58
|
+
const canSave = useMemo(() => {
|
|
59
|
+
const { message } = formData;
|
|
60
|
+
return !!message && !!listUser.length;
|
|
61
|
+
}, [formData, listUser.length]);
|
|
62
|
+
|
|
63
|
+
const arrColor = useMemo(
|
|
64
|
+
() => [
|
|
65
|
+
Colors.GeekBlue3,
|
|
66
|
+
Colors.Purple3,
|
|
67
|
+
Colors.Orange3,
|
|
68
|
+
Colors.Volcano3,
|
|
69
|
+
Colors.Blue9,
|
|
70
|
+
Colors.Green3,
|
|
71
|
+
Colors.Cyan2,
|
|
72
|
+
],
|
|
73
|
+
[]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const onChecked = useCallback(
|
|
77
|
+
(id) => (checked) => {
|
|
78
|
+
setListUser((prevListUser) =>
|
|
79
|
+
checked
|
|
80
|
+
? [...prevListUser, id]
|
|
81
|
+
: prevListUser.filter((userId) => userId !== id)
|
|
82
|
+
);
|
|
83
|
+
},
|
|
84
|
+
[]
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const RowMember = memo(({ member, index, onValueChange }) => {
|
|
88
|
+
const { id, name, avatar, share_id, phone_number } = member;
|
|
89
|
+
const [role, roleColor] = useMemo(() => {
|
|
90
|
+
if (!share_id) {
|
|
91
|
+
return [t('owner'), Colors.Primary];
|
|
92
|
+
}
|
|
93
|
+
if (id === currentUserId) {
|
|
94
|
+
return [t('me'), Colors.Primary];
|
|
95
|
+
}
|
|
96
|
+
return [t('member'), Colors.Gray6];
|
|
97
|
+
}, [share_id, id]);
|
|
98
|
+
|
|
99
|
+
const firstWordsInName = useMemo(() => {
|
|
100
|
+
return name.charAt();
|
|
101
|
+
}, [name]);
|
|
102
|
+
|
|
103
|
+
const circleColor = arrColor[index % arrColor.length];
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<View style={styles.rowContainer}>
|
|
107
|
+
<View style={styles.border}>
|
|
108
|
+
<CheckBox
|
|
109
|
+
disabled={!phone_number}
|
|
110
|
+
lineWidth={4}
|
|
111
|
+
value={listUser.includes(id)}
|
|
112
|
+
onValueChange={onValueChange(id)}
|
|
113
|
+
style={styles.checkbox}
|
|
114
|
+
/>
|
|
115
|
+
<View style={styles.paddingLeft16}>
|
|
116
|
+
{avatar ? (
|
|
117
|
+
<Image source={{ uri: avatar }} style={styles.avatar} />
|
|
118
|
+
) : (
|
|
119
|
+
<CircleView size={40} backgroundColor={circleColor} center>
|
|
120
|
+
<Text color={Colors.White}>{firstWordsInName}</Text>
|
|
121
|
+
</CircleView>
|
|
122
|
+
)}
|
|
123
|
+
</View>
|
|
124
|
+
<View style={styles.paddingLeft16}>
|
|
125
|
+
<Text style={styles.titleName}>{name}</Text>
|
|
126
|
+
{phone_number ? (
|
|
127
|
+
<Text style={styles.status}>{phone_number}</Text>
|
|
128
|
+
) : (
|
|
129
|
+
<Text style={styles.invalid}>{t('no_phone_number')}</Text>
|
|
130
|
+
)}
|
|
131
|
+
</View>
|
|
132
|
+
<View style={styles.endFlex}>
|
|
133
|
+
<Text style={[styles.textRole, { color: roleColor }]}>{role}</Text>
|
|
134
|
+
</View>
|
|
135
|
+
</View>
|
|
136
|
+
</View>
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<View style={styles.wrap}>
|
|
142
|
+
<HeaderCustom isShowClose onClose={goBack} title={t('sms_to')} />
|
|
143
|
+
<FlatList
|
|
144
|
+
data={members}
|
|
145
|
+
renderItem={({ item, index }) => (
|
|
146
|
+
<RowMember member={item} index={index} onValueChange={onChecked} />
|
|
147
|
+
)}
|
|
148
|
+
keyExtractor={(item) => item.id.toString()}
|
|
149
|
+
ListEmptyComponent={
|
|
150
|
+
<View style={styles.viewEmpty}>
|
|
151
|
+
<Text style={styles.textCenter}>{t('no_member')}</Text>
|
|
152
|
+
</View>
|
|
153
|
+
}
|
|
154
|
+
/>
|
|
155
|
+
<View style={styles.container}>
|
|
156
|
+
<BottomButtonView
|
|
157
|
+
style={styles.bottomButtonView}
|
|
158
|
+
mainTitle={t('done')}
|
|
159
|
+
onPressMain={onNext}
|
|
160
|
+
typeMain={canSave ? 'primary' : 'disabled'}
|
|
161
|
+
/>
|
|
162
|
+
</View>
|
|
163
|
+
</View>
|
|
164
|
+
);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export default SetupScriptReceiverSms;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import { Keyboard, TouchableWithoutFeedback, View } from 'react-native';
|
|
3
|
+
import { useNavigation } from '@react-navigation/native';
|
|
4
|
+
import styles from './Styles/SetupScriptEmailStyles';
|
|
5
|
+
import { HeaderCustom, Text } 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 Routes from '../../../utils/Route';
|
|
12
|
+
import { Colors } from '../../../configs';
|
|
13
|
+
|
|
14
|
+
const SetupScriptSms = ({ route }) => {
|
|
15
|
+
const t = useTranslations();
|
|
16
|
+
const { goBack, navigate } = useNavigation();
|
|
17
|
+
const { automate, unitId, multiUnit } = route.params || {};
|
|
18
|
+
const initialUnitId = useMemo(
|
|
19
|
+
() => unitId || multiUnit.id,
|
|
20
|
+
[unitId, multiUnit?.id]
|
|
21
|
+
);
|
|
22
|
+
const [formData, setFormData] = useState({ unit: initialUnitId });
|
|
23
|
+
|
|
24
|
+
const onChangeMessage = (value) => {
|
|
25
|
+
setFormData((state) => ({
|
|
26
|
+
...state,
|
|
27
|
+
message: value,
|
|
28
|
+
}));
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const onNext = useCallback(async () => {
|
|
32
|
+
navigate(Routes.SetupScriptReceiverSms, {
|
|
33
|
+
automate,
|
|
34
|
+
unitId: initialUnitId,
|
|
35
|
+
formData,
|
|
36
|
+
});
|
|
37
|
+
}, [navigate, automate, initialUnitId, formData]);
|
|
38
|
+
|
|
39
|
+
const canSave = useMemo(() => {
|
|
40
|
+
const { message } = formData || {};
|
|
41
|
+
return !!message;
|
|
42
|
+
}, [formData]);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<View style={styles.wrap}>
|
|
46
|
+
<HeaderCustom isShowClose onClose={goBack} title={t('sms_content')} />
|
|
47
|
+
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
|
|
48
|
+
<View style={styles.container}>
|
|
49
|
+
<_TextInput
|
|
50
|
+
placeholder={t('message_sms')}
|
|
51
|
+
onChange={onChangeMessage}
|
|
52
|
+
textInputStyle={styles.textMessage}
|
|
53
|
+
value={formData?.message}
|
|
54
|
+
accessibilityLabel={AccessibilityLabel.AUTOMATE_MESSAGE_NOTIFY}
|
|
55
|
+
multiline={true}
|
|
56
|
+
maxLength={255}
|
|
57
|
+
/>
|
|
58
|
+
<View style={styles.textWarning}>
|
|
59
|
+
<Text color={Colors.Gray}>{t('only_in_local_control')}</Text>
|
|
60
|
+
</View>
|
|
61
|
+
<BottomButtonView
|
|
62
|
+
style={styles.bottomButtonView}
|
|
63
|
+
mainTitle={t('next')}
|
|
64
|
+
onPressMain={onNext}
|
|
65
|
+
typeMain={canSave ? 'primary' : 'disabled'}
|
|
66
|
+
/>
|
|
67
|
+
</View>
|
|
68
|
+
</TouchableWithoutFeedback>
|
|
69
|
+
</View>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default SetupScriptSms;
|