@eohjsc/react-native-smart-city 0.7.16 → 0.7.18
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 +1 -1
- package/src/commons/Unit/__test__/SharedUnit.test.js +57 -0
- package/src/configs/AccessibilityLabel.js +4 -0
- package/src/hooks/Common/useBlockBack.js +10 -2
- package/src/screens/Automate/ScriptDetail/Styles/indexStyles.js +2 -2
- package/src/screens/Automate/ScriptDetail/__test__/index.test.js +257 -32
- package/src/screens/Automate/ScriptDetail/index.js +53 -32
- package/src/utils/I18n/translations/vi.js +2 -2
package/package.json
CHANGED
|
@@ -109,6 +109,63 @@ describe('Test SharedUnit', () => {
|
|
|
109
109
|
});
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
+
it('Test remove pin and start shared unit', async () => {
|
|
113
|
+
const navigation = useNavigation();
|
|
114
|
+
item.is_pin = true;
|
|
115
|
+
item.is_star = true;
|
|
116
|
+
const mockedRenewItem = jest.fn();
|
|
117
|
+
await act(async () => {
|
|
118
|
+
tree = await create(
|
|
119
|
+
wrapComponent(item, navigation, undefined, mockedRenewItem)
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
const instance = tree.root;
|
|
123
|
+
|
|
124
|
+
const iconAddPinSharedUnit = instance.findAll(
|
|
125
|
+
(el) =>
|
|
126
|
+
el.props.accessibilityLabel ===
|
|
127
|
+
AccessibilityLabel.ICON_ADD_PIN_SHARED_UNIT + '-69'
|
|
128
|
+
);
|
|
129
|
+
const iconAddStarSharedUnit = instance.findAll(
|
|
130
|
+
(el) =>
|
|
131
|
+
el.props.accessibilityLabel ===
|
|
132
|
+
AccessibilityLabel.ICON_ADD_STAR_SHARED_UNIT + '-69'
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const iconRemovePinSharedUnit = instance.find(
|
|
136
|
+
(el) =>
|
|
137
|
+
el.props.accessibilityLabel ===
|
|
138
|
+
AccessibilityLabel.ICON_REMOVE_PIN_SHARED_UNIT + '-69'
|
|
139
|
+
);
|
|
140
|
+
mock.onPost(API.UNIT.UNPIN_UNIT(3)).reply(200);
|
|
141
|
+
await act(async () => {
|
|
142
|
+
iconRemovePinSharedUnit.props.onPress();
|
|
143
|
+
});
|
|
144
|
+
mock.onPost(API.UNIT.UNPIN_UNIT(3)).reply(400);
|
|
145
|
+
await act(async () => {
|
|
146
|
+
iconRemovePinSharedUnit.props.onPress();
|
|
147
|
+
});
|
|
148
|
+
expect(mockedRenewItem).toHaveBeenCalledTimes(1);
|
|
149
|
+
|
|
150
|
+
const iconRemoveStarSharedUnit = instance.find(
|
|
151
|
+
(el) =>
|
|
152
|
+
el.props.accessibilityLabel ===
|
|
153
|
+
AccessibilityLabel.ICON_REMOVE_STAR_SHARED_UNIT + '-69'
|
|
154
|
+
);
|
|
155
|
+
mock.onPost(API.UNIT.UNSTAR_UNIT(3)).reply(200);
|
|
156
|
+
await act(async () => {
|
|
157
|
+
iconRemoveStarSharedUnit.props.onPress();
|
|
158
|
+
});
|
|
159
|
+
mock.onPost(API.UNIT.UNSTAR_UNIT(3)).reply(400);
|
|
160
|
+
await act(async () => {
|
|
161
|
+
iconRemoveStarSharedUnit.props.onPress();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
expect(mockedRenewItem).toHaveBeenCalledTimes(2);
|
|
165
|
+
expect(iconAddPinSharedUnit).toHaveLength(0);
|
|
166
|
+
expect(iconAddStarSharedUnit).toHaveLength(0);
|
|
167
|
+
});
|
|
168
|
+
|
|
112
169
|
// it('test create SharedUnit unit without unit owner', async () => {
|
|
113
170
|
// const navigation = useNavigation();
|
|
114
171
|
// delete unit.owner_name;
|
|
@@ -251,6 +251,10 @@ export default {
|
|
|
251
251
|
AUTOMATE_TITLE_NOTIFY: 'AUTOMATE_TITLE_NOTIFY',
|
|
252
252
|
AUTOMATE_MESSAGE_NOTIFY: 'AUTOMATE_MESSAGE_NOTIFY',
|
|
253
253
|
AUTOMATE_INPUT_DELAY: 'AUTOMATE_INPUT_DELAY',
|
|
254
|
+
AUTOMATE_SHOW_LOCAL_CONTROL: 'AUTOMATE_SHOW_LOCAL_CONTROL',
|
|
255
|
+
AUTOMATE_DISABLE_LOCAL_CONTROL: 'AUTOMATE_DISABLE_LOCAL_CONTROL',
|
|
256
|
+
AUTOMATE_ENABLE_LOCAL_CONTROL: 'AUTOMATE_ENABLE_LOCAL_CONTROL',
|
|
257
|
+
|
|
254
258
|
// Parking input maunaly spot
|
|
255
259
|
PARKING_SPOT_INFO_BUTTON: 'PARKING_SPOT_INFO_BUTTON',
|
|
256
260
|
PARKING_SPOT_CONFIRM_SPOT: 'PARKING_SPOT_CONFIRM_SPOT',
|
|
@@ -5,6 +5,7 @@ import { BackHandler, Platform } from 'react-native';
|
|
|
5
5
|
export const useBlockBack = (actionBack) => {
|
|
6
6
|
const navigation = useNavigation();
|
|
7
7
|
const isListening = useRef(false);
|
|
8
|
+
const isBeforeRemoveHandled = useRef(false);
|
|
8
9
|
|
|
9
10
|
const blockBack = useCallback(() => {
|
|
10
11
|
actionBack && actionBack();
|
|
@@ -13,8 +14,15 @@ export const useBlockBack = (actionBack) => {
|
|
|
13
14
|
|
|
14
15
|
const blockBeforeRemove = useCallback(
|
|
15
16
|
(e) => {
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
if (isBeforeRemoveHandled.current) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const { type } = e.data.action;
|
|
21
|
+
if (type === 'GO_BACK' || type === 'POP') {
|
|
22
|
+
isBeforeRemoveHandled.current = true;
|
|
23
|
+
e.preventDefault();
|
|
24
|
+
blockBack();
|
|
25
|
+
}
|
|
18
26
|
},
|
|
19
27
|
[blockBack]
|
|
20
28
|
);
|
|
@@ -42,11 +42,16 @@ describe('Test ScriptDetail', () => {
|
|
|
42
42
|
const mockAddListener = useNavigation().addListener;
|
|
43
43
|
const mockedNavigate = useNavigation().navigate;
|
|
44
44
|
const mockedDispatch = useNavigation().dispatch;
|
|
45
|
+
const mockedGetState = useNavigation().getState;
|
|
46
|
+
|
|
45
47
|
beforeEach(() => {
|
|
46
48
|
mockGoBack.mockClear();
|
|
47
49
|
mockAddListener.mockClear();
|
|
48
50
|
mockedNavigate.mockClear();
|
|
49
51
|
mockedDispatch.mockClear();
|
|
52
|
+
mockedGetState.mockReturnValue({
|
|
53
|
+
routes: [{ name: 'Home' }],
|
|
54
|
+
});
|
|
50
55
|
global.mockedNavigate.mockClear();
|
|
51
56
|
mock.reset();
|
|
52
57
|
route = {
|
|
@@ -111,12 +116,15 @@ describe('Test ScriptDetail', () => {
|
|
|
111
116
|
};
|
|
112
117
|
});
|
|
113
118
|
|
|
119
|
+
afterEach(() => {
|
|
120
|
+
jest.useRealTimers();
|
|
121
|
+
});
|
|
122
|
+
|
|
114
123
|
const expectMockedDispatch = (screen, screenParams) => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
expect(updatedState).toEqual({
|
|
124
|
+
act(() => {
|
|
125
|
+
jest.runAllTimers();
|
|
126
|
+
});
|
|
127
|
+
expect(mockedDispatch).toHaveBeenCalledWith({
|
|
120
128
|
type: 'RESET',
|
|
121
129
|
payload: {
|
|
122
130
|
index: 2,
|
|
@@ -136,6 +144,12 @@ describe('Test ScriptDetail', () => {
|
|
|
136
144
|
},
|
|
137
145
|
});
|
|
138
146
|
expect(mockedDispatch).toHaveBeenCalledTimes(1);
|
|
147
|
+
mockedGetState.mockReturnValue({
|
|
148
|
+
routes: [
|
|
149
|
+
{ name: 'Home' },
|
|
150
|
+
{ name: Routes.ScriptDetail, params: route.params },
|
|
151
|
+
],
|
|
152
|
+
});
|
|
139
153
|
mockedDispatch.mockClear();
|
|
140
154
|
};
|
|
141
155
|
|
|
@@ -359,6 +373,7 @@ describe('Test ScriptDetail', () => {
|
|
|
359
373
|
});
|
|
360
374
|
|
|
361
375
|
it('test press add action', async () => {
|
|
376
|
+
jest.useFakeTimers();
|
|
362
377
|
data = [
|
|
363
378
|
{
|
|
364
379
|
id: 1,
|
|
@@ -425,6 +440,50 @@ describe('Test ScriptDetail', () => {
|
|
|
425
440
|
expectMockedDispatch(Routes.SetupScriptDelay, {
|
|
426
441
|
automate: route.params.preAutomate,
|
|
427
442
|
});
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
it('test press add email and sms', async () => {
|
|
446
|
+
jest.useFakeTimers();
|
|
447
|
+
data = [
|
|
448
|
+
{
|
|
449
|
+
id: 1,
|
|
450
|
+
unit: 1,
|
|
451
|
+
script_items: null,
|
|
452
|
+
},
|
|
453
|
+
];
|
|
454
|
+
mock.onGet(API.AUTOMATE.SCRIPT(1)).reply(200, data);
|
|
455
|
+
const auth = {
|
|
456
|
+
account: {
|
|
457
|
+
user: {
|
|
458
|
+
permissions: {
|
|
459
|
+
max_actions_per_automation: 2,
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
};
|
|
464
|
+
await act(async () => {
|
|
465
|
+
tree = await create(wrapComponent(route, { auth }));
|
|
466
|
+
});
|
|
467
|
+
const instance = tree.root;
|
|
468
|
+
const button = instance.find(
|
|
469
|
+
(el) =>
|
|
470
|
+
el.props.accessibilityLabel ===
|
|
471
|
+
AccessibilityLabel.BUTTON_ADD_SCRIPT_ACTION &&
|
|
472
|
+
el.type === TouchableOpacity
|
|
473
|
+
);
|
|
474
|
+
await act(async () => {
|
|
475
|
+
await button.props.onPress();
|
|
476
|
+
});
|
|
477
|
+
const texts = instance.findByType(AddActionScript);
|
|
478
|
+
expect(texts.props.isVisible).toBeTruthy();
|
|
479
|
+
|
|
480
|
+
const listScriptActions = instance.findAll(
|
|
481
|
+
(el) =>
|
|
482
|
+
el.props.accessibilityLabel ===
|
|
483
|
+
AccessibilityLabel.AUTOMATE_LIST_SCRIPT_ACTION &&
|
|
484
|
+
el.type === TouchableOpacity
|
|
485
|
+
);
|
|
486
|
+
expect(listScriptActions).toHaveLength(5);
|
|
428
487
|
await act(async () => {
|
|
429
488
|
await listScriptActions[3].props.onPress();
|
|
430
489
|
});
|
|
@@ -433,6 +492,103 @@ describe('Test ScriptDetail', () => {
|
|
|
433
492
|
automate: route.params.preAutomate,
|
|
434
493
|
unitId: route.params.preAutomate.unit,
|
|
435
494
|
});
|
|
495
|
+
await act(async () => {
|
|
496
|
+
await listScriptActions[4].props.onPress();
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
expectMockedDispatch(Routes.SetupScriptSms, {
|
|
500
|
+
automate: route.params.preAutomate,
|
|
501
|
+
unitId: route.params.preAutomate.unit,
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it('test press add action when has closeScreen is present in stacks', async () => {
|
|
506
|
+
jest.useFakeTimers();
|
|
507
|
+
data = [
|
|
508
|
+
{
|
|
509
|
+
id: 1,
|
|
510
|
+
unit: 1,
|
|
511
|
+
script_items: null,
|
|
512
|
+
},
|
|
513
|
+
];
|
|
514
|
+
mock.onGet(API.AUTOMATE.SCRIPT(1)).reply(200, data);
|
|
515
|
+
const auth = {
|
|
516
|
+
account: {
|
|
517
|
+
user: {
|
|
518
|
+
permissions: {
|
|
519
|
+
max_actions_per_automation: 2,
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
};
|
|
524
|
+
route.params.closeScreen = Routes.UnitDetail;
|
|
525
|
+
await act(async () => {
|
|
526
|
+
tree = await create(wrapComponent(route, { auth }));
|
|
527
|
+
});
|
|
528
|
+
const instance = tree.root;
|
|
529
|
+
const button = instance.find(
|
|
530
|
+
(el) =>
|
|
531
|
+
el.props.accessibilityLabel ===
|
|
532
|
+
AccessibilityLabel.BUTTON_ADD_SCRIPT_ACTION &&
|
|
533
|
+
el.type === TouchableOpacity
|
|
534
|
+
);
|
|
535
|
+
await act(async () => {
|
|
536
|
+
await button.props.onPress();
|
|
537
|
+
});
|
|
538
|
+
const texts = instance.findByType(AddActionScript);
|
|
539
|
+
expect(texts.props.isVisible).toBeTruthy();
|
|
540
|
+
|
|
541
|
+
const listScriptActions = instance.findAll(
|
|
542
|
+
(el) =>
|
|
543
|
+
el.props.accessibilityLabel ===
|
|
544
|
+
AccessibilityLabel.AUTOMATE_LIST_SCRIPT_ACTION &&
|
|
545
|
+
el.type === TouchableOpacity
|
|
546
|
+
);
|
|
547
|
+
expect(listScriptActions).toHaveLength(5);
|
|
548
|
+
|
|
549
|
+
mockedGetState.mockReturnValue({
|
|
550
|
+
routes: [
|
|
551
|
+
{ name: 'Home' },
|
|
552
|
+
{ name: Routes.UnitDetail, params: { unitId: 1 } },
|
|
553
|
+
{ name: Routes.ScriptDetail, params: route.params },
|
|
554
|
+
],
|
|
555
|
+
});
|
|
556
|
+
await act(async () => {
|
|
557
|
+
await listScriptActions[0].props.onPress();
|
|
558
|
+
});
|
|
559
|
+
await act(async () => {
|
|
560
|
+
jest.runAllTimers();
|
|
561
|
+
});
|
|
562
|
+
expect(mockedDispatch).toHaveBeenCalledWith({
|
|
563
|
+
type: 'RESET',
|
|
564
|
+
payload: {
|
|
565
|
+
index: 3,
|
|
566
|
+
routes: [
|
|
567
|
+
{
|
|
568
|
+
name: 'Home',
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
name: Routes.UnitDetail,
|
|
572
|
+
params: { unitId: 1 },
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
name: Routes.ScriptDetail,
|
|
576
|
+
params: route.params,
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
name: Routes.SelectControlDevices,
|
|
580
|
+
params: {
|
|
581
|
+
unitId: route.params.preAutomate.unit,
|
|
582
|
+
automateId: route.params.preAutomate.id,
|
|
583
|
+
numberActionCanAdd: 2,
|
|
584
|
+
closeScreen: undefined,
|
|
585
|
+
routeName: null,
|
|
586
|
+
},
|
|
587
|
+
},
|
|
588
|
+
],
|
|
589
|
+
},
|
|
590
|
+
});
|
|
591
|
+
expect(mockedDispatch).toHaveBeenCalledTimes(1);
|
|
436
592
|
});
|
|
437
593
|
|
|
438
594
|
it('test press disable script', async () => {
|
|
@@ -573,6 +729,7 @@ describe('Test ScriptDetail', () => {
|
|
|
573
729
|
_testGoToActivityLog(3, AUTOMATE_TYPE.VALUE_CHANGE, 'automate', undefined);
|
|
574
730
|
|
|
575
731
|
it('Test render textCondition value change >', async () => {
|
|
732
|
+
jest.useFakeTimers();
|
|
576
733
|
const automate = {
|
|
577
734
|
can_edit: true,
|
|
578
735
|
type: AUTOMATE_TYPE.VALUE_CHANGE,
|
|
@@ -613,33 +770,10 @@ describe('Test ScriptDetail', () => {
|
|
|
613
770
|
menuActionMore.props.listMenuItem[2].doAction();
|
|
614
771
|
});
|
|
615
772
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
const updatedState = mockedDispatch.mock.calls[0][0](mockState);
|
|
620
|
-
expect(updatedState).toEqual({
|
|
621
|
-
type: 'RESET',
|
|
622
|
-
payload: {
|
|
623
|
-
index: 2,
|
|
624
|
-
routes: [
|
|
625
|
-
{
|
|
626
|
-
name: 'Home',
|
|
627
|
-
},
|
|
628
|
-
{
|
|
629
|
-
name: Routes.ScriptDetail,
|
|
630
|
-
params: route.params,
|
|
631
|
-
},
|
|
632
|
-
{
|
|
633
|
-
name: Routes.AddUnknownTypeSmart,
|
|
634
|
-
params: {
|
|
635
|
-
automate: automate,
|
|
636
|
-
closeScreen: undefined,
|
|
637
|
-
},
|
|
638
|
-
},
|
|
639
|
-
],
|
|
640
|
-
},
|
|
773
|
+
expectMockedDispatch(Routes.AddUnknownTypeSmart, {
|
|
774
|
+
automate: automate,
|
|
775
|
+
closeScreen: undefined,
|
|
641
776
|
});
|
|
642
|
-
expect(mockedDispatch).toHaveBeenCalledTimes(1);
|
|
643
777
|
|
|
644
778
|
expect(menuActionMore.props.listMenuItem[0].text).toEqual('Device display');
|
|
645
779
|
await act(async () => {
|
|
@@ -755,7 +889,10 @@ describe('Test ScriptDetail', () => {
|
|
|
755
889
|
|
|
756
890
|
// beforeRemove test when swipe to back
|
|
757
891
|
const navigation = useNavigation();
|
|
758
|
-
const beforeRemoveEvent = {
|
|
892
|
+
const beforeRemoveEvent = {
|
|
893
|
+
preventDefault: jest.fn(),
|
|
894
|
+
data: { action: { type: 'GO_BACK' } },
|
|
895
|
+
};
|
|
759
896
|
const beforeRemoveListener = navigation.addListener.mock.calls.find(
|
|
760
897
|
([event]) => event === 'beforeRemove'
|
|
761
898
|
)[1];
|
|
@@ -771,6 +908,34 @@ describe('Test ScriptDetail', () => {
|
|
|
771
908
|
});
|
|
772
909
|
});
|
|
773
910
|
|
|
911
|
+
it('test skip block back when navigate on ios', async () => {
|
|
912
|
+
Platform.OS = 'ios';
|
|
913
|
+
route.params.closeScreen = Routes.UnitDetail;
|
|
914
|
+
route.params.preAutomate.unit = 2;
|
|
915
|
+
|
|
916
|
+
await act(async () => {
|
|
917
|
+
await create(wrapComponent(route));
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
// beforeRemove test when navigate
|
|
921
|
+
const navigation = useNavigation();
|
|
922
|
+
const beforeRemoveEvent = {
|
|
923
|
+
preventDefault: jest.fn(),
|
|
924
|
+
data: { action: { type: 'NAVIGATE' } },
|
|
925
|
+
};
|
|
926
|
+
const beforeRemoveListener = navigation.addListener.mock.calls.find(
|
|
927
|
+
([event]) => event === 'beforeRemove'
|
|
928
|
+
)[1];
|
|
929
|
+
|
|
930
|
+
await act(async () => {
|
|
931
|
+
beforeRemoveListener(beforeRemoveEvent);
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
expect(mockAddListener).toHaveBeenCalled();
|
|
935
|
+
expect(beforeRemoveEvent.preventDefault).not.toHaveBeenCalled();
|
|
936
|
+
expect(mockedNavigate).not.toHaveBeenCalled();
|
|
937
|
+
});
|
|
938
|
+
|
|
774
939
|
it('test navigate to UnitDetail on event hardwareBackPress', async () => {
|
|
775
940
|
Platform.OS = 'android';
|
|
776
941
|
jest.spyOn(BackHandler, 'addEventListener');
|
|
@@ -797,4 +962,64 @@ describe('Test ScriptDetail', () => {
|
|
|
797
962
|
unitId: 2,
|
|
798
963
|
});
|
|
799
964
|
});
|
|
965
|
+
|
|
966
|
+
it('test enable local control', async () => {
|
|
967
|
+
mock.onPost(API.AUTOMATE.ENABLE_LOCAL_CONTROL(1)).reply(200);
|
|
968
|
+
mock
|
|
969
|
+
.onGet(API.DEV_MODE.GATEWAY.SHARED())
|
|
970
|
+
.reply(200, [{ name: 'Chip', id: 1 }]);
|
|
971
|
+
|
|
972
|
+
await act(async () => {
|
|
973
|
+
tree = await create(wrapComponent(route));
|
|
974
|
+
});
|
|
975
|
+
const instance = tree.root;
|
|
976
|
+
const show_local_control = instance.find(
|
|
977
|
+
(el) =>
|
|
978
|
+
el.props.accessibilityLabel ===
|
|
979
|
+
AccessibilityLabel.AUTOMATE_SHOW_LOCAL_CONTROL &&
|
|
980
|
+
el.type === TouchableOpacity
|
|
981
|
+
);
|
|
982
|
+
await act(async () => {
|
|
983
|
+
await show_local_control.props.onPress();
|
|
984
|
+
});
|
|
985
|
+
const disable = instance.find(
|
|
986
|
+
(el) =>
|
|
987
|
+
el.props.accessibilityLabel ===
|
|
988
|
+
AccessibilityLabel.AUTOMATE_ENABLE_LOCAL_CONTROL &&
|
|
989
|
+
el.type === TouchableOpacity
|
|
990
|
+
);
|
|
991
|
+
await act(async () => {
|
|
992
|
+
await disable.props.onPress();
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
expect(mock.history.post[0].url).toEqual(
|
|
996
|
+
API.AUTOMATE.ENABLE_LOCAL_CONTROL(1)
|
|
997
|
+
);
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
it('test disable local control', async () => {
|
|
1001
|
+
await act(async () => {
|
|
1002
|
+
tree = await create(wrapComponent(route));
|
|
1003
|
+
});
|
|
1004
|
+
const instance = tree.root;
|
|
1005
|
+
const show_local_control = instance.find(
|
|
1006
|
+
(el) =>
|
|
1007
|
+
el.props.accessibilityLabel ===
|
|
1008
|
+
AccessibilityLabel.AUTOMATE_SHOW_LOCAL_CONTROL &&
|
|
1009
|
+
el.type === TouchableOpacity
|
|
1010
|
+
);
|
|
1011
|
+
await act(async () => {
|
|
1012
|
+
await show_local_control.props.onPress();
|
|
1013
|
+
});
|
|
1014
|
+
const disable = instance.find(
|
|
1015
|
+
(el) =>
|
|
1016
|
+
el.props.accessibilityLabel ===
|
|
1017
|
+
AccessibilityLabel.AUTOMATE_DISABLE_LOCAL_CONTROL &&
|
|
1018
|
+
el.type === TouchableOpacity
|
|
1019
|
+
);
|
|
1020
|
+
await act(async () => {
|
|
1021
|
+
await disable.props.onPress();
|
|
1022
|
+
});
|
|
1023
|
+
expect(mock.history.post.length).toBe(0);
|
|
1024
|
+
});
|
|
800
1025
|
});
|
|
@@ -46,7 +46,7 @@ import { Card } from '../../../commons/CardShadow';
|
|
|
46
46
|
const PreventDoubleTouch = withPreventDoubleClick(TouchableOpacity);
|
|
47
47
|
|
|
48
48
|
const ScriptDetail = ({ route }) => {
|
|
49
|
-
const { dispatch, navigate, goBack } = useNavigation();
|
|
49
|
+
const { dispatch, navigate, goBack, getState } = useNavigation();
|
|
50
50
|
const { params = {} } = route;
|
|
51
51
|
const refMenuAction = useRef();
|
|
52
52
|
const { childRef, showingPopover, showPopoverWithRef, hidePopover } =
|
|
@@ -114,38 +114,41 @@ const ScriptDetail = ({ route }) => {
|
|
|
114
114
|
|
|
115
115
|
const handleNavigate = useCallback(
|
|
116
116
|
(screen, screenParams) => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (!hasScriptDetail) {
|
|
130
|
-
routes.push({
|
|
131
|
-
name: Routes.ScriptDetail,
|
|
132
|
-
params: route.params,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
117
|
+
const state = getState();
|
|
118
|
+
const index = state.routes.findIndex((r) => r.name === closeScreen);
|
|
119
|
+
let routes = [];
|
|
120
|
+
if (!closeScreen) {
|
|
121
|
+
routes = state.routes;
|
|
122
|
+
} else if (index >= 0) {
|
|
123
|
+
routes = state.routes.slice(0, index + 1);
|
|
124
|
+
}
|
|
125
|
+
const hasScriptDetail = routes.some(
|
|
126
|
+
(r) => r.name === Routes.ScriptDetail
|
|
127
|
+
);
|
|
135
128
|
|
|
129
|
+
if (!hasScriptDetail) {
|
|
136
130
|
routes.push({
|
|
137
|
-
name:
|
|
138
|
-
params:
|
|
131
|
+
name: Routes.ScriptDetail,
|
|
132
|
+
params: route.params,
|
|
139
133
|
});
|
|
134
|
+
}
|
|
140
135
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
index: routes.length - 1,
|
|
145
|
-
});
|
|
136
|
+
routes.push({
|
|
137
|
+
name: screen,
|
|
138
|
+
params: screenParams,
|
|
146
139
|
});
|
|
140
|
+
|
|
141
|
+
setTimeout(() => {
|
|
142
|
+
dispatch(
|
|
143
|
+
CommonActions.reset({
|
|
144
|
+
routes: routes,
|
|
145
|
+
index: routes.length - 1,
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
}, 500); // Workaround: Fix crash on iOS when resetting the stack with dispatch.
|
|
149
|
+
// Delay added to ensure navigation state updates before dispatching reset.
|
|
147
150
|
},
|
|
148
|
-
[closeScreen, dispatch, route.params]
|
|
151
|
+
[closeScreen, dispatch, getState, route.params]
|
|
149
152
|
);
|
|
150
153
|
|
|
151
154
|
const handleUpdateAutomate = useCallback(async () => {
|
|
@@ -267,7 +270,10 @@ const ScriptDetail = ({ route }) => {
|
|
|
267
270
|
closeScreen === Routes.MultiUnits ||
|
|
268
271
|
closeScreen === Routes.Automate
|
|
269
272
|
) {
|
|
270
|
-
navigate(
|
|
273
|
+
navigate({
|
|
274
|
+
name: closeScreen,
|
|
275
|
+
merge: true,
|
|
276
|
+
});
|
|
271
277
|
} else {
|
|
272
278
|
goBack();
|
|
273
279
|
}
|
|
@@ -356,6 +362,10 @@ const ScriptDetail = ({ route }) => {
|
|
|
356
362
|
|
|
357
363
|
const onPressSelectChip = useCallback(
|
|
358
364
|
async (enable_local_control, item) => {
|
|
365
|
+
if (!enable_local_control && !local_control.chip_id_local_control) {
|
|
366
|
+
setIsShowLocalControl(false);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
359
369
|
const { success } = await axiosPost(
|
|
360
370
|
API.AUTOMATE.ENABLE_LOCAL_CONTROL(automateId),
|
|
361
371
|
{
|
|
@@ -372,20 +382,23 @@ const ScriptDetail = ({ route }) => {
|
|
|
372
382
|
});
|
|
373
383
|
}
|
|
374
384
|
},
|
|
375
|
-
[automateId]
|
|
385
|
+
[automateId, local_control.chip_id_local_control]
|
|
376
386
|
);
|
|
377
387
|
|
|
378
388
|
const renderLocalControl = useMemo(() => {
|
|
379
389
|
if (!!can_edit && type !== AUTOMATE_TYPE.ONE_TAP) {
|
|
380
390
|
return (
|
|
381
391
|
<View style={styles.row}>
|
|
382
|
-
<Text type="H3" semibold style={styles.
|
|
392
|
+
<Text type="H3" semibold style={styles.width50}>
|
|
383
393
|
{t('local_control')}
|
|
384
394
|
</Text>
|
|
385
395
|
<Card style={styles.card}>
|
|
386
396
|
<TouchableOpacity
|
|
387
397
|
style={styles.localControl}
|
|
388
398
|
onPress={onShowLocalControl}
|
|
399
|
+
accessibilityLabel={
|
|
400
|
+
AccessibilityLabel.AUTOMATE_SHOW_LOCAL_CONTROL
|
|
401
|
+
}
|
|
389
402
|
>
|
|
390
403
|
<Text numberOfLines={1}>
|
|
391
404
|
{local_control.is_local_control
|
|
@@ -534,27 +547,35 @@ const ScriptDetail = ({ route }) => {
|
|
|
534
547
|
onBackButtonPress={onCloseLocalControl}
|
|
535
548
|
onBackdropPress={onCloseLocalControl}
|
|
536
549
|
>
|
|
537
|
-
<View style={styles.popoverStyle}>
|
|
550
|
+
<View key={'localControl'} style={styles.popoverStyle}>
|
|
538
551
|
<TouchableOpacity
|
|
539
552
|
style={styles.textDisable}
|
|
553
|
+
key={'listChip'}
|
|
540
554
|
onPress={() => {
|
|
541
555
|
onPressSelectChip(false, {
|
|
542
556
|
id: local_control.chip_id_local_control,
|
|
543
557
|
name: local_control.chip_local_control,
|
|
544
558
|
});
|
|
545
559
|
}}
|
|
560
|
+
accessibilityLabel={
|
|
561
|
+
AccessibilityLabel.AUTOMATE_DISABLE_LOCAL_CONTROL
|
|
562
|
+
}
|
|
546
563
|
>
|
|
547
564
|
<Text>{t('disable')}</Text>
|
|
548
565
|
{!local_control.is_local_control && (
|
|
549
566
|
<IconOutline style={styles.checked} name={'check'} size={20} />
|
|
550
567
|
)}
|
|
551
568
|
</TouchableOpacity>
|
|
552
|
-
{listChipShared.map((item) => (
|
|
569
|
+
{listChipShared.map((item, index) => (
|
|
553
570
|
<TouchableOpacity
|
|
571
|
+
key={'listChip' + index} // Add key fix warning
|
|
554
572
|
style={styles.listChip}
|
|
555
573
|
onPress={() => {
|
|
556
574
|
onPressSelectChip(true, item);
|
|
557
575
|
}}
|
|
576
|
+
accessibilityLabel={
|
|
577
|
+
AccessibilityLabel.AUTOMATE_ENABLE_LOCAL_CONTROL
|
|
578
|
+
}
|
|
558
579
|
>
|
|
559
580
|
<Text>{item.name}</Text>
|
|
560
581
|
{local_control.chip_id_local_control === item.id &&
|
|
@@ -1081,7 +1081,7 @@ export default {
|
|
|
1081
1081
|
automate: 'Tự động',
|
|
1082
1082
|
smart: 'Thông minh',
|
|
1083
1083
|
enable_this_script: 'Kích hoạt kịch bản này',
|
|
1084
|
-
local_control: 'Điều khiển
|
|
1084
|
+
local_control: 'Điều khiển tại biên',
|
|
1085
1085
|
choose_gateway: 'Chọn gateway',
|
|
1086
1086
|
this_script_has_been_disabled: 'Kịch bản này đã bị vô hiệu hoá',
|
|
1087
1087
|
disable: 'Vô hiệu hóa',
|
|
@@ -1090,7 +1090,7 @@ export default {
|
|
|
1090
1090
|
sms_alarm: 'Cảnh báo SMS',
|
|
1091
1091
|
message_sms:
|
|
1092
1092
|
'Nội dung cảnh báo: “Đang có tín hiệu báo cháy từ lầu 1, vui lòng kiểm tra.”',
|
|
1093
|
-
only_in_local_control: 'Chỉ hoạt động ở chế độ điều khiển
|
|
1093
|
+
only_in_local_control: 'Chỉ hoạt động ở chế độ điều khiển tại biên',
|
|
1094
1094
|
send_app_notification: 'Gửi thông báo qua ứng dụng',
|
|
1095
1095
|
delay_the_action: 'Trì hoãn hành động',
|
|
1096
1096
|
wait: 'Chờ đợi',
|