@eohjsc/react-native-smart-city 0.3.1 → 0.3.2

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 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.01",
4
+ "version": "0.3.02",
5
5
  "description": "TODO",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -100,7 +100,7 @@
100
100
  "@ant-design/icons-react-native": "^2.2.1",
101
101
  "@ant-design/react-native": "^4.0.5",
102
102
  "@babel/helper-environment-visitor": "^7.16.7",
103
- "@eohjsc/highcharts": "^1.0.8",
103
+ "@eohjsc/highcharts": "^1.0.9",
104
104
  "@eohjsc/react-native-keyboard-aware-scroll-view": "^0.9.5",
105
105
  "@formatjs/intl-getcanonicallocales": "^1.4.5",
106
106
  "@formatjs/intl-numberformat": "^5.6.2",
@@ -22,6 +22,7 @@ Pod::Spec.new do |s|
22
22
  s.requires_arc = true
23
23
 
24
24
  s.dependency "React"
25
+ s.dependency "react-native-permissions"
25
26
  # ...
26
27
  # s.dependency "..."
27
28
  end
@@ -2,13 +2,17 @@ import React, { memo } from 'react';
2
2
  import { View } from 'react-native';
3
3
  import styles from './DeviceItemStyles';
4
4
  import FImage from '../../FImage';
5
- import Text from '../../Text';
5
+ import _TextInput from '../../Form/TextInput';
6
6
 
7
- const DeviceItem = memo(({ icon, name }) => {
7
+ const DeviceItem = memo(({ icon, name, setNewName }) => {
8
8
  return (
9
9
  <View style={styles.container}>
10
10
  {!!icon && <FImage source={{ uri: icon }} style={styles.iconSensor} />}
11
- <Text style={styles.textItem}>{name}</Text>
11
+ <_TextInput
12
+ value={name}
13
+ textInputStyle={styles.textItem}
14
+ onChange={setNewName}
15
+ />
12
16
  </View>
13
17
  );
14
18
  });
@@ -1,23 +1,14 @@
1
1
  import { StyleSheet } from 'react-native';
2
- import { Constants, Colors } from '../../../configs';
3
-
4
- const marginItem = 12;
5
- const marginHorizontal = 5;
6
- const widthItem = (Constants.width - marginHorizontal * 2 - marginItem) / 2;
7
- const heightItem = (widthItem / 166) * 60;
2
+ import { Colors } from '../../../configs';
8
3
 
9
4
  export default StyleSheet.create({
10
5
  container: {
11
- paddingHorizontal: 15,
12
6
  borderRadius: 10,
13
7
  width: 250,
14
- height: heightItem,
15
8
  borderWidth: 1,
16
9
  borderColor: Colors.Gray4,
17
10
  backgroundColor: Colors.White,
18
11
  justifyContent: 'center',
19
- alignItems: 'center',
20
- marginBottom: 16,
21
12
  flexDirection: 'row',
22
13
  shadowColor: Colors.Shadow,
23
14
  shadowOffset: {
@@ -36,7 +27,13 @@ export default StyleSheet.create({
36
27
  resizeMode: 'contain',
37
28
  },
38
29
  textItem: {
39
- marginLeft: 15,
30
+ borderWidth: 0,
31
+ borderBottomWidth: 1,
32
+ borderBottomColor: Colors.Primary,
33
+ textAlign: 'center',
34
+ marginTop: -10,
35
+ padding: 5,
40
36
  fontSize: 16,
37
+ height: 50,
41
38
  },
42
39
  });
@@ -2,7 +2,8 @@ import React from 'react';
2
2
  import renderer, { act } from 'react-test-renderer';
3
3
  import DeviceItem from '../DeviceItem/DeviceItem';
4
4
  import FImage from '../../FImage';
5
- import Text from '../../Text';
5
+ import _TextInput from '../../Form/TextInput';
6
+
6
7
  describe('Test DeviceItem button', () => {
7
8
  let tree;
8
9
  test('create DeviceItem button', () => {
@@ -11,7 +12,7 @@ describe('Test DeviceItem button', () => {
11
12
  });
12
13
  const instance = tree.root;
13
14
  const image = instance.findAllByType(FImage);
14
- const text = instance.findAllByType(Text);
15
+ const text = instance.findAllByType(_TextInput);
15
16
  expect(image).toHaveLength(0);
16
17
  expect(text).toHaveLength(1);
17
18
  });
@@ -6,7 +6,7 @@ import { useNavigation } from '@react-navigation/native';
6
6
  import ImageSuccessfully from '../../Images/Common/SuccessfullyConnected.svg';
7
7
 
8
8
  import Text from '../Text';
9
- import { axiosPost } from '../../utils/Apis/axios';
9
+ import { axiosPatch, axiosPost } from '../../utils/Apis/axios';
10
10
  import { API, Colors } from '../../configs';
11
11
  import styles from './styles';
12
12
  import DeviceItem from './DeviceItem/DeviceItem';
@@ -32,25 +32,26 @@ const ConnectingProcess = ({ route }) => {
32
32
  const [isLoading, setIsLoading] = useState(true);
33
33
  const [sensor, setSensor] = useState(null);
34
34
  const user = useSCContextSelector((state) => state?.auth?.account?.user);
35
+ const [newName, setNewName] = useState('');
35
36
 
36
37
  const ConnectingDevice = useCallback(async () => {
37
38
  setIsLoading(true);
38
39
  switch (devicePrefixName) {
39
- case 'SENSOR':
40
- {
41
- const body = { imei: scan_sensor_data?.imei, chip: gateway?.id };
42
- const { success, data } = await axiosPost(
43
- API.SUB_UNIT.SENSOR_SCAN(station.id),
44
- body
45
- );
46
- if (success) {
47
- setSensor(data);
48
- } else {
49
- ToastBottomHelper.error(JSON.stringify(data));
50
- goBack();
51
- }
40
+ case 'SENSOR': {
41
+ const body = { imei: scan_sensor_data?.imei, chip: gateway?.id };
42
+ const { success, data } = await axiosPost(
43
+ API.SUB_UNIT.SENSOR_SCAN(station.id),
44
+ body
45
+ );
46
+ if (success) {
47
+ setSensor(data);
48
+ } else {
49
+ ToastBottomHelper.error(JSON.stringify(data));
50
+ goBack();
52
51
  }
52
+
53
53
  break;
54
+ }
54
55
  case 'ROBOT': {
55
56
  const { success, data } = await axiosPost(API.UNIT.CHIP_SCAN(unit.id), {
56
57
  imei: gateway?.imei,
@@ -114,12 +115,46 @@ const ConnectingProcess = ({ route }) => {
114
115
  station?.name !== undefined ? '- ' + station?.name : ''
115
116
  }`}
116
117
  </Text>
117
- <DeviceItem icon={sensor?.icon_kit} name={sensor?.name} />
118
+ <DeviceItem
119
+ icon={sensor?.icon_kit}
120
+ name={newName}
121
+ setNewName={setNewName}
122
+ />
118
123
  </View>
119
124
  );
120
- }, [sensor?.icon_kit, sensor?.name, station?.name, t, unit?.name, unit_name]);
125
+ }, [newName, sensor?.icon_kit, station?.name, t, unit?.name, unit_name]);
126
+
127
+ const handleDone = useCallback(async () => {
128
+ let result, message;
129
+ switch (devicePrefixName) {
130
+ case 'SENSOR': {
131
+ const { success, data } = await axiosPatch(
132
+ API.SENSOR.SENSOR_DETAIL(sensor?.id),
133
+ {
134
+ name: newName,
135
+ }
136
+ );
137
+ result = success;
138
+ message = data;
139
+ break;
140
+ }
141
+ case 'ROBOT':
142
+ case 'LITE': {
143
+ const { success, data } = await axiosPatch(
144
+ API.CHIP.CHIP_DETAIL(sensor?.id),
145
+ {
146
+ name: newName,
147
+ }
148
+ );
149
+ result = success;
150
+ message = data;
151
+ break;
152
+ }
153
+ }
121
154
 
122
- const handleDone = useCallback(() => {
155
+ if (!result) {
156
+ ToastBottomHelper.error(JSON.stringify(message));
157
+ }
123
158
  navigate(Routes.UnitStack, {
124
159
  screen: Routes.UnitDetail,
125
160
  params: {
@@ -129,7 +164,15 @@ const ConnectingProcess = ({ route }) => {
129
164
  stationId: station?.id,
130
165
  },
131
166
  });
132
- }, [navigate, station?.id, unit, unit_id]);
167
+ }, [
168
+ devicePrefixName,
169
+ navigate,
170
+ newName,
171
+ sensor?.id,
172
+ station?.id,
173
+ unit,
174
+ unit_id,
175
+ ]);
133
176
 
134
177
  useEffect(() => {
135
178
  ConnectingDevice();
@@ -68,7 +68,7 @@ const MediaPlayerDetail = ({
68
68
  newHeight = 224;
69
69
  break;
70
70
  case 4:
71
- newWidth = Constants.width / 2;
71
+ newWidth = Constants.width / 1.5;
72
72
  newHeight = 112;
73
73
  break;
74
74
  case 6:
@@ -91,9 +91,9 @@ const MediaPlayerDetail = ({
91
91
  <VLCPlayer
92
92
  autoAspectRatio={true}
93
93
  videoAspectRatio={
94
- width && height
95
- ? `${width}:${height}`
96
- : `${getWidthHeight().width}:${getWidthHeight().height}`
94
+ amount
95
+ ? `${getWidthHeight().width}:${getWidthHeight().height}`
96
+ : `${width}:${height}`
97
97
  }
98
98
  source={{
99
99
  initType: 2,
@@ -114,8 +114,8 @@ const MediaPlayerDetail = ({
114
114
  styles.player,
115
115
  style,
116
116
  {
117
- width: width || getWidthHeight().width,
118
- height: height || getWidthHeight().height,
117
+ width: amount ? getWidthHeight().width : width,
118
+ height: amount ? getWidthHeight().height : height,
119
119
  },
120
120
  ]}
121
121
  isLive={true}
@@ -15,6 +15,12 @@ import { standardizeCameraScreenSize } from '../../utils/Utils';
15
15
  import Routes from '../../utils/Route';
16
16
  import FastImage from 'react-native-fast-image';
17
17
  import MediaPlayerDetail from '../MediaPlayerDetail';
18
+ import {
19
+ keyPermission,
20
+ OpenSetting,
21
+ permitPermissionFunction,
22
+ } from '../../utils/Permission/common';
23
+ import { RESULTS } from 'react-native-permissions';
18
24
 
19
25
  const { standardizeWidth, standardizeHeight } = standardizeCameraScreenSize(
20
26
  Device.screenWidth - 32
@@ -71,13 +77,24 @@ const ShortDetailSubUnit = ({ unit, station }) => {
71
77
  };
72
78
 
73
79
  const handleOnAddNew = () => {
74
- navigate(Routes.AddDeviceStack, {
75
- screen: Routes.ScanSensorQR,
76
- params: {
77
- station_id: station?.id,
78
- unit_id: unit.id,
79
- unit_name: unit.name,
80
- },
80
+ permitPermissionFunction(keyPermission.CAMERA, (res) => {
81
+ if (res === RESULTS.GRANTED) {
82
+ navigate(Routes.AddDeviceStack, {
83
+ screen: Routes.ScanSensorQR,
84
+ params: {
85
+ station_id: station?.id,
86
+ unit_id: unit.id,
87
+ unit_name: unit.name,
88
+ },
89
+ });
90
+ return;
91
+ }
92
+ if (res === RESULTS.BLOCKED) {
93
+ OpenSetting(
94
+ t('camera_request_permission'),
95
+ t('camera_request_permission_des')
96
+ );
97
+ }
81
98
  });
82
99
  };
83
100
 
@@ -1,12 +1,14 @@
1
1
  import React from 'react';
2
2
  import { Image, View } from 'react-native';
3
3
  import { act, create } from 'react-test-renderer';
4
+ import mock from 'react-native-permissions/mock';
4
5
  import { TESTID } from '../../../configs/Constants';
5
6
  import { SCProvider } from '../../../context';
6
7
  import { mockSCStore } from '../../../context/mockStore';
7
8
  import ShortDetailSubUnit from '../ShortDetail';
8
9
  import ItemAddNew from '../../Device/ItemAddNew';
9
10
  import Routes from '../../../utils/Route';
11
+ import { keyPermission } from '../../../utils/Permission/common';
10
12
 
11
13
  const wrapComponent = (props) => (
12
14
  <SCProvider initState={mockSCStore({})}>
@@ -21,6 +23,10 @@ jest.mock('react', () => {
21
23
  };
22
24
  });
23
25
 
26
+ jest.mock('react-native-permissions', () => {
27
+ return mock;
28
+ });
29
+
24
30
  const mockedNavigate = jest.fn();
25
31
  jest.mock('@react-navigation/native', () => {
26
32
  return {
@@ -148,7 +154,7 @@ describe('test ShortDetail Subunit', () => {
148
154
  expect(itemDevice.length).toBe(1);
149
155
  });
150
156
 
151
- test('render ShortDetail add new device', () => {
157
+ test('render ShortDetail add new device', async () => {
152
158
  act(() => {
153
159
  tree = create(wrapComponent(props));
154
160
  });
@@ -157,6 +163,7 @@ describe('test ShortDetail Subunit', () => {
157
163
  act(() => {
158
164
  buttonAddNew.props.onAddNew();
159
165
  });
166
+ await mock.request(keyPermission.CAMERA);
160
167
  expect(mockedNavigate).toHaveBeenCalledWith(Routes.AddDeviceStack, {
161
168
  screen: Routes.ScanSensorQR,
162
169
  params: {
@@ -41,6 +41,10 @@ const API = {
41
41
  CHIP: {
42
42
  CHECK_FINALIZED: () => '/property_manager/stations/check_chip_finalized/',
43
43
  GET_CHIPS_FROM_UNIT: (id) => `/property_manager/get_chips/?unit_id=${id}`,
44
+ CHIP_DETAIL: (id) => `/chip_manager/chips/${id}/`,
45
+ },
46
+ SENSOR: {
47
+ SENSOR_DETAIL: (id) => `/chip_manager/sensors/${id}/`,
44
48
  },
45
49
  DEVICE: {
46
50
  SENSOR_DETAIL: (id) => `/property_manager/sensors/${id}/`,
@@ -12,14 +12,14 @@ const useSensorsStatus = (unit, sensors) => {
12
12
 
13
13
  // eslint-disable-next-line react-hooks/exhaustive-deps
14
14
  const getSensorsStatus = useCallback(
15
- throttle(async () => {
15
+ throttle(async (_unit, _sensors) => {
16
16
  const params = new URLSearchParams();
17
- if (sensors?.length > 0) {
18
- sensors.forEach((sensor) => {
17
+ if (_sensors?.length > 0) {
18
+ _sensors.forEach((sensor) => {
19
19
  params.append('sensors', sensor.id);
20
20
  });
21
21
  const { success, data, resp_status } = await axiosGet(
22
- API.UNIT.SENSORS_STATUS(unit.id),
22
+ API.UNIT.SENSORS_STATUS(_unit.id),
23
23
  {
24
24
  params: params,
25
25
  }
@@ -32,19 +32,22 @@ const useSensorsStatus = (unit, sensors) => {
32
32
  }
33
33
  }
34
34
  }, 3000),
35
- [unit, sensors]
35
+ []
36
36
  );
37
37
 
38
38
  useEffect(() => {
39
39
  if (isFocused) {
40
- getSensorsStatus();
41
- const updateInterval = setInterval(getSensorsStatus, 5000);
40
+ getSensorsStatus(unit, sensors);
41
+ const updateInterval = setInterval(
42
+ () => getSensorsStatus(unit, sensors),
43
+ 5000
44
+ );
42
45
  intervalSensorStatus.current = updateInterval;
43
46
  return () => clearInterval(updateInterval);
44
47
  } else {
45
48
  clearInterval(intervalSensorStatus.current);
46
49
  }
47
- }, [isFocused, getSensorsStatus]);
50
+ }, [isFocused, getSensorsStatus, unit, sensors]);
48
51
 
49
52
  const getStatus = (sensor) => {
50
53
  sensorsStatus.find((s) => s.id === sensor.id);
@@ -5,7 +5,7 @@ const useReceiveNotifications = () => {
5
5
  const [dataNofitication, setDataNofitication] = useState(null);
6
6
 
7
7
  const onReceived = useCallback((data) => {
8
- const { additionalData } = data.payload;
8
+ const { additionalData } = data;
9
9
  setDataNofitication(additionalData);
10
10
  }, []);
11
11
 
@@ -22,11 +22,11 @@ const Item = memo((props) => {
22
22
  <View size={14} color={Colors.Gray8} style={styles.textTitleX}>
23
23
  <Text>{title}</Text>
24
24
  {waterType && (
25
- <TouchableOpacity onPress={goToDetail}>
25
+ <TouchableOpacity onPress={goToDetail} style={styles.buttonInfo}>
26
26
  <IconOutline
27
27
  style={styles.row}
28
28
  name="info-circle"
29
- size={14}
29
+ size={20}
30
30
  color={Colors.Black}
31
31
  />
32
32
  </TouchableOpacity>
@@ -79,4 +79,12 @@ const styles = StyleSheet.create({
79
79
  row: {
80
80
  flexDirection: 'row',
81
81
  },
82
+ buttonInfo: {
83
+ width: 40,
84
+ height: 40,
85
+ justifyContent: 'center',
86
+ alignItems: 'center',
87
+ marginTop: -10,
88
+ marginRight: -10,
89
+ },
82
90
  });
@@ -987,5 +987,7 @@
987
987
  "back": "Back",
988
988
  "invaild_data": "Invaild data",
989
989
  "This {name} was removed!" : "This {name} was removed!",
990
+ "camera_request_permission": "Camera request permission",
991
+ "camera_request_permission_des": "To use camera, please unlock camera permission",
990
992
  "location_require_message": "EoH needs your location permission to get current location."
991
993
  }
@@ -988,5 +988,7 @@
988
988
  "invaild_data": "Dữ liệu không hợp lệ",
989
989
  "This {name} was removed!" : "{Name} này đã bị xóa!",
990
990
  "location_perm_denied": "Ứng dụng không cho phép truy cập vị trí.",
991
+ "camera_request_permission": "Quyền yêu cầu máy ảnh",
992
+ "camera_request_permission_des": "Để sử dụng máy ảnh, vui lòng mở khóa quyền đối với máy ảnh",
991
993
  "location_require_message": "Eoh cần quyền truy cập vị trí của bạn để lấy vị trí hiện tại."
992
994
  }
@@ -1,4 +1,13 @@
1
- import { openSettings, PERMISSIONS } from 'react-native-permissions';
1
+ /* eslint-disable promise/prefer-await-to-then */
2
+ /* eslint-disable promise/prefer-await-to-callbacks */
3
+
4
+ import {
5
+ check,
6
+ openSettings,
7
+ PERMISSIONS,
8
+ request,
9
+ RESULTS,
10
+ } from 'react-native-permissions';
2
11
  import AlertAsync from 'react-native-alert-async';
3
12
  import t from '../../hooks/Common/useTranslations';
4
13
  import { Platform } from 'react-native';
@@ -21,6 +30,25 @@ export const OpenSetting = async (alertTitle, alertMessage) => {
21
30
  ]);
22
31
  };
23
32
 
33
+ export const permitPermissionFunction = (keyPermission, callback) => {
34
+ if (keyPermission) {
35
+ check(keyPermission).then((result) => {
36
+ switch (result) {
37
+ case RESULTS.DENIED:
38
+ case RESULTS.LIMITED:
39
+ request(keyPermission).then((res) => callback(res));
40
+ break;
41
+ case RESULTS.BLOCKED:
42
+ case RESULTS.GRANTED:
43
+ callback(result);
44
+ break;
45
+ default:
46
+ break;
47
+ }
48
+ });
49
+ }
50
+ };
51
+
24
52
  export const keyPermission = {
25
53
  SMS_RECEIVE: PERMISSIONS.ANDROID.RECEIVE_SMS,
26
54
  LOCATION: isAndroid