Files
matrix-quotes-bot/node_modules/matrix-js-sdk/lib/models/relations-container.js
unfunny dfe4cc537f init
2026-09-13 00:29:11 -04:00

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