@eohjsc/react-native-smart-city 0.7.27 → 0.7.30
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/index.js +2 -0
- package/package.json +2 -1
- package/src/commons/Dashboard/MyDashboardDevice/__test__/index.test.js +68 -0
- package/src/commons/Dashboard/MyDashboardDevice/index.js +46 -11
- package/src/commons/Dashboard/MyUnit/__test__/MyUnit.test.js +43 -11
- package/src/commons/Dashboard/MyUnit/index.js +40 -32
- package/src/commons/ModalAlert/index.js +51 -0
- package/src/commons/ModalAlert/styles.js +54 -0
- package/src/commons/SubUnit/ShortDetail.js +20 -4
- package/src/commons/SubUnit/__test__/ShortDetail.test.js +46 -1
- package/src/configs/API.js +6 -0
- package/src/configs/AccessibilityLabel.js +1 -0
- package/src/configs/Constants.js +7 -0
- package/src/configs/SCConfig.js +6 -0
- package/src/context/SCContext.tsx +12 -1
- package/src/context/SCStore.ts +14 -0
- package/src/context/actionType.ts +10 -0
- package/src/context/mockStore.ts +30 -1
- package/src/context/reducer.ts +35 -0
- package/src/hooks/IoT/useRemoteControl.js +4 -1
- package/src/hooks/IoT/useWatchSharedChips.js +130 -0
- package/src/hooks/Review/__test__/useInAppReview.test.js +99 -0
- package/src/hooks/Review/useInAppReview.js +70 -0
- package/src/hooks/useMqtt.js +78 -27
- package/src/iot/Monitor.js +149 -26
- package/src/iot/UpdateStates.js +60 -0
- package/src/iot/mqtt.js +177 -22
- package/src/navigations/UnitStack.js +16 -0
- package/src/screens/ActivityLog/ItemLog.js +1 -0
- package/src/screens/AddNewGateway/RenameNewDevices.js +5 -0
- package/src/screens/AddNewGateway/__test__/RenameNewDevices.test.js +18 -0
- package/src/screens/Automate/AddNewAction/ReceiverSelect.js +208 -0
- package/src/screens/Automate/AddNewAction/SetupScriptEmail.js +1 -1
- package/src/screens/Automate/AddNewAction/SetupScriptNotify.js +18 -28
- package/src/screens/Automate/AddNewAction/SetupScriptReceiverEmail.js +22 -129
- package/src/screens/Automate/AddNewAction/SetupScriptReceiverNotify.js +59 -0
- package/src/screens/Automate/AddNewAction/SetupScriptReceiverSms.js +22 -129
- package/src/screens/Automate/AddNewAction/SetupScriptSms.js +1 -1
- package/src/screens/Automate/AddNewAction/Styles/{SetupScriptReceiverEmailStyles.js → ReceiverSelectStyles.js} +18 -1
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptNotify.test.js +16 -33
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverEmail.test.js +10 -8
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverNotify.test.js +217 -0
- package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverSms.test.js +10 -8
- package/src/screens/Automate/Components/InputName.js +5 -1
- package/src/screens/Automate/OneTap/__test__/AddNewOneTap.test.js +18 -0
- package/src/screens/Automate/ScriptDetail/index.js +6 -6
- package/src/screens/CreatePassword/__test__/index.test.js +133 -0
- package/src/screens/CreatePassword/index.js +134 -0
- package/src/screens/CreatePassword/styles.js +45 -0
- package/src/screens/Device/__test__/DeviceDetail-3rdparty.test.js +447 -0
- package/src/screens/Device/__test__/DeviceDetail-arduino.test.js +344 -0
- package/src/screens/Device/__test__/{mqttDetail.test.js → DeviceDetail-modbus.test.js} +287 -320
- package/src/screens/Device/__test__/DeviceDetail-zigbee.test.js +451 -0
- package/src/screens/Device/__test__/DeviceDetail.test.js +502 -0
- package/src/screens/Device/__test__/detail.test.js +61 -3
- package/src/screens/Device/__test__/sensorDisplayItem.test.js +28 -3
- package/src/screens/Device/detail.js +14 -6
- package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +3 -2
- package/src/screens/EnterPassword/__test__/EnterPassword.test.js +76 -1
- package/src/screens/EnterPassword/index.js +34 -4
- package/src/screens/EnterPassword/styles.js +1 -1
- package/src/utils/FactoryGateway.js +597 -0
- package/src/utils/I18n/translations/en.js +10 -0
- package/src/utils/I18n/translations/vi.js +10 -0
- package/src/utils/Route/index.js +3 -1
- package/src/utils/Validation.js +5 -0
- package/src/utils/store.js +5 -0
package/src/iot/mqtt.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import moment from 'moment';
|
|
2
1
|
import { getObject, storeObject } from '../utils/Storage';
|
|
3
|
-
import { updateGlobalValue } from './
|
|
2
|
+
import { getLastUpdated, updateGlobalValue } from './UpdateStates';
|
|
4
3
|
import { Buffer } from 'buffer';
|
|
5
4
|
|
|
6
5
|
const struct = require('python-struct');
|
|
@@ -87,6 +86,84 @@ const float_cdab = (bytes_str) => {
|
|
|
87
86
|
return value_unpacked('>f', swapped);
|
|
88
87
|
};
|
|
89
88
|
|
|
89
|
+
const uint_64_abcd = (bytes_str) => {
|
|
90
|
+
return value_unpacked('>Q', bytes_str?.slice(0, 16));
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const uint_64_dcba = (bytes_str) => {
|
|
94
|
+
return value_unpacked('<Q', bytes_str?.slice(0, 16));
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const uint_64_badc = (bytes_str) => {
|
|
98
|
+
const swapped =
|
|
99
|
+
bytes_str?.slice(12, 16) +
|
|
100
|
+
bytes_str?.slice(8, 12) +
|
|
101
|
+
bytes_str?.slice(4, 8) +
|
|
102
|
+
bytes_str?.slice(0, 4);
|
|
103
|
+
return value_unpacked('<Q', swapped);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const uint_64_cdab = (bytes_str) => {
|
|
107
|
+
const swapped =
|
|
108
|
+
bytes_str?.slice(12, 16) +
|
|
109
|
+
bytes_str?.slice(8, 12) +
|
|
110
|
+
bytes_str?.slice(4, 8) +
|
|
111
|
+
bytes_str?.slice(0, 4);
|
|
112
|
+
return value_unpacked('>Q', swapped);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const int_64_abcd = (bytes_str) => {
|
|
116
|
+
return value_unpacked('>q', bytes_str?.slice(0, 16));
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const int_64_dcba = (bytes_str) => {
|
|
120
|
+
return value_unpacked('<q', bytes_str?.slice(0, 16));
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const int_64_badc = (bytes_str) => {
|
|
124
|
+
const swapped =
|
|
125
|
+
bytes_str?.slice(12, 16) +
|
|
126
|
+
bytes_str?.slice(8, 12) +
|
|
127
|
+
bytes_str?.slice(4, 8) +
|
|
128
|
+
bytes_str?.slice(0, 4);
|
|
129
|
+
return value_unpacked('<q', swapped);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const int_64_cdab = (bytes_str) => {
|
|
133
|
+
const swapped =
|
|
134
|
+
bytes_str?.slice(12, 16) +
|
|
135
|
+
bytes_str?.slice(8, 12) +
|
|
136
|
+
bytes_str?.slice(4, 8) +
|
|
137
|
+
bytes_str?.slice(0, 4);
|
|
138
|
+
return value_unpacked('>q', swapped);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const double_abcd = (bytes_str) => {
|
|
142
|
+
return value_unpacked('>d', bytes_str?.slice(0, 16));
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const double_dcba = (bytes_str) => {
|
|
146
|
+
return value_unpacked('<d', bytes_str?.slice(0, 16));
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const double_badc = (bytes_str) => {
|
|
150
|
+
const swapped =
|
|
151
|
+
bytes_str?.slice(12, 16) +
|
|
152
|
+
bytes_str?.slice(8, 12) +
|
|
153
|
+
bytes_str?.slice(4, 8) +
|
|
154
|
+
bytes_str?.slice(0, 4);
|
|
155
|
+
return value_unpacked('<d', swapped);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const double_cdab = (bytes_str) => {
|
|
159
|
+
const swapped =
|
|
160
|
+
bytes_str?.slice(12, 16) +
|
|
161
|
+
bytes_str?.slice(8, 12) +
|
|
162
|
+
bytes_str?.slice(4, 8) +
|
|
163
|
+
bytes_str?.slice(0, 4);
|
|
164
|
+
return value_unpacked('>d', swapped);
|
|
165
|
+
};
|
|
166
|
+
|
|
90
167
|
const range_value_converting = (
|
|
91
168
|
raw_value,
|
|
92
169
|
raw_min,
|
|
@@ -133,6 +210,18 @@ const mappingTransformer = {
|
|
|
133
210
|
convert_ai: convert_ai,
|
|
134
211
|
int_first4: int_first4,
|
|
135
212
|
int_last4: int_last4,
|
|
213
|
+
double_abcd: double_abcd,
|
|
214
|
+
double_dcba: double_dcba,
|
|
215
|
+
double_badc: double_badc,
|
|
216
|
+
double_cdab: double_cdab,
|
|
217
|
+
uint_64_abcd: uint_64_abcd,
|
|
218
|
+
uint_64_dcba: uint_64_dcba,
|
|
219
|
+
uint_64_badc: uint_64_badc,
|
|
220
|
+
uint_64_cdab: uint_64_cdab,
|
|
221
|
+
int_64_abcd: int_64_abcd,
|
|
222
|
+
int_64_dcba: int_64_dcba,
|
|
223
|
+
int_64_badc: int_64_badc,
|
|
224
|
+
int_64_cdab: int_64_cdab,
|
|
136
225
|
};
|
|
137
226
|
|
|
138
227
|
const extract_config_value = (register, value_bytes) => {
|
|
@@ -227,7 +316,7 @@ const check_filter = (value, config) => {
|
|
|
227
316
|
return value < low || value > high;
|
|
228
317
|
};
|
|
229
318
|
|
|
230
|
-
const updateGlobalValueMqtt = async (config, value) => {
|
|
319
|
+
const updateGlobalValueMqtt = async (config, value, time) => {
|
|
231
320
|
if (!config?.id) {
|
|
232
321
|
return;
|
|
233
322
|
}
|
|
@@ -235,9 +324,10 @@ const updateGlobalValueMqtt = async (config, value) => {
|
|
|
235
324
|
return;
|
|
236
325
|
}
|
|
237
326
|
|
|
238
|
-
const last_updated =
|
|
327
|
+
const { last_updated, last_updated_micro } = getLastUpdated(time);
|
|
328
|
+
|
|
239
329
|
if (typeof value === 'string') {
|
|
240
|
-
updateGlobalValue(config.id, { value, last_updated });
|
|
330
|
+
updateGlobalValue(config.id, { value, last_updated, last_updated_micro });
|
|
241
331
|
return;
|
|
242
332
|
}
|
|
243
333
|
|
|
@@ -248,10 +338,18 @@ const updateGlobalValueMqtt = async (config, value) => {
|
|
|
248
338
|
return;
|
|
249
339
|
}
|
|
250
340
|
|
|
251
|
-
updateGlobalValue(config.id, { value, last_updated });
|
|
341
|
+
updateGlobalValue(config.id, { value, last_updated, last_updated_micro });
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
export const handleSelfSensorData = (chip, configsById, data, time) => {
|
|
345
|
+
chip?.self_sensor?.configs.forEach((config) => {
|
|
346
|
+
const configId = config.id;
|
|
347
|
+
const value = data?.[config.name];
|
|
348
|
+
updateGlobalValueMqtt(configsById[configId], value, time);
|
|
349
|
+
});
|
|
252
350
|
};
|
|
253
351
|
|
|
254
|
-
export const handleModbusData = (chip,
|
|
352
|
+
export const handleModbusData = (chip, configsById, data, time) => {
|
|
255
353
|
const modbus_gateway = chip?.modbus_gateway;
|
|
256
354
|
if (!modbus_gateway) {
|
|
257
355
|
return;
|
|
@@ -269,16 +367,22 @@ export const handleModbusData = (chip, configById, data) => {
|
|
|
269
367
|
sensor_data = new_sensor_data;
|
|
270
368
|
|
|
271
369
|
const configId = register?.config;
|
|
272
|
-
updateGlobalValueMqtt(
|
|
370
|
+
updateGlobalValueMqtt(configsById[configId], value, time);
|
|
273
371
|
});
|
|
274
372
|
});
|
|
275
373
|
};
|
|
276
374
|
|
|
277
|
-
export const handleChipData = (
|
|
278
|
-
updateGlobalValueMqtt(
|
|
375
|
+
export const handleChipData = (configsById, configId, data, time) => {
|
|
376
|
+
updateGlobalValueMqtt(configsById[configId], data?.v, time);
|
|
279
377
|
};
|
|
280
378
|
|
|
281
|
-
export const handleZigbeeData = (
|
|
379
|
+
export const handleZigbeeData = (
|
|
380
|
+
chip,
|
|
381
|
+
configsById,
|
|
382
|
+
ieee_address,
|
|
383
|
+
data,
|
|
384
|
+
time
|
|
385
|
+
) => {
|
|
282
386
|
const zigbee_gateway = chip?.zigbee_gateway;
|
|
283
387
|
if (!zigbee_gateway) {
|
|
284
388
|
return;
|
|
@@ -293,14 +397,20 @@ export const handleZigbeeData = (chip, configById, ieee_address, data) => {
|
|
|
293
397
|
const device = zigbee_device_by_ieee[ieee_address];
|
|
294
398
|
if (device) {
|
|
295
399
|
(device.configs || []).forEach((config_map) => {
|
|
296
|
-
let value = data?.data[config_map?.key];
|
|
400
|
+
let value = data?.data?.[config_map?.key];
|
|
297
401
|
const configId = config_map?.config;
|
|
298
|
-
updateGlobalValueMqtt(
|
|
402
|
+
updateGlobalValueMqtt(configsById[configId], value, time);
|
|
299
403
|
});
|
|
300
404
|
}
|
|
301
405
|
};
|
|
302
406
|
|
|
303
|
-
export const handleThirdPartyData = (
|
|
407
|
+
export const handleThirdPartyData = (
|
|
408
|
+
chip,
|
|
409
|
+
configsById,
|
|
410
|
+
device_uid,
|
|
411
|
+
data,
|
|
412
|
+
time
|
|
413
|
+
) => {
|
|
304
414
|
const third_party_gateway = chip?.third_party_gateway;
|
|
305
415
|
if (!third_party_gateway) {
|
|
306
416
|
return;
|
|
@@ -314,9 +424,9 @@ export const handleThirdPartyData = (chip, configById, device_uid, data) => {
|
|
|
314
424
|
|
|
315
425
|
const device = third_party_device_by_uid[device_uid];
|
|
316
426
|
device?.configs?.forEach((config_map) => {
|
|
317
|
-
const value = data[config_map?.key];
|
|
427
|
+
const value = data?.[config_map?.key];
|
|
318
428
|
const configId = config_map?.config;
|
|
319
|
-
updateGlobalValueMqtt(
|
|
429
|
+
updateGlobalValueMqtt(configsById[configId], value, time);
|
|
320
430
|
});
|
|
321
431
|
};
|
|
322
432
|
|
|
@@ -325,13 +435,29 @@ export const handleMqttMessage = (
|
|
|
325
435
|
payloadData,
|
|
326
436
|
code,
|
|
327
437
|
chip,
|
|
328
|
-
|
|
438
|
+
configsById
|
|
329
439
|
) => {
|
|
440
|
+
if (topic === `eoh/chip/${code}/is_online`) {
|
|
441
|
+
// Online Status
|
|
442
|
+
// topic: eoh/chip/${code}/is_online
|
|
443
|
+
// payload: {ol: 1}
|
|
444
|
+
handleSelfSensorData(chip, configsById, { is_online: payloadData?.ol });
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (topic === `eoh/chip/${code}/self_sensor`) {
|
|
449
|
+
// Self Sensor
|
|
450
|
+
// topic: eoh/chip/${code}/self_sensor
|
|
451
|
+
// payload: {is_battery: 1, temperature: 27.5, volt: 3.3, ...}
|
|
452
|
+
handleSelfSensorData(chip, configsById, payloadData, payloadData?.time);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
330
456
|
if (topic === `eoh/chip/${code}/data`) {
|
|
331
457
|
// Modbus
|
|
332
458
|
// topic: eoh/chip/${code}/data
|
|
333
459
|
// payload: {data: '01aa010a', 'ack':'1', 'chip_temperature':5333, 'rssi':-61}
|
|
334
|
-
handleModbusData(chip,
|
|
460
|
+
handleModbusData(chip, configsById, payloadData?.data, payloadData?.time);
|
|
335
461
|
}
|
|
336
462
|
|
|
337
463
|
const matchArduinoConfigValue = topic.match(/config\/(\d+)\/value/);
|
|
@@ -339,7 +465,29 @@ export const handleMqttMessage = (
|
|
|
339
465
|
// Arduino
|
|
340
466
|
// topic: eoh/chip/{code}/config/127363/value
|
|
341
467
|
// payload: {v: 2130} or {v: 'on'}
|
|
342
|
-
handleChipData(
|
|
468
|
+
handleChipData(
|
|
469
|
+
configsById,
|
|
470
|
+
matchArduinoConfigValue[1],
|
|
471
|
+
payloadData,
|
|
472
|
+
payloadData?.time
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const matchBatchConfigValue = topic.match(/config_value/);
|
|
477
|
+
if (matchBatchConfigValue) {
|
|
478
|
+
// Arduino
|
|
479
|
+
// topic: eoh/chip/{code}/config_value
|
|
480
|
+
// payload: {1: 2130, 2: 3000}
|
|
481
|
+
// This is a batch update for multiple configs
|
|
482
|
+
Object.keys(payloadData).forEach((key) => {
|
|
483
|
+
handleChipData(
|
|
484
|
+
configsById,
|
|
485
|
+
key,
|
|
486
|
+
{ v: payloadData[key] },
|
|
487
|
+
payloadData.time
|
|
488
|
+
);
|
|
489
|
+
});
|
|
490
|
+
return;
|
|
343
491
|
}
|
|
344
492
|
|
|
345
493
|
const matchZigbeeConfigValue = topic.match(/zigbee\/(.+)\/data/);
|
|
@@ -347,7 +495,13 @@ export const handleMqttMessage = (
|
|
|
347
495
|
// Zigbee
|
|
348
496
|
// topic: eoh/chip/code-123/zigbee/IEEE-123/data
|
|
349
497
|
// payload: {zigbee_data: 'aaa', 'data': {state: 1, state_0: 0}}
|
|
350
|
-
handleZigbeeData(
|
|
498
|
+
handleZigbeeData(
|
|
499
|
+
chip,
|
|
500
|
+
configsById,
|
|
501
|
+
matchZigbeeConfigValue[1],
|
|
502
|
+
payloadData,
|
|
503
|
+
payloadData?.time
|
|
504
|
+
);
|
|
351
505
|
}
|
|
352
506
|
|
|
353
507
|
const matchThirdPartyConfigValue = topic.match(/third_party\/(.+)\/data/);
|
|
@@ -357,9 +511,10 @@ export const handleMqttMessage = (
|
|
|
357
511
|
// payload: {state: 1, temperature: 27.6}
|
|
358
512
|
handleThirdPartyData(
|
|
359
513
|
chip,
|
|
360
|
-
|
|
514
|
+
configsById,
|
|
361
515
|
matchThirdPartyConfigValue[1],
|
|
362
|
-
payloadData
|
|
516
|
+
payloadData,
|
|
517
|
+
payloadData?.time
|
|
363
518
|
);
|
|
364
519
|
}
|
|
365
520
|
};
|
|
@@ -29,6 +29,7 @@ import SetupScriptDelay from '../screens/Automate/AddNewAction/SetupScriptDelay'
|
|
|
29
29
|
import SetupScriptNotify from '../screens/Automate/AddNewAction/SetupScriptNotify';
|
|
30
30
|
import SetupScriptEmail from '../screens/Automate/AddNewAction/SetupScriptEmail';
|
|
31
31
|
import SetupScriptReceiverEmail from '../screens/Automate/AddNewAction/SetupScriptReceiverEmail';
|
|
32
|
+
import SetupScriptReceiverNotify from '../screens/Automate/AddNewAction/SetupScriptReceiverNotify';
|
|
32
33
|
import AddAutomationTypeSmart from '../screens/Automate/AddNewAutoSmart/AddAutomationTypeSmart';
|
|
33
34
|
import AddUnknownTypeSmart from '../screens/Automate/AddNewAutoSmart/AddUnknownTypeSmart';
|
|
34
35
|
import EditActionsList from '../screens/Automate/EditActionsList';
|
|
@@ -44,6 +45,7 @@ import DeviceDetail from '../screens/Device/detail';
|
|
|
44
45
|
import EditDevice from '../screens/Device/EditDevice/index';
|
|
45
46
|
import DeviceInfo from '../screens/DeviceInfo';
|
|
46
47
|
import EmergencySetting from '../screens/EmergencySetting';
|
|
48
|
+
import CreatePassword from '../screens/CreatePassword';
|
|
47
49
|
import EnterPassword from '../screens/EnterPassword';
|
|
48
50
|
import GuestInfo from '../screens/GuestInfo';
|
|
49
51
|
import ManageAccessScreen from '../screens/ManageAccess';
|
|
@@ -374,6 +376,13 @@ export const UnitStack = memo((props) => {
|
|
|
374
376
|
headerShown: false,
|
|
375
377
|
}}
|
|
376
378
|
/>
|
|
379
|
+
<Stack.Screen
|
|
380
|
+
name={Route.CreatePassword}
|
|
381
|
+
component={CreatePassword}
|
|
382
|
+
options={{
|
|
383
|
+
headerShown: false,
|
|
384
|
+
}}
|
|
385
|
+
/>
|
|
377
386
|
<Stack.Screen
|
|
378
387
|
name={Route.EnterPassword}
|
|
379
388
|
component={EnterPassword}
|
|
@@ -465,6 +474,13 @@ export const UnitStack = memo((props) => {
|
|
|
465
474
|
headerShown: false,
|
|
466
475
|
}}
|
|
467
476
|
/>
|
|
477
|
+
<Stack.Screen
|
|
478
|
+
name={Route.SetupScriptReceiverNotify}
|
|
479
|
+
component={SetupScriptReceiverNotify}
|
|
480
|
+
options={{
|
|
481
|
+
headerShown: false,
|
|
482
|
+
}}
|
|
483
|
+
/>
|
|
468
484
|
<Stack.Screen
|
|
469
485
|
name={Route.UpdateReceiverEmailScript}
|
|
470
486
|
component={UpdateReceiverEmailScript}
|
|
@@ -8,6 +8,7 @@ import Text from '../../commons/Text';
|
|
|
8
8
|
import GatewayIcon from '../../../assets/images/AddNewDevice/gateway-icon.svg';
|
|
9
9
|
import _TextInput from '../../commons/Form/TextInput';
|
|
10
10
|
import AccessibilityLabel from '../../configs/AccessibilityLabel';
|
|
11
|
+
import useInAppReview from '../../hooks/Review/useInAppReview';
|
|
11
12
|
import Routes from '../../utils/Route';
|
|
12
13
|
import styles from './RenameNewDevicesStyles';
|
|
13
14
|
import { axiosGet, axiosPost } from '../../utils/Apis/axios';
|
|
@@ -33,6 +34,7 @@ const RenameNewDevices = memo(({ route }) => {
|
|
|
33
34
|
const [selectedDevice, setSelectedDevice] = useState([]);
|
|
34
35
|
const [originalDevices, setOriginalDevices] = useState([]);
|
|
35
36
|
const [isAutoFocus, setIsAutoFocus] = useState(true);
|
|
37
|
+
const { allowInAppReview } = useInAppReview(false);
|
|
36
38
|
|
|
37
39
|
const renameLabel = AccessibilityLabel.CONNECTED_DEVICE_RENAME_DEVICE;
|
|
38
40
|
|
|
@@ -94,6 +96,7 @@ const RenameNewDevices = memo(({ route }) => {
|
|
|
94
96
|
setIsDisabled(false);
|
|
95
97
|
clearTimeout(clear);
|
|
96
98
|
}, 3000);
|
|
99
|
+
allowInAppReview();
|
|
97
100
|
goBackUnitDetail();
|
|
98
101
|
return;
|
|
99
102
|
}
|
|
@@ -135,6 +138,7 @@ const RenameNewDevices = memo(({ route }) => {
|
|
|
135
138
|
}
|
|
136
139
|
|
|
137
140
|
if (success1 || success2) {
|
|
141
|
+
allowInAppReview();
|
|
138
142
|
goBackUnitDetail();
|
|
139
143
|
}
|
|
140
144
|
if (!success1) {
|
|
@@ -145,6 +149,7 @@ const RenameNewDevices = memo(({ route }) => {
|
|
|
145
149
|
}
|
|
146
150
|
setLoading(false);
|
|
147
151
|
}, [
|
|
152
|
+
allowInAppReview,
|
|
148
153
|
goBackUnitDetail,
|
|
149
154
|
info,
|
|
150
155
|
isChangedName,
|
|
@@ -14,6 +14,18 @@ import api from '../../../utils/Apis/axios';
|
|
|
14
14
|
import { CheckBoxCustom } from '../../Sharing/Components';
|
|
15
15
|
import RenameNewDevices from '../RenameNewDevices';
|
|
16
16
|
|
|
17
|
+
const mockAllowInAppReview = jest.fn();
|
|
18
|
+
const mockAskReview = jest.fn();
|
|
19
|
+
jest.mock('../../../hooks/Review/useInAppReview', () => {
|
|
20
|
+
return {
|
|
21
|
+
__esModule: true,
|
|
22
|
+
default: jest.fn(() => ({
|
|
23
|
+
allowInAppReview: mockAllowInAppReview,
|
|
24
|
+
askReview: mockAskReview,
|
|
25
|
+
})),
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
17
29
|
const wrapComponent = (route) => (
|
|
18
30
|
<SCProvider initState={mockSCStore({})}>
|
|
19
31
|
<RenameNewDevices route={route} />
|
|
@@ -103,6 +115,8 @@ describe('Test rename new devices', () => {
|
|
|
103
115
|
};
|
|
104
116
|
|
|
105
117
|
beforeEach(() => {
|
|
118
|
+
mockAllowInAppReview.mockClear();
|
|
119
|
+
mockAskReview.mockClear();
|
|
106
120
|
navigate.mockClear();
|
|
107
121
|
mock.reset();
|
|
108
122
|
route = {
|
|
@@ -403,6 +417,8 @@ describe('Test rename new devices', () => {
|
|
|
403
417
|
],
|
|
404
418
|
})
|
|
405
419
|
);
|
|
420
|
+
expect(mockAllowInAppReview).toHaveBeenCalled();
|
|
421
|
+
expect(mockAskReview).not.toHaveBeenCalled();
|
|
406
422
|
mockedDispatchNavigate();
|
|
407
423
|
});
|
|
408
424
|
|
|
@@ -443,6 +459,8 @@ describe('Test rename new devices', () => {
|
|
|
443
459
|
});
|
|
444
460
|
|
|
445
461
|
expect(mock.history.post).toHaveLength(0);
|
|
462
|
+
expect(mockAllowInAppReview).toHaveBeenCalled();
|
|
463
|
+
expect(mockAskReview).not.toHaveBeenCalled();
|
|
446
464
|
mockedDispatchNavigate();
|
|
447
465
|
});
|
|
448
466
|
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState, useEffect, memo } from 'react';
|
|
2
|
+
import { FlatList, View } from 'react-native';
|
|
3
|
+
import { useNavigation } from '@react-navigation/native';
|
|
4
|
+
import CheckBox from '@react-native-community/checkbox';
|
|
5
|
+
import styles from './Styles/ReceiverSelectStyles';
|
|
6
|
+
import { CircleView, HeaderCustom, Text } from '../../../commons';
|
|
7
|
+
import { useTranslations } from '../../../hooks/Common/useTranslations';
|
|
8
|
+
|
|
9
|
+
import BottomButtonView from '../../../commons/BottomButtonView';
|
|
10
|
+
import { Search } from '../../../commons/DevMode';
|
|
11
|
+
import { convertToSlug } from '../../../utils/Functions/Search';
|
|
12
|
+
import { axiosGet } from '../../../utils/Apis/axios';
|
|
13
|
+
import { API, Colors } from '../../../configs';
|
|
14
|
+
import { useSCContextSelector } from '../../../context';
|
|
15
|
+
import { Image } from 'react-native';
|
|
16
|
+
|
|
17
|
+
const ReceiverSelect = ({
|
|
18
|
+
listUser,
|
|
19
|
+
unitId,
|
|
20
|
+
title,
|
|
21
|
+
setListUser,
|
|
22
|
+
onLoadOption,
|
|
23
|
+
onNext,
|
|
24
|
+
}) => {
|
|
25
|
+
const t = useTranslations();
|
|
26
|
+
const { goBack } = useNavigation();
|
|
27
|
+
const [members, setMembers] = useState([]);
|
|
28
|
+
const [search, setSearch] = useState('');
|
|
29
|
+
const currentUserId = useSCContextSelector(
|
|
30
|
+
(state) => state.auth.account.user.id
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const loadMembers = useCallback(async () => {
|
|
34
|
+
const { success, data } = await axiosGet(API.SHARE.UNITS_MEMBERS(unitId));
|
|
35
|
+
if (success) {
|
|
36
|
+
setMembers(
|
|
37
|
+
data.map((item) => ({
|
|
38
|
+
...item,
|
|
39
|
+
...onLoadOption(item),
|
|
40
|
+
}))
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}, [unitId, onLoadOption]);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
loadMembers();
|
|
47
|
+
}, [loadMembers]);
|
|
48
|
+
|
|
49
|
+
const filterMembers = useMemo(() => {
|
|
50
|
+
return members.filter((member) => {
|
|
51
|
+
const text = convertToSlug(search);
|
|
52
|
+
const isNameMatch = convertToSlug(member.name ?? '').includes(text);
|
|
53
|
+
const isPhoneMatch = convertToSlug(member.phone_number ?? '').includes(
|
|
54
|
+
text
|
|
55
|
+
);
|
|
56
|
+
const isEmailMatch = convertToSlug(member.email ?? '').includes(text);
|
|
57
|
+
return isNameMatch || isPhoneMatch || isEmailMatch;
|
|
58
|
+
});
|
|
59
|
+
}, [members, search]);
|
|
60
|
+
|
|
61
|
+
const arrColor = useMemo(
|
|
62
|
+
() => [
|
|
63
|
+
Colors.GeekBlue3,
|
|
64
|
+
Colors.Purple3,
|
|
65
|
+
Colors.Orange3,
|
|
66
|
+
Colors.Volcano3,
|
|
67
|
+
Colors.Blue9,
|
|
68
|
+
Colors.Green3,
|
|
69
|
+
Colors.Cyan2,
|
|
70
|
+
],
|
|
71
|
+
[]
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const selectableIds = useMemo(
|
|
75
|
+
() =>
|
|
76
|
+
members.filter((member) => !member.disabled).map((member) => member.id),
|
|
77
|
+
[members]
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const allSelected = useMemo(
|
|
81
|
+
() => selectableIds.length === listUser.length,
|
|
82
|
+
[listUser, selectableIds]
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const onChecked = useCallback(
|
|
86
|
+
(id) => (checked) => {
|
|
87
|
+
setListUser((prev) =>
|
|
88
|
+
checked ? [...prev, id] : prev.filter((userId) => userId !== id)
|
|
89
|
+
);
|
|
90
|
+
},
|
|
91
|
+
[setListUser]
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const onDeselectAll = useCallback(() => {
|
|
95
|
+
setListUser([]);
|
|
96
|
+
}, [setListUser]);
|
|
97
|
+
|
|
98
|
+
const onSelectAll = useCallback(() => {
|
|
99
|
+
setListUser(selectableIds);
|
|
100
|
+
}, [selectableIds, setListUser]);
|
|
101
|
+
|
|
102
|
+
const onToggleAll = useCallback(
|
|
103
|
+
(checked) => {
|
|
104
|
+
if (checked) {
|
|
105
|
+
onSelectAll();
|
|
106
|
+
} else {
|
|
107
|
+
onDeselectAll();
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
[onSelectAll, onDeselectAll]
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const RowMember = memo(({ member, index, onValueChange }) => {
|
|
114
|
+
const { id, name, avatar, share_id, label, invalidLabel, disabled } =
|
|
115
|
+
member;
|
|
116
|
+
const [role, roleColor] = useMemo(() => {
|
|
117
|
+
if (!share_id) {
|
|
118
|
+
return [t('owner'), Colors.Primary];
|
|
119
|
+
}
|
|
120
|
+
if (id === currentUserId) {
|
|
121
|
+
return [t('me'), Colors.Primary];
|
|
122
|
+
}
|
|
123
|
+
return [t('member'), Colors.Gray6];
|
|
124
|
+
}, [share_id, id]);
|
|
125
|
+
|
|
126
|
+
const firstWordsInName = useMemo(() => {
|
|
127
|
+
return name.charAt();
|
|
128
|
+
}, [name]);
|
|
129
|
+
|
|
130
|
+
const circleColor = arrColor[index % arrColor.length];
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<View style={styles.rowContainer}>
|
|
134
|
+
<View style={styles.border}>
|
|
135
|
+
<CheckBox
|
|
136
|
+
disabled={disabled}
|
|
137
|
+
lineWidth={4}
|
|
138
|
+
value={listUser.includes(id)}
|
|
139
|
+
onValueChange={onValueChange(id)}
|
|
140
|
+
style={styles.checkbox}
|
|
141
|
+
/>
|
|
142
|
+
<View style={styles.paddingLeft16}>
|
|
143
|
+
{avatar ? (
|
|
144
|
+
<Image source={{ uri: avatar }} style={styles.avatar} />
|
|
145
|
+
) : (
|
|
146
|
+
<CircleView size={40} backgroundColor={circleColor} center>
|
|
147
|
+
<Text color={Colors.White}>{firstWordsInName}</Text>
|
|
148
|
+
</CircleView>
|
|
149
|
+
)}
|
|
150
|
+
</View>
|
|
151
|
+
<View style={styles.paddingLeft16}>
|
|
152
|
+
<Text style={styles.titleName}>{name}</Text>
|
|
153
|
+
{label ? (
|
|
154
|
+
<Text style={styles.status}>{label}</Text>
|
|
155
|
+
) : (
|
|
156
|
+
<Text style={styles.invalid}>{invalidLabel}</Text>
|
|
157
|
+
)}
|
|
158
|
+
</View>
|
|
159
|
+
<View style={styles.endFlex}>
|
|
160
|
+
<Text style={[styles.textRole, { color: roleColor }]}>{role}</Text>
|
|
161
|
+
</View>
|
|
162
|
+
</View>
|
|
163
|
+
</View>
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<View style={styles.wrap}>
|
|
169
|
+
<HeaderCustom isShowClose onClose={goBack} title={title} />
|
|
170
|
+
|
|
171
|
+
<View style={styles.wrapSelectAll}>
|
|
172
|
+
<Search onSearch={setSearch} />
|
|
173
|
+
<View style={styles.wrapCheckboxSelectAll}>
|
|
174
|
+
<CheckBox
|
|
175
|
+
value={allSelected}
|
|
176
|
+
onValueChange={onToggleAll}
|
|
177
|
+
style={styles.checkboxSelectAll}
|
|
178
|
+
/>
|
|
179
|
+
<Text>{allSelected ? t('deselect_all') : t('select_all')}</Text>
|
|
180
|
+
</View>
|
|
181
|
+
</View>
|
|
182
|
+
<View style={styles.borderSelectAll} />
|
|
183
|
+
|
|
184
|
+
<FlatList
|
|
185
|
+
data={filterMembers}
|
|
186
|
+
renderItem={({ item, index }) => (
|
|
187
|
+
<RowMember member={item} index={index} onValueChange={onChecked} />
|
|
188
|
+
)}
|
|
189
|
+
keyExtractor={(item) => item.id.toString()}
|
|
190
|
+
ListEmptyComponent={
|
|
191
|
+
<View style={styles.viewEmpty}>
|
|
192
|
+
<Text style={styles.textCenter}>{t('no_member')}</Text>
|
|
193
|
+
</View>
|
|
194
|
+
}
|
|
195
|
+
/>
|
|
196
|
+
<View style={styles.container}>
|
|
197
|
+
<BottomButtonView
|
|
198
|
+
style={styles.bottomButtonView}
|
|
199
|
+
mainTitle={t('done')}
|
|
200
|
+
onPressMain={onNext}
|
|
201
|
+
typeMain={listUser.length ? 'primary' : 'disabled'}
|
|
202
|
+
/>
|
|
203
|
+
</View>
|
|
204
|
+
</View>
|
|
205
|
+
);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export default ReceiverSelect;
|
|
@@ -42,7 +42,7 @@ const SetupScriptEmail = ({ route }) => {
|
|
|
42
42
|
}, [navigate, automate, initialUnitId, formData]);
|
|
43
43
|
|
|
44
44
|
const canSave = useMemo(() => {
|
|
45
|
-
const { title, message } = formData
|
|
45
|
+
const { title, message } = formData;
|
|
46
46
|
return !!title && !!message;
|
|
47
47
|
}, [formData]);
|
|
48
48
|
|