From 2bdb4d396a1e13ab82581617b3ccd0d43d385b71 Mon Sep 17 00:00:00 2001 From: Kerry Date: Thu, 9 Jun 2022 14:25:30 +0200 Subject: [PATCH] Live location share - show loading UI for beacons with start timestamp in the future (PSF-1081) (#8775) * check for beacons that are yet to start and show loading ui Signed-off-by: Kerry Archibald * update snapshots for js-sdk rename Signed-off-by: Kerry Archibald * remove debug Signed-off-by: Kerry Archibald * Update test/components/views/messages/MBeaconBody-test.tsx Co-authored-by: Travis Ralston * Update src/components/views/beacon/displayStatus.ts Co-authored-by: Travis Ralston Co-authored-by: Travis Ralston --- src/components/views/beacon/displayStatus.ts | 8 ++++++-- src/components/views/messages/MBeaconBody.tsx | 13 +++++++++++-- src/utils/beacon/duration.ts | 7 +++++++ test/components/views/messages/MBeaconBody-test.tsx | 13 +++++++++++++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/components/views/beacon/displayStatus.ts b/src/components/views/beacon/displayStatus.ts index ee65991070..73bc7bec30 100644 --- a/src/components/views/beacon/displayStatus.ts +++ b/src/components/views/beacon/displayStatus.ts @@ -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; } diff --git a/src/components/views/messages/MBeaconBody.tsx b/src/components/views/messages/MBeaconBody.tsx index bd581d1bce..91c54d701a 100644 --- a/src/components/views/messages/MBeaconBody.tsx +++ b/src/components/views/messages/MBeaconBody.tsx @@ -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'; @@ -39,6 +39,7 @@ const useBeaconState = (beaconInfoEvent: MatrixEvent): { description?: string; latestLocationState?: BeaconLocationState; isLive?: boolean; + waitingToStart?: boolean; } => { const beacon = useBeacon(beaconInfoEvent); @@ -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, }; }; @@ -84,12 +92,13 @@ const MBeaconBody: React.FC = React.forwardRef(({ mxEvent }, ref) => beacon, isLive, latestLocationState, + waitingToStart, } = useBeaconState(mxEvent); const mapId = useUniqueId(mxEvent.getId()); const matrixClient = useContext(MatrixClientContext); const [error, setError] = useState(); - 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(); diff --git a/src/utils/beacon/duration.ts b/src/utils/beacon/duration.ts index c49eef1bd5..bbd51c7b5d 100644 --- a/src/utils/beacon/duration.ts +++ b/src/utils/beacon/duration.ts @@ -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(); diff --git a/test/components/views/messages/MBeaconBody-test.tsx b/test/components/views/messages/MBeaconBody-test.tsx index e9cb719f7f..b817ac2995 100644 --- a/test/components/views/messages/MBeaconBody-test.tsx +++ b/test/components/views/messages/MBeaconBody-test.tsx @@ -111,6 +111,19 @@ describe('', () => { 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,