diff --git a/src/stores/OwnBeaconStore.ts b/src/stores/OwnBeaconStore.ts index 1dbf8668fc..c0c442aaaf 100644 --- a/src/stores/OwnBeaconStore.ts +++ b/src/stores/OwnBeaconStore.ts @@ -106,7 +106,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient { * ids of live beacons * ordered by creation time descending */ - private liveBeaconIds = []; + private liveBeaconIds: BeaconIdentifier[] = []; private locationInterval: number; private geolocationError: GeolocationError | undefined; private clearPositionWatch: ClearWatchCallback | undefined; @@ -392,6 +392,12 @@ export class OwnBeaconStore extends AsyncStoreWithClient { roomId: Room['roomId'], beaconInfoContent: MBeaconInfoEventContent, ): Promise => { + // explicitly stop any live beacons this user has + // to ensure they remain stopped + // if the new replacing beacon is redacted + const existingLiveBeaconIdsForRoom = this.getLiveBeaconIds(roomId); + await Promise.all(existingLiveBeaconIdsForRoom.map(beaconId => this.stopBeacon(beaconId))); + // eslint-disable-next-line camelcase const { event_id } = await this.matrixClient.unstable_createLiveBeacon( roomId, diff --git a/test/components/views/location/LocationShareMenu-test.tsx b/test/components/views/location/LocationShareMenu-test.tsx index c7820bc765..7e41f3f149 100644 --- a/test/components/views/location/LocationShareMenu-test.tsx +++ b/test/components/views/location/LocationShareMenu-test.tsx @@ -78,6 +78,7 @@ describe('', () => { }), sendMessage: jest.fn(), unstable_createLiveBeacon: jest.fn().mockResolvedValue({ event_id: '1' }), + unstable_setLiveBeacon: jest.fn().mockResolvedValue({ event_id: '1' }), getVisibleRooms: jest.fn().mockReturnValue([]), }); @@ -386,7 +387,7 @@ describe('', () => { expect(getShareTypeOption(component, LocationShareType.Live).length).toBeFalsy(); }); - it('creates beacon info event on submission', () => { + it('creates beacon info event on submission', async () => { const onFinished = jest.fn(); const component = getComponent({ onFinished }); @@ -399,6 +400,9 @@ describe('', () => { component.setProps({}); }); + // flush stopping existing beacons promises + await flushPromisesWithFakeTimers(); + expect(onFinished).toHaveBeenCalled(); const [eventRoomId, eventContent] = mockClient.unstable_createLiveBeacon.mock.calls[0]; expect(eventRoomId).toEqual(defaultProps.roomId); @@ -429,6 +433,7 @@ describe('', () => { component.setProps({}); }); + await flushPromisesWithFakeTimers(); await flushPromisesWithFakeTimers(); await flushPromisesWithFakeTimers(); diff --git a/test/stores/OwnBeaconStore-test.ts b/test/stores/OwnBeaconStore-test.ts index 66204580fc..30bc2be5fa 100644 --- a/test/stores/OwnBeaconStore-test.ts +++ b/test/stores/OwnBeaconStore-test.ts @@ -1347,5 +1347,29 @@ describe('OwnBeaconStore', () => { // didn't throw, no error log expect(loggerErrorSpy).not.toHaveBeenCalled(); }); + + it('stops existing live beacon for room before creates new beacon', async () => { + // room1 already has a live beacon for alice + makeRoomsWithStateEvents([ + alicesRoom1BeaconInfo, + alicesRoom2BeaconInfo, + ]); + const store = await makeOwnBeaconStore(); + + const content = makeBeaconInfoContent(100); + await store.createLiveBeacon(room1Id, content); + + // stop alicesRoom1BeaconInfo + expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith( + room1Id, expect.objectContaining({ live: false }), + ); + // only called for beacons in room1, room2 beacon is not stopped + expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledTimes(1); + + // new beacon created + expect(mockClient.unstable_createLiveBeacon).toHaveBeenCalledWith( + room1Id, content, + ); + }); }); });