@eohjsc/react-native-smart-city 0.2.95 → 0.2.96
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 +2 -2
- package/src/Images/Common/member.svg +4 -0
- package/src/Images/Common/owner.svg +3 -0
- package/src/commons/ActionGroup/NumberUpDownActionTemplate.js +5 -1
- package/src/commons/ActionGroup/OptionsDropdownActionTemplate.js +5 -1
- package/src/commons/ActionGroup/__test__/OptionsDropdownTemplate.test.js +2 -2
- package/src/commons/Connecting /__test__/Connecting.test.js +23 -0
- package/src/commons/Connecting /index.js +67 -0
- package/src/commons/Connecting /styles.js +28 -0
- package/src/commons/ConnectingProcess/index.js +3 -54
- package/src/commons/MenuActionMore/index.js +8 -1
- package/src/commons/Sharing/MemberList.js +5 -10
- package/src/commons/Sharing/RowMember.js +128 -38
- package/src/commons/Sharing/__test__/MemberList.test.js +3 -3
- package/src/commons/Sharing/__test__/RowMember.test.js +4 -4
- package/src/configs/API.js +10 -0
- package/src/configs/Constants.js +10 -0
- package/src/hooks/Common/useSensorsStatus.js +33 -23
- package/src/navigations/UnitStack.js +16 -0
- package/src/screens/AddNewGateway/PlugAndPlay/ConnectWifiWarning.js +12 -3
- package/src/screens/AddNewGateway/PlugAndPlay/FirstWarning.js +5 -1
- package/src/screens/AddNewGateway/PlugAndPlay/GatewayWifiList.js +9 -1
- package/src/screens/EditActionsList/Styles/indexStyles.js +1 -0
- package/src/screens/EmergencyContacts/EmergencyContactsList.js +1 -1
- package/src/screens/EmergencyContacts/EmergencyContactsSelectContacts.js +29 -11
- package/src/screens/EmergencyContacts/__test__/EmergencyContactList.test.js +1 -0
- package/src/screens/EmergencyContacts/__test__/EmergencyContactsSelectContacts.test.js +56 -0
- package/src/screens/EnterPassword/__test__/EnterPassword.test.js +124 -0
- package/src/screens/EnterPassword/index.js +84 -0
- package/src/screens/EnterPassword/styles.js +36 -0
- package/src/screens/Sharing/Components/ItemChangeRole.js +43 -0
- package/src/screens/Sharing/Components/SensorItem.js +7 -1
- package/src/screens/Sharing/Components/Styles/ItemChangeRoleStyles.js +35 -0
- package/src/screens/Sharing/Components/__test__/ItemChangeRole.test.js +37 -0
- package/src/screens/Sharing/Components/__test__/SensorItem.test.js +53 -0
- package/src/screens/Sharing/InfoMemberUnit.js +274 -0
- package/src/screens/Sharing/MemberList.js +50 -53
- package/src/screens/Sharing/SelectPermission.js +93 -12
- package/src/screens/Sharing/Styles/inforMemberUnitStyles.js +92 -0
- package/src/screens/Sharing/__test__/InfoMemberUnit.test.js +121 -0
- package/src/screens/Sharing/__test__/MemberList.test.js +9 -24
- package/src/screens/Sharing/__test__/SelectPermission.test.js +53 -0
- package/src/screens/Sharing/hooks/index.js +76 -32
- package/src/screens/Unit/Detail.js +12 -10
- package/src/screens/Unit/ManageUnit.js +5 -5
- package/src/screens/Unit/ManageUnitStyles.js +1 -0
- package/src/utils/I18n/translations/en.json +13 -1
- package/src/utils/I18n/translations/vi.json +11 -1
- package/src/utils/Route/index.js +2 -0
- package/src/utils/Utils.js +21 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eohjsc/react-native-smart-city",
|
|
3
3
|
"title": "React Native Smart Home",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.96",
|
|
5
5
|
"description": "TODO",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"files": [
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"dependencies": {
|
|
98
98
|
"@ant-design/icons-react-native": "^2.2.1",
|
|
99
99
|
"@ant-design/react-native": "^4.0.5",
|
|
100
|
-
"@eohjsc/highcharts": "^1.0.
|
|
100
|
+
"@eohjsc/highcharts": "^1.0.8",
|
|
101
101
|
"@eohjsc/react-native-keyboard-aware-scroll-view": "^0.9.5",
|
|
102
102
|
"@formatjs/intl-getcanonicallocales": "^1.4.5",
|
|
103
103
|
"@formatjs/intl-numberformat": "^5.6.2",
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M12 6C13.1046 6 14 5.10457 14 4C14 2.89543 13.1046 2 12 2C10.8954 2 10 2.89543 10 4C10 5.10457 10.8954 6 12 6Z" fill="#262626"/>
|
|
3
|
+
<path d="M15.89 8.11C15.5 7.72 14.83 7 13.53 7C13.32 7 12.11 7 10.99 7C8.24 6.99 6 4.75 6 2H4C4 5.16 6.11 7.84 9 8.71V22H11V16H13V22H15V10.05L18.95 14L20.36 12.59L15.89 8.11Z" fill="#262626"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.753 9.0184C21.753 9.66253 21.2308 10.1847 20.5867 10.1847C20.4682 10.1847 20.3559 10.1617 20.2483 10.1291L18.5961 15.808H5.15687L3.50472 10.1291C3.39711 10.1617 3.28483 10.1847 3.16633 10.1847C2.52221 10.1847 2 9.66253 2 9.0184C2 8.37428 2.52221 7.85207 3.16633 7.85207C3.81046 7.85207 4.33266 8.37428 4.33266 9.0184C4.33266 9.24732 4.26424 9.45912 4.15041 9.6392L7.88142 12.1939L8.36195 7.3196C7.78003 7.25367 7.32625 6.76567 7.32625 6.16633C7.32625 5.52221 7.84846 5 8.49258 5C9.13671 5 9.65892 5.52221 9.65892 6.16633C9.65892 6.59305 9.42752 6.96224 9.08539 7.16565L11.8765 11.7398L14.6863 7.13517C14.3743 6.92585 14.1687 6.57035 14.1687 6.16633C14.1687 5.52221 14.6909 5 15.3351 5C15.9792 5 16.5014 5.52221 16.5014 6.16633C16.5014 6.79118 16.0091 7.2969 15.392 7.32707L15.8716 12.1939L19.6026 9.6392C19.4888 9.45912 19.4203 9.24732 19.4203 9.0184C19.4203 8.37428 19.9425 7.85207 20.5867 7.85207C21.2308 7.85207 21.753 8.37428 21.753 9.0184ZM5.15782 16.6384H18.5962V18.712H5.15782V16.6384Z" fill="#262626"/>
|
|
3
|
+
</svg>
|
|
@@ -39,7 +39,11 @@ const NumberUpDownActionTemplate = ({ actionGroup, doAction, sensor }) => {
|
|
|
39
39
|
|
|
40
40
|
const doActionAndWatchConfig = useCallback(
|
|
41
41
|
(actionData, actionValue, actionName) => {
|
|
42
|
-
doAction(
|
|
42
|
+
doAction(
|
|
43
|
+
actionData,
|
|
44
|
+
JSON.stringify({ temperature: actionValue }),
|
|
45
|
+
actionName
|
|
46
|
+
);
|
|
43
47
|
if (!sensor?.is_managed_by_backend) {
|
|
44
48
|
return;
|
|
45
49
|
}
|
|
@@ -61,7 +61,11 @@ const OptionsDropdownActionTemplate = ({ actionGroup, doAction, sensor }) => {
|
|
|
61
61
|
const value = getOptionValue(newOption);
|
|
62
62
|
let actionName = `${sensor?.name} ${title?.toLowerCase()} ${value}`;
|
|
63
63
|
actionName = actionName.replace(/\s+/g, ' ').trim();
|
|
64
|
-
doAction(
|
|
64
|
+
doAction(
|
|
65
|
+
action_data,
|
|
66
|
+
JSON.stringify({ level: value, key_code: newOption?.value_int }),
|
|
67
|
+
actionName
|
|
68
|
+
);
|
|
65
69
|
if (sensor?.is_managed_by_backend) {
|
|
66
70
|
configuration.config && watchMultiConfigs([configuration.config]);
|
|
67
71
|
}
|
|
@@ -152,7 +152,7 @@ describe('Test OptionsDropdownActionTemplate', () => {
|
|
|
152
152
|
|
|
153
153
|
expect(mockDoAction).toHaveBeenCalledWith(
|
|
154
154
|
action_data,
|
|
155
|
-
1,
|
|
155
|
+
JSON.stringify({ level: 1, key_code: 1 }),
|
|
156
156
|
'Sensor name fan speed 1'
|
|
157
157
|
);
|
|
158
158
|
is_managed_by_backend
|
|
@@ -221,7 +221,7 @@ describe('Test OptionsDropdownActionTemplate', () => {
|
|
|
221
221
|
|
|
222
222
|
expect(mockDoAction).toHaveBeenCalledWith(
|
|
223
223
|
action_data,
|
|
224
|
-
'level-1',
|
|
224
|
+
JSON.stringify({ level: 'level-1', key_code: 1 }),
|
|
225
225
|
'Sensor name fan speed level-1'
|
|
226
226
|
); // doAction with text instead of int
|
|
227
227
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer, { act } from 'react-test-renderer';
|
|
3
|
+
import Connecting from '../index';
|
|
4
|
+
import Text from '../../Text';
|
|
5
|
+
import { SCProvider } from '../../../context';
|
|
6
|
+
import { mockSCStore } from '../../../context/mockStore';
|
|
7
|
+
|
|
8
|
+
const wrapComponent = (route) => (
|
|
9
|
+
<SCProvider initState={mockSCStore({})}>
|
|
10
|
+
<Connecting route={route} />
|
|
11
|
+
</SCProvider>
|
|
12
|
+
);
|
|
13
|
+
describe('Test Connecting', () => {
|
|
14
|
+
let tree;
|
|
15
|
+
test('create Connecting', () => {
|
|
16
|
+
act(() => {
|
|
17
|
+
tree = renderer.create(wrapComponent());
|
|
18
|
+
});
|
|
19
|
+
const instance = tree.root;
|
|
20
|
+
const text = instance.findAllByType(Text);
|
|
21
|
+
expect(text).toHaveLength(4);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React, { useEffect, useCallback, useState } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import * as Progress from 'react-native-progress';
|
|
4
|
+
import Text from '../Text';
|
|
5
|
+
import { Colors, Constants } from '../../configs';
|
|
6
|
+
import styles from './styles';
|
|
7
|
+
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
8
|
+
import { HeaderCustom } from '../Header';
|
|
9
|
+
|
|
10
|
+
const Connecting = ({ isLoading, isConnect, isPercentConnect }) => {
|
|
11
|
+
const t = useTranslations();
|
|
12
|
+
const [percent, setPercent] = useState(0);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
processLoading();
|
|
16
|
+
}, [processLoading]);
|
|
17
|
+
|
|
18
|
+
const processLoading = useCallback(() => {
|
|
19
|
+
let interval;
|
|
20
|
+
if (isLoading) {
|
|
21
|
+
interval = setInterval(() => {
|
|
22
|
+
setPercent((prev) => {
|
|
23
|
+
if (prev === 1) {
|
|
24
|
+
clearInterval(interval);
|
|
25
|
+
return 1;
|
|
26
|
+
} else if (prev === 0.8 && isConnect) {
|
|
27
|
+
clearInterval(interval);
|
|
28
|
+
return 0.8;
|
|
29
|
+
} else {
|
|
30
|
+
return prev + 0.2;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}, 180);
|
|
34
|
+
} else {
|
|
35
|
+
clearInterval(interval);
|
|
36
|
+
}
|
|
37
|
+
}, [isConnect, isLoading]);
|
|
38
|
+
return (
|
|
39
|
+
<View style={styles.screen}>
|
|
40
|
+
<HeaderCustom title={t('connect_device')} isShowSeparator />
|
|
41
|
+
<View style={styles.body}>
|
|
42
|
+
<View style={styles.connecting}>
|
|
43
|
+
<Text type="H4" bold>
|
|
44
|
+
{t('connecting')}
|
|
45
|
+
</Text>
|
|
46
|
+
</View>
|
|
47
|
+
<View style={styles.percentLoad}>
|
|
48
|
+
<Progress.Bar
|
|
49
|
+
progress={isPercentConnect || percent}
|
|
50
|
+
animated={true}
|
|
51
|
+
color={Colors.Primary}
|
|
52
|
+
indeterminateAnimationDuration={1000}
|
|
53
|
+
height={7}
|
|
54
|
+
width={Constants.width - 80}
|
|
55
|
+
useNativeDriver={true}
|
|
56
|
+
/>
|
|
57
|
+
<Text style={styles.textPercentLoad}>{`${parseInt(
|
|
58
|
+
(isPercentConnect || percent) * 100,
|
|
59
|
+
10
|
|
60
|
+
)}%`}</Text>
|
|
61
|
+
</View>
|
|
62
|
+
</View>
|
|
63
|
+
</View>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default Connecting;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
import { Constants } from '../../configs';
|
|
3
|
+
|
|
4
|
+
export default StyleSheet.create({
|
|
5
|
+
screen: {
|
|
6
|
+
flex: 1,
|
|
7
|
+
width: '100%',
|
|
8
|
+
},
|
|
9
|
+
body: {
|
|
10
|
+
width: '100%',
|
|
11
|
+
height: Constants.height - 50,
|
|
12
|
+
justifyContent: 'center',
|
|
13
|
+
alignItems: 'center',
|
|
14
|
+
},
|
|
15
|
+
connecting: {
|
|
16
|
+
flexDirection: 'row',
|
|
17
|
+
justifyContent: 'center',
|
|
18
|
+
marginBottom: 10,
|
|
19
|
+
},
|
|
20
|
+
percentLoad: {
|
|
21
|
+
flexDirection: 'row',
|
|
22
|
+
justifyContent: 'center',
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
},
|
|
25
|
+
textPercentLoad: {
|
|
26
|
+
marginLeft: 10,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -3,14 +3,14 @@ import { SafeAreaView, View, TouchableOpacity } from 'react-native';
|
|
|
3
3
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
4
4
|
import Routes from '../../utils/Route';
|
|
5
5
|
import { useNavigation } from '@react-navigation/native';
|
|
6
|
-
import * as Progress from 'react-native-progress';
|
|
7
6
|
import ImageSuccessfully from '../../Images/Common/SuccessfullyConnected.svg';
|
|
8
7
|
|
|
9
8
|
import Text from '../Text';
|
|
10
9
|
import { axiosPost } from '../../utils/Apis/axios';
|
|
11
|
-
import { API, Colors
|
|
10
|
+
import { API, Colors } from '../../configs';
|
|
12
11
|
import styles from './styles';
|
|
13
12
|
import DeviceItem from './DeviceItem/DeviceItem';
|
|
13
|
+
import Connecting from '../Connecting ';
|
|
14
14
|
import { useSCContextSelector } from '../../context';
|
|
15
15
|
|
|
16
16
|
const ConnectingProcess = ({ route }) => {
|
|
@@ -29,31 +29,9 @@ const ConnectingProcess = ({ route }) => {
|
|
|
29
29
|
wifi_pass,
|
|
30
30
|
chip_id,
|
|
31
31
|
} = route.params;
|
|
32
|
-
const [percent, setPercent] = useState(0);
|
|
33
32
|
const [isLoading, setIsLoading] = useState(true);
|
|
34
33
|
const [sensor, setSensor] = useState(null);
|
|
35
34
|
const user = useSCContextSelector((state) => state?.auth?.account?.user);
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
processLoading();
|
|
38
|
-
}, [processLoading]);
|
|
39
|
-
|
|
40
|
-
const processLoading = useCallback(() => {
|
|
41
|
-
let interval;
|
|
42
|
-
if (isLoading) {
|
|
43
|
-
interval = setInterval(() => {
|
|
44
|
-
setPercent((prev) => {
|
|
45
|
-
if (prev === 1) {
|
|
46
|
-
clearInterval(interval);
|
|
47
|
-
return 1;
|
|
48
|
-
} else {
|
|
49
|
-
return prev + 0.2;
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}, 180);
|
|
53
|
-
} else {
|
|
54
|
-
clearInterval(interval);
|
|
55
|
-
}
|
|
56
|
-
}, [isLoading]);
|
|
57
35
|
|
|
58
36
|
const ConnectingDevice = useCallback(async () => {
|
|
59
37
|
setIsLoading(true);
|
|
@@ -120,35 +98,6 @@ const ConnectingProcess = ({ route }) => {
|
|
|
120
98
|
chip_id,
|
|
121
99
|
]);
|
|
122
100
|
|
|
123
|
-
const Connecting = useCallback(() => {
|
|
124
|
-
return (
|
|
125
|
-
<>
|
|
126
|
-
<View style={styles.progressBar}>
|
|
127
|
-
<View style={styles.connecting}>
|
|
128
|
-
<Text type="H4" bold>
|
|
129
|
-
{t('connecting')}
|
|
130
|
-
</Text>
|
|
131
|
-
</View>
|
|
132
|
-
</View>
|
|
133
|
-
<View style={styles.percentLoad}>
|
|
134
|
-
<Progress.Bar
|
|
135
|
-
progress={percent}
|
|
136
|
-
animated={true}
|
|
137
|
-
color={Colors.Primary}
|
|
138
|
-
indeterminateAnimationDuration={1000}
|
|
139
|
-
height={7}
|
|
140
|
-
width={Constants.width - 80}
|
|
141
|
-
useNativeDriver={true}
|
|
142
|
-
/>
|
|
143
|
-
<Text style={styles.textPercentLoad}>{`${parseInt(
|
|
144
|
-
percent * 100,
|
|
145
|
-
10
|
|
146
|
-
)}%`}</Text>
|
|
147
|
-
</View>
|
|
148
|
-
</>
|
|
149
|
-
);
|
|
150
|
-
}, [percent, t]);
|
|
151
|
-
|
|
152
101
|
const ConnectingSuccess = useCallback(() => {
|
|
153
102
|
return (
|
|
154
103
|
<View style={styles.ConnectingSuccess}>
|
|
@@ -188,7 +137,7 @@ const ConnectingProcess = ({ route }) => {
|
|
|
188
137
|
<Text bold style={styles.connectingText}>
|
|
189
138
|
{t('connect_device')}
|
|
190
139
|
</Text>
|
|
191
|
-
{!!isLoading && <Connecting />}
|
|
140
|
+
{!!isLoading && <Connecting isLoading={isLoading} />}
|
|
192
141
|
{!isLoading && <ConnectingSuccess />}
|
|
193
142
|
</View>
|
|
194
143
|
{!isLoading && (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { memo, useCallback, useEffect } from 'react';
|
|
1
|
+
import React, { memo, useCallback, useEffect, useState } from 'react';
|
|
2
2
|
import { TouchableOpacity, ScrollView } from 'react-native';
|
|
3
3
|
import Popover from 'react-native-popover-view';
|
|
4
4
|
|
|
@@ -19,10 +19,16 @@ const MenuActionMore = memo(
|
|
|
19
19
|
wrapStyle,
|
|
20
20
|
isTextCenter = true,
|
|
21
21
|
}) => {
|
|
22
|
+
const [isDisable, setIsDisable] = useState(false);
|
|
22
23
|
const onPress = useCallback(
|
|
23
24
|
(item, index) => () => {
|
|
25
|
+
setIsDisable(true);
|
|
24
26
|
hideMore && hideMore();
|
|
25
27
|
onItemClick && onItemClick(item, index);
|
|
28
|
+
const timeout = setTimeout(() => {
|
|
29
|
+
setIsDisable(false);
|
|
30
|
+
clearTimeout(timeout);
|
|
31
|
+
}, 500);
|
|
26
32
|
},
|
|
27
33
|
[hideMore, onItemClick]
|
|
28
34
|
);
|
|
@@ -55,6 +61,7 @@ const MenuActionMore = memo(
|
|
|
55
61
|
onPress={onPress(item, index)}
|
|
56
62
|
key={index}
|
|
57
63
|
testID={TESTID.TOUCHABLE_ACTION_ADD_MORE}
|
|
64
|
+
disabled={isDisable}
|
|
58
65
|
>
|
|
59
66
|
<Text style={styles.modalHeaderText}>{item.text}</Text>
|
|
60
67
|
</TouchableOpacity>
|
|
@@ -1,29 +1,27 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
2
|
import { View, StyleSheet } from 'react-native';
|
|
3
3
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
4
4
|
import { Colors } from '../../configs';
|
|
5
5
|
import Text from '../../commons/Text';
|
|
6
|
-
|
|
7
6
|
import RowMember from './RowMember';
|
|
8
7
|
|
|
9
8
|
const MemberList = ({
|
|
10
9
|
dataMember,
|
|
11
10
|
ownerId,
|
|
11
|
+
unit,
|
|
12
12
|
currentUserId, //user is using app
|
|
13
|
-
onPressRemove,
|
|
14
13
|
}) => {
|
|
15
14
|
const t = useTranslations();
|
|
16
|
-
|
|
17
15
|
return (
|
|
18
16
|
<View style={styles.box}>
|
|
19
17
|
{!!dataMember.length &&
|
|
20
18
|
dataMember.map((item, index) => (
|
|
21
19
|
<RowMember
|
|
22
20
|
member={item}
|
|
21
|
+
unit={unit}
|
|
23
22
|
index={index}
|
|
24
23
|
ownerId={ownerId}
|
|
25
24
|
currentUserId={currentUserId}
|
|
26
|
-
onPressRemove={onPressRemove}
|
|
27
25
|
key={index.toString()}
|
|
28
26
|
/>
|
|
29
27
|
))}
|
|
@@ -33,13 +31,10 @@ const MemberList = ({
|
|
|
33
31
|
</View>
|
|
34
32
|
);
|
|
35
33
|
};
|
|
36
|
-
export default MemberList;
|
|
34
|
+
export default memo(MemberList);
|
|
35
|
+
|
|
37
36
|
const styles = StyleSheet.create({
|
|
38
37
|
box: {
|
|
39
|
-
paddingBottom: 16,
|
|
40
|
-
borderRadius: 20,
|
|
41
38
|
backgroundColor: Colors.White,
|
|
42
|
-
borderWidth: 1,
|
|
43
|
-
borderColor: Colors.Gray4,
|
|
44
39
|
},
|
|
45
40
|
});
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import React, { memo, useMemo } from 'react';
|
|
2
|
-
import { StyleSheet, View } from 'react-native';
|
|
3
|
-
import { IconOutline } from '@ant-design/icons-react-native';
|
|
2
|
+
import { StyleSheet, View, TouchableOpacity, Image } from 'react-native';
|
|
4
3
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
5
|
-
|
|
6
4
|
import { Colors } from '../../configs';
|
|
7
5
|
import Text from '../../commons/Text';
|
|
8
6
|
import { shortEmailName } from '../../utils/Utils';
|
|
9
|
-
|
|
10
|
-
import
|
|
7
|
+
import { CircleView } from '../CircleView';
|
|
8
|
+
import { useNavigation } from '@react-navigation/native';
|
|
9
|
+
import Routes from '../../utils/Route';
|
|
11
10
|
|
|
12
11
|
const arrColor = [
|
|
13
12
|
Colors.GeekBlue3,
|
|
@@ -19,46 +18,88 @@ const arrColor = [
|
|
|
19
18
|
Colors.Cyan2,
|
|
20
19
|
];
|
|
21
20
|
const RowMember = memo(
|
|
22
|
-
({
|
|
21
|
+
({
|
|
22
|
+
member,
|
|
23
|
+
index,
|
|
24
|
+
ownerId,
|
|
25
|
+
unit,
|
|
26
|
+
currentUserId,
|
|
27
|
+
type,
|
|
28
|
+
leftIcon = true,
|
|
29
|
+
rightComponent = true,
|
|
30
|
+
}) => {
|
|
23
31
|
const t = useTranslations();
|
|
24
|
-
const
|
|
25
|
-
() => ownerId === currentUserId && member.id !== ownerId,
|
|
26
|
-
[currentUserId, member.id, ownerId]
|
|
27
|
-
);
|
|
32
|
+
const { navigate } = useNavigation();
|
|
28
33
|
const [role, roleColor] = useMemo(
|
|
29
34
|
() =>
|
|
30
|
-
member
|
|
35
|
+
member?.id === ownerId
|
|
31
36
|
? [t('owner'), Colors.Primary]
|
|
32
|
-
: member
|
|
33
|
-
? [t('me'), Colors.
|
|
34
|
-
: ['',
|
|
35
|
-
[currentUserId, member
|
|
37
|
+
: member?.id === currentUserId
|
|
38
|
+
? [t('me'), Colors.Primary]
|
|
39
|
+
: [t('member'), Colors.Gray6],
|
|
40
|
+
[currentUserId, member?.id, ownerId, t]
|
|
36
41
|
);
|
|
37
|
-
const
|
|
42
|
+
const firstWordsInName = useMemo(() => {
|
|
43
|
+
const wordTemp = member?.name || shortEmailName(member?.email) || '';
|
|
44
|
+
return wordTemp?.charAt();
|
|
45
|
+
}, [member?.email, member?.name]);
|
|
46
|
+
|
|
38
47
|
if (member?.id === ownerId && member?.share_id) {
|
|
39
48
|
return null;
|
|
40
49
|
}
|
|
50
|
+
const circleColorTypes = {
|
|
51
|
+
primary: 'primary',
|
|
52
|
+
disable: 'disable',
|
|
53
|
+
noneBG: 'none',
|
|
54
|
+
};
|
|
55
|
+
const circleColor = type
|
|
56
|
+
? circleColorTypes[type]
|
|
57
|
+
: arrColor[index % arrColor.length];
|
|
58
|
+
|
|
59
|
+
const onPressInfo = () => {
|
|
60
|
+
navigate(Routes.UnitMemberInformation, {
|
|
61
|
+
member,
|
|
62
|
+
ownerId,
|
|
63
|
+
unit,
|
|
64
|
+
});
|
|
65
|
+
};
|
|
41
66
|
return (
|
|
42
67
|
<View style={styles.rowContainer}>
|
|
43
|
-
<
|
|
44
|
-
style={
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
<TouchableOpacity onPress={onPressInfo} disabled={type === 'disable'}>
|
|
69
|
+
<View style={styles.Border}>
|
|
70
|
+
{!!leftIcon && (
|
|
71
|
+
<View style={styles.paddingLeft16}>
|
|
72
|
+
{member?.avatar ? (
|
|
73
|
+
<Image
|
|
74
|
+
source={{ uri: member?.avatar }}
|
|
75
|
+
style={styles.avatar}
|
|
76
|
+
/>
|
|
77
|
+
) : (
|
|
78
|
+
<CircleView size={40} backgroundColor={circleColor} center>
|
|
79
|
+
<Text color={Colors.White}>{firstWordsInName}</Text>
|
|
80
|
+
</CircleView>
|
|
81
|
+
)}
|
|
82
|
+
</View>
|
|
83
|
+
)}
|
|
84
|
+
<View style={styles.columnFlex}>
|
|
85
|
+
<Text style={styles.titleName}>
|
|
86
|
+
{member?.name || shortEmailName(member?.email) || ''}
|
|
87
|
+
</Text>
|
|
88
|
+
{member?.phone_number && (
|
|
89
|
+
<Text style={styles.status}> {member?.phone_number}</Text>
|
|
90
|
+
)}
|
|
91
|
+
</View>
|
|
92
|
+
{!!rightComponent && (
|
|
93
|
+
<View style={styles.endFlex}>
|
|
94
|
+
{!!role && (
|
|
95
|
+
<Text style={[styles.textRole, { color: roleColor }]}>
|
|
96
|
+
{role}
|
|
97
|
+
</Text>
|
|
98
|
+
)}
|
|
99
|
+
</View>
|
|
100
|
+
)}
|
|
101
|
+
</View>
|
|
102
|
+
</TouchableOpacity>
|
|
62
103
|
</View>
|
|
63
104
|
);
|
|
64
105
|
}
|
|
@@ -68,9 +109,7 @@ export default RowMember;
|
|
|
68
109
|
|
|
69
110
|
const styles = StyleSheet.create({
|
|
70
111
|
rowContainer: {
|
|
71
|
-
|
|
72
|
-
alignItems: 'center',
|
|
73
|
-
paddingHorizontal: 16,
|
|
112
|
+
flex: 1,
|
|
74
113
|
},
|
|
75
114
|
iconContainer: {
|
|
76
115
|
alignItems: 'center',
|
|
@@ -107,4 +146,55 @@ const styles = StyleSheet.create({
|
|
|
107
146
|
textCenter: {
|
|
108
147
|
alignSelf: 'center',
|
|
109
148
|
},
|
|
149
|
+
columnFlex: {
|
|
150
|
+
paddingLeft: 16,
|
|
151
|
+
justifyContent: 'center',
|
|
152
|
+
flexDirection: 'column',
|
|
153
|
+
},
|
|
154
|
+
rowFlex: {
|
|
155
|
+
flexDirection: 'row',
|
|
156
|
+
},
|
|
157
|
+
endFlex: {
|
|
158
|
+
flex: 1,
|
|
159
|
+
justifyContent: 'flex-start',
|
|
160
|
+
alignItems: 'flex-end',
|
|
161
|
+
paddingRight: 16,
|
|
162
|
+
},
|
|
163
|
+
Border: {
|
|
164
|
+
display: 'flex',
|
|
165
|
+
width: '100%',
|
|
166
|
+
borderWidth: 0,
|
|
167
|
+
borderColor: Colors.Gray4,
|
|
168
|
+
borderBottomWidth: 1,
|
|
169
|
+
borderStyle: 'solid',
|
|
170
|
+
flexDirection: 'row',
|
|
171
|
+
paddingTop: 16,
|
|
172
|
+
paddingBottom: 16,
|
|
173
|
+
},
|
|
174
|
+
titleName: {
|
|
175
|
+
fontSize: 16,
|
|
176
|
+
lineHeight: 24,
|
|
177
|
+
fontStyle: 'normal',
|
|
178
|
+
fontWeight: '400',
|
|
179
|
+
color: Colors.Gray9,
|
|
180
|
+
},
|
|
181
|
+
status: {
|
|
182
|
+
fontSize: 12,
|
|
183
|
+
lineHeight: 20,
|
|
184
|
+
fontStyle: 'normal',
|
|
185
|
+
fontWeight: '400',
|
|
186
|
+
color: Colors.Gray7,
|
|
187
|
+
},
|
|
188
|
+
container: {
|
|
189
|
+
paddingTop: 0,
|
|
190
|
+
},
|
|
191
|
+
paddingLeft16: {
|
|
192
|
+
paddingLeft: 16,
|
|
193
|
+
},
|
|
194
|
+
avatar: {
|
|
195
|
+
height: 40,
|
|
196
|
+
width: 40,
|
|
197
|
+
borderRadius: 40,
|
|
198
|
+
backgroundColor: Colors.Primary,
|
|
199
|
+
},
|
|
110
200
|
});
|
|
@@ -27,7 +27,7 @@ describe('MemberList', () => {
|
|
|
27
27
|
});
|
|
28
28
|
const instance = tree.root;
|
|
29
29
|
const textInputs = instance.findAllByType(Text);
|
|
30
|
-
expect(textInputs.length).toBe(
|
|
30
|
+
expect(textInputs.length).toBe(5);
|
|
31
31
|
});
|
|
32
32
|
test('MemberList snapshot id dataMember !== ownerId', () => {
|
|
33
33
|
const dataMember = [{ id: 1, name: 'CEO' }];
|
|
@@ -36,7 +36,7 @@ describe('MemberList', () => {
|
|
|
36
36
|
});
|
|
37
37
|
const instance = tree.root;
|
|
38
38
|
const textInputs = instance.findAllByType(Text);
|
|
39
|
-
expect(textInputs.length).toBe(
|
|
39
|
+
expect(textInputs.length).toBe(5);
|
|
40
40
|
});
|
|
41
41
|
test('MemberList snapshot id dataMember === currentUserId', () => {
|
|
42
42
|
const dataMember = [{ id: 1, name: 'CEO' }];
|
|
@@ -45,7 +45,7 @@ describe('MemberList', () => {
|
|
|
45
45
|
});
|
|
46
46
|
const instance = tree.root;
|
|
47
47
|
const textInputs = instance.findAllByType(Text);
|
|
48
|
-
expect(textInputs.length).toBe(
|
|
48
|
+
expect(textInputs.length).toBe(5);
|
|
49
49
|
});
|
|
50
50
|
test('MemberList dataMember null', () => {
|
|
51
51
|
const dataMember = [];
|
|
@@ -26,8 +26,8 @@ describe('RowMember', () => {
|
|
|
26
26
|
});
|
|
27
27
|
const instance = tree.root;
|
|
28
28
|
const textInputs = instance.findAllByType(Text);
|
|
29
|
-
expect(textInputs.length).toBe(
|
|
30
|
-
expect(textInputs[
|
|
29
|
+
expect(textInputs.length).toBe(5);
|
|
30
|
+
expect(textInputs[1].props.children).toEqual('CEO');
|
|
31
31
|
});
|
|
32
32
|
test('RowMember owner dont have name show start of email ', () => {
|
|
33
33
|
const dataMember = { id: 1, name: '', email: 'abc@gmail.com' };
|
|
@@ -36,7 +36,7 @@ describe('RowMember', () => {
|
|
|
36
36
|
});
|
|
37
37
|
const instance = tree.root;
|
|
38
38
|
const textInputs = instance.findAllByType(Text);
|
|
39
|
-
expect(textInputs.length).toBe(
|
|
40
|
-
expect(textInputs[
|
|
39
|
+
expect(textInputs.length).toBe(5);
|
|
40
|
+
expect(textInputs[1].props.children).toEqual('abc');
|
|
41
41
|
});
|
|
42
42
|
});
|
package/src/configs/API.js
CHANGED
|
@@ -39,6 +39,8 @@ const API = {
|
|
|
39
39
|
SCConfig.apiRoot + `/property_manager/units/${id}/add_gateway/`,
|
|
40
40
|
SENSORS_STATUS: (id) =>
|
|
41
41
|
SCConfig.apiRoot + `/property_manager/units/${id}/sensors_status/`,
|
|
42
|
+
CHANGE_OWNER: (id) =>
|
|
43
|
+
SCConfig.apiRoot + `/property_manager/units/${id}/change_owner/`,
|
|
42
44
|
FAVOURITE_DEVICES: (id) =>
|
|
43
45
|
SCConfig.apiRoot + `/property_manager/units/${id}/favourite_devices/`,
|
|
44
46
|
},
|
|
@@ -150,6 +152,12 @@ const API = {
|
|
|
150
152
|
UNITS_MEMBER_DETAIL: (id, shareId) =>
|
|
151
153
|
SCConfig.apiRoot +
|
|
152
154
|
`/property_manager/sharing/units/${id}/members/${shareId}/`,
|
|
155
|
+
UNIT_MEMBER_INFO: (unit_id, member_id) =>
|
|
156
|
+
SCConfig.apiRoot +
|
|
157
|
+
`/property_manager/sharing/units/${unit_id}/member/${member_id}/information/`,
|
|
158
|
+
UNIT_MEMBER_SHARE_DEVICE: (unit_id, member_id) =>
|
|
159
|
+
SCConfig.apiRoot +
|
|
160
|
+
`/property_manager/sharing/units/${unit_id}/member/${member_id}/shared_devices/`,
|
|
153
161
|
SHARE: () => SCConfig.apiRoot + '/property_manager/sharing/share/',
|
|
154
162
|
},
|
|
155
163
|
EMERGENCY: {
|
|
@@ -157,6 +165,8 @@ const API = {
|
|
|
157
165
|
},
|
|
158
166
|
EMERGENCY_BUTTON: {
|
|
159
167
|
CREATE_CONTACT: () => SCConfig.apiRoot + '/emergency_button/contacts/',
|
|
168
|
+
CREATE_BATCH: () =>
|
|
169
|
+
SCConfig.apiRoot + '/emergency_button/contacts/create_batch/',
|
|
160
170
|
CONTACTS: () => SCConfig.apiRoot + '/emergency_button/contacts/',
|
|
161
171
|
REMOVE_CONTACTS: (id) =>
|
|
162
172
|
SCConfig.apiRoot + `/emergency_button/contacts/${id}/`,
|