You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-07-31 15:24:23 +03:00
Make local echo work for threads (#2026)
This commit is contained in:
@ -23,7 +23,7 @@ describe("MatrixClient retrying", function() {
|
||||
);
|
||||
httpBackend = testClient.httpBackend;
|
||||
client = testClient.client;
|
||||
room = new Room(roomId);
|
||||
room = new Room(roomId, client, userId);
|
||||
client.store.storeRoom(room);
|
||||
});
|
||||
|
||||
@ -50,7 +50,10 @@ describe("MatrixClient retrying", function() {
|
||||
|
||||
it("should mark events as EventStatus.CANCELLED when cancelled", function() {
|
||||
// send a couple of events; the second will be queued
|
||||
const p1 = client.sendMessage(roomId, "m1").then(function(ev) {
|
||||
const p1 = client.sendMessage(roomId, {
|
||||
"msgtype": "m.text",
|
||||
"body": "m1",
|
||||
}).then(function(ev) {
|
||||
// we expect the first message to fail
|
||||
throw new Error('Message 1 unexpectedly sent successfully');
|
||||
}, (e) => {
|
||||
@ -60,7 +63,10 @@ describe("MatrixClient retrying", function() {
|
||||
// XXX: it turns out that the promise returned by this message
|
||||
// never gets resolved.
|
||||
// https://github.com/matrix-org/matrix-js-sdk/issues/496
|
||||
client.sendMessage(roomId, "m2");
|
||||
client.sendMessage(roomId, {
|
||||
"msgtype": "m.text",
|
||||
"body": "m2",
|
||||
});
|
||||
|
||||
// both events should be in the timeline at this point
|
||||
const tl = room.getLiveTimeline().getEvents();
|
||||
@ -88,7 +94,7 @@ describe("MatrixClient retrying", function() {
|
||||
}).respond(400); // fail the first message
|
||||
|
||||
// wait for the localecho of ev1 to be updated
|
||||
const p3 = new Promise((resolve, reject) => {
|
||||
const p3 = new Promise<void>((resolve, reject) => {
|
||||
room.on("Room.localEchoUpdated", (ev0) => {
|
||||
if (ev0 === ev1) {
|
||||
resolve();
|
326
src/client.ts
326
src/client.ts
@ -689,6 +689,12 @@ interface IRoomsKeysResponse {
|
||||
}
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
// We're using this constant for methods overloading and inspect whether a variable
|
||||
// contains an eventId or not. This was required to ensure backwards compatibility
|
||||
// of methods for threads
|
||||
// Probably not the most graceful solution but does a good enough job for now
|
||||
const EVENT_ID_PREFIX = "$";
|
||||
|
||||
/**
|
||||
* Represents a Matrix Client. Only directly construct this if you want to use
|
||||
* custom modules. Normally, {@link createClient} should be used
|
||||
@ -3392,10 +3398,11 @@ export class MatrixClient extends EventEmitter {
|
||||
|
||||
/**
|
||||
* @param {string} roomId
|
||||
* @param {string} threadId
|
||||
* @param {string} eventType
|
||||
* @param {Object} content
|
||||
* @param {string} txnId Optional.
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @param {module:client.callback} callback Optional. Deprecated
|
||||
* @return {Promise} Resolves: to an empty object {}
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
@ -3405,20 +3412,45 @@ export class MatrixClient extends EventEmitter {
|
||||
content: IContent,
|
||||
txnId?: string,
|
||||
callback?: Callback,
|
||||
);
|
||||
public sendEvent(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
eventType: string,
|
||||
content: IContent,
|
||||
txnId?: string,
|
||||
callback?: Callback,
|
||||
)
|
||||
public sendEvent(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
eventType: string | IContent,
|
||||
content: IContent | string,
|
||||
txnId?: string | Callback,
|
||||
callback?: Callback,
|
||||
): Promise<ISendEventResponse> {
|
||||
return this.sendCompleteEvent(roomId, { type: eventType, content }, txnId, callback);
|
||||
if (!threadId?.startsWith(EVENT_ID_PREFIX) && threadId !== null) {
|
||||
callback = txnId as Callback;
|
||||
txnId = content as string;
|
||||
content = eventType as IContent;
|
||||
eventType = threadId;
|
||||
threadId = null;
|
||||
}
|
||||
return this.sendCompleteEvent(roomId, threadId, { type: eventType, content }, txnId as string, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} roomId
|
||||
* @param {string} threadId
|
||||
* @param {object} eventObject An object with the partial structure of an event, to which event_id, user_id, room_id and origin_server_ts will be added.
|
||||
* @param {string} txnId Optional.
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @param {module:client.callback} callback Optional. Deprecated
|
||||
* @return {Promise} Resolves: to an empty object {}
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
private sendCompleteEvent(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
eventObject: any,
|
||||
txnId?: string,
|
||||
callback?: Callback,
|
||||
@ -3444,6 +3476,10 @@ export class MatrixClient extends EventEmitter {
|
||||
}));
|
||||
|
||||
const room = this.getRoom(roomId);
|
||||
const thread = room?.threads.get(threadId);
|
||||
if (thread) {
|
||||
localEvent.setThread(thread);
|
||||
}
|
||||
|
||||
// if this is a relation or redaction of an event
|
||||
// that hasn't been sent yet (e.g. with a local id starting with a ~)
|
||||
@ -3460,12 +3496,12 @@ export class MatrixClient extends EventEmitter {
|
||||
const type = localEvent.getType();
|
||||
logger.log(`sendEvent of type ${type} in ${roomId} with txnId ${txnId}`);
|
||||
|
||||
localEvent.setTxnId(txnId);
|
||||
localEvent.setTxnId(txnId as string);
|
||||
localEvent.setStatus(EventStatus.SENDING);
|
||||
|
||||
// add this event immediately to the local store as 'sending'.
|
||||
if (room) {
|
||||
room.addPendingEvent(localEvent, txnId);
|
||||
room.addPendingEvent(localEvent, txnId as string);
|
||||
}
|
||||
|
||||
// addPendingEvent can change the state to NOT_SENT if it believes
|
||||
@ -3655,7 +3691,7 @@ export class MatrixClient extends EventEmitter {
|
||||
* supplied.
|
||||
* @param {object|module:client.callback} cbOrOpts
|
||||
* Options to pass on, may contain `reason`.
|
||||
* Can be callback for backwards compatibility.
|
||||
* Can be callback for backwards compatibility. Deprecated
|
||||
* @return {Promise} Resolves: TODO
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
@ -3664,22 +3700,43 @@ export class MatrixClient extends EventEmitter {
|
||||
eventId: string,
|
||||
txnId?: string,
|
||||
cbOrOpts?: Callback | IRedactOpts,
|
||||
);
|
||||
public redactEvent(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
eventId: string,
|
||||
txnId?: string,
|
||||
cbOrOpts?: Callback | IRedactOpts,
|
||||
);
|
||||
public redactEvent(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
eventId: string,
|
||||
txnId?: string | Callback | IRedactOpts,
|
||||
cbOrOpts?: Callback | IRedactOpts,
|
||||
): Promise<ISendEventResponse> {
|
||||
if (!threadId?.startsWith(EVENT_ID_PREFIX) && threadId !== null) {
|
||||
cbOrOpts = txnId as (Callback | IRedactOpts);
|
||||
txnId = eventId;
|
||||
eventId = threadId;
|
||||
threadId = null;
|
||||
}
|
||||
const opts = typeof (cbOrOpts) === 'object' ? cbOrOpts : {};
|
||||
const reason = opts.reason;
|
||||
const callback = typeof (cbOrOpts) === 'function' ? cbOrOpts : undefined;
|
||||
return this.sendCompleteEvent(roomId, {
|
||||
return this.sendCompleteEvent(roomId, threadId, {
|
||||
type: EventType.RoomRedaction,
|
||||
content: { reason: reason },
|
||||
redacts: eventId,
|
||||
}, txnId, callback);
|
||||
}, txnId as string, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} roomId
|
||||
* @param {string} threadId
|
||||
* @param {Object} content
|
||||
* @param {string} txnId Optional.
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @param {module:client.callback} callback Optional. Deprecated
|
||||
* @return {Promise} Resolves: to an ISendEventResponse object
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
@ -3688,19 +3745,47 @@ export class MatrixClient extends EventEmitter {
|
||||
content: IContent,
|
||||
txnId?: string,
|
||||
callback?: Callback,
|
||||
)
|
||||
public sendMessage(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
content: IContent,
|
||||
txnId?: string,
|
||||
callback?: Callback,
|
||||
)
|
||||
public sendMessage(
|
||||
roomId: string,
|
||||
threadId: string | null | IContent,
|
||||
content: IContent | string,
|
||||
txnId?: string | Callback,
|
||||
callback?: Callback,
|
||||
): Promise<ISendEventResponse> {
|
||||
if (typeof threadId !== "string" && threadId !== null) {
|
||||
callback = txnId as Callback;
|
||||
txnId = content as string;
|
||||
content = threadId as IContent;
|
||||
threadId = null;
|
||||
}
|
||||
if (utils.isFunction(txnId)) {
|
||||
callback = txnId as any as Callback; // for legacy
|
||||
txnId = undefined;
|
||||
}
|
||||
return this.sendEvent(roomId, EventType.RoomMessage, content, txnId, callback);
|
||||
return this.sendEvent(
|
||||
roomId,
|
||||
threadId as (string | null),
|
||||
EventType.RoomMessage,
|
||||
content as IContent,
|
||||
txnId as string,
|
||||
callback,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} roomId
|
||||
* @param {string} threadId
|
||||
* @param {string} body
|
||||
* @param {string} txnId Optional.
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @param {module:client.callback} callback Optional. Deprecated
|
||||
* @return {Promise} Resolves: to an empty object {}
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
@ -3709,29 +3794,76 @@ export class MatrixClient extends EventEmitter {
|
||||
body: string,
|
||||
txnId?: string,
|
||||
callback?: Callback,
|
||||
)
|
||||
public sendTextMessage(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
body: string,
|
||||
txnId?: string,
|
||||
callback?: Callback,
|
||||
)
|
||||
public sendTextMessage(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
body: string,
|
||||
txnId?: string | Callback,
|
||||
callback?: Callback,
|
||||
): Promise<ISendEventResponse> {
|
||||
if (!threadId?.startsWith(EVENT_ID_PREFIX) && threadId !== null) {
|
||||
callback = txnId as Callback;
|
||||
txnId = body;
|
||||
body = threadId;
|
||||
threadId = null;
|
||||
}
|
||||
const content = ContentHelpers.makeTextMessage(body);
|
||||
return this.sendMessage(roomId, content, txnId, callback);
|
||||
return this.sendMessage(roomId, threadId, content, txnId as string, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} roomId
|
||||
* @param {string} threadId
|
||||
* @param {string} body
|
||||
* @param {string} txnId Optional.
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @param {module:client.callback} callback Optional. Deprecated
|
||||
* @return {Promise} Resolves: to a ISendEventResponse object
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
public sendNotice(roomId: string, body: string, txnId?: string, callback?: Callback): Promise<ISendEventResponse> {
|
||||
public sendNotice(
|
||||
roomId: string,
|
||||
body: string,
|
||||
txnId?: string,
|
||||
callback?: Callback,
|
||||
)
|
||||
public sendNotice(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
body: string,
|
||||
txnId?: string,
|
||||
callback?: Callback,
|
||||
);
|
||||
public sendNotice(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
body: string,
|
||||
txnId?: string | Callback,
|
||||
callback?: Callback,
|
||||
): Promise<ISendEventResponse> {
|
||||
if (!threadId?.startsWith(EVENT_ID_PREFIX) && threadId !== null) {
|
||||
callback = txnId as Callback;
|
||||
txnId = body;
|
||||
body = threadId;
|
||||
threadId = null;
|
||||
}
|
||||
const content = ContentHelpers.makeNotice(body);
|
||||
return this.sendMessage(roomId, content, txnId, callback);
|
||||
return this.sendMessage(roomId, threadId, content, txnId as string, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} roomId
|
||||
* @param {string} threadId
|
||||
* @param {string} body
|
||||
* @param {string} txnId Optional.
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @param {module:client.callback} callback Optional. Deprecated
|
||||
* @return {Promise} Resolves: to a ISendEventResponse object
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
@ -3740,17 +3872,38 @@ export class MatrixClient extends EventEmitter {
|
||||
body: string,
|
||||
txnId?: string,
|
||||
callback?: Callback,
|
||||
)
|
||||
public sendEmoteMessage(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
body: string,
|
||||
txnId?: string,
|
||||
callback?: Callback,
|
||||
);
|
||||
public sendEmoteMessage(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
body: string,
|
||||
txnId?: string | Callback,
|
||||
callback?: Callback,
|
||||
): Promise<ISendEventResponse> {
|
||||
if (!threadId?.startsWith(EVENT_ID_PREFIX) && threadId !== null) {
|
||||
callback = txnId as Callback;
|
||||
txnId = body;
|
||||
body = threadId;
|
||||
threadId = null;
|
||||
}
|
||||
const content = ContentHelpers.makeEmoteMessage(body);
|
||||
return this.sendMessage(roomId, content, txnId, callback);
|
||||
return this.sendMessage(roomId, threadId, content, txnId as string, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} roomId
|
||||
* @param {string} threadId
|
||||
* @param {string} url
|
||||
* @param {Object} info
|
||||
* @param {string} text
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @param {module:client.callback} callback Optional. Deprecated
|
||||
* @return {Promise} Resolves: to a ISendEventResponse object
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
@ -3758,9 +3911,32 @@ export class MatrixClient extends EventEmitter {
|
||||
roomId: string,
|
||||
url: string,
|
||||
info?: IImageInfo,
|
||||
text = "Image",
|
||||
text?: string,
|
||||
callback?: Callback,
|
||||
);
|
||||
public sendImageMessage(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
url: string,
|
||||
info?: IImageInfo,
|
||||
text?: string,
|
||||
callback?: Callback,
|
||||
);
|
||||
public sendImageMessage(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
url: string | IImageInfo,
|
||||
info?: IImageInfo | string,
|
||||
text: Callback | string = "Image",
|
||||
callback?: Callback,
|
||||
): Promise<ISendEventResponse> {
|
||||
if (!threadId?.startsWith(EVENT_ID_PREFIX) && threadId !== null) {
|
||||
callback = text as Callback;
|
||||
text = info as string || "Image";
|
||||
info = url as IImageInfo;
|
||||
url = threadId as string;
|
||||
threadId = null;
|
||||
}
|
||||
if (utils.isFunction(text)) {
|
||||
callback = text as any as Callback; // legacy
|
||||
text = undefined;
|
||||
@ -3771,15 +3947,16 @@ export class MatrixClient extends EventEmitter {
|
||||
info: info,
|
||||
body: text,
|
||||
};
|
||||
return this.sendMessage(roomId, content, undefined, callback);
|
||||
return this.sendMessage(roomId, threadId, content, undefined, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} roomId
|
||||
* @param {string} threadId
|
||||
* @param {string} url
|
||||
* @param {Object} info
|
||||
* @param {string} text
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @param {module:client.callback} callback Optional. Deprecated
|
||||
* @return {Promise} Resolves: to a ISendEventResponse object
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
@ -3787,9 +3964,32 @@ export class MatrixClient extends EventEmitter {
|
||||
roomId: string,
|
||||
url: string,
|
||||
info?: IImageInfo,
|
||||
text = "Sticker",
|
||||
text?: string,
|
||||
callback?: Callback,
|
||||
);
|
||||
public sendStickerMessage(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
url: string,
|
||||
info?: IImageInfo,
|
||||
text?: string,
|
||||
callback?: Callback,
|
||||
);
|
||||
public sendStickerMessage(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
url: string | IImageInfo,
|
||||
info?: IImageInfo | string,
|
||||
text: Callback | string = "Sticker",
|
||||
callback?: Callback,
|
||||
): Promise<ISendEventResponse> {
|
||||
if (!threadId?.startsWith(EVENT_ID_PREFIX) && threadId !== null) {
|
||||
callback = text as Callback;
|
||||
text = info as string || "Sticker";
|
||||
info = url as IImageInfo;
|
||||
url = threadId as string;
|
||||
threadId = null;
|
||||
}
|
||||
if (utils.isFunction(text)) {
|
||||
callback = text as any as Callback; // legacy
|
||||
text = undefined;
|
||||
@ -3799,14 +3999,15 @@ export class MatrixClient extends EventEmitter {
|
||||
info: info,
|
||||
body: text,
|
||||
};
|
||||
return this.sendEvent(roomId, EventType.Sticker, content, undefined, callback);
|
||||
return this.sendEvent(roomId, threadId, EventType.Sticker, content, undefined, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} roomId
|
||||
* @param {string} threadId
|
||||
* @param {string} body
|
||||
* @param {string} htmlBody
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @param {module:client.callback} callback Optional. Deprecated
|
||||
* @return {Promise} Resolves: to a ISendEventResponse object
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
@ -3815,16 +4016,36 @@ export class MatrixClient extends EventEmitter {
|
||||
body: string,
|
||||
htmlBody: string,
|
||||
callback?: Callback,
|
||||
);
|
||||
public sendHtmlMessage(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
body: string,
|
||||
htmlBody: string,
|
||||
callback?: Callback,
|
||||
)
|
||||
public sendHtmlMessage(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
body: string,
|
||||
htmlBody: string | Callback,
|
||||
callback?: Callback,
|
||||
): Promise<ISendEventResponse> {
|
||||
const content = ContentHelpers.makeHtmlMessage(body, htmlBody);
|
||||
return this.sendMessage(roomId, content, undefined, callback);
|
||||
if (!threadId?.startsWith(EVENT_ID_PREFIX) && threadId !== null) {
|
||||
callback = htmlBody as Callback;
|
||||
htmlBody = body as string;
|
||||
body = threadId;
|
||||
threadId = null;
|
||||
}
|
||||
const content = ContentHelpers.makeHtmlMessage(body, htmlBody as string);
|
||||
return this.sendMessage(roomId, threadId, content, undefined, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} roomId
|
||||
* @param {string} body
|
||||
* @param {string} htmlBody
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @param {module:client.callback} callback Optional. Deprecated
|
||||
* @return {Promise} Resolves: to a ISendEventResponse object
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
@ -3833,16 +4054,37 @@ export class MatrixClient extends EventEmitter {
|
||||
body: string,
|
||||
htmlBody: string,
|
||||
callback?: Callback,
|
||||
);
|
||||
public sendHtmlNotice(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
body: string,
|
||||
htmlBody: string,
|
||||
callback?: Callback,
|
||||
)
|
||||
public sendHtmlNotice(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
body: string,
|
||||
htmlBody: string | Callback,
|
||||
callback?: Callback,
|
||||
): Promise<ISendEventResponse> {
|
||||
const content = ContentHelpers.makeHtmlNotice(body, htmlBody);
|
||||
return this.sendMessage(roomId, content, undefined, callback);
|
||||
if (!threadId?.startsWith(EVENT_ID_PREFIX) && threadId !== null) {
|
||||
callback = htmlBody as Callback;
|
||||
htmlBody = body as string;
|
||||
body = threadId;
|
||||
threadId = null;
|
||||
}
|
||||
const content = ContentHelpers.makeHtmlNotice(body, htmlBody as string);
|
||||
return this.sendMessage(roomId, threadId, content, undefined, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} roomId
|
||||
* @param {string} threadId
|
||||
* @param {string} body
|
||||
* @param {string} htmlBody
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @param {module:client.callback} callback Optional. Deprecated
|
||||
* @return {Promise} Resolves: to a ISendEventResponse object
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
@ -3851,9 +4093,29 @@ export class MatrixClient extends EventEmitter {
|
||||
body: string,
|
||||
htmlBody: string,
|
||||
callback?: Callback,
|
||||
);
|
||||
public sendHtmlEmote(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
body: string,
|
||||
htmlBody: string,
|
||||
callback?: Callback,
|
||||
)
|
||||
public sendHtmlEmote(
|
||||
roomId: string,
|
||||
threadId: string | null,
|
||||
body: string,
|
||||
htmlBody: string | Callback,
|
||||
callback?: Callback,
|
||||
): Promise<ISendEventResponse> {
|
||||
const content = ContentHelpers.makeHtmlEmote(body, htmlBody);
|
||||
return this.sendMessage(roomId, content, undefined, callback);
|
||||
if (!threadId?.startsWith(EVENT_ID_PREFIX) && threadId !== null) {
|
||||
callback = htmlBody as Callback;
|
||||
htmlBody = body as string;
|
||||
body = threadId;
|
||||
threadId = null;
|
||||
}
|
||||
const content = ContentHelpers.makeHtmlEmote(body, htmlBody as string);
|
||||
return this.sendMessage(roomId, threadId, content, undefined, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,6 +28,7 @@ import { Room } from "./room";
|
||||
import { Filter } from "../filter";
|
||||
import { EventType, RelationType } from "../@types/event";
|
||||
import { RoomState } from "./room-state";
|
||||
import { Thread } from "./thread";
|
||||
|
||||
// var DEBUG = false;
|
||||
const DEBUG = true;
|
||||
@ -153,18 +154,18 @@ export class EventTimelineSet extends EventEmitter {
|
||||
*
|
||||
* @throws If <code>opts.pendingEventOrdering</code> was not 'detached'
|
||||
*/
|
||||
public getPendingEvents(): MatrixEvent[] {
|
||||
public getPendingEvents(thread?: Thread): MatrixEvent[] {
|
||||
if (!this.room || !this.displayPendingEvents) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const pendingEvents = this.room.getPendingEvents(thread);
|
||||
if (this.filter) {
|
||||
return this.filter.filterRoomTimeline(this.room.getPendingEvents());
|
||||
return this.filter.filterRoomTimeline(pendingEvents);
|
||||
} else {
|
||||
return this.room.getPendingEvents();
|
||||
return pendingEvents;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the live timeline for this room.
|
||||
*
|
||||
|
@ -225,12 +225,6 @@ export class Room extends EventEmitter {
|
||||
this.reEmitter = new ReEmitter(this);
|
||||
|
||||
opts.pendingEventOrdering = opts.pendingEventOrdering || PendingEventOrdering.Chronological;
|
||||
if (["chronological", "detached"].indexOf(opts.pendingEventOrdering) === -1) {
|
||||
throw new Error(
|
||||
"opts.pendingEventOrdering MUST be either 'chronological' or " +
|
||||
"'detached'. Got: '" + opts.pendingEventOrdering + "'",
|
||||
);
|
||||
}
|
||||
|
||||
this.name = roomId;
|
||||
|
||||
@ -241,7 +235,7 @@ export class Room extends EventEmitter {
|
||||
|
||||
this.fixUpLegacyTimelineFields();
|
||||
|
||||
if (this.opts.pendingEventOrdering == "detached") {
|
||||
if (this.opts.pendingEventOrdering === PendingEventOrdering.Detached) {
|
||||
this.pendingEventList = [];
|
||||
const serializedPendingEventList = client.sessionStore.store.getItem(pendingEventsKey(this.roomId));
|
||||
if (serializedPendingEventList) {
|
||||
@ -452,14 +446,16 @@ export class Room extends EventEmitter {
|
||||
*
|
||||
* @throws If <code>opts.pendingEventOrdering</code> was not 'detached'
|
||||
*/
|
||||
public getPendingEvents(): MatrixEvent[] {
|
||||
if (this.opts.pendingEventOrdering !== "detached") {
|
||||
public getPendingEvents(thread?: Thread): MatrixEvent[] {
|
||||
if (this.opts.pendingEventOrdering !== PendingEventOrdering.Detached) {
|
||||
throw new Error(
|
||||
"Cannot call getPendingEvents with pendingEventOrdering == " +
|
||||
this.opts.pendingEventOrdering);
|
||||
}
|
||||
|
||||
return this.pendingEventList;
|
||||
return this.pendingEventList.filter(event => {
|
||||
return !thread || thread.id === event.threadRootId;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -469,7 +465,7 @@ export class Room extends EventEmitter {
|
||||
* @return {boolean} True if an element was removed.
|
||||
*/
|
||||
public removePendingEvent(eventId: string): boolean {
|
||||
if (this.opts.pendingEventOrdering !== "detached") {
|
||||
if (this.opts.pendingEventOrdering !== PendingEventOrdering.Detached) {
|
||||
throw new Error(
|
||||
"Cannot call removePendingEvent with pendingEventOrdering == " +
|
||||
this.opts.pendingEventOrdering);
|
||||
@ -495,7 +491,7 @@ export class Room extends EventEmitter {
|
||||
* @return {boolean}
|
||||
*/
|
||||
public hasPendingEvent(eventId: string): boolean {
|
||||
if (this.opts.pendingEventOrdering !== "detached") {
|
||||
if (this.opts.pendingEventOrdering !== PendingEventOrdering.Detached) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -509,7 +505,7 @@ export class Room extends EventEmitter {
|
||||
* @return {MatrixEvent}
|
||||
*/
|
||||
public getPendingEvent(eventId: string): MatrixEvent | null {
|
||||
if (this.opts.pendingEventOrdering !== "detached") {
|
||||
if (this.opts.pendingEventOrdering !== PendingEventOrdering.Detached) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -856,8 +852,14 @@ export class Room extends EventEmitter {
|
||||
* the given event, or null if unknown
|
||||
*/
|
||||
public getTimelineForEvent(eventId: string): EventTimeline {
|
||||
const event = this.findEventById(eventId);
|
||||
const thread = this.findThreadForEvent(event);
|
||||
if (thread) {
|
||||
return thread.timelineSet.getLiveTimeline();
|
||||
} else {
|
||||
return this.getUnfilteredTimelineSet().getTimelineForEvent(eventId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new timeline to this room's unfiltered timeline set
|
||||
@ -1403,13 +1405,6 @@ export class Room extends EventEmitter {
|
||||
* unique transaction id.
|
||||
*/
|
||||
public addPendingEvent(event: MatrixEvent, txnId: string): void {
|
||||
// TODO: Enable "pending events" for threads
|
||||
// There's a fair few things to update to make them work with Threads
|
||||
// Will get back to it when the plan is to build a more polished UI ready for production
|
||||
if (this.client?.supportsExperimentalThreads() && event.threadRootId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.status !== EventStatus.SENDING && event.status !== EventStatus.NOT_SENT) {
|
||||
throw new Error("addPendingEvent called on an event with status " +
|
||||
event.status);
|
||||
@ -1426,8 +1421,8 @@ export class Room extends EventEmitter {
|
||||
EventTimeline.setEventMetadata(event, this.getLiveTimeline().getState(EventTimeline.FORWARDS), false);
|
||||
|
||||
this.txnToEvent[txnId] = event;
|
||||
|
||||
if (this.opts.pendingEventOrdering == "detached") {
|
||||
const thread = this.threads.get(event.threadRootId);
|
||||
if (this.opts.pendingEventOrdering === PendingEventOrdering.Detached && !thread) {
|
||||
if (this.pendingEventList.some((e) => e.status === EventStatus.NOT_SENT)) {
|
||||
logger.warn("Setting event as NOT_SENT due to messages in the same state");
|
||||
event.setStatus(EventStatus.NOT_SENT);
|
||||
@ -1446,13 +1441,17 @@ export class Room extends EventEmitter {
|
||||
let redactedEvent = this.pendingEventList &&
|
||||
this.pendingEventList.find(e => e.getId() === redactId);
|
||||
if (!redactedEvent) {
|
||||
redactedEvent = this.getUnfilteredTimelineSet().findEventById(redactId);
|
||||
redactedEvent = this.findEventById(redactId);
|
||||
}
|
||||
if (redactedEvent) {
|
||||
redactedEvent.markLocallyRedacted(event);
|
||||
this.emit("Room.redaction", event, this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (thread) {
|
||||
thread.timelineSet.addEventToTimeline(event,
|
||||
thread.timelineSet.getLiveTimeline(), false);
|
||||
} else {
|
||||
for (let i = 0; i < this.timelineSets.length; i++) {
|
||||
const timelineSet = this.timelineSets[i];
|
||||
@ -1467,6 +1466,7 @@ export class Room extends EventEmitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.emit("Room.localEchoUpdated", event, this, null, null);
|
||||
}
|
||||
@ -1521,6 +1521,10 @@ export class Room extends EventEmitter {
|
||||
* @param {module:models/event.MatrixEvent} event the relation event that needs to be aggregated.
|
||||
*/
|
||||
private aggregateNonLiveRelation(event: MatrixEvent): void {
|
||||
const thread = this.findThreadForEvent(event);
|
||||
if (thread) {
|
||||
thread.timelineSet.aggregateRelations(event);
|
||||
} else {
|
||||
// TODO: We should consider whether this means it would be a better
|
||||
// design to lift the relations handling up to the room instead.
|
||||
for (let i = 0; i < this.timelineSets.length; i++) {
|
||||
@ -1534,6 +1538,7 @@ export class Room extends EventEmitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deal with the echo of a message we sent.
|
||||
@ -1571,12 +1576,17 @@ export class Room extends EventEmitter {
|
||||
// any, which is good, because we don't want to try decoding it again).
|
||||
localEvent.handleRemoteEcho(remoteEvent.event);
|
||||
|
||||
const thread = this.threads.get(remoteEvent.threadRootId);
|
||||
if (thread) {
|
||||
thread.timelineSet.handleRemoteEcho(localEvent, oldEventId, newEventId);
|
||||
} else {
|
||||
for (let i = 0; i < this.timelineSets.length; i++) {
|
||||
const timelineSet = this.timelineSets[i];
|
||||
|
||||
// if it's already in the timeline, update the timeline map. If it's not, add it.
|
||||
timelineSet.handleRemoteEcho(localEvent, oldEventId, newEventId);
|
||||
}
|
||||
}
|
||||
|
||||
this.emit("Room.localEchoUpdated", localEvent, this,
|
||||
oldEventId, oldStatus);
|
||||
@ -1608,7 +1618,7 @@ export class Room extends EventEmitter {
|
||||
|
||||
// SENT races against /sync, so we have to special-case it.
|
||||
if (newStatus == EventStatus.SENT) {
|
||||
const timeline = this.getUnfilteredTimelineSet().eventIdToTimeline(newEventId);
|
||||
const timeline = this.getTimelineForEvent(newEventId);
|
||||
if (timeline) {
|
||||
// we've already received the event via the event stream.
|
||||
// nothing more to do here.
|
||||
@ -1636,12 +1646,17 @@ export class Room extends EventEmitter {
|
||||
// update the event id
|
||||
event.replaceLocalEventId(newEventId);
|
||||
|
||||
const thread = this.findThreadForEvent(event);
|
||||
if (thread) {
|
||||
thread.timelineSet.replaceEventId(oldEventId, newEventId);
|
||||
} else {
|
||||
// if the event was already in the timeline (which will be the case if
|
||||
// opts.pendingEventOrdering==chronological), we need to update the
|
||||
// timeline map.
|
||||
for (let i = 0; i < this.timelineSets.length; i++) {
|
||||
this.timelineSets[i].replaceEventId(oldEventId, newEventId);
|
||||
}
|
||||
}
|
||||
} else if (newStatus == EventStatus.CANCELLED) {
|
||||
// remove it from the pending event list, or the timeline.
|
||||
if (this.pendingEventList) {
|
||||
|
@ -55,11 +55,20 @@ export class Thread extends TypedEventEmitter<ThreadEvent> {
|
||||
this.timelineSet = new EventTimelineSet(this.room, {
|
||||
unstableClientRelationAggregation: true,
|
||||
timelineSupport: true,
|
||||
pendingEvents: false,
|
||||
pendingEvents: true,
|
||||
});
|
||||
events.forEach(event => this.addEvent(event));
|
||||
|
||||
room.on("Room.localEchoUpdated", this.onEcho);
|
||||
room.on("Room.timeline", this.onEcho);
|
||||
}
|
||||
|
||||
onEcho = (event: MatrixEvent) => {
|
||||
if (this.timelineSet.eventIdToTimeline(event.getId())) {
|
||||
this.emit(ThreadEvent.Update, this);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an event to the thread and updates
|
||||
* the tail/root references if needed
|
||||
|
Reference in New Issue
Block a user