feat: complete Ancestor quote bot with production-ready Docker support
This commit is contained in:
548
node_modules/matrix-js-sdk/lib/models/thread.js
generated
vendored
548
node_modules/matrix-js-sdk/lib/models/thread.js
generated
vendored
@@ -1,34 +1,42 @@
|
||||
import _objectSpread from "@babel/runtime/helpers/objectSpread2";
|
||||
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
||||
/*
|
||||
Copyright 2021 - 2023 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 { PendingEventOrdering } from "../client.js";
|
||||
import { TypedReEmitter } from "../ReEmitter.js";
|
||||
import { RelationType } from "../@types/event.js";
|
||||
import { MatrixEvent, MatrixEventEvent } from "./event.js";
|
||||
import { Direction, EventTimeline } from "./event-timeline.js";
|
||||
import { EventTimelineSet } from "./event-timeline-set.js";
|
||||
import { RoomEvent } from "./room.js";
|
||||
import { ServerControlledNamespacedValue } from "../NamespacedValue.js";
|
||||
import { logger } from "../logger.js";
|
||||
import { ReadReceipt } from "./read-receipt.js";
|
||||
import { ReceiptType } from "../@types/read_receipts.js";
|
||||
import { Feature, ServerSupport } from "../feature.js";
|
||||
export let ThreadEvent = /*#__PURE__*/function (ThreadEvent) {
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ThreadFilterType = exports.ThreadEvent = exports.Thread = exports.THREAD_RELATION_TYPE = exports.FeatureSupport = exports.FILTER_RELATED_BY_SENDERS = exports.FILTER_RELATED_BY_REL_TYPES = void 0;
|
||||
exports.determineFeatureSupport = determineFeatureSupport;
|
||||
exports.threadFilterTypeToFilter = threadFilterTypeToFilter;
|
||||
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
||||
var _client = require("../client");
|
||||
var _ReEmitter = require("../ReEmitter");
|
||||
var _event = require("../@types/event");
|
||||
var _event2 = require("./event");
|
||||
var _eventTimeline = require("./event-timeline");
|
||||
var _eventTimelineSet = require("./event-timeline-set");
|
||||
var _room = require("./room");
|
||||
var _NamespacedValue = require("../NamespacedValue");
|
||||
var _logger = require("../logger");
|
||||
var _readReceipt = require("./read-receipt");
|
||||
var _read_receipts = require("../@types/read_receipts");
|
||||
var _feature = require("../feature");
|
||||
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 - 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.
|
||||
*/
|
||||
let ThreadEvent = /*#__PURE__*/function (ThreadEvent) {
|
||||
ThreadEvent["New"] = "Thread.new";
|
||||
ThreadEvent["Update"] = "Thread.update";
|
||||
ThreadEvent["NewReply"] = "Thread.newReply";
|
||||
@@ -36,13 +44,18 @@ export let ThreadEvent = /*#__PURE__*/function (ThreadEvent) {
|
||||
ThreadEvent["Delete"] = "Thread.delete";
|
||||
return ThreadEvent;
|
||||
}({});
|
||||
export let FeatureSupport = /*#__PURE__*/function (FeatureSupport) {
|
||||
/**
|
||||
* @deprecated please use ThreadEventHandlerMap instead
|
||||
*/
|
||||
exports.ThreadEvent = ThreadEvent;
|
||||
let FeatureSupport = /*#__PURE__*/function (FeatureSupport) {
|
||||
FeatureSupport[FeatureSupport["None"] = 0] = "None";
|
||||
FeatureSupport[FeatureSupport["Experimental"] = 1] = "Experimental";
|
||||
FeatureSupport[FeatureSupport["Stable"] = 2] = "Stable";
|
||||
return FeatureSupport;
|
||||
}({});
|
||||
export function determineFeatureSupport(stable, unstable) {
|
||||
exports.FeatureSupport = FeatureSupport;
|
||||
function determineFeatureSupport(stable, unstable) {
|
||||
if (stable) {
|
||||
return FeatureSupport.Stable;
|
||||
} else if (unstable) {
|
||||
@@ -51,80 +64,40 @@ export function determineFeatureSupport(stable, unstable) {
|
||||
return FeatureSupport.None;
|
||||
}
|
||||
}
|
||||
export class Thread extends ReadReceipt {
|
||||
class Thread extends _readReceipt.ReadReceipt {
|
||||
constructor(id, rootEvent, opts) {
|
||||
var _opts$pendingEventOrd;
|
||||
super();
|
||||
|
||||
// each Event in the thread adds a reemitter, so we could hit the listener limit.
|
||||
this.id = id;
|
||||
this.rootEvent = rootEvent;
|
||||
/**
|
||||
* A reference to all the events ID at the bottom of the threads
|
||||
*/
|
||||
_defineProperty(this, "timelineSet", void 0);
|
||||
_defineProperty(this, "_currentUserParticipated", false);
|
||||
_defineProperty(this, "reEmitter", void 0);
|
||||
/**
|
||||
* The last event in this thread, if we don't yet have this in the timeline.
|
||||
*
|
||||
* When we run {@link processRootEvent} (which I think happens during the
|
||||
* setting-up of the thread), we set this to the event pointed to by the
|
||||
* server in `latest_event` [1] that came through with the thread root.
|
||||
*
|
||||
* [1]: https://spec.matrix.org/v1.8/client-server-api/#server-side-aggregation-of-mthread-relationships
|
||||
*
|
||||
* Later, when we have populated the timeline, this is set to undefined, so
|
||||
* that methods like {@link replyToEvent} fall through to use lastReply,
|
||||
* which looks in the timeline for the latest event that is a "thread reply"
|
||||
* i.e. directly refers to the thread root with an m.thread relation.
|
||||
*
|
||||
* So it looks like this is only really relevant when initialEventsFetched
|
||||
* is false, because as soon as the initial events have been fetched, we
|
||||
* should have a proper chunk of timeline from the pagination fetch.
|
||||
*
|
||||
* If all replies in this thread are redacted, this is set to the root
|
||||
* event. I'm not clear what the meaning of this is, since usually after the
|
||||
* initial events have been fetched, lastEvent should be undefined.
|
||||
* In fact, the whole usage inside onRedaction looks suspect - it may be
|
||||
* that we were thinking lastEvent always refers to the actual last event,
|
||||
* but it only does so before initialEventsFetched becomes true.
|
||||
*
|
||||
* The usage of lastEvent inside {@link onEcho} looks suspicious, since I'd
|
||||
* think we probably mean {@link replyToEvent} there - we are trying not to
|
||||
* echo a duplicate event, and we probably want that behaviour even after
|
||||
* initialEventsFetched has become true.
|
||||
*
|
||||
* -- andyb
|
||||
*/
|
||||
_defineProperty(this, "lastEvent", void 0);
|
||||
_defineProperty(this, "replyCount", 0);
|
||||
_defineProperty(this, "lastPendingEvent", void 0);
|
||||
_defineProperty(this, "pendingReplyCount", 0);
|
||||
_defineProperty(this, "room", void 0);
|
||||
_defineProperty(this, "client", void 0);
|
||||
_defineProperty(this, "pendingEventOrdering", void 0);
|
||||
_defineProperty(this, "processRootEventPromise", void 0);
|
||||
/**
|
||||
* Whether or not we need to fetch the initial set of events for the thread. We can
|
||||
* only do this if the server has support for it, so if it doesn't we just pretend
|
||||
* that we've already fetched them.
|
||||
*/
|
||||
_defineProperty(this, "initialEventsFetched", !Thread.hasServerSideSupport);
|
||||
_defineProperty(this, "initalEventFetchProm", void 0);
|
||||
_defineProperty(this, "preInitEventTargets", new Map());
|
||||
_defineProperty(this, "preInitObserverState", {
|
||||
active: true
|
||||
});
|
||||
(0, _defineProperty2.default)(this, "timelineSet", void 0);
|
||||
(0, _defineProperty2.default)(this, "timeline", []);
|
||||
(0, _defineProperty2.default)(this, "_currentUserParticipated", false);
|
||||
(0, _defineProperty2.default)(this, "reEmitter", void 0);
|
||||
(0, _defineProperty2.default)(this, "lastEvent", void 0);
|
||||
(0, _defineProperty2.default)(this, "replyCount", 0);
|
||||
(0, _defineProperty2.default)(this, "lastPendingEvent", void 0);
|
||||
(0, _defineProperty2.default)(this, "pendingReplyCount", 0);
|
||||
(0, _defineProperty2.default)(this, "room", void 0);
|
||||
(0, _defineProperty2.default)(this, "client", void 0);
|
||||
(0, _defineProperty2.default)(this, "pendingEventOrdering", void 0);
|
||||
(0, _defineProperty2.default)(this, "processRootEventPromise", void 0);
|
||||
(0, _defineProperty2.default)(this, "initialEventsFetched", !Thread.hasServerSideSupport);
|
||||
/**
|
||||
* An array of events to add to the timeline once the thread has been initialised
|
||||
* with server suppport.
|
||||
*/
|
||||
_defineProperty(this, "replayEvents", []);
|
||||
_defineProperty(this, "onTimelineReset", async () => {
|
||||
(0, _defineProperty2.default)(this, "replayEvents", []);
|
||||
(0, _defineProperty2.default)(this, "onTimelineReset", async () => {
|
||||
// We hit a gappy sync, ask the server for an update
|
||||
await this.processRootEventPromise;
|
||||
this.processRootEventPromise = undefined;
|
||||
});
|
||||
_defineProperty(this, "onBeforeRedaction", (event, redaction) => {
|
||||
if (event?.isRelation(THREAD_RELATION_TYPE.name) && this.room.eventShouldLiveIn(event).threadId === this.id && event.getId() !== this.id &&
|
||||
(0, _defineProperty2.default)(this, "onBeforeRedaction", (event, redaction) => {
|
||||
if (event !== null && event !== void 0 && event.isRelation(THREAD_RELATION_TYPE.name) && this.room.eventShouldLiveIn(event).threadId === this.id && event.getId() !== this.id &&
|
||||
// the root event isn't counted in the length so ignore this redaction
|
||||
!redaction.status // only respect it when it succeeds
|
||||
) {
|
||||
@@ -133,8 +106,8 @@ export class Thread extends ReadReceipt {
|
||||
this.emit(ThreadEvent.Update, this);
|
||||
}
|
||||
});
|
||||
_defineProperty(this, "onRedaction", async (event, room, threadRootId) => {
|
||||
if (threadRootId !== this.id) return; // ignore redactions for other timelines
|
||||
(0, _defineProperty2.default)(this, "onRedaction", async event => {
|
||||
if (event.threadRootId !== this.id) return; // ignore redactions for other timelines
|
||||
if (this.replyCount <= 0) {
|
||||
for (const threadEvent of this.timeline) {
|
||||
this.clearEventMetadata(threadEvent);
|
||||
@@ -143,7 +116,8 @@ export class Thread extends ReadReceipt {
|
||||
this._currentUserParticipated = false;
|
||||
this.emit(ThreadEvent.Delete, this);
|
||||
} else {
|
||||
if (this.lastEvent?.getId() === event.getAssociatedId()) {
|
||||
var _this$lastEvent;
|
||||
if (((_this$lastEvent = this.lastEvent) === null || _this$lastEvent === void 0 ? void 0 : _this$lastEvent.getId()) === event.getAssociatedId()) {
|
||||
// XXX: If our last event got redacted we query the server for the last event once again
|
||||
await this.processRootEventPromise;
|
||||
this.processRootEventPromise = undefined;
|
||||
@@ -151,23 +125,23 @@ export class Thread extends ReadReceipt {
|
||||
await this.updateThreadMetadata();
|
||||
}
|
||||
});
|
||||
_defineProperty(this, "onTimelineEvent", (event, room, toStartOfTimeline) => {
|
||||
(0, _defineProperty2.default)(this, "onTimelineEvent", (event, room, toStartOfTimeline) => {
|
||||
// Add a synthesized receipt when paginating forward in the timeline
|
||||
if (!toStartOfTimeline) {
|
||||
const sender = event.getSender();
|
||||
if (sender && room && this.shouldSendLocalEchoReceipt(sender, event)) {
|
||||
room.addLocalEchoReceipt(sender, event, ReceiptType.Read);
|
||||
room.addLocalEchoReceipt(sender, event, _read_receipts.ReceiptType.Read);
|
||||
}
|
||||
if (event.getId() !== this.id && event.isRelation(THREAD_RELATION_TYPE.name)) {
|
||||
this.replyCount++;
|
||||
}
|
||||
}
|
||||
this.onEcho(event, toStartOfTimeline ?? false);
|
||||
this.onEcho(event, toStartOfTimeline !== null && toStartOfTimeline !== void 0 ? toStartOfTimeline : false);
|
||||
});
|
||||
_defineProperty(this, "onLocalEcho", event => {
|
||||
(0, _defineProperty2.default)(this, "onLocalEcho", event => {
|
||||
this.onEcho(event, false);
|
||||
});
|
||||
_defineProperty(this, "onEcho", async (event, toStartOfTimeline) => {
|
||||
(0, _defineProperty2.default)(this, "onEcho", async (event, toStartOfTimeline) => {
|
||||
if (event.threadRootId !== this.id) return; // ignore echoes for other timelines
|
||||
if (this.lastEvent === event) return; // ignore duplicate events
|
||||
await this.updateThreadMetadata();
|
||||
@@ -177,31 +151,25 @@ export class Thread extends ReadReceipt {
|
||||
this.lastEvent = undefined;
|
||||
this.emit(ThreadEvent.NewReply, this, event);
|
||||
});
|
||||
this.id = id;
|
||||
this.rootEvent = rootEvent;
|
||||
this.setMaxListeners(1000);
|
||||
if (!opts?.room) {
|
||||
if (!(opts !== null && opts !== void 0 && opts.room)) {
|
||||
// Logging/debugging for https://github.com/vector-im/element-web/issues/22141
|
||||
// Hope is that we end up with a more obvious stack trace.
|
||||
throw new Error("element-web#22141: A thread requires a room in order to function");
|
||||
}
|
||||
this.room = opts.room;
|
||||
this.client = opts.client;
|
||||
this.pendingEventOrdering = opts.pendingEventOrdering ?? PendingEventOrdering.Chronological;
|
||||
this.timelineSet = new EventTimelineSet(this.room, {
|
||||
this.pendingEventOrdering = (_opts$pendingEventOrd = opts.pendingEventOrdering) !== null && _opts$pendingEventOrd !== void 0 ? _opts$pendingEventOrd : _client.PendingEventOrdering.Chronological;
|
||||
this.timelineSet = new _eventTimelineSet.EventTimelineSet(this.room, {
|
||||
timelineSupport: true,
|
||||
pendingEvents: true
|
||||
}, this.client, this);
|
||||
this.reEmitter = new TypedReEmitter(this);
|
||||
this.reEmitter.reEmit(this.timelineSet, [RoomEvent.Timeline, RoomEvent.TimelineReset]);
|
||||
this.once(ThreadEvent.Delete, () => {
|
||||
this.preInitObserverState.active = false;
|
||||
});
|
||||
this.room.on(MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction);
|
||||
this.room.on(RoomEvent.Redaction, this.onRedaction);
|
||||
this.room.on(RoomEvent.LocalEchoUpdated, this.onLocalEcho);
|
||||
this.room.on(RoomEvent.TimelineReset, this.onTimelineReset);
|
||||
this.timelineSet.on(RoomEvent.Timeline, this.onTimelineEvent);
|
||||
this.reEmitter = new _ReEmitter.TypedReEmitter(this);
|
||||
this.reEmitter.reEmit(this.timelineSet, [_room.RoomEvent.Timeline, _room.RoomEvent.TimelineReset]);
|
||||
this.room.on(_event2.MatrixEventEvent.BeforeRedaction, this.onBeforeRedaction);
|
||||
this.room.on(_room.RoomEvent.Redaction, this.onRedaction);
|
||||
this.room.on(_room.RoomEvent.LocalEchoUpdated, this.onLocalEcho);
|
||||
this.room.on(_room.RoomEvent.TimelineReset, this.onTimelineReset);
|
||||
this.timelineSet.on(_room.RoomEvent.Timeline, this.onTimelineEvent);
|
||||
this.processReceipts(opts.receipts);
|
||||
|
||||
// even if this thread is thought to be originating from this client, we initialise it as we may be in a
|
||||
@@ -210,20 +178,19 @@ export class Thread extends ReadReceipt {
|
||||
this.setEventMetadata(this.rootEvent);
|
||||
}
|
||||
async fetchRootEvent() {
|
||||
// Always fetch the root event, even if we already have it, so we can get the latest
|
||||
// state (via unsigned).
|
||||
this.rootEvent = this.room.findEventById(this.id);
|
||||
// If the rootEvent does not exist in the local stores, then fetch it from the server.
|
||||
try {
|
||||
const eventData = await this.client.fetchRoomEvent(this.roomId, this.id);
|
||||
const mapper = this.client.getEventMapper();
|
||||
this.rootEvent = mapper(eventData); // will merge with existing event object if such is known
|
||||
} catch (e) {
|
||||
logger.error("Failed to fetch thread root to construct thread with", e);
|
||||
_logger.logger.error("Failed to fetch thread root to construct thread with", e);
|
||||
}
|
||||
await this.processEvent(this.rootEvent);
|
||||
}
|
||||
static setServerSideSupport(status) {
|
||||
Thread.hasServerSideSupport = status;
|
||||
// XXX: This global latching behaviour is really unexpected and means that you can't undo when moving to a server without support
|
||||
if (status !== FeatureSupport.Stable) {
|
||||
FILTER_RELATED_BY_SENDERS.setPreferUnstable(true);
|
||||
FILTER_RELATED_BY_REL_TYPES.setPreferUnstable(true);
|
||||
@@ -237,12 +204,14 @@ export class Thread extends ReadReceipt {
|
||||
Thread.hasServerSideFwdPaginationSupport = status;
|
||||
}
|
||||
shouldSendLocalEchoReceipt(sender, event) {
|
||||
const recursionSupport = this.client.canSupport.get(Feature.RelationsRecursion) ?? ServerSupport.Unsupported;
|
||||
if (recursionSupport === ServerSupport.Unsupported) {
|
||||
var _this$client$canSuppo;
|
||||
const recursionSupport = (_this$client$canSuppo = this.client.canSupport.get(_feature.Feature.RelationsRecursion)) !== null && _this$client$canSuppo !== void 0 ? _this$client$canSuppo : _feature.ServerSupport.Unsupported;
|
||||
if (recursionSupport === _feature.ServerSupport.Unsupported) {
|
||||
var _this$getReadReceiptF;
|
||||
// Normally we add a local receipt, but if we don't have
|
||||
// recursion support, then events may arrive out of order, so we
|
||||
// only create a receipt if it's after our existing receipt.
|
||||
const oldReceiptEventId = this.getReadReceiptForUserId(sender)?.eventId;
|
||||
const oldReceiptEventId = (_this$getReadReceiptF = this.getReadReceiptForUserId(sender)) === null || _this$getReadReceiptF === void 0 ? void 0 : _this$getReadReceiptF.eventId;
|
||||
if (oldReceiptEventId) {
|
||||
const receiptEvent = this.findEventById(oldReceiptEventId);
|
||||
if (receiptEvent && receiptEvent.getTs() > event.getTs()) {
|
||||
@@ -253,16 +222,16 @@ export class Thread extends ReadReceipt {
|
||||
return true;
|
||||
}
|
||||
get roomState() {
|
||||
return this.room.getLiveTimeline().getState(EventTimeline.FORWARDS);
|
||||
return this.room.getLiveTimeline().getState(_eventTimeline.EventTimeline.FORWARDS);
|
||||
}
|
||||
addEventToTimeline(event, toStartOfTimeline) {
|
||||
if (!this.findEventById(event.getId())) {
|
||||
this.timelineSet.addEventToTimeline(event, this.liveTimeline, {
|
||||
toStartOfTimeline,
|
||||
fromCache: false,
|
||||
roomState: this.roomState,
|
||||
addToState: false
|
||||
roomState: this.roomState
|
||||
});
|
||||
this.timeline = this.events;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +257,10 @@ export class Thread extends ReadReceipt {
|
||||
if (this.findEventById(eventId)) {
|
||||
return;
|
||||
}
|
||||
this.timelineSet.insertEventIntoTimeline(event, this.liveTimeline, this.roomState, false);
|
||||
this.timelineSet.insertEventIntoTimeline(event, this.liveTimeline, this.roomState);
|
||||
|
||||
// As far as we know, timeline should always be the same as events
|
||||
this.timeline = this.events;
|
||||
}
|
||||
addEvents(events, toStartOfTimeline) {
|
||||
events.forEach(ev => this.addEvent(ev, toStartOfTimeline, false));
|
||||
@@ -304,47 +276,49 @@ export class Thread extends ReadReceipt {
|
||||
* to the start (and not the end) of the timeline.
|
||||
* @param emit - whether to emit the Update event if the thread was updated or not.
|
||||
*/
|
||||
addEvent(event, toStartOfTimeline, emit = true) {
|
||||
// Modify this event to point at our room's state, and mark its thread
|
||||
// as this.
|
||||
async addEvent(event, toStartOfTimeline, emit = true) {
|
||||
this.setEventMetadata(event);
|
||||
const eventId = event.getId();
|
||||
if (!this.initialEventsFetched && eventId && !event.isRelation(RelationType.Annotation) && !event.isRelation(RelationType.Replace)) {
|
||||
this.preInitEventTargets.set(eventId, event);
|
||||
}
|
||||
|
||||
// Decide whether this event is going to be added at the end of the timeline.
|
||||
const lastReply = this.lastReply();
|
||||
const isNewestReply = !lastReply || event.localTimestamp >= lastReply.localTimestamp;
|
||||
|
||||
// Add all incoming events to the thread's timeline set when there's no server support
|
||||
if (!Thread.hasServerSideSupport) {
|
||||
// When there's no server-side support, just add it to the end of the timeline.
|
||||
// all the relevant membership info to hydrate events with a sender
|
||||
// is held in the main room timeline
|
||||
// We want to fetch the room state from there and pass it down to this thread
|
||||
// timeline set to let it reconcile an event with its relevant RoomMember
|
||||
this.addEventToTimeline(event, toStartOfTimeline);
|
||||
this.client.decryptEventIfNeeded(event);
|
||||
} else if (event.isRelation(RelationType.Annotation) || event.isRelation(RelationType.Replace)) {
|
||||
this.addRelatedThreadEvent(event, toStartOfTimeline);
|
||||
return;
|
||||
} else if (!toStartOfTimeline && isNewestReply) {
|
||||
// When we've asked for the event to be added to the end,
|
||||
// and this event belongs at the end, add it.
|
||||
this.client.decryptEventIfNeeded(event, {});
|
||||
} else if (!toStartOfTimeline && this.initialEventsFetched && isNewestReply) {
|
||||
this.addEventToTimeline(event, false);
|
||||
this.fetchEditsWhereNeeded(event);
|
||||
} else {
|
||||
// Otherwise, we should add it, but we suspect it is out of order.
|
||||
// This may be because we've just created the thread object and are
|
||||
// still fetching events, in which case add it where we think is sensible
|
||||
// and it will be removed and replaced with the events from the pagination
|
||||
// request once that completes.
|
||||
if (toStartOfTimeline) {
|
||||
// If we're adding at the start of the timeline, it doesn't
|
||||
// matter that it's out of order.
|
||||
this.addEventToTimeline(event, toStartOfTimeline);
|
||||
} else if (event.isRelation(_event.RelationType.Annotation) || event.isRelation(_event.RelationType.Replace)) {
|
||||
var _this$timelineSet$rel, _this$timelineSet$rel2;
|
||||
if (!this.initialEventsFetched) {
|
||||
var _this$replayEvents;
|
||||
/**
|
||||
* A thread can be fully discovered via a single sync response
|
||||
* And when that's the case we still ask the server to do an initialisation
|
||||
* as it's the safest to ensure we have everything.
|
||||
* However when we are in that scenario we might loose annotation or edits
|
||||
*
|
||||
* This fix keeps a reference to those events and replay them once the thread
|
||||
* has been initialised properly.
|
||||
*/
|
||||
(_this$replayEvents = this.replayEvents) === null || _this$replayEvents === void 0 ? void 0 : _this$replayEvents.push(event);
|
||||
} else {
|
||||
// We think this event might be out of order, because isNewestReply
|
||||
// is false (otherwise we would have gone into the earlier if
|
||||
// clause), so try to insert it in the right place based on
|
||||
// timestamp.
|
||||
this.insertEventIntoTimeline(event);
|
||||
var _this$client$canSuppo2;
|
||||
const recursionSupport = (_this$client$canSuppo2 = this.client.canSupport.get(_feature.Feature.RelationsRecursion)) !== null && _this$client$canSuppo2 !== void 0 ? _this$client$canSuppo2 : _feature.ServerSupport.Unsupported;
|
||||
if (recursionSupport === _feature.ServerSupport.Unsupported) {
|
||||
this.insertEventIntoTimeline(event);
|
||||
} else {
|
||||
this.addEventToTimeline(event, toStartOfTimeline);
|
||||
}
|
||||
}
|
||||
// Apply annotations and replace relations to the relations of the timeline only
|
||||
(_this$timelineSet$rel = this.timelineSet.relations) === null || _this$timelineSet$rel === void 0 ? void 0 : _this$timelineSet$rel.aggregateParentEvent(event);
|
||||
(_this$timelineSet$rel2 = this.timelineSet.relations) === null || _this$timelineSet$rel2 === void 0 ? void 0 : _this$timelineSet$rel2.aggregateChildEvent(event, this.timelineSet);
|
||||
return;
|
||||
}
|
||||
if (event.getId() !== this.id && event.isRelation(THREAD_RELATION_TYPE.name) && !toStartOfTimeline && isNewestReply) {
|
||||
// Clear the last event as we have the latest end of the timeline
|
||||
@@ -355,87 +329,12 @@ export class Thread extends ReadReceipt {
|
||||
this.updateThreadMetadata();
|
||||
}
|
||||
}
|
||||
addRelatedThreadEvent(event, toStartOfTimeline) {
|
||||
// If this event is not a direct member of the thread, but is a
|
||||
// reference to something that is, then we have two cases:
|
||||
|
||||
if (!this.initialEventsFetched) {
|
||||
// Case 1: we haven't yet fetched events from the server. In
|
||||
// this case, when we do, the events we get back might only be
|
||||
// the first-order ones, so this event (which is second-order -
|
||||
// a reference to something directly in the thread) needs to be
|
||||
// kept so we can replay it when the first-order ones turn up.
|
||||
|
||||
/**
|
||||
* A thread can be fully discovered via a single sync response
|
||||
* And when that's the case we still ask the server to do an initialisation
|
||||
* as it's the safest to ensure we have everything.
|
||||
* However when we are in that scenario we might loose annotation or edits
|
||||
*
|
||||
* This fix keeps a reference to those events and replay them once the thread
|
||||
* has been initialised properly.
|
||||
*/
|
||||
this.replayEvents?.push(event);
|
||||
|
||||
// Aggregate annotations immediately to keep reaction counts visible.
|
||||
if (event.isRelation(RelationType.Annotation)) {
|
||||
this.timelineSet.relations?.aggregateChildEvent(event, this.timelineSet);
|
||||
}
|
||||
|
||||
// Edits can also be aggregated immediately when their target is already
|
||||
// known. If it is not known yet, replay still handles them after pagination.
|
||||
if (event.isRelation(RelationType.Replace)) {
|
||||
const targetEventId = event.getRelation()?.event_id;
|
||||
const targetEvent = targetEventId ? this.findPreInitTargetEvent(targetEventId) : undefined;
|
||||
if (targetEvent && targetEventId) {
|
||||
const weakThread = new WeakRef(this);
|
||||
const observerState = this.preInitObserverState;
|
||||
const eventId = event.getId();
|
||||
void this.timelineSet.relations.aggregateChildEvent(event, this.timelineSet, targetEvent).then(() => {
|
||||
const thread = weakThread.deref();
|
||||
if (!thread || !observerState.active) return;
|
||||
const effectiveTarget = thread.findPreInitTargetEvent(targetEventId);
|
||||
if (effectiveTarget?.replacingEvent()?.getId() === eventId) {
|
||||
thread.emit(ThreadEvent.Update, thread);
|
||||
}
|
||||
}).catch(error => logger.error("Failed to aggregate pre-initialization thread edit: ", error));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Case 2: this is happening later, and we have a timeline. In
|
||||
// this case, these events might be out-of order.
|
||||
//
|
||||
// Specifically, if the server doesn't support recursion, so we
|
||||
// only get these events through sync, they might be coming
|
||||
// later than the first-order ones, so we insert them based on
|
||||
// timestamp (despite the problems with this documented in
|
||||
// #3325).
|
||||
//
|
||||
// If the server does support recursion, we should have got all
|
||||
// the interspersed events from the server when we fetched the
|
||||
// initial events, so if they are coming via sync they should be
|
||||
// the latest ones, so we can add them as normal.
|
||||
//
|
||||
// (Note that both insertEventIntoTimeline and addEventToTimeline
|
||||
// do nothing if we have seen this event before.)
|
||||
|
||||
const recursionSupport = this.client.canSupport.get(Feature.RelationsRecursion) ?? ServerSupport.Unsupported;
|
||||
if (recursionSupport === ServerSupport.Unsupported) {
|
||||
this.insertEventIntoTimeline(event);
|
||||
} else {
|
||||
this.addEventToTimeline(event, toStartOfTimeline);
|
||||
}
|
||||
// Aggregation is handled by EventTimelineSet when inserting/adding.
|
||||
}
|
||||
}
|
||||
findPreInitTargetEvent(eventId) {
|
||||
return this.timelineSet.findEventById(eventId) ?? this.room.findEventById(eventId) ?? this.preInitEventTargets.get(eventId) ?? (this.lastEvent?.getId() === eventId ? this.lastEvent : undefined);
|
||||
}
|
||||
async processEvent(event) {
|
||||
if (event) {
|
||||
this.setEventMetadata(event);
|
||||
await this.fetchEditsWhereNeeded(event);
|
||||
}
|
||||
this.timeline = this.events;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -456,7 +355,7 @@ export class Thread extends ReadReceipt {
|
||||
}
|
||||
}
|
||||
getRootEventBundledRelationship(rootEvent = this.rootEvent) {
|
||||
return rootEvent?.getServerAggregatedRelation(THREAD_RELATION_TYPE.name);
|
||||
return rootEvent === null || rootEvent === void 0 ? void 0 : rootEvent.getServerAggregatedRelation(THREAD_RELATION_TYPE.name);
|
||||
}
|
||||
async processRootEvent() {
|
||||
const bundledRelationship = this.getRootEventBundledRelationship();
|
||||
@@ -473,8 +372,11 @@ export class Thread extends ReadReceipt {
|
||||
}
|
||||
}
|
||||
updatePendingReplyCount() {
|
||||
const unfilteredPendingEvents = this.pendingEventOrdering === PendingEventOrdering.Detached ? this.room.getPendingEvents() : this.events;
|
||||
const pendingEvents = unfilteredPendingEvents.filter(ev => ev.threadRootId === this.id && ev.isRelation(THREAD_RELATION_TYPE.name) && ev.status !== null && ev.getId() !== this.lastEvent?.getId());
|
||||
const unfilteredPendingEvents = this.pendingEventOrdering === _client.PendingEventOrdering.Detached ? this.room.getPendingEvents() : this.events;
|
||||
const pendingEvents = unfilteredPendingEvents.filter(ev => {
|
||||
var _this$lastEvent2;
|
||||
return ev.threadRootId === this.id && ev.isRelation(THREAD_RELATION_TYPE.name) && ev.status !== null && ev.getId() !== ((_this$lastEvent2 = this.lastEvent) === null || _this$lastEvent2 === void 0 ? void 0 : _this$lastEvent2.getId());
|
||||
});
|
||||
this.lastPendingEvent = pendingEvents.length ? pendingEvents[pendingEvents.length - 1] : undefined;
|
||||
this.pendingReplyCount = pendingEvents.length;
|
||||
}
|
||||
@@ -495,7 +397,7 @@ export class Thread extends ReadReceipt {
|
||||
*/
|
||||
async resetLiveTimeline(backPaginationToken, forwardPaginationToken) {
|
||||
const oldLive = this.liveTimeline;
|
||||
this.timelineSet.resetLiveTimeline(backPaginationToken ?? undefined, forwardPaginationToken ?? undefined);
|
||||
this.timelineSet.resetLiveTimeline(backPaginationToken !== null && backPaginationToken !== void 0 ? backPaginationToken : undefined, forwardPaginationToken !== null && forwardPaginationToken !== void 0 ? forwardPaginationToken : undefined);
|
||||
const newLive = this.liveTimeline;
|
||||
|
||||
// FIXME: Remove the following as soon as https://github.com/matrix-org/synapse/issues/14830 is resolved.
|
||||
@@ -511,20 +413,22 @@ export class Thread extends ReadReceipt {
|
||||
let newBackward;
|
||||
let oldForward;
|
||||
if (backPaginationToken) {
|
||||
const res = await this.client.createMessagesRequest(this.roomId, backPaginationToken, 1, Direction.Forward);
|
||||
const res = await this.client.createMessagesRequest(this.roomId, backPaginationToken, 1, _eventTimeline.Direction.Forward);
|
||||
newBackward = res.end;
|
||||
}
|
||||
if (forwardPaginationToken) {
|
||||
const res = await this.client.createMessagesRequest(this.roomId, forwardPaginationToken, 1, Direction.Backward);
|
||||
const res = await this.client.createMessagesRequest(this.roomId, forwardPaginationToken, 1, _eventTimeline.Direction.Backward);
|
||||
oldForward = res.start;
|
||||
}
|
||||
// Only replace the token if we don't have paginated away from this position already. This situation doesn't
|
||||
// occur today, but if the above issue is resolved, we'd have to go down this path.
|
||||
if (forwardPaginationToken && oldLive.getPaginationToken(Direction.Forward) === forwardPaginationToken) {
|
||||
oldLive.setPaginationToken(oldForward ?? null, Direction.Forward);
|
||||
if (forwardPaginationToken && oldLive.getPaginationToken(_eventTimeline.Direction.Forward) === forwardPaginationToken) {
|
||||
var _oldForward;
|
||||
oldLive.setPaginationToken((_oldForward = oldForward) !== null && _oldForward !== void 0 ? _oldForward : null, _eventTimeline.Direction.Forward);
|
||||
}
|
||||
if (backPaginationToken && newLive.getPaginationToken(Direction.Backward) === backPaginationToken) {
|
||||
newLive.setPaginationToken(newBackward ?? null, Direction.Backward);
|
||||
if (backPaginationToken && newLive.getPaginationToken(_eventTimeline.Direction.Backward) === backPaginationToken) {
|
||||
var _newBackward;
|
||||
newLive.setPaginationToken((_newBackward = newBackward) !== null && _newBackward !== void 0 ? _newBackward : null, _eventTimeline.Direction.Backward);
|
||||
}
|
||||
}
|
||||
async updateThreadFromRootEvent() {
|
||||
@@ -546,49 +450,30 @@ export class Thread extends ReadReceipt {
|
||||
}
|
||||
await this.processRootEventPromise;
|
||||
if (!this.initialEventsFetched) {
|
||||
if (this.initalEventFetchProm) {
|
||||
await this.initalEventFetchProm;
|
||||
} else {
|
||||
// fetch initial events to allow proper pagination
|
||||
try {
|
||||
// clear out any events that were added before the pagination request
|
||||
// completed (eg. from sync). They'll be replaced by those from the pagination.
|
||||
// Really, we should do this after the pagination request completes, but
|
||||
// paginateEventTimeline does the request and adds the events in one go, so
|
||||
// this would need a refactor in order to do. It's therefore possible there's
|
||||
// a remaining race where an event comes in while the pagination request is
|
||||
// happening.
|
||||
this.timelineSet.resetLiveTimeline();
|
||||
// if the thread has regular events, this will just load the last reply.
|
||||
// if the thread is newly created, this will load the root event.
|
||||
if (this.replyCount === 0 && this.rootEvent) {
|
||||
this.timelineSet.addEventsToTimeline([this.rootEvent], true, false, this.liveTimeline, null);
|
||||
this.liveTimeline.setPaginationToken(null, Direction.Backward);
|
||||
} else {
|
||||
this.initalEventFetchProm = this.client.paginateEventTimeline(this.liveTimeline, {
|
||||
backwards: true
|
||||
});
|
||||
await this.initalEventFetchProm;
|
||||
}
|
||||
// We have now fetched the initial events, so set the flag. We need to do this before
|
||||
// we actually add the events, so `this.addEvents` knows that it can now safely add
|
||||
// them rather than buffer them in the pending event list. The main thing is that this
|
||||
// must remain false while the async fetch happens, so we don't try to add events before
|
||||
// the pagination has finished. The important thing is that we're not await-ing anything
|
||||
// else between setting this and adding events, so no races.
|
||||
this.initialEventsFetched = true;
|
||||
for (const event of this.replayEvents) {
|
||||
this.addEvent(event, false);
|
||||
}
|
||||
this.replayEvents = null;
|
||||
this.preInitEventTargets.clear();
|
||||
// just to make sure that, if we've created a timeline window for this thread before the thread itself
|
||||
// existed (e.g. when creating a new thread), we'll make sure the panel is force refreshed correctly.
|
||||
this.emit(RoomEvent.TimelineReset, this.room, this.timelineSet, true);
|
||||
} catch (e) {
|
||||
logger.error("Failed to load start of newly created thread: ", e);
|
||||
this.initialEventsFetched = false;
|
||||
this.initialEventsFetched = true;
|
||||
// fetch initial event to allow proper pagination
|
||||
try {
|
||||
// if the thread has regular events, this will just load the last reply.
|
||||
// if the thread is newly created, this will load the root event.
|
||||
if (this.replyCount === 0 && this.rootEvent) {
|
||||
this.timelineSet.addEventsToTimeline([this.rootEvent], true, this.liveTimeline, null);
|
||||
this.liveTimeline.setPaginationToken(null, _eventTimeline.Direction.Backward);
|
||||
} else {
|
||||
await this.client.paginateEventTimeline(this.liveTimeline, {
|
||||
backwards: true,
|
||||
limit: Math.max(1, this.length)
|
||||
});
|
||||
}
|
||||
for (const event of this.replayEvents) {
|
||||
this.addEvent(event, false);
|
||||
}
|
||||
this.replayEvents = null;
|
||||
// just to make sure that, if we've created a timeline window for this thread before the thread itself
|
||||
// existed (e.g. when creating a new thread), we'll make sure the panel is force refreshed correctly.
|
||||
this.emit(_room.RoomEvent.TimelineReset, this.room, this.timelineSet, true);
|
||||
} catch (e) {
|
||||
_logger.logger.error("Failed to load start of newly created thread: ", e);
|
||||
this.initialEventsFetched = false;
|
||||
}
|
||||
}
|
||||
this.emit(ThreadEvent.Update, this);
|
||||
@@ -596,11 +481,12 @@ export class Thread extends ReadReceipt {
|
||||
|
||||
// XXX: Workaround for https://github.com/matrix-org/matrix-spec-proposals/pull/2676/files#r827240084
|
||||
async fetchEditsWhereNeeded(...events) {
|
||||
const recursionSupport = this.client.canSupport.get(Feature.RelationsRecursion) ?? ServerSupport.Unsupported;
|
||||
if (recursionSupport === ServerSupport.Unsupported) {
|
||||
var _this$client$canSuppo3;
|
||||
const recursionSupport = (_this$client$canSuppo3 = this.client.canSupport.get(_feature.Feature.RelationsRecursion)) !== null && _this$client$canSuppo3 !== void 0 ? _this$client$canSuppo3 : _feature.ServerSupport.Unsupported;
|
||||
if (recursionSupport === _feature.ServerSupport.Unsupported) {
|
||||
return Promise.all(events.filter(isAnEncryptedThreadMessage).map(async event => {
|
||||
try {
|
||||
const relations = await this.client.relations(this.roomId, event.getId(), RelationType.Replace, event.getType(), {
|
||||
const relations = await this.client.relations(this.roomId, event.getId(), _event.RelationType.Replace, event.getType(), {
|
||||
limit: 1
|
||||
});
|
||||
if (relations.events.length) {
|
||||
@@ -609,21 +495,22 @@ export class Thread extends ReadReceipt {
|
||||
this.insertEventIntoTimeline(editEvent);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error("Failed to load edits for encrypted thread event", e);
|
||||
_logger.logger.error("Failed to load edits for encrypted thread event", e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
setEventMetadata(event) {
|
||||
if (event) {
|
||||
EventTimeline.setEventMetadata(event, this.roomState, false);
|
||||
_eventTimeline.EventTimeline.setEventMetadata(event, this.roomState, false);
|
||||
event.setThread(this);
|
||||
}
|
||||
}
|
||||
clearEventMetadata(event) {
|
||||
if (event) {
|
||||
var _event$event;
|
||||
event.setThread(undefined);
|
||||
delete event.event?.unsigned?.["m.relations"]?.[THREAD_RELATION_TYPE.name];
|
||||
(_event$event = event.event) === null || _event$event === void 0 || (_event$event = _event$event.unsigned) === null || _event$event === void 0 || (_event$event = _event$event["m.relations"]) === null || _event$event === void 0 || delete _event$event[THREAD_RELATION_TYPE.name];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -637,7 +524,7 @@ export class Thread extends ReadReceipt {
|
||||
/**
|
||||
* Return last reply to the thread, if known.
|
||||
*/
|
||||
lastReply(matches = ev => ev.isRelation(THREAD_RELATION_TYPE.name)) {
|
||||
lastReply(matches = ev => ev.isRelation(_event.RelationType.Thread)) {
|
||||
for (let i = this.timeline.length - 1; i >= 0; i--) {
|
||||
const event = this.timeline[i];
|
||||
if (matches(event)) {
|
||||
@@ -664,23 +551,14 @@ export class Thread extends ReadReceipt {
|
||||
* This might be a synthesized event, if so, it will not emit any events to listeners.
|
||||
*/
|
||||
get replyToEvent() {
|
||||
return this.lastPendingEvent ?? this.lastEvent ?? this.lastReply();
|
||||
}
|
||||
|
||||
/**
|
||||
* The live event timeline for this thread.
|
||||
* @deprecated Present for backwards compatibility.
|
||||
* Use this.events instead
|
||||
* @returns The live event timeline for this thread.
|
||||
*/
|
||||
get timeline() {
|
||||
return this.events;
|
||||
var _ref, _this$lastPendingEven;
|
||||
return (_ref = (_this$lastPendingEven = this.lastPendingEvent) !== null && _this$lastPendingEven !== void 0 ? _this$lastPendingEven : this.lastEvent) !== null && _ref !== void 0 ? _ref : this.lastReply();
|
||||
}
|
||||
get events() {
|
||||
return this.liveTimeline.getEvents();
|
||||
}
|
||||
has(eventId) {
|
||||
return this.timelineSet.findEventById(eventId) instanceof MatrixEvent;
|
||||
return this.timelineSet.findEventById(eventId) instanceof _event2.MatrixEvent;
|
||||
}
|
||||
get hasCurrentUserParticipated() {
|
||||
return this._currentUserParticipated;
|
||||
@@ -705,27 +583,6 @@ export class Thread extends ReadReceipt {
|
||||
* @returns ID of the latest event that the given user has read, or null.
|
||||
*/
|
||||
getEventReadUpTo(userId, ignoreSynthesized) {
|
||||
// TODO: we think the implementation here is not right. Here is a sketch
|
||||
// of the right answer:
|
||||
//
|
||||
// for event in timeline.events.reversed():
|
||||
// if room.hasUserReadEvent(event):
|
||||
// return event
|
||||
// return null
|
||||
//
|
||||
// If this is too slow, we might be able to improve it by trying walking
|
||||
// forward from the threaded receipt in this thread. We could alternate
|
||||
// between backwards-from-front and forwards-from-threaded-receipt to
|
||||
// improve our chances of hitting the right answer sooner.
|
||||
//
|
||||
// Either way, it's still fundamentally slow because we have to walk
|
||||
// events.
|
||||
//
|
||||
// We also might just want to limit the time we spend on this by giving
|
||||
// up after, say, 100 events.
|
||||
//
|
||||
// --- andyb
|
||||
|
||||
const isCurrentUser = userId === this.client.getUserId();
|
||||
const lastReply = this.timeline[this.timeline.length - 1];
|
||||
if (isCurrentUser && lastReply) {
|
||||
@@ -749,7 +606,8 @@ export class Thread extends ReadReceipt {
|
||||
if (!unthreadedReceipt) {
|
||||
return readUpToId;
|
||||
}
|
||||
for (let i = this.timeline?.length - 1; i >= 0; --i) {
|
||||
for (let i = ((_this$timeline = this.timeline) === null || _this$timeline === void 0 ? void 0 : _this$timeline.length) - 1; i >= 0; --i) {
|
||||
var _this$timeline, _ev$getId;
|
||||
const ev = this.timeline[i];
|
||||
// If we encounter the `readUpToId` we do not need to look further
|
||||
// there is no "more recent" unthreaded read receipt
|
||||
@@ -759,7 +617,7 @@ export class Thread extends ReadReceipt {
|
||||
// whether an unthreaded read receipt is more recent that the current event.
|
||||
// We usually prefer relying on the order of the DAG but in this scenario
|
||||
// it is not possible and we have to rely on timestamp
|
||||
if (ev.getTs() < unthreadedReceipt.ts) return ev.getId() ?? readUpToId;
|
||||
if (ev.getTs() < unthreadedReceipt.ts) return (_ev$getId = ev.getId()) !== null && _ev$getId !== void 0 ? _ev$getId : readUpToId;
|
||||
}
|
||||
}
|
||||
return readUpToId;
|
||||
@@ -778,18 +636,19 @@ export class Thread extends ReadReceipt {
|
||||
*/
|
||||
hasUserReadEvent(userId, eventId) {
|
||||
if (userId === this.client.getUserId()) {
|
||||
var _this$lastReply$getTs, _this$lastReply, _this$room$getLastUnt, _this$room$getLastUnt2, _this$lastReply$getTs2, _this$lastReply2;
|
||||
// Consider an event read if it's part of a thread that is before the
|
||||
// first threaded receipt sent in that room. It is likely that it is
|
||||
// part of a thread that was created before MSC3771 was implemented.
|
||||
// Or before the last unthreaded receipt for the logged in user
|
||||
const beforeFirstThreadedReceipt = (this.lastReply()?.getTs() ?? 0) < this.room.getOldestThreadedReceiptTs();
|
||||
const unthreadedReceiptTs = this.room.getLastUnthreadedReceiptFor(userId)?.ts ?? 0;
|
||||
const beforeLastUnthreadedReceipt = (this?.lastReply()?.getTs() ?? 0) < unthreadedReceiptTs;
|
||||
const beforeFirstThreadedReceipt = ((_this$lastReply$getTs = (_this$lastReply = this.lastReply()) === null || _this$lastReply === void 0 ? void 0 : _this$lastReply.getTs()) !== null && _this$lastReply$getTs !== void 0 ? _this$lastReply$getTs : 0) < this.room.getOldestThreadedReceiptTs();
|
||||
const unthreadedReceiptTs = (_this$room$getLastUnt = (_this$room$getLastUnt2 = this.room.getLastUnthreadedReceiptFor(userId)) === null || _this$room$getLastUnt2 === void 0 ? void 0 : _this$room$getLastUnt2.ts) !== null && _this$room$getLastUnt !== void 0 ? _this$room$getLastUnt : 0;
|
||||
const beforeLastUnthreadedReceipt = ((_this$lastReply$getTs2 = this === null || this === void 0 || (_this$lastReply2 = this.lastReply()) === null || _this$lastReply2 === void 0 ? void 0 : _this$lastReply2.getTs()) !== null && _this$lastReply$getTs2 !== void 0 ? _this$lastReply$getTs2 : 0) < unthreadedReceiptTs;
|
||||
if (beforeFirstThreadedReceipt || beforeLastUnthreadedReceipt) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return this.room.hasUserReadEvent(userId, eventId);
|
||||
return super.hasUserReadEvent(userId, eventId);
|
||||
}
|
||||
setUnread(type, count) {
|
||||
return this.room.setThreadUnreadNotificationCount(this.id, type, count);
|
||||
@@ -813,21 +672,26 @@ export class Thread extends ReadReceipt {
|
||||
* @returns true if this event is encrypted and is a message that is part of a
|
||||
* thread - either inside it, or a root.
|
||||
*/
|
||||
_defineProperty(Thread, "hasServerSideSupport", FeatureSupport.None);
|
||||
_defineProperty(Thread, "hasServerSideListSupport", FeatureSupport.None);
|
||||
_defineProperty(Thread, "hasServerSideFwdPaginationSupport", FeatureSupport.None);
|
||||
exports.Thread = Thread;
|
||||
(0, _defineProperty2.default)(Thread, "hasServerSideSupport", FeatureSupport.None);
|
||||
(0, _defineProperty2.default)(Thread, "hasServerSideListSupport", FeatureSupport.None);
|
||||
(0, _defineProperty2.default)(Thread, "hasServerSideFwdPaginationSupport", FeatureSupport.None);
|
||||
function isAnEncryptedThreadMessage(event) {
|
||||
return event.isEncrypted() && (event.isRelation(THREAD_RELATION_TYPE.name) || event.isThreadRoot);
|
||||
}
|
||||
export const FILTER_RELATED_BY_SENDERS = new ServerControlledNamespacedValue("related_by_senders", "io.element.relation_senders");
|
||||
export const FILTER_RELATED_BY_REL_TYPES = new ServerControlledNamespacedValue("related_by_rel_types", "io.element.relation_types");
|
||||
export const THREAD_RELATION_TYPE = new ServerControlledNamespacedValue("m.thread", "io.element.thread");
|
||||
export let ThreadFilterType = /*#__PURE__*/function (ThreadFilterType) {
|
||||
const FILTER_RELATED_BY_SENDERS = new _NamespacedValue.ServerControlledNamespacedValue("related_by_senders", "io.element.relation_senders");
|
||||
exports.FILTER_RELATED_BY_SENDERS = FILTER_RELATED_BY_SENDERS;
|
||||
const FILTER_RELATED_BY_REL_TYPES = new _NamespacedValue.ServerControlledNamespacedValue("related_by_rel_types", "io.element.relation_types");
|
||||
exports.FILTER_RELATED_BY_REL_TYPES = FILTER_RELATED_BY_REL_TYPES;
|
||||
const THREAD_RELATION_TYPE = new _NamespacedValue.ServerControlledNamespacedValue("m.thread", "io.element.thread");
|
||||
exports.THREAD_RELATION_TYPE = THREAD_RELATION_TYPE;
|
||||
let ThreadFilterType = /*#__PURE__*/function (ThreadFilterType) {
|
||||
ThreadFilterType[ThreadFilterType["My"] = 0] = "My";
|
||||
ThreadFilterType[ThreadFilterType["All"] = 1] = "All";
|
||||
return ThreadFilterType;
|
||||
}({});
|
||||
export function threadFilterTypeToFilter(type) {
|
||||
exports.ThreadFilterType = ThreadFilterType;
|
||||
function threadFilterTypeToFilter(type) {
|
||||
switch (type) {
|
||||
case ThreadFilterType.My:
|
||||
return "participated";
|
||||
|
||||
Reference in New Issue
Block a user