You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-23 17:02:25 +03:00
Merge branch 'develop' into gsouquet/pr-review-linting-rules
This commit is contained in:
54
CHANGELOG.md
54
CHANGELOG.md
@@ -1,3 +1,57 @@
|
|||||||
|
Changes in [11.1.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v11.1.0) (2021-05-24)
|
||||||
|
==================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v11.1.0-rc.1...v11.1.0)
|
||||||
|
|
||||||
|
* [Release] Bump libolm version and update package name
|
||||||
|
[\#1707](https://github.com/matrix-org/matrix-js-sdk/pull/1707)
|
||||||
|
* [Release] Change call event handlers to adapt to undecrypted events
|
||||||
|
[\#1699](https://github.com/matrix-org/matrix-js-sdk/pull/1699)
|
||||||
|
|
||||||
|
Changes in [11.1.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v11.1.0-rc.1) (2021-05-19)
|
||||||
|
============================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v11.0.0...v11.1.0-rc.1)
|
||||||
|
|
||||||
|
* Fix regressed glare
|
||||||
|
[\#1690](https://github.com/matrix-org/matrix-js-sdk/pull/1690)
|
||||||
|
* Add m.reaction to EventType enum
|
||||||
|
[\#1692](https://github.com/matrix-org/matrix-js-sdk/pull/1692)
|
||||||
|
* Prioritise and reduce the amount of events decrypted on application startup
|
||||||
|
[\#1684](https://github.com/matrix-org/matrix-js-sdk/pull/1684)
|
||||||
|
* Decrypt relations before applying them to target event
|
||||||
|
[\#1696](https://github.com/matrix-org/matrix-js-sdk/pull/1696)
|
||||||
|
* Guard against duplicates in `Relations` model
|
||||||
|
|
||||||
|
Changes in [11.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v11.0.0) (2021-05-17)
|
||||||
|
==================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v11.0.0-rc.1...v11.0.0)
|
||||||
|
|
||||||
|
* [Release] Fix regressed glare
|
||||||
|
[\#1695](https://github.com/matrix-org/matrix-js-sdk/pull/1695)
|
||||||
|
|
||||||
|
Changes in [11.0.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v11.0.0-rc.1) (2021-05-11)
|
||||||
|
============================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v10.1.0...v11.0.0-rc.1)
|
||||||
|
|
||||||
|
BREAKING CHANGES
|
||||||
|
---
|
||||||
|
|
||||||
|
* `MatrixCall` and related APIs have been redesigned to support multiple streams
|
||||||
|
(see [\#1660](https://github.com/matrix-org/matrix-js-sdk/pull/1660) for more details)
|
||||||
|
|
||||||
|
All changes
|
||||||
|
---
|
||||||
|
|
||||||
|
* Switch from MSC1772 unstable prefixes to stable
|
||||||
|
[\#1679](https://github.com/matrix-org/matrix-js-sdk/pull/1679)
|
||||||
|
* Update the VoIP example to work with the new changes
|
||||||
|
[\#1680](https://github.com/matrix-org/matrix-js-sdk/pull/1680)
|
||||||
|
* Bump hosted-git-info from 2.8.8 to 2.8.9
|
||||||
|
[\#1687](https://github.com/matrix-org/matrix-js-sdk/pull/1687)
|
||||||
|
* Support for multiple streams (not MSC3077)
|
||||||
|
[\#1660](https://github.com/matrix-org/matrix-js-sdk/pull/1660)
|
||||||
|
* Tweak missing m.room.create errors to describe their source
|
||||||
|
[\#1683](https://github.com/matrix-org/matrix-js-sdk/pull/1683)
|
||||||
|
|
||||||
Changes in [10.1.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v10.1.0) (2021-05-10)
|
Changes in [10.1.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v10.1.0) (2021-05-10)
|
||||||
==================================================================================================
|
==================================================================================================
|
||||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v10.1.0-rc.1...v10.1.0)
|
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v10.1.0-rc.1...v10.1.0)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "matrix-js-sdk",
|
"name": "matrix-js-sdk",
|
||||||
"version": "10.1.0",
|
"version": "11.1.0",
|
||||||
"description": "Matrix Client-Server SDK for Javascript",
|
"description": "Matrix Client-Server SDK for Javascript",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublishOnly": "yarn build",
|
"prepublishOnly": "yarn build",
|
||||||
@@ -72,6 +72,7 @@
|
|||||||
"@babel/preset-env": "^7.12.11",
|
"@babel/preset-env": "^7.12.11",
|
||||||
"@babel/preset-typescript": "^7.12.7",
|
"@babel/preset-typescript": "^7.12.7",
|
||||||
"@babel/register": "^7.12.10",
|
"@babel/register": "^7.12.10",
|
||||||
|
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||||
"@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",
|
||||||
@@ -91,7 +92,6 @@
|
|||||||
"jest-localstorage-mock": "^2.4.6",
|
"jest-localstorage-mock": "^2.4.6",
|
||||||
"jsdoc": "^3.6.6",
|
"jsdoc": "^3.6.6",
|
||||||
"matrix-mock-request": "^1.2.3",
|
"matrix-mock-request": "^1.2.3",
|
||||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
|
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"terser": "^5.5.1",
|
"terser": "^5.5.1",
|
||||||
"tsify": "^5.0.2",
|
"tsify": "^5.0.2",
|
||||||
|
|||||||
@@ -28,11 +28,10 @@ limitations under the License.
|
|||||||
// load olm before the sdk if possible
|
// load olm before the sdk if possible
|
||||||
import '../olm-loader';
|
import '../olm-loader';
|
||||||
|
|
||||||
import { logger } from '../../src/logger';
|
import {logger} from '../../src/logger';
|
||||||
import * as testUtils from "../test-utils";
|
import * as testUtils from "../test-utils";
|
||||||
import * as utils from "../../src/utils";
|
import {TestClient} from "../TestClient";
|
||||||
import { TestClient } from "../TestClient";
|
import {CRYPTO_ENABLED} from "../../src/client";
|
||||||
import { CRYPTO_ENABLED } from "../../src/client";
|
|
||||||
|
|
||||||
let aliTestClient;
|
let aliTestClient;
|
||||||
const roomId = "!room:localhost";
|
const roomId = "!room:localhost";
|
||||||
@@ -76,7 +75,7 @@ function expectAliQueryKeys() {
|
|||||||
);
|
);
|
||||||
const result = {};
|
const result = {};
|
||||||
result[bobUserId] = bobKeys;
|
result[bobUserId] = bobKeys;
|
||||||
return { device_keys: result };
|
return {device_keys: result};
|
||||||
});
|
});
|
||||||
return aliTestClient.httpBackend.flush("/keys/query", 1);
|
return aliTestClient.httpBackend.flush("/keys/query", 1);
|
||||||
}
|
}
|
||||||
@@ -104,7 +103,7 @@ function expectBobQueryKeys() {
|
|||||||
);
|
);
|
||||||
const result = {};
|
const result = {};
|
||||||
result[aliUserId] = aliKeys;
|
result[aliUserId] = aliKeys;
|
||||||
return { device_keys: result };
|
return {device_keys: result};
|
||||||
});
|
});
|
||||||
return bobTestClient.httpBackend.flush("/keys/query", 1);
|
return bobTestClient.httpBackend.flush("/keys/query", 1);
|
||||||
}
|
}
|
||||||
@@ -133,7 +132,7 @@ function expectAliClaimKeys() {
|
|||||||
result[bobUserId] = {};
|
result[bobUserId] = {};
|
||||||
result[bobUserId][bobDeviceId] = {};
|
result[bobUserId][bobDeviceId] = {};
|
||||||
result[bobUserId][bobDeviceId][keyId] = keys[keyId];
|
result[bobUserId][bobDeviceId][keyId] = keys[keyId];
|
||||||
return { one_time_keys: result };
|
return {one_time_keys: result};
|
||||||
});
|
});
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// it can take a while to process the key query, so give it some extra
|
// it can take a while to process the key query, so give it some extra
|
||||||
@@ -145,6 +144,7 @@ function expectAliClaimKeys() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function aliDownloadsKeys() {
|
function aliDownloadsKeys() {
|
||||||
// can't query keys before bob has uploaded them
|
// can't query keys before bob has uploaded them
|
||||||
expect(bobTestClient.getSigningKey()).toBeTruthy();
|
expect(bobTestClient.getSigningKey()).toBeTruthy();
|
||||||
@@ -243,7 +243,7 @@ function bobSendsReplyMessage() {
|
|||||||
function expectAliSendMessageRequest() {
|
function expectAliSendMessageRequest() {
|
||||||
return expectSendMessageRequest(aliTestClient.httpBackend).then(function(content) {
|
return expectSendMessageRequest(aliTestClient.httpBackend).then(function(content) {
|
||||||
aliMessages.push(content);
|
aliMessages.push(content);
|
||||||
expect(utils.keys(content.ciphertext)).toEqual([bobTestClient.getDeviceKey()]);
|
expect(Object.keys(content.ciphertext)).toEqual([bobTestClient.getDeviceKey()]);
|
||||||
const ciphertext = content.ciphertext[bobTestClient.getDeviceKey()];
|
const ciphertext = content.ciphertext[bobTestClient.getDeviceKey()];
|
||||||
expect(ciphertext).toBeTruthy();
|
expect(ciphertext).toBeTruthy();
|
||||||
return ciphertext;
|
return ciphertext;
|
||||||
@@ -260,7 +260,7 @@ function expectBobSendMessageRequest() {
|
|||||||
bobMessages.push(content);
|
bobMessages.push(content);
|
||||||
const aliKeyId = "curve25519:" + aliDeviceId;
|
const aliKeyId = "curve25519:" + aliDeviceId;
|
||||||
const aliDeviceCurve25519Key = aliTestClient.deviceKeys.keys[aliKeyId];
|
const aliDeviceCurve25519Key = aliTestClient.deviceKeys.keys[aliKeyId];
|
||||||
expect(utils.keys(content.ciphertext)).toEqual([aliDeviceCurve25519Key]);
|
expect(Object.keys(content.ciphertext)).toEqual([aliDeviceCurve25519Key]);
|
||||||
const ciphertext = content.ciphertext[aliDeviceCurve25519Key];
|
const ciphertext = content.ciphertext[aliDeviceCurve25519Key];
|
||||||
expect(ciphertext).toBeTruthy();
|
expect(ciphertext).toBeTruthy();
|
||||||
return ciphertext;
|
return ciphertext;
|
||||||
@@ -269,7 +269,7 @@ function expectBobSendMessageRequest() {
|
|||||||
|
|
||||||
function sendMessage(client) {
|
function sendMessage(client) {
|
||||||
return client.sendMessage(
|
return client.sendMessage(
|
||||||
roomId, { msgtype: "m.text", body: "Hello, World" },
|
roomId, {msgtype: "m.text", body: "Hello, World"},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,6 +357,7 @@ function recvMessage(httpBackend, client, sender, message) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send an initial sync response to the client (which just includes the member
|
* Send an initial sync response to the client (which just includes the member
|
||||||
* list for our test room).
|
* list for our test room).
|
||||||
@@ -394,6 +395,7 @@ function firstSync(testClient) {
|
|||||||
return testClient.flushSync();
|
return testClient.flushSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
describe("MatrixClient crypto", function() {
|
describe("MatrixClient crypto", function() {
|
||||||
if (!CRYPTO_ENABLED) {
|
if (!CRYPTO_ENABLED) {
|
||||||
return;
|
return;
|
||||||
@@ -475,7 +477,7 @@ describe("MatrixClient crypto", function() {
|
|||||||
).respond(200, function(path, content) {
|
).respond(200, function(path, content) {
|
||||||
const result = {};
|
const result = {};
|
||||||
result[bobUserId] = bobKeys;
|
result[bobUserId] = bobKeys;
|
||||||
return { device_keys: result };
|
return {device_keys: result};
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
@@ -517,7 +519,7 @@ describe("MatrixClient crypto", function() {
|
|||||||
).respond(200, function(path, content) {
|
).respond(200, function(path, content) {
|
||||||
const result = {};
|
const result = {};
|
||||||
result[bobUserId] = bobKeys;
|
result[bobUserId] = bobKeys;
|
||||||
return { device_keys: result };
|
return {device_keys: result};
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
@@ -531,6 +533,7 @@ describe("MatrixClient crypto", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it("Bob starts his client and uploads device keys and one-time keys", function() {
|
it("Bob starts his client and uploads device keys and one-time keys", function() {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => bobTestClient.start())
|
.then(() => bobTestClient.start())
|
||||||
@@ -542,7 +545,7 @@ describe("MatrixClient crypto", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Ali sends a message", function() {
|
it("Ali sends a message", function() {
|
||||||
aliTestClient.expectKeyQuery({ device_keys: { [aliUserId]: {} } });
|
aliTestClient.expectKeyQuery({device_keys: {[aliUserId]: {}}});
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => aliTestClient.start())
|
.then(() => aliTestClient.start())
|
||||||
.then(() => bobTestClient.start())
|
.then(() => bobTestClient.start())
|
||||||
@@ -552,7 +555,7 @@ describe("MatrixClient crypto", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Bob receives a message", function() {
|
it("Bob receives a message", function() {
|
||||||
aliTestClient.expectKeyQuery({ device_keys: { [aliUserId]: {} } });
|
aliTestClient.expectKeyQuery({device_keys: {[aliUserId]: {}}});
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => aliTestClient.start())
|
.then(() => aliTestClient.start())
|
||||||
.then(() => bobTestClient.start())
|
.then(() => bobTestClient.start())
|
||||||
@@ -563,7 +566,7 @@ describe("MatrixClient crypto", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Bob receives a message with a bogus sender", function() {
|
it("Bob receives a message with a bogus sender", function() {
|
||||||
aliTestClient.expectKeyQuery({ device_keys: { [aliUserId]: {} } });
|
aliTestClient.expectKeyQuery({device_keys: {[aliUserId]: {}}});
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => aliTestClient.start())
|
.then(() => aliTestClient.start())
|
||||||
.then(() => bobTestClient.start())
|
.then(() => bobTestClient.start())
|
||||||
@@ -617,7 +620,7 @@ describe("MatrixClient crypto", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Ali blocks Bob's device", function() {
|
it("Ali blocks Bob's device", function() {
|
||||||
aliTestClient.expectKeyQuery({ device_keys: { [aliUserId]: {} } });
|
aliTestClient.expectKeyQuery({device_keys: {[aliUserId]: {}}});
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => aliTestClient.start())
|
.then(() => aliTestClient.start())
|
||||||
.then(() => bobTestClient.start())
|
.then(() => bobTestClient.start())
|
||||||
@@ -637,7 +640,7 @@ describe("MatrixClient crypto", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Bob receives two pre-key messages", function() {
|
it("Bob receives two pre-key messages", function() {
|
||||||
aliTestClient.expectKeyQuery({ device_keys: { [aliUserId]: {} } });
|
aliTestClient.expectKeyQuery({device_keys: {[aliUserId]: {}}});
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => aliTestClient.start())
|
.then(() => aliTestClient.start())
|
||||||
.then(() => bobTestClient.start())
|
.then(() => bobTestClient.start())
|
||||||
@@ -650,8 +653,8 @@ describe("MatrixClient crypto", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("Bob replies to the message", function() {
|
it("Bob replies to the message", function() {
|
||||||
aliTestClient.expectKeyQuery({ device_keys: { [aliUserId]: {} } });
|
aliTestClient.expectKeyQuery({device_keys: {[aliUserId]: {}}});
|
||||||
bobTestClient.expectKeyQuery({ device_keys: { [bobUserId]: {} } });
|
bobTestClient.expectKeyQuery({device_keys: {[bobUserId]: {}}});
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => aliTestClient.start())
|
.then(() => aliTestClient.start())
|
||||||
.then(() => bobTestClient.start())
|
.then(() => bobTestClient.start())
|
||||||
@@ -669,7 +672,7 @@ describe("MatrixClient crypto", function() {
|
|||||||
it("Ali does a key query when encryption is enabled", function() {
|
it("Ali does a key query when encryption is enabled", function() {
|
||||||
// enabling encryption in the room should make alice download devices
|
// enabling encryption in the room should make alice download devices
|
||||||
// for both members.
|
// for both members.
|
||||||
aliTestClient.expectKeyQuery({ device_keys: { [aliUserId]: {} } });
|
aliTestClient.expectKeyQuery({device_keys: {[aliUserId]: {}}});
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => aliTestClient.start())
|
.then(() => aliTestClient.start())
|
||||||
.then(() => firstSync(aliTestClient))
|
.then(() => firstSync(aliTestClient))
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import anotherjson from "another-json";
|
import anotherjson from "another-json";
|
||||||
import * as utils from "../../src/utils";
|
|
||||||
import * as testUtils from "../test-utils";
|
import * as testUtils from "../test-utils";
|
||||||
import { TestClient } from "../TestClient";
|
import { TestClient } from "../TestClient";
|
||||||
import { logger } from "../../src/logger";
|
import { logger } from "../../src/logger";
|
||||||
@@ -32,7 +31,7 @@ const ROOM_ID = "!room:id";
|
|||||||
*/
|
*/
|
||||||
function createOlmSession(olmAccount, recipientTestClient) {
|
function createOlmSession(olmAccount, recipientTestClient) {
|
||||||
return recipientTestClient.awaitOneTimeKeyUpload().then((keys) => {
|
return recipientTestClient.awaitOneTimeKeyUpload().then((keys) => {
|
||||||
const otkId = utils.keys(keys)[0];
|
const otkId = Object.keys(keys)[0];
|
||||||
const otk = keys[otkId];
|
const otk = keys[otkId];
|
||||||
|
|
||||||
const session = new global.Olm.Session();
|
const session = new global.Olm.Session();
|
||||||
@@ -256,7 +255,7 @@ describe("megolm", function() {
|
|||||||
const testOneTimeKeys = JSON.parse(testOlmAccount.one_time_keys());
|
const testOneTimeKeys = JSON.parse(testOlmAccount.one_time_keys());
|
||||||
testOlmAccount.mark_keys_as_published();
|
testOlmAccount.mark_keys_as_published();
|
||||||
|
|
||||||
const keyId = utils.keys(testOneTimeKeys.curve25519)[0];
|
const keyId = Object.keys(testOneTimeKeys.curve25519)[0];
|
||||||
const oneTimeKey = testOneTimeKeys.curve25519[keyId];
|
const oneTimeKey = testOneTimeKeys.curve25519[keyId];
|
||||||
const keyResult = {
|
const keyResult = {
|
||||||
'key': oneTimeKey,
|
'key': oneTimeKey,
|
||||||
@@ -483,8 +482,9 @@ describe("megolm", function() {
|
|||||||
return aliceTestClient.flushSync().then(() => {
|
return aliceTestClient.flushSync().then(() => {
|
||||||
return aliceTestClient.flushSync();
|
return aliceTestClient.flushSync();
|
||||||
});
|
});
|
||||||
}).then(function() {
|
}).then(async function() {
|
||||||
const room = aliceTestClient.client.getRoom(ROOM_ID);
|
const room = aliceTestClient.client.getRoom(ROOM_ID);
|
||||||
|
await room.decryptCriticalEvents();
|
||||||
const event = room.getLiveTimeline().getEvents()[0];
|
const event = room.getLiveTimeline().getEvents()[0];
|
||||||
expect(event.getContent().body).toEqual('42');
|
expect(event.getContent().body).toEqual('42');
|
||||||
});
|
});
|
||||||
@@ -930,8 +930,9 @@ describe("megolm", function() {
|
|||||||
|
|
||||||
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse);
|
aliceTestClient.httpBackend.when("GET", "/sync").respond(200, syncResponse);
|
||||||
return aliceTestClient.flushSync();
|
return aliceTestClient.flushSync();
|
||||||
}).then(function() {
|
}).then(async function() {
|
||||||
const room = aliceTestClient.client.getRoom(ROOM_ID);
|
const room = aliceTestClient.client.getRoom(ROOM_ID);
|
||||||
|
await room.decryptCriticalEvents();
|
||||||
const event = room.getLiveTimeline().getEvents()[0];
|
const event = room.getLiveTimeline().getEvents()[0];
|
||||||
expect(event.getContent().body).toEqual('42');
|
expect(event.getContent().body).toEqual('42');
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import * as utils from "../src/utils";
|
|||||||
|
|
||||||
// try to load the olm library.
|
// try to load the olm library.
|
||||||
try {
|
try {
|
||||||
global.Olm = require('olm');
|
global.Olm = require('@matrix-org/olm');
|
||||||
logger.log('loaded libolm');
|
logger.log('loaded libolm');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.warn("unable to run crypto tests: libolm not available");
|
logger.warn("unable to run crypto tests: libolm not available");
|
||||||
|
|||||||
@@ -210,18 +210,21 @@ MockStorageApi.prototype = {
|
|||||||
* @returns {Promise} promise which resolves (to `event`) when the event has been decrypted
|
* @returns {Promise} promise which resolves (to `event`) when the event has been decrypted
|
||||||
*/
|
*/
|
||||||
export function awaitDecryption(event) {
|
export function awaitDecryption(event) {
|
||||||
if (!event.isBeingDecrypted()) {
|
// An event is not always decrypted ahead of time
|
||||||
return Promise.resolve(event);
|
// getClearContent is a good signal to know whether an event has been decrypted
|
||||||
}
|
// already
|
||||||
|
if (event.getClearContent() !== null) {
|
||||||
|
return event;
|
||||||
|
} else {
|
||||||
|
logger.log(`${Date.now()} event ${event.getId()} is being decrypted; waiting`);
|
||||||
|
|
||||||
logger.log(`${Date.now()} event ${event.getId()} is being decrypted; waiting`);
|
return new Promise((resolve, reject) => {
|
||||||
|
event.once('Event.decrypted', (ev) => {
|
||||||
return new Promise((resolve, reject) => {
|
logger.log(`${Date.now()} event ${event.getId()} now decrypted`);
|
||||||
event.once('Event.decrypted', (ev) => {
|
resolve(ev);
|
||||||
logger.log(`${Date.now()} event ${event.getId()} now decrypted`);
|
});
|
||||||
resolve(ev);
|
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function HttpResponse(
|
export function HttpResponse(
|
||||||
|
|||||||
@@ -26,40 +26,6 @@ describe("utils", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("forEach", function() {
|
|
||||||
it("should be invoked for each element", function() {
|
|
||||||
const arr = [];
|
|
||||||
utils.forEach([55, 66, 77], function(element) {
|
|
||||||
arr.push(element);
|
|
||||||
});
|
|
||||||
expect(arr).toEqual([55, 66, 77]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("findElement", function() {
|
|
||||||
it("should find only 1 element if there is a match", function() {
|
|
||||||
const matchFn = function() {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
const arr = [55, 66, 77];
|
|
||||||
expect(utils.findElement(arr, matchFn)).toEqual(55);
|
|
||||||
});
|
|
||||||
it("should be able to find in reverse order", function() {
|
|
||||||
const matchFn = function() {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
const arr = [55, 66, 77];
|
|
||||||
expect(utils.findElement(arr, matchFn, true)).toEqual(77);
|
|
||||||
});
|
|
||||||
it("should find nothing if the function never returns true", function() {
|
|
||||||
const matchFn = function() {
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
const arr = [55, 66, 77];
|
|
||||||
expect(utils.findElement(arr, matchFn)).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("removeElement", function() {
|
describe("removeElement", function() {
|
||||||
it("should remove only 1 element if there is a match", function() {
|
it("should remove only 1 element if there is a match", function() {
|
||||||
const matchFn = function() {
|
const matchFn = function() {
|
||||||
@@ -103,20 +69,6 @@ describe("utils", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("isArray", function() {
|
|
||||||
it("should return true for arrays", function() {
|
|
||||||
expect(utils.isArray([])).toBe(true);
|
|
||||||
expect(utils.isArray([5, 3, 7])).toBe(true);
|
|
||||||
|
|
||||||
expect(utils.isArray()).toBe(false);
|
|
||||||
expect(utils.isArray(null)).toBe(false);
|
|
||||||
expect(utils.isArray({})).toBe(false);
|
|
||||||
expect(utils.isArray("foo")).toBe(false);
|
|
||||||
expect(utils.isArray(555)).toBe(false);
|
|
||||||
expect(utils.isArray(function() {})).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("checkObjectHasKeys", function() {
|
describe("checkObjectHasKeys", function() {
|
||||||
it("should throw for missing keys", function() {
|
it("should throw for missing keys", function() {
|
||||||
expect(function() {
|
expect(function() {
|
||||||
|
|||||||
@@ -36,9 +36,8 @@ export enum EventType {
|
|||||||
*/
|
*/
|
||||||
RoomAliases = "m.room.aliases", // deprecated https://matrix.org/docs/spec/client_server/r0.6.1#historical-events
|
RoomAliases = "m.room.aliases", // deprecated https://matrix.org/docs/spec/client_server/r0.6.1#historical-events
|
||||||
|
|
||||||
// Spaces MSC1772
|
SpaceChild = "m.space.child",
|
||||||
SpaceChild = "org.matrix.msc1772.space.child",
|
SpaceParent = "m.space.parent",
|
||||||
SpaceParent = "org.matrix.msc1772.space.parent",
|
|
||||||
|
|
||||||
// Room timeline events
|
// Room timeline events
|
||||||
RoomRedaction = "m.room.redaction",
|
RoomRedaction = "m.room.redaction",
|
||||||
@@ -62,6 +61,7 @@ export enum EventType {
|
|||||||
KeyVerificationDone = "m.key.verification.done",
|
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",
|
||||||
|
Reaction = "m.reaction",
|
||||||
|
|
||||||
// Room ephemeral events
|
// Room ephemeral events
|
||||||
Typing = "m.typing",
|
Typing = "m.typing",
|
||||||
@@ -95,8 +95,8 @@ export enum MsgType {
|
|||||||
Video = "m.video",
|
Video = "m.video",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RoomCreateTypeField = "org.matrix.msc1772.type"; // Spaces MSC1772
|
export const RoomCreateTypeField = "type";
|
||||||
|
|
||||||
export enum RoomType {
|
export enum RoomType {
|
||||||
Space = "org.matrix.msc1772.space", // Spaces MSC1772
|
Space = "m.space",
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/@types/global.d.ts
vendored
2
src/@types/global.d.ts
vendored
@@ -15,7 +15,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// this is needed to tell TS about global.Olm
|
// this is needed to tell TS about global.Olm
|
||||||
import * as Olm from "olm"; // eslint-disable-line @typescript-eslint/no-unused-vars
|
import * as Olm from "@matrix-org/olm"; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
|
|
||||||
export {};
|
export {};
|
||||||
|
|
||||||
|
|||||||
@@ -352,6 +352,10 @@ export function MatrixClient(opts) {
|
|||||||
if (call) {
|
if (call) {
|
||||||
this._callEventHandler = new CallEventHandler(this);
|
this._callEventHandler = new CallEventHandler(this);
|
||||||
this._supportsVoip = true;
|
this._supportsVoip = true;
|
||||||
|
// Start listening for calls after the initial sync is done
|
||||||
|
// We do not need to backfill the call event buffer
|
||||||
|
// with encrypted events that might never get decrypted
|
||||||
|
this.on("sync", this._startCallEventHandler);
|
||||||
} else {
|
} else {
|
||||||
this._callEventHandler = null;
|
this._callEventHandler = null;
|
||||||
}
|
}
|
||||||
@@ -3971,9 +3975,9 @@ MatrixClient.prototype.scrollback = function(room, limit, callback) {
|
|||||||
limit,
|
limit,
|
||||||
'b');
|
'b');
|
||||||
}).then(function(res) {
|
}).then(function(res) {
|
||||||
const matrixEvents = utils.map(res.chunk, _PojoToMatrixEventMapper(self));
|
const matrixEvents = res.chunk.map(_PojoToMatrixEventMapper(self));
|
||||||
if (res.state) {
|
if (res.state) {
|
||||||
const stateEvents = utils.map(res.state, _PojoToMatrixEventMapper(self));
|
const stateEvents = res.state.map(_PojoToMatrixEventMapper(self));
|
||||||
room.currentState.setUnknownStateEvents(stateEvents);
|
room.currentState.setUnknownStateEvents(stateEvents);
|
||||||
}
|
}
|
||||||
room.addEventsToTimeline(matrixEvents, true, room.getLiveTimeline());
|
room.addEventsToTimeline(matrixEvents, true, room.getLiveTimeline());
|
||||||
@@ -4062,16 +4066,16 @@ MatrixClient.prototype.getEventTimeline = function(timelineSet, eventId) {
|
|||||||
const events = res.events_after
|
const events = res.events_after
|
||||||
.concat([res.event])
|
.concat([res.event])
|
||||||
.concat(res.events_before);
|
.concat(res.events_before);
|
||||||
const matrixEvents = utils.map(events, self.getEventMapper());
|
const matrixEvents = events.map(self.getEventMapper());
|
||||||
|
|
||||||
let timeline = timelineSet.getTimelineForEvent(matrixEvents[0].getId());
|
let timeline = timelineSet.getTimelineForEvent(matrixEvents[0].getId());
|
||||||
if (!timeline) {
|
if (!timeline) {
|
||||||
timeline = timelineSet.addTimeline();
|
timeline = timelineSet.addTimeline();
|
||||||
timeline.initialiseState(utils.map(res.state,
|
timeline.initialiseState(res.state.map(
|
||||||
self.getEventMapper()));
|
self.getEventMapper()));
|
||||||
timeline.getState(EventTimeline.FORWARDS).paginationToken = res.end;
|
timeline.getState(EventTimeline.FORWARDS).paginationToken = res.end;
|
||||||
} else {
|
} else {
|
||||||
const stateEvents = utils.map(res.state, self.getEventMapper());
|
const stateEvents = res.state.map(self.getEventMapper());
|
||||||
timeline.getState(EventTimeline.BACKWARDS).setUnknownStateEvents(stateEvents);
|
timeline.getState(EventTimeline.BACKWARDS).setUnknownStateEvents(stateEvents);
|
||||||
}
|
}
|
||||||
timelineSet.addEventsToTimeline(matrixEvents, true, timeline, res.start);
|
timelineSet.addEventsToTimeline(matrixEvents, true, timeline, res.start);
|
||||||
@@ -4234,11 +4238,11 @@ MatrixClient.prototype.paginateEventTimeline = function(eventTimeline, opts) {
|
|||||||
promise.then(function(res) {
|
promise.then(function(res) {
|
||||||
if (res.state) {
|
if (res.state) {
|
||||||
const roomState = eventTimeline.getState(dir);
|
const roomState = eventTimeline.getState(dir);
|
||||||
const stateEvents = utils.map(res.state, self.getEventMapper());
|
const stateEvents = res.state.map(self.getEventMapper());
|
||||||
roomState.setUnknownStateEvents(stateEvents);
|
roomState.setUnknownStateEvents(stateEvents);
|
||||||
}
|
}
|
||||||
const token = res.end;
|
const token = res.end;
|
||||||
const matrixEvents = utils.map(res.chunk, self.getEventMapper());
|
const matrixEvents = res.chunk.map(self.getEventMapper());
|
||||||
eventTimeline.getTimelineSet()
|
eventTimeline.getTimelineSet()
|
||||||
.addEventsToTimeline(matrixEvents, backwards, eventTimeline, token);
|
.addEventsToTimeline(matrixEvents, backwards, eventTimeline, token);
|
||||||
|
|
||||||
@@ -4977,6 +4981,13 @@ MatrixClient.prototype.getOpenIdToken = function() {
|
|||||||
// VoIP operations
|
// VoIP operations
|
||||||
// ===============
|
// ===============
|
||||||
|
|
||||||
|
MatrixClient.prototype._startCallEventHandler = function() {
|
||||||
|
if (this.isInitialSyncComplete()) {
|
||||||
|
this._callEventHandler.start();
|
||||||
|
this.off("sync", this._startCallEventHandler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {module:client.callback} callback Optional.
|
* @param {module:client.callback} callback Optional.
|
||||||
* @return {Promise} Resolves: TODO
|
* @return {Promise} Resolves: TODO
|
||||||
@@ -5544,8 +5555,9 @@ function _resolve(callback, resolve, res) {
|
|||||||
resolve(res);
|
resolve(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _PojoToMatrixEventMapper(client, options) {
|
function _PojoToMatrixEventMapper(client, options = {}) {
|
||||||
const preventReEmit = Boolean(options && options.preventReEmit);
|
const preventReEmit = Boolean(options.preventReEmit);
|
||||||
|
const decrypt = options.decrypt !== false;
|
||||||
function mapper(plainOldJsObject) {
|
function mapper(plainOldJsObject) {
|
||||||
const event = new MatrixEvent(plainOldJsObject);
|
const event = new MatrixEvent(plainOldJsObject);
|
||||||
if (event.isEncrypted()) {
|
if (event.isEncrypted()) {
|
||||||
@@ -5554,7 +5566,9 @@ function _PojoToMatrixEventMapper(client, options) {
|
|||||||
"Event.decrypted",
|
"Event.decrypted",
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
event.attemptDecryption(client._crypto);
|
if (decrypt) {
|
||||||
|
client.decryptEventIfNeeded(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!preventReEmit) {
|
if (!preventReEmit) {
|
||||||
client.reEmitter.reEmit(event, ["Event.replaced"]);
|
client.reEmitter.reEmit(event, ["Event.replaced"]);
|
||||||
@@ -5567,6 +5581,7 @@ function _PojoToMatrixEventMapper(client, options) {
|
|||||||
/**
|
/**
|
||||||
* @param {object} [options]
|
* @param {object} [options]
|
||||||
* @param {bool} options.preventReEmit don't reemit events emitted on an event mapped by this mapper on the client
|
* @param {bool} options.preventReEmit don't reemit events emitted on an event mapped by this mapper on the client
|
||||||
|
* @param {bool} options.decrypt decrypt event proactively
|
||||||
* @return {Function}
|
* @return {Function}
|
||||||
*/
|
*/
|
||||||
MatrixClient.prototype.getEventMapper = function(options = undefined) {
|
MatrixClient.prototype.getEventMapper = function(options = undefined) {
|
||||||
@@ -5594,6 +5609,26 @@ MatrixClient.prototype.generateClientSecret = function() {
|
|||||||
return randomString(32);
|
return randomString(32);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to decrypt an event
|
||||||
|
* @param {MatrixEvent} event The event to decrypt
|
||||||
|
* @returns {Promise<void>} A decryption promise
|
||||||
|
* @param {object} options
|
||||||
|
* @param {bool} options.isRetry True if this is a retry (enables more logging)
|
||||||
|
* @param {bool} options.emit Emits "event.decrypted" if set to true
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.decryptEventIfNeeded = function(event, options) {
|
||||||
|
if (event.shouldAttemptDecryption()) {
|
||||||
|
event.attemptDecryption(this._crypto, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.isBeingDecrypted()) {
|
||||||
|
return event._decryptionPromise;
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// MatrixClient Event JSDocs
|
// MatrixClient Event JSDocs
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export function getHttpUriForMxc(baseUrl, mxc, width, height,
|
|||||||
if (resizeMethod) {
|
if (resizeMethod) {
|
||||||
params.method = resizeMethod;
|
params.method = resizeMethod;
|
||||||
}
|
}
|
||||||
if (utils.keys(params).length > 0) {
|
if (Object.keys(params).length > 0) {
|
||||||
// these are thumbnailing params so they probably want the
|
// these are thumbnailing params so they probably want the
|
||||||
// thumbnailing API...
|
// thumbnailing API...
|
||||||
prefix = "/_matrix/media/r0/thumbnail/";
|
prefix = "/_matrix/media/r0/thumbnail/";
|
||||||
@@ -72,6 +72,6 @@ export function getHttpUriForMxc(baseUrl, mxc, width, height,
|
|||||||
serverAndMediaId = serverAndMediaId.substr(0, fragmentOffset);
|
serverAndMediaId = serverAndMediaId.substr(0, fragmentOffset);
|
||||||
}
|
}
|
||||||
return baseUrl + prefix + serverAndMediaId +
|
return baseUrl + prefix + serverAndMediaId +
|
||||||
(utils.keys(params).length === 0 ? "" :
|
(Object.keys(params).length === 0 ? "" :
|
||||||
("?" + utils.encodeParams(params))) + fragment;
|
("?" + utils.encodeParams(params))) + fragment;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { logger } from '../logger';
|
import {logger} from '../logger';
|
||||||
import * as utils from '../utils';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal module. Management of outgoing room key requests.
|
* Internal module. Management of outgoing room key requests.
|
||||||
@@ -496,7 +495,7 @@ function stringifyRequestBody(requestBody) {
|
|||||||
|
|
||||||
function stringifyRecipientList(recipients) {
|
function stringifyRecipientList(recipients) {
|
||||||
return '['
|
return '['
|
||||||
+ utils.map(recipients, (r) => `${r.userId}:${r.deviceId}`).join(",")
|
+ recipients.map((r) => `${r.userId}:${r.deviceId}`).join(",")
|
||||||
+ ']';
|
+ ']';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1074,7 +1074,7 @@ MegolmEncryption.prototype._removeUnknownDevices = function(devicesInRoom) {
|
|||||||
*/
|
*/
|
||||||
MegolmEncryption.prototype._getDevicesInRoom = async function(room) {
|
MegolmEncryption.prototype._getDevicesInRoom = async function(room) {
|
||||||
const members = await room.getEncryptionTargetMembers();
|
const members = await room.getEncryptionTargetMembers();
|
||||||
const roomMembers = utils.map(members, function(u) {
|
const roomMembers = members.map(function(u) {
|
||||||
return u.userId;
|
return u.userId;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1368,7 +1368,7 @@ MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
|
|||||||
if (event.getType() == "m.forwarded_room_key") {
|
if (event.getType() == "m.forwarded_room_key") {
|
||||||
exportFormat = true;
|
exportFormat = true;
|
||||||
forwardingKeyChain = content.forwarding_curve25519_key_chain;
|
forwardingKeyChain = content.forwarding_curve25519_key_chain;
|
||||||
if (!utils.isArray(forwardingKeyChain)) {
|
if (!Array.isArray(forwardingKeyChain)) {
|
||||||
forwardingKeyChain = [];
|
forwardingKeyChain = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1689,7 +1689,7 @@ MegolmDecryption.prototype._retryDecryption = async function(senderKey, sessionI
|
|||||||
|
|
||||||
await Promise.all([...pending].map(async (ev) => {
|
await Promise.all([...pending].map(async (ev) => {
|
||||||
try {
|
try {
|
||||||
await ev.attemptDecryption(this._crypto, true);
|
await ev.attemptDecryption(this._crypto, { isRetry: true });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// don't die if something goes wrong
|
// don't die if something goes wrong
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ OlmEncryption.prototype.encryptMessage = async function(room, eventType, content
|
|||||||
|
|
||||||
const members = await room.getEncryptionTargetMembers();
|
const members = await room.getEncryptionTargetMembers();
|
||||||
|
|
||||||
const users = utils.map(members, function(u) {
|
const users = members.map(function(u) {
|
||||||
return u.userId;
|
return u.userId;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ export function Crypto(baseApis, sessionStore, userId, deviceId,
|
|||||||
// map from algorithm to DecryptionAlgorithm instance, for each room
|
// map from algorithm to DecryptionAlgorithm instance, for each room
|
||||||
this._roomDecryptors = {};
|
this._roomDecryptors = {};
|
||||||
|
|
||||||
this._supportedAlgorithms = utils.keys(
|
this._supportedAlgorithms = Object.keys(
|
||||||
algorithms.DECRYPTION_CLASSES,
|
algorithms.DECRYPTION_CLASSES,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -3317,7 +3317,10 @@ Crypto.prototype._onToDeviceEvent = function(event) {
|
|||||||
this._onKeyVerificationMessage(event);
|
this._onKeyVerificationMessage(event);
|
||||||
} else if (event.getContent().msgtype === "m.bad.encrypted") {
|
} else if (event.getContent().msgtype === "m.bad.encrypted") {
|
||||||
this._onToDeviceBadEncrypted(event);
|
this._onToDeviceBadEncrypted(event);
|
||||||
} else if (event.isBeingDecrypted()) {
|
} else if (event.isBeingDecrypted() || event.shouldAttemptDecryption()) {
|
||||||
|
if (!event.isBeingDecrypted()) {
|
||||||
|
event.attemptDecryption(this);
|
||||||
|
}
|
||||||
// once the event has been decrypted, try again
|
// once the event has been decrypted, try again
|
||||||
event.once('Event.decrypted', (ev) => {
|
event.once('Event.decrypted', (ev) => {
|
||||||
this._onToDeviceEvent(ev);
|
this._onToDeviceEvent(ev);
|
||||||
|
|||||||
@@ -803,7 +803,8 @@ const requestCallback = function(
|
|||||||
}
|
}
|
||||||
if (!err) {
|
if (!err) {
|
||||||
try {
|
try {
|
||||||
if (response.statusCode >= 400) {
|
const httpStatus = response.status || response.statusCode; // XMLHttpRequest vs http.IncomingMessage
|
||||||
|
if (httpStatus >= 400) {
|
||||||
err = parseErrorResponse(response, body);
|
err = parseErrorResponse(response, body);
|
||||||
} else if (bodyParser) {
|
} else if (bodyParser) {
|
||||||
body = bodyParser(body);
|
body = bodyParser(body);
|
||||||
@@ -818,7 +819,7 @@ const requestCallback = function(
|
|||||||
userDefinedCallback(err);
|
userDefinedCallback(err);
|
||||||
} else {
|
} else {
|
||||||
const res = {
|
const res = {
|
||||||
code: response.statusCode,
|
code: response.status || response.statusCode, // XMLHttpRequest vs http.IncomingMessage
|
||||||
|
|
||||||
// XXX: why do we bother with this? it doesn't work for
|
// XXX: why do we bother with this? it doesn't work for
|
||||||
// XMLHttpRequest, so clearly we don't use it.
|
// XMLHttpRequest, so clearly we don't use it.
|
||||||
@@ -842,7 +843,7 @@ const requestCallback = function(
|
|||||||
* @returns {Error}
|
* @returns {Error}
|
||||||
*/
|
*/
|
||||||
function parseErrorResponse(response, body) {
|
function parseErrorResponse(response, body) {
|
||||||
const httpStatus = response.statusCode;
|
const httpStatus = response.status || response.statusCode; // XMLHttpRequest vs http.IncomingMessage
|
||||||
const contentType = getResponseContentType(response);
|
const contentType = getResponseContentType(response);
|
||||||
|
|
||||||
let err;
|
let err;
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import * as utils from "./utils";
|
|||||||
import request from "request";
|
import request from "request";
|
||||||
|
|
||||||
matrixcs.request(request);
|
matrixcs.request(request);
|
||||||
utils.runPolyfills();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ EventTimelineSet.prototype.findEventById = function(eventId) {
|
|||||||
if (!tl) {
|
if (!tl) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return utils.findElement(tl.getEvents(), function(ev) {
|
return tl.getEvents().find(function(ev) {
|
||||||
return ev.getId() == eventId;
|
return ev.getId() == eventId;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -739,16 +739,11 @@ EventTimelineSet.prototype.setRelationsTarget = function(event) {
|
|||||||
if (!relationsForEvent) {
|
if (!relationsForEvent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// don't need it for non m.replace relations for now
|
|
||||||
const relationsWithRelType = relationsForEvent["m.replace"];
|
|
||||||
if (!relationsWithRelType) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// only doing replacements for messages for now (e.g. edits)
|
|
||||||
const relationsWithEventType = relationsWithRelType["m.room.message"];
|
|
||||||
|
|
||||||
if (relationsWithEventType) {
|
for (const relationsWithRelType of Object.values(relationsForEvent)) {
|
||||||
relationsWithEventType.setTargetEvent(event);
|
for (const relationsWithEventType of Object.values(relationsWithRelType)) {
|
||||||
|
relationsWithEventType.setTargetEvent(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -768,7 +763,7 @@ EventTimelineSet.prototype.aggregateRelations = function(event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the event is currently encrypted, wait until it has been decrypted.
|
// If the event is currently encrypted, wait until it has been decrypted.
|
||||||
if (event.isBeingDecrypted()) {
|
if (event.isBeingDecrypted() || event.shouldAttemptDecryption()) {
|
||||||
event.once("Event.decrypted", () => {
|
event.once("Event.decrypted", () => {
|
||||||
this.aggregateRelations(event);
|
this.aggregateRelations(event);
|
||||||
});
|
});
|
||||||
@@ -796,7 +791,6 @@ EventTimelineSet.prototype.aggregateRelations = function(event) {
|
|||||||
}
|
}
|
||||||
let relationsWithEventType = relationsWithRelType[eventType];
|
let relationsWithEventType = relationsWithRelType[eventType];
|
||||||
|
|
||||||
let isNewRelations = false;
|
|
||||||
let relatesToEvent;
|
let relatesToEvent;
|
||||||
if (!relationsWithEventType) {
|
if (!relationsWithEventType) {
|
||||||
relationsWithEventType = relationsWithRelType[eventType] = new Relations(
|
relationsWithEventType = relationsWithRelType[eventType] = new Relations(
|
||||||
@@ -804,7 +798,6 @@ EventTimelineSet.prototype.aggregateRelations = function(event) {
|
|||||||
eventType,
|
eventType,
|
||||||
this.room,
|
this.room,
|
||||||
);
|
);
|
||||||
isNewRelations = true;
|
|
||||||
relatesToEvent = this.findEventById(relatesToEventId) || this.room.getPendingEvent(relatesToEventId);
|
relatesToEvent = this.findEventById(relatesToEventId) || this.room.getPendingEvent(relatesToEventId);
|
||||||
if (relatesToEvent) {
|
if (relatesToEvent) {
|
||||||
relationsWithEventType.setTargetEvent(relatesToEvent);
|
relationsWithEventType.setTargetEvent(relatesToEvent);
|
||||||
@@ -812,11 +805,6 @@ EventTimelineSet.prototype.aggregateRelations = function(event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
relationsWithEventType.addEvent(event);
|
relationsWithEventType.addEvent(event);
|
||||||
|
|
||||||
// only emit once event has been added to relations
|
|
||||||
if (isNewRelations && relatesToEvent) {
|
|
||||||
relatesToEvent.emit("Event.relationsCreated", relationType, eventType);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -398,6 +398,12 @@ utils.extend(MatrixEvent.prototype, {
|
|||||||
this._clearEvent.content.msgtype === "m.bad.encrypted";
|
this._clearEvent.content.msgtype === "m.bad.encrypted";
|
||||||
},
|
},
|
||||||
|
|
||||||
|
shouldAttemptDecryption: function() {
|
||||||
|
return this.isEncrypted()
|
||||||
|
&& !this.isBeingDecrypted()
|
||||||
|
&& this.getClearContent() === null;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the process of trying to decrypt this event.
|
* Start the process of trying to decrypt this event.
|
||||||
*
|
*
|
||||||
@@ -406,12 +412,22 @@ utils.extend(MatrixEvent.prototype, {
|
|||||||
* @internal
|
* @internal
|
||||||
*
|
*
|
||||||
* @param {module:crypto} crypto crypto module
|
* @param {module:crypto} crypto crypto module
|
||||||
* @param {bool} isRetry True if this is a retry (enables more logging)
|
* @param {object} options
|
||||||
|
* @param {bool} options.isRetry True if this is a retry (enables more logging)
|
||||||
|
* @param {bool} options.emit Emits "event.decrypted" if set to true
|
||||||
*
|
*
|
||||||
* @returns {Promise} promise which resolves (to undefined) when the decryption
|
* @returns {Promise} promise which resolves (to undefined) when the decryption
|
||||||
* attempt is completed.
|
* attempt is completed.
|
||||||
*/
|
*/
|
||||||
attemptDecryption: async function(crypto, isRetry) {
|
attemptDecryption: async function(crypto, options = {}) {
|
||||||
|
// For backwards compatibility purposes
|
||||||
|
// The function signature used to be attemptDecryption(crypto, isRetry)
|
||||||
|
if (typeof options === "boolean") {
|
||||||
|
options = {
|
||||||
|
isRetry: options,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// start with a couple of sanity checks.
|
// start with a couple of sanity checks.
|
||||||
if (!this.isEncrypted()) {
|
if (!this.isEncrypted()) {
|
||||||
throw new Error("Attempt to decrypt event which isn't encrypted");
|
throw new Error("Attempt to decrypt event which isn't encrypted");
|
||||||
@@ -441,7 +457,7 @@ utils.extend(MatrixEvent.prototype, {
|
|||||||
return this._decryptionPromise;
|
return this._decryptionPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._decryptionPromise = this._decryptionLoop(crypto, isRetry);
|
this._decryptionPromise = this._decryptionLoop(crypto, options);
|
||||||
return this._decryptionPromise;
|
return this._decryptionPromise;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -486,7 +502,7 @@ utils.extend(MatrixEvent.prototype, {
|
|||||||
return recipients;
|
return recipients;
|
||||||
},
|
},
|
||||||
|
|
||||||
_decryptionLoop: async function(crypto, isRetry) {
|
_decryptionLoop: async function(crypto, options = {}) {
|
||||||
// make sure that this method never runs completely synchronously.
|
// make sure that this method never runs completely synchronously.
|
||||||
// (doing so would mean that we would clear _decryptionPromise *before*
|
// (doing so would mean that we would clear _decryptionPromise *before*
|
||||||
// it is set in attemptDecryption - and hence end up with a stuck
|
// it is set in attemptDecryption - and hence end up with a stuck
|
||||||
@@ -503,7 +519,7 @@ utils.extend(MatrixEvent.prototype, {
|
|||||||
res = this._badEncryptedMessage("Encryption not enabled");
|
res = this._badEncryptedMessage("Encryption not enabled");
|
||||||
} else {
|
} else {
|
||||||
res = await crypto.decryptEvent(this);
|
res = await crypto.decryptEvent(this);
|
||||||
if (isRetry) {
|
if (options.isRetry === true) {
|
||||||
logger.info(`Decrypted event on retry (id=${this.getId()})`);
|
logger.info(`Decrypted event on retry (id=${this.getId()})`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -511,7 +527,7 @@ utils.extend(MatrixEvent.prototype, {
|
|||||||
if (e.name !== "DecryptionError") {
|
if (e.name !== "DecryptionError") {
|
||||||
// not a decryption error: log the whole exception as an error
|
// not a decryption error: log the whole exception as an error
|
||||||
// (and don't bother with a retry)
|
// (and don't bother with a retry)
|
||||||
const re = isRetry ? 're' : '';
|
const re = options.isRetry ? 're' : '';
|
||||||
logger.error(
|
logger.error(
|
||||||
`Error ${re}decrypting event ` +
|
`Error ${re}decrypting event ` +
|
||||||
`(id=${this.getId()}): ${e.stack || e}`,
|
`(id=${this.getId()}): ${e.stack || e}`,
|
||||||
@@ -577,7 +593,9 @@ utils.extend(MatrixEvent.prototype, {
|
|||||||
// pick up the wrong contents.
|
// pick up the wrong contents.
|
||||||
this.setPushActions(null);
|
this.setPushActions(null);
|
||||||
|
|
||||||
this.emit("Event.decrypted", this, err);
|
if (options.emit !== false) {
|
||||||
|
this.emit("Event.decrypted", this, err);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,11 +41,14 @@ export class Relations extends EventEmitter {
|
|||||||
super();
|
super();
|
||||||
this.relationType = relationType;
|
this.relationType = relationType;
|
||||||
this.eventType = eventType;
|
this.eventType = eventType;
|
||||||
|
this._relationEventIds = new Set();
|
||||||
this._relations = new Set();
|
this._relations = new Set();
|
||||||
this._annotationsByKey = {};
|
this._annotationsByKey = {};
|
||||||
this._annotationsBySender = {};
|
this._annotationsBySender = {};
|
||||||
this._sortedAnnotationsByKey = [];
|
this._sortedAnnotationsByKey = [];
|
||||||
this._targetEvent = null;
|
this._targetEvent = null;
|
||||||
|
this._room = room;
|
||||||
|
this._creationEmitted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,8 +57,8 @@ export class Relations extends EventEmitter {
|
|||||||
* @param {MatrixEvent} event
|
* @param {MatrixEvent} event
|
||||||
* The new relation event to be added.
|
* The new relation event to be added.
|
||||||
*/
|
*/
|
||||||
addEvent(event) {
|
async addEvent(event) {
|
||||||
if (this._relations.has(event)) {
|
if (this._relationEventIds.has(event.getId())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,16 +83,20 @@ export class Relations extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._relations.add(event);
|
this._relations.add(event);
|
||||||
|
this._relationEventIds.add(event.getId());
|
||||||
|
|
||||||
if (this.relationType === "m.annotation") {
|
if (this.relationType === "m.annotation") {
|
||||||
this._addAnnotationToAggregation(event);
|
this._addAnnotationToAggregation(event);
|
||||||
} else if (this.relationType === "m.replace" && this._targetEvent) {
|
} else if (this.relationType === "m.replace" && this._targetEvent) {
|
||||||
this._targetEvent.makeReplaced(this.getLastReplacement());
|
const lastReplacement = await this.getLastReplacement();
|
||||||
|
this._targetEvent.makeReplaced(lastReplacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
event.on("Event.beforeRedaction", this._onBeforeRedaction);
|
event.on("Event.beforeRedaction", this._onBeforeRedaction);
|
||||||
|
|
||||||
this.emit("Relations.add", event);
|
this.emit("Relations.add", event);
|
||||||
|
|
||||||
|
this._maybeEmitCreated();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,7 +105,7 @@ export class Relations extends EventEmitter {
|
|||||||
* @param {MatrixEvent} event
|
* @param {MatrixEvent} event
|
||||||
* The relation event to remove.
|
* The relation event to remove.
|
||||||
*/
|
*/
|
||||||
_removeEvent(event) {
|
async _removeEvent(event) {
|
||||||
if (!this._relations.has(event)) {
|
if (!this._relations.has(event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -122,7 +129,8 @@ export class Relations extends EventEmitter {
|
|||||||
if (this.relationType === "m.annotation") {
|
if (this.relationType === "m.annotation") {
|
||||||
this._removeAnnotationFromAggregation(event);
|
this._removeAnnotationFromAggregation(event);
|
||||||
} else if (this.relationType === "m.replace" && this._targetEvent) {
|
} else if (this.relationType === "m.replace" && this._targetEvent) {
|
||||||
this._targetEvent.makeReplaced(this.getLastReplacement());
|
const lastReplacement = await this.getLastReplacement();
|
||||||
|
this._targetEvent.makeReplaced(lastReplacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit("Relations.remove", event);
|
this.emit("Relations.remove", event);
|
||||||
@@ -227,7 +235,7 @@ export class Relations extends EventEmitter {
|
|||||||
* @param {MatrixEvent} redactedEvent
|
* @param {MatrixEvent} redactedEvent
|
||||||
* The original relation event that is about to be redacted.
|
* The original relation event that is about to be redacted.
|
||||||
*/
|
*/
|
||||||
_onBeforeRedaction = (redactedEvent) => {
|
_onBeforeRedaction = async (redactedEvent) => {
|
||||||
if (!this._relations.has(redactedEvent)) {
|
if (!this._relations.has(redactedEvent)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -238,7 +246,8 @@ export class Relations extends EventEmitter {
|
|||||||
// Remove the redacted annotation from aggregation by key
|
// Remove the redacted annotation from aggregation by key
|
||||||
this._removeAnnotationFromAggregation(redactedEvent);
|
this._removeAnnotationFromAggregation(redactedEvent);
|
||||||
} else if (this.relationType === "m.replace" && this._targetEvent) {
|
} else if (this.relationType === "m.replace" && this._targetEvent) {
|
||||||
this._targetEvent.makeReplaced(this.getLastReplacement());
|
const lastReplacement = await this.getLastReplacement();
|
||||||
|
this._targetEvent.makeReplaced(lastReplacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
redactedEvent.removeListener("Event.beforeRedaction", this._onBeforeRedaction);
|
redactedEvent.removeListener("Event.beforeRedaction", this._onBeforeRedaction);
|
||||||
@@ -291,7 +300,7 @@ export class Relations extends EventEmitter {
|
|||||||
*
|
*
|
||||||
* @return {MatrixEvent?}
|
* @return {MatrixEvent?}
|
||||||
*/
|
*/
|
||||||
getLastReplacement() {
|
async getLastReplacement() {
|
||||||
if (this.relationType !== "m.replace") {
|
if (this.relationType !== "m.replace") {
|
||||||
// Aggregating on last only makes sense for this relation type
|
// Aggregating on last only makes sense for this relation type
|
||||||
return null;
|
return null;
|
||||||
@@ -309,7 +318,7 @@ export class Relations extends EventEmitter {
|
|||||||
this._targetEvent.getServerAggregatedRelation("m.replace");
|
this._targetEvent.getServerAggregatedRelation("m.replace");
|
||||||
const minTs = replaceRelation && replaceRelation.origin_server_ts;
|
const minTs = replaceRelation && replaceRelation.origin_server_ts;
|
||||||
|
|
||||||
return this.getRelations().reduce((last, event) => {
|
const lastReplacement = this.getRelations().reduce((last, event) => {
|
||||||
if (event.getSender() !== this._targetEvent.getSender()) {
|
if (event.getSender() !== this._targetEvent.getSender()) {
|
||||||
return last;
|
return last;
|
||||||
}
|
}
|
||||||
@@ -321,23 +330,51 @@ export class Relations extends EventEmitter {
|
|||||||
}
|
}
|
||||||
return event;
|
return event;
|
||||||
}, null);
|
}, null);
|
||||||
|
|
||||||
|
if (lastReplacement?.shouldAttemptDecryption()) {
|
||||||
|
await lastReplacement.attemptDecryption(this._room._client._crypto);
|
||||||
|
} else if (lastReplacement?.isBeingDecrypted()) {
|
||||||
|
await lastReplacement._decryptionPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastReplacement;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @param {MatrixEvent} targetEvent the event the relations are related to.
|
* @param {MatrixEvent} targetEvent the event the relations are related to.
|
||||||
*/
|
*/
|
||||||
setTargetEvent(event) {
|
async setTargetEvent(event) {
|
||||||
if (this._targetEvent) {
|
if (this._targetEvent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._targetEvent = event;
|
this._targetEvent = event;
|
||||||
|
|
||||||
if (this.relationType === "m.replace") {
|
if (this.relationType === "m.replace") {
|
||||||
const replacement = this.getLastReplacement();
|
const replacement = await this.getLastReplacement();
|
||||||
// this is the initial update, so only call it if we already have something
|
// this is the initial update, so only call it if we already have something
|
||||||
// to not emit Event.replaced needlessly
|
// to not emit Event.replaced needlessly
|
||||||
if (replacement) {
|
if (replacement) {
|
||||||
this._targetEvent.makeReplaced(replacement);
|
this._targetEvent.makeReplaced(replacement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._maybeEmitCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
_maybeEmitCreated() {
|
||||||
|
if (this._creationEmitted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Only emit we're "created" once we have a target event instance _and_
|
||||||
|
// at least one related event.
|
||||||
|
if (!this._targetEvent || !this._relations.size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._creationEmitted = true;
|
||||||
|
this._targetEvent.emit(
|
||||||
|
"Event.relationsCreated",
|
||||||
|
this.relationType,
|
||||||
|
this.eventType,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,14 +133,15 @@ RoomMember.prototype.setPowerLevelEvent = function(powerLevelEvent) {
|
|||||||
const evContent = powerLevelEvent.getDirectionalContent();
|
const evContent = powerLevelEvent.getDirectionalContent();
|
||||||
|
|
||||||
let maxLevel = evContent.users_default || 0;
|
let maxLevel = evContent.users_default || 0;
|
||||||
utils.forEach(utils.values(evContent.users), function(lvl) {
|
const users = evContent.users || {};
|
||||||
|
Object.values(users).forEach(function(lvl) {
|
||||||
maxLevel = Math.max(maxLevel, lvl);
|
maxLevel = Math.max(maxLevel, lvl);
|
||||||
});
|
});
|
||||||
const oldPowerLevel = this.powerLevel;
|
const oldPowerLevel = this.powerLevel;
|
||||||
const oldPowerLevelNorm = this.powerLevelNorm;
|
const oldPowerLevelNorm = this.powerLevelNorm;
|
||||||
|
|
||||||
if (evContent.users && evContent.users[this.userId] !== undefined) {
|
if (users[this.userId] !== undefined) {
|
||||||
this.powerLevel = evContent.users[this.userId];
|
this.powerLevel = users[this.userId];
|
||||||
} else if (evContent.users_default !== undefined) {
|
} else if (evContent.users_default !== undefined) {
|
||||||
this.powerLevel = evContent.users_default;
|
this.powerLevel = evContent.users_default;
|
||||||
} else {
|
} else {
|
||||||
@@ -172,7 +173,7 @@ RoomMember.prototype.setTypingEvent = function(event) {
|
|||||||
const oldTyping = this.typing;
|
const oldTyping = this.typing;
|
||||||
this.typing = false;
|
this.typing = false;
|
||||||
const typingList = event.getContent().user_ids;
|
const typingList = event.getContent().user_ids;
|
||||||
if (!utils.isArray(typingList)) {
|
if (!Array.isArray(typingList)) {
|
||||||
// malformed event :/ bail early. TODO: whine?
|
// malformed event :/ bail early. TODO: whine?
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ RoomState.prototype.setInvitedMemberCount = function(count) {
|
|||||||
* @return {Array<RoomMember>} A list of RoomMembers.
|
* @return {Array<RoomMember>} A list of RoomMembers.
|
||||||
*/
|
*/
|
||||||
RoomState.prototype.getMembers = function() {
|
RoomState.prototype.getMembers = function() {
|
||||||
return utils.values(this.members);
|
return Object.values(this.members);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -163,7 +163,7 @@ RoomState.prototype.getMembers = function() {
|
|||||||
* @return {Array<RoomMember>} A list of RoomMembers.
|
* @return {Array<RoomMember>} A list of RoomMembers.
|
||||||
*/
|
*/
|
||||||
RoomState.prototype.getMembersExcept = function(excludedIds) {
|
RoomState.prototype.getMembersExcept = function(excludedIds) {
|
||||||
return utils.values(this.members)
|
return Object.values(this.members)
|
||||||
.filter((m) => !excludedIds.includes(m.userId));
|
.filter((m) => !excludedIds.includes(m.userId));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -296,7 +296,7 @@ RoomState.prototype.setStateEvents = function(stateEvents) {
|
|||||||
this._updateModifiedTime();
|
this._updateModifiedTime();
|
||||||
|
|
||||||
// update the core event dict
|
// update the core event dict
|
||||||
utils.forEach(stateEvents, function(event) {
|
stateEvents.forEach(function(event) {
|
||||||
if (event.getRoomId() !== self.roomId) {
|
if (event.getRoomId() !== self.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -319,7 +319,7 @@ RoomState.prototype.setStateEvents = function(stateEvents) {
|
|||||||
// core event dict as these structures may depend on other state events in
|
// core event dict as these structures may depend on other state events in
|
||||||
// the given array (e.g. disambiguating display names in one go to do both
|
// the given array (e.g. disambiguating display names in one go to do both
|
||||||
// clashing names rather than progressively which only catches 1 of them).
|
// clashing names rather than progressively which only catches 1 of them).
|
||||||
utils.forEach(stateEvents, function(event) {
|
stateEvents.forEach(function(event) {
|
||||||
if (event.getRoomId() !== self.roomId) {
|
if (event.getRoomId() !== self.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -349,8 +349,8 @@ RoomState.prototype.setStateEvents = function(stateEvents) {
|
|||||||
self._updateMember(member);
|
self._updateMember(member);
|
||||||
self.emit("RoomState.members", event, self, member);
|
self.emit("RoomState.members", event, self, member);
|
||||||
} else if (event.getType() === "m.room.power_levels") {
|
} else if (event.getType() === "m.room.power_levels") {
|
||||||
const members = utils.values(self.members);
|
const members = Object.values(self.members);
|
||||||
utils.forEach(members, function(member) {
|
members.forEach(function(member) {
|
||||||
// We only propagate `RoomState.members` event if the
|
// We only propagate `RoomState.members` event if the
|
||||||
// power levels has been changed
|
// power levels has been changed
|
||||||
// large room suffer from large re-rendering especially when not needed
|
// large room suffer from large re-rendering especially when not needed
|
||||||
@@ -511,7 +511,7 @@ RoomState.prototype._setOutOfBandMember = function(stateEvent) {
|
|||||||
* @param {MatrixEvent} event The typing event
|
* @param {MatrixEvent} event The typing event
|
||||||
*/
|
*/
|
||||||
RoomState.prototype.setTypingEvent = function(event) {
|
RoomState.prototype.setTypingEvent = function(event) {
|
||||||
utils.forEach(utils.values(this.members), function(member) {
|
Object.values(this.members).forEach(function(member) {
|
||||||
member.setTypingEvent(event);
|
member.setTypingEvent(event);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,17 +20,18 @@ limitations under the License.
|
|||||||
* @module models/room
|
* @module models/room
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EventEmitter } from "events";
|
import {EventEmitter} from "events";
|
||||||
import { EventTimelineSet } from "./event-timeline-set";
|
import {EventTimelineSet} from "./event-timeline-set";
|
||||||
import { EventTimeline } from "./event-timeline";
|
import {EventTimeline} from "./event-timeline";
|
||||||
import { getHttpUriForMxc } from "../content-repo";
|
import {getHttpUriForMxc} from "../content-repo";
|
||||||
import * as utils from "../utils";
|
import * as utils from "../utils";
|
||||||
import { EventStatus, MatrixEvent } from "./event";
|
import {EventStatus, MatrixEvent} from "./event";
|
||||||
import { RoomMember } from "./room-member";
|
import {RoomMember} from "./room-member";
|
||||||
import { RoomSummary } from "./room-summary";
|
import {RoomSummary} from "./room-summary";
|
||||||
import { logger } from '../logger';
|
import {logger} from '../logger';
|
||||||
import { ReEmitter } from '../ReEmitter';
|
import {ReEmitter} from '../ReEmitter';
|
||||||
import { EventType, RoomCreateTypeField, RoomType } from "../@types/event";
|
import {EventType, RoomCreateTypeField, RoomType} from "../@types/event";
|
||||||
|
import { normalize } from "../utils";
|
||||||
|
|
||||||
// These constants are used as sane defaults when the homeserver doesn't support
|
// These constants are used as sane defaults when the homeserver doesn't support
|
||||||
// the m.room_versions capability. In practice, KNOWN_SAFE_ROOM_VERSION should be
|
// the m.room_versions capability. In practice, KNOWN_SAFE_ROOM_VERSION should be
|
||||||
@@ -58,6 +59,7 @@ function synthesizeReceipt(userId, event, receiptType) {
|
|||||||
return new MatrixEvent(fakeReceipt);
|
return new MatrixEvent(fakeReceipt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new Room.
|
* Construct a new Room.
|
||||||
*
|
*
|
||||||
@@ -101,6 +103,7 @@ function synthesizeReceipt(userId, event, receiptType) {
|
|||||||
*
|
*
|
||||||
* @prop {string} roomId The ID of this room.
|
* @prop {string} roomId The ID of this room.
|
||||||
* @prop {string} name The human-readable display name for this room.
|
* @prop {string} name The human-readable display name for this room.
|
||||||
|
* @prop {string} normalizedName The unhomoglyphed name for this room.
|
||||||
* @prop {Array<MatrixEvent>} timeline The live event timeline for this room,
|
* @prop {Array<MatrixEvent>} timeline The live event timeline for this room,
|
||||||
* with the oldest event at index 0. Present for backwards compatibility -
|
* with the oldest event at index 0. Present for backwards compatibility -
|
||||||
* prefer getLiveTimeline().getEvents().
|
* prefer getLiveTimeline().getEvents().
|
||||||
@@ -215,6 +218,10 @@ export function Room(roomId, client, myUserId, opts) {
|
|||||||
} else {
|
} else {
|
||||||
this._membersPromise = null;
|
this._membersPromise = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// flags to stop logspam about missing m.room.create events
|
||||||
|
this.getTypeWarning = false;
|
||||||
|
this.getVersionWarning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -227,6 +234,51 @@ function pendingEventsKey(roomId) {
|
|||||||
|
|
||||||
utils.inherits(Room, EventEmitter);
|
utils.inherits(Room, EventEmitter);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk decrypt critical events in a room
|
||||||
|
*
|
||||||
|
* Critical events represents the minimal set of events to decrypt
|
||||||
|
* for a typical UI to function properly
|
||||||
|
*
|
||||||
|
* - Last event of every room (to generate likely message preview)
|
||||||
|
* - All events up to the read receipt (to calculate an accurate notification count)
|
||||||
|
*
|
||||||
|
* @returns {Promise} Signals when all events have been decrypted
|
||||||
|
*/
|
||||||
|
Room.prototype.decryptCriticalEvents = function() {
|
||||||
|
const readReceiptEventId = this.getEventReadUpTo(this._client.getUserId(), true);
|
||||||
|
const events = this.getLiveTimeline().getEvents();
|
||||||
|
const readReceiptTimelineIndex = events.findIndex(matrixEvent => {
|
||||||
|
return matrixEvent.event.event_id === readReceiptEventId;
|
||||||
|
});
|
||||||
|
|
||||||
|
const decryptionPromises = events
|
||||||
|
.slice(readReceiptTimelineIndex)
|
||||||
|
.filter(event => event.shouldAttemptDecryption())
|
||||||
|
.reverse()
|
||||||
|
.map(event => event.attemptDecryption(this._client._crypto, { isRetry: true }));
|
||||||
|
|
||||||
|
return Promise.allSettled(decryptionPromises);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk decrypt events in a room
|
||||||
|
*
|
||||||
|
* @returns {Promise} Signals when all events have been decrypted
|
||||||
|
*/
|
||||||
|
Room.prototype.decryptAllEvents = function() {
|
||||||
|
const decryptionPromises = this
|
||||||
|
.getUnfilteredTimelineSet()
|
||||||
|
.getLiveTimeline()
|
||||||
|
.getEvents()
|
||||||
|
.filter(event => event.shouldAttemptDecryption())
|
||||||
|
.reverse()
|
||||||
|
.map(event => event.attemptDecryption(this._client._crypto, { isRetry: true }));
|
||||||
|
|
||||||
|
return Promise.allSettled(decryptionPromises);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the version of the room
|
* Gets the version of the room
|
||||||
* @returns {string} The version of the room, or null if it could not be determined
|
* @returns {string} The version of the room, or null if it could not be determined
|
||||||
@@ -234,7 +286,10 @@ utils.inherits(Room, EventEmitter);
|
|||||||
Room.prototype.getVersion = function() {
|
Room.prototype.getVersion = function() {
|
||||||
const createEvent = this.currentState.getStateEvents("m.room.create", "");
|
const createEvent = this.currentState.getStateEvents("m.room.create", "");
|
||||||
if (!createEvent) {
|
if (!createEvent) {
|
||||||
logger.warn("[getVersion] Room " + this.roomId + " does not have an m.room.create event");
|
if (!this.getVersionWarning) {
|
||||||
|
logger.warn("[getVersion] Room " + this.roomId + " does not have an m.room.create event");
|
||||||
|
this.getVersionWarning = true;
|
||||||
|
}
|
||||||
return '1';
|
return '1';
|
||||||
}
|
}
|
||||||
const ver = createEvent.getContent()['room_version'];
|
const ver = createEvent.getContent()['room_version'];
|
||||||
@@ -440,6 +495,7 @@ Room.prototype.getLiveTimeline = function() {
|
|||||||
return this.getUnfilteredTimelineSet().getLiveTimeline();
|
return this.getUnfilteredTimelineSet().getLiveTimeline();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the timestamp of the last message in the room
|
* Get the timestamp of the last message in the room
|
||||||
*
|
*
|
||||||
@@ -578,12 +634,13 @@ Room.prototype._loadMembersFromServer = async function() {
|
|||||||
at: lastSyncToken,
|
at: lastSyncToken,
|
||||||
});
|
});
|
||||||
const path = utils.encodeUri("/rooms/$roomId/members?" + queryString,
|
const path = utils.encodeUri("/rooms/$roomId/members?" + queryString,
|
||||||
{ $roomId: this.roomId });
|
{$roomId: this.roomId});
|
||||||
const http = this._client._http;
|
const http = this._client._http;
|
||||||
const response = await http.authedRequest(undefined, "GET", path);
|
const response = await http.authedRequest(undefined, "GET", path);
|
||||||
return response.chunk;
|
return response.chunk;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Room.prototype._loadMembers = async function() {
|
Room.prototype._loadMembers = async function() {
|
||||||
// were the members loaded from the server?
|
// were the members loaded from the server?
|
||||||
let fromServer = false;
|
let fromServer = false;
|
||||||
@@ -596,7 +653,7 @@ Room.prototype._loadMembers = async function() {
|
|||||||
`members from server for room ${this.roomId}`);
|
`members from server for room ${this.roomId}`);
|
||||||
}
|
}
|
||||||
const memberEvents = rawMembersEvents.map(this._client.getEventMapper());
|
const memberEvents = rawMembersEvents.map(this._client.getEventMapper());
|
||||||
return { memberEvents, fromServer };
|
return {memberEvents, fromServer};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -901,7 +958,7 @@ Room.prototype.getAliases = function() {
|
|||||||
if (aliasEvents) {
|
if (aliasEvents) {
|
||||||
for (let i = 0; i < aliasEvents.length; ++i) {
|
for (let i = 0; i < aliasEvents.length; ++i) {
|
||||||
const aliasEvent = aliasEvents[i];
|
const aliasEvent = aliasEvents[i];
|
||||||
if (utils.isArray(aliasEvent.getContent().aliases)) {
|
if (Array.isArray(aliasEvent.getContent().aliases)) {
|
||||||
const filteredAliases = aliasEvent.getContent().aliases.filter(a => {
|
const filteredAliases = aliasEvent.getContent().aliases.filter(a => {
|
||||||
if (typeof(a) !== "string") return false;
|
if (typeof(a) !== "string") return false;
|
||||||
if (a[0] !== '#') return false;
|
if (a[0] !== '#') return false;
|
||||||
@@ -1029,7 +1086,7 @@ Room.prototype.getInvitedAndJoinedMemberCount = function() {
|
|||||||
* @return {RoomMember[]} A list of members with the given membership state.
|
* @return {RoomMember[]} A list of members with the given membership state.
|
||||||
*/
|
*/
|
||||||
Room.prototype.getMembersWithMembership = function(membership) {
|
Room.prototype.getMembersWithMembership = function(membership) {
|
||||||
return utils.filter(this.currentState.getMembers(), function(m) {
|
return this.currentState.getMembers().filter(function(m) {
|
||||||
return m.membership === membership;
|
return m.membership === membership;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -1068,6 +1125,7 @@ Room.prototype.getInvitedAndJoinedMemberCount = function() {
|
|||||||
return calculateRoomName(this, userId, true);
|
return calculateRoomName(this, userId, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the given user_id has the given membership state.
|
* Check if the given user_id has the given membership state.
|
||||||
* @param {string} userId The user ID to check.
|
* @param {string} userId The user ID to check.
|
||||||
@@ -1224,6 +1282,7 @@ Room.prototype._addLiveEvent = function(event, duplicateStrategy, fromCache) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a pending outgoing event to this room.
|
* Add a pending outgoing event to this room.
|
||||||
*
|
*
|
||||||
@@ -1634,6 +1693,7 @@ Room.prototype.removeEvent = function(eventId) {
|
|||||||
return removedAny;
|
return removedAny;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recalculate various aspects of the room, including the room name and
|
* Recalculate various aspects of the room, including the room name and
|
||||||
* room summary. Call this any time the room's current state is modified.
|
* room summary. Call this any time the room's current state is modified.
|
||||||
@@ -1649,7 +1709,7 @@ Room.prototype.recalculate = function() {
|
|||||||
);
|
);
|
||||||
if (membershipEvent && membershipEvent.getContent().membership === "invite") {
|
if (membershipEvent && membershipEvent.getContent().membership === "invite") {
|
||||||
const strippedStateEvents = membershipEvent.event.invite_room_state || [];
|
const strippedStateEvents = membershipEvent.event.invite_room_state || [];
|
||||||
utils.forEach(strippedStateEvents, function(strippedEvent) {
|
strippedStateEvents.forEach(function(strippedEvent) {
|
||||||
const existingEvent = self.currentState.getStateEvents(
|
const existingEvent = self.currentState.getStateEvents(
|
||||||
strippedEvent.type, strippedEvent.state_key,
|
strippedEvent.type, strippedEvent.state_key,
|
||||||
);
|
);
|
||||||
@@ -1669,6 +1729,7 @@ Room.prototype.recalculate = function() {
|
|||||||
|
|
||||||
const oldName = this.name;
|
const oldName = this.name;
|
||||||
this.name = calculateRoomName(this, this.myUserId);
|
this.name = calculateRoomName(this, this.myUserId);
|
||||||
|
this.normalizedName = normalize(this.name);
|
||||||
this.summary = new RoomSummary(this.roomId, {
|
this.summary = new RoomSummary(this.roomId, {
|
||||||
title: this.name,
|
title: this.name,
|
||||||
});
|
});
|
||||||
@@ -1799,9 +1860,9 @@ Room.prototype.addReceipt = function(event, fake) {
|
|||||||
*/
|
*/
|
||||||
Room.prototype._addReceiptsToStructure = function(event, receipts) {
|
Room.prototype._addReceiptsToStructure = function(event, receipts) {
|
||||||
const self = this;
|
const self = this;
|
||||||
utils.keys(event.getContent()).forEach(function(eventId) {
|
Object.keys(event.getContent()).forEach(function(eventId) {
|
||||||
utils.keys(event.getContent()[eventId]).forEach(function(receiptType) {
|
Object.keys(event.getContent()[eventId]).forEach(function(receiptType) {
|
||||||
utils.keys(event.getContent()[eventId][receiptType]).forEach(
|
Object.keys(event.getContent()[eventId][receiptType]).forEach(
|
||||||
function(userId) {
|
function(userId) {
|
||||||
const receipt = event.getContent()[eventId][receiptType][userId];
|
const receipt = event.getContent()[eventId][receiptType][userId];
|
||||||
|
|
||||||
@@ -1841,8 +1902,8 @@ Room.prototype._addReceiptsToStructure = function(event, receipts) {
|
|||||||
*/
|
*/
|
||||||
Room.prototype._buildReceiptCache = function(receipts) {
|
Room.prototype._buildReceiptCache = function(receipts) {
|
||||||
const receiptCacheByEventId = {};
|
const receiptCacheByEventId = {};
|
||||||
utils.keys(receipts).forEach(function(receiptType) {
|
Object.keys(receipts).forEach(function(receiptType) {
|
||||||
utils.keys(receipts[receiptType]).forEach(function(userId) {
|
Object.keys(receipts[receiptType]).forEach(function(userId) {
|
||||||
const receipt = receipts[receiptType][userId];
|
const receipt = receipts[receiptType][userId];
|
||||||
if (!receiptCacheByEventId[receipt.eventId]) {
|
if (!receiptCacheByEventId[receipt.eventId]) {
|
||||||
receiptCacheByEventId[receipt.eventId] = [];
|
receiptCacheByEventId[receipt.eventId] = [];
|
||||||
@@ -1857,6 +1918,7 @@ Room.prototype._buildReceiptCache = function(receipts) {
|
|||||||
return receiptCacheByEventId;
|
return receiptCacheByEventId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a temporary local-echo receipt to the room to reflect in the
|
* Add a temporary local-echo receipt to the room to reflect in the
|
||||||
* client the fact that we've sent one.
|
* client the fact that we've sent one.
|
||||||
@@ -1914,6 +1976,7 @@ Room.prototype.getAccountData = function(type) {
|
|||||||
return this.accountData[type];
|
return this.accountData[type];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the syncing user has permission to send a message in the room
|
* Returns whether the syncing user has permission to send a message in the room
|
||||||
* @return {boolean} true if the user should be permitted to send
|
* @return {boolean} true if the user should be permitted to send
|
||||||
@@ -1955,7 +2018,10 @@ Room.prototype.getJoinRule = function() {
|
|||||||
Room.prototype.getType = function() {
|
Room.prototype.getType = function() {
|
||||||
const createEvent = this.currentState.getStateEvents("m.room.create", "");
|
const createEvent = this.currentState.getStateEvents("m.room.create", "");
|
||||||
if (!createEvent) {
|
if (!createEvent) {
|
||||||
logger.warn("[getType] Room " + this.roomId + " does not have an m.room.create event");
|
if (!this.getTypeWarning) {
|
||||||
|
logger.warn("[getType] Room " + this.roomId + " does not have an m.room.create event");
|
||||||
|
this.getTypeWarning = true;
|
||||||
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return createEvent.getContent()[RoomCreateTypeField];
|
return createEvent.getContent()[RoomCreateTypeField];
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ limitations under the License.
|
|||||||
* @module models/search-result
|
* @module models/search-result
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as utils from "../utils";
|
import {EventContext} from "./event-context";
|
||||||
import { EventContext } from "./event-context";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new SearchResult
|
* Construct a new SearchResult
|
||||||
@@ -52,8 +51,8 @@ SearchResult.fromJson = function(jsonObj, eventMapper) {
|
|||||||
const context = new EventContext(eventMapper(jsonObj.result));
|
const context = new EventContext(eventMapper(jsonObj.result));
|
||||||
|
|
||||||
context.setPaginateToken(jsonContext.start, true);
|
context.setPaginateToken(jsonContext.start, true);
|
||||||
context.addEvents(utils.map(events_before, eventMapper), true);
|
context.addEvents(events_before.map(eventMapper), true);
|
||||||
context.addEvents(utils.map(events_after, eventMapper), false);
|
context.addEvents(events_after.map(eventMapper), false);
|
||||||
context.setPaginateToken(jsonContext.end, false);
|
context.setPaginateToken(jsonContext.end, false);
|
||||||
|
|
||||||
return new SearchResult(jsonObj.rank, context);
|
return new SearchResult(jsonObj.rank, context);
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ MatrixScheduler.prototype.getQueueForEvent = function(event) {
|
|||||||
if (!name || !this._queues[name]) {
|
if (!name || !this._queues[name]) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return utils.map(this._queues[name], function(obj) {
|
return this._queues[name].map(function(obj) {
|
||||||
return obj.event;
|
return obj.event;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -195,16 +195,18 @@ function _startProcessingQueues(scheduler) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// for each inactive queue with events in them
|
// for each inactive queue with events in them
|
||||||
utils.forEach(utils.filter(utils.keys(scheduler._queues), function(queueName) {
|
Object.keys(scheduler._queues)
|
||||||
return scheduler._activeQueues.indexOf(queueName) === -1 &&
|
.filter(function(queueName) {
|
||||||
scheduler._queues[queueName].length > 0;
|
return scheduler._activeQueues.indexOf(queueName) === -1 &&
|
||||||
}), function(queueName) {
|
scheduler._queues[queueName].length > 0;
|
||||||
// mark the queue as active
|
})
|
||||||
scheduler._activeQueues.push(queueName);
|
.forEach(function(queueName) {
|
||||||
// begin processing the head of the queue
|
// mark the queue as active
|
||||||
debuglog("Spinning up queue: '%s'", queueName);
|
scheduler._activeQueues.push(queueName);
|
||||||
_processQueue(scheduler, queueName);
|
// begin processing the head of the queue
|
||||||
});
|
debuglog("Spinning up queue: '%s'", queueName);
|
||||||
|
_processQueue(scheduler, queueName);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function _processQueue(scheduler, queueName) {
|
function _processQueue(scheduler, queueName) {
|
||||||
@@ -266,7 +268,7 @@ function _processQueue(scheduler, queueName) {
|
|||||||
|
|
||||||
function _peekNextEvent(scheduler, queueName) {
|
function _peekNextEvent(scheduler, queueName) {
|
||||||
const queue = scheduler._queues[queueName];
|
const queue = scheduler._queues[queueName];
|
||||||
if (!utils.isArray(queue)) {
|
if (!Array.isArray(queue)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return queue[0];
|
return queue[0];
|
||||||
@@ -274,7 +276,7 @@ function _peekNextEvent(scheduler, queueName) {
|
|||||||
|
|
||||||
function _removeNextEvent(scheduler, queueName) {
|
function _removeNextEvent(scheduler, queueName) {
|
||||||
const queue = scheduler._queues[queueName];
|
const queue = scheduler._queues[queueName];
|
||||||
if (!utils.isArray(queue)) {
|
if (!Array.isArray(queue)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return queue.shift();
|
return queue.shift();
|
||||||
|
|||||||
@@ -22,8 +22,7 @@ limitations under the License.
|
|||||||
* @module store/memory
|
* @module store/memory
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { User } from "../models/user";
|
import {User} from "../models/user";
|
||||||
import * as utils from "../utils";
|
|
||||||
|
|
||||||
function isValidFilterId(filterId) {
|
function isValidFilterId(filterId) {
|
||||||
const isValidStr = typeof filterId === "string" &&
|
const isValidStr = typeof filterId === "string" &&
|
||||||
@@ -113,7 +112,7 @@ MemoryStore.prototype = {
|
|||||||
* @return {Group[]} A list of groups, which may be empty.
|
* @return {Group[]} A list of groups, which may be empty.
|
||||||
*/
|
*/
|
||||||
getGroups: function() {
|
getGroups: function() {
|
||||||
return utils.values(this.groups);
|
return Object.values(this.groups);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -175,7 +174,7 @@ MemoryStore.prototype = {
|
|||||||
* @return {Room[]} A list of rooms, which may be empty.
|
* @return {Room[]} A list of rooms, which may be empty.
|
||||||
*/
|
*/
|
||||||
getRooms: function() {
|
getRooms: function() {
|
||||||
return utils.values(this.rooms);
|
return Object.values(this.rooms);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -194,7 +193,7 @@ MemoryStore.prototype = {
|
|||||||
* @return {RoomSummary[]} A summary of each room.
|
* @return {RoomSummary[]} A summary of each room.
|
||||||
*/
|
*/
|
||||||
getRoomSummaries: function() {
|
getRoomSummaries: function() {
|
||||||
return utils.map(utils.values(this.rooms), function(room) {
|
return Object.values(this.rooms).map(function(room) {
|
||||||
return room.summary;
|
return room.summary;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -221,7 +220,7 @@ MemoryStore.prototype = {
|
|||||||
* @return {User[]} A list of users, which may be empty.
|
* @return {User[]} A list of users, which may be empty.
|
||||||
*/
|
*/
|
||||||
getUsers: function() {
|
getUsers: function() {
|
||||||
return utils.values(this.users);
|
return Object.values(this.users);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
43
src/sync.js
43
src/sync.js
@@ -276,20 +276,15 @@ SyncApi.prototype.peek = function(roomId) {
|
|||||||
|
|
||||||
// FIXME: Mostly duplicated from _processRoomEvents but not entirely
|
// FIXME: Mostly duplicated from _processRoomEvents but not entirely
|
||||||
// because "state" in this API is at the BEGINNING of the chunk
|
// because "state" in this API is at the BEGINNING of the chunk
|
||||||
const oldStateEvents = utils.map(
|
const oldStateEvents = utils.deepCopy(response.state)
|
||||||
utils.deepCopy(response.state), client.getEventMapper(),
|
.map(client.getEventMapper());
|
||||||
);
|
const stateEvents = response.state.map(client.getEventMapper());
|
||||||
const stateEvents = utils.map(
|
const messages = response.messages.chunk.map(client.getEventMapper());
|
||||||
response.state, client.getEventMapper(),
|
|
||||||
);
|
|
||||||
const messages = utils.map(
|
|
||||||
response.messages.chunk, client.getEventMapper(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// XXX: copypasted from /sync until we kill off this
|
// XXX: copypasted from /sync until we kill off this
|
||||||
// minging v1 API stuff)
|
// minging v1 API stuff)
|
||||||
// handle presence events (User objects)
|
// handle presence events (User objects)
|
||||||
if (response.presence && utils.isArray(response.presence)) {
|
if (response.presence && Array.isArray(response.presence)) {
|
||||||
response.presence.map(client.getEventMapper()).forEach(
|
response.presence.map(client.getEventMapper()).forEach(
|
||||||
function(presenceEvent) {
|
function(presenceEvent) {
|
||||||
let user = client.store.getUser(presenceEvent.getContent().user_id);
|
let user = client.store.getUser(presenceEvent.getContent().user_id);
|
||||||
@@ -1004,7 +999,7 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
// - The isBrandNewRoom boilerplate is boilerplatey.
|
// - The isBrandNewRoom boilerplate is boilerplatey.
|
||||||
|
|
||||||
// handle presence events (User objects)
|
// handle presence events (User objects)
|
||||||
if (data.presence && utils.isArray(data.presence.events)) {
|
if (data.presence && Array.isArray(data.presence.events)) {
|
||||||
data.presence.events.map(client.getEventMapper()).forEach(
|
data.presence.events.map(client.getEventMapper()).forEach(
|
||||||
function(presenceEvent) {
|
function(presenceEvent) {
|
||||||
let user = client.store.getUser(presenceEvent.getSender());
|
let user = client.store.getUser(presenceEvent.getSender());
|
||||||
@@ -1020,7 +1015,7 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handle non-room account_data
|
// handle non-room account_data
|
||||||
if (data.account_data && utils.isArray(data.account_data.events)) {
|
if (data.account_data && Array.isArray(data.account_data.events)) {
|
||||||
const events = data.account_data.events.map(client.getEventMapper());
|
const events = data.account_data.events.map(client.getEventMapper());
|
||||||
const prevEventsMap = events.reduce((m, c) => {
|
const prevEventsMap = events.reduce((m, c) => {
|
||||||
m[c.getId()] = client.store.getAccountData(c.getType());
|
m[c.getId()] = client.store.getAccountData(c.getType());
|
||||||
@@ -1045,7 +1040,7 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handle to-device events
|
// handle to-device events
|
||||||
if (data.to_device && utils.isArray(data.to_device.events) &&
|
if (data.to_device && Array.isArray(data.to_device.events) &&
|
||||||
data.to_device.events.length > 0
|
data.to_device.events.length > 0
|
||||||
) {
|
) {
|
||||||
const cancelledKeyVerificationTxns = [];
|
const cancelledKeyVerificationTxns = [];
|
||||||
@@ -1156,10 +1151,15 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
await utils.promiseMapSeries(joinRooms, async function(joinObj) {
|
await utils.promiseMapSeries(joinRooms, async function(joinObj) {
|
||||||
const room = joinObj.room;
|
const room = joinObj.room;
|
||||||
const stateEvents = self._mapSyncEventsFormat(joinObj.state, room);
|
const stateEvents = self._mapSyncEventsFormat(joinObj.state, room);
|
||||||
const timelineEvents = self._mapSyncEventsFormat(joinObj.timeline, room);
|
// Prevent events from being decrypted ahead of time
|
||||||
|
// this helps large account to speed up faster
|
||||||
|
// room::decryptCriticalEvent is in charge of decrypting all the events
|
||||||
|
// required for a client to function properly
|
||||||
|
const timelineEvents = self._mapSyncEventsFormat(joinObj.timeline, room, false);
|
||||||
const ephemeralEvents = self._mapSyncEventsFormat(joinObj.ephemeral);
|
const ephemeralEvents = self._mapSyncEventsFormat(joinObj.ephemeral);
|
||||||
const accountDataEvents = self._mapSyncEventsFormat(joinObj.account_data);
|
const accountDataEvents = self._mapSyncEventsFormat(joinObj.account_data);
|
||||||
|
|
||||||
|
const encrypted = client.isRoomEncrypted(room.roomId);
|
||||||
// we do this first so it's correct when any of the events fire
|
// we do this first so it's correct when any of the events fire
|
||||||
if (joinObj.unread_notifications) {
|
if (joinObj.unread_notifications) {
|
||||||
room.setUnreadNotificationCount(
|
room.setUnreadNotificationCount(
|
||||||
@@ -1170,7 +1170,6 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
// bother setting it here. We trust our calculations better than the
|
// bother setting it here. We trust our calculations better than the
|
||||||
// server's for this case, and therefore will assume that our non-zero
|
// server's for this case, and therefore will assume that our non-zero
|
||||||
// count is accurate.
|
// count is accurate.
|
||||||
const encrypted = client.isRoomEncrypted(room.roomId);
|
|
||||||
if (!encrypted
|
if (!encrypted
|
||||||
|| (encrypted && room.getUnreadNotificationCount('highlight') <= 0)) {
|
|| (encrypted && room.getUnreadNotificationCount('highlight') <= 0)) {
|
||||||
room.setUnreadNotificationCount(
|
room.setUnreadNotificationCount(
|
||||||
@@ -1292,6 +1291,11 @@ SyncApi.prototype._processSyncResponse = async function(
|
|||||||
});
|
});
|
||||||
|
|
||||||
room.updateMyMembership("join");
|
room.updateMyMembership("join");
|
||||||
|
|
||||||
|
// Decrypt only the last message in all rooms to make sure we can generate a preview
|
||||||
|
// And decrypt all events after the recorded read receipt to ensure an accurate
|
||||||
|
// notification count
|
||||||
|
room.decryptCriticalEvents();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle leaves (e.g. kicked rooms)
|
// Handle leaves (e.g. kicked rooms)
|
||||||
@@ -1497,7 +1501,7 @@ SyncApi.prototype._mapSyncResponseToRoomArray = function(obj) {
|
|||||||
// [{stuff+Room+isBrandNewRoom}, {stuff+Room+isBrandNewRoom}]
|
// [{stuff+Room+isBrandNewRoom}, {stuff+Room+isBrandNewRoom}]
|
||||||
const client = this.client;
|
const client = this.client;
|
||||||
const self = this;
|
const self = this;
|
||||||
return utils.keys(obj).map(function(roomId) {
|
return Object.keys(obj).map(function(roomId) {
|
||||||
const arrObj = obj[roomId];
|
const arrObj = obj[roomId];
|
||||||
let room = client.store.getRoom(roomId);
|
let room = client.store.getRoom(roomId);
|
||||||
let isBrandNewRoom = false;
|
let isBrandNewRoom = false;
|
||||||
@@ -1514,13 +1518,14 @@ SyncApi.prototype._mapSyncResponseToRoomArray = function(obj) {
|
|||||||
/**
|
/**
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {Room} room
|
* @param {Room} room
|
||||||
|
* @param {bool} decrypt
|
||||||
* @return {MatrixEvent[]}
|
* @return {MatrixEvent[]}
|
||||||
*/
|
*/
|
||||||
SyncApi.prototype._mapSyncEventsFormat = function(obj, room) {
|
SyncApi.prototype._mapSyncEventsFormat = function(obj, room, decrypt = true) {
|
||||||
if (!obj || !utils.isArray(obj.events)) {
|
if (!obj || !Array.isArray(obj.events)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const mapper = this.client.getEventMapper();
|
const mapper = this.client.getEventMapper({ decrypt });
|
||||||
return obj.events.map(function(e) {
|
return obj.events.map(function(e) {
|
||||||
if (room) {
|
if (room) {
|
||||||
e.room_id = room.roomId;
|
e.room_id = room.roomId;
|
||||||
|
|||||||
331
src/utils.ts
331
src/utils.ts
@@ -61,116 +61,6 @@ export function encodeUri(pathTemplate: string,
|
|||||||
return pathTemplate;
|
return pathTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies a map function to the given array.
|
|
||||||
* @param {Array} array The array to apply the function to.
|
|
||||||
* @param {Function} fn The function that will be invoked for each element in
|
|
||||||
* the array with the signature <code>fn(element){...}</code>
|
|
||||||
* @return {Array} A new array with the results of the function.
|
|
||||||
*/
|
|
||||||
export function map<T, S>(array: T[], fn: (t: T) => S): S[] {
|
|
||||||
const results = new Array(array.length);
|
|
||||||
for (let i = 0; i < array.length; i++) {
|
|
||||||
results[i] = fn(array[i]);
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies a filter function to the given array.
|
|
||||||
* @param {Array} array The array to apply the function to.
|
|
||||||
* @param {Function} fn The function that will be invoked for each element in
|
|
||||||
* the array. It should return true to keep the element. The function signature
|
|
||||||
* looks like <code>fn(element, index, array){...}</code>.
|
|
||||||
* @return {Array} A new array with the results of the function.
|
|
||||||
*/
|
|
||||||
export function filter<T>(array: T[],
|
|
||||||
fn: (t: T, i?: number, a?: T[]) => boolean): T[] {
|
|
||||||
const results: T[] = [];
|
|
||||||
for (let i = 0; i < array.length; i++) {
|
|
||||||
if (fn(array[i], i, array)) {
|
|
||||||
results.push(array[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the keys for an object. Same as <code>Object.keys()</code>.
|
|
||||||
* @param {Object} obj The object to get the keys for.
|
|
||||||
* @return {string[]} The keys of the object.
|
|
||||||
*/
|
|
||||||
export function keys(obj: object): string[] {
|
|
||||||
const result = [];
|
|
||||||
for (const key in obj) {
|
|
||||||
if (!obj.hasOwnProperty(key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result.push(key);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the values for an object.
|
|
||||||
* @param {Object} obj The object to get the values for.
|
|
||||||
* @return {Array<*>} The values of the object.
|
|
||||||
*/
|
|
||||||
export function values<T>(obj: Record<string, T>): T[] {
|
|
||||||
const result = [];
|
|
||||||
for (const key in obj) {
|
|
||||||
if (!obj.hasOwnProperty(key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result.push(obj[key]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoke a function for each item in the array.
|
|
||||||
* @param {Array} array The array.
|
|
||||||
* @param {Function} fn The function to invoke for each element. Has the
|
|
||||||
* function signature <code>fn(element, index)</code>.
|
|
||||||
*/
|
|
||||||
export function forEach<T>(array: T[], fn: (t: T, i: number) => void) {
|
|
||||||
for (let i = 0; i < array.length; i++) {
|
|
||||||
fn(array[i], i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The findElement() method returns a value in the array, if an element in the array
|
|
||||||
* satisfies (returns true) the provided testing function. Otherwise undefined
|
|
||||||
* is returned.
|
|
||||||
* @param {Array} array The array.
|
|
||||||
* @param {Function} fn Function to execute on each value in the array, with the
|
|
||||||
* function signature <code>fn(element, index, array)</code>
|
|
||||||
* @param {boolean} reverse True to search in reverse order.
|
|
||||||
* @return {*} The first value in the array which returns <code>true</code> for
|
|
||||||
* the given function.
|
|
||||||
*/
|
|
||||||
export function findElement<T>(
|
|
||||||
array: T[],
|
|
||||||
fn: (t: T, i?: number, a?: T[]) => boolean,
|
|
||||||
reverse?: boolean,
|
|
||||||
) {
|
|
||||||
let i;
|
|
||||||
if (reverse) {
|
|
||||||
for (i = array.length - 1; i >= 0; i--) {
|
|
||||||
if (fn(array[i], i, array)) {
|
|
||||||
return array[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < array.length; i++) {
|
|
||||||
if (fn(array[i], i, array)) {
|
|
||||||
return array[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The removeElement() method removes the first element in the array that
|
* The removeElement() method removes the first element in the array that
|
||||||
* satisfies (returns true) the provided testing function.
|
* satisfies (returns true) the provided testing function.
|
||||||
@@ -217,16 +107,6 @@ export function isFunction(value: any) {
|
|||||||
return Object.prototype.toString.call(value) === "[object Function]";
|
return Object.prototype.toString.call(value) === "[object Function]";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the given thing is an array.
|
|
||||||
* @param {*} value The thing to check.
|
|
||||||
* @return {boolean} True if it is an array.
|
|
||||||
*/
|
|
||||||
export function isArray(value: any) {
|
|
||||||
return Array.isArray ? Array.isArray(value) :
|
|
||||||
Boolean(value && value.constructor === Array);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks that the given object has the specified keys.
|
* Checks that the given object has the specified keys.
|
||||||
* @param {Object} obj The object to check.
|
* @param {Object} obj The object to check.
|
||||||
@@ -380,207 +260,6 @@ export function extend(...restParams) {
|
|||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Run polyfills to add Array.map and Array.filter if they are missing.
|
|
||||||
*/
|
|
||||||
export function runPolyfills() {
|
|
||||||
// Array.prototype.filter
|
|
||||||
// ========================================================
|
|
||||||
// SOURCE:
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
|
|
||||||
if (!Array.prototype.filter) {
|
|
||||||
// eslint-disable-next-line no-extend-native
|
|
||||||
Array.prototype.filter = function(fun: Function/*, thisArg*/, ...restProps) {
|
|
||||||
if (this === void 0 || this === null) {
|
|
||||||
throw new TypeError();
|
|
||||||
}
|
|
||||||
|
|
||||||
const t = Object(this);
|
|
||||||
const len = t.length >>> 0;
|
|
||||||
if (typeof fun !== 'function') {
|
|
||||||
throw new TypeError();
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = [];
|
|
||||||
const thisArg = restProps ? restProps[0] : void 0;
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
if (i in t) {
|
|
||||||
const val = t[i];
|
|
||||||
|
|
||||||
// NOTE: Technically this should Object.defineProperty at
|
|
||||||
// the next index, as push can be affected by
|
|
||||||
// properties on Object.prototype and Array.prototype.
|
|
||||||
// But that method's new, and collisions should be
|
|
||||||
// rare, so use the more-compatible alternative.
|
|
||||||
if (fun.call(thisArg, val, i, t)) {
|
|
||||||
res.push(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Array.prototype.map
|
|
||||||
// ========================================================
|
|
||||||
// SOURCE:
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
|
|
||||||
// Production steps of ECMA-262, Edition 5, 15.4.4.19
|
|
||||||
// Reference: http://es5.github.io/#x15.4.4.19
|
|
||||||
if (!Array.prototype.map) {
|
|
||||||
// eslint-disable-next-line no-extend-native
|
|
||||||
Array.prototype.map = function(callback, thisArg) {
|
|
||||||
let T;
|
|
||||||
let k;
|
|
||||||
|
|
||||||
if (this === null || this === undefined) {
|
|
||||||
throw new TypeError(' this is null or not defined');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Let O be the result of calling ToObject passing the |this|
|
|
||||||
// value as the argument.
|
|
||||||
const O = Object(this);
|
|
||||||
|
|
||||||
// 2. Let lenValue be the result of calling the Get internal
|
|
||||||
// method of O with the argument "length".
|
|
||||||
// 3. Let len be ToUint32(lenValue).
|
|
||||||
const len = O.length >>> 0;
|
|
||||||
|
|
||||||
// 4. If IsCallable(callback) is false, throw a TypeError exception.
|
|
||||||
// See: http://es5.github.com/#x9.11
|
|
||||||
if (typeof callback !== 'function') {
|
|
||||||
throw new TypeError(callback + ' is not a function');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
|
||||||
if (arguments.length > 1) {
|
|
||||||
T = thisArg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Let A be a new array created as if by the expression new Array(len)
|
|
||||||
// where Array is the standard built-in constructor with that name and
|
|
||||||
// len is the value of len.
|
|
||||||
const A = new Array(len);
|
|
||||||
|
|
||||||
// 7. Let k be 0
|
|
||||||
k = 0;
|
|
||||||
|
|
||||||
// 8. Repeat, while k < len
|
|
||||||
while (k < len) {
|
|
||||||
let kValue;
|
|
||||||
let mappedValue;
|
|
||||||
|
|
||||||
// a. Let Pk be ToString(k).
|
|
||||||
// This is implicit for LHS operands of the in operator
|
|
||||||
// b. Let kPresent be the result of calling the HasProperty internal
|
|
||||||
// method of O with argument Pk.
|
|
||||||
// This step can be combined with c
|
|
||||||
// c. If kPresent is true, then
|
|
||||||
if (k in O) {
|
|
||||||
// i. Let kValue be the result of calling the Get internal
|
|
||||||
// method of O with argument Pk.
|
|
||||||
kValue = O[k];
|
|
||||||
|
|
||||||
// ii. Let mappedValue be the result of calling the Call internal
|
|
||||||
// method of callback with T as the this value and argument
|
|
||||||
// list containing kValue, k, and O.
|
|
||||||
mappedValue = callback.call(T, kValue, k, O);
|
|
||||||
|
|
||||||
// iii. Call the DefineOwnProperty internal method of A with arguments
|
|
||||||
// Pk, Property Descriptor
|
|
||||||
// { Value: mappedValue,
|
|
||||||
// Writable: true,
|
|
||||||
// Enumerable: true,
|
|
||||||
// Configurable: true },
|
|
||||||
// and false.
|
|
||||||
|
|
||||||
// In browsers that support Object.defineProperty, use the following:
|
|
||||||
// Object.defineProperty(A, k, {
|
|
||||||
// value: mappedValue,
|
|
||||||
// writable: true,
|
|
||||||
// enumerable: true,
|
|
||||||
// configurable: true
|
|
||||||
// });
|
|
||||||
|
|
||||||
// For best browser support, use the following:
|
|
||||||
A[k] = mappedValue;
|
|
||||||
}
|
|
||||||
// d. Increase k by 1.
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 9. return A
|
|
||||||
return A;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Array.prototype.forEach
|
|
||||||
// ========================================================
|
|
||||||
// SOURCE:
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
|
|
||||||
// Production steps of ECMA-262, Edition 5, 15.4.4.18
|
|
||||||
// Reference: http://es5.github.io/#x15.4.4.18
|
|
||||||
if (!Array.prototype.forEach) {
|
|
||||||
// eslint-disable-next-line no-extend-native
|
|
||||||
Array.prototype.forEach = function(callback, thisArg) {
|
|
||||||
let T;
|
|
||||||
let k;
|
|
||||||
|
|
||||||
if (this === null || this === undefined) {
|
|
||||||
throw new TypeError(' this is null or not defined');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Let O be the result of calling ToObject passing the |this| value as the
|
|
||||||
// argument.
|
|
||||||
const O = Object(this);
|
|
||||||
|
|
||||||
// 2. Let lenValue be the result of calling the Get internal method of O with the
|
|
||||||
// argument "length".
|
|
||||||
// 3. Let len be ToUint32(lenValue).
|
|
||||||
const len = O.length >>> 0;
|
|
||||||
|
|
||||||
// 4. If IsCallable(callback) is false, throw a TypeError exception.
|
|
||||||
// See: http://es5.github.com/#x9.11
|
|
||||||
if (typeof callback !== "function") {
|
|
||||||
throw new TypeError(callback + ' is not a function');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
|
||||||
if (arguments.length > 1) {
|
|
||||||
T = thisArg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Let k be 0
|
|
||||||
k = 0;
|
|
||||||
|
|
||||||
// 7. Repeat, while k < len
|
|
||||||
while (k < len) {
|
|
||||||
let kValue;
|
|
||||||
|
|
||||||
// a. Let Pk be ToString(k).
|
|
||||||
// This is implicit for LHS operands of the in operator
|
|
||||||
// b. Let kPresent be the result of calling the HasProperty internal
|
|
||||||
// method of O with
|
|
||||||
// argument Pk.
|
|
||||||
// This step can be combined with c
|
|
||||||
// c. If kPresent is true, then
|
|
||||||
if (k in O) {
|
|
||||||
// i. Let kValue be the result of calling the Get internal method of O with
|
|
||||||
// argument Pk
|
|
||||||
kValue = O[k];
|
|
||||||
|
|
||||||
// ii. Call the Call internal method of callback with T as the this value and
|
|
||||||
// argument list containing kValue, k, and O.
|
|
||||||
callback.call(T, kValue, k, O);
|
|
||||||
}
|
|
||||||
// d. Increase k by 1.
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
// 8. return undefined
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inherit the prototype methods from one constructor into another. This is a
|
* Inherit the prototype methods from one constructor into another. This is a
|
||||||
* port of the Node.js implementation with an Object.create polyfill.
|
* port of the Node.js implementation with an Object.create polyfill.
|
||||||
@@ -667,6 +346,16 @@ export function removeHiddenChars(str: string): string {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalize(str: string): string {
|
||||||
|
// Note: we have to match the filter with the removeHiddenChars() because the
|
||||||
|
// function strips spaces and other characters (M becomes RN for example, in lowercase).
|
||||||
|
return removeHiddenChars(str.toLowerCase())
|
||||||
|
// Strip all punctuation
|
||||||
|
.replace(/[\\'!"#$%&()*+,\-./:;<=>?@[\]^_`{|}~\u2000-\u206f\u2e00-\u2e7f]/g, "")
|
||||||
|
// We also doubly convert to lowercase to work around oddities of the library.
|
||||||
|
.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
// Regex matching bunch of unicode control characters and otherwise misleading/invisible characters.
|
// Regex matching bunch of unicode control characters and otherwise misleading/invisible characters.
|
||||||
// Includes:
|
// Includes:
|
||||||
// various width spaces U+2000 - U+200D
|
// various width spaces U+2000 - U+200D
|
||||||
|
|||||||
@@ -1505,7 +1505,8 @@ export class MatrixCall extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Order is important here: first we stopAllMedia() and only then we can deleteAllFeeds()
|
// Order is important here: first we stopAllMedia() and only then we can deleteAllFeeds()
|
||||||
this.stopAllMedia();
|
// We don't stop media if the call was replaced as we want to re-use streams in the successor
|
||||||
|
if (hangupReason !== CallErrorCode.Replaced) this.stopAllMedia();
|
||||||
this.deleteAllFeeds();
|
this.deleteAllFeeds();
|
||||||
|
|
||||||
this.hangupParty = hangupParty;
|
this.hangupParty = hangupParty;
|
||||||
@@ -1802,7 +1803,10 @@ export function createNewMatrixCall(client: any, roomId: string, options?: CallO
|
|||||||
window.RTCIceCandidate || navigator.mediaDevices,
|
window.RTCIceCandidate || navigator.mediaDevices,
|
||||||
);
|
);
|
||||||
if (!supported) {
|
if (!supported) {
|
||||||
logger.error("WebRTC is not supported in this browser / environment");
|
// Adds a lot of noise to test runs, so disable logging there.
|
||||||
|
if (process.env.NODE_ENV !== "test") {
|
||||||
|
logger.error("WebRTC is not supported in this browser / environment");
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ export class CallEventHandler {
|
|||||||
// after loading and after we've been offline for a bit.
|
// after loading and after we've been offline for a bit.
|
||||||
this.callEventBuffer = [];
|
this.callEventBuffer = [];
|
||||||
this.candidateEventsByCall = new Map<string, Array<MatrixEvent>>();
|
this.candidateEventsByCall = new Map<string, Array<MatrixEvent>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public start() {
|
||||||
this.client.on("sync", this.evaluateEventBuffer);
|
this.client.on("sync", this.evaluateEventBuffer);
|
||||||
this.client.on("event", this.onEvent);
|
this.client.on("event", this.onEvent);
|
||||||
}
|
}
|
||||||
@@ -52,10 +55,11 @@ export class CallEventHandler {
|
|||||||
this.client.removeListener("event", this.onEvent);
|
this.client.removeListener("event", this.onEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private evaluateEventBuffer = () => {
|
private evaluateEventBuffer = async () => {
|
||||||
if (this.client.getSyncState() === "SYNCING") {
|
if (this.client.getSyncState() === "SYNCING") {
|
||||||
// don't process any events until they are all decrypted
|
await Promise.all(this.callEventBuffer.map(event => {
|
||||||
if (this.callEventBuffer.some((e) => e.isBeingDecrypted())) return;
|
this.client.decryptEventIfNeeded(event);
|
||||||
|
}));
|
||||||
|
|
||||||
const ignoreCallIds = new Set<String>();
|
const ignoreCallIds = new Set<String>();
|
||||||
// inspect the buffer and mark all calls which have been answered
|
// inspect the buffer and mark all calls which have been answered
|
||||||
@@ -86,6 +90,7 @@ export class CallEventHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onEvent = (event: MatrixEvent) => {
|
private onEvent = (event: MatrixEvent) => {
|
||||||
|
this.client.decryptEventIfNeeded(event);
|
||||||
// any call events or ones that might be once they're decrypted
|
// any call events or ones that might be once they're decrypted
|
||||||
if (
|
if (
|
||||||
event.getType().indexOf("m.call.") === 0 ||
|
event.getType().indexOf("m.call.") === 0 ||
|
||||||
|
|||||||
@@ -1130,6 +1130,10 @@
|
|||||||
"@types/yargs" "^15.0.0"
|
"@types/yargs" "^15.0.0"
|
||||||
chalk "^4.0.0"
|
chalk "^4.0.0"
|
||||||
|
|
||||||
|
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz":
|
||||||
|
version "3.2.3"
|
||||||
|
resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz#cc332fdd25c08ef0e40f4d33fc3f822a0f98b6f4"
|
||||||
|
|
||||||
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents":
|
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents":
|
||||||
version "2.1.8-no-fsevents"
|
version "2.1.8-no-fsevents"
|
||||||
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz#da7c3996b8e6e19ebd14d82eaced2313e7769f9b"
|
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz#da7c3996b8e6e19ebd14d82eaced2313e7769f9b"
|
||||||
@@ -5142,10 +5146,6 @@ object.pick@^1.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
isobject "^3.0.1"
|
isobject "^3.0.1"
|
||||||
|
|
||||||
"olm@https://packages.matrix.org/npm/olm/olm-3.2.1.tgz":
|
|
||||||
version "3.2.1"
|
|
||||||
resolved "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz#d623d76f99c3518dde68be8c86618d68bc7b004a"
|
|
||||||
|
|
||||||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||||
|
|||||||
Reference in New Issue
Block a user