Live Location Sharing - left panel warning with error (#8201)

* add error style to left panel beacon warning

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test

Signed-off-by: Kerry Archibald <kerrya@element.io>

* add beacon sort util

* link to latest beacon room from left panel warning

Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
Kerry
2022-03-31 13:51:44 +02:00
committed by GitHub
parent 1175226bcb
commit 4922e19b5a
8 changed files with 289 additions and 24 deletions

View File

@@ -21,11 +21,31 @@ import { useEventEmitterState } from '../../../hooks/useEventEmitter';
import { _t } from '../../../languageHandler';
import { OwnBeaconStore, OwnBeaconStoreEvent } from '../../../stores/OwnBeaconStore';
import { Icon as LiveLocationIcon } from '../../../../res/img/location/live-location.svg';
import { ViewRoomPayload } from '../../../dispatcher/payloads/ViewRoomPayload';
import { Action } from '../../../dispatcher/actions';
import dispatcher from '../../../dispatcher/dispatcher';
import AccessibleButton from '../elements/AccessibleButton';
interface Props {
isMinimized?: boolean;
}
/**
* Choose the most relevant beacon
* and get its roomId
*/
const chooseBestBeaconRoomId = (liveBeaconIds, errorBeaconIds): string | undefined => {
// both lists are ordered by creation timestamp in store
// so select latest beacon
const beaconId = errorBeaconIds?.[0] ?? liveBeaconIds?.[0];
if (!beaconId) {
return undefined;
}
const beacon = OwnBeaconStore.instance.getBeaconById(beaconId);
return beacon?.roomId;
};
const LeftPanelLiveShareWarning: React.FC<Props> = ({ isMinimized }) => {
const isMonitoringLiveLocation = useEventEmitterState(
OwnBeaconStore.instance,
@@ -33,18 +53,48 @@ const LeftPanelLiveShareWarning: React.FC<Props> = ({ isMinimized }) => {
() => OwnBeaconStore.instance.isMonitoringLiveLocation,
);
const beaconIdsWithWireError = useEventEmitterState(
OwnBeaconStore.instance,
OwnBeaconStoreEvent.WireError,
() => OwnBeaconStore.instance.getLiveBeaconIdsWithWireError(),
);
const liveBeaconIds = useEventEmitterState(
OwnBeaconStore.instance,
OwnBeaconStoreEvent.LivenessChange,
() => OwnBeaconStore.instance.getLiveBeaconIds(),
);
const hasWireErrors = !!beaconIdsWithWireError.length;
if (!isMonitoringLiveLocation) {
return null;
}
return <div
const relevantBeaconRoomId = chooseBestBeaconRoomId(liveBeaconIds, beaconIdsWithWireError);
const onWarningClick = relevantBeaconRoomId ? () => {
dispatcher.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: relevantBeaconRoomId,
metricsTrigger: undefined,
});
} : undefined;
const label = hasWireErrors ?
_t('An error occured whilst sharing your live location') :
_t('You are sharing your live location');
return <AccessibleButton
className={classNames('mx_LeftPanelLiveShareWarning', {
'mx_LeftPanelLiveShareWarning__minimized': isMinimized,
'mx_LeftPanelLiveShareWarning__error': hasWireErrors,
})}
title={isMinimized ? _t('You are sharing your live location') : undefined}
title={isMinimized ? label : undefined}
onClick={onWarningClick}
>
{ isMinimized ? <LiveLocationIcon height={10} /> : _t('You are sharing your live location') }
</div>;
{ isMinimized ? <LiveLocationIcon height={10} /> : label }
</AccessibleButton>;
};
export default LeftPanelLiveShareWarning;

View File

@@ -2896,6 +2896,7 @@
"Beta": "Beta",
"Leave the beta": "Leave the beta",
"Join the beta": "Join the beta",
"An error occured whilst sharing your live location": "An error occured whilst sharing your live location",
"You are sharing your live location": "You are sharing your live location",
"%(timeRemaining)s left": "%(timeRemaining)s left",
"An error occured whilst sharing your live location, please try again": "An error occured whilst sharing your live location, please try again",

View File

@@ -38,6 +38,7 @@ import {
ClearWatchCallback,
GeolocationError,
mapGeolocationPositionToTimedGeo,
sortBeaconsByLatestCreation,
TimedGeoUri,
watchPosition,
} from "../utils/beacon";
@@ -73,6 +74,10 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
* Reset on successful publish of location
*/
public readonly beaconWireErrorCounts = new Map<string, number>();
/**
* ids of live beacons
* ordered by creation time descending
*/
private liveBeaconIds = [];
private locationInterval: number;
private geolocationError: GeolocationError | undefined;
@@ -126,17 +131,17 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
// we don't actually do anything here
}
public hasLiveBeacons(roomId?: string): boolean {
public hasLiveBeacons = (roomId?: string): boolean => {
return !!this.getLiveBeaconIds(roomId).length;
}
};
/**
* Some live beacon has a wire error
* Optionally filter by room
*/
public hasWireErrors(roomId?: string): boolean {
public hasWireErrors = (roomId?: string): boolean => {
return this.getLiveBeaconIds(roomId).some(this.beaconHasWireError);
}
};
/**
* If a beacon has failed to publish position
@@ -157,16 +162,20 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
this.publishCurrentLocationToBeacons();
};
public getLiveBeaconIds(roomId?: string): string[] {
public getLiveBeaconIds = (roomId?: string): string[] => {
if (!roomId) {
return this.liveBeaconIds;
}
return this.liveBeaconIds.filter(beaconId => this.beaconsByRoomId.get(roomId)?.has(beaconId));
}
};
public getBeaconById(beaconId: string): Beacon | undefined {
public getLiveBeaconIdsWithWireError = (roomId?: string): string[] => {
return this.getLiveBeaconIds(roomId).filter(this.beaconHasWireError);
};
public getBeaconById = (beaconId: string): Beacon | undefined => {
return this.beacons.get(beaconId);
}
};
public stopBeacon = async (beaconInfoType: string): Promise<void> => {
const beacon = this.beacons.get(beaconInfoType);
@@ -287,6 +296,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
const prevLiveBeaconIds = this.getLiveBeaconIds();
this.liveBeaconIds = [...this.beacons.values()]
.filter(beacon => beacon.isLive)
.sort(sortBeaconsByLatestCreation)
.map(beacon => beacon.identifier);
const diff = arrayDiff(prevLiveBeaconIds, this.liveBeaconIds);

View File

@@ -34,3 +34,7 @@ export const getBeaconExpiryTimestamp = (beacon: Beacon): number =>
export const sortBeaconsByLatestExpiry = (left: Beacon, right: Beacon): number =>
getBeaconExpiryTimestamp(right) - getBeaconExpiryTimestamp(left);
// aka sort by timestamp descending
export const sortBeaconsByLatestCreation = (left: Beacon, right: Beacon): number =>
right.beaconInfo.timestamp - left.beaconInfo.timestamp;