feat: complete Ancestor quote bot with production-ready Docker support

This commit is contained in:
unfunny
2026-09-13 14:14:38 -04:00
parent f2016da05a
commit fe7351a7dc
1145 changed files with 30890 additions and 97684 deletions

View File

@@ -1,21 +1,9 @@
import { type MatrixClient } from "../client.ts";
import { type IContent, type MatrixEvent } from "./event.ts";
import { type MSC3089TreeSpace } from "./MSC3089TreeSpace.ts";
import { type FileType } from "../http-api/index.ts";
import type { ISendEventResponse } from "../@types/requests.ts";
import { type EncryptedFile } from "../@types/media.ts";
export interface MSC3089EventContent {
active?: boolean;
name?: string;
locked?: boolean;
version?: number;
}
export interface MSC3089EventContent {
active?: boolean;
name?: string;
locked?: boolean;
version?: number;
}
import { MatrixClient } from "../client";
import { IEncryptedFile } from "../@types/event";
import { IContent, MatrixEvent } from "./event";
import { MSC3089TreeSpace } from "./MSC3089TreeSpace";
import { FileType } from "../http-api";
import type { ISendEventResponse } from "../@types/requests";
/**
* Represents a [MSC3089](https://github.com/matrix-org/matrix-doc/pull/3089) branch - a reference
* to a file (leaf) in the tree. Note that this is UNSTABLE and subject to breaking changes
@@ -71,7 +59,7 @@ export declare class MSC3089Branch {
* @returns Information about the file.
*/
getFileInfo(): Promise<{
info: EncryptedFile;
info: IEncryptedFile;
httpUrl: string;
}>;
/**
@@ -87,7 +75,7 @@ export declare class MSC3089Branch {
* @param additionalContent - Optional event content fields to include in the message.
* @returns Promise which resolves to the file event's sent response.
*/
createNewVersion(name: string, encryptedContents: FileType, info: EncryptedFile, additionalContent?: IContent): Promise<ISendEventResponse>;
createNewVersion(name: string, encryptedContents: FileType, info: Partial<IEncryptedFile>, additionalContent?: IContent): Promise<ISendEventResponse>;
/**
* Gets the file's version history, starting at this file.
* @returns Promise which resolves to the file's version history, with the

View File

@@ -1 +1 @@
{"version":3,"file":"MSC3089Branch.d.ts","sourceRoot":"","sources":["../../src/models/MSC3089Branch.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,qBAAa,aAAa;IAElB,OAAO,CAAC,MAAM;aACE,UAAU,EAAE,WAAW;aACvB,SAAS,EAAE,gBAAgB;IAH/C,YACY,MAAM,EAAE,YAAY,EACZ,UAAU,EAAE,WAAW,EACvB,SAAS,EAAE,gBAAgB,EAG9C;IAED;;OAEG;IACH,IAAW,EAAE,IAAI,MAAM,CAMtB;IAED;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED;;OAEG;IACH,IAAW,OAAO,IAAI,MAAM,CAE3B;IAED,OAAO,KAAK,MAAM,GAEjB;IAED;;;OAGG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAMnC;IAED;;;OAGG;IACI,OAAO,IAAI,MAAM,CAEvB;IAED;;;;OAIG;IACU,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhD;IAED;;;OAGG;IACI,QAAQ,IAAI,OAAO,CAEzB;IAED;;;;OAIG;IACU,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAUrD;IAED;;;OAGG;IACU,WAAW,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAW5E;IAED;;;OAGG;IACU,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC,CAkBhD;IAED;;;;;;;OAOG;IACU,gBAAgB,CACzB,IAAI,EAAE,MAAM,EACZ,iBAAiB,EAAE,QAAQ,EAC3B,IAAI,EAAE,aAAa,EACnB,iBAAiB,CAAC,EAAE,QAAQ,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CAkC7B;IAED;;;;OAIG;IACU,iBAAiB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CA+BzD;CACJ"}
{"version":3,"file":"MSC3089Branch.d.ts","sourceRoot":"","sources":["../../src/models/MSC3089Branch.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,cAAc,EAAyC,MAAM,iBAAiB,CAAC;AACxF,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAE7D;;;;GAIG;AACH,qBAAa,aAAa;IAElB,OAAO,CAAC,MAAM;aACE,UAAU,EAAE,WAAW;aACvB,SAAS,EAAE,gBAAgB;gBAFnC,MAAM,EAAE,YAAY,EACZ,UAAU,EAAE,WAAW,EACvB,SAAS,EAAE,gBAAgB;IAK/C;;OAEG;IACH,IAAW,EAAE,IAAI,MAAM,CAMtB;IAED;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED;;OAEG;IACH,IAAW,OAAO,IAAI,MAAM,CAE3B;IAED,OAAO,KAAK,MAAM,GAEjB;IAED;;;OAGG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAQpC;;;OAGG;IACI,OAAO,IAAI,MAAM;IAIxB;;;;OAIG;IACU,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYjD;;;OAGG;IACI,QAAQ,IAAI,OAAO;IAI1B;;;;OAIG;IACU,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAYtD;;;OAGG;IACU,WAAW,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAa9E;;;OAGG;IACU,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC;IAqBjD;;;;;;;OAOG;IACU,gBAAgB,CACzB,IAAI,EAAE,MAAM,EACZ,iBAAiB,EAAE,QAAQ,EAC3B,IAAI,EAAE,OAAO,CAAC,cAAc,CAAC,EAC7B,iBAAiB,CAAC,EAAE,QAAQ,GAC7B,OAAO,CAAC,kBAAkB,CAAC;IAoC9B;;;;OAIG;IACU,iBAAiB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;CAgC7D"}

View File

@@ -1,28 +1,35 @@
import _objectSpread from "@babel/runtime/helpers/objectSpread2";
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
"use strict";
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 { RelationType, UNSTABLE_MSC3089_BRANCH } from "../@types/event.js";
import { EventTimeline } from "./event-timeline.js";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.MSC3089Branch = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _event = require("../@types/event");
var _eventTimeline = require("./event-timeline");
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } /*
Copyright 2021 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.
*/
/**
* Represents a [MSC3089](https://github.com/matrix-org/matrix-doc/pull/3089) branch - a reference
* to a file (leaf) in the tree. Note that this is UNSTABLE and subject to breaking changes
* without notice.
*/
export class MSC3089Branch {
class MSC3089Branch {
constructor(client, indexEvent, directory) {
this.client = client;
this.indexEvent = indexEvent;
@@ -51,7 +58,8 @@ export class MSC3089Branch {
* Version for the file, one-indexed.
*/
get version() {
return this.indexEvent.getContent()["version"] ?? 1;
var _this$indexEvent$getC;
return (_this$indexEvent$getC = this.indexEvent.getContent()["version"]) !== null && _this$indexEvent$getC !== void 0 ? _this$indexEvent$getC : 1;
}
get roomId() {
return this.indexEvent.getRoomId();
@@ -62,7 +70,7 @@ export class MSC3089Branch {
* @returns Promise which resolves when complete.
*/
async delete() {
await this.client.sendStateEvent(this.roomId, UNSTABLE_MSC3089_BRANCH.name, {}, this.id);
await this.client.sendStateEvent(this.roomId, _event.UNSTABLE_MSC3089_BRANCH.name, {}, this.id);
await this.client.redactEvent(this.roomId, this.id);
const nextVersion = (await this.getVersionHistory())[1]; // [0] will be us
if (nextVersion) await nextVersion.delete(); // implicit recursion
@@ -82,7 +90,7 @@ export class MSC3089Branch {
* @returns Promise which resolves when complete.
*/
async setName(name) {
await this.client.sendStateEvent(this.roomId, UNSTABLE_MSC3089_BRANCH.name, _objectSpread(_objectSpread({}, this.indexEvent.getContent()), {}, {
await this.client.sendStateEvent(this.roomId, _event.UNSTABLE_MSC3089_BRANCH.name, _objectSpread(_objectSpread({}, this.indexEvent.getContent()), {}, {
name: name
}), this.id);
}
@@ -101,7 +109,7 @@ export class MSC3089Branch {
* @returns Promise which resolves when complete.
*/
async setLocked(locked) {
await this.client.sendStateEvent(this.roomId, UNSTABLE_MSC3089_BRANCH.name, _objectSpread(_objectSpread({}, this.indexEvent.getContent()), {}, {
await this.client.sendStateEvent(this.roomId, _event.UNSTABLE_MSC3089_BRANCH.name, _objectSpread(_objectSpread({}, this.indexEvent.getContent()), {}, {
locked: locked
}), this.id);
}
@@ -133,14 +141,18 @@ export class MSC3089Branch {
let event = room.getUnfilteredTimelineSet().findEventById(this.id);
// keep scrolling back if needed until we find the event or reach the start of the room:
while (!event && room.getLiveTimeline().getState(EventTimeline.BACKWARDS).paginationToken) {
while (!event && room.getLiveTimeline().getState(_eventTimeline.EventTimeline.BACKWARDS).paginationToken) {
await this.client.scrollback(room, 100);
event = room.getUnfilteredTimelineSet().findEventById(this.id);
}
if (!event) throw new Error("Failed to find event");
// Sometimes the event isn't decrypted for us, so do that.
await this.client.decryptEventIfNeeded(event);
// Sometimes the event isn't decrypted for us, so do that. We specifically set `emit: true`
// to ensure that the relations system in the sdk will function.
await this.client.decryptEventIfNeeded(event, {
emit: true,
isRetry: true
});
return event;
}
@@ -153,23 +165,23 @@ export class MSC3089Branch {
* @returns Promise which resolves to the file event's sent response.
*/
async createNewVersion(name, encryptedContents, info, additionalContent) {
const fileEventResponse = await this.directory.createFile(name, encryptedContents, info, _objectSpread(_objectSpread({}, additionalContent), {}, {
const fileEventResponse = await this.directory.createFile(name, encryptedContents, info, _objectSpread(_objectSpread({}, additionalContent !== null && additionalContent !== void 0 ? additionalContent : {}), {}, {
"m.new_content": true,
"m.relates_to": {
rel_type: RelationType.Replace,
rel_type: _event.RelationType.Replace,
event_id: this.id
}
}));
// Update the version of the new event
await this.client.sendStateEvent(this.roomId, UNSTABLE_MSC3089_BRANCH.name, {
await this.client.sendStateEvent(this.roomId, _event.UNSTABLE_MSC3089_BRANCH.name, {
active: true,
name: name,
version: this.version + 1
}, fileEventResponse["event_id"]);
// Deprecate ourselves
await this.client.sendStateEvent(this.roomId, UNSTABLE_MSC3089_BRANCH.name, _objectSpread(_objectSpread({}, this.indexEvent.getContent()), {}, {
await this.client.sendStateEvent(this.roomId, _event.UNSTABLE_MSC3089_BRANCH.name, _objectSpread(_objectSpread({}, this.indexEvent.getContent()), {}, {
active: false
}), this.id);
return fileEventResponse;
@@ -212,4 +224,5 @@ export class MSC3089Branch {
return fileHistory;
}
}
exports.MSC3089Branch = MSC3089Branch;
//# sourceMappingURL=MSC3089Branch.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +1,10 @@
import { type MatrixClient } from "../client.ts";
import { UNSTABLE_MSC3089_LEAF } from "../@types/event.ts";
import { type Room } from "./room.ts";
import { type IContent } from "./event.ts";
import { MSC3089Branch } from "./MSC3089Branch.ts";
import { type ISendEventResponse } from "../@types/requests.ts";
import { type FileType } from "../http-api/index.ts";
import type { EncryptedFile } from "../@types/media.ts";
import { type EmptyObject } from "../@types/common.ts";
import { MatrixClient } from "../client";
import { IEncryptedFile } from "../@types/event";
import { Room } from "./room";
import { IContent } from "./event";
import { MSC3089Branch } from "./MSC3089Branch";
import { ISendEventResponse } from "../@types/requests";
import { FileType } from "../http-api";
/**
* The recommended defaults for a tree space's power levels. Note that this
* is UNSTABLE and subject to breaking changes without notice.
@@ -36,15 +34,10 @@ export declare const DEFAULT_TREE_POWER_LEVELS_TEMPLATE: {
* Note that this is UNSTABLE and subject to breaking changes without notice.
*/
export declare enum TreePermissions {
Viewer = "viewer",// Default
Editor = "editor",// "Moderator" or ~PL50
Viewer = "viewer",
Editor = "editor",
Owner = "owner"
}
declare module "../@types/media" {
interface FileContent {
[UNSTABLE_MSC3089_LEAF.name]?: EmptyObject;
}
}
/**
* Represents a [MSC3089](https://github.com/matrix-org/matrix-doc/pull/3089)
* file tree Space. Note that this is UNSTABLE and subject to breaking changes
@@ -75,9 +68,14 @@ export declare class MSC3089TreeSpace {
* @param userId - The user ID to invite.
* @param andSubspaces - True (default) to invite the user to all
* directories/subspaces too, recursively.
* @param shareHistoryKeys - True (default) to share encryption keys
* with the invited user. This will allow them to decrypt the events (files)
* in the tree. Keys will not be shared if the room is lacking appropriate
* history visibility (by default, history visibility is "shared" in trees,
* which is an appropriate visibility for these purposes).
* @returns Promise which resolves when complete.
*/
invite(userId: string, andSubspaces?: boolean): Promise<void>;
invite(userId: string, andSubspaces?: boolean, shareHistoryKeys?: boolean): Promise<void>;
private retryInvite;
/**
* Sets the permissions of a user to the given role. Note that if setting a user
@@ -145,7 +143,7 @@ export declare class MSC3089TreeSpace {
* @param additionalContent - Optional event content fields to include in the message.
* @returns Promise which resolves to the file event's sent response.
*/
createFile(name: string, encryptedContents: FileType, info: EncryptedFile, additionalContent?: IContent): Promise<ISendEventResponse>;
createFile(name: string, encryptedContents: FileType, info: Partial<IEncryptedFile>, additionalContent?: IContent): Promise<ISendEventResponse>;
/**
* Retrieves a file from the tree.
* @param fileEventId - The event ID of the file.

View File

@@ -1 +1 @@
{"version":3,"file":"MSC3089TreeSpace.d.ts","sourceRoot":"","sources":["../../src/models/MSC3089TreeSpace.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAA+C,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACxG,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,KAAK,QAAQ,EAAoB,MAAM,YAAY,CAAC;AAS7D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,KAAK,QAAQ,EAAe,MAAM,sBAAsB,CAAC;AAGlE,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;CA2B9C,CAAC;AAEF;;;GAGG;AACH,oBAAY,eAAe;IACvB,MAAM,WAAW,CAAE,UAAU;IAC7B,MAAM,WAAW,CAAE,uBAAuB;IAC1C,KAAK,UAAU;CAClB;AAED,OAAO,QAAQ,iBAAiB,CAAC;IAC7B,UAAU,WAAW;QACjB,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,EAAE,WAAW,CAAC;KAC9C;CACJ;AAED;;;;GAIG;AACH,qBAAa,gBAAgB;IAIrB,OAAO,CAAC,MAAM;aACE,MAAM,EAAE,MAAM;IAJlC,SAAgB,IAAI,EAAE,IAAI,CAAC;IAE3B,YACY,MAAM,EAAE,YAAY,EACZ,MAAM,EAAE,MAAM,EAKjC;IAED;;OAEG;IACH,IAAW,EAAE,IAAI,MAAM,CAEtB;IAED;;OAEG;IACH,IAAW,UAAU,IAAI,OAAO,CAM/B;IAED;;;;OAIG;IACU,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhD;IAED;;;;;;;OAOG;IACU,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAMtE;YAEa,WAAW;IAazB;;;;;;;OAOG;IACU,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BhF;IAED;;;;;;OAMG;IACI,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAarD;IAED;;;;OAIG;IACU,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsBpE;IAED;;;OAGG;IACI,cAAc,IAAI,gBAAgB,EAAE,CAe1C;IAED;;;;;OAKG;IACI,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAEhE;IAED;;;OAGG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAoBnC;IAED,OAAO,CAAC,kBAAkB;IA+B1B,OAAO,CAAC,aAAa;IAerB;;;;OAIG;IACI,QAAQ,IAAI,MAAM,CAQxB;IAED;;;;;;;OAOG;IACU,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8GlD;IAED;;;;;;;;OAQG;IACU,UAAU,CACnB,IAAI,EAAE,MAAM,EACZ,iBAAiB,EAAE,QAAQ,EAC3B,IAAI,EAAE,aAAa,EACnB,iBAAiB,CAAC,EAAE,QAAQ,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CAsC7B;IAED;;;;OAIG;IACI,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAGxD;IAED;;;OAGG;IACI,SAAS,IAAI,aAAa,EAAE,CAElC;IAED;;;OAGG;IACI,YAAY,IAAI,aAAa,EAAE,CAGrC;CACJ"}
{"version":3,"file":"MSC3089TreeSpace.d.ts","sourceRoot":"","sources":["../../src/models/MSC3089TreeSpace.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAa,cAAc,EAA2D,MAAM,iBAAiB,CAAC;AACrH,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,OAAO,EAAE,QAAQ,EAAe,MAAM,SAAS,CAAC;AAShD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;CA2B9C,CAAC;AAEF;;;GAGG;AACH,oBAAY,eAAe;IACvB,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,KAAK,UAAU;CAClB;AAED;;;;GAIG;AACH,qBAAa,gBAAgB;IAGN,OAAO,CAAC,MAAM;aAAgC,MAAM,EAAE,MAAM;IAF/E,SAAgB,IAAI,EAAE,IAAI,CAAC;gBAEA,MAAM,EAAE,YAAY,EAAkB,MAAM,EAAE,MAAM;IAM/E;;OAEG;IACH,IAAW,EAAE,IAAI,MAAM,CAEtB;IAED;;OAEG;IACH,IAAW,UAAU,IAAI,OAAO,CAM/B;IAED;;;;OAIG;IACU,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD;;;;;;;;;;;;OAYG;IACU,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,UAAO,EAAE,gBAAgB,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBhG,OAAO,CAAC,WAAW;IAYnB;;;;;;;OAOG;IACU,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BjF;;;;;;OAMG;IACI,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe;IAetD;;;;OAIG;IACU,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwBrE;;;OAGG;IACI,cAAc,IAAI,gBAAgB,EAAE;IAiB3C;;;;;OAKG;IACI,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAIjE;;;OAGG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBpC,OAAO,CAAC,kBAAkB;IA+B1B,OAAO,CAAC,aAAa;IAerB;;;;OAIG;IACI,QAAQ,IAAI,MAAM;IAUzB;;;;;;;OAOG;IACU,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8GnD;;;;;;;;OAQG;IACU,UAAU,CACnB,IAAI,EAAE,MAAM,EACZ,iBAAiB,EAAE,QAAQ,EAC3B,IAAI,EAAE,OAAO,CAAC,cAAc,CAAC,EAC7B,iBAAiB,CAAC,EAAE,QAAQ,GAC7B,OAAO,CAAC,kBAAkB,CAAC;IAwC9B;;;;OAIG;IACI,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAKzD;;;OAGG;IACI,SAAS,IAAI,aAAa,EAAE;IAInC;;;OAGG;IACI,YAAY,IAAI,aAAa,EAAE;CAIzC"}

View File

@@ -1,32 +1,38 @@
import _objectSpread from "@babel/runtime/helpers/objectSpread2";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
"use strict";
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 { EventType, MsgType, UNSTABLE_MSC3089_BRANCH, UNSTABLE_MSC3089_LEAF } from "../@types/event.js";
import { logger } from "../logger.js";
import { averageBetweenStrings, DEFAULT_ALPHABET, lexicographicCompare, nextString, prevString, simpleRetryOperation } from "../utils.js";
import { MSC3089Branch } from "./MSC3089Branch.js";
import { MatrixError } from "../http-api/index.js";
import { KnownMembership } from "../@types/membership.js";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.TreePermissions = exports.MSC3089TreeSpace = exports.DEFAULT_TREE_POWER_LEVELS_TEMPLATE = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _pRetry = _interopRequireDefault(require("p-retry"));
var _event = require("../@types/event");
var _logger = require("../logger");
var _utils = require("../utils");
var _MSC3089Branch = require("./MSC3089Branch");
var _megolm = require("../crypto/algorithms/megolm");
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } /*
Copyright 2021 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.
*/
/**
* The recommended defaults for a tree space's power levels. Note that this
* is UNSTABLE and subject to breaking changes without notice.
*/
export const DEFAULT_TREE_POWER_LEVELS_TEMPLATE = {
const DEFAULT_TREE_POWER_LEVELS_TEMPLATE = {
// Owner
invite: 100,
kick: 100,
@@ -39,14 +45,14 @@ export const DEFAULT_TREE_POWER_LEVELS_TEMPLATE = {
users_default: 0,
// Mixed
events: {
[EventType.RoomPowerLevels]: 100,
[EventType.RoomHistoryVisibility]: 100,
[EventType.RoomTombstone]: 100,
[EventType.RoomEncryption]: 100,
[EventType.RoomName]: 50,
[EventType.RoomMessage]: 50,
[EventType.RoomMessageEncrypted]: 50,
[EventType.Sticker]: 50
[_event.EventType.RoomPowerLevels]: 100,
[_event.EventType.RoomHistoryVisibility]: 100,
[_event.EventType.RoomTombstone]: 100,
[_event.EventType.RoomEncryption]: 100,
[_event.EventType.RoomName]: 50,
[_event.EventType.RoomMessage]: 50,
[_event.EventType.RoomMessageEncrypted]: 50,
[_event.EventType.Sticker]: 50
},
users: {} // defined by calling code
};
@@ -55,24 +61,24 @@ export const DEFAULT_TREE_POWER_LEVELS_TEMPLATE = {
* Ease-of-use representation for power levels represented as simple roles.
* Note that this is UNSTABLE and subject to breaking changes without notice.
*/
export let TreePermissions = /*#__PURE__*/function (TreePermissions) {
exports.DEFAULT_TREE_POWER_LEVELS_TEMPLATE = DEFAULT_TREE_POWER_LEVELS_TEMPLATE;
let TreePermissions = /*#__PURE__*/function (TreePermissions) {
TreePermissions["Viewer"] = "viewer";
// Default
TreePermissions["Editor"] = "editor";
// "Moderator" or ~PL50
TreePermissions["Owner"] = "owner"; // "Admin" or PL100
TreePermissions["Owner"] = "owner";
return TreePermissions;
}({});
}({}); // "Admin" or PL100
/**
* Represents a [MSC3089](https://github.com/matrix-org/matrix-doc/pull/3089)
* file tree Space. Note that this is UNSTABLE and subject to breaking changes
* without notice.
*/
export class MSC3089TreeSpace {
exports.TreePermissions = TreePermissions;
class MSC3089TreeSpace {
constructor(client, roomId) {
_defineProperty(this, "room", void 0);
this.client = client;
this.roomId = roomId;
(0, _defineProperty2.default)(this, "room", void 0);
this.room = this.client.getRoom(this.roomId);
if (!this.room) throw new Error("Unknown room");
}
@@ -90,9 +96,12 @@ export class MSC3089TreeSpace {
get isTopLevel() {
// XXX: This is absolutely not how you find out if the space is top level
// but is safe for a managed usecase like we offer in the SDK.
const parentEvents = this.room.currentState.getStateEvents(EventType.SpaceParent);
if (!parentEvents?.length) return true;
return parentEvents.every(e => !e.getContent()?.["via"]);
const parentEvents = this.room.currentState.getStateEvents(_event.EventType.SpaceParent);
if (!(parentEvents !== null && parentEvents !== void 0 && parentEvents.length)) return true;
return parentEvents.every(e => {
var _e$getContent;
return !((_e$getContent = e.getContent()) !== null && _e$getContent !== void 0 && _e$getContent["via"]);
});
}
/**
@@ -101,7 +110,7 @@ export class MSC3089TreeSpace {
* @returns Promise which resolves when complete.
*/
async setName(name) {
await this.client.sendStateEvent(this.roomId, EventType.RoomName, {
await this.client.sendStateEvent(this.roomId, _event.EventType.RoomName, {
name
}, "");
}
@@ -112,22 +121,38 @@ export class MSC3089TreeSpace {
* @param userId - The user ID to invite.
* @param andSubspaces - True (default) to invite the user to all
* directories/subspaces too, recursively.
* @param shareHistoryKeys - True (default) to share encryption keys
* with the invited user. This will allow them to decrypt the events (files)
* in the tree. Keys will not be shared if the room is lacking appropriate
* history visibility (by default, history visibility is "shared" in trees,
* which is an appropriate visibility for these purposes).
* @returns Promise which resolves when complete.
*/
async invite(userId, andSubspaces = true) {
async invite(userId, andSubspaces = true, shareHistoryKeys = true) {
const promises = [this.retryInvite(userId)];
if (andSubspaces) {
promises.push(...this.getDirectories().map(d => d.invite(userId, andSubspaces)));
promises.push(...this.getDirectories().map(d => d.invite(userId, andSubspaces, shareHistoryKeys)));
}
await Promise.all(promises);
}
async retryInvite(userId) {
await simpleRetryOperation(() => this.client.invite(this.roomId, userId), e => {
// We don't want to retry permission errors forever...
if (e instanceof MatrixError && e.errcode === "M_FORBIDDEN") {
return false;
return Promise.all(promises).then(() => {
// Note: key sharing is default on because for file trees it is relatively important that the invite
// target can actually decrypt the files. The implied use case is that by inviting a user to the tree
// it means the sender would like the receiver to view/download the files contained within, much like
// sharing a folder in other circles.
if (shareHistoryKeys && (0, _megolm.isRoomSharedHistory)(this.room)) {
// noinspection JSIgnoredPromiseFromCall - we aren't concerned as much if this fails.
this.client.sendSharedHistoryKeys(this.roomId, [userId]);
}
return true;
});
}
retryInvite(userId) {
return (0, _utils.simpleRetryOperation)(async () => {
await this.client.invite(this.roomId, userId).catch(e => {
// We don't want to retry permission errors forever...
if ((e === null || e === void 0 ? void 0 : e.errcode) === "M_FORBIDDEN") {
throw new _pRetry.default.AbortError(e);
}
throw e;
});
});
}
@@ -140,12 +165,13 @@ export class MSC3089TreeSpace {
* @returns Promise which resolves when complete.
*/
async setPermissions(userId, role) {
const currentPls = this.room.currentState.getStateEvents(EventType.RoomPowerLevels, "");
var _pls$events;
const currentPls = this.room.currentState.getStateEvents(_event.EventType.RoomPowerLevels, "");
if (Array.isArray(currentPls)) throw new Error("Unexpected return type for power levels");
const pls = currentPls?.getContent() || {};
const pls = (currentPls === null || currentPls === void 0 ? void 0 : currentPls.getContent()) || {};
const viewLevel = pls["users_default"] || 0;
const editLevel = pls["events_default"] || 50;
const adminLevel = pls["events"]?.[EventType.RoomPowerLevels] || 100;
const adminLevel = ((_pls$events = pls["events"]) === null || _pls$events === void 0 ? void 0 : _pls$events[_event.EventType.RoomPowerLevels]) || 100;
const users = pls["users"] || {};
switch (role) {
case TreePermissions.Viewer:
@@ -161,7 +187,7 @@ export class MSC3089TreeSpace {
throw new Error("Invalid role: " + role);
}
pls["users"] = users;
await this.client.sendStateEvent(this.roomId, EventType.RoomPowerLevels, pls, "");
await this.client.sendStateEvent(this.roomId, _event.EventType.RoomPowerLevels, pls, "");
}
/**
@@ -172,13 +198,14 @@ export class MSC3089TreeSpace {
* @returns The permissions for the user, defaulting to Viewer.
*/
getPermissions(userId) {
const currentPls = this.room.currentState.getStateEvents(EventType.RoomPowerLevels, "");
var _pls$events2, _pls$users;
const currentPls = this.room.currentState.getStateEvents(_event.EventType.RoomPowerLevels, "");
if (Array.isArray(currentPls)) throw new Error("Unexpected return type for power levels");
const pls = currentPls?.getContent() || {};
const pls = (currentPls === null || currentPls === void 0 ? void 0 : currentPls.getContent()) || {};
const viewLevel = pls["users_default"] || 0;
const editLevel = pls["events_default"] || 50;
const adminLevel = pls["events"]?.[EventType.RoomPowerLevels] || 100;
const userLevel = pls["users"]?.[userId] || viewLevel;
const adminLevel = ((_pls$events2 = pls["events"]) === null || _pls$events2 === void 0 ? void 0 : _pls$events2[_event.EventType.RoomPowerLevels]) || 100;
const userLevel = ((_pls$users = pls["users"]) === null || _pls$users === void 0 ? void 0 : _pls$users[userId]) || viewLevel;
if (userLevel >= adminLevel) return TreePermissions.Owner;
if (userLevel >= editLevel) return TreePermissions.Editor;
return TreePermissions.Viewer;
@@ -191,10 +218,10 @@ export class MSC3089TreeSpace {
*/
async createDirectory(name) {
const directory = await this.client.unstableCreateFileTree(name);
await this.client.sendStateEvent(this.roomId, EventType.SpaceChild, {
await this.client.sendStateEvent(this.roomId, _event.EventType.SpaceChild, {
via: [this.client.getDomain()]
}, directory.roomId);
await this.client.sendStateEvent(directory.roomId, EventType.SpaceParent, {
await this.client.sendStateEvent(directory.roomId, _event.EventType.SpaceParent, {
via: [this.client.getDomain()]
}, this.roomId);
return directory;
@@ -206,7 +233,7 @@ export class MSC3089TreeSpace {
*/
getDirectories() {
const trees = [];
const children = this.room.currentState.getStateEvents(EventType.SpaceChild);
const children = this.room.currentState.getStateEvents(_event.EventType.SpaceChild);
for (const child of children) {
try {
const stateKey = child.getStateKey();
@@ -215,7 +242,7 @@ export class MSC3089TreeSpace {
if (tree) trees.push(tree);
}
} catch (e) {
logger.warn("Unable to create tree space instance for listing. Are we joined?", e);
_logger.logger.warn("Unable to create tree space instance for listing. Are we joined?", e);
}
}
return trees;
@@ -240,8 +267,8 @@ export class MSC3089TreeSpace {
for (const dir of subdirectories) {
await dir.delete();
}
const kickMemberships = [KnownMembership.Invite, KnownMembership.Knock, KnownMembership.Join];
const members = this.room.currentState.getStateEvents(EventType.RoomMember);
const kickMemberships = ["invite", "knock", "join"];
const members = this.room.currentState.getStateEvents(_event.EventType.RoomMember);
for (const member of members) {
const isNotUs = member.getStateKey() !== this.client.getUserId();
if (isNotUs && kickMemberships.includes(member.getContent().membership)) {
@@ -265,27 +292,28 @@ export class MSC3089TreeSpace {
} else if (!a.order && b.order) {
return 1;
} else if (!a.order && !b.order) {
var _roomA$currentState$g, _roomA$currentState$g2, _roomB$currentState$g, _roomB$currentState$g2;
const roomA = this.client.getRoom(a.roomId);
const roomB = this.client.getRoom(b.roomId);
if (!roomA || !roomB) {
// just don't bother trying to do more partial sorting
return lexicographicCompare(a.roomId, b.roomId);
return (0, _utils.lexicographicCompare)(a.roomId, b.roomId);
}
const createTsA = roomA.currentState.getStateEvents(EventType.RoomCreate, "")?.getTs() ?? 0;
const createTsB = roomB.currentState.getStateEvents(EventType.RoomCreate, "")?.getTs() ?? 0;
const createTsA = (_roomA$currentState$g = (_roomA$currentState$g2 = roomA.currentState.getStateEvents(_event.EventType.RoomCreate, "")) === null || _roomA$currentState$g2 === void 0 ? void 0 : _roomA$currentState$g2.getTs()) !== null && _roomA$currentState$g !== void 0 ? _roomA$currentState$g : 0;
const createTsB = (_roomB$currentState$g = (_roomB$currentState$g2 = roomB.currentState.getStateEvents(_event.EventType.RoomCreate, "")) === null || _roomB$currentState$g2 === void 0 ? void 0 : _roomB$currentState$g2.getTs()) !== null && _roomB$currentState$g !== void 0 ? _roomB$currentState$g : 0;
if (createTsA === createTsB) {
return lexicographicCompare(a.roomId, b.roomId);
return (0, _utils.lexicographicCompare)(a.roomId, b.roomId);
}
return createTsA - createTsB;
} else {
// both not-null orders
return lexicographicCompare(a.order, b.order);
return (0, _utils.lexicographicCompare)(a.order, b.order);
}
});
return ordered;
}
getParentRoom() {
const parents = this.room.currentState.getStateEvents(EventType.SpaceParent);
const parents = this.room.currentState.getStateEvents(_event.EventType.SpaceParent);
const parent = parents[0]; // XXX: Wild assumption
if (!parent) throw new Error("Expected to have a parent in a non-top level space");
@@ -306,7 +334,7 @@ export class MSC3089TreeSpace {
getOrder() {
if (this.isTopLevel) return -1;
const parentRoom = this.getParentRoom();
const children = parentRoom.currentState.getStateEvents(EventType.SpaceChild);
const children = parentRoom.currentState.getStateEvents(_event.EventType.SpaceChild);
const ordered = this.getOrderedChildren(children);
return ordered.findIndex(c => c.roomId === this.roomId);
}
@@ -320,9 +348,10 @@ export class MSC3089TreeSpace {
* @throws Throws if this is a top level space.
*/
async setOrder(index) {
var _currentChild$getCont2;
if (this.isTopLevel) throw new Error("Cannot set order of top level spaces currently");
const parentRoom = this.getParentRoom();
const children = parentRoom.currentState.getStateEvents(EventType.SpaceChild);
const children = parentRoom.currentState.getStateEvents(_event.EventType.SpaceChild);
const ordered = this.getOrderedChildren(children);
index = Math.max(Math.min(index, ordered.length - 1), 0);
const currentIndex = this.getOrder();
@@ -334,36 +363,36 @@ export class MSC3089TreeSpace {
}
const prev = ordered[movingUp ? index : index - 1];
const next = ordered[movingUp ? index + 1 : index];
let newOrder = DEFAULT_ALPHABET[0];
let newOrder = _utils.DEFAULT_ALPHABET[0];
let ensureBeforeIsSane = false;
if (!prev) {
// Move to front
if (next?.order) {
newOrder = prevString(next.order);
if (next !== null && next !== void 0 && next.order) {
newOrder = (0, _utils.prevString)(next.order);
}
} else if (index === ordered.length - 1) {
// Move to back
if (next?.order) {
newOrder = nextString(next.order);
if (next !== null && next !== void 0 && next.order) {
newOrder = (0, _utils.nextString)(next.order);
}
} else {
// Move somewhere in the middle
const startOrder = prev?.order;
const endOrder = next?.order;
const startOrder = prev === null || prev === void 0 ? void 0 : prev.order;
const endOrder = next === null || next === void 0 ? void 0 : next.order;
if (startOrder && endOrder) {
if (startOrder === endOrder) {
// Error case: just move +1 to break out of awful math
newOrder = nextString(startOrder);
newOrder = (0, _utils.nextString)(startOrder);
} else {
newOrder = averageBetweenStrings(startOrder, endOrder);
newOrder = (0, _utils.averageBetweenStrings)(startOrder, endOrder);
}
} else {
if (startOrder) {
// We're at the end (endOrder is null, so no explicit order)
newOrder = nextString(startOrder);
newOrder = (0, _utils.nextString)(startOrder);
} else if (endOrder) {
// We're at the start (startOrder is null, so nothing before us)
newOrder = prevString(endOrder);
newOrder = (0, _utils.prevString)(endOrder);
} else {
// Both points are unknown. We're likely in a range where all the children
// don't have particular order values, so we may need to update them too.
@@ -384,13 +413,14 @@ export class MSC3089TreeSpace {
lastOrder = target.order;
}
if (!target.order) {
var _currentChild$getCont;
// XXX: We should be creating gaps to avoid conflicts
lastOrder = lastOrder ? nextString(lastOrder) : DEFAULT_ALPHABET[0];
const currentChild = parentRoom.currentState.getStateEvents(EventType.SpaceChild, target.roomId);
const content = currentChild?.getContent() ?? {
lastOrder = lastOrder ? (0, _utils.nextString)(lastOrder) : _utils.DEFAULT_ALPHABET[0];
const currentChild = parentRoom.currentState.getStateEvents(_event.EventType.SpaceChild, target.roomId);
const content = (_currentChild$getCont = currentChild === null || currentChild === void 0 ? void 0 : currentChild.getContent()) !== null && _currentChild$getCont !== void 0 ? _currentChild$getCont : {
via: [this.client.getDomain()]
};
await this.client.sendStateEvent(parentRoom.roomId, EventType.SpaceChild, _objectSpread(_objectSpread({}, content), {}, {
await this.client.sendStateEvent(parentRoom.roomId, _event.EventType.SpaceChild, _objectSpread(_objectSpread({}, content), {}, {
order: lastOrder
}), target.roomId);
} else {
@@ -398,18 +428,18 @@ export class MSC3089TreeSpace {
}
}
if (lastOrder) {
newOrder = nextString(lastOrder);
newOrder = (0, _utils.nextString)(lastOrder);
}
}
// TODO: Deal with order conflicts by reordering
// Now we can finally update our own order state
const currentChild = parentRoom.currentState.getStateEvents(EventType.SpaceChild, this.roomId);
const content = currentChild?.getContent() ?? {
const currentChild = parentRoom.currentState.getStateEvents(_event.EventType.SpaceChild, this.roomId);
const content = (_currentChild$getCont2 = currentChild === null || currentChild === void 0 ? void 0 : currentChild.getContent()) !== null && _currentChild$getCont2 !== void 0 ? _currentChild$getCont2 : {
via: [this.client.getDomain()]
};
await this.client.sendStateEvent(parentRoom.roomId, EventType.SpaceChild, _objectSpread(_objectSpread({}, content), {}, {
await this.client.sendStateEvent(parentRoom.roomId, _event.EventType.SpaceChild, _objectSpread(_objectSpread({}, content), {}, {
// TODO: Safely constrain to 50 character limit required by spaces.
order: newOrder
}), this.roomId);
@@ -425,6 +455,7 @@ export class MSC3089TreeSpace {
* @returns Promise which resolves to the file event's sent response.
*/
async createFile(name, encryptedContents, info, additionalContent) {
var _additionalContent;
const {
content_uri: mxc
} = await this.client.uploadContent(encryptedContents, {
@@ -432,12 +463,12 @@ export class MSC3089TreeSpace {
});
info.url = mxc;
const fileContent = {
msgtype: MsgType.File,
msgtype: _event.MsgType.File,
body: name,
url: mxc,
file: info
};
additionalContent = additionalContent ?? {};
additionalContent = (_additionalContent = additionalContent) !== null && _additionalContent !== void 0 ? _additionalContent : {};
if (additionalContent["m.new_content"]) {
// We do the right thing according to the spec, but due to how relations are
// handled we also end up duplicating this information to the regular `content`
@@ -445,9 +476,9 @@ export class MSC3089TreeSpace {
additionalContent["m.new_content"] = fileContent;
}
const res = await this.client.sendMessage(this.roomId, _objectSpread(_objectSpread(_objectSpread({}, additionalContent), fileContent), {}, {
[UNSTABLE_MSC3089_LEAF.name]: {}
[_event.UNSTABLE_MSC3089_LEAF.name]: {}
}));
await this.client.sendStateEvent(this.roomId, UNSTABLE_MSC3089_BRANCH.name, {
await this.client.sendStateEvent(this.roomId, _event.UNSTABLE_MSC3089_BRANCH.name, {
active: true,
name: name
}, res["event_id"]);
@@ -460,8 +491,8 @@ export class MSC3089TreeSpace {
* @returns The file, or null if not found.
*/
getFile(fileEventId) {
const branch = this.room.currentState.getStateEvents(UNSTABLE_MSC3089_BRANCH.name, fileEventId);
return branch ? new MSC3089Branch(this.client, branch, this) : null;
const branch = this.room.currentState.getStateEvents(_event.UNSTABLE_MSC3089_BRANCH.name, fileEventId);
return branch ? new _MSC3089Branch.MSC3089Branch(this.client, branch, this) : null;
}
/**
@@ -477,8 +508,10 @@ export class MSC3089TreeSpace {
* @returns The known files. May be empty, but not null.
*/
listAllFiles() {
const branches = this.room.currentState.getStateEvents(UNSTABLE_MSC3089_BRANCH.name) ?? [];
return branches.map(e => new MSC3089Branch(this.client, e, this));
var _this$room$currentSta;
const branches = (_this$room$currentSta = this.room.currentState.getStateEvents(_event.UNSTABLE_MSC3089_BRANCH.name)) !== null && _this$room$currentSta !== void 0 ? _this$room$currentSta : [];
return branches.map(e => new _MSC3089Branch.MSC3089Branch(this.client, e, this));
}
}
exports.MSC3089TreeSpace = MSC3089TreeSpace;
//# sourceMappingURL=MSC3089TreeSpace.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,6 @@
export {};
export {};
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=ToDeviceMessage.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"ToDeviceMessage.js","names":[],"sources":["../../src/models/ToDeviceMessage.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport type ToDevicePayload = Record<string, any>;\n\nexport interface ToDeviceMessage {\n userId: string;\n deviceId: string;\n payload: ToDevicePayload;\n}\n\nexport interface ToDeviceBatch {\n eventType: string;\n batch: ToDeviceMessage[];\n}\n\n// Only used internally\nexport interface ToDeviceBatchWithTxnId extends ToDeviceBatch {\n txnId: string;\n}\n\n// Only used internally\nexport interface IndexedToDeviceBatch extends ToDeviceBatchWithTxnId {\n id: number;\n}\n"],"mappings":"","ignoreList":[]}
{"version":3,"file":"ToDeviceMessage.js","names":[],"sources":["../../src/models/ToDeviceMessage.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport type ToDevicePayload = Record<string, any>;\n\nexport interface ToDeviceMessage {\n userId: string;\n deviceId: string;\n payload: ToDevicePayload;\n}\n\nexport interface ToDeviceBatch {\n eventType: string;\n batch: ToDeviceMessage[];\n}\n\n// Only used internally\nexport interface ToDeviceBatchWithTxnId extends ToDeviceBatch {\n txnId: string;\n}\n\n// Only used internally\nexport interface IndexedToDeviceBatch extends ToDeviceBatchWithTxnId {\n id: number;\n}\n"],"mappings":""}

View File

@@ -1,6 +1,6 @@
import { type BeaconInfoState, type BeaconLocationState } from "../content-helpers.ts";
import { type MatrixEvent } from "./event.ts";
import { TypedEventEmitter } from "./typed-event-emitter.ts";
import { BeaconInfoState, BeaconLocationState } from "../content-helpers";
import { MatrixEvent } from "./event";
import { TypedEventEmitter } from "./typed-event-emitter";
export declare enum BeaconEvent {
New = "Beacon.new",
Update = "Beacon.update",
@@ -13,6 +13,7 @@ export type BeaconEventHandlerMap = {
[BeaconEvent.LivenessChange]: (isLive: boolean, beacon: Beacon) => void;
[BeaconEvent.Destroy]: (beaconIdentifier: string) => void;
[BeaconEvent.LocationUpdate]: (locationState: BeaconLocationState) => void;
[BeaconEvent.Destroy]: (beaconIdentifier: string) => void;
};
export declare const isTimestampInDuration: (startTimestamp: number, durationMs: number, timestamp: number) => boolean;
export type BeaconIdentifier = string;

View File

@@ -1 +1 @@
{"version":3,"file":"beacon.d.ts","sourceRoot":"","sources":["../../src/models/beacon.ts"],"names":[],"mappings":"AAiBA,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAG3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,oBAAY,WAAW;IACnB,GAAG,eAAe;IAClB,MAAM,kBAAkB;IACxB,cAAc,0BAA0B;IACxC,OAAO,mBAAmB;IAC1B,cAAc,0BAA0B;CAC3C;AAED,MAAM,MAAM,qBAAqB,GAAG;IAChC,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnE,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxE,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC,aAAa,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC9E,CAAC;AAEF,eAAO,MAAM,qBAAqB,mBAAoB,MAAM,cAAc,MAAM,aAAa,MAAM,KAAG,OAC3B,CAAC;AAI5E,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC;AACtC,eAAO,MAAM,uBAAuB,UAAW,WAAW,KAAG,gBACZ,CAAC;AAGlD,qBAAa,MAAO,SAAQ,iBAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,qBAAqB,CAAC;IASpF,OAAO,CAAC,SAAS;IARpC,SAAgB,MAAM,EAAE,MAAM,CAAC;IAG/B,OAAO,CAAC,WAAW,CAAmB;IACtC,OAAO,CAAC,OAAO,CAAC,CAAU;IAC1B,OAAO,CAAC,oBAAoB,CAAC,CAAgC;IAC7D,OAAO,CAAC,oBAAoB,CAAC,CAAc;IAE3C,YAA2B,SAAS,EAAE,WAAW,EAIhD;IAED,IAAW,MAAM,IAAI,OAAO,CAE3B;IAED,IAAW,UAAU,IAAI,gBAAgB,CAExC;IAED,IAAW,YAAY,IAAI,MAAM,CAEhC;IAED,IAAW,eAAe,IAAI,MAAM,CAEnC;IAED,IAAW,mBAAmB,IAAI,MAAM,CAEvC;IAED,IAAW,UAAU,IAAI,eAAe,CAEvC;IAED,IAAW,mBAAmB,IAAI,mBAAmB,GAAG,SAAS,CAEhE;IAED,IAAW,mBAAmB,IAAI,WAAW,GAAG,SAAS,CAExD;IAEM,MAAM,CAAC,eAAe,EAAE,WAAW,GAAG,IAAI,CAahD;IAEM,OAAO,IAAI,IAAI,CAOrB;IAED;;;OAGG;IACI,eAAe,IAAI,IAAI,CAqB7B;IAED;;;OAGG;IACI,YAAY,CAAC,oBAAoB,EAAE,WAAW,EAAE,GAAG,IAAI,CAyB7D;IAED,OAAO,CAAC,mBAAmB,CAGzB;IAEF,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,aAAa;CAqBxB"}
{"version":3,"file":"beacon.d.ts","sourceRoot":"","sources":["../../src/models/beacon.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAA8C,MAAM,oBAAoB,CAAC;AACtH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,oBAAY,WAAW;IACnB,GAAG,eAAe;IAClB,MAAM,kBAAkB;IACxB,cAAc,0BAA0B;IACxC,OAAO,mBAAmB;IAC1B,cAAc,0BAA0B;CAC3C;AAED,MAAM,MAAM,qBAAqB,GAAG;IAChC,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnE,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxE,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC,aAAa,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC3E,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7D,CAAC;AAEF,eAAO,MAAM,qBAAqB,mBAAoB,MAAM,cAAc,MAAM,aAAa,MAAM,KAAG,OAC3B,CAAC;AAI5E,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC;AACtC,eAAO,MAAM,uBAAuB,UAAW,WAAW,KAAG,gBACZ,CAAC;AAGlD,qBAAa,MAAO,SAAQ,iBAAiB,CAAC,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,qBAAqB,CAAC;IASpF,OAAO,CAAC,SAAS;IARpC,SAAgB,MAAM,EAAE,MAAM,CAAC;IAG/B,OAAO,CAAC,WAAW,CAAmB;IACtC,OAAO,CAAC,OAAO,CAAC,CAAU;IAC1B,OAAO,CAAC,oBAAoB,CAAC,CAAgC;IAC7D,OAAO,CAAC,oBAAoB,CAAC,CAAc;gBAEhB,SAAS,EAAE,WAAW;IAMjD,IAAW,MAAM,IAAI,OAAO,CAE3B;IAED,IAAW,UAAU,IAAI,gBAAgB,CAExC;IAED,IAAW,YAAY,IAAI,MAAM,CAEhC;IAED,IAAW,eAAe,IAAI,MAAM,CAEnC;IAED,IAAW,mBAAmB,IAAI,MAAM,CAEvC;IAED,IAAW,UAAU,IAAI,eAAe,CAEvC;IAED,IAAW,mBAAmB,IAAI,mBAAmB,GAAG,SAAS,CAEhE;IAED,IAAW,mBAAmB,IAAI,WAAW,GAAG,SAAS,CAExD;IAEM,MAAM,CAAC,eAAe,EAAE,WAAW,GAAG,IAAI;IAe1C,OAAO,IAAI,IAAI;IAStB;;;OAGG;IACI,eAAe,IAAI,IAAI;IAuB9B;;;OAGG;IACI,YAAY,CAAC,oBAAoB,EAAE,WAAW,EAAE,GAAG,IAAI;IA2B9D,OAAO,CAAC,mBAAmB,CAGzB;IAEF,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,aAAa;CAqBxB"}

View File

@@ -1,4 +1,14 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isTimestampInDuration = exports.getBeaconInfoIdentifier = exports.BeaconEvent = exports.Beacon = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _contentHelpers = require("../content-helpers");
var _utils = require("../utils");
var _typedEventEmitter = require("./typed-event-emitter");
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
@@ -14,11 +24,7 @@ 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 { parseBeaconContent, parseBeaconInfoContent } from "../content-helpers.js";
import { sortEventsByLatestContentTimestamp } from "../utils.js";
import { TypedEventEmitter } from "./typed-event-emitter.js";
export let BeaconEvent = /*#__PURE__*/function (BeaconEvent) {
let BeaconEvent = /*#__PURE__*/function (BeaconEvent) {
BeaconEvent["New"] = "Beacon.new";
BeaconEvent["Update"] = "Beacon.update";
BeaconEvent["LivenessChange"] = "Beacon.LivenessChange";
@@ -26,29 +32,31 @@ export let BeaconEvent = /*#__PURE__*/function (BeaconEvent) {
BeaconEvent["LocationUpdate"] = "Beacon.LocationUpdate";
return BeaconEvent;
}({});
export const isTimestampInDuration = (startTimestamp, durationMs, timestamp) => timestamp >= startTimestamp && startTimestamp + durationMs >= timestamp;
exports.BeaconEvent = BeaconEvent;
const isTimestampInDuration = (startTimestamp, durationMs, timestamp) => timestamp >= startTimestamp && startTimestamp + durationMs >= timestamp;
// beacon info events are uniquely identified by
// `<roomId>_<state_key>`
export const getBeaconInfoIdentifier = event => `${event.getRoomId()}_${event.getStateKey()}`;
exports.isTimestampInDuration = isTimestampInDuration;
const getBeaconInfoIdentifier = event => `${event.getRoomId()}_${event.getStateKey()}`;
// https://github.com/matrix-org/matrix-spec-proposals/pull/3672
export class Beacon extends TypedEventEmitter {
exports.getBeaconInfoIdentifier = getBeaconInfoIdentifier;
class Beacon extends _typedEventEmitter.TypedEventEmitter {
constructor(rootEvent) {
super();
_defineProperty(this, "roomId", void 0);
this.rootEvent = rootEvent;
(0, _defineProperty2.default)(this, "roomId", void 0);
// beaconInfo is assigned by setBeaconInfo in the constructor
// ! to make tsc believe it is definitely assigned
_defineProperty(this, "_beaconInfo", void 0);
_defineProperty(this, "_isLive", void 0);
_defineProperty(this, "livenessWatchTimeout", void 0);
_defineProperty(this, "_latestLocationEvent", void 0);
_defineProperty(this, "clearLatestLocation", () => {
(0, _defineProperty2.default)(this, "_beaconInfo", void 0);
(0, _defineProperty2.default)(this, "_isLive", void 0);
(0, _defineProperty2.default)(this, "livenessWatchTimeout", void 0);
(0, _defineProperty2.default)(this, "_latestLocationEvent", void 0);
(0, _defineProperty2.default)(this, "clearLatestLocation", () => {
this._latestLocationEvent = undefined;
this.emit(BeaconEvent.LocationUpdate, this.latestLocationState);
});
this.rootEvent = rootEvent;
this.roomId = this.rootEvent.getRoomId();
this.setBeaconInfo(this.rootEvent);
}
@@ -71,7 +79,7 @@ export class Beacon extends TypedEventEmitter {
return this._beaconInfo;
}
get latestLocationState() {
return this._latestLocationEvent && parseBeaconContent(this._latestLocationEvent.getContent());
return this._latestLocationEvent && (0, _contentHelpers.parseBeaconContent)(this._latestLocationEvent.getContent());
}
get latestLocationEvent() {
return this._latestLocationEvent;
@@ -128,13 +136,14 @@ export class Beacon extends TypedEventEmitter {
* Emits BeaconEvent.LocationUpdate
*/
addLocations(beaconLocationEvents) {
var _validLocationEvents$;
// discard locations for beacons that are not live
if (!this.isLive) {
return;
}
const validLocationEvents = beaconLocationEvents.filter(event => {
const content = event.getContent();
const parsed = parseBeaconContent(content);
const parsed = (0, _contentHelpers.parseBeaconContent)(content);
if (!parsed.uri || !parsed.timestamp) return false; // we won't be able to process these
const {
timestamp
@@ -145,14 +154,14 @@ export class Beacon extends TypedEventEmitter {
// ignore positions older than our current latest location
!this.latestLocationState || timestamp > this.latestLocationState.timestamp);
});
const latestLocationEvent = validLocationEvents.sort(sortEventsByLatestContentTimestamp)?.[0];
const latestLocationEvent = (_validLocationEvents$ = validLocationEvents.sort(_utils.sortEventsByLatestContentTimestamp)) === null || _validLocationEvents$ === void 0 ? void 0 : _validLocationEvents$[0];
if (latestLocationEvent) {
this._latestLocationEvent = latestLocationEvent;
this.emit(BeaconEvent.LocationUpdate, this.latestLocationState);
}
}
setBeaconInfo(event) {
this._beaconInfo = parseBeaconInfoContent(event.getContent());
this._beaconInfo = (0, _contentHelpers.parseBeaconInfoContent)(event.getContent());
this.checkLiveness();
}
checkLiveness() {
@@ -170,4 +179,5 @@ export class Beacon extends TypedEventEmitter {
}
}
}
exports.Beacon = Beacon;
//# sourceMappingURL=beacon.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,24 +0,0 @@
import { type Room } from "./room.ts";
/**
* Determine the order of two events in a room.
*
* In principle this should use the same order as the server, but in practice
* this is difficult for events that were not received over the Sync API. See
* MSC4033 for details.
*
* This implementation leans on the order of events within their timelines, and
* falls back to comparing event timestamps when they are in different
* timelines.
*
* See https://github.com/matrix-org/matrix-js-sdk/issues/3325 for where we are
* tracking the work to fix this.
*
* @param room - the room we are looking in
* @param leftEventId - the id of the first event
* @param rightEventId - the id of the second event
* @returns -1 if left \< right, 1 if left \> right, 0 if left == right, null if
* we can't tell (because we can't find the events).
*/
export declare function compareEventOrdering(room: Room, leftEventId: string, rightEventId: string): number | null;
//# sourceMappingURL=compare-event-ordering.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"compare-event-ordering.d.ts","sourceRoot":"","sources":["../../src/models/compare-event-ordering.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAGtC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqBzG"}

View File

@@ -1,120 +0,0 @@
/*
Copyright 2023 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 { inMainTimelineForReceipt, threadIdForReceipt } from "../client.js";
/**
* Determine the order of two events in a room.
*
* In principle this should use the same order as the server, but in practice
* this is difficult for events that were not received over the Sync API. See
* MSC4033 for details.
*
* This implementation leans on the order of events within their timelines, and
* falls back to comparing event timestamps when they are in different
* timelines.
*
* See https://github.com/matrix-org/matrix-js-sdk/issues/3325 for where we are
* tracking the work to fix this.
*
* @param room - the room we are looking in
* @param leftEventId - the id of the first event
* @param rightEventId - the id of the second event
* @returns -1 if left \< right, 1 if left \> right, 0 if left == right, null if
* we can't tell (because we can't find the events).
*/
export function compareEventOrdering(room, leftEventId, rightEventId) {
const leftEvent = room.findEventById(leftEventId);
const rightEvent = room.findEventById(rightEventId);
if (!leftEvent || !rightEvent) {
// Without the events themselves, we can't find their thread or
// timeline, or guess based on timestamp, so we just don't know.
return null;
}
// Check whether the events are in the main timeline
const isLeftEventInMainTimeline = inMainTimelineForReceipt(leftEvent);
const isRightEventInMainTimeline = inMainTimelineForReceipt(rightEvent);
if (isLeftEventInMainTimeline && isRightEventInMainTimeline) {
return compareEventsInMainTimeline(room, leftEventId, rightEventId, leftEvent, rightEvent);
} else {
// At least one event is not in the timeline, so we can't use the room's
// unfiltered timeline set.
return compareEventsInThreads(leftEventId, rightEventId, leftEvent, rightEvent);
}
}
function compareEventsInMainTimeline(room, leftEventId, rightEventId, leftEvent, rightEvent) {
// Get the timeline set that contains all the events.
const timelineSet = room.getUnfilteredTimelineSet();
// If they are in the same timeline, compareEventOrdering does what we need
const compareSameTimeline = timelineSet.compareEventOrdering(leftEventId, rightEventId);
if (compareSameTimeline !== null) {
return compareSameTimeline;
}
// Find which timeline each event is in. Refuse to provide an ordering if we
// can't find either of the events.
const leftTimeline = timelineSet.getTimelineForEvent(leftEventId);
if (leftTimeline === timelineSet.getLiveTimeline()) {
// The left event is part of the live timeline, so it must be after the
// right event (since they are not in the same timeline or we would have
// returned after compareEventOrdering.
return 1;
}
const rightTimeline = timelineSet.getTimelineForEvent(rightEventId);
if (rightTimeline === timelineSet.getLiveTimeline()) {
// The right event is part of the live timeline, so it must be after the
// left event.
return -1;
}
// They are in older timeline sets (because they were fetched by paging up).
return guessOrderBasedOnTimestamp(leftEvent, rightEvent);
}
function compareEventsInThreads(leftEventId, rightEventId, leftEvent, rightEvent) {
const leftEventThreadId = threadIdForReceipt(leftEvent);
const rightEventThreadId = threadIdForReceipt(rightEvent);
const leftThread = leftEvent.getThread();
if (leftThread && leftEventThreadId === rightEventThreadId) {
// They are in the same thread, so we can ask the thread's timeline to
// figure it out for us
return leftThread.timelineSet.compareEventOrdering(leftEventId, rightEventId);
} else {
return guessOrderBasedOnTimestamp(leftEvent, rightEvent);
}
}
/**
* Guess the order of events based on server timestamp. This is not good, but
* difficult to avoid without MSC4033.
*
* See https://github.com/matrix-org/matrix-js-sdk/issues/3325
*/
function guessOrderBasedOnTimestamp(leftEvent, rightEvent) {
const leftTs = leftEvent.getTs();
const rightTs = rightEvent.getTs();
if (leftTs < rightTs) {
return -1;
} else if (leftTs > rightTs) {
return 1;
} else {
return 0;
}
}
//# sourceMappingURL=compare-event-ordering.js.map

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@ export declare enum DeviceVerification {
export type DeviceMap = Map<string, Map<string, Device>>;
type DeviceParameters = Pick<Device, "deviceId" | "userId" | "algorithms" | "keys"> & Partial<Device>;
/**
* Information on a user's device, as returned by {@link crypto-api!CryptoApi.getUserDeviceInfo}.
* Information on a user's device, as returned by {@link Crypto.CryptoApi.getUserDeviceInfo}.
*/
export declare class Device {
/** id of the device */
@@ -25,8 +25,6 @@ export declare class Device {
readonly signatures: Map<string, Map<string, string>>;
/** display name of the device */
readonly displayName?: string;
/** whether the device is a dehydrated device */
readonly dehydrated: boolean;
constructor(opts: DeviceParameters);
/**
* Get the fingerprint for this device (ie, the Ed25519 key)

View File

@@ -1 +1 @@
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/models/device.ts"],"names":[],"mappings":"AAgBA,+CAA+C;AAC/C,oBAAY,kBAAkB;IAC1B,OAAO,KAAK;IACZ,UAAU,IAAI;IACd,QAAQ,IAAI;CACf;AAED,gDAAgD;AAChD,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEzD,KAAK,gBAAgB,GAAG,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAEtG;;GAEG;AACH,qBAAa,MAAM;IACf,uBAAuB;IACvB,SAAgB,QAAQ,EAAE,MAAM,CAAC;IAEjC,0CAA0C;IAC1C,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,kDAAkD;IAClD,SAAgB,UAAU,EAAE,MAAM,EAAE,CAAC;IAErC,2DAA2D;IAC3D,SAAgB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE1C,+DAA+D;IAC/D,SAAgB,QAAQ,EAAE,kBAAkB,CAAC;IAE7C,4DAA4D;IAC5D,SAAgB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAE7D,iCAAiC;IACjC,SAAgB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErC,gDAAgD;IAChD,SAAgB,UAAU,EAAE,OAAO,CAAS;IAE5C,YAAmB,IAAI,EAAE,gBAAgB,EASxC;IAED;;;;OAIG;IACI,cAAc,IAAI,MAAM,GAAG,SAAS,CAE1C;IAED;;;;OAIG;IACI,cAAc,IAAI,MAAM,GAAG,SAAS,CAE1C;CACJ"}
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../src/models/device.ts"],"names":[],"mappings":"AAgBA,+CAA+C;AAC/C,oBAAY,kBAAkB;IAC1B,OAAO,KAAK;IACZ,UAAU,IAAI;IACd,QAAQ,IAAI;CACf;AAED,gDAAgD;AAChD,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEzD,KAAK,gBAAgB,GAAG,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAEtG;;GAEG;AACH,qBAAa,MAAM;IACf,uBAAuB;IACvB,SAAgB,QAAQ,EAAE,MAAM,CAAC;IAEjC,0CAA0C;IAC1C,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,kDAAkD;IAClD,SAAgB,UAAU,EAAE,MAAM,EAAE,CAAC;IAErC,2DAA2D;IAC3D,SAAgB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE1C,+DAA+D;IAC/D,SAAgB,QAAQ,EAAE,kBAAkB,CAAC;IAE7C,4DAA4D;IAC5D,SAAgB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAE7D,iCAAiC;IACjC,SAAgB,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,IAAI,EAAE,gBAAgB;IAUzC;;;;OAIG;IACI,cAAc,IAAI,MAAM,GAAG,SAAS;IAI3C;;;;OAIG;IACI,cAAc,IAAI,MAAM,GAAG,SAAS;CAG9C"}

View File

@@ -1,4 +1,11 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.DeviceVerification = exports.Device = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
/*
Copyright 2023 The Matrix.org Foundation C.I.C.
@@ -14,38 +21,34 @@ 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.
*/
/** State of the verification of the device. */
export let DeviceVerification = /*#__PURE__*/function (DeviceVerification) {
let DeviceVerification = /*#__PURE__*/function (DeviceVerification) {
DeviceVerification[DeviceVerification["Blocked"] = -1] = "Blocked";
DeviceVerification[DeviceVerification["Unverified"] = 0] = "Unverified";
DeviceVerification[DeviceVerification["Verified"] = 1] = "Verified";
return DeviceVerification;
}({});
/** A map from user ID to device ID to Device */
exports.DeviceVerification = DeviceVerification;
/**
* Information on a user's device, as returned by {@link crypto-api!CryptoApi.getUserDeviceInfo}.
* Information on a user's device, as returned by {@link Crypto.CryptoApi.getUserDeviceInfo}.
*/
export class Device {
class Device {
constructor(opts) {
/** id of the device */
_defineProperty(this, "deviceId", void 0);
(0, _defineProperty2.default)(this, "deviceId", void 0);
/** id of the user that owns the device */
_defineProperty(this, "userId", void 0);
(0, _defineProperty2.default)(this, "userId", void 0);
/** list of algorithms supported by this device */
_defineProperty(this, "algorithms", void 0);
(0, _defineProperty2.default)(this, "algorithms", void 0);
/** a map from `<key type>:<id> -> <base64-encoded key>` */
_defineProperty(this, "keys", void 0);
(0, _defineProperty2.default)(this, "keys", void 0);
/** whether the device has been verified/blocked by the user */
_defineProperty(this, "verified", void 0);
(0, _defineProperty2.default)(this, "verified", void 0);
/** a map `<userId, map<algorithm:device_id, signature>>` */
_defineProperty(this, "signatures", void 0);
(0, _defineProperty2.default)(this, "signatures", void 0);
/** display name of the device */
_defineProperty(this, "displayName", void 0);
/** whether the device is a dehydrated device */
_defineProperty(this, "dehydrated", false);
(0, _defineProperty2.default)(this, "displayName", void 0);
this.deviceId = opts.deviceId;
this.userId = opts.userId;
this.algorithms = opts.algorithms;
@@ -53,7 +56,6 @@ export class Device {
this.verified = opts.verified || DeviceVerification.Unverified;
this.signatures = opts.signatures || new Map();
this.displayName = opts.displayName;
this.dehydrated = !!opts.dehydrated;
}
/**
@@ -74,4 +76,5 @@ export class Device {
return this.keys.get(`curve25519:${this.deviceId}`);
}
}
exports.Device = Device;
//# sourceMappingURL=device.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"device.js","names":[],"sources":["../../src/models/device.ts"],"sourcesContent":["/*\nCopyright 2023 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/** State of the verification of the device. */\nexport enum DeviceVerification {\n Blocked = -1,\n Unverified = 0,\n Verified = 1,\n}\n\n/** A map from user ID to device ID to Device */\nexport type DeviceMap = Map<string, Map<string, Device>>;\n\ntype DeviceParameters = Pick<Device, \"deviceId\" | \"userId\" | \"algorithms\" | \"keys\"> & Partial<Device>;\n\n/**\n * Information on a user's device, as returned by {@link crypto-api!CryptoApi.getUserDeviceInfo}.\n */\nexport class Device {\n /** id of the device */\n public readonly deviceId: string;\n\n /** id of the user that owns the device */\n public readonly userId: string;\n\n /** list of algorithms supported by this device */\n public readonly algorithms: string[];\n\n /** a map from `<key type>:<id> -> <base64-encoded key>` */\n public readonly keys: Map<string, string>;\n\n /** whether the device has been verified/blocked by the user */\n public readonly verified: DeviceVerification;\n\n /** a map `<userId, map<algorithm:device_id, signature>>` */\n public readonly signatures: Map<string, Map<string, string>>;\n\n /** display name of the device */\n public readonly displayName?: string;\n\n /** whether the device is a dehydrated device */\n public readonly dehydrated: boolean = false;\n\n public constructor(opts: DeviceParameters) {\n this.deviceId = opts.deviceId;\n this.userId = opts.userId;\n this.algorithms = opts.algorithms;\n this.keys = opts.keys;\n this.verified = opts.verified || DeviceVerification.Unverified;\n this.signatures = opts.signatures || new Map();\n this.displayName = opts.displayName;\n this.dehydrated = !!opts.dehydrated;\n }\n\n /**\n * Get the fingerprint for this device (ie, the Ed25519 key)\n *\n * @returns base64-encoded fingerprint of this device\n */\n public getFingerprint(): string | undefined {\n return this.keys.get(`ed25519:${this.deviceId}`);\n }\n\n /**\n * Get the identity key for this device (ie, the Curve25519 key)\n *\n * @returns base64-encoded identity key of this device\n */\n public getIdentityKey(): string | undefined {\n return this.keys.get(`curve25519:${this.deviceId}`);\n }\n}\n"],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,WAAY,kBAAkB,0BAAlB,kBAAkB;EAAlB,kBAAkB,CAAlB,kBAAkB;EAAlB,kBAAkB,CAAlB,kBAAkB;EAAlB,kBAAkB,CAAlB,kBAAkB;EAAA,OAAlB,kBAAkB;AAAA;;AAM9B;;AAKA;AACA;AACA;AACA,OAAO,MAAM,MAAM,CAAC;EAyBT,WAAW,CAAC,IAAsB,EAAE;IAxB3C;IAAA;IAGA;IAAA;IAGA;IAAA;IAGA;IAAA;IAGA;IAAA;IAGA;IAAA;IAGA;IAAA;IAGA;IAAA,oCACsC,KAAK;IAGvC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ;IAC7B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM;IACzB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU;IACjC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI;IACrB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,kBAAkB,CAAC,UAAU;IAC9D,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,GAAG,CAAC,CAAC;IAC9C,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW;IACnC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU;EACvC;;EAEA;AACJ;AACA;AACA;AACA;EACW,cAAc,GAAuB;IACxC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,QAAQ,EAAE,CAAC;EACpD;;EAEA;AACJ;AACA;AACA;AACA;EACW,cAAc,GAAuB;IACxC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,IAAI,CAAC,QAAQ,EAAE,CAAC;EACvD;AACJ","ignoreList":[]}
{"version":3,"file":"device.js","names":["DeviceVerification","exports","Device","constructor","opts","_defineProperty2","default","deviceId","userId","algorithms","keys","verified","Unverified","signatures","Map","displayName","getFingerprint","get","getIdentityKey"],"sources":["../../src/models/device.ts"],"sourcesContent":["/*\nCopyright 2023 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/** State of the verification of the device. */\nexport enum DeviceVerification {\n Blocked = -1,\n Unverified = 0,\n Verified = 1,\n}\n\n/** A map from user ID to device ID to Device */\nexport type DeviceMap = Map<string, Map<string, Device>>;\n\ntype DeviceParameters = Pick<Device, \"deviceId\" | \"userId\" | \"algorithms\" | \"keys\"> & Partial<Device>;\n\n/**\n * Information on a user's device, as returned by {@link Crypto.CryptoApi.getUserDeviceInfo}.\n */\nexport class Device {\n /** id of the device */\n public readonly deviceId: string;\n\n /** id of the user that owns the device */\n public readonly userId: string;\n\n /** list of algorithms supported by this device */\n public readonly algorithms: string[];\n\n /** a map from `<key type>:<id> -> <base64-encoded key>` */\n public readonly keys: Map<string, string>;\n\n /** whether the device has been verified/blocked by the user */\n public readonly verified: DeviceVerification;\n\n /** a map `<userId, map<algorithm:device_id, signature>>` */\n public readonly signatures: Map<string, Map<string, string>>;\n\n /** display name of the device */\n public readonly displayName?: string;\n\n public constructor(opts: DeviceParameters) {\n this.deviceId = opts.deviceId;\n this.userId = opts.userId;\n this.algorithms = opts.algorithms;\n this.keys = opts.keys;\n this.verified = opts.verified || DeviceVerification.Unverified;\n this.signatures = opts.signatures || new Map();\n this.displayName = opts.displayName;\n }\n\n /**\n * Get the fingerprint for this device (ie, the Ed25519 key)\n *\n * @returns base64-encoded fingerprint of this device\n */\n public getFingerprint(): string | undefined {\n return this.keys.get(`ed25519:${this.deviceId}`);\n }\n\n /**\n * Get the identity key for this device (ie, the Curve25519 key)\n *\n * @returns base64-encoded identity key of this device\n */\n public getIdentityKey(): string | undefined {\n return this.keys.get(`curve25519:${this.deviceId}`);\n }\n}\n"],"mappings":";;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAA,IACYA,kBAAkB,0BAAlBA,kBAAkB;EAAlBA,kBAAkB,CAAlBA,kBAAkB;EAAlBA,kBAAkB,CAAlBA,kBAAkB;EAAlBA,kBAAkB,CAAlBA,kBAAkB;EAAA,OAAlBA,kBAAkB;AAAA;AAM9B;AAAAC,OAAA,CAAAD,kBAAA,GAAAA,kBAAA;AAKA;AACA;AACA;AACO,MAAME,MAAM,CAAC;EAsBTC,WAAWA,CAACC,IAAsB,EAAE;IArB3C;IAAA,IAAAC,gBAAA,CAAAC,OAAA;IAGA;IAAA,IAAAD,gBAAA,CAAAC,OAAA;IAGA;IAAA,IAAAD,gBAAA,CAAAC,OAAA;IAGA;IAAA,IAAAD,gBAAA,CAAAC,OAAA;IAGA;IAAA,IAAAD,gBAAA,CAAAC,OAAA;IAGA;IAAA,IAAAD,gBAAA,CAAAC,OAAA;IAGA;IAAA,IAAAD,gBAAA,CAAAC,OAAA;IAII,IAAI,CAACC,QAAQ,GAAGH,IAAI,CAACG,QAAQ;IAC7B,IAAI,CAACC,MAAM,GAAGJ,IAAI,CAACI,MAAM;IACzB,IAAI,CAACC,UAAU,GAAGL,IAAI,CAACK,UAAU;IACjC,IAAI,CAACC,IAAI,GAAGN,IAAI,CAACM,IAAI;IACrB,IAAI,CAACC,QAAQ,GAAGP,IAAI,CAACO,QAAQ,IAAIX,kBAAkB,CAACY,UAAU;IAC9D,IAAI,CAACC,UAAU,GAAGT,IAAI,CAACS,UAAU,IAAI,IAAIC,GAAG,CAAC,CAAC;IAC9C,IAAI,CAACC,WAAW,GAAGX,IAAI,CAACW,WAAW;EACvC;;EAEA;AACJ;AACA;AACA;AACA;EACWC,cAAcA,CAAA,EAAuB;IACxC,OAAO,IAAI,CAACN,IAAI,CAACO,GAAG,CAAE,WAAU,IAAI,CAACV,QAAS,EAAC,CAAC;EACpD;;EAEA;AACJ;AACA;AACA;AACA;EACWW,cAAcA,CAAA,EAAuB;IACxC,OAAO,IAAI,CAACR,IAAI,CAACO,GAAG,CAAE,cAAa,IAAI,CAACV,QAAS,EAAC,CAAC;EACvD;AACJ;AAACN,OAAA,CAAAC,MAAA,GAAAA,MAAA"}

View File

@@ -1,4 +1,4 @@
import { type MatrixEvent } from "./event.ts";
import { MatrixEvent } from "./event";
export declare class EventContext {
readonly ourEvent: MatrixEvent;
private timeline;

View File

@@ -1 +1 @@
{"version":3,"file":"event-context.d.ts","sourceRoot":"","sources":["../../src/models/event-context.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,qBAAa,YAAY;aAoBc,QAAQ,EAAE,WAAW;IAnBxD,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAGpB;IAEF;;;;;;;;;;;OAWG;IACH,YAAmC,QAAQ,EAAE,WAAW,EAEvD;IAED;;;;;;OAMG;IACI,QAAQ,IAAI,WAAW,CAE7B;IAED;;;;OAIG;IACI,WAAW,IAAI,WAAW,EAAE,CAElC;IAED;;OAEG;IACI,gBAAgB,IAAI,MAAM,CAEhC;IAED;;;;OAIG;IACI,gBAAgB,CAAC,SAAS,UAAQ,GAAG,MAAM,GAAG,IAAI,CAExD;IAED;;;;;;;;OAQG;IACI,gBAAgB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,IAAI,CAE/D;IAED;;;;;OAKG;IACI,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,OAAO,UAAQ,GAAG,IAAI,CAU7D;CACJ"}
{"version":3,"file":"event-context.d.ts","sourceRoot":"","sources":["../../src/models/event-context.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGtC,qBAAa,YAAY;aAoBc,QAAQ,EAAE,WAAW;IAnBxD,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAGpB;IAEF;;;;;;;;;;;OAWG;gBACgC,QAAQ,EAAE,WAAW;IAIxD;;;;;;OAMG;IACI,QAAQ,IAAI,WAAW;IAI9B;;;;OAIG;IACI,WAAW,IAAI,WAAW,EAAE;IAInC;;OAEG;IACI,gBAAgB,IAAI,MAAM;IAIjC;;;;OAIG;IACI,gBAAgB,CAAC,SAAS,UAAQ,GAAG,MAAM,GAAG,IAAI;IAIzD;;;;;;;;OAQG;IACI,gBAAgB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,IAAI;IAIhE;;;;;OAKG;IACI,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,OAAO,UAAQ,GAAG,IAAI;CAWjE"}

View File

@@ -1,4 +1,12 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.EventContext = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _eventTimeline = require("./event-timeline");
/*
Copyright 2015 - 2021 The Matrix.org Foundation C.I.C.
@@ -15,8 +23,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { Direction } from "./event-timeline.js";
export class EventContext {
class EventContext {
/**
* Construct a new EventContext
*
@@ -30,13 +37,13 @@ export class EventContext {
* @param ourEvent - the event at the centre of this context
*/
constructor(ourEvent) {
_defineProperty(this, "timeline", void 0);
_defineProperty(this, "ourEventIndex", 0);
_defineProperty(this, "paginateTokens", {
[Direction.Backward]: null,
[Direction.Forward]: null
});
this.ourEvent = ourEvent;
(0, _defineProperty2.default)(this, "timeline", void 0);
(0, _defineProperty2.default)(this, "ourEventIndex", 0);
(0, _defineProperty2.default)(this, "paginateTokens", {
[_eventTimeline.Direction.Backward]: null,
[_eventTimeline.Direction.Forward]: null
});
this.timeline = [ourEvent];
}
@@ -73,7 +80,7 @@ export class EventContext {
* @param backwards - true to get the pagination token for going
*/
getPaginateToken(backwards = false) {
return this.paginateTokens[backwards ? Direction.Backward : Direction.Forward];
return this.paginateTokens[backwards ? _eventTimeline.Direction.Backward : _eventTimeline.Direction.Forward];
}
/**
@@ -86,7 +93,7 @@ export class EventContext {
* backwards in time
*/
setPaginateToken(token, backwards = false) {
this.paginateTokens[backwards ? Direction.Backward : Direction.Forward] = token ?? null;
this.paginateTokens[backwards ? _eventTimeline.Direction.Backward : _eventTimeline.Direction.Forward] = token !== null && token !== void 0 ? token : null;
}
/**
@@ -107,4 +114,5 @@ export class EventContext {
}
}
}
exports.EventContext = EventContext;
//# sourceMappingURL=event-context.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.EventStatus = void 0;
/*
Copyright 2015 - 2022 The Matrix.org Foundation C.I.C.
@@ -13,24 +19,18 @@ 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.
*/
/**
* Enum for event statuses.
* @readonly
*/
export let EventStatus = /*#__PURE__*/function (EventStatus) {
/** The event was not sent and will no longer be retried. */
let EventStatus = /*#__PURE__*/function (EventStatus) {
EventStatus["NOT_SENT"] = "not_sent";
/** The message is being encrypted */
EventStatus["ENCRYPTING"] = "encrypting";
/** The event is in the process of being sent. */
EventStatus["SENDING"] = "sending";
/** The event is in a queue waiting to be sent. */
EventStatus["QUEUED"] = "queued";
/** The event has been sent to the server, but we have not yet received the echo. */
EventStatus["SENT"] = "sent";
/** The event was cancelled before it was successfully sent. */
EventStatus["CANCELLED"] = "cancelled";
return EventStatus;
}({});
exports.EventStatus = EventStatus;
//# sourceMappingURL=event-status.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"event-status.js","names":[],"sources":["../../src/models/event-status.ts"],"sourcesContent":["/*\nCopyright 2015 - 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Enum for event statuses.\n * @readonly\n */\nexport enum EventStatus {\n /** The event was not sent and will no longer be retried. */\n NOT_SENT = \"not_sent\",\n\n /** The message is being encrypted */\n ENCRYPTING = \"encrypting\",\n\n /** The event is in the process of being sent. */\n SENDING = \"sending\",\n\n /** The event is in a queue waiting to be sent. */\n QUEUED = \"queued\",\n\n /** The event has been sent to the server, but we have not yet received the echo. */\n SENT = \"sent\",\n\n /** The event was cancelled before it was successfully sent. */\n CANCELLED = \"cancelled\",\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,WAAY,WAAW,0BAAX,WAAW;EACnB;EADQ,WAAW;EAInB;EAJQ,WAAW;EAOnB;EAPQ,WAAW;EAUnB;EAVQ,WAAW;EAanB;EAbQ,WAAW;EAgBnB;EAhBQ,WAAW;EAAA,OAAX,WAAW;AAAA","ignoreList":[]}
{"version":3,"file":"event-status.js","names":["EventStatus","exports"],"sources":["../../src/models/event-status.ts"],"sourcesContent":["/*\nCopyright 2015 - 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Enum for event statuses.\n * @readonly\n */\nexport enum EventStatus {\n /** The event was not sent and will no longer be retried. */\n NOT_SENT = \"not_sent\",\n\n /** The message is being encrypted */\n ENCRYPTING = \"encrypting\",\n\n /** The event is in the process of being sent. */\n SENDING = \"sending\",\n\n /** The event is in a queue waiting to be sent. */\n QUEUED = \"queued\",\n\n /** The event has been sent to the server, but we have not yet received the echo. */\n SENT = \"sent\",\n\n /** The event was cancelled before it was successfully sent. */\n CANCELLED = \"cancelled\",\n}\n"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAHA,IAIYA,WAAW,0BAAXA,WAAW;EAAXA,WAAW;EAAXA,WAAW;EAAXA,WAAW;EAAXA,WAAW;EAAXA,WAAW;EAAXA,WAAW;EAAA,OAAXA,WAAW;AAAA;AAAAC,OAAA,CAAAD,WAAA,GAAAA,WAAA"}

View File

@@ -1,12 +1,12 @@
import { EventTimeline, type IAddEventOptions } from "./event-timeline.ts";
import { type MatrixEvent } from "./event.ts";
import { type Room, RoomEvent } from "./room.ts";
import { type Filter } from "../filter.ts";
import { type RoomState } from "./room-state.ts";
import { TypedEventEmitter } from "./typed-event-emitter.ts";
import { RelationsContainer } from "./relations-container.ts";
import { type MatrixClient } from "../client.ts";
import { type Thread, type ThreadFilterType } from "./thread.ts";
import { EventTimeline, IAddEventOptions } from "./event-timeline";
import { MatrixEvent } from "./event";
import { Room, RoomEvent } from "./room";
import { Filter } from "../filter";
import { RoomState } from "./room-state";
import { TypedEventEmitter } from "./typed-event-emitter";
import { RelationsContainer } from "./relations-container";
import { MatrixClient } from "../client";
import { Thread, ThreadFilterType } from "./thread";
interface IOpts {
timelineSupport?: boolean;
filter?: Filter;
@@ -20,11 +20,11 @@ export interface IRoomTimelineData {
timeline: EventTimeline;
liveEvent?: boolean;
}
export interface IAddEventToTimelineOptions extends Pick<IAddEventOptions, "toStartOfTimeline" | "roomState" | "timelineWasEmpty" | "addToState"> {
export interface IAddEventToTimelineOptions extends Pick<IAddEventOptions, "toStartOfTimeline" | "roomState" | "timelineWasEmpty"> {
/** Whether the sync response came from cache */
fromCache?: boolean;
}
export interface IAddLiveEventOptions extends Pick<IAddEventToTimelineOptions, "fromCache" | "roomState" | "timelineWasEmpty" | "addToState"> {
export interface IAddLiveEventOptions extends Pick<IAddEventToTimelineOptions, "fromCache" | "roomState" | "timelineWasEmpty"> {
/** Applies to events in the timeline only. If this is 'replace' then if a
* duplicate is encountered, the event passed to this function will replace
* the existing event in the timeline. If this is not specified, or is
@@ -68,7 +68,7 @@ export type EventTimelineSetHandlerMap = {
* @param timelineSet - timelineSet room whose live timeline was reset
* @param resetAllTimelines - True if all timelines were reset.
*/
[RoomEvent.TimelineReset]: (room: Room | undefined, timelineSet: EventTimelineSet, resetAllTimelines: boolean) => void;
[RoomEvent.TimelineReset]: (room: Room | undefined, eventTimelineSet: EventTimelineSet, resetAllTimelines: boolean) => void;
};
export declare class EventTimelineSet extends TypedEventEmitter<EmittedEvents, EventTimelineSetHandlerMap> {
readonly room: Room | undefined;
@@ -108,7 +108,7 @@ export declare class EventTimelineSet extends TypedEventEmitter<EmittedEvents, E
* @param client - the Matrix client which owns this EventTimelineSet,
* can be omitted if room is specified.
* @param thread - the thread to which this timeline set relates.
* @param threadListType - the type of thread list represented, if any
* @param isThreadTimeline - Whether this timeline set relates to a thread list timeline
* (e.g., All threads or My threads)
*/
constructor(room: Room | undefined, opts?: IOpts, client?: MatrixClient, thread?: Thread | undefined, threadListType?: ThreadFilterType | null);
@@ -217,28 +217,34 @@ export declare class EventTimelineSet extends TypedEventEmitter<EmittedEvents, E
* Fires {@link RoomEvent.Timeline}
*
*/
addEventsToTimeline(events: MatrixEvent[], toStartOfTimeline: boolean, addToState: boolean, timeline: EventTimeline, paginationToken?: string | null): void;
addEventsToTimeline(events: MatrixEvent[], toStartOfTimeline: boolean, timeline: EventTimeline, paginationToken?: string | null): void;
/**
* Add an event to the end of this live timeline.
*
* @param event - Event to be added
* @param options - addLiveEvent options
*/
addLiveEvent(event: MatrixEvent, { duplicateStrategy, fromCache, roomState, timelineWasEmpty, addToState }: IAddLiveEventOptions): void;
addLiveEvent(event: MatrixEvent, { duplicateStrategy, fromCache, roomState, timelineWasEmpty }: IAddLiveEventOptions): void;
/**
* @deprecated In favor of the overload with `IAddLiveEventOptions`
*/
addLiveEvent(event: MatrixEvent, duplicateStrategy?: DuplicateStrategy, fromCache?: boolean, roomState?: RoomState): void;
/**
* Add event to the given timeline, and emit Room.timeline. Assumes
* we have already checked we don't know about this event.
*
* Will fire "Room.timeline" for each event added.
*
* @param event - the event to add
* @param timeline - the timeline onto which to add it
* @param options - addEventToTimeline options
*
* @remarks
* Fires {@link RoomEvent.Timeline}
*/
addEventToTimeline(event: MatrixEvent, timeline: EventTimeline, { toStartOfTimeline, fromCache, roomState, timelineWasEmpty, addToState }: IAddEventToTimelineOptions): void;
addEventToTimeline(event: MatrixEvent, timeline: EventTimeline, { toStartOfTimeline, fromCache, roomState, timelineWasEmpty }: IAddEventToTimelineOptions): void;
/**
* @deprecated In favor of the overload with `IAddEventToTimelineOptions`
*/
addEventToTimeline(event: MatrixEvent, timeline: EventTimeline, toStartOfTimeline: boolean, fromCache?: boolean, roomState?: RoomState): void;
/**
* Insert event to the given timeline, and emit Room.timeline. Assumes
* we have already checked we don't know about this event.
@@ -254,10 +260,12 @@ export declare class EventTimelineSet extends TypedEventEmitter<EmittedEvents, E
*
* @internal
*
* @param options - addEventToTimeline options
*
* @remarks
* Fires {@link RoomEvent.Timeline}
*/
insertEventIntoTimeline(event: MatrixEvent, timeline: EventTimeline, roomState: RoomState, addToState: boolean): void;
insertEventIntoTimeline(event: MatrixEvent, timeline: EventTimeline, roomState: RoomState): void;
/**
* Replaces event with ID oldEventId with one with newEventId, if oldEventId is
* recognised. Otherwise, add to the live timeline. Used to handle remote echos.
@@ -285,10 +293,11 @@ export declare class EventTimelineSet extends TypedEventEmitter<EmittedEvents, E
* @param eventId1 - The id of the first event
* @param eventId2 - The id of the second event
* @returns -1 if eventId1 precedes eventId2, and +1 eventId1 succeeds
* eventId2. 0 if they are the same event; null if we can't tell (either
* because we don't know about one of the events, or because they are in
* separate timelines which don't join up).
* @returns a number less than zero if eventId1 precedes eventId2, and
* greater than zero if eventId1 succeeds eventId2. zero if they are the
* same event; null if we can't tell (either because we don't know about one
* of the events, or because they are in separate timelines which don't join
* up).
*/
compareEventOrdering(eventId1: string, eventId2: string): number | null;
/**

View File

@@ -1 +1 @@
{"version":3,"file":"event-timeline-set.d.ts","sourceRoot":"","sources":["../../src/models/event-timeline-set.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"event-timeline-set.d.ts","sourceRoot":"","sources":["../../src/models/event-timeline-set.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAcpD,UAAU,KAAK;IAEX,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,oBAAY,iBAAiB;IACzB,MAAM,WAAW;IACjB,OAAO,YAAY;CACtB;AAED,MAAM,WAAW,iBAAiB;IAE9B,QAAQ,EAAE,aAAa,CAAC;IAExB,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,0BACb,SAAQ,IAAI,CAAC,gBAAgB,EAAE,mBAAmB,GAAG,WAAW,GAAG,kBAAkB,CAAC;IACtF,gDAAgD;IAChD,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,oBACb,SAAQ,IAAI,CAAC,0BAA0B,EAAE,WAAW,GAAG,WAAW,GAAG,kBAAkB,CAAC;IACxF;;;;;wDAKoD;IACpD,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;CACzC;AAED,KAAK,aAAa,GAAG,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,aAAa,CAAC;AAElE,MAAM,MAAM,0BAA0B,GAAG;IACrC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAClB,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,IAAI,GAAG,SAAS,EACtB,iBAAiB,EAAE,OAAO,GAAG,SAAS,EACtC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,iBAAiB,KACtB,IAAI,CAAC;IACV;;;;;;;;;;;OAWG;IACH,CAAC,SAAS,CAAC,aAAa,CAAC,EAAE,CACvB,IAAI,EAAE,IAAI,GAAG,SAAS,EACtB,gBAAgB,EAAE,gBAAgB,EAClC,iBAAiB,EAAE,OAAO,KACzB,IAAI,CAAC;CACb,CAAC;AAEF,qBAAa,gBAAiB,SAAQ,iBAAiB,CAAC,aAAa,EAAE,0BAA0B,CAAC;aAwC1E,IAAI,EAAE,IAAI,GAAG,SAAS;aAGtB,MAAM,CAAC;aACP,cAAc,EAAE,gBAAgB,GAAG,IAAI;IA3C3D,SAAgB,SAAS,EAAE,kBAAkB,CAAC;IAC9C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAC1C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAU;IAC/C,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,kBAAkB,CAAoC;IAC9D,OAAO,CAAC,MAAM,CAAC,CAAS;IAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;gBAEiB,IAAI,EAAE,IAAI,GAAG,SAAS,EACtC,IAAI,GAAE,KAAU,EAChB,MAAM,CAAC,EAAE,YAAY,EACL,MAAM,CAAC,oBAAQ,EACf,cAAc,GAAE,gBAAgB,GAAG,IAAW;IAiBlE;;;OAGG;IACI,YAAY,IAAI,aAAa,EAAE;IAItC;;;OAGG;IACI,SAAS,IAAI,MAAM,GAAG,SAAS;IAItC;;;;OAIG;IACI,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAIvC;;;;;;;;OAQG;IACI,gBAAgB,IAAI,WAAW,EAAE;IAOxC;;;;OAIG;IACI,eAAe,IAAI,aAAa;IAIvC;;;;OAIG;IACI,eAAe,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAIrD;;;;OAIG;IACI,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIpE;;;;;OAKG;IACI,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAQnE;;;;;;;;;;;OAWG;IACI,iBAAiB,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI;IAwC7F;;;;;;OAMG;IACI,mBAAmB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAQlE;;;;;OAKG;IACI,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAU9D;;;;OAIG;IACI,WAAW,IAAI,aAAa;IAcnC;;;;;;;;;;;;;;;;;;;OAmBG;IACI,mBAAmB,CACtB,MAAM,EAAE,WAAW,EAAE,EACrB,iBAAiB,EAAE,OAAO,EAC1B,QAAQ,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,GAChC,IAAI;IA+LP;;;;;OAKG;IACI,YAAY,CACf,KAAK,EAAE,WAAW,EAClB,EAAE,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAE,gBAAgB,EAAE,EAAE,oBAAoB,GACpF,IAAI;IACP;;OAEG;IACI,YAAY,CACf,KAAK,EAAE,WAAW,EAClB,iBAAiB,CAAC,EAAE,iBAAiB,EACrC,SAAS,CAAC,EAAE,OAAO,EACnB,SAAS,CAAC,EAAE,SAAS,GACtB,IAAI;IAkEP;;;;;;;;;;OAUG;IACI,kBAAkB,CACrB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,aAAa,EACvB,EAAE,iBAAiB,EAAE,SAAS,EAAE,SAAS,EAAE,gBAAgB,EAAE,EAAE,0BAA0B,GAC1F,IAAI;IACP;;OAEG;IACI,kBAAkB,CACrB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,aAAa,EACvB,iBAAiB,EAAE,OAAO,EAC1B,SAAS,CAAC,EAAE,OAAO,EACnB,SAAS,CAAC,EAAE,SAAS,GACtB,IAAI;IA+DP;;;;;;;;;;;;;;;;;;;OAmBG;IACI,uBAAuB,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI;IAoEvG;;;;;;;;;;OAUG;IACI,gBAAgB,CAAC,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAa9F;;;;;;;OAOG;IACI,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAiBvD;;;;;;;;;;;OAWG;IACI,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IA4D9E;;;;;;;;;;OAUG;IACI,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO;CAwBjD"}

View File

@@ -1,4 +1,16 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.EventTimelineSet = exports.DuplicateStrategy = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _eventTimeline = require("./event-timeline");
var _logger = require("../logger");
var _room = require("./room");
var _typedEventEmitter = require("./typed-event-emitter");
var _relationsContainer = require("./relations-container");
/*
Copyright 2016 - 2021 The Matrix.org Foundation C.I.C.
@@ -15,28 +27,24 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { EventTimeline } from "./event-timeline.js";
import { logger } from "../logger.js";
import { RoomEvent } from "./room.js";
import { TypedEventEmitter } from "./typed-event-emitter.js";
import { RelationsContainer } from "./relations-container.js";
const DEBUG = true;
/* istanbul ignore next */
let debuglog;
if (DEBUG) {
// using bind means that we get to keep useful line numbers in the console
debuglog = logger.log.bind(logger);
debuglog = _logger.logger.log.bind(_logger.logger);
} else {
/* istanbul ignore next */
debuglog = function () {};
}
export let DuplicateStrategy = /*#__PURE__*/function (DuplicateStrategy) {
let DuplicateStrategy = /*#__PURE__*/function (DuplicateStrategy) {
DuplicateStrategy["Ignore"] = "ignore";
DuplicateStrategy["Replace"] = "replace";
return DuplicateStrategy;
}({});
export class EventTimelineSet extends TypedEventEmitter {
exports.DuplicateStrategy = DuplicateStrategy;
class EventTimelineSet extends _typedEventEmitter.TypedEventEmitter {
/**
* Construct a set of EventTimeline objects, typically on behalf of a given
* room. A room may have multiple EventTimelineSets for different levels
@@ -64,30 +72,31 @@ export class EventTimelineSet extends TypedEventEmitter {
* @param client - the Matrix client which owns this EventTimelineSet,
* can be omitted if room is specified.
* @param thread - the thread to which this timeline set relates.
* @param threadListType - the type of thread list represented, if any
* @param isThreadTimeline - Whether this timeline set relates to a thread list timeline
* (e.g., All threads or My threads)
*/
constructor(room, opts = {}, client, thread, threadListType = null) {
var _this$room$relations, _this$room, _room$client;
super();
_defineProperty(this, "relations", void 0);
_defineProperty(this, "timelineSupport", void 0);
_defineProperty(this, "displayPendingEvents", void 0);
_defineProperty(this, "liveTimeline", void 0);
_defineProperty(this, "timelines", void 0);
_defineProperty(this, "_eventIdToTimeline", new Map());
_defineProperty(this, "filter", void 0);
this.room = room;
this.thread = thread;
this.threadListType = threadListType;
(0, _defineProperty2.default)(this, "relations", void 0);
(0, _defineProperty2.default)(this, "timelineSupport", void 0);
(0, _defineProperty2.default)(this, "displayPendingEvents", void 0);
(0, _defineProperty2.default)(this, "liveTimeline", void 0);
(0, _defineProperty2.default)(this, "timelines", void 0);
(0, _defineProperty2.default)(this, "_eventIdToTimeline", new Map());
(0, _defineProperty2.default)(this, "filter", void 0);
this.timelineSupport = Boolean(opts.timelineSupport);
this.liveTimeline = new EventTimeline(this);
this.liveTimeline = new _eventTimeline.EventTimeline(this);
this.displayPendingEvents = opts.pendingEvents !== false;
// just a list - *not* ordered.
this.timelines = [this.liveTimeline];
this._eventIdToTimeline = new Map();
this.filter = opts.filter;
this.relations = this.room?.relations ?? new RelationsContainer(room?.client ?? client);
this.relations = (_this$room$relations = (_this$room = this.room) === null || _this$room === void 0 ? void 0 : _this$room.relations) !== null && _this$room$relations !== void 0 ? _this$room$relations : new _relationsContainer.RelationsContainer((_room$client = room === null || room === void 0 ? void 0 : room.client) !== null && _room$client !== void 0 ? _room$client : client);
}
/**
@@ -195,7 +204,7 @@ export class EventTimelineSet extends TypedEventEmitter {
// if timeline support is disabled, forget about the old timelines
const resetAllTimelines = !this.timelineSupport || !forwardPaginationToken;
const oldTimeline = this.liveTimeline;
const newTimeline = resetAllTimelines ? oldTimeline.forkLive(EventTimeline.FORWARDS) : oldTimeline.fork(EventTimeline.FORWARDS);
const newTimeline = resetAllTimelines ? oldTimeline.forkLive(_eventTimeline.EventTimeline.FORWARDS) : oldTimeline.fork(_eventTimeline.EventTimeline.FORWARDS);
if (resetAllTimelines) {
this.timelines = [newTimeline];
this._eventIdToTimeline = new Map();
@@ -205,17 +214,17 @@ export class EventTimelineSet extends TypedEventEmitter {
if (forwardPaginationToken) {
// Now set the forward pagination token on the old live timeline
// so it can be forward-paginated.
oldTimeline.setPaginationToken(forwardPaginationToken, EventTimeline.FORWARDS);
oldTimeline.setPaginationToken(forwardPaginationToken, _eventTimeline.EventTimeline.FORWARDS);
}
// make sure we set the pagination token before firing timelineReset,
// otherwise clients which start back-paginating will fail, and then get
// stuck without realising that they *can* back-paginate.
newTimeline.setPaginationToken(backPaginationToken ?? null, EventTimeline.BACKWARDS);
newTimeline.setPaginationToken(backPaginationToken !== null && backPaginationToken !== void 0 ? backPaginationToken : null, _eventTimeline.EventTimeline.BACKWARDS);
// Now we can swap the live timeline to the new one.
this.liveTimeline = newTimeline;
this.emit(RoomEvent.TimelineReset, this.room, this, resetAllTimelines);
this.emit(_room.RoomEvent.TimelineReset, this.room, this, resetAllTimelines);
}
/**
@@ -258,7 +267,7 @@ export class EventTimelineSet extends TypedEventEmitter {
if (!this.timelineSupport) {
throw new Error("timeline support is disabled. Set the 'timelineSupport'" + " parameter to true when creating MatrixClient to enable" + " it.");
}
const timeline = new EventTimeline(this);
const timeline = new _eventTimeline.EventTimeline(this);
this.timelines.push(timeline);
return timeline;
}
@@ -283,7 +292,7 @@ export class EventTimelineSet extends TypedEventEmitter {
* Fires {@link RoomEvent.Timeline}
*
*/
addEventsToTimeline(events, toStartOfTimeline, addToState, timeline, paginationToken) {
addEventsToTimeline(events, toStartOfTimeline, timeline, paginationToken) {
if (!timeline) {
throw new Error("'timeline' not specified for EventTimelineSet.addEventsToTimeline");
}
@@ -296,8 +305,8 @@ export class EventTimelineSet extends TypedEventEmitter {
return;
}
}
const direction = toStartOfTimeline ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
const inverseDirection = toStartOfTimeline ? EventTimeline.FORWARDS : EventTimeline.BACKWARDS;
const direction = toStartOfTimeline ? _eventTimeline.EventTimeline.BACKWARDS : _eventTimeline.EventTimeline.FORWARDS;
const inverseDirection = toStartOfTimeline ? _eventTimeline.EventTimeline.FORWARDS : _eventTimeline.EventTimeline.BACKWARDS;
// Adding events to timelines can be quite complicated. The following
// illustrates some of the corner-cases.
@@ -376,8 +385,7 @@ export class EventTimelineSet extends TypedEventEmitter {
if (!existingTimeline) {
// we don't know about this event yet. Just add it to the timeline.
this.addEventToTimeline(event, timeline, {
toStartOfTimeline,
addToState
toStartOfTimeline
});
lastEventWasNew = true;
didUpdate = true;
@@ -400,33 +408,34 @@ export class EventTimelineSet extends TypedEventEmitter {
// that would happen, so I'm going to ignore it for now.
//
if (existingTimeline == neighbour) {
debuglog("Event " + eventId + " in neighbouring timeline - switching to " + existingTimeline);
debuglog("Event " + eventId + " in neighbouring timeline - " + "switching to " + existingTimeline);
} else {
debuglog("Event " + eventId + " already in a different timeline " + existingTimeline);
debuglog("Event " + eventId + " already in a different " + "timeline " + existingTimeline);
}
timeline = existingTimeline;
continue;
}
// time to join the timelines.
logger.info("Already have timeline for " + eventId + " - joining timeline " + timeline + " to " + existingTimeline);
_logger.logger.info("Already have timeline for " + eventId + " - joining timeline " + timeline + " to " + existingTimeline);
// Variables to keep the line length limited below.
const existingIsLive = existingTimeline === this.liveTimeline;
const timelineIsLive = timeline === this.liveTimeline;
const backwardsIsLive = direction === EventTimeline.BACKWARDS && existingIsLive;
const forwardsIsLive = direction === EventTimeline.FORWARDS && timelineIsLive;
const backwardsIsLive = direction === _eventTimeline.EventTimeline.BACKWARDS && existingIsLive;
const forwardsIsLive = direction === _eventTimeline.EventTimeline.FORWARDS && timelineIsLive;
if (backwardsIsLive || forwardsIsLive) {
// The live timeline should never be spliced into a non-live position.
// We use independent logging to better discover the problem at a glance.
if (backwardsIsLive) {
logger.warn("Refusing to set a preceding existingTimeLine on our " + "timeline as the existingTimeLine is live (" + existingTimeline + ")");
_logger.logger.warn("Refusing to set a preceding existingTimeLine on our " + "timeline as the existingTimeLine is live (" + existingTimeline + ")");
}
if (forwardsIsLive) {
logger.warn("Refusing to set our preceding timeline on a existingTimeLine " + "as our timeline is live (" + timeline + ")");
_logger.logger.warn("Refusing to set our preceding timeline on a existingTimeLine " + "as our timeline is live (" + timeline + ")");
}
continue; // abort splicing - try next event
}
timeline.setNeighbouringTimeline(existingTimeline, direction);
existingTimeline.setNeighbouringTimeline(timeline, inverseDirection);
timeline = existingTimeline;
@@ -437,15 +446,15 @@ export class EventTimelineSet extends TypedEventEmitter {
// new information, we update the pagination token for whatever
// timeline we ended up on.
if (lastEventWasNew || !didUpdate) {
if (direction === EventTimeline.FORWARDS && timeline === this.liveTimeline) {
logger.warn({
if (direction === _eventTimeline.EventTimeline.FORWARDS && timeline === this.liveTimeline) {
_logger.logger.warn({
lastEventWasNew,
didUpdate
}); // for debugging
logger.warn(`Refusing to set forwards pagination token of live timeline ${timeline} to ${paginationToken}`);
_logger.logger.warn(`Refusing to set forwards pagination token of live timeline ` + `${timeline} to ${paginationToken}`);
return;
}
timeline.setPaginationToken(paginationToken ?? null, direction);
timeline.setPaginationToken(paginationToken !== null && paginationToken !== void 0 ? paginationToken : null, direction);
}
}
@@ -455,13 +464,26 @@ export class EventTimelineSet extends TypedEventEmitter {
* @param event - Event to be added
* @param options - addLiveEvent options
*/
addLiveEvent(event, {
duplicateStrategy,
fromCache,
roomState,
timelineWasEmpty,
addToState
}) {
/**
* @deprecated In favor of the overload with `IAddLiveEventOptions`
*/
addLiveEvent(event, duplicateStrategyOrOpts, fromCache = false, roomState) {
let duplicateStrategy = duplicateStrategyOrOpts || DuplicateStrategy.Ignore;
let timelineWasEmpty;
if (typeof duplicateStrategyOrOpts === "object") {
({
duplicateStrategy = DuplicateStrategy.Ignore,
fromCache = false,
roomState,
timelineWasEmpty
} = duplicateStrategyOrOpts);
} else if (duplicateStrategyOrOpts !== undefined) {
// Deprecation warning
// FIXME: Remove after 2023-06-01 (technical debt)
_logger.logger.warn("Overload deprecated: " + "`EventTimelineSet.addLiveEvent(event, duplicateStrategy?, fromCache?, roomState?)` " + "is deprecated in favor of the overload with " + "`EventTimelineSet.addLiveEvent(event, IAddLiveEventOptions)`");
}
if (this.filter) {
const events = this.filter.filterRoomTimeline([event]);
if (!events.length) {
@@ -477,9 +499,9 @@ export class EventTimelineSet extends TypedEventEmitter {
if (tlEvents[j].getId() === event.getId()) {
// still need to set the right metadata on this event
if (!roomState) {
roomState = timeline.getState(EventTimeline.FORWARDS);
roomState = timeline.getState(_eventTimeline.EventTimeline.FORWARDS);
}
EventTimeline.setEventMetadata(event, roomState, false);
_eventTimeline.EventTimeline.setEventMetadata(event, roomState, false);
tlEvents[j] = event;
// XXX: we need to fire an event when this happens.
@@ -495,8 +517,7 @@ export class EventTimelineSet extends TypedEventEmitter {
toStartOfTimeline: false,
fromCache,
roomState,
timelineWasEmpty,
addToState
timelineWasEmpty
});
}
@@ -506,23 +527,35 @@ export class EventTimelineSet extends TypedEventEmitter {
*
* Will fire "Room.timeline" for each event added.
*
* @param event - the event to add
* @param timeline - the timeline onto which to add it
* @param options - addEventToTimeline options
*
* @remarks
* Fires {@link RoomEvent.Timeline}
*/
addEventToTimeline(event, timeline, {
toStartOfTimeline,
fromCache = false,
roomState,
timelineWasEmpty,
addToState
}) {
/**
* @deprecated In favor of the overload with `IAddEventToTimelineOptions`
*/
addEventToTimeline(event, timeline, toStartOfTimelineOrOpts, fromCache = false, roomState) {
let toStartOfTimeline = !!toStartOfTimelineOrOpts;
let timelineWasEmpty;
if (typeof toStartOfTimelineOrOpts === "object") {
({
toStartOfTimeline,
fromCache = false,
roomState,
timelineWasEmpty
} = toStartOfTimelineOrOpts);
} else if (toStartOfTimelineOrOpts !== undefined) {
// Deprecation warning
// FIXME: Remove after 2023-06-01 (technical debt)
_logger.logger.warn("Overload deprecated: " + "`EventTimelineSet.addEventToTimeline(event, timeline, toStartOfTimeline, fromCache?, roomState?)` " + "is deprecated in favor of the overload with " + "`EventTimelineSet.addEventToTimeline(event, timeline, IAddEventToTimelineOptions)`");
}
if (timeline.getTimelineSet() !== this) {
var _this$thread;
throw new Error(`EventTimelineSet.addEventToTimeline: Timeline=${timeline.toString()} does not belong " +
"in timelineSet(threadId=${this.thread?.id})`);
"in timelineSet(threadId=${(_this$thread = this.thread) === null || _this$thread === void 0 ? void 0 : _this$thread.id})`);
}
const eventId = event.getId();
this.relations.aggregateParentEvent(event);
@@ -534,25 +567,25 @@ export class EventTimelineSet extends TypedEventEmitter {
// We can only run this check for timelines with a `room` because `canContain`
// requires it
if (this.room && !this.canContain(event)) {
var _this$thread2;
let eventDebugString = `event=${eventId}`;
if (event.threadRootId) {
eventDebugString += `(belongs to thread=${event.threadRootId})`;
}
logger.warn(`EventTimelineSet.addEventToTimeline: Ignoring ${eventDebugString} that does not belong ` + `in timeline=${timeline.toString()} timelineSet(threadId=${this.thread?.id})`);
_logger.logger.warn(`EventTimelineSet.addEventToTimeline: Ignoring ${eventDebugString} that does not belong ` + `in timeline=${timeline.toString()} timelineSet(threadId=${(_this$thread2 = this.thread) === null || _this$thread2 === void 0 ? void 0 : _this$thread2.id})`);
return;
}
timeline.addEvent(event, {
toStartOfTimeline,
roomState,
timelineWasEmpty,
addToState
timelineWasEmpty
});
this._eventIdToTimeline.set(eventId, timeline);
const data = {
timeline: timeline,
liveEvent: !toStartOfTimeline && timeline == this.liveTimeline && !fromCache
};
this.emit(RoomEvent.Timeline, event, this.room, Boolean(toStartOfTimeline), false, data);
this.emit(_room.RoomEvent.Timeline, event, this.room, Boolean(toStartOfTimeline), false, data);
}
/**
@@ -570,13 +603,16 @@ export class EventTimelineSet extends TypedEventEmitter {
*
* @internal
*
* @param options - addEventToTimeline options
*
* @remarks
* Fires {@link RoomEvent.Timeline}
*/
insertEventIntoTimeline(event, timeline, roomState, addToState) {
insertEventIntoTimeline(event, timeline, roomState) {
if (timeline.getTimelineSet() !== this) {
var _this$thread3;
throw new Error(`EventTimelineSet.insertEventIntoTimeline: Timeline=${timeline.toString()} does not belong " +
"in timelineSet(threadId=${this.thread?.id})`);
"in timelineSet(threadId=${(_this$thread3 = this.thread) === null || _this$thread3 === void 0 ? void 0 : _this$thread3.id})`);
}
const eventId = event.getId();
this.relations.aggregateParentEvent(event);
@@ -588,11 +624,12 @@ export class EventTimelineSet extends TypedEventEmitter {
// We can only run this check for timelines with a `room` because `canContain`
// requires it
if (this.room && !this.canContain(event)) {
var _this$thread4;
let eventDebugString = `event=${eventId}`;
if (event.threadRootId) {
eventDebugString += `(belongs to thread=${event.threadRootId})`;
}
logger.warn(`EventTimelineSet.insertEventIntoTimeline: Ignoring ${eventDebugString} that does not belong ` + `in timeline=${timeline.toString()} timelineSet(threadId=${this.thread?.id})`);
_logger.logger.warn(`EventTimelineSet.insertEventIntoTimeline: Ignoring ${eventDebugString} that does not belong ` + `in timeline=${timeline.toString()} timelineSet(threadId=${(_this$thread4 = this.thread) === null || _this$thread4 === void 0 ? void 0 : _this$thread4.id})`);
return;
}
@@ -604,8 +641,7 @@ export class EventTimelineSet extends TypedEventEmitter {
toStartOfTimeline: false,
fromCache: false,
timelineWasEmpty: false,
roomState,
addToState
roomState
});
return;
}
@@ -626,16 +662,13 @@ export class EventTimelineSet extends TypedEventEmitter {
// If we got to the end of the loop, insertIndex points at the end of
// the list.
timeline.insertEvent(event, insertIndex, roomState, addToState);
timeline.insertEvent(event, insertIndex, roomState);
this._eventIdToTimeline.set(eventId, timeline);
const data = {
timeline: timeline,
// The purpose of this method is inserting events in the middle of the
// timeline, so the events are, by definition, not live (whether or not
// we're adding them to the live timeline).
liveEvent: false
liveEvent: timeline == this.liveTimeline
};
this.emit(RoomEvent.Timeline, event, this.room, false, false, data);
this.emit(_room.RoomEvent.Timeline, event, this.room, false, false, data);
}
/**
@@ -657,8 +690,7 @@ export class EventTimelineSet extends TypedEventEmitter {
this._eventIdToTimeline.set(newEventId, existingTimeline);
} else if (!this.filter || this.filter.filterRoomTimeline([localEvent]).length) {
this.addEventToTimeline(localEvent, this.liveTimeline, {
toStartOfTimeline: false,
addToState: false
toStartOfTimeline: false
});
}
}
@@ -682,7 +714,7 @@ export class EventTimelineSet extends TypedEventEmitter {
const data = {
timeline: timeline
};
this.emit(RoomEvent.Timeline, removed, this.room, undefined, true, data);
this.emit(_room.RoomEvent.Timeline, removed, this.room, undefined, true, data);
}
return removed;
}
@@ -692,10 +724,11 @@ export class EventTimelineSet extends TypedEventEmitter {
*
* @param eventId1 - The id of the first event
* @param eventId2 - The id of the second event
* @returns -1 if eventId1 precedes eventId2, and +1 eventId1 succeeds
* eventId2. 0 if they are the same event; null if we can't tell (either
* because we don't know about one of the events, or because they are in
* separate timelines which don't join up).
* @returns a number less than zero if eventId1 precedes eventId2, and
* greater than zero if eventId1 succeeds eventId2. zero if they are the
* same event; null if we can't tell (either because we don't know about one
* of the events, or because they are in separate timelines which don't join
* up).
*/
compareEventOrdering(eventId1, eventId2) {
if (eventId1 == eventId2) {
@@ -724,16 +757,7 @@ export class EventTimelineSet extends TypedEventEmitter {
idx2 = idx;
}
}
const difference = idx1 - idx2;
// Return the sign of difference.
if (difference < 0) {
return -1;
} else if (difference > 0) {
return 1;
} else {
return 0;
}
return idx1 - idx2;
}
// the events are in different timelines. Iterate through the
@@ -746,7 +770,7 @@ export class EventTimelineSet extends TypedEventEmitter {
// timeline1 is before timeline2
return -1;
}
tl = tl.getNeighbouringTimeline(EventTimeline.FORWARDS);
tl = tl.getNeighbouringTimeline(_eventTimeline.EventTimeline.FORWARDS);
}
// now try backwards from timeline1
@@ -756,7 +780,7 @@ export class EventTimelineSet extends TypedEventEmitter {
// timeline2 is before timeline1
return 1;
}
tl = tl.getNeighbouringTimeline(EventTimeline.BACKWARDS);
tl = tl.getNeighbouringTimeline(_eventTimeline.EventTimeline.BACKWARDS);
}
// the timelines are not contiguous.
@@ -787,9 +811,11 @@ export class EventTimelineSet extends TypedEventEmitter {
return this.thread.id === threadId;
}
if (!shouldLiveInRoom && !shouldLiveInThread) {
logger.warn(`EventTimelineSet:canContain event encountered which cannot be added to any timeline roomId=${this.room?.roomId} eventId=${event.getId()} threadId=${event.threadRootId}`);
var _this$room2;
_logger.logger.warn(`EventTimelineSet:canContain event encountered which cannot be added to any timeline roomId=${(_this$room2 = this.room) === null || _this$room2 === void 0 ? void 0 : _this$room2.roomId} eventId=${event.getId()} threadId=${event.threadRootId}`);
}
return shouldLiveInRoom;
}
}
exports.EventTimelineSet = EventTimelineSet;
//# sourceMappingURL=event-timeline-set.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
import { type IMarkerFoundOptions, RoomState } from "./room-state.ts";
import { type EventTimelineSet } from "./event-timeline-set.ts";
import { type MatrixEvent } from "./event.ts";
import { type Filter } from "../filter.ts";
import { IMarkerFoundOptions, RoomState } from "./room-state";
import { EventTimelineSet } from "./event-timeline-set";
import { MatrixEvent } from "./event";
import { Filter } from "../filter";
export interface IInitialiseStateOptions extends Pick<IMarkerFoundOptions, "timelineWasEmpty"> {
}
export interface IAddEventOptions extends Pick<IMarkerFoundOptions, "timelineWasEmpty"> {
@@ -11,11 +11,6 @@ export interface IAddEventOptions extends Pick<IMarkerFoundOptions, "timelineWas
toStartOfTimeline: boolean;
/** The state events to reconcile metadata from */
roomState?: RoomState;
/** Whether to add timeline events to the state as was done in legacy sync v2.
* If true then timeline events will be added to the state.
* In sync v2 with org.matrix.msc4222.use_state_after and simplified sliding sync,
* all state arrives explicitly and timeline events should not be added. */
addToState: boolean;
}
export declare enum Direction {
Backward = "b",
@@ -149,7 +144,7 @@ export declare class EventTimeline {
/**
* Get a pagination token
*
* @param direction - EventTimeline.BACKWARDS to get the pagination
* @param direction - EventTimeline.BACKWARDS to get the pagination
* token for going backwards in time; EventTimeline.FORWARDS to get the
* pagination token for going forwards in time.
*
@@ -194,7 +189,11 @@ export declare class EventTimeline {
* @param event - new event
* @param options - addEvent options
*/
addEvent(event: MatrixEvent, { toStartOfTimeline, roomState, timelineWasEmpty, addToState }: IAddEventOptions): void;
addEvent(event: MatrixEvent, { toStartOfTimeline, roomState, timelineWasEmpty }: IAddEventOptions): void;
/**
* @deprecated In favor of the overload with `IAddEventOptions`
*/
addEvent(event: MatrixEvent, toStartOfTimeline: boolean, roomState?: RoomState): void;
/**
* Insert a new event into the timeline, and update the state.
*
@@ -206,7 +205,7 @@ export declare class EventTimeline {
*
* @internal
*/
insertEvent(event: MatrixEvent, insertIndex: number, roomState: RoomState, addToState: boolean): void;
insertEvent(event: MatrixEvent, insertIndex: number, roomState: RoomState): void;
/**
* Remove an event from the timeline
*

View File

@@ -1 +1 @@
{"version":3,"file":"event-timeline.d.ts","sourceRoot":"","sources":["../../src/models/event-timeline.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"event-timeline.d.ts","sourceRoot":"","sources":["../../src/models/event-timeline.ts"],"names":[],"mappings":"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"}

View File

@@ -1,4 +1,14 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.EventTimeline = exports.Direction = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _logger = require("../logger");
var _roomState2 = require("./room-state");
var _event = require("../@types/event");
/*
Copyright 2016 - 2021 The Matrix.org Foundation C.I.C.
@@ -14,15 +24,13 @@ 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 { RoomState } from "./room-state.js";
import { EventType } from "../@types/event.js";
export let Direction = /*#__PURE__*/function (Direction) {
let Direction = /*#__PURE__*/function (Direction) {
Direction["Backward"] = "b";
Direction["Forward"] = "f";
return Direction;
}({});
export class EventTimeline {
exports.Direction = Direction;
class EventTimeline {
/**
* Static helper method to set sender and target properties
*
@@ -31,7 +39,27 @@ export class EventTimeline {
* @param toStartOfTimeline - if true the event's forwardLooking flag is set false
*/
static setEventMetadata(event, stateContext, toStartOfTimeline) {
event.setMetadata(stateContext, toStartOfTimeline);
var _event$sender, _event$target;
// When we try to generate a sentinel member before we have that member
// in the members object, we still generate a sentinel but it doesn't
// have a membership event, so test to see if events.member is set. We
// check this to avoid overriding non-sentinel members by sentinel ones
// when adding the event to a filtered timeline
if (!((_event$sender = event.sender) !== null && _event$sender !== void 0 && (_event$sender = _event$sender.events) !== null && _event$sender !== void 0 && _event$sender.member)) {
event.sender = stateContext.getSentinelMember(event.getSender());
}
if (!((_event$target = event.target) !== null && _event$target !== void 0 && (_event$target = _event$target.events) !== null && _event$target !== void 0 && _event$target.member) && event.getType() === _event.EventType.RoomMember) {
event.target = stateContext.getSentinelMember(event.getStateKey());
}
if (event.isState()) {
// room state has no concept of 'old' or 'current', but we want the
// room state to regress back to previous values if toStartOfTimeline
// is set, which means inspecting prev_content if it exists. This
// is done by toggling the forwardLooking flag.
if (toStartOfTimeline) {
event.forwardLooking = false;
}
}
}
/**
* Construct a new EventTimeline
@@ -53,27 +81,28 @@ export class EventTimeline {
* @param eventTimelineSet - the set of timelines this is part of
*/
constructor(eventTimelineSet) {
_defineProperty(this, "roomId", void 0);
_defineProperty(this, "name", void 0);
_defineProperty(this, "events", []);
_defineProperty(this, "baseIndex", 0);
_defineProperty(this, "startState", void 0);
_defineProperty(this, "endState", void 0);
var _eventTimelineSet$roo, _eventTimelineSet$roo2;
this.eventTimelineSet = eventTimelineSet;
(0, _defineProperty2.default)(this, "roomId", void 0);
(0, _defineProperty2.default)(this, "name", void 0);
(0, _defineProperty2.default)(this, "events", []);
(0, _defineProperty2.default)(this, "baseIndex", 0);
(0, _defineProperty2.default)(this, "startState", void 0);
(0, _defineProperty2.default)(this, "endState", void 0);
// If we have a roomId then we delegate pagination token storage to the room state objects `startState` and
// `endState`, but for things like the notification timeline which mix multiple rooms we store the tokens ourselves.
_defineProperty(this, "startToken", null);
_defineProperty(this, "endToken", null);
_defineProperty(this, "prevTimeline", null);
_defineProperty(this, "nextTimeline", null);
_defineProperty(this, "paginationRequests", {
(0, _defineProperty2.default)(this, "startToken", null);
(0, _defineProperty2.default)(this, "endToken", null);
(0, _defineProperty2.default)(this, "prevTimeline", null);
(0, _defineProperty2.default)(this, "nextTimeline", null);
(0, _defineProperty2.default)(this, "paginationRequests", {
[Direction.Backward]: null,
[Direction.Forward]: null
});
this.eventTimelineSet = eventTimelineSet;
this.roomId = eventTimelineSet.room?.roomId ?? null;
this.roomId = (_eventTimelineSet$roo = (_eventTimelineSet$roo2 = eventTimelineSet.room) === null || _eventTimelineSet$roo2 === void 0 ? void 0 : _eventTimelineSet$roo2.roomId) !== null && _eventTimelineSet$roo !== void 0 ? _eventTimelineSet$roo : null;
if (this.roomId) {
this.startState = new RoomState(this.roomId);
this.endState = new RoomState(this.roomId);
this.startState = new _roomState2.RoomState(this.roomId);
this.endState = new _roomState2.RoomState(this.roomId);
}
// this is used by client.js
@@ -96,13 +125,14 @@ export class EventTimeline {
initialiseState(stateEvents, {
timelineWasEmpty
} = {}) {
var _this$startState, _this$endState;
if (this.events.length > 0) {
throw new Error("Cannot initialise state after events are added");
}
this.startState?.setStateEvents(stateEvents, {
(_this$startState = this.startState) === null || _this$startState === void 0 ? void 0 : _this$startState.setStateEvents(stateEvents, {
timelineWasEmpty
});
this.endState?.setStateEvents(stateEvents, {
(_this$endState = this.endState) === null || _this$endState === void 0 ? void 0 : _this$endState.setStateEvents(stateEvents, {
timelineWasEmpty
});
}
@@ -121,7 +151,7 @@ export class EventTimeline {
forkLive(direction) {
const forkState = this.getState(direction);
const timeline = new EventTimeline(this.eventTimelineSet);
timeline.startState = forkState?.clone();
timeline.startState = forkState === null || forkState === void 0 ? void 0 : forkState.clone();
// Now clobber the end state of the new live timeline with that from the
// previous live timeline. It will be identical except that we'll keep
// using the same RoomMember objects for the 'live' set of members with any
@@ -129,7 +159,7 @@ export class EventTimeline {
timeline.endState = forkState;
// Firstly, we just stole the current timeline's end state, so it needs a new one.
// Make an immutable copy of the state so back pagination will get the correct sentinels.
this.endState = forkState?.clone();
this.endState = forkState === null || forkState === void 0 ? void 0 : forkState.clone();
return timeline;
}
@@ -145,8 +175,8 @@ export class EventTimeline {
fork(direction) {
const forkState = this.getState(direction);
const timeline = new EventTimeline(this.eventTimelineSet);
timeline.startState = forkState?.clone();
timeline.endState = forkState?.clone();
timeline.startState = forkState === null || forkState === void 0 ? void 0 : forkState.clone();
timeline.endState = forkState === null || forkState === void 0 ? void 0 : forkState.clone();
return timeline;
}
@@ -218,7 +248,7 @@ export class EventTimeline {
/**
* Get a pagination token
*
* @param direction - EventTimeline.BACKWARDS to get the pagination
* @param direction - EventTimeline.BACKWARDS to get the pagination
* token for going backwards in time; EventTimeline.FORWARDS to get the
* pagination token for going forwards in time.
*
@@ -305,12 +335,25 @@ export class EventTimeline {
* @param event - new event
* @param options - addEvent options
*/
addEvent(event, {
toStartOfTimeline,
roomState,
timelineWasEmpty,
addToState
}) {
/**
* @deprecated In favor of the overload with `IAddEventOptions`
*/
addEvent(event, toStartOfTimelineOrOpts, roomState) {
let toStartOfTimeline = !!toStartOfTimelineOrOpts;
let timelineWasEmpty;
if (typeof toStartOfTimelineOrOpts === "object") {
({
toStartOfTimeline,
roomState,
timelineWasEmpty
} = toStartOfTimelineOrOpts);
} else if (toStartOfTimelineOrOpts !== undefined) {
// Deprecation warning
// FIXME: Remove after 2023-06-01 (technical debt)
_logger.logger.warn("Overload deprecated: " + "`EventTimeline.addEvent(event, toStartOfTimeline, roomState?)` " + "is deprecated in favor of the overload with `EventTimeline.addEvent(event, IAddEventOptions)`");
}
if (!roomState) {
roomState = toStartOfTimeline ? this.startState : this.endState;
}
@@ -319,8 +362,9 @@ export class EventTimeline {
EventTimeline.setEventMetadata(event, roomState, toStartOfTimeline);
// modify state but only on unfiltered timelineSets
if (addToState && event.isState() && timelineSet.room.getUnfilteredTimelineSet() === timelineSet) {
roomState?.setStateEvents([event], {
if (event.isState() && timelineSet.room.getUnfilteredTimelineSet() === timelineSet) {
var _roomState;
(_roomState = roomState) === null || _roomState === void 0 ? void 0 : _roomState.setStateEvents([event], {
timelineWasEmpty
});
// it is possible that the act of setting the state event means we
@@ -333,7 +377,7 @@ export class EventTimeline {
// back in time, else we'll set the .sender value for BEFORE the given
// member event, whereas we want to set the .sender value for the ACTUAL
// member event itself.
if (!event.sender || event.getType() === EventType.RoomMember && !toStartOfTimeline) {
if (!event.sender || event.getType() === _event.EventType.RoomMember && !toStartOfTimeline) {
EventTimeline.setEventMetadata(event, roomState, toStartOfTimeline);
}
}
@@ -361,13 +405,13 @@ export class EventTimeline {
*
* @internal
*/
insertEvent(event, insertIndex, roomState, addToState) {
insertEvent(event, insertIndex, roomState) {
const timelineSet = this.getTimelineSet();
if (timelineSet.room) {
EventTimeline.setEventMetadata(event, roomState, false);
// modify state but only on unfiltered timelineSets
if (addToState && event.isState() && timelineSet.room.getUnfilteredTimelineSet() === timelineSet) {
if (event.isState() && timelineSet.room.getUnfilteredTimelineSet() === timelineSet) {
roomState.setStateEvents([event], {});
// it is possible that the act of setting the state event means we
// can set more metadata (specifically sender/target props), so try
@@ -379,7 +423,7 @@ export class EventTimeline {
// back in time, else we'll set the .sender value for BEFORE the given
// member event, whereas we want to set the .sender value for the ACTUAL
// member event itself.
if (!event.sender || event.getType() === EventType.RoomMember) {
if (!event.sender || event.getType() === _event.EventType.RoomMember) {
EventTimeline.setEventMetadata(event, roomState, false);
}
}
@@ -416,14 +460,15 @@ export class EventTimeline {
return this.name;
}
}
exports.EventTimeline = EventTimeline;
/**
* Symbolic constant for methods which take a 'direction' argument:
* refers to the start of the timeline, or backwards in time.
*/
_defineProperty(EventTimeline, "BACKWARDS", Direction.Backward);
(0, _defineProperty2.default)(EventTimeline, "BACKWARDS", Direction.Backward);
/**
* Symbolic constant for methods which take a 'direction' argument:
* refers to the end of the timeline, or forwards in time.
*/
_defineProperty(EventTimeline, "FORWARDS", Direction.Forward);
(0, _defineProperty2.default)(EventTimeline, "FORWARDS", Direction.Forward);
//# sourceMappingURL=event-timeline.js.map

File diff suppressed because one or more lines are too long

View File

@@ -2,26 +2,23 @@
* This is an internal module. See {@link MatrixEvent} and {@link RoomEvent} for
* the public classes.
*/
import { type ExtensibleEvent } from "matrix-events-sdk";
import { EventType, type MsgType, RelationType, UNSIGNED_THREAD_ID_FIELD } from "../@types/event.ts";
import { type RoomMember } from "./room-member.ts";
import { type Thread, ThreadEvent, type ThreadEventHandlerMap } from "./thread.ts";
import { type IActionsObject } from "../pushprocessor.ts";
import { type MatrixError } from "../http-api/index.ts";
import { TypedEventEmitter } from "./typed-event-emitter.ts";
import { type EventStatus } from "./event-status.ts";
import { type CryptoBackend } from "../common-crypto/CryptoBackend.ts";
import { type IAnnotatedPushRule } from "../@types/PushRules.ts";
import { type Room } from "./room.ts";
import { type Membership } from "../@types/membership.ts";
import { DecryptionFailureCode } from "../crypto-api/index.ts";
import { type RoomState } from "./room-state.ts";
import { type EmptyObject } from "../@types/common.ts";
export { EventStatus } from "./event-status.ts";
import { ExtensibleEvent, Optional } from "matrix-events-sdk";
import { VerificationRequest } from "../crypto/verification/request/VerificationRequest";
import { EventType, MsgType, RelationType, UNSIGNED_THREAD_ID_FIELD } from "../@types/event";
import { Crypto } from "../crypto";
import { RoomMember } from "./room-member";
import { Thread, ThreadEvent, ThreadEventHandlerMap } from "./thread";
import { IActionsObject } from "../pushprocessor";
import { MatrixError } from "../http-api";
import { TypedEventEmitter } from "./typed-event-emitter";
import { EventStatus } from "./event-status";
import { CryptoBackend } from "../common-crypto/CryptoBackend";
import { IAnnotatedPushRule } from "../@types/PushRules";
export { EventStatus } from "./event-status";
export interface IContent {
[key: string]: any;
"msgtype"?: MsgType | string;
"membership"?: Membership;
"membership"?: string;
"avatar_url"?: string;
"displayname"?: string;
"m.relates_to"?: IEventRelation;
@@ -38,10 +35,7 @@ export interface IUnsigned {
"transaction_id"?: string;
"invite_room_state"?: StrippedState[];
"m.relations"?: Record<RelationType | string, any>;
"msc4354_sticky_duration_ttl_ms"?: number;
[UNSIGNED_THREAD_ID_FIELD.name]?: string;
"membership"?: Membership;
"io.element.msc4115.membership"?: Membership;
}
export interface IThreadBundledRelationship {
latest_event: IEvent;
@@ -57,12 +51,21 @@ export interface IEvent {
origin_server_ts: number;
txn_id?: string;
state_key?: string;
membership?: Membership;
membership?: string;
unsigned: IUnsigned;
redacts?: string;
msc4354_sticky?: {
duration_ms: number;
};
/**
* @deprecated in favour of `sender`
*/
user_id?: string;
/**
* @deprecated in favour of `unsigned.prev_content`
*/
prev_content?: IContent;
/**
* @deprecated in favour of `origin_server_ts`
*/
age?: number;
}
export interface IAggregatedRelation {
origin_server_ts: number;
@@ -110,13 +113,9 @@ export interface IVisibilityChange {
*/
reason: string | null;
}
export interface IMarkedUnreadEvent {
unread: boolean;
}
export interface IClearEvent {
room_id?: string;
type: string;
state_key?: string;
content: Omit<IContent, "membership" | "avatar_url" | "displayname" | "m.relates_to">;
unsigned?: IUnsigned;
}
@@ -125,20 +124,8 @@ interface IKeyRequestRecipient {
deviceId: "*" | string;
}
export interface IDecryptOptions {
/** Whether to emit {@link MatrixEventEvent.Decrypted} events on successful decryption. Defaults to true.
*/
emit?: boolean;
/**
* True if this is a retry, after receiving an update to the session key. (Enables more logging.)
*
* This is only intended for use within the js-sdk.
*
* @internal
*/
isRetry?: boolean;
/**
* @deprecated does nothing
*/
forceRedecryptIfUntrusted?: boolean;
}
/**
@@ -162,29 +149,23 @@ export interface IMessageVisibilityHidden {
*/
readonly reason: string | null;
}
export declare const MAX_STICKY_DURATION_MS = 3600000;
export declare enum MatrixEventEvent {
/**
* An event has been decrypted, or we have failed to decrypt it.
*
* The payload consists of:
*
* * `event` - The {@link MatrixEvent} which we attempted to decrypt.
*
* * `err` - The error that occurred during decryption, or `undefined` if no error occurred.
* Avoid use of this: {@link MatrixEvent.decryptionFailureReason} is more useful.
*/
Decrypted = "Event.decrypted",
BeforeRedaction = "Event.beforeRedaction",
VisibilityChange = "Event.visibilityChange",
LocalEventIdReplaced = "Event.localEventIdReplaced",
Status = "Event.status",
Replaced = "Event.replaced",
RelationsCreated = "Event.relationsCreated",
SentinelUpdated = "Event.sentinelUpdated"
RelationsCreated = "Event.relationsCreated"
}
export type MatrixEventEmittedEvents = MatrixEventEvent | ThreadEvent.Update;
export type MatrixEventHandlerMap = {
/**
* Fires when an event is decrypted
*
* @param event - The matrix event which has been decrypted
* @param err - The error that occurred during decryption, or `undefined` if no error occurred.
*/
[MatrixEventEvent.Decrypted]: (event: MatrixEvent, err?: Error) => void;
[MatrixEventEvent.BeforeRedaction]: (event: MatrixEvent, redactionEvent: MatrixEvent) => void;
[MatrixEventEvent.VisibilityChange]: (event: MatrixEvent, visible: boolean) => void;
@@ -192,7 +173,6 @@ export type MatrixEventHandlerMap = {
[MatrixEventEvent.Status]: (event: MatrixEvent, status: EventStatus | null) => void;
[MatrixEventEvent.Replaced]: (event: MatrixEvent) => void;
[MatrixEventEvent.RelationsCreated]: (relationType: string, eventType: string) => void;
[MatrixEventEvent.SentinelUpdated]: () => void;
} & Pick<ThreadEventHandlerMap, ThreadEvent.Update>;
export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, MatrixEventHandlerMap> {
event: Partial<IEvent>;
@@ -203,17 +183,11 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
private clearEvent?;
private visibility;
private _hasCachedExtEv;
private _cachedExtEv?;
/** If we failed to decrypt this event, the reason for the failure. Otherwise, `null`. */
private _decryptionFailureReason;
private _cachedExtEv;
private senderCurve25519Key;
private claimedEd25519Key;
/**
* If another user forwarded the key to this message
* (eg via [MSC4268](https://github.com/matrix-org/matrix-spec-proposals/pull/4268)),
* the ID of that user.
*/
private keyForwardedBy?;
private forwardingCurve25519KeyChain;
private untrusted;
private decryptionPromise;
private retryDecryption;
private txnId?;
@@ -222,6 +196,7 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
*/
private thread?;
private threadId?;
private encryptedDisabledForUnverifiedDevices;
localTimestamp: number;
/**
* The room member who sent this event, or null e.g.
@@ -239,13 +214,6 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
* Should be read-only
*/
target: RoomMember | null;
/**
* Update the sentinels and forwardLooking flag for this event.
* @param stateContext - the room state to be queried
* @param toStartOfTimeline - if true the event's forwardLooking flag is set false
* @internal
*/
setMetadata(stateContext: RoomState, toStartOfTimeline: boolean): void;
/**
* The sending status of the event.
* @privateRemarks
@@ -255,7 +223,7 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
/**
* most recent error associated with sending the event, if any
* @privateRemarks
* Should be read-only. May not be a MatrixError.
* Should be read-only
*/
error: MatrixError | null;
/**
@@ -267,17 +235,8 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
* Should be read-only
*/
forwardLooking: boolean;
verificationRequest?: VerificationRequest;
private readonly reEmitter;
/**
* The timestamp for when this event should expire, in milliseconds.
* Prefers using the server-provided value, but will fall back to local calculation.
*
* This value is **safe** to use, as malicious start time and duration are appropriately capped.
*
* If the event is not a sticky event (or not supported by the server),
* then this returns `undefined`.
*/
readonly unstableStickyExpiresAt: number | undefined;
/**
* Construct a Matrix Event object
*
@@ -294,15 +253,12 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
*
* @deprecated Use stable functions where possible.
*/
get unstableExtensibleEvent(): ExtensibleEvent | undefined;
get unstableExtensibleEvent(): Optional<ExtensibleEvent>;
private invalidateExtensibleEvent;
/**
* Gets the event as it would appear if it had been sent unencrypted.
*
* If the event is encrypted, we attempt to mock up an event as it would have looked had the sender not encrypted it.
* If the event is not encrypted, a copy of it is simply returned as-is.
*
* @returns A shallow copy of the event, in wire format, as it would have been had it not been encrypted.
* Gets the event as though it would appear unencrypted. If the event is already not
* encrypted, it is simply returned as-is.
* @returns The event in wire format.
*/
getEffectiveEvent(): IEvent;
/**
@@ -422,32 +378,16 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
*/
getLocalAge(): number;
/**
* Get the event state_key if it has one. If necessary, this will perform
* string-unpacking on the state key, as per MSC4362. This will return
* <code>undefined</code> for message events.
* @returns The event's `state_key`.
*/
getStateKey(): string | undefined;
/**
* Get the raw event state_key if it has one. This may be string-packed as per
* MSC4362 if the state event is encrypted. This will return <code>undefined
* Get the event state_key if it has one. This will return <code>undefined
* </code> for message events.
* @returns The event's `state_key`.
*/
getWireStateKey(): string | undefined;
getStateKey(): string | undefined;
/**
* Check if this event is a state event.
* @returns True if this is a state event.
*/
isState(): boolean;
/**
* Get the user's room membership at the time the event was sent, as reported
* by the server. This uses MSC4115.
*
* @returns The user's room membership, or `undefined` if the server does
* not report it.
*/
getMembershipAtEvent(): Membership | string | undefined;
/**
* Replace the content of this event with encrypted versions.
* (This is used when sending an event; it should not be used by applications).
@@ -484,8 +424,7 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
* couldn't decrypt.
*/
isDecryptionFailure(): boolean;
/** If we failed to decrypt this event, the reason for the failure. Otherwise, `null`. */
get decryptionFailureReason(): DecryptionFailureCode | null;
get isEncryptedDisabledForUnverifiedDevices(): boolean;
shouldAttemptDecryption(): boolean;
/**
* Start the process of trying to decrypt this event.
@@ -500,6 +439,15 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
* attempt is completed.
*/
attemptDecryption(crypto: CryptoBackend, options?: IDecryptOptions): Promise<void>;
/**
* Cancel any room key request for this event and resend another.
*
* @param crypto - crypto module
* @param userId - the user who received this event
*
* @returns a promise that resolves when the request is queued
*/
cancelAndResendKeyRequest(crypto: Crypto, userId: string): Promise<void>;
/**
* Calculate the recipients for keyshare requests.
*
@@ -509,6 +457,7 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
*/
getKeyRequestRecipients(userId: string): IKeyRequestRecipient[];
private decryptionLoop;
private badEncryptedMessage;
/**
* Update the cleartext data on this event.
*
@@ -517,14 +466,11 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
* @internal
*
* @param decryptionResult - the decryption result, including the plaintext and some key info
*
* @remarks
* Fires {@link MatrixEventEvent.Decrypted}
*/
private setClearData;
/**
* Update the cleartext data on this event after a decryption failure.
*
* @param reason - the textual reason for the failure
*/
private setClearDataForDecryptionFailure;
/**
* Gets the cleartext content for this event. If the event is not encrypted,
* or encryption has not been completed, this will return null.
@@ -568,31 +514,29 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
* signing the public curve25519 key with the ed25519 key.
*
* In general, applications should not use this method directly, but should
* instead use {@link crypto-api!CryptoApi#getEncryptionInfoForEvent}.
* instead use MatrixClient.getEventSenderDeviceInfo.
*/
getClaimedEd25519Key(): string | null;
/**
* Returns an empty array.
* Get the curve25519 keys of the devices which were involved in telling us
* about the claimedEd25519Key and sender curve25519 key.
*
* Previously, this returned the chain of Curve25519 keys through which
* this session was forwarded, via `m.forwarded_room_key` events.
* However, that is not cryptographically reliable, and clients should not
* be using it.
* Normally this will be empty, but in the case of a forwarded megolm
* session, the sender keys are sent to us by another device (the forwarding
* device), which we need to trust to do this. In that case, the result will
* be a list consisting of one entry.
*
* @see https://github.com/matrix-org/matrix-spec/issues/1089
* @deprecated
* If the device that sent us the key (A) got it from another device which
* it wasn't prepared to vouch for (B), the result will be [A, B]. And so on.
*
* @returns base64-encoded curve25519 keys, from oldest to newest.
*/
getForwardingCurve25519KeyChain(): string[];
/**
* @deprecated always returns false
* Whether the decryption key was obtained from an untrusted source. If so,
* we cannot verify the authenticity of the message.
*/
isKeySourceUntrusted(): false;
/**
* If another user forwarded the key to this message
* (eg via [MSC4268](https://github.com/matrix-org/matrix-spec-proposals/pull/4268)),
* get the ID of that user.
*/
getKeyForwardingUser(): string | undefined;
isKeySourceUntrusted(): boolean | undefined;
getUnsigned(): IUnsigned;
setUnsigned(unsigned: IUnsigned): void;
unmarkLocallyRedacted(): boolean;
@@ -623,11 +567,8 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
* if it were redacted before it was sent to us
*
* @param redactionEvent - event causing the redaction
* @param room - the room in which the event exists
*/
makeRedacted(redactionEvent: MatrixEvent, room: Room): void;
private moveAllRelatedToMainTimeline;
private moveToMainTimeline;
makeRedacted(redactionEvent: MatrixEvent): void;
/**
* Check if this event has been redacted
*
@@ -662,7 +603,7 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
*
* @returns The redaction event JSON, or an empty object
*/
getRedactionEvent(): IEvent | EmptyObject | null;
getRedactionEvent(): IEvent | {} | null;
/**
* Get the push actions, if known, for this event
*
@@ -675,6 +616,14 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
* @returns push actions
*/
getPushDetails(): PushDetails;
/**
* Set the push actions for this event.
* Clears rule from push details if present
* @deprecated use `setPushDetails`
*
* @param pushActions - push actions
*/
setPushActions(pushActions: IActionsObject | null): void;
/**
* Set the push details for this event.
*
@@ -750,6 +699,11 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
* For relations and redactions, returns the event_id this event is referring to.
*/
getAssociatedId(): string | undefined;
/**
* Checks if this event is associated with another event. See `getAssociatedId`.
* @deprecated use hasAssociation instead.
*/
hasAssocation(): boolean;
/**
* Checks if this event is associated with another event. See `getAssociatedId`.
*/
@@ -798,25 +752,20 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
* @param otherEvent - The other event to check against.
* @returns True if the events are the same, false otherwise.
*/
isEquivalentTo(otherEvent?: MatrixEvent): boolean;
isEquivalentTo(otherEvent: MatrixEvent): boolean;
/**
* Summarise the event as JSON.
* Summarise the event as JSON. This is currently used by React SDK's view
* event source feature and Seshat's event indexing, so take care when
* adjusting the output here.
*
* If encrypted, include both the decrypted and encrypted view of the event.
*
* This is named `toJSON` for use with `JSON.stringify` which checks objects
* for functions named `toJSON` and will call them to customise the output
* if they are defined.
*
* **WARNING** Do not log the result of this method; otherwise, it will end up
* in rageshakes, leading to a privacy violation.
*
* @deprecated Prefer to use {@link MatrixEvent#getEffectiveEvent} or similar.
* This method will be removed soon; it is too easy to use it accidentally
* and cause a privacy violation (cf https://github.com/vector-im/element-web/issues/26380).
* In any case, the value it returns is not a faithful serialization of the object.
*/
toJSON(): object;
setVerificationRequest(request: VerificationRequest): void;
setTxnId(txnId: string): void;
getTxnId(): string | undefined;
/**
@@ -829,16 +778,5 @@ export declare class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEve
*/
getThread(): Thread | undefined;
setThreadId(threadId?: string): void;
/**
* Unstable getter to try and get the sticky information for the event.
* If the event is not a sticky event (or not supported by the server),
* then this returns `undefined`.
*
* `duration_ms` is safely bounded to a hour.
*/
get unstableStickyInfo(): {
duration_ms: number;
duration_ttl_ms?: number;
} | undefined;
}
//# sourceMappingURL=event.d.ts.map

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,27 +0,0 @@
import { UnstableValue } from "matrix-events-sdk";
export declare const POLICIES_ACCOUNT_EVENT_TYPE: UnstableValue<"m.policies", "org.matrix.msc3847.policies">;
export declare const IGNORE_INVITES_ACCOUNT_EVENT_KEY: UnstableValue<"m.ignore.invites", "org.matrix.msc3847.ignore.invites">;
export declare enum PolicyRecommendation {
Ban = "m.ban"
}
/**
* The various scopes for policies.
*/
export declare enum PolicyScope {
/**
* The policy deals with an individual user, e.g. reject invites
* from this user.
*/
User = "m.policy.user",
/**
* The policy deals with a room, e.g. reject invites towards
* a specific room.
*/
Room = "m.policy.room",
/**
* The policy deals with a server, e.g. reject invites from
* this server.
*/
Server = "m.policy.server"
}
//# sourceMappingURL=invites-ignorer-types.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"invites-ignorer-types.d.ts","sourceRoot":"","sources":["../../src/models/invites-ignorer-types.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKlD,eAAO,MAAM,2BAA2B,4DAAiE,CAAC;AAK1G,eAAO,MAAM,gCAAgC,wEAG5C,CAAC;AAGF,oBAAY,oBAAoB;IAC5B,GAAG,UAAU;CAChB;AAED;;GAEG;AACH,oBAAY,WAAW;IACnB;;;OAGG;IACH,IAAI,kBAAkB;IAEtB;;;OAGG;IACH,IAAI,kBAAkB;IAEtB;;;OAGG;IACH,MAAM,oBAAoB;CAC7B"}

View File

@@ -1,56 +0,0 @@
/*
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 { UnstableValue } from "matrix-events-sdk";
/// The event type storing the user's individual policies.
///
/// Exported for testing purposes.
export const POLICIES_ACCOUNT_EVENT_TYPE = new UnstableValue("m.policies", "org.matrix.msc3847.policies");
/// The key within the user's individual policies storing the user's ignored invites.
///
/// Exported for testing purposes.
export const IGNORE_INVITES_ACCOUNT_EVENT_KEY = new UnstableValue("m.ignore.invites", "org.matrix.msc3847.ignore.invites");
/// The types of recommendations understood.
export let PolicyRecommendation = /*#__PURE__*/function (PolicyRecommendation) {
PolicyRecommendation["Ban"] = "m.ban";
return PolicyRecommendation;
}({});
/**
* The various scopes for policies.
*/
export let PolicyScope = /*#__PURE__*/function (PolicyScope) {
/**
* The policy deals with an individual user, e.g. reject invites
* from this user.
*/
PolicyScope["User"] = "m.policy.user";
/**
* The policy deals with a room, e.g. reject invites towards
* a specific room.
*/
PolicyScope["Room"] = "m.policy.room";
/**
* The policy deals with a server, e.g. reject invites from
* this server.
*/
PolicyScope["Server"] = "m.policy.server";
return PolicyScope;
}({});
//# sourceMappingURL=invites-ignorer-types.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"invites-ignorer-types.js","names":[],"sources":["../../src/models/invites-ignorer-types.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { UnstableValue } from \"matrix-events-sdk\";\n\n/// The event type storing the user's individual policies.\n///\n/// Exported for testing purposes.\nexport const POLICIES_ACCOUNT_EVENT_TYPE = new UnstableValue(\"m.policies\", \"org.matrix.msc3847.policies\");\n\n/// The key within the user's individual policies storing the user's ignored invites.\n///\n/// Exported for testing purposes.\nexport const IGNORE_INVITES_ACCOUNT_EVENT_KEY = new UnstableValue(\n \"m.ignore.invites\",\n \"org.matrix.msc3847.ignore.invites\",\n);\n\n/// The types of recommendations understood.\nexport enum PolicyRecommendation {\n Ban = \"m.ban\",\n}\n\n/**\n * The various scopes for policies.\n */\nexport enum PolicyScope {\n /**\n * The policy deals with an individual user, e.g. reject invites\n * from this user.\n */\n User = \"m.policy.user\",\n\n /**\n * The policy deals with a room, e.g. reject invites towards\n * a specific room.\n */\n Room = \"m.policy.room\",\n\n /**\n * The policy deals with a server, e.g. reject invites from\n * this server.\n */\n Server = \"m.policy.server\",\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAAS,aAAa,QAAQ,mBAAmB;;AAEjD;AACA;AACA;AACA,OAAO,MAAM,2BAA2B,GAAG,IAAI,aAAa,CAAC,YAAY,EAAE,6BAA6B,CAAC;;AAEzG;AACA;AACA;AACA,OAAO,MAAM,gCAAgC,GAAG,IAAI,aAAa,CAC7D,kBAAkB,EAClB,mCACJ,CAAC;;AAED;AACA,WAAY,oBAAoB,0BAApB,oBAAoB;EAApB,oBAAoB;EAAA,OAApB,oBAAoB;AAAA;;AAIhC;AACA;AACA;AACA,WAAY,WAAW,0BAAX,WAAW;EACnB;AACJ;AACA;AACA;EAJY,WAAW;EAOnB;AACJ;AACA;AACA;EAVY,WAAW;EAanB;AACJ;AACA;AACA;EAhBY,WAAW;EAAA,OAAX,WAAW;AAAA","ignoreList":[]}

View File

@@ -1,8 +1,29 @@
import { type MatrixClient } from "../client.ts";
import { type MatrixEvent } from "./event.ts";
import { type Room } from "./room.ts";
import { IGNORE_INVITES_ACCOUNT_EVENT_KEY, POLICIES_ACCOUNT_EVENT_TYPE, PolicyRecommendation, PolicyScope } from "./invites-ignorer-types.ts";
export { IGNORE_INVITES_ACCOUNT_EVENT_KEY, POLICIES_ACCOUNT_EVENT_TYPE, PolicyRecommendation, PolicyScope };
import { UnstableValue } from "matrix-events-sdk";
import { MatrixClient } from "../client";
import { MatrixEvent } from "./event";
import { Room } from "./room";
export declare const POLICIES_ACCOUNT_EVENT_TYPE: UnstableValue<"m.policies", "org.matrix.msc3847.policies">;
export declare const IGNORE_INVITES_ACCOUNT_EVENT_KEY: UnstableValue<"m.ignore.invites", "org.matrix.msc3847.ignore.invites">;
/**
* The various scopes for policies.
*/
export declare enum PolicyScope {
/**
* The policy deals with an individual user, e.g. reject invites
* from this user.
*/
User = "m.policy.user",
/**
* The policy deals with a room, e.g. reject invites towards
* a specific room.
*/
Room = "m.policy.room",
/**
* The policy deals with a server, e.g. reject invites from
* this server.
*/
Server = "m.policy.server"
}
/**
* A container for ignored invites.
*
@@ -49,9 +70,8 @@ export declare class IgnoredInvites {
/**
* Find out whether an invite should be ignored.
*
* @param params
* @param params.sender - The user id for the user who issued the invite.
* @param params.roomId - The room to which the user is invited.
* @param sender - The user id for the user who issued the invite.
* @param roomId - The room to which the user is invited.
* @returns A rule matching the entity, if any was found, `null` otherwise.
*/
getRuleForInvite({ sender, roomId, }: {

View File

@@ -1 +1 @@
{"version":3,"file":"invites-ignorer.d.ts","sourceRoot":"","sources":["../../src/models/invites-ignorer.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAI7D,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EACH,gCAAgC,EAChC,2BAA2B,EAC3B,oBAAoB,EACpB,WAAW,EACd,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,gCAAgC,EAAE,2BAA2B,EAAE,oBAAoB,EAAE,WAAW,EAAE,CAAC;AAQ5G;;;;;;;;;GASG;AACH,qBAAa,cAAc;IACJ,OAAO,CAAC,QAAQ,CAAC,MAAM;IAA1C,YAAoC,MAAM,EAAE,YAAY,EAAI;IAE5D;;;;;;;OAOG;IACU,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQxF;IAED;;OAEG;IACU,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzD;IAED;;;;;;;;;;;;;;;OAeG;IACU,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiBvD;IAED;;;;;;;OAOG;IACU,gBAAgB,CAAC,EAC1B,MAAM,EACN,MAAM,GACT,EAAE;QACC,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CAmDxC;IAED;;;;;;;;;;;;;OAaG;IACU,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CA8BlD;IAED;;;;;;;;;;;;;OAaG;IACU,sBAAsB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAmCrD;IAED;;;;;;;;;OASG;IACH,OAAO,CAAC,wBAAwB;IAIhC;;OAEG;YACW,yBAAyB;IASvC;;;OAGG;IACH,OAAO,CAAC,mCAAmC;CAmC9C"}
{"version":3,"file":"invites-ignorer.d.ts","sourceRoot":"","sources":["../../src/models/invites-ignorer.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAY,WAAW,EAAE,MAAM,SAAS,CAAC;AAIhD,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAK9B,eAAO,MAAM,2BAA2B,4DAAiE,CAAC;AAK1G,eAAO,MAAM,gCAAgC,wEAG5C,CAAC;AAOF;;GAEG;AACH,oBAAY,WAAW;IACnB;;;OAGG;IACH,IAAI,kBAAkB;IAEtB;;;OAGG;IACH,IAAI,kBAAkB;IAEtB;;;OAGG;IACH,MAAM,oBAAoB;CAC7B;AAED;;;;;;;;;GASG;AACH,qBAAa,cAAc;IACJ,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,YAAY;IAExD;;;;;;;OAOG;IACU,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUzF;;OAEG;IACU,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D;;;;;;;;;;;;;;;OAeG;IACU,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAmBxD;;;;;;OAMG;IACU,gBAAgB,CAAC,EAC1B,MAAM,EACN,MAAM,GACT,EAAE;QACC,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAqDzC;;;;;;;;;;;;;OAaG;IACU,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAgCnD;;;;;;;;;;;;;OAaG;IACU,sBAAsB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IAqCtD;;;;;;;;;OASG;IACH,OAAO,CAAC,wBAAwB;IAIhC;;OAEG;YACW,yBAAyB;IASvC;;;OAGG;IACH,OAAO,CAAC,mCAAmC;CAmC9C"}

View File

@@ -1,3 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.PolicyScope = exports.POLICIES_ACCOUNT_EVENT_TYPE = exports.IgnoredInvites = exports.IGNORE_INVITES_ACCOUNT_EVENT_KEY = void 0;
var _matrixEventsSdk = require("matrix-events-sdk");
var _eventTimeline = require("./event-timeline");
var _partials = require("../@types/partials");
var _utils = require("../utils");
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
@@ -14,18 +24,32 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { EventTimeline } from "./event-timeline.js";
import { Preset } from "../@types/partials.js";
import { globToRegexp } from "../utils.js";
import { EventType } from "../@types/event.js";
import { IGNORE_INVITES_ACCOUNT_EVENT_KEY, POLICIES_ACCOUNT_EVENT_TYPE, PolicyRecommendation, PolicyScope } from "./invites-ignorer-types.js";
export { IGNORE_INVITES_ACCOUNT_EVENT_KEY, POLICIES_ACCOUNT_EVENT_TYPE, PolicyRecommendation, PolicyScope };
const scopeToEventTypeMap = {
[PolicyScope.User]: EventType.PolicyRuleUser,
[PolicyScope.Room]: EventType.PolicyRuleRoom,
[PolicyScope.Server]: EventType.PolicyRuleServer
};
/// The event type storing the user's individual policies.
///
/// Exported for testing purposes.
const POLICIES_ACCOUNT_EVENT_TYPE = new _matrixEventsSdk.UnstableValue("m.policies", "org.matrix.msc3847.policies");
/// The key within the user's individual policies storing the user's ignored invites.
///
/// Exported for testing purposes.
exports.POLICIES_ACCOUNT_EVENT_TYPE = POLICIES_ACCOUNT_EVENT_TYPE;
const IGNORE_INVITES_ACCOUNT_EVENT_KEY = new _matrixEventsSdk.UnstableValue("m.ignore.invites", "org.matrix.msc3847.ignore.invites");
/// The types of recommendations understood.
exports.IGNORE_INVITES_ACCOUNT_EVENT_KEY = IGNORE_INVITES_ACCOUNT_EVENT_KEY;
var PolicyRecommendation = /*#__PURE__*/function (PolicyRecommendation) {
PolicyRecommendation["Ban"] = "m.ban";
return PolicyRecommendation;
}(PolicyRecommendation || {});
/**
* The various scopes for policies.
*/
let PolicyScope = /*#__PURE__*/function (PolicyScope) {
PolicyScope["User"] = "m.policy.user";
PolicyScope["Room"] = "m.policy.room";
PolicyScope["Server"] = "m.policy.server";
return PolicyScope;
}({});
/**
* A container for ignored invites.
*
@@ -36,7 +60,8 @@ const scopeToEventTypeMap = {
* applications turn out to require longer lists, we may need to rework
* our data structures.
*/
export class IgnoredInvites {
exports.PolicyScope = PolicyScope;
class IgnoredInvites {
constructor(client) {
this.client = client;
}
@@ -51,7 +76,7 @@ export class IgnoredInvites {
*/
async addRule(scope, entity, reason) {
const target = await this.getOrCreateTargetRoom();
const response = await this.client.sendStateEvent(target.roomId, scopeToEventTypeMap[scope], {
const response = await this.client.sendStateEvent(target.roomId, scope, {
entity,
reason,
recommendation: PolicyRecommendation.Ban
@@ -104,9 +129,8 @@ export class IgnoredInvites {
/**
* Find out whether an invite should be ignored.
*
* @param params
* @param params.sender - The user id for the user who issued the invite.
* @param params.roomId - The room to which the user is invited.
* @param sender - The user id for the user who issued the invite.
* @param roomId - The room to which the user is invited.
* @returns A rule matching the entity, if any was found, `null` otherwise.
*/
async getRuleForInvite({
@@ -127,7 +151,7 @@ export class IgnoredInvites {
const senderServer = sender.split(":")[1];
const roomServer = roomId.split(":")[1];
for (const room of policyRooms) {
const state = room.getUnfilteredTimelineSet().getLiveTimeline().getState(EventTimeline.FORWARDS);
const state = room.getUnfilteredTimelineSet().getLiveTimeline().getState(_eventTimeline.EventTimeline.FORWARDS);
for (const {
scope,
entities
@@ -141,22 +165,22 @@ export class IgnoredInvites {
scope: PolicyScope.Server,
entities: [senderServer, roomServer]
}]) {
const events = state.getStateEvents(scopeToEventTypeMap[scope]);
const events = state.getStateEvents(scope);
for (const event of events) {
const content = event.getContent();
if (content?.recommendation != PolicyRecommendation.Ban) {
if ((content === null || content === void 0 ? void 0 : content.recommendation) != PolicyRecommendation.Ban) {
// Ignoring invites only looks at `m.ban` recommendations.
continue;
}
const glob = content?.entity;
const glob = content === null || content === void 0 ? void 0 : content.entity;
if (!glob) {
// Invalid event.
continue;
}
let regexp;
try {
regexp = new RegExp(globToRegexp(glob));
} catch {
regexp = new RegExp((0, _utils.globToRegexp)(glob));
} catch (ex) {
// Assume invalid event.
continue;
}
@@ -169,6 +193,7 @@ export class IgnoredInvites {
}
}
}
return null;
}
@@ -206,7 +231,7 @@ export class IgnoredInvites {
// We need to create our own policy room for ignoring invites.
target = (await this.client.createRoom({
name: "Individual Policy Room",
preset: Preset.PrivateChat
preset: _partials.Preset.PrivateChat
})).room_id;
await this.withIgnoreInvitesPolicies(ignoreInvitesPolicies => {
ignoreInvitesPolicies.target = target;
@@ -299,10 +324,11 @@ export class IgnoredInvites {
getPoliciesAndIgnoreInvitesPolicies() {
let policies = {};
for (const key of [POLICIES_ACCOUNT_EVENT_TYPE.name, POLICIES_ACCOUNT_EVENT_TYPE.altName]) {
var _this$client$getAccou;
if (!key) {
continue;
}
const value = this.client.getAccountData(key)?.getContent();
const value = (_this$client$getAccou = this.client.getAccountData(key)) === null || _this$client$getAccou === void 0 ? void 0 : _this$client$getAccou.getContent();
if (value) {
policies = value;
break;
@@ -330,4 +356,5 @@ export class IgnoredInvites {
};
}
}
exports.IgnoredInvites = IgnoredInvites;
//# sourceMappingURL=invites-ignorer.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,9 +1,9 @@
import { type MatrixClient } from "../client.ts";
import { type PollStartEvent } from "../extensible_events_v1/PollStartEvent.ts";
import { type MatrixEvent } from "./event.ts";
import { Relations } from "./relations.ts";
import { type Room } from "./room.ts";
import { TypedEventEmitter } from "./typed-event-emitter.ts";
import { MatrixClient } from "../client";
import { PollStartEvent } from "../extensible_events_v1/PollStartEvent";
import { MatrixEvent } from "./event";
import { Relations } from "./relations";
import { Room } from "./room";
import { TypedEventEmitter } from "./typed-event-emitter";
export declare enum PollEvent {
New = "Poll.new",
End = "Poll.end",

View File

@@ -1 +1 @@
{"version":3,"file":"poll.d.ts","sourceRoot":"","sources":["../../src/models/poll.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,2CAA2C,CAAC;AAChF,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,oBAAY,SAAS;IACjB,GAAG,aAAa;IAChB,GAAG,aAAa;IAChB,MAAM,gBAAgB;IACtB,SAAS,mBAAmB;IAC5B,OAAO,iBAAiB;IACxB,sBAAsB,gCAAgC;CACzD;AAED,MAAM,MAAM,mBAAmB,GAAG;IAC9B,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAC7D,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;IACtD,CAAC,SAAS,CAAC,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/D,CAAC;AAuBF,qBAAa,IAAK,SAAQ,iBAAiB,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,mBAAmB,CAAC;aAc3E,SAAS,EAAE,WAAW;IACtC,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,IAAI;IAfhB,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,SAAS,EAAE,cAAc,CAAC;IAC1C,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,QAAQ,CAA0B;IAC1C;;;OAGG;IACH,OAAO,CAAC,6BAA6B,CAAqB;IAE1D,YACoB,SAAS,EAAE,WAAW,EAC9B,YAAY,EAAE,YAAY,EAC1B,IAAI,EAAE,IAAI,EAQrB;IAED,IAAW,MAAM,IAAI,MAAM,CAE1B;IAED,IAAW,UAAU,IAAI,MAAM,GAAG,SAAS,CAE1C;IAED,IAAW,OAAO,IAAI,OAAO,CAE5B;IAED,IAAW,mBAAmB,IAAI,OAAO,CAExC;IAED,IAAW,2BAA2B,IAAI,MAAM,CAE/C;IAEY,YAAY,IAAI,OAAO,CAAC,SAAS,CAAC,CAc9C;IAED;;;;OAIG;IACI,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAwB7C;YAEa,cAAc;IA0D5B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAe9B,OAAO,CAAC,wBAAwB,CAW9B;IAEF,OAAO,CAAC,gBAAgB;CAyB3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,UAAW,WAAW,KAAG,OAGhD,CAAC"}
{"version":3,"file":"poll.d.ts","sourceRoot":"","sources":["../../src/models/poll.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,oBAAY,SAAS;IACjB,GAAG,aAAa;IAChB,GAAG,aAAa;IAChB,MAAM,gBAAgB;IACtB,SAAS,mBAAmB;IAC5B,OAAO,iBAAiB;IACxB,sBAAsB,gCAAgC;CACzD;AAED,MAAM,MAAM,mBAAmB,GAAG;IAC9B,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAC7D,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;IACtD,CAAC,SAAS,CAAC,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/D,CAAC;AAuBF,qBAAa,IAAK,SAAQ,iBAAiB,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,mBAAmB,CAAC;aAa5D,SAAS,EAAE,WAAW;IAAE,OAAO,CAAC,YAAY;IAAgB,OAAO,CAAC,IAAI;IAZ3G,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,SAAS,EAAE,cAAc,CAAC;IAC1C,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,QAAQ,CAA0B;IAC1C;;;OAGG;IACH,OAAO,CAAC,6BAA6B,CAAqB;gBAEvB,SAAS,EAAE,WAAW,EAAU,YAAY,EAAE,YAAY,EAAU,IAAI,EAAE,IAAI;IASjH,IAAW,MAAM,IAAI,MAAM,CAE1B;IAED,IAAW,UAAU,IAAI,MAAM,GAAG,SAAS,CAE1C;IAED,IAAW,OAAO,IAAI,OAAO,CAE5B;IAED,IAAW,mBAAmB,IAAI,OAAO,CAExC;IAED,IAAW,2BAA2B,IAAI,MAAM,CAE/C;IAEY,YAAY,IAAI,OAAO,CAAC,SAAS,CAAC;IAgB/C;;;;OAIG;IACI,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;YA0BhC,cAAc;IA0D5B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAe9B,OAAO,CAAC,wBAAwB,CAW9B;IAEF,OAAO,CAAC,gBAAgB;CAyB3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,UAAW,WAAW,KAAG,OAGhD,CAAC"}

View File

@@ -1,4 +1,15 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isPollEvent = exports.PollEvent = exports.Poll = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _matrixEventsSdk = require("matrix-events-sdk");
var _polls = require("../@types/polls");
var _relations = require("./relations");
var _typedEventEmitter = require("./typed-event-emitter");
/*
Copyright 2023 The Matrix.org Foundation C.I.C.
@@ -14,12 +25,7 @@ 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 { M_POLL_START } from "matrix-events-sdk";
import { M_POLL_END, M_POLL_RESPONSE } from "../@types/polls.js";
import { Relations } from "./relations.js";
import { TypedEventEmitter } from "./typed-event-emitter.js";
export let PollEvent = /*#__PURE__*/function (PollEvent) {
let PollEvent = /*#__PURE__*/function (PollEvent) {
PollEvent["New"] = "Poll.new";
PollEvent["End"] = "Poll.end";
PollEvent["Update"] = "Poll.update";
@@ -28,12 +34,13 @@ export let PollEvent = /*#__PURE__*/function (PollEvent) {
PollEvent["UndecryptableRelations"] = "Poll.UndecryptableRelations";
return PollEvent;
}({});
exports.PollEvent = PollEvent;
const filterResponseRelations = (relationEvents, pollEndTimestamp) => {
const responseEvents = relationEvents.filter(event => {
if (event.isDecryptionFailure()) {
return;
}
return M_POLL_RESPONSE.matches(event.getType()) &&
return _polls.M_POLL_RESPONSE.matches(event.getType()) &&
// From MSC3381:
// "Votes sent on or before the end event's timestamp are valid votes"
event.getTs() <= pollEndTimestamp;
@@ -42,21 +49,24 @@ const filterResponseRelations = (relationEvents, pollEndTimestamp) => {
responseEvents
};
};
export class Poll extends TypedEventEmitter {
class Poll extends _typedEventEmitter.TypedEventEmitter {
constructor(rootEvent, matrixClient, room) {
super();
_defineProperty(this, "roomId", void 0);
_defineProperty(this, "pollEvent", void 0);
_defineProperty(this, "_isFetchingResponses", false);
_defineProperty(this, "relationsNextBatch", void 0);
_defineProperty(this, "responses", null);
_defineProperty(this, "endEvent", void 0);
this.rootEvent = rootEvent;
this.matrixClient = matrixClient;
this.room = room;
(0, _defineProperty2.default)(this, "roomId", void 0);
(0, _defineProperty2.default)(this, "pollEvent", void 0);
(0, _defineProperty2.default)(this, "_isFetchingResponses", false);
(0, _defineProperty2.default)(this, "relationsNextBatch", void 0);
(0, _defineProperty2.default)(this, "responses", null);
(0, _defineProperty2.default)(this, "endEvent", void 0);
/**
* Keep track of undecryptable relations
* As incomplete result sets affect poll results
*/
_defineProperty(this, "undecryptableRelationEventIds", new Set());
_defineProperty(this, "countUndecryptableEvents", events => {
(0, _defineProperty2.default)(this, "undecryptableRelationEventIds", new Set());
(0, _defineProperty2.default)(this, "countUndecryptableEvents", events => {
const undecryptableEventIds = events.filter(event => event.isDecryptionFailure()).map(event => event.getId());
const previousCount = this.undecryptableRelationsCount;
this.undecryptableRelationEventIds = new Set([...this.undecryptableRelationEventIds, ...undecryptableEventIds]);
@@ -64,9 +74,6 @@ export class Poll extends TypedEventEmitter {
this.emit(PollEvent.UndecryptableRelations, this.undecryptableRelationsCount);
}
});
this.rootEvent = rootEvent;
this.matrixClient = matrixClient;
this.room = room;
if (!this.rootEvent.getRoomId() || !this.rootEvent.getId()) {
throw new Error("Invalid poll start event.");
}
@@ -77,7 +84,8 @@ export class Poll extends TypedEventEmitter {
return this.rootEvent.getId();
}
get endEventId() {
return this.endEvent?.getId();
var _this$endEvent;
return (_this$endEvent = this.endEvent) === null || _this$endEvent === void 0 ? void 0 : _this$endEvent.getId();
}
get isEnded() {
return !!this.endEvent;
@@ -110,7 +118,8 @@ export class Poll extends TypedEventEmitter {
* @returns void
*/
onNewRelation(event) {
if (M_POLL_END.matches(event.getType()) && this.validateEndEvent(event)) {
var _this$endEvent2;
if (_polls.M_POLL_END.matches(event.getType()) && this.validateEndEvent(event)) {
this.endEvent = event;
this.refilterResponsesOnEnd();
this.emit(PollEvent.End);
@@ -120,7 +129,7 @@ export class Poll extends TypedEventEmitter {
if (!this.responses) {
return;
}
const pollEndTimestamp = this.endEvent?.getTs() || Number.MAX_SAFE_INTEGER;
const pollEndTimestamp = ((_this$endEvent2 = this.endEvent) === null || _this$endEvent2 === void 0 ? void 0 : _this$endEvent2.getTs()) || Number.MAX_SAFE_INTEGER;
const {
responseEvents
} = filterResponseRelations([event], pollEndTimestamp);
@@ -133,6 +142,7 @@ export class Poll extends TypedEventEmitter {
}
}
async fetchResponses() {
var _this$endEvent3, _allRelations$nextBat;
this._isFetchingResponses = true;
// we want:
@@ -143,21 +153,21 @@ export class Poll extends TypedEventEmitter {
from: this.relationsNextBatch || undefined
});
await Promise.all(allRelations.events.map(event => this.matrixClient.decryptEventIfNeeded(event)));
const responses = this.responses || new Relations("m.reference", M_POLL_RESPONSE.name, this.matrixClient, [M_POLL_RESPONSE.altName]);
const pollEndEvent = allRelations.events.find(event => M_POLL_END.matches(event.getType()));
const responses = this.responses || new _relations.Relations("m.reference", _polls.M_POLL_RESPONSE.name, this.matrixClient, [_polls.M_POLL_RESPONSE.altName]);
const pollEndEvent = allRelations.events.find(event => _polls.M_POLL_END.matches(event.getType()));
if (this.validateEndEvent(pollEndEvent)) {
this.endEvent = pollEndEvent;
this.refilterResponsesOnEnd();
this.emit(PollEvent.End);
}
const pollCloseTimestamp = this.endEvent?.getTs() || Number.MAX_SAFE_INTEGER;
const pollCloseTimestamp = ((_this$endEvent3 = this.endEvent) === null || _this$endEvent3 === void 0 ? void 0 : _this$endEvent3.getTs()) || Number.MAX_SAFE_INTEGER;
const {
responseEvents
} = filterResponseRelations(allRelations.events, pollCloseTimestamp);
responseEvents.forEach(event => {
responses.addEvent(event);
});
this.relationsNextBatch = allRelations.nextBatch ?? undefined;
this.relationsNextBatch = (_allRelations$nextBat = allRelations.nextBatch) !== null && _allRelations$nextBat !== void 0 ? _allRelations$nextBat : undefined;
this.responses = responses;
this.countUndecryptableEvents(allRelations.events);
@@ -182,13 +192,15 @@ export class Poll extends TypedEventEmitter {
* To ensure responses are valid
*/
refilterResponsesOnEnd() {
var _this$endEvent4;
if (!this.responses) {
return;
}
const pollEndTimestamp = this.endEvent?.getTs() || Number.MAX_SAFE_INTEGER;
const pollEndTimestamp = ((_this$endEvent4 = this.endEvent) === null || _this$endEvent4 === void 0 ? void 0 : _this$endEvent4.getTs()) || Number.MAX_SAFE_INTEGER;
this.responses.getRelations().forEach(event => {
if (event.getTs() > pollEndTimestamp) {
this.responses?.removeEvent(event);
var _this$responses;
(_this$responses = this.responses) === null || _this$responses === void 0 ? void 0 : _this$responses.removeEvent(event);
}
});
this.emit(PollEvent.Responses, this.responses);
@@ -222,8 +234,10 @@ export class Poll extends TypedEventEmitter {
* @param event - Event to test
* @returns true if the event is a poll event, else false
*/
export const isPollEvent = event => {
exports.Poll = Poll;
const isPollEvent = event => {
const eventType = event.getType();
return M_POLL_START.matches(eventType) || M_POLL_RESPONSE.matches(eventType) || M_POLL_END.matches(eventType);
return _matrixEventsSdk.M_POLL_START.matches(eventType) || _polls.M_POLL_RESPONSE.matches(eventType) || _polls.M_POLL_END.matches(eventType);
};
exports.isPollEvent = isPollEvent;
//# sourceMappingURL=poll.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,17 +0,0 @@
/**
* The timezone the user is currently in. The value of this property should
* match a timezone provided in https://www.iana.org/time-zones.
*
* This key was introduced in Matrix v1.16.
*/
export declare const ProfileKeyTimezone = "m.tz";
/**
* The timezone the user is currently in. The value of this property should
* match a timezone provided in https://www.iana.org/time-zones.
*
* @see https://github.com/matrix-org/matrix-spec-proposals/blob/clokep/profile-tz/proposals/4175-profile-field-time-zone.md
* @experimental
* @deprecated Unstable MSC field - Use `ProfileKeyTimezone`
*/
export declare const ProfileKeyMSC4175Timezone = "us.cloke.msc4175.tz";
//# sourceMappingURL=profile-keys.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"profile-keys.d.ts","sourceRoot":"","sources":["../../src/models/profile-keys.ts"],"names":[],"mappings":"AAgBA;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,SAAS,CAAC;AAEzC;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,wBAAwB,CAAC"}

View File

@@ -1,34 +0,0 @@
/*
Copyright 2025 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.
*/
/**
* The timezone the user is currently in. The value of this property should
* match a timezone provided in https://www.iana.org/time-zones.
*
* This key was introduced in Matrix v1.16.
*/
export const ProfileKeyTimezone = "m.tz";
/**
* The timezone the user is currently in. The value of this property should
* match a timezone provided in https://www.iana.org/time-zones.
*
* @see https://github.com/matrix-org/matrix-spec-proposals/blob/clokep/profile-tz/proposals/4175-profile-field-time-zone.md
* @experimental
* @deprecated Unstable MSC field - Use `ProfileKeyTimezone`
*/
export const ProfileKeyMSC4175Timezone = "us.cloke.msc4175.tz";
//# sourceMappingURL=profile-keys.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"profile-keys.js","names":[],"sources":["../../src/models/profile-keys.ts"],"sourcesContent":["/*\nCopyright 2025 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * The timezone the user is currently in. The value of this property should\n * match a timezone provided in https://www.iana.org/time-zones.\n *\n * This key was introduced in Matrix v1.16.\n */\nexport const ProfileKeyTimezone = \"m.tz\";\n\n/**\n * The timezone the user is currently in. The value of this property should\n * match a timezone provided in https://www.iana.org/time-zones.\n *\n * @see https://github.com/matrix-org/matrix-spec-proposals/blob/clokep/profile-tz/proposals/4175-profile-field-time-zone.md\n * @experimental\n * @deprecated Unstable MSC field - Use `ProfileKeyTimezone`\n */\nexport const ProfileKeyMSC4175Timezone = \"us.cloke.msc4175.tz\";\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAM,kBAAkB,GAAG,MAAM;;AAExC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAM,yBAAyB,GAAG,qBAAqB","ignoreList":[]}

View File

@@ -1,22 +1,14 @@
import { type CachedReceipt, type Receipt, ReceiptType, type WrappedReceipt } from "../@types/read_receipts.ts";
import { type ListenerMap, TypedEventEmitter } from "./typed-event-emitter.ts";
import { MatrixEvent } from "./event.ts";
import { type EventTimelineSet } from "./event-timeline-set.ts";
import { NotificationCountType } from "./room.ts";
/**
* Create a synthetic receipt for the given event
* @param userId - The user ID if the receipt sender
* @param event - The event that is to be acknowledged
* @param receiptType - The type of receipt
* @param unthreaded - the receipt is unthreaded
* @returns a new event with the synthetic receipt in it
*/
export declare function synthesizeReceipt(userId: string, event: MatrixEvent, receiptType: ReceiptType, unthreaded?: boolean): MatrixEvent;
import { CachedReceipt, Receipt, ReceiptType, WrappedReceipt } from "../@types/read_receipts";
import { ListenerMap, TypedEventEmitter } from "./typed-event-emitter";
import { MatrixEvent } from "./event";
import { EventTimelineSet } from "./event-timeline-set";
import { NotificationCountType } from "./room";
export declare function synthesizeReceipt(userId: string, event: MatrixEvent, receiptType: ReceiptType): MatrixEvent;
export declare abstract class ReadReceipt<Events extends string, Arguments extends ListenerMap<Events>, SuperclassArguments extends ListenerMap<any> = Arguments> extends TypedEventEmitter<Events, Arguments, SuperclassArguments> {
private receipts;
private receiptCacheByEventId;
abstract getUnfilteredTimelineSet(): EventTimelineSet;
abstract get timeline(): MatrixEvent[];
abstract timeline: MatrixEvent[];
/**
* Gets the latest receipt for a given user in the room
* @param userId - The id of the user for which we want the receipt
@@ -27,28 +19,15 @@ export declare abstract class ReadReceipt<Events extends string, Arguments exten
getReadReceiptForUserId(userId: string, ignoreSynthesized?: boolean, receiptType?: ReceiptType): WrappedReceipt | null;
private compareReceipts;
/**
* Get the ID of the event that a given user has read up to, or null if:
* - we have received no read receipts for them, or
* - the receipt we have points at an event we don't have, or
* - the thread ID in the receipt does not match the thread root of the
* referenced event.
*
* (The event might not exist if it is not loaded, and the thread ID might
* not match if the event has moved thread because it was redacted.)
*
* Get the ID of the event that a given user has read up to, or null if we
* have received no read receipts from them.
* @param userId - The user ID to get read receipt event ID for
* @param ignoreSynthesized - If true, return only receipts that have been
* sent by the server, not implicit ones generated
* by the JS SDK.
* @returns ID of the latest existing event that the given user has read, or null.
* sent by the server, not implicit ones generated
* by the JS SDK.
* @returns ID of the latest event that the given user has read, or null.
*/
getEventReadUpTo(userId: string, ignoreSynthesized?: boolean): string | null;
/**
* Returns true if the event pointed at by this receipt exists, and its
* threadRootId is consistent with the thread information in the receipt.
*/
private receiptPointsAtConsistentEvent;
private getLatestReceipt;
addReceiptToStructure(eventId: string, receiptType: ReceiptType, userId: string, receipt: Receipt, synthetic: boolean): void;
/**
* Get a list of receipts for the given event.
@@ -59,12 +38,6 @@ export declare abstract class ReadReceipt<Events extends string, Arguments exten
getReceiptsForEvent(event: MatrixEvent): CachedReceipt[];
abstract addReceipt(event: MatrixEvent, synthetic: boolean): void;
abstract setUnread(type: NotificationCountType, count: number): void;
/**
* Look in this room/thread's timeline to find an event. If `this` is a
* room, we look in all threads, but if `this` is a thread, we look only
* inside this thread.
*/
abstract findEventById(eventId: string): MatrixEvent | undefined;
/**
* This issue should also be addressed on synapse's side and is tracked as part
* of https://github.com/matrix-org/synapse/issues/14837
@@ -83,9 +56,8 @@ export declare abstract class ReadReceipt<Events extends string, Arguments exten
* @param userId - The user ID if the receipt sender
* @param e - The event that is to be acknowledged
* @param receiptType - The type of receipt
* @param unthreaded - the receipt is unthreaded
*/
addLocalEchoReceipt(userId: string, e: MatrixEvent, receiptType: ReceiptType, unthreaded?: boolean): void;
addLocalEchoReceipt(userId: string, e: MatrixEvent, receiptType: ReceiptType): void;
/**
* Get a list of user IDs who have <b>read up to</b> the given event.
* @param event - the event to get read receipts for.
@@ -100,15 +72,13 @@ export declare abstract class ReadReceipt<Events extends string, Arguments exten
* @param eventId - The event ID to check if the user read.
* @returns True if the user has read the event, false otherwise.
*/
abstract hasUserReadEvent(userId: string, eventId: string): boolean;
hasUserReadEvent(userId: string, eventId: string): boolean;
/**
* Returns the most recent unthreaded receipt for a given user
* @param userId - the MxID of the User
* @returns an unthreaded Receipt. Can be undefined if receipts have been disabled
* or a user chooses to use private read receipts (or we have simply not received
* a receipt from this user yet).
*
* @deprecated use `hasUserReadEvent` or `getEventReadUpTo` instead
*/
abstract getLastUnthreadedReceiptFor(userId: string): Receipt | undefined;
}

View File

@@ -1 +1 @@
{"version":3,"file":"read-receipt.d.ts","sourceRoot":"","sources":["../../src/models/read-receipt.ts"],"names":[],"mappings":"AAaA,OAAO,EACH,KAAK,aAAa,EAElB,KAAK,OAAO,EAEZ,WAAW,EACX,KAAK,cAAc,EACtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,KAAK,WAAW,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE/E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAIlD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,WAAW,EAClB,WAAW,EAAE,WAAW,EACxB,UAAU,UAAQ,GACnB,WAAW,CAeb;AAKD,8BAAsB,WAAW,CAC7B,MAAM,SAAS,MAAM,EACrB,SAAS,SAAS,WAAW,CAAC,MAAM,CAAC,EACrC,mBAAmB,SAAS,WAAW,CAAC,GAAG,CAAC,GAAG,SAAS,CAC1D,SAAQ,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,mBAAmB,CAAC;IAM/D,OAAO,CAAC,QAAQ,CAGG;IACnB,OAAO,CAAC,qBAAqB,CAA2B;IAExD,SAAgB,wBAAwB,IAAI,gBAAgB,CAAC;IAC7D,aAAoB,QAAQ,IAAI,WAAW,EAAE,CAAC;IAE9C;;;;;;OAMG;IACI,uBAAuB,CAC1B,MAAM,EAAE,MAAM,EACd,iBAAiB,UAAQ,EACzB,WAAW,cAAmB,GAC/B,cAAc,GAAG,IAAI,CAOvB;IAED,OAAO,CAAC,eAAe;IAOvB;;;;;;;;;;;;;;;OAeG;IACI,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,iBAAiB,UAAQ,GAAG,MAAM,GAAG,IAAI,CAShF;IAED;;;OAGG;IACH,OAAO,CAAC,8BAA8B;IA8EtC,OAAO,CAAC,gBAAgB;IAsBjB,qBAAqB,CACxB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,OAAO,GACnB,IAAI,CAmFN;IAED;;;;;OAKG;IACI,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,aAAa,EAAE,CAE9D;IAED,SAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IAEzE,SAAgB,SAAS,CAAC,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5E;;;;OAIG;IACH,SAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;IAExE;;;;;;;;;;OAUG;IACI,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAQ9C;IAED;;;;;;;OAOG;IACI,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,UAAQ,GAAG,IAAI,CAE7G;IAED;;;;OAIG;IACI,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE,CAQpD;IAED;;;;;;;OAOG;IACH,SAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAE3E;;;;;;;;OAQG;IACH,SAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CACpF"}
{"version":3,"file":"read-receipt.d.ts","sourceRoot":"","sources":["../../src/models/read-receipt.ts"],"names":[],"mappings":"AAaA,OAAO,EACH,aAAa,EAEb,OAAO,EAEP,WAAW,EACX,cAAc,EACjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAC;AAE/C,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,GAAG,WAAW,CAe3G;AAKD,8BAAsB,WAAW,CAC7B,MAAM,SAAS,MAAM,EACrB,SAAS,SAAS,WAAW,CAAC,MAAM,CAAC,EACrC,mBAAmB,SAAS,WAAW,CAAC,GAAG,CAAC,GAAG,SAAS,CAC1D,SAAQ,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,mBAAmB,CAAC;IAM/D,OAAO,CAAC,QAAQ,CAGG;IACnB,OAAO,CAAC,qBAAqB,CAA2B;aAExC,wBAAwB,IAAI,gBAAgB;IAC5D,SAAgB,QAAQ,EAAE,WAAW,EAAE,CAAC;IAExC;;;;;;OAMG;IACI,uBAAuB,CAC1B,MAAM,EAAE,MAAM,EACd,iBAAiB,UAAQ,EACzB,WAAW,cAAmB,GAC/B,cAAc,GAAG,IAAI;IASxB,OAAO,CAAC,eAAe;IAOvB;;;;;;;;OAQG;IACI,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,iBAAiB,UAAQ,GAAG,MAAM,GAAG,IAAI;IAsB1E,qBAAqB,CACxB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,OAAO,GACnB,IAAI;IAqFP;;;;;OAKG;IACI,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,aAAa,EAAE;aAI/C,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI;aAExD,SAAS,CAAC,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAE3E;;;;;;;;;;OAUG;IACI,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAU/C;;;;;;OAMG;IACI,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI;IAI1F;;;;OAIG;IACI,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,EAAE;IAUrD;;;;;;;OAOG;IACI,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IA4BjE;;;;;;OAMG;aACa,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;CACnF"}

View File

@@ -1,5 +1,18 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _objectSpread from "@babel/runtime/helpers/objectSpread2";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ReadReceipt = void 0;
exports.synthesizeReceipt = synthesizeReceipt;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _read_receipts = require("../@types/read_receipts");
var _typedEventEmitter = require("./typed-event-emitter");
var _utils = require("../utils");
var _event = require("./event");
var _event2 = require("../@types/event");
var _room = require("./room");
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,44 +26,26 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { MAIN_ROOM_TIMELINE, ReceiptType } from "../@types/read_receipts.js";
import { TypedEventEmitter } from "./typed-event-emitter.js";
import { isSupportedReceiptType } from "../utils.js";
import { MatrixEvent } from "./event.js";
import { EventType } from "../@types/event.js";
import { MapWithDefault } from "../utils.js";
import { NotificationCountType } from "./room.js";
import { logger } from "../logger.js";
import { inMainTimelineForReceipt, threadIdForReceipt } from "../client.js";
/**
* Create a synthetic receipt for the given event
* @param userId - The user ID if the receipt sender
* @param event - The event that is to be acknowledged
* @param receiptType - The type of receipt
* @param unthreaded - the receipt is unthreaded
* @returns a new event with the synthetic receipt in it
*/
export function synthesizeReceipt(userId, event, receiptType, unthreaded = false) {
return new MatrixEvent({
function synthesizeReceipt(userId, event, receiptType) {
var _event$threadRootId;
return new _event.MatrixEvent({
content: {
[event.getId()]: {
[receiptType]: {
[userId]: _objectSpread({
ts: event.getTs()
}, !unthreaded && {
thread_id: threadIdForReceipt(event)
})
[userId]: {
ts: event.getTs(),
thread_id: (_event$threadRootId = event.threadRootId) !== null && _event$threadRootId !== void 0 ? _event$threadRootId : _read_receipts.MAIN_ROOM_TIMELINE
}
}
}
},
type: EventType.Receipt,
type: _event2.EventType.Receipt,
room_id: event.getRoomId()
});
}
const ReceiptPairRealIndex = 0;
const ReceiptPairSyntheticIndex = 1;
export class ReadReceipt extends TypedEventEmitter {
class ReadReceipt extends _typedEventEmitter.TypedEventEmitter {
constructor(...args) {
super(...args);
// receipts should clobber based on receipt_type and user_id pairs hence
@@ -58,8 +53,9 @@ export class ReadReceipt extends TypedEventEmitter {
// which pass in an event ID and get back some receipts, so we also store
// a pre-cached list for this purpose.
// Map: receipt type → user Id → receipt
_defineProperty(this, "receipts", new MapWithDefault(() => new Map()));
_defineProperty(this, "receiptCacheByEventId", new Map());
(0, _defineProperty2.default)(this, "receipts", new _utils.MapWithDefault(() => new Map()));
(0, _defineProperty2.default)(this, "receiptCacheByEventId", new Map());
(0, _defineProperty2.default)(this, "timeline", void 0);
}
/**
* Gets the latest receipt for a given user in the room
@@ -68,142 +64,55 @@ export class ReadReceipt extends TypedEventEmitter {
* @param receiptType - Optional. The type of the receipt we want to get
* @returns the latest receipts of the chosen type for the chosen user
*/
getReadReceiptForUserId(userId, ignoreSynthesized = false, receiptType = ReceiptType.Read) {
const [realReceipt, syntheticReceipt] = this.receipts.get(receiptType)?.get(userId) ?? [null, null];
getReadReceiptForUserId(userId, ignoreSynthesized = false, receiptType = _read_receipts.ReceiptType.Read) {
var _this$receipts$get$ge, _this$receipts$get;
const [realReceipt, syntheticReceipt] = (_this$receipts$get$ge = (_this$receipts$get = this.receipts.get(receiptType)) === null || _this$receipts$get === void 0 ? void 0 : _this$receipts$get.get(userId)) !== null && _this$receipts$get$ge !== void 0 ? _this$receipts$get$ge : [null, null];
if (ignoreSynthesized) {
return realReceipt;
}
return syntheticReceipt ?? realReceipt;
return syntheticReceipt !== null && syntheticReceipt !== void 0 ? syntheticReceipt : realReceipt;
}
compareReceipts(a, b) {
var _this$getUnfilteredTi;
// Try compare them in our unfiltered timeline set order, falling back to receipt timestamp which should be
// relatively sane as receipts are set only by the originating homeserver so as long as its clock doesn't
// jump around then it should be valid.
return this.getUnfilteredTimelineSet().compareEventOrdering(a.eventId, b.eventId) ?? a.data.ts - b.data.ts;
return (_this$getUnfilteredTi = this.getUnfilteredTimelineSet().compareEventOrdering(a.eventId, b.eventId)) !== null && _this$getUnfilteredTi !== void 0 ? _this$getUnfilteredTi : a.data.ts - b.data.ts;
}
/**
* Get the ID of the event that a given user has read up to, or null if:
* - we have received no read receipts for them, or
* - the receipt we have points at an event we don't have, or
* - the thread ID in the receipt does not match the thread root of the
* referenced event.
*
* (The event might not exist if it is not loaded, and the thread ID might
* not match if the event has moved thread because it was redacted.)
*
* Get the ID of the event that a given user has read up to, or null if we
* have received no read receipts from them.
* @param userId - The user ID to get read receipt event ID for
* @param ignoreSynthesized - If true, return only receipts that have been
* sent by the server, not implicit ones generated
* by the JS SDK.
* @returns ID of the latest existing event that the given user has read, or null.
* sent by the server, not implicit ones generated
* by the JS SDK.
* @returns ID of the latest event that the given user has read, or null.
*/
getEventReadUpTo(userId, ignoreSynthesized = false) {
// Find what the latest receipt says is the latest event we have read
const latestReceipt = this.getLatestReceipt(userId, ignoreSynthesized);
if (!latestReceipt) {
return null;
}
return this.receiptPointsAtConsistentEvent(latestReceipt) ? latestReceipt.eventId : null;
}
/**
* Returns true if the event pointed at by this receipt exists, and its
* threadRootId is consistent with the thread information in the receipt.
*/
receiptPointsAtConsistentEvent(receipt) {
const event = this.findEventById(receipt.eventId);
if (!event) {
// If the receipt points at a non-existent event, we have multiple
// possibilities:
//
// 1. We don't have the event because it's not loaded yet - probably
// it's old and we're best off ignoring the receipt - we can just
// send a new one when we read a new event.
//
// 2. We have a bug e.g. we misclassified this event into the wrong
// thread.
//
// 3. The referenced event moved out of this thread (e.g. because it
// was deleted.)
//
// 4. The receipt had the incorrect thread ID (due to a bug in a
// client, or malicious behaviour).
// This receipt is not "valid" because it doesn't point at an event
// we have. We want to pretend it doesn't exist.
return false;
}
if (!receipt.data?.thread_id) {
// If this is an unthreaded receipt, it could point at any event, so
// there is no need to validate further - this receipt is valid.
return true;
}
// Otherwise it is a threaded receipt...
if (receipt.data.thread_id === MAIN_ROOM_TIMELINE) {
// The receipt is for the main timeline: we check that the event is
// in the main timeline.
// Check if the event is in the main timeline
const eventIsInMainTimeline = inMainTimelineForReceipt(event);
if (eventIsInMainTimeline) {
// The receipt is for the main timeline, and so is the event, so
// the receipt is valid.
return true;
}
} else {
// The receipt is for a different thread (not the main timeline)
if (event.threadRootId === receipt.data.thread_id) {
// If the receipt and event agree on the thread ID, the receipt
// is valid.
return true;
}
}
// The receipt thread ID disagrees with the event thread ID. There are 2
// possibilities:
//
// 1. The event moved to a different thread after the receipt was
// created. This can happen if the event was redacted because that
// moves it to the main timeline.
//
// 2. There is a bug somewhere - either we put the event into the wrong
// thread, or someone sent an incorrect receipt.
//
// In many cases, we won't get here because the call to findEventById
// would have already returned null. We include this check to cover
// cases when `this` is a room, meaning findEventById will find events
// in any thread, and to be defensive against unforeseen code paths.
logger.warn(`Ignoring receipt because its thread_id (${receipt.data.thread_id}) disagrees ` + `with the thread root (${event.threadRootId}) of the referenced event ` + `(event ID = ${receipt.eventId})`);
// This receipt is not "valid" because it disagrees with us about what
// thread the event is in. We want to pretend it doesn't exist.
return false;
}
getLatestReceipt(userId, ignoreSynthesized) {
var _ref, _privateReadReceipt$e, _ref2;
// XXX: This is very very ugly and I hope I won't have to ever add a new
// receipt type here again. IMHO this should be done by the server in
// some more intelligent manner or the client should just use timestamps
const publicReadReceipt = this.getReadReceiptForUserId(userId, ignoreSynthesized, ReceiptType.Read);
const privateReadReceipt = this.getReadReceiptForUserId(userId, ignoreSynthesized, ReceiptType.ReadPrivate);
const publicReadReceipt = this.getReadReceiptForUserId(userId, ignoreSynthesized, _read_receipts.ReceiptType.Read);
const privateReadReceipt = this.getReadReceiptForUserId(userId, ignoreSynthesized, _read_receipts.ReceiptType.ReadPrivate);
// If we have both, compare them
let comparison;
if (publicReadReceipt?.eventId && privateReadReceipt?.eventId) {
if (publicReadReceipt !== null && publicReadReceipt !== void 0 && publicReadReceipt.eventId && privateReadReceipt !== null && privateReadReceipt !== void 0 && privateReadReceipt.eventId) {
comparison = this.compareReceipts(publicReadReceipt, privateReadReceipt);
}
// The public receipt is more likely to drift out of date so the private
// one has precedence
if (!comparison) return privateReadReceipt ?? publicReadReceipt ?? null;
if (!comparison) return (_ref = (_privateReadReceipt$e = privateReadReceipt === null || privateReadReceipt === void 0 ? void 0 : privateReadReceipt.eventId) !== null && _privateReadReceipt$e !== void 0 ? _privateReadReceipt$e : publicReadReceipt === null || publicReadReceipt === void 0 ? void 0 : publicReadReceipt.eventId) !== null && _ref !== void 0 ? _ref : null;
// If public read receipt is older, return the private one
return (comparison < 0 ? privateReadReceipt : publicReadReceipt) ?? null;
return (_ref2 = comparison < 0 ? privateReadReceipt === null || privateReadReceipt === void 0 ? void 0 : privateReadReceipt.eventId : publicReadReceipt === null || publicReadReceipt === void 0 ? void 0 : publicReadReceipt.eventId) !== null && _ref2 !== void 0 ? _ref2 : null;
}
addReceiptToStructure(eventId, receiptType, userId, receipt, synthetic) {
var _pair$ReceiptPairSynt2, _pair$ReceiptPairSynt3;
const receiptTypesMap = this.receipts.getOrCreate(receiptType);
let pair = receiptTypesMap.get(userId);
if (!pair) {
@@ -212,7 +121,8 @@ export class ReadReceipt extends TypedEventEmitter {
}
let existingReceipt = pair[ReceiptPairRealIndex];
if (synthetic) {
existingReceipt = pair[ReceiptPairSyntheticIndex] ?? pair[ReceiptPairRealIndex];
var _pair$ReceiptPairSynt;
existingReceipt = (_pair$ReceiptPairSynt = pair[ReceiptPairSyntheticIndex]) !== null && _pair$ReceiptPairSynt !== void 0 ? _pair$ReceiptPairSynt : pair[ReceiptPairRealIndex];
}
const wrappedReceipt = {
eventId,
@@ -236,7 +146,7 @@ export class ReadReceipt extends TypedEventEmitter {
// we don't bother caching just real receipts by event ID as there's nothing that would read it.
// Take the current cached receipt before we overwrite the pair elements.
const cachedReceipt = pair[ReceiptPairSyntheticIndex] ?? pair[ReceiptPairRealIndex];
const cachedReceipt = (_pair$ReceiptPairSynt2 = pair[ReceiptPairSyntheticIndex]) !== null && _pair$ReceiptPairSynt2 !== void 0 ? _pair$ReceiptPairSynt2 : pair[ReceiptPairRealIndex];
if (synthetic && preferSynthetic) {
pair[ReceiptPairSyntheticIndex] = wrappedReceipt;
} else if (!synthetic) {
@@ -245,7 +155,7 @@ export class ReadReceipt extends TypedEventEmitter {
pair[ReceiptPairSyntheticIndex] = null;
}
}
const newCachedReceipt = pair[ReceiptPairSyntheticIndex] ?? pair[ReceiptPairRealIndex];
const newCachedReceipt = (_pair$ReceiptPairSynt3 = pair[ReceiptPairSyntheticIndex]) !== null && _pair$ReceiptPairSynt3 !== void 0 ? _pair$ReceiptPairSynt3 : pair[ReceiptPairRealIndex];
if (cachedReceipt === newCachedReceipt) return;
// clean up any previous cache entry
@@ -280,13 +190,6 @@ export class ReadReceipt extends TypedEventEmitter {
getReceiptsForEvent(event) {
return this.receiptCacheByEventId.get(event.getId()) || [];
}
/**
* Look in this room/thread's timeline to find an event. If `this` is a
* room, we look in all threads, but if `this` is a thread, we look only
* inside this thread.
*/
/**
* This issue should also be addressed on synapse's side and is tracked as part
* of https://github.com/matrix-org/synapse/issues/14837
@@ -301,9 +204,9 @@ export class ReadReceipt extends TypedEventEmitter {
fixupNotifications(userId) {
const receipt = this.getReadReceiptForUserId(userId, false);
const lastEvent = this.timeline[this.timeline.length - 1];
if (lastEvent && receipt?.eventId === lastEvent.getId() && userId === lastEvent.getSender()) {
this.setUnread(NotificationCountType.Total, 0);
this.setUnread(NotificationCountType.Highlight, 0);
if (lastEvent && (receipt === null || receipt === void 0 ? void 0 : receipt.eventId) === lastEvent.getId() && userId === lastEvent.getSender()) {
this.setUnread(_room.NotificationCountType.Total, 0);
this.setUnread(_room.NotificationCountType.Highlight, 0);
}
}
@@ -313,10 +216,9 @@ export class ReadReceipt extends TypedEventEmitter {
* @param userId - The user ID if the receipt sender
* @param e - The event that is to be acknowledged
* @param receiptType - The type of receipt
* @param unthreaded - the receipt is unthreaded
*/
addLocalEchoReceipt(userId, e, receiptType, unthreaded = false) {
this.addReceipt(synthesizeReceipt(userId, e, receiptType, unthreaded), true);
addLocalEchoReceipt(userId, e, receiptType) {
this.addReceipt(synthesizeReceipt(userId, e, receiptType), true);
}
/**
@@ -326,7 +228,7 @@ export class ReadReceipt extends TypedEventEmitter {
*/
getUsersReadUpTo(event) {
return this.getReceiptsForEvent(event).filter(function (receipt) {
return isSupportedReceiptType(receipt.type);
return (0, _utils.isSupportedReceiptType)(receipt.type);
}).map(function (receipt) {
return receipt.userId;
});
@@ -340,6 +242,29 @@ export class ReadReceipt extends TypedEventEmitter {
* @param eventId - The event ID to check if the user read.
* @returns True if the user has read the event, false otherwise.
*/
hasUserReadEvent(userId, eventId) {
var _this$timeline;
const readUpToId = this.getEventReadUpTo(userId, false);
if (readUpToId === eventId) return true;
if ((_this$timeline = this.timeline) !== null && _this$timeline !== void 0 && _this$timeline.length && this.timeline[this.timeline.length - 1].getSender() && this.timeline[this.timeline.length - 1].getSender() === userId) {
// It doesn't matter where the event is in the timeline, the user has read
// it because they've sent the latest event.
return true;
}
for (let i = ((_this$timeline2 = this.timeline) === null || _this$timeline2 === void 0 ? void 0 : _this$timeline2.length) - 1; i >= 0; --i) {
var _this$timeline2;
const ev = this.timeline[i];
// If we encounter the target event first, the user hasn't read it
// however if we encounter the readUpToId first then the user has read
// it. These rules apply because we're iterating bottom-up.
if (ev.getId() === eventId) return false;
if (ev.getId() === readUpToId) return true;
}
// We don't know if the user has read it, so assume not.
return false;
}
/**
* Returns the most recent unthreaded receipt for a given user
@@ -347,8 +272,7 @@ export class ReadReceipt extends TypedEventEmitter {
* @returns an unthreaded Receipt. Can be undefined if receipts have been disabled
* or a user chooses to use private read receipts (or we have simply not received
* a receipt from this user yet).
*
* @deprecated use `hasUserReadEvent` or `getEventReadUpTo` instead
*/
}
exports.ReadReceipt = ReadReceipt;
//# sourceMappingURL=read-receipt.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
import { type Relations, type RelationsEvent, type EventHandlerMap } from "./relations.ts";
import { type MatrixEvent } from "./event.ts";
import { type Listener } from "./typed-event-emitter.ts";
import { Relations, RelationsEvent, EventHandlerMap } from "./relations";
import { MatrixEvent } from "./event";
import { Listener } from "./typed-event-emitter";
export declare class RelatedRelations {
private relations;
constructor(relations: Relations[]);

View File

@@ -1 +1 @@
{"version":3,"file":"related-relations.d.ts","sourceRoot":"","sources":["../../src/models/related-relations.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,qBAAa,gBAAgB;IACzB,OAAO,CAAC,SAAS,CAAc;IAE/B,YAAmB,SAAS,EAAE,SAAS,EAAE,EAExC;IAEM,YAAY,IAAI,WAAW,EAAE,CAEnC;IAEM,EAAE,CAAC,CAAC,SAAS,cAAc,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,cAAc,EAAE,eAAe,EAAE,CAAC,CAAC,GAAG,IAAI,CAEjG;IAEM,GAAG,CAAC,CAAC,SAAS,cAAc,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,cAAc,EAAE,eAAe,EAAE,CAAC,CAAC,GAAG,IAAI,CAElG;CACJ"}
{"version":3,"file":"related-relations.d.ts","sourceRoot":"","sources":["../../src/models/related-relations.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,qBAAa,gBAAgB;IACzB,OAAO,CAAC,SAAS,CAAc;gBAEZ,SAAS,EAAE,SAAS,EAAE;IAIlC,YAAY,IAAI,WAAW,EAAE;IAI7B,EAAE,CAAC,CAAC,SAAS,cAAc,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,cAAc,EAAE,eAAe,EAAE,CAAC,CAAC,GAAG,IAAI;IAI3F,GAAG,CAAC,CAAC,SAAS,cAAc,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,cAAc,EAAE,eAAe,EAAE,CAAC,CAAC,GAAG,IAAI;CAGtG"}

View File

@@ -1,4 +1,11 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.RelatedRelations = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
@@ -15,9 +22,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
export class RelatedRelations {
class RelatedRelations {
constructor(relations) {
_defineProperty(this, "relations", void 0);
(0, _defineProperty2.default)(this, "relations", void 0);
this.relations = relations.filter(r => !!r);
}
getRelations() {
@@ -30,4 +37,5 @@ export class RelatedRelations {
this.relations.forEach(r => r.off(ev, fn));
}
}
exports.RelatedRelations = RelatedRelations;
//# sourceMappingURL=related-relations.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"related-relations.js","names":[],"sources":["../../src/models/related-relations.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { type Relations, type RelationsEvent, type EventHandlerMap } from \"./relations.ts\";\nimport { type MatrixEvent } from \"./event.ts\";\nimport { type Listener } from \"./typed-event-emitter.ts\";\n\nexport class RelatedRelations {\n private relations: Relations[];\n\n public constructor(relations: Relations[]) {\n this.relations = relations.filter((r) => !!r);\n }\n\n public getRelations(): MatrixEvent[] {\n return this.relations.reduce<MatrixEvent[]>((c, p) => [...c, ...p.getRelations()], []);\n }\n\n public on<T extends RelationsEvent>(ev: T, fn: Listener<RelationsEvent, EventHandlerMap, T>): void {\n this.relations.forEach((r) => r.on(ev, fn));\n }\n\n public off<T extends RelationsEvent>(ev: T, fn: Listener<RelationsEvent, EventHandlerMap, T>): void {\n this.relations.forEach((r) => r.off(ev, fn));\n }\n}\n"],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMA,OAAO,MAAM,gBAAgB,CAAC;EAGnB,WAAW,CAAC,SAAsB,EAAE;IAAA;IACvC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,MAAM,CAAE,CAAC,IAAK,CAAC,CAAC,CAAC,CAAC;EACjD;EAEO,YAAY,GAAkB;IACjC,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAgB,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;EAC1F;EAEO,EAAE,CAA2B,EAAK,EAAE,EAAgD,EAAQ;IAC/F,IAAI,CAAC,SAAS,CAAC,OAAO,CAAE,CAAC,IAAK,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;EAC/C;EAEO,GAAG,CAA2B,EAAK,EAAE,EAAgD,EAAQ;IAChG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAE,CAAC,IAAK,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;EAChD;AACJ","ignoreList":[]}
{"version":3,"file":"related-relations.js","names":["RelatedRelations","constructor","relations","_defineProperty2","default","filter","r","getRelations","reduce","c","p","on","ev","fn","forEach","off","exports"],"sources":["../../src/models/related-relations.ts"],"sourcesContent":["/*\nCopyright 2022 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { Relations, RelationsEvent, EventHandlerMap } from \"./relations\";\nimport { MatrixEvent } from \"./event\";\nimport { Listener } from \"./typed-event-emitter\";\n\nexport class RelatedRelations {\n private relations: Relations[];\n\n public constructor(relations: Relations[]) {\n this.relations = relations.filter((r) => !!r);\n }\n\n public getRelations(): MatrixEvent[] {\n return this.relations.reduce<MatrixEvent[]>((c, p) => [...c, ...p.getRelations()], []);\n }\n\n public on<T extends RelationsEvent>(ev: T, fn: Listener<RelationsEvent, EventHandlerMap, T>): void {\n this.relations.forEach((r) => r.on(ev, fn));\n }\n\n public off<T extends RelationsEvent>(ev: T, fn: Listener<RelationsEvent, EventHandlerMap, T>): void {\n this.relations.forEach((r) => r.off(ev, fn));\n }\n}\n"],"mappings":";;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAMO,MAAMA,gBAAgB,CAAC;EAGnBC,WAAWA,CAACC,SAAsB,EAAE;IAAA,IAAAC,gBAAA,CAAAC,OAAA;IACvC,IAAI,CAACF,SAAS,GAAGA,SAAS,CAACG,MAAM,CAAEC,CAAC,IAAK,CAAC,CAACA,CAAC,CAAC;EACjD;EAEOC,YAAYA,CAAA,EAAkB;IACjC,OAAO,IAAI,CAACL,SAAS,CAACM,MAAM,CAAgB,CAACC,CAAC,EAAEC,CAAC,KAAK,CAAC,GAAGD,CAAC,EAAE,GAAGC,CAAC,CAACH,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;EAC1F;EAEOI,EAAEA,CAA2BC,EAAK,EAAEC,EAAgD,EAAQ;IAC/F,IAAI,CAACX,SAAS,CAACY,OAAO,CAAER,CAAC,IAAKA,CAAC,CAACK,EAAE,CAACC,EAAE,EAAEC,EAAE,CAAC,CAAC;EAC/C;EAEOE,GAAGA,CAA2BH,EAAK,EAAEC,EAAgD,EAAQ;IAChG,IAAI,CAACX,SAAS,CAACY,OAAO,CAAER,CAAC,IAAKA,CAAC,CAACS,GAAG,CAACH,EAAE,EAAEC,EAAE,CAAC,CAAC;EAChD;AACJ;AAACG,OAAA,CAAAhB,gBAAA,GAAAA,gBAAA"}

View File

@@ -1,9 +1,9 @@
import { Relations } from "./relations.ts";
import { type EventType, type RelationType } from "../@types/event.ts";
import { type MatrixEvent } from "./event.ts";
import { type EventTimelineSet } from "./event-timeline-set.ts";
import { type MatrixClient } from "../client.ts";
import { type Room } from "./room.ts";
import { Relations } from "./relations";
import { EventType, RelationType } from "../@types/event";
import { MatrixEvent } from "./event";
import { EventTimelineSet } from "./event-timeline-set";
import { MatrixClient } from "../client";
import { Room } from "./room";
export declare class RelationsContainer {
private readonly client;
private readonly room?;
@@ -40,15 +40,5 @@ export declare class RelationsContainer {
* @param timelineSet - The event timeline set within which to search for the related event if any.
*/
aggregateChildEvent(event: MatrixEvent, timelineSet?: EventTimelineSet): void;
/**
* 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: MatrixEvent, timelineSet: EventTimelineSet, targetEvent: MatrixEvent): Promise<void>;
private aggregateChildEventInternal;
}
//# sourceMappingURL=relations-container.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"relations-container.d.ts","sourceRoot":"","sources":["../../src/models/relations-container.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAe,KAAK,WAAW,EAAoB,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAGtC,qBAAa,kBAAkB;IAMvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;IAJ1B,OAAO,CAAC,SAAS,CAAqF;IAEtG,YACqB,MAAM,EAAE,YAAY,EACpB,IAAI,CAAC,EAAE,IAAI,YAAA,EAC5B;IAEJ;;;;;;;;;;;;;OAaG;IACI,sBAAsB,CACzB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,GAAG,MAAM,EACnC,SAAS,EAAE,SAAS,GAAG,MAAM,GAC9B,SAAS,GAAG,SAAS,CAEvB;IAEM,yBAAyB,CAAC,aAAa,EAAE,MAAM,GAAG,WAAW,EAAE,CAUrE;IAED;;;;;;OAMG;IACI,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CASpD;IAED;;;;;OAKG;IACI,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACrF;;;;;;;OAOG;IACI,mBAAmB,CACtB,KAAK,EAAE,WAAW,EAClB,WAAW,EAAE,gBAAgB,EAC7B,WAAW,EAAE,WAAW,GACzB,OAAO,CAAC,IAAI,CAAC,CAAC;IAYjB,OAAO,CAAC,2BAA2B;CAmEtC"}
{"version":3,"file":"relations-container.d.ts","sourceRoot":"","sources":["../../src/models/relations-container.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAe,WAAW,EAAoB,MAAM,SAAS,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,qBAAa,kBAAkB;IAKR,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAgB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;IAFhF,OAAO,CAAC,SAAS,CAAqF;gBAElE,MAAM,EAAE,YAAY,EAAmB,IAAI,CAAC,kBAAM;IAEtF;;;;;;;;;;;;;OAaG;IACI,sBAAsB,CACzB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,GAAG,MAAM,EACnC,SAAS,EAAE,SAAS,GAAG,MAAM,GAC9B,SAAS,GAAG,SAAS;IAIjB,yBAAyB,CAAC,aAAa,EAAE,MAAM,GAAG,WAAW,EAAE;IAYtE;;;;;;OAMG;IACI,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAWrD;;;;;OAKG;IACI,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,gBAAgB,GAAG,IAAI;CAyDvF"}

View File

@@ -1,4 +1,13 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.RelationsContainer = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _relations = require("./relations");
var _event = require("./event");
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
@@ -15,16 +24,13 @@ 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 {
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;
// A tree of objects to access a set of related children for an event, as in:
// this.relations.get(parentEventId).get(relationType).get(relationEventType)
(0, _defineProperty2.default)(this, "relations", new Map());
}
/**
@@ -42,10 +48,12 @@ export class RelationsContainer {
* the relationType.
*/
getChildEventsForEvent(eventId, relationType, eventType) {
return this.relations.get(eventId)?.get(relationType)?.get(eventType);
var _this$relations$get;
return (_this$relations$get = this.relations.get(eventId)) === null || _this$relations$get === void 0 || (_this$relations$get = _this$relations$get.get(relationType)) === null || _this$relations$get === void 0 ? void 0 : _this$relations$get.get(eventType);
}
getAllChildEventsForEvent(parentEventId) {
const relationsForEvent = this.relations.get(parentEventId) ?? new Map();
var _this$relations$get2;
const relationsForEvent = (_this$relations$get2 = this.relations.get(parentEventId)) !== null && _this$relations$get2 !== void 0 ? _this$relations$get2 : new Map();
const events = [];
for (const relationsRecord of relationsForEvent.values()) {
for (const relations of relationsRecord.values()) {
@@ -78,42 +86,26 @@ export class RelationsContainer {
* @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();
aggregateChildEvent(event, timelineSet) {
if (event.isRedacted() || event.status === _event.EventStatus.CANCELLED) {
return;
}
const relation = event.getRelation();
if (!relation) return Promise.resolve();
if (!relation) return;
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(_event.MatrixEventEvent.Decrypted, onEventDecrypted);
return;
}
this.aggregateChildEvent(event, timelineSet);
};
// 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);
});
event.once(_event.MatrixEventEvent.Decrypted, onEventDecrypted);
return;
}
const {
event_id: relatesToEventId,
@@ -131,21 +123,18 @@ export class RelationsContainer {
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);
var _this$room, _ref, _timelineSet$findEven;
relationsWithEventType = new _relations.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;
const room = (_this$room = this.room) !== null && _this$room !== void 0 ? _this$room : timelineSet === null || timelineSet === void 0 ? void 0 : timelineSet.room;
const relatesToEvent = (_ref = (_timelineSet$findEven = timelineSet === null || timelineSet === void 0 ? void 0 : timelineSet.findEventById(relatesToEventId)) !== null && _timelineSet$findEven !== void 0 ? _timelineSet$findEven : room === null || room === void 0 ? void 0 : room.findEventById(relatesToEventId)) !== null && _ref !== void 0 ? _ref : room === null || room === void 0 ? void 0 : room.getPendingEvent(relatesToEventId);
if (relatesToEvent) {
targetEventUpdate = relationsWithEventType.setTargetEvent(relatesToEvent);
relationsWithEventType.setTargetEvent(relatesToEvent);
}
}
const addEventUpdate = relationsWithEventType.addEvent(event);
return Promise.all([targetEventUpdate, addEventUpdate]).then(() => undefined);
relationsWithEventType.addEvent(event);
}
}
exports.RelationsContainer = RelationsContainer;
//# sourceMappingURL=relations-container.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
import { type MatrixEvent } from "./event.ts";
import { RelationType } from "../@types/event.ts";
import { TypedEventEmitter } from "./typed-event-emitter.ts";
import { type MatrixClient } from "../client.ts";
import { Room } from "./room.ts";
import { MatrixEvent } from "./event";
import { RelationType } from "../@types/event";
import { TypedEventEmitter } from "./typed-event-emitter";
import { MatrixClient } from "../client";
import { Room } from "./room";
export declare enum RelationsEvent {
Add = "Relations.add",
Remove = "Relations.remove",
@@ -32,7 +32,6 @@ export declare class Relations extends TypedEventEmitter<RelationsEvent, EventHa
private sortedAnnotationsByKey;
private targetEvent;
private creationEmitted;
private replacementUpdateId;
private readonly client;
/**
* @param relationType - The type of relation involved, such as "m.annotation", "m.reference", "m.replace", etc.
@@ -110,14 +109,6 @@ export declare class Relations extends TypedEventEmitter<RelationsEvent, EventHa
*/
getLastReplacement(): Promise<MatrixEvent | null>;
setTargetEvent(event: MatrixEvent): Promise<void>;
/**
* Updates the target event with the latest replacement.
*
* Multiple replacement updates can be triggered concurrently (for example
* while edits are still being decrypted). A monotonic update counter guards
* against older async resolutions overriding newer replacement selections.
*/
private updateTargetEventReplacement;
private maybeEmitCreated;
}
//# sourceMappingURL=relations.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../../src/models/relations.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAyC,KAAK,WAAW,EAAoB,MAAM,YAAY,CAAC;AAEvG,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,oBAAY,cAAc;IACtB,GAAG,kBAAkB;IACrB,MAAM,qBAAqB;IAC3B,SAAS,wBAAwB;CACpC;AAED,MAAM,MAAM,eAAe,GAAG;IAC1B,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACnD,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACtD,CAAC,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;CAC5D,CAAC;AAKF;;;;;;;GAOG;AACH,qBAAa,SAAU,SAAQ,iBAAiB,CAAC,cAAc,EAAE,eAAe,CAAC;aAkBzD,YAAY,EAAE,YAAY,GAAG,MAAM;aACnC,SAAS,EAAE,MAAM;aAEjB,aAAa,CAAC,EAAE,MAAM,EAAE;IApB5C,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,gBAAgB,CAAwC;IAChE,OAAO,CAAC,mBAAmB,CAAwC;IACnE,OAAO,CAAC,sBAAsB,CAAoC;IAClE,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IAEtC;;;;;OAKG;IACH,YACoB,YAAY,EAAE,YAAY,GAAG,MAAM,EACnC,SAAS,EAAE,MAAM,EACjC,MAAM,EAAE,YAAY,GAAG,IAAI,EACX,aAAa,CAAC,EAAE,MAAM,EAAE,YAAA,EAI3C;IAED;;;;OAIG;IACU,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCvD;IAED;;;;OAIG;IACU,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAc1D;IAED;;;;;OAKG;IACH,OAAO,CAAC,aAAa,CAYnB;IAEF;;;;;;;;OAQG;IACI,YAAY,IAAI,WAAW,EAAE,CAEnC;IAED,OAAO,CAAC,0BAA0B;IA2BlC,OAAO,CAAC,+BAA+B;IAuBvC;;;;;;;;;OASG;IACH,OAAO,CAAC,iBAAiB,CAiBvB;IAEF;;;;;;;;OAQG;IACI,yBAAyB,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,EAAE,GAAG,IAAI,CAOtE;IAED;;;;;;;OAOG;IACI,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,IAAI,CAOvE;IAED;;;;;OAKG;IACU,kBAAkB,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAsC7D;IAKY,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAW7D;IAED;;;;;;OAMG;YACW,4BAA4B;IAsB1C,OAAO,CAAC,gBAAgB;CAY3B"}
{"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../../src/models/relations.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAoC,WAAW,EAAoB,MAAM,SAAS,CAAC;AAE1F,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,oBAAY,cAAc;IACtB,GAAG,kBAAkB;IACrB,MAAM,qBAAqB;IAC3B,SAAS,wBAAwB;CACpC;AAED,MAAM,MAAM,eAAe,GAAG;IAC1B,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACnD,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACtD,CAAC,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;CAC5D,CAAC;AAKF;;;;;;;GAOG;AACH,qBAAa,SAAU,SAAQ,iBAAiB,CAAC,cAAc,EAAE,eAAe,CAAC;aAiBzD,YAAY,EAAE,YAAY,GAAG,MAAM;aACnC,SAAS,EAAE,MAAM;aAEjB,aAAa,CAAC;IAnBlC,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,gBAAgB,CAAwC;IAChE,OAAO,CAAC,mBAAmB,CAAwC;IACnE,OAAO,CAAC,sBAAsB,CAAoC;IAClE,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IAEtC;;;;;OAKG;gBAEiB,YAAY,EAAE,YAAY,GAAG,MAAM,EACnC,SAAS,EAAE,MAAM,EACjC,MAAM,EAAE,YAAY,GAAG,IAAI,EACX,aAAa,CAAC,sBAAU;IAM5C;;;;OAIG;IACU,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA0CxD;;;;OAIG;IACU,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB3D;;;;;OAKG;IACH,OAAO,CAAC,aAAa,CAYnB;IAEF;;;;;;;;OAQG;IACI,YAAY,IAAI,WAAW,EAAE;IAIpC,OAAO,CAAC,0BAA0B;IA2BlC,OAAO,CAAC,+BAA+B;IAuBvC;;;;;;;;;OASG;IACH,OAAO,CAAC,iBAAiB,CAkBvB;IAEF;;;;;;;;OAQG;IACI,yBAAyB,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,EAAE,GAAG,IAAI;IASvE;;;;;;;OAOG;IACI,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,IAAI;IASxE;;;;;OAKG;IACU,kBAAkB,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IA0CjD,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB9D,OAAO,CAAC,gBAAgB;CAY3B"}

View File

@@ -1,4 +1,16 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.RelationsEvent = exports.Relations = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _event = require("./event");
var _logger = require("../logger");
var _event2 = require("../@types/event");
var _typedEventEmitter = require("./typed-event-emitter");
var _room = require("./room");
/*
Copyright 2019, 2021, 2023 The Matrix.org Foundation C.I.C.
@@ -14,18 +26,13 @@ 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 { EventStatus, MatrixEventEvent } from "./event.js";
import { logger } from "../logger.js";
import { RelationType } from "../@types/event.js";
import { TypedEventEmitter } from "./typed-event-emitter.js";
import { Room } from "./room.js";
export let RelationsEvent = /*#__PURE__*/function (RelationsEvent) {
let RelationsEvent = /*#__PURE__*/function (RelationsEvent) {
RelationsEvent["Add"] = "Relations.add";
RelationsEvent["Remove"] = "Relations.remove";
RelationsEvent["Redaction"] = "Relations.redaction";
return RelationsEvent;
}({});
exports.RelationsEvent = RelationsEvent;
const matchesEventType = (eventType, targetEventType, altTargetEventTypes = []) => [targetEventType, ...altTargetEventTypes].includes(eventType);
/**
@@ -36,7 +43,7 @@ const matchesEventType = (eventType, targetEventType, altTargetEventTypes = [])
* The typical way to get one of these containers is via
* EventTimelineSet#getRelationsForEvent.
*/
export class Relations extends TypedEventEmitter {
class Relations extends _typedEventEmitter.TypedEventEmitter {
/**
* @param relationType - The type of relation involved, such as "m.annotation", "m.reference", "m.replace", etc.
* @param eventType - The relation event's type, such as "m.reaction", etc.
@@ -45,32 +52,34 @@ export class Relations extends TypedEventEmitter {
*/
constructor(relationType, eventType, client, altEventTypes) {
super();
_defineProperty(this, "relationEventIds", new Set());
_defineProperty(this, "relations", new Set());
_defineProperty(this, "annotationsByKey", {});
_defineProperty(this, "annotationsBySender", {});
_defineProperty(this, "sortedAnnotationsByKey", []);
_defineProperty(this, "targetEvent", null);
_defineProperty(this, "creationEmitted", false);
_defineProperty(this, "replacementUpdateId", 0);
_defineProperty(this, "client", void 0);
this.relationType = relationType;
this.eventType = eventType;
this.altEventTypes = altEventTypes;
(0, _defineProperty2.default)(this, "relationEventIds", new Set());
(0, _defineProperty2.default)(this, "relations", new Set());
(0, _defineProperty2.default)(this, "annotationsByKey", {});
(0, _defineProperty2.default)(this, "annotationsBySender", {});
(0, _defineProperty2.default)(this, "sortedAnnotationsByKey", []);
(0, _defineProperty2.default)(this, "targetEvent", null);
(0, _defineProperty2.default)(this, "creationEmitted", false);
(0, _defineProperty2.default)(this, "client", void 0);
/**
* Listens for event status changes to remove cancelled events.
*
* @param event - The event whose status has changed
* @param status - The new status
*/
_defineProperty(this, "onEventStatus", (event, status) => {
(0, _defineProperty2.default)(this, "onEventStatus", (event, status) => {
if (!event.isSending()) {
// Sending is done, so we don't need to listen anymore
event.removeListener(MatrixEventEvent.Status, this.onEventStatus);
event.removeListener(_event.MatrixEventEvent.Status, this.onEventStatus);
return;
}
if (status !== EventStatus.CANCELLED) {
if (status !== _event.EventStatus.CANCELLED) {
return;
}
// Event was cancelled, remove from the collection
event.removeListener(MatrixEventEvent.Status, this.onEventStatus);
event.removeListener(_event.MatrixEventEvent.Status, this.onEventStatus);
this.removeEvent(event);
});
/**
@@ -83,24 +92,22 @@ export class Relations extends TypedEventEmitter {
*
* @param redactedEvent - The original relation event that is about to be redacted.
*/
_defineProperty(this, "onBeforeRedaction", async redactedEvent => {
(0, _defineProperty2.default)(this, "onBeforeRedaction", async redactedEvent => {
if (!this.relations.has(redactedEvent)) {
return;
}
this.relations.delete(redactedEvent);
if (this.relationType === RelationType.Annotation) {
if (this.relationType === _event2.RelationType.Annotation) {
// Remove the redacted annotation from aggregation by key
this.removeAnnotationFromAggregation(redactedEvent);
} else if (this.relationType === RelationType.Replace) {
await this.updateTargetEventReplacement();
} else if (this.relationType === _event2.RelationType.Replace && this.targetEvent && !this.targetEvent.isState()) {
const lastReplacement = await this.getLastReplacement();
this.targetEvent.makeReplaced(lastReplacement);
}
redactedEvent.removeListener(MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction);
redactedEvent.removeListener(_event.MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction);
this.emit(RelationsEvent.Redaction, redactedEvent);
});
this.relationType = relationType;
this.eventType = eventType;
this.altEventTypes = altEventTypes;
this.client = client instanceof Room ? client.client : client;
this.client = client instanceof _room.Room ? client.client : client;
}
/**
@@ -114,29 +121,30 @@ export class Relations extends TypedEventEmitter {
}
const relation = event.getRelation();
if (!relation) {
logger.error("Event must have relation info");
_logger.logger.error("Event must have relation info");
return;
}
const relationType = relation.rel_type;
const eventType = event.getType();
if (this.relationType !== relationType || !matchesEventType(eventType, this.eventType, this.altEventTypes)) {
logger.error("Event relation info doesn't match this container");
_logger.logger.error("Event relation info doesn't match this container");
return;
}
// If the event is in the process of being sent, listen for cancellation
// so we can remove the event from the collection.
if (event.isSending()) {
event.on(MatrixEventEvent.Status, this.onEventStatus);
event.on(_event.MatrixEventEvent.Status, this.onEventStatus);
}
this.relations.add(event);
this.relationEventIds.add(event.getId());
if (this.relationType === RelationType.Annotation) {
if (this.relationType === _event2.RelationType.Annotation) {
this.addAnnotationToAggregation(event);
} else if (this.relationType === RelationType.Replace) {
await this.updateTargetEventReplacement();
} else if (this.relationType === _event2.RelationType.Replace && this.targetEvent && !this.targetEvent.isState()) {
const lastReplacement = await this.getLastReplacement();
this.targetEvent.makeReplaced(lastReplacement);
}
event.on(MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction);
event.on(_event.MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction);
this.emit(RelationsEvent.Add, event);
this.maybeEmitCreated();
}
@@ -151,10 +159,11 @@ export class Relations extends TypedEventEmitter {
return;
}
this.relations.delete(event);
if (this.relationType === RelationType.Annotation) {
if (this.relationType === _event2.RelationType.Annotation) {
this.removeAnnotationFromAggregation(event);
} else if (this.relationType === RelationType.Replace) {
await this.updateTargetEventReplacement();
} else if (this.relationType === _event2.RelationType.Replace && this.targetEvent && !this.targetEvent.isState()) {
const lastReplacement = await this.getLastReplacement();
this.targetEvent.makeReplaced(lastReplacement);
}
this.emit(RelationsEvent.Remove, event);
}
@@ -171,9 +180,10 @@ export class Relations extends TypedEventEmitter {
return [...this.relations];
}
addAnnotationToAggregation(event) {
var _event$getRelation;
const {
key
} = event.getRelation() ?? {};
} = (_event$getRelation = event.getRelation()) !== null && _event$getRelation !== void 0 ? _event$getRelation : {};
if (!key) return;
let eventsForKey = this.annotationsByKey[key];
if (!eventsForKey) {
@@ -197,9 +207,10 @@ export class Relations extends TypedEventEmitter {
eventsFromSender.add(event);
}
removeAnnotationFromAggregation(event) {
var _event$getRelation2;
const {
key
} = event.getRelation() ?? {};
} = (_event$getRelation2 = event.getRelation()) !== null && _event$getRelation2 !== void 0 ? _event$getRelation2 : {};
if (!key) return;
const eventsForKey = this.annotationsByKey[key];
if (eventsForKey) {
@@ -228,7 +239,7 @@ export class Relations extends TypedEventEmitter {
* The events are stored in a Set (which preserves insertion order).
*/
getSortedAnnotationsByKey() {
if (this.relationType !== RelationType.Annotation) {
if (this.relationType !== _event2.RelationType.Annotation) {
// Other relation types are not grouped currently.
return null;
}
@@ -244,7 +255,7 @@ export class Relations extends TypedEventEmitter {
* events for that sender as a value.
*/
getAnnotationsBySender() {
if (this.relationType !== RelationType.Annotation) {
if (this.relationType !== _event2.RelationType.Annotation) {
// Other relation types are not grouped currently.
return null;
}
@@ -258,7 +269,7 @@ export class Relations extends TypedEventEmitter {
* once the target event is known, see `addEvent`.
*/
async getLastReplacement() {
if (this.relationType !== RelationType.Replace) {
if (this.relationType !== _event2.RelationType.Replace) {
// Aggregating on last only makes sense for this relation type
return null;
}
@@ -271,8 +282,8 @@ export class Relations extends TypedEventEmitter {
// the all-knowning server tells us that the event at some point had
// this timestamp for its replacement, so any following replacement should definitely not be less
const replaceRelation = this.targetEvent.getServerAggregatedRelation(RelationType.Replace);
const minTs = replaceRelation?.origin_server_ts;
const replaceRelation = this.targetEvent.getServerAggregatedRelation(_event2.RelationType.Replace);
const minTs = replaceRelation === null || replaceRelation === void 0 ? void 0 : replaceRelation.origin_server_ts;
const lastReplacement = this.getRelations().reduce((last, event) => {
if (event.getSender() !== this.targetEvent.getSender()) {
return last;
@@ -285,10 +296,9 @@ export class Relations extends TypedEventEmitter {
}
return event;
}, null);
if (lastReplacement?.shouldAttemptDecryption() && this.client.getCrypto()) {
// Dirty but we are expecting to pass the cryptoBackend which is not accessible here
await lastReplacement.attemptDecryption(this.client.getCrypto());
} else if (lastReplacement?.isBeingDecrypted()) {
if (lastReplacement !== null && lastReplacement !== void 0 && lastReplacement.shouldAttemptDecryption() && this.client.isCryptoEnabled()) {
await lastReplacement.attemptDecryption(this.client.crypto);
} else if (lastReplacement !== null && lastReplacement !== void 0 && lastReplacement.isBeingDecrypted()) {
await lastReplacement.getDecryptionPromise();
}
return lastReplacement;
@@ -298,42 +308,20 @@ export class Relations extends TypedEventEmitter {
* @param targetEvent - the event the relations are related to.
*/
async setTargetEvent(event) {
if (this.targetEvent === event) {
if (this.targetEvent) {
return;
}
this.targetEvent = event;
if (this.relationType === RelationType.Replace) {
await this.updateTargetEventReplacement();
if (this.relationType === _event2.RelationType.Replace && !this.targetEvent.isState()) {
const replacement = await this.getLastReplacement();
// this is the initial update, so only call it if we already have something
// to not emit Event.replaced needlessly
if (replacement) {
this.targetEvent.makeReplaced(replacement);
}
}
this.maybeEmitCreated();
}
/**
* Updates the target event with the latest replacement.
*
* Multiple replacement updates can be triggered concurrently (for example
* while edits are still being decrypted). A monotonic update counter guards
* against older async resolutions overriding newer replacement selections.
*/
async updateTargetEventReplacement() {
if (!this.targetEvent || this.targetEvent.isState()) {
return;
}
const targetEvent = this.targetEvent;
const updateId = ++this.replacementUpdateId;
const lastReplacement = await this.getLastReplacement();
// If a newer update started while we were awaiting, discard this stale result.
if (updateId !== this.replacementUpdateId || this.targetEvent !== targetEvent) {
return;
}
// Avoid emitting Event.replaced when there is no replacement and none currently set.
if (!lastReplacement && !targetEvent.replacingEvent()) {
return;
}
targetEvent.makeReplaced(lastReplacement ?? undefined);
}
maybeEmitCreated() {
if (this.creationEmitted) {
return;
@@ -344,7 +332,8 @@ export class Relations extends TypedEventEmitter {
return;
}
this.creationEmitted = true;
this.targetEvent.emit(MatrixEventEvent.RelationsCreated, this.relationType, this.eventType);
this.targetEvent.emit(_event.MatrixEventEvent.RelationsCreated, this.relationType, this.eventType);
}
}
exports.Relations = Relations;
//# sourceMappingURL=relations.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,7 @@
import { type User } from "./user.ts";
import { type MatrixEvent } from "./event.ts";
import { type RoomState } from "./room-state.ts";
import { TypedEventEmitter } from "./typed-event-emitter.ts";
import { type Membership } from "../@types/membership.ts";
import { User } from "./user";
import { MatrixEvent } from "./event";
import { RoomState } from "./room-state";
import { TypedEventEmitter } from "./typed-event-emitter";
export declare enum RoomMemberEvent {
Membership = "RoomMember.membership",
Name = "RoomMember.name",
@@ -44,6 +43,7 @@ export type RoomMemberEventHandlerMap = {
* ```
* matrixClient.on("RoomMember.powerLevel", function(event, member){
* var newPowerLevel = member.powerLevel;
* var newNormPowerLevel = member.powerLevelNorm;
* });
* ```
*/
@@ -72,23 +72,23 @@ export declare class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomM
*/
typing: boolean;
/**
* The human-readable name for this room member. Similar to {@link rawDisplayName}, but
* The human-readable name for this room member. This will be
* disambiguated with a suffix of " (\@user_id:matrix.org)" if another member shares the
* same displayname.
*/
name: string;
/**
* The ambiguous displayname of this room member, with some preprocessing:
*
* * Direction override characters (RTO and LRO) are removed.
* * If the displayname is empty, or contains only blank, non-printing, or diacritcic characters, it is
* replaced with the user ID.
* The ambiguous displayname of this room member.
*/
rawDisplayName: string;
/**
* The power level for this room member.
*/
powerLevel: number;
/**
* The normalised power level (0-100) for this room member.
*/
powerLevelNorm: number;
/**
* The User object for this room member, if one exists.
*/
@@ -96,7 +96,7 @@ export declare class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomM
/**
* The membership state for this room member e.g. 'join'.
*/
membership?: Membership;
membership?: string;
/**
* True if the member's name is disambiguated.
*/
@@ -140,26 +140,14 @@ export declare class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomM
*/
setMembershipEvent(event: MatrixEvent, roomState?: RoomState): void;
/**
* Recalculate the disambiguation flag for this member based on current room state.
* This should be called when another member's display name changes and may affect
* whether this member needs disambiguation.
*
* @param roomState - The current room state to use for disambiguation check
* @returns true if the member's name changed as a result of the disambiguation update
*
* @remarks
* Fires {@link RoomMemberEvent.Name}
*/
recalculateDisambiguatedName(roomState: RoomState): boolean;
/**
* Update this room member's power level event. Will fire
* "RoomMember.powerLevel" if the new power level is different
* @param powerLevel - The power level of the room member.
* Update this room member's power level event. May fire
* "RoomMember.powerLevel" if this event updates this member's power levels.
* @param powerLevelEvent - The `m.room.power_levels` event
*
* @remarks
* Fires {@link RoomMemberEvent.PowerLevel}
*/
setPowerLevel(powerLevel: number, powerLevelEvent: MatrixEvent): void;
setPowerLevelEvent(powerLevelEvent: MatrixEvent): void;
/**
* Update this room member's typing event. May fire "RoomMember.typing" if
* this event changes this member's typing state.
@@ -203,19 +191,13 @@ export declare class RoomMember extends TypedEventEmitter<RoomMemberEvent, RoomM
* If false, any non-matrix content URLs will be ignored. Setting this option to
* true will expose URLs that, if fetched, will leak information about the user
* to anyone who they share a room with.
* @param useAuthentication - (optional) If true, the caller supports authenticated
* media and wants an authentication-required URL. Note that server support for
* authenticated media will not be checked - it is the caller's responsibility
* to do so before calling this function. Note also that useAuthentication
* implies allowRedirects. Defaults to false (unauthenticated endpoints).
* @returns the avatar URL or null.
*/
getAvatarUrl(baseUrl: string, width: number, height: number, resizeMethod: string, allowDefault: boolean | undefined, allowDirectLinks: boolean, useAuthentication?: boolean): string | null;
getAvatarUrl(baseUrl: string, width: number, height: number, resizeMethod: string, allowDefault: boolean | undefined, allowDirectLinks: boolean): string | null;
/**
* get the mxc avatar url, either from a state event, or from a lazily loaded member
* @returns the mxc avatar url
*/
getMxcAvatarUrl(): string | undefined;
}
export declare const MXID_PATTERN: RegExp;
//# sourceMappingURL=room-member.d.ts.map

View File

@@ -1 +1 @@
{"version":3,"file":"room-member.d.ts","sourceRoot":"","sources":["../../src/models/room-member.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"room-member.d.ts","sourceRoot":"","sources":["../../src/models/room-member.ts"],"names":[],"mappings":"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"}

View File

@@ -1,4 +1,16 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.RoomMemberEvent = exports.RoomMember = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _contentRepo = require("../content-repo");
var _utils = require("../utils");
var _logger = require("../logger");
var _typedEventEmitter = require("./typed-event-emitter");
var _event = require("../@types/event");
/*
Copyright 2015 - 2021 The Matrix.org Foundation C.I.C.
@@ -14,21 +26,15 @@ 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 { getHttpUriForMxc } from "../content-repo.js";
import { removeDirectionOverrideChars, removeHiddenChars } from "../utils.js";
import { logger } from "../logger.js";
import { TypedEventEmitter } from "./typed-event-emitter.js";
import { EventType } from "../@types/event.js";
import { KnownMembership } from "../@types/membership.js";
export let RoomMemberEvent = /*#__PURE__*/function (RoomMemberEvent) {
let RoomMemberEvent = /*#__PURE__*/function (RoomMemberEvent) {
RoomMemberEvent["Membership"] = "RoomMember.membership";
RoomMemberEvent["Name"] = "RoomMember.name";
RoomMemberEvent["PowerLevel"] = "RoomMember.powerLevel";
RoomMemberEvent["Typing"] = "RoomMember.typing";
return RoomMemberEvent;
}({});
export class RoomMember extends TypedEventEmitter {
exports.RoomMemberEvent = RoomMemberEvent;
class RoomMember extends _typedEventEmitter.TypedEventEmitter {
/**
* Construct a new room member.
*
@@ -37,51 +43,51 @@ export class RoomMember extends TypedEventEmitter {
*/
constructor(roomId, userId) {
super();
_defineProperty(this, "_isOutOfBand", false);
_defineProperty(this, "modified", -1);
_defineProperty(this, "requestedProfileInfo", false);
this.roomId = roomId;
this.userId = userId;
(0, _defineProperty2.default)(this, "_isOutOfBand", false);
(0, _defineProperty2.default)(this, "modified", -1);
(0, _defineProperty2.default)(this, "requestedProfileInfo", false);
// used by sync.ts
// XXX these should be read-only
/**
* True if the room member is currently typing.
*/
_defineProperty(this, "typing", false);
(0, _defineProperty2.default)(this, "typing", false);
/**
* The human-readable name for this room member. Similar to {@link rawDisplayName}, but
* The human-readable name for this room member. This will be
* disambiguated with a suffix of " (\@user_id:matrix.org)" if another member shares the
* same displayname.
*/
_defineProperty(this, "name", void 0);
(0, _defineProperty2.default)(this, "name", void 0);
/**
* The ambiguous displayname of this room member, with some preprocessing:
*
* * Direction override characters (RTO and LRO) are removed.
* * If the displayname is empty, or contains only blank, non-printing, or diacritcic characters, it is
* replaced with the user ID.
* The ambiguous displayname of this room member.
*/
_defineProperty(this, "rawDisplayName", void 0);
(0, _defineProperty2.default)(this, "rawDisplayName", void 0);
/**
* The power level for this room member.
*/
_defineProperty(this, "powerLevel", 0);
(0, _defineProperty2.default)(this, "powerLevel", 0);
/**
* The normalised power level (0-100) for this room member.
*/
(0, _defineProperty2.default)(this, "powerLevelNorm", 0);
/**
* The User object for this room member, if one exists.
*/
_defineProperty(this, "user", void 0);
(0, _defineProperty2.default)(this, "user", void 0);
/**
* The membership state for this room member e.g. 'join'.
*/
_defineProperty(this, "membership", void 0);
(0, _defineProperty2.default)(this, "membership", void 0);
/**
* True if the member's name is disambiguated.
*/
_defineProperty(this, "disambiguate", false);
(0, _defineProperty2.default)(this, "disambiguate", false);
/**
* The events describing this RoomMember.
*/
_defineProperty(this, "events", {});
this.roomId = roomId;
this.userId = userId;
(0, _defineProperty2.default)(this, "events", {});
this.name = userId;
this.rawDisplayName = userId;
this.updateModifiedTime();
@@ -115,8 +121,9 @@ export class RoomMember extends TypedEventEmitter {
* Fires {@link RoomMemberEvent.Membership}
*/
setMembershipEvent(event, roomState) {
const displayName = event.getDirectionalContent().displayname ?? "";
if (event.getType() !== EventType.RoomMember) {
var _event$getDirectional, _event$getDirectional2;
const displayName = (_event$getDirectional = event.getDirectionalContent().displayname) !== null && _event$getDirectional !== void 0 ? _event$getDirectional : "";
if (event.getType() !== _event.EventType.RoomMember) {
return;
}
this._isOutOfBand = false;
@@ -126,7 +133,7 @@ export class RoomMember extends TypedEventEmitter {
if (this.membership === undefined) {
// logging to diagnose https://github.com/vector-im/element-web/issues/20962
// (logs event content, although only of membership events)
logger.trace(`membership event with membership undefined (forwardLooking: ${event.forwardLooking})!`, event.getContent(), `prevcontent is `, event.getPrevContent());
_logger.logger.trace(`membership event with membership undefined (forwardLooking: ${event.forwardLooking})!`, event.getContent(), `prevcontent is `, event.getPrevContent());
}
this.disambiguate = shouldDisambiguate(this.userId, displayName, roomState);
const oldName = this.name;
@@ -134,8 +141,8 @@ export class RoomMember extends TypedEventEmitter {
// not quite raw: we strip direction override chars so it can safely be inserted into
// blocks of text without breaking the text direction
this.rawDisplayName = removeDirectionOverrideChars(event.getDirectionalContent().displayname ?? "");
if (!this.rawDisplayName || !removeHiddenChars(this.rawDisplayName)) {
this.rawDisplayName = (0, _utils.removeDirectionOverrideChars)((_event$getDirectional2 = event.getDirectionalContent().displayname) !== null && _event$getDirectional2 !== void 0 ? _event$getDirectional2 : "");
if (!this.rawDisplayName || !(0, _utils.removeHiddenChars)(this.rawDisplayName)) {
this.rawDisplayName = this.userId;
}
if (oldMembership !== this.membership) {
@@ -149,48 +156,40 @@ export class RoomMember extends TypedEventEmitter {
}
/**
* Recalculate the disambiguation flag for this member based on current room state.
* This should be called when another member's display name changes and may affect
* whether this member needs disambiguation.
*
* @param roomState - The current room state to use for disambiguation check
* @returns true if the member's name changed as a result of the disambiguation update
*
* @remarks
* Fires {@link RoomMemberEvent.Name}
*/
recalculateDisambiguatedName(roomState) {
if (!this.events.member) {
return false;
}
const displayName = this.events.member.getDirectionalContent().displayname ?? "";
const newDisambiguate = shouldDisambiguate(this.userId, displayName, roomState);
if (newDisambiguate === this.disambiguate) {
return false;
}
this.disambiguate = newDisambiguate;
const oldName = this.name;
this.name = calculateDisplayName(this.userId, displayName, this.disambiguate);
if (oldName !== this.name) {
this.updateModifiedTime();
this.emit(RoomMemberEvent.Name, this.events.member, this, oldName);
return true;
}
return false;
}
/**
* Update this room member's power level event. Will fire
* "RoomMember.powerLevel" if the new power level is different
* @param powerLevel - The power level of the room member.
* Update this room member's power level event. May fire
* "RoomMember.powerLevel" if this event updates this member's power levels.
* @param powerLevelEvent - The `m.room.power_levels` event
*
* @remarks
* Fires {@link RoomMemberEvent.PowerLevel}
*/
setPowerLevel(powerLevel, powerLevelEvent) {
setPowerLevelEvent(powerLevelEvent) {
if (powerLevelEvent.getType() !== _event.EventType.RoomPowerLevels || powerLevelEvent.getStateKey() !== "") {
return;
}
const evContent = powerLevelEvent.getDirectionalContent();
let maxLevel = evContent.users_default || 0;
const users = evContent.users || {};
Object.values(users).forEach(lvl => {
maxLevel = Math.max(maxLevel, lvl);
});
const oldPowerLevel = this.powerLevel;
this.powerLevel = powerLevel;
if (oldPowerLevel !== this.powerLevel) {
const oldPowerLevelNorm = this.powerLevelNorm;
if (users[this.userId] !== undefined && Number.isInteger(users[this.userId])) {
this.powerLevel = users[this.userId];
} else if (evContent.users_default !== undefined) {
this.powerLevel = evContent.users_default;
} else {
this.powerLevel = 0;
}
this.powerLevelNorm = 0;
if (maxLevel > 0) {
this.powerLevelNorm = this.powerLevel * 100 / maxLevel;
}
// emit for changes in powerLevelNorm as well (since the app will need to
// redraw everyone's level if the max has changed)
if (oldPowerLevel !== this.powerLevel || oldPowerLevelNorm !== this.powerLevelNorm) {
this.updateModifiedTime();
this.emit(RoomMemberEvent.PowerLevel, powerLevelEvent, this);
}
@@ -241,7 +240,7 @@ export class RoomMember extends TypedEventEmitter {
return this.modified;
}
isKicked() {
return this.membership === KnownMembership.Leave && this.events.member !== undefined && this.events.member.getSender() !== this.events.member.getStateKey();
return this.membership === "leave" && this.events.member !== undefined && this.events.member.getSender() !== this.events.member.getStateKey();
}
/**
@@ -263,11 +262,11 @@ export class RoomMember extends TypedEventEmitter {
const memberEvent = this.events.member;
let memberContent = memberEvent.getContent();
let inviteSender = memberEvent.getSender();
if (memberContent.membership === KnownMembership.Join) {
if (memberContent.membership === "join") {
memberContent = memberEvent.getPrevContent();
inviteSender = memberEvent.getUnsigned().prev_sender;
}
if (memberContent.membership === KnownMembership.Invite && memberContent.is_direct) {
if (memberContent.membership === "invite" && memberContent.is_direct) {
return inviteSender;
}
}
@@ -289,19 +288,14 @@ export class RoomMember extends TypedEventEmitter {
* If false, any non-matrix content URLs will be ignored. Setting this option to
* true will expose URLs that, if fetched, will leak information about the user
* to anyone who they share a room with.
* @param useAuthentication - (optional) If true, the caller supports authenticated
* media and wants an authentication-required URL. Note that server support for
* authenticated media will not be checked - it is the caller's responsibility
* to do so before calling this function. Note also that useAuthentication
* implies allowRedirects. Defaults to false (unauthenticated endpoints).
* @returns the avatar URL or null.
*/
getAvatarUrl(baseUrl, width, height, resizeMethod, allowDefault = true, allowDirectLinks, useAuthentication = false) {
getAvatarUrl(baseUrl, width, height, resizeMethod, allowDefault = true, allowDirectLinks) {
const rawUrl = this.getMxcAvatarUrl();
if (!rawUrl && !allowDefault) {
return null;
}
const httpUrl = getHttpUriForMxc(baseUrl, rawUrl, width, height, resizeMethod, allowDirectLinks, undefined, useAuthentication);
const httpUrl = (0, _contentRepo.getHttpUriForMxc)(baseUrl, rawUrl, width, height, resizeMethod, allowDirectLinks);
if (httpUrl) {
return httpUrl;
}
@@ -320,21 +314,21 @@ export class RoomMember extends TypedEventEmitter {
}
}
}
export const MXID_PATTERN = /@.+:.+/;
exports.RoomMember = RoomMember;
const MXID_PATTERN = /@.+:.+/;
const LTR_RTL_PATTERN = /[\u200E\u200F\u202A-\u202F]/;
function shouldDisambiguate(selfUserId, displayName, roomState) {
if (!displayName || displayName === selfUserId) return false;
if (!roomState) return false;
const strippedDisplayName = removeHiddenChars(displayName);
// First check if the displayname is something we consider truthy
// after stripping it of zero width characters and padding spaces
if (!strippedDisplayName) return false;
if (!(0, _utils.removeHiddenChars)(displayName)) return false;
if (!roomState) return false;
// Next check if the name contains something that look like a mxid
// If it does, it may be someone trying to impersonate someone else
// Show full mxid in this case
if (MXID_PATTERN.test(strippedDisplayName)) return true;
if (MXID_PATTERN.test(displayName)) return true;
// Also show mxid if the display name contains any LTR/RTL characters as these
// make it very difficult for us to find similar *looking* display names
@@ -349,11 +343,11 @@ function shouldDisambiguate(selfUserId, displayName, roomState) {
}
function calculateDisplayName(selfUserId, displayName, disambiguate) {
if (!displayName || displayName === selfUserId) return selfUserId;
if (disambiguate) return removeDirectionOverrideChars(displayName) + " (" + selfUserId + ")";
if (disambiguate) return (0, _utils.removeDirectionOverrideChars)(displayName) + " (" + selfUserId + ")";
// First check if the displayname is something we consider truthy
// after stripping it of zero width characters and padding spaces
if (!removeHiddenChars(displayName)) return selfUserId;
if (!(0, _utils.removeHiddenChars)(displayName)) return selfUserId;
// We always strip the direction override characters (LRO and RLO).
// These override the text direction for all subsequent characters
@@ -366,6 +360,6 @@ function calculateDisplayName(selfUserId, displayName, disambiguate) {
// names should flip into the correct direction automatically based on
// the characters, and you can still embed rtl in ltr or vice versa
// with the embed chars or marker chars.
return removeDirectionOverrideChars(displayName);
return (0, _utils.removeDirectionOverrideChars)(displayName);
}
//# sourceMappingURL=room-member.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,39 +0,0 @@
import { type ReceiptContent } from "../@types/read_receipts.ts";
import { type Room } from "./room.ts";
/**
* The latest receipts we have for a room.
*/
export declare class RoomReceipts {
private room;
private threadedReceipts;
private unthreadedReceipts;
private danglingReceipts;
constructor(room: Room);
/**
* Remember the receipt information supplied. For each receipt:
*
* If we don't have the event for this receipt, store it as "dangling" so we
* can process it later.
*
* Otherwise store it per-user in either the threaded store for its
* thread_id, or the unthreaded store if there is no thread_id.
*
* Ignores any receipt that is before an existing receipt for the same user
* (in the same thread, if applicable). "Before" is defined by the
* unfilteredTimelineSet of the room.
*/
add(receiptContent: ReceiptContent, synthetic: boolean): void;
/**
* Look for dangling receipts for the given event ID,
* and add them to the thread of unthread receipts if found.
* @param event - the event to look for
*/
private onTimelineEvent;
hasUserReadEvent(userId: string, eventId: string): boolean;
/**
* @returns true if the thread with this ID can be found, and the supplied
* user sent the latest message in it.
*/
private userSentLatestEventInThread;
}
//# sourceMappingURL=room-receipts.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"room-receipts.d.ts","sourceRoot":"","sources":["../../src/models/room-receipts.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAoC,KAAK,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEnG,OAAO,EAAE,KAAK,IAAI,EAAa,MAAM,WAAW,CAAC;AAIjD;;GAEG;AACH,qBAAa,YAAY;IACrB,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,kBAAkB,CAAiB;IAC3C,OAAO,CAAC,gBAAgB,CAAmB;IAE3C,YAAmB,IAAI,EAAE,IAAI,EAO5B;IAED;;;;;;;;;;;;OAYG;IACI,GAAG,CAAC,cAAc,EAAE,cAAc,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CA0CnE;IAED;;;;OAIG;IACH,OAAO,CAAC,eAAe,CA2BrB;IAEK,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAyChE;IAED;;;OAGG;IACH,OAAO,CAAC,2BAA2B;CAQtC"}

View File

@@ -1,388 +0,0 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
/*
Copyright 2023 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 { MAIN_ROOM_TIMELINE } from "../@types/read_receipts.js";
import { threadIdForReceipt } from "../client.js";
import { RoomEvent } from "./room.js";
import { logger } from "../logger.js";
/**
* The latest receipts we have for a room.
*/
export class RoomReceipts {
constructor(room) {
_defineProperty(this, "room", void 0);
_defineProperty(this, "threadedReceipts", void 0);
_defineProperty(this, "unthreadedReceipts", void 0);
_defineProperty(this, "danglingReceipts", void 0);
/**
* Look for dangling receipts for the given event ID,
* and add them to the thread of unthread receipts if found.
* @param event - the event to look for
*/
_defineProperty(this, "onTimelineEvent", event => {
const eventId = event.getId();
if (!eventId) return;
const danglingReceipts = this.danglingReceipts.remove(eventId);
danglingReceipts?.forEach(danglingReceipt => {
// The receipt is a thread receipt
if (danglingReceipt.receipt.thread_id) {
this.threadedReceipts.set(danglingReceipt.receipt.thread_id, danglingReceipt.eventId, danglingReceipt.receiptType, danglingReceipt.userId, danglingReceipt.receipt.ts, danglingReceipt.synthetic);
} else {
this.unthreadedReceipts.set(eventId, danglingReceipt.receiptType, danglingReceipt.userId, danglingReceipt.receipt.ts, danglingReceipt.synthetic);
}
});
});
this.room = room;
this.threadedReceipts = new ThreadedReceipts(room);
this.unthreadedReceipts = new ReceiptsByUser(room);
this.danglingReceipts = new DanglingReceipts();
// We listen for timeline events so we can process dangling receipts
room.on(RoomEvent.Timeline, this.onTimelineEvent);
}
/**
* Remember the receipt information supplied. For each receipt:
*
* If we don't have the event for this receipt, store it as "dangling" so we
* can process it later.
*
* Otherwise store it per-user in either the threaded store for its
* thread_id, or the unthreaded store if there is no thread_id.
*
* Ignores any receipt that is before an existing receipt for the same user
* (in the same thread, if applicable). "Before" is defined by the
* unfilteredTimelineSet of the room.
*/
add(receiptContent, synthetic) {
/*
Transform this structure:
{
"$EVENTID": {
"m.read|m.read.private": {
"@user:example.org": {
"ts": 1661,
"thread_id": "main|$THREAD_ROOT_ID" // or missing/undefined for an unthreaded receipt
}
}
},
...
}
into maps of:
threaded :: threadid :: userId :: ReceiptInfo
unthreaded :: userId :: ReceiptInfo
dangling :: eventId :: DanglingReceipt
*/
for (const [eventId, eventReceipt] of Object.entries(receiptContent)) {
for (const [receiptType, receiptsByUser] of Object.entries(eventReceipt)) {
for (const [userId, receipt] of Object.entries(receiptsByUser)) {
const referencedEvent = this.room.findEventById(eventId);
if (!referencedEvent) {
this.danglingReceipts.add(new DanglingReceipt(eventId, receiptType, userId, receipt, synthetic));
} else if (receipt.thread_id) {
this.threadedReceipts.set(receipt.thread_id, eventId, receiptType, userId, receipt.ts, synthetic);
} else {
this.unthreadedReceipts.set(eventId, receiptType, userId, receipt.ts, synthetic);
}
}
}
}
}
hasUserReadEvent(userId, eventId) {
const unthreaded = this.unthreadedReceipts.get(userId);
if (unthreaded) {
if (isAfterOrSame(unthreaded.eventId, eventId, this.room)) {
// The unthreaded receipt is after this event, so we have read it.
return true;
}
}
const event = this.room.findEventById(eventId);
if (!event) {
// We don't know whether the user has read it - default to caution and say no.
// This shouldn't really happen and feels like it ought to be an exception: let's
// log a warn for now.
logger.warn(`hasUserReadEvent event ID ${eventId} not found in room ${this.room.roomId}: this shouldn't happen!`);
return false;
}
const threadId = threadIdForReceipt(event);
const threaded = this.threadedReceipts.get(threadId, userId);
if (threaded) {
if (isAfterOrSame(threaded.eventId, eventId, this.room)) {
// The threaded receipt is after this event, so we have read it.
return true;
}
}
// TODO: what if they sent the second-last event in the thread?
if (this.userSentLatestEventInThread(threadId, userId)) {
// The user sent the latest message in this event's thread, so we
// consider everything in the thread to be read.
//
// Note: maybe we don't need this because synthetic receipts should
// do this job for us?
return true;
}
// Neither of the receipts were after the event, so it's unread.
return false;
}
/**
* @returns true if the thread with this ID can be found, and the supplied
* user sent the latest message in it.
*/
userSentLatestEventInThread(threadId, userId) {
const timeline = threadId === MAIN_ROOM_TIMELINE ? this.room.getLiveTimeline().getEvents() : this.room.getThread(threadId)?.timeline;
return !!(timeline && timeline.length > 0 && timeline[timeline.length - 1].getSender() === userId);
}
}
// --- implementation details ---
/**
* The information "inside" a receipt once it has been stored inside
* RoomReceipts - what eventId it refers to, its type, and its ts.
*
* Does not contain userId or threadId since these are stored as keys of the
* maps in RoomReceipts.
*/
class ReceiptInfo {
constructor(eventId, receiptType, ts) {
this.eventId = eventId;
this.receiptType = receiptType;
this.ts = ts;
}
}
/**
* Everything we know about a receipt that is "dangling" because we can't find
* the event to which it refers.
*/
class DanglingReceipt {
constructor(eventId, receiptType, userId, receipt, synthetic) {
this.eventId = eventId;
this.receiptType = receiptType;
this.userId = userId;
this.receipt = receipt;
this.synthetic = synthetic;
}
}
class UserReceipts {
constructor(room) {
_defineProperty(this, "room", void 0);
/**
* The real receipt for this user.
*/
_defineProperty(this, "real", void 0);
/**
* The synthetic receipt for this user. If this is defined, it is later than real.
*/
_defineProperty(this, "synthetic", void 0);
this.room = room;
this.real = undefined;
this.synthetic = undefined;
}
set(synthetic, receiptInfo) {
if (synthetic) {
this.synthetic = receiptInfo;
} else {
this.real = receiptInfo;
}
// Preserve the invariant: synthetic is only defined if it's later than real
if (this.synthetic && this.real) {
if (isAfterOrSame(this.real.eventId, this.synthetic.eventId, this.room)) {
this.synthetic = undefined;
}
}
}
/**
* Return the latest receipt we have - synthetic if we have one (and it's
* later), otherwise real.
*/
get() {
// Relies on the invariant that synthetic is only defined if it's later than real.
return this.synthetic ?? this.real;
}
/**
* Return the latest receipt we have of the specified type (synthetic or not).
*/
getByType(synthetic) {
return synthetic ? this.synthetic : this.real;
}
}
/**
* The latest receipt info we have, either for a single thread, or all the
* unthreaded receipts for a room.
*
* userId: ReceiptInfo
*/
class ReceiptsByUser {
constructor(room) {
_defineProperty(this, "room", void 0);
/** map of userId: UserReceipts */
_defineProperty(this, "data", void 0);
this.room = room;
this.data = new Map();
}
/**
* Add the supplied receipt to our structure, if it is not earlier than the
* one we already hold for this user.
*/
set(eventId, receiptType, userId, ts, synthetic) {
const userReceipts = getOrCreate(this.data, userId, () => new UserReceipts(this.room));
const existingReceipt = userReceipts.getByType(synthetic);
if (existingReceipt && isAfter(existingReceipt.eventId, eventId, this.room)) {
// The new receipt is before the existing one - don't store it.
return;
}
// Possibilities:
//
// 1. there was no existing receipt, or
// 2. the existing receipt was before this one, or
// 3. we were unable to compare the receipts.
//
// In the case of 3 it's difficult to decide what to do, so the
// most-recently-received receipt wins.
//
// Case 3 can only happen if the events for these receipts have
// disappeared, which is quite unlikely since the new one has just been
// checked, and the old one was checked before it was inserted here.
//
// We go ahead and store this receipt (replacing the other if it exists)
userReceipts.set(synthetic, new ReceiptInfo(eventId, receiptType, ts));
}
/**
* Find the latest receipt we have for this user. (Note - there is only one
* receipt per user, because we are already inside a specific thread or
* unthreaded list.)
*
* If there is a later synthetic receipt for this user, return that.
* Otherwise, return the real receipt.
*
* @returns the found receipt info, or undefined if we have no receipt for this user.
*/
get(userId) {
return this.data.get(userId)?.get();
}
}
/**
* The latest threaded receipts we have for a room.
*/
class ThreadedReceipts {
constructor(room) {
_defineProperty(this, "room", void 0);
/** map of threadId: ReceiptsByUser */
_defineProperty(this, "data", void 0);
this.room = room;
this.data = new Map();
}
/**
* Add the supplied receipt to our structure, if it is not earlier than one
* we already hold for this user in this thread.
*/
set(threadId, eventId, receiptType, userId, ts, synthetic) {
const receiptsByUser = getOrCreate(this.data, threadId, () => new ReceiptsByUser(this.room));
receiptsByUser.set(eventId, receiptType, userId, ts, synthetic);
}
/**
* Find the latest threaded receipt for the supplied user in the supplied thread.
*
* @returns the found receipt info or undefined if we don't have one.
*/
get(threadId, userId) {
return this.data.get(threadId)?.get(userId);
}
}
/**
* All the receipts that we have received but can't process because we can't
* find the event they refer to.
*
* We hold on to them so we can process them if their event arrives later.
*/
class DanglingReceipts {
constructor() {
/**
* eventId: DanglingReceipt[]
*/
_defineProperty(this, "data", new Map());
}
/**
* Remember the supplied dangling receipt.
*/
add(danglingReceipt) {
const danglingReceipts = getOrCreate(this.data, danglingReceipt.eventId, () => []);
danglingReceipts.push(danglingReceipt);
}
/**
* Remove and return the dangling receipts for the given event ID.
* @param eventId - the event ID to look for
* @returns the found dangling receipts, or undefined if we don't have one.
*/
remove(eventId) {
const danglingReceipts = this.data.get(eventId);
this.data.delete(eventId);
return danglingReceipts;
}
}
function getOrCreate(m, key, createFn) {
const found = m.get(key);
if (found) {
return found;
} else {
const created = createFn();
m.set(key, created);
return created;
}
}
/**
* Is left after right (or the same)?
*
* Only returns true if both events can be found, and left is after or the same
* as right.
*
* @returns left \>= right
*/
function isAfterOrSame(leftEventId, rightEventId, room) {
const comparison = room.compareEventOrdering(leftEventId, rightEventId);
return comparison !== null && comparison >= 0;
}
/**
* Is left strictly after right?
*
* Only returns true if both events can be found, and left is strictly after right.
*
* @returns left \> right
*/
function isAfter(leftEventId, rightEventId, room) {
const comparison = room.compareEventOrdering(leftEventId, rightEventId);
return comparison !== null && comparison > 0;
}
//# sourceMappingURL=room-receipts.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,28 +0,0 @@
import { MatrixEvent } from "../models/event.ts";
import { type Room } from "./room.ts";
import type { RetentionPolicyService } from "../retentionPolicy.ts";
import type { IStore } from "../store/index.ts";
/**
* Applies https://github.com/matrix-org/matrix-spec-proposals/pull/1763 by checking the current
* state of the room and applying the *latest* retention policy to all preceeding events.
* Currently does not:
* - Apply global server policies (requires Synapse work)
* - React at all to `min_retention` configs.
* - Loop
*/
export declare class RoomRetentionPolicy {
private readonly room;
private readonly retentionService;
private readonly store;
private maxRetention;
private retentionTimeout?;
private readonly logger;
constructor(room: Room, retentionService: RetentionPolicyService, store: IStore);
shouldEventBeRetained(ev: MatrixEvent): boolean;
private readonly handleRetentionUpdate;
private readonly recalculateRetention;
private readonly timelineUpdated;
private readonly scheduleTimelineCheck;
private processTimeline;
}
//# sourceMappingURL=room-retention.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"room-retention.d.ts","sourceRoot":"","sources":["../../src/models/room-retention.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAa,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhD;;;;;;;GAOG;AACH,qBAAa,mBAAmB;IAOxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAP1B,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,gBAAgB,CAAC,CAAgC;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC,YACqB,IAAI,EAAE,IAAI,EACV,gBAAgB,EAAE,sBAAsB,EACxC,KAAK,EAAE,MAAM,EAwBjC;IAEM,qBAAqB,CAAC,EAAE,EAAE,WAAW,GAAG,OAAO,CAMrD;IAED,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAqBpC;IAEF,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CA6DnC;IAEF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAG9B;IAEF,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAQpC;IAEF,OAAO,CAAC,eAAe;CAoD1B"}

View File

@@ -1,192 +0,0 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
/*
Copyright 2026 Element Creations Ltd.
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 { MatrixEvent } from "../models/event.js";
import { RoomEvent } from "./room.js";
import { RoomStateEvent } from "./room-state.js";
import { logger as rootLogger } from "../logger.js";
import { EventType } from "../@types/event.js";
import { ROOM_RETENTION_TYPE } from "../@types/retention.js";
/**
* Applies https://github.com/matrix-org/matrix-spec-proposals/pull/1763 by checking the current
* state of the room and applying the *latest* retention policy to all preceeding events.
* Currently does not:
* - Apply global server policies (requires Synapse work)
* - React at all to `min_retention` configs.
* - Loop
*/
export class RoomRetentionPolicy {
constructor(room, retentionService, store) {
// minRetention is not implemented.
_defineProperty(this, "maxRetention", null);
_defineProperty(this, "retentionTimeout", void 0);
_defineProperty(this, "logger", void 0);
_defineProperty(this, "handleRetentionUpdate", async () => {
// First store if we currently have a retention period.
const hadRetentionPeriod = this.maxRetention !== null;
try {
await this.recalculateRetention(this.room.currentState);
} catch (err) {
this.logger.warn("Failed to recalculate retention policy", err);
return;
}
// Bind/unbind the Timeline event handler based on whether we should be running retention.
if (hadRetentionPeriod !== (this.maxRetention !== null)) {
this.logger.info("retention recalculated to be", this.maxRetention);
// We've changed
if (this.maxRetention) {
this.room.on(RoomEvent.Timeline, this.timelineUpdated);
} else {
this.room.off(RoomEvent.Timeline, this.timelineUpdated);
}
}
});
_defineProperty(this, "recalculateRetention", async roomState => {
const unstableEvent = roomState.getStateEvents(ROOM_RETENTION_TYPE.name).find(e => e.getStateKey() === "");
const stableEvent = roomState.getStateEvents(ROOM_RETENTION_TYPE.altName).find(e => e.getStateKey() === "");
const serverPolicy = this.retentionService.getCached();
const serverRoomPolicy = serverPolicy?.policies?.[this.room.roomId];
const roomStatePolicy = unstableEvent?.getContent() ?? stableEvent?.getContent();
const content =
// * if the homeserver defines a specific retention policy for this room, then use this policy as the effective retention policy of the room.
serverRoomPolicy ??
// * otherwise, if the state of the room includes a `m.room.retention` event with an empty state key:
roomStatePolicy ??
// * otherwise, if the state of the room does not include a m.room.retention event with an empty state key:
serverPolicy?.policies?.["*"];
if (!content) {
// * otherwise, don't apply a retention policy in this room.
this.maxRetention = null;
return;
}
const validatePolicy = !serverRoomPolicy && roomStatePolicy;
// Parse it
let {
max_lifetime: maxLifetime
} = content;
if (!maxLifetime && validatePolicy) {
// if there is no value specified in the room's state, use the limit's min value for the effective retention policy of the room (which can be null or absent).
maxLifetime = serverPolicy?.limits?.max_lifetime?.min;
if (!maxLifetime) {
this.maxRetention = null;
return;
}
}
if (typeof maxLifetime !== "number") {
throw Error(`max_lifetime must be a number, got "${maxLifetime}"`);
}
if (maxLifetime < 0 || !Number.isInteger(maxLifetime)) {
throw Error(`max_lifetime must be >= 0, got ${maxLifetime}`);
}
this.maxRetention = maxLifetime;
if (validatePolicy && serverPolicy?.limits?.max_lifetime) {
// We're using room state so we need to validate this policy matches the server.
/**
if the value specified in the room's state complies with the limit, use this value for the effective retention policy of the room.
if the value specified in the room's state is lower than the limit's min value, use the min value for the effective retention policy of the room.
if the value specified in the room's state is greater than the limit's max value, use the max value for the effective retention policy of the room.
*/
this.maxRetention = Math.max(this.maxRetention, serverPolicy.limits.max_lifetime.min ?? 0);
this.maxRetention = Math.min(this.maxRetention, serverPolicy.limits.max_lifetime.max ?? Number.MAX_SAFE_INTEGER);
}
this.processTimeline();
});
_defineProperty(this, "timelineUpdated", () => {
this.logger.info("timelineUpdated");
this.scheduleTimelineCheck(200);
});
_defineProperty(this, "scheduleTimelineCheck", nextTs => {
if (this.retentionTimeout) {
clearTimeout(this.retentionTimeout);
}
this.retentionTimeout = setTimeout(() => {
this.processTimeline();
this.retentionTimeout = undefined;
}, nextTs);
});
this.room = room;
this.retentionService = retentionService;
this.store = store;
this.logger = rootLogger.getChild(`RetentionPolicy ${room.roomId}`);
// Recalculate on room state update.
room.on(RoomStateEvent.Events, event => {
if (event.getStateKey() !== "" || event.getType() !== "org.matrix.msc1763.retention" && event.getType() !== "m.room.retention") {
return;
}
void this.handleRetentionUpdate();
});
// Recalculate our retention whenever the global policy changes.
this.retentionService.on("update", () => {
this.logger.info("Got global retention policy update!");
void this.handleRetentionUpdate();
});
// Do an initial check on construction.
void this.handleRetentionUpdate();
// Only bind RoomEvent.Timeline once we know we have a retention policy.
}
shouldEventBeRetained(ev) {
if (!this.maxRetention) {
return true;
}
const expireBefore = Date.now() - this.maxRetention;
return ev.getTs() > expireBefore;
}
processTimeline() {
if (!this.maxRetention) {
return; // No policy, skip.
}
this.logger.info(`Running processTimeline`);
const expireBefore = Date.now() - this.maxRetention;
const events = this.room.getLiveTimeline().getEvents().filter(ev => ev.getStateKey() === undefined && !ev.isRedacted() && ev.getType() !== "m.room.redaction");
const expiredEvents = events.filter(ev => ev.getTs() < expireBefore);
const [earliestExpiringEvent] = events.filter(ev => ev.getTs() >= expireBefore).sort((a, b) => a.getTs() - b.getTs());
if (earliestExpiringEvent) {
const nextTs = earliestExpiringEvent.getTs() + this.maxRetention - Date.now();
this.logger.info(`Next expiry scheduled for ${nextTs}`);
this.scheduleTimelineCheck(nextTs);
}
if (expiredEvents.length === 0) {
this.logger.info("Found no expired events");
return;
}
this.logger.info(`Found ${expiredEvents.length} expired events`);
for (const event of expiredEvents) {
this.room.tryApplyRedaction(new MatrixEvent({
type: EventType.RoomRedaction,
sender: event.getSender(),
event_id: "$synthetic_redaction",
room_id: this.room.roomId,
redacts: event.getId(),
content: {
reason: "Retention policy"
},
origin_server_ts: Date.now(),
unsigned: {}
}));
}
void this.store.removeEventsFromRoom(this.room.roomId, expiredEvents.map(e => e.getId())).catch(ex => {
this.logger.warn(`Failed to remove events from store`, ex);
});
}
}
//# sourceMappingURL=room-retention.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,11 +1,11 @@
import { RoomMember } from "./room-member.ts";
import { EventType } from "../@types/event.ts";
import { type MatrixEvent } from "./event.ts";
import { type MatrixClient } from "../client.ts";
import { GuestAccess, HistoryVisibility, JoinRule } from "../@types/partials.ts";
import { TypedEventEmitter } from "./typed-event-emitter.ts";
import { Beacon, BeaconEvent, type BeaconEventHandlerMap, type BeaconIdentifier } from "./beacon.ts";
import { TypedReEmitter } from "../ReEmitter.ts";
import { RoomMember } from "./room-member";
import { EventType } from "../@types/event";
import { MatrixEvent } from "./event";
import { MatrixClient } from "../client";
import { GuestAccess, HistoryVisibility, JoinRule } from "../@types/partials";
import { TypedEventEmitter } from "./typed-event-emitter";
import { Beacon, BeaconEvent, BeaconEventHandlerMap, BeaconIdentifier } from "./beacon";
import { TypedReEmitter } from "../ReEmitter";
export interface IMarkerFoundOptions {
/** Whether the timeline was empty before the marker event arrived in the
* room. This could be happen in a variety of cases:
@@ -29,12 +29,10 @@ declare enum OobStatus {
export interface IPowerLevelsContent {
users?: Record<string, number>;
events?: Record<string, number>;
notifications?: Partial<Record<"room", number>>;
users_default?: number;
events_default?: number;
state_default?: number;
ban?: number;
invite?: number;
kick?: number;
redact?: number;
}
@@ -42,15 +40,13 @@ export declare enum RoomStateEvent {
Events = "RoomState.events",
Members = "RoomState.members",
NewMember = "RoomState.newMember",
Update = "RoomState.update",// signals batches of updates without specificity
Update = "RoomState.update",
BeaconLiveness = "RoomState.BeaconLiveness",
Marker = "RoomState.Marker"
}
export type RoomStateEventHandlerMap = {
/**
* Fires whenever the event dictionary in room state is updated.
* This does not guarantee that any related objects (like RoomMember) have been updated.
* Use RoomStateEvent.Update for that.
* @param event - The matrix event which caused this event to fire.
* @param state - The room state whose RoomState.events dictionary
* was updated.
@@ -65,7 +61,7 @@ export type RoomStateEventHandlerMap = {
* });
* ```
*/
[RoomStateEvent.Events]: (event: MatrixEvent, state: RoomState, prevEvent: MatrixEvent | null) => void;
[RoomStateEvent.Events]: (event: MatrixEvent, state: RoomState, lastStateEvent: MatrixEvent | null) => void;
/**
* Fires whenever a member in the members dictionary is updated in any way.
* @param event - The matrix event which caused this event to fire.
@@ -103,9 +99,6 @@ export type RoomStateEventHandlerMap = {
};
type EmittedEvents = RoomStateEvent | BeaconEvent;
type EventHandlerMap = RoomStateEventHandlerMap & BeaconEventHandlerMap;
type KeysMatching<T, V> = {
[K in keyof T]-?: T[K] extends V ? K : never;
}[keyof T];
export declare class RoomState extends TypedEventEmitter<EmittedEvents, EventHandlerMap> {
readonly roomId: string;
private oobMemberFlags;
@@ -124,7 +117,6 @@ export declare class RoomState extends TypedEventEmitter<EmittedEvents, EventHan
paginationToken: string | null;
readonly beacons: Map<string, Beacon>;
private _liveBeaconIds;
private getVersionWarning;
/**
* Construct room state.
*
@@ -156,11 +148,6 @@ export declare class RoomState extends TypedEventEmitter<EmittedEvents, EventHan
constructor(roomId: string, oobMemberFlags?: {
status: OobStatus;
});
/**
* Gets the version of the room
* @returns The version of the room
*/
getRoomVersion(): string;
/**
* Returns the number of joined members in this room
* This method caches the result.
@@ -212,15 +199,13 @@ export declare class RoomState extends TypedEventEmitter<EmittedEvents, EventHan
/**
* Get state events from the state of the room.
* @param eventType - The event type of the state event.
* @returns A list of events
* @param stateKey - Optional. The state_key of the state event. If
* this is `undefined` then all matching state events will be
* returned.
* @returns A list of events if state_key was
* `undefined`, else a single event (or null if no match found).
*/
getStateEvents(eventType: EventType | string): MatrixEvent[];
/**
* Get state events from the state of the room.
* @param eventType - The event type of the state event.
* @param stateKey - The state_key of the state event.
* @returns A single event (or null if no match found).
*/
getStateEvents(eventType: EventType | string, stateKey: string): MatrixEvent | null;
get hasLiveBeacons(): boolean;
get liveBeaconIds(): BeaconIdentifier[];
@@ -358,7 +343,7 @@ export declare class RoomState extends TypedEventEmitter<EmittedEvents, EventHan
* @param powerLevel - The power level of the member
* @returns true if the given power level is sufficient
*/
hasSufficientPowerLevelFor(action: KeysMatching<Required<IPowerLevelsContent>, number>, powerLevel: number): boolean;
hasSufficientPowerLevelFor(action: "ban" | "kick" | "redact", powerLevel: number): boolean;
/**
* Short-form for maySendEvent('m.room.message', userId)
* @param userId - The user ID of the user to test permission for

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,21 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.RoomStateEvent = exports.RoomState = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _roomMember = require("./room-member");
var _logger = require("../logger");
var _utils = require("../utils");
var _event = require("../@types/event");
var _event2 = require("./event");
var _partials = require("../@types/partials");
var _typedEventEmitter = require("./typed-event-emitter");
var _beacon = require("./beacon");
var _ReEmitter = require("../ReEmitter");
var _beacon2 = require("../@types/beacon");
/*
Copyright 2015 - 2021 The Matrix.org Foundation C.I.C.
@@ -14,19 +31,6 @@ 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 { RoomMember } from "./room-member.js";
import { logger } from "../logger.js";
import { isNumber, removeHiddenChars } from "../utils.js";
import { EventType, UNSTABLE_MSC2716_MARKER } from "../@types/event.js";
import { MatrixEventEvent } from "./event.js";
import { GuestAccess, HistoryVisibility, JoinRule } from "../@types/partials.js";
import { TypedEventEmitter } from "./typed-event-emitter.js";
import { Beacon, BeaconEvent, getBeaconInfoIdentifier } from "./beacon.js";
import { TypedReEmitter } from "../ReEmitter.js";
import { M_BEACON, M_BEACON_INFO } from "../@types/beacon.js";
import { KnownMembership } from "../@types/membership.js";
import { shouldUseHydraForRoomVersion } from "../utils/roomVersion.js";
// possible statuses for out-of-band member loading
var OobStatus = /*#__PURE__*/function (OobStatus) {
OobStatus[OobStatus["NotStarted"] = 0] = "NotStarted";
@@ -34,17 +38,17 @@ var OobStatus = /*#__PURE__*/function (OobStatus) {
OobStatus[OobStatus["Finished"] = 2] = "Finished";
return OobStatus;
}(OobStatus || {});
export let RoomStateEvent = /*#__PURE__*/function (RoomStateEvent) {
let RoomStateEvent = /*#__PURE__*/function (RoomStateEvent) {
RoomStateEvent["Events"] = "RoomState.events";
RoomStateEvent["Members"] = "RoomState.members";
RoomStateEvent["NewMember"] = "RoomState.newMember";
RoomStateEvent["Update"] = "RoomState.update";
// signals batches of updates without specificity
RoomStateEvent["BeaconLiveness"] = "RoomState.BeaconLiveness";
RoomStateEvent["Marker"] = "RoomState.Marker";
return RoomStateEvent;
}({});
export class RoomState extends TypedEventEmitter {
exports.RoomStateEvent = RoomStateEvent;
class RoomState extends _typedEventEmitter.TypedEventEmitter {
/**
* Construct room state.
*
@@ -77,60 +81,42 @@ export class RoomState extends TypedEventEmitter {
status: OobStatus.NotStarted
}) {
super();
_defineProperty(this, "reEmitter", new TypedReEmitter(this));
_defineProperty(this, "sentinels", {});
this.roomId = roomId;
this.oobMemberFlags = oobMemberFlags;
(0, _defineProperty2.default)(this, "reEmitter", new _ReEmitter.TypedReEmitter(this));
(0, _defineProperty2.default)(this, "sentinels", {});
// userId: RoomMember
// stores fuzzy matches to a list of userIDs (applies utils.removeHiddenChars to keys)
_defineProperty(this, "displayNameToUserIds", new Map());
_defineProperty(this, "userIdsToDisplayNames", {});
_defineProperty(this, "tokenToInvite", {});
(0, _defineProperty2.default)(this, "displayNameToUserIds", new Map());
(0, _defineProperty2.default)(this, "userIdsToDisplayNames", {});
(0, _defineProperty2.default)(this, "tokenToInvite", {});
// 3pid invite state_key to m.room.member invite
_defineProperty(this, "joinedMemberCount", null);
(0, _defineProperty2.default)(this, "joinedMemberCount", null);
// cache of the number of joined members
// joined members count from summary api
// once set, we know the server supports the summary api
// and we should only trust that
// we could also only trust that before OOB members
// are loaded but doesn't seem worth the hassle atm
_defineProperty(this, "summaryJoinedMemberCount", null);
(0, _defineProperty2.default)(this, "summaryJoinedMemberCount", null);
// same for invited member count
_defineProperty(this, "invitedMemberCount", null);
_defineProperty(this, "summaryInvitedMemberCount", null);
_defineProperty(this, "modified", -1);
(0, _defineProperty2.default)(this, "invitedMemberCount", null);
(0, _defineProperty2.default)(this, "summaryInvitedMemberCount", null);
(0, _defineProperty2.default)(this, "modified", -1);
// XXX: Should be read-only
// The room member dictionary, keyed on the user's ID.
_defineProperty(this, "members", {});
(0, _defineProperty2.default)(this, "members", {});
// userId: RoomMember
// The state events dictionary, keyed on the event type and then the state_key value.
_defineProperty(this, "events", new Map());
(0, _defineProperty2.default)(this, "events", new Map());
// Map<eventType, Map<stateKey, MatrixEvent>>
// The pagination token for this state.
_defineProperty(this, "paginationToken", null);
_defineProperty(this, "beacons", new Map());
_defineProperty(this, "_liveBeaconIds", []);
// We only wants to print warnings about bad room state once.
_defineProperty(this, "getVersionWarning", false);
this.roomId = roomId;
this.oobMemberFlags = oobMemberFlags;
(0, _defineProperty2.default)(this, "paginationToken", null);
(0, _defineProperty2.default)(this, "beacons", new Map());
(0, _defineProperty2.default)(this, "_liveBeaconIds", []);
this.updateModifiedTime();
}
/**
* Gets the version of the room
* @returns The version of the room
*/
getRoomVersion() {
const createEvent = this.getStateEvents(EventType.RoomCreate, "");
if (!createEvent) {
if (!this.getVersionWarning) {
logger.warn("[getVersion] Room " + this.roomId + " does not have an m.room.create event");
this.getVersionWarning = true;
}
return "1";
}
return createEvent.getContent()["room_version"] ?? "1";
}
/**
* Returns the number of joined members in this room
* This method caches the result.
@@ -142,7 +128,7 @@ export class RoomState extends TypedEventEmitter {
}
if (this.joinedMemberCount === null) {
this.joinedMemberCount = this.getMembers().reduce((count, m) => {
return m.membership === KnownMembership.Join ? count + 1 : count;
return m.membership === "join" ? count + 1 : count;
}, 0);
}
return this.joinedMemberCount;
@@ -166,7 +152,7 @@ export class RoomState extends TypedEventEmitter {
}
if (this.invitedMemberCount === null) {
this.invitedMemberCount = this.getMembers().reduce((count, m) => {
return m.membership === KnownMembership.Invite ? count + 1 : count;
return m.membership === "invite" ? count + 1 : count;
}, 0);
}
return this.invitedMemberCount;
@@ -219,9 +205,9 @@ export class RoomState extends TypedEventEmitter {
if (!userId) return null;
let sentinel = this.sentinels[userId];
if (sentinel === undefined) {
sentinel = new RoomMember(this.roomId, userId);
sentinel = new _roomMember.RoomMember(this.roomId, userId);
const member = this.members[userId];
if (member?.events.member) {
if (member !== null && member !== void 0 && member.events.member) {
sentinel.setMembershipEvent(member.events.member, this);
}
this.sentinels[userId] = sentinel;
@@ -232,14 +218,11 @@ export class RoomState extends TypedEventEmitter {
/**
* Get state events from the state of the room.
* @param eventType - The event type of the state event.
* @returns A list of events
*/
/**
* Get state events from the state of the room.
* @param eventType - The event type of the state event.
* @param stateKey - The state_key of the state event.
* @returns A single event (or null if no match found).
* @param stateKey - Optional. The state_key of the state event. If
* this is `undefined` then all matching state events will be
* returned.
* @returns A list of events if state_key was
* `undefined`, else a single event (or null if no match found).
*/
getStateEvents(eventType, stateKey) {
@@ -255,7 +238,8 @@ export class RoomState extends TypedEventEmitter {
return event ? event : null;
}
get hasLiveBeacons() {
return !!this.liveBeaconIds?.length;
var _this$liveBeaconIds;
return !!((_this$liveBeaconIds = this.liveBeaconIds) !== null && _this$liveBeaconIds !== void 0 && _this$liveBeaconIds.length);
}
get liveBeaconIds() {
return this._liveBeaconIds;
@@ -294,7 +278,8 @@ export class RoomState extends TypedEventEmitter {
// copy markOutOfBand flags
this.getMembers().forEach(member => {
if (member.isOutOfBand()) {
copy.getMember(member.userId)?.markOutOfBand();
var _copy$getMember;
(_copy$getMember = copy.getMember(member.userId)) === null || _copy$getMember === void 0 ? void 0 : _copy$getMember.markOutOfBand();
}
});
}
@@ -333,33 +318,16 @@ export class RoomState extends TypedEventEmitter {
this.updateModifiedTime();
// update the core event dict
// Track display names that change so we can recalculate disambiguation
const affectedDisplayNames = new Set();
// Track userIds whose membership events we process so we don't emit duplicate events
const processedMemberUserIds = new Set();
stateEvents.forEach(event => {
if (event.getRoomId() !== this.roomId || !event.isState()) return;
if (M_BEACON_INFO.matches(event.getType())) {
if (_beacon2.M_BEACON_INFO.matches(event.getType())) {
this.setBeacon(event);
}
const lastStateEvent = this.getStateEventMatching(event);
this.setStateEvent(event);
if (event.getType() === EventType.RoomMember) {
const userId = event.getStateKey();
processedMemberUserIds.add(userId);
const newDisplayName = event.getContent().displayname ?? "";
const oldDisplayName = this.userIdsToDisplayNames[userId];
// Track both old and new display names for disambiguation recalculation
if (oldDisplayName) {
const strippedOld = removeHiddenChars(oldDisplayName);
if (strippedOld) affectedDisplayNames.add(strippedOld);
}
if (newDisplayName) {
const strippedNew = removeHiddenChars(newDisplayName);
if (strippedNew) affectedDisplayNames.add(strippedNew);
}
this.updateDisplayNameCache(userId, newDisplayName);
if (event.getType() === _event.EventType.RoomMember) {
var _event$getContent$dis;
this.updateDisplayNameCache(event.getStateKey(), (_event$getContent$dis = event.getContent().displayname) !== null && _event$getContent$dis !== void 0 ? _event$getContent$dis : "");
this.updateThirdPartyTokenCache(event);
}
this.emit(RoomStateEvent.Events, event, this, lastStateEvent);
@@ -371,13 +339,13 @@ export class RoomState extends TypedEventEmitter {
// clashing names rather than progressively which only catches 1 of them).
stateEvents.forEach(event => {
if (event.getRoomId() !== this.roomId || !event.isState()) return;
if (event.getType() === EventType.RoomMember) {
if (event.getType() === _event.EventType.RoomMember) {
const userId = event.getStateKey();
// leave events apparently elide the displayname or avatar_url,
// so let's fake one up so that we don't leak user ids
// into the timeline
if (event.getContent().membership === KnownMembership.Leave || event.getContent().membership === KnownMembership.Ban) {
if (event.getContent().membership === "leave" || event.getContent().membership === "ban") {
event.getContent().avatar_url = event.getContent().avatar_url || event.getPrevContent().avatar_url;
event.getContent().displayname = event.getContent().displayname || event.getPrevContent().displayname;
}
@@ -385,24 +353,19 @@ export class RoomState extends TypedEventEmitter {
member.setMembershipEvent(event, this);
this.updateMember(member);
this.emit(RoomStateEvent.Members, event, this, member);
} else if (event.getType() === EventType.RoomPowerLevels) {
} else if (event.getType() === _event.EventType.RoomPowerLevels) {
// events with unknown state keys should be ignored
// and should not aggregate onto members power levels
if (event.getStateKey() !== "") {
return;
}
const members = Object.values(this.members);
const createEvent = this.getStateEvents(EventType.RoomCreate, "");
const creators = getCreators(this.getRoomVersion(), createEvent);
members.forEach(member => {
// We only propagate `RoomState.members` event if the
// power levels has been changed
// large room suffer from large re-rendering especially when not needed
const oldLastModified = member.getLastModifiedTime();
if (createEvent) {
const pl = powerLevelForUserId(member.userId, event, creators);
member.setPowerLevel(pl, event);
}
member.setPowerLevelEvent(event);
if (oldLastModified !== member.getLastModifiedTime()) {
this.emit(RoomStateEvent.Members, event, this, member);
}
@@ -410,37 +373,10 @@ export class RoomState extends TypedEventEmitter {
// assume all our sentinels are now out-of-date
this.sentinels = {};
} else if (UNSTABLE_MSC2716_MARKER.matches(event.getType())) {
} else if (_event.UNSTABLE_MSC2716_MARKER.matches(event.getType())) {
this.emit(RoomStateEvent.Marker, event, markerFoundOptions);
}
});
// Recalculate disambiguation for all members whose display names were affected.
// This ensures that when a user changes their name to match (or stop matching)
// another user, all affected users' disambiguation flags are updated correctly.
if (affectedDisplayNames.size > 0) {
// Collect all affected user IDs first to avoid duplicate processing
const affectedUserIds = new Set();
for (const displayName of affectedDisplayNames) {
const userIds = this.displayNameToUserIds.get(displayName) ?? [];
userIds.forEach(id => affectedUserIds.add(id));
}
// Process each affected member once, excluding those whose membership
// events were already processed (they already got their events emitted)
for (const userId of affectedUserIds) {
if (processedMemberUserIds.has(userId)) {
continue;
}
const member = this.members[userId];
if (member?.events.member) {
const nameChanged = member.recalculateDisambiguatedName(this);
if (nameChanged) {
this.emit(RoomStateEvent.Members, member.events.member, this, member);
}
}
}
}
this.emit(RoomStateEvent.Update, this);
}
async processBeaconEvents(events, matrixClient) {
@@ -454,7 +390,7 @@ export class RoomState extends TypedEventEmitter {
return dict;
}, {});
const processBeaconRelation = (beaconInfoEventId, event) => {
if (!M_BEACON.matches(event.getType())) {
if (!_beacon2.M_BEACON.matches(event.getType())) {
return;
}
const beacon = beaconByEventIdDict[beaconInfoEventId];
@@ -463,17 +399,18 @@ export class RoomState extends TypedEventEmitter {
}
};
for (const event of events) {
const relatedToEventId = event.getRelation()?.event_id;
var _event$getRelation;
const relatedToEventId = (_event$getRelation = event.getRelation()) === null || _event$getRelation === void 0 ? void 0 : _event$getRelation.event_id;
// not related to a beacon we know about; discard
if (!relatedToEventId || !beaconByEventIdDict[relatedToEventId]) return;
if (!M_BEACON.matches(event.getType()) && !event.isEncrypted()) return;
if (!_beacon2.M_BEACON.matches(event.getType()) && !event.isEncrypted()) return;
try {
await matrixClient.decryptEventIfNeeded(event);
processBeaconRelation(relatedToEventId, event);
} catch {
if (event.isDecryptionFailure()) {
// add an event listener for once the event is decrypted.
event.once(MatrixEventEvent.Decrypted, async () => {
event.once(_event2.MatrixEventEvent.Decrypted, async () => {
processBeaconRelation(relatedToEventId, event);
});
}
@@ -496,7 +433,7 @@ export class RoomState extends TypedEventEmitter {
getOrCreateMember(userId, event) {
let member = this.members[userId];
if (!member) {
member = new RoomMember(this.roomId, userId);
member = new _roomMember.RoomMember(this.roomId, userId);
// add member to members before emitting any events,
// as event handlers often lookup the member
this.members[userId] = member;
@@ -515,11 +452,12 @@ export class RoomState extends TypedEventEmitter {
* @experimental
*/
setBeacon(event) {
const beaconIdentifier = getBeaconInfoIdentifier(event);
const beaconIdentifier = (0, _beacon.getBeaconInfoIdentifier)(event);
if (this.beacons.has(beaconIdentifier)) {
const beacon = this.beacons.get(beaconIdentifier);
if (event.isRedacted()) {
if (beacon.beaconInfoId === event.getRedactionEvent()?.redacts) {
var _event$getRedactionEv;
if (beacon.beaconInfoId === ((_event$getRedactionEv = event.getRedactionEvent()) === null || _event$getRedactionEv === void 0 ? void 0 : _event$getRedactionEv.redacts)) {
beacon.destroy();
this.beacons.delete(beaconIdentifier);
}
@@ -530,11 +468,11 @@ export class RoomState extends TypedEventEmitter {
if (event.isRedacted()) {
return;
}
const beacon = new Beacon(event);
this.reEmitter.reEmit(beacon, [BeaconEvent.New, BeaconEvent.Update, BeaconEvent.Destroy, BeaconEvent.LivenessChange]);
this.emit(BeaconEvent.New, event, beacon);
beacon.on(BeaconEvent.LivenessChange, this.onBeaconLivenessChange.bind(this));
beacon.on(BeaconEvent.Destroy, this.onBeaconLivenessChange.bind(this));
const beacon = new _beacon.Beacon(event);
this.reEmitter.reEmit(beacon, [_beacon.BeaconEvent.New, _beacon.BeaconEvent.Update, _beacon.BeaconEvent.Destroy, _beacon.BeaconEvent.LivenessChange]);
this.emit(_beacon.BeaconEvent.New, event, beacon);
beacon.on(_beacon.BeaconEvent.LivenessChange, this.onBeaconLivenessChange.bind(this));
beacon.on(_beacon.BeaconEvent.Destroy, this.onBeaconLivenessChange.bind(this));
this.beacons.set(beacon.identifier, beacon);
}
@@ -548,15 +486,14 @@ export class RoomState extends TypedEventEmitter {
this.emit(RoomStateEvent.BeaconLiveness, this, this.hasLiveBeacons);
}
getStateEventMatching(event) {
return this.events.get(event.getType())?.get(event.getStateKey()) ?? null;
var _this$events$get$get, _this$events$get;
return (_this$events$get$get = (_this$events$get = this.events.get(event.getType())) === null || _this$events$get === void 0 ? void 0 : _this$events$get.get(event.getStateKey())) !== null && _this$events$get$get !== void 0 ? _this$events$get$get : null;
}
updateMember(member) {
// this member may have a power level already, so set it.
const createEvent = this.getStateEvents(EventType.RoomCreate, "");
const pwrLvlEvent = this.getStateEvents(EventType.RoomPowerLevels, "");
if (pwrLvlEvent && createEvent) {
const powerLevel = powerLevelForUserId(member.userId, pwrLvlEvent, getCreators(this.getRoomVersion(), createEvent));
member.setPowerLevel(powerLevel, pwrLvlEvent);
const pwrLvlEvent = this.getStateEvents(_event.EventType.RoomPowerLevels, "");
if (pwrLvlEvent) {
member.setPowerLevelEvent(pwrLvlEvent);
}
// blow away the sentinel which is now outdated
@@ -619,7 +556,7 @@ export class RoomState extends TypedEventEmitter {
delete this.members[userId];
}
});
logger.log(`LL: RoomState removed ${count} members...`);
_logger.logger.log(`LL: RoomState removed ${count} members...`);
this.oobMemberFlags.status = OobStatus.NotStarted;
}
@@ -628,11 +565,11 @@ export class RoomState extends TypedEventEmitter {
* @param stateEvents - array of membership state events
*/
setOutOfBandMembers(stateEvents) {
logger.log(`LL: RoomState about to set ${stateEvents.length} OOB members ...`);
_logger.logger.log(`LL: RoomState about to set ${stateEvents.length} OOB members ...`);
if (this.oobMemberFlags.status !== OobStatus.InProgress) {
return;
}
logger.log(`LL: RoomState put in finished state ...`);
_logger.logger.log(`LL: RoomState put in finished state ...`);
this.oobMemberFlags.status = OobStatus.Finished;
stateEvents.forEach(e => this.setOutOfBandMember(e));
this.emit(RoomStateEvent.Update, this);
@@ -643,7 +580,7 @@ export class RoomState extends TypedEventEmitter {
* @param stateEvent - membership state event
*/
setOutOfBandMember(stateEvent) {
if (stateEvent.getType() !== EventType.RoomMember) {
if (stateEvent.getType() !== _event.EventType.RoomMember) {
return;
}
const userId = stateEvent.getStateKey();
@@ -706,7 +643,8 @@ export class RoomState extends TypedEventEmitter {
* @returns An array of user IDs or an empty array.
*/
getUserIdsWithDisplayName(displayName) {
return this.displayNameToUserIds.get(removeHiddenChars(displayName)) ?? [];
var _this$displayNameToUs;
return (_this$displayNameToUs = this.displayNameToUserIds.get((0, _utils.removeHiddenChars)(displayName))) !== null && _this$displayNameToUs !== void 0 ? _this$displayNameToUs : [];
}
/**
@@ -718,14 +656,13 @@ export class RoomState extends TypedEventEmitter {
*/
maySendRedactionForEvent(mxEvent, userId) {
const member = this.getMember(userId);
if (!member || member.membership === KnownMembership.Leave) return false;
if (!member || member.membership === "leave") return false;
if (mxEvent.status || mxEvent.isRedacted()) return false;
// The user may have been the sender, but they can't redact their own message
// if redactions are blocked.
const canRedact = this.maySendEvent(EventType.RoomRedaction, userId);
if (!canRedact) return false;
if (mxEvent.getSender() === userId) return true;
const canRedact = this.maySendEvent(_event.EventType.RoomRedaction, userId);
if (mxEvent.getSender() === userId) return canRedact;
return this.hasSufficientPowerLevelFor("redact", member.powerLevel);
}
@@ -736,13 +673,13 @@ export class RoomState extends TypedEventEmitter {
* @returns true if the given power level is sufficient
*/
hasSufficientPowerLevelFor(action, powerLevel) {
const powerLevelsEvent = this.getStateEvents(EventType.RoomPowerLevels, "");
const powerLevelsEvent = this.getStateEvents(_event.EventType.RoomPowerLevels, "");
let powerLevels = {};
if (powerLevelsEvent) {
powerLevels = powerLevelsEvent.getContent();
}
let requiredLevel = 50;
if (isNumber(powerLevels[action])) {
if ((0, _utils.isNumber)(powerLevels[action])) {
requiredLevel = powerLevels[action];
}
return powerLevel >= requiredLevel;
@@ -755,7 +692,7 @@ export class RoomState extends TypedEventEmitter {
* message events into the given room.
*/
maySendMessage(userId) {
return this.maySendEventOfType(EventType.RoomMessage, userId, false);
return this.maySendEventOfType(_event.EventType.RoomMessage, userId, false);
}
/**
@@ -813,11 +750,12 @@ export class RoomState extends TypedEventEmitter {
* according to the room's state.
*/
maySendEventOfType(eventType, userId, state) {
const powerLevelsEvent = this.getStateEvents(EventType.RoomPowerLevels, "");
const powerLevelsEvent = this.getStateEvents(_event.EventType.RoomPowerLevels, "");
let powerLevels;
let eventsLevels = {};
let stateDefault = 0;
let eventsDefault = 0;
let powerLevel = 0;
if (powerLevelsEvent) {
powerLevels = powerLevelsEvent.getContent();
eventsLevels = powerLevels.events || {};
@@ -826,6 +764,12 @@ export class RoomState extends TypedEventEmitter {
} else {
stateDefault = 50;
}
const userPowerLevel = powerLevels.users && powerLevels.users[userId];
if (Number.isSafeInteger(userPowerLevel)) {
powerLevel = userPowerLevel;
} else if (Number.isSafeInteger(powerLevels.users_default)) {
powerLevel = powerLevels.users_default;
}
if (Number.isSafeInteger(powerLevels.events_default)) {
eventsDefault = powerLevels.events_default;
}
@@ -834,9 +778,7 @@ export class RoomState extends TypedEventEmitter {
if (Number.isSafeInteger(eventsLevels[eventType])) {
requiredLevel = eventsLevels[eventType];
}
const roomMember = this.getMember(userId);
const userPowerLevel = roomMember?.powerLevel ?? 0;
return userPowerLevel >= requiredLevel;
return powerLevel >= requiredLevel;
}
/**
@@ -852,9 +794,9 @@ export class RoomState extends TypedEventEmitter {
if (!member) {
return false;
}
const powerLevelsEvent = this.getStateEvents(EventType.RoomPowerLevels, "");
const powerLevelsEvent = this.getStateEvents(_event.EventType.RoomPowerLevels, "");
let notifLevel = 50;
if (powerLevelsEvent && powerLevelsEvent.getContent() && powerLevelsEvent.getContent().notifications && isNumber(powerLevelsEvent.getContent().notifications[notifLevelKey])) {
if (powerLevelsEvent && powerLevelsEvent.getContent() && powerLevelsEvent.getContent().notifications && (0, _utils.isNumber)(powerLevelsEvent.getContent().notifications[notifLevelKey])) {
notifLevel = powerLevelsEvent.getContent().notifications[notifLevelKey];
}
return member.powerLevel >= notifLevel;
@@ -865,9 +807,10 @@ export class RoomState extends TypedEventEmitter {
* @returns the join_rule applied to this room
*/
getJoinRule() {
const joinRuleEvent = this.getStateEvents(EventType.RoomJoinRules, "");
const joinRuleContent = joinRuleEvent?.getContent() ?? {};
return joinRuleContent["join_rule"] || JoinRule.Invite;
var _joinRuleEvent$getCon;
const joinRuleEvent = this.getStateEvents(_event.EventType.RoomJoinRules, "");
const joinRuleContent = (_joinRuleEvent$getCon = joinRuleEvent === null || joinRuleEvent === void 0 ? void 0 : joinRuleEvent.getContent()) !== null && _joinRuleEvent$getCon !== void 0 ? _joinRuleEvent$getCon : {};
return joinRuleContent["join_rule"] || _partials.JoinRule.Invite;
}
/**
@@ -875,9 +818,10 @@ export class RoomState extends TypedEventEmitter {
* @returns the history_visibility applied to this room
*/
getHistoryVisibility() {
const historyVisibilityEvent = this.getStateEvents(EventType.RoomHistoryVisibility, "");
const historyVisibilityContent = historyVisibilityEvent?.getContent() ?? {};
return historyVisibilityContent["history_visibility"] || HistoryVisibility.Shared;
var _historyVisibilityEve;
const historyVisibilityEvent = this.getStateEvents(_event.EventType.RoomHistoryVisibility, "");
const historyVisibilityContent = (_historyVisibilityEve = historyVisibilityEvent === null || historyVisibilityEvent === void 0 ? void 0 : historyVisibilityEvent.getContent()) !== null && _historyVisibilityEve !== void 0 ? _historyVisibilityEve : {};
return historyVisibilityContent["history_visibility"] || _partials.HistoryVisibility.Shared;
}
/**
@@ -885,9 +829,10 @@ export class RoomState extends TypedEventEmitter {
* @returns the guest_access applied to this room
*/
getGuestAccess() {
const guestAccessEvent = this.getStateEvents(EventType.RoomGuestAccess, "");
const guestAccessContent = guestAccessEvent?.getContent() ?? {};
return guestAccessContent["guest_access"] || GuestAccess.Forbidden;
var _guestAccessEvent$get;
const guestAccessEvent = this.getStateEvents(_event.EventType.RoomGuestAccess, "");
const guestAccessContent = (_guestAccessEvent$get = guestAccessEvent === null || guestAccessEvent === void 0 ? void 0 : guestAccessEvent.getContent()) !== null && _guestAccessEvent$get !== void 0 ? _guestAccessEvent$get : {};
return guestAccessContent["guest_access"] || _partials.GuestAccess.Forbidden;
}
/**
@@ -913,7 +858,7 @@ export class RoomState extends TypedEventEmitter {
// which just calls through to here.
if (msc3946ProcessDynamicPredecessor) {
const predecessorEvent = this.getStateEvents(EventType.RoomPredecessor, "");
const predecessorEvent = this.getStateEvents(_event.EventType.RoomPredecessor, "");
if (predecessorEvent) {
const content = predecessorEvent.getContent();
const roomId = content.predecessor_room_id;
@@ -934,7 +879,7 @@ export class RoomState extends TypedEventEmitter {
}
}
}
const createEvent = this.getStateEvents(EventType.RoomCreate, "");
const createEvent = this.getStateEvents(_event.EventType.RoomCreate, "");
if (createEvent) {
const predecessor = createEvent.getContent()["predecessor"];
if (predecessor) {
@@ -961,7 +906,7 @@ export class RoomState extends TypedEventEmitter {
if (!token) {
return;
}
const threePidInvite = this.getStateEvents(EventType.RoomThirdPartyInvite, token);
const threePidInvite = this.getStateEvents(_event.EventType.RoomThirdPartyInvite, token);
if (!threePidInvite) {
return;
}
@@ -975,7 +920,7 @@ export class RoomState extends TypedEventEmitter {
// We clobber the user_id > name lookup but the name -> [user_id] lookup
// means we need to remove that user ID from that array rather than nuking
// the lot.
const strippedOldName = removeHiddenChars(oldName);
const strippedOldName = (0, _utils.removeHiddenChars)(oldName);
const existingUserIds = this.displayNameToUserIds.get(strippedOldName);
if (existingUserIds) {
// remove this user ID from this array
@@ -984,54 +929,15 @@ export class RoomState extends TypedEventEmitter {
}
}
this.userIdsToDisplayNames[userId] = displayName;
const strippedDisplayname = displayName && removeHiddenChars(displayName);
const strippedDisplayname = displayName && (0, _utils.removeHiddenChars)(displayName);
// an empty stripped displayname (undefined/'') will be set to MXID in room-member.js
if (strippedDisplayname) {
const arr = this.displayNameToUserIds.get(strippedDisplayname) ?? [];
var _this$displayNameToUs2;
const arr = (_this$displayNameToUs2 = this.displayNameToUserIds.get(strippedDisplayname)) !== null && _this$displayNameToUs2 !== void 0 ? _this$displayNameToUs2 : [];
arr.push(userId);
this.displayNameToUserIds.set(strippedDisplayname, arr);
}
}
}
/**
* Get the set of creator user IDs for a room: empty if the room is not a 'hydra' room, otherwise
* computed from the sender of the m.room.create event plus the additional_creators field.
* @param roomVersion The version of the room
* @param roomCreateEvent The m.room.create event for the room
* @returns A set of user IDs of the creators of the room.
*/
function getCreators(roomVersion, roomCreateEvent) {
const creators = new Set();
if (shouldUseHydraForRoomVersion(roomVersion) && roomCreateEvent) {
const roomCreateSender = roomCreateEvent.getSender();
if (roomCreateSender) creators.add(roomCreateSender);
const additionalCreators = roomCreateEvent.getDirectionalContent().additional_creators;
if (Array.isArray(additionalCreators)) additionalCreators.forEach(c => creators.add(c));
}
return creators;
}
/**
*
* @param userId The user ID to compute the power level for
* @param powerLevelEvents The power level event for the room
* @param creators The set of creator user IDs for the room if the room is a 'hydra' room, otherwise the empty set.
*/
function powerLevelForUserId(userId, powerLevelEvent, creators) {
if (creators.has(userId)) {
// As of "Hydra", If the user is a creator, they always have the highest power level
return Infinity;
} else {
const evContent = powerLevelEvent.getDirectionalContent();
const users = evContent.users || {};
if (users[userId] !== undefined && Number.isInteger(users[userId])) {
return users[userId];
} else if (evContent.users_default !== undefined) {
return evContent.users_default;
} else {
return 0;
}
}
}
exports.RoomState = RoomState;
//# sourceMappingURL=room-state.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,111 +0,0 @@
import { type MatrixEvent } from "./event.ts";
import { TypedEventEmitter } from "./typed-event-emitter.ts";
export declare enum RoomStickyEventsEvent {
Update = "RoomStickyEvents.Update"
}
export type StickyMatrixEvent = MatrixEvent & {
unstableStickyExpiresAt: number;
};
export type RoomStickyEventsMap = {
/**
* Fires when any sticky event changes happen in a room.
* @param added Any new sticky events with no predecessor events (matching sender, type, and sticky_key)
* @param updated Any sticky events that supersede an existing event (matching sender, type, and sticky_key)
* @param removed The events that were removed from the map due to expiry.
*/
[RoomStickyEventsEvent.Update]: (added: StickyMatrixEvent[], updated: {
current: StickyMatrixEvent;
previous: StickyMatrixEvent;
}[], removed: StickyMatrixEvent[]) => void;
};
/**
* Tracks sticky events on behalf of one room, and fires an event
* whenever a sticky event is updated or replaced.
*/
export declare class RoomStickyEventsStore extends TypedEventEmitter<RoomStickyEventsEvent, RoomStickyEventsMap> {
/**
* Sticky event map is a nested map of:
* eventType -> `content.sticky_key sender` -> StickyMatrixEvent[]
*
* The events are ordered in latest to earliest expiry, so that the first event
* in the array will always be the "current" one.
*/
private readonly stickyEventsMap;
/**
* These are sticky events that have no sticky key and therefore exist outside the tuple
* system above. They are just held in this Set until they expire.
*/
private readonly unkeyedStickyEvents;
private stickyEventTimer?;
private nextStickyEventExpiryTs;
/**
* Sort two sticky events by order of expiry. This assumes the sticky events have the same
* `type`, `sticky_key` and `sender`.
* @returns A positive value if event A will expire sooner, or a negative value if event B will expire sooner.
*/
private static sortStickyEvent;
/**
* Generate the correct key for an event to be found in the inner maps of `stickyEventsMap`.
* @param stickyKey The sticky key of an event.
* @param sender The sender of the event.
*/
private static stickyMapKey;
/**
* Get all sticky events that are currently active.
* @returns An iterable set of events.
* @yields {@link StickyMatrixEvent}
*/
getStickyEvents(): Iterable<StickyMatrixEvent>;
/**
* Get an active sticky event that match the given `type`, `sender`, and `stickyKey`
* @param type The event `type`.
* @param sender The sender of the sticky event.
* @param stickyKey The sticky key used by the event.
* @returns A matching active sticky event, or undefined.
*/
getKeyedStickyEvent(sender: string, type: string, stickyKey: string): StickyMatrixEvent | undefined;
/**
* Get active sticky events without a sticky key that match the given `type` and `sender`.
* @param type The event `type`.
* @param sender The sender of the sticky event.
* @returns An array of matching sticky events.
*/
getUnkeyedStickyEvent(sender: string, type: string): StickyMatrixEvent[];
/**
* Adds a sticky event into the local sticky event map.
*
* NOTE: This will not cause `RoomEvent.StickyEvents` to be emitted.
*
* @throws If the `event` does not contain valid sticky data.
* @param event The MatrixEvent that contains sticky data.
* @returns An object describing whether the event was added to the map,
* and the previous event it may have replaced.
*/
private addStickyEvent;
/**
* Add a series of sticky events, emitting `RoomEvent.StickyEvents` if any
* changes were made.
* @param events A set of new sticky events.
*/
addStickyEvents(events: MatrixEvent[]): void;
/**
* Schedule the sticky event expiry timer. The timer will
* run immediately if an event has already expired.
*/
private scheduleStickyTimer;
/**
* Clean out any expired sticky events.
*/
private readonly cleanExpiredStickyEvents;
/**
* Handles incoming event redactions. Checks the sticky map
* for any active sticky events being redacted.
* @param redactedEvent The MatrixEvent OR event ID of the event being redacted. MAY not be a sticky event.
*/
handleRedaction(redactedEvent: MatrixEvent | string): void;
/**
* Clear all events and stop the timer from firing.
*/
clear(): void;
}
//# sourceMappingURL=room-sticky-events.d.ts.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"room-sticky-events.d.ts","sourceRoot":"","sources":["../../src/models/room-sticky-events.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAI7D,oBAAY,qBAAqB;IAC7B,MAAM,4BAA4B;CACrC;AAED,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG;IAAE,uBAAuB,EAAE,MAAM,CAAA;CAAE,CAAC;AAElF,MAAM,MAAM,mBAAmB,GAAG;IAC9B;;;;;OAKG;IACH,CAAC,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAC5B,KAAK,EAAE,iBAAiB,EAAE,EAC1B,OAAO,EAAE;QAAE,OAAO,EAAE,iBAAiB,CAAC;QAAC,QAAQ,EAAE,iBAAiB,CAAA;KAAE,EAAE,EACtE,OAAO,EAAE,iBAAiB,EAAE,KAC3B,IAAI,CAAC;CACb,CAAC;AASF;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,iBAAiB,CAAC,qBAAqB,EAAE,mBAAmB,CAAC;IACpG;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAuD;IACvF;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgC;IAEpE,OAAO,CAAC,gBAAgB,CAAC,CAAgC;IACzD,OAAO,CAAC,uBAAuB,CAAmC;IAElE;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAe9B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAI3B;;;;OAIG;IACK,eAAe,IAAI,QAAQ,CAAC,iBAAiB,CAAC,CASrD;IAED;;;;;;OAMG;IACI,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAGzG;IAED;;;;;OAKG;IACI,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAE9E;IAED;;;;;;;;;OASG;IACH,OAAO,CAAC,cAAc;IA2DtB;;;;OAIG;IACI,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAoBlD;IAED;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAY3B;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAgDvC;IAEF;;;;OAIG;IACI,eAAe,CAAC,aAAa,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAgFhE;IAED;;OAEG;IACI,KAAK,IAAI,IAAI,CAKnB;CACJ"}

View File

@@ -1,349 +0,0 @@
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { logger as loggerInstance } from "../logger.js";
import { TypedEventEmitter } from "./typed-event-emitter.js";
const logger = loggerInstance.getChild("RoomStickyEvents");
export let RoomStickyEventsEvent = /*#__PURE__*/function (RoomStickyEventsEvent) {
RoomStickyEventsEvent["Update"] = "RoomStickyEvents.Update";
return RoomStickyEventsEvent;
}({});
function assertIsUserId(value) {
if (typeof value !== "string") throw new Error("Not a string");
if (!value.startsWith("@")) throw new Error("Not a userId");
}
/**
* Tracks sticky events on behalf of one room, and fires an event
* whenever a sticky event is updated or replaced.
*/
export class RoomStickyEventsStore extends TypedEventEmitter {
constructor(...args) {
super(...args);
/**
* Sticky event map is a nested map of:
* eventType -> `content.sticky_key sender` -> StickyMatrixEvent[]
*
* The events are ordered in latest to earliest expiry, so that the first event
* in the array will always be the "current" one.
*/
_defineProperty(this, "stickyEventsMap", new Map());
/**
* These are sticky events that have no sticky key and therefore exist outside the tuple
* system above. They are just held in this Set until they expire.
*/
_defineProperty(this, "unkeyedStickyEvents", new Set());
_defineProperty(this, "stickyEventTimer", void 0);
_defineProperty(this, "nextStickyEventExpiryTs", Number.MAX_SAFE_INTEGER);
/**
* Clean out any expired sticky events.
*/
_defineProperty(this, "cleanExpiredStickyEvents", () => {
const now = Date.now();
const removedEvents = [];
// We will recalculate this as we check all events.
this.nextStickyEventExpiryTs = Number.MAX_SAFE_INTEGER;
for (const [eventType, innerEvents] of this.stickyEventsMap.entries()) {
for (const [innerMapKey, [currentEvent, ...previousEvents]] of innerEvents) {
// we only added items with `sticky` into this map so we can assert non-null here
if (now >= currentEvent.unstableStickyExpiresAt) {
logger.debug("Expiring sticky event", currentEvent.getId());
removedEvents.push(currentEvent);
this.stickyEventsMap.get(eventType).delete(innerMapKey);
} else {
// Ensure we remove any previous events which have now expired, to avoid unbounded memory consumption.
this.stickyEventsMap.get(eventType).set(innerMapKey, [currentEvent, ...previousEvents.filter(e => e.unstableStickyExpiresAt <= now)]);
// If not removing the event, check to see if it's the next lowest expiry.
this.nextStickyEventExpiryTs = Math.min(this.nextStickyEventExpiryTs, currentEvent.unstableStickyExpiresAt);
}
}
// Clean up map after use.
if (this.stickyEventsMap.get(eventType)?.size === 0) {
this.stickyEventsMap.delete(eventType);
}
}
for (const event of this.unkeyedStickyEvents) {
if (now >= event.unstableStickyExpiresAt) {
logger.debug("Expiring sticky event", event.getId());
this.unkeyedStickyEvents.delete(event);
removedEvents.push(event);
} else {
// If not removing the event, check to see if it's the next lowest expiry.
this.nextStickyEventExpiryTs = Math.min(this.nextStickyEventExpiryTs, event.unstableStickyExpiresAt);
}
}
if (removedEvents.length) {
this.emit(RoomStickyEventsEvent.Update, [], [], removedEvents);
}
// Finally, schedule the next run.
this.scheduleStickyTimer();
});
}
/**
* Sort two sticky events by order of expiry. This assumes the sticky events have the same
* `type`, `sticky_key` and `sender`.
* @returns A positive value if event A will expire sooner, or a negative value if event B will expire sooner.
*/
static sortStickyEvent(eventA, eventB) {
// Sticky events with the same key have to use the same expiration duration.
// Hence, comparing via `origin_server_ts` yields the exact same result as comparing their expiration time.
if (eventB.getTs() !== eventA.getTs()) {
return eventB.getTs() - eventA.getTs();
}
if ((eventB.getId() ?? "") > (eventA.getId() ?? "")) {
return 1;
}
// This should fail as we've got corruption in our sticky array.
throw Error("Comparing two sticky events with the same event ID is not allowed.");
}
/**
* Generate the correct key for an event to be found in the inner maps of `stickyEventsMap`.
* @param stickyKey The sticky key of an event.
* @param sender The sender of the event.
*/
static stickyMapKey(stickyKey, sender) {
return `${stickyKey}${sender}`;
}
/**
* Get all sticky events that are currently active.
* @returns An iterable set of events.
* @yields {@link StickyMatrixEvent}
*/
*getStickyEvents() {
yield* this.unkeyedStickyEvents;
for (const innerMap of this.stickyEventsMap.values()) {
// Inner map contains a map of sender+stickykeys => all sticky events
for (const events of innerMap.values()) {
// The first sticky event is the "current" one in the sticky map.
yield events[0];
}
}
}
/**
* Get an active sticky event that match the given `type`, `sender`, and `stickyKey`
* @param type The event `type`.
* @param sender The sender of the sticky event.
* @param stickyKey The sticky key used by the event.
* @returns A matching active sticky event, or undefined.
*/
getKeyedStickyEvent(sender, type, stickyKey) {
assertIsUserId(sender);
return this.stickyEventsMap.get(type)?.get(RoomStickyEventsStore.stickyMapKey(stickyKey, sender))?.[0];
}
/**
* Get active sticky events without a sticky key that match the given `type` and `sender`.
* @param type The event `type`.
* @param sender The sender of the sticky event.
* @returns An array of matching sticky events.
*/
getUnkeyedStickyEvent(sender, type) {
return [...this.unkeyedStickyEvents].filter(ev => ev.getType() === type && ev.getSender() === sender);
}
/**
* Adds a sticky event into the local sticky event map.
*
* NOTE: This will not cause `RoomEvent.StickyEvents` to be emitted.
*
* @throws If the `event` does not contain valid sticky data.
* @param event The MatrixEvent that contains sticky data.
* @returns An object describing whether the event was added to the map,
* and the previous event it may have replaced.
*/
addStickyEvent(event) {
const stickyKey = event.getContent().msc4354_sticky_key;
if (typeof stickyKey !== "string" && stickyKey !== undefined) {
throw new Error(`${event.getId()} is missing msc4354_sticky_key`);
}
// With this we have the guarantee, that all events in stickyEventsMap are correctly formatted
if (event.unstableStickyExpiresAt === undefined) {
throw new Error(`${event.getId()} is missing msc4354_sticky.duration_ms`);
}
const sender = event.getSender();
const type = event.getType();
assertIsUserId(sender);
if (event.unstableStickyExpiresAt <= Date.now()) {
logger.info("ignored sticky event with older expiration time than current time", stickyKey);
return {
added: false
};
}
// While we fully expect the server to always provide the correct value,
// this is just insurance to protect against attacks on our Map.
if (!sender.startsWith("@")) {
throw new Error("Expected sender to start with @");
}
const stickyEvent = event;
if (stickyKey === undefined) {
this.unkeyedStickyEvents.add(stickyEvent);
// Recalculate the next expiry time.
this.nextStickyEventExpiryTs = Math.min(event.unstableStickyExpiresAt, this.nextStickyEventExpiryTs);
this.scheduleStickyTimer();
return {
added: true
};
}
// Why this is safe:
// A type may contain anything but the *sender* is tightly
// constrained so that a key will always end with a @<user_id>
// E.g. Where a malicious event type might be "rtc.member.event@foo:bar" the key becomes:
// "rtc.member.event.@foo:bar@bar:baz"
const innerMapKey = RoomStickyEventsStore.stickyMapKey(stickyKey, sender);
const currentEventSet = [stickyEvent, ...(this.stickyEventsMap.get(type)?.get(innerMapKey) ?? [])].sort(RoomStickyEventsStore.sortStickyEvent);
if (!this.stickyEventsMap.has(type)) {
this.stickyEventsMap.set(type, new Map());
}
this.stickyEventsMap.get(type)?.set(innerMapKey, currentEventSet);
// Recalculate the next expiry time.
this.nextStickyEventExpiryTs = Math.min(stickyEvent.unstableStickyExpiresAt, this.nextStickyEventExpiryTs);
this.scheduleStickyTimer();
return {
added: currentEventSet[0] === stickyEvent,
prevEvent: currentEventSet?.[1]
};
}
/**
* Add a series of sticky events, emitting `RoomEvent.StickyEvents` if any
* changes were made.
* @param events A set of new sticky events.
*/
addStickyEvents(events) {
const added = [];
const updated = [];
for (const event of events) {
try {
const result = this.addStickyEvent(event);
if (result.added) {
if (result.prevEvent) {
// e is validated as a StickyMatrixEvent by virtue of `addStickyEvent` returning added: true.
updated.push({
current: event,
previous: result.prevEvent
});
} else {
added.push(event);
}
}
} catch (ex) {
logger.warn("ignored invalid sticky event", ex);
}
}
if (added.length || updated.length) this.emit(RoomStickyEventsEvent.Update, added, updated, []);
this.scheduleStickyTimer();
}
/**
* Schedule the sticky event expiry timer. The timer will
* run immediately if an event has already expired.
*/
scheduleStickyTimer() {
if (this.stickyEventTimer) {
clearTimeout(this.stickyEventTimer);
this.stickyEventTimer = undefined;
}
if (this.nextStickyEventExpiryTs === Number.MAX_SAFE_INTEGER) {
// We have no events due to expire.
return;
} // otherwise, schedule in the future
this.stickyEventTimer = setTimeout(this.cleanExpiredStickyEvents, this.nextStickyEventExpiryTs - Date.now());
}
/**
* Handles incoming event redactions. Checks the sticky map
* for any active sticky events being redacted.
* @param redactedEvent The MatrixEvent OR event ID of the event being redacted. MAY not be a sticky event.
*/
handleRedaction(redactedEvent) {
// Note, we do not adjust`nextStickyEventExpiryTs` here.
// If this event happens to be the most recent expiring event
// then we may do one extra iteration of cleanExpiredStickyEvents
// but this saves us having to iterate over all events here to calculate
// the next expiry time.
// Note, as soon as we find a positive match on an event in this function
// we can return. There is no need to continue iterating on a positive match
// as an event can only appear in one map.
// Handle unkeyedStickyEvents first since it's *quick*.
const redactEventId = typeof redactedEvent === "string" ? redactedEvent : redactedEvent.getId();
for (const event of this.unkeyedStickyEvents) {
if (event.getId() === redactEventId) {
this.unkeyedStickyEvents.delete(event);
this.emit(RoomStickyEventsEvent.Update, [], [], [event]);
return;
}
}
// Faster method of finding the event since we have the event cached.
if (typeof redactedEvent !== "string" && !redactedEvent.isRedacted()) {
const stickyKey = redactedEvent.getContent().msc4354_sticky_key;
if (typeof stickyKey !== "string" && stickyKey !== undefined) {
return; // Not a sticky event.
}
const eventType = redactedEvent.getType();
const sender = redactedEvent.getSender();
assertIsUserId(sender);
const innerMap = this.stickyEventsMap.get(eventType);
if (!innerMap) {
return;
}
const mapKey = RoomStickyEventsStore.stickyMapKey(stickyKey, sender);
const [currentEvent, ...previousEvents] = innerMap.get(mapKey) ?? [];
if (!currentEvent) {
// No event current in the map so ignore.
return;
}
logger.debug(`Redaction for ${redactEventId} under sticky key ${stickyKey}`);
// Revert to previous state, taking care to skip any other redacted events.
const newEvents = previousEvents.filter(e => !e.isRedacted()).sort(RoomStickyEventsStore.sortStickyEvent);
this.stickyEventsMap.get(eventType)?.set(mapKey, newEvents);
if (newEvents.length) {
this.emit(RoomStickyEventsEvent.Update, [], [{
// This looks confusing. This emits that the newer event
// has been redacted and the previous event has taken it's place.
previous: currentEvent,
current: newEvents[0]
}], []);
} else {
// We did not find a previous event, so just expire.
innerMap.delete(mapKey);
if (innerMap.size === 0) {
this.stickyEventsMap.delete(eventType);
}
this.emit(RoomStickyEventsEvent.Update, [], [], [currentEvent]);
}
return;
}
// We only know the event ID of the redacted event, so we need to
// traverse the map to find our event.
for (const innerMap of this.stickyEventsMap.values()) {
for (const [currentEvent] of innerMap.values()) {
if (currentEvent.getId() !== redactEventId) {
continue;
}
// Found the event.
return this.handleRedaction(currentEvent);
}
}
}
/**
* Clear all events and stop the timer from firing.
*/
clear() {
this.stickyEventsMap.clear();
// Unschedule timer.
this.nextStickyEventExpiryTs = Number.MAX_SAFE_INTEGER;
this.scheduleStickyTimer();
}
}
//# sourceMappingURL=room-sticky-events.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,36 +1,6 @@
/**
* A stripped m.room.member event which contains the key renderable fields from the event,
* sent only in simplified sliding sync (not `/v3/sync`).
* This is very similar to MSC4186Hero from sliding-sync.ts but an internal format with
* camelCase rather than underscores.
*/
export type Hero = {
userId: string;
displayName?: string;
avatarUrl?: string;
/**
* If true, the hero is from an MSC4186 summary, in which case `displayName` and `avatarUrl` will
* have been set by the server if available. If false, the `Hero` has been constructed from a `/v3/sync` response,
* so these fields will always be undefined.
*/
fromMSC4186: boolean;
};
/**
* High level summary information for a room, as returned by `/v3/sync`.
*/
export interface IRoomSummary {
/**
* The room heroes: a selected set of members that can be used when summarising or
* generating a name for a room. List of user IDs.
*/
"m.heroes": string[];
/**
* The number of joined members in the room.
*/
"m.joined_member_count"?: number;
/**
* The number of invited members in the room.
*/
"m.invited_member_count"?: number;
}
interface IInfo {

View File

@@ -1 +1 @@
{"version":3,"file":"room-summary.d.ts","sourceRoot":"","sources":["../../src/models/room-summary.ts"],"names":[],"mappings":"AAgBA;;;;;GAKG;AACH,MAAM,MAAM,IAAI,GAAG;IACf,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB;;;OAGG;IACH,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB;;OAEG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,UAAU,KAAK;IACX,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,qBAAa,WAAW;aAEA,MAAM,EAAE,MAAM;IADlC,YACoB,MAAM,EAAE,MAAM,EAC9B,IAAI,CAAC,EAAE,KAAK,EACZ;CACP"}
{"version":3,"file":"room-summary.d.ts","sourceRoot":"","sources":["../../src/models/room-summary.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,YAAY;IACzB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,UAAU,KAAK;IACX,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,qBAAa,WAAW;aACe,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,KAAK;CAClE"}

View File

@@ -1,3 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.RoomSummary = void 0;
/*
Copyright 2015 - 2021 The Matrix.org Foundation C.I.C.
@@ -14,26 +20,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* A stripped m.room.member event which contains the key renderable fields from the event,
* sent only in simplified sliding sync (not `/v3/sync`).
* This is very similar to MSC4186Hero from sliding-sync.ts but an internal format with
* camelCase rather than underscores.
*/
/**
* High level summary information for a room, as returned by `/v3/sync`.
*/
/**
* Construct a new Room Summary. A summary can be used for display on a recent
* list, without having to load the entire room list into memory.
* @param roomId - Required. The ID of this room.
* @param info - Optional. The summary info. Additional keys are supported.
*/
export class RoomSummary {
class RoomSummary {
constructor(roomId, info) {
this.roomId = roomId;
}
}
exports.RoomSummary = RoomSummary;
//# sourceMappingURL=room-summary.js.map

View File

@@ -1 +1 @@
{"version":3,"file":"room-summary.js","names":[],"sources":["../../src/models/room-summary.ts"],"sourcesContent":["/*\nCopyright 2015 - 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * A stripped m.room.member event which contains the key renderable fields from the event,\n * sent only in simplified sliding sync (not `/v3/sync`).\n * This is very similar to MSC4186Hero from sliding-sync.ts but an internal format with\n * camelCase rather than underscores.\n */\nexport type Hero = {\n userId: string;\n displayName?: string;\n avatarUrl?: string;\n /**\n * If true, the hero is from an MSC4186 summary, in which case `displayName` and `avatarUrl` will\n * have been set by the server if available. If false, the `Hero` has been constructed from a `/v3/sync` response,\n * so these fields will always be undefined.\n */\n fromMSC4186: boolean;\n};\n\n/**\n * High level summary information for a room, as returned by `/v3/sync`.\n */\nexport interface IRoomSummary {\n /**\n * The room heroes: a selected set of members that can be used when summarising or\n * generating a name for a room. List of user IDs.\n */\n \"m.heroes\": string[];\n /**\n * The number of joined members in the room.\n */\n \"m.joined_member_count\"?: number;\n /**\n * The number of invited members in the room.\n */\n \"m.invited_member_count\"?: number;\n}\n\ninterface IInfo {\n /** The title of the room (e.g. `m.room.name`) */\n title: string;\n /** The description of the room (e.g. `m.room.topic`) */\n desc?: string;\n /** The number of joined users. */\n numMembers?: number;\n /** The list of aliases for this room. */\n aliases?: string[];\n /** The timestamp for this room. */\n timestamp?: number;\n}\n\n/**\n * Construct a new Room Summary. A summary can be used for display on a recent\n * list, without having to load the entire room list into memory.\n * @param roomId - Required. The ID of this room.\n * @param info - Optional. The summary info. Additional keys are supported.\n */\nexport class RoomSummary {\n public constructor(\n public readonly roomId: string,\n info?: IInfo,\n ) {}\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAaA;AACA;AACA;;AA8BA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAM,WAAW,CAAC;EACd,WAAW,CACE,MAAc,EAC9B,IAAY,EACd;IAAA,KAFkB,MAAc,GAAd,MAAc;EAE/B;AACP","ignoreList":[]}
{"version":3,"file":"room-summary.js","names":["RoomSummary","constructor","roomId","info","exports"],"sources":["../../src/models/room-summary.ts"],"sourcesContent":["/*\nCopyright 2015 - 2021 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport interface IRoomSummary {\n \"m.heroes\": string[];\n \"m.joined_member_count\"?: number;\n \"m.invited_member_count\"?: number;\n}\n\ninterface IInfo {\n /** The title of the room (e.g. `m.room.name`) */\n title: string;\n /** The description of the room (e.g. `m.room.topic`) */\n desc?: string;\n /** The number of joined users. */\n numMembers?: number;\n /** The list of aliases for this room. */\n aliases?: string[];\n /** The timestamp for this room. */\n timestamp?: number;\n}\n\n/**\n * Construct a new Room Summary. A summary can be used for display on a recent\n * list, without having to load the entire room list into memory.\n * @param roomId - Required. The ID of this room.\n * @param info - Optional. The summary info. Additional keys are supported.\n */\nexport class RoomSummary {\n public constructor(public readonly roomId: string, info?: IInfo) {}\n}\n"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAqBA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMA,WAAW,CAAC;EACdC,WAAWA,CAAiBC,MAAc,EAAEC,IAAY,EAAE;IAAA,KAA9BD,MAAc,GAAdA,MAAc;EAAiB;AACtE;AAACE,OAAA,CAAAJ,WAAA,GAAAA,WAAA"}

View File

@@ -1,24 +1,21 @@
import { EventTimelineSet, type EventTimelineSetHandlerMap, type IAddLiveEventOptions } from "./event-timeline-set.ts";
import { EventTimeline } from "./event-timeline.ts";
import { MatrixEvent, MatrixEventEvent, type MatrixEventHandlerMap } from "./event.ts";
import { EventStatus } from "./event-status.ts";
import { RoomMember } from "./room-member.ts";
import { type IRoomSummary, RoomSummary } from "./room-summary.ts";
import { TypedReEmitter } from "../ReEmitter.ts";
import { EventType, RoomType } from "../@types/event.ts";
import { type MatrixClient, PendingEventOrdering } from "../client.ts";
import { type GuestAccess, type HistoryVisibility, type JoinRule, type ResizeMethod } from "../@types/partials.ts";
import { Filter } from "../filter.ts";
import { type RoomState, RoomStateEvent, type RoomStateEventHandlerMap } from "./room-state.ts";
import { BeaconEvent, type BeaconEventHandlerMap } from "./beacon.ts";
import { Thread, ThreadEvent, type ThreadEventHandlerMap as ThreadHandlerMap } from "./thread.ts";
import { type CachedReceiptStructure, type Receipt } from "../@types/read_receipts.ts";
import { RelationsContainer } from "./relations-container.ts";
import { ReadReceipt } from "./read-receipt.ts";
import { Poll, PollEvent } from "./poll.ts";
import { type Membership } from "../@types/membership.ts";
import { type MSC4186Hero } from "../sliding-sync.ts";
import { RoomStickyEventsStore, RoomStickyEventsEvent, type RoomStickyEventsMap } from "./room-sticky-events.ts";
import { EventTimelineSet, DuplicateStrategy, IAddLiveEventOptions, EventTimelineSetHandlerMap } from "./event-timeline-set";
import { EventTimeline } from "./event-timeline";
import { MatrixEvent, MatrixEventEvent, MatrixEventHandlerMap } from "./event";
import { EventStatus } from "./event-status";
import { RoomMember } from "./room-member";
import { IRoomSummary, RoomSummary } from "./room-summary";
import { TypedReEmitter } from "../ReEmitter";
import { EventType, RoomType } from "../@types/event";
import { MatrixClient, PendingEventOrdering } from "../client";
import { GuestAccess, HistoryVisibility, JoinRule, ResizeMethod } from "../@types/partials";
import { Filter } from "../filter";
import { RoomState, RoomStateEvent, RoomStateEventHandlerMap } from "./room-state";
import { BeaconEvent, BeaconEventHandlerMap } from "./beacon";
import { Thread, ThreadEvent, ThreadEventHandlerMap as ThreadHandlerMap } from "./thread";
import { CachedReceiptStructure, Receipt } from "../@types/read_receipts";
import { RelationsContainer } from "./relations-container";
import { ReadReceipt } from "./read-receipt";
import { Poll, PollEvent } from "./poll";
export declare const KNOWN_SAFE_ROOM_VERSION = "10";
interface IOpts {
/**
@@ -65,10 +62,9 @@ export declare enum RoomEvent {
OldStateUpdated = "Room.OldStateUpdated",
CurrentStateUpdated = "Room.CurrentStateUpdated",
HistoryImportedWithinTimeline = "Room.historyImportedWithinTimeline",
UnreadNotifications = "Room.UnreadNotifications",
Summary = "Room.Summary"
UnreadNotifications = "Room.UnreadNotifications"
}
export type RoomEmittedEvents = RoomEvent | RoomStateEvent.Events | RoomStateEvent.Members | RoomStateEvent.NewMember | RoomStateEvent.Update | RoomStateEvent.Marker | RoomStickyEventsEvent.Update | ThreadEvent.New | ThreadEvent.Update | ThreadEvent.NewReply | ThreadEvent.Delete | MatrixEventEvent.BeforeRedaction | BeaconEvent.New | BeaconEvent.Update | BeaconEvent.Destroy | BeaconEvent.LivenessChange | PollEvent.New;
export type RoomEmittedEvents = RoomEvent | RoomStateEvent.Events | RoomStateEvent.Members | RoomStateEvent.NewMember | RoomStateEvent.Update | RoomStateEvent.Marker | ThreadEvent.New | ThreadEvent.Update | ThreadEvent.NewReply | ThreadEvent.Delete | MatrixEventEvent.BeforeRedaction | BeaconEvent.New | BeaconEvent.Update | BeaconEvent.Destroy | BeaconEvent.LivenessChange | PollEvent.New;
export type RoomEventHandlerMap = {
/**
* Fires when the logged in user's membership in the room is updated.
@@ -77,7 +73,7 @@ export type RoomEventHandlerMap = {
* @param membership - The new membership value
* @param prevMembership - The previous membership value
*/
[RoomEvent.MyMembership]: (room: Room, membership: Membership, prevMembership?: Membership) => void;
[RoomEvent.MyMembership]: (room: Room, membership: string, prevMembership?: string) => void;
/**
* Fires whenever a room's tags are updated.
* @param event - The tags event
@@ -106,7 +102,7 @@ export type RoomEventHandlerMap = {
* });
* ```
*/
[RoomEvent.AccountData]: (event: MatrixEvent, room: Room, prevEvent?: MatrixEvent) => void;
[RoomEvent.AccountData]: (event: MatrixEvent, room: Room, lastEvent?: MatrixEvent) => void;
/**
* Fires whenever a receipt is received for a room
* @param event - The receipt event
@@ -138,9 +134,8 @@ export type RoomEventHandlerMap = {
*
* @param event - The matrix redaction event
* @param room - The room containing the redacted event
* @param threadId - The thread containing the redacted event (before it was redacted)
*/
[RoomEvent.Redaction]: (event: MatrixEvent, room: Room, threadId?: string) => void;
[RoomEvent.Redaction]: (event: MatrixEvent, room: Room) => void;
/**
* Fires when an event that was previously redacted isn't anymore.
* This happens when the redaction couldn't be sent and
@@ -189,21 +184,13 @@ export type RoomEventHandlerMap = {
[RoomEvent.HistoryImportedWithinTimeline]: (markerEvent: MatrixEvent, room: Room) => void;
[RoomEvent.UnreadNotifications]: (unreadNotifications?: NotificationCount, threadId?: string) => void;
[RoomEvent.TimelineRefresh]: (room: Room, eventTimelineSet: EventTimelineSet) => void;
/**
* Fires when a new room summary is returned by `/sync`.
*
* See https://spec.matrix.org/v1.8/client-server-api/#_matrixclientv3sync_roomsummary
* for full details
* @param summary - the room summary object
*/
[RoomEvent.Summary]: (summary: IRoomSummary) => void;
[ThreadEvent.New]: (thread: Thread, toStartOfTimeline: boolean) => void;
/**
* Fires when a new poll instance is added to the room state
* @param poll - the new poll
*/
[PollEvent.New]: (poll: Poll) => void;
} & Pick<ThreadHandlerMap, ThreadEvent.Update | ThreadEvent.NewReply | ThreadEvent.Delete> & EventTimelineSetHandlerMap & Pick<MatrixEventHandlerMap, MatrixEventEvent.BeforeRedaction> & Pick<RoomStickyEventsMap, RoomStickyEventsEvent.Update> & Pick<RoomStateEventHandlerMap, RoomStateEvent.Events | RoomStateEvent.Members | RoomStateEvent.NewMember | RoomStateEvent.Update | RoomStateEvent.Marker | BeaconEvent.New> & Pick<BeaconEventHandlerMap, BeaconEvent.Update | BeaconEvent.Destroy | BeaconEvent.LivenessChange>;
} & Pick<ThreadHandlerMap, ThreadEvent.Update | ThreadEvent.NewReply | ThreadEvent.Delete> & EventTimelineSetHandlerMap & Pick<MatrixEventHandlerMap, MatrixEventEvent.BeforeRedaction> & Pick<RoomStateEventHandlerMap, RoomStateEvent.Events | RoomStateEvent.Members | RoomStateEvent.NewMember | RoomStateEvent.Update | RoomStateEvent.Marker | BeaconEvent.New> & Pick<BeaconEventHandlerMap, BeaconEvent.Update | BeaconEvent.Destroy | BeaconEvent.LivenessChange>;
export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandlerMap> {
readonly roomId: string;
readonly client: MatrixClient;
@@ -212,7 +199,6 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
readonly reEmitter: TypedReEmitter<RoomEmittedEvents, RoomEventHandlerMap>;
private txnToEvent;
private notificationCounts;
private bumpStamp;
private readonly threadNotifications;
readonly cachedThreadReadReceipts: Map<string, CachedReceiptStructure[]>;
private oldestThreadedReceiptTs;
@@ -234,17 +220,9 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
private readonly pendingEventList?;
private blacklistUnverifiedDevices?;
private selfMembership?;
/**
* A `Hero` is a stripped `m.room.member` event which contains the important renderable fields from the event.
*
* It is used in MSC4186 (Simplified Sliding Sync) as a replacement for the old `summary` field.
*
* When we are doing old-style (`/v3/sync`) sync, we simulate the SSS behaviour by constructing
* a `Hero` object based on the user id we get from the summary. Obviously, in that case,
* the `Hero` will lack a `displayName` or `avatarUrl`.
*/
private heroes;
private summaryHeroes;
private getTypeWarning;
private getVersionWarning;
private membersPromise?;
/**
* The human-readable display name for this room.
@@ -268,6 +246,13 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* The room summary.
*/
summary: RoomSummary | null;
/**
* The live event timeline for this room, with the oldest event at index 0.
*
* @deprecated Present for backwards compatibility.
* Use getLiveTimeline().getEvents() instead
*/
timeline: MatrixEvent[];
/**
* oldState The state of the room at the time of the oldest event in the live timeline.
*
@@ -288,6 +273,11 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* This is not a comprehensive list of the threads that exist in this room
*/
private threads;
/**
* @deprecated This value is unreliable. It may not contain the last thread.
* Use {@link Room.getLastThread} instead.
*/
lastThread?: Thread;
/**
* A mapping of eventId to all visibility changes to apply
* to the event, by chronological order, as per
@@ -305,16 +295,6 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* @experimental
*/
private visibilityEvents;
/**
* The latest receipts (synthetic and real) for each user in each thread
* (and unthreaded).
*/
private roomReceipts;
/**
* Stores and tracks sticky events
*/
private stickyEvents;
private readonly retention?;
/**
* Construct a new Room.
*
@@ -366,9 +346,16 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
getCreator(): string | null;
/**
* Gets the version of the room
* @returns The version of the room
* @returns The version of the room, or null if it could not be determined
*/
getVersion(): string;
/**
* Determines whether this room needs to be upgraded to a new version
* @returns What version the room should be upgraded to, or null if
* the room does not require upgrading at this time.
* @deprecated Use #getRecommendedVersion() instead
*/
shouldUpgradeToVersion(): string | null;
/**
* Determines the recommended room version for the room. This returns an
* object with 3 properties: `version` as the new version the
@@ -422,13 +409,6 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* @returns live timeline
*/
getLiveTimeline(): EventTimeline;
/**
* The live event timeline for this room, with the oldest event at index 0.
*
* @deprecated Present for backwards compatibility.
* Use getLiveTimeline().getEvents() instead
*/
get timeline(): MatrixEvent[];
/**
* Get the timestamp of the last message in the room
*
@@ -457,9 +437,9 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
*/
getLastThread(): Thread | undefined;
/**
* @returns the membership type (join | leave | invite | knock) for the logged in user
* @returns the membership type (join | leave | invite) for the logged in user
*/
getMyMembership(): Membership;
getMyMembership(): string;
/**
* If this room is a DM we're invited to,
* try to find out who invited us
@@ -471,21 +451,12 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* @returns user id of the other member (could be syncing user)
*/
guessDMUserId(): string;
/**
* Gets the "functional members" in this room.
*
* Returns the list of userIDs from the `io.element.functional_members` event. Does not consider the
* current membership states of those users.
*
* @see https://github.com/element-hq/element-meta/blob/develop/spec/functional_members.md.
*/
private getFunctionalMembers;
getAvatarFallbackMember(): RoomMember | undefined;
/**
* Sets the membership this room was received as during sync
* @param membership - join | leave | invite
*/
updateMyMembership(membership: Membership): void;
updateMyMembership(membership: string): void;
private loadMembersFromServer;
private loadMembers;
/**
@@ -545,8 +516,16 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* @internal
*/
private fixUpLegacyTimelineFields;
private onReceipt;
private clearNotificationsOnReceipt;
/**
* Returns whether there are any devices in the room that are unverified
*
* Note: Callers should first check if crypto is enabled on this device. If it is
* disabled, then we aren't tracking room devices at all, so we can't answer this, and an
* error will be thrown.
*
* @returns the result
*/
hasUnverifiedDevices(): Promise<boolean>;
/**
* Return the timeline sets for this room.
* @returns array of timeline sets for this room
@@ -633,32 +612,9 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
*/
get threadsAggregateNotificationType(): NotificationCountType | null;
/**
* Resets the total thread notifications for all threads in this room to zero,
* excluding any threads whose IDs are given in `exceptThreadIds`.
*
* If the room is not encrypted, also resets the highlight notification count to zero
* for the same set of threads.
*
* This is intended for use from the sync code since we calculate highlight notification
* counts locally from decrypted messages. We want to partially trust the total from the
* server such that we clear notifications when read receipts arrive. The weird name is
* intended to reflect this. You probably do not want to use this.
*
* @param exceptThreadIds - The thread IDs to exclude from the reset.
* Resets the thread notifications for this room
*/
resetThreadUnreadNotificationCountFromSync(exceptThreadIds?: string[]): void;
/**
* Set the bump stamp for this room. This can be used for sorting rooms when the timeline
* entries are unknown. Used in MSC4186: Simplified Sliding Sync.
* @param bumpStamp The bump_stamp value from the server
*/
setBumpStamp(bumpStamp: number): void;
/**
* Get the bump stamp for this room. This can be used for sorting rooms when the timeline
* entries are unknown. Used in MSC4186: Simplified Sliding Sync.
* @returns The bump stamp for the room, if it exists.
*/
getBumpStamp(): number | undefined;
resetThreadUnreadNotificationCount(notificationsToKeep?: string[]): void;
/**
* Set one of the notification counts for this room
* @param type - The type of notification count to set.
@@ -666,20 +622,7 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
*/
setUnreadNotificationCount(type: NotificationCountType, count: number): void;
setUnread(type: NotificationCountType, count: number): void;
/**
* Takes a legacy room summary (/v3/sync as opposed to MSC4186) and updates the room with it.
*
* @param summary - The room summary to update the room with
*/
setSummary(summary: IRoomSummary): void;
/**
* Takes information from the MSC4186 room summary and updates the room with it.
*
* @param heroes - The room's hero members
* @param joinedCount - The number of joined members
* @param invitedCount - The number of invited members
*/
setMSC4186SummaryData(heroes: MSC4186Hero[] | undefined, joinedCount: number | undefined, invitedCount: number | undefined): void;
/**
* Whether to send encrypted messages to devices within this room.
* @param value - true to blacklist unverified devices, null
@@ -702,14 +645,9 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* "crop" or "scale".
* @param allowDefault - True to allow an identicon for this room if an
* avatar URL wasn't explicitly set. Default: true. (Deprecated)
* @param useAuthentication - (optional) If true, the caller supports authenticated
* media and wants an authentication-required URL. Note that server support for
* authenticated media will not be checked - it is the caller's responsibility
* to do so before calling this function. Note also that useAuthentication
* implies allowRedirects. Defaults to false (unauthenticated endpoints).
* @returns the avatar URL or null.
*/
getAvatarUrl(baseUrl: string, width: number, height: number, resizeMethod: ResizeMethod, allowDefault?: boolean, useAuthentication?: boolean): string | null;
getAvatarUrl(baseUrl: string, width: number, height: number, resizeMethod: ResizeMethod, allowDefault?: boolean): string | null;
/**
* Get the mxc avatar url for the room, if one was set.
* @returns the mxc avatar url or falsy
@@ -746,7 +684,7 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* @remarks
* Fires {@link RoomEvent.Timeline}
*/
addEventsToTimeline(events: MatrixEvent[], toStartOfTimeline: boolean, addToState: boolean, timeline: EventTimeline, paginationToken?: string): void;
addEventsToTimeline(events: MatrixEvent[], toStartOfTimeline: boolean, timeline: EventTimeline, paginationToken?: string): void;
/**
* Get the instance of the thread associated with the current event
* @param eventId - the ID of the current event
@@ -797,7 +735,7 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* @param membership - The membership state.
* @returns A list of members with the given membership state.
*/
getMembersWithMembership(membership: Membership): RoomMember[];
getMembersWithMembership(membership: string): RoomMember[];
/**
* Get a list of members we should be encrypting for in this room
* @returns A list of members who
@@ -823,7 +761,7 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* @param membership - The membership e.g. `'join'`
* @returns True if this user_id has the given membership state.
*/
hasMembershipState(userId: string, membership: Membership): boolean;
hasMembershipState(userId: string, membership: string): boolean;
/**
* Add a timelineSet for this room with the given filter
* @param filter - The filter to be applied to this timelineSet
@@ -845,7 +783,7 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
*/
fetchRoomThreads(): Promise<void>;
/**
* Process a list of poll events.
* Calls {@link processPollEvent} for a list of events.
*
* @param events - List of events
*/
@@ -866,7 +804,6 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* @internal
*/
private fetchRoomThreadList;
private onThreadUpdate;
private onThreadReply;
private onThreadDelete;
/**
@@ -882,12 +819,6 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* Relations (other than m.thread), redactions, replies to a thread root live only in the main timeline
* Relations, redactions, replies where the parent cannot be found live in no timelines but should be aggregated regardless.
* Otherwise, the event lives in the main timeline only.
*
* Note: when a redaction is applied, the redacted event, events relating
* to it, and the redaction event itself, will all move to the main thread.
* This method classifies them as inside the thread of the redacted event.
* They are moved later as part of makeRedacted.
* This will change if MSC3389 is merged.
*/
eventShouldLiveIn(event: MatrixEvent, events?: MatrixEvent[], roots?: Set<string>): {
shouldLiveInRoom: boolean;
@@ -903,18 +834,7 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
private updateThreadRootEvents;
private updateThreadRootEvent;
createThread(threadId: string, rootEvent: MatrixEvent | undefined, events: MatrixEvent[] | undefined, toStartOfTimeline: boolean): Thread;
/**
* Applies an event as a redaction of another event, regardless of whether the redacting
* event is actually a redaction.
*
* Callers should use tryApplyRedaction instead.
*
* @param redactionEvent The event which redacts an event.
* @param redactedEvent The event being redacted.
* @private
*/
private applyEventAsRedaction;
readonly tryApplyRedaction: (event: MatrixEvent) => void;
private applyRedaction;
private processLiveEvent;
/**
* Add an event to the end of this room's live timelines. Will fire
@@ -1005,7 +925,6 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
*/
updatePendingEvent(event: MatrixEvent, newStatus: EventStatus, newEventId?: string): void;
private revertRedactionLocalEcho;
private assertTimelineSetsAreLive;
/**
* Add some events to this room. This can include state events, message
* events and typing notifications. These events are treated as "live" so
@@ -1015,7 +934,11 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* @param addLiveEventOptions - addLiveEvent options
* @throws If `duplicateStrategy` is not falsey, 'replace' or 'ignore'.
*/
addLiveEvents(events: MatrixEvent[], addLiveEventOptions: IAddLiveEventOptions): Promise<void>;
addLiveEvents(events: MatrixEvent[], addLiveEventOptions?: IAddLiveEventOptions): Promise<void>;
/**
* @deprecated In favor of the overload with `IAddLiveEventOptions`
*/
addLiveEvents(events: MatrixEvent[], duplicateStrategy?: DuplicateStrategy, fromCache?: boolean): Promise<void>;
partitionThreadedEvents(events: MatrixEvent[]): [timelineEvents: MatrixEvent[], threadedEvents: MatrixEvent[], unknownRelations: MatrixEvent[]];
/**
* Given some events, find the IDs of all the thread roots that are referred to by them.
@@ -1070,32 +993,6 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* @returns the account_data event in question
*/
getAccountData(type: EventType | string): MatrixEvent | undefined;
/**
* Get an iterator of currently active sticky events.
*/
_unstable_getStickyEvents(): ReturnType<RoomStickyEventsStore["getStickyEvents"]>;
/**
* Get a sticky event that match the given `type`, `sender`, and `stickyKey`
* @param type The event `type`.
* @param sender The sender of the sticky event.
* @param stickyKey The sticky key used by the event.
* @returns A matching active sticky event, or undefined.
*/
_unstable_getKeyedStickyEvent(sender: string, type: string, stickyKey: string): ReturnType<RoomStickyEventsStore["getKeyedStickyEvent"]>;
/**
* Get active sticky events without a sticky key that match the given `type` and `sender`.
* @param type The event `type`.
* @param sender The sender of the sticky event.
* @returns An array of matching sticky events.
*/
_unstable_getUnkeyedStickyEvent(sender: string, type: string): ReturnType<RoomStickyEventsStore["getUnkeyedStickyEvent"]>;
/**
* Add a series of sticky events, emitting `RoomEvent.StickyEvents` if any
* changes were made.
* @param events A set of new sticky events.
* @internal
*/
_unstable_addStickyEvents(events: MatrixEvent[]): ReturnType<RoomStickyEventsStore["addStickyEvents"]>;
/**
* Returns whether the syncing user has permission to send a message in the room
* @returns true if the user should be permitted to send
@@ -1203,16 +1100,6 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* @returns the timestamp of the oldest threaded receipt
*/
getOldestThreadedReceiptTs(): number;
/**
* Determines if the given user has read a particular event ID with the known
* history of the room. This is not a definitive check as it relies only on
* what is available to the room at the time of execution.
*
* @param userId - The user ID to check the read state of.
* @param eventId - The event ID to check if the user read.
* @returns true if the user has read the event, false otherwise.
*/
hasUserReadEvent(userId: string, eventId: string): boolean;
/**
* Returns the most recent unthreaded receipt for a given user
* @param userId - the MxID of the User
@@ -1233,34 +1120,6 @@ export declare class Room extends ReadReceipt<RoomEmittedEvents, RoomEventHandle
* contexts
*/
fixupNotifications(userId: string): void;
/**
* Determine the order of two events in this room.
*
* In principle this should use the same order as the server, but in practice
* this is difficult for events that were not received over the Sync API. See
* MSC4033 for details.
*
* This implementation leans on the order of events within their timelines, and
* falls back to comparing event timestamps when they are in different
* timelines.
*
* See https://github.com/matrix-org/matrix-js-sdk/issues/3325 for where we are
* tracking the work to fix this.
*
* @param leftEventId - the id of the first event
* @param rightEventId - the id of the second event
* @returns -1 if left \< right, 1 if left \> right, 0 if left == right, null if
* we can't tell (because we can't find the events).
*/
compareEventOrdering(leftEventId: string, rightEventId: string): number | null;
/**
* Return true if this room has an `m.room.encryption` state event.
*
* If this returns `true`, events sent to this room should be encrypted (and `MatrixClient.sendEvent` and friends
* will encrypt outgoing events).
*/
hasEncryptionStateEvent(): boolean;
}
export declare enum RoomNameType {
EmptyRoom = 0,

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More