@eohjsc/react-native-smart-city 0.2.86 → 0.2.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 (74) hide show
  1. package/assets/images/brightnessBlack.svg +12 -0
  2. package/index.js +4 -0
  3. package/package.json +1 -1
  4. package/src/Images/SmartIr/AC.svg +14 -0
  5. package/src/Images/SmartIr/DIY.svg +3 -0
  6. package/src/Images/SmartIr/Fan.svg +10 -0
  7. package/src/Images/SmartIr/Fridge.svg +5 -0
  8. package/src/Images/SmartIr/TV.svg +10 -0
  9. package/src/Images/SmartIr/WM.svg +11 -0
  10. package/src/Images/SmartIr/index.js +7 -0
  11. package/src/commons/ActionGroup/OnOffSmartLock/AutoLock/AutoLockStyles.js +40 -0
  12. package/src/commons/ActionGroup/OnOffSmartLock/AutoLock/ButtonWrapper.js +65 -0
  13. package/src/commons/ActionGroup/OnOffSmartLock/AutoLock/ButtonWrapperStyles.js +43 -0
  14. package/src/commons/ActionGroup/OnOffSmartLock/AutoLock/__test__/index.test.js +48 -0
  15. package/src/commons/ActionGroup/OnOffSmartLock/AutoLock/index.js +57 -0
  16. package/src/commons/ActionGroup/{OnOffSmartLock.js → OnOffSmartLock/OnOffSmartLock.js} +5 -5
  17. package/src/commons/ActionGroup/{OnOffSmartLockStyle.js → OnOffSmartLock/OnOffSmartLockStyle.js} +1 -1
  18. package/src/commons/ActionGroup/OnOffSmartLock/PasscodeList/ItemPasscode.js +48 -0
  19. package/src/commons/ActionGroup/OnOffSmartLock/PasscodeList/ItemPasscodeStyles.js +42 -0
  20. package/src/commons/ActionGroup/OnOffSmartLock/PasscodeList/PasscodeListStyles.js +49 -0
  21. package/src/commons/ActionGroup/OnOffSmartLock/PasscodeList/index.js +66 -0
  22. package/src/commons/ActionGroup/OnOffSmartLock/SetupGeneratePasscode/ButtonWrapper.js +96 -0
  23. package/src/commons/ActionGroup/OnOffSmartLock/SetupGeneratePasscode/SetupGeneratePasscodeStyles.js +98 -0
  24. package/src/commons/ActionGroup/OnOffSmartLock/SetupGeneratePasscode/__test__/index.test.js +62 -0
  25. package/src/commons/ActionGroup/OnOffSmartLock/SetupGeneratePasscode/index.js +249 -0
  26. package/src/commons/ActionGroup/OnOffTemplate/index.js +4 -2
  27. package/src/commons/ActionGroup/OptionsDropdownActionTemplate.js +2 -1
  28. package/src/commons/ActionGroup/SmartTiviActionTemplate/SmartTiviActionTemplate.js +188 -186
  29. package/src/commons/ActionGroup/StatesGridActionTemplate.js +2 -1
  30. package/src/commons/ActionGroup/index.js +1 -1
  31. package/src/commons/Device/DisconnectedView.js +7 -1
  32. package/src/commons/Device/LinearChart.js +3 -40
  33. package/src/commons/Device/__test__/DisconnectedView.test.js +13 -2
  34. package/src/commons/RowItem/index.js +12 -7
  35. package/src/commons/WheelDateTimePicker/index.js +18 -4
  36. package/src/configs/API.js +1 -1
  37. package/src/configs/Colors.js +1 -0
  38. package/src/configs/Constants.js +11 -0
  39. package/src/context/mockStore.ts +1 -0
  40. package/src/iot/RemoteControl/Bluetooth.js +3 -3
  41. package/src/navigations/SmartIrStack.js +31 -0
  42. package/src/navigations/SmartLockStack.js +51 -0
  43. package/src/navigations/UnitStack.js +12 -3
  44. package/src/screens/AddCommon/__test__/SelectSubUnit.test.js +1 -1
  45. package/src/screens/AddNewGateway/PlugAndPlay/ConnectWifiWarning.js +77 -55
  46. package/src/screens/AddNewGateway/PlugAndPlay/GatewayWifiList.js +141 -27
  47. package/src/screens/AddNewGateway/PlugAndPlay/__test__/ConnectWifiWarning.test.js +58 -0
  48. package/src/screens/AddNewGateway/PlugAndPlay/__test__/GatewayWifiList.test.js +26 -2
  49. package/src/screens/Device/components/SensorConnectStatusViewHeader.js +13 -2
  50. package/src/screens/Device/detail.js +49 -10
  51. package/src/screens/Device/hooks/useDisconnectedDevice.js +21 -5
  52. package/src/screens/GuestInfo/components/AccessScheduleItem.js +9 -2
  53. package/src/screens/GuestInfo/components/RecurringDetail.js +3 -2
  54. package/src/screens/GuestInfo/components/TemporaryDetail.js +3 -2
  55. package/src/screens/GuestInfo/styles/AccessScheduleItemStyles.js +3 -0
  56. package/src/screens/Notification/__test__/NotificationItem.test.js +1 -0
  57. package/src/screens/Notification/components/NotificationItem.js +16 -0
  58. package/src/screens/SmartIr/__test__/GroupButtonByType.test.js +54 -0
  59. package/src/screens/SmartIr/__test__/SelectBrand.test.js +74 -0
  60. package/src/screens/SmartIr/__test__/SelectDeviceType.test.js +64 -0
  61. package/src/screens/SmartIr/__test__/SmartIr.test.js +1 -0
  62. package/src/screens/SmartIr/components/GroupButtonByType/GroupButtonByType.js +86 -0
  63. package/src/screens/SmartIr/components/GroupButtonByType/GroupButtonByTypeStyles.js +76 -0
  64. package/src/screens/SmartIr/components/SelectBrand.js +61 -0
  65. package/src/screens/SmartIr/components/SelectBrandStyles.js +14 -0
  66. package/src/screens/SmartIr/components/SelectDeviceType.js +96 -0
  67. package/src/screens/SmartIr/components/SelectDeviceTypeStyles.js +30 -0
  68. package/src/screens/SmartIr/index.js +8 -3
  69. package/src/screens/Unit/Detail.js +7 -3
  70. package/src/screens/Unit/components/MyUnitDevice/index.js +2 -4
  71. package/src/screens/Unit/components/__test__/MyUnitDevice.test.js +38 -9
  72. package/src/utils/I18n/translations/en.json +24 -1
  73. package/src/utils/I18n/translations/vi.json +24 -1
  74. package/src/utils/Route/index.js +9 -1
@@ -79,7 +79,7 @@ const DeviceDetail = ({ route }) => {
79
79
  isConnected: true,
80
80
  displayTemplate: true,
81
81
  });
82
-
82
+ const [serverDown, setServerDown] = useState(false);
83
83
  const isDeviceConnectedViaBle = useMemo(
84
84
  () => isDeviceConnected(sensor?.remote_control_options?.bluetooth?.address),
85
85
  [sensor]
@@ -99,15 +99,23 @@ const DeviceDetail = ({ route }) => {
99
99
  });
100
100
  }, [display]);
101
101
 
102
- useDisconnectedDevice(sensorName, isDeviceHasBle);
102
+ useDisconnectedDevice(sensorName, isDeviceHasBle, serverDown);
103
103
 
104
104
  const netInfo = useNetInfo();
105
105
 
106
- const isShowSetupEmergencyContact =
106
+ const isShowSetupEmergencyContact = useMemo(() => {
107
107
  display.items.filter(
108
108
  (item) =>
109
109
  item.type === 'emergency' && item.configuration.type === 'button'
110
110
  ).length > 0;
111
+ }, [display.items]);
112
+
113
+ const isShowSetUpSmartLock = useMemo(() => {
114
+ display.items.filter(
115
+ (item) =>
116
+ item.type === 'smartLock' && item.configuration.type === 'button'
117
+ ).length > 0;
118
+ }, [display.items]);
111
119
 
112
120
  const addToFavorites = useCallback(async () => {
113
121
  const { success } = await axiosPost(
@@ -177,16 +185,16 @@ const DeviceDetail = ({ route }) => {
177
185
  return;
178
186
  }
179
187
 
180
- const displayResult = await axiosGet(
188
+ const { success, data, resp_status } = await axiosGet(
181
189
  API.SENSOR.DISPLAY(sensor?.id),
182
190
  {},
183
191
  true
184
192
  );
185
-
186
- if (displayResult.success) {
187
- setDisplay(displayResult.data);
188
- if (displayResult.data.items.length) {
189
- const config = displayResult.data.items[0].configuration;
193
+ if (success) {
194
+ setDisplay(data);
195
+ setServerDown(false);
196
+ if (data.items.length) {
197
+ const config = data.items[0].configuration;
190
198
  if (config.hasOwnProperty('max_value')) {
191
199
  setMaxValue(config.max_value);
192
200
  }
@@ -201,6 +209,8 @@ const DeviceDetail = ({ route }) => {
201
209
  }
202
210
  }
203
211
  }
212
+ } else if (resp_status >= 500) {
213
+ setServerDown(true);
204
214
  }
205
215
  setLoading((preState) => ({ ...preState, displayTemplate: false }));
206
216
 
@@ -303,6 +313,33 @@ const DeviceDetail = ({ route }) => {
303
313
  deviceInfo: display.items.filter((item) => item.type === 'device_info'),
304
314
  },
305
315
  });
316
+ if (isShowSetUpSmartLock) {
317
+ menuItems.push({
318
+ route: Routes.SmartLockStack,
319
+ data: {
320
+ screen: Routes.AutoLock,
321
+ },
322
+ text: t('auto_lock'),
323
+ });
324
+ }
325
+ if (isOwner && isShowSetUpSmartLock) {
326
+ menuItems.push({
327
+ route: Routes.SmartLockStack,
328
+ data: {
329
+ screen: Routes.SetupGeneratePasscode,
330
+ },
331
+ text: t('setup_generate_passcode'),
332
+ });
333
+ }
334
+ if (isShowSetUpSmartLock) {
335
+ menuItems.push({
336
+ route: Routes.SmartLockStack,
337
+ data: {
338
+ screen: Routes.PasscodeList,
339
+ },
340
+ text: t('passcode_list'),
341
+ });
342
+ }
306
343
  if (!isFavourite) {
307
344
  menuItems.unshift({
308
345
  doAction: addToFavorites,
@@ -319,11 +356,12 @@ const DeviceDetail = ({ route }) => {
319
356
  display.items,
320
357
  isOwner,
321
358
  isShowSetupEmergencyContact,
359
+ isShowSetUpSmartLock,
322
360
  t,
323
361
  isFavourite,
324
362
  sensor,
325
- sensorName,
326
363
  unit,
364
+ sensorName,
327
365
  station,
328
366
  emergencyDeviceId,
329
367
  addToFavorites,
@@ -465,6 +503,7 @@ const DeviceDetail = ({ route }) => {
465
503
  isDisplayTime={isShowSetupEmergencyContact ? false : isDisplayTime}
466
504
  showWindDirection={showWindDirection}
467
505
  isGGHomeConnected={isGGHomeConnected}
506
+ isDeviceHasBle={isDeviceHasBle}
468
507
  >
469
508
  {display.items.map((item) => (
470
509
  <SensorDisplayItem
@@ -1,4 +1,5 @@
1
1
  import NetInfo from '@react-native-community/netinfo';
2
+ import { useNavigation } from '@react-navigation/native';
2
3
  import { useCallback, useEffect } from 'react';
3
4
  import { Alert, Linking, Platform } from 'react-native';
4
5
  import { useTranslations } from '../../../hooks/Common/useTranslations';
@@ -8,16 +9,22 @@ import {
8
9
  } from '../../../iot/RemoteControl/Bluetooth';
9
10
  import { ToastBottomHelper } from '../../../utils/Utils';
10
11
 
11
- export const useDisconnectedDevice = (sensorName, isDeviceHasBle) => {
12
+ export const useDisconnectedDevice = (
13
+ sensorName,
14
+ isDeviceHasBle,
15
+ serverDown
16
+ ) => {
12
17
  const t = useTranslations();
13
18
  const openBluetoothIOS = () => {
14
19
  Linking.openURL('App-Prefs:Bluetooth');
15
20
  };
21
+ const { goBack } = useNavigation();
16
22
  const actions =
17
23
  Platform.OS === 'ios'
18
24
  ? [
19
25
  {
20
26
  text: 'Skip',
27
+ onPress: () => goBack(),
21
28
  },
22
29
  {
23
30
  text: 'Open',
@@ -27,6 +34,7 @@ export const useDisconnectedDevice = (sensorName, isDeviceHasBle) => {
27
34
  : [
28
35
  {
29
36
  text: 'Skip',
37
+ onPress: () => goBack(),
30
38
  },
31
39
  {
32
40
  text: 'Open',
@@ -38,8 +46,11 @@ export const useDisconnectedDevice = (sensorName, isDeviceHasBle) => {
38
46
  const isBluetoothEnabled = useIsBluetoothEnabled();
39
47
 
40
48
  const checkNetWorkConnect = useCallback(
41
- async (isHavingInternet, isBtEnabled) => {
42
- if (isHavingInternet === false && isDeviceHasBle) {
49
+ async (isHavingInternet, isBtEnabled, serverDown) => {
50
+ if (!isDeviceHasBle) {
51
+ return;
52
+ }
53
+ if (isHavingInternet === false || serverDown) {
43
54
  // TODO avoid case first render isHavingInternet == null
44
55
  if (isBtEnabled === true) {
45
56
  ToastBottomHelper.info(
@@ -64,6 +75,11 @@ export const useDisconnectedDevice = (sensorName, isDeviceHasBle) => {
64
75
  );
65
76
 
66
77
  useEffect(() => {
67
- checkNetWorkConnect(netState.isConnected, isBluetoothEnabled);
68
- }, [netState.isConnected, isBluetoothEnabled, checkNetWorkConnect]);
78
+ checkNetWorkConnect(netState.isConnected, isBluetoothEnabled, serverDown);
79
+ }, [
80
+ netState.isConnected,
81
+ isBluetoothEnabled,
82
+ checkNetWorkConnect,
83
+ serverDown,
84
+ ]);
69
85
  };
@@ -5,14 +5,21 @@ import Text from '../../../commons/Text';
5
5
  import { TESTID } from '../../../configs/Constants';
6
6
  import styles from '../styles/AccessScheduleItemStyles';
7
7
 
8
- const AccessScheduleItem = ({ item, isSelected, onSelect }) => {
8
+ const AccessScheduleItem = ({
9
+ item,
10
+ isSelected,
11
+ onSelect,
12
+ isSetupGeneratePasscode,
13
+ }) => {
9
14
  const DetailComponent = item.detail;
10
15
  const handleOnSelect = useCallback(() => {
11
16
  onSelect(item.value);
12
17
  }, [item.value, onSelect]);
13
18
 
14
19
  return (
15
- <View style={styles.rowWrap}>
20
+ <View
21
+ style={isSetupGeneratePasscode ? styles.WrapnotPadding : styles.rowWrap}
22
+ >
16
23
  <View style={styles.rowContent}>
17
24
  <TouchableOpacity
18
25
  style={styles.row}
@@ -7,6 +7,7 @@ import { Colors } from '../../../configs';
7
7
  import { REPEAT_ITEMS } from '../constant';
8
8
  import styles from '../styles/AccessScheduleDetailStyles';
9
9
  import { TESTID } from '../../../configs/Constants';
10
+ import moment from 'moment';
10
11
 
11
12
  const RecurringDetail = ({
12
13
  onShowSetDateTime,
@@ -73,7 +74,7 @@ const RecurringDetail = ({
73
74
  testID={TESTID.RECURRING_TEXT_BUTTON}
74
75
  >
75
76
  <Text type="Body" color={Colors.Orange} style={styles.value}>
76
- {recurringTimeStart.format('hh:mm A')}
77
+ {moment(recurringTimeStart).format('hh:mm A')}
77
78
  </Text>
78
79
  </TouchableOpacity>
79
80
  <Text type="Body" color={Colors.Gray8} style={styles.title}>
@@ -84,7 +85,7 @@ const RecurringDetail = ({
84
85
  testID={TESTID.RECURRING_TEXT_BUTTON}
85
86
  >
86
87
  <Text type="Body" color={Colors.Orange} style={styles.value}>
87
- {recurringTimeEnd.format('hh:mm A')}
88
+ {moment(recurringTimeEnd).format('hh:mm A')}
88
89
  </Text>
89
90
  </TouchableOpacity>
90
91
  <Text type="Body" color={Colors.Gray8} style={styles.title}>
@@ -5,6 +5,7 @@ import Text from '../../../commons/Text';
5
5
  import { Colors } from '../../../configs';
6
6
  import styles from '../styles/AccessScheduleDetailStyles';
7
7
  import { TESTID } from '../../../configs/Constants';
8
+ import moment from 'moment';
8
9
 
9
10
  const TemporaryDetail = ({
10
11
  onShowSetDateTime,
@@ -32,7 +33,7 @@ const TemporaryDetail = ({
32
33
  testID={TESTID.TEMPORARY_TEXT_BUTTON}
33
34
  >
34
35
  <Text type="Body" color={Colors.Orange} style={styles.value}>
35
- {temporaryTimeStart.format('hh:mm A DD/MM/YYYY')}
36
+ {moment(temporaryTimeStart).format('hh:mm A DD/MM/YYYY')}
36
37
  </Text>
37
38
  </TouchableOpacity>
38
39
  <Text type="Body" color={Colors.Gray8} style={styles.title}>
@@ -43,7 +44,7 @@ const TemporaryDetail = ({
43
44
  testID={TESTID.TEMPORARY_TEXT_BUTTON}
44
45
  >
45
46
  <Text type="Body" color={Colors.Orange} style={styles.value}>
46
- {temporaryTimeEnd.format('hh:mm A DD/MM/YYYY')}
47
+ {moment(temporaryTimeEnd).format('hh:mm A DD/MM/YYYY')}
47
48
  </Text>
48
49
  </TouchableOpacity>
49
50
  </View>
@@ -5,6 +5,9 @@ export default StyleSheet.create({
5
5
  rowWrap: {
6
6
  paddingHorizontal: 16,
7
7
  },
8
+ WrapnotPadding: {
9
+ paddingHorizontal: 0,
10
+ },
8
11
  rowContent: {
9
12
  paddingVertical: 24,
10
13
  },
@@ -191,6 +191,7 @@ describe('test NotificationItem', () => {
191
191
  SENSOR_TYPE.SMOKE,
192
192
  SENSOR_TYPE.FIRE,
193
193
  SENSOR_TYPE.SOS,
194
+ SENSOR_TYPE.FILTER_WATER,
194
195
  ];
195
196
  for (const sensorType of listSensorType2) {
196
197
  test(`create ItemNotification NOTIFY_INDICATOR sensor_type ${sensorType}`, () => {
@@ -352,6 +352,22 @@ const NotificationItem = memo(({ item }) => {
352
352
  }),
353
353
  iconContent: <Image source={Images.logo} style={styles.logo} />,
354
354
  };
355
+ case SENSOR_TYPE.FILTER_WATER:
356
+ return {
357
+ content: customColorText(
358
+ t('text_notification_content_replace_water_filter'),
359
+ arrParams
360
+ ),
361
+ redirect: () =>
362
+ navigation.navigate(Routes.UnitStack, {
363
+ screen: Routes.DeviceDetail,
364
+ params: {
365
+ unitId,
366
+ sensorId,
367
+ },
368
+ }),
369
+ iconContent: <Image source={Images.logo} style={styles.logo} />,
370
+ };
355
371
  default:
356
372
  return {
357
373
  content: customColorText(
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+ import renderer, { act } from 'react-test-renderer';
3
+ import GroupButtonByType from '../components/GroupButtonByType/GroupButtonByType';
4
+ import { SCProvider } from '../../../context';
5
+ import { mockSCStore } from '../../../context/mockStore';
6
+ import WrapHeaderScrollable from '../../../commons/Sharing/WrapHeaderScrollable';
7
+
8
+ const wrapComponent = (route) => (
9
+ <SCProvider initState={mockSCStore({})}>
10
+ <GroupButtonByType route={route} />
11
+ </SCProvider>
12
+ );
13
+
14
+ const mockNavigate = jest.fn();
15
+ const mockSetState = jest.fn();
16
+ jest.mock('@react-navigation/native', () => {
17
+ return {
18
+ ...jest.requireActual('@react-navigation/native'),
19
+ useNavigation: () => ({
20
+ navigate: mockNavigate,
21
+ }),
22
+ };
23
+ });
24
+ jest.mock('react', () => {
25
+ return {
26
+ ...jest.requireActual('react'),
27
+ memo: (x) => x,
28
+ useState: jest.fn((init) => [init, mockSetState]),
29
+ };
30
+ });
31
+ describe('Test GroupButtonByType', () => {
32
+ let tree;
33
+ beforeEach(() => {
34
+ mockNavigate.mockClear();
35
+ });
36
+ afterEach(() => {
37
+ mockSetState.mockClear();
38
+ });
39
+
40
+ test('render GroupButtonByType', () => {
41
+ const route = {
42
+ params: {
43
+ device_type: { id: 1, name: '', icon: '' },
44
+ brand: { id: 1, name: '' },
45
+ },
46
+ };
47
+ act(() => {
48
+ tree = renderer.create(wrapComponent(route));
49
+ });
50
+ const instance = tree.root;
51
+ const wrapHeaderScrollable = instance.findAllByType(WrapHeaderScrollable);
52
+ expect(wrapHeaderScrollable).toHaveLength(1);
53
+ });
54
+ });
@@ -0,0 +1,74 @@
1
+ import React from 'react';
2
+ import { ScrollView } from 'react-native';
3
+ import renderer, { act } from 'react-test-renderer';
4
+ import SelectBrand from '../components/SelectBrand';
5
+ import { SCProvider } from '../../../context';
6
+ import { mockSCStore } from '../../../context/mockStore';
7
+ import { RowItem } from '../../../commons/RowItem';
8
+
9
+ const wrapComponent = (route) => (
10
+ <SCProvider initState={mockSCStore({})}>
11
+ <SelectBrand route={route} />
12
+ </SCProvider>
13
+ );
14
+
15
+ const mockNavigate = jest.fn();
16
+ const mockSetState = jest.fn();
17
+ jest.mock('@react-navigation/native', () => {
18
+ return {
19
+ ...jest.requireActual('@react-navigation/native'),
20
+ useNavigation: () => ({
21
+ navigate: mockNavigate,
22
+ }),
23
+ };
24
+ });
25
+ jest.mock('react', () => {
26
+ return {
27
+ ...jest.requireActual('react'),
28
+ memo: (x) => x,
29
+ useState: jest.fn((init) => [init, mockSetState]),
30
+ };
31
+ });
32
+ describe('Test SelectBrand', () => {
33
+ let tree;
34
+ beforeEach(() => {
35
+ mockNavigate.mockClear();
36
+ });
37
+ afterEach(() => {
38
+ mockSetState.mockClear();
39
+ });
40
+
41
+ test('render SelectBrand', () => {
42
+ const route = {
43
+ params: {
44
+ device_type: { id: 1, name: '' },
45
+ },
46
+ };
47
+ act(() => {
48
+ tree = renderer.create(wrapComponent(route));
49
+ });
50
+ const instance = tree.root;
51
+ const scrollView = instance.findAllByType(ScrollView);
52
+ expect(scrollView).toHaveLength(1);
53
+ });
54
+
55
+ test('Row SmartIr onPress', () => {
56
+ const route = {
57
+ params: {
58
+ device_type: { id: 1, name: '' },
59
+ },
60
+ };
61
+ act(() => {
62
+ tree = renderer.create(wrapComponent(route));
63
+ });
64
+ const instance = tree.root;
65
+ const scrollView = instance.findAllByType(ScrollView);
66
+ expect(scrollView).toHaveLength(1);
67
+ const rowItem = instance.findAllByType(RowItem);
68
+ expect(rowItem).toHaveLength(3);
69
+ act(() => {
70
+ rowItem[0].props.onPress();
71
+ });
72
+ expect(mockNavigate).toHaveBeenCalled();
73
+ });
74
+ });
@@ -0,0 +1,64 @@
1
+ import React from 'react';
2
+ import { ScrollView, TouchableOpacity } from 'react-native';
3
+ import renderer, { act } from 'react-test-renderer';
4
+ import SelectDeviceType from '../components/SelectDeviceType';
5
+ import { SCProvider } from '../../../context';
6
+ import { mockSCStore } from '../../../context/mockStore';
7
+
8
+ const wrapComponent = () => (
9
+ <SCProvider initState={mockSCStore({})}>
10
+ <SelectDeviceType />
11
+ </SCProvider>
12
+ );
13
+
14
+ const mockNavigate = jest.fn();
15
+ const mockSetState = jest.fn();
16
+ jest.mock('@react-navigation/native', () => {
17
+ return {
18
+ ...jest.requireActual('@react-navigation/native'),
19
+ useNavigation: () => ({
20
+ navigate: mockNavigate,
21
+ }),
22
+ };
23
+ });
24
+ jest.mock('react', () => {
25
+ return {
26
+ ...jest.requireActual('react'),
27
+ memo: (x) => x,
28
+ useState: jest.fn((init) => [init, mockSetState]),
29
+ };
30
+ });
31
+ describe('Test SelectDeviceType', () => {
32
+ let tree;
33
+ beforeEach(() => {
34
+ mockNavigate.mockClear();
35
+ });
36
+ afterEach(() => {
37
+ mockSetState.mockClear();
38
+ });
39
+
40
+ test('render SelectDeviceType', () => {
41
+ act(() => {
42
+ tree = renderer.create(wrapComponent());
43
+ });
44
+ const instance = tree.root;
45
+ const scrollView = instance.findAllByType(ScrollView);
46
+ expect(scrollView).toHaveLength(1);
47
+ });
48
+
49
+ test('itemDevice SelectDeviceType onPress', () => {
50
+ act(() => {
51
+ tree = renderer.create(wrapComponent());
52
+ });
53
+ const instance = tree.root;
54
+ const scrollView = instance.findAllByType(ScrollView);
55
+ expect(scrollView).toHaveLength(1);
56
+ const touchableOpacity = instance.findAllByType(TouchableOpacity);
57
+ expect(touchableOpacity).toHaveLength(7);
58
+ const itemDevice = touchableOpacity[1];
59
+ act(() => {
60
+ itemDevice.props.onPress();
61
+ });
62
+ expect(mockNavigate).toHaveBeenCalled();
63
+ });
64
+ });
@@ -57,5 +57,6 @@ describe('Test SmartIr', () => {
57
57
  act(() => {
58
58
  itemAddNew[0].props.onPress();
59
59
  });
60
+ expect(mockNavigate).toHaveBeenCalled();
60
61
  });
61
62
  });
@@ -0,0 +1,86 @@
1
+ import React, { memo, useCallback, useState, useMemo } from 'react';
2
+ import { View, TouchableOpacity, Alert } from 'react-native';
3
+ import WrapHeaderScrollable from '../../../../commons/Sharing/WrapHeaderScrollable';
4
+ import { useTranslations } from '../../../../hooks/Common/useTranslations';
5
+ import SmartTiviActionTemplate from '../../../../commons/ActionGroup/SmartTiviActionTemplate/SmartTiviActionTemplate';
6
+ import Text from '../../../../commons/Text';
7
+
8
+ import { IconOutline } from '@ant-design/icons-react-native';
9
+ import { Colors } from '../../../../configs';
10
+
11
+ import styles from './GroupButtonByTypeStyles';
12
+
13
+ const GroupButtonByType = memo(({ route }) => {
14
+ const t = useTranslations();
15
+ const [groupButton] = useState({ id: 1, template: 'tivi' });
16
+ const GroupButtonTemplate = useCallback(() => {
17
+ switch (groupButton?.template) {
18
+ case 'tivi':
19
+ return <SmartTiviActionTemplate />;
20
+ default:
21
+ return <></>;
22
+ }
23
+ }, [groupButton]);
24
+ const rightComponent = useMemo(
25
+ () => (
26
+ <View style={styles.rightComponent}>
27
+ <>
28
+ <TouchableOpacity
29
+ onPress={() => Alert.alert(t('feature_under_development'))}
30
+ style={[styles.headerButton]}
31
+ >
32
+ <IconOutline name={'reload'} size={27} color={Colors.Black} />
33
+ </TouchableOpacity>
34
+ <TouchableOpacity
35
+ onPress={() => Alert.alert(t('feature_under_development'))}
36
+ style={[styles.headerButton]}
37
+ >
38
+ <IconOutline name={'more'} size={27} color={Colors.Black} />
39
+ </TouchableOpacity>
40
+ </>
41
+ )
42
+ </View>
43
+ ),
44
+ [t]
45
+ );
46
+ const AlertBottom = useCallback(() => {
47
+ return (
48
+ <View style={styles.alertBottom}>
49
+ <View style={styles.alertContent}>
50
+ <Text>{t('make_sure_3_button_available')}</Text>
51
+ <View style={styles.alertButtons}>
52
+ <TouchableOpacity
53
+ style={styles.selectButton}
54
+ onPress={() => Alert.alert(t('feature_under_development'))}
55
+ >
56
+ <Text color={Colors.White}>{t('select_this_one')}</Text>
57
+ </TouchableOpacity>
58
+ <TouchableOpacity
59
+ style={styles.nextButton}
60
+ onPress={() => Alert.alert(t('feature_under_development'))}
61
+ >
62
+ <Text color={Colors.Gray8}>{t('next_one')}</Text>
63
+ </TouchableOpacity>
64
+ </View>
65
+ </View>
66
+ </View>
67
+ );
68
+ }, [t]);
69
+ return (
70
+ <View style={styles.container}>
71
+ <WrapHeaderScrollable
72
+ title={t('Tivi')}
73
+ headerAniStyle={styles.header}
74
+ styleScrollView={styles.scrollView}
75
+ rightComponent={rightComponent}
76
+ >
77
+ <View style={styles.wrapItem}>
78
+ <GroupButtonTemplate />
79
+ </View>
80
+ </WrapHeaderScrollable>
81
+ <AlertBottom />
82
+ </View>
83
+ );
84
+ });
85
+
86
+ export default GroupButtonByType;
@@ -0,0 +1,76 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { Colors, Constants } from '../../../../configs';
3
+ import { getStatusBarHeight } from 'react-native-iphone-x-helper';
4
+ export const heightHeader = 44 + 44 + getStatusBarHeight() + 10;
5
+
6
+ const flexCenter = {
7
+ justifyContent: 'center',
8
+ alignItems: 'center',
9
+ };
10
+ const styleButton = {
11
+ width: Constants.width / 2 - 32,
12
+ paddingVertical: 8,
13
+ paddingHorizontal: 32,
14
+ borderRadius: 30,
15
+ marginHorizontal: 8,
16
+ ...flexCenter,
17
+ };
18
+
19
+ export default StyleSheet.create({
20
+ container: {
21
+ flex: 1,
22
+ },
23
+ header: {
24
+ borderBottomWidth: 0,
25
+ },
26
+ scrollView: {
27
+ backgroundColor: Colors.White,
28
+ position: 'relative',
29
+ },
30
+ wrapItem: {
31
+ flex: 1,
32
+ },
33
+ rightComponent: {
34
+ flexDirection: 'row',
35
+ paddingRight: 8,
36
+ },
37
+ headerButton: {
38
+ paddingHorizontal: 10,
39
+ },
40
+ alertBottom: {
41
+ backgroundColor: Colors.White,
42
+ height: 120,
43
+ left: 0,
44
+ right: 0,
45
+ bottom: 0,
46
+ borderRadius: 10,
47
+ },
48
+ alertContent: {
49
+ flex: 1,
50
+ backgroundColor: Colors.Blue13,
51
+ ...flexCenter,
52
+ borderRadius: 10,
53
+ shadowColor: Colors.Shadow,
54
+ shadowOffset: {
55
+ width: 0,
56
+ height: 20,
57
+ },
58
+ shadowOpacity: 0.25,
59
+ shadowRadius: 4.84,
60
+ elevation: 5,
61
+ },
62
+ alertButtons: {
63
+ marginTop: 16,
64
+ flexDirection: 'row',
65
+ },
66
+ selectButton: {
67
+ backgroundColor: Colors.Primary,
68
+ ...styleButton,
69
+ },
70
+ nextButton: {
71
+ backgroundColor: Colors.Gray1,
72
+ borderColor: Colors.Gray5,
73
+ borderWidth: 1,
74
+ ...styleButton,
75
+ },
76
+ });