diff --git a/src/components/views/beacon/RoomLiveShareWarning.tsx b/src/components/views/beacon/RoomLiveShareWarning.tsx index d51c22f6449..0c1b67dc100 100644 --- a/src/components/views/beacon/RoomLiveShareWarning.tsx +++ b/src/components/views/beacon/RoomLiveShareWarning.tsx @@ -16,7 +16,12 @@ limitations under the License. import React, { useCallback, useEffect, useState } from 'react'; import classNames from 'classnames'; -import { Room, Beacon } from 'matrix-js-sdk/src/matrix'; +import { + Room, + Beacon, + BeaconEvent, + BeaconIdentifier, +} from 'matrix-js-sdk/src/matrix'; import { formatDuration } from '../../../DateUtils'; import { _t } from '../../../languageHandler'; @@ -45,16 +50,22 @@ const getUpdateInterval = (ms: number) => { return 1000; }; const useMsRemaining = (beacon: Beacon): number => { - const [msRemaining, setMsRemaining] = useState(() => getBeaconMsUntilExpiry(beacon)); + const beaconInfo = useEventEmitterState( + beacon, + BeaconEvent.Update, + () => beacon.beaconInfo, + ); + + const [msRemaining, setMsRemaining] = useState(() => getBeaconMsUntilExpiry(beaconInfo)); useEffect(() => { - setMsRemaining(getBeaconMsUntilExpiry(beacon)); - }, [beacon]); + setMsRemaining(getBeaconMsUntilExpiry(beaconInfo)); + }, [beaconInfo]); const updateMsRemaining = useCallback(() => { - const ms = getBeaconMsUntilExpiry(beacon); + const ms = getBeaconMsUntilExpiry(beaconInfo); setMsRemaining(ms); - }, [beacon]); + }, [beaconInfo]); useInterval(updateMsRemaining, getUpdateInterval(msRemaining)); @@ -74,7 +85,7 @@ type LiveBeaconsState = { hasStopSharingError?: boolean; hasWireError?: boolean; }; -const useLiveBeacons = (liveBeaconIds: string[], roomId: string): LiveBeaconsState => { +const useLiveBeacons = (liveBeaconIds: BeaconIdentifier[], roomId: string): LiveBeaconsState => { const [stoppingInProgress, setStoppingInProgress] = useState(false); const [error, setError] = useState(); diff --git a/src/components/views/location/shareLocation.ts b/src/components/views/location/shareLocation.ts index 8fa801e13a2..6654a389a06 100644 --- a/src/components/views/location/shareLocation.ts +++ b/src/components/views/location/shareLocation.ts @@ -78,8 +78,7 @@ export const shareLiveLocation = ( description, LocationAssetType.Self, ), - // use timestamp as unique suffix in interim - `${Date.now()}`); + ); } catch (error) { handleShareError(error, openMenu, LocationShareType.Live); } diff --git a/src/stores/OwnBeaconStore.ts b/src/stores/OwnBeaconStore.ts index 31ddd42b858..cca428f83ea 100644 --- a/src/stores/OwnBeaconStore.ts +++ b/src/stores/OwnBeaconStore.ts @@ -17,6 +17,7 @@ limitations under the License. import { debounce } from "lodash"; import { Beacon, + BeaconIdentifier, BeaconEvent, MatrixEvent, Room, @@ -58,22 +59,22 @@ const STATIC_UPDATE_INTERVAL = 30000; const BAIL_AFTER_CONSECUTIVE_ERROR_COUNT = 2; type OwnBeaconStoreState = { - beacons: Map; + beacons: Map; beaconWireErrors: Map; - beaconsByRoomId: Map>; - liveBeaconIds: string[]; + beaconsByRoomId: Map>; + liveBeaconIds: BeaconIdentifier[]; }; export class OwnBeaconStore extends AsyncStoreWithClient { private static internalInstance = new OwnBeaconStore(); // users beacons, keyed by event type - public readonly beacons = new Map(); - public readonly beaconsByRoomId = new Map>(); + public readonly beacons = new Map(); + public readonly beaconsByRoomId = new Map>(); /** * Track over the wire errors for published positions * Counts consecutive wire errors per beacon * Reset on successful publish of location */ - public readonly beaconWireErrorCounts = new Map(); + public readonly beaconWireErrorCounts = new Map(); /** * ids of live beacons * ordered by creation time descending @@ -108,6 +109,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient { protected async onNotReady() { this.matrixClient.removeListener(BeaconEvent.LivenessChange, this.onBeaconLiveness); this.matrixClient.removeListener(BeaconEvent.New, this.onNewBeacon); + this.matrixClient.removeListener(BeaconEvent.Update, this.onUpdateBeacon); this.matrixClient.removeListener(RoomStateEvent.Members, this.onRoomStateMembers); this.beacons.forEach(beacon => beacon.destroy()); @@ -122,6 +124,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient { protected async onReady(): Promise { this.matrixClient.on(BeaconEvent.LivenessChange, this.onBeaconLiveness); this.matrixClient.on(BeaconEvent.New, this.onNewBeacon); + this.matrixClient.removeListener(BeaconEvent.Update, this.onUpdateBeacon); this.matrixClient.on(RoomStateEvent.Members, this.onRoomStateMembers); this.initialiseBeaconState(); @@ -177,8 +180,8 @@ export class OwnBeaconStore extends AsyncStoreWithClient { return this.beacons.get(beaconId); }; - public stopBeacon = async (beaconInfoType: string): Promise => { - const beacon = this.beacons.get(beaconInfoType); + public stopBeacon = async (beaconIdentifier: string): Promise => { + const beacon = this.beacons.get(beaconIdentifier); // if no beacon, or beacon is already explicitly set isLive: false // do nothing if (!beacon?.beaconInfo?.live) { @@ -200,6 +203,17 @@ export class OwnBeaconStore extends AsyncStoreWithClient { this.checkLiveness(); }; + /** + * This will be called when a beacon is replaced + */ + private onUpdateBeacon = (_event: MatrixEvent, beacon: Beacon): void => { + if (!isOwnBeacon(beacon, this.matrixClient.getUserId())) { + return; + } + + this.checkLiveness(); + }; + private onBeaconLiveness = (isLive: boolean, beacon: Beacon): void => { // check if we care about this beacon if (!this.beacons.has(beacon.identifier)) { @@ -439,7 +453,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient { assetType, timestamp); - await this.matrixClient.unstable_setLiveBeacon(beacon.roomId, beacon.beaconInfoEventType, updateContent); + await this.matrixClient.unstable_setLiveBeacon(beacon.roomId, updateContent); }; /** diff --git a/src/utils/beacon/duration.ts b/src/utils/beacon/duration.ts index b8338a8536f..c49eef1bd55 100644 --- a/src/utils/beacon/duration.ts +++ b/src/utils/beacon/duration.ts @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { BeaconInfoState } from "matrix-js-sdk/src/content-helpers"; import { Beacon } from "matrix-js-sdk/src/matrix"; /** @@ -26,8 +27,8 @@ import { Beacon } from "matrix-js-sdk/src/matrix"; export const msUntilExpiry = (startTimestamp: number, durationMs: number): number => Math.max(0, (startTimestamp + durationMs) - Date.now()); -export const getBeaconMsUntilExpiry = (beacon: Beacon): number => - msUntilExpiry(beacon.beaconInfo.timestamp, beacon.beaconInfo.timeout); +export const getBeaconMsUntilExpiry = (beaconInfo: BeaconInfoState): number => + msUntilExpiry(beaconInfo.timestamp, beaconInfo.timeout); export const getBeaconExpiryTimestamp = (beacon: Beacon): number => beacon.beaconInfo.timestamp + beacon.beaconInfo.timeout; diff --git a/test/components/views/beacon/RoomLiveShareWarning-test.tsx b/test/components/views/beacon/RoomLiveShareWarning-test.tsx index 03f9784738a..2c022868811 100644 --- a/test/components/views/beacon/RoomLiveShareWarning-test.tsx +++ b/test/components/views/beacon/RoomLiveShareWarning-test.tsx @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import { act } from 'react-dom/test-utils'; import { mount } from 'enzyme'; -import { Room, Beacon, BeaconEvent } from 'matrix-js-sdk/src/matrix'; +import { Room, Beacon, BeaconEvent, getBeaconInfoIdentifier } from 'matrix-js-sdk/src/matrix'; import { logger } from 'matrix-js-sdk/src/logger'; import RoomLiveShareWarning from '../../../../src/components/views/beacon/RoomLiveShareWarning'; @@ -221,6 +221,25 @@ describe('', () => { expect(getExpiryText(component)).toEqual('35m left'); }); + it('updates beacon time left when beacon updates', () => { + const component = getComponent({ roomId: room1Id }); + expect(getExpiryText(component)).toEqual('1h left'); + + expect(getExpiryText(component)).toEqual('1h left'); + + act(() => { + const beacon = OwnBeaconStore.instance.getBeaconById(getBeaconInfoIdentifier(room1Beacon1)); + const room1Beacon1Update = makeBeaconInfoEvent(aliceId, room1Id, { + isLive: true, + timeout: 3 * HOUR_MS, + }, '$0'); + beacon.update(room1Beacon1Update); + }); + + // update to expiry of new beacon + expect(getExpiryText(component)).toEqual('3h left'); + }); + it('clears expiry time interval on unmount', () => { const clearIntervalSpy = jest.spyOn(global, 'clearInterval'); const component = getComponent({ roomId: room1Id }); @@ -242,7 +261,7 @@ describe('', () => { component.setProps({}); }); - expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledTimes(2); + expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalled(); expect(component.find('Spinner').length).toBeTruthy(); expect(findByTestId(component, 'room-live-share-primary-button').at(0).props().disabled).toBeTruthy(); }); @@ -314,7 +333,7 @@ describe('', () => { // update mock and emit event act(() => { hasWireErrorsSpy.mockReturnValue(true); - OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.WireError, room2Beacon1.getType()); + OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.WireError, getBeaconInfoIdentifier(room2Beacon1)); }); component.setProps({}); @@ -332,7 +351,7 @@ describe('', () => { // update mock and emit event act(() => { hasWireErrorsSpy.mockReturnValue(false); - OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.WireError, room2Beacon1.getType()); + OwnBeaconStore.instance.emit(OwnBeaconStoreEvent.WireError, getBeaconInfoIdentifier(room2Beacon1)); }); component.setProps({}); @@ -353,8 +372,7 @@ describe('', () => { findByTestId(component, 'room-live-share-primary-button').at(0).simulate('click'); }); - expect(resetErrorSpy).toHaveBeenCalledWith(room2Beacon1.getType()); - expect(resetErrorSpy).toHaveBeenCalledWith(room2Beacon2.getType()); + expect(resetErrorSpy).toHaveBeenCalledWith(getBeaconInfoIdentifier(room2Beacon1)); }); it('clicking close button stops beacons', async () => { @@ -367,8 +385,7 @@ describe('', () => { findByTestId(component, 'room-live-share-wire-error-close-button').at(0).simulate('click'); }); - expect(stopBeaconSpy).toHaveBeenCalledWith(room2Beacon1.getType()); - expect(stopBeaconSpy).toHaveBeenCalledWith(room2Beacon2.getType()); + expect(stopBeaconSpy).toHaveBeenCalledWith(getBeaconInfoIdentifier(room2Beacon1)); }); }); }); diff --git a/test/components/views/beacon/__snapshots__/RoomLiveShareWarning-test.tsx.snap b/test/components/views/beacon/__snapshots__/RoomLiveShareWarning-test.tsx.snap index 8ae076a2a14..65fcd271378 100644 --- a/test/components/views/beacon/__snapshots__/RoomLiveShareWarning-test.tsx.snap +++ b/test/components/views/beacon/__snapshots__/RoomLiveShareWarning-test.tsx.snap @@ -13,8 +13,7 @@ exports[` when user has live beacons and geolocation is ', () => { }); expect(onFinished).toHaveBeenCalled(); - const [eventRoomId, eventContent, eventTypeSuffix] = mockClient.unstable_createLiveBeacon.mock.calls[0]; + const [eventRoomId, eventContent] = mockClient.unstable_createLiveBeacon.mock.calls[0]; expect(eventRoomId).toEqual(defaultProps.roomId); - expect(eventTypeSuffix).toBeTruthy(); expect(eventContent).toEqual(expect.objectContaining({ - [M_BEACON_INFO.name]: { - // default timeout - timeout: DEFAULT_DURATION_MS, - description: `Ernie's live location`, - live: true, - }, + // default timeout + timeout: DEFAULT_DURATION_MS, + description: `Ernie's live location`, + live: true, [M_ASSET.name]: { type: LocationAssetType.Self, }, diff --git a/test/stores/OwnBeaconStore-test.ts b/test/stores/OwnBeaconStore-test.ts index 57e66d636b2..6a95c82ad07 100644 --- a/test/stores/OwnBeaconStore-test.ts +++ b/test/stores/OwnBeaconStore-test.ts @@ -18,12 +18,13 @@ import { Room, Beacon, BeaconEvent, + getBeaconInfoIdentifier, MatrixEvent, RoomStateEvent, RoomMember, } from "matrix-js-sdk/src/matrix"; import { makeBeaconContent } from "matrix-js-sdk/src/content-helpers"; -import { M_BEACON, M_BEACON_INFO } from "matrix-js-sdk/src/@types/beacon"; +import { M_BEACON } from "matrix-js-sdk/src/@types/beacon"; import { logger } from "matrix-js-sdk/src/logger"; import { OwnBeaconStore, OwnBeaconStoreEvent } from "../../src/stores/OwnBeaconStore"; @@ -80,32 +81,27 @@ describe('OwnBeaconStore', () => { const alicesRoom1BeaconInfo = makeBeaconInfoEvent(aliceId, room1Id, { isLive: true }, - '$alice-room1-1' - , '$alice-room1-1', + '$alice-room1-1', ); const alicesRoom2BeaconInfo = makeBeaconInfoEvent(aliceId, room2Id, { isLive: true }, - '$alice-room2-1' - , '$alice-room2-1', + '$alice-room2-1', ); const alicesOldRoomIdBeaconInfo = makeBeaconInfoEvent(aliceId, room1Id, { isLive: false }, - '$alice-room1-2' - , '$alice-room1-2', + '$alice-room1-2', ); const bobsRoom1BeaconInfo = makeBeaconInfoEvent(bobId, room1Id, { isLive: true }, - '$bob-room1-1' - , '$bob-room1-1', + '$bob-room1-1', ); const bobsOldRoom1BeaconInfo = makeBeaconInfoEvent(bobId, room1Id, { isLive: false }, - '$bob-room1-2' - , '$bob-room1-2', + '$bob-room1-2', ); // make fresh rooms every time @@ -129,7 +125,7 @@ describe('OwnBeaconStore', () => { }; const expireBeaconAndEmit = (store, beaconInfoEvent: MatrixEvent): void => { - const beacon = store.getBeaconById(beaconInfoEvent.getType()); + const beacon = store.getBeaconById(getBeaconInfoIdentifier(beaconInfoEvent)); // time travel until beacon is expired advanceDateAndTime(beacon.beaconInfo.timeout + 100); @@ -141,16 +137,14 @@ describe('OwnBeaconStore', () => { }; const updateBeaconLivenessAndEmit = (store, beaconInfoEvent: MatrixEvent, isLive: boolean): void => { - const beacon = store.getBeaconById(beaconInfoEvent.getType()); + const beacon = store.getBeaconById(getBeaconInfoIdentifier(beaconInfoEvent)); // matches original state of event content // except for live property const updateEvent = makeBeaconInfoEvent( beaconInfoEvent.getSender(), beaconInfoEvent.getRoomId(), { isLive, timeout: beacon.beaconInfo.timeout }, - undefined, ); - updateEvent.event.type = beaconInfoEvent.getType(); beacon.update(updateEvent); mockClient.emit(BeaconEvent.Update, beaconInfoEvent, beacon); @@ -197,15 +191,14 @@ describe('OwnBeaconStore', () => { makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, alicesRoom2BeaconInfo, - alicesOldRoomIdBeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo, ]); const store = await makeOwnBeaconStore(); expect(store.hasLiveBeacons()).toBe(true); expect(store.getLiveBeaconIds()).toEqual([ - alicesRoom1BeaconInfo.getType(), - alicesRoom2BeaconInfo.getType(), + getBeaconInfoIdentifier(alicesRoom1BeaconInfo), + getBeaconInfoIdentifier(alicesRoom2BeaconInfo), ]); }); @@ -251,7 +244,8 @@ describe('OwnBeaconStore', () => { expect(removeSpy.mock.calls[0]).toEqual(expect.arrayContaining([BeaconEvent.LivenessChange])); expect(removeSpy.mock.calls[1]).toEqual(expect.arrayContaining([BeaconEvent.New])); - expect(removeSpy.mock.calls[2]).toEqual(expect.arrayContaining([RoomStateEvent.Members])); + expect(removeSpy.mock.calls[2]).toEqual(expect.arrayContaining([BeaconEvent.Update])); + expect(removeSpy.mock.calls[3]).toEqual(expect.arrayContaining([RoomStateEvent.Members])); }); it('destroys beacons', async () => { @@ -259,7 +253,7 @@ describe('OwnBeaconStore', () => { alicesRoom1BeaconInfo, ]); const store = await makeOwnBeaconStore(); - const beacon = room1.currentState.beacons.get(alicesRoom1BeaconInfo.getType()); + const beacon = room1.currentState.beacons.get(getBeaconInfoIdentifier(alicesRoom1BeaconInfo)); const destroySpy = jest.spyOn(beacon, 'destroy'); // @ts-ignore store.onNotReady(); @@ -273,7 +267,6 @@ describe('OwnBeaconStore', () => { makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, alicesRoom2BeaconInfo, - alicesOldRoomIdBeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo, ]); @@ -282,7 +275,6 @@ describe('OwnBeaconStore', () => { it('returns true when user has live beacons', async () => { makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, - alicesOldRoomIdBeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo, ]); @@ -302,7 +294,6 @@ describe('OwnBeaconStore', () => { it('returns true when user has live beacons for roomId', async () => { makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, - alicesOldRoomIdBeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo, ]); @@ -313,7 +304,6 @@ describe('OwnBeaconStore', () => { it('returns false when user does not have live beacons for roomId', async () => { makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, - alicesOldRoomIdBeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo, ]); @@ -327,7 +317,6 @@ describe('OwnBeaconStore', () => { makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, alicesRoom2BeaconInfo, - alicesOldRoomIdBeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo, ]); @@ -336,13 +325,12 @@ describe('OwnBeaconStore', () => { it('returns live beacons when user has live beacons', async () => { makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, - alicesOldRoomIdBeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo, ]); const store = await makeOwnBeaconStore(); expect(store.getLiveBeaconIds()).toEqual([ - alicesRoom1BeaconInfo.getType(), + getBeaconInfoIdentifier(alicesRoom1BeaconInfo), ]); }); @@ -359,23 +347,21 @@ describe('OwnBeaconStore', () => { makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, alicesRoom2BeaconInfo, - alicesOldRoomIdBeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo, ]); const store = await makeOwnBeaconStore(); expect(store.getLiveBeaconIds(room1Id)).toEqual([ - alicesRoom1BeaconInfo.getType(), + getBeaconInfoIdentifier(alicesRoom1BeaconInfo), ]); expect(store.getLiveBeaconIds(room2Id)).toEqual([ - alicesRoom2BeaconInfo.getType(), + getBeaconInfoIdentifier(alicesRoom2BeaconInfo), ]); }); it('returns empty array when user does not have live beacons for roomId', async () => { makeRoomsWithStateEvents([ alicesRoom1BeaconInfo, - alicesOldRoomIdBeaconInfo, bobsRoom1BeaconInfo, bobsOldRoom1BeaconInfo, ]); @@ -419,7 +405,7 @@ describe('OwnBeaconStore', () => { mockClient.emit(BeaconEvent.New, alicesRoom1BeaconInfo, alicesLiveBeacon); - expect(emitSpy).toHaveBeenCalledWith(OwnBeaconStoreEvent.LivenessChange, [alicesRoom1BeaconInfo.getType()]); + expect(emitSpy).toHaveBeenCalledWith(OwnBeaconStoreEvent.LivenessChange, [alicesLiveBeacon.identifier]); }); it('emits a liveness change event when new beacons do not change live state', async () => { @@ -485,14 +471,10 @@ describe('OwnBeaconStore', () => { // except for live property const expectedUpdateContent = { ...prevEventContent, - [M_BEACON_INFO.name]: { - ...prevEventContent[M_BEACON_INFO.name], - live: false, - }, + live: false, }; expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith( room1Id, - alicesRoom1BeaconInfo.getType(), expectedUpdateContent, ); }); @@ -513,7 +495,7 @@ describe('OwnBeaconStore', () => { expect(store.hasLiveBeacons(room1Id)).toBe(true); expect(emitSpy).toHaveBeenCalledWith( OwnBeaconStoreEvent.LivenessChange, - [alicesOldRoomIdBeaconInfo.getType()], + [getBeaconInfoIdentifier(alicesOldRoomIdBeaconInfo)], ); }); }); @@ -603,7 +585,7 @@ describe('OwnBeaconStore', () => { alicesRoom2BeaconInfo, ]); const store = await makeOwnBeaconStore(); - const room1BeaconInstance = store.beacons.get(alicesRoom1BeaconInfo.getType()); + const room1BeaconInstance = store.beacons.get(getBeaconInfoIdentifier(alicesRoom1BeaconInfo)); const beaconDestroySpy = jest.spyOn(room1BeaconInstance, 'destroy'); const emitSpy = jest.spyOn(store, 'emit'); @@ -617,7 +599,7 @@ describe('OwnBeaconStore', () => { expect(emitSpy).toHaveBeenCalledWith( OwnBeaconStoreEvent.LivenessChange, // other rooms beacons still live - [alicesRoom2BeaconInfo.getType()], + [getBeaconInfoIdentifier(alicesRoom2BeaconInfo)], ); expect(beaconDestroySpy).toHaveBeenCalledTimes(1); expect(store.getLiveBeaconIds(room1Id)).toEqual([]); @@ -640,57 +622,53 @@ describe('OwnBeaconStore', () => { it('does nothing for a beacon that is already not live', async () => { const store = await makeOwnBeaconStore(); - await store.stopBeacon(alicesOldRoomIdBeaconInfo.getId()); + await store.stopBeacon(getBeaconInfoIdentifier(alicesOldRoomIdBeaconInfo)); expect(mockClient.unstable_setLiveBeacon).not.toHaveBeenCalled(); }); it('updates beacon to live:false when it is unexpired', async () => { + makeRoomsWithStateEvents([ + alicesRoom1BeaconInfo, + ]); const store = await makeOwnBeaconStore(); - await store.stopBeacon(alicesOldRoomIdBeaconInfo.getType()); const prevEventContent = alicesRoom1BeaconInfo.getContent(); - await store.stopBeacon(alicesRoom1BeaconInfo.getType()); + await store.stopBeacon(getBeaconInfoIdentifier(alicesRoom1BeaconInfo)); // matches original state of event content // except for live property const expectedUpdateContent = { ...prevEventContent, - [M_BEACON_INFO.name]: { - ...prevEventContent[M_BEACON_INFO.name], - live: false, - }, + live: false, }; expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith( room1Id, - alicesRoom1BeaconInfo.getType(), expectedUpdateContent, ); }); it('updates beacon to live:false when it is expired but live property is true', async () => { + makeRoomsWithStateEvents([ + alicesRoom1BeaconInfo, + ]); const store = await makeOwnBeaconStore(); - await store.stopBeacon(alicesOldRoomIdBeaconInfo.getType()); const prevEventContent = alicesRoom1BeaconInfo.getContent(); // time travel until beacon is expired advanceDateAndTime(HOUR_MS * 3); - await store.stopBeacon(alicesRoom1BeaconInfo.getType()); + await store.stopBeacon(getBeaconInfoIdentifier(alicesRoom1BeaconInfo)); // matches original state of event content // except for live property const expectedUpdateContent = { ...prevEventContent, - [M_BEACON_INFO.name]: { - ...prevEventContent[M_BEACON_INFO.name], - live: false, - }, + live: false, }; expect(mockClient.unstable_setLiveBeacon).toHaveBeenCalledWith( room1Id, - alicesRoom1BeaconInfo.getType(), expectedUpdateContent, ); }); @@ -863,7 +841,7 @@ describe('OwnBeaconStore', () => { // called for each position from watchPosition expect(mockClient.sendEvent).toHaveBeenCalledTimes(5); - expect(store.beaconHasWireError(alicesRoom1BeaconInfo.getType())).toBe(false); + expect(store.beaconHasWireError(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))).toBe(false); expect(store.hasWireErrors()).toBe(false); }); @@ -890,10 +868,10 @@ describe('OwnBeaconStore', () => { // called for each position from watchPosition expect(mockClient.sendEvent).toHaveBeenCalledTimes(5); - expect(store.beaconHasWireError(alicesRoom1BeaconInfo.getType())).toBe(false); + expect(store.beaconHasWireError(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))).toBe(false); expect(store.hasWireErrors()).toBe(false); expect(emitSpy).not.toHaveBeenCalledWith( - OwnBeaconStoreEvent.WireError, alicesRoom1BeaconInfo.getType(), + OwnBeaconStoreEvent.WireError, getBeaconInfoIdentifier(alicesRoom1BeaconInfo), ); }); @@ -913,10 +891,10 @@ describe('OwnBeaconStore', () => { // only two allowed failures expect(mockClient.sendEvent).toHaveBeenCalledTimes(2); - expect(store.beaconHasWireError(alicesRoom1BeaconInfo.getType())).toBe(true); + expect(store.beaconHasWireError(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))).toBe(true); expect(store.hasWireErrors()).toBe(true); expect(emitSpy).toHaveBeenCalledWith( - OwnBeaconStoreEvent.WireError, alicesRoom1BeaconInfo.getType(), + OwnBeaconStoreEvent.WireError, getBeaconInfoIdentifier(alicesRoom1BeaconInfo), ); }); @@ -936,18 +914,18 @@ describe('OwnBeaconStore', () => { // only two allowed failures expect(mockClient.sendEvent).toHaveBeenCalledTimes(2); - expect(store.beaconHasWireError(alicesRoom1BeaconInfo.getType())).toBe(true); + expect(store.beaconHasWireError(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))).toBe(true); expect(store.hasWireErrors()).toBe(true); expect(store.hasWireErrors(room1Id)).toBe(true); expect(emitSpy).toHaveBeenCalledWith( - OwnBeaconStoreEvent.WireError, alicesRoom1BeaconInfo.getType(), + OwnBeaconStoreEvent.WireError, getBeaconInfoIdentifier(alicesRoom1BeaconInfo), ); // reset emitSpy mock counts to asser on wireError again emitSpy.mockClear(); - store.resetWireError(alicesRoom1BeaconInfo.getType()); + store.resetWireError(getBeaconInfoIdentifier(alicesRoom1BeaconInfo)); - expect(store.beaconHasWireError(alicesRoom1BeaconInfo.getType())).toBe(false); + expect(store.beaconHasWireError(getBeaconInfoIdentifier(alicesRoom1BeaconInfo))).toBe(false); // 2 more positions from watchPosition in this period await advanceAndFlushPromises(10000); @@ -955,7 +933,7 @@ describe('OwnBeaconStore', () => { // 2 from before, 2 new ones expect(mockClient.sendEvent).toHaveBeenCalledTimes(4); expect(emitSpy).toHaveBeenCalledWith( - OwnBeaconStoreEvent.WireError, alicesRoom1BeaconInfo.getType(), + OwnBeaconStoreEvent.WireError, getBeaconInfoIdentifier(alicesRoom1BeaconInfo), ); }); }); diff --git a/test/test-utils/beacon.ts b/test/test-utils/beacon.ts index e985305ed45..19764a2600b 100644 --- a/test/test-utils/beacon.ts +++ b/test/test-utils/beacon.ts @@ -33,8 +33,6 @@ const DEFAULT_INFO_CONTENT_PROPS: InfoContentProps = { timeout: 3600000, }; -let count = 1; - /** * Create an m.beacon_info event * all required properties are mocked @@ -45,7 +43,6 @@ export const makeBeaconInfoEvent = ( roomId: string, contentProps: Partial = {}, eventId?: string, - eventTypeSuffix?: string, ): MatrixEvent => { const { timeout, @@ -58,12 +55,15 @@ export const makeBeaconInfoEvent = ( ...contentProps, }; const event = new MatrixEvent({ - type: `${M_BEACON_INFO.name}.${sender}.${eventTypeSuffix || ++count}`, + type: M_BEACON_INFO.name, room_id: roomId, state_key: sender, + sender, content: makeBeaconInfoContent(timeout, isLive, description, assetType, timestamp), }); + event.event.origin_server_ts = Date.now(); + // live beacons use the beacon_info event id // set or default this event.replaceLocalEventId(eventId || `$${Math.random()}-${Math.random()}`);