From a54f30c02fad9fa04adc3ea063be877fdb5581d6 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Thu, 3 May 2018 13:42:33 +0100 Subject: [PATCH 01/22] Add getMediaLimits to client --- src/client.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/client.js b/src/client.js index e8c44c653..1e7a12781 100644 --- a/src/client.js +++ b/src/client.js @@ -727,6 +727,20 @@ MatrixClient.prototype.getGroups = function() { return this.store.getGroups(); }; +/** + * Get the room for the given room ID. + * This function will return a valid room for any room for which a Room event + * has been emitted. Note in particular that other events, eg. RoomState.members + * will be emitted for a room before this function will return the given room. + * @param {module:client.callback} callback Optional. + * @return {module:client.Promise} Resolves with an object containing the limits. + */ +MatrixClient.prototype.getMediaLimits = function(callback) { + return return this._http.requestWithPrefix( + callback, "GET", "/limits", undefined, undefined, httpApi.PREFIX_MEDIA_R0, + ) +}; + // Room ops // ======== From 68c23af5aea1ce823b51f7c5f7f5a6e61dc5bfcb Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Thu, 3 May 2018 17:31:04 +0100 Subject: [PATCH 02/22] Remove extra return --- src/client.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client.js b/src/client.js index 1e7a12781..8f1d921be 100644 --- a/src/client.js +++ b/src/client.js @@ -736,9 +736,9 @@ MatrixClient.prototype.getGroups = function() { * @return {module:client.Promise} Resolves with an object containing the limits. */ MatrixClient.prototype.getMediaLimits = function(callback) { - return return this._http.requestWithPrefix( + return this._http.requestWithPrefix( callback, "GET", "/limits", undefined, undefined, httpApi.PREFIX_MEDIA_R0, - ) + ); }; // Room ops From 329f09ce0a734ab3d5abbf975a7012de127987af Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Wed, 20 Jun 2018 17:24:45 +0100 Subject: [PATCH 03/22] Media/limits => /config --- src/client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.js b/src/client.js index 8f1d921be..7ab2203d0 100644 --- a/src/client.js +++ b/src/client.js @@ -737,7 +737,7 @@ MatrixClient.prototype.getGroups = function() { */ MatrixClient.prototype.getMediaLimits = function(callback) { return this._http.requestWithPrefix( - callback, "GET", "/limits", undefined, undefined, httpApi.PREFIX_MEDIA_R0, + callback, "GET", "/config", undefined, undefined, httpApi.PREFIX_MEDIA_R0, ); }; From aec7ef6f9c8166bc087c8d4d1f8a7013aa6d5c12 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Sat, 23 Jun 2018 12:57:29 +0100 Subject: [PATCH 04/22] getMediaLimits -> getMediaConfig --- src/client.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/client.js b/src/client.js index 7ab2203d0..899a27d06 100644 --- a/src/client.js +++ b/src/client.js @@ -728,14 +728,11 @@ MatrixClient.prototype.getGroups = function() { }; /** - * Get the room for the given room ID. - * This function will return a valid room for any room for which a Room event - * has been emitted. Note in particular that other events, eg. RoomState.members - * will be emitted for a room before this function will return the given room. + * Get the config for the media repository. * @param {module:client.callback} callback Optional. - * @return {module:client.Promise} Resolves with an object containing the limits. + * @return {module:client.Promise} Resolves with an object containing the config. */ -MatrixClient.prototype.getMediaLimits = function(callback) { +MatrixClient.prototype.getMediaConfig = function(callback) { return this._http.requestWithPrefix( callback, "GET", "/config", undefined, undefined, httpApi.PREFIX_MEDIA_R0, ); From d28f829b1cf825f2707fe0c2ee9b6871738aedee Mon Sep 17 00:00:00 2001 From: Ben Parsons Date: Thu, 20 Sep 2018 13:12:50 +0100 Subject: [PATCH 05/22] add new examples, to be expanded into a post --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/README.md b/README.md index ec11da0a3..15eae610d 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,61 @@ In Node.js console.log("Public Rooms: %s", JSON.stringify(data)); }); ``` + See below for how to include libolm to enable end-to-end-encryption. Please check [the Node.js terminal app](examples/node) for a more complex example. +To start the client: + +```javascript +client.startClient({initialSyncLimit: 10}); +``` + +You can perform a call to `/sync` to get the current state of the client: + +```javascript +client.once('sync', function(state, prevState, res) { + if(state === 'PREPARED') { + console.log("prepared"); + } else { + console.log(state); + process.exit(1); + } +}); +``` + +To send a message: + +```javascript +var content = { + "body": "message text", + "msgtype": "m.text" +}; +client.sendEvent("roomId", "m.room.message", content, "", (err, res) => { + console.log(err); +}); +``` + +To listen for message events: + +```javascript +client.on("Room.timeline", function(event, room, toStartOfTimeline) { + if (event.getType() !== "m.room.message") { + return; // only use messages + } + console.log(event.event.content.body); +}); +``` + +By default, the `matrix-js-sdk` client uses the `MatrixInMemoryStore` to store events as they are received. Access this via `client.store`. For example to iterate through the currently stored timeline for a room: + +```javascript +Object.keys(client.store.rooms).forEach((roomId) => { + client.store.rooms[roomId].timeline.forEach(t => { + console.log(t.event); + }); +}); +``` What does this SDK do? ---------------------- From fbca7951fc3457e58b3643ba9ff988fb4a1d422a Mon Sep 17 00:00:00 2001 From: Ben Parsons Date: Mon, 1 Oct 2018 14:29:24 +0100 Subject: [PATCH 06/22] improvements suggested by Bruno --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 15eae610d..20dd640b6 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ See below for how to include libolm to enable end-to-end-encryption. Please chec To start the client: ```javascript -client.startClient({initialSyncLimit: 10}); +await client.startClient({initialSyncLimit: 10}); ``` You can perform a call to `/sync` to get the current state of the client: @@ -76,11 +76,11 @@ client.on("Room.timeline", function(event, room, toStartOfTimeline) { }); ``` -By default, the `matrix-js-sdk` client uses the `MatrixInMemoryStore` to store events as they are received. Access this via `client.store`. For example to iterate through the currently stored timeline for a room: +By default, the `matrix-js-sdk` client uses the `MatrixInMemoryStore` to store events as they are received. For example to iterate through the currently stored timeline for a room: ```javascript Object.keys(client.store.rooms).forEach((roomId) => { - client.store.rooms[roomId].timeline.forEach(t => { + client.getRoom(roomId).timeline.forEach(t => { console.log(t.event); }); }); From 8116c5b3f74062ae64ac753cb6f785c025304f8d Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 4 Oct 2018 13:49:32 +0100 Subject: [PATCH 07/22] Make e2e work on Edge We were sucessfully opening indexeddb but any queries using compound indicies were failing because Edge doesn't support them, so messages were failing to decrypt with 'DataError'. Try a dummy query at startup, so if it fails we fall back to a different store (ie. end up using localstorage on Edge). --- src/crypto/store/indexeddb-crypto-store.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/crypto/store/indexeddb-crypto-store.js b/src/crypto/store/indexeddb-crypto-store.js index 249b29b63..012ae4800 100644 --- a/src/crypto/store/indexeddb-crypto-store.js +++ b/src/crypto/store/indexeddb-crypto-store.js @@ -92,6 +92,15 @@ export default class IndexedDBCryptoStore { console.log(`connected to indexeddb ${this._dbName}`); resolve(new IndexedDBCryptoStoreBackend.Backend(db)); }; + }).then((backend) => { + // Edge has IndexedDB but doesn't support compund keys which we use fairly extensively. + // Try a dummy query which will fail if the browser doesn't support compund keys, so + // we can fall back to a different backend. + return backend.doTxn('readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + backend.getEndToEndInboundGroupSession('', '', txn, () => {}); + }).then(() => { + return backend; + }); }).catch((e) => { console.warn( `unable to connect to indexeddb ${this._dbName}` + From 4cda54ca1ca812662382e1cfe31bbac38d419cbf Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 4 Oct 2018 15:15:30 +0100 Subject: [PATCH 08/22] lint --- src/crypto/store/indexeddb-crypto-store.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/crypto/store/indexeddb-crypto-store.js b/src/crypto/store/indexeddb-crypto-store.js index 012ae4800..bb2a22ca7 100644 --- a/src/crypto/store/indexeddb-crypto-store.js +++ b/src/crypto/store/indexeddb-crypto-store.js @@ -96,11 +96,13 @@ export default class IndexedDBCryptoStore { // Edge has IndexedDB but doesn't support compund keys which we use fairly extensively. // Try a dummy query which will fail if the browser doesn't support compund keys, so // we can fall back to a different backend. - return backend.doTxn('readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { - backend.getEndToEndInboundGroupSession('', '', txn, () => {}); - }).then(() => { - return backend; - }); + return backend.doTxn( + 'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + backend.getEndToEndInboundGroupSession('', '', txn, () => {}); + }).then(() => { + return backend; + }, + ); }).catch((e) => { console.warn( `unable to connect to indexeddb ${this._dbName}` + From 40dc13b2e2b276570551d3025ea81e3884647e85 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 4 Oct 2018 15:38:08 +0100 Subject: [PATCH 09/22] lint try 2 --- src/crypto/store/indexeddb-crypto-store.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/crypto/store/indexeddb-crypto-store.js b/src/crypto/store/indexeddb-crypto-store.js index bb2a22ca7..0e0654deb 100644 --- a/src/crypto/store/indexeddb-crypto-store.js +++ b/src/crypto/store/indexeddb-crypto-store.js @@ -97,7 +97,9 @@ export default class IndexedDBCryptoStore { // Try a dummy query which will fail if the browser doesn't support compund keys, so // we can fall back to a different backend. return backend.doTxn( - 'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { + 'readonly', + [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], + (txn) => { backend.getEndToEndInboundGroupSession('', '', txn, () => {}); }).then(() => { return backend; From 30362091e543b09f713d77603ce8c77edd5f47f9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 10 Oct 2018 16:59:36 +0100 Subject: [PATCH 10/22] Don't fail to start up if lazy load check fails Do the lazy loading check in the batch of things we do before starting a sync rather than at client start time, so we don't fail to start the client if we can't hit the HS to determine LL support. Fixes https://github.com/vector-im/riot-web/issues/7455 --- src/client.js | 63 +++--------------------------------------- src/errors.js | 2 +- src/filter.js | 11 ++++++++ src/sync.js | 76 +++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 90 insertions(+), 62 deletions(-) diff --git a/src/client.js b/src/client.js index 577cf5ba8..d5ca91059 100644 --- a/src/client.js +++ b/src/client.js @@ -44,22 +44,11 @@ const ContentHelpers = require("./content-helpers"); import ReEmitter from './ReEmitter'; import RoomList from './crypto/RoomList'; -import {InvalidStoreError} from './errors'; // Disable warnings for now: we use deprecated bluebird functions // and need to migrate, but they spam the console with warnings. Promise.config({warnings: false}); -const LAZY_LOADING_MESSAGES_FILTER = { - lazy_load_members: true, -}; - -const LAZY_LOADING_SYNC_FILTER = { - room: { - state: LAZY_LOADING_MESSAGES_FILTER, - }, -}; - const SCROLLBACK_DELAY_MS = 3000; let CRYPTO_ENABLED = false; @@ -2071,7 +2060,7 @@ MatrixClient.prototype.getEventTimeline = function(timelineSet, eventId) { let params = undefined; if (this._clientOpts.lazyLoadMembers) { - params = {filter: JSON.stringify(LAZY_LOADING_MESSAGES_FILTER)}; + params = {filter: JSON.stringify(Filter.LAZY_LOADING_MESSAGES_FILTER)}; } // TODO: we should implement a backoff (as per scrollback()) to deal more @@ -2152,7 +2141,7 @@ function(roomId, fromToken, limit, dir, timelineFilter = undefined) { if (this._clientOpts.lazyLoadMembers) { // create a shallow copy of LAZY_LOADING_MESSAGES_FILTER, // so the timelineFilter doesn't get written into it below - filter = Object.assign({}, LAZY_LOADING_MESSAGES_FILTER); + filter = Object.assign({}, Filter.LAZY_LOADING_MESSAGES_FILTER); } if (timelineFilter) { // XXX: it's horrific that /messages' filter parameter doesn't match @@ -3109,29 +3098,6 @@ MatrixClient.prototype.startClient = async function(opts) { // shallow-copy the opts dict before modifying and storing it opts = Object.assign({}, opts); - if (opts.lazyLoadMembers && this.isGuest()) { - opts.lazyLoadMembers = false; - } - if (opts.lazyLoadMembers) { - const supported = await this.doesServerSupportLazyLoading(); - if (supported) { - opts.filter = await this.createFilter(LAZY_LOADING_SYNC_FILTER); - } else { - console.log("LL: lazy loading requested but not supported " + - "by server, so disabling"); - opts.lazyLoadMembers = false; - } - } - // need to vape the store when enabling LL and wasn't enabled before - const shouldClear = await this._wasLazyLoadingToggled(opts.lazyLoadMembers); - if (shouldClear) { - const reason = InvalidStoreError.TOGGLED_LAZY_LOADING; - throw new InvalidStoreError(reason, !!opts.lazyLoadMembers); - } - if (opts.lazyLoadMembers && this._crypto) { - this._crypto.enableLazyLoading(); - } - opts.crypto = this._crypto; opts.canResetEntireTimeline = (roomId) => { if (!this._canResetTimelineCallback) { @@ -3140,40 +3106,19 @@ MatrixClient.prototype.startClient = async function(opts) { return this._canResetTimelineCallback(roomId); }; this._clientOpts = opts; - await this._storeClientOptions(this._clientOpts); this._syncApi = new SyncApi(this, opts); this._syncApi.sync(); }; -/** - * Is the lazy loading option different than in previous session? - * @param {bool} lazyLoadMembers current options for lazy loading - * @return {bool} whether or not the option has changed compared to the previous session */ -MatrixClient.prototype._wasLazyLoadingToggled = async function(lazyLoadMembers) { - lazyLoadMembers = !!lazyLoadMembers; - // assume it was turned off before - // if we don't know any better - let lazyLoadMembersBefore = false; - const isStoreNewlyCreated = await this.store.isNewlyCreated(); - if (!isStoreNewlyCreated) { - const prevClientOptions = await this.store.getClientOptions(); - if (prevClientOptions) { - lazyLoadMembersBefore = !!prevClientOptions.lazyLoadMembers; - } - return lazyLoadMembersBefore !== lazyLoadMembers; - } - return false; -}; - /** * store client options with boolean/string/numeric values * to know in the next session what flags the sync data was * created with (e.g. lazy loading) * @param {object} opts the complete set of client options * @return {Promise} for store operation */ -MatrixClient.prototype._storeClientOptions = function(opts) { +MatrixClient.prototype._storeClientOptions = function() { const primTypes = ["boolean", "string", "number"]; - const serializableOpts = Object.entries(opts) + const serializableOpts = Object.entries(this._clientOpts) .filter(([key, value]) => { return primTypes.includes(typeof value); }) diff --git a/src/errors.js b/src/errors.js index 04e14f2c8..337b058db 100644 --- a/src/errors.js +++ b/src/errors.js @@ -2,7 +2,7 @@ // because of http://babeljs.io/docs/usage/caveats/#classes function InvalidStoreError(reason, value) { const message = `Store is invalid because ${reason}, ` + - `please delete all data and retry`; + `please stopthe client, delete all data and start the client again`; const instance = Reflect.construct(Error, [message]); Reflect.setPrototypeOf(instance, Reflect.getPrototypeOf(this)); instance.reason = reason; diff --git a/src/filter.js b/src/filter.js index e0f03daa7..a63fcee13 100644 --- a/src/filter.js +++ b/src/filter.js @@ -51,6 +51,17 @@ function Filter(userId, filterId) { this.definition = {}; } +Filter.LAZY_LOADING_MESSAGES_FILTER = { + lazy_load_members: true, +}; + +Filter.LAZY_LOADING_SYNC_FILTER = { + room: { + state: Filter.LAZY_LOADING_MESSAGES_FILTER, + }, +}; + + /** * Get the ID of this filter on your homeserver (if known) * @return {?Number} The filter ID diff --git a/src/sync.js b/src/sync.js index 39e82abe4..634d07e1e 100644 --- a/src/sync.js +++ b/src/sync.js @@ -33,6 +33,8 @@ const utils = require("./utils"); const Filter = require("./filter"); const EventTimeline = require("./models/event-timeline"); +import {InvalidStoreError} from './errors'; + const DEBUG = true; // /sync requests allow you to set a timeout= but the request may continue @@ -100,6 +102,7 @@ function SyncApi(client, opts) { this._connectionReturnedDefer = null; this._notifEvents = []; // accumulator of sync events in the current sync response this._failedSyncCount = 0; // Number of consecutive failed /sync requests + this._storeIsInvalid = false; // flag set if the store needs to be cleared before we can start if (client.getNotifTimelineSet()) { client.reEmitter.reEmit(client.getNotifTimelineSet(), @@ -422,6 +425,28 @@ SyncApi.prototype.recoverFromSyncStartupError = async function(savedSyncPromise, await keepaliveProm; }; +/** + * Is the lazy loading option different than in previous session? + * @param {bool} lazyLoadMembers current options for lazy loading + * @return {bool} whether or not the option has changed compared to the previous session */ +SyncApi.prototype._wasLazyLoadingToggled = async function(lazyLoadMembers) { + lazyLoadMembers = !!lazyLoadMembers; + // assume it was turned off before + // if we don't know any better + let lazyLoadMembersBefore = false; + const isStoreNewlyCreated = await this.client.store.isNewlyCreated(); + console.log("store newly created? "+isStoreNewlyCreated); + if (!isStoreNewlyCreated) { + const prevClientOptions = await this.client.store.getClientOptions(); + if (prevClientOptions) { + lazyLoadMembersBefore = !!prevClientOptions.lazyLoadMembers; + } + console.log("prev ll: "+lazyLoadMembersBefore); + return lazyLoadMembersBefore !== lazyLoadMembers; + } + return false; +}; + /** * Main entry point */ @@ -444,6 +469,8 @@ SyncApi.prototype.sync = function() { // 1) We need to get push rules so we can check if events should bing as we get // them from /sync. // 2) We need to get/create a filter which we can use for /sync. + // 3) We need to check the lazy loading option matches what was used in the + // stored sync. If it doesn't, we can't use the stored sync. async function getPushRules() { try { @@ -458,9 +485,49 @@ SyncApi.prototype.sync = function() { getPushRules(); return; } - getFilter(); // Now get the filter and start syncing + checkLazyLoadStatus(); // advance to the next stage } + const checkLazyLoadStatus = async () => { + if (this.opts.lazyLoadMembers && client.isGuest()) { + this.opts.lazyLoadMembers = false; + } + if (this.opts.lazyLoadMembers) { + const supported = await client.doesServerSupportLazyLoading(); + console.log("server supports ll? "+supported); + if (supported) { + this.opts.filter = await client.createFilter( + Filter.LAZY_LOADING_SYNC_FILTER, + ); + } else { + console.log("LL: lazy loading requested but not supported " + + "by server, so disabling"); + this.opts.lazyLoadMembers = false; + } + } + // need to vape the store when enabling LL and wasn't enabled before + const shouldClear = await this._wasLazyLoadingToggled(this.opts.lazyLoadMembers); + console.log("was toggled? "+shouldClear); + if (shouldClear) { + this._storeIsInvalid = true; + const reason = InvalidStoreError.TOGGLED_LAZY_LOADING; + const error = new InvalidStoreError(reason, !!this.opts.lazyLoadMembers); + this._updateSyncState("ERROR", { error }); + // bail out of the sync loop now: the app needs to respond to this error. + // we leave the state as 'ERROR' which isn't great since this normally means + // we're retrying. The client must be stopped before clearing the stores anyway + // so the app should stop the client, clear the store and start it again. + console.warn("InvalidStoreError: store is not usable: stopping sync."); + return; + } + if (this.opts.lazyLoadMembers && this._crypto) { + this.opts.crypto.enableLazyLoading(); + } + await this.client._storeClientOptions(); + + getFilter(); // Now get the filter and start syncing + }; + async function getFilter() { let filter; if (self.opts.filter) { @@ -588,7 +655,12 @@ SyncApi.prototype._syncFromCache = async function(savedSync) { console.error("Error processing cached sync", e.stack || e); } - this._updateSyncState("PREPARED", syncEventData); + // Don't emit a prepared if we've bailed because the store is invalid: + // in this case the client will not be usable until stopped & restarted + // so this would be useless and misleading. + if (!this._storeIsInvalid) { + this._updateSyncState("PREPARED", syncEventData); + } }; /** From dea70af889767cc1cb5e8958561791007925bd79 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 10 Oct 2018 17:06:26 +0100 Subject: [PATCH 11/22] remove debug logging --- src/sync.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/sync.js b/src/sync.js index 634d07e1e..76122874b 100644 --- a/src/sync.js +++ b/src/sync.js @@ -435,13 +435,11 @@ SyncApi.prototype._wasLazyLoadingToggled = async function(lazyLoadMembers) { // if we don't know any better let lazyLoadMembersBefore = false; const isStoreNewlyCreated = await this.client.store.isNewlyCreated(); - console.log("store newly created? "+isStoreNewlyCreated); if (!isStoreNewlyCreated) { const prevClientOptions = await this.client.store.getClientOptions(); if (prevClientOptions) { lazyLoadMembersBefore = !!prevClientOptions.lazyLoadMembers; } - console.log("prev ll: "+lazyLoadMembersBefore); return lazyLoadMembersBefore !== lazyLoadMembers; } return false; @@ -494,7 +492,6 @@ SyncApi.prototype.sync = function() { } if (this.opts.lazyLoadMembers) { const supported = await client.doesServerSupportLazyLoading(); - console.log("server supports ll? "+supported); if (supported) { this.opts.filter = await client.createFilter( Filter.LAZY_LOADING_SYNC_FILTER, @@ -507,7 +504,6 @@ SyncApi.prototype.sync = function() { } // need to vape the store when enabling LL and wasn't enabled before const shouldClear = await this._wasLazyLoadingToggled(this.opts.lazyLoadMembers); - console.log("was toggled? "+shouldClear); if (shouldClear) { this._storeIsInvalid = true; const reason = InvalidStoreError.TOGGLED_LAZY_LOADING; From ecc3e18e850929431f8c966520eb2881df1be6b5 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 10 Oct 2018 17:27:06 +0100 Subject: [PATCH 12/22] typo --- src/errors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/errors.js b/src/errors.js index 337b058db..409fafd9c 100644 --- a/src/errors.js +++ b/src/errors.js @@ -2,7 +2,7 @@ // because of http://babeljs.io/docs/usage/caveats/#classes function InvalidStoreError(reason, value) { const message = `Store is invalid because ${reason}, ` + - `please stopthe client, delete all data and start the client again`; + `please stop the client, delete all data and start the client again`; const instance = Reflect.construct(Error, [message]); Reflect.setPrototypeOf(instance, Reflect.getPrototypeOf(this)); instance.reason = reason; From 3cf23f8a5ca23227aa566f4ff5280cfc22e94d11 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 11 Oct 2018 12:13:25 +0100 Subject: [PATCH 13/22] Document breaking change --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c9b82dc..896e3667b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +Latest Changes: +=============== + +BREAKING CHANGES +---------------- + * If js-sdk finds data in the store that is incompatible with the options currently being used, + it will emit sync state ERROR with an error of type InvalidStoreError. It will also stop trying + to sync in this situation: the app must stop the client and then either clear the store or + change the options (in this case, enable or disable lazy loading of members) and then start + the client again. + Changes in [0.11.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.11.1) (2018-10-01) ================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.11.1-rc.1...v0.11.1) From d098b3902417d99a5672dc64ebcbc26170e2f7bc Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 11 Oct 2018 14:11:40 +0200 Subject: [PATCH 14/22] never replace /sync'ed memberships with OOB ones --- src/models/room-state.js | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/models/room-state.js b/src/models/room-state.js index 52bc5ed36..a868d63c1 100644 --- a/src/models/room-state.js +++ b/src/models/room-state.js @@ -483,21 +483,9 @@ RoomState.prototype._setOutOfBandMember = function(stateEvent) { } const userId = stateEvent.getStateKey(); const existingMember = this.getMember(userId); - if (existingMember) { - const existingMemberEvent = existingMember.events.member; - // ignore out of band members with events we are - // already aware of. - if (existingMemberEvent.getId() === stateEvent.getId()) { - return; - } - // this member was updated since we started - // loading the out of band members. - // Ignore the out of band member and clear - // the "supersedes" flag as the out of members are now loaded - if (existingMember.supersedesOutOfBand()) { - existingMember.clearSupersedesOutOfBand(); - return; - } + // never replace members replaced as part of the sync + if (existingMember && !existingMember.isOutOfBand()) { + return; } const member = this._getOrCreateMember(userId, stateEvent); From a0ef6ab811d82a4a81e1fac34293745027ed55e6 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 11 Oct 2018 14:14:42 +0200 Subject: [PATCH 15/22] typo --- src/models/room-state.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/room-state.js b/src/models/room-state.js index a868d63c1..9a85e71ce 100644 --- a/src/models/room-state.js +++ b/src/models/room-state.js @@ -483,7 +483,7 @@ RoomState.prototype._setOutOfBandMember = function(stateEvent) { } const userId = stateEvent.getStateKey(); const existingMember = this.getMember(userId); - // never replace members replaced as part of the sync + // never replace members received as part of the sync if (existingMember && !existingMember.isOutOfBand()) { return; } From 2ed694b041438e9f4ec1f0789837551cb4bd14d6 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 11 Oct 2018 14:32:03 +0200 Subject: [PATCH 16/22] remove supersedes OOB logic --- spec/unit/room-member.spec.js | 16 --------- spec/unit/room-state.spec.js | 62 +++-------------------------------- src/models/room-member.js | 26 --------------- src/models/room-state.js | 24 ++------------ 4 files changed, 7 insertions(+), 121 deletions(-) diff --git a/spec/unit/room-member.spec.js b/spec/unit/room-member.spec.js index 5212282b1..298771128 100644 --- a/spec/unit/room-member.spec.js +++ b/spec/unit/room-member.spec.js @@ -201,22 +201,6 @@ describe("RoomMember", function() { }); }); - describe("supersedesOutOfBand", function() { - it("should be set by markSupersedesOutOfBand", function() { - const member = new RoomMember(); - expect(member.supersedesOutOfBand()).toEqual(false); - member.markSupersedesOutOfBand(); - expect(member.supersedesOutOfBand()).toEqual(true); - }); - it("should be cleared by clearSupersedesOutOfBand", function() { - const member = new RoomMember(); - member.markSupersedesOutOfBand(); - expect(member.supersedesOutOfBand()).toEqual(true); - member.clearSupersedesOutOfBand(); - expect(member.supersedesOutOfBand()).toEqual(false); - }); - }); - describe("setMembershipEvent", function() { const joinEvent = utils.mkMembership({ event: true, diff --git a/spec/unit/room-state.spec.js b/spec/unit/room-state.spec.js index 6619c64a7..baa602781 100644 --- a/spec/unit/room-state.spec.js +++ b/spec/unit/room-state.spec.js @@ -299,41 +299,25 @@ describe("RoomState", function() { expect(eventReceived).toEqual(true); }); - it("should overwrite existing members", function() { + it("should never overwrite existing members", function() { const oobMemberEvent = utils.mkMembership({ user: userA, mship: "join", room: roomId, event: true, }); state.markOutOfBandMembersStarted(); state.setOutOfBandMembers([oobMemberEvent]); const memberA = state.getMember(userA); - expect(memberA.events.member.getId()).toEqual(oobMemberEvent.getId()); - expect(memberA.isOutOfBand()).toEqual(true); - }); - - it("should allow later state events to overwrite", function() { - const oobMemberEvent = utils.mkMembership({ - user: userA, mship: "join", room: roomId, event: true, - }); - const memberEvent = utils.mkMembership({ - user: userA, mship: "join", room: roomId, event: true, - }); - - state.markOutOfBandMembersStarted(); - state.setOutOfBandMembers([oobMemberEvent]); - state.setStateEvents([memberEvent]); - - const memberA = state.getMember(userA); - expect(memberA.events.member.getId()).toEqual(memberEvent.getId()); + expect(memberA.events.member.getId()).toNotEqual(oobMemberEvent.getId()); expect(memberA.isOutOfBand()).toEqual(false); }); it("should emit members when updating a member", function() { + const doesntExistYetUserId = "@doesntexistyet:hs"; const oobMemberEvent = utils.mkMembership({ - user: userA, mship: "join", room: roomId, event: true, + user: doesntExistYetUserId, mship: "join", room: roomId, event: true, }); let eventReceived = false; state.once('RoomState.members', (_, __, member) => { - expect(member.userId).toEqual(userA); + expect(member.userId).toEqual(doesntExistYetUserId); eventReceived = true; }); @@ -341,28 +325,6 @@ describe("RoomState", function() { state.setOutOfBandMembers([oobMemberEvent]); expect(eventReceived).toEqual(true); }); - - - it("should not overwrite members updated since starting loading oob", - function() { - const oobMemberEvent = utils.mkMembership({ - user: userA, mship: "join", room: roomId, event: true, - }); - - const existingMemberEvent = utils.mkMembership({ - user: userA, mship: "join", room: roomId, event: true, - }); - - state.markOutOfBandMembersStarted(); - state.setStateEvents([existingMemberEvent]); - expect(state.getMember(userA).supersedesOutOfBand()).toEqual(true); - state.setOutOfBandMembers([oobMemberEvent]); - - const memberA = state.getMember(userA); - expect(memberA.events.member.getId()).toEqual(existingMemberEvent.getId()); - expect(memberA.isOutOfBand()).toEqual(false); - expect(memberA.supersedesOutOfBand()).toEqual(false); - }); }); describe("clone", function() { @@ -386,20 +348,6 @@ describe("RoomState", function() { expect(state.getJoinedMemberCount()).toEqual(copy.getJoinedMemberCount()); }); - it("should copy supersedes flag when OOB loading is progress", - function() { - // include OOB members in copy - state.markOutOfBandMembersStarted(); - state.setStateEvents([utils.mkMembership({ - user: userA, mship: "join", room: roomId, event: true, - })]); - const copy = state.clone(); - const memberA = state.getMember(userA); - const memberACopy = copy.getMember(userA); - expect(memberA.supersedesOutOfBand()).toEqual(true); - expect(memberACopy.supersedesOutOfBand()).toEqual(true); - }); - it("should mark old copy as not waiting for out of band anymore", function() { state.markOutOfBandMembersStarted(); const copy = state.clone(); diff --git a/src/models/room-member.js b/src/models/room-member.js index 598cae398..e7a4bf88c 100644 --- a/src/models/room-member.js +++ b/src/models/room-member.js @@ -59,7 +59,6 @@ function RoomMember(roomId, userId) { member: null, }; this._isOutOfBand = false; - this._supersedesOutOfBand = false; this._updateModifiedTime(); } utils.inherits(RoomMember, EventEmitter); @@ -80,31 +79,6 @@ RoomMember.prototype.isOutOfBand = function() { return this._isOutOfBand; }; -/** - * Does the member supersede an incoming out-of-band - * member? If so the out-of-band member should be ignored. - * @return {bool} - */ -RoomMember.prototype.supersedesOutOfBand = function() { - return this._supersedesOutOfBand; -}; - -/** - * Mark the member as superseding the future incoming - * out-of-band members. - */ -RoomMember.prototype.markSupersedesOutOfBand = function() { - this._supersedesOutOfBand = true; -}; - -/** - * Clear the member superseding the future incoming - * out-of-band members, as loading finished or failed. - */ -RoomMember.prototype.clearSupersedesOutOfBand = function() { - this._supersedesOutOfBand = false; -}; - /** * Update this room member's membership event. May fire "RoomMember.name" if * this event updates this member's name. diff --git a/src/models/room-state.js b/src/models/room-state.js index 9a85e71ce..ac5e20077 100644 --- a/src/models/room-state.js +++ b/src/models/room-state.js @@ -220,8 +220,7 @@ RoomState.prototype.clone = function() { // if loading is in progress (through _oobMemberFlags) // since these are not new members, we're merely copying them // set the status to not started - // after copying, we set back the status and - // copy the superseding flag from the current state + // after copying, we set back the status const status = this._oobMemberFlags.status; this._oobMemberFlags.status = OOB_STATUS_NOTSTARTED; @@ -249,14 +248,6 @@ RoomState.prototype.clone = function() { copyMember.markOutOfBand(); } }); - } else if (this._oobMemberFlags.status == OOB_STATUS_INPROGRESS) { - // copy markSupersedesOutOfBand flags - this.getMembers().forEach((member) => { - if (member.supersedesOutOfBand()) { - const copyMember = copy.getMember(member.userId); - copyMember.markSupersedesOutOfBand(); - } - }); } return copy; @@ -341,11 +332,6 @@ RoomState.prototype.setStateEvents = function(stateEvents) { const member = self._getOrCreateMember(userId, event); member.setMembershipEvent(event, self); - // if out of band members are loading, - // mark the member as more recent - if (self._oobMemberFlags.status == OOB_STATUS_INPROGRESS) { - member.markSupersedesOutOfBand(); - } self._updateMember(member); self.emit("RoomState.members", event, self, member); @@ -434,12 +420,6 @@ RoomState.prototype.markOutOfBandMembersFailed = function() { if (this._oobMemberFlags.status !== OOB_STATUS_INPROGRESS) { return; } - // the request failed, there is nothing to supersede - // in case of a retry, these event would not supersede the - // retry anymore. - this.getMembers().forEach((m) => { - m.clearSupersedesOutOfBand(); - }); this._oobMemberFlags.status = OOB_STATUS_NOTSTARTED; }; @@ -491,7 +471,7 @@ RoomState.prototype._setOutOfBandMember = function(stateEvent) { const member = this._getOrCreateMember(userId, stateEvent); member.setMembershipEvent(stateEvent, this); // needed to know which members need to be stored seperately - // as the are not part of the sync accumulator + // as they are not part of the sync accumulator // this is cleared by setMembershipEvent so when it's updated through /sync member.markOutOfBand(); From c0ae78ae82ef9765ce892844dcb34654c6bdb7f2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 11 Oct 2018 14:42:29 +0100 Subject: [PATCH 17/22] Prepare changelog for v0.12.0-rc.1 --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 896e3667b..255527f7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,30 @@ +Changes in [0.12.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.12.0-rc.1) (2018-10-11) +============================================================================================================ +[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.11.1...v0.12.0-rc.1) + + * never replace /sync'ed memberships with OOB ones + [\#760](https://github.com/matrix-org/matrix-js-sdk/pull/760) + * Don't fail to start up if lazy load check fails + [\#759](https://github.com/matrix-org/matrix-js-sdk/pull/759) + * Make e2e work on Edge + [\#754](https://github.com/matrix-org/matrix-js-sdk/pull/754) + * throw error with same name and message over idb worker boundary + [\#758](https://github.com/matrix-org/matrix-js-sdk/pull/758) + * Default to a room version of 1 when there is no room create event + [\#755](https://github.com/matrix-org/matrix-js-sdk/pull/755) + * Silence bluebird warnings + [\#757](https://github.com/matrix-org/matrix-js-sdk/pull/757) + * allow non-ff merge from release branch into master + [\#750](https://github.com/matrix-org/matrix-js-sdk/pull/750) + * Reject with the actual error on indexeddb error + [\#751](https://github.com/matrix-org/matrix-js-sdk/pull/751) + * Update mocha to v5 + [\#744](https://github.com/matrix-org/matrix-js-sdk/pull/744) + * disable lazy loading for guests as they cant create filters + [\#748](https://github.com/matrix-org/matrix-js-sdk/pull/748) + * Revert "Add getMediaLimits to client" + [\#745](https://github.com/matrix-org/matrix-js-sdk/pull/745) + Latest Changes: =============== From 6aff3ed407ddd94daef5ca001c209eef02819d2a Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 11 Oct 2018 14:42:29 +0100 Subject: [PATCH 18/22] v0.12.0-rc.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index db0b14bb1..3c4065e91 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-js-sdk", - "version": "0.11.1", + "version": "0.12.0-rc.1", "description": "Matrix Client-Server SDK for Javascript", "main": "index.js", "scripts": { From 874029dff0686c1b30ae41cb5811c7dd8cf60d3d Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 11 Oct 2018 15:38:34 +0100 Subject: [PATCH 19/22] oops - fix changelog format in retrospect --- CHANGELOG.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 255527f7f..fe454f3ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ Changes in [0.12.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/ta ============================================================================================================ [Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.11.1...v0.12.0-rc.1) +BREAKING CHANGES +---------------- + * If js-sdk finds data in the store that is incompatible with the options currently being used, + it will emit sync state ERROR with an error of type InvalidStoreError. It will also stop trying + to sync in this situation: the app must stop the client and then either clear the store or + change the options (in this case, enable or disable lazy loading of members) and then start + the client again. + +All Changes +----------- + * never replace /sync'ed memberships with OOB ones [\#760](https://github.com/matrix-org/matrix-js-sdk/pull/760) * Don't fail to start up if lazy load check fails @@ -25,17 +36,6 @@ Changes in [0.12.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/ta * Revert "Add getMediaLimits to client" [\#745](https://github.com/matrix-org/matrix-js-sdk/pull/745) -Latest Changes: -=============== - -BREAKING CHANGES ----------------- - * If js-sdk finds data in the store that is incompatible with the options currently being used, - it will emit sync state ERROR with an error of type InvalidStoreError. It will also stop trying - to sync in this situation: the app must stop the client and then either clear the store or - change the options (in this case, enable or disable lazy loading of members) and then start - the client again. - Changes in [0.11.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.11.1) (2018-10-01) ================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.11.1-rc.1...v0.11.1) From 68497d3a1fea6d85687338862e72587294ee4ae7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 Oct 2018 10:47:32 +0100 Subject: [PATCH 20/22] Prepare changelog for v0.12.0 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe454f3ac..82902fc22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Changes in [0.12.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.12.0) (2018-10-16) +================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.12.0-rc.1...v0.12.0) + + * No changes since rc.1 + Changes in [0.12.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.12.0-rc.1) (2018-10-11) ============================================================================================================ [Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.11.1...v0.12.0-rc.1) From 874bdea634677dda51d163b2815cb1cc28935013 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 Oct 2018 10:47:33 +0100 Subject: [PATCH 21/22] v0.12.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3c4065e91..cc5a7a0e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-js-sdk", - "version": "0.12.0-rc.1", + "version": "0.12.0", "description": "Matrix Client-Server SDK for Javascript", "main": "index.js", "scripts": { From 8d35bea830205073ca5c5ac71442c7b5b69b040d Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Sat, 20 Oct 2018 21:37:55 -0500 Subject: [PATCH 22/22] Add repository type to package.json to make it valid Signed-off-by: Aaron Raimist --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index cc5a7a0e5..88f87f90b 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "prepublish": "npm run clean && npm run build && git rev-parse HEAD > git-revision.txt" }, "repository": { + "type": "git", "url": "https://github.com/matrix-org/matrix-js-sdk" }, "keywords": [