@eohjsc/react-native-smart-city 0.3.85 → 0.3.87
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 +3 -2
- package/src/Images/Common/logo.png +0 -0
- package/src/Images/Common/logo@2x.png +0 -0
- package/src/Images/Common/logo@3x.png +0 -0
- package/src/Images/Common/unit_default_background.png +0 -0
- package/src/Images/Common/unit_default_background@2x.png +0 -0
- package/src/Images/Common/unit_default_background@3x.png +0 -0
- package/src/commons/ActionGroup/OnOffSmartLock/AutoLock/index.js +1 -1
- package/src/commons/ActionGroup/SliderRangeTemplate.js +7 -13
- package/src/commons/ActionGroup/SliderRangeTemplateStyles.js +0 -1
- package/src/commons/ActionGroup/__test__/SliderRangeTemplate.test.js +4 -4
- package/src/commons/ActionGroup/__test__/TwoButtonTemplate.test.js +30 -0
- package/src/commons/ActionGroup/__test__/index.test.js +1 -1
- package/src/commons/Automate/__test__/ItemAutomate.test.js +25 -3
- package/src/commons/{FourButtonFilterHistory → ChartAggregationOption}/__test__/FourButtonFilterHistory.test.js +2 -2
- package/src/commons/ChartAggregationOption/index.js +72 -0
- package/src/commons/{FourButtonFilterHistory → ChartAggregationOption}/styles.js +0 -0
- package/src/commons/Dashboard/MyPinnedSharedUnit/__test__/MyPinnedSharedUnit.test.js +19 -0
- package/src/commons/Dashboard/MyPinnedSharedUnit/index.js +30 -9
- package/src/commons/Dashboard/MyUnit/__test__/MyUnit.test.js +49 -0
- package/src/commons/Dashboard/MyUnit/index.js +48 -16
- package/src/commons/DateTimeRangeChange/DateTimeButton.js +3 -12
- package/src/commons/DateTimeRangeChange/index.js +113 -19
- package/src/commons/Device/HistoryChart.js +2 -2
- package/src/commons/Device/LinearChart.js +2 -2
- package/src/commons/Device/ProgressBar/__test__/ProgressBar.test.js +1 -1
- package/src/commons/Device/ProgressBar/index.js +4 -2
- package/src/commons/Device/ProgressBar/styles.js +1 -0
- package/src/commons/Device/SonosSpeaker/__test__/SonosSpeaker.test.js +1 -1
- package/src/commons/Device/SonosSpeaker/index.js +1 -1
- package/src/commons/Device/WindSpeed/Anemometer/index.js +101 -14
- package/src/commons/Device/WindSpeed/__test__/Anemometer.test.js +89 -2
- package/src/commons/ModalPopupCT/index.js +21 -2
- package/src/commons/ModalPopupCT/styles.js +7 -0
- package/src/commons/UnitSummary/ConfigHistoryChart/__test__/ConfigHistoryChart.test.js +7 -7
- package/src/commons/UnitSummary/ConfigHistoryChart/index.js +40 -13
- package/src/commons/WrapParallaxScrollView/index.js +5 -4
- package/src/configs/API.js +4 -3
- package/src/configs/AccessibilityLabel.js +7 -0
- package/src/configs/Constants.js +1 -0
- package/src/configs/Images.js +1 -0
- package/src/context/actionType.ts +5 -0
- package/src/context/mockStore.ts +1 -0
- package/src/context/reducer.ts +29 -0
- package/src/hooks/Common/useTranslations.ts +1 -1
- package/src/hooks/IoT/useRemoteControl.js +0 -1
- package/src/screens/ActivityLog/__test__/FilterPopup.test.js +1 -1
- package/src/screens/ActivityLog/__test__/index.test.js +1 -1
- package/src/screens/AddNewAction/__test__/LoadingSelectAction.test.js +16 -0
- package/src/screens/AddNewGateway/__test__/ScanModbusQR.test.js +15 -0
- package/src/screens/AllCamera/__test__/index.test.js +1 -1
- package/src/screens/AllCamera/index.js +2 -2
- package/src/screens/AllGateway/DetailConfigActionInternal/__test__/index.test.js +179 -111
- package/src/screens/AllGateway/DetailConfigActionInternal/index.js +48 -50
- package/src/screens/AllGateway/DeviceInternalDetail/__test__/index.test.js +4 -0
- package/src/screens/AllGateway/DeviceInternalDetail/index.js +14 -1
- package/src/screens/AllGateway/GatewayConnectionMethods/__test__/index.test.js +25 -0
- package/src/screens/AllGateway/GatewayConnectionMethods/index.js +59 -35
- package/src/screens/AllGateway/GatewayInfo/__test__/index.test.js +14 -4
- package/src/screens/AllGateway/GatewayInfo/index.js +21 -2
- package/src/screens/AllGateway/components/Detail/__test__/index.test.js +28 -1
- package/src/screens/AllGateway/components/Detail/index.js +8 -2
- package/src/screens/AllGateway/components/Information/index.js +4 -1
- package/src/screens/AllGateway/components/TabPaneCT/__test__/index.test.js +1 -1
- package/src/screens/AllGateway/components/TabPaneCT/index.js +1 -1
- package/src/screens/AllGateway/components/TabPaneCT/styles.js +1 -1
- package/src/screens/ConfirmUnitDeletion/index.js +6 -2
- package/src/screens/Device/__test__/DetailHistoryChart.test.js +1 -0
- package/src/screens/Device/__test__/sensorDisplayItem.test.js +150 -2
- package/src/screens/Device/components/ChartWrapper.js +39 -0
- package/src/screens/Device/components/ChartWrapperStyles.js +42 -0
- package/src/screens/Device/components/SensorDisplayItem.js +6 -2
- package/src/screens/Device/components/VisualChart.js +255 -0
- package/src/screens/Device/components/__test__/VisualChart.test.js +440 -0
- package/src/screens/EmergencyContacts/__test__/EmergencyContactsSelectContacts.test.js +7 -6
- package/src/screens/SmartAccount/SuccessfullyConnected/__test__/SuccessfullyConnected.test.js +3 -0
- package/src/screens/SmartIr/__test__/GroupButtonByType.test.js +47 -0
- package/src/screens/SmartIr/components/GroupButtonByType/GroupButtonByType.js +15 -2
- package/src/screens/SyncLGDevice/__test__/AddLGDevice.test.js +52 -0
- package/src/screens/Unit/ManageUnit.js +7 -3
- package/src/screens/Unit/components/__test__/Header.test.js +9 -0
- package/src/utils/I18n/translations/en.json +4 -1
- package/src/utils/I18n/translations/vi.json +4 -1
- package/src/utils/Utils.js +6 -0
- package/src/commons/FourButtonFilterHistory/index.js +0 -72
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.87",
|
|
5
5
|
"description": "TODO",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"files": [
|
|
@@ -108,6 +108,7 @@
|
|
|
108
108
|
"@formatjs/intl-pluralrules": "^3.4.7",
|
|
109
109
|
"@invertase/react-native-apple-authentication": "^1.1.2",
|
|
110
110
|
"@messageformat/core": "^3.0.0",
|
|
111
|
+
"@miblanchard/react-native-slider": "^2.2.0",
|
|
111
112
|
"@react-native-clipboard/clipboard": "^1.11.1",
|
|
112
113
|
"@react-native-community/async-storage": "^1.12.1",
|
|
113
114
|
"@react-native-community/cameraroll": "^4.0.0",
|
|
@@ -179,6 +180,7 @@
|
|
|
179
180
|
"react-native-maps-directions": "^1.8.0",
|
|
180
181
|
"react-native-modal": "^11.5.6",
|
|
181
182
|
"react-native-modal-datetime-picker": "^8.9.3",
|
|
183
|
+
"react-native-new-snap-carousel": "^3.9.3",
|
|
182
184
|
"react-native-onesignal": "^4.3.1",
|
|
183
185
|
"react-native-pager-view": "^5.4.1",
|
|
184
186
|
"react-native-parallax-scroll-view": "^0.21.3",
|
|
@@ -190,7 +192,6 @@
|
|
|
190
192
|
"react-native-responsive-fontsize": "^0.5.1",
|
|
191
193
|
"react-native-safe-area-context": "^3.1.1",
|
|
192
194
|
"react-native-screens": "^2.9.0",
|
|
193
|
-
"react-native-snap-carousel": "4.0.0-beta.5",
|
|
194
195
|
"react-native-super-grid": "^4.0.3",
|
|
195
196
|
"react-native-svg": "^12.1.0",
|
|
196
197
|
"react-native-toast-message": "^2.1.1",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { memo, useCallback, useState } from 'react';
|
|
2
2
|
import { View } from 'react-native';
|
|
3
|
-
import Slider from '@react-native-
|
|
3
|
+
import { Slider } from '@miblanchard/react-native-slider';
|
|
4
4
|
import Text from '../../../Text';
|
|
5
5
|
import { HeaderCustom } from '../../../Header';
|
|
6
6
|
import ButtonWrapper from './ButtonWrapper';
|
|
@@ -5,7 +5,7 @@ import styles from './SliderRangeTemplateStyles';
|
|
|
5
5
|
import Text from '../Text';
|
|
6
6
|
import { Colors } from '../../configs';
|
|
7
7
|
import SvgBrightness from '../../../assets/images/brightness.svg';
|
|
8
|
-
import Slider from '@react-native-
|
|
8
|
+
import { Slider } from '@miblanchard/react-native-slider';
|
|
9
9
|
import { useConfigGlobalState } from '../../iot/states';
|
|
10
10
|
import { DEVICE_TYPE } from '../../configs/Constants';
|
|
11
11
|
|
|
@@ -17,14 +17,8 @@ const SliderRangeTemplate = memo(({ actionGroup = {}, doAction, sensor }) => {
|
|
|
17
17
|
const { config = undefined } = configuration;
|
|
18
18
|
|
|
19
19
|
const getPercent = useCallback(() => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const { value = 0 } = configValue;
|
|
23
|
-
if (value) {
|
|
24
|
-
return value;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return 0;
|
|
20
|
+
const { value } = configValues[config] || { value: 0 };
|
|
21
|
+
return value;
|
|
28
22
|
}, [config, configValues]);
|
|
29
23
|
|
|
30
24
|
const [valueBrightness, setValueBrightness] = useState(getPercent());
|
|
@@ -32,9 +26,10 @@ const SliderRangeTemplate = memo(({ actionGroup = {}, doAction, sensor }) => {
|
|
|
32
26
|
|
|
33
27
|
const onChangeBrightness = useCallback(
|
|
34
28
|
async (value) => {
|
|
29
|
+
const value_brness = value[0];
|
|
35
30
|
await doAction(
|
|
36
31
|
configuration?.action_data,
|
|
37
|
-
JSON.stringify({ value_brness:
|
|
32
|
+
JSON.stringify({ value_brness: value_brness, value: value_brness })
|
|
38
33
|
);
|
|
39
34
|
},
|
|
40
35
|
[configuration?.action_data, doAction]
|
|
@@ -45,8 +40,7 @@ const SliderRangeTemplate = memo(({ actionGroup = {}, doAction, sensor }) => {
|
|
|
45
40
|
}, [valueBrightness]);
|
|
46
41
|
|
|
47
42
|
useEffect(() => {
|
|
48
|
-
const
|
|
49
|
-
const { value = 0 } = configValue;
|
|
43
|
+
const { value } = configValues[config] || { value: 0 };
|
|
50
44
|
if (value && isFirstTime) {
|
|
51
45
|
setValueBrightness(value);
|
|
52
46
|
setIsFirstTime(false);
|
|
@@ -71,7 +65,7 @@ const SliderRangeTemplate = memo(({ actionGroup = {}, doAction, sensor }) => {
|
|
|
71
65
|
</View>
|
|
72
66
|
<View style={styles.RightBrightness}>
|
|
73
67
|
<Slider
|
|
74
|
-
|
|
68
|
+
containerStyle={styles.slider}
|
|
75
69
|
value={valueBrightness}
|
|
76
70
|
onSlidingComplete={onChangeBrightness}
|
|
77
71
|
onValueChange={setValueBrightness}
|
|
@@ -5,7 +5,7 @@ import SliderRangeTemplate from '../SliderRangeTemplate';
|
|
|
5
5
|
import { SCProvider } from '../../../context';
|
|
6
6
|
import { mockSCStore } from '../../../context/mockStore';
|
|
7
7
|
import { useConfigGlobalState } from '../../../iot/states';
|
|
8
|
-
import
|
|
8
|
+
import { Slider } from '@miblanchard/react-native-slider';
|
|
9
9
|
|
|
10
10
|
jest.mock('../../../iot/Monitor');
|
|
11
11
|
const mockDoAction = jest.fn();
|
|
@@ -55,10 +55,10 @@ describe('Test SliderRangeTemplate', () => {
|
|
|
55
55
|
wrapper = await create(wrapComponent(actionGroup, mockDoAction, sensor));
|
|
56
56
|
});
|
|
57
57
|
const instance = wrapper.root;
|
|
58
|
-
const silderRange = instance.findAllByType(
|
|
58
|
+
const silderRange = instance.findAllByType(Slider);
|
|
59
59
|
expect(silderRange).toHaveLength(1);
|
|
60
60
|
await act(async () => {
|
|
61
|
-
await silderRange[0].props.onSlidingComplete(50);
|
|
61
|
+
await silderRange[0].props.onSlidingComplete([50]);
|
|
62
62
|
});
|
|
63
63
|
expect(mockDoAction).toHaveBeenCalledWith(
|
|
64
64
|
action_data,
|
|
@@ -80,7 +80,7 @@ describe('Test SliderRangeTemplate', () => {
|
|
|
80
80
|
wrapper = await create(wrapComponent(actionGroup, mockDoAction, sensor));
|
|
81
81
|
});
|
|
82
82
|
const instance = wrapper.root;
|
|
83
|
-
const silderRange = instance.findByType(
|
|
83
|
+
const silderRange = instance.findByType(Slider);
|
|
84
84
|
expect(silderRange.props.value).toBe(50);
|
|
85
85
|
});
|
|
86
86
|
});
|
|
@@ -172,4 +172,34 @@ describe('Test TwoButtonTemplate', () => {
|
|
|
172
172
|
const touchableOpacities = instance.findAllByType(TouchableOpacity);
|
|
173
173
|
expect(touchableOpacities.length).toEqual(2);
|
|
174
174
|
});
|
|
175
|
+
|
|
176
|
+
it('render data configuration case button1 on value is null', async () => {
|
|
177
|
+
const mockDoAction = jest.fn();
|
|
178
|
+
let new_actionGroup = {
|
|
179
|
+
...actionGroup,
|
|
180
|
+
configuration: {
|
|
181
|
+
...actionGroup.configuration,
|
|
182
|
+
button1: {
|
|
183
|
+
...actionGroup.configuration.button1,
|
|
184
|
+
is_on_value: null,
|
|
185
|
+
},
|
|
186
|
+
button2: {
|
|
187
|
+
...actionGroup.configuration.button2,
|
|
188
|
+
is_on_value: [true],
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
await act(async () => {
|
|
193
|
+
wrapper = create(
|
|
194
|
+
<TwoButtonTemplate
|
|
195
|
+
actionGroup={new_actionGroup}
|
|
196
|
+
doAction={mockDoAction}
|
|
197
|
+
sensor={sensor}
|
|
198
|
+
/>
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
const instance = wrapper.root;
|
|
202
|
+
const touchableOpacities = instance.findAllByType(TouchableOpacity);
|
|
203
|
+
expect(touchableOpacities.length).toEqual(2);
|
|
204
|
+
});
|
|
175
205
|
});
|
|
@@ -14,7 +14,7 @@ import Text from '../../Text';
|
|
|
14
14
|
import { AccessibilityLabel } from '../../../configs/Constants';
|
|
15
15
|
import { SCProvider } from '../../../context';
|
|
16
16
|
import { mockSCStore } from '../../../context/mockStore';
|
|
17
|
-
import Slider from '@react-native-
|
|
17
|
+
import { Slider } from '@miblanchard/react-native-slider';
|
|
18
18
|
import { WheelColorPicker } from '../ColorPickerTemplate';
|
|
19
19
|
import RadioCircle from '../../RadioCircle';
|
|
20
20
|
|
|
@@ -1,24 +1,46 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { act, create } from 'react-test-renderer';
|
|
3
|
+
import { TouchableOpacity } from 'react-native';
|
|
4
|
+
|
|
3
5
|
import { SCProvider } from '../../../context';
|
|
4
6
|
import { mockSCStore } from '../../../context/mockStore';
|
|
5
7
|
import Text from '../../Text';
|
|
6
8
|
import ItemAutomate from '../ItemAutomate';
|
|
7
9
|
|
|
8
|
-
const wrapComponent = (
|
|
10
|
+
const wrapComponent = (rest) => (
|
|
9
11
|
<SCProvider initState={mockSCStore({})}>
|
|
10
|
-
<ItemAutomate
|
|
12
|
+
<ItemAutomate {...rest} />
|
|
11
13
|
</SCProvider>
|
|
12
14
|
);
|
|
13
15
|
|
|
14
16
|
describe('Test ItemAutomate', () => {
|
|
15
17
|
let tree;
|
|
18
|
+
let defaultProps = {
|
|
19
|
+
type: 'one_tap',
|
|
20
|
+
};
|
|
16
21
|
it('Test render', async () => {
|
|
17
22
|
await act(async () => {
|
|
18
|
-
tree = await create(wrapComponent());
|
|
23
|
+
tree = await create(wrapComponent(defaultProps));
|
|
19
24
|
});
|
|
20
25
|
const instance = tree.root;
|
|
21
26
|
const texts = instance.findAllByType(Text);
|
|
22
27
|
expect(texts[0].props.children).toEqual('Launch One-Tap');
|
|
23
28
|
});
|
|
29
|
+
|
|
30
|
+
it('Test render TouchableOpacity', async () => {
|
|
31
|
+
const mockOnPress = jest.fn();
|
|
32
|
+
defaultProps = {
|
|
33
|
+
type: 'one_tap',
|
|
34
|
+
onPress: mockOnPress,
|
|
35
|
+
};
|
|
36
|
+
await act(async () => {
|
|
37
|
+
tree = await create(wrapComponent(defaultProps));
|
|
38
|
+
});
|
|
39
|
+
const instance = tree.root;
|
|
40
|
+
const touchableOpacities = instance.findAllByType(TouchableOpacity);
|
|
41
|
+
await act(async () => {
|
|
42
|
+
touchableOpacities[0].props.onPress();
|
|
43
|
+
});
|
|
44
|
+
expect(mockOnPress).toBeCalledTimes(1);
|
|
45
|
+
});
|
|
24
46
|
});
|
|
@@ -2,12 +2,12 @@ import React from 'react';
|
|
|
2
2
|
import { act, create } from 'react-test-renderer';
|
|
3
3
|
import { SCProvider } from '../../../context';
|
|
4
4
|
import { mockSCStore } from '../../../context/mockStore';
|
|
5
|
-
import
|
|
5
|
+
import ChartAggregationOption from '../index';
|
|
6
6
|
import { TouchableOpacity } from 'react-native';
|
|
7
7
|
|
|
8
8
|
const wrapComponent = (props) => (
|
|
9
9
|
<SCProvider initState={mockSCStore({})}>
|
|
10
|
-
<
|
|
10
|
+
<ChartAggregationOption {...props} />
|
|
11
11
|
</SCProvider>
|
|
12
12
|
);
|
|
13
13
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
import { View, TouchableOpacity } from 'react-native';
|
|
3
|
+
import Text from '../Text';
|
|
4
|
+
import { Colors } from '../../configs';
|
|
5
|
+
import styles from './styles';
|
|
6
|
+
|
|
7
|
+
const defaultOptions = [
|
|
8
|
+
{
|
|
9
|
+
title: 'D',
|
|
10
|
+
data: 'date',
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
title: 'W',
|
|
15
|
+
data: 'week',
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
{
|
|
19
|
+
title: 'M',
|
|
20
|
+
data: 'month',
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
{
|
|
24
|
+
title: 'Y',
|
|
25
|
+
data: 'year',
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const ItemButton = ({ title, onPress, isSelected }) => {
|
|
30
|
+
return (
|
|
31
|
+
<TouchableOpacity
|
|
32
|
+
onPress={onPress}
|
|
33
|
+
style={[styles.button, isSelected && styles.selectedButton]}
|
|
34
|
+
>
|
|
35
|
+
<View>
|
|
36
|
+
<Text bold color={isSelected && Colors.Primary}>
|
|
37
|
+
{title}
|
|
38
|
+
</Text>
|
|
39
|
+
</View>
|
|
40
|
+
</TouchableOpacity>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const ChartAggregationOption = ({
|
|
45
|
+
groupBy,
|
|
46
|
+
setGroupBy,
|
|
47
|
+
options = defaultOptions,
|
|
48
|
+
}) => {
|
|
49
|
+
const onPressButton = useCallback(
|
|
50
|
+
(data) => () => {
|
|
51
|
+
setGroupBy && setGroupBy(data);
|
|
52
|
+
},
|
|
53
|
+
[setGroupBy]
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<View style={styles.row}>
|
|
58
|
+
{options.map((item, index) => {
|
|
59
|
+
return (
|
|
60
|
+
<ItemButton
|
|
61
|
+
key={index}
|
|
62
|
+
title={item.title}
|
|
63
|
+
onPress={onPressButton(item.data)}
|
|
64
|
+
isSelected={item.data === groupBy}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
})}
|
|
68
|
+
</View>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export default ChartAggregationOption;
|
|
File without changes
|
|
@@ -12,10 +12,13 @@ import { API } from '../../../../configs';
|
|
|
12
12
|
import { TouchableOpacity } from 'react-native';
|
|
13
13
|
import api from '../../../../utils/Apis/axios';
|
|
14
14
|
import SharedUnit from '../../../Unit/SharedUnit';
|
|
15
|
+
import { Action } from '../../../../context/actionType';
|
|
15
16
|
|
|
16
17
|
const mock = new MockAdapter(api.axiosInstance);
|
|
17
18
|
|
|
18
19
|
const mockedNavigate = jest.fn();
|
|
20
|
+
const mockSetAction = jest.fn();
|
|
21
|
+
|
|
19
22
|
jest.mock('@react-navigation/native', () => {
|
|
20
23
|
return {
|
|
21
24
|
...jest.requireActual('@react-navigation/native'),
|
|
@@ -26,6 +29,18 @@ jest.mock('@react-navigation/native', () => {
|
|
|
26
29
|
};
|
|
27
30
|
});
|
|
28
31
|
|
|
32
|
+
jest.mock('react', () => {
|
|
33
|
+
return {
|
|
34
|
+
...jest.requireActual('react'),
|
|
35
|
+
useContext: () => ({
|
|
36
|
+
stateData: mockSCStore({
|
|
37
|
+
app: { isNeedUpdateCache: true },
|
|
38
|
+
}),
|
|
39
|
+
setAction: mockSetAction,
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
|
|
29
44
|
const wrapComponent = (route) => (
|
|
30
45
|
<SCProvider initState={mockSCStore({})}>
|
|
31
46
|
<MyPinnedSharedUnit />
|
|
@@ -60,6 +75,10 @@ describe('Test MyPinnedSharedUnit', () => {
|
|
|
60
75
|
goToAllSharedUnits.props.onPress();
|
|
61
76
|
});
|
|
62
77
|
expect(mockedNavigate).toHaveBeenCalled();
|
|
78
|
+
expect(mockSetAction).toBeCalledWith(
|
|
79
|
+
Action.IS_CHECK_CLEAR_CACHE_UNITS,
|
|
80
|
+
false
|
|
81
|
+
);
|
|
63
82
|
});
|
|
64
83
|
|
|
65
84
|
it('render without item', async () => {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useState,
|
|
5
|
+
memo,
|
|
6
|
+
useContext,
|
|
7
|
+
} from 'react';
|
|
2
8
|
import { TouchableOpacity, View } from 'react-native';
|
|
3
9
|
|
|
4
10
|
import styles from './styles';
|
|
@@ -10,15 +16,22 @@ import Routes from '../../../utils/Route';
|
|
|
10
16
|
import { AccessibilityLabel } from '../../../configs/Constants';
|
|
11
17
|
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
12
18
|
import SharedUnit from '../../Unit/SharedUnit';
|
|
13
|
-
import { fetchWithCache } from '../../../utils/Apis/axios';
|
|
19
|
+
import { axiosGet, fetchWithCache } from '../../../utils/Apis/axios';
|
|
14
20
|
import { preloadImagesFromUnits } from '../../../utils/Functions/preloadImages';
|
|
15
21
|
import { STORAGE_KEY } from '../../../utils/Storage';
|
|
22
|
+
import { SCContext, useSCContextSelector } from '../../../context';
|
|
23
|
+
import { Action } from '../../../context/actionType';
|
|
16
24
|
|
|
17
25
|
const MyPinnedSharedUnit = ({ refreshing }) => {
|
|
18
26
|
const t = useTranslations();
|
|
19
27
|
const isFocused = useIsFocused();
|
|
20
28
|
const navigation = useNavigation();
|
|
21
29
|
const [sharedUnits, setSharedUnits] = useState([]);
|
|
30
|
+
const { setAction } = useContext(SCContext);
|
|
31
|
+
|
|
32
|
+
const isNeedUpdateCache = useSCContextSelector(
|
|
33
|
+
(state) => state.app.isNeedUpdateCache
|
|
34
|
+
);
|
|
22
35
|
|
|
23
36
|
const goToAllSharedUnits = () => {
|
|
24
37
|
navigation.navigate(Routes.SharedStack, {
|
|
@@ -27,13 +40,21 @@ const MyPinnedSharedUnit = ({ refreshing }) => {
|
|
|
27
40
|
};
|
|
28
41
|
|
|
29
42
|
const fetchSharedUnitDashboard = useCallback(async () => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
if (isNeedUpdateCache) {
|
|
44
|
+
setAction(Action.IS_CHECK_CLEAR_CACHE_UNITS, false);
|
|
45
|
+
const { success, data } = await axiosGet(
|
|
46
|
+
API.UNIT.SHARED_UNITS(),
|
|
47
|
+
{},
|
|
48
|
+
true
|
|
49
|
+
);
|
|
50
|
+
success && setSharedUnits(data);
|
|
51
|
+
} else {
|
|
52
|
+
await fetchWithCache(API.UNIT.SHARED_UNITS(), {}, (response) => {
|
|
53
|
+
const { success, data } = response;
|
|
54
|
+
success && setSharedUnits(data);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
37
58
|
}, [setSharedUnits]);
|
|
38
59
|
|
|
39
60
|
useEffect(() => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import renderer, { act } from 'react-test-renderer';
|
|
3
3
|
import MockAdapter from 'axios-mock-adapter';
|
|
4
|
+
|
|
4
5
|
import MyUnit from '..';
|
|
5
6
|
import MyUnitDevice from '../../../../screens/Unit/components/MyUnitDevice';
|
|
6
7
|
import { AccessibilityLabel } from '../../../../configs/Constants';
|
|
@@ -8,11 +9,34 @@ import { SCProvider } from '../../../../context';
|
|
|
8
9
|
import { mockSCStore } from '../../../../context/mockStore';
|
|
9
10
|
import api from '../../../../utils/Apis/axios';
|
|
10
11
|
import { API } from '../../../../configs';
|
|
12
|
+
import Routes from '../../../../utils/Route';
|
|
13
|
+
import { Action } from '../../../../context/actionType';
|
|
11
14
|
|
|
12
15
|
const mock = new MockAdapter(api.axiosInstance);
|
|
13
16
|
|
|
14
17
|
const mockedNavigate = jest.fn();
|
|
15
18
|
const mockedDispatch = jest.fn();
|
|
19
|
+
const mockedSetAction = jest.fn();
|
|
20
|
+
|
|
21
|
+
const mockUseContext = jest.fn().mockImplementation(() => ({
|
|
22
|
+
stateData: mockSCStore({}),
|
|
23
|
+
setAction: mockedSetAction,
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
React.useContext = mockUseContext;
|
|
27
|
+
|
|
28
|
+
const mockSetAction = jest.fn();
|
|
29
|
+
jest.mock('react', () => {
|
|
30
|
+
return {
|
|
31
|
+
...jest.requireActual('react'),
|
|
32
|
+
useContext: () => ({
|
|
33
|
+
stateData: mockSCStore({
|
|
34
|
+
app: { isDeleteUnitSuccessFully: true, isNeedUpdateCache: true },
|
|
35
|
+
}),
|
|
36
|
+
setAction: mockSetAction,
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
});
|
|
16
40
|
|
|
17
41
|
jest.mock('@react-navigation/native', () => {
|
|
18
42
|
return {
|
|
@@ -104,5 +128,30 @@ describe('Test MyUnit', () => {
|
|
|
104
128
|
const instance = tree.root;
|
|
105
129
|
const devices = instance.findAllByType(MyUnitDevice);
|
|
106
130
|
expect(devices).toHaveLength(2);
|
|
131
|
+
|
|
132
|
+
const button = instance.findByProps({
|
|
133
|
+
accessibilityLabel: `${AccessibilityLabel.MY_UNIT_GO_TO_DETAIL}-0`,
|
|
134
|
+
});
|
|
135
|
+
await act(async () => {
|
|
136
|
+
await button.props.onPress(data[0]);
|
|
137
|
+
});
|
|
138
|
+
expect(mockedNavigate).toBeCalledWith(Routes.UnitStack, {
|
|
139
|
+
params: { unitId: 1 },
|
|
140
|
+
screen: Routes.UnitDetail,
|
|
141
|
+
});
|
|
142
|
+
expect(mockSetAction).toBeCalledWith(
|
|
143
|
+
Action.IS_CHECK_CLEAR_CACHE_UNITS,
|
|
144
|
+
false
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('Test isDeleteUnitSuccessFully', async () => {
|
|
149
|
+
jest.useFakeTimers();
|
|
150
|
+
mock.onGet(API.UNIT.MY_UNITS()).replyOnce(200, data);
|
|
151
|
+
await act(async () => {
|
|
152
|
+
tree = await renderer.create(wrapComponent());
|
|
153
|
+
});
|
|
154
|
+
jest.runAllTimers();
|
|
155
|
+
expect(mockSetAction).toBeCalledWith(Action.RESET_DELETE_UNIT_ACTION);
|
|
107
156
|
});
|
|
108
157
|
});
|
|
@@ -5,8 +5,9 @@ import React, {
|
|
|
5
5
|
useState,
|
|
6
6
|
useContext,
|
|
7
7
|
useMemo,
|
|
8
|
+
useRef,
|
|
8
9
|
} from 'react';
|
|
9
|
-
import { View,
|
|
10
|
+
import { View, TouchableOpacity, Dimensions } from 'react-native';
|
|
10
11
|
import {
|
|
11
12
|
useNavigation,
|
|
12
13
|
useIsFocused,
|
|
@@ -14,35 +15,44 @@ import {
|
|
|
14
15
|
} from '@react-navigation/native';
|
|
15
16
|
import NetInfo from '@react-native-community/netinfo';
|
|
16
17
|
import { BleManager } from 'react-native-ble-plx';
|
|
17
|
-
import Carousel from 'react-native-snap-carousel';
|
|
18
|
-
|
|
19
18
|
import { API, Colors, Images } from '../../../configs';
|
|
20
19
|
import Text from '../../Text';
|
|
21
|
-
import { fetchWithCache } from '../../../utils/Apis/axios';
|
|
20
|
+
import { axiosGet, fetchWithCache } from '../../../utils/Apis/axios';
|
|
21
|
+
|
|
22
22
|
import styles from './styles';
|
|
23
23
|
import { Section } from '../../Section';
|
|
24
24
|
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
25
25
|
import { useUnitConnectRemoteDevices } from '../../../screens/Unit/hook/useUnitConnectRemoteDevices';
|
|
26
26
|
import { useWatchConfigs, useBluetoothConnection } from '../../../hooks/IoT';
|
|
27
|
-
import { SCContext } from '../../../context';
|
|
27
|
+
import { SCContext, useSCContextSelector } from '../../../context';
|
|
28
28
|
import { Action } from '../../../context/actionType';
|
|
29
|
+
|
|
30
|
+
import Carousel from 'react-native-new-snap-carousel';
|
|
29
31
|
import { AccessibilityLabel, DEVICE_TYPE } from '../../../configs/Constants';
|
|
30
32
|
import Routes from '../../../utils/Route';
|
|
31
33
|
import MyUnitDevice from '../../../screens/Unit/components/MyUnitDevice';
|
|
32
34
|
import { STORAGE_KEY } from '../../../utils/Storage';
|
|
33
35
|
import { preloadImagesFromUnits } from '../../../utils/Functions/preloadImages';
|
|
36
|
+
import FImage from '../../FImage';
|
|
34
37
|
|
|
35
38
|
let screenWidth = Dimensions.get('window').width;
|
|
36
39
|
|
|
37
40
|
const bleManager = new BleManager();
|
|
38
41
|
|
|
39
42
|
const MyUnit = ({ refreshing }) => {
|
|
43
|
+
const carouselRef = useRef();
|
|
40
44
|
const t = useTranslations();
|
|
41
45
|
const isFocused = useIsFocused();
|
|
42
46
|
const navigation = useNavigation();
|
|
43
47
|
const [myUnits, setMyUnits] = useState([]);
|
|
44
48
|
const [slideIndex, setSlideIndex] = useState(0);
|
|
45
49
|
const { setAction } = useContext(SCContext);
|
|
50
|
+
const isDeleteUnitSuccessFully = useSCContextSelector(
|
|
51
|
+
(state) => state.app.isDeleteUnitSuccessFully
|
|
52
|
+
);
|
|
53
|
+
const isNeedUpdateCache = useSCContextSelector(
|
|
54
|
+
(state) => state.app.isNeedUpdateCache
|
|
55
|
+
);
|
|
46
56
|
|
|
47
57
|
const {
|
|
48
58
|
permissionsRequested: bluetoothPermRequested,
|
|
@@ -54,17 +64,18 @@ const MyUnit = ({ refreshing }) => {
|
|
|
54
64
|
}, [bluetoothPermRequested, requestBluetoothPerm]);
|
|
55
65
|
|
|
56
66
|
const fetchMyUnitDashboard = useCallback(async () => {
|
|
57
|
-
|
|
58
|
-
|
|
67
|
+
if (isNeedUpdateCache) {
|
|
68
|
+
setAction(Action.IS_CHECK_CLEAR_CACHE_UNITS, false);
|
|
69
|
+
const { success, data } = await axiosGet(API.UNIT.MY_UNITS(), {}, true);
|
|
59
70
|
success && setMyUnits(data);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
fetchMyUnitDashboard();
|
|
71
|
+
} else {
|
|
72
|
+
await fetchWithCache(API.UNIT.MY_UNITS(), {}, (response) => {
|
|
73
|
+
const { success, data } = response;
|
|
74
|
+
success && setMyUnits(data);
|
|
75
|
+
});
|
|
66
76
|
}
|
|
67
|
-
|
|
77
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
78
|
+
}, [setMyUnits]);
|
|
68
79
|
|
|
69
80
|
useFocusEffect(
|
|
70
81
|
useCallback(() => {
|
|
@@ -136,9 +147,13 @@ const MyUnit = ({ refreshing }) => {
|
|
|
136
147
|
accessibilityLabel={`${AccessibilityLabel.MY_UNIT_GO_TO_DETAIL}-${index}`}
|
|
137
148
|
>
|
|
138
149
|
<View style={styles.overlay} />
|
|
139
|
-
<
|
|
150
|
+
<FImage
|
|
140
151
|
style={styles.bgMyUnit}
|
|
141
|
-
source={
|
|
152
|
+
source={
|
|
153
|
+
item?.background
|
|
154
|
+
? { uri: item.background }
|
|
155
|
+
: Images.unitDefaultBackground
|
|
156
|
+
}
|
|
142
157
|
defaultSource={Images.BgUnit}
|
|
143
158
|
resizeMode="cover"
|
|
144
159
|
/>
|
|
@@ -158,6 +173,22 @@ const MyUnit = ({ refreshing }) => {
|
|
|
158
173
|
preloadImagesFromUnits(myUnits, STORAGE_KEY.IS_FIRST_TIME_LOAD_MY_UNITS);
|
|
159
174
|
}, [myUnits]);
|
|
160
175
|
|
|
176
|
+
useEffect(() => {
|
|
177
|
+
if (isFocused || refreshing) {
|
|
178
|
+
fetchMyUnitDashboard();
|
|
179
|
+
}
|
|
180
|
+
}, [fetchMyUnitDashboard, isFocused, refreshing]);
|
|
181
|
+
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
if (isDeleteUnitSuccessFully) {
|
|
184
|
+
const to = setTimeout(() => {
|
|
185
|
+
carouselRef?.current?.snapToItem();
|
|
186
|
+
setAction(Action.RESET_DELETE_UNIT_ACTION);
|
|
187
|
+
}, 60);
|
|
188
|
+
return () => clearTimeout(to);
|
|
189
|
+
}
|
|
190
|
+
}, [isDeleteUnitSuccessFully, setAction]);
|
|
191
|
+
|
|
161
192
|
return (
|
|
162
193
|
<>
|
|
163
194
|
<Section style={styles.boxTxtMyUnit}>
|
|
@@ -167,6 +198,7 @@ const MyUnit = ({ refreshing }) => {
|
|
|
167
198
|
<View style={styles.container}>
|
|
168
199
|
{myUnits.length ? (
|
|
169
200
|
<Carousel
|
|
201
|
+
ref={carouselRef}
|
|
170
202
|
layout={'default'}
|
|
171
203
|
data={myUnits}
|
|
172
204
|
sliderWidth={screenWidth}
|