151 lines
6.2 KiB
JavaScript
151 lines
6.2 KiB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
/*
|
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
import { Relations } from "./relations.js";
|
|
import { EventStatus, MatrixEventEvent } from "./event.js";
|
|
import { logger } from "../logger.js";
|
|
export class RelationsContainer {
|
|
constructor(client, room) {
|
|
// A tree of objects to access a set of related children for an event, as in:
|
|
// this.relations.get(parentEventId).get(relationType).get(relationEventType)
|
|
_defineProperty(this, "relations", new Map());
|
|
this.client = client;
|
|
this.room = room;
|
|
}
|
|
|
|
/**
|
|
* Get a collection of child events to a given event in this timeline set.
|
|
*
|
|
* @param eventId - The ID of the event that you'd like to access child events for.
|
|
* For example, with annotations, this would be the ID of the event being annotated.
|
|
* @param relationType - The type of relationship involved, such as "m.annotation", "m.reference", "m.replace", etc.
|
|
* @param eventType - The relation event's type, such as "m.reaction", etc.
|
|
* @throws If `eventId</code>, <code>relationType</code> or <code>eventType`
|
|
* are not valid.
|
|
*
|
|
* @returns
|
|
* A container for relation events or undefined if there are no relation events for
|
|
* the relationType.
|
|
*/
|
|
getChildEventsForEvent(eventId, relationType, eventType) {
|
|
return this.relations.get(eventId)?.get(relationType)?.get(eventType);
|
|
}
|
|
getAllChildEventsForEvent(parentEventId) {
|
|
const relationsForEvent = this.relations.get(parentEventId) ?? new Map();
|
|
const events = [];
|
|
for (const relationsRecord of relationsForEvent.values()) {
|
|
for (const relations of relationsRecord.values()) {
|
|
events.push(...relations.getRelations());
|
|
}
|
|
}
|
|
return events;
|
|
}
|
|
|
|
/**
|
|
* Set an event as the target event if any Relations exist for it already.
|
|
* Child events can point to other child events as their parent, so this method may be
|
|
* called for events which are also logically child events.
|
|
*
|
|
* @param event - The event to check as relation target.
|
|
*/
|
|
aggregateParentEvent(event) {
|
|
const relationsForEvent = this.relations.get(event.getId());
|
|
if (!relationsForEvent) return;
|
|
for (const relationsWithRelType of relationsForEvent.values()) {
|
|
for (const relationsWithEventType of relationsWithRelType.values()) {
|
|
relationsWithEventType.setTargetEvent(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add relation events to the relevant relation collection.
|
|
*
|
|
* @param event - The new child event to be aggregated.
|
|
* @param timelineSet - The event timeline set within which to search for the related event if any.
|
|
*/
|
|
|
|
/**
|
|
* Add a relation event using a known target that is not in the timeline yet.
|
|
*
|
|
* @param event - The new child event to be aggregated.
|
|
* @param timelineSet - The event timeline set within which to search for the related event.
|
|
* @param targetEvent - The known relation target.
|
|
* @returns A promise which resolves after aggregation.
|
|
*/
|
|
|
|
aggregateChildEvent(event, timelineSet, targetEvent) {
|
|
const aggregation = this.aggregateChildEventInternal(event, timelineSet, targetEvent);
|
|
if (targetEvent) return aggregation;
|
|
void aggregation.catch(error => logger.error("Failed to aggregate child event: ", error));
|
|
}
|
|
aggregateChildEventInternal(event, timelineSet, targetEvent) {
|
|
if (event.isRedacted() || event.status === EventStatus.CANCELLED) {
|
|
return Promise.resolve();
|
|
}
|
|
const relation = event.getRelation();
|
|
if (!relation) return Promise.resolve();
|
|
|
|
// If the event is currently encrypted, wait until it has been decrypted.
|
|
if (event.isBeingDecrypted() || event.shouldAttemptDecryption()) {
|
|
return new Promise((resolve, reject) => {
|
|
const onEventDecrypted = () => {
|
|
if (event.isDecryptionFailure()) {
|
|
// This could for example happen if the encryption keys are not yet available.
|
|
// The event may still be decrypted later. Register the listener again.
|
|
event.once(MatrixEventEvent.Decrypted, onEventDecrypted);
|
|
return;
|
|
}
|
|
void this.aggregateChildEventInternal(event, timelineSet, targetEvent).then(resolve, reject);
|
|
};
|
|
event.once(MatrixEventEvent.Decrypted, onEventDecrypted);
|
|
});
|
|
}
|
|
const {
|
|
event_id: relatesToEventId,
|
|
rel_type: relationType
|
|
} = relation;
|
|
const eventType = event.getType();
|
|
let relationsForEvent = this.relations.get(relatesToEventId);
|
|
if (!relationsForEvent) {
|
|
relationsForEvent = new Map();
|
|
this.relations.set(relatesToEventId, relationsForEvent);
|
|
}
|
|
let relationsWithRelType = relationsForEvent.get(relationType);
|
|
if (!relationsWithRelType) {
|
|
relationsWithRelType = new Map();
|
|
relationsForEvent.set(relationType, relationsWithRelType);
|
|
}
|
|
let relationsWithEventType = relationsWithRelType.get(eventType);
|
|
const isNewRelationCollection = !relationsWithEventType;
|
|
let targetEventUpdate = Promise.resolve();
|
|
if (!relationsWithEventType) {
|
|
relationsWithEventType = new Relations(relationType, eventType, this.client);
|
|
relationsWithRelType.set(eventType, relationsWithEventType);
|
|
}
|
|
if (isNewRelationCollection || targetEvent) {
|
|
const room = this.room ?? timelineSet?.room;
|
|
const relatesToEvent = timelineSet?.findEventById(relatesToEventId) ?? room?.findEventById(relatesToEventId) ?? room?.getPendingEvent(relatesToEventId) ?? targetEvent;
|
|
if (relatesToEvent) {
|
|
targetEventUpdate = relationsWithEventType.setTargetEvent(relatesToEvent);
|
|
}
|
|
}
|
|
const addEventUpdate = relationsWithEventType.addEvent(event);
|
|
return Promise.all([targetEventUpdate, addEventUpdate]).then(() => undefined);
|
|
}
|
|
}
|
|
//# sourceMappingURL=relations-container.js.map
|