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

Live location share - show loading UI for beacons with start timestamp in the future (PSF-1081) #8775

Merged
merged 8 commits into from
Jun 9, 2022
8 changes: 6 additions & 2 deletions src/components/views/beacon/displayStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,18 @@ export enum BeaconDisplayStatus {
export const getBeaconDisplayStatus = (
isLive: boolean,
latestLocationState?: BeaconLocationState,
error?: Error): BeaconDisplayStatus => {
error?: Error,
waitingToStart?: boolean,
): BeaconDisplayStatus => {
if (error) {
return BeaconDisplayStatus.Error;
}
if (waitingToStart) {
return BeaconDisplayStatus.Loading;
}
if (!isLive) {
return BeaconDisplayStatus.Stopped;
}

if (!latestLocationState) {
return BeaconDisplayStatus.Loading;
}
Expand Down
13 changes: 11 additions & 2 deletions src/components/views/messages/MBeaconBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import MatrixClientContext from '../../../contexts/MatrixClientContext';
import { useEventEmitterState } from '../../../hooks/useEventEmitter';
import { _t } from '../../../languageHandler';
import Modal from '../../../Modal';
import { useBeacon } from '../../../utils/beacon';
import { isBeaconWaitingToStart, useBeacon } from '../../../utils/beacon';
import { isSelfLocation } from '../../../utils/location';
import { BeaconDisplayStatus, getBeaconDisplayStatus } from '../beacon/displayStatus';
import BeaconStatus from '../beacon/BeaconStatus';
Expand All @@ -39,6 +39,7 @@ const useBeaconState = (beaconInfoEvent: MatrixEvent): {
description?: string;
latestLocationState?: BeaconLocationState;
isLive?: boolean;
waitingToStart?: boolean;
} => {
const beacon = useBeacon(beaconInfoEvent);

Expand All @@ -56,12 +57,19 @@ const useBeaconState = (beaconInfoEvent: MatrixEvent): {
return {};
}

// a beacon's starting timestamp can be in the future
// (either from small deviations in system clock times, or on purpose from another client)
// a beacon is only live between its start timestamp and expiry
// detect when a beacon is waiting to become live
// and display a loading state
const waitingToStart = !!beacon && isBeaconWaitingToStart(beacon);
const { description } = beacon.beaconInfo;

return {
beacon,
description,
isLive,
waitingToStart,
latestLocationState,
};
};
Expand All @@ -84,12 +92,13 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent }, ref) =>
beacon,
isLive,
latestLocationState,
waitingToStart,
} = useBeaconState(mxEvent);
const mapId = useUniqueId(mxEvent.getId());

const matrixClient = useContext(MatrixClientContext);
const [error, setError] = useState<Error>();
const displayStatus = getBeaconDisplayStatus(isLive, latestLocationState, error);
const displayStatus = getBeaconDisplayStatus(isLive, latestLocationState, error, waitingToStart);
const markerRoomMember = isSelfLocation(mxEvent.getContent()) ? mxEvent.sender : undefined;
const isOwnBeacon = beacon?.beaconInfoOwner === matrixClient.getUserId();

Expand Down
7 changes: 7 additions & 0 deletions src/utils/beacon/duration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,10 @@ export const sortBeaconsByLatestExpiry = (left: Beacon, right: Beacon): number =
// aka sort by timestamp descending
export const sortBeaconsByLatestCreation = (left: Beacon, right: Beacon): number =>
right.beaconInfo.timestamp - left.beaconInfo.timestamp;

// a beacon's starting timestamp can be in the future
// (either from small deviations in system clock times, or on purpose from another client)
// a beacon is only live between its start timestamp and expiry
// detect when a beacon is waiting to become live
export const isBeaconWaitingToStart = (beacon: Beacon): boolean =>
!beacon.isLive && beacon.beaconInfo.timestamp > Date.now() && getBeaconExpiryTimestamp(beacon) > Date.now();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there might still be clock drift, but this is probably fine enough.

13 changes: 13 additions & 0 deletions test/components/views/messages/MBeaconBody-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,19 @@ describe('<MBeaconBody />', () => {
expect(component.text()).toEqual("Live location ended");
});

it('renders loading beacon UI for a beacon that has not started yet', () => {
const beaconInfoEvent = makeBeaconInfoEvent(
aliceId,
roomId,
// puts this beacons start timestamp in the future
{ isLive: true, timestamp: now + 60000, timeout: 500 },
'$alice-room1-1',
);
makeRoomWithStateEvents([beaconInfoEvent], { roomId, mockClient });
const component = getComponent({ mxEvent: beaconInfoEvent });
expect(component.text()).toEqual("Loading live location...");
});

it('does not open maximised map when on click when beacon is stopped', () => {
const beaconInfoEvent = makeBeaconInfoEvent(aliceId,
roomId,
Expand Down