@eohjsc/react-native-smart-city 0.4.94 → 0.4.96

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/README.md CHANGED
@@ -132,7 +132,7 @@ const MyScreen = () => {
132
132
  };
133
133
  ```
134
134
 
135
- 3. Trigger quick action
135
+ 3. Trigger quick action
136
136
 
137
137
  ```javascript
138
138
  import React from 'react';
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.4.94",
4
+ "version": "0.4.96",
5
5
  "description": "TODO",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -196,13 +196,13 @@
196
196
  "react-native-super-grid": "^4.0.3",
197
197
  "react-native-svg": "^12.1.0",
198
198
  "react-native-toast-message": "^2.1.1",
199
- "react-native-udp": "^4.1.7",
199
+ "react-native-udp": "4.1.3",
200
200
  "react-native-version-check": "^3.4.2",
201
201
  "react-native-vlc-media-player": "^1.0.41",
202
202
  "react-native-webview": "11.22.7",
203
203
  "react-native-wheel-color-picker": "^1.2.0",
204
204
  "react-native-wheel-scrollview-picker": "^1.2.2",
205
- "react-native-wifi-reborn": "^4.11.0",
205
+ "react-native-wifi-reborn": "4.5.0",
206
206
  "react-navigation": "^2.2.0",
207
207
  "string-format": "^2.0.0",
208
208
  "sync-directory": "^5.1.7",
@@ -91,10 +91,7 @@ const SliderRangeTemplate = memo(
91
91
  <View
92
92
  style={(isWidgetOrder && styles.wrapOrderItem) || styles.viewBrightness}
93
93
  >
94
- <Text type="H4" style={styles.LeftTextBrightness}>
95
- {item?.title || item?.label || t('brightness')}
96
- </Text>
97
-
94
+ <Text type="H4">{item?.title || item?.label || t('brightness')}</Text>
98
95
  <View style={styles.wrap}>
99
96
  <Slider
100
97
  step={1}
@@ -0,0 +1,68 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import { View, TouchableOpacity } from 'react-native';
3
+ import { IconOutline } from '@ant-design/icons-react-native';
4
+ import Text from '../../commons/Text';
5
+ import { AlertAction } from '../../commons';
6
+ import { useDropdownAction } from './hooks/useDropdownAction';
7
+ import { useConfigGlobalState } from '../../iot/states';
8
+ import styles from './TextBoxTemplateStyle';
9
+
10
+ import { useTranslations } from '../../hooks/Common/useTranslations';
11
+ import _TextInput from '../Form/TextInput';
12
+ import AccessibilityLabel from '../../configs/AccessibilityLabel';
13
+
14
+ const TextBoxTemplate = ({ item, doAction, isWidgetOrder }) => {
15
+ const t = useTranslations();
16
+ const { label, configuration } = item;
17
+ const { action_data, config } = configuration;
18
+ const [configValues] = useConfigGlobalState('configValues');
19
+ const [value, setValue] = useState();
20
+
21
+ const { stateAlert, hideAlertAction, onShowAlert } = useDropdownAction();
22
+
23
+ const onDone = useCallback(() => {
24
+ doAction(action_data, JSON.stringify({ value: value }));
25
+ hideAlertAction();
26
+ }, [doAction, action_data, value, hideAlertAction]);
27
+
28
+ const onInputChange = (value) => {
29
+ setValue(value);
30
+ };
31
+ return (
32
+ <View style={(isWidgetOrder && styles.wrapOrderItem) || styles.wrap}>
33
+ <View>
34
+ <Text type="H4">{label}</Text>
35
+ </View>
36
+
37
+ <View style={styles.iconAndText}>
38
+ <Text type="H4">{configValues[config.id]?.value}</Text>
39
+ <TouchableOpacity
40
+ style={styles.iconAndTextOption}
41
+ onPress={onShowAlert}
42
+ accessibilityLabel={AccessibilityLabel.TEXT_BOX_BUTTON_EDIT}
43
+ >
44
+ <IconOutline name="edit" size={20} />
45
+ </TouchableOpacity>
46
+ <AlertAction
47
+ visible={stateAlert.visible}
48
+ hideModal={hideAlertAction}
49
+ title={t('change')}
50
+ message={stateAlert.message}
51
+ leftButtonTitle={stateAlert.leftButton}
52
+ leftButtonClick={hideAlertAction}
53
+ rightButtonTitle={stateAlert.rightButton}
54
+ rightButtonClick={onDone}
55
+ rightDisabled={!value}
56
+ >
57
+ <_TextInput
58
+ wrapStyle={styles.wrapInputStyle}
59
+ value={value}
60
+ onChange={onInputChange}
61
+ />
62
+ </AlertAction>
63
+ </View>
64
+ </View>
65
+ );
66
+ };
67
+
68
+ export default TextBoxTemplate;
@@ -0,0 +1,37 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { Colors } from '../../configs';
3
+
4
+ export default StyleSheet.create({
5
+ wrapOrderItem: {
6
+ flex: 1,
7
+ flexDirection: 'row',
8
+ justifyContent: 'space-between',
9
+ padding: 16,
10
+ },
11
+ wrap: {
12
+ padding: 16,
13
+ marginHorizontal: 16,
14
+ marginBottom: 16,
15
+ borderWidth: 1,
16
+ borderColor: Colors.Gray4,
17
+ borderRadius: 10,
18
+ flex: 1,
19
+ flexDirection: 'column',
20
+ justifyContent: 'space-between',
21
+ },
22
+ iconAndText: {
23
+ flex: 1,
24
+ flexDirection: 'row',
25
+ alignItems: 'center',
26
+ },
27
+ iconAndTextOption: {
28
+ flex: 1,
29
+ flexDirection: 'row',
30
+ alignItems: 'center',
31
+ justifyContent: 'flex-end',
32
+ },
33
+ wrapInputStyle: {
34
+ marginRight: 14,
35
+ paddingLeft: 14,
36
+ },
37
+ });
@@ -0,0 +1,130 @@
1
+ import React from 'react';
2
+ import { TouchableOpacity, View } from 'react-native';
3
+ import { act, create } from 'react-test-renderer';
4
+ import { watchMultiConfigs } from '../../../iot/Monitor';
5
+ import { AlertAction } from '../..';
6
+ import { AccessibilityLabel } from '../../../configs/Constants';
7
+ import { SCProvider } from '../../../context';
8
+ import { mockSCStore } from '../../../context/mockStore';
9
+
10
+ import TextBoxTemplate from '../TextBoxTemplate';
11
+ import _TextInput from '../../Form/TextInput';
12
+ import { Colors } from '../../../configs';
13
+
14
+ const wrapComponent = (item, mockDoAction, isWidgetOrder = false) => (
15
+ <SCProvider initState={mockSCStore({})}>
16
+ <TextBoxTemplate
17
+ item={item}
18
+ doAction={mockDoAction}
19
+ isWidgetOrder={isWidgetOrder}
20
+ />
21
+ </SCProvider>
22
+ );
23
+
24
+ jest.mock('../../../iot/Monitor');
25
+
26
+ jest.mock('../../../iot/states', () => ({
27
+ useConfigGlobalState: () => [{ 5: { value: 2 } }, null],
28
+ }));
29
+
30
+ jest.mock('@react-navigation/native', () => {
31
+ return {
32
+ ...jest.requireActual('@react-navigation/native'),
33
+ useFocusEffect: jest.fn(),
34
+ };
35
+ });
36
+
37
+ describe('Test OptionsDropdownActionTemplate', () => {
38
+ const actionData = {
39
+ color: '#00979D',
40
+ command_prefer_over_bluetooth: true,
41
+ command_prefer_over_googlehome: false,
42
+ command_prefer_over_internet: false,
43
+ googlehome_actions: [],
44
+ icon: 'caret-up',
45
+ id: 20,
46
+ key: '5ed1d4dc-a905-47cd-b0c9-f979644bd21a',
47
+ };
48
+
49
+ let displayItem;
50
+ let sensor;
51
+ let wrapper;
52
+
53
+ beforeEach(() => {
54
+ watchMultiConfigs.mockClear();
55
+ displayItem = {
56
+ label: 'label',
57
+ configuration: {
58
+ title: 'Fan Speed',
59
+ action_data: actionData,
60
+ config: { id: 5 },
61
+ icon: 'slack',
62
+ },
63
+ };
64
+ sensor = {
65
+ name: 'Sensor name',
66
+ is_managed_by_backend: false,
67
+ };
68
+ });
69
+
70
+ it('render template', async () => {
71
+ const mockDoAction = jest.fn();
72
+ await act(async () => {
73
+ wrapper = await create(wrapComponent(displayItem, mockDoAction));
74
+ });
75
+ const instance = wrapper.root;
76
+ const views = instance.findAllByType(View);
77
+
78
+ expect(views[0].props.style).toEqual({
79
+ padding: 16,
80
+ marginHorizontal: 16,
81
+ marginBottom: 16,
82
+ borderWidth: 1,
83
+ borderColor: Colors.Gray4,
84
+ borderRadius: 10,
85
+ flex: 1,
86
+ flexDirection: 'column',
87
+ justifyContent: 'space-between',
88
+ });
89
+ const buttonEdit = instance.find(
90
+ (el) =>
91
+ el.props.accessibilityLabel ===
92
+ AccessibilityLabel.TEXT_BOX_BUTTON_EDIT &&
93
+ el.type === TouchableOpacity
94
+ );
95
+ await act(async () => {
96
+ await buttonEdit.props.onPress();
97
+ });
98
+ const alertAction = instance.findByType(AlertAction);
99
+ expect(alertAction.props.visible).toBeTruthy();
100
+ expect(alertAction.props.rightDisabled).toBeTruthy();
101
+ const textInput = instance.findByType(_TextInput);
102
+ await act(async () => {
103
+ await textInput.props.onChange('123');
104
+ });
105
+ expect(alertAction.props.rightDisabled).toBeFalsy();
106
+ await act(async () => {
107
+ await alertAction.props.rightButtonClick();
108
+ });
109
+ expect(mockDoAction).toHaveBeenCalledWith(
110
+ actionData,
111
+ JSON.stringify({ value: '123' })
112
+ );
113
+ });
114
+ it('render template isWidgetOrder change position', async () => {
115
+ const mockDoAction = jest.fn();
116
+ sensor.is_managed_by_backend = true;
117
+ await act(async () => {
118
+ wrapper = await create(wrapComponent(displayItem, mockDoAction, true));
119
+ });
120
+ const instance = wrapper.root;
121
+ const views = instance.findAllByType(View);
122
+
123
+ expect(views[0].props.style).toEqual({
124
+ flex: 1,
125
+ flexDirection: 'row',
126
+ justifyContent: 'space-between',
127
+ padding: 16,
128
+ });
129
+ });
130
+ });
@@ -13,6 +13,7 @@ import SliderRangeTemplate from './SliderRangeTemplate';
13
13
  import OnOffSmartLock from './OnOffSmartLock/OnOffSmartLock';
14
14
  import TwoButtonTemplate from './TwoButtonTemplate';
15
15
  import SwitchButtonTemplate from './OnOffTemplate/SwitchButtonTemplate';
16
+ import TextBoxTemplate from './TextBoxTemplate';
16
17
 
17
18
  export const getActionComponent = (template) => {
18
19
  switch (template) {
@@ -46,6 +47,8 @@ export const getActionComponent = (template) => {
46
47
  return TwoButtonTemplate;
47
48
  case 'switch_button_action_template':
48
49
  return SwitchButtonTemplate;
50
+ case 'TextBoxTemplate':
51
+ return TextBoxTemplate;
49
52
  default:
50
53
  return null;
51
54
  }
@@ -23,6 +23,7 @@ const AlertAction = ({
23
23
  boxLeftButtonStyle,
24
24
  boxRightButtonStyle,
25
25
  transY = 0,
26
+ rightDisabled = false,
26
27
  }) => {
27
28
  const [keyboardAnim] = useState(new Animated.Value(0));
28
29
 
@@ -82,6 +83,7 @@ const AlertAction = ({
82
83
  accessibilityLabelPrefix={accessibilityLabelPrefix}
83
84
  wrapStyle={styles.wrapViewButtonStyle}
84
85
  disableKeyBoardAnimated
86
+ rightDisabled={rightDisabled}
85
87
  />
86
88
  </View>
87
89
  </Animated.View>
@@ -559,6 +559,8 @@ export default {
559
559
  NUMBER_UP_DOWN_ACTION_DONE: 'NUMBER_UP_DOWN_ACTION_DONE',
560
560
  NUMBER_ACTION_UP: 'NUMBER_ACTION_UP',
561
561
  NUMBER_ACTION_DOWN: 'NUMBER_ACTION_DOWN',
562
+ //TestBoxTemplate
563
+ TEXT_BOX_BUTTON_EDIT: 'TEXT_BOX_BUTTON_EDIT',
562
564
 
563
565
  // OnOffButtonAction
564
566
  ON_OFF_BUTTON_ACTION_TITLE: 'ON_OFF_BUTTON_ACTION_TITLE',
@@ -43,7 +43,9 @@ const NewActionWrapper = ({ name, children, canNext, onNext, nextTitle }) => {
43
43
  isShowSeparator
44
44
  wrapTitleStyle={styles.wrapTitleStyle}
45
45
  />
46
- <KeyboardAwareScrollView>{children}</KeyboardAwareScrollView>
46
+ <KeyboardAwareScrollView contentContainerStyle={styles.scroll}>
47
+ {children}
48
+ </KeyboardAwareScrollView>
47
49
  {hasNext && (
48
50
  <BottomButtonView
49
51
  style={styles.bottomButtonView}
@@ -81,4 +81,7 @@ export default StyleSheet.create({
81
81
  paddingLeft: 16,
82
82
  paddingRight: 16,
83
83
  },
84
+ scroll: {
85
+ paddingBottom: 200,
86
+ },
84
87
  });
@@ -61,7 +61,7 @@ const ChangePosition = () => {
61
61
  <View style={styles.disableSensor} />
62
62
  <View style={styles.widgetOrder}>
63
63
  <SensorDisplayItem
64
- isWidgetOrder
64
+ isWidgetOrder={true}
65
65
  item={item}
66
66
  sensor={sensor}
67
67
  offsetTitle={offsetTitle}
@@ -18,14 +18,14 @@ import { SCProvider } from '../../../context';
18
18
  import { mockSCStore } from '../../../context/mockStore';
19
19
  import Routes from '../../../utils/Route';
20
20
  import api from '../../../utils/Apis/axios';
21
- import { watchMultiConfigs } from '../../../iot/Monitor';
21
+ import { useWatchConfigs } from '../../../hooks/IoT';
22
22
 
23
23
  const mock = new MockAdapter(api.axiosInstance);
24
24
 
25
- jest.mock('../../../iot/Monitor', () => {
25
+ jest.mock('../../../hooks/IoT', () => {
26
26
  return {
27
- ...jest.requireActual('../../../iot/Monitor'),
28
- watchMultiConfigs: jest.fn(),
27
+ ...jest.requireActual('../../../hooks/IoT'),
28
+ useWatchConfigs: jest.fn(),
29
29
  };
30
30
  });
31
31
 
@@ -195,7 +195,7 @@ describe('test DeviceDetail', () => {
195
195
  useLayoutEffectSpy.mockImplementation(() => setState);
196
196
  mock.reset();
197
197
  mockedNavigate.mockClear();
198
- watchMultiConfigs.mockReset();
198
+ useWatchConfigs.mockReset();
199
199
  });
200
200
 
201
201
  it('render DeviceDetail render SensorDisplayItem', async () => {
@@ -810,7 +810,7 @@ describe('test DeviceDetail', () => {
810
810
  el.props.accessibilityLabel === AccessibilityLabel.SENSOR_DISPLAY_ITEM
811
811
  );
812
812
  expect(sensorDisplayItem.length).toEqual(1);
813
- expect(watchMultiConfigs).toHaveBeenCalledWith([]);
813
+ expect(useWatchConfigs).toHaveBeenCalledWith([]);
814
814
  });
815
815
 
816
816
  it('watch action config', async () => {
@@ -838,7 +838,7 @@ describe('test DeviceDetail', () => {
838
838
  tree = await create(wrapComponent(store, account, route));
839
839
  });
840
840
 
841
- expect(watchMultiConfigs).toHaveBeenCalledWith([]);
841
+ expect(useWatchConfigs).toHaveBeenCalledWith([]);
842
842
  });
843
843
 
844
844
  it('watch stages grid action config', async () => {
@@ -869,6 +869,6 @@ describe('test DeviceDetail', () => {
869
869
  tree = await create(wrapComponent(store, account, route));
870
870
  });
871
871
 
872
- expect(watchMultiConfigs).toHaveBeenCalledWith([]);
872
+ expect(useWatchConfigs).toHaveBeenCalledWith([]);
873
873
  });
874
874
  });
@@ -170,6 +170,7 @@ export const SensorDisplayItem = ({
170
170
  case 'slider_range_template':
171
171
  case 'two_button_action_template':
172
172
  case 'switch_button_action_template':
173
+ case 'TextBoxTemplate':
173
174
  return (
174
175
  <ActionGroup
175
176
  accessibilityLabel={AccessibilityLabel.DEVICE_DETAIL_ACTION_GROUP}
@@ -32,6 +32,7 @@ import {
32
32
  useBluetoothDeviceConnected,
33
33
  useHomeAssistantDeviceConnected,
34
34
  useEoHBackendDeviceConnected,
35
+ useWatchConfigs,
35
36
  } from '../../hooks/IoT';
36
37
  import { useDisconnectedDevice } from './hooks/useDisconnectedDevice';
37
38
  import { useDeviceWatchConfigControl } from './hooks/useDeviceWatchConfigControl';
@@ -59,7 +60,6 @@ import { useReceiveNotifications } from '../../hooks';
59
60
  import useChipJsonConfiguration, {
60
61
  useConnectChipMqtt,
61
62
  } from '../../hooks/useMqtt';
62
- import { watchMultiConfigs } from '../../iot/Monitor';
63
63
 
64
64
  const DeviceDetail = ({ route }) => {
65
65
  const configIdsTemp = useRef([]);
@@ -67,9 +67,7 @@ const DeviceDetail = ({ route }) => {
67
67
  const t = useTranslations();
68
68
  const navigation = useNavigation();
69
69
  const token = useSCContextSelector((state) => state.auth.account.token);
70
- const { isLockWhenPickColor, appState } = useSCContextSelector(
71
- (state) => state.app
72
- );
70
+ const { isLockWhenPickColor } = useSCContextSelector((state) => state.app);
73
71
 
74
72
  const { setAction } = useContext(SCContext);
75
73
  const [offsetTitle, setOffsetTitle] = useState(1);
@@ -612,6 +610,8 @@ const DeviceDetail = ({ route }) => {
612
610
  }, [display.items, isNetworkConnected, sensor, mqttConfigs])
613
611
  );
614
612
 
613
+ useWatchConfigs(configIdsTemp.current);
614
+
615
615
  const isShowEmergencyResolve =
616
616
  display.items.filter(
617
617
  (item) =>
@@ -719,10 +719,6 @@ const DeviceDetail = ({ route }) => {
719
719
  SCConfig.setCurrentSensorDisplay(sensor);
720
720
  }, [sensor]);
721
721
 
722
- useEffect(() => {
723
- appState === 'active' && watchMultiConfigs(configIdsTemp.current);
724
- }, [appState]);
725
-
726
722
  const shouldRender =
727
723
  loading.displayTemplate === false &&
728
724
  loading.isConnected === false &&