Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 1c215e2

Browse files
author
Kerry
authored
Live location sharing - render users own beacons in timeline (#8296)
* extract location markers into generic Marker Signed-off-by: Kerry Archibald <[email protected]> * wrap marker in smartmarker Signed-off-by: Kerry Archibald <[email protected]> * test smartmarker Signed-off-by: Kerry Archibald <[email protected]> * working map in location body Signed-off-by: Kerry Archibald <[email protected]> * remove skinned sdk Signed-off-by: Kerry Archibald <[email protected]> * use new ZoomButtons in MLocationBody Signed-off-by: Kerry Archibald <[email protected]> * test LocationViewDialog Signed-off-by: Kerry Archibald <[email protected]> * update commentt Signed-off-by: Kerry Archibald <[email protected]> * lint Signed-off-by: Kerry Archibald <[email protected]> * lint Signed-off-by: Kerry Archibald <[email protected]> * extract livetimeremaining into own component Signed-off-by: Kerry Archibald <[email protected]> * extract more beacon state utils Signed-off-by: Kerry Archibald <[email protected]> * update tests for roomlivesharewarning Signed-off-by: Kerry Archibald <[email protected]> * add beacon map and status chin Signed-off-by: Kerry Archibald <[email protected]> * add handling for bubbles Signed-off-by: Kerry Archibald <[email protected]> * tests for BeaconBody Signed-off-by: Kerry Archibald <[email protected]> * move displaystatus check up to mbeaconbody Signed-off-by: Kerry Archibald <[email protected]> * test BeaconStatus Signed-off-by: Kerry Archibald <[email protected]> * rename BeaconStatusChin -> BeaconStatus Signed-off-by: Kerry Archibald <[email protected]> * make BeaconStatus generic Signed-off-by: Kerry Archibald <[email protected]> * lint Signed-off-by: Kerry Archibald <[email protected]> * adjust spinner size Signed-off-by: Kerry Archibald <[email protected]> * lint Signed-off-by: Kerry Archibald <[email protected]> * add static time remaining option to beacon status Signed-off-by: Kerry Archibald <[email protected]> * render time differently for own beacon Signed-off-by: Kerry Archibald <[email protected]> * use children to add actions to BeaconStatus Signed-off-by: Kerry Archibald <[email protected]> * add OwnBeaconStatus wrapper with stop button Signed-off-by: Kerry Archibald <[email protected]> * add error states for own beacon Signed-off-by: Kerry Archibald <[email protected]> * test OwnBeaconStatus Signed-off-by: Kerry Archibald <[email protected]> * move ownbeaconstatus to write dir Signed-off-by: Kerry Archibald <[email protected]> * improve button styling Signed-off-by: Kerry Archibald <[email protected]> * i18n Signed-off-by: Kerry Archibald <[email protected]> * lint Signed-off-by: Kerry Archibald <[email protected]>
1 parent 7a1a2c4 commit 1c215e2

File tree

11 files changed

+440
-114
lines changed

11 files changed

+440
-114
lines changed

res/css/_components.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
@import "./components/views/beacon/_BeaconStatus.scss";
88
@import "./components/views/beacon/_LeftPanelLiveShareWarning.scss";
99
@import "./components/views/beacon/_LiveTimeRemaining.scss";
10+
@import "./components/views/beacon/_OwnBeaconStatus.scss";
1011
@import "./components/views/beacon/_RoomLiveShareWarning.scss";
1112
@import "./components/views/beacon/_StyledLiveBeaconIcon.scss";
1213
@import "./components/views/location/_LiveDurationDropdown.scss";

res/css/components/views/beacon/_BeaconStatus.scss

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,17 @@ limitations under the License.
4545
margin-right: $spacing-8;
4646
}
4747

48-
.mx_BeaconStatus_activeDescription {
48+
.mx_BeaconStatus_description {
4949
flex: 1;
5050
display: flex;
5151
flex-direction: column;
5252
line-height: $font-14px;
53+
54+
padding-right: $spacing-8;
55+
56+
// TODO handle text-overflow
5357
}
5458

55-
.mx_BeaconStatus_stopButton {
56-
// override button link_inline styles
57-
color: $alert !important;
58-
font-weight: $font-semi-bold !important;
59-
text-transform: uppercase;
59+
.mx_BeaconStatus_expiryTime {
60+
color: $secondary-content;
6061
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
.mx_EventTile[data-layout="bubble"] .mx_OwnBeaconStatus_button {
18+
// align to top to make room for timestamp
19+
// in bubble view
20+
align-self: start;
21+
}
22+
23+
.mx_OwnBeaconStatus_destructiveButton {
24+
// override button link_inline styles
25+
color: $alert !important;
26+
font-weight: $font-semi-bold !important;
27+
}

src/components/views/beacon/BeaconStatus.tsx

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,33 @@ import { Beacon } from 'matrix-js-sdk/src/matrix';
2020

2121
import StyledLiveBeaconIcon from './StyledLiveBeaconIcon';
2222
import { _t } from '../../../languageHandler';
23-
import AccessibleButton from '../elements/AccessibleButton';
2423
import LiveTimeRemaining from './LiveTimeRemaining';
2524
import { BeaconDisplayStatus } from './displayStatus';
25+
import { getBeaconExpiryTimestamp } from '../../../utils/beacon';
26+
import { formatTime } from '../../../DateUtils';
2627

2728
interface Props {
2829
displayStatus: BeaconDisplayStatus;
30+
displayLiveTimeRemaining?: boolean;
2931
beacon?: Beacon;
3032
label?: string;
31-
// assumes permission to stop was checked by parent
32-
stopBeacon?: () => void;
3333
}
3434

35+
const BeaconExpiryTime: React.FC<{ beacon: Beacon }> = ({ beacon }) => {
36+
const expiryTime = formatTime(new Date(getBeaconExpiryTimestamp(beacon)));
37+
return <span className='mx_BeaconStatus_expiryTime'>{ _t('Live until %(expiryTime)s', { expiryTime }) }</span>;
38+
};
39+
3540
const BeaconStatus: React.FC<Props & HTMLProps<HTMLDivElement>> =
36-
({ beacon, displayStatus, label, stopBeacon, className, ...rest }) => {
41+
({
42+
beacon,
43+
displayStatus,
44+
displayLiveTimeRemaining,
45+
label,
46+
className,
47+
children,
48+
...rest
49+
}) => {
3750
const isIdle = displayStatus === BeaconDisplayStatus.Loading ||
3851
displayStatus === BeaconDisplayStatus.Stopped;
3952

@@ -46,25 +59,25 @@ const BeaconStatus: React.FC<Props & HTMLProps<HTMLDivElement>> =
4659
withError={displayStatus === BeaconDisplayStatus.Error}
4760
isIdle={isIdle}
4861
/>
49-
{ displayStatus === BeaconDisplayStatus.Loading && <span>{ _t('Loading live location...') }</span> }
50-
{ displayStatus === BeaconDisplayStatus.Stopped && <span>{ _t('Live location ended') }</span> }
62+
<div className='mx_BeaconStatus_description'>
63+
64+
{ displayStatus === BeaconDisplayStatus.Loading && <span>{ _t('Loading live location...') }</span> }
65+
{ displayStatus === BeaconDisplayStatus.Stopped && <span>{ _t('Live location ended') }</span> }
5166

52-
{ /* TODO error */ }
67+
{ displayStatus === BeaconDisplayStatus.Error && <span>{ _t('Live location error') }</span> }
5368

54-
{ displayStatus === BeaconDisplayStatus.Active && beacon && <>
55-
<div className='mx_BeaconStatus_activeDescription'>
56-
{ label }
57-
<LiveTimeRemaining beacon={beacon} />
58-
</div>
59-
{ stopBeacon && <AccessibleButton
60-
data-test-id='beacon-status-stop-beacon'
61-
kind='link'
62-
onClick={stopBeacon}
63-
className='mx_BeaconStatus_stopButton'
64-
>{ _t('Stop') }</AccessibleButton>
69+
{ displayStatus === BeaconDisplayStatus.Active && beacon && <>
70+
<>
71+
{ label }
72+
{ displayLiveTimeRemaining ?
73+
<LiveTimeRemaining beacon={beacon} /> :
74+
<BeaconExpiryTime beacon={beacon} />
75+
}
76+
</>
77+
</>
6578
}
66-
</>
67-
}
79+
</div>
80+
{ children }
6881
</div>;
6982
};
7083

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import { Beacon } from 'matrix-js-sdk/src/matrix';
18+
import React, { HTMLProps } from 'react';
19+
20+
import { _t } from '../../../languageHandler';
21+
import { useOwnLiveBeacons } from '../../../utils/beacon';
22+
import BeaconStatus from './BeaconStatus';
23+
import { BeaconDisplayStatus } from './displayStatus';
24+
import AccessibleButton from '../elements/AccessibleButton';
25+
26+
interface Props {
27+
displayStatus: BeaconDisplayStatus;
28+
beacon?: Beacon;
29+
}
30+
31+
/**
32+
* Wraps BeaconStatus with more capabilities
33+
* for errors and actions available for users own live beacons
34+
*/
35+
const OwnBeaconStatus: React.FC<Props & HTMLProps<HTMLDivElement>> = ({
36+
beacon, displayStatus, className, ...rest
37+
}) => {
38+
const {
39+
hasWireError,
40+
hasStopSharingError,
41+
stoppingInProgress,
42+
onStopSharing,
43+
onResetWireError,
44+
} = useOwnLiveBeacons([beacon?.identifier]);
45+
46+
// combine display status with errors that only occur for user's own beacons
47+
const ownDisplayStatus = hasWireError || hasStopSharingError ?
48+
BeaconDisplayStatus.Error :
49+
displayStatus;
50+
51+
return <BeaconStatus
52+
className='mx_MBeaconBody_chin'
53+
beacon={beacon}
54+
displayStatus={ownDisplayStatus}
55+
label={_t('Live location enabled')}
56+
displayLiveTimeRemaining
57+
{...rest}
58+
>
59+
{ ownDisplayStatus === BeaconDisplayStatus.Active && <AccessibleButton
60+
data-test-id='beacon-status-stop-beacon'
61+
kind='link'
62+
onClick={onStopSharing}
63+
className='mx_OwnBeaconStatus_button mx_OwnBeaconStatus_destructiveButton'
64+
disabled={stoppingInProgress}
65+
>
66+
{ _t('Stop') }
67+
</AccessibleButton>
68+
}
69+
{ hasWireError && <AccessibleButton
70+
data-test-id='beacon-status-reset-wire-error'
71+
kind='link'
72+
onClick={onResetWireError}
73+
className='mx_OwnBeaconStatus_button mx_OwnBeaconStatus_destructiveButton'
74+
>
75+
{ _t('Retry') }
76+
</AccessibleButton>
77+
}
78+
{ hasStopSharingError && <AccessibleButton
79+
data-test-id='beacon-status-stop-beacon-retry'
80+
kind='link'
81+
onClick={onStopSharing}
82+
className='mx_OwnBeaconStatus_button mx_OwnBeaconStatus_destructiveButton'
83+
>
84+
{ _t('Retry') }
85+
</AccessibleButton> }
86+
</BeaconStatus>;
87+
};
88+
89+
export default OwnBeaconStatus;

src/components/views/messages/MBeaconBody.tsx

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,24 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import React, { useEffect, useState } from 'react';
17+
import React, { useContext, useEffect, useState } from 'react';
1818
import { Beacon, BeaconEvent, MatrixEvent } from 'matrix-js-sdk/src/matrix';
1919
import { BeaconLocationState } from 'matrix-js-sdk/src/content-helpers';
2020
import { randomString } from 'matrix-js-sdk/src/randomstring';
2121

2222
import { Icon as LocationMarkerIcon } from '../../../../res/img/element-icons/location.svg';
23+
import MatrixClientContext from '../../../contexts/MatrixClientContext';
2324
import { useEventEmitterState } from '../../../hooks/useEventEmitter';
25+
import { _t } from '../../../languageHandler';
2426
import { useBeacon } from '../../../utils/beacon';
2527
import { isSelfLocation } from '../../../utils/location';
2628
import { BeaconDisplayStatus, getBeaconDisplayStatus } from '../beacon/displayStatus';
29+
import BeaconStatus from '../beacon/BeaconStatus';
2730
import Spinner from '../elements/Spinner';
2831
import Map from '../location/Map';
2932
import SmartMarker from '../location/SmartMarker';
30-
import BeaconStatus from '../beacon/BeaconStatus';
33+
import OwnBeaconStatus from '../beacon/OwnBeaconStatus';
3134
import { IBodyProps } from "./IBodyProps";
32-
import { _t } from '../../../languageHandler';
3335

3436
const useBeaconState = (beaconInfoEvent: MatrixEvent): {
3537
beacon?: Beacon;
@@ -83,13 +85,13 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent }, ref) =>
8385
latestLocationState,
8486
} = useBeaconState(mxEvent);
8587
const mapId = useUniqueId(mxEvent.getId());
86-
8788
const [error, setError] = useState<Error>();
88-
89+
const matrixClient = useContext(MatrixClientContext);
8990
const displayStatus = getBeaconDisplayStatus(isLive, latestLocationState, error);
90-
9191
const markerRoomMember = isSelfLocation(mxEvent.getContent()) ? mxEvent.sender : undefined;
9292

93+
const isOwnBeacon = beacon?.beaconInfoOwner === matrixClient.getUserId();
94+
9395
return (
9496
<div className='mx_MBeaconBody' ref={ref}>
9597
{ displayStatus === BeaconDisplayStatus.Active ?
@@ -106,6 +108,7 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent }, ref) =>
106108
id={`${mapId}-marker`}
107109
geoUri={latestLocationState.uri}
108110
roomMember={markerRoomMember}
111+
useMemberColor
109112
/>
110113
}
111114
</Map>
@@ -116,12 +119,19 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent }, ref) =>
116119
}
117120
</div>
118121
}
119-
<BeaconStatus
120-
className='mx_MBeaconBody_chin'
121-
beacon={beacon}
122-
displayStatus={displayStatus}
123-
label={_t('View live location')}
124-
/>
122+
{ isOwnBeacon ?
123+
<OwnBeaconStatus
124+
className='mx_MBeaconBody_chin'
125+
beacon={beacon}
126+
displayStatus={displayStatus}
127+
/> :
128+
<BeaconStatus
129+
className='mx_MBeaconBody_chin'
130+
beacon={beacon}
131+
displayStatus={displayStatus}
132+
label={_t('View live location')}
133+
/>
134+
}
125135
</div>
126136
);
127137
});

src/i18n/strings/en_EN.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2905,11 +2905,14 @@
29052905
"Beta": "Beta",
29062906
"Leave the beta": "Leave the beta",
29072907
"Join the beta": "Join the beta",
2908+
"Live until %(expiryTime)s": "Live until %(expiryTime)s",
29082909
"Loading live location...": "Loading live location...",
29092910
"Live location ended": "Live location ended",
2911+
"Live location error": "Live location error",
29102912
"An error occured whilst sharing your live location": "An error occured whilst sharing your live location",
29112913
"You are sharing your live location": "You are sharing your live location",
29122914
"%(timeRemaining)s left": "%(timeRemaining)s left",
2915+
"Live location enabled": "Live location enabled",
29132916
"An error occured whilst sharing your live location, please try again": "An error occured whilst sharing your live location, please try again",
29142917
"An error occurred while stopping your live location, please try again": "An error occurred while stopping your live location, please try again",
29152918
"Stop sharing": "Stop sharing",

0 commit comments

Comments
 (0)