1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-26 17:03:12 +03:00

Merge branch 'develop' into feed

This commit is contained in:
Šimon Brandner
2021-04-13 12:33:23 +02:00
committed by GitHub
14 changed files with 285 additions and 746 deletions

View File

@@ -1,13 +1,12 @@
module.exports = { module.exports = {
extends: ["matrix-org"],
plugins: [ plugins: [
"babel", "matrix-org",
], ],
extends: ["plugin:matrix-org/javascript"],
env: { env: {
browser: true, browser: true,
node: true, node: true,
}, },
rules: { rules: {
"no-var": ["warn"], "no-var": ["warn"],
"prefer-rest-params": ["warn"], "prefer-rest-params": ["warn"],
@@ -33,7 +32,7 @@ module.exports = {
}, },
overrides: [{ overrides: [{
"files": ["src/**/*.ts"], "files": ["src/**/*.ts"],
"extends": ["matrix-org/ts"], "extends": ["plugin:matrix-org/typescript"],
"rules": { "rules": {
// We're okay being explicit at the moment // We're okay being explicit at the moment
"@typescript-eslint/no-empty-interface": "off", "@typescript-eslint/no-empty-interface": "off",

View File

@@ -1,3 +1,28 @@
Changes in [9.11.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.11.0) (2021-04-12)
==================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.11.0-rc.1...v9.11.0)
* No changes since rc.1
Changes in [9.11.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.11.0-rc.1) (2021-04-07)
============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.10.0...v9.11.0-rc.1)
* Only try to cache private keys we know exist
[\#1657](https://github.com/matrix-org/matrix-js-sdk/pull/1657)
* Properly terminate screen-share calls if NoUserMedia
[\#1654](https://github.com/matrix-org/matrix-js-sdk/pull/1654)
* Attended transfer
[\#1652](https://github.com/matrix-org/matrix-js-sdk/pull/1652)
* Remove catch handlers in private key retrieval
[\#1653](https://github.com/matrix-org/matrix-js-sdk/pull/1653)
* Fixed the media fail error on caller's side
[\#1651](https://github.com/matrix-org/matrix-js-sdk/pull/1651)
* Add function to share megolm keys for historical messages, take 2
[\#1640](https://github.com/matrix-org/matrix-js-sdk/pull/1640)
* Cache cross-signing private keys if needed on bootstrap
[\#1649](https://github.com/matrix-org/matrix-js-sdk/pull/1649)
Changes in [9.10.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.10.0) (2021-03-29) Changes in [9.10.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v9.10.0) (2021-03-29)
================================================================================================== ==================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.10.0-rc.1...v9.10.0) [Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v9.10.0-rc.1...v9.10.0)

View File

@@ -1,6 +1,6 @@
{ {
"name": "matrix-js-sdk", "name": "matrix-js-sdk",
"version": "9.10.0", "version": "9.11.0",
"description": "Matrix Client-Server SDK for Javascript", "description": "Matrix Client-Server SDK for Javascript",
"scripts": { "scripts": {
"prepublishOnly": "yarn build", "prepublishOnly": "yarn build",
@@ -61,6 +61,8 @@
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.12.10", "@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10", "@babel/core": "^7.12.10",
"@babel/eslint-parser": "^7.12.10",
"@babel/eslint-plugin": "^7.12.10",
"@babel/plugin-proposal-class-properties": "^7.12.1", "@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-numeric-separator": "^7.12.7", "@babel/plugin-proposal-numeric-separator": "^7.12.7",
"@babel/plugin-proposal-object-rest-spread": "^7.12.1", "@babel/plugin-proposal-object-rest-spread": "^7.12.1",
@@ -72,15 +74,16 @@
"@types/jest": "^26.0.20", "@types/jest": "^26.0.20",
"@types/node": "12", "@types/node": "12",
"@types/request": "^2.48.5", "@types/request": "^2.48.5",
"babel-eslint": "^10.1.0", "@typescript-eslint/eslint-plugin": "^4.17.0",
"@typescript-eslint/parser": "^4.17.0",
"babel-jest": "^26.6.3", "babel-jest": "^26.6.3",
"babelify": "^10.0.0", "babelify": "^10.0.0",
"better-docs": "^2.3.2", "better-docs": "^2.3.2",
"browserify": "^17.0.0", "browserify": "^17.0.0",
"docdash": "^1.2.0", "docdash": "^1.2.0",
"eslint": "7.18.0", "eslint": "7.18.0",
"eslint-config-matrix-org": "^0.2.0", "eslint-config-google": "^0.14.0",
"eslint-plugin-babel": "^5.3.1", "eslint-plugin-matrix-org": "github:matrix-org/eslint-plugin-matrix-org#main",
"exorcist": "^1.0.1", "exorcist": "^1.0.1",
"fake-indexeddb": "^3.1.2", "fake-indexeddb": "^3.1.2",
"jest": "^26.6.3", "jest": "^26.6.3",

View File

@@ -30,7 +30,7 @@ export async function makeTestClients(userInfos, options) {
for (const [deviceId, msg] of Object.entries(devMap)) { for (const [deviceId, msg] of Object.entries(devMap)) {
if (deviceId in clientMap[userId]) { if (deviceId in clientMap[userId]) {
const event = new MatrixEvent({ const event = new MatrixEvent({
sender: this.getUserId(), // eslint-disable-line babel/no-invalid-this sender: this.getUserId(), // eslint-disable-line @babel/no-invalid-this
type: type, type: type,
content: msg, content: msg,
}); });
@@ -49,9 +49,9 @@ export async function makeTestClients(userInfos, options) {
}; };
const sendEvent = function(room, type, content) { const sendEvent = function(room, type, content) {
// make up a unique ID as the event ID // make up a unique ID as the event ID
const eventId = "$" + this.makeTxnId(); // eslint-disable-line babel/no-invalid-this const eventId = "$" + this.makeTxnId(); // eslint-disable-line @babel/no-invalid-this
const rawEvent = { const rawEvent = {
sender: this.getUserId(), // eslint-disable-line babel/no-invalid-this sender: this.getUserId(), // eslint-disable-line @babel/no-invalid-this
type: type, type: type,
content: content, content: content,
room_id: room, room_id: room,
@@ -61,13 +61,13 @@ export async function makeTestClients(userInfos, options) {
const event = new MatrixEvent(rawEvent); const event = new MatrixEvent(rawEvent);
const remoteEcho = new MatrixEvent(Object.assign({}, rawEvent, { const remoteEcho = new MatrixEvent(Object.assign({}, rawEvent, {
unsigned: { unsigned: {
transaction_id: this.makeTxnId(), // eslint-disable-line babel/no-invalid-this transaction_id: this.makeTxnId(), // eslint-disable-line @babel/no-invalid-this
}, },
})); }));
setImmediate(() => { setImmediate(() => {
for (const tc of clients) { for (const tc of clients) {
if (tc.client === this) { // eslint-disable-line babel/no-invalid-this if (tc.client === this) { // eslint-disable-line @babel/no-invalid-this
logger.log("sending remote echo!!"); logger.log("sending remote echo!!");
tc.client.emit("Room.timeline", remoteEcho); tc.client.emit("Room.timeline", remoteEcho);
} else { } else {

View File

@@ -46,8 +46,8 @@ describe("realtime-callbacks", function() {
it("should set 'this' to the global object", function() { it("should set 'this' to the global object", function() {
let passed = false; let passed = false;
const callback = function() { const callback = function() {
expect(this).toBe(global); // eslint-disable-line babel/no-invalid-this expect(this).toBe(global); // eslint-disable-line @babel/no-invalid-this
expect(this.console).toBeTruthy(); // eslint-disable-line babel/no-invalid-this expect(this.console).toBeTruthy(); // eslint-disable-line @babel/no-invalid-this
passed = true; passed = true;
}; };
callbacks.setTimeout(callback); callbacks.setTimeout(callback);

View File

@@ -3,6 +3,7 @@ import {EventStatus, MatrixEvent} from "../../src/models/event";
import {EventTimeline} from "../../src/models/event-timeline"; import {EventTimeline} from "../../src/models/event-timeline";
import {RoomState} from "../../src/models/room-state"; import {RoomState} from "../../src/models/room-state";
import {Room} from "../../src/models/room"; import {Room} from "../../src/models/room";
import {TestClient} from "../TestClient";
describe("Room", function() { describe("Room", function() {
const roomId = "!foo:bar"; const roomId = "!foo:bar";
@@ -1176,7 +1177,10 @@ describe("Room", function() {
describe("addPendingEvent", function() { describe("addPendingEvent", function() {
it("should add pending events to the pendingEventList if " + it("should add pending events to the pendingEventList if " +
"pendingEventOrdering == 'detached'", function() { "pendingEventOrdering == 'detached'", function() {
const room = new Room(roomId, null, userA, { const client = (new TestClient(
"@alice:example.com", "alicedevice",
)).client;
const room = new Room(roomId, client, userA, {
pendingEventOrdering: "detached", pendingEventOrdering: "detached",
}); });
const eventA = utils.mkMessage({ const eventA = utils.mkMessage({
@@ -1226,7 +1230,10 @@ describe("Room", function() {
describe("updatePendingEvent", function() { describe("updatePendingEvent", function() {
it("should remove cancelled events from the pending list", function() { it("should remove cancelled events from the pending list", function() {
const room = new Room(roomId, null, userA, { const client = (new TestClient(
"@alice:example.com", "alicedevice",
)).client;
const room = new Room(roomId, client, userA, {
pendingEventOrdering: "detached", pendingEventOrdering: "detached",
}); });
const eventA = utils.mkMessage({ const eventA = utils.mkMessage({

View File

@@ -57,6 +57,7 @@ export enum EventType {
KeyVerificationStart = "m.key.verification.start", KeyVerificationStart = "m.key.verification.start",
KeyVerificationCancel = "m.key.verification.cancel", KeyVerificationCancel = "m.key.verification.cancel",
KeyVerificationMac = "m.key.verification.mac", KeyVerificationMac = "m.key.verification.mac",
KeyVerificationDone = "m.key.verification.done",
// use of this is discouraged https://matrix.org/docs/spec/client_server/r0.6.1#m-room-message-feedback // use of this is discouraged https://matrix.org/docs/spec/client_server/r0.6.1#m-room-message-feedback
RoomMessageFeedback = "m.room.message.feedback", RoomMessageFeedback = "m.room.message.feedback",

View File

@@ -36,11 +36,11 @@ const DEFAULT_NAMESPACE = "matrix";
// when logging so we always get the current value of console methods. // when logging so we always get the current value of console methods.
log.methodFactory = function(methodName, logLevel, loggerName) { log.methodFactory = function(methodName, logLevel, loggerName) {
return function(...args) { return function(...args) {
/* eslint-disable babel/no-invalid-this */ /* eslint-disable @babel/no-invalid-this */
if (this.prefix) { if (this.prefix) {
args.unshift(this.prefix); args.unshift(this.prefix);
} }
/* eslint-enable babel/no-invalid-this */ /* eslint-enable @babel/no-invalid-this */
const supportedByConsole = methodName === "error" || const supportedByConsole = methodName === "error" ||
methodName === "warn" || methodName === "warn" ||
methodName === "trace" || methodName === "trace" ||

View File

@@ -168,7 +168,7 @@ export const MatrixEvent = function(
/* The txnId with which this event was sent if it was during this session, /* The txnId with which this event was sent if it was during this session,
allows for a unique ID which does not change when the event comes back down sync. allows for a unique ID which does not change when the event comes back down sync.
*/ */
this._txnId = null; this._txnId = event.txn_id || null;
/* Set an approximate timestamp for the event relative the local clock. /* Set an approximate timestamp for the event relative the local clock.
* This will inherently be approximate because it doesn't take into account * This will inherently be approximate because it doesn't take into account

View File

@@ -123,6 +123,8 @@ export function Room(roomId, client, myUserId, opts) {
opts = opts || {}; opts = opts || {};
opts.pendingEventOrdering = opts.pendingEventOrdering || "chronological"; opts.pendingEventOrdering = opts.pendingEventOrdering || "chronological";
this._client = client;
// In some cases, we add listeners for every displayed Matrix event, so it's // In some cases, we add listeners for every displayed Matrix event, so it's
// common to have quite a few more than the default limit. // common to have quite a few more than the default limit.
this.setMaxListeners(100); this.setMaxListeners(100);
@@ -189,6 +191,18 @@ export function Room(roomId, client, myUserId, opts) {
if (this._opts.pendingEventOrdering == "detached") { if (this._opts.pendingEventOrdering == "detached") {
this._pendingEventList = []; this._pendingEventList = [];
const serializedPendingEventList = client._sessionStore.store.getItem(pendingEventsKey(this.roomId));
if (serializedPendingEventList) {
JSON.parse(serializedPendingEventList)
.forEach(async serializedEvent => {
const event = new MatrixEvent(serializedEvent);
if (event.getType() === "m.room.encrypted") {
await event.attemptDecryption(this._client._crypto);
}
event.setStatus(EventStatus.NOT_SENT);
this.addPendingEvent(event, event.getTxnId());
});
}
} }
// read by megolm; boolean value - null indicates "use global value" // read by megolm; boolean value - null indicates "use global value"
@@ -197,7 +211,6 @@ export function Room(roomId, client, myUserId, opts) {
this._summaryHeroes = null; this._summaryHeroes = null;
// awaited by getEncryptionTargetMembers while room members are loading // awaited by getEncryptionTargetMembers while room members are loading
this._client = client;
if (!this._opts.lazyLoadMembers) { if (!this._opts.lazyLoadMembers) {
this._membersPromise = Promise.resolve(); this._membersPromise = Promise.resolve();
} else { } else {
@@ -205,6 +218,14 @@ export function Room(roomId, client, myUserId, opts) {
} }
} }
/**
* @param {string} roomId ID of the current room
* @returns {string} Storage key to retrieve pending events
*/
function pendingEventsKey(roomId) {
return `mx_pending_events_${roomId}`;
}
utils.inherits(Room, EventEmitter); utils.inherits(Room, EventEmitter);
/** /**
@@ -357,6 +378,31 @@ Room.prototype.getPendingEvents = function() {
return this._pendingEventList; return this._pendingEventList;
}; };
/**
* Removes a pending event for this room
*
* @param {string} eventId
* @return {boolean} True if an element was removed.
*/
Room.prototype.removePendingEvent = function(eventId) {
if (this._opts.pendingEventOrdering !== "detached") {
throw new Error(
"Cannot call removePendingEvent with pendingEventOrdering == " +
this._opts.pendingEventOrdering);
}
const removed = utils.removeElement(
this._pendingEventList,
function(ev) {
return ev.getId() == eventId;
}, false,
);
this._savePendingEvents();
return removed;
};
/** /**
* Check whether the pending event list contains a given event by ID. * Check whether the pending event list contains a given event by ID.
* If pending event ordering is not "detached" then this returns false. * If pending event ordering is not "detached" then this returns false.
@@ -1192,7 +1238,7 @@ Room.prototype._addLiveEvent = function(event, duplicateStrategy, fromCache) {
* unique transaction id. * unique transaction id.
*/ */
Room.prototype.addPendingEvent = function(event, txnId) { Room.prototype.addPendingEvent = function(event, txnId) {
if (event.status !== EventStatus.SENDING) { if (event.status !== EventStatus.SENDING && event.status !== EventStatus.NOT_SENT) {
throw new Error("addPendingEvent called on an event with status " + throw new Error("addPendingEvent called on an event with status " +
event.status); event.status);
} }
@@ -1219,7 +1265,7 @@ Room.prototype.addPendingEvent = function(event, txnId) {
event.setStatus(EventStatus.NOT_SENT); event.setStatus(EventStatus.NOT_SENT);
} }
this._pendingEventList.push(event); this._pendingEventList.push(event);
this._savePendingEvents();
if (event.isRelation()) { if (event.isRelation()) {
// For pending events, add them to the relations collection immediately. // For pending events, add them to the relations collection immediately.
// (The alternate case below already covers this as part of adding to // (The alternate case below already covers this as part of adding to
@@ -1256,6 +1302,46 @@ Room.prototype.addPendingEvent = function(event, txnId) {
this.emit("Room.localEchoUpdated", event, this, null, null); this.emit("Room.localEchoUpdated", event, this, null, null);
}; };
/**
* Persists all pending events to local storage
*
* If the current room is encrypted only encrypted events will be persisted
* all messages that are not yet encrypted will be discarded
*
* This is because the flow of EVENT_STATUS transition is
* queued => sending => encrypting => sending => sent
*
* Steps 3 and 4 are skipped for unencrypted room.
* It is better to discard an unencrypted message rather than persisting
* it locally for everyone to read
*/
Room.prototype._savePendingEvents = function() {
if (this._pendingEventList) {
const pendingEvents = this._pendingEventList.map(event => {
return {
...event.event,
txn_id: event.getTxnId(),
};
}).filter(event => {
// Filter out the unencrypted messages if the room is encrypted
const isEventEncrypted = event.type === "m.room.encrypted";
const isRoomEncrypted = this._client.isRoomEncrypted(this.roomId);
return isEventEncrypted || !isRoomEncrypted;
});
const { store } = this._client._sessionStore;
if (this._pendingEventList.length > 0) {
store.setItem(
pendingEventsKey(this.roomId),
JSON.stringify(pendingEvents),
);
} else {
store.removeItem(pendingEventsKey(this.roomId));
}
}
};
/** /**
* Used to aggregate the local echo for a relation, and also * Used to aggregate the local echo for a relation, and also
* for re-applying a relation after it's redaction has been cancelled, * for re-applying a relation after it's redaction has been cancelled,
@@ -1310,12 +1396,7 @@ Room.prototype._handleRemoteEcho = function(remoteEvent, localEvent) {
// if it's in the pending list, remove it // if it's in the pending list, remove it
if (this._pendingEventList) { if (this._pendingEventList) {
utils.removeElement( this.removePendingEvent(oldEventId);
this._pendingEventList,
function(ev) {
return ev.getId() == oldEventId;
}, false,
);
} }
// replace the event source (this will preserve the plaintext payload if // replace the event source (this will preserve the plaintext payload if
@@ -1434,6 +1515,7 @@ Room.prototype.updatePendingEvent = function(event, newStatus, newEventId) {
} }
this.removeEvent(oldEventId); this.removeEvent(oldEventId);
} }
this._savePendingEvents();
this.emit("Room.localEchoUpdated", event, this, oldEventId, oldStatus); this.emit("Room.localEchoUpdated", event, this, oldEventId, oldStatus);
}; };

View File

@@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
/* eslint-disable babel/no-invalid-this */ /* eslint-disable @babel/no-invalid-this */
import {MemoryStore} from "./memory"; import {MemoryStore} from "./memory";
import * as utils from "../utils"; import * as utils from "../utils";

View File

@@ -476,7 +476,7 @@ SyncApi.prototype.sync = function() {
this._running = true; this._running = true;
if (global.window) { if (global.window && global.window.addEventListener) {
this._onOnlineBound = this._onOnline.bind(this); this._onOnlineBound = this._onOnline.bind(this);
global.window.addEventListener("online", this._onOnlineBound, false); global.window.addEventListener("online", this._onOnlineBound, false);
} }

View File

@@ -216,9 +216,9 @@ export function getDesktopCapturerSources(): Promise<Array<DesktopCapturerSource
} }
export class CallError extends Error { export class CallError extends Error {
code : string; code: string;
constructor(code : CallErrorCode, msg: string, err: Error) { constructor(code: CallErrorCode, msg: string, err: Error) {
// Stil ldon't think there's any way to have proper nested errors // Stil ldon't think there's any way to have proper nested errors
super(msg + ": " + err); super(msg + ": " + err);
@@ -1104,7 +1104,7 @@ export class MatrixCall extends EventEmitter {
} }
} }
private callHasEnded() : boolean { private callHasEnded(): boolean {
// This exists as workaround to typescript trying to be clever and erroring // This exists as workaround to typescript trying to be clever and erroring
// when putting if (this.state === CallState.Ended) return; twice in the same // when putting if (this.state === CallState.Ended) return; twice in the same
// function, even though that function is async. // function, even though that function is async.

846
yarn.lock

File diff suppressed because it is too large Load Diff