@eohjsc/react-native-smart-city 0.3.92 → 0.3.94
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 +5 -1
- package/src/commons/ActionGroup/SliderRangeTemplate.js +2 -2
- package/src/commons/AlertAction/index.js +5 -0
- package/src/commons/BottomButtonView/index.js +22 -4
- package/src/commons/Button/index.js +5 -0
- package/src/commons/Device/ConnectedViewHeader.js +0 -1
- package/src/commons/Device/Emergency/EmergencyDetail.js +4 -2
- package/src/commons/Device/Emergency/__test__/EmergencyDetail.test.js +4 -2
- package/src/commons/Device/ProgressBar/index.js +3 -1
- package/src/commons/Device/ProgressBar/styles.js +1 -4
- package/src/commons/Device/WindSpeed/Anemometer/index.js +1 -1
- package/src/commons/Header/HeaderCustom.js +8 -18
- package/src/commons/SubUnit/OneTap/ItemOneTap.js +84 -129
- package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +74 -39
- package/src/commons/SubUnit/OneTap/index.js +24 -4
- package/src/commons/ViewButtonBottom/index.js +32 -4
- package/src/configs/API.js +4 -0
- package/src/configs/AccessibilityLabel.js +1 -0
- package/src/configs/BLE.js +1 -0
- package/src/context/actionType.ts +2 -1
- package/src/context/mockStore.ts +1 -0
- package/src/context/reducer.ts +12 -1
- package/src/hooks/Explore/useKeyboardAnimated.js +10 -4
- package/src/hooks/IoT/useBluetoothConnection.js +14 -26
- package/src/hooks/useMqtt.js +95 -0
- package/src/iot/Monitor.js +2 -1
- package/src/iot/RemoteControl/Bluetooth.js +56 -19
- package/src/iot/RemoteControl/__test__/Bluetooth.test.js +140 -0
- package/src/iot/mqtt.js +233 -0
- package/src/navigations/UnitStack.js +11 -3
- package/src/screens/AddLocationMaps/index.js +18 -16
- package/src/screens/AddLocationMaps/indexStyle.js +3 -0
- package/src/screens/AddNewGateway/ConnectingDevice.js +7 -0
- package/src/screens/AddNewGateway/RenameNewDevices.js +2 -2
- package/src/screens/AddNewGateway/SelectDeviceType.js +67 -9
- package/src/screens/AddNewGateway/__test__/ConnectingZigbeeDevice.test.js +35 -0
- package/src/screens/AddNewGateway/__test__/SelectDeviceType.test.js +183 -29
- package/src/screens/Automate/AddNewAction/NewActionWrapper.js +3 -6
- package/src/screens/Automate/AddNewAction/SetupConfigCondition.js +29 -29
- package/src/screens/Automate/AddNewAction/__test__/SetupSensor.test.js +45 -39
- package/src/screens/Automate/AddNewAutoSmart/AddAutomationTypeSmart.js +25 -0
- package/src/screens/{AddNewAutoSmart/index.js → Automate/AddNewAutoSmart/AddTypeSmart.js} +16 -51
- package/src/screens/Automate/AddNewAutoSmart/AddUnknownTypeSmart.js +29 -0
- package/src/screens/{AddNewAutoSmart → Automate/AddNewAutoSmart}/__test__/AddNewAutoSmart.test.js +11 -11
- package/src/screens/{AddNewAutoSmart → Automate/AddNewAutoSmart}/styles/AddNewAutoSmartStyles.js +1 -1
- package/src/screens/Automate/Components/InputNameStyles.js +1 -1
- package/src/screens/Automate/EditActionsList/__tests__/index.test.js +11 -25
- package/src/screens/Automate/EditActionsList/index.js +32 -33
- package/src/screens/Automate/MultiUnits.js +29 -19
- package/src/screens/Automate/ScriptDetail/__test__/index.test.js +78 -5
- package/src/screens/Automate/ScriptDetail/index.js +38 -10
- package/src/screens/Automate/Styles/MultiUnitsStyles.js +1 -1
- package/src/screens/Automate/__test__/MultiUnits.test.js +64 -7
- package/src/screens/Automate/__test__/index.test.js +45 -11
- package/src/screens/Automate/index.js +53 -38
- package/src/screens/Device/__test__/detail.test.js +1 -1
- package/src/screens/Device/__test__/mqttDetail.test.js +599 -0
- package/src/screens/Device/components/SensorDisplayItem.js +1 -7
- package/src/screens/Device/detail.js +64 -30
- package/src/screens/Device/hooks/__test__/useEvaluateValue.test.js +24 -1
- package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +13 -3
- package/src/screens/SelectUnit/__test__/index.test.js +8 -13
- package/src/screens/Sharing/InfoMemberUnit.js +2 -2
- package/src/screens/Sharing/MemberList.js +28 -7
- package/src/screens/Sharing/__test__/InfoMemberUnit.test.js +32 -18
- package/src/screens/Sharing/__test__/MemberList.test.js +37 -4
- package/src/screens/SmartAccount/__test__/SmartAccount.test.js +8 -4
- package/src/screens/SmartAccount/index.js +8 -9
- package/src/screens/SmartAccount/style.js +8 -7
- package/src/screens/Unit/AddMenu.js +42 -19
- package/src/screens/Unit/Detail.js +4 -19
- package/src/screens/Unit/Summaries.js +6 -17
- package/src/screens/Unit/__test__/AddMenu.test.js +68 -15
- package/src/screens/Unit/__test__/Summaries.test.js +2 -2
- package/src/screens/Unit/components/AutomateScript/index.js +1 -1
- package/src/utils/FactoryGateway.js +525 -0
- package/src/utils/I18n/translations/en.js +1409 -0
- package/src/utils/I18n/translations/vi.js +1411 -0
- package/src/utils/I18n/translations.ts +2 -2
- package/src/utils/Permission/backend.js +7 -0
- package/src/utils/Route/index.js +2 -1
- package/src/utils/Utils.js +11 -0
- package/src/commons/Device/SensorConnectedStatus.js +0 -56
- package/src/commons/Device/__test__/SensorConnectedStatus.test.js +0 -29
- package/src/screens/Sharing/__test__/MemberList2.test.js +0 -74
- package/src/utils/I18n/translations/en.json +0 -1138
- package/src/utils/I18n/translations/vi.json +0 -1136
|
@@ -7,61 +7,84 @@ import AddDeviceIcon from '../../../assets/images/Popover/Dashboard/AddDevice.sv
|
|
|
7
7
|
import SmartAccount from '../../../assets/images/Popover/Dashboard/SmartAccount.svg';
|
|
8
8
|
import AddMemberIcon from '../../../assets/images/Popover/Dashboard/AddMember.svg';
|
|
9
9
|
import { useNavigation } from '@react-navigation/native';
|
|
10
|
+
import { ToastBottomHelper } from '../../utils/Utils';
|
|
11
|
+
import { useBackendPermission } from '../../utils/Permission/backend';
|
|
10
12
|
|
|
11
13
|
const AddMenu = memo(({ unit, afterItemClick, showAdd, setHideAdd }) => {
|
|
12
14
|
const t = useTranslations();
|
|
13
15
|
const navigation = useNavigation();
|
|
16
|
+
const permissions = useBackendPermission();
|
|
14
17
|
|
|
15
18
|
const onItemClick = useCallback(
|
|
16
|
-
(
|
|
19
|
+
(item) => {
|
|
17
20
|
setHideAdd(true);
|
|
18
21
|
afterItemClick();
|
|
19
|
-
|
|
22
|
+
item.onClick && item.onClick();
|
|
20
23
|
},
|
|
21
|
-
[setHideAdd, afterItemClick
|
|
24
|
+
[setHideAdd, afterItemClick]
|
|
22
25
|
);
|
|
23
26
|
|
|
24
27
|
const listItem = useMemo(() => {
|
|
25
28
|
return [
|
|
26
29
|
{
|
|
27
30
|
id: 'add_sub_unit',
|
|
28
|
-
route: Routes.AddSubUnitStack,
|
|
29
31
|
text: t('sub_unit'),
|
|
30
32
|
image: <AddSubUnitIcon width={43} height={43} />,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
onClick: () => {
|
|
34
|
+
// 2 is station favorite and smart
|
|
35
|
+
if (unit.stations.length - 2 >= permissions.max_stations_per_unit) {
|
|
36
|
+
ToastBottomHelper.error(t('reach_max_stations_per_unit'));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
navigation.navigate(Routes.AddSubUnitStack, {
|
|
40
|
+
screen: Routes.AddSubUnit,
|
|
41
|
+
params: { unit, isInsideUnit: true },
|
|
42
|
+
});
|
|
34
43
|
},
|
|
35
44
|
},
|
|
36
45
|
{
|
|
37
46
|
id: 'add_member',
|
|
38
|
-
route: Routes.AddMemberStack,
|
|
39
47
|
text: t('member'),
|
|
40
48
|
image: <AddMemberIcon width={43} height={43} />,
|
|
41
|
-
|
|
49
|
+
onClick: () =>
|
|
50
|
+
navigation.navigate(Routes.AddMemberStack, {
|
|
51
|
+
screen: Routes.SharingSelectPermission,
|
|
52
|
+
params: { unit },
|
|
53
|
+
}),
|
|
42
54
|
},
|
|
43
55
|
{
|
|
44
56
|
id: 'add_device',
|
|
45
|
-
route: Routes.AddGatewayStack,
|
|
46
57
|
text: t('device'),
|
|
47
58
|
image: <AddDeviceIcon width={43} height={43} />,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
59
|
+
onClick: () => {
|
|
60
|
+
let hasPermission = false;
|
|
61
|
+
Object.keys(permissions).forEach((key) => {
|
|
62
|
+
if (key.includes('plug_and_play') && permissions[key]) {
|
|
63
|
+
hasPermission = true;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
if (!hasPermission) {
|
|
67
|
+
ToastBottomHelper.error(t('not_support_plug_and_play'));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
navigation.navigate(Routes.AddGatewayStack, {
|
|
71
|
+
screen: Routes.SelectDeviceType,
|
|
72
|
+
params: { unit },
|
|
73
|
+
});
|
|
51
74
|
},
|
|
52
75
|
},
|
|
53
76
|
{
|
|
54
77
|
id: 'add_smart_account',
|
|
55
|
-
route: Routes.SmartAccountStack,
|
|
56
78
|
text: t('name_smart_account'),
|
|
57
79
|
image: <SmartAccount width={43} height={43} />,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
80
|
+
onClick: () =>
|
|
81
|
+
navigation.navigate(Routes.SmartAccountStack, {
|
|
82
|
+
screen: Routes.SmartAccount,
|
|
83
|
+
params: { unitId: unit?.id, unitName: unit?.name },
|
|
84
|
+
}),
|
|
62
85
|
},
|
|
63
86
|
];
|
|
64
|
-
}, [t, unit]);
|
|
87
|
+
}, [t, unit, navigation, permissions]);
|
|
65
88
|
|
|
66
89
|
const hideAddModal = useCallback(() => {
|
|
67
90
|
setHideAdd(false);
|
|
@@ -2,11 +2,10 @@ import React, {
|
|
|
2
2
|
useCallback,
|
|
3
3
|
useEffect,
|
|
4
4
|
useState,
|
|
5
|
-
useRef,
|
|
6
5
|
useContext,
|
|
7
6
|
useMemo,
|
|
8
7
|
} from 'react';
|
|
9
|
-
import {
|
|
8
|
+
import { RefreshControl, View, Platform } from 'react-native';
|
|
10
9
|
import { useIsFocused } from '@react-navigation/native';
|
|
11
10
|
|
|
12
11
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
@@ -219,7 +218,7 @@ const UnitDetail = ({ route }) => {
|
|
|
219
218
|
isSuccessfullyConnected ? RouterHardware(Routes.Dashboard) : goBack
|
|
220
219
|
);
|
|
221
220
|
const user = useSCContextSelector((state) => state?.auth?.account?.user);
|
|
222
|
-
const { isLavidaSource, isFirstOpenCamera } = useSCContextSelector(
|
|
221
|
+
const { isLavidaSource, isFirstOpenCamera, appState } = useSCContextSelector(
|
|
223
222
|
(state) => state.app
|
|
224
223
|
);
|
|
225
224
|
|
|
@@ -228,7 +227,6 @@ const UnitDetail = ({ route }) => {
|
|
|
228
227
|
const [showAdd, setShowAdd, setHideAdd] = useBoolean();
|
|
229
228
|
const [showPreventAccess, setShowPreventAccess, setHidePreventAccess] =
|
|
230
229
|
useBoolean(false);
|
|
231
|
-
const appState = useRef(AppState.currentState);
|
|
232
230
|
|
|
233
231
|
const { childRef, showingPopover, showPopoverWithRef, hidePopover } =
|
|
234
232
|
usePopover();
|
|
@@ -306,21 +304,8 @@ const UnitDetail = ({ route }) => {
|
|
|
306
304
|
);
|
|
307
305
|
|
|
308
306
|
useEffect(() => {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
appState.current.match(/inactive|background/) &&
|
|
312
|
-
nextAppState === 'active'
|
|
313
|
-
) {
|
|
314
|
-
fetchDetails();
|
|
315
|
-
}
|
|
316
|
-
appState.current = nextAppState;
|
|
317
|
-
};
|
|
318
|
-
const subscription = AppState.addEventListener('change', handler);
|
|
319
|
-
|
|
320
|
-
return () => {
|
|
321
|
-
subscription?.remove();
|
|
322
|
-
};
|
|
323
|
-
}, [fetchDetails]);
|
|
307
|
+
appState === 'active' && fetchDetails();
|
|
308
|
+
}, [appState, fetchDetails]);
|
|
324
309
|
|
|
325
310
|
useValueEvaluations(unitId);
|
|
326
311
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { memo, useCallback, useEffect, useState
|
|
2
|
-
import {
|
|
1
|
+
import React, { memo, useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { ScrollView } from 'react-native';
|
|
3
3
|
import SummaryItem from '../../commons/SummaryItem';
|
|
4
4
|
import Routes from '../../utils/Route';
|
|
5
5
|
import { useIsFocused, useNavigation } from '@react-navigation/native';
|
|
@@ -7,6 +7,7 @@ import { axiosGet } from '../../utils/Apis/axios';
|
|
|
7
7
|
import { API } from '../../configs';
|
|
8
8
|
import { useReceiveNotifications } from '../../hooks';
|
|
9
9
|
import { AccessibilityLabel } from '../../configs/Constants';
|
|
10
|
+
import { useSCContextSelector } from '../../context';
|
|
10
11
|
|
|
11
12
|
let timeoutId;
|
|
12
13
|
|
|
@@ -14,7 +15,7 @@ const Summaries = memo(({ unit }) => {
|
|
|
14
15
|
const [unitSummaries, setUnitSummaries] = useState([]);
|
|
15
16
|
const isFocused = useIsFocused();
|
|
16
17
|
const navigation = useNavigation();
|
|
17
|
-
const appState =
|
|
18
|
+
const appState = useSCContextSelector((state) => state.app.appState);
|
|
18
19
|
|
|
19
20
|
const fetchUnitSummary = useCallback(async () => {
|
|
20
21
|
if (!unit.id) {
|
|
@@ -59,20 +60,8 @@ const Summaries = memo(({ unit }) => {
|
|
|
59
60
|
}, [fetchUnitSummary]);
|
|
60
61
|
|
|
61
62
|
useEffect(() => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
appState.current.match(/inactive|background/) &&
|
|
65
|
-
nextAppState === 'active'
|
|
66
|
-
) {
|
|
67
|
-
fetchUnitSummary();
|
|
68
|
-
}
|
|
69
|
-
appState.current = nextAppState;
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
return () => {
|
|
73
|
-
subscription?.remove();
|
|
74
|
-
};
|
|
75
|
-
}, [fetchUnitSummary]);
|
|
63
|
+
appState === 'active' && fetchUnitSummary();
|
|
64
|
+
}, [appState, fetchUnitSummary]);
|
|
76
65
|
|
|
77
66
|
useEffect(() => {
|
|
78
67
|
if (!isFocused) {
|
|
@@ -5,13 +5,15 @@ import MenuActionAddnew from '../../../commons/MenuActionAddnew';
|
|
|
5
5
|
import { SCProvider } from '../../../context';
|
|
6
6
|
import { mockSCStore } from '../../../context/mockStore';
|
|
7
7
|
import AddMenu from '../AddMenu';
|
|
8
|
+
import { useNavigation } from '@react-navigation/native';
|
|
9
|
+
import { ToastBottomHelper } from '../../../utils/Utils';
|
|
10
|
+
import { getTranslate } from '../../../utils/I18n';
|
|
8
11
|
|
|
9
|
-
const mockedNavigate = jest.fn();
|
|
10
12
|
const mockedAfterItemClick = jest.fn();
|
|
11
13
|
const mockedHideAddModal = jest.fn();
|
|
12
14
|
|
|
13
|
-
const wrapComponent = (unit) => (
|
|
14
|
-
<SCProvider initState={mockSCStore(
|
|
15
|
+
const wrapComponent = (unit, storeData = {}) => (
|
|
16
|
+
<SCProvider initState={mockSCStore(storeData)}>
|
|
15
17
|
<AddMenu
|
|
16
18
|
unit={unit}
|
|
17
19
|
afterItemClick={mockedAfterItemClick}
|
|
@@ -21,17 +23,15 @@ const wrapComponent = (unit) => (
|
|
|
21
23
|
</SCProvider>
|
|
22
24
|
);
|
|
23
25
|
|
|
24
|
-
jest.mock('@react-navigation/native', () => {
|
|
25
|
-
return {
|
|
26
|
-
...jest.requireActual('@react-navigation/native'),
|
|
27
|
-
useNavigation: () => ({
|
|
28
|
-
navigate: mockedNavigate,
|
|
29
|
-
}),
|
|
30
|
-
useIsFocused: jest.fn(),
|
|
31
|
-
};
|
|
32
|
-
});
|
|
33
|
-
|
|
34
26
|
describe('Test AddMenu Unit', () => {
|
|
27
|
+
const mockedNavigate = useNavigation().navigate;
|
|
28
|
+
const spyToastError = jest.spyOn(ToastBottomHelper, 'error');
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
mockedNavigate.mockClear();
|
|
32
|
+
spyToastError.mockClear();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
35
|
let tree;
|
|
36
36
|
it('render AddMenu without route', async () => {
|
|
37
37
|
let unit = { id: 1, name: 'Unit 1' };
|
|
@@ -68,8 +68,9 @@ describe('Test AddMenu Unit', () => {
|
|
|
68
68
|
const menuActionAddnew = instance.findByType(MenuActionAddnew);
|
|
69
69
|
await act(async () => {
|
|
70
70
|
menuActionAddnew.props.onItemClick({
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
onClick: () => {
|
|
72
|
+
mockedNavigate('route test', 'data test');
|
|
73
|
+
},
|
|
73
74
|
});
|
|
74
75
|
});
|
|
75
76
|
|
|
@@ -77,4 +78,56 @@ describe('Test AddMenu Unit', () => {
|
|
|
77
78
|
expect(mockedAfterItemClick).toHaveBeenCalled();
|
|
78
79
|
expect(mockedNavigate).toHaveBeenCalledWith('route test', 'data test');
|
|
79
80
|
});
|
|
81
|
+
|
|
82
|
+
it('add new device but have no permission', async () => {
|
|
83
|
+
let unit = { id: 1, name: 'Unit 1' };
|
|
84
|
+
await act(async () => {
|
|
85
|
+
tree = await create(
|
|
86
|
+
wrapComponent(unit, {
|
|
87
|
+
auth: {
|
|
88
|
+
account: {
|
|
89
|
+
user: {
|
|
90
|
+
permissions: {},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const instance = tree.root;
|
|
99
|
+
const menuActionAddnew = instance.findByType(MenuActionAddnew);
|
|
100
|
+
await act(async () => {
|
|
101
|
+
menuActionAddnew.props.onItemClick(menuActionAddnew.props.dataActions[2]);
|
|
102
|
+
});
|
|
103
|
+
expect(spyToastError).toBeCalledWith(
|
|
104
|
+
getTranslate('en', 'not_support_plug_and_play')
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('add new device has permission', async () => {
|
|
109
|
+
let unit = { id: 1, name: 'Unit 1' };
|
|
110
|
+
await act(async () => {
|
|
111
|
+
tree = await create(
|
|
112
|
+
wrapComponent(unit, {
|
|
113
|
+
auth: {
|
|
114
|
+
account: {
|
|
115
|
+
user: {
|
|
116
|
+
permissions: {
|
|
117
|
+
plug_and_play_zigbee: true,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
})
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const instance = tree.root;
|
|
127
|
+
const menuActionAddnew = instance.findByType(MenuActionAddnew);
|
|
128
|
+
await act(async () => {
|
|
129
|
+
menuActionAddnew.props.onItemClick(menuActionAddnew.props.dataActions[2]);
|
|
130
|
+
});
|
|
131
|
+
expect(spyToastError).not.toBeCalled();
|
|
132
|
+
});
|
|
80
133
|
});
|
|
@@ -104,10 +104,10 @@ describe('Test Summaries', () => {
|
|
|
104
104
|
await act(async () => {
|
|
105
105
|
tree = await renderer.create(wrapComponent(props));
|
|
106
106
|
});
|
|
107
|
-
expect(mock.history.get).toHaveLength(
|
|
107
|
+
expect(mock.history.get).toHaveLength(2);
|
|
108
108
|
await act(async () => {
|
|
109
109
|
jest.runOnlyPendingTimers();
|
|
110
110
|
});
|
|
111
|
-
expect(mock.history.get).toHaveLength(
|
|
111
|
+
expect(mock.history.get).toHaveLength(3);
|
|
112
112
|
});
|
|
113
113
|
});
|
|
@@ -59,7 +59,7 @@ const AutomateScript = ({ automate, onPress, isSelected }) => {
|
|
|
59
59
|
</Text>
|
|
60
60
|
<View style={styles.descriptionContainer}>
|
|
61
61
|
<Text numberOfLines={1} type={'Label'} color={Colors.Gray7}>
|
|
62
|
-
{
|
|
62
|
+
{t('created_by', { name: author })}
|
|
63
63
|
</Text>
|
|
64
64
|
<IconOutline name="right" size={12} />
|
|
65
65
|
</View>
|