You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-07-30 04:23:07 +03:00
Live location sharing: handle encrypted messages in processBeaconEvents (#2327)
* handle encrypted locations Signed-off-by: Kerry Archibald <kerrya@element.io> * fix processBeaconEvents to handle encrypted events Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
@ -1047,7 +1047,7 @@ describe("MatrixClient", function() {
|
|||||||
expect(roomStateProcessSpy).not.toHaveBeenCalled();
|
expect(roomStateProcessSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls room states processBeaconEvents with m.beacon events', () => {
|
it('calls room states processBeaconEvents with events', () => {
|
||||||
const room = new Room(roomId, client, userId);
|
const room = new Room(roomId, client, userId);
|
||||||
const roomStateProcessSpy = jest.spyOn(room.currentState, 'processBeaconEvents');
|
const roomStateProcessSpy = jest.spyOn(room.currentState, 'processBeaconEvents');
|
||||||
|
|
||||||
@ -1055,7 +1055,7 @@ describe("MatrixClient", function() {
|
|||||||
const beaconEvent = makeBeaconEvent(userId);
|
const beaconEvent = makeBeaconEvent(userId);
|
||||||
|
|
||||||
client.processBeaconEvents(room, [messageEvent, beaconEvent]);
|
client.processBeaconEvents(room, [messageEvent, beaconEvent]);
|
||||||
expect(roomStateProcessSpy).toHaveBeenCalledWith([beaconEvent]);
|
expect(roomStateProcessSpy).toHaveBeenCalledWith([messageEvent, beaconEvent], client);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,12 @@ import { makeBeaconEvent, makeBeaconInfoEvent } from "../test-utils/beacon";
|
|||||||
import { filterEmitCallsByEventType } from "../test-utils/emitter";
|
import { filterEmitCallsByEventType } from "../test-utils/emitter";
|
||||||
import { RoomState, RoomStateEvent } from "../../src/models/room-state";
|
import { RoomState, RoomStateEvent } from "../../src/models/room-state";
|
||||||
import { BeaconEvent, getBeaconInfoIdentifier } from "../../src/models/beacon";
|
import { BeaconEvent, getBeaconInfoIdentifier } from "../../src/models/beacon";
|
||||||
|
import { EventType, RelationType } from "../../src/@types/event";
|
||||||
|
import {
|
||||||
|
MatrixEvent,
|
||||||
|
MatrixEventEvent,
|
||||||
|
} from "../../src/models/event";
|
||||||
|
import { M_BEACON } from "../../src/@types/beacon";
|
||||||
|
|
||||||
describe("RoomState", function() {
|
describe("RoomState", function() {
|
||||||
const roomId = "!foo:bar";
|
const roomId = "!foo:bar";
|
||||||
@ -717,52 +723,238 @@ describe("RoomState", function() {
|
|||||||
const beacon1 = makeBeaconInfoEvent(userA, roomId, {}, '$beacon1', '$beacon1');
|
const beacon1 = makeBeaconInfoEvent(userA, roomId, {}, '$beacon1', '$beacon1');
|
||||||
const beacon2 = makeBeaconInfoEvent(userB, roomId, {}, '$beacon2', '$beacon2');
|
const beacon2 = makeBeaconInfoEvent(userB, roomId, {}, '$beacon2', '$beacon2');
|
||||||
|
|
||||||
|
const mockClient = { decryptEventIfNeeded: jest.fn() };
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockClient.decryptEventIfNeeded.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
it('does nothing when state has no beacons', () => {
|
it('does nothing when state has no beacons', () => {
|
||||||
const emitSpy = jest.spyOn(state, 'emit');
|
const emitSpy = jest.spyOn(state, 'emit');
|
||||||
state.processBeaconEvents([makeBeaconEvent(userA, { beaconInfoId: '$beacon1' })]);
|
state.processBeaconEvents([makeBeaconEvent(userA, { beaconInfoId: '$beacon1' })], mockClient);
|
||||||
expect(emitSpy).not.toHaveBeenCalled();
|
expect(emitSpy).not.toHaveBeenCalled();
|
||||||
|
expect(mockClient.decryptEventIfNeeded).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does nothing when there are no events', () => {
|
it('does nothing when there are no events', () => {
|
||||||
state.setStateEvents([beacon1, beacon2]);
|
state.setStateEvents([beacon1, beacon2]);
|
||||||
const emitSpy = jest.spyOn(state, 'emit').mockClear();
|
const emitSpy = jest.spyOn(state, 'emit').mockClear();
|
||||||
state.processBeaconEvents([]);
|
state.processBeaconEvents([], mockClient);
|
||||||
expect(emitSpy).not.toHaveBeenCalled();
|
expect(emitSpy).not.toHaveBeenCalled();
|
||||||
|
expect(mockClient.decryptEventIfNeeded).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('discards events for beacons that are not in state', () => {
|
describe('without encryption', () => {
|
||||||
const location = makeBeaconEvent(userA, {
|
it('discards events for beacons that are not in state', () => {
|
||||||
beaconInfoId: 'some-other-beacon',
|
const location = makeBeaconEvent(userA, {
|
||||||
|
beaconInfoId: 'some-other-beacon',
|
||||||
|
});
|
||||||
|
const otherRelatedEvent = new MatrixEvent({
|
||||||
|
sender: userA,
|
||||||
|
type: EventType.RoomMessage,
|
||||||
|
content: {
|
||||||
|
['m.relates_to']: {
|
||||||
|
event_id: 'whatever',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
state.setStateEvents([beacon1, beacon2]);
|
||||||
|
const emitSpy = jest.spyOn(state, 'emit').mockClear();
|
||||||
|
state.processBeaconEvents([location, otherRelatedEvent], mockClient);
|
||||||
|
expect(emitSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('discards events that are not beacon type', () => {
|
||||||
|
// related to beacon1
|
||||||
|
const otherRelatedEvent = new MatrixEvent({
|
||||||
|
sender: userA,
|
||||||
|
type: EventType.RoomMessage,
|
||||||
|
content: {
|
||||||
|
['m.relates_to']: {
|
||||||
|
rel_type: RelationType.Reference,
|
||||||
|
event_id: beacon1.getId(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
state.setStateEvents([beacon1, beacon2]);
|
||||||
|
const emitSpy = jest.spyOn(state, 'emit').mockClear();
|
||||||
|
state.processBeaconEvents([otherRelatedEvent], mockClient);
|
||||||
|
expect(emitSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds locations to beacons', () => {
|
||||||
|
const location1 = makeBeaconEvent(userA, {
|
||||||
|
beaconInfoId: '$beacon1', timestamp: Date.now() + 1,
|
||||||
|
});
|
||||||
|
const location2 = makeBeaconEvent(userA, {
|
||||||
|
beaconInfoId: '$beacon1', timestamp: Date.now() + 2,
|
||||||
|
});
|
||||||
|
const location3 = makeBeaconEvent(userB, {
|
||||||
|
beaconInfoId: 'some-other-beacon',
|
||||||
|
});
|
||||||
|
|
||||||
|
state.setStateEvents([beacon1, beacon2], mockClient);
|
||||||
|
|
||||||
|
expect(state.beacons.size).toEqual(2);
|
||||||
|
|
||||||
|
const beaconInstance = state.beacons.get(getBeaconInfoIdentifier(beacon1));
|
||||||
|
const addLocationsSpy = jest.spyOn(beaconInstance, 'addLocations');
|
||||||
|
|
||||||
|
state.processBeaconEvents([location1, location2, location3], mockClient);
|
||||||
|
|
||||||
|
expect(addLocationsSpy).toHaveBeenCalledTimes(2);
|
||||||
|
// only called with locations for beacon1
|
||||||
|
expect(addLocationsSpy).toHaveBeenCalledWith([location1]);
|
||||||
|
expect(addLocationsSpy).toHaveBeenCalledWith([location2]);
|
||||||
});
|
});
|
||||||
state.setStateEvents([beacon1, beacon2]);
|
|
||||||
const emitSpy = jest.spyOn(state, 'emit').mockClear();
|
|
||||||
state.processBeaconEvents([location]);
|
|
||||||
expect(emitSpy).not.toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds locations to beacons', () => {
|
describe('with encryption', () => {
|
||||||
const location1 = makeBeaconEvent(userA, {
|
const beacon1RelationContent = { ['m.relates_to']: {
|
||||||
beaconInfoId: '$beacon1', timestamp: Date.now() + 1,
|
rel_type: RelationType.Reference,
|
||||||
|
event_id: beacon1.getId(),
|
||||||
|
} };
|
||||||
|
const relatedEncryptedEvent = new MatrixEvent({
|
||||||
|
sender: userA,
|
||||||
|
type: EventType.RoomMessageEncrypted,
|
||||||
|
content: beacon1RelationContent,
|
||||||
});
|
});
|
||||||
const location2 = makeBeaconEvent(userA, {
|
const decryptingRelatedEvent = new MatrixEvent({
|
||||||
beaconInfoId: '$beacon1', timestamp: Date.now() + 2,
|
sender: userA,
|
||||||
|
type: EventType.RoomMessageEncrypted,
|
||||||
|
content: beacon1RelationContent,
|
||||||
});
|
});
|
||||||
const location3 = makeBeaconEvent(userB, {
|
jest.spyOn(decryptingRelatedEvent, 'isBeingDecrypted').mockReturnValue(true);
|
||||||
beaconInfoId: 'some-other-beacon',
|
|
||||||
|
const failedDecryptionRelatedEvent = new MatrixEvent({
|
||||||
|
sender: userA,
|
||||||
|
type: EventType.RoomMessageEncrypted,
|
||||||
|
content: beacon1RelationContent,
|
||||||
|
});
|
||||||
|
jest.spyOn(failedDecryptionRelatedEvent, 'isDecryptionFailure').mockReturnValue(true);
|
||||||
|
|
||||||
|
it('discards events without relations', () => {
|
||||||
|
const unrelatedEvent = new MatrixEvent({
|
||||||
|
sender: userA,
|
||||||
|
type: EventType.RoomMessageEncrypted,
|
||||||
|
});
|
||||||
|
state.setStateEvents([beacon1, beacon2]);
|
||||||
|
const emitSpy = jest.spyOn(state, 'emit').mockClear();
|
||||||
|
state.processBeaconEvents([unrelatedEvent], mockClient);
|
||||||
|
expect(emitSpy).not.toHaveBeenCalled();
|
||||||
|
// discard unrelated events early
|
||||||
|
expect(mockClient.decryptEventIfNeeded).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
state.setStateEvents([beacon1, beacon2]);
|
it('discards events for beacons that are not in state', () => {
|
||||||
|
const location = makeBeaconEvent(userA, {
|
||||||
|
beaconInfoId: 'some-other-beacon',
|
||||||
|
});
|
||||||
|
const otherRelatedEvent = new MatrixEvent({
|
||||||
|
sender: userA,
|
||||||
|
type: EventType.RoomMessageEncrypted,
|
||||||
|
content: {
|
||||||
|
['m.relates_to']: {
|
||||||
|
rel_type: RelationType.Reference,
|
||||||
|
event_id: 'whatever',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
state.setStateEvents([beacon1, beacon2]);
|
||||||
|
|
||||||
expect(state.beacons.size).toEqual(2);
|
const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1));
|
||||||
|
const addLocationsSpy = jest.spyOn(beacon, 'addLocations').mockClear();
|
||||||
|
state.processBeaconEvents([location, otherRelatedEvent], mockClient);
|
||||||
|
expect(addLocationsSpy).not.toHaveBeenCalled();
|
||||||
|
// discard unrelated events early
|
||||||
|
expect(mockClient.decryptEventIfNeeded).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
const beaconInstance = state.beacons.get(getBeaconInfoIdentifier(beacon1));
|
it('decrypts related events if needed', () => {
|
||||||
const addLocationsSpy = jest.spyOn(beaconInstance, 'addLocations');
|
const location = makeBeaconEvent(userA, {
|
||||||
|
beaconInfoId: beacon1.getId(),
|
||||||
|
});
|
||||||
|
state.setStateEvents([beacon1, beacon2]);
|
||||||
|
state.processBeaconEvents([location, relatedEncryptedEvent], mockClient);
|
||||||
|
// discard unrelated events early
|
||||||
|
expect(mockClient.decryptEventIfNeeded).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
state.processBeaconEvents([location1, location2, location3]);
|
it('listens for decryption on events that are being decrypted', () => {
|
||||||
|
const decryptingRelatedEvent = new MatrixEvent({
|
||||||
|
sender: userA,
|
||||||
|
type: EventType.RoomMessageEncrypted,
|
||||||
|
content: beacon1RelationContent,
|
||||||
|
});
|
||||||
|
jest.spyOn(decryptingRelatedEvent, 'isBeingDecrypted').mockReturnValue(true);
|
||||||
|
// spy on event.once
|
||||||
|
const eventOnceSpy = jest.spyOn(decryptingRelatedEvent, 'once');
|
||||||
|
|
||||||
expect(addLocationsSpy).toHaveBeenCalledTimes(1);
|
state.setStateEvents([beacon1, beacon2]);
|
||||||
// only called with locations for beacon1
|
state.processBeaconEvents([decryptingRelatedEvent], mockClient);
|
||||||
expect(addLocationsSpy).toHaveBeenCalledWith([location1, location2]);
|
|
||||||
|
// listener was added
|
||||||
|
expect(eventOnceSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('listens for decryption on events that have decryption failure', () => {
|
||||||
|
const failedDecryptionRelatedEvent = new MatrixEvent({
|
||||||
|
sender: userA,
|
||||||
|
type: EventType.RoomMessageEncrypted,
|
||||||
|
content: beacon1RelationContent,
|
||||||
|
});
|
||||||
|
jest.spyOn(failedDecryptionRelatedEvent, 'isDecryptionFailure').mockReturnValue(true);
|
||||||
|
// spy on event.once
|
||||||
|
const eventOnceSpy = jest.spyOn(decryptingRelatedEvent, 'once');
|
||||||
|
|
||||||
|
state.setStateEvents([beacon1, beacon2]);
|
||||||
|
state.processBeaconEvents([decryptingRelatedEvent], mockClient);
|
||||||
|
|
||||||
|
// listener was added
|
||||||
|
expect(eventOnceSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('discard events that are not m.beacon type after decryption', () => {
|
||||||
|
const decryptingRelatedEvent = new MatrixEvent({
|
||||||
|
sender: userA,
|
||||||
|
type: EventType.RoomMessageEncrypted,
|
||||||
|
content: beacon1RelationContent,
|
||||||
|
});
|
||||||
|
jest.spyOn(decryptingRelatedEvent, 'isBeingDecrypted').mockReturnValue(true);
|
||||||
|
state.setStateEvents([beacon1, beacon2]);
|
||||||
|
const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1));
|
||||||
|
const addLocationsSpy = jest.spyOn(beacon, 'addLocations').mockClear();
|
||||||
|
state.processBeaconEvents([decryptingRelatedEvent], mockClient);
|
||||||
|
|
||||||
|
// this event is a message after decryption
|
||||||
|
decryptingRelatedEvent.type = EventType.RoomMessage;
|
||||||
|
decryptingRelatedEvent.emit(MatrixEventEvent.Decrypted);
|
||||||
|
|
||||||
|
expect(addLocationsSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds locations to beacons after decryption', () => {
|
||||||
|
const decryptingRelatedEvent = new MatrixEvent({
|
||||||
|
sender: userA,
|
||||||
|
type: EventType.RoomMessageEncrypted,
|
||||||
|
content: beacon1RelationContent,
|
||||||
|
});
|
||||||
|
const locationEvent = makeBeaconEvent(userA, {
|
||||||
|
beaconInfoId: '$beacon1', timestamp: Date.now() + 1,
|
||||||
|
});
|
||||||
|
jest.spyOn(decryptingRelatedEvent, 'isBeingDecrypted').mockReturnValue(true);
|
||||||
|
state.setStateEvents([beacon1, beacon2]);
|
||||||
|
const beacon = state.beacons.get(getBeaconInfoIdentifier(beacon1));
|
||||||
|
const addLocationsSpy = jest.spyOn(beacon, 'addLocations').mockClear();
|
||||||
|
state.processBeaconEvents([decryptingRelatedEvent], mockClient);
|
||||||
|
|
||||||
|
// update type after '''decryption'''
|
||||||
|
decryptingRelatedEvent.event.type = M_BEACON.name;
|
||||||
|
decryptingRelatedEvent.event.content = locationEvent.content;
|
||||||
|
decryptingRelatedEvent.emit(MatrixEventEvent.Decrypted);
|
||||||
|
|
||||||
|
expect(addLocationsSpy).toHaveBeenCalledWith([decryptingRelatedEvent]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -180,7 +180,7 @@ import { MediaHandler } from "./webrtc/mediaHandler";
|
|||||||
import { IRefreshTokenResponse } from "./@types/auth";
|
import { IRefreshTokenResponse } from "./@types/auth";
|
||||||
import { TypedEventEmitter } from "./models/typed-event-emitter";
|
import { TypedEventEmitter } from "./models/typed-event-emitter";
|
||||||
import { Thread, THREAD_RELATION_TYPE } from "./models/thread";
|
import { Thread, THREAD_RELATION_TYPE } from "./models/thread";
|
||||||
import { MBeaconInfoEventContent, M_BEACON, M_BEACON_INFO } from "./@types/beacon";
|
import { MBeaconInfoEventContent, M_BEACON_INFO } from "./@types/beacon";
|
||||||
|
|
||||||
export type Store = IStore;
|
export type Store = IStore;
|
||||||
export type SessionStore = WebStorageSessionStore;
|
export type SessionStore = WebStorageSessionStore;
|
||||||
@ -5192,7 +5192,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
|
|
||||||
const [timelineEvents, threadedEvents] = room.partitionThreadedEvents(matrixEvents);
|
const [timelineEvents, threadedEvents] = room.partitionThreadedEvents(matrixEvents);
|
||||||
|
|
||||||
this.processBeaconEvents(room, matrixEvents);
|
this.processBeaconEvents(room, timelineEvents);
|
||||||
room.addEventsToTimeline(timelineEvents, true, room.getLiveTimeline());
|
room.addEventsToTimeline(timelineEvents, true, room.getLiveTimeline());
|
||||||
await this.processThreadEvents(room, threadedEvents, true);
|
await this.processThreadEvents(room, threadedEvents, true);
|
||||||
|
|
||||||
@ -5335,7 +5335,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
timelineSet.addEventsToTimeline(timelineEvents, true, timeline, res.start);
|
timelineSet.addEventsToTimeline(timelineEvents, true, timeline, res.start);
|
||||||
// The target event is not in a thread but process the contextual events, so we can show any threads around it.
|
// The target event is not in a thread but process the contextual events, so we can show any threads around it.
|
||||||
await this.processThreadEvents(timelineSet.room, threadedEvents, true);
|
await this.processThreadEvents(timelineSet.room, threadedEvents, true);
|
||||||
this.processBeaconEvents(timelineSet.room, events);
|
this.processBeaconEvents(timelineSet.room, timelineEvents);
|
||||||
|
|
||||||
// There is no guarantee that the event ended up in "timeline" (we might have switched to a neighbouring
|
// There is no guarantee that the event ended up in "timeline" (we might have switched to a neighbouring
|
||||||
// timeline) - so check the room's index again. On the other hand, there's no guarantee the event ended up
|
// timeline) - so check the room's index again. On the other hand, there's no guarantee the event ended up
|
||||||
@ -5503,7 +5503,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
const timelineSet = eventTimeline.getTimelineSet();
|
const timelineSet = eventTimeline.getTimelineSet();
|
||||||
const [timelineEvents, threadedEvents] = timelineSet.room.partitionThreadedEvents(matrixEvents);
|
const [timelineEvents, threadedEvents] = timelineSet.room.partitionThreadedEvents(matrixEvents);
|
||||||
timelineSet.addEventsToTimeline(timelineEvents, backwards, eventTimeline, token);
|
timelineSet.addEventsToTimeline(timelineEvents, backwards, eventTimeline, token);
|
||||||
this.processBeaconEvents(timelineSet.room, matrixEvents);
|
this.processBeaconEvents(timelineSet.room, timelineEvents);
|
||||||
await this.processThreadEvents(room, threadedEvents, backwards);
|
await this.processThreadEvents(room, threadedEvents, backwards);
|
||||||
|
|
||||||
// if we've hit the end of the timeline, we need to stop trying to
|
// if we've hit the end of the timeline, we need to stop trying to
|
||||||
@ -8929,8 +8929,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
|
|||||||
if (!events?.length) {
|
if (!events?.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const beaconEvents = events.filter(event => M_BEACON.matches(event.getType()));
|
room.currentState.processBeaconEvents(events, this);
|
||||||
room.currentState.processBeaconEvents(beaconEvents);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,15 +22,14 @@ import { RoomMember } from "./room-member";
|
|||||||
import { logger } from '../logger';
|
import { logger } from '../logger';
|
||||||
import * as utils from "../utils";
|
import * as utils from "../utils";
|
||||||
import { EventType } from "../@types/event";
|
import { EventType } from "../@types/event";
|
||||||
import { MatrixEvent } from "./event";
|
import { MatrixEvent, MatrixEventEvent } from "./event";
|
||||||
import { MatrixClient } from "../client";
|
import { MatrixClient } from "../client";
|
||||||
import { GuestAccess, HistoryVisibility, IJoinRuleEventContent, JoinRule } from "../@types/partials";
|
import { GuestAccess, HistoryVisibility, IJoinRuleEventContent, JoinRule } from "../@types/partials";
|
||||||
import { TypedEventEmitter } from "./typed-event-emitter";
|
import { TypedEventEmitter } from "./typed-event-emitter";
|
||||||
import { Beacon, BeaconEvent, BeaconEventHandlerMap } from "./beacon";
|
import { Beacon, BeaconEvent, BeaconEventHandlerMap } from "./beacon";
|
||||||
import { TypedReEmitter } from "../ReEmitter";
|
import { TypedReEmitter } from "../ReEmitter";
|
||||||
import { M_BEACON_INFO } from "../@types/beacon";
|
import { M_BEACON, M_BEACON_INFO } from "../@types/beacon";
|
||||||
import { getBeaconInfoIdentifier } from "./beacon";
|
import { getBeaconInfoIdentifier, BeaconIdentifier } from "./beacon";
|
||||||
import { BeaconIdentifier } from "..";
|
|
||||||
|
|
||||||
// possible statuses for out-of-band member loading
|
// possible statuses for out-of-band member loading
|
||||||
enum OobStatus {
|
enum OobStatus {
|
||||||
@ -411,7 +410,7 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
|||||||
this.emit(RoomStateEvent.Update, this);
|
this.emit(RoomStateEvent.Update, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public processBeaconEvents(events: MatrixEvent[]): void {
|
public processBeaconEvents(events: MatrixEvent[], matrixClient: MatrixClient): void {
|
||||||
if (
|
if (
|
||||||
!events.length ||
|
!events.length ||
|
||||||
// discard locations if we have no beacons
|
// discard locations if we have no beacons
|
||||||
@ -420,24 +419,38 @@ export class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// names are confusing here
|
const beaconByEventIdDict: Record<string, Beacon> =
|
||||||
// a Beacon is the parent event, but event type is 'm.beacon_info'
|
[...this.beacons.values()].reduce((dict, beacon) => ({ ...dict, [beacon.beaconInfoId]: beacon }), {});
|
||||||
// a location is the 'child' related to the Beacon, but the event type is 'm.beacon'
|
|
||||||
// group locations by beaconInfo event id
|
|
||||||
const locationEventsByBeaconEventId = events.reduce<Record<string, MatrixEvent[]>>((acc, event) => {
|
|
||||||
const beaconInfoEventId = event.getRelation()?.event_id;
|
|
||||||
if (!acc[beaconInfoEventId]) {
|
|
||||||
acc[beaconInfoEventId] = [];
|
|
||||||
}
|
|
||||||
acc[beaconInfoEventId].push(event);
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
Object.entries(locationEventsByBeaconEventId).forEach(([beaconInfoEventId, events]) => {
|
const processBeaconRelation = (beaconInfoEventId: string, event: MatrixEvent): void => {
|
||||||
const beacon = [...this.beacons.values()].find(beacon => beacon.beaconInfoId === beaconInfoEventId);
|
if (!M_BEACON.matches(event.getType())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const beacon = beaconByEventIdDict[beaconInfoEventId];
|
||||||
|
|
||||||
if (beacon) {
|
if (beacon) {
|
||||||
beacon.addLocations(events);
|
beacon.addLocations([event]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
events.forEach((event: MatrixEvent) => {
|
||||||
|
const relatedToEventId = event.getRelation()?.event_id;
|
||||||
|
// not related to a beacon we know about
|
||||||
|
// discard
|
||||||
|
if (!beaconByEventIdDict[relatedToEventId]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
matrixClient.decryptEventIfNeeded(event);
|
||||||
|
|
||||||
|
if (event.isBeingDecrypted() || event.isDecryptionFailure()) {
|
||||||
|
// add an event listener for once the event is decrypted.
|
||||||
|
event.once(MatrixEventEvent.Decrypted, async () => {
|
||||||
|
processBeaconRelation(relatedToEventId, event);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
processBeaconRelation(relatedToEventId, event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user