1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-06 12:02:40 +03:00

Update thread info after MSC3440 updates (#2209)

This commit is contained in:
Germain
2022-03-02 10:52:08 +00:00
committed by GitHub
parent 9b429c1902
commit 4e4afdb795
5 changed files with 130 additions and 27 deletions

View File

@@ -1,4 +1,8 @@
import { RelationType, UNSTABLE_FILTER_RELATION_SENDERS, UNSTABLE_FILTER_RELATION_TYPES } from "../../src"; import {
RelationType,
UNSTABLE_FILTER_RELATED_BY_REL_TYPES,
UNSTABLE_FILTER_RELATED_BY_SENDERS,
} from "../../src";
import { FilterComponent } from "../../src/filter-component"; import { FilterComponent } from "../../src/filter-component";
import { mkEvent } from '../test-utils'; import { mkEvent } from '../test-utils';
@@ -35,7 +39,7 @@ describe("Filter Component", function() {
it("should filter out events by relation participation", function() { it("should filter out events by relation participation", function() {
const currentUserId = '@me:server.org'; const currentUserId = '@me:server.org';
const filter = new FilterComponent({ const filter = new FilterComponent({
[UNSTABLE_FILTER_RELATION_SENDERS.name]: currentUserId, [UNSTABLE_FILTER_RELATED_BY_SENDERS.name]: [currentUserId],
}, currentUserId); }, currentUserId);
const threadRootNotParticipated = mkEvent({ const threadRootNotParticipated = mkEvent({
@@ -60,7 +64,7 @@ describe("Filter Component", function() {
it("should keep events by relation participation", function() { it("should keep events by relation participation", function() {
const currentUserId = '@me:server.org'; const currentUserId = '@me:server.org';
const filter = new FilterComponent({ const filter = new FilterComponent({
[UNSTABLE_FILTER_RELATION_SENDERS.name]: currentUserId, [UNSTABLE_FILTER_RELATED_BY_SENDERS.name]: [currentUserId],
}, currentUserId); }, currentUserId);
const threadRootParticipated = mkEvent({ const threadRootParticipated = mkEvent({
@@ -84,7 +88,7 @@ describe("Filter Component", function() {
it("should filter out events by relation type", function() { it("should filter out events by relation type", function() {
const filter = new FilterComponent({ const filter = new FilterComponent({
[UNSTABLE_FILTER_RELATION_TYPES.name]: RelationType.Thread, [UNSTABLE_FILTER_RELATED_BY_REL_TYPES.name]: [RelationType.Thread],
}); });
const referenceRelationEvent = mkEvent({ const referenceRelationEvent = mkEvent({
@@ -104,7 +108,7 @@ describe("Filter Component", function() {
it("should keep events by relation type", function() { it("should keep events by relation type", function() {
const filter = new FilterComponent({ const filter = new FilterComponent({
[UNSTABLE_FILTER_RELATION_TYPES.name]: RelationType.Thread, [UNSTABLE_FILTER_RELATED_BY_REL_TYPES.name]: [RelationType.Thread],
}); });
const threadRootEvent = mkEvent({ const threadRootEvent = mkEvent({

View File

@@ -1,3 +1,24 @@
/*
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.
*/
/**
* This is an internal module. See {@link MatrixClient} for the public class.
* @module client
*/
import * as utils from "../test-utils"; import * as utils from "../test-utils";
import { DuplicateStrategy, EventStatus, MatrixEvent, PendingEventOrdering, RoomEvent } from "../../src"; import { DuplicateStrategy, EventStatus, MatrixEvent, PendingEventOrdering, RoomEvent } from "../../src";
import { EventTimeline } from "../../src/models/event-timeline"; import { EventTimeline } from "../../src/models/event-timeline";
@@ -5,6 +26,7 @@ import { Room } from "../../src/models/room";
import { RoomState } from "../../src/models/room-state"; import { RoomState } from "../../src/models/room-state";
import { RelationType, UNSTABLE_ELEMENT_FUNCTIONAL_USERS } from "../../src/@types/event"; import { RelationType, UNSTABLE_ELEMENT_FUNCTIONAL_USERS } from "../../src/@types/event";
import { TestClient } from "../TestClient"; import { TestClient } from "../TestClient";
import { Thread } from "../../src/models/thread";
describe("Room", function() { describe("Room", function() {
const roomId = "!foo:bar"; const roomId = "!foo:bar";
@@ -1845,6 +1867,54 @@ describe("Room", function() {
expect(() => room.createThread(rootEvent, [])).not.toThrow(); expect(() => room.createThread(rootEvent, [])).not.toThrow();
}); });
it("should not add events before server supports is known", function() {
Thread.hasServerSideSupport = undefined;
const rootEvent = new MatrixEvent({
event_id: "$666",
room_id: roomId,
content: {},
unsigned: {
"age": 1,
"m.relations": {
[RelationType.Thread]: {
latest_event: null,
count: 1,
current_user_participated: false,
},
},
},
});
let age = 1;
function mkEvt(id): MatrixEvent {
return new MatrixEvent({
event_id: id,
room_id: roomId,
content: {
"m.relates_to": {
"rel_type": RelationType.Thread,
"event_id": "$666",
},
},
unsigned: {
"age": age++,
},
});
}
const thread = room.createThread(rootEvent, []);
expect(thread.length).toBe(0);
thread.addEvent(mkEvt("$1"));
expect(thread.length).toBe(0);
Thread.hasServerSideSupport = true;
thread.addEvent(mkEvt("$2"));
expect(thread.length).toBeGreaterThan(0);
});
}); });
}); });
}); });

View File

@@ -15,7 +15,10 @@ limitations under the License.
*/ */
import { RelationType } from "./@types/event"; import { RelationType } from "./@types/event";
import { UNSTABLE_FILTER_RELATION_SENDERS, UNSTABLE_FILTER_RELATION_TYPES } from "./filter"; import {
UNSTABLE_FILTER_RELATED_BY_REL_TYPES,
UNSTABLE_FILTER_RELATED_BY_SENDERS,
} from "./filter";
import { MatrixEvent } from "./models/event"; import { MatrixEvent } from "./models/event";
/** /**
@@ -48,7 +51,8 @@ export interface IFilterComponent {
not_senders?: string[]; not_senders?: string[];
contains_url?: boolean; contains_url?: boolean;
limit?: number; limit?: number;
[UNSTABLE_FILTER_RELATION_TYPES.name]?: Array<RelationType | string>; [UNSTABLE_FILTER_RELATED_BY_SENDERS.name]?: string[];
[UNSTABLE_FILTER_RELATED_BY_REL_TYPES.name]?: Array<RelationType | string>;
} }
/* eslint-enable camelcase */ /* eslint-enable camelcase */
@@ -106,8 +110,8 @@ export class FilterComponent {
senders: this.filterJson.senders || null, senders: this.filterJson.senders || null,
not_senders: this.filterJson.not_senders || [], not_senders: this.filterJson.not_senders || [],
contains_url: this.filterJson.contains_url || null, contains_url: this.filterJson.contains_url || null,
[UNSTABLE_FILTER_RELATION_SENDERS.name]: UNSTABLE_FILTER_RELATION_SENDERS.findIn(this.filterJson), [UNSTABLE_FILTER_RELATED_BY_SENDERS.name]: UNSTABLE_FILTER_RELATED_BY_SENDERS.findIn(this.filterJson),
[UNSTABLE_FILTER_RELATION_TYPES.name]: UNSTABLE_FILTER_RELATION_TYPES.findIn(this.filterJson), [UNSTABLE_FILTER_RELATED_BY_REL_TYPES.name]: UNSTABLE_FILTER_RELATED_BY_REL_TYPES.findIn(this.filterJson),
}; };
} }
@@ -161,14 +165,14 @@ export class FilterComponent {
return false; return false;
} }
const relationTypesFilter = this.filterJson[UNSTABLE_FILTER_RELATION_TYPES.name]; const relationTypesFilter = this.filterJson[UNSTABLE_FILTER_RELATED_BY_REL_TYPES.name];
if (relationTypesFilter !== undefined) { if (relationTypesFilter !== undefined) {
if (!this.arrayMatchesFilter(relationTypesFilter, relationTypes)) { if (!this.arrayMatchesFilter(relationTypesFilter, relationTypes)) {
return false; return false;
} }
} }
const relationSendersFilter = this.filterJson[UNSTABLE_FILTER_RELATION_SENDERS.name]; const relationSendersFilter = this.filterJson[UNSTABLE_FILTER_RELATED_BY_SENDERS.name];
if (relationSendersFilter !== undefined) { if (relationSendersFilter !== undefined) {
if (!this.arrayMatchesFilter(relationSendersFilter, relationSenders)) { if (!this.arrayMatchesFilter(relationSendersFilter, relationSenders)) {
return false; return false;

View File

@@ -26,13 +26,13 @@ import { FilterComponent, IFilterComponent } from "./filter-component";
import { MatrixEvent } from "./models/event"; import { MatrixEvent } from "./models/event";
import { UnstableValue } from "./NamespacedValue"; import { UnstableValue } from "./NamespacedValue";
export const UNSTABLE_FILTER_RELATION_SENDERS = new UnstableValue( export const UNSTABLE_FILTER_RELATED_BY_SENDERS = new UnstableValue(
"relation_senders", "related_by_senders",
"io.element.relation_senders", "io.element.relation_senders",
); );
export const UNSTABLE_FILTER_RELATION_TYPES = new UnstableValue( export const UNSTABLE_FILTER_RELATED_BY_REL_TYPES = new UnstableValue(
"relation_types", "related_by_rel_types",
"io.element.relation_types", "io.element.relation_types",
); );
@@ -66,8 +66,8 @@ export interface IRoomEventFilter extends IFilterComponent {
lazy_load_members?: boolean; lazy_load_members?: boolean;
include_redundant_members?: boolean; include_redundant_members?: boolean;
types?: Array<EventType | string>; types?: Array<EventType | string>;
[UNSTABLE_FILTER_RELATION_TYPES.name]?: Array<RelationType | string>; [UNSTABLE_FILTER_RELATED_BY_REL_TYPES.name]?: Array<RelationType | string>;
[UNSTABLE_FILTER_RELATION_SENDERS.name]?: string[]; [UNSTABLE_FILTER_RELATED_BY_SENDERS.name]?: string[];
} }
interface IStateFilter extends IRoomEventFilter {} interface IStateFilter extends IRoomEventFilter {}

View File

@@ -52,6 +52,9 @@ interface IThreadOpts {
* @experimental * @experimental
*/ */
export class Thread extends TypedEventEmitter<EmittedEvents, EventHandlerMap> { export class Thread extends TypedEventEmitter<EmittedEvents, EventHandlerMap> {
public static hasServerSideSupport: boolean;
private static serverSupportPromise: Promise<boolean> | null;
/** /**
* A reference to all the events ID at the bottom of the threads * A reference to all the events ID at the bottom of the threads
*/ */
@@ -91,6 +94,15 @@ export class Thread extends TypedEventEmitter<EmittedEvents, EventHandlerMap> {
RoomEvent.TimelineReset, RoomEvent.TimelineReset,
]); ]);
if (Thread.hasServerSideSupport === undefined) {
Thread.serverSupportPromise = this.client.doesServerSupportUnstableFeature("org.matrix.msc3440");
Thread.serverSupportPromise.then((serverSupportsThread) => {
Thread.hasServerSideSupport = serverSupportsThread;
}).catch(() => {
Thread.serverSupportPromise = null;
});
}
// If we weren't able to find the root event, it's probably missing // If we weren't able to find the root event, it's probably missing
// and we define the thread ID from one of the thread relation // and we define the thread ID from one of the thread relation
if (!rootEvent) { if (!rootEvent) {
@@ -107,11 +119,6 @@ export class Thread extends TypedEventEmitter<EmittedEvents, EventHandlerMap> {
this.room.on(RoomEvent.Timeline, this.onEcho); this.room.on(RoomEvent.Timeline, this.onEcho);
} }
public get hasServerSideSupport(): boolean {
return this.client.cachedCapabilities
?.capabilities?.[RelationType.Thread]?.enabled;
}
private onEcho = (event: MatrixEvent) => { private onEcho = (event: MatrixEvent) => {
if (this.timelineSet.eventIdToTimeline(event.getId())) { if (this.timelineSet.eventIdToTimeline(event.getId())) {
this.emit(ThreadEvent.Update, this); this.emit(ThreadEvent.Update, this);
@@ -152,8 +159,12 @@ export class Thread extends TypedEventEmitter<EmittedEvents, EventHandlerMap> {
* to the start (and not the end) of the timeline. * to the start (and not the end) of the timeline.
*/ */
public async addEvent(event: MatrixEvent, toStartOfTimeline = false): Promise<void> { public async addEvent(event: MatrixEvent, toStartOfTimeline = false): Promise<void> {
if (Thread.hasServerSideSupport === undefined) {
await Thread.serverSupportPromise;
}
// Add all incoming events to the thread's timeline set when there's no server support // Add all incoming events to the thread's timeline set when there's no server support
if (!this.hasServerSideSupport) { if (!Thread.hasServerSideSupport) {
// all the relevant membership info to hydrate events with a sender // all the relevant membership info to hydrate events with a sender
// is held in the main room timeline // is held in the main room timeline
// We want to fetch the room state from there and pass it down to this thread // We want to fetch the room state from there and pass it down to this thread
@@ -165,7 +176,7 @@ export class Thread extends TypedEventEmitter<EmittedEvents, EventHandlerMap> {
await this.client.decryptEventIfNeeded(event, {}); await this.client.decryptEventIfNeeded(event, {});
} }
if (this.hasServerSideSupport && this.initialEventsFetched) { if (Thread.hasServerSideSupport && this.initialEventsFetched) {
if (event.localTimestamp > this.lastReply().localTimestamp) { if (event.localTimestamp > this.lastReply().localTimestamp) {
this.addEventToTimeline(event, false); this.addEventToTimeline(event, false);
} }
@@ -178,7 +189,7 @@ export class Thread extends TypedEventEmitter<EmittedEvents, EventHandlerMap> {
const isThreadReply = event.getRelation()?.rel_type === RelationType.Thread; const isThreadReply = event.getRelation()?.rel_type === RelationType.Thread;
// If no thread support exists we want to count all thread relation // If no thread support exists we want to count all thread relation
// added as a reply. We can't rely on the bundled relationships count // added as a reply. We can't rely on the bundled relationships count
if (!this.hasServerSideSupport && isThreadReply) { if (!Thread.hasServerSideSupport && isThreadReply) {
this.replyCount++; this.replyCount++;
} }
@@ -191,7 +202,7 @@ export class Thread extends TypedEventEmitter<EmittedEvents, EventHandlerMap> {
// This counting only works when server side support is enabled // This counting only works when server side support is enabled
// as we started the counting from the value returned in the // as we started the counting from the value returned in the
// bundled relationship // bundled relationship
if (this.hasServerSideSupport) { if (Thread.hasServerSideSupport) {
this.replyCount++; this.replyCount++;
} }
@@ -203,10 +214,17 @@ export class Thread extends TypedEventEmitter<EmittedEvents, EventHandlerMap> {
} }
private initialiseThread(rootEvent: MatrixEvent | undefined): void { private initialiseThread(rootEvent: MatrixEvent | undefined): void {
if (Thread.hasServerSideSupport === undefined) {
Thread.serverSupportPromise.then(() => {
this.initialiseThread(rootEvent);
});
return;
}
const bundledRelationship = rootEvent const bundledRelationship = rootEvent
?.getServerAggregatedRelation<IThreadBundledRelationship>(RelationType.Thread); ?.getServerAggregatedRelation<IThreadBundledRelationship>(RelationType.Thread);
if (this.hasServerSideSupport && bundledRelationship) { if (Thread.hasServerSideSupport && bundledRelationship) {
this.replyCount = bundledRelationship.count; this.replyCount = bundledRelationship.count;
this._currentUserParticipated = bundledRelationship.current_user_participated; this._currentUserParticipated = bundledRelationship.current_user_participated;
@@ -221,6 +239,9 @@ export class Thread extends TypedEventEmitter<EmittedEvents, EventHandlerMap> {
} }
public async fetchInitialEvents(): Promise<boolean> { public async fetchInitialEvents(): Promise<boolean> {
if (Thread.hasServerSideSupport === undefined) {
await Thread.serverSupportPromise;
}
try { try {
await this.fetchEvents(); await this.fetchEvents();
this.initialEventsFetched = true; this.initialEventsFetched = true;
@@ -296,6 +317,10 @@ export class Thread extends TypedEventEmitter<EmittedEvents, EventHandlerMap> {
nextBatch?: string; nextBatch?: string;
prevBatch?: string; prevBatch?: string;
}> { }> {
if (Thread.serverSupportPromise) {
await Thread.serverSupportPromise;
}
let { let {
originalEvent, originalEvent,
events, events,