@eohjsc/react-native-smart-city 0.7.41 → 0.7.42

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 (29) hide show
  1. package/package.json +1 -1
  2. package/src/commons/Dashboard/MyDashboardDevice/index.js +14 -12
  3. package/src/commons/Dashboard/MyUnit/index.js +4 -3
  4. package/src/commons/DateTimeRangeChange/index.js +5 -5
  5. package/src/commons/Device/HorizontalBarChart.js +11 -24
  6. package/src/commons/Device/PowerConsumptionChart.js +195 -103
  7. package/src/commons/Form/CurrencyInput.js +9 -18
  8. package/src/commons/SubUnit/Favorites/index.js +35 -10
  9. package/src/commons/SubUnit/ShortDetail.js +17 -4
  10. package/src/commons/SubUnit/__test__/Favorites.test.js +90 -0
  11. package/src/commons/SubUnit/__test__/ShortDetail.test.js +45 -2
  12. package/src/hooks/IoT/__test__/useValueEvaluation.test.js +12 -12
  13. package/src/iot/UpdateStates.js +37 -16
  14. package/src/iot/mqtt.js +23 -0
  15. package/src/screens/Device/__test__/DeviceDetail-3rdparty.test.js +16 -5
  16. package/src/screens/Device/__test__/DeviceDetail-arduino.test.js +10 -0
  17. package/src/screens/Device/__test__/DeviceDetail-modbus.test.js +37 -8
  18. package/src/screens/Device/__test__/DeviceDetail-zigbee.test.js +16 -5
  19. package/src/screens/Device/__test__/DeviceDetail.test.js +17 -6
  20. package/src/screens/Device/__test__/sensorDisplayItem.test.js +47 -11
  21. package/src/screens/Device/components/SensorDisplayItem.js +23 -5
  22. package/src/screens/Device/styles.js +4 -0
  23. package/src/screens/UnitSummary/components/3PPowerConsumption/index.js +8 -49
  24. package/src/screens/UnitSummary/components/PowerConsumption/__test__/PowerConsumption.test.js +121 -6
  25. package/src/screens/UnitSummary/components/PowerConsumption/index.js +6 -47
  26. package/src/utils/I18n/translations/en.js +1 -0
  27. package/src/utils/I18n/translations/vi.js +1 -0
  28. package/src/utils/chartHelper/index.js +53 -0
  29. package/src/utils/chartHelper/getMaxValueIndex.js +0 -11
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.7.41",
4
+ "version": "0.7.42",
5
5
  "description": "TODO",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -43,19 +43,21 @@ const MyDashboardDevice = ({ refreshing }) => {
43
43
  );
44
44
  const [myDashboardDevices, setMyDashboardDevices] = useState([]);
45
45
 
46
- const listChipDashboard = useMemo(() => {
47
- return myDashboardDevices.flatMap((dashboard) =>
48
- dashboard.devices.map((device) => device.chip_id)
46
+ const chipIdsDashboardSet = useMemo(() => {
47
+ return new Set(
48
+ myDashboardDevices.flatMap((dashboard) =>
49
+ dashboard.devices.map((device) => device.chip_id)
50
+ )
49
51
  );
50
52
  }, [myDashboardDevices]);
51
53
 
52
54
  const { chips, isFetching } = useChipJsonConfiguration({
53
- ready: !!listChipDashboard.length,
55
+ ready: !!chipIdsDashboardSet.size,
54
56
  });
55
57
 
56
58
  const listChipsMqtt = useMemo(() => {
57
- return chips.filter((chip) => listChipDashboard.includes(chip.id));
58
- }, [chips, listChipDashboard]);
59
+ return chips.filter((chip) => chipIdsDashboardSet.has(chip.id));
60
+ }, [chips, chipIdsDashboardSet]);
59
61
 
60
62
  const { mqttConfigs } = useConnectChipMqtt(listChipsMqtt);
61
63
 
@@ -81,14 +83,14 @@ const MyDashboardDevice = ({ refreshing }) => {
81
83
 
82
84
  useWatchConfigs(isFetching ? [] : configsNeedWatching);
83
85
 
84
- const listSharedChips = useMemo(() => {
85
- const chipMqttIds = listChipsMqtt.map((chip) => chip.id);
86
- return listChipDashboard.filter((id) => !chipMqttIds.includes(id));
87
- }, [listChipsMqtt, listChipDashboard]);
86
+ const listSharedChipIds = useMemo(() => {
87
+ const chipMqttIdsSet = new Set(listChipsMqtt.map((chip) => chip.id));
88
+ return [...chipIdsDashboardSet].filter((id) => !chipMqttIdsSet.has(id));
89
+ }, [listChipsMqtt, chipIdsDashboardSet]);
88
90
 
89
91
  useWatchSharedChips({
90
- filterChipIds: listSharedChips,
91
- ready: !!listSharedChips.length,
92
+ filterChipIds: listSharedChipIds,
93
+ ready: !!listSharedChipIds.length,
92
94
  });
93
95
 
94
96
  const fetchMyDashboardDevices = useCallback(async () => {
@@ -157,9 +157,10 @@ const MyUnit = ({ refreshing }) => {
157
157
  });
158
158
 
159
159
  const listChipsMqtt = useMemo(() => {
160
- const listChipUnit =
161
- myUnitDetail?.abstract_devices.map((device) => device.chip_id) ?? [];
162
- return chips.filter((chip) => listChipUnit.includes(chip.id));
160
+ const chipIdsUnitSet = new Set(
161
+ myUnitDetail?.abstract_devices.map((device) => device.chip_id) ?? []
162
+ );
163
+ return chips.filter((chip) => chipIdsUnitSet.has(chip.id));
163
164
  }, [chips, myUnitDetail]);
164
165
 
165
166
  useConnectChipMqtt(listChipsMqtt);
@@ -1,13 +1,13 @@
1
- import React, { memo, useCallback, useEffect, useState } from 'react';
2
- import { StyleSheet, View } from 'react-native';
3
1
  import moment from 'moment/moment';
2
+ import { memo, useCallback, useEffect, useState } from 'react';
3
+ import { StyleSheet, View } from 'react-native';
4
4
  import DateTimePickerModal from 'react-native-modal-datetime-picker';
5
5
 
6
- import Text from '../Text';
7
6
  import { Colors } from '../../configs';
8
- import DateTimeButton from './DateTimeButton';
9
- import { useTranslations } from '../../hooks/Common/useTranslations';
10
7
  import { DATE_TIME_FORMAT } from '../../configs/Constants';
8
+ import { useTranslations } from '../../hooks/Common/useTranslations';
9
+ import Text from '../Text';
10
+ import DateTimeButton from './DateTimeButton';
11
11
 
12
12
  const DateTimeRangeChange = memo(
13
13
  ({
@@ -1,9 +1,12 @@
1
1
  import { isEmpty } from 'lodash';
2
- import React, { memo, useEffect, useMemo, useState } from 'react';
2
+ import { memo, useEffect, useMemo, useState } from 'react';
3
3
  import { StyleSheet, View } from 'react-native';
4
4
 
5
5
  import { Colors } from '../../configs';
6
- import { getMaxValueIndex } from '../../utils/chartHelper/getMaxValueIndex';
6
+ import {
7
+ createDataLabelFormatter,
8
+ getMaxValueIndex,
9
+ } from '../../utils/chartHelper';
7
10
  import Highcharts from '../Highcharts';
8
11
 
9
12
  // https://github.com/facebook/hermes/issues/114#issuecomment-887106990
@@ -41,7 +44,7 @@ import Highcharts from '../Highcharts';
41
44
  // );
42
45
  // }
43
46
 
44
- const HorizontalBarChart = memo(({ datas, config }) => {
47
+ const HorizontalBarChart = memo(({ datas, config, color = Colors.Primary }) => {
45
48
  const [chartOptions, setChartOptions] = useState({
46
49
  chart: {
47
50
  type: 'bar',
@@ -92,23 +95,7 @@ const HorizontalBarChart = memo(({ datas, config }) => {
92
95
  dataLabels: {
93
96
  enabled: true,
94
97
  useHTML: true,
95
- formatter: `function formatter() {
96
- var _JSON$parse = JSON.parse(this.series.name),
97
- unit = _JSON$parse.unit,
98
- price = _JSON$parse.price;
99
- var textColor = this.color === '#00979D' ? this.y === 0 ? '#262626' : '#FFFFFF' : '#262626';
100
- var valueStyle = "color:" + textColor + ";font-weight:normal;font-size:12px;";
101
- var costStyle = valueStyle + 'font-weight:bold;';
102
- var label = "<span style=\\"" + valueStyle + "\\">" + ("" + this.y + unit);
103
- if (price === '' || price === null || isNaN(price)) {
104
- return label + '</span>';
105
- }
106
- var formatMoney = function formatMoney(number) {
107
- var formatNumber = number.toFixed();
108
- return parseInt(formatNumber, 10).toFixed(0).toString().replace(/(\\d)(?=(\\d\\d\\d)+(?!\\d))/g, '$1.') + 'đ';
109
- };
110
- return label + ("/<span style=\\"" + costStyle + "\\">") + formatMoney(this.y * price) + '</span></span>';
111
- }`,
98
+ formatter: createDataLabelFormatter(),
112
99
  align: 'right',
113
100
  inside: false,
114
101
  },
@@ -139,14 +126,14 @@ const HorizontalBarChart = memo(({ datas, config }) => {
139
126
  useEffect(() => {
140
127
  const dataY = (datas[0]?.data || []).map((item, index) => {
141
128
  return {
142
- color: index % 2 === 0 ? Colors.Primary + '20' : Colors.Primary + '16',
129
+ color: index % 2 === 0 ? color + '50' : color + '30',
143
130
  y: item.y,
144
131
  };
145
132
  });
146
133
  const dataX = (datas[0]?.data || []).map((item) => item.x);
147
134
  const maxY = getMaxValueIndex(dataY);
148
135
  if (!isEmpty(maxY.max)) {
149
- dataY.splice(maxY._index, 1, { ...maxY.max, color: Colors.Primary });
136
+ dataY.splice(maxY._index, 1, { ...maxY.max, color: color });
150
137
  }
151
138
 
152
139
  const isAllZero = dataY.every((item) => item.y === 0);
@@ -166,12 +153,12 @@ const HorizontalBarChart = memo(({ datas, config }) => {
166
153
  },
167
154
  series: [
168
155
  {
169
- name: JSON.stringify(config),
156
+ name: JSON.stringify({ ...config, color }),
170
157
  data: dataY,
171
158
  },
172
159
  ],
173
160
  }));
174
- }, [datas, config]);
161
+ }, [datas, config, color]);
175
162
 
176
163
  return (
177
164
  <View style={[styles.container, { height: heightChart }]}>
@@ -1,93 +1,130 @@
1
- import React, { memo, useCallback, useState, useMemo, useEffect } from 'react';
2
- import { StyleSheet, View } from 'react-native';
3
1
  import moment from 'moment';
2
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
3
+ import { StyleSheet, View } from 'react-native';
4
4
  import { useTranslations } from '../../hooks/Common/useTranslations';
5
5
 
6
- import { Colors } from '../../configs';
7
- import Text from '../Text';
6
+ import { API, Colors } from '../../configs';
7
+ import AccessibilityLabel from '../../configs/AccessibilityLabel';
8
+ import { axiosGet } from '../../utils/Apis/axios';
9
+ import { formatMoney } from '../../utils/Utils';
8
10
  import Button from '../Button';
9
- import CurrencyInput from '../Form/CurrencyInput';
11
+ import ChartAggregationOption from '../ChartAggregationOption';
10
12
  import DateTimeRangeChange from '../DateTimeRangeChange';
13
+ import CurrencyInput from '../Form/CurrencyInput';
14
+ import Text from '../Text';
11
15
  import HorizontalBarChart from './HorizontalBarChart';
12
- import ChartAggregationOption from '../ChartAggregationOption';
13
- import { formatMoney } from '../../utils/Utils';
14
- import AccessibilityLabel from '../../configs/AccessibilityLabel';
15
-
16
- const PowerConsumptionChart = memo(
17
- ({
18
- datas,
19
- chartConfig,
20
- setChartConfig,
21
- style,
22
- groupBy,
23
- setGroupBy,
24
- onChangeDate,
25
- }) => {
26
- const t = useTranslations();
27
- const [price, setPrice] = useState(null);
28
16
 
29
- const onCalculateCost = useCallback(() => {
30
- setChartConfig((config) => ({
31
- ...config,
32
- price,
33
- }));
34
- }, [setChartConfig, price]);
17
+ const PowerConsumptionChart = ({
18
+ label,
19
+ powerConfigId,
20
+ defaultPeriod = 'date',
21
+ color = Colors.Primary,
22
+ style,
23
+ }) => {
24
+ const t = useTranslations();
25
+ const [price, setPrice] = useState(null);
26
+ const [groupBy, setGroupBy] = useState(defaultPeriod);
27
+ const [datas, setDatas] = useState([]);
28
+ const [chartConfig, setChartConfig] = useState({
29
+ unit: 'kWh',
30
+ price: '',
31
+ });
32
+ const [value, setValue] = useState([moment().subtract(6, 'days'), moment()]);
35
33
 
36
- const totalPrice = useMemo(() => {
37
- const { price: chartPrice } = chartConfig;
38
- if (chartPrice === '' || isNaN(chartPrice)) {
39
- return null;
34
+ const fetchData = useCallback(
35
+ async (startDate, endDate) => {
36
+ let params = new URLSearchParams();
37
+ params.append('config', powerConfigId);
38
+ params.append('group_by', groupBy);
39
+ if (groupBy === 'date') {
40
+ params.append('date_from', moment(startDate).format('YYYY-MM-DD'));
41
+ params.append('date_to', moment(endDate).format('YYYY-MM-DD'));
40
42
  }
41
- const sum = datas[0].data.reduce((a, b) => a + b.y, 0);
42
- const roundedSum = sum * chartPrice;
43
- return roundedSum.toFixed();
44
- }, [datas, chartConfig]);
43
+ const { success, data } = await axiosGet(
44
+ API.VALUE_CONSUME.DISPLAY_HISTORY(),
45
+ {
46
+ params,
47
+ },
48
+ true
49
+ );
50
+ if (success) {
51
+ setDatas(data);
52
+ }
53
+ },
54
+ [groupBy, powerConfigId]
55
+ );
45
56
 
46
- const renderChart = useMemo(() => {
47
- return <HorizontalBarChart datas={datas} config={chartConfig} />;
48
- }, [chartConfig, datas]);
57
+ useEffect(() => {
58
+ fetchData(value[0], value[1]);
59
+ }, [fetchData, value]);
49
60
 
50
- const [value, setValue] = useState([
51
- moment().subtract(6, 'days'),
52
- moment(),
53
- ]);
61
+ useEffect(() => {
62
+ setGroupBy(defaultPeriod);
63
+ }, [defaultPeriod]);
54
64
 
55
- useEffect(() => {
56
- onChangeDate(value[0], value[1]);
57
- }, [onChangeDate, value]);
65
+ const onCalculateCost = useCallback(() => {
66
+ setChartConfig((config) => ({
67
+ ...config,
68
+ price,
69
+ }));
70
+ }, [setChartConfig, price]);
58
71
 
59
- const selectStart = (date) => {
60
- setValue((state) => [date, state[1]]);
61
- };
62
- const selectEnd = (date) => {
63
- setValue((state) => [state[0], date]);
64
- };
72
+ const totalPrice = useMemo(() => {
73
+ const { price: chartPrice } = chartConfig;
74
+ if (chartPrice === '' || isNaN(chartPrice)) {
75
+ return null;
76
+ }
77
+ const sum =
78
+ datas[0] && datas[0].data
79
+ ? datas[0].data.reduce((a, b) => a + b.y, 0)
80
+ : 0;
81
+ const roundedSum = sum * chartPrice;
82
+ return roundedSum.toFixed();
83
+ }, [datas, chartConfig]);
65
84
 
85
+ const renderChart = useMemo(() => {
86
+ if (!datas || datas.length === 0) {
87
+ return null;
88
+ }
66
89
  return (
67
- <View style={style}>
68
- <View style={styles.historyView}>
69
- <View style={styles.titleHistory}>
70
- <Text size={20} semibold color={Colors.Gray9}>
71
- {t('history')}
72
- </Text>
90
+ <HorizontalBarChart datas={datas} config={chartConfig} color={color} />
91
+ );
92
+ }, [chartConfig, datas, color]);
73
93
 
74
- <ChartAggregationOption groupBy={groupBy} setGroupBy={setGroupBy} />
75
- </View>
76
- {groupBy === 'date' && (
77
- <DateTimeRangeChange
78
- startTime={value[0]}
79
- endTime={value[1]}
80
- selectStart={selectStart}
81
- selectEnd={selectEnd}
82
- />
83
- )}
84
- </View>
94
+ const selectStart = (date) => {
95
+ setValue((state) => [date, state[1]]);
96
+ };
97
+ const selectEnd = (date) => {
98
+ setValue((state) => [state[0], date]);
99
+ };
85
100
 
86
- <View style={styles.wrapCalculateCost}>
87
- <Text type="H4">
88
- {t('input_price_to_calculate_electricity_cost')}
101
+ return (
102
+ <View style={style}>
103
+ <View style={styles.historyView}>
104
+ <View style={styles.titleHistory}>
105
+ <Text style={styles.label} bold color={Colors.Gray9}>
106
+ {label || t('power_consumption_chart')}
89
107
  </Text>
90
- <View style={styles.row}>
108
+
109
+ <ChartAggregationOption groupBy={groupBy} setGroupBy={setGroupBy} />
110
+ </View>
111
+ {groupBy === 'date' && (
112
+ <DateTimeRangeChange
113
+ startTime={value[0]}
114
+ endTime={value[1]}
115
+ selectStart={selectStart}
116
+ selectEnd={selectEnd}
117
+ style={styles.dateTimeRange}
118
+ />
119
+ )}
120
+ </View>
121
+
122
+ <View style={styles.calculateCostCard}>
123
+ <Text style={styles.title}>
124
+ {t('input_price_to_calculate_electricity_cost')}
125
+ </Text>
126
+ <View style={styles.inputRow}>
127
+ <View style={styles.inputWrapper}>
91
128
  <CurrencyInput
92
129
  value={price}
93
130
  onChange={setPrice}
@@ -95,65 +132,120 @@ const PowerConsumptionChart = memo(
95
132
  maxValue={100000000}
96
133
  placeholder={'0'}
97
134
  onSubmitEditing={onCalculateCost}
98
- />
99
- <Button
100
- type="primary"
101
- title={t('calculate_cost')}
102
- onPress={onCalculateCost}
103
- style={styles.buttonCalculate}
104
- textSemiBold={false}
105
- accessibilityLabel={AccessibilityLabel.BUTTON_CALCULATE_COST}
135
+ currency="đ/kWh"
136
+ wrapperStyle={styles.currencyInputNoBorder}
106
137
  />
107
138
  </View>
139
+ <Button
140
+ type="primary"
141
+ title={t('calculate_cost')}
142
+ height={42}
143
+ onPress={onCalculateCost}
144
+ style={styles.buttonCalculate}
145
+ textSemiBold={true}
146
+ accessibilityLabel={AccessibilityLabel.BUTTON_CALCULATE_COST}
147
+ />
108
148
  </View>
149
+ </View>
109
150
 
151
+ <View style={styles.chartCard}>
110
152
  {renderChart}
111
153
  {!!chartConfig.price && (
112
- <Text type="H4">
113
- {t('total_power_price')}{' '}
114
- <Text bold accessibilityLabel={AccessibilityLabel.TOTAL_PRICE}>
154
+ <View style={styles.totalPriceRow}>
155
+ <Text style={styles.totalLabel}>{t('total_power_price')}</Text>
156
+ <Text
157
+ style={styles.totalValue}
158
+ accessibilityLabel={AccessibilityLabel.TOTAL_PRICE}
159
+ >
115
160
  {formatMoney(totalPrice)}
116
161
  </Text>
117
- </Text>
162
+ </View>
118
163
  )}
119
164
  </View>
120
- );
121
- }
122
- );
165
+ </View>
166
+ );
167
+ };
123
168
 
124
- export default PowerConsumptionChart;
169
+ export default React.memo(PowerConsumptionChart);
125
170
 
126
171
  const styles = StyleSheet.create({
127
172
  historyView: {
128
- paddingTop: 16,
173
+ paddingTop: 0,
129
174
  },
130
175
  titleHistory: {
131
176
  flexDirection: 'row',
132
177
  alignItems: 'center',
133
178
  justifyContent: 'space-between',
134
179
  },
135
- chartInfo: {
136
- flexDirection: 'row',
137
- justifyContent: 'center',
138
- alignItems: 'center',
139
- },
140
- chartContainer: {
141
- marginLeft: -8,
180
+ dateTimeRange: {
181
+ justifyContent: 'flex-end',
182
+ marginTop: 12,
183
+ marginLeft: 6,
142
184
  },
143
- chart: {
144
- height: 300,
185
+ calculateCostCard: {
186
+ backgroundColor: Colors.LightPrimary,
187
+ borderRadius: 8,
188
+ padding: 16,
189
+ marginTop: 16,
145
190
  },
146
- wrapCalculateCost: {
147
- marginTop: 12,
191
+ title: {
192
+ fontSize: 14,
193
+ fontWeight: 'normal',
194
+ color: Colors.Primary,
195
+ marginBottom: 12,
148
196
  },
149
- row: {
197
+ inputRow: {
150
198
  flexDirection: 'row',
151
199
  alignItems: 'center',
152
- marginTop: 8,
200
+ gap: 12,
201
+ },
202
+ inputWrapper: {
203
+ flex: 1,
204
+ backgroundColor: Colors.White,
205
+ borderWidth: 1,
206
+ borderColor: Colors.Cyan2,
207
+ borderRadius: 8,
208
+ height: 42,
209
+ justifyContent: 'center',
210
+ paddingHorizontal: 12,
211
+ },
212
+ currencyInputNoBorder: {
213
+ borderBottomWidth: 0,
153
214
  },
154
215
  buttonCalculate: {
155
216
  width: null,
156
217
  flex: null,
157
- paddingHorizontal: 12,
218
+ paddingHorizontal: 16,
219
+ borderRadius: 8,
220
+ },
221
+ chartCard: {
222
+ backgroundColor: Colors.White,
223
+ borderRadius: 12,
224
+ padding: 12,
225
+ marginTop: 16,
226
+ borderWidth: 1,
227
+ borderColor: Colors.Gray4,
228
+ },
229
+ totalPriceRow: {
230
+ flexDirection: 'row',
231
+ justifyContent: 'space-between',
232
+ alignItems: 'center',
233
+ paddingTop: 16,
234
+ marginTop: 12,
235
+ borderTopWidth: 1,
236
+ borderTopColor: Colors.Gray4,
237
+ },
238
+ totalLabel: {
239
+ fontSize: 14,
240
+ color: Colors.Gray8,
241
+ },
242
+ totalValue: {
243
+ fontSize: 18,
244
+ fontWeight: '700',
245
+ color: Colors.Primary,
246
+ },
247
+ label: {
248
+ fontSize: 18,
249
+ fontWeight: '700',
158
250
  },
159
251
  });
@@ -1,14 +1,13 @@
1
- import React, { useState, useRef, useCallback, useMemo } from 'react';
1
+ import { useCallback, useMemo, useRef, useState } from 'react';
2
2
  import {
3
- View,
4
- TextInput,
5
3
  StyleSheet,
4
+ TextInput,
6
5
  TouchableWithoutFeedback,
7
- Platform,
6
+ View,
8
7
  } from 'react-native';
9
- import Text from '../Text';
10
8
  import { Colors } from '../../configs';
11
9
  import AccessibilityLabel from '../../configs/AccessibilityLabel';
10
+ import Text from '../Text';
12
11
 
13
12
  const formatNumber = (input, options) => {
14
13
  const {
@@ -44,12 +43,13 @@ const formatNumber = (input, options) => {
44
43
  const CurrencyInput = ({
45
44
  value,
46
45
  onChange,
47
- currency = 'đ',
46
+ currency,
48
47
  minValue,
49
48
  maxValue,
50
49
  precision = 0,
51
50
  placeholder,
52
51
  onSubmitEditing,
52
+ wrapperStyle,
53
53
  }) => {
54
54
  const [startingWithSign, setStartingWithSign] = useState();
55
55
  const noNegativeValues = typeof minValue === 'number' && minValue >= 0;
@@ -119,16 +119,9 @@ const CurrencyInput = ({
119
119
  : formattedValue;
120
120
  }, [formattedValue, startingWithSign]);
121
121
 
122
- const currencyMarginLeft = useMemo(() => {
123
- if (Platform.OS === 'ios') {
124
- return 5;
125
- }
126
- return textInputValue.toString().length < 2 ? -12 : 0;
127
- }, [textInputValue]);
128
-
129
122
  return (
130
123
  <TouchableWithoutFeedback onPress={focusInput}>
131
- <View style={styles.wrap}>
124
+ <View style={[styles.wrap, wrapperStyle]}>
132
125
  <TextInput
133
126
  ref={inputRef}
134
127
  value={textInputValue}
@@ -140,10 +133,7 @@ const CurrencyInput = ({
140
133
  maxLength={10}
141
134
  accessibilityLabel={AccessibilityLabel.INPUT_CALCULATE_COST}
142
135
  />
143
- <Text
144
- semibold
145
- style={[{ marginLeft: currencyMarginLeft }, styles.currency]}
146
- >
136
+ <Text semibold style={styles.currency}>
147
137
  {currency}
148
138
  </Text>
149
139
  </View>
@@ -164,6 +154,7 @@ const styles = StyleSheet.create({
164
154
  input: {
165
155
  fontSize: 16,
166
156
  lineHeight: 24,
157
+ flex: 1,
167
158
  },
168
159
  currency: {
169
160
  fontSize: 16,
@@ -4,6 +4,10 @@ import { useNavigation } from '@react-navigation/native';
4
4
  import { useTranslations } from '../../../hooks/Common/useTranslations';
5
5
  import { useDevicesStatus } from '../../../hooks/Common';
6
6
  import { useWatchConfigs } from '../../../hooks/IoT';
7
+ import useChipJsonConfiguration, {
8
+ useConnectChipMqtt,
9
+ } from '../../../hooks/useMqtt';
10
+ import useWatchSharedChips from '../../../hooks/IoT/useWatchSharedChips';
7
11
 
8
12
  import { Section } from '../../Section';
9
13
  import ItemDevice from '../../Device/ItemDevice';
@@ -11,7 +15,6 @@ import ItemOneTap from '../OneTap/ItemOneTap';
11
15
  import ItemAddNew from '../../Device/ItemAddNew';
12
16
  import styles from './styles';
13
17
  import Routes from '../../../utils/Route';
14
- import { DEVICE_TYPE } from '../../../configs/Constants';
15
18
 
16
19
  const SubUnitFavorites = ({
17
20
  isOwner,
@@ -25,22 +28,44 @@ const SubUnitFavorites = ({
25
28
 
26
29
  useDevicesStatus(unit, favoriteDevices);
27
30
 
31
+ const favoriteChipIdsSet = useMemo(() => {
32
+ return new Set(favoriteDevices?.map((device) => device.chip_id));
33
+ }, [favoriteDevices]);
34
+
35
+ const { chips, isFetching } = useChipJsonConfiguration({
36
+ dashboardId: unit?.id,
37
+ });
38
+
39
+ const listChipsMqtt = useMemo(() => {
40
+ return chips.filter((chip) => favoriteChipIdsSet.has(chip.id));
41
+ }, [chips, favoriteChipIdsSet]);
42
+
43
+ const { mqttConfigs } = useConnectChipMqtt(listChipsMqtt);
44
+
28
45
  const configsNeedWatching = useMemo(() => {
29
46
  const configIds = [];
47
+
30
48
  favoriteDevices.forEach((device) => {
31
- if (
32
- device?.quick_action?.config_id &&
33
- ![DEVICE_TYPE.GOOGLE_HOME, DEVICE_TYPE.LG_THINQ].includes(
34
- device?.device_type
35
- )
36
- ) {
49
+ if (device?.quick_action?.config_id && device?.is_managed_by_backend) {
37
50
  configIds.push(device.quick_action.config_id);
38
51
  }
39
52
  });
40
- return configIds;
41
- }, [favoriteDevices]);
42
53
 
43
- useWatchConfigs(configsNeedWatching);
54
+ return configIds.filter((id) => !mqttConfigs[id]);
55
+ }, [favoriteDevices, mqttConfigs]);
56
+
57
+ useWatchConfigs(isFetching ? [] : configsNeedWatching);
58
+
59
+ const listSharedChipIds = useMemo(() => {
60
+ const chipMqttIdsSet = new Set(listChipsMqtt.map((chip) => chip.id));
61
+ return [...favoriteChipIdsSet].filter((id) => !chipMqttIdsSet.has(id));
62
+ }, [listChipsMqtt, favoriteChipIdsSet]);
63
+
64
+ useWatchSharedChips({
65
+ dashboardId: unit?.id,
66
+ filterChipIds: listSharedChipIds,
67
+ ready: !!listSharedChipIds.length,
68
+ });
44
69
 
45
70
  const handleOnAddNew = () => {
46
71
  navigate(Routes.SelectAddToFavorites, {