@eohjsc/react-native-smart-city 0.3.84 → 0.3.86

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 (50) 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__/index.test.js +1 -1
  13. package/src/commons/Automate/__test__/ItemAutomate.test.js +25 -3
  14. package/src/commons/Dashboard/MyUnit/__test__/MyUnit.test.js +37 -0
  15. package/src/commons/Dashboard/MyUnit/index.js +25 -4
  16. package/src/commons/Device/ProgressBar/__test__/ProgressBar.test.js +1 -1
  17. package/src/commons/Device/ProgressBar/index.js +4 -2
  18. package/src/commons/Device/ProgressBar/styles.js +1 -0
  19. package/src/commons/Device/SonosSpeaker/__test__/SonosSpeaker.test.js +1 -1
  20. package/src/commons/Device/SonosSpeaker/index.js +1 -1
  21. package/src/commons/Device/WindSpeed/Anemometer/index.js +96 -14
  22. package/src/commons/Device/WindSpeed/__test__/Anemometer.test.js +89 -2
  23. package/src/commons/ModalPopupCT/index.js +21 -2
  24. package/src/commons/ModalPopupCT/styles.js +7 -0
  25. package/src/commons/WrapParallaxScrollView/index.js +5 -4
  26. package/src/configs/API.js +3 -2
  27. package/src/configs/AccessibilityLabel.js +3 -0
  28. package/src/configs/Constants.js +1 -0
  29. package/src/configs/Images.js +1 -0
  30. package/src/context/actionType.ts +3 -0
  31. package/src/context/mockStore.ts +1 -0
  32. package/src/context/reducer.ts +19 -0
  33. package/src/screens/AddNewAction/__test__/LoadingSelectAction.test.js +16 -0
  34. package/src/screens/AllGateway/DetailConfigActionInternal/__test__/index.test.js +179 -111
  35. package/src/screens/AllGateway/DetailConfigActionInternal/index.js +48 -50
  36. package/src/screens/AllGateway/DeviceInternalDetail/__test__/index.test.js +4 -0
  37. package/src/screens/AllGateway/DeviceInternalDetail/index.js +14 -1
  38. package/src/screens/AllGateway/GatewayConnectionMethods/__test__/index.test.js +25 -0
  39. package/src/screens/AllGateway/GatewayConnectionMethods/index.js +59 -35
  40. package/src/screens/AllGateway/GatewayInfo/__test__/index.test.js +14 -4
  41. package/src/screens/AllGateway/GatewayInfo/index.js +21 -2
  42. package/src/screens/AllGateway/components/Detail/__test__/index.test.js +28 -1
  43. package/src/screens/AllGateway/components/Detail/index.js +8 -2
  44. package/src/screens/AllGateway/components/Information/index.js +4 -1
  45. package/src/screens/AllGateway/components/TabPaneCT/styles.js +1 -1
  46. package/src/screens/ConfirmUnitDeletion/index.js +6 -2
  47. package/src/screens/Unit/ManageUnit.js +7 -3
  48. package/src/utils/I18n/translations/en.json +3 -0
  49. package/src/utils/I18n/translations/vi.json +3 -0
  50. package/src/utils/Utils.js +6 -0
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.84",
4
+ "version": "0.3.86",
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
  });
@@ -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
  });
@@ -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,12 +9,27 @@ 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();
16
19
 
20
+ const mockSetAction = jest.fn();
21
+ jest.mock('react', () => {
22
+ return {
23
+ ...jest.requireActual('react'),
24
+ useContext: () => ({
25
+ stateData: mockSCStore({
26
+ app: { isDeleteUnitSuccessFully: true },
27
+ }),
28
+ setAction: mockSetAction,
29
+ }),
30
+ };
31
+ });
32
+
17
33
  jest.mock('@react-navigation/native', () => {
18
34
  return {
19
35
  ...jest.requireActual('@react-navigation/native'),
@@ -104,5 +120,26 @@ describe('Test MyUnit', () => {
104
120
  const instance = tree.root;
105
121
  const devices = instance.findAllByType(MyUnitDevice);
106
122
  expect(devices).toHaveLength(2);
123
+
124
+ const button = instance.findByProps({
125
+ accessibilityLabel: `${AccessibilityLabel.MY_UNIT_GO_TO_DETAIL}-0`,
126
+ });
127
+ await act(async () => {
128
+ await button.props.onPress(data[0]);
129
+ });
130
+ expect(mockedNavigate).toBeCalledWith(Routes.UnitStack, {
131
+ params: { unitId: 1 },
132
+ screen: Routes.UnitDetail,
133
+ });
134
+ });
135
+
136
+ it('Test isDeleteUnitSuccessFully', async () => {
137
+ jest.useFakeTimers();
138
+ mock.onGet(API.UNIT.MY_UNITS()).replyOnce(200, data);
139
+ await act(async () => {
140
+ tree = await renderer.create(wrapComponent());
141
+ });
142
+ jest.runAllTimers();
143
+ expect(mockSetAction).toBeCalledWith(Action.RESET_DELETE_UNIT_ACTION);
107
144
  });
108
145
  });
@@ -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,
@@ -23,7 +24,7 @@ import { Section } from '../../Section';
23
24
  import { useTranslations } from '../../../hooks/Common/useTranslations';
24
25
  import { useUnitConnectRemoteDevices } from '../../../screens/Unit/hook/useUnitConnectRemoteDevices';
25
26
  import { useWatchConfigs, useBluetoothConnection } from '../../../hooks/IoT';
26
- import { SCContext } from '../../../context';
27
+ import { SCContext, useSCContextSelector } from '../../../context';
27
28
  import { Action } from '../../../context/actionType';
28
29
 
29
30
  import Carousel from 'react-native-new-snap-carousel';
@@ -32,18 +33,23 @@ import Routes from '../../../utils/Route';
32
33
  import MyUnitDevice from '../../../screens/Unit/components/MyUnitDevice';
33
34
  import { STORAGE_KEY } from '../../../utils/Storage';
34
35
  import { preloadImagesFromUnits } from '../../../utils/Functions/preloadImages';
36
+ import FImage from '../../FImage';
35
37
 
36
38
  let screenWidth = Dimensions.get('window').width;
37
39
 
38
40
  const bleManager = new BleManager();
39
41
 
40
42
  const MyUnit = ({ refreshing }) => {
43
+ const carouselRef = useRef();
41
44
  const t = useTranslations();
42
45
  const isFocused = useIsFocused();
43
46
  const navigation = useNavigation();
44
47
  const [myUnits, setMyUnits] = useState([]);
45
48
  const [slideIndex, setSlideIndex] = useState(0);
46
49
  const { setAction } = useContext(SCContext);
50
+ const isDeleteUnitSuccessFully = useSCContextSelector(
51
+ (state) => state.app.isDeleteUnitSuccessFully
52
+ );
47
53
 
48
54
  const {
49
55
  permissionsRequested: bluetoothPermRequested,
@@ -137,9 +143,13 @@ const MyUnit = ({ refreshing }) => {
137
143
  accessibilityLabel={`${AccessibilityLabel.MY_UNIT_GO_TO_DETAIL}-${index}`}
138
144
  >
139
145
  <View style={styles.overlay} />
140
- <Image
146
+ <FImage
141
147
  style={styles.bgMyUnit}
142
- source={{ uri: item.background }}
148
+ source={
149
+ item?.background
150
+ ? { uri: item.background }
151
+ : Images.unitDefaultBackground
152
+ }
143
153
  defaultSource={Images.BgUnit}
144
154
  resizeMode="cover"
145
155
  />
@@ -159,6 +169,16 @@ const MyUnit = ({ refreshing }) => {
159
169
  preloadImagesFromUnits(myUnits, STORAGE_KEY.IS_FIRST_TIME_LOAD_MY_UNITS);
160
170
  }, [myUnits]);
161
171
 
172
+ useEffect(() => {
173
+ if (isDeleteUnitSuccessFully) {
174
+ const to = setTimeout(() => {
175
+ carouselRef?.current?.snapToItem();
176
+ setAction(Action.RESET_DELETE_UNIT_ACTION);
177
+ }, 60);
178
+ return () => clearTimeout(to);
179
+ }
180
+ }, [isDeleteUnitSuccessFully, setAction]);
181
+
162
182
  return (
163
183
  <>
164
184
  <Section style={styles.boxTxtMyUnit}>
@@ -168,6 +188,7 @@ const MyUnit = ({ refreshing }) => {
168
188
  <View style={styles.container}>
169
189
  {myUnits.length ? (
170
190
  <Carousel
191
+ ref={carouselRef}
171
192
  layout={'default'}
172
193
  data={myUnits}
173
194
  sliderWidth={screenWidth}
@@ -44,6 +44,6 @@ describe('Test ProgressBar', () => {
44
44
  const texts = instance.findAllByType(Text);
45
45
  expect(texts[0].props.children).toBe('Value bar');
46
46
  expect(texts[1].props.children).toEqual(['Max value', ': ', 100]);
47
- expect(texts[2].props.children).toEqual([10, '%']);
47
+ expect(texts[2].props.children).toEqual([10, ' ', undefined]);
48
48
  });
49
49
  });
@@ -13,7 +13,7 @@ const ProgressBar = memo(({ data = [], item }) => {
13
13
  const maxValue = useMemo(() => {
14
14
  return Number(item?.configuration?.max_value) || 60;
15
15
  }, [item?.configuration?.max_value]);
16
- const value = data.length ? data[0].value : 0;
16
+ const { value = 0, measure, unit } = data.length ? data[0] : {};
17
17
  const percent = value / maxValue; // a number between 0 and 1
18
18
 
19
19
  return (
@@ -36,7 +36,9 @@ const ProgressBar = memo(({ data = [], item }) => {
36
36
  borderWidth={0}
37
37
  borderRadius={10}
38
38
  />
39
- <Text style={styles.textValue}>{value}%</Text>
39
+ <Text style={styles.textValue}>
40
+ {value} {unit || measure}
41
+ </Text>
40
42
  </View>
41
43
  </View>
42
44
  );
@@ -12,6 +12,7 @@ export default StyleSheet.create({
12
12
  padding: 12,
13
13
  borderRadius: 8,
14
14
  borderColor: Colors.Gray4,
15
+ marginBottom: 12,
15
16
  },
16
17
  textLabel: {
17
18
  paddingHorizontal: 16,
@@ -6,7 +6,7 @@ import { mockSCStore } from '../../../../context/mockStore';
6
6
  import Text from '../../../Text';
7
7
  import SonosSpeaker from '..';
8
8
  import { TouchableOpacity } from 'react-native';
9
- import Slider from '@react-native-community/slider';
9
+ import { Slider } from '@miblanchard/react-native-slider';
10
10
 
11
11
  const wrapComponent = () => (
12
12
  <SCProvider initState={mockSCStore({})}>
@@ -2,7 +2,7 @@ import React, { memo, useState } from 'react';
2
2
  import { View, TouchableOpacity } from 'react-native';
3
3
  import { useTranslations } from '../../../hooks/Common/useTranslations';
4
4
  import { Icon } from '@ant-design/react-native';
5
- import Slider from '@react-native-community/slider';
5
+ import { Slider } from '@miblanchard/react-native-slider';
6
6
 
7
7
  import Text from '../../Text';
8
8
  import { Colors } from '../../../configs';
@@ -1,12 +1,13 @@
1
- import React, { memo, useCallback, useMemo } from 'react';
1
+ import React, { memo, useCallback, useMemo, useState } from 'react';
2
2
  import Svg, { Path, Text, Circle } from 'react-native-svg';
3
- import { View, StyleSheet } from 'react-native';
3
+ import { View, StyleSheet, Text as ReactText } from 'react-native';
4
4
 
5
5
  import {
6
6
  drawArc,
7
7
  polarToCartesian,
8
8
  } from '../../../UnitSummary/AirQuality/SegmentedRoundDisplay/helper';
9
9
  import { Colors, Fonts } from '../../../../configs';
10
+ import AccessibilityLabel from '../../../../configs/AccessibilityLabel';
10
11
 
11
12
  const { PI } = Math;
12
13
 
@@ -16,12 +17,13 @@ const Anemometer = memo(
16
17
  width = 240,
17
18
  size = 170,
18
19
  strokeWidth = 16,
19
- numberOfSection = 4,
20
+ numberOfSection = 2,
20
21
  startAngle = -45,
21
22
  endAngle = 225,
22
23
  txtColor = Colors.Gray8,
23
24
  item,
24
25
  }) => {
26
+ const [isShowToolTip, setIsShowToolTip] = useState(false);
25
27
  const center = {
26
28
  x: size / 2,
27
29
  y: size / 2,
@@ -121,6 +123,38 @@ const Anemometer = memo(
121
123
  txtColor,
122
124
  ]);
123
125
 
126
+ // eslint-disable-next-line no-shadow
127
+ const handleShowToolTip = (value) => () => setIsShowToolTip(value);
128
+
129
+ const valueSize = useMemo(() => {
130
+ if ([null, undefined, NaN].includes(value)) {
131
+ return;
132
+ }
133
+ if (value.toString().length < 4) {
134
+ return [56, 16];
135
+ }
136
+ if (value.toString().length < 7) {
137
+ return [35, 10];
138
+ }
139
+ if (value.toString().length < 10) {
140
+ return [26, 6];
141
+ }
142
+ return [20, 2];
143
+ }, [value]);
144
+
145
+ const toolTipTranslateX = useMemo(() => {
146
+ if (value.toString().length < 4) {
147
+ return -20;
148
+ }
149
+ if (value.toString().length < 6) {
150
+ return -30;
151
+ }
152
+ if (value.toString().length < 8) {
153
+ return -40;
154
+ }
155
+ return -50;
156
+ }, [value]);
157
+
124
158
  return (
125
159
  <View style={styles.standard}>
126
160
  <View style={styles.wrapOffset}>
@@ -137,6 +171,41 @@ const Anemometer = memo(
137
171
  />
138
172
  </Svg>
139
173
  </View>
174
+
175
+ <View style={styles.textValue}>
176
+ <Svg width={width} height={width} {...viewBox}>
177
+ <Text
178
+ fill={Colors.Lime6}
179
+ fontSize={valueSize[0]}
180
+ fontWeight="bold"
181
+ x={width / 2}
182
+ y={width / 2 + valueSize[1]}
183
+ textAnchor="middle"
184
+ fontFamily={Fonts.Regular}
185
+ onPressIn={handleShowToolTip(true)}
186
+ onPressOut={handleShowToolTip(false)}
187
+ >
188
+ {value.toString().length > 10
189
+ ? value.toString().substring(0, 10) + '...'
190
+ : value}
191
+ </Text>
192
+ </Svg>
193
+ {isShowToolTip && (
194
+ <View
195
+ style={{
196
+ ...styles.toolTip,
197
+ top: width / 2 - 50,
198
+ left: width / 2,
199
+ transform: [{ translateX: toolTipTranslateX }],
200
+ }}
201
+ accessibilityLabel={AccessibilityLabel.WRAP_TOOL_TIP}
202
+ >
203
+ <ReactText style={styles.textToolTip}>{value}</ReactText>
204
+ <View style={styles.triangle} />
205
+ </View>
206
+ )}
207
+ </View>
208
+
140
209
  <Svg width={width} height={width} {...viewBox}>
141
210
  <Path
142
211
  x={(width - size) / 2}
@@ -147,17 +216,6 @@ const Anemometer = memo(
147
216
  {...{ d, strokeWidth }}
148
217
  />
149
218
  {textAngles()}
150
- <Text
151
- fill={Colors.Lime6}
152
- fontSize={48}
153
- fontWeight="bold"
154
- x={width / 2}
155
- y={width / 2 + 16}
156
- textAnchor="middle"
157
- fontFamily={Fonts.Regular}
158
- >
159
- {value}
160
- </Text>
161
219
  <Text
162
220
  fill={Colors.Gray8}
163
221
  fontSize={16}
@@ -186,4 +244,28 @@ const styles = StyleSheet.create({
186
244
  position: 'absolute',
187
245
  zIndex: 2,
188
246
  },
247
+ textValue: {
248
+ position: 'absolute',
249
+ zIndex: 4,
250
+ },
251
+ toolTip: {
252
+ position: 'absolute',
253
+ paddingHorizontal: 14,
254
+ backgroundColor: Colors.Black,
255
+ justifyContent: 'center',
256
+ alignItems: 'center',
257
+ borderRadius: 20,
258
+ },
259
+ textToolTip: {
260
+ color: Colors.White,
261
+ },
262
+ triangle: {
263
+ position: 'absolute',
264
+ bottom: -6,
265
+ borderTopWidth: 6,
266
+ borderRightWidth: 4,
267
+ borderLeftWidth: 4,
268
+ borderRightColor: Colors.TextTransparent,
269
+ borderLeftColor: Colors.TextTransparent,
270
+ },
189
271
  });