@eohjsc/react-native-smart-city 0.3.24 → 0.3.27
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/ActionGroup/SliderRangeTemplate.js +7 -7
- package/src/commons/Dashboard/MyUnit/__test__/MyUnit.test.js +55 -5
- package/src/commons/Dashboard/MyUnit/index.js +58 -11
- package/src/commons/MenuActionMore/index.js +1 -3
- package/src/commons/Popover/index.js +26 -0
- package/src/configs/Constants.js +4 -0
- package/src/context/actionType.ts +2 -0
- package/src/context/reducer.ts +10 -0
- package/src/hooks/Common/useGGHomeDeviceConnected.js +9 -2
- package/src/hooks/Common/usePopover.js +0 -8
- package/src/hooks/IoT/useGGHomeConnection.js +0 -1
- package/src/navigations/UnitStack.js +10 -2
- package/src/screens/GuestInfo/index.js +11 -4
- package/src/screens/GuestInfo/styles/indexStyles.js +7 -0
- package/src/screens/ManageAccess/index.js +14 -5
- package/src/screens/ManageAccess/styles/ManageAccessStyles.js +9 -0
- package/src/screens/PlayBackCamera/Timer.js +3 -0
- package/src/screens/PlayBackCamera/__test__/index.test.js +8 -1
- package/src/screens/PlayBackCamera/index.js +67 -46
- package/src/screens/ScriptDetail/__test__/index.test.js +0 -3
- package/src/screens/ScriptDetail/index.js +7 -10
- package/src/screens/Unit/Detail.js +16 -10
- package/src/screens/Unit/SmartAccount.js +7 -6
- package/src/screens/Unit/Summaries.js +8 -1
- package/src/screens/Unit/components/Header/index.js +1 -1
- package/src/screens/Unit/components/MyUnitDevice/index.js +29 -12
- package/src/screens/Unit/components/__test__/AutomateScript.test.js +116 -0
- package/src/screens/Unit/components/__test__/Header.test.js +1 -1
- package/src/screens/Unit/components/__test__/MyUnitDevice.test.js +2 -2
- package/src/screens/Unit/hook/useUnitConnectRemoteDevices.js +6 -5
- package/src/screens/Unit/components/MyUnit/index.js +0 -136
- package/src/screens/Unit/components/__test__/MyUnit.test.js +0 -35
|
@@ -13,13 +13,13 @@ import Text from '../../commons/Text';
|
|
|
13
13
|
import Timer from './Timer';
|
|
14
14
|
import { useStatusBarPreview } from '../../hooks/Common/useStatusBar';
|
|
15
15
|
import MediaPlayerFull from '../../commons/MediaPlayerDetail/MediaPlayerFull';
|
|
16
|
-
import { TESTID } from '../../configs/Constants';
|
|
16
|
+
import { DATE_TIME_FORMAT, TESTID } from '../../configs/Constants';
|
|
17
17
|
|
|
18
|
-
let dateTemp = moment().format(
|
|
18
|
+
let dateTemp = moment().format(DATE_TIME_FORMAT.YMD);
|
|
19
19
|
let isFirstTime = true;
|
|
20
20
|
|
|
21
21
|
const PlayBackCamera = () => {
|
|
22
|
-
const now = useMemo(() => moment().format(
|
|
22
|
+
const now = useMemo(() => moment().format(DATE_TIME_FORMAT.YMD), []);
|
|
23
23
|
const hourTemp = useMemo(() => moment().format('HH:mm:ss'), []);
|
|
24
24
|
const arrHourTemp = useMemo(() => hourTemp.split(':'), [hourTemp]);
|
|
25
25
|
const t = useTranslations();
|
|
@@ -33,7 +33,8 @@ const PlayBackCamera = () => {
|
|
|
33
33
|
s: arrHourTemp[2],
|
|
34
34
|
});
|
|
35
35
|
const [uri, setUri] = useState(item?.configuration?.uri);
|
|
36
|
-
const [
|
|
36
|
+
const [mainURI, setMainURI] = useState(uri);
|
|
37
|
+
const [paused, setPaused] = useState(false);
|
|
37
38
|
const onOpenDateModal = useCallback(() => {
|
|
38
39
|
setIsShowDate(true);
|
|
39
40
|
}, []);
|
|
@@ -49,15 +50,19 @@ const PlayBackCamera = () => {
|
|
|
49
50
|
}, [selected]);
|
|
50
51
|
|
|
51
52
|
const onAddDate = useCallback(() => {
|
|
53
|
+
setPaused(true);
|
|
52
54
|
if (selected !== now) {
|
|
53
|
-
const date = moment(selected).add(1, 'days').format(
|
|
55
|
+
const date = moment(selected).add(1, 'days').format(DATE_TIME_FORMAT.YMD);
|
|
54
56
|
setSelected(date);
|
|
55
57
|
dateTemp = date;
|
|
56
58
|
}
|
|
57
59
|
}, [selected, now]);
|
|
58
60
|
|
|
59
61
|
const onSubtractDate = useCallback(() => {
|
|
60
|
-
|
|
62
|
+
setPaused(true);
|
|
63
|
+
const date = moment(selected)
|
|
64
|
+
.subtract(1, 'days')
|
|
65
|
+
.format(DATE_TIME_FORMAT.YMD);
|
|
61
66
|
setSelected(date);
|
|
62
67
|
dateTemp = date;
|
|
63
68
|
}, [selected]);
|
|
@@ -72,6 +77,25 @@ const PlayBackCamera = () => {
|
|
|
72
77
|
[item?.configuration?.time_zone]
|
|
73
78
|
);
|
|
74
79
|
|
|
80
|
+
const onScrollEndDrag = useCallback(() => {
|
|
81
|
+
setMainURI(uri);
|
|
82
|
+
setPaused(false);
|
|
83
|
+
}, [uri]);
|
|
84
|
+
|
|
85
|
+
const onDayPress = useCallback((day) => {
|
|
86
|
+
setPaused(true);
|
|
87
|
+
setSelected(day.dateString);
|
|
88
|
+
}, []);
|
|
89
|
+
|
|
90
|
+
const onPressArrowLeft = useCallback((subtractMonth) => {
|
|
91
|
+
setPaused(true);
|
|
92
|
+
subtractMonth && subtractMonth();
|
|
93
|
+
}, []);
|
|
94
|
+
|
|
95
|
+
const onPressArrowRight = useCallback((addMonth) => {
|
|
96
|
+
addMonth && addMonth();
|
|
97
|
+
}, []);
|
|
98
|
+
|
|
75
99
|
const onChangeValue = useCallback(
|
|
76
100
|
(value, selectedTime) => {
|
|
77
101
|
if (!isFirstTime) {
|
|
@@ -100,10 +124,6 @@ const PlayBackCamera = () => {
|
|
|
100
124
|
}T${getHourWithTimeZone(h)}${m}${s}Z`
|
|
101
125
|
);
|
|
102
126
|
}
|
|
103
|
-
const to = setTimeout(() => {
|
|
104
|
-
setPaused(false);
|
|
105
|
-
clearTimeout(to);
|
|
106
|
-
}, 100);
|
|
107
127
|
}
|
|
108
128
|
},
|
|
109
129
|
[
|
|
@@ -115,27 +135,37 @@ const PlayBackCamera = () => {
|
|
|
115
135
|
]
|
|
116
136
|
);
|
|
117
137
|
|
|
138
|
+
const renderArrow = useCallback((direction) => {
|
|
139
|
+
return (
|
|
140
|
+
<Image
|
|
141
|
+
source={Images.arrowLeft}
|
|
142
|
+
style={[direction !== 'left' && styles.arrowRight]}
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
}, []);
|
|
146
|
+
|
|
118
147
|
useEffect(() => {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
148
|
+
if (!isFirstTime) {
|
|
149
|
+
const date = selected.split('-');
|
|
150
|
+
const playback = item?.configuration?.playback || '';
|
|
151
|
+
if (
|
|
152
|
+
selected === now &&
|
|
153
|
+
parseInt(moment().format('HH:mm:ss'), 10) <=
|
|
154
|
+
parseInt(`${hour.h}:${hour.m}:${hour.s}`, 10)
|
|
155
|
+
) {
|
|
156
|
+
setMainURI(item?.configuration?.uri);
|
|
157
|
+
} else {
|
|
158
|
+
setMainURI(
|
|
159
|
+
`${playback.split('=')[0]}=${date[0]}${date[1]}${
|
|
160
|
+
date[2]
|
|
161
|
+
}T${getHourWithTimeZone(hour.h)}${hour.m}${hour.s}Z`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
const to = setTimeout(() => {
|
|
165
|
+
setPaused(false);
|
|
166
|
+
clearTimeout(to);
|
|
167
|
+
}, 100);
|
|
134
168
|
}
|
|
135
|
-
const to = setTimeout(() => {
|
|
136
|
-
setPaused(false);
|
|
137
|
-
clearTimeout(to);
|
|
138
|
-
}, 100);
|
|
139
169
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
140
170
|
}, [selected, getHourWithTimeZone]);
|
|
141
171
|
|
|
@@ -152,7 +182,7 @@ const PlayBackCamera = () => {
|
|
|
152
182
|
clearTimeout(to);
|
|
153
183
|
}, 2000);
|
|
154
184
|
return () => {
|
|
155
|
-
dateTemp = moment().format(
|
|
185
|
+
dateTemp = moment().format(DATE_TIME_FORMAT.YMD);
|
|
156
186
|
isFirstTime = true;
|
|
157
187
|
};
|
|
158
188
|
}, []);
|
|
@@ -161,7 +191,7 @@ const PlayBackCamera = () => {
|
|
|
161
191
|
<View style={styles.wrap}>
|
|
162
192
|
<HeaderCustom title={t('video_detail')} />
|
|
163
193
|
<MediaPlayerFull
|
|
164
|
-
uri={
|
|
194
|
+
uri={mainURI}
|
|
165
195
|
isPaused={paused}
|
|
166
196
|
thumbnail={thumbnail}
|
|
167
197
|
isShowFullScreenIcon
|
|
@@ -223,6 +253,7 @@ const PlayBackCamera = () => {
|
|
|
223
253
|
indicatorHeight={40}
|
|
224
254
|
onChangeValue={onChangeValue}
|
|
225
255
|
selected={selected}
|
|
256
|
+
onScrollEndDrag={onScrollEndDrag}
|
|
226
257
|
/>
|
|
227
258
|
</View>
|
|
228
259
|
</View>
|
|
@@ -230,13 +261,10 @@ const PlayBackCamera = () => {
|
|
|
230
261
|
<ModalCustom isVisible={isShowDate} style={styles.modal}>
|
|
231
262
|
<View style={styles.wrapDate}>
|
|
232
263
|
<Calendar
|
|
233
|
-
onDayPress={
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
onMonthChange={(month) => {}}
|
|
238
|
-
onPressArrowLeft={(subtractMonth) => subtractMonth()}
|
|
239
|
-
onPressArrowRight={(addMonth) => addMonth()}
|
|
264
|
+
onDayPress={onDayPress}
|
|
265
|
+
maxDate={moment().format(DATE_TIME_FORMAT.YMD)}
|
|
266
|
+
onPressArrowLeft={onPressArrowLeft}
|
|
267
|
+
onPressArrowRight={onPressArrowRight}
|
|
240
268
|
markedDates={{
|
|
241
269
|
[selected]: {
|
|
242
270
|
selected: true,
|
|
@@ -246,14 +274,7 @@ const PlayBackCamera = () => {
|
|
|
246
274
|
},
|
|
247
275
|
}}
|
|
248
276
|
enableSwipeMonths={true}
|
|
249
|
-
renderArrow={
|
|
250
|
-
if (direction === 'left') {
|
|
251
|
-
return <Image source={Images.arrowLeft} />;
|
|
252
|
-
}
|
|
253
|
-
return (
|
|
254
|
-
<Image source={Images.arrowLeft} style={styles.arrowRight} />
|
|
255
|
-
);
|
|
256
|
-
}}
|
|
277
|
+
renderArrow={renderArrow}
|
|
257
278
|
headerStyle={styles.headerStyle}
|
|
258
279
|
/>
|
|
259
280
|
<View style={styles.viewSeparated} />
|
|
@@ -103,7 +103,6 @@ describe('Test ScriptDetail', () => {
|
|
|
103
103
|
|
|
104
104
|
await act(async () => {
|
|
105
105
|
await menu.props.onItemClick(rename);
|
|
106
|
-
await menu.props.hideComplete();
|
|
107
106
|
});
|
|
108
107
|
expect(menu.props.isVisible).toBeFalsy();
|
|
109
108
|
expect(alertAction.props.visible).toBeTruthy();
|
|
@@ -130,7 +129,6 @@ describe('Test ScriptDetail', () => {
|
|
|
130
129
|
|
|
131
130
|
await act(async () => {
|
|
132
131
|
await menu.props.onItemClick(rename);
|
|
133
|
-
await menu.props.hideComplete();
|
|
134
132
|
});
|
|
135
133
|
expect(menu.props.isVisible).toBeFalsy();
|
|
136
134
|
expect(alertAction.props.visible).toBeTruthy();
|
|
@@ -154,7 +152,6 @@ describe('Test ScriptDetail', () => {
|
|
|
154
152
|
|
|
155
153
|
await act(async () => {
|
|
156
154
|
await menu.props.onItemClick(deleteItem);
|
|
157
|
-
await menu.props.hideComplete();
|
|
158
155
|
});
|
|
159
156
|
expect(alertAction.props.visible).toBeTruthy();
|
|
160
157
|
mock.onDelete(API.AUTOMATE.SCRIPT(1)).reply(204);
|
|
@@ -43,6 +43,7 @@ import { popAction } from '../../navigations/utils';
|
|
|
43
43
|
import { TESTID } from '../../configs/Constants';
|
|
44
44
|
import useKeyboardAnimated from '../../hooks/Explore/useKeyboardAnimated';
|
|
45
45
|
import { REPEAT_OPTIONS } from '../SetSchedule/components/RepeatOptionsPopup';
|
|
46
|
+
import { useSCContextSelector } from '../../context';
|
|
46
47
|
|
|
47
48
|
const PreventDoubleTouch = withPreventDoubleClick(TouchableOpacity);
|
|
48
49
|
|
|
@@ -50,14 +51,8 @@ const ScriptDetail = ({ route }) => {
|
|
|
50
51
|
const { navigate, goBack, dispatch } = useNavigation();
|
|
51
52
|
const { params = {} } = route;
|
|
52
53
|
const refMenuAction = useRef();
|
|
53
|
-
const {
|
|
54
|
-
|
|
55
|
-
showingPopover,
|
|
56
|
-
showPopoverWithRef,
|
|
57
|
-
hidePopover,
|
|
58
|
-
hidingPopoverComplete,
|
|
59
|
-
hidePopoverComplete,
|
|
60
|
-
} = usePopover();
|
|
54
|
+
const { childRef, showingPopover, showPopoverWithRef, hidePopover } =
|
|
55
|
+
usePopover();
|
|
61
56
|
const t = useTranslations();
|
|
62
57
|
const {
|
|
63
58
|
id,
|
|
@@ -84,6 +79,9 @@ const ScriptDetail = ({ route }) => {
|
|
|
84
79
|
const [data, setData] = useState([]);
|
|
85
80
|
|
|
86
81
|
const { isStarred, starScript, unstarScript } = useStarredScript(automate);
|
|
82
|
+
const popoverAnimating = useSCContextSelector(
|
|
83
|
+
(state) => state.app.popoverAnimating
|
|
84
|
+
);
|
|
87
85
|
|
|
88
86
|
const [transY] = useKeyboardAnimated(-16);
|
|
89
87
|
const animatedStyle = Platform.select({
|
|
@@ -483,7 +481,6 @@ const ScriptDetail = ({ route }) => {
|
|
|
483
481
|
<MenuActionMore
|
|
484
482
|
isVisible={showingPopover}
|
|
485
483
|
hideMore={hidePopover}
|
|
486
|
-
hideComplete={hidePopoverComplete}
|
|
487
484
|
listMenuItem={listMenuItem}
|
|
488
485
|
childRef={childRef}
|
|
489
486
|
onItemClick={onItemClick}
|
|
@@ -491,7 +488,7 @@ const ScriptDetail = ({ route }) => {
|
|
|
491
488
|
wrapStyle={styles.wrapStyle}
|
|
492
489
|
/>
|
|
493
490
|
<AlertAction
|
|
494
|
-
visible={stateAlertAction.visible &&
|
|
491
|
+
visible={stateAlertAction.visible && !popoverAnimating}
|
|
495
492
|
hideModal={hideAlertAction}
|
|
496
493
|
title={stateAlertAction.title}
|
|
497
494
|
message={stateAlertAction.message}
|
|
@@ -291,6 +291,21 @@ const UnitDetail = ({ route }) => {
|
|
|
291
291
|
navigate(isLavidaSource ? Routes.SmartHomeDashboard : Routes.Dashboard);
|
|
292
292
|
}, [isLavidaSource, navigate]);
|
|
293
293
|
|
|
294
|
+
const renderCamera = useMemo(() => {
|
|
295
|
+
return (
|
|
296
|
+
isFirstOpenCamera &&
|
|
297
|
+
isIOS && (
|
|
298
|
+
<MediaPlayerDetail
|
|
299
|
+
uri={Constants.URL_STREAM_CAMERA_DEMO}
|
|
300
|
+
isPaused={false}
|
|
301
|
+
width={1}
|
|
302
|
+
height={1}
|
|
303
|
+
style={styles.fakeCamera}
|
|
304
|
+
/>
|
|
305
|
+
)
|
|
306
|
+
);
|
|
307
|
+
}, [isFirstOpenCamera, isIOS]);
|
|
308
|
+
|
|
294
309
|
useEffect(() => {
|
|
295
310
|
watchNotificationData(user, onRefresh);
|
|
296
311
|
return () => unwatchNotificationData(user);
|
|
@@ -319,16 +334,7 @@ const UnitDetail = ({ route }) => {
|
|
|
319
334
|
hideRightPlus={!isOwner}
|
|
320
335
|
onBack={(isSuccessfullyConnected && Dashboard) || (routeName && onBack)}
|
|
321
336
|
>
|
|
322
|
-
{
|
|
323
|
-
{isFirstOpenCamera && isIOS && (
|
|
324
|
-
<MediaPlayerDetail
|
|
325
|
-
uri={Constants.URL_STREAM_CAMERA_DEMO}
|
|
326
|
-
isPaused={false}
|
|
327
|
-
width={1}
|
|
328
|
-
height={1}
|
|
329
|
-
style={styles.fakeCamera}
|
|
330
|
-
/>
|
|
331
|
-
)}
|
|
337
|
+
{renderCamera}
|
|
332
338
|
|
|
333
339
|
<View style={styles.container}>
|
|
334
340
|
<Summaries unit={unit} />
|
|
@@ -14,11 +14,12 @@ import Routes from '../../utils/Route';
|
|
|
14
14
|
import { useNavigation } from '@react-navigation/native';
|
|
15
15
|
import { axiosDelete, axiosGet } from '../../utils/Apis/axios';
|
|
16
16
|
import { SmartAccountItem } from './SmartAccountItem';
|
|
17
|
-
import { usePopover
|
|
17
|
+
import { usePopover } from '../../hooks/Common';
|
|
18
18
|
import { MenuActionMore, AlertAction, FullLoading } from '../../commons';
|
|
19
19
|
import { useTranslations } from '../../hooks/Common/useTranslations';
|
|
20
20
|
import { useStateAlertRemove } from '../Unit/hook/useStateAlertRemove';
|
|
21
21
|
import { ToastBottomHelper } from '../../utils/Utils';
|
|
22
|
+
import { useSCContextSelector } from '../../context';
|
|
22
23
|
|
|
23
24
|
const ListSmartAccount = ({ route }) => {
|
|
24
25
|
const { unitId } = route?.params || {};
|
|
@@ -27,6 +28,9 @@ const ListSmartAccount = ({ route }) => {
|
|
|
27
28
|
const smartAccountRef = useRef(null);
|
|
28
29
|
const { navigate } = useNavigation();
|
|
29
30
|
const [loadingRemoveItem, setLoadingRemoveItem] = useState(false);
|
|
31
|
+
const popoverAnimating = useSCContextSelector(
|
|
32
|
+
(state) => state.app.popoverAnimating
|
|
33
|
+
);
|
|
30
34
|
|
|
31
35
|
const getAllSmartAccounts = useCallback(async () => {
|
|
32
36
|
const { success, data: accountData } = await axiosGet(
|
|
@@ -39,7 +43,6 @@ const ListSmartAccount = ({ route }) => {
|
|
|
39
43
|
|
|
40
44
|
const { childRef, showingPopover, showPopoverWithRef, hidePopover } =
|
|
41
45
|
usePopover();
|
|
42
|
-
const [lockShowing, acquireLockShowing, releaseLockShowing] = useBoolean();
|
|
43
46
|
const { stateAlertRemove, onShowRemoveAlert, hideAlertAction } =
|
|
44
47
|
useStateAlertRemove();
|
|
45
48
|
|
|
@@ -62,11 +65,10 @@ const ListSmartAccount = ({ route }) => {
|
|
|
62
65
|
return;
|
|
63
66
|
}
|
|
64
67
|
if (item.action === 'remove') {
|
|
65
|
-
acquireLockShowing();
|
|
66
68
|
onShowRemoveAlert(smartAccountRef.current.brand)();
|
|
67
69
|
}
|
|
68
70
|
},
|
|
69
|
-
[
|
|
71
|
+
[onShowRemoveAlert]
|
|
70
72
|
);
|
|
71
73
|
|
|
72
74
|
const deleteSmartAccount = useCallback(async () => {
|
|
@@ -124,7 +126,7 @@ const ListSmartAccount = ({ route }) => {
|
|
|
124
126
|
})}
|
|
125
127
|
</View>
|
|
126
128
|
<AlertAction
|
|
127
|
-
visible={stateAlertRemove.visible && !
|
|
129
|
+
visible={stateAlertRemove.visible && !popoverAnimating}
|
|
128
130
|
hideModal={hideAlertAction}
|
|
129
131
|
title={stateAlertRemove.title}
|
|
130
132
|
message={stateAlertRemove.message}
|
|
@@ -142,7 +144,6 @@ const ListSmartAccount = ({ route }) => {
|
|
|
142
144
|
listMenuItem={listMenuItem}
|
|
143
145
|
childRef={childRef}
|
|
144
146
|
onItemClick={onItemClick}
|
|
145
|
-
hideComplete={releaseLockShowing}
|
|
146
147
|
/>
|
|
147
148
|
</WrapHeaderScrollable>
|
|
148
149
|
{loadingRemoveItem && <FullLoading />}
|
|
@@ -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 { useSCContextSelector } from '../../context';
|
|
9
10
|
|
|
10
11
|
const Summaries = memo(({ unit }) => {
|
|
11
12
|
const [unitSummaries, setUnitSummaries] = useState([]);
|
|
@@ -14,6 +15,9 @@ const Summaries = memo(({ unit }) => {
|
|
|
14
15
|
const isFocused = useIsFocused();
|
|
15
16
|
const navigation = useNavigation();
|
|
16
17
|
const appState = useRef(AppState.currentState);
|
|
18
|
+
const popoverAnimating = useSCContextSelector(
|
|
19
|
+
(state) => state.app.popoverAnimating
|
|
20
|
+
);
|
|
17
21
|
|
|
18
22
|
const fetchUnitSummary = useCallback(async () => {
|
|
19
23
|
if (!unit.id) {
|
|
@@ -33,6 +37,9 @@ const Summaries = memo(({ unit }) => {
|
|
|
33
37
|
|
|
34
38
|
const goToSummary = useCallback(
|
|
35
39
|
(summary) => {
|
|
40
|
+
if (popoverAnimating) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
36
43
|
navigation.navigate(Routes.UnitSummary, {
|
|
37
44
|
summaryId: summary.id,
|
|
38
45
|
unitId: unit.id,
|
|
@@ -40,7 +47,7 @@ const Summaries = memo(({ unit }) => {
|
|
|
40
47
|
unitData: unit,
|
|
41
48
|
});
|
|
42
49
|
},
|
|
43
|
-
[navigation, unit]
|
|
50
|
+
[navigation, popoverAnimating, unit]
|
|
44
51
|
);
|
|
45
52
|
|
|
46
53
|
const continuousFetchSummary = useCallback(async () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useRef, useState } from 'react';
|
|
2
2
|
import { Dimensions, View, TouchableOpacity, StyleSheet } from 'react-native';
|
|
3
|
-
import Popover from '
|
|
3
|
+
import Popover from '../../../../commons/Popover';
|
|
4
4
|
import { IconOutline } from '@ant-design/icons-react-native';
|
|
5
5
|
import { useTranslations } from '../../../../hooks/Common/useTranslations';
|
|
6
6
|
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import React, { useState, useCallback } from 'react';
|
|
2
2
|
import { StyleSheet, View, TouchableOpacity } from 'react-native';
|
|
3
3
|
import { useNavigation } from '@react-navigation/native';
|
|
4
|
+
import { useGGHomeDeviceConnected } from '../../../../hooks/Common';
|
|
4
5
|
|
|
5
6
|
import ItemQuickAction from '../../../../commons/Action/ItemQuickAction';
|
|
6
7
|
import Text from '../../../../commons/Text';
|
|
7
8
|
import Routes from '../../../../utils/Route';
|
|
8
9
|
import { Colors } from '../../../../configs';
|
|
9
10
|
import FImage from '../../../../commons/FImage';
|
|
11
|
+
import { DEVICE_TYPE } from '../../../../configs/Constants';
|
|
10
12
|
|
|
11
|
-
const MyUnitDevice = ({
|
|
12
|
-
const [status, setStatus] = useState(
|
|
13
|
+
const MyUnitDevice = ({ device, unit }) => {
|
|
14
|
+
const [status, setStatus] = useState(device.status);
|
|
13
15
|
const { navigate } = useNavigation();
|
|
14
16
|
|
|
15
17
|
const goToSensorDisplay = useCallback(() => {
|
|
@@ -17,34 +19,49 @@ const MyUnitDevice = ({ sensor, unit }) => {
|
|
|
17
19
|
screen: Routes.DeviceDetail,
|
|
18
20
|
params: {
|
|
19
21
|
unitData: unit,
|
|
20
|
-
sensorData:
|
|
22
|
+
sensorData: device,
|
|
21
23
|
},
|
|
22
24
|
});
|
|
23
|
-
}, [navigate,
|
|
25
|
+
}, [navigate, device, unit]);
|
|
26
|
+
|
|
27
|
+
const { isConnecting: isGGHomeConnecting } = useGGHomeDeviceConnected(device);
|
|
28
|
+
|
|
29
|
+
const canRenderQuickAction = (() => {
|
|
30
|
+
if (
|
|
31
|
+
!!device &&
|
|
32
|
+
!device?.is_managed_by_backend &&
|
|
33
|
+
device?.device_type === DEVICE_TYPE.GOOGLE_HOME
|
|
34
|
+
) {
|
|
35
|
+
return !isGGHomeConnecting;
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
})();
|
|
24
39
|
|
|
25
40
|
return (
|
|
26
41
|
<View style={styles.item}>
|
|
27
42
|
<TouchableOpacity style={styles.flex1} onPress={goToSensorDisplay}>
|
|
28
43
|
<View style={styles.rowCenter}>
|
|
29
|
-
<FImage style={styles.image} source={{ uri:
|
|
44
|
+
<FImage style={styles.image} source={{ uri: device?.icon_kit }} />
|
|
30
45
|
<View style={styles.marginTop3}>
|
|
31
46
|
<Text numberOfLines={1} semibold style={styles.nameDevice}>
|
|
32
|
-
{
|
|
47
|
+
{device.name}
|
|
33
48
|
</Text>
|
|
34
49
|
<View style={styles.roomDevice}>
|
|
35
50
|
<Text numberOfLines={1} style={styles.roomDevicePart}>
|
|
36
|
-
{
|
|
51
|
+
{device.station_name}
|
|
37
52
|
{status ? ` - ${status}` : ''}
|
|
38
53
|
</Text>
|
|
39
54
|
</View>
|
|
40
55
|
</View>
|
|
41
56
|
</View>
|
|
42
57
|
</TouchableOpacity>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
{canRenderQuickAction && (
|
|
59
|
+
<ItemQuickAction
|
|
60
|
+
sensor={device}
|
|
61
|
+
wrapperStyle={styles.iconCircle}
|
|
62
|
+
setStatus={setStatus}
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
48
65
|
</View>
|
|
49
66
|
);
|
|
50
67
|
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { TouchableWithoutFeedback, View } from 'react-native';
|
|
3
|
+
import { act, create } from 'react-test-renderer';
|
|
4
|
+
import { AUTOMATE_TYPE } from '../../../../configs/Constants';
|
|
5
|
+
import { SCProvider } from '../../../../context';
|
|
6
|
+
import { mockSCStore } from '../../../../context/mockStore';
|
|
7
|
+
import AutomateScript from '../AutomateScript';
|
|
8
|
+
|
|
9
|
+
const mockOnPress = jest.fn();
|
|
10
|
+
|
|
11
|
+
const wrapComponent = (isSelected, automate = 'automate') => (
|
|
12
|
+
<SCProvider initState={mockSCStore({})}>
|
|
13
|
+
<AutomateScript
|
|
14
|
+
automate={automate}
|
|
15
|
+
onPress={mockOnPress}
|
|
16
|
+
isSelected={isSelected}
|
|
17
|
+
/>
|
|
18
|
+
</SCProvider>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
describe('test AutomateScript screen', () => {
|
|
22
|
+
let tree,
|
|
23
|
+
automate = { type: '' };
|
|
24
|
+
it('Test isSelected=false', async () => {
|
|
25
|
+
await act(async () => {
|
|
26
|
+
tree = await create(wrapComponent(false, automate));
|
|
27
|
+
});
|
|
28
|
+
const instance = tree.root;
|
|
29
|
+
const TouchableWithoutFeedbacks = instance.findAllByType(
|
|
30
|
+
TouchableWithoutFeedback
|
|
31
|
+
);
|
|
32
|
+
await TouchableWithoutFeedbacks[0].props.onPress();
|
|
33
|
+
expect(mockOnPress).toBeCalledWith(automate);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('Test isSelected=true', async () => {
|
|
37
|
+
await act(async () => {
|
|
38
|
+
tree = await create(wrapComponent(true));
|
|
39
|
+
});
|
|
40
|
+
const instance = tree.root;
|
|
41
|
+
const Views = instance.findAllByType(View);
|
|
42
|
+
expect(Views[0].props.style).toEqual([
|
|
43
|
+
{
|
|
44
|
+
padding: 12,
|
|
45
|
+
borderRadius: 10,
|
|
46
|
+
shadowColor: '#303133',
|
|
47
|
+
shadowOffset: {
|
|
48
|
+
height: 2,
|
|
49
|
+
width: 0,
|
|
50
|
+
},
|
|
51
|
+
shadowOpacity: 0.1,
|
|
52
|
+
shadowRadius: 3,
|
|
53
|
+
elevation: 4,
|
|
54
|
+
width: 353,
|
|
55
|
+
height: 204.144578313253,
|
|
56
|
+
backgroundColor: '#FFFFFF',
|
|
57
|
+
justifyContent: 'space-between',
|
|
58
|
+
marginBottom: 8,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
borderColor: '#00979D',
|
|
62
|
+
borderWidth: 2,
|
|
63
|
+
},
|
|
64
|
+
]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('Test render VALUE_CHANGE', async () => {
|
|
68
|
+
automate = { ...automate, type: AUTOMATE_TYPE.VALUE_CHANGE };
|
|
69
|
+
await act(async () => {
|
|
70
|
+
tree = await create(wrapComponent(true, automate));
|
|
71
|
+
});
|
|
72
|
+
const instance = tree.root;
|
|
73
|
+
const Views = instance.findAllByType(View);
|
|
74
|
+
expect(Views).toHaveLength(9);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('Test render ONE_TAP', async () => {
|
|
78
|
+
automate = { ...automate, type: AUTOMATE_TYPE.ONE_TAP };
|
|
79
|
+
await act(async () => {
|
|
80
|
+
tree = await create(wrapComponent(true, automate));
|
|
81
|
+
});
|
|
82
|
+
const instance = tree.root;
|
|
83
|
+
const Views = instance.findAllByType(View);
|
|
84
|
+
expect(Views).toHaveLength(9);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('Test render EVENT', async () => {
|
|
88
|
+
automate = { ...automate, type: AUTOMATE_TYPE.EVENT };
|
|
89
|
+
await act(async () => {
|
|
90
|
+
tree = await create(wrapComponent(true, automate));
|
|
91
|
+
});
|
|
92
|
+
const instance = tree.root;
|
|
93
|
+
const Views = instance.findAllByType(View);
|
|
94
|
+
expect(Views).toHaveLength(9);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('Test render SCHEDULE', async () => {
|
|
98
|
+
automate = { ...automate, type: AUTOMATE_TYPE.SCHEDULE };
|
|
99
|
+
await act(async () => {
|
|
100
|
+
tree = await create(wrapComponent(true, automate));
|
|
101
|
+
});
|
|
102
|
+
const instance = tree.root;
|
|
103
|
+
const Views = instance.findAllByType(View);
|
|
104
|
+
expect(Views).toHaveLength(9);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('Test render with iconKit', async () => {
|
|
108
|
+
automate = { ...automate, script: { icon_kit: 'iconKit' } };
|
|
109
|
+
await act(async () => {
|
|
110
|
+
tree = await create(wrapComponent(true, automate));
|
|
111
|
+
});
|
|
112
|
+
const instance = tree.root;
|
|
113
|
+
const Views = instance.findAllByType(View);
|
|
114
|
+
expect(Views).toHaveLength(10);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import renderer, { act } from 'react-test-renderer';
|
|
3
3
|
import Modal from 'react-native-modal';
|
|
4
|
-
import Popover from '
|
|
4
|
+
import Popover from '../../../../commons/Popover';
|
|
5
5
|
|
|
6
6
|
import { SCProvider } from '../../../../context';
|
|
7
7
|
import { mockSCStore } from '../../../../context/mockStore';
|
|
@@ -26,7 +26,7 @@ describe('Test MyUnitDevice', () => {
|
|
|
26
26
|
|
|
27
27
|
beforeEach(() => {
|
|
28
28
|
props = {
|
|
29
|
-
|
|
29
|
+
device: {
|
|
30
30
|
status: 'Ok',
|
|
31
31
|
name: 'Test',
|
|
32
32
|
station_name: '',
|
|
@@ -54,7 +54,7 @@ describe('Test MyUnitDevice', () => {
|
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
it('Test render without status', async () => {
|
|
57
|
-
props.
|
|
57
|
+
props.device.status = undefined;
|
|
58
58
|
await act(() => {
|
|
59
59
|
tree = create(wrapComponent(props));
|
|
60
60
|
});
|
|
@@ -5,7 +5,9 @@ import { useGGHomeConnection } from '../../../hooks/IoT';
|
|
|
5
5
|
import { useSCContextSelector } from '../../../context';
|
|
6
6
|
|
|
7
7
|
export const useUnitConnectRemoteDevices = (unit) => {
|
|
8
|
-
const
|
|
8
|
+
const isNetworkConnected = useSCContextSelector(
|
|
9
|
+
(state) => state.app.isNetworkConnected
|
|
10
|
+
);
|
|
9
11
|
|
|
10
12
|
const { connectGoogleHome } = useGGHomeConnection();
|
|
11
13
|
|
|
@@ -21,15 +23,14 @@ export const useUnitConnectRemoteDevices = (unit) => {
|
|
|
21
23
|
}, []);
|
|
22
24
|
|
|
23
25
|
useEffect(() => {
|
|
24
|
-
if (unit
|
|
26
|
+
if (unit?.remote_control_options?.bluetooth) {
|
|
25
27
|
scanBluetoothDevices(unit.remote_control_options.bluetooth);
|
|
26
28
|
}
|
|
27
29
|
}, [unit]);
|
|
28
30
|
|
|
29
31
|
useEffect(() => {
|
|
30
32
|
if (
|
|
31
|
-
unit
|
|
32
|
-
unit.remote_control_options.googlehome?.length &&
|
|
33
|
+
unit?.remote_control_options?.googlehome?.length &&
|
|
33
34
|
isNetworkConnected
|
|
34
35
|
) {
|
|
35
36
|
(async () => {
|
|
@@ -40,7 +41,7 @@ export const useUnitConnectRemoteDevices = (unit) => {
|
|
|
40
41
|
}, [unit, isNetworkConnected]);
|
|
41
42
|
|
|
42
43
|
useEffect(() => {
|
|
43
|
-
if (unit
|
|
44
|
+
if (unit?.remote_control_options?.lg_thinq) {
|
|
44
45
|
(async () => {
|
|
45
46
|
await handleLgThinqConnect(unit.remote_control_options.lg_thinq);
|
|
46
47
|
})();
|