@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.
Files changed (85) hide show
  1. package/package.json +3 -2
  2. package/src/Images/Common/logo.png +0 -0
  3. package/src/Images/Common/logo@2x.png +0 -0
  4. package/src/Images/Common/logo@3x.png +0 -0
  5. package/src/Images/Common/unit_default_background.png +0 -0
  6. package/src/Images/Common/unit_default_background@2x.png +0 -0
  7. package/src/Images/Common/unit_default_background@3x.png +0 -0
  8. package/src/commons/ActionGroup/OnOffSmartLock/AutoLock/index.js +1 -1
  9. package/src/commons/ActionGroup/SliderRangeTemplate.js +7 -13
  10. package/src/commons/ActionGroup/SliderRangeTemplateStyles.js +0 -1
  11. package/src/commons/ActionGroup/__test__/SliderRangeTemplate.test.js +4 -4
  12. package/src/commons/ActionGroup/__test__/TwoButtonTemplate.test.js +30 -0
  13. package/src/commons/ActionGroup/__test__/index.test.js +1 -1
  14. package/src/commons/Automate/__test__/ItemAutomate.test.js +25 -3
  15. package/src/commons/{FourButtonFilterHistory → ChartAggregationOption}/__test__/FourButtonFilterHistory.test.js +2 -2
  16. package/src/commons/ChartAggregationOption/index.js +72 -0
  17. package/src/commons/{FourButtonFilterHistory → ChartAggregationOption}/styles.js +0 -0
  18. package/src/commons/Dashboard/MyPinnedSharedUnit/__test__/MyPinnedSharedUnit.test.js +19 -0
  19. package/src/commons/Dashboard/MyPinnedSharedUnit/index.js +30 -9
  20. package/src/commons/Dashboard/MyUnit/__test__/MyUnit.test.js +49 -0
  21. package/src/commons/Dashboard/MyUnit/index.js +48 -16
  22. package/src/commons/DateTimeRangeChange/DateTimeButton.js +3 -12
  23. package/src/commons/DateTimeRangeChange/index.js +113 -19
  24. package/src/commons/Device/HistoryChart.js +2 -2
  25. package/src/commons/Device/LinearChart.js +2 -2
  26. package/src/commons/Device/ProgressBar/__test__/ProgressBar.test.js +1 -1
  27. package/src/commons/Device/ProgressBar/index.js +4 -2
  28. package/src/commons/Device/ProgressBar/styles.js +1 -0
  29. package/src/commons/Device/SonosSpeaker/__test__/SonosSpeaker.test.js +1 -1
  30. package/src/commons/Device/SonosSpeaker/index.js +1 -1
  31. package/src/commons/Device/WindSpeed/Anemometer/index.js +101 -14
  32. package/src/commons/Device/WindSpeed/__test__/Anemometer.test.js +89 -2
  33. package/src/commons/ModalPopupCT/index.js +21 -2
  34. package/src/commons/ModalPopupCT/styles.js +7 -0
  35. package/src/commons/UnitSummary/ConfigHistoryChart/__test__/ConfigHistoryChart.test.js +7 -7
  36. package/src/commons/UnitSummary/ConfigHistoryChart/index.js +40 -13
  37. package/src/commons/WrapParallaxScrollView/index.js +5 -4
  38. package/src/configs/API.js +4 -3
  39. package/src/configs/AccessibilityLabel.js +7 -0
  40. package/src/configs/Constants.js +1 -0
  41. package/src/configs/Images.js +1 -0
  42. package/src/context/actionType.ts +5 -0
  43. package/src/context/mockStore.ts +1 -0
  44. package/src/context/reducer.ts +29 -0
  45. package/src/hooks/Common/useTranslations.ts +1 -1
  46. package/src/hooks/IoT/useRemoteControl.js +0 -1
  47. package/src/screens/ActivityLog/__test__/FilterPopup.test.js +1 -1
  48. package/src/screens/ActivityLog/__test__/index.test.js +1 -1
  49. package/src/screens/AddNewAction/__test__/LoadingSelectAction.test.js +16 -0
  50. package/src/screens/AddNewGateway/__test__/ScanModbusQR.test.js +15 -0
  51. package/src/screens/AllCamera/__test__/index.test.js +1 -1
  52. package/src/screens/AllCamera/index.js +2 -2
  53. package/src/screens/AllGateway/DetailConfigActionInternal/__test__/index.test.js +179 -111
  54. package/src/screens/AllGateway/DetailConfigActionInternal/index.js +48 -50
  55. package/src/screens/AllGateway/DeviceInternalDetail/__test__/index.test.js +4 -0
  56. package/src/screens/AllGateway/DeviceInternalDetail/index.js +14 -1
  57. package/src/screens/AllGateway/GatewayConnectionMethods/__test__/index.test.js +25 -0
  58. package/src/screens/AllGateway/GatewayConnectionMethods/index.js +59 -35
  59. package/src/screens/AllGateway/GatewayInfo/__test__/index.test.js +14 -4
  60. package/src/screens/AllGateway/GatewayInfo/index.js +21 -2
  61. package/src/screens/AllGateway/components/Detail/__test__/index.test.js +28 -1
  62. package/src/screens/AllGateway/components/Detail/index.js +8 -2
  63. package/src/screens/AllGateway/components/Information/index.js +4 -1
  64. package/src/screens/AllGateway/components/TabPaneCT/__test__/index.test.js +1 -1
  65. package/src/screens/AllGateway/components/TabPaneCT/index.js +1 -1
  66. package/src/screens/AllGateway/components/TabPaneCT/styles.js +1 -1
  67. package/src/screens/ConfirmUnitDeletion/index.js +6 -2
  68. package/src/screens/Device/__test__/DetailHistoryChart.test.js +1 -0
  69. package/src/screens/Device/__test__/sensorDisplayItem.test.js +150 -2
  70. package/src/screens/Device/components/ChartWrapper.js +39 -0
  71. package/src/screens/Device/components/ChartWrapperStyles.js +42 -0
  72. package/src/screens/Device/components/SensorDisplayItem.js +6 -2
  73. package/src/screens/Device/components/VisualChart.js +255 -0
  74. package/src/screens/Device/components/__test__/VisualChart.test.js +440 -0
  75. package/src/screens/EmergencyContacts/__test__/EmergencyContactsSelectContacts.test.js +7 -6
  76. package/src/screens/SmartAccount/SuccessfullyConnected/__test__/SuccessfullyConnected.test.js +3 -0
  77. package/src/screens/SmartIr/__test__/GroupButtonByType.test.js +47 -0
  78. package/src/screens/SmartIr/components/GroupButtonByType/GroupButtonByType.js +15 -2
  79. package/src/screens/SyncLGDevice/__test__/AddLGDevice.test.js +52 -0
  80. package/src/screens/Unit/ManageUnit.js +7 -3
  81. package/src/screens/Unit/components/__test__/Header.test.js +9 -0
  82. package/src/utils/I18n/translations/en.json +4 -1
  83. package/src/utils/I18n/translations/vi.json +4 -1
  84. package/src/utils/Utils.js +6 -0
  85. 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.85",
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
@@ -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-community/slider';
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-community/slider';
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
- if (config) {
21
- const configValue = configValues[config] || {};
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: value, value: value })
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 configValue = configValues[config] || {};
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
- style={styles.slider}
68
+ containerStyle={styles.slider}
75
69
  value={valueBrightness}
76
70
  onSlidingComplete={onChangeBrightness}
77
71
  onValueChange={setValueBrightness}
@@ -15,7 +15,6 @@ export default StyleSheet.create({
15
15
  },
16
16
  slider: {
17
17
  width: 131,
18
- transform: [{ scaleY: 2 }],
19
18
  },
20
19
  trackSlider: {
21
20
  height: 8,
@@ -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 SliderRange from '@react-native-community/slider';
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(SliderRange);
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(SliderRange);
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-community/slider';
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 = (type = 'one_tap') => (
10
+ const wrapComponent = (rest) => (
9
11
  <SCProvider initState={mockSCStore({})}>
10
- <ItemAutomate type={type} />
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 FourButtonFilterHistory from '../index';
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
- <FourButtonFilterHistory {...props} />
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;
@@ -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, { useCallback, useEffect, useState, memo } from '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
- await fetchWithCache(API.UNIT.SHARED_UNITS(), {}, (response) => {
31
- const { success, data } = response;
32
-
33
- if (success) {
34
- setSharedUnits(data);
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, Image, TouchableOpacity, Dimensions } from 'react-native';
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
- await fetchWithCache(API.UNIT.MY_UNITS(), {}, (response) => {
58
- const { success, data } = response;
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
- }, [setMyUnits]);
62
-
63
- useEffect(() => {
64
- if (isFocused || refreshing) {
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
- }, [fetchMyUnitDashboard, isFocused, refreshing]);
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
- <Image
150
+ <FImage
140
151
  style={styles.bgMyUnit}
141
- source={{ uri: item.background }}
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}