Live location sharing: allow retry when stop sharing fails (#8193)
* allow retry when stop sharing fails Signed-off-by: Kerry Archibald <kerrya@element.io> * tidy Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
@@ -73,9 +73,11 @@ type LiveBeaconsState = {
|
||||
beacon?: Beacon;
|
||||
onStopSharing?: () => void;
|
||||
stoppingInProgress?: boolean;
|
||||
hasStopSharingError?: boolean;
|
||||
};
|
||||
const useLiveBeacons = (roomId: Room['roomId']): LiveBeaconsState => {
|
||||
const [stoppingInProgress, setStoppingInProgress] = useState(false);
|
||||
const [error, setError] = useState<Error>();
|
||||
|
||||
// do we have an active geolocation.watchPosition
|
||||
const isMonitoringLiveLocation = useEventEmitterState(
|
||||
@@ -93,6 +95,7 @@ const useLiveBeacons = (roomId: Room['roomId']): LiveBeaconsState => {
|
||||
// reset stopping in progress on change in live ids
|
||||
useEffect(() => {
|
||||
setStoppingInProgress(false);
|
||||
setError(undefined);
|
||||
}, [liveBeaconIds]);
|
||||
|
||||
if (!isMonitoringLiveLocation || !liveBeaconIds?.length) {
|
||||
@@ -112,11 +115,12 @@ const useLiveBeacons = (roomId: Room['roomId']): LiveBeaconsState => {
|
||||
// only clear loading in case of error
|
||||
// to avoid flash of not-loading state
|
||||
// after beacons have been stopped but we wait for sync
|
||||
setError(error);
|
||||
setStoppingInProgress(false);
|
||||
}
|
||||
};
|
||||
|
||||
return { onStopSharing, beacon, stoppingInProgress };
|
||||
return { onStopSharing, beacon, stoppingInProgress, hasStopSharingError: !!error };
|
||||
};
|
||||
|
||||
const LiveTimeRemaining: React.FC<{ beacon: Beacon }> = ({ beacon }) => {
|
||||
@@ -136,6 +140,7 @@ const RoomLiveShareWarning: React.FC<Props> = ({ roomId }) => {
|
||||
onStopSharing,
|
||||
beacon,
|
||||
stoppingInProgress,
|
||||
hasStopSharingError,
|
||||
} = useLiveBeacons(roomId);
|
||||
|
||||
if (!beacon) {
|
||||
@@ -145,15 +150,19 @@ const RoomLiveShareWarning: React.FC<Props> = ({ roomId }) => {
|
||||
return <div
|
||||
className={classNames('mx_RoomLiveShareWarning')}
|
||||
>
|
||||
<StyledLiveBeaconIcon className="mx_RoomLiveShareWarning_icon" />
|
||||
<StyledLiveBeaconIcon className="mx_RoomLiveShareWarning_icon" withError={hasStopSharingError} />
|
||||
<span className="mx_RoomLiveShareWarning_label">
|
||||
{ _t('You are sharing your live location') }
|
||||
{ hasStopSharingError ?
|
||||
_t('An error occurred while stopping your live location, please try again') :
|
||||
_t('You are sharing your live location')
|
||||
}
|
||||
</span>
|
||||
|
||||
{ stoppingInProgress ?
|
||||
<span className='mx_RoomLiveShareWarning_spinner'><Spinner h={16} w={16} /></span> :
|
||||
<LiveTimeRemaining beacon={beacon} />
|
||||
{ stoppingInProgress &&
|
||||
<span className='mx_RoomLiveShareWarning_spinner'><Spinner h={16} w={16} /></span>
|
||||
}
|
||||
{ !stoppingInProgress && !hasStopSharingError && <LiveTimeRemaining beacon={beacon} /> }
|
||||
|
||||
<AccessibleButton
|
||||
data-test-id='room-live-share-stop-sharing'
|
||||
onClick={onStopSharing}
|
||||
@@ -161,7 +170,7 @@ const RoomLiveShareWarning: React.FC<Props> = ({ roomId }) => {
|
||||
element='button'
|
||||
disabled={stoppingInProgress}
|
||||
>
|
||||
{ _t('Stop sharing') }
|
||||
{ hasStopSharingError ? _t('Retry') : _t('Stop sharing') }
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
};
|
||||
|
||||
@@ -19,10 +19,14 @@ import classNames from 'classnames';
|
||||
|
||||
import { Icon as LiveLocationIcon } from '../../../../res/img/location/live-location.svg';
|
||||
|
||||
const StyledLiveBeaconIcon: React.FC<React.SVGProps<SVGSVGElement>> = ({ className, ...props }) =>
|
||||
interface Props extends React.SVGProps<SVGSVGElement> {
|
||||
// use error styling when true
|
||||
withError?: boolean;
|
||||
}
|
||||
const StyledLiveBeaconIcon: React.FC<Props> = ({ className, withError, ...props }) =>
|
||||
<LiveLocationIcon
|
||||
{...props}
|
||||
className={classNames('mx_StyledLiveBeaconIcon', className)}
|
||||
className={classNames('mx_StyledLiveBeaconIcon', className, { 'mx_StyledLiveBeaconIcon_error': withError })}
|
||||
/>;
|
||||
|
||||
export default StyledLiveBeaconIcon;
|
||||
|
||||
@@ -2898,6 +2898,7 @@
|
||||
"Join the beta": "Join the beta",
|
||||
"You are sharing your live location": "You are sharing your live location",
|
||||
"%(timeRemaining)s left": "%(timeRemaining)s left",
|
||||
"An error occurred while stopping your live location, please try again": "An error occurred while stopping your live location, please try again",
|
||||
"Stop sharing": "Stop sharing",
|
||||
"Avatar": "Avatar",
|
||||
"This room is public": "This room is public",
|
||||
|
||||
@@ -55,6 +55,7 @@ const STATIC_UPDATE_INTERVAL = 30000;
|
||||
|
||||
type OwnBeaconStoreState = {
|
||||
beacons: Map<string, Beacon>;
|
||||
beaconWireErrors: Map<string, Beacon>;
|
||||
beaconsByRoomId: Map<Room['roomId'], Set<string>>;
|
||||
liveBeaconIds: string[];
|
||||
};
|
||||
@@ -63,6 +64,10 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||
// users beacons, keyed by event type
|
||||
public readonly beacons = new Map<string, Beacon>();
|
||||
public readonly beaconsByRoomId = new Map<Room['roomId'], Set<string>>();
|
||||
/**
|
||||
* Track over the wire errors for beacons
|
||||
*/
|
||||
public readonly beaconWireErrors = new Map<string, Error>();
|
||||
private liveBeaconIds = [];
|
||||
private locationInterval: number;
|
||||
private geolocationError: GeolocationError | undefined;
|
||||
@@ -101,6 +106,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||
this.beacons.clear();
|
||||
this.beaconsByRoomId.clear();
|
||||
this.liveBeaconIds = [];
|
||||
this.beaconWireErrors.clear();
|
||||
}
|
||||
|
||||
protected async onReady(): Promise<void> {
|
||||
@@ -362,7 +368,6 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||
private publishCurrentLocationToBeacons = async () => {
|
||||
try {
|
||||
const position = await getCurrentPosition();
|
||||
// TODO error handling
|
||||
this.publishLocationToBeacons(mapGeolocationPositionToTimedGeo(position));
|
||||
} catch (error) {
|
||||
this.onGeolocationError(error?.message);
|
||||
@@ -394,7 +399,6 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||
*/
|
||||
private publishLocationToBeacons = async (position: TimedGeoUri) => {
|
||||
this.lastPublishedPositionTimestamp = Date.now();
|
||||
// TODO handle failure in individual beacon without rejecting rest
|
||||
await Promise.all(this.liveBeaconIds.map(beaconId =>
|
||||
this.sendLocationToBeacon(this.beacons.get(beaconId), position)),
|
||||
);
|
||||
@@ -407,6 +411,11 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||
*/
|
||||
private sendLocationToBeacon = async (beacon: Beacon, { geoUri, timestamp }: TimedGeoUri) => {
|
||||
const content = makeBeaconContent(geoUri, timestamp, beacon.beaconInfoId);
|
||||
await this.matrixClient.sendEvent(beacon.roomId, M_BEACON.name, content);
|
||||
try {
|
||||
await this.matrixClient.sendEvent(beacon.roomId, M_BEACON.name, content);
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
this.beaconWireErrors.set(beacon.identifier, error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user