From 72404d7216c5c3859518fa283fc312f45cd5fc1a Mon Sep 17 00:00:00 2001
From: Kerry <kerrya@element.io>
Date: Tue, 14 Mar 2023 10:55:50 +1300
Subject: [PATCH] Apply `strictNullChecks` to `src/utils/beacon/*` (#10337)

* strictnullchecks fixes in utils/beacon

* user filterBoolean
---
 src/utils/beacon/bounds.ts         |  9 ++++++---
 src/utils/beacon/duration.ts       | 11 +++++++----
 src/utils/beacon/geolocation.ts    |  2 +-
 src/utils/beacon/useLiveBeacons.ts |  9 +++++++--
 test/utils/beacon/duration-test.ts | 17 +++++++++++++++++
 5 files changed, 38 insertions(+), 10 deletions(-)

diff --git a/src/utils/beacon/bounds.ts b/src/utils/beacon/bounds.ts
index ad1b515fb4..829f16d3ca 100644
--- a/src/utils/beacon/bounds.ts
+++ b/src/utils/beacon/bounds.ts
@@ -16,6 +16,7 @@ limitations under the License.
 
 import { Beacon } from "matrix-js-sdk/src/matrix";
 
+import { filterBoolean } from "../arrays";
 import { parseGeoUri } from "../location";
 
 export type Bounds = {
@@ -36,9 +37,11 @@ export type Bounds = {
  * west of Greenwich has a negative longitude, min -180
  */
 export const getBeaconBounds = (beacons: Beacon[]): Bounds | undefined => {
-    const coords = beacons
-        .filter((beacon) => !!beacon.latestLocationState)
-        .map((beacon) => parseGeoUri(beacon.latestLocationState.uri));
+    const coords = filterBoolean<GeolocationCoordinates>(
+        beacons.map((beacon) =>
+            !!beacon.latestLocationState?.uri ? parseGeoUri(beacon.latestLocationState.uri) : undefined,
+        ),
+    );
 
     if (!coords.length) {
         return;
diff --git a/src/utils/beacon/duration.ts b/src/utils/beacon/duration.ts
index 136207b6f3..a0a3686101 100644
--- a/src/utils/beacon/duration.ts
+++ b/src/utils/beacon/duration.ts
@@ -28,21 +28,24 @@ export const msUntilExpiry = (startTimestamp: number, durationMs: number): numbe
     Math.max(0, startTimestamp + durationMs - Date.now());
 
 export const getBeaconMsUntilExpiry = (beaconInfo: BeaconInfoState): number =>
-    msUntilExpiry(beaconInfo.timestamp, beaconInfo.timeout);
+    msUntilExpiry(beaconInfo.timestamp || 0, beaconInfo.timeout);
 
 export const getBeaconExpiryTimestamp = (beacon: Beacon): number =>
-    beacon.beaconInfo.timestamp + beacon.beaconInfo.timeout;
+    (beacon.beaconInfo.timestamp || 0) + beacon.beaconInfo.timeout;
 
 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;
+    (right.beaconInfo.timestamp || 0) - (left.beaconInfo.timestamp || 0);
 
 // 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();
+    !beacon.isLive &&
+    !!beacon.beaconInfo.timestamp &&
+    beacon.beaconInfo.timestamp > Date.now() &&
+    getBeaconExpiryTimestamp(beacon) > Date.now();
diff --git a/src/utils/beacon/geolocation.ts b/src/utils/beacon/geolocation.ts
index 1fa3367b6f..3a55bbe2dc 100644
--- a/src/utils/beacon/geolocation.ts
+++ b/src/utils/beacon/geolocation.ts
@@ -93,7 +93,7 @@ export const genericPositionFromGeolocation = (geoPosition: GeolocationPosition)
         timestamp: Date.now(),
         latitude,
         longitude,
-        altitude,
+        altitude: altitude ?? undefined,
         accuracy,
     };
 };
diff --git a/src/utils/beacon/useLiveBeacons.ts b/src/utils/beacon/useLiveBeacons.ts
index 556e6b3dea..2e78841290 100644
--- a/src/utils/beacon/useLiveBeacons.ts
+++ b/src/utils/beacon/useLiveBeacons.ts
@@ -26,8 +26,13 @@ import { useEventEmitterState } from "../../hooks/useEventEmitter";
 export const useLiveBeacons = (roomId: Room["roomId"], matrixClient: MatrixClient): Beacon[] => {
     const room = matrixClient.getRoom(roomId);
 
-    const liveBeacons = useEventEmitterState(room?.currentState, RoomStateEvent.BeaconLiveness, () =>
-        room?.currentState?.liveBeaconIds.map((beaconIdentifier) => room.currentState.beacons.get(beaconIdentifier)),
+    const liveBeacons = useEventEmitterState(
+        room?.currentState,
+        RoomStateEvent.BeaconLiveness,
+        () =>
+            room?.currentState?.liveBeaconIds.map(
+                (beaconIdentifier) => room.currentState.beacons.get(beaconIdentifier)!,
+            ) || [],
     );
 
     return liveBeacons;
diff --git a/test/utils/beacon/duration-test.ts b/test/utils/beacon/duration-test.ts
index ed71865f29..e4ac952086 100644
--- a/test/utils/beacon/duration-test.ts
+++ b/test/utils/beacon/duration-test.ts
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
+import { M_TIMESTAMP } from "matrix-js-sdk/src/@types/location";
 import { Beacon } from "matrix-js-sdk/src/matrix";
 
 import { msUntilExpiry, sortBeaconsByLatestExpiry, sortBeaconsByLatestCreation } from "../../../src/utils/beacon";
@@ -68,9 +69,25 @@ describe("beacon utils", () => {
             makeBeaconInfoEvent(aliceId, roomId, { timeout: HOUR_MS + 1, timestamp: now - HOUR_MS }, "$3"),
         );
 
+        const noTimestampEvent = makeBeaconInfoEvent(
+            aliceId,
+            roomId,
+            { timeout: HOUR_MS + 1, timestamp: undefined },
+            "$3",
+        );
+        // beacon info helper defaults to date when timestamp is falsy
+        // hard set it to undefined
+        // @ts-ignore
+        noTimestampEvent.event.content[M_TIMESTAMP.name] = undefined;
+        const beaconNoTimestamp = new Beacon(noTimestampEvent);
+
         it("sorts beacons by descending expiry time", () => {
             expect([beacon2, beacon3, beacon1].sort(sortBeaconsByLatestExpiry)).toEqual([beacon1, beacon2, beacon3]);
         });
+
+        it("sorts beacons with timestamps before beacons without", () => {
+            expect([beaconNoTimestamp, beacon3].sort(sortBeaconsByLatestExpiry)).toEqual([beacon3, beaconNoTimestamp]);
+        });
     });
 
     describe("sortBeaconsByLatestCreation()", () => {