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

Commit ab934a2

Browse files
author
Kerry
authored
kill beacons on expiry (#8075)
Signed-off-by: Kerry Archibald <[email protected]>
1 parent 12d2655 commit ab934a2

File tree

2 files changed

+138
-5
lines changed

2 files changed

+138
-5
lines changed

src/stores/OwnBeaconStore.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import {
2020
MatrixEvent,
2121
Room,
2222
} from "matrix-js-sdk/src/matrix";
23+
import {
24+
BeaconInfoState, makeBeaconInfoContent,
25+
} from "matrix-js-sdk/src/content-helpers";
2326

2427
import defaultDispatcher from "../dispatcher/dispatcher";
2528
import { ActionPayload } from "../dispatcher/payloads";
@@ -83,6 +86,17 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
8386
return this.liveBeaconIds.filter(beaconId => this.beaconsByRoomId.get(roomId)?.has(beaconId));
8487
}
8588

89+
public stopBeacon = async (beaconInfoId: string): Promise<void> => {
90+
const beacon = this.beacons.get(beaconInfoId);
91+
// if no beacon, or beacon is already explicitly set isLive: false
92+
// do nothing
93+
if (!beacon?.beaconInfo?.live) {
94+
return;
95+
}
96+
97+
return await this.updateBeaconEvent(beacon, { live: false });
98+
};
99+
86100
private onNewBeacon = (_event: MatrixEvent, beacon: Beacon): void => {
87101
if (!isOwnBeacon(beacon, this.matrixClient.getUserId())) {
88102
return;
@@ -106,9 +120,14 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
106120
this.liveBeaconIds.push(beacon.beaconInfoId);
107121
}
108122

123+
// beacon expired, update beacon to un-alive state
124+
if (!isLive) {
125+
this.stopBeacon(beacon.beaconInfoId);
126+
}
127+
128+
// TODO start location polling here
129+
109130
this.emit(OwnBeaconStoreEvent.LivenessChange, this.hasLiveBeacons());
110-
// TODO stop or start polling here
111-
// if not content is live but beacon is not, update state event with live: false
112131
};
113132

114133
private initialiseBeaconState = () => {
@@ -134,6 +153,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
134153
}
135154

136155
this.beaconsByRoomId.get(beacon.roomId).add(beacon.beaconInfoId);
156+
137157
beacon.monitorLiveness();
138158
};
139159

@@ -149,4 +169,19 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
149169
this.emit(OwnBeaconStoreEvent.LivenessChange, newLiveness);
150170
}
151171
};
172+
173+
private updateBeaconEvent = async (beacon: Beacon, update: Partial<BeaconInfoState>): Promise<void> => {
174+
const { description, timeout, timestamp, live, assetType } = {
175+
...beacon.beaconInfo,
176+
...update,
177+
};
178+
179+
const updateContent = makeBeaconInfoContent(timeout,
180+
live,
181+
description,
182+
assetType,
183+
timestamp);
184+
185+
await this.matrixClient.unstable_setLiveBeacon(beacon.roomId, beacon.beaconInfoEventType, updateContent);
186+
};
152187
}

test/stores/OwnBeaconStore-test.ts

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ limitations under the License.
1515
*/
1616

1717
import { Room, Beacon, BeaconEvent } from "matrix-js-sdk/src/matrix";
18+
import { M_BEACON_INFO } from "matrix-js-sdk/src/@types/beacon";
1819

1920
import { OwnBeaconStore, OwnBeaconStoreEvent } from "../../src/stores/OwnBeaconStore";
2021
import { resetAsyncStoreWithClient, setupAsyncStoreWithClient } from "../test-utils";
@@ -33,6 +34,7 @@ describe('OwnBeaconStore', () => {
3334
const mockClient = getMockClientWithEventEmitter({
3435
getUserId: jest.fn().mockReturnValue(aliceId),
3536
getVisibleRooms: jest.fn().mockReturnValue([]),
37+
unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: '1' }),
3638
});
3739
const room1Id = '$room1:server.org';
3840
const room2Id = '$room2:server.org';
@@ -78,6 +80,7 @@ describe('OwnBeaconStore', () => {
7880

7981
beforeEach(() => {
8082
mockClient.getVisibleRooms.mockReturnValue([]);
83+
mockClient.unstable_setLiveBeacon.mockClear().mockResolvedValue({ event_id: '1' });
8184
jest.spyOn(global.Date, 'now').mockReturnValue(now);
8285
jest.spyOn(OwnBeaconStore.instance, 'emit').mockRestore();
8386
});
@@ -335,7 +338,7 @@ describe('OwnBeaconStore', () => {
335338
expect(store.getLiveBeaconIds()).toBe(oldLiveBeaconIds);
336339
});
337340

338-
it('updates state and when beacon liveness changes from true to false', async () => {
341+
it('updates state and emits beacon liveness changes from true to false', async () => {
339342
makeRoomsWithStateEvents([
340343
alicesRoom1BeaconInfo,
341344
]);
@@ -356,6 +359,35 @@ describe('OwnBeaconStore', () => {
356359
expect(emitSpy).toHaveBeenCalledWith(OwnBeaconStoreEvent.LivenessChange, false);
357360
});
358361

362+
it('stops beacon when liveness changes from true to false and beacon is expired', async () => {
363+
makeRoomsWithStateEvents([
364+
alicesRoom1BeaconInfo,
365+
]);
366+
await makeOwnBeaconStore();
367+
const alicesBeacon = new Beacon(alicesRoom1BeaconInfo);
368+
const prevEventContent = alicesRoom1BeaconInfo.getContent();
369+
370+
// time travel until beacon is expired
371+
advanceDateAndTime(HOUR_MS * 3);
372+
373+
mockClient.emit(BeaconEvent.LivenessChange, false, alicesBeacon);
374+
375+
// matches original state of event content
376+
// except for live property
377+
const expectedUpdateContent = {
378+
...prevEventContent,
379+
[M_BEACON_INFO.name]: {
380+
...prevEventContent[M_BEACON_INFO.name],
381+
live: false,
382+
},
383+
};
384+
expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith(
385+
room1Id,
386+
alicesRoom1BeaconInfo.getType(),
387+
expectedUpdateContent,
388+
);
389+
});
390+
359391
it('updates state and when beacon liveness changes from false to true', async () => {
360392
makeRoomsWithStateEvents([
361393
alicesOldRoomIdBeaconInfo,
@@ -381,9 +413,75 @@ describe('OwnBeaconStore', () => {
381413
});
382414
});
383415

384-
describe('on LivenessChange event', () => {
385-
it('ignores events for irrelevant beacons', async () => {
416+
describe('stopBeacon()', () => {
417+
beforeEach(() => {
418+
makeRoomsWithStateEvents([
419+
alicesRoom1BeaconInfo,
420+
alicesOldRoomIdBeaconInfo,
421+
]);
422+
});
423+
424+
it('does nothing for an unknown beacon id', async () => {
425+
const store = await makeOwnBeaconStore();
426+
await store.stopBeacon('randomBeaconId');
427+
expect(mockClient.unstable_setLiveBeacon).not.toHaveBeenCalled();
428+
});
429+
430+
it('does nothing for a beacon that is already not live', async () => {
431+
const store = await makeOwnBeaconStore();
432+
await store.stopBeacon(alicesOldRoomIdBeaconInfo.getId());
433+
expect(mockClient.unstable_setLiveBeacon).not.toHaveBeenCalled();
434+
});
386435

436+
it('updates beacon to live:false when it is unexpired', async () => {
437+
const store = await makeOwnBeaconStore();
438+
439+
await store.stopBeacon(alicesOldRoomIdBeaconInfo.getId());
440+
const prevEventContent = alicesRoom1BeaconInfo.getContent();
441+
442+
await store.stopBeacon(alicesRoom1BeaconInfo.getId());
443+
444+
// matches original state of event content
445+
// except for live property
446+
const expectedUpdateContent = {
447+
...prevEventContent,
448+
[M_BEACON_INFO.name]: {
449+
...prevEventContent[M_BEACON_INFO.name],
450+
live: false,
451+
},
452+
};
453+
expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith(
454+
room1Id,
455+
alicesRoom1BeaconInfo.getType(),
456+
expectedUpdateContent,
457+
);
458+
});
459+
460+
it('updates beacon to live:false when it is expired but live property is true', async () => {
461+
const store = await makeOwnBeaconStore();
462+
463+
await store.stopBeacon(alicesOldRoomIdBeaconInfo.getId());
464+
const prevEventContent = alicesRoom1BeaconInfo.getContent();
465+
466+
// time travel until beacon is expired
467+
advanceDateAndTime(HOUR_MS * 3);
468+
469+
await store.stopBeacon(alicesRoom1BeaconInfo.getId());
470+
471+
// matches original state of event content
472+
// except for live property
473+
const expectedUpdateContent = {
474+
...prevEventContent,
475+
[M_BEACON_INFO.name]: {
476+
...prevEventContent[M_BEACON_INFO.name],
477+
live: false,
478+
},
479+
};
480+
expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith(
481+
room1Id,
482+
alicesRoom1BeaconInfo.getType(),
483+
expectedUpdateContent,
484+
);
387485
});
388486
});
389487
});

0 commit comments

Comments
 (0)