@eohjsc/react-native-smart-city 0.3.27 → 0.3.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 +1 -1
- package/src/Images/Common/device_icon.png +0 -0
- package/src/Images/DevMode/gateway.png +0 -0
- package/src/Images/DevMode/gateway@2x.png +0 -0
- package/src/Images/DevMode/gateway@3x.png +0 -0
- package/src/Images/DevMode/menu.png +0 -0
- package/src/Images/DevMode/menu@2x.png +0 -0
- package/src/Images/DevMode/menu@3x.png +0 -0
- package/src/Images/DevMode/search.png +0 -0
- package/src/Images/DevMode/search@2x.png +0 -0
- package/src/Images/DevMode/search@3x.png +0 -0
- package/src/Images/DevMode/smart.png +0 -0
- package/src/Images/DevMode/smart@2x.png +0 -0
- package/src/Images/DevMode/smart@3x.png +0 -0
- package/src/Images/DevMode/template.png +0 -0
- package/src/Images/DevMode/template@2x.png +0 -0
- package/src/Images/DevMode/template@3x.png +0 -0
- package/src/commons/Action/ItemQuickAction.js +1 -0
- package/src/commons/ActionGroup/CurtainButtonTemplate.js +1 -2
- package/src/commons/ActionGroup/OnOffTemplate/OnOffButtonTemplate.js +2 -0
- package/src/commons/ActionGroup/SliderRangeTemplate.js +22 -14
- package/src/commons/ActionTemplate/CurtainAction.js +60 -0
- package/src/commons/ActionTemplate/CurtainActionStyles.js +11 -0
- package/src/commons/ActionTemplate/OnOffSmartLockAction.js +44 -0
- package/src/commons/ActionTemplate/OnOffSmartLockActionStyles.js +11 -0
- package/src/commons/ActionTemplate/index.js +18 -0
- package/src/commons/BottomButtonView/index.js +1 -0
- package/src/commons/Button/index.js +2 -0
- package/src/commons/CameraDevice/index.js +1 -2
- package/src/commons/ConnectingProcess/DeviceItem/DeviceItem.js +20 -12
- package/src/commons/ConnectingProcess/DeviceItem/DeviceItemStyles.js +2 -0
- package/src/commons/ConnectingProcess/__test__/DeviceItem.test.js +1 -1
- package/src/commons/ConnectingProcess/index.js +11 -0
- package/src/commons/Dashboard/MyUnit/index.js +1 -1
- package/src/commons/DevMode/Label.js +10 -0
- package/src/commons/DevMode/Search.js +20 -0
- package/src/commons/DevMode/Styles/LabelStyles.js +8 -0
- package/src/commons/DevMode/Styles/SearchStyles.js +21 -0
- package/src/commons/DevMode/index.js +3 -0
- package/src/commons/Device/ItemAddNew/index.js +5 -1
- package/src/commons/Device/ItemDevice.js +12 -9
- package/src/commons/Form/TextInput.js +4 -0
- package/src/commons/HeaderAni/index.js +1 -0
- package/src/commons/MediaPlayerDetail/index.js +0 -20
- package/src/commons/MenuActionMore/index.js +11 -1
- package/src/commons/Modal/index.js +1 -2
- package/src/commons/NavBar/index.js +13 -1
- package/src/commons/Popover/index.js +7 -6
- package/src/commons/SubUnit/OneTap/ItemOneTap.js +4 -1
- package/src/commons/SubUnit/ShortDetail.js +1 -0
- package/src/commons/SummaryItem/index.js +2 -1
- package/src/commons/Tabbar/Styles/indexStyles.js +51 -0
- package/src/commons/Tabbar/index.js +110 -0
- package/src/commons/Unit/HeaderUnit/index.js +2 -0
- package/src/commons/Unit/SharedUnit.js +1 -0
- package/src/commons/WrapParallaxScrollView/index.js +16 -2
- package/src/configs/Colors.js +4 -0
- package/src/configs/Constants.js +16 -0
- package/src/configs/Images.js +6 -0
- package/src/hooks/Common/useDevicesStatus.js +1 -1
- package/src/hooks/IoT/useValueEvaluation.js +10 -19
- package/src/iot/RemoteControl/GoogleHome.js +6 -6
- package/src/navigations/GatewayStack.js +23 -0
- package/src/navigations/Main.js +144 -0
- package/src/navigations/SmartStack.js +23 -0
- package/src/navigations/TemplateStack.js +23 -0
- package/src/navigations/UnitStack.js +5 -8
- package/src/screens/AddNewAction/Device/index.js +5 -1
- package/src/screens/AddNewAction/SelectAction.js +36 -15
- package/src/screens/AddNewAction/__test__/SelectAction.test.js +1 -0
- package/src/screens/AddNewAutoSmart/index.js +2 -0
- package/src/screens/AddNewGateway/PlugAndPlay/GatewayWifiList.js +13 -1
- package/src/screens/AllCamera/__test__/index.test.js +1 -8
- package/src/screens/AllCamera/index.js +0 -13
- package/src/screens/Device/components/SensorConnectStatusViewHeader.js +10 -11
- package/src/screens/Device/detail.js +35 -16
- package/src/screens/Device/hooks/__test__/useEmergencyButton.test.js +37 -0
- package/src/screens/Device/hooks/useFavoriteDevice.js +4 -2
- package/src/screens/Drawer/Drawer.test.js +24 -0
- package/src/screens/Drawer/index.js +198 -0
- package/src/screens/EmergencyContacts/EmergencyContactsAddNew.js +3 -3
- package/src/screens/EmergencyContacts/EmergencyContactsSelectContacts.js +4 -7
- package/src/screens/Gateway/__test__/index.test.js +16 -0
- package/src/screens/Gateway/index.js +8 -0
- package/src/screens/Notification/__test__/NotificationItem.test.js +74 -104
- package/src/screens/Notification/components/NotificationItem.js +40 -239
- package/src/screens/ScriptDetail/__test__/index.test.js +40 -1
- package/src/screens/ScriptDetail/index.js +2 -1
- package/src/screens/Sharing/Components/SensorItem.js +4 -1
- package/src/screens/Sharing/Components/Styles/SensorItemStyles.js +4 -0
- package/src/screens/Sharing/Components/Styles/TitleCheckBoxStyles.js +4 -0
- package/src/screens/Sharing/Components/TitleCheckBox.js +17 -8
- package/src/screens/Smart/__test__/index.test.js +16 -0
- package/src/screens/Smart/index.js +8 -0
- package/src/screens/SubUnit/AddSubUnit.js +1 -1
- package/src/screens/SubUnit/EditSubUnit.js +4 -1
- package/src/screens/Template/Styles/indexStyles.js +51 -0
- package/src/screens/Template/__test__/index.test.js +16 -0
- package/src/screens/Template/index.js +84 -0
- package/src/screens/Unit/Detail.js +16 -28
- package/src/screens/Unit/MoreMenu.js +16 -1
- package/src/screens/Unit/SelectAddToFavorites.js +11 -1
- package/src/screens/Unit/Station/__test__/index.test.js +41 -0
- package/src/screens/Unit/Station/index.js +0 -1
- package/src/screens/Unit/Summaries.js +6 -1
- package/src/screens/Unit/__test__/Detail.test.js +1 -5
- package/src/screens/Unit/components/AutomateScript/index.js +5 -2
- package/src/utils/Converter/__test__/timer.test.js +99 -0
- package/src/utils/Functions/Search.js +17 -0
- package/src/utils/Functions/ShortEmail.js +4 -0
- package/src/utils/Functions/__test__/Search.test.js +6 -0
- package/src/utils/Functions/__test__/ShortEmail.test.js +6 -0
- package/src/utils/I18n/translations/en.json +37 -42
- package/src/utils/I18n/translations/vi.json +37 -44
- package/src/utils/Route/index.js +6 -0
- package/src/commons/Modal/ModalFullVideo.js +0 -48
- package/src/commons/Modal/Styles/ModalFullVideoStyles.js +0 -26
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { Colors, Constants } from '../../../configs';
|
|
3
|
+
|
|
4
|
+
export default StyleSheet.create({
|
|
5
|
+
wrap: {
|
|
6
|
+
flex: 1,
|
|
7
|
+
backgroundColor: Colors.White,
|
|
8
|
+
padding: 16,
|
|
9
|
+
},
|
|
10
|
+
wrapEmpty: {
|
|
11
|
+
flex: 1,
|
|
12
|
+
justifyContent: 'center',
|
|
13
|
+
alignItems: 'center',
|
|
14
|
+
marginTop: -100,
|
|
15
|
+
},
|
|
16
|
+
textEmpty1: {
|
|
17
|
+
fontSize: 20,
|
|
18
|
+
fontWeight: 'bold',
|
|
19
|
+
},
|
|
20
|
+
textEmpty2: {
|
|
21
|
+
fontSize: 14,
|
|
22
|
+
fontWeight: '400',
|
|
23
|
+
color: Colors.Neutral.Neutral5,
|
|
24
|
+
marginTop: 7,
|
|
25
|
+
},
|
|
26
|
+
contentContainerStyle: {
|
|
27
|
+
flex: 1,
|
|
28
|
+
},
|
|
29
|
+
item: {
|
|
30
|
+
width: (Constants.width - 42) / 2,
|
|
31
|
+
height: 128,
|
|
32
|
+
borderRadius: 8,
|
|
33
|
+
borderWidth: 1,
|
|
34
|
+
borderColor: Colors.Neutral.Neutral3,
|
|
35
|
+
marginBottom: 16,
|
|
36
|
+
justifyContent: 'space-around',
|
|
37
|
+
alignItems: 'center',
|
|
38
|
+
},
|
|
39
|
+
oddItem: {
|
|
40
|
+
marginRight: 10,
|
|
41
|
+
},
|
|
42
|
+
nameItem: {
|
|
43
|
+
fontSize: 14,
|
|
44
|
+
fontWeight: 'bold',
|
|
45
|
+
},
|
|
46
|
+
countItem: {
|
|
47
|
+
fontSize: 12,
|
|
48
|
+
fontWeight: 400,
|
|
49
|
+
color: Colors.Neutral.Neutral5,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FlatList } from 'react-native';
|
|
3
|
+
import { act, create } from 'react-test-renderer';
|
|
4
|
+
import Template from '..';
|
|
5
|
+
|
|
6
|
+
describe('Test Template screen', () => {
|
|
7
|
+
let tree;
|
|
8
|
+
it('Test render', async () => {
|
|
9
|
+
await act(async () => {
|
|
10
|
+
tree = await create(<Template />);
|
|
11
|
+
});
|
|
12
|
+
const instance = tree.root;
|
|
13
|
+
const FlatLists = instance.findAllByType(FlatList);
|
|
14
|
+
expect(FlatLists).toHaveLength(1);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
TouchableWithoutFeedback,
|
|
5
|
+
Keyboard,
|
|
6
|
+
FlatList,
|
|
7
|
+
} from 'react-native';
|
|
8
|
+
import { Label } from '../../commons/DevMode';
|
|
9
|
+
import Search from '../../commons/DevMode/Search';
|
|
10
|
+
import Text from '../../commons/Text';
|
|
11
|
+
import t from '../../hooks/Common/useTranslations';
|
|
12
|
+
import { convertToSlug } from '../../utils/Functions/Search';
|
|
13
|
+
import styles from './Styles/indexStyles';
|
|
14
|
+
|
|
15
|
+
const arrTemplates = [
|
|
16
|
+
{
|
|
17
|
+
id: 1,
|
|
18
|
+
name: 'Template name 1',
|
|
19
|
+
count: 2,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 2,
|
|
23
|
+
name: 'Template name 2',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 3,
|
|
27
|
+
name: 'Template name 3',
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const Template = () => {
|
|
32
|
+
const [data, setData] = useState(arrTemplates);
|
|
33
|
+
|
|
34
|
+
const onSearch = useCallback((value) => {
|
|
35
|
+
if (value === '') {
|
|
36
|
+
setData(arrTemplates);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const dataTemp = arrTemplates.filter((item) =>
|
|
40
|
+
convertToSlug(item?.name).includes(convertToSlug(value))
|
|
41
|
+
);
|
|
42
|
+
setData(dataTemp);
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
const renderListEmptyComponent = useMemo(() => {
|
|
46
|
+
return (
|
|
47
|
+
<View style={styles.wrapEmpty}>
|
|
48
|
+
<Text style={styles.textEmpty1}>{t('no_template_yet')}</Text>
|
|
49
|
+
<Text style={styles.textEmpty2}>{t('add_your_template')}</Text>
|
|
50
|
+
</View>
|
|
51
|
+
);
|
|
52
|
+
}, []);
|
|
53
|
+
|
|
54
|
+
const renderItem = ({ item, index }) => {
|
|
55
|
+
return (
|
|
56
|
+
<View style={[styles.item, index % 2 === 0 && styles.oddItem]}>
|
|
57
|
+
<Text style={styles.nameItem}>{item?.name}</Text>
|
|
58
|
+
<Text style={styles.countItem}>{`${
|
|
59
|
+
item?.count > 0 ? item?.count : t('no')
|
|
60
|
+
} ${t('gateways').toLowerCase()}`}</Text>
|
|
61
|
+
</View>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
|
|
67
|
+
<View style={styles.wrap}>
|
|
68
|
+
<Label name={t('template')} />
|
|
69
|
+
<Search onSearch={onSearch} />
|
|
70
|
+
<FlatList
|
|
71
|
+
contentContainerStyle={styles.contentContainerStyle}
|
|
72
|
+
keyExtractor={(item) => item?.id}
|
|
73
|
+
data={data}
|
|
74
|
+
renderItem={renderItem}
|
|
75
|
+
extraData={data}
|
|
76
|
+
ListEmptyComponent={renderListEmptyComponent}
|
|
77
|
+
numColumns={2}
|
|
78
|
+
/>
|
|
79
|
+
</View>
|
|
80
|
+
</TouchableWithoutFeedback>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default Template;
|
|
@@ -33,7 +33,6 @@ import WrapParallaxScrollView from '../../commons/WrapParallaxScrollView';
|
|
|
33
33
|
import { SCContext, useSCContextSelector } from '../../context';
|
|
34
34
|
import { Action } from '../../context/actionType';
|
|
35
35
|
import CameraDevice from '../../commons/CameraDevice';
|
|
36
|
-
import { ModalFullVideo } from '../../commons/Modal';
|
|
37
36
|
import { useNavigation } from '@react-navigation/native';
|
|
38
37
|
import Routes from '../../utils/Route';
|
|
39
38
|
import SubUnitAutomate from '../../commons/SubUnit/OneTap';
|
|
@@ -63,6 +62,7 @@ const UnitDetail = ({ route }) => {
|
|
|
63
62
|
routeName,
|
|
64
63
|
stationId,
|
|
65
64
|
isAddSubUnit,
|
|
65
|
+
isEditSubUnit,
|
|
66
66
|
isSuccessfullyConnected,
|
|
67
67
|
} = route.params;
|
|
68
68
|
|
|
@@ -92,8 +92,6 @@ const UnitDetail = ({ route }) => {
|
|
|
92
92
|
const [showAdd, setShowAdd, setHideAdd] = useBoolean();
|
|
93
93
|
const [showPreventAccess, setShowPreventAccess, setHidePreventAccess] =
|
|
94
94
|
useBoolean(false);
|
|
95
|
-
const [isFullScreen, setIsFullScreen] = useState(false);
|
|
96
|
-
const [dataFullScreen, setDataFullScreen] = useState();
|
|
97
95
|
const appState = useRef(AppState.currentState);
|
|
98
96
|
|
|
99
97
|
const { childRef, showingPopover, showPopoverWithRef, hidePopover } =
|
|
@@ -105,24 +103,17 @@ const UnitDetail = ({ route }) => {
|
|
|
105
103
|
listAutomate
|
|
106
104
|
);
|
|
107
105
|
|
|
108
|
-
const handleFullScreen = (data) => {
|
|
109
|
-
setIsFullScreen(!isFullScreen);
|
|
110
|
-
setDataFullScreen(data);
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const onClose = useCallback(() => {
|
|
114
|
-
setIsFullScreen(false);
|
|
115
|
-
}, []);
|
|
116
|
-
|
|
117
106
|
const prepareData = useCallback(
|
|
118
107
|
(rawUnitData) => {
|
|
119
108
|
rawUnitData.stations.unshift({
|
|
120
109
|
isOneTap: true,
|
|
121
110
|
name: t('smart'),
|
|
111
|
+
id: 'smart',
|
|
122
112
|
});
|
|
123
113
|
rawUnitData.stations.unshift({
|
|
124
114
|
isFavorites: true,
|
|
125
115
|
name: t('favorites'),
|
|
116
|
+
id: 'favorites',
|
|
126
117
|
});
|
|
127
118
|
},
|
|
128
119
|
[t]
|
|
@@ -217,8 +208,9 @@ const UnitDetail = ({ route }) => {
|
|
|
217
208
|
}, [unit, indexStation]);
|
|
218
209
|
|
|
219
210
|
useEffect(() => {
|
|
211
|
+
isEditSubUnit && setIndexStation(0);
|
|
220
212
|
isOneTap && setIndexStation(1);
|
|
221
|
-
}, [isOneTap]);
|
|
213
|
+
}, [isEditSubUnit, isOneTap]);
|
|
222
214
|
|
|
223
215
|
useEffect(() => {
|
|
224
216
|
if (listMenuItem.length && isAddSubUnit) {
|
|
@@ -227,7 +219,7 @@ const UnitDetail = ({ route }) => {
|
|
|
227
219
|
}, [listMenuItem.length, isAddSubUnit]);
|
|
228
220
|
|
|
229
221
|
useEffect(() => {
|
|
230
|
-
if (listMenuItem.length && stationId) {
|
|
222
|
+
if (!isEditSubUnit && listMenuItem.length && stationId) {
|
|
231
223
|
const getStationCurrent = listMenuItem.filter(
|
|
232
224
|
(item) => item?.station.id === stationId
|
|
233
225
|
);
|
|
@@ -262,13 +254,7 @@ const UnitDetail = ({ route }) => {
|
|
|
262
254
|
);
|
|
263
255
|
}
|
|
264
256
|
if (station?.camera_devices) {
|
|
265
|
-
return
|
|
266
|
-
<CameraDevice
|
|
267
|
-
station={station}
|
|
268
|
-
handleFullScreen={handleFullScreen}
|
|
269
|
-
goToPlayBack={goToPlayBack}
|
|
270
|
-
/>
|
|
271
|
-
);
|
|
257
|
+
return <CameraDevice station={station} goToPlayBack={goToPlayBack} />;
|
|
272
258
|
} else if (station?.isOneTap) {
|
|
273
259
|
return (
|
|
274
260
|
<SubUnitAutomate
|
|
@@ -318,7 +304,8 @@ const UnitDetail = ({ route }) => {
|
|
|
318
304
|
clearTimeout(to);
|
|
319
305
|
}, 3000);
|
|
320
306
|
}
|
|
321
|
-
|
|
307
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
308
|
+
}, [isFirstOpenCamera, isIOS]);
|
|
322
309
|
|
|
323
310
|
return (
|
|
324
311
|
<WrapParallaxScrollView
|
|
@@ -333,6 +320,8 @@ const UnitDetail = ({ route }) => {
|
|
|
333
320
|
onMore={showPopoverWithRef}
|
|
334
321
|
hideRightPlus={!isOwner}
|
|
335
322
|
onBack={(isSuccessfullyConnected && Dashboard) || (routeName && onBack)}
|
|
323
|
+
accessibilityLabel={TESTID.UNIT_DETAIL_PARALLAX_SCROLLVIEW}
|
|
324
|
+
idButtonMore={TESTID.UNIT_DETAIL_PARALLAX_BUTTON_MORE}
|
|
336
325
|
>
|
|
337
326
|
{renderCamera}
|
|
338
327
|
|
|
@@ -344,6 +333,9 @@ const UnitDetail = ({ route }) => {
|
|
|
344
333
|
listMenuItem={listMenuItem}
|
|
345
334
|
onSnapToItem={onSnapToItem}
|
|
346
335
|
indexStation={indexStation}
|
|
336
|
+
idLabelScrollView={TESTID.NAV_LIST}
|
|
337
|
+
idLabelItem={TESTID.SUB_UNIT_NAME}
|
|
338
|
+
idLabelIconBars={TESTID.NAVBAR_ICON_BARS}
|
|
347
339
|
/>
|
|
348
340
|
{renderDetailSubUnit()}
|
|
349
341
|
{!!unit.can_add && unit.stations.length === 0 && (
|
|
@@ -364,12 +356,8 @@ const UnitDetail = ({ route }) => {
|
|
|
364
356
|
isOwner={isOwner}
|
|
365
357
|
childRef={childRef}
|
|
366
358
|
showingPopover={showingPopover}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
isVisible={isFullScreen}
|
|
370
|
-
data={dataFullScreen}
|
|
371
|
-
modalStyles={styles.modal}
|
|
372
|
-
onClose={onClose}
|
|
359
|
+
idLabelPopover={TESTID.UNIT_DETAIL_POPUP_MORE}
|
|
360
|
+
idLabelItem={TESTID.UNIT_DETAIL_POPUP_MORE_ITEM}
|
|
373
361
|
/>
|
|
374
362
|
<PreventAccess
|
|
375
363
|
visible={showPreventAccess}
|
|
@@ -5,7 +5,16 @@ import { useNavigation } from '@react-navigation/native';
|
|
|
5
5
|
import { MenuActionMore } from '../../commons';
|
|
6
6
|
|
|
7
7
|
const MoreMenu = memo(
|
|
8
|
-
({
|
|
8
|
+
({
|
|
9
|
+
unit,
|
|
10
|
+
isOwner,
|
|
11
|
+
hidePopover,
|
|
12
|
+
childRef,
|
|
13
|
+
showingPopover,
|
|
14
|
+
idLabelPopover,
|
|
15
|
+
idLabelScrollView,
|
|
16
|
+
idLabelItem,
|
|
17
|
+
}) => {
|
|
9
18
|
const t = useTranslations();
|
|
10
19
|
const navigation = useNavigation();
|
|
11
20
|
|
|
@@ -19,16 +28,19 @@ const MoreMenu = memo(
|
|
|
19
28
|
|
|
20
29
|
const listMenuItem = useMemo(() => {
|
|
21
30
|
const RouteManageUnit = {
|
|
31
|
+
id: 1,
|
|
22
32
|
route: Routes.ManageUnit,
|
|
23
33
|
text: t('manage_unit'),
|
|
24
34
|
data: { unitId: unit.id, unit },
|
|
25
35
|
};
|
|
26
36
|
const RouteUnitMemberList = {
|
|
37
|
+
id: 2,
|
|
27
38
|
route: Routes.UnitMemberList,
|
|
28
39
|
text: t('members'),
|
|
29
40
|
data: { unitId: unit.id, unit },
|
|
30
41
|
};
|
|
31
42
|
const ListSmartAccount = {
|
|
43
|
+
id: 3,
|
|
32
44
|
route: Routes.ListSmartAccount,
|
|
33
45
|
text: t('smart_account'),
|
|
34
46
|
data: { unitId: unit.id, unit },
|
|
@@ -45,6 +57,9 @@ const MoreMenu = memo(
|
|
|
45
57
|
listMenuItem={listMenuItem}
|
|
46
58
|
childRef={childRef}
|
|
47
59
|
onItemClick={onItemClick}
|
|
60
|
+
idLabelPopover={idLabelPopover}
|
|
61
|
+
idLabelScrollView={idLabelScrollView}
|
|
62
|
+
idLabelItem={idLabelItem}
|
|
48
63
|
/>
|
|
49
64
|
);
|
|
50
65
|
}
|
|
@@ -21,6 +21,7 @@ import { SCContext } from '../../context';
|
|
|
21
21
|
import { Action } from '../../context/actionType';
|
|
22
22
|
import { axiosGet, axiosPost } from '../../utils/Apis/axios';
|
|
23
23
|
import { API, Colors } from '../../configs';
|
|
24
|
+
import { TESTID } from '../../configs/Constants';
|
|
24
25
|
import styles from './SelectAddToFavoritesStyles';
|
|
25
26
|
|
|
26
27
|
const SelectAddToFavorites = memo(({ route }) => {
|
|
@@ -136,7 +137,12 @@ const SelectAddToFavorites = memo(({ route }) => {
|
|
|
136
137
|
|
|
137
138
|
const rightComponent = useMemo(
|
|
138
139
|
() => (
|
|
139
|
-
<TouchableOpacity
|
|
140
|
+
<TouchableOpacity
|
|
141
|
+
testID={TESTID.ICON_BACK}
|
|
142
|
+
accessibilityLabel={TESTID.ICON_BACK}
|
|
143
|
+
style={styles.buttonClose}
|
|
144
|
+
onPress={goBack}
|
|
145
|
+
>
|
|
140
146
|
<Icon name={'close'} size={24} color={Colors.Black} />
|
|
141
147
|
</TouchableOpacity>
|
|
142
148
|
),
|
|
@@ -151,6 +157,8 @@ const SelectAddToFavorites = memo(({ route }) => {
|
|
|
151
157
|
style={styles.wrap}
|
|
152
158
|
contentContainerStyle={styles.contentContainerStyle}
|
|
153
159
|
scrollIndicatorInsets={{ right: 1 }}
|
|
160
|
+
testID={TESTID.LIST_FAVORITES}
|
|
161
|
+
idLabelScrollView={TESTID.LIST_FAVORITES}
|
|
154
162
|
>
|
|
155
163
|
<Text bold type="H2" style={styles.title}>
|
|
156
164
|
{t('select_device')}
|
|
@@ -162,6 +170,8 @@ const SelectAddToFavorites = memo(({ route }) => {
|
|
|
162
170
|
listMenuItem={listMenuItem}
|
|
163
171
|
onSnapToItem={onSnapToItem}
|
|
164
172
|
indexStation={indexStation}
|
|
173
|
+
idLabelItem={TESTID.SUB_UNIT_FAVORITES}
|
|
174
|
+
idLabelIconBars={TESTID.NAVBAR_ICON_BARS_ADD_FAVORITES}
|
|
165
175
|
style={styles.navbar}
|
|
166
176
|
/>
|
|
167
177
|
)}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FlatList } from 'react-native';
|
|
3
|
+
import { act, create } from 'react-test-renderer';
|
|
4
|
+
import Station from '..';
|
|
5
|
+
import { SCProvider } from '../../../../context';
|
|
6
|
+
import { mockSCStore } from '../../../../context/mockStore';
|
|
7
|
+
|
|
8
|
+
const mockOnSnapToItem = jest.fn();
|
|
9
|
+
|
|
10
|
+
const wrapComponent = (route) => (
|
|
11
|
+
<SCProvider initState={mockSCStore({})}>
|
|
12
|
+
<Station
|
|
13
|
+
listStation={[{ id: 1, station: { id: 1 }, text: 'station1' }]}
|
|
14
|
+
onSnapToItem={mockOnSnapToItem}
|
|
15
|
+
indexStation={1}
|
|
16
|
+
/>
|
|
17
|
+
</SCProvider>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
describe('Test Station', async () => {
|
|
21
|
+
let tree;
|
|
22
|
+
let route = {
|
|
23
|
+
unitId: 1,
|
|
24
|
+
unitData: {
|
|
25
|
+
id: 1,
|
|
26
|
+
},
|
|
27
|
+
isOneTap: false,
|
|
28
|
+
routeName: 'Test',
|
|
29
|
+
stationId: 1,
|
|
30
|
+
isAddSubUnit: false,
|
|
31
|
+
isSuccessfullyConnected: false,
|
|
32
|
+
};
|
|
33
|
+
it('Test render', async () => {
|
|
34
|
+
await act(async () => {
|
|
35
|
+
tree = create(wrapComponent(route));
|
|
36
|
+
});
|
|
37
|
+
const instance = tree.root;
|
|
38
|
+
const FlatLists = instance.findAllByType(FlatList);
|
|
39
|
+
expect(FlatLists).toHaveLength(1);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -6,6 +6,7 @@ import { useIsFocused, useNavigation } from '@react-navigation/native';
|
|
|
6
6
|
import { axiosGet } from '../../utils/Apis/axios';
|
|
7
7
|
import { API } from '../../configs';
|
|
8
8
|
import { useReceiveNotifications } from '../../hooks';
|
|
9
|
+
import { TESTID } from '../../configs/Constants';
|
|
9
10
|
import { useSCContextSelector } from '../../context';
|
|
10
11
|
|
|
11
12
|
const Summaries = memo(({ unit }) => {
|
|
@@ -108,7 +109,11 @@ const Summaries = memo(({ unit }) => {
|
|
|
108
109
|
return (
|
|
109
110
|
<>
|
|
110
111
|
{!unitSummaries || !unitSummaries.length ? null : (
|
|
111
|
-
<ScrollView
|
|
112
|
+
<ScrollView
|
|
113
|
+
horizontal={true}
|
|
114
|
+
scrollIndicatorInsets={{ right: 1 }}
|
|
115
|
+
accessibilityLabel={TESTID.UNIT_DETAIL_UNIT_SUMMARY_VIEW}
|
|
116
|
+
>
|
|
112
117
|
{unitSummaries.map((item, index) => (
|
|
113
118
|
<SummaryItem key={index} item={item} goToSummary={goToSummary} />
|
|
114
119
|
))}
|
|
@@ -12,7 +12,6 @@ import { TESTID } from '../../../configs/Constants';
|
|
|
12
12
|
import { SCProvider } from '../../../context';
|
|
13
13
|
import { mockSCStore } from '../../../context/mockStore';
|
|
14
14
|
import CameraDevice from '../../../commons/CameraDevice';
|
|
15
|
-
import { ModalFullVideo } from '../../../commons/Modal';
|
|
16
15
|
import SubUnitFavorites from '../../../commons/SubUnit/Favorites';
|
|
17
16
|
import api from '../../../utils/Apis/axios';
|
|
18
17
|
import PreventAccess from '../../../commons/PreventAccess';
|
|
@@ -352,11 +351,7 @@ describe('Test UnitDetail', () => {
|
|
|
352
351
|
el.props.testID === TESTID.SUB_UNIT_FULL_CAMERA &&
|
|
353
352
|
el.type === TouchableOpacity
|
|
354
353
|
);
|
|
355
|
-
|
|
356
354
|
expect(fullCamera).toHaveLength(0);
|
|
357
|
-
const fullView = instance.findAllByType(ModalFullVideo);
|
|
358
|
-
expect(fullView).toHaveLength(1);
|
|
359
|
-
expect(fullView[0].props.isVisible).toEqual(false);
|
|
360
355
|
});
|
|
361
356
|
|
|
362
357
|
test('onPress subunit camera devices', async () => {
|
|
@@ -438,6 +433,7 @@ describe('Test UnitDetail', () => {
|
|
|
438
433
|
};
|
|
439
434
|
route.params.isAddSubUnit = true;
|
|
440
435
|
route.params.unitData = unitData;
|
|
436
|
+
route.params.isSuccessfullyConnected = false;
|
|
441
437
|
await act(async () => {
|
|
442
438
|
tree = await renderer.create(wrapComponent(route, account));
|
|
443
439
|
});
|
|
@@ -9,7 +9,7 @@ import ValueChange from '../../../../../assets/images/ValueChange.svg';
|
|
|
9
9
|
import Schedule from '../../../../../assets/images/Schedule.svg';
|
|
10
10
|
import Event from '../../../../../assets/images/Event.svg';
|
|
11
11
|
import styles from './styles';
|
|
12
|
-
import { AUTOMATE_TYPE } from '../../../../configs/Constants';
|
|
12
|
+
import { AUTOMATE_TYPE, TESTID } from '../../../../configs/Constants';
|
|
13
13
|
import { Colors } from '../../../../configs';
|
|
14
14
|
|
|
15
15
|
const AutomateScript = ({ automate, onPress, isSelected }) => {
|
|
@@ -36,7 +36,10 @@ const AutomateScript = ({ automate, onPress, isSelected }) => {
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
return (
|
|
39
|
-
<TouchableWithoutFeedback
|
|
39
|
+
<TouchableWithoutFeedback
|
|
40
|
+
onPress={_onPress}
|
|
41
|
+
accessibilityLabel={`${TESTID.TOUCHABLE_ACTION_ADD_ITEM_AUTOMATE_FAVORITE}-${automate.id}`}
|
|
42
|
+
>
|
|
40
43
|
<View style={[styles.container, isSelected && styles.active]}>
|
|
41
44
|
<View style={styles.boxIcon}>{displayIcon()}</View>
|
|
42
45
|
<View>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { getDateData, getTitleFromTime, timeDifference } from '../time';
|
|
2
|
+
|
|
3
|
+
describe('Test timer', () => {
|
|
4
|
+
let result;
|
|
5
|
+
it('Test timeDifference when elapsed < msPerMinute and symbol=false', async () => {
|
|
6
|
+
result = await timeDifference(
|
|
7
|
+
new Date('2022-08-01T03:24:01'),
|
|
8
|
+
new Date('2022-08-01T03:24:00')
|
|
9
|
+
);
|
|
10
|
+
expect(result).toBe('1 seconds ago');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('Test timeDifference when elapsed < msPerMinute and symbol=true', async () => {
|
|
14
|
+
result = await timeDifference(
|
|
15
|
+
new Date('2022-08-01T03:24:01'),
|
|
16
|
+
new Date('2022-08-01T03:24:00'),
|
|
17
|
+
true
|
|
18
|
+
);
|
|
19
|
+
expect(result).toBe('1 secs ago');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('Test timeDifference when elapsed < msPerHour and symbol=false', async () => {
|
|
23
|
+
result = await timeDifference(
|
|
24
|
+
new Date('2022-08-01T03:24:01'),
|
|
25
|
+
new Date('2022-08-01T03:00:00')
|
|
26
|
+
);
|
|
27
|
+
expect(result).toBe('24 minutes ago');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('Test timeDifference when elapsed < msPerHour and symbol=true', async () => {
|
|
31
|
+
result = await timeDifference(
|
|
32
|
+
new Date('2022-08-01T03:24:01'),
|
|
33
|
+
new Date('2022-08-01T03:00:00'),
|
|
34
|
+
true
|
|
35
|
+
);
|
|
36
|
+
expect(result).toBe('24 mins ago');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('Test timeDifference when elapsed < msPerDay and symbol=false', async () => {
|
|
40
|
+
result = await timeDifference(
|
|
41
|
+
new Date('2022-08-01T03:24:01'),
|
|
42
|
+
new Date('2022-08-01T02:00:00')
|
|
43
|
+
);
|
|
44
|
+
expect(result).toBe('1 hours ago');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('Test timeDifference when elapsed < msPerMonthe', async () => {
|
|
48
|
+
result = await timeDifference(
|
|
49
|
+
new Date('2022-08-01T03:24:01'),
|
|
50
|
+
new Date('2022-07-31T02:00:00')
|
|
51
|
+
);
|
|
52
|
+
expect(result).toBe('1 days ago');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('Test timeDifference when elapsed < msPerYear', async () => {
|
|
56
|
+
result = await timeDifference(
|
|
57
|
+
new Date('2022-08-01T03:24:01'),
|
|
58
|
+
new Date('2022-05-31T02:00:00')
|
|
59
|
+
);
|
|
60
|
+
expect(result).toBe('2 months ago');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('Test timeDifference when elapsed > msPerYear', async () => {
|
|
64
|
+
result = await timeDifference(
|
|
65
|
+
new Date('2022-08-01T03:24:01'),
|
|
66
|
+
new Date('2021-05-31T02:00:00')
|
|
67
|
+
);
|
|
68
|
+
expect(result).toBe('1 years ago');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('Test getTitleFromTime when time = current', async () => {
|
|
72
|
+
result = await getTitleFromTime(new Date(), new Date());
|
|
73
|
+
expect(result).toBe('Today');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('Test getTitleFromTime when time > current', async () => {
|
|
77
|
+
result = await getTitleFromTime(
|
|
78
|
+
new Date('2022-05-31T02:00:00'),
|
|
79
|
+
new Date('2022-05-30T02:00:00')
|
|
80
|
+
);
|
|
81
|
+
expect(result).toBe('31/05/2022');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('Test getTitleFromTime when time < current', async () => {
|
|
85
|
+
result = await getTitleFromTime(
|
|
86
|
+
new Date('2022-05-29T02:00:00'),
|
|
87
|
+
new Date('2022-05-30T02:00:00')
|
|
88
|
+
);
|
|
89
|
+
expect(result).toBe('Yesterday');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('Test getDateData', async () => {
|
|
93
|
+
result = await getDateData(
|
|
94
|
+
new Date('2022-05-30T02:00:00'),
|
|
95
|
+
new Date('2022-06-30T02:00:00')
|
|
96
|
+
);
|
|
97
|
+
expect(result).toEqual([[], 0]);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const formatVietnamese = (str) => {
|
|
2
|
+
str = str.toLowerCase();
|
|
3
|
+
str = str.replace(/à|á|ạ|ả|ã|â|ầ|ấ|ậ|ẩ|ẫ|ă|ằ|ắ|ặ|ẳ|ẵ/g, 'a');
|
|
4
|
+
str = str.replace(/è|é|ẹ|ẻ|ẽ|ê|ề|ế|ệ|ể|ễ/g, 'e');
|
|
5
|
+
str = str.replace(/ì|í|ị|ỉ|ĩ/g, 'i');
|
|
6
|
+
str = str.replace(/ò|ó|ọ|ỏ|õ|ô|ồ|ố|ộ|ổ|ỗ|ơ|ờ|ớ|ợ|ở|ỡ/g, 'o');
|
|
7
|
+
str = str.replace(/ù|ú|ụ|ủ|ũ|ư|ừ|ứ|ự|ử|ữ/g, 'u');
|
|
8
|
+
str = str.replace(/ỳ|ý|ỵ|ỷ|ỹ/g, 'y');
|
|
9
|
+
str = str.replace(/đ/g, 'd');
|
|
10
|
+
return str;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const convertToSlug = (text) => {
|
|
14
|
+
text = text.substring(0, Math.max(80, text.length));
|
|
15
|
+
text = formatVietnamese(text);
|
|
16
|
+
return text.toLowerCase().replace(/[^\w ]+/g, '');
|
|
17
|
+
};
|