1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-29 16:43:09 +03:00

Merge remote-tracking branch 'origin/develop' into dbkr/e2e_backups

This commit is contained in:
David Baker
2018-10-02 16:50:37 +01:00
18 changed files with 303 additions and 73 deletions

View File

@@ -1,3 +1,38 @@
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)
* No changes since rc.1
Changes in [0.11.1-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.11.1-rc.1) (2018-09-27)
============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.11.0...v0.11.1-rc.1)
* make usage of hub compatible with latest version (2.5)
[\#747](https://github.com/matrix-org/matrix-js-sdk/pull/747)
* Detect when lazy loading has been toggled in client.startClient
[\#746](https://github.com/matrix-org/matrix-js-sdk/pull/746)
* Add getMediaLimits to client
[\#644](https://github.com/matrix-org/matrix-js-sdk/pull/644)
* Split npm start into an init and watch script
[\#742](https://github.com/matrix-org/matrix-js-sdk/pull/742)
* Revert "room name should only take canonical alias into account"
[\#738](https://github.com/matrix-org/matrix-js-sdk/pull/738)
* fix display name disambiguation with LL
[\#737](https://github.com/matrix-org/matrix-js-sdk/pull/737)
* Introduce Room.myMembership event
[\#735](https://github.com/matrix-org/matrix-js-sdk/pull/735)
* room name should only take canonical alias into account
[\#733](https://github.com/matrix-org/matrix-js-sdk/pull/733)
* state events from context response were not wrapped in a MatrixEvent
[\#732](https://github.com/matrix-org/matrix-js-sdk/pull/732)
* Reduce amount of promises created when inserting members
[\#724](https://github.com/matrix-org/matrix-js-sdk/pull/724)
* dont wait for LL members to be stored to resolve the members
[\#726](https://github.com/matrix-org/matrix-js-sdk/pull/726)
* RoomState.members emitted with wrong argument order for OOB members
[\#728](https://github.com/matrix-org/matrix-js-sdk/pull/728)
Changes in [0.11.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.11.0) (2018-09-10) Changes in [0.11.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.11.0) (2018-09-10)
================================================================================================== ==================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.11.0-rc.1...v0.11.0) [Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.11.0-rc.1...v0.11.0)

View File

@@ -1,6 +1,6 @@
{ {
"name": "matrix-js-sdk", "name": "matrix-js-sdk",
"version": "0.11.0", "version": "0.11.1",
"description": "Matrix Client-Server SDK for Javascript", "description": "Matrix Client-Server SDK for Javascript",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@@ -10,7 +10,9 @@
"test": "npm run test:build && npm run test:run", "test": "npm run test:build && npm run test:run",
"check": "npm run test:build && _mocha --recursive specbuild --colors", "check": "npm run test:build && _mocha --recursive specbuild --colors",
"gendoc": "babel --no-babelrc -d .jsdocbuild src && jsdoc -r .jsdocbuild -P package.json -R README.md -d .jsdoc", "gendoc": "babel --no-babelrc -d .jsdocbuild src && jsdoc -r .jsdocbuild -P package.json -R README.md -d .jsdoc",
"start": "babel -s -w -d lib src", "start": "npm run start:init && npm run start:watch",
"start:watch": "babel -s -w --skip-initial-build -d lib src",
"start:init": "babel -s -d lib src",
"clean": "rimraf lib dist", "clean": "rimraf lib dist",
"build": "babel -s -d lib src && rimraf dist && mkdir dist && browserify -d browser-index.js | exorcist dist/browser-matrix.js.map > dist/browser-matrix.js && uglifyjs -c -m -o dist/browser-matrix.min.js --source-map dist/browser-matrix.min.js.map --in-source-map dist/browser-matrix.js.map dist/browser-matrix.js", "build": "babel -s -d lib src && rimraf dist && mkdir dist && browserify -d browser-index.js | exorcist dist/browser-matrix.js.map > dist/browser-matrix.js && uglifyjs -c -m -o dist/browser-matrix.min.js --source-map dist/browser-matrix.min.js.map --in-source-map dist/browser-matrix.js.map dist/browser-matrix.js",
"dist": "npm run build", "dist": "npm run build",
@@ -72,8 +74,8 @@
"jsdoc": "^3.5.5", "jsdoc": "^3.5.5",
"lolex": "^1.5.2", "lolex": "^1.5.2",
"matrix-mock-request": "^1.2.0", "matrix-mock-request": "^1.2.0",
"mocha": "^3.2.0", "mocha": "^5.2.0",
"mocha-jenkins-reporter": "^0.3.6", "mocha-jenkins-reporter": "^0.4.0",
"rimraf": "^2.5.4", "rimraf": "^2.5.4",
"source-map-support": "^0.4.11", "source-map-support": "^0.4.11",
"sourceify": "^0.1.0", "sourceify": "^0.1.0",

View File

@@ -245,7 +245,7 @@ release_text=`mktemp`
echo "$tag" > "${release_text}" echo "$tag" > "${release_text}"
echo >> "${release_text}" echo >> "${release_text}"
cat "${latest_changes}" >> "${release_text}" cat "${latest_changes}" >> "${release_text}"
hub release create $hubflags $assets -f "${release_text}" "$tag" hub release create $hubflags $assets -F "${release_text}" "$tag"
if [ $dodist -eq 0 ]; then if [ $dodist -eq 0 ]; then
rm -rf "$builddir" rm -rf "$builddir"

View File

@@ -94,7 +94,7 @@ describe("MatrixClient opts", function() {
httpBackend.flush("/txn1", 1); httpBackend.flush("/txn1", 1);
}); });
it("should be able to sync / get new events", function(done) { it("should be able to sync / get new events", async function() {
const expectedEventTypes = [ // from /initialSync const expectedEventTypes = [ // from /initialSync
"m.room.message", "m.room.name", "m.room.member", "m.room.member", "m.room.message", "m.room.name", "m.room.member", "m.room.member",
"m.room.create", "m.room.create",
@@ -110,20 +110,16 @@ describe("MatrixClient opts", function() {
httpBackend.when("GET", "/pushrules").respond(200, {}); httpBackend.when("GET", "/pushrules").respond(200, {});
httpBackend.when("POST", "/filter").respond(200, { filter_id: "foo" }); httpBackend.when("POST", "/filter").respond(200, { filter_id: "foo" });
httpBackend.when("GET", "/sync").respond(200, syncData); httpBackend.when("GET", "/sync").respond(200, syncData);
client.startClient(); await client.startClient();
httpBackend.flush("/pushrules", 1).then(function() { await httpBackend.flush("/pushrules", 1);
return httpBackend.flush("/filter", 1); await httpBackend.flush("/filter", 1);
}).then(function() { await Promise.all([
return Promise.all([ httpBackend.flush("/sync", 1),
httpBackend.flush("/sync", 1), utils.syncPromise(client),
utils.syncPromise(client), ]);
]); expect(expectedEventTypes.length).toEqual(
}).done(function() { 0, "Expected to see event types: " + expectedEventTypes,
expect(expectedEventTypes.length).toEqual( );
0, "Expected to see event types: " + expectedEventTypes,
);
done();
});
}); });
}); });

View File

@@ -139,6 +139,9 @@ describe("MatrixClient", function() {
store.getSavedSync = expect.createSpy().andReturn(Promise.resolve(null)); store.getSavedSync = expect.createSpy().andReturn(Promise.resolve(null));
store.getSavedSyncToken = expect.createSpy().andReturn(Promise.resolve(null)); store.getSavedSyncToken = expect.createSpy().andReturn(Promise.resolve(null));
store.setSyncData = expect.createSpy().andReturn(Promise.resolve(null)); store.setSyncData = expect.createSpy().andReturn(Promise.resolve(null));
store.getClientOptions = expect.createSpy().andReturn(Promise.resolve(null));
store.storeClientOptions = expect.createSpy().andReturn(Promise.resolve(null));
store.isNewlyCreated = expect.createSpy().andReturn(Promise.resolve(true));
client = new MatrixClient({ client = new MatrixClient({
baseUrl: "https://my.home.server", baseUrl: "https://my.home.server",
idBaseUrl: identityServerUrl, idBaseUrl: identityServerUrl,
@@ -182,7 +185,7 @@ describe("MatrixClient", function() {
}); });
}); });
it("should not POST /filter if a matching filter already exists", function(done) { it("should not POST /filter if a matching filter already exists", async function() {
httpLookups = []; httpLookups = [];
httpLookups.push(PUSH_RULES_RESPONSE); httpLookups.push(PUSH_RULES_RESPONSE);
httpLookups.push(SYNC_RESPONSE); httpLookups.push(SYNC_RESPONSE);
@@ -191,15 +194,19 @@ describe("MatrixClient", function() {
const filter = new sdk.Filter(0, filterId); const filter = new sdk.Filter(0, filterId);
filter.setDefinition({"room": {"timeline": {"limit": 8}}}); filter.setDefinition({"room": {"timeline": {"limit": 8}}});
store.getFilter.andReturn(filter); store.getFilter.andReturn(filter);
client.startClient(); const syncPromise = new Promise((resolve, reject) => {
client.on("sync", function syncListener(state) {
client.on("sync", function syncListener(state) { if (state === "SYNCING") {
if (state === "SYNCING") { expect(httpLookups.length).toEqual(0);
expect(httpLookups.length).toEqual(0); client.removeListener("sync", syncListener);
client.removeListener("sync", syncListener); resolve();
done(); } else if (state === "ERROR") {
} reject(new Error("sync error"));
}
});
}); });
await client.startClient();
await syncPromise;
}); });
describe("getSyncState", function() { describe("getSyncState", function() {
@@ -207,15 +214,18 @@ describe("MatrixClient", function() {
expect(client.getSyncState()).toBe(null); expect(client.getSyncState()).toBe(null);
}); });
it("should return the same sync state as emitted sync events", function(done) { it("should return the same sync state as emitted sync events", async function() {
client.on("sync", function syncListener(state) { const syncingPromise = new Promise((resolve) => {
expect(state).toEqual(client.getSyncState()); client.on("sync", function syncListener(state) {
if (state === "SYNCING") { expect(state).toEqual(client.getSyncState());
client.removeListener("sync", syncListener); if (state === "SYNCING") {
done(); client.removeListener("sync", syncListener);
} resolve();
}
});
}); });
client.startClient(); await client.startClient();
await syncingPromise;
}); });
}); });
@@ -258,8 +268,8 @@ describe("MatrixClient", function() {
}); });
describe("retryImmediately", function() { describe("retryImmediately", function() {
it("should return false if there is no request waiting", function() { it("should return false if there is no request waiting", async function() {
client.startClient(); await client.startClient();
expect(client.retryImmediately()).toBe(false); expect(client.retryImmediately()).toBe(false);
}); });

View File

@@ -1402,13 +1402,25 @@ describe("Room", function() {
it("should return synced membership if membership isn't available yet", it("should return synced membership if membership isn't available yet",
function() { function() {
const room = new Room(roomId, null, userA); const room = new Room(roomId, null, userA);
room.setSyncedMembership("invite"); room.updateMyMembership("invite");
expect(room.getMyMembership()).toEqual("invite"); expect(room.getMyMembership()).toEqual("invite");
room.addLiveEvents([utils.mkMembership({ });
user: userA, mship: "join", it("should emit a Room.myMembership event on a change",
room: roomId, event: true, function() {
})]); const room = new Room(roomId, null, userA);
const events = [];
room.on("Room.myMembership", (_room, membership, oldMembership) => {
events.push({membership, oldMembership});
});
room.updateMyMembership("invite");
expect(room.getMyMembership()).toEqual("invite");
expect(events[0]).toEqual({membership: "invite", oldMembership: null});
events.splice(0); //clear
room.updateMyMembership("invite");
expect(events.length).toEqual(0);
room.updateMyMembership("join");
expect(room.getMyMembership()).toEqual("join"); expect(room.getMyMembership()).toEqual("join");
expect(events[0]).toEqual({membership: "join", oldMembership: "invite"});
}); });
}); });
@@ -1439,11 +1451,11 @@ describe("Room", function() {
it("should return false if synced membership not join", it("should return false if synced membership not join",
function() { function() {
const room = new Room(roomId, null, userA); const room = new Room(roomId, null, userA);
room.setSyncedMembership("invite"); room.updateMyMembership("invite");
expect(room.maySendMessage()).toEqual(false); expect(room.maySendMessage()).toEqual(false);
room.setSyncedMembership("leave"); room.updateMyMembership("leave");
expect(room.maySendMessage()).toEqual(false); expect(room.maySendMessage()).toEqual(false);
room.setSyncedMembership("join"); room.updateMyMembership("join");
expect(room.maySendMessage()).toEqual(true); expect(room.maySendMessage()).toEqual(true);
}); });
}); });

View File

@@ -45,6 +45,7 @@ const olmlib = require("./crypto/olmlib");
import ReEmitter from './ReEmitter'; import ReEmitter from './ReEmitter';
import RoomList from './crypto/RoomList'; import RoomList from './crypto/RoomList';
import {InvalidStoreError} from './errors';
const LAZY_LOADING_MESSAGES_FILTER = { const LAZY_LOADING_MESSAGES_FILTER = {
@@ -2437,7 +2438,8 @@ MatrixClient.prototype.getEventTimeline = function(timelineSet, eventId) {
self.getEventMapper())); self.getEventMapper()));
timeline.getState(EventTimeline.FORWARDS).paginationToken = res.end; timeline.getState(EventTimeline.FORWARDS).paginationToken = res.end;
} else { } else {
timeline.getState(EventTimeline.BACKWARDS).setUnknownStateEvents(res.state); const stateEvents = utils.map(res.state, self.getEventMapper());
timeline.getState(EventTimeline.BACKWARDS).setUnknownStateEvents(stateEvents);
} }
timelineSet.addEventsToTimeline(matrixEvents, true, timeline, res.start); timelineSet.addEventsToTimeline(matrixEvents, true, timeline, res.start);
@@ -3439,6 +3441,9 @@ MatrixClient.prototype.startClient = async function(opts) {
// shallow-copy the opts dict before modifying and storing it // shallow-copy the opts dict before modifying and storing it
opts = Object.assign({}, opts); opts = Object.assign({}, opts);
if (opts.lazyLoadMembers && this.isGuest()) {
opts.lazyLoadMembers = false;
}
if (opts.lazyLoadMembers) { if (opts.lazyLoadMembers) {
const supported = await this.doesServerSupportLazyLoading(); const supported = await this.doesServerSupportLazyLoading();
if (supported) { if (supported) {
@@ -3449,7 +3454,12 @@ MatrixClient.prototype.startClient = async function(opts) {
opts.lazyLoadMembers = false; 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) { if (opts.lazyLoadMembers && this._crypto) {
this._crypto.enableLazyLoading(); this._crypto.enableLazyLoading();
} }
@@ -3462,11 +3472,50 @@ MatrixClient.prototype.startClient = async function(opts) {
return this._canResetTimelineCallback(roomId); return this._canResetTimelineCallback(roomId);
}; };
this._clientOpts = opts; this._clientOpts = opts;
await this._storeClientOptions(this._clientOpts);
this._syncApi = new SyncApi(this, opts); this._syncApi = new SyncApi(this, opts);
this._syncApi.sync(); 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) {
const primTypes = ["boolean", "string", "number"];
const serializableOpts = Object.entries(opts)
.filter(([key, value]) => {
return primTypes.includes(typeof value);
})
.reduce((obj, [key, value]) => {
obj[key] = value;
return obj;
}, {});
return this.store.storeClientOptions(serializableOpts);
};
/** /**
* High level helper method to stop the client from polling and allow a * High level helper method to stop the client from polling and allow a
* clean shutdown. * clean shutdown.

25
src/errors.js Normal file
View File

@@ -0,0 +1,25 @@
// can't just do InvalidStoreError extends Error
// 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`;
const instance = Reflect.construct(Error, [message]);
Reflect.setPrototypeOf(instance, Reflect.getPrototypeOf(this));
instance.reason = reason;
instance.value = value;
return instance;
}
InvalidStoreError.TOGGLED_LAZY_LOADING = "TOGGLED_LAZY_LOADING";
InvalidStoreError.prototype = Object.create(Error.prototype, {
constructor: {
value: Error,
enumerable: false,
writable: true,
configurable: true,
},
});
Reflect.setPrototypeOf(InvalidStoreError, Error);
module.exports.InvalidStoreError = InvalidStoreError;

View File

@@ -34,6 +34,8 @@ module.exports.SyncAccumulator = require("./sync-accumulator");
module.exports.MatrixHttpApi = require("./http-api").MatrixHttpApi; module.exports.MatrixHttpApi = require("./http-api").MatrixHttpApi;
/** The {@link module:http-api.MatrixError|MatrixError} class. */ /** The {@link module:http-api.MatrixError|MatrixError} class. */
module.exports.MatrixError = require("./http-api").MatrixError; module.exports.MatrixError = require("./http-api").MatrixError;
/** The {@link module:errors.InvalidStoreError|InvalidStoreError} class. */
module.exports.InvalidStoreError = require("./errors").InvalidStoreError;
/** The {@link module:client.MatrixClient|MatrixClient} class. */ /** The {@link module:client.MatrixClient|MatrixClient} class. */
module.exports.MatrixClient = require("./client").MatrixClient; module.exports.MatrixClient = require("./client").MatrixClient;
/** The {@link module:models/room|Room} class. */ /** The {@link module:models/room|Room} class. */

View File

@@ -180,7 +180,7 @@ RoomState.prototype.getSentinelMember = function(userId) {
sentinel = new RoomMember(this.roomId, userId); sentinel = new RoomMember(this.roomId, userId);
const member = this.members[userId]; const member = this.members[userId];
if (member) { if (member) {
sentinel.setMembershipEvent(member.events.member); sentinel.setMembershipEvent(member.events.member, this);
} }
this._sentinels[userId] = sentinel; this._sentinels[userId] = sentinel;
} }
@@ -501,7 +501,7 @@ RoomState.prototype._setOutOfBandMember = function(stateEvent) {
} }
const member = this._getOrCreateMember(userId, stateEvent); const member = this._getOrCreateMember(userId, stateEvent);
member.setMembershipEvent(stateEvent); member.setMembershipEvent(stateEvent, this);
// needed to know which members need to be stored seperately // needed to know which members need to be stored seperately
// as the are not part of the sync accumulator // as the are not part of the sync accumulator
// this is cleared by setMembershipEvent so when it's updated through /sync // this is cleared by setMembershipEvent so when it's updated through /sync

View File

@@ -178,7 +178,7 @@ function Room(roomId, client, myUserId, opts) {
// read by megolm; boolean value - null indicates "use global value" // read by megolm; boolean value - null indicates "use global value"
this._blacklistUnverifiedDevices = null; this._blacklistUnverifiedDevices = null;
this._syncedMembership = null; this._selfMembership = null;
this._summaryHeroes = null; this._summaryHeroes = null;
// awaited by getEncryptionTargetMembers while room members are loading // awaited by getEncryptionTargetMembers while room members are loading
@@ -257,13 +257,7 @@ Room.prototype.getLiveTimeline = function() {
* @return {string} the membership type (join | leave | invite) for the logged in user * @return {string} the membership type (join | leave | invite) for the logged in user
*/ */
Room.prototype.getMyMembership = function() { Room.prototype.getMyMembership = function() {
if (this.myUserId) { return this._selfMembership;
const me = this.getMember(this.myUserId);
if (me) {
return me.membership;
}
}
return this._syncedMembership;
}; };
/** /**
@@ -278,7 +272,7 @@ Room.prototype.getDMInviter = function() {
return me.getDMInviter(); return me.getDMInviter();
} }
} }
if (this._syncedMembership === "invite") { if (this._selfMembership === "invite") {
// fall back to summary information // fall back to summary information
const memberCount = this.getInvitedAndJoinedMemberCount(); const memberCount = this.getInvitedAndJoinedMemberCount();
if (memberCount == 2 && this._summaryHeroes.length) { if (memberCount == 2 && this._summaryHeroes.length) {
@@ -362,8 +356,15 @@ Room.prototype.getAvatarFallbackMember = function() {
* Sets the membership this room was received as during sync * Sets the membership this room was received as during sync
* @param {string} membership join | leave | invite * @param {string} membership join | leave | invite
*/ */
Room.prototype.setSyncedMembership = function(membership) { Room.prototype.updateMyMembership = function(membership) {
this._syncedMembership = membership; const prevMembership = this._selfMembership;
this._selfMembership = membership;
if (prevMembership !== membership) {
if (membership === "leave") {
this._cleanupAfterLeaving();
}
this.emit("Room.myMembership", this, membership, prevMembership);
}
}; };
Room.prototype._loadMembersFromServer = async function() { Room.prototype._loadMembersFromServer = async function() {
@@ -470,7 +471,7 @@ Room.prototype.clearLoadedMembersIfNeeded = async function() {
* called when sync receives this room in the leave section * called when sync receives this room in the leave section
* to do cleanup after leaving a room. Possibly called multiple times. * to do cleanup after leaving a room. Possibly called multiple times.
*/ */
Room.prototype.onLeft = function() { Room.prototype._cleanupAfterLeaving = function() {
this.clearLoadedMembersIfNeeded().catch((err) => { this.clearLoadedMembersIfNeeded().catch((err) => {
console.error(`error after clearing loaded members from ` + console.error(`error after clearing loaded members from ` +
`room ${this.roomId} after leaving`); `room ${this.roomId} after leaving`);

View File

@@ -19,7 +19,7 @@ import Promise from 'bluebird';
import SyncAccumulator from "../sync-accumulator"; import SyncAccumulator from "../sync-accumulator";
import utils from "../utils"; import utils from "../utils";
const VERSION = 2; const VERSION = 3;
function createDatabase(db) { function createDatabase(db) {
// Make user store, clobber based on user ID. (userId property of User objects) // Make user store, clobber based on user ID. (userId property of User objects)
@@ -41,6 +41,12 @@ function upgradeSchemaV2(db) {
oobMembersStore.createIndex("room", "room_id"); oobMembersStore.createIndex("room", "room_id");
} }
function upgradeSchemaV3(db) {
db.createObjectStore("client_options",
{ keyPath: ["clobber"]});
}
/** /**
* Helper method to collect results from a Cursor and promiseify it. * Helper method to collect results from a Cursor and promiseify it.
* @param {ObjectStore|Index} store The store to perform openCursor on. * @param {ObjectStore|Index} store The store to perform openCursor on.
@@ -123,6 +129,7 @@ const LocalIndexedDBStoreBackend = function LocalIndexedDBStoreBackend(
this.db = null; this.db = null;
this._disconnected = true; this._disconnected = true;
this._syncAccumulator = new SyncAccumulator(); this._syncAccumulator = new SyncAccumulator();
this._isNewlyCreated = false;
}; };
@@ -153,11 +160,15 @@ LocalIndexedDBStoreBackend.prototype = {
`LocalIndexedDBStoreBackend.connect: upgrading from ${oldVersion}`, `LocalIndexedDBStoreBackend.connect: upgrading from ${oldVersion}`,
); );
if (oldVersion < 1) { // The database did not previously exist. if (oldVersion < 1) { // The database did not previously exist.
this._isNewlyCreated = true;
createDatabase(db); createDatabase(db);
} }
if (oldVersion < 2) { if (oldVersion < 2) {
upgradeSchemaV2(db); upgradeSchemaV2(db);
} }
if (oldVersion < 3) {
upgradeSchemaV3(db);
}
// Expand as needed. // Expand as needed.
}; };
@@ -185,6 +196,10 @@ LocalIndexedDBStoreBackend.prototype = {
return this._init(); return this._init();
}); });
}, },
/** @return {bool} whether or not the database was newly created in this session. */
isNewlyCreated: function() {
return Promise.resolve(this._isNewlyCreated);
},
/** /**
* Having connected, load initial data from the database and prepare for use * Having connected, load initial data from the database and prepare for use
@@ -529,6 +544,28 @@ LocalIndexedDBStoreBackend.prototype = {
}); });
}); });
}, },
getClientOptions: function() {
return Promise.resolve().then(() => {
const txn = this.db.transaction(["client_options"], "readonly");
const store = txn.objectStore("client_options");
return selectQuery(store, undefined, (cursor) => {
if (cursor.value && cursor.value && cursor.value.options) {
return cursor.value.options;
}
}).then((results) => results[0]);
});
},
storeClientOptions: async function(options) {
const txn = this.db.transaction(["client_options"], "readwrite");
const store = txn.objectStore("client_options");
store.put({
clobber: "-", // constant key so will always clobber
options: options,
}); // put == UPSERT
await txnAsPromise(txn);
},
}; };
export default LocalIndexedDBStoreBackend; export default LocalIndexedDBStoreBackend;

View File

@@ -65,7 +65,10 @@ RemoteIndexedDBStoreBackend.prototype = {
clearDatabase: function() { clearDatabase: function() {
return this._ensureStarted().then(() => this._doCmd('clearDatabase')); return this._ensureStarted().then(() => this._doCmd('clearDatabase'));
}, },
/** @return {Promise<bool>} whether or not the database was newly created in this session. */
isNewlyCreated: function() {
return this._doCmd('isNewlyCreated');
},
/** /**
* @return {Promise} Resolves with a sync response to restore the * @return {Promise} Resolves with a sync response to restore the
* client state to where it was at the last save, or null if there * client state to where it was at the last save, or null if there
@@ -114,6 +117,14 @@ RemoteIndexedDBStoreBackend.prototype = {
return this._doCmd('clearOutOfBandMembers', [roomId]); return this._doCmd('clearOutOfBandMembers', [roomId]);
}, },
getClientOptions: function() {
return this._doCmd('getClientOptions');
},
storeClientOptions: function(options) {
return this._doCmd('storeClientOptions', [options]);
},
/** /**
* Load all user presence events from the database. This is not cached. * Load all user presence events from the database. This is not cached.
* @return {Promise<Object[]>} A list of presence events in their raw form. * @return {Promise<Object[]>} A list of presence events in their raw form.

View File

@@ -67,6 +67,9 @@ class IndexedDBStoreWorker {
case 'connect': case 'connect':
prom = this.backend.connect(); prom = this.backend.connect();
break; break;
case 'isNewlyCreated':
prom = this.backend.isNewlyCreated();
break;
case 'clearDatabase': case 'clearDatabase':
prom = this.backend.clearDatabase().then((result) => { prom = this.backend.clearDatabase().then((result) => {
// This returns special classes which can't be cloned // This returns special classes which can't be cloned
@@ -101,10 +104,16 @@ class IndexedDBStoreWorker {
case 'setOutOfBandMembers': case 'setOutOfBandMembers':
prom = this.backend.setOutOfBandMembers(msg.args[0], msg.args[1]); prom = this.backend.setOutOfBandMembers(msg.args[0], msg.args[1]);
break; break;
case 'getClientOptions':
prom = this.backend.getClientOptions();
break;
case 'storeClientOptions':
prom = this.backend.storeClientOptions(msg.args[0]);
break;
} }
if (prom === undefined) { if (prom === undefined) {
postMessage({ this.postMessage({
command: 'cmd_fail', command: 'cmd_fail',
seq: msg.seq, seq: msg.seq,
// Can't be an Error because they're not structured cloneable // Can't be an Error because they're not structured cloneable

View File

@@ -146,6 +146,11 @@ IndexedDBStore.prototype.getSavedSync = function() {
return this.backend.getSavedSync(); return this.backend.getSavedSync();
}; };
/** @return {Promise<bool>} whether or not the database was newly created in this session. */
IndexedDBStore.prototype.isNewlyCreated = function() {
return this.backend.isNewlyCreated();
};
/** /**
* @return {Promise} If there is a saved sync, the nextBatch token * @return {Promise} If there is a saved sync, the nextBatch token
* for this sync, otherwise null. * for this sync, otherwise null.
@@ -246,4 +251,12 @@ IndexedDBStore.prototype.clearOutOfBandMembers = function(roomId) {
return this.backend.clearOutOfBandMembers(roomId); return this.backend.clearOutOfBandMembers(roomId);
}; };
IndexedDBStore.prototype.getClientOptions = function() {
return this.backend.getClientOptions();
};
IndexedDBStore.prototype.storeClientOptions = function(options) {
return this.backend.storeClientOptions(options);
};
module.exports.IndexedDBStore = IndexedDBStore; module.exports.IndexedDBStore = IndexedDBStore;

View File

@@ -55,6 +55,7 @@ module.exports.MatrixInMemoryStore = function MatrixInMemoryStore(opts) {
this._oobMembers = { this._oobMembers = {
// roomId: [member events] // roomId: [member events]
}; };
this._clientOptions = {};
}; };
module.exports.MatrixInMemoryStore.prototype = { module.exports.MatrixInMemoryStore.prototype = {
@@ -67,6 +68,10 @@ module.exports.MatrixInMemoryStore.prototype = {
return this.syncToken; return this.syncToken;
}, },
/** @return {Promise<bool>} whether or not the database was newly created in this session. */
isNewlyCreated: function() {
return Promise.resolve(true);
},
/** /**
* Set the token to stream from. * Set the token to stream from.
@@ -402,4 +407,13 @@ module.exports.MatrixInMemoryStore.prototype = {
this._oobMembers[roomId] = membershipEvents; this._oobMembers[roomId] = membershipEvents;
return Promise.resolve(); return Promise.resolve();
}, },
getClientOptions: function() {
return Promise.resolve(this._clientOptions);
},
storeClientOptions: function(options) {
this._clientOptions = Object.assign({}, options);
return Promise.resolve();
},
}; };

View File

@@ -32,6 +32,11 @@ function StubStore() {
StubStore.prototype = { StubStore.prototype = {
/** @return {Promise<bool>} whether or not the database was newly created in this session. */
isNewlyCreated: function() {
return Promise.resolve(true);
},
/** /**
* Get the sync token. * Get the sync token.
* @return {string} * @return {string}
@@ -276,6 +281,14 @@ StubStore.prototype = {
clearOutOfBandMembers: function() { clearOutOfBandMembers: function() {
return Promise.resolve(); return Promise.resolve();
}, },
getClientOptions: function() {
return Promise.resolve();
},
storeClientOptions: function() {
return Promise.resolve();
},
}; };
/** Stub Store class. */ /** Stub Store class. */

View File

@@ -123,6 +123,7 @@ SyncApi.prototype.createRoom = function(roomId) {
"Room.timelineReset", "Room.timelineReset",
"Room.localEchoUpdated", "Room.localEchoUpdated",
"Room.accountData", "Room.accountData",
"Room.myMembership",
]); ]);
this._registerStateListeners(room); this._registerStateListeners(room);
return room; return room;
@@ -976,9 +977,10 @@ SyncApi.prototype._processSyncResponse = async function(
// Handle invites // Handle invites
inviteRooms.forEach(function(inviteObj) { inviteRooms.forEach(function(inviteObj) {
const room = inviteObj.room; const room = inviteObj.room;
room.setSyncedMembership("invite");
const stateEvents = const stateEvents =
self._mapSyncEventsFormat(inviteObj.invite_state, room); self._mapSyncEventsFormat(inviteObj.invite_state, room);
room.updateMyMembership("invite");
self._processRoomEvents(room, stateEvents); self._processRoomEvents(room, stateEvents);
if (inviteObj.isBrandNewRoom) { if (inviteObj.isBrandNewRoom) {
room.recalculate(); room.recalculate();
@@ -993,7 +995,6 @@ SyncApi.prototype._processSyncResponse = async function(
// Handle joins // Handle joins
await Promise.mapSeries(joinRooms, async function(joinObj) { await Promise.mapSeries(joinRooms, async function(joinObj) {
const room = joinObj.room; const room = joinObj.room;
room.setSyncedMembership("join");
const stateEvents = self._mapSyncEventsFormat(joinObj.state, room); const stateEvents = self._mapSyncEventsFormat(joinObj.state, room);
const timelineEvents = self._mapSyncEventsFormat(joinObj.timeline, room); const timelineEvents = self._mapSyncEventsFormat(joinObj.timeline, room);
const ephemeralEvents = self._mapSyncEventsFormat(joinObj.ephemeral); const ephemeralEvents = self._mapSyncEventsFormat(joinObj.ephemeral);
@@ -1009,6 +1010,8 @@ SyncApi.prototype._processSyncResponse = async function(
); );
} }
room.updateMyMembership("join");
joinObj.timeline = joinObj.timeline || {}; joinObj.timeline = joinObj.timeline || {};
if (joinObj.isBrandNewRoom) { if (joinObj.isBrandNewRoom) {
@@ -1116,8 +1119,6 @@ SyncApi.prototype._processSyncResponse = async function(
// Handle leaves (e.g. kicked rooms) // Handle leaves (e.g. kicked rooms)
leaveRooms.forEach(function(leaveObj) { leaveRooms.forEach(function(leaveObj) {
const room = leaveObj.room; const room = leaveObj.room;
room.setSyncedMembership("leave");
const stateEvents = const stateEvents =
self._mapSyncEventsFormat(leaveObj.state, room); self._mapSyncEventsFormat(leaveObj.state, room);
const timelineEvents = const timelineEvents =
@@ -1125,6 +1126,8 @@ SyncApi.prototype._processSyncResponse = async function(
const accountDataEvents = const accountDataEvents =
self._mapSyncEventsFormat(leaveObj.account_data); self._mapSyncEventsFormat(leaveObj.account_data);
room.updateMyMembership("leave");
self._processRoomEvents(room, stateEvents, timelineEvents); self._processRoomEvents(room, stateEvents, timelineEvents);
room.addAccountData(accountDataEvents); room.addAccountData(accountDataEvents);
@@ -1145,8 +1148,6 @@ SyncApi.prototype._processSyncResponse = async function(
accountDataEvents.forEach(function(e) { accountDataEvents.forEach(function(e) {
client.emit("event", e); client.emit("event", e);
}); });
room.onLeft();
}); });
// update the notification timeline, if appropriate. // update the notification timeline, if appropriate.