@eohjsc/react-native-smart-city 0.3.19 → 0.3.22
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 +1 -1
- package/src/commons/ActionGroup/OnOffTemplate/index.js +9 -4
- package/src/commons/SubUnit/Favorites/index.js +2 -1
- package/src/configs/API.js +6 -1
- package/src/context/actionType.ts +2 -2
- package/src/context/reducer.ts +3 -3
- package/src/hooks/IoT/useValueEvaluation.js +17 -4
- package/src/navigations/UnitStack.js +3 -3
- package/src/screens/Device/detail.js +14 -11
- package/src/screens/EmergencyContacts/EmergencyContactsAddNew.js +35 -22
- package/src/screens/EmergencyContacts/EmergencyContactsSelectContacts.js +2 -1
- package/src/screens/EmergencyContacts/__test__/EmergencyContactAddNew.test.js +36 -2
- package/src/screens/ScriptDetail/hooks/useStarredScript.js +2 -2
- package/src/screens/Unit/{SelectFavoritesDevices.js → SelectAddToFavorites.js} +81 -26
- package/src/screens/Unit/{SelectFavoritesDevicesStyles.js → SelectAddToFavoritesStyles.js} +0 -0
- package/src/screens/Unit/__test__/SelectAddToFavorites.test.js +267 -0
- package/src/screens/Unit/components/AutomateScript/index.js +65 -0
- package/src/screens/Unit/components/AutomateScript/styles.js +48 -0
- package/src/utils/I18n/translations/en.json +2 -1
- package/src/utils/I18n/translations/vi.json +2 -1
- package/src/utils/Route/index.js +1 -1
- package/src/screens/Unit/__test__/SelectFavoritesDevices.test.js +0 -110
package/package.json
CHANGED
|
@@ -55,7 +55,6 @@ const OnOffTemplate = memo(({ actionGroup, doAction, sensor }) => {
|
|
|
55
55
|
);
|
|
56
56
|
|
|
57
57
|
const triggerAction = useCallback(async () => {
|
|
58
|
-
setTempIsOn((prev) => !prev);
|
|
59
58
|
switch (sensor?.device_type) {
|
|
60
59
|
case DEVICE_TYPE.ZIGBEE:
|
|
61
60
|
if (action_on_data && action_off_data) {
|
|
@@ -75,11 +74,10 @@ const OnOffTemplate = memo(({ actionGroup, doAction, sensor }) => {
|
|
|
75
74
|
}
|
|
76
75
|
break;
|
|
77
76
|
case DEVICE_TYPE.LG_THINQ:
|
|
77
|
+
setTempIsOn((prev) => !prev);
|
|
78
78
|
if (action_data) {
|
|
79
79
|
await doAction(action_data, JSON.stringify({ value: !isOn }));
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
updateStatusFromPusher();
|
|
83
81
|
break;
|
|
84
82
|
default:
|
|
85
83
|
if (action_data) {
|
|
@@ -98,6 +96,7 @@ const OnOffTemplate = memo(({ actionGroup, doAction, sensor }) => {
|
|
|
98
96
|
}
|
|
99
97
|
break;
|
|
100
98
|
}
|
|
99
|
+
updateStatusFromPusher();
|
|
101
100
|
if (sensor?.is_managed_by_backend) {
|
|
102
101
|
configuration?.config &&
|
|
103
102
|
sensor?.device_type !== 'GOOGLE_HOME' &&
|
|
@@ -130,7 +129,13 @@ const OnOffTemplate = memo(({ actionGroup, doAction, sensor }) => {
|
|
|
130
129
|
if (sensor?.is_managed_by_backend && sensor.device_type !== 'GOOGLE_HOME') {
|
|
131
130
|
watchMultiConfigs([configuration.config]);
|
|
132
131
|
}
|
|
133
|
-
}, [sensor, configuration.config]);
|
|
132
|
+
}, [sensor, configuration.config, getIsOnValue]);
|
|
133
|
+
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
if (sensor?.device_type !== DEVICE_TYPE.LG_THINQ) {
|
|
136
|
+
setTempIsOn(getIsOnValue());
|
|
137
|
+
}
|
|
138
|
+
}, [getIsOnValue, sensor?.device_type]);
|
|
134
139
|
|
|
135
140
|
const Component = useMemo(() => {
|
|
136
141
|
return getComponent(actionGroup.template);
|
|
@@ -24,7 +24,7 @@ const SubUnitFavorites = ({
|
|
|
24
24
|
const { getStatus, serverDown } = useSensorsStatus(unit, favoriteDevices);
|
|
25
25
|
|
|
26
26
|
const handleOnAddNew = () => {
|
|
27
|
-
navigate(Routes.
|
|
27
|
+
navigate(Routes.SelectAddToFavorites, {
|
|
28
28
|
unitId: unit.id,
|
|
29
29
|
});
|
|
30
30
|
};
|
|
@@ -58,6 +58,7 @@ const SubUnitFavorites = ({
|
|
|
58
58
|
isOwner={isOwner}
|
|
59
59
|
automate={automate}
|
|
60
60
|
unit={unit}
|
|
61
|
+
wrapStyle={wrapItemStyle}
|
|
61
62
|
/>
|
|
62
63
|
))}
|
|
63
64
|
<ItemAddNew
|
package/src/configs/API.js
CHANGED
|
@@ -29,9 +29,14 @@ const API = {
|
|
|
29
29
|
CHANGE_OWNER: (id) => `/property_manager/units/${id}/change_owner/`,
|
|
30
30
|
FAVOURITE_DEVICES: (id) =>
|
|
31
31
|
`/property_manager/units/${id}/favourite_devices/`,
|
|
32
|
-
|
|
32
|
+
DEVICES_NOT_FAVORITES: (id) =>
|
|
33
|
+
`/property_manager/units/${id}/devices_not_favourites/`,
|
|
33
34
|
ADD_DEVICES_TO_FAVORITES: (id) =>
|
|
34
35
|
`/property_manager/units/${id}/add_devices_to_favourites/`,
|
|
36
|
+
AUTOMATE_SCRIPTS_NOT_STARRED: (id) =>
|
|
37
|
+
`/property_manager/units/${id}/automate_scripts_not_starred/`,
|
|
38
|
+
STAR_AUTOMATE_SCRIPTS: (id) =>
|
|
39
|
+
`/property_manager/units/${id}/star_automate_scripts/`,
|
|
35
40
|
},
|
|
36
41
|
SUB_UNIT: {
|
|
37
42
|
REMOVE_SUB_UNIT: (unitId, id) =>
|
|
@@ -15,8 +15,8 @@ export const Action = {
|
|
|
15
15
|
ADD_DEVICES_TO_FAVORITES: 'ADD_DEVICE_TO_FAVORITES',
|
|
16
16
|
REMOVE_DEVICES_FROM_FAVORITES: 'REMOVE_DEVICE_FROM_FAVORITES',
|
|
17
17
|
SET_STARRED_SCRIPTS: 'SET_STARRED_SCRIPTS',
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
STAR_SCRIPTS: 'STAR_SCRIPTS',
|
|
19
|
+
UNSTAR_SCRIPTS: 'UNSTAR_SCRIPTS',
|
|
20
20
|
CONNECTING_GOOGLE_HOME: 'CONNECTING_GOOGLE_HOME',
|
|
21
21
|
SET_GOOGLE_HOME_CONNECTIONS: 'SET_GOOGLE_HOME_CONNECTIONS',
|
|
22
22
|
CHANGE_GOOGLE_HOME_CONN_STATE: 'CHANGE_GOOGLE_HOME_CONN_STATE',
|
package/src/context/reducer.ts
CHANGED
|
@@ -235,7 +235,7 @@ export const reducer = (currentState: ContextData, action: Action) => {
|
|
|
235
235
|
starredScriptIds: payload,
|
|
236
236
|
},
|
|
237
237
|
};
|
|
238
|
-
case Action.
|
|
238
|
+
case Action.STAR_SCRIPTS:
|
|
239
239
|
return {
|
|
240
240
|
...currentState,
|
|
241
241
|
automate: {
|
|
@@ -245,13 +245,13 @@ export const reducer = (currentState: ContextData, action: Action) => {
|
|
|
245
245
|
),
|
|
246
246
|
},
|
|
247
247
|
};
|
|
248
|
-
case Action.
|
|
248
|
+
case Action.UNSTAR_SCRIPTS:
|
|
249
249
|
return {
|
|
250
250
|
...currentState,
|
|
251
251
|
automate: {
|
|
252
252
|
...currentState.automate,
|
|
253
253
|
starredScriptIds: currentState.automate.starredScriptIds.filter(
|
|
254
|
-
(scriptId) => scriptId
|
|
254
|
+
(scriptId) => !payload.includes(scriptId)
|
|
255
255
|
),
|
|
256
256
|
},
|
|
257
257
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useContext, useEffect } from 'react';
|
|
1
|
+
import { useCallback, useContext, useEffect, useState } from 'react';
|
|
2
2
|
import { API } from '../../configs';
|
|
3
3
|
import { SCContext, useSCContextSelector } from '../../context';
|
|
4
4
|
import { Action } from '../../context/actionType';
|
|
@@ -6,12 +6,14 @@ import { axiosGet } from '../../utils/Apis/axios';
|
|
|
6
6
|
|
|
7
7
|
const useValueEvaluations = (unitId) => {
|
|
8
8
|
const { setAction } = useContext(SCContext);
|
|
9
|
+
const [fetching, setFetching] = useState(false);
|
|
9
10
|
|
|
10
11
|
const fetchConfigValueEvaluations = useCallback(
|
|
11
12
|
async (page = 1) => {
|
|
12
13
|
if (!unitId) {
|
|
13
14
|
return;
|
|
14
15
|
}
|
|
16
|
+
setFetching(true);
|
|
15
17
|
const params = new URLSearchParams();
|
|
16
18
|
params.append('config__end_device__station__unit', unitId);
|
|
17
19
|
params.append('page', page);
|
|
@@ -24,9 +26,15 @@ const useValueEvaluations = (unitId) => {
|
|
|
24
26
|
data: data.results,
|
|
25
27
|
});
|
|
26
28
|
if (data.next) {
|
|
27
|
-
await fetchConfigValueEvaluations(page + 1);
|
|
29
|
+
return await fetchConfigValueEvaluations(page + 1);
|
|
28
30
|
}
|
|
31
|
+
} else {
|
|
32
|
+
setAction(Action.UPDATE_VALUE_EVALUATIONS, {
|
|
33
|
+
unitId,
|
|
34
|
+
data: [],
|
|
35
|
+
});
|
|
29
36
|
}
|
|
37
|
+
setFetching(false);
|
|
30
38
|
},
|
|
31
39
|
[unitId, setAction]
|
|
32
40
|
);
|
|
@@ -36,10 +44,15 @@ const useValueEvaluations = (unitId) => {
|
|
|
36
44
|
});
|
|
37
45
|
|
|
38
46
|
useEffect(() => {
|
|
39
|
-
if (!(fetchedValueEvaluationUnits.indexOf(unitId) !== -1)) {
|
|
47
|
+
if (!fetching && !(fetchedValueEvaluationUnits.indexOf(unitId) !== -1)) {
|
|
40
48
|
fetchConfigValueEvaluations();
|
|
41
49
|
}
|
|
42
|
-
}, [
|
|
50
|
+
}, [
|
|
51
|
+
unitId,
|
|
52
|
+
fetching,
|
|
53
|
+
fetchConfigValueEvaluations,
|
|
54
|
+
fetchedValueEvaluationUnits,
|
|
55
|
+
]);
|
|
43
56
|
};
|
|
44
57
|
|
|
45
58
|
export default useValueEvaluations;
|
|
@@ -51,7 +51,7 @@ import EmergencySetting from '../screens/EmergencySetting';
|
|
|
51
51
|
import ConfirmUnitDeletion from '../screens/ConfirmUnitDeletion';
|
|
52
52
|
import InfoMemberUnit from '../screens/Sharing/InfoMemberUnit';
|
|
53
53
|
import EnterPassword from '../screens/EnterPassword';
|
|
54
|
-
import
|
|
54
|
+
import SelectAddToFavorites from '../screens/Unit/SelectAddToFavorites';
|
|
55
55
|
import { HanetCameraStack } from './HanetCameraStack';
|
|
56
56
|
import { axiosGet } from '../utils/Apis/axios';
|
|
57
57
|
import { API } from '../configs';
|
|
@@ -402,8 +402,8 @@ export const UnitStack = memo((props) => {
|
|
|
402
402
|
}}
|
|
403
403
|
/>
|
|
404
404
|
<Stack.Screen
|
|
405
|
-
name={Route.
|
|
406
|
-
component={
|
|
405
|
+
name={Route.SelectAddToFavorites}
|
|
406
|
+
component={SelectAddToFavorites}
|
|
407
407
|
options={{
|
|
408
408
|
headerShown: false,
|
|
409
409
|
}}
|
|
@@ -148,7 +148,7 @@ const DeviceDetail = ({ route }) => {
|
|
|
148
148
|
);
|
|
149
149
|
|
|
150
150
|
const canManageSubUnit = useMemo(() => {
|
|
151
|
-
return currentUserId === unit?.user_id;
|
|
151
|
+
return Number(currentUserId) === Number(unit?.user_id);
|
|
152
152
|
}, [currentUserId, unit]);
|
|
153
153
|
|
|
154
154
|
const fetchUnitDetail = useCallback(async () => {
|
|
@@ -721,16 +721,19 @@ const DeviceDetail = ({ route }) => {
|
|
|
721
721
|
isNetworkConnected !== null &&
|
|
722
722
|
renderSensorConnected()}
|
|
723
723
|
</View>
|
|
724
|
-
{
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
724
|
+
{isNetworkConnected &&
|
|
725
|
+
isConnected &&
|
|
726
|
+
isShowSetupEmergencyContact &&
|
|
727
|
+
canManageSubUnit && (
|
|
728
|
+
<BottomButtonView
|
|
729
|
+
style={styles.bottomButtonEmergencyContact}
|
|
730
|
+
mainIcon={<Icon name="plus" size={16} color={Colors.Primary} />}
|
|
731
|
+
mainTitle={t('setup_my_emergency_contact')}
|
|
732
|
+
onPressMain={onSetupContacts}
|
|
733
|
+
typeMain="primaryBorder"
|
|
734
|
+
semiboldMain={false}
|
|
735
|
+
/>
|
|
736
|
+
)}
|
|
734
737
|
<AlertSendConfirm
|
|
735
738
|
showAlertConfirm={showAlertConfirm && !lockShowing}
|
|
736
739
|
countDown={countDown}
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import React, { useCallback, useState } from 'react';
|
|
2
|
-
import { SafeAreaView, StyleSheet, TextInput } from 'react-native';
|
|
2
|
+
import { SafeAreaView, StyleSheet, TextInput, View } from 'react-native';
|
|
3
3
|
import { useNavigation } from '@react-navigation/native';
|
|
4
4
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
5
5
|
import { Section, ViewButtonBottom } from '../../commons';
|
|
6
6
|
import WrapHeaderScrollable from '../../commons/Sharing/WrapHeaderScrollable';
|
|
7
7
|
import { API, Colors } from '../../configs';
|
|
8
|
-
import { useKeyboardShow } from '../../hooks/Common';
|
|
9
8
|
import { TESTID } from '../../configs/Constants';
|
|
10
9
|
import { axiosPost } from '../../utils/Apis/axios';
|
|
11
10
|
import { ToastBottomHelper } from '../../utils/Utils';
|
|
11
|
+
import { isValidPhoneNumber } from '../../utils/Validation';
|
|
12
12
|
|
|
13
13
|
export const EmergencyContactsAddNew = ({ route }) => {
|
|
14
14
|
const t = useTranslations();
|
|
15
15
|
const { group } = route.params;
|
|
16
16
|
const { goBack } = useNavigation();
|
|
17
|
-
const { keyboardBottomPadding } = useKeyboardShow();
|
|
18
17
|
const [textName, setTextName] = useState('');
|
|
19
18
|
const [textPhone, setTextPhone] = useState('');
|
|
20
19
|
const onTextNameChange = useCallback(
|
|
@@ -33,22 +32,28 @@ export const EmergencyContactsAddNew = ({ route }) => {
|
|
|
33
32
|
goBack();
|
|
34
33
|
}, [goBack]);
|
|
35
34
|
const onSave = useCallback(async () => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
if (isValidPhoneNumber(textPhone)) {
|
|
36
|
+
const { success } = await axiosPost(
|
|
37
|
+
API.EMERGENCY_BUTTON.CREATE_CONTACT(),
|
|
38
|
+
{
|
|
39
|
+
group: group.id,
|
|
40
|
+
phone_number: textPhone,
|
|
41
|
+
name: textName,
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
if (success) {
|
|
45
|
+
goBack();
|
|
46
|
+
ToastBottomHelper.success(t('create_contact_success'));
|
|
47
|
+
} else {
|
|
48
|
+
ToastBottomHelper.error(t('create_contact_failed'));
|
|
49
|
+
}
|
|
43
50
|
} else {
|
|
44
|
-
ToastBottomHelper.error(t('
|
|
51
|
+
ToastBottomHelper.error(t('invalid_phone_number'));
|
|
45
52
|
}
|
|
46
53
|
}, [goBack, group.id, t, textName, textPhone]);
|
|
47
54
|
|
|
48
55
|
return (
|
|
49
|
-
<SafeAreaView
|
|
50
|
-
style={[styles.wrap, { marginBottom: keyboardBottomPadding }]}
|
|
51
|
-
>
|
|
56
|
+
<SafeAreaView style={styles.wrap}>
|
|
52
57
|
<WrapHeaderScrollable title={t('create_contact')}>
|
|
53
58
|
<Section type={'border'}>
|
|
54
59
|
<TextInput
|
|
@@ -58,6 +63,7 @@ export const EmergencyContactsAddNew = ({ route }) => {
|
|
|
58
63
|
placeholder={t('text_name')}
|
|
59
64
|
underlineColorAndroid={null}
|
|
60
65
|
onChangeText={onTextNameChange}
|
|
66
|
+
maxLength={64}
|
|
61
67
|
/>
|
|
62
68
|
<TextInput
|
|
63
69
|
testID={TESTID.ON_CHANGE_PHONE_EMERGENCY_CONTACT}
|
|
@@ -67,17 +73,20 @@ export const EmergencyContactsAddNew = ({ route }) => {
|
|
|
67
73
|
underlineColorAndroid={null}
|
|
68
74
|
keyboardType={'phone-pad'}
|
|
69
75
|
onChangeText={onTextPhoneChange}
|
|
76
|
+
maxLength={64}
|
|
70
77
|
/>
|
|
71
78
|
</Section>
|
|
72
79
|
</WrapHeaderScrollable>
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
<View style={styles.viewButtonBottom}>
|
|
81
|
+
<ViewButtonBottom
|
|
82
|
+
leftTitle={t('cancel')}
|
|
83
|
+
leftDisabled={false}
|
|
84
|
+
onLeftClick={onCancel}
|
|
85
|
+
rightTitle={t('save')}
|
|
86
|
+
rightDisabled={!textPhone || !textName}
|
|
87
|
+
onRightClick={onSave}
|
|
88
|
+
/>
|
|
89
|
+
</View>
|
|
81
90
|
</SafeAreaView>
|
|
82
91
|
);
|
|
83
92
|
};
|
|
@@ -95,4 +104,8 @@ const styles = StyleSheet.create({
|
|
|
95
104
|
fontFamily: 'SFProDisplay-Regular',
|
|
96
105
|
lineHeight: 24,
|
|
97
106
|
},
|
|
107
|
+
viewButtonBottom: {
|
|
108
|
+
borderTopWidth: 1,
|
|
109
|
+
borderTopColor: Colors.Gray4,
|
|
110
|
+
},
|
|
98
111
|
});
|
|
@@ -27,8 +27,9 @@ export const EmergencyContactsSelectContacts = ({ route }) => {
|
|
|
27
27
|
setLoading(true);
|
|
28
28
|
const { success, data } = await axiosGet(API.SHARE.UNITS_MEMBERS(id));
|
|
29
29
|
if (success) {
|
|
30
|
+
const result = data.filter((item) => item?.name && item?.phone_number);
|
|
30
31
|
setLoading(false);
|
|
31
|
-
setDataContact(
|
|
32
|
+
setDataContact(result);
|
|
32
33
|
}
|
|
33
34
|
}, []);
|
|
34
35
|
|
|
@@ -115,22 +115,40 @@ describe('test EmergencyContactAddNew', () => {
|
|
|
115
115
|
const instance = tree.root;
|
|
116
116
|
const viewButtonBottom = instance.findByType(ViewButtonBottom);
|
|
117
117
|
|
|
118
|
+
const TextInput_ = instance.find(
|
|
119
|
+
(el) =>
|
|
120
|
+
el.props.testID === TESTID.ON_CHANGE_PHONE_EMERGENCY_CONTACT &&
|
|
121
|
+
el.type === TextInput
|
|
122
|
+
);
|
|
123
|
+
|
|
118
124
|
await act(async () => {
|
|
125
|
+
await TextInput_.props.onChangeText('0901603859');
|
|
119
126
|
await viewButtonBottom.props.onRightClick();
|
|
120
127
|
});
|
|
121
128
|
|
|
122
129
|
expect(mockedGoBack).toHaveBeenCalledTimes(1);
|
|
123
130
|
});
|
|
124
131
|
|
|
125
|
-
test('onSave
|
|
132
|
+
test('onSave failed', async () => {
|
|
126
133
|
await act(async () => {
|
|
127
134
|
tree = await create(wrapComponent(route));
|
|
128
135
|
});
|
|
129
136
|
const instance = tree.root;
|
|
130
137
|
const viewButtonBottom = instance.findByType(ViewButtonBottom);
|
|
131
138
|
|
|
139
|
+
const TextInput_ = instance.find(
|
|
140
|
+
(el) =>
|
|
141
|
+
el.props.testID === TESTID.ON_CHANGE_PHONE_EMERGENCY_CONTACT &&
|
|
142
|
+
el.type === TextInput
|
|
143
|
+
);
|
|
144
|
+
|
|
132
145
|
mock.onPost(API.EMERGENCY_BUTTON.CREATE_CONTACT()).reply(400);
|
|
133
|
-
|
|
146
|
+
|
|
147
|
+
await act(async () => {
|
|
148
|
+
await TextInput_.props.onChangeText('0901603859');
|
|
149
|
+
await viewButtonBottom.props.onRightClick();
|
|
150
|
+
});
|
|
151
|
+
|
|
134
152
|
expect(Toast.show).toHaveBeenCalledWith({
|
|
135
153
|
type: 'error',
|
|
136
154
|
position: 'bottom',
|
|
@@ -139,4 +157,20 @@ describe('test EmergencyContactAddNew', () => {
|
|
|
139
157
|
});
|
|
140
158
|
expect(mockedGoBack).not.toHaveBeenCalled();
|
|
141
159
|
});
|
|
160
|
+
|
|
161
|
+
test('invalid phone number', async () => {
|
|
162
|
+
await act(async () => {
|
|
163
|
+
tree = await create(wrapComponent(route));
|
|
164
|
+
});
|
|
165
|
+
const instance = tree.root;
|
|
166
|
+
const viewButtonBottom = instance.findByType(ViewButtonBottom);
|
|
167
|
+
|
|
168
|
+
await viewButtonBottom.props.onRightClick();
|
|
169
|
+
expect(Toast.show).toHaveBeenCalledWith({
|
|
170
|
+
type: 'error',
|
|
171
|
+
position: 'bottom',
|
|
172
|
+
text1: getTranslate('en', 'invalid_phone_number'),
|
|
173
|
+
visibilityTime: 1000,
|
|
174
|
+
});
|
|
175
|
+
});
|
|
142
176
|
});
|
|
@@ -14,14 +14,14 @@ export const useStarredScript = (automate) => {
|
|
|
14
14
|
|
|
15
15
|
const starScript = useCallback(async () => {
|
|
16
16
|
const { success } = await axiosPost(API.AUTOMATE.STAR_SCRIPT(automate?.id));
|
|
17
|
-
success && setAction(Action.
|
|
17
|
+
success && setAction(Action.STAR_SCRIPTS, [automate?.script?.id]);
|
|
18
18
|
}, [automate, setAction]);
|
|
19
19
|
|
|
20
20
|
const unstarScript = useCallback(async () => {
|
|
21
21
|
const { success } = await axiosPost(
|
|
22
22
|
API.AUTOMATE.UNSTAR_SCRIPT(automate?.id)
|
|
23
23
|
);
|
|
24
|
-
success && setAction(Action.
|
|
24
|
+
success && setAction(Action.UNSTAR_SCRIPTS, [automate?.script?.id]);
|
|
25
25
|
}, [automate, setAction]);
|
|
26
26
|
|
|
27
27
|
return {
|
|
@@ -15,14 +15,15 @@ import NavBar from '../../commons/NavBar';
|
|
|
15
15
|
import BottomButtonView from '../../commons/BottomButtonView';
|
|
16
16
|
import { FullLoading } from '../../commons';
|
|
17
17
|
import Device from '../AddNewAction/Device';
|
|
18
|
+
import AutomateScript from './components/AutomateScript';
|
|
18
19
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
19
20
|
import { SCContext } from '../../context';
|
|
20
21
|
import { Action } from '../../context/actionType';
|
|
21
22
|
import { axiosGet, axiosPost } from '../../utils/Apis/axios';
|
|
22
23
|
import { API, Colors } from '../../configs';
|
|
23
|
-
import styles from './
|
|
24
|
+
import styles from './SelectAddToFavoritesStyles';
|
|
24
25
|
|
|
25
|
-
const
|
|
26
|
+
const SelectAddToFavorites = memo(({ route }) => {
|
|
26
27
|
const t = useTranslations();
|
|
27
28
|
const { goBack } = useNavigation();
|
|
28
29
|
const { unitId } = route.params;
|
|
@@ -31,43 +32,69 @@ const SelectFavoritesDevices = memo(({ route }) => {
|
|
|
31
32
|
const [listMenuItem, setListMenuItem] = useState([]);
|
|
32
33
|
const [indexStation, setIndexStation] = useState(0);
|
|
33
34
|
const [stations, setStations] = useState([]);
|
|
34
|
-
const [
|
|
35
|
+
const [automates, setAutomates] = useState([]);
|
|
36
|
+
|
|
37
|
+
const [selectedDeviceIds, setSelectedDeviceIds] = useState([]);
|
|
38
|
+
const [selectedScriptIds, setSelectedScriptIds] = useState([]);
|
|
35
39
|
const [loading, setLoading] = useState(true);
|
|
36
40
|
|
|
37
41
|
const fetchData = useCallback(async () => {
|
|
38
42
|
setLoading(true);
|
|
39
|
-
const { success, data } = await axiosGet(
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
const { success: successDevice, data: dataDevice } = await axiosGet(
|
|
44
|
+
API.UNIT.DEVICES_NOT_FAVORITES(unitId)
|
|
45
|
+
);
|
|
46
|
+
const { success: successAutomate, data: dataAutomate } = await axiosGet(
|
|
47
|
+
API.UNIT.AUTOMATE_SCRIPTS_NOT_STARRED(unitId)
|
|
48
|
+
);
|
|
49
|
+
if (successDevice && successAutomate) {
|
|
50
|
+
const newData = dataDevice.filter((item) => item.devices.length > 0);
|
|
51
|
+
if (dataAutomate.length) {
|
|
52
|
+
newData.unshift({
|
|
53
|
+
isSmart: true,
|
|
54
|
+
name: t('smart'),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
42
57
|
const listMenu = newData.map((item, index) => ({
|
|
43
58
|
text: item.name,
|
|
44
59
|
station: item,
|
|
45
60
|
index: index,
|
|
46
61
|
}));
|
|
47
62
|
setStations(newData);
|
|
63
|
+
setAutomates(dataAutomate);
|
|
48
64
|
setListMenuItem(listMenu);
|
|
49
65
|
setListStation(listMenu);
|
|
50
66
|
}
|
|
51
67
|
setLoading(false);
|
|
52
|
-
}, [unitId]);
|
|
68
|
+
}, [unitId, t]);
|
|
53
69
|
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
70
|
+
const addToFavorites = useCallback(async () => {
|
|
71
|
+
if (!selectedDeviceIds.length && !selectedScriptIds.length) {
|
|
56
72
|
return;
|
|
57
73
|
}
|
|
58
74
|
setLoading(true);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
|
|
76
|
+
let response, success1, success2;
|
|
77
|
+
|
|
78
|
+
if (selectedDeviceIds.length) {
|
|
79
|
+
response = await axiosPost(API.UNIT.ADD_DEVICES_TO_FAVORITES(unitId), {
|
|
80
|
+
devices: selectedDeviceIds,
|
|
81
|
+
});
|
|
82
|
+
success1 = response.success;
|
|
83
|
+
}
|
|
84
|
+
if (selectedScriptIds.length) {
|
|
85
|
+
response = await axiosPost(API.UNIT.STAR_AUTOMATE_SCRIPTS(unitId), {
|
|
86
|
+
scripts: selectedScriptIds,
|
|
87
|
+
});
|
|
88
|
+
success2 = response.success;
|
|
89
|
+
}
|
|
90
|
+
success1 && setAction(Action.ADD_DEVICES_TO_FAVORITES, selectedDeviceIds);
|
|
91
|
+
success2 && setAction(Action.STAR_SCRIPTS, selectedScriptIds);
|
|
92
|
+
if (success1 || success2) {
|
|
67
93
|
goBack();
|
|
68
94
|
}
|
|
95
|
+
|
|
69
96
|
setLoading(false);
|
|
70
|
-
}, [unitId,
|
|
97
|
+
}, [unitId, selectedDeviceIds, selectedScriptIds, setAction, goBack]);
|
|
71
98
|
|
|
72
99
|
useEffect(() => {
|
|
73
100
|
fetchData();
|
|
@@ -83,7 +110,7 @@ const SelectFavoritesDevices = memo(({ route }) => {
|
|
|
83
110
|
|
|
84
111
|
const onSelectDevice = useCallback(
|
|
85
112
|
(device) => {
|
|
86
|
-
|
|
113
|
+
setSelectedDeviceIds((ids) => {
|
|
87
114
|
const index = ids.indexOf(device.id);
|
|
88
115
|
if (index !== -1) {
|
|
89
116
|
return ids.filter((id) => id !== device.id);
|
|
@@ -91,7 +118,20 @@ const SelectFavoritesDevices = memo(({ route }) => {
|
|
|
91
118
|
return [...ids, device.id];
|
|
92
119
|
});
|
|
93
120
|
},
|
|
94
|
-
[
|
|
121
|
+
[setSelectedDeviceIds]
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const onSelectAutomateScript = useCallback(
|
|
125
|
+
(automate) => {
|
|
126
|
+
setSelectedScriptIds((ids) => {
|
|
127
|
+
const index = ids.indexOf(automate?.script?.id);
|
|
128
|
+
if (index !== -1) {
|
|
129
|
+
return ids.filter((id) => id !== automate?.script?.id);
|
|
130
|
+
}
|
|
131
|
+
return [...ids, automate?.script?.id];
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
[setSelectedScriptIds]
|
|
95
135
|
);
|
|
96
136
|
|
|
97
137
|
const rightComponent = useMemo(
|
|
@@ -132,13 +172,24 @@ const SelectFavoritesDevices = memo(({ route }) => {
|
|
|
132
172
|
)}
|
|
133
173
|
|
|
134
174
|
<View style={styles.boxDevices}>
|
|
135
|
-
{stations[indexStation]?.
|
|
136
|
-
|
|
175
|
+
{stations[indexStation]?.isSmart &&
|
|
176
|
+
automates.map((automate, index) => (
|
|
177
|
+
<AutomateScript
|
|
178
|
+
key={`automate_script_${index}`}
|
|
179
|
+
automate={automate}
|
|
180
|
+
onPress={onSelectAutomateScript}
|
|
181
|
+
isSelected={selectedScriptIds.includes(automate?.script?.id)}
|
|
182
|
+
/>
|
|
183
|
+
))}
|
|
184
|
+
{!stations[indexStation]?.isSmart &&
|
|
185
|
+
stations[indexStation]?.devices &&
|
|
186
|
+
stations[indexStation].devices.map((device, index) => (
|
|
137
187
|
<Device
|
|
188
|
+
key={`device_${index}`}
|
|
138
189
|
svgMain={device.icon || 'sensor'}
|
|
139
190
|
title={device.name}
|
|
140
191
|
sensor={device}
|
|
141
|
-
isSelectDevice={
|
|
192
|
+
isSelectDevice={selectedDeviceIds.includes(device.id)}
|
|
142
193
|
onPress={onSelectDevice}
|
|
143
194
|
/>
|
|
144
195
|
))}
|
|
@@ -148,12 +199,16 @@ const SelectFavoritesDevices = memo(({ route }) => {
|
|
|
148
199
|
<BottomButtonView
|
|
149
200
|
style={styles.bottomButtonView}
|
|
150
201
|
mainTitle={t('done')}
|
|
151
|
-
onPressMain={
|
|
152
|
-
typeMain={
|
|
202
|
+
onPressMain={addToFavorites}
|
|
203
|
+
typeMain={
|
|
204
|
+
!selectedDeviceIds.length && !selectedScriptIds.length
|
|
205
|
+
? 'disabled'
|
|
206
|
+
: 'primary'
|
|
207
|
+
}
|
|
153
208
|
/>
|
|
154
209
|
{loading && <FullLoading />}
|
|
155
210
|
</View>
|
|
156
211
|
);
|
|
157
212
|
});
|
|
158
213
|
|
|
159
|
-
export default
|
|
214
|
+
export default SelectAddToFavorites;
|
|
File without changes
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { act, create } 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 SelectAddToFavorites from '../SelectAddToFavorites';
|
|
8
|
+
import NavBar from '../../../commons/NavBar';
|
|
9
|
+
import Device from '../../AddNewAction/Device';
|
|
10
|
+
import AutomateScript from '../components/AutomateScript';
|
|
11
|
+
import BottomButtonView from '../../../commons/BottomButtonView';
|
|
12
|
+
import { API } from '../../../configs';
|
|
13
|
+
import api from '../../../utils/Apis/axios';
|
|
14
|
+
|
|
15
|
+
const wrapComponent = (route) => (
|
|
16
|
+
<SCProvider initState={mockSCStore({})}>
|
|
17
|
+
<SelectAddToFavorites route={route} />
|
|
18
|
+
</SCProvider>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const mock = new MockAdapter(api.axiosInstance);
|
|
22
|
+
|
|
23
|
+
const mockGoBack = jest.fn();
|
|
24
|
+
jest.mock('@react-navigation/native', () => {
|
|
25
|
+
return {
|
|
26
|
+
...jest.requireActual('@react-navigation/native'),
|
|
27
|
+
useNavigation: () => ({
|
|
28
|
+
goBack: mockGoBack,
|
|
29
|
+
}),
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
jest.mock('react', () => {
|
|
34
|
+
return {
|
|
35
|
+
...jest.requireActual('react'),
|
|
36
|
+
memo: (x) => x,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('Test SelectAddToFavorites', () => {
|
|
41
|
+
let tree, route;
|
|
42
|
+
let dataDevice = [
|
|
43
|
+
{
|
|
44
|
+
id: 1,
|
|
45
|
+
name: 'station 1',
|
|
46
|
+
devices: [
|
|
47
|
+
{
|
|
48
|
+
id: 1,
|
|
49
|
+
name: 'device 1',
|
|
50
|
+
icon: 'sensor',
|
|
51
|
+
icon_kit: null,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 2,
|
|
55
|
+
name: 'device 2',
|
|
56
|
+
icon: null,
|
|
57
|
+
icon_kit: 'icon',
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 2,
|
|
63
|
+
name: 'station 2',
|
|
64
|
+
devices: [],
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
let dataAutomate = [
|
|
68
|
+
{
|
|
69
|
+
id: 1,
|
|
70
|
+
script: {
|
|
71
|
+
id: 1,
|
|
72
|
+
icon_kit: 'sensor',
|
|
73
|
+
name: 'script 1',
|
|
74
|
+
},
|
|
75
|
+
type: 'one_tap',
|
|
76
|
+
author: 'author',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 2,
|
|
80
|
+
script: {
|
|
81
|
+
id: 2,
|
|
82
|
+
icon_kit: 'sensor',
|
|
83
|
+
name: 'script 2',
|
|
84
|
+
},
|
|
85
|
+
type: 'value_change',
|
|
86
|
+
author: 'author',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 3,
|
|
90
|
+
script: {
|
|
91
|
+
id: 3,
|
|
92
|
+
icon_kit: 'sensor',
|
|
93
|
+
name: 'script 3',
|
|
94
|
+
},
|
|
95
|
+
type: 'schedule',
|
|
96
|
+
author: 'author',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 4,
|
|
100
|
+
script: {
|
|
101
|
+
id: 4,
|
|
102
|
+
icon_kit: 'sensor',
|
|
103
|
+
name: 'script 4',
|
|
104
|
+
},
|
|
105
|
+
type: 'event',
|
|
106
|
+
author: 'author',
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
beforeEach(() => {
|
|
111
|
+
mockGoBack.mockClear();
|
|
112
|
+
mock.resetHistory();
|
|
113
|
+
route = {
|
|
114
|
+
params: {
|
|
115
|
+
unitId: 1,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('test select then add devices to favorites, star scripts', async () => {
|
|
121
|
+
mock.onGet(API.UNIT.DEVICES_NOT_FAVORITES(1)).replyOnce(200, dataDevice);
|
|
122
|
+
mock
|
|
123
|
+
.onGet(API.UNIT.AUTOMATE_SCRIPTS_NOT_STARRED(1))
|
|
124
|
+
.replyOnce(200, dataAutomate);
|
|
125
|
+
mock.onPost(API.UNIT.ADD_DEVICES_TO_FAVORITES(1)).replyOnce(200);
|
|
126
|
+
mock.onPost(API.UNIT.STAR_AUTOMATE_SCRIPTS(1)).replyOnce(200);
|
|
127
|
+
|
|
128
|
+
await act(async () => {
|
|
129
|
+
tree = await create(wrapComponent(route));
|
|
130
|
+
});
|
|
131
|
+
expect(mock.history.get).toHaveLength(2);
|
|
132
|
+
const instance = tree.root;
|
|
133
|
+
|
|
134
|
+
const navbar = instance.findByType(NavBar);
|
|
135
|
+
const bottomButton = instance.findByType(BottomButtonView);
|
|
136
|
+
|
|
137
|
+
await act(async () => {
|
|
138
|
+
await bottomButton.props.onPressMain();
|
|
139
|
+
});
|
|
140
|
+
expect(mock.history.post).toHaveLength(0);
|
|
141
|
+
|
|
142
|
+
const scripts = instance.findAllByType(AutomateScript);
|
|
143
|
+
expect(scripts).toHaveLength(4);
|
|
144
|
+
|
|
145
|
+
await act(async () => {
|
|
146
|
+
await scripts[0].props.onPress({ script: { id: 1 } });
|
|
147
|
+
});
|
|
148
|
+
await act(async () => {
|
|
149
|
+
await scripts[0].props.onPress({ script: { id: 2 } });
|
|
150
|
+
});
|
|
151
|
+
await act(async () => {
|
|
152
|
+
await scripts[0].props.onPress({ script: { id: 1 } });
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
await act(async () => {
|
|
156
|
+
await navbar.props.onSnapToItem(null, 1);
|
|
157
|
+
});
|
|
158
|
+
const devices = instance.findAllByType(Device);
|
|
159
|
+
expect(devices).toHaveLength(2);
|
|
160
|
+
|
|
161
|
+
await act(async () => {
|
|
162
|
+
await devices[0].props.onPress({ id: 1 });
|
|
163
|
+
});
|
|
164
|
+
await act(async () => {
|
|
165
|
+
await devices[1].props.onPress({ id: 2 });
|
|
166
|
+
});
|
|
167
|
+
await act(async () => {
|
|
168
|
+
await devices[0].props.onPress({ id: 1 });
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
await act(async () => {
|
|
172
|
+
await bottomButton.props.onPressMain();
|
|
173
|
+
});
|
|
174
|
+
expect(mock.history.post).toHaveLength(2);
|
|
175
|
+
expect(mockGoBack).toBeCalled();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test('test select only devices then click done', async () => {
|
|
179
|
+
mock.onGet(API.UNIT.DEVICES_NOT_FAVORITES(1)).replyOnce(200, dataDevice);
|
|
180
|
+
mock
|
|
181
|
+
.onGet(API.UNIT.AUTOMATE_SCRIPTS_NOT_STARRED(1))
|
|
182
|
+
.replyOnce(200, dataAutomate);
|
|
183
|
+
mock.onPost(API.UNIT.ADD_DEVICES_TO_FAVORITES(1)).replyOnce(200);
|
|
184
|
+
mock.onPost(API.UNIT.STAR_AUTOMATE_SCRIPTS(1)).replyOnce(200);
|
|
185
|
+
|
|
186
|
+
await act(async () => {
|
|
187
|
+
tree = await create(wrapComponent(route));
|
|
188
|
+
});
|
|
189
|
+
expect(mock.history.get).toHaveLength(2);
|
|
190
|
+
const instance = tree.root;
|
|
191
|
+
|
|
192
|
+
const navbar = instance.findByType(NavBar);
|
|
193
|
+
const bottomButton = instance.findByType(BottomButtonView);
|
|
194
|
+
|
|
195
|
+
await act(async () => {
|
|
196
|
+
await navbar.props.onSnapToItem(null, 1);
|
|
197
|
+
});
|
|
198
|
+
const devices = instance.findAllByType(Device);
|
|
199
|
+
expect(devices).toHaveLength(2);
|
|
200
|
+
|
|
201
|
+
await act(async () => {
|
|
202
|
+
await devices[0].props.onPress({ id: 1 });
|
|
203
|
+
});
|
|
204
|
+
await act(async () => {
|
|
205
|
+
await bottomButton.props.onPressMain();
|
|
206
|
+
});
|
|
207
|
+
expect(mock.history.post).toHaveLength(1);
|
|
208
|
+
expect(mockGoBack).toBeCalled();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('test select only scripts then click done', async () => {
|
|
212
|
+
mock.onGet(API.UNIT.DEVICES_NOT_FAVORITES(1)).replyOnce(200, dataDevice);
|
|
213
|
+
mock
|
|
214
|
+
.onGet(API.UNIT.AUTOMATE_SCRIPTS_NOT_STARRED(1))
|
|
215
|
+
.replyOnce(200, dataAutomate);
|
|
216
|
+
mock.onPost(API.UNIT.ADD_DEVICES_TO_FAVORITES(1)).replyOnce(200);
|
|
217
|
+
mock.onPost(API.UNIT.STAR_AUTOMATE_SCRIPTS(1)).replyOnce(200);
|
|
218
|
+
|
|
219
|
+
await act(async () => {
|
|
220
|
+
tree = await create(wrapComponent(route));
|
|
221
|
+
});
|
|
222
|
+
expect(mock.history.get).toHaveLength(2);
|
|
223
|
+
const instance = tree.root;
|
|
224
|
+
|
|
225
|
+
const bottomButton = instance.findByType(BottomButtonView);
|
|
226
|
+
|
|
227
|
+
const scripts = instance.findAllByType(AutomateScript);
|
|
228
|
+
expect(scripts).toHaveLength(4);
|
|
229
|
+
|
|
230
|
+
await act(async () => {
|
|
231
|
+
await scripts[0].props.onPress({ script: { id: 1 } });
|
|
232
|
+
});
|
|
233
|
+
await act(async () => {
|
|
234
|
+
await bottomButton.props.onPressMain();
|
|
235
|
+
});
|
|
236
|
+
expect(mock.history.post).toHaveLength(1);
|
|
237
|
+
expect(mockGoBack).toBeCalled();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('test click done call api fail not goBack', async () => {
|
|
241
|
+
mock.onGet(API.UNIT.DEVICES_NOT_FAVORITES(1)).replyOnce(200, dataDevice);
|
|
242
|
+
mock
|
|
243
|
+
.onGet(API.UNIT.AUTOMATE_SCRIPTS_NOT_STARRED(1))
|
|
244
|
+
.replyOnce(200, dataAutomate);
|
|
245
|
+
mock.onPost(API.UNIT.ADD_DEVICES_TO_FAVORITES(1)).replyOnce(200);
|
|
246
|
+
mock.onPost(API.UNIT.STAR_AUTOMATE_SCRIPTS(1)).replyOnce(400);
|
|
247
|
+
|
|
248
|
+
await act(async () => {
|
|
249
|
+
tree = await create(wrapComponent(route));
|
|
250
|
+
});
|
|
251
|
+
expect(mock.history.get).toHaveLength(2);
|
|
252
|
+
const instance = tree.root;
|
|
253
|
+
|
|
254
|
+
const bottomButton = instance.findByType(BottomButtonView);
|
|
255
|
+
|
|
256
|
+
const scripts = instance.findAllByType(AutomateScript);
|
|
257
|
+
expect(scripts).toHaveLength(4);
|
|
258
|
+
|
|
259
|
+
await act(async () => {
|
|
260
|
+
await scripts[0].props.onPress({ script: { id: 1 } });
|
|
261
|
+
});
|
|
262
|
+
await act(async () => {
|
|
263
|
+
await bottomButton.props.onPressMain();
|
|
264
|
+
});
|
|
265
|
+
expect(mock.history.post).toHaveLength(1);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, TouchableWithoutFeedback } from 'react-native';
|
|
3
|
+
import { IconOutline } from '@ant-design/icons-react-native';
|
|
4
|
+
import { useTranslations } from '../../../../hooks/Common/useTranslations';
|
|
5
|
+
import Text from '../../../../commons/Text';
|
|
6
|
+
import FImage from '../../../../commons/FImage';
|
|
7
|
+
import OneTap from '../../../../../assets/images/OneTap.svg';
|
|
8
|
+
import ValueChange from '../../../../../assets/images/ValueChange.svg';
|
|
9
|
+
import Schedule from '../../../../../assets/images/Schedule.svg';
|
|
10
|
+
import Event from '../../../../../assets/images/Event.svg';
|
|
11
|
+
import styles from './styles';
|
|
12
|
+
import { AUTOMATE_TYPE } from '../../../../configs/Constants';
|
|
13
|
+
import { Colors } from '../../../../configs';
|
|
14
|
+
|
|
15
|
+
const AutomateScript = ({ automate, onPress, isSelected }) => {
|
|
16
|
+
const t = useTranslations();
|
|
17
|
+
const { script, type, author = '' } = automate;
|
|
18
|
+
|
|
19
|
+
const _onPress = () => {
|
|
20
|
+
onPress && onPress(automate);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const displayIcon = () => {
|
|
24
|
+
const iconKit = script?.icon_kit;
|
|
25
|
+
if (iconKit) {
|
|
26
|
+
return <FImage source={{ uri: iconKit }} style={styles.iconSensor} />;
|
|
27
|
+
} else if (type === AUTOMATE_TYPE.ONE_TAP) {
|
|
28
|
+
return <OneTap />;
|
|
29
|
+
} else if (type === AUTOMATE_TYPE.VALUE_CHANGE) {
|
|
30
|
+
return <ValueChange />;
|
|
31
|
+
} else if (type === AUTOMATE_TYPE.EVENT) {
|
|
32
|
+
return <Event />;
|
|
33
|
+
} else {
|
|
34
|
+
return <Schedule />;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<TouchableWithoutFeedback onPress={_onPress}>
|
|
40
|
+
<View style={[styles.container, isSelected && styles.active]}>
|
|
41
|
+
<View style={styles.boxIcon}>{displayIcon()}</View>
|
|
42
|
+
<View>
|
|
43
|
+
<Text
|
|
44
|
+
numberOfLines={1}
|
|
45
|
+
semibold
|
|
46
|
+
size={14}
|
|
47
|
+
color={Colors.Gray9}
|
|
48
|
+
type="Body"
|
|
49
|
+
style={styles.name}
|
|
50
|
+
>
|
|
51
|
+
{script?.name}
|
|
52
|
+
</Text>
|
|
53
|
+
<View style={styles.descriptionContainer}>
|
|
54
|
+
<Text numberOfLines={1} type={'Label'} color={Colors.Gray7}>
|
|
55
|
+
{`${t('create_by')} ${author}`}
|
|
56
|
+
</Text>
|
|
57
|
+
<IconOutline name="right" size={12} />
|
|
58
|
+
</View>
|
|
59
|
+
</View>
|
|
60
|
+
</View>
|
|
61
|
+
</TouchableWithoutFeedback>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export default AutomateScript;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { Constants, Colors } from '../../../../configs';
|
|
3
|
+
|
|
4
|
+
const marginItem = 12;
|
|
5
|
+
const marginHorizontal = 16;
|
|
6
|
+
const widthItem = (Constants.width - marginHorizontal * 2 - marginItem) / 2;
|
|
7
|
+
const heightItem = (widthItem / 166) * 96;
|
|
8
|
+
|
|
9
|
+
export default StyleSheet.create({
|
|
10
|
+
active: {
|
|
11
|
+
borderColor: Colors.Primary,
|
|
12
|
+
borderWidth: 2,
|
|
13
|
+
},
|
|
14
|
+
container: {
|
|
15
|
+
padding: 12,
|
|
16
|
+
borderRadius: 10,
|
|
17
|
+
shadowColor: Colors.Shadow,
|
|
18
|
+
shadowOffset: {
|
|
19
|
+
width: 0,
|
|
20
|
+
height: 2,
|
|
21
|
+
},
|
|
22
|
+
shadowOpacity: 0.1,
|
|
23
|
+
shadowRadius: 3,
|
|
24
|
+
elevation: 4,
|
|
25
|
+
width: widthItem,
|
|
26
|
+
height: heightItem,
|
|
27
|
+
backgroundColor: Colors.White,
|
|
28
|
+
justifyContent: 'space-between',
|
|
29
|
+
marginBottom: 8,
|
|
30
|
+
},
|
|
31
|
+
boxIcon: {
|
|
32
|
+
flexDirection: 'row',
|
|
33
|
+
justifyContent: 'space-between',
|
|
34
|
+
},
|
|
35
|
+
descriptionContainer: {
|
|
36
|
+
flexDirection: 'row',
|
|
37
|
+
justifyContent: 'space-between',
|
|
38
|
+
alignItems: 'center',
|
|
39
|
+
},
|
|
40
|
+
iconSensor: {
|
|
41
|
+
width: 40,
|
|
42
|
+
height: 40,
|
|
43
|
+
resizeMode: 'contain',
|
|
44
|
+
},
|
|
45
|
+
name: {
|
|
46
|
+
marginTop: 6,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
@@ -997,5 +997,6 @@
|
|
|
997
997
|
"activated": "Activated",
|
|
998
998
|
"text_unit_add_to_favorites_no_devices": "You don't have any devices or all your devices was added to your favorites",
|
|
999
999
|
"not_found": "Not found",
|
|
1000
|
-
"not_activated": "Not activated"
|
|
1000
|
+
"not_activated": "Not activated",
|
|
1001
|
+
"create_contact_success": "Create contact success!"
|
|
1001
1002
|
}
|
|
@@ -998,5 +998,6 @@
|
|
|
998
998
|
"activated": "Được kích hoạt",
|
|
999
999
|
"text_unit_add_to_favorites_no_devices": "Bạn không có thiết bị nào hoặc tất cả thiết bị của bạn đã được thêm vào yêu thích",
|
|
1000
1000
|
"not_found": "Không tìm thấy",
|
|
1001
|
-
"not_activated": "Không kích hoạt"
|
|
1001
|
+
"not_activated": "Không kích hoạt",
|
|
1002
|
+
"create_contact_success": "Tạo liên hệ thành công!"
|
|
1002
1003
|
}
|
package/src/utils/Route/index.js
CHANGED
|
@@ -146,7 +146,7 @@ const Routes = {
|
|
|
146
146
|
ItemPasscode: 'ItemPasscode',
|
|
147
147
|
UnitMemberInformation: 'UnitMemberInformation',
|
|
148
148
|
EnterPassword: 'EnterPassword',
|
|
149
|
-
|
|
149
|
+
SelectAddToFavorites: 'SelectAddToFavorites',
|
|
150
150
|
};
|
|
151
151
|
|
|
152
152
|
export default Routes;
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { act, create } 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 SelectFavoritesDevices from '../SelectFavoritesDevices';
|
|
8
|
-
import Device from '../../AddNewAction/Device';
|
|
9
|
-
import BottomButtonView from '../../../commons/BottomButtonView';
|
|
10
|
-
import { API } from '../../../configs';
|
|
11
|
-
import api from '../../../utils/Apis/axios';
|
|
12
|
-
|
|
13
|
-
const wrapComponent = (route) => (
|
|
14
|
-
<SCProvider initState={mockSCStore({})}>
|
|
15
|
-
<SelectFavoritesDevices route={route} />
|
|
16
|
-
</SCProvider>
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
const mock = new MockAdapter(api.axiosInstance);
|
|
20
|
-
|
|
21
|
-
const mockGoBack = jest.fn();
|
|
22
|
-
jest.mock('@react-navigation/native', () => {
|
|
23
|
-
return {
|
|
24
|
-
...jest.requireActual('@react-navigation/native'),
|
|
25
|
-
useNavigation: () => ({
|
|
26
|
-
goBack: mockGoBack,
|
|
27
|
-
}),
|
|
28
|
-
};
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
jest.mock('react', () => {
|
|
32
|
-
return {
|
|
33
|
-
...jest.requireActual('react'),
|
|
34
|
-
memo: (x) => x,
|
|
35
|
-
};
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
describe('Test SelectFavoritesDevices', () => {
|
|
39
|
-
let tree, route;
|
|
40
|
-
|
|
41
|
-
beforeAll(() => {
|
|
42
|
-
mockGoBack.mockClear();
|
|
43
|
-
mock.resetHistory();
|
|
44
|
-
route = {
|
|
45
|
-
params: {
|
|
46
|
-
unitId: 1,
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test('test select then add devices to favorites', async () => {
|
|
52
|
-
let data = [
|
|
53
|
-
{
|
|
54
|
-
id: 1,
|
|
55
|
-
name: 'station 1',
|
|
56
|
-
devices: [
|
|
57
|
-
{
|
|
58
|
-
id: 1,
|
|
59
|
-
name: 'device 1',
|
|
60
|
-
icon: 'sensor',
|
|
61
|
-
icon_kit: null,
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
id: 2,
|
|
65
|
-
name: 'device 2',
|
|
66
|
-
icon: null,
|
|
67
|
-
icon_kit: 'icon',
|
|
68
|
-
},
|
|
69
|
-
],
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
id: 2,
|
|
73
|
-
name: 'station 2',
|
|
74
|
-
devices: [],
|
|
75
|
-
},
|
|
76
|
-
];
|
|
77
|
-
mock.onGet(API.UNIT.DEVICES(1)).replyOnce(200, data);
|
|
78
|
-
mock.onPost(API.UNIT.ADD_DEVICES_TO_FAVORITES(1)).replyOnce(200);
|
|
79
|
-
|
|
80
|
-
await act(async () => {
|
|
81
|
-
tree = await create(wrapComponent(route));
|
|
82
|
-
});
|
|
83
|
-
const instance = tree.root;
|
|
84
|
-
|
|
85
|
-
const bottomButton = instance.findByType(BottomButtonView);
|
|
86
|
-
|
|
87
|
-
await act(async () => {
|
|
88
|
-
await bottomButton.props.onPressMain();
|
|
89
|
-
});
|
|
90
|
-
expect(mock.history.post).toHaveLength(0);
|
|
91
|
-
|
|
92
|
-
const devices = instance.findAllByType(Device);
|
|
93
|
-
expect(devices).toHaveLength(2);
|
|
94
|
-
|
|
95
|
-
await act(async () => {
|
|
96
|
-
await devices[0].props.onPress({ id: 1 });
|
|
97
|
-
});
|
|
98
|
-
await act(async () => {
|
|
99
|
-
await devices[1].props.onPress({ id: 2 });
|
|
100
|
-
});
|
|
101
|
-
await act(async () => {
|
|
102
|
-
await devices[0].props.onPress({ id: 1 });
|
|
103
|
-
});
|
|
104
|
-
await act(async () => {
|
|
105
|
-
await bottomButton.props.onPressMain();
|
|
106
|
-
});
|
|
107
|
-
expect(mock.history.post).toHaveLength(1);
|
|
108
|
-
expect(mockGoBack).toBeCalled();
|
|
109
|
-
});
|
|
110
|
-
});
|