@eohjsc/react-native-smart-city 0.3.51 → 0.3.53
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 -2
- package/src/commons/Action/__test__/ItemQuickAction.test.js +56 -0
- package/src/commons/ActionTemplate/OnOffButtonAction.js +4 -2
- package/src/commons/ActionTemplate/OnOffSimpleAction.js +2 -0
- package/src/commons/ActionTemplate/OnOffSmartLockAction.js +4 -2
- package/src/commons/Device/ItemDevice.js +1 -1
- package/src/commons/ImagePicker/__test__/ImagePicker.test.js +40 -27
- package/src/commons/ImagePicker/index.js +53 -88
- package/src/commons/SelectGateway/index.js +10 -6
- package/src/commons/ViewButtonBottom/index.js +11 -3
- package/src/configs/SCConfig.js +4 -0
- package/src/hooks/IoT/useBluetoothConnection.js +3 -1
- package/src/screens/AddNewGateway/ConnectingWifiGuide.js +1 -0
- package/src/screens/AddNewGateway/SelectZigbeeGateway.js +8 -7
- package/src/screens/Device/__test__/detail.test.js +13 -2
- package/src/screens/Device/detail.js +78 -67
- package/src/screens/MoveToAnotherSubUnit/index.js +11 -4
- package/src/screens/PlayBackCamera/index.js +4 -1
- package/src/screens/ScriptDetail/__test__/useStarredScript.test.js +46 -0
- package/src/screens/Sharing/SelectPermission.js +2 -2
- package/src/screens/SubUnit/AddSubUnit.js +1 -0
- package/src/screens/Unit/components/MyUnitDevice/index.js +1 -0
- package/src/screens/UnitSummary/__test__/index.test.js +31 -2
- package/src/utils/I18n/translations/en.json +3 -1
- package/src/utils/I18n/translations/vi.json +3 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eohjsc/react-native-smart-city",
|
|
3
3
|
"title": "React Native Smart Home",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.53",
|
|
5
5
|
"description": "TODO",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"files": [
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
"@react-native-community/datetimepicker": "https://github.com/hinh-eoh/datepicker",
|
|
117
117
|
"@react-native-community/geolocation": "^2.0.2",
|
|
118
118
|
"@react-native-community/masked-view": "^0.1.10",
|
|
119
|
-
"@react-native-community/netinfo": "^
|
|
119
|
+
"@react-native-community/netinfo": "^9.3.4",
|
|
120
120
|
"@react-native-community/segmented-control": "^2.1.1",
|
|
121
121
|
"@react-native-community/slider": "^3.0.3",
|
|
122
122
|
"@react-native-community/toolbar-android": "^0.1.0-rc.2",
|
|
@@ -148,6 +148,62 @@ describe('Test ItemQuickAction', () => {
|
|
|
148
148
|
assertIsActionOn();
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
+
// eslint-disable-next-line max-len
|
|
152
|
+
it('trigger action with quick action with auto update and allow_config_store_value_id = quick_action.config_id and action.name includes off', async () => {
|
|
153
|
+
sensor.quick_action.will_auto_update_status = true;
|
|
154
|
+
sensor.quick_action.on_action.allow_config_store_value_id =
|
|
155
|
+
sensor.quick_action.config_id;
|
|
156
|
+
sensor.quick_action.on_action.name = 'off_action';
|
|
157
|
+
|
|
158
|
+
await act(async () => {
|
|
159
|
+
tree = await create(
|
|
160
|
+
<ItemQuickAction sensor={sensor} wrapperStyle={style} />
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
const instance = tree.root;
|
|
164
|
+
const buttonOnActionPress = instance.find(
|
|
165
|
+
(el) =>
|
|
166
|
+
el.props.accessibilityLabel ===
|
|
167
|
+
`${AccessibilityLabel.ITEM_QUICK_ACTION_PRESS}-${sensor?.id}` &&
|
|
168
|
+
el.type === TouchableOpacity
|
|
169
|
+
);
|
|
170
|
+
await act(async () => {
|
|
171
|
+
await buttonOnActionPress.props.onPress();
|
|
172
|
+
});
|
|
173
|
+
await act(async () => {
|
|
174
|
+
jest.runAllTimers();
|
|
175
|
+
});
|
|
176
|
+
assertIsActionOn();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// eslint-disable-next-line max-len
|
|
180
|
+
it('trigger action with quick action with auto update and allow_config_store_value_id = quick_action.config_id and action.name not includes off', async () => {
|
|
181
|
+
sensor.quick_action.will_auto_update_status = true;
|
|
182
|
+
sensor.quick_action.on_action.allow_config_store_value_id =
|
|
183
|
+
sensor.quick_action.config_id;
|
|
184
|
+
sensor.quick_action.on_action.name = 'on_action';
|
|
185
|
+
|
|
186
|
+
await act(async () => {
|
|
187
|
+
tree = await create(
|
|
188
|
+
<ItemQuickAction sensor={sensor} wrapperStyle={style} />
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
const instance = tree.root;
|
|
192
|
+
const buttonOnActionPress = instance.find(
|
|
193
|
+
(el) =>
|
|
194
|
+
el.props.accessibilityLabel ===
|
|
195
|
+
`${AccessibilityLabel.ITEM_QUICK_ACTION_PRESS}-${sensor?.id}` &&
|
|
196
|
+
el.type === TouchableOpacity
|
|
197
|
+
);
|
|
198
|
+
await act(async () => {
|
|
199
|
+
await buttonOnActionPress.props.onPress();
|
|
200
|
+
});
|
|
201
|
+
await act(async () => {
|
|
202
|
+
jest.runAllTimers();
|
|
203
|
+
});
|
|
204
|
+
assertIsActionOn();
|
|
205
|
+
});
|
|
206
|
+
|
|
151
207
|
it('on press will toggle action', async () => {
|
|
152
208
|
sensor.quick_action.on_action.id = 10; // off action
|
|
153
209
|
const mockSetStatus = jest.fn();
|
|
@@ -13,8 +13,9 @@ const OnOffButtonAction = ({ title, configuration, onPress, template }) => {
|
|
|
13
13
|
name: text_on,
|
|
14
14
|
action: action_on,
|
|
15
15
|
action_off: null,
|
|
16
|
+
template,
|
|
16
17
|
});
|
|
17
|
-
}, [onPress, configuration, text_on, action_on]);
|
|
18
|
+
}, [onPress, configuration, text_on, action_on, template]);
|
|
18
19
|
|
|
19
20
|
const onPressActionOff = useCallback(() => {
|
|
20
21
|
onPress &&
|
|
@@ -23,8 +24,9 @@ const OnOffButtonAction = ({ title, configuration, onPress, template }) => {
|
|
|
23
24
|
name: text_off,
|
|
24
25
|
action: action_off,
|
|
25
26
|
action_on: null,
|
|
27
|
+
template,
|
|
26
28
|
});
|
|
27
|
-
}, [onPress, configuration, text_off, action_off]);
|
|
29
|
+
}, [onPress, configuration, text_off, action_off, template]);
|
|
28
30
|
|
|
29
31
|
return (
|
|
30
32
|
<>
|
|
@@ -15,6 +15,7 @@ const OnOffSimpleAction = ({ configuration, onPress, template }) => {
|
|
|
15
15
|
name: t('text_on'),
|
|
16
16
|
action: action_on,
|
|
17
17
|
action_off: null,
|
|
18
|
+
template,
|
|
18
19
|
});
|
|
19
20
|
};
|
|
20
21
|
|
|
@@ -25,6 +26,7 @@ const OnOffSimpleAction = ({ configuration, onPress, template }) => {
|
|
|
25
26
|
name: t('text_off'),
|
|
26
27
|
action: action_off,
|
|
27
28
|
action_on: null,
|
|
29
|
+
template,
|
|
28
30
|
});
|
|
29
31
|
};
|
|
30
32
|
|
|
@@ -13,8 +13,9 @@ const OnOffSmartLockAction = ({ configuration, onPress, template }) => {
|
|
|
13
13
|
name: text_on,
|
|
14
14
|
action: action_on,
|
|
15
15
|
action_off: null,
|
|
16
|
+
template,
|
|
16
17
|
});
|
|
17
|
-
}, [onPress, configuration, text_on, action_on]);
|
|
18
|
+
}, [onPress, configuration, text_on, action_on, template]);
|
|
18
19
|
|
|
19
20
|
const onPressActionClose = useCallback(() => {
|
|
20
21
|
onPress &&
|
|
@@ -23,8 +24,9 @@ const OnOffSmartLockAction = ({ configuration, onPress, template }) => {
|
|
|
23
24
|
name: text_off,
|
|
24
25
|
action: action_off,
|
|
25
26
|
action_on: null,
|
|
27
|
+
template,
|
|
26
28
|
});
|
|
27
|
-
}, [onPress, configuration, text_off, action_off]);
|
|
29
|
+
}, [onPress, configuration, text_off, action_off, template]);
|
|
28
30
|
|
|
29
31
|
return (
|
|
30
32
|
<>
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
import { act, create } from 'react-test-renderer';
|
|
3
4
|
|
|
4
5
|
import ImagePicker from '../index';
|
|
5
6
|
import ButtonPopup from '../../ButtonPopup';
|
|
6
7
|
import { SCProvider } from '../../../context';
|
|
7
8
|
import { mockSCStore } from '../../../context/mockStore';
|
|
8
9
|
|
|
9
|
-
const
|
|
10
|
+
const mockSetImageUrl = jest.fn();
|
|
11
|
+
const mockSetShowImagePicker = jest.fn();
|
|
12
|
+
|
|
13
|
+
const wrapComponent = (options, showImagePicker) => (
|
|
10
14
|
<SCProvider initState={mockSCStore({})}>
|
|
11
15
|
<ImagePicker
|
|
12
|
-
showImagePicker={
|
|
13
|
-
setShowImagePicker={
|
|
14
|
-
setImageUrl={
|
|
16
|
+
showImagePicker={showImagePicker}
|
|
17
|
+
setShowImagePicker={mockSetShowImagePicker}
|
|
18
|
+
setImageUrl={mockSetImageUrl}
|
|
15
19
|
optionsCapture={options}
|
|
16
20
|
optionsSelect={{
|
|
17
21
|
mediaType: 'photo',
|
|
@@ -23,14 +27,18 @@ const wrapComponent = (options) => (
|
|
|
23
27
|
|
|
24
28
|
describe('Test ImagePicker', () => {
|
|
25
29
|
let tree;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
|
|
31
|
+
const options = {
|
|
32
|
+
mediaType: 'photo',
|
|
33
|
+
compressImageMaxWidth: 1280,
|
|
34
|
+
compressImageMaxHeight: 720,
|
|
35
|
+
compressImageQuality: 0.8,
|
|
36
|
+
};
|
|
37
|
+
|
|
30
38
|
it('create ImagePicker', async () => {
|
|
31
39
|
Platform.OS = 'android';
|
|
32
40
|
await act(async () => {
|
|
33
|
-
tree =
|
|
41
|
+
tree = await create(wrapComponent());
|
|
34
42
|
});
|
|
35
43
|
const instance = tree.root;
|
|
36
44
|
const textInputs = instance.findAllByType(ButtonPopup);
|
|
@@ -39,14 +47,8 @@ describe('Test ImagePicker', () => {
|
|
|
39
47
|
|
|
40
48
|
it('create ImagePicker optionsCapture', async () => {
|
|
41
49
|
Platform.OS = 'android';
|
|
42
|
-
const options = {
|
|
43
|
-
mediaType: 'photo',
|
|
44
|
-
compressImageMaxWidth: 1280,
|
|
45
|
-
compressImageMaxHeight: 720,
|
|
46
|
-
compressImageQuality: 0.8,
|
|
47
|
-
};
|
|
48
50
|
await act(async () => {
|
|
49
|
-
tree =
|
|
51
|
+
tree = await create(wrapComponent(options));
|
|
50
52
|
});
|
|
51
53
|
const instance = tree.root;
|
|
52
54
|
const textInputs = instance.findAllByType(ButtonPopup);
|
|
@@ -54,23 +56,34 @@ describe('Test ImagePicker', () => {
|
|
|
54
56
|
await act(async () => {
|
|
55
57
|
textInputs[0].props.onPressMain();
|
|
56
58
|
});
|
|
59
|
+
expect(mockSetImageUrl).toBeCalledWith({ path: 'path_from_camera' });
|
|
57
60
|
});
|
|
61
|
+
|
|
58
62
|
it('create ImagePicker onChooseFile', async () => {
|
|
59
63
|
Platform.OS = 'android';
|
|
60
|
-
const options = {
|
|
61
|
-
mediaType: 'photo',
|
|
62
|
-
compressImageMaxWidth: 1280,
|
|
63
|
-
compressImageMaxHeight: 720,
|
|
64
|
-
compressImageQuality: 0.8,
|
|
65
|
-
};
|
|
66
64
|
await act(async () => {
|
|
67
|
-
tree =
|
|
65
|
+
tree = await create(wrapComponent(options));
|
|
68
66
|
});
|
|
69
67
|
const instance = tree.root;
|
|
70
|
-
const
|
|
71
|
-
|
|
68
|
+
const buttonPopup = instance.findByType(ButtonPopup);
|
|
69
|
+
await act(async () => {
|
|
70
|
+
buttonPopup.props.onPressSecondary();
|
|
71
|
+
});
|
|
72
|
+
expect(mockSetImageUrl).toBeCalledWith({
|
|
73
|
+
path: 'file:///data/user/0/com.eohjsc.eohmobile/cache/Camera/80fbbd4b-926d-425f-a081-e21b13f2f7d0.jpg',
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('Test onClose', async () => {
|
|
78
|
+
Platform.OS = 'ios';
|
|
79
|
+
await act(async () => {
|
|
80
|
+
tree = await create(wrapComponent(options, true));
|
|
81
|
+
});
|
|
82
|
+
const instance = tree.root;
|
|
83
|
+
const buttonPopup = instance.findByType(ButtonPopup);
|
|
72
84
|
await act(async () => {
|
|
73
|
-
|
|
85
|
+
await buttonPopup.props.onClose();
|
|
74
86
|
});
|
|
87
|
+
expect(mockSetShowImagePicker).toBeCalledWith(false);
|
|
75
88
|
});
|
|
76
89
|
});
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import React, { useCallback } from 'react';
|
|
2
|
-
import { Platform, PermissionsAndroid } from 'react-native';
|
|
1
|
+
import React, { memo, useCallback } from 'react';
|
|
3
2
|
import ImagePickerCrop from 'react-native-image-crop-picker';
|
|
3
|
+
import { RESULTS } from 'react-native-permissions';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
keyPermission,
|
|
7
|
+
OpenSetting,
|
|
8
|
+
permitPermissionFunction,
|
|
9
|
+
} from '../../utils/Permission/common';
|
|
7
10
|
import ButtonPopup from '../ButtonPopup';
|
|
11
|
+
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
8
12
|
|
|
9
13
|
const ImagePicker = ({
|
|
10
14
|
showImagePicker,
|
|
@@ -14,47 +18,6 @@ const ImagePicker = ({
|
|
|
14
18
|
optionsSelect,
|
|
15
19
|
}) => {
|
|
16
20
|
const t = useTranslations();
|
|
17
|
-
|
|
18
|
-
const requestCameraPermission = useCallback(async () => {
|
|
19
|
-
if (Platform.OS === 'android') {
|
|
20
|
-
try {
|
|
21
|
-
const granted = await PermissionsAndroid.request(
|
|
22
|
-
PermissionsAndroid.PERMISSIONS.CAMERA,
|
|
23
|
-
{
|
|
24
|
-
title: 'Camera Permission',
|
|
25
|
-
message: 'App needs camera permission',
|
|
26
|
-
}
|
|
27
|
-
);
|
|
28
|
-
// If CAMERA Permission is granted
|
|
29
|
-
return granted === PermissionsAndroid.RESULTS.GRANTED;
|
|
30
|
-
} catch (err) {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
} else {
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
}, []);
|
|
37
|
-
|
|
38
|
-
const requestExternalWritePermission = useCallback(async () => {
|
|
39
|
-
if (Platform.OS === 'android') {
|
|
40
|
-
try {
|
|
41
|
-
const granted = await PermissionsAndroid.request(
|
|
42
|
-
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
|
|
43
|
-
{
|
|
44
|
-
title: 'External Storage Write Permission',
|
|
45
|
-
message: 'App needs write permission',
|
|
46
|
-
}
|
|
47
|
-
);
|
|
48
|
-
// If WRITE_EXTERNAL_STORAGE Permission is granted
|
|
49
|
-
return granted === PermissionsAndroid.RESULTS.GRANTED;
|
|
50
|
-
} catch (err) {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
} else {
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
}, []);
|
|
57
|
-
|
|
58
21
|
const captureImage = useCallback(
|
|
59
22
|
async (type) => {
|
|
60
23
|
let options = optionsCapture
|
|
@@ -65,28 +28,27 @@ const ImagePicker = ({
|
|
|
65
28
|
compressImageMaxHeight: 720,
|
|
66
29
|
compressImageQuality: 0.8,
|
|
67
30
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
31
|
+
permitPermissionFunction(keyPermission.CAMERA, async (result) => {
|
|
32
|
+
if (result === RESULTS.GRANTED) {
|
|
33
|
+
await ImagePickerCrop.openCamera(options)
|
|
34
|
+
.then((response) => {
|
|
35
|
+
setImageUrl(response);
|
|
36
|
+
setShowImagePicker(false);
|
|
37
|
+
})
|
|
38
|
+
.catch((e) => {
|
|
39
|
+
/* eslint-disable no-console */
|
|
40
|
+
console.log('ERROR ' + e);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (result === RESULTS.BLOCKED) {
|
|
44
|
+
OpenSetting(
|
|
45
|
+
t('camera_request_permission'),
|
|
46
|
+
t('camera_request_permission_des')
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
82
50
|
},
|
|
83
|
-
[
|
|
84
|
-
setImageUrl,
|
|
85
|
-
setShowImagePicker,
|
|
86
|
-
requestCameraPermission,
|
|
87
|
-
requestExternalWritePermission,
|
|
88
|
-
optionsCapture,
|
|
89
|
-
]
|
|
51
|
+
[optionsCapture, setImageUrl, setShowImagePicker, t]
|
|
90
52
|
);
|
|
91
53
|
|
|
92
54
|
const chooseFile = useCallback(
|
|
@@ -99,17 +61,27 @@ const ImagePicker = ({
|
|
|
99
61
|
compressImageMaxHeight: 720,
|
|
100
62
|
compressImageQuality: 0.8,
|
|
101
63
|
};
|
|
102
|
-
|
|
103
|
-
.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
64
|
+
permitPermissionFunction(keyPermission.SELECT_PHOTO, async (result) => {
|
|
65
|
+
if (result === RESULTS.GRANTED || result === RESULTS.LIMITED) {
|
|
66
|
+
await ImagePickerCrop.openPicker(options)
|
|
67
|
+
.then((response) => {
|
|
68
|
+
setImageUrl(response);
|
|
69
|
+
setShowImagePicker(false);
|
|
70
|
+
})
|
|
71
|
+
.catch((e) => {
|
|
72
|
+
/* eslint-disable no-console */
|
|
73
|
+
console.log('ERROR ' + e);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (result === RESULTS.BLOCKED) {
|
|
77
|
+
OpenSetting(
|
|
78
|
+
t('photo_request_permission'),
|
|
79
|
+
t('photo_request_permission_des')
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
111
83
|
},
|
|
112
|
-
[setImageUrl, setShowImagePicker,
|
|
84
|
+
[optionsSelect, setImageUrl, setShowImagePicker, t]
|
|
113
85
|
);
|
|
114
86
|
|
|
115
87
|
const onCaptureImage = useCallback(() => {
|
|
@@ -124,25 +96,18 @@ const ImagePicker = ({
|
|
|
124
96
|
setShowImagePicker(false);
|
|
125
97
|
}, [setShowImagePicker]);
|
|
126
98
|
|
|
127
|
-
const buttonEvent = {
|
|
128
|
-
main_title: t('take_photo'),
|
|
129
|
-
secondary_title: t('choose_from_library'),
|
|
130
|
-
on_press_main: onCaptureImage,
|
|
131
|
-
on_press_secondary: onChooseFile,
|
|
132
|
-
};
|
|
133
|
-
|
|
134
99
|
return (
|
|
135
100
|
<ButtonPopup
|
|
136
101
|
rowButton={false}
|
|
137
102
|
visible={showImagePicker}
|
|
138
103
|
onClose={onClose}
|
|
139
|
-
mainTitle={
|
|
140
|
-
onPressMain={
|
|
141
|
-
secondaryTitle={
|
|
142
|
-
onPressSecondary={
|
|
104
|
+
mainTitle={t('take_photo')}
|
|
105
|
+
onPressMain={onCaptureImage}
|
|
106
|
+
secondaryTitle={t('choose_from_library')}
|
|
107
|
+
onPressSecondary={onChooseFile}
|
|
143
108
|
typeSecondary={'primary'}
|
|
144
109
|
/>
|
|
145
110
|
);
|
|
146
111
|
};
|
|
147
112
|
|
|
148
|
-
export default ImagePicker;
|
|
113
|
+
export default memo(ImagePicker);
|
|
@@ -55,7 +55,7 @@ const SelectGateway = ({
|
|
|
55
55
|
const t = useTranslations();
|
|
56
56
|
const { goBack } = useNavigation();
|
|
57
57
|
const isFocused = useIsFocused();
|
|
58
|
-
const [selectedIndex, setSelectedIndex] = useState(
|
|
58
|
+
const [selectedIndex, setSelectedIndex] = useState(-1);
|
|
59
59
|
const [gateways, setGateways] = useState([]);
|
|
60
60
|
const [showPopupGuide, setShowPopupGuide, setHidePopupGuide] =
|
|
61
61
|
useBoolean(false);
|
|
@@ -77,15 +77,19 @@ const SelectGateway = ({
|
|
|
77
77
|
}
|
|
78
78
|
}, [setShowPopupGuide, type, unitId]);
|
|
79
79
|
|
|
80
|
-
const
|
|
80
|
+
const handleButtonModalOk = useCallback(() => {
|
|
81
81
|
setHidePopupGuide();
|
|
82
|
-
onPressOk();
|
|
83
|
-
};
|
|
82
|
+
onPressOk && onPressOk();
|
|
83
|
+
}, [onPressOk, setHidePopupGuide]);
|
|
84
84
|
|
|
85
85
|
useEffect(() => {
|
|
86
86
|
isFocused && fetchDetails();
|
|
87
87
|
}, [fetchDetails, isFocused]);
|
|
88
88
|
|
|
89
|
+
const onRightClickNext = useCallback(() => {
|
|
90
|
+
onPressNext && onPressNext(gateways[selectedIndex]);
|
|
91
|
+
}, [gateways, onPressNext, selectedIndex]);
|
|
92
|
+
|
|
89
93
|
return (
|
|
90
94
|
<View style={styles.container}>
|
|
91
95
|
<HeaderCustom title={title} isShowSeparator />
|
|
@@ -115,7 +119,7 @@ const SelectGateway = ({
|
|
|
115
119
|
onLeftClick={goBack}
|
|
116
120
|
rightTitle={t('next')}
|
|
117
121
|
rightDisabled={selectedIndex === -1}
|
|
118
|
-
onRightClick={
|
|
122
|
+
onRightClick={onRightClickNext}
|
|
119
123
|
accessibilityLabelPrefix={AccessibilityLabel.PREFIX.SELECT_UNIT}
|
|
120
124
|
/>
|
|
121
125
|
<ModalCustom // todo Huy - make reused component
|
|
@@ -134,7 +138,7 @@ const SelectGateway = ({
|
|
|
134
138
|
</View>
|
|
135
139
|
<ViewButtonBottom
|
|
136
140
|
rightTitle={t('ok')}
|
|
137
|
-
onRightClick={
|
|
141
|
+
onRightClick={handleButtonModalOk}
|
|
138
142
|
styleButton={styles.bottomButton}
|
|
139
143
|
/>
|
|
140
144
|
</View>
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
2
|
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import { Colors } from '../../configs';
|
|
5
5
|
import Text from '../../commons/Text';
|
|
6
6
|
import { AccessibilityLabel } from '../../configs/Constants';
|
|
7
|
+
import withPreventDoubleClick from '../WithPreventDoubleClick';
|
|
8
|
+
|
|
9
|
+
const PreventDoubleTouch = withPreventDoubleClick(TouchableOpacity);
|
|
7
10
|
|
|
8
11
|
// this view support either 1 or 2 button
|
|
9
12
|
const ViewButtonBottom = ({
|
|
@@ -19,9 +22,14 @@ const ViewButtonBottom = ({
|
|
|
19
22
|
styleButtonLeft,
|
|
20
23
|
styleButtonRight,
|
|
21
24
|
accessibilityLabelPrefix = '',
|
|
25
|
+
isPreventDoubleTouch = false,
|
|
22
26
|
}) => {
|
|
23
27
|
const useTwoButton = leftTitle && rightTitle;
|
|
24
28
|
|
|
29
|
+
const RightButtonView = useMemo(() => {
|
|
30
|
+
return isPreventDoubleTouch ? PreventDoubleTouch : TouchableOpacity;
|
|
31
|
+
}, [isPreventDoubleTouch]);
|
|
32
|
+
|
|
25
33
|
return (
|
|
26
34
|
<View style={styles.container}>
|
|
27
35
|
{leftTitle && (
|
|
@@ -47,7 +55,7 @@ const ViewButtonBottom = ({
|
|
|
47
55
|
</TouchableOpacity>
|
|
48
56
|
)}
|
|
49
57
|
{rightTitle && (
|
|
50
|
-
<
|
|
58
|
+
<RightButtonView
|
|
51
59
|
style={[
|
|
52
60
|
styles.button,
|
|
53
61
|
styleButton,
|
|
@@ -66,7 +74,7 @@ const ViewButtonBottom = ({
|
|
|
66
74
|
>
|
|
67
75
|
{rightTitle}
|
|
68
76
|
</Text>
|
|
69
|
-
</
|
|
77
|
+
</RightButtonView>
|
|
70
78
|
)}
|
|
71
79
|
</View>
|
|
72
80
|
);
|
package/src/configs/SCConfig.js
CHANGED
|
@@ -97,6 +97,7 @@ const SCDefaultConfig = {
|
|
|
97
97
|
pusherAppKey: '8557fcc63959f564f1aa',
|
|
98
98
|
pusherAppCluster: 'ap1',
|
|
99
99
|
intervalWatchConfigTime: 30000,
|
|
100
|
+
setCurrentSensorDisplay: () => {},
|
|
100
101
|
};
|
|
101
102
|
|
|
102
103
|
export class SCConfig {
|
|
@@ -112,6 +113,7 @@ export class SCConfig {
|
|
|
112
113
|
static pusherAppCluste = SCDefaultConfig.pusherAppCluster;
|
|
113
114
|
static language = 'en';
|
|
114
115
|
static intervalWatchConfigTime = SCDefaultConfig.intervalWatchConfigTime;
|
|
116
|
+
static setCurrentSensorDisplay = SCDefaultConfig.setCurrentSensorDisplay;
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
export const initSCConfig = (config) => {
|
|
@@ -135,4 +137,6 @@ export const initSCConfig = (config) => {
|
|
|
135
137
|
config.pusherAppCluster ?? SCDefaultConfig.pusherAppCluster;
|
|
136
138
|
SCConfig.intervalWatchConfigTime =
|
|
137
139
|
config.intervalWatchConfigTime ?? SCDefaultConfig.intervalWatchConfigTime;
|
|
140
|
+
SCConfig.setCurrentSensorDisplay =
|
|
141
|
+
config.setCurrentSensorDisplay ?? SCDefaultConfig.setCurrentSensorDisplay;
|
|
138
142
|
};
|
|
@@ -13,7 +13,8 @@ const permissions = [
|
|
|
13
13
|
'android.permission.BLUETOOTH_ADVERTISE',
|
|
14
14
|
];
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
// NOTE: fnCallback is used for Lavida when found device
|
|
17
|
+
const useBluetoothConnection = (fnCallback) => {
|
|
17
18
|
const t = useTranslations();
|
|
18
19
|
const appState = useRef(AppState.currentState);
|
|
19
20
|
|
|
@@ -29,6 +30,7 @@ const useBluetoothConnection = () => {
|
|
|
29
30
|
useSCContextSelector((state) => state.bluetooth.permissionsGranted);
|
|
30
31
|
|
|
31
32
|
const onDeviceFound = useCallback(async (name, device) => {
|
|
33
|
+
fnCallback && fnCallback({ name, device });
|
|
32
34
|
setAction(Action.SET_BLUETOOTH_CONNECTED_DEVICE, { name, device });
|
|
33
35
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
34
36
|
}, []);
|
|
@@ -6,18 +6,19 @@ import Routes from '../../utils/Route';
|
|
|
6
6
|
|
|
7
7
|
const SelectZigbeeGateway = ({ route }) => {
|
|
8
8
|
const t = useTranslations();
|
|
9
|
-
const
|
|
9
|
+
const { navigate } = useNavigation();
|
|
10
10
|
const { unitId, subUnit } = route?.params || {};
|
|
11
11
|
|
|
12
12
|
const onPressNext = useCallback(
|
|
13
13
|
(gateway) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
gateway &&
|
|
15
|
+
navigate(Routes.ZigbeeDeviceConnectGuide, {
|
|
16
|
+
unitId,
|
|
17
|
+
subUnit,
|
|
18
|
+
chipId: gateway.id,
|
|
19
|
+
});
|
|
19
20
|
},
|
|
20
|
-
[
|
|
21
|
+
[unitId, subUnit, navigate]
|
|
21
22
|
);
|
|
22
23
|
|
|
23
24
|
return (
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { Alert, ScrollView } from 'react-native';
|
|
3
3
|
import { act, create } from 'react-test-renderer';
|
|
4
4
|
import MockAdapter from 'axios-mock-adapter';
|
|
5
|
+
import { NavigationContext } from '@react-navigation/native';
|
|
5
6
|
|
|
6
7
|
import DeviceDetail from '../detail';
|
|
7
8
|
import { API } from '../../../configs';
|
|
@@ -29,7 +30,6 @@ jest.mock('@react-navigation/native', () => {
|
|
|
29
30
|
useNavigation: () => ({
|
|
30
31
|
navigate: mockedNavigate,
|
|
31
32
|
}),
|
|
32
|
-
useFocusEffect: jest.fn(),
|
|
33
33
|
};
|
|
34
34
|
});
|
|
35
35
|
|
|
@@ -73,9 +73,20 @@ const mockAxios = (
|
|
|
73
73
|
|
|
74
74
|
let store = mockSCStore({});
|
|
75
75
|
|
|
76
|
+
const navContextValue = {
|
|
77
|
+
isFocused: () => false,
|
|
78
|
+
addListener: jest.fn(() => jest.fn()),
|
|
79
|
+
};
|
|
76
80
|
const wrapComponent = (state, account, route) => (
|
|
77
81
|
<SCProvider initState={state}>
|
|
78
|
-
<
|
|
82
|
+
<NavigationContext.Provider
|
|
83
|
+
value={{
|
|
84
|
+
...navContextValue,
|
|
85
|
+
isFocused: () => true,
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
<DeviceDetail account={account} route={route} />
|
|
89
|
+
</NavigationContext.Provider>
|
|
79
90
|
</SCProvider>
|
|
80
91
|
);
|
|
81
92
|
|
|
@@ -7,7 +7,7 @@ import React, {
|
|
|
7
7
|
useContext,
|
|
8
8
|
} from 'react';
|
|
9
9
|
import { View, TouchableOpacity, Platform } from 'react-native';
|
|
10
|
-
import { useNavigation } from '@react-navigation/native';
|
|
10
|
+
import { useNavigation, useFocusEffect } from '@react-navigation/native';
|
|
11
11
|
import { useSelector } from 'react-redux';
|
|
12
12
|
import { IconFill, IconOutline } from '@ant-design/icons-react-native';
|
|
13
13
|
import { Icon } from '@ant-design/react-native';
|
|
@@ -55,7 +55,7 @@ import PreventAccess from '../../commons/PreventAccess';
|
|
|
55
55
|
import styles from './styles';
|
|
56
56
|
import Routes from '../../utils/Route';
|
|
57
57
|
import { getData as getLocalData } from '../../utils/Storage';
|
|
58
|
-
import { API, Colors } from '../../configs';
|
|
58
|
+
import { API, Colors, SCConfig } from '../../configs';
|
|
59
59
|
import { DEVICE_TYPE, AccessibilityLabel } from '../../configs/Constants';
|
|
60
60
|
import { axiosGet } from '../../utils/Apis/axios';
|
|
61
61
|
import { notImplemented } from '../../utils/Utils';
|
|
@@ -81,7 +81,8 @@ const DeviceDetail = ({ route }) => {
|
|
|
81
81
|
// eslint-disable-next-line no-unused-vars
|
|
82
82
|
const [configValues, setConfigValues] = useConfigGlobalState('configValues');
|
|
83
83
|
|
|
84
|
-
const { unitData, unitId, sensorData, sensorId } =
|
|
84
|
+
const { unitData, unitId, sensorData, sensorId, isMyUnitDeviceScreen } =
|
|
85
|
+
route?.params || {};
|
|
85
86
|
const [unit, setUnit] = useState(unitData || { id: unitId });
|
|
86
87
|
const [sensor, setSensor] = useState(sensorData || { id: sensorId });
|
|
87
88
|
const [station, setStation] = useState(sensor?.station);
|
|
@@ -116,6 +117,9 @@ const DeviceDetail = ({ route }) => {
|
|
|
116
117
|
useBluetoothDeviceConnected(sensor);
|
|
117
118
|
|
|
118
119
|
const isDeviceHasBle = useMemo(() => {
|
|
120
|
+
if (display.items.length === 0) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
119
123
|
const action = display.items.filter((item) => item.type === 'action');
|
|
120
124
|
if (action.length === 0) {
|
|
121
125
|
return false;
|
|
@@ -348,7 +352,7 @@ const DeviceDetail = ({ route }) => {
|
|
|
348
352
|
menuItems.push({
|
|
349
353
|
text: t('move_to_another_sub_unit'),
|
|
350
354
|
route: Routes.MoveToAnotherSubUnit,
|
|
351
|
-
data: { unit, sensor, station },
|
|
355
|
+
data: { unit, sensor, station, isMyUnitDeviceScreen },
|
|
352
356
|
});
|
|
353
357
|
}
|
|
354
358
|
if (isShowSetupEmergencyContact) {
|
|
@@ -428,14 +432,15 @@ const DeviceDetail = ({ route }) => {
|
|
|
428
432
|
display.items,
|
|
429
433
|
isOwner,
|
|
430
434
|
isShowSetupEmergencyContact,
|
|
431
|
-
|
|
435
|
+
sideMenu,
|
|
432
436
|
t,
|
|
437
|
+
isShowSetUpSmartLock,
|
|
433
438
|
isFavorite,
|
|
434
439
|
sensor,
|
|
435
440
|
unit,
|
|
436
441
|
sensorName,
|
|
437
|
-
sideMenu,
|
|
438
442
|
station,
|
|
443
|
+
isMyUnitDeviceScreen,
|
|
439
444
|
emergencyDeviceId,
|
|
440
445
|
addToFavorites,
|
|
441
446
|
removeFromFavorites,
|
|
@@ -496,77 +501,79 @@ const DeviceDetail = ({ route }) => {
|
|
|
496
501
|
[configValues, displayValuesData, evaluateValue]
|
|
497
502
|
);
|
|
498
503
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
display.items.map((item) => {
|
|
504
|
-
if (item.type !== 'value') {
|
|
505
|
-
return;
|
|
506
|
-
}
|
|
504
|
+
useFocusEffect(
|
|
505
|
+
useCallback(() => {
|
|
506
|
+
let params = new URLSearchParams();
|
|
507
|
+
const configIds = [];
|
|
507
508
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
509
|
+
display.items.map((item) => {
|
|
510
|
+
if (item.type !== 'value') {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
511
513
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
configIds.push(config.id);
|
|
514
|
+
if (!item.configuration) {
|
|
515
|
+
return;
|
|
515
516
|
}
|
|
517
|
+
|
|
518
|
+
item.configuration.configs.map((config) => {
|
|
519
|
+
if (!configIds.includes(config.id)) {
|
|
520
|
+
configIds.push(config.id);
|
|
521
|
+
}
|
|
522
|
+
});
|
|
516
523
|
});
|
|
517
|
-
});
|
|
518
524
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
525
|
+
configIds.map((id) => {
|
|
526
|
+
params.append('config', id);
|
|
527
|
+
});
|
|
522
528
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
529
|
+
const fetchValues = async () => {
|
|
530
|
+
const { success, data, resp_status } = await axiosGet(
|
|
531
|
+
API.DEVICE.DISPLAY_VALUES_V2(sensor?.id),
|
|
532
|
+
{
|
|
533
|
+
params: params,
|
|
534
|
+
}
|
|
535
|
+
);
|
|
536
|
+
if (success) {
|
|
537
|
+
data.isConnected = data.is_connected;
|
|
538
|
+
data.lastUpdated = data.last_updated
|
|
539
|
+
? moment(data.last_updated)
|
|
540
|
+
: data.last_updated;
|
|
541
|
+
setDisplayValuesData((prevState) => {
|
|
542
|
+
if (prevState.isConnected !== data.isConnected) {
|
|
543
|
+
setAction(Action.SET_DEVICES_STATUS, [
|
|
544
|
+
{ id: sensor?.id, is_connected: data.is_connected },
|
|
545
|
+
]);
|
|
546
|
+
}
|
|
547
|
+
return data;
|
|
548
|
+
});
|
|
549
|
+
} else if (resp_status >= 500) {
|
|
550
|
+
setServerDown(true);
|
|
528
551
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
setDisplayValuesData((prevState) => {
|
|
536
|
-
if (prevState.isConnected !== data.isConnected) {
|
|
537
|
-
setAction(Action.SET_DEVICES_STATUS, [
|
|
538
|
-
{ id: sensor?.id, is_connected: data.is_connected },
|
|
539
|
-
]);
|
|
552
|
+
setLoading((preState) => {
|
|
553
|
+
if (preState.isConnected) {
|
|
554
|
+
return {
|
|
555
|
+
...preState,
|
|
556
|
+
isConnected: false,
|
|
557
|
+
};
|
|
540
558
|
}
|
|
541
|
-
return
|
|
559
|
+
return preState;
|
|
542
560
|
});
|
|
543
|
-
}
|
|
544
|
-
|
|
561
|
+
};
|
|
562
|
+
if (
|
|
563
|
+
isNetworkConnected &&
|
|
564
|
+
sensor?.is_managed_by_backend &&
|
|
565
|
+
sensor?.device_type !== DEVICE_TYPE.LG_THINQ
|
|
566
|
+
) {
|
|
567
|
+
const updateInterval = setInterval(() => fetchValues(), 5000);
|
|
568
|
+
fetchValues();
|
|
569
|
+
return () => clearInterval(updateInterval);
|
|
570
|
+
} else {
|
|
571
|
+
Object.keys(sensor).length > 1 &&
|
|
572
|
+
setLoading((preState) => ({ ...preState, isConnected: false }));
|
|
545
573
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
...preState,
|
|
550
|
-
isConnected: false,
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
return preState;
|
|
554
|
-
});
|
|
555
|
-
};
|
|
556
|
-
if (
|
|
557
|
-
isNetworkConnected &&
|
|
558
|
-
sensor?.is_managed_by_backend &&
|
|
559
|
-
sensor?.device_type !== DEVICE_TYPE.LG_THINQ
|
|
560
|
-
) {
|
|
561
|
-
const updateInterval = setInterval(() => fetchValues(), 5000);
|
|
562
|
-
fetchValues();
|
|
563
|
-
return () => clearInterval(updateInterval);
|
|
564
|
-
} else {
|
|
565
|
-
Object.keys(sensor).length > 1 &&
|
|
566
|
-
setLoading((preState) => ({ ...preState, isConnected: false }));
|
|
567
|
-
}
|
|
568
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
569
|
-
}, [sensor, display, isNetworkConnected]);
|
|
574
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
575
|
+
}, [sensor, display, isNetworkConnected])
|
|
576
|
+
);
|
|
570
577
|
|
|
571
578
|
const isShowEmergencyResolve =
|
|
572
579
|
display.items.filter(
|
|
@@ -751,6 +758,10 @@ const DeviceDetail = ({ route }) => {
|
|
|
751
758
|
]
|
|
752
759
|
);
|
|
753
760
|
|
|
761
|
+
useEffect(() => {
|
|
762
|
+
SCConfig.setCurrentSensorDisplay(sensor);
|
|
763
|
+
}, [sensor]);
|
|
764
|
+
|
|
754
765
|
return (
|
|
755
766
|
<View style={styles.wrap}>
|
|
756
767
|
<WrapHeaderScrollable
|
|
@@ -33,15 +33,17 @@ const RowSubUnit = ({ subUnit, isSelected, onSelect }) => {
|
|
|
33
33
|
const MoveToAnotherSubUnit = memo(({ route }) => {
|
|
34
34
|
const t = useTranslations();
|
|
35
35
|
const { params = {} } = route;
|
|
36
|
-
const { unit, sensor, station } = params;
|
|
36
|
+
const { unit, sensor, station, isMyUnitDeviceScreen } = params;
|
|
37
37
|
const { navigate } = useNavigation();
|
|
38
38
|
const [selectedSubUnit, setSelectedSubUnit] = useState(
|
|
39
39
|
unit?.stations?.find((subUnit) => subUnit.id === station.id)
|
|
40
40
|
);
|
|
41
41
|
|
|
42
42
|
const listStationUnit = useMemo(() => {
|
|
43
|
-
return
|
|
44
|
-
|
|
43
|
+
return isMyUnitDeviceScreen
|
|
44
|
+
? unit?.stations || []
|
|
45
|
+
: unit?.stations.slice(2) || [];
|
|
46
|
+
}, [isMyUnitDeviceScreen, unit?.stations]);
|
|
45
47
|
|
|
46
48
|
const handleOnSelect = useCallback((item) => {
|
|
47
49
|
setSelectedSubUnit(item);
|
|
@@ -55,7 +57,12 @@ const MoveToAnotherSubUnit = memo(({ route }) => {
|
|
|
55
57
|
}
|
|
56
58
|
);
|
|
57
59
|
if (success) {
|
|
58
|
-
navigate(Routes.
|
|
60
|
+
navigate(Routes.UnitStack, {
|
|
61
|
+
screen: Routes.UnitDetail,
|
|
62
|
+
params: {
|
|
63
|
+
unitId: unit.id,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
59
66
|
}
|
|
60
67
|
}, [navigate, selectedSubUnit?.id, sensor?.id, station?.id, unit?.id]);
|
|
61
68
|
|
|
@@ -72,7 +72,10 @@ const PlayBackCamera = () => {
|
|
|
72
72
|
(hour) => {
|
|
73
73
|
const hourWithTimezone =
|
|
74
74
|
parseInt(hour, 10) + parseInt(item?.configuration?.time_zone || 0, 10);
|
|
75
|
-
|
|
75
|
+
if (hourWithTimezone < 0 || hourWithTimezone > 9) {
|
|
76
|
+
return hourWithTimezone;
|
|
77
|
+
}
|
|
78
|
+
return '0' + hourWithTimezone;
|
|
76
79
|
},
|
|
77
80
|
[item?.configuration?.time_zone]
|
|
78
81
|
);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { act, renderHook } from '@testing-library/react-hooks';
|
|
3
|
+
import { SCProvider } from '../../../context';
|
|
4
|
+
import { mockSCStore } from '../../../context/mockStore';
|
|
5
|
+
import { useStarredScript } from '../hooks/useStarredScript';
|
|
6
|
+
import MockAdapter from 'axios-mock-adapter';
|
|
7
|
+
import api from '../../../utils/Apis/axios';
|
|
8
|
+
import { API } from '../../../configs';
|
|
9
|
+
import { Action } from '../../../context/actionType';
|
|
10
|
+
|
|
11
|
+
const mockedSetAction = jest.fn();
|
|
12
|
+
const mock = new MockAdapter(api.axiosInstance);
|
|
13
|
+
|
|
14
|
+
const wrapper = ({ children }) => <SCProvider>{children}</SCProvider>;
|
|
15
|
+
|
|
16
|
+
const mockUseContext = jest.fn().mockImplementation(() => ({
|
|
17
|
+
stateData: mockSCStore({}),
|
|
18
|
+
setAction: mockedSetAction,
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
React.useContext = mockUseContext;
|
|
22
|
+
|
|
23
|
+
jest.mock('react-native-deep-linking');
|
|
24
|
+
|
|
25
|
+
describe('Test useStarredScript', async () => {
|
|
26
|
+
let props;
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
mockedSetAction.mockClear();
|
|
29
|
+
props = {
|
|
30
|
+
name: 'AutomateScript',
|
|
31
|
+
id: 1,
|
|
32
|
+
script: { id: 2, name: 'ScriptName' },
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('test send remote command action null', async () => {
|
|
37
|
+
mock.onPost(API.AUTOMATE.UNSTAR_SCRIPT(props.id)).reply(200);
|
|
38
|
+
const { result } = renderHook(() => useStarredScript(props), {
|
|
39
|
+
wrapper,
|
|
40
|
+
});
|
|
41
|
+
await act(async () => {
|
|
42
|
+
await result.current.unstarScript();
|
|
43
|
+
});
|
|
44
|
+
expect(mockedSetAction).toBeCalledWith(Action.UNSTAR_SCRIPTS, [2]);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -267,8 +267,8 @@ const SelectPermission = ({ route }) => {
|
|
|
267
267
|
arrIdReadTemp.length &&
|
|
268
268
|
read_permissions.push({ id: item.id, values: arrIdReadTemp });
|
|
269
269
|
|
|
270
|
-
!arrIdControlTemp &&
|
|
271
|
-
!arrIdReadTemp &&
|
|
270
|
+
!arrIdControlTemp.length &&
|
|
271
|
+
!arrIdReadTemp.length &&
|
|
272
272
|
item.isChecked &&
|
|
273
273
|
read_permissions.push({ id: item.id, values: [] });
|
|
274
274
|
}
|
|
@@ -19,6 +19,7 @@ import { useReceiveNotifications } from '../../../hooks';
|
|
|
19
19
|
import { SCProvider } from '../../../context';
|
|
20
20
|
import { mockSCStore } from '../../../context/mockStore';
|
|
21
21
|
import api from '../../../utils/Apis/axios';
|
|
22
|
+
import { WrapHeaderScrollable } from '../../../commons';
|
|
22
23
|
|
|
23
24
|
const mock = new MockAdapter(api.axiosInstance);
|
|
24
25
|
|
|
@@ -57,8 +58,7 @@ describe('Test UnitSummary', () => {
|
|
|
57
58
|
let route;
|
|
58
59
|
|
|
59
60
|
beforeEach(() => {
|
|
60
|
-
mock.
|
|
61
|
-
|
|
61
|
+
mock.reset();
|
|
62
62
|
Date.now = jest.fn(() => new Date('2021-01-24T12:00:00.000Z'));
|
|
63
63
|
route = {
|
|
64
64
|
params: {
|
|
@@ -144,6 +144,22 @@ describe('Test UnitSummary', () => {
|
|
|
144
144
|
expect(componentName.props.unit).toEqual({ id: 1 });
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
+
it('render fetchUnitSummary failure', async () => {
|
|
148
|
+
route.params.summaryData = null;
|
|
149
|
+
jest.useFakeTimers();
|
|
150
|
+
mock.onGet(API.UNIT.UNIT_SUMMARY_DETAIL(1, 1)).reply(200, { data: {} });
|
|
151
|
+
mock.onGet(API.UNIT.UNIT_SUMMARY(1)).reply(400);
|
|
152
|
+
await act(async () => {
|
|
153
|
+
tree = await create(wrapComponent(route));
|
|
154
|
+
});
|
|
155
|
+
await act(async () => {
|
|
156
|
+
jest.runOnlyPendingTimers();
|
|
157
|
+
});
|
|
158
|
+
const instance = tree.root;
|
|
159
|
+
const wapHeaderScrollable = instance.findByType(WrapHeaderScrollable);
|
|
160
|
+
expect(wapHeaderScrollable.props.children).toBe(null);
|
|
161
|
+
});
|
|
162
|
+
|
|
147
163
|
it('render fetchUnitDetail success', async () => {
|
|
148
164
|
route = {
|
|
149
165
|
params: {
|
|
@@ -275,4 +291,17 @@ describe('Test UnitSummary', () => {
|
|
|
275
291
|
}
|
|
276
292
|
});
|
|
277
293
|
});
|
|
294
|
+
|
|
295
|
+
it('Test render without params', async () => {
|
|
296
|
+
useReceiveNotifications.mockImplementationOnce(() => ({
|
|
297
|
+
dataNotification: {},
|
|
298
|
+
}));
|
|
299
|
+
await act(async () => {
|
|
300
|
+
tree = await create(wrapComponent(route));
|
|
301
|
+
});
|
|
302
|
+
const instance = tree.root;
|
|
303
|
+
const wapHeaderScrollable = instance.findByType(WrapHeaderScrollable);
|
|
304
|
+
expect(wapHeaderScrollable.props.title).toBe('');
|
|
305
|
+
expect(wapHeaderScrollable.props.subTitle).toBe(null);
|
|
306
|
+
});
|
|
278
307
|
});
|
|
@@ -1096,5 +1096,7 @@
|
|
|
1096
1096
|
"read_config_permission_error": "You don't have permission to read this config",
|
|
1097
1097
|
"continue_to_wait": "Continue to wait?",
|
|
1098
1098
|
"it_has_been_5_minutes": "It has been 5 minutes...",
|
|
1099
|
-
"click_here_to_setup_device": "Click here to setup device"
|
|
1099
|
+
"click_here_to_setup_device": "Click here to setup device",
|
|
1100
|
+
"photo_request_permission": "Photo request permission",
|
|
1101
|
+
"photo_request_permission_des": "To select photo from gallery, please unlock access photo permission"
|
|
1100
1102
|
}
|
|
@@ -1094,5 +1094,7 @@
|
|
|
1094
1094
|
"read_config_permission_error": "Bạn không có quyền đọc cấu hình này",
|
|
1095
1095
|
"continue_to_wait": "Bạn có muốn đợi tiếp?",
|
|
1096
1096
|
"it_has_been_5_minutes": "Đã 5 phút trôi qua...",
|
|
1097
|
-
"click_here_to_setup_device": "Chọn thiết bị thêm vào phòng"
|
|
1097
|
+
"click_here_to_setup_device": "Chọn thiết bị thêm vào phòng",
|
|
1098
|
+
"photo_request_permission": "Quyền yêu cầu ảnh",
|
|
1099
|
+
"photo_request_permission_des": "Để chọn ảnh từ thư viện, vui lòng mở khóa quyền truy cập ảnh"
|
|
1098
1100
|
}
|