Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 33a1139772 | |||
| c5b62903f3 | |||
| 387fd16b40 | |||
| c91b67d370 | |||
| 96e21700bd | |||
| 7e8f25bce3 | |||
| affdfccd60 | |||
| 402f8c27a9 | |||
| 6b8dd42547 | |||
| e3586411e0 | |||
| a0639a32c7 | |||
| 759c6e77a7 | |||
| 04ad3d7c3c | |||
| 49badd9a2f | |||
| 8b00083bca | |||
| 3d98e324b5 | |||
| 768c66313f | |||
| 3561fd1c05 | |||
| a6c055b6d1 | |||
| 0d24c18fed | |||
| dd8b2a79fb | |||
| f0095611bc | |||
| a3567f0918 | |||
| f0d3d0d74e | |||
| 3e32bc0d5d | |||
| 632e4aa120 | |||
| 2391ce198d | |||
| e5e2bbd482 | |||
| 0b6632123b | |||
| b1801fc953 | |||
| ca1a1c4f28 | |||
| 3363cc4f1d | |||
| f84684982f | |||
| 3bed5969bf | |||
| ebc162e3d8 | |||
| f30136dba3 | |||
| 9b1926f902 | |||
| d29524ba3f | |||
| 7258fe4e5c | |||
| f8ea1702f8 | |||
| b8744a79ae | |||
| 9b54c9b807 | |||
| 1bb608cdb6 | |||
| 1239485b30 | |||
| 0d23d047fc | |||
| d837ae64ac | |||
| d72a70396a | |||
| 8f98504183 | |||
| 1b77ee0ef4 |
+58
-12
@@ -1,3 +1,61 @@
|
||||
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)
|
||||
|
||||
BREAKING CHANGES
|
||||
----------------
|
||||
* v0.11.0-rc.1 introduced some breaking changes - see the respective release notes.
|
||||
|
||||
No changes since rc.1
|
||||
|
||||
Changes in [0.11.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.11.0-rc.1) (2018-09-07)
|
||||
============================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.10.9...v0.11.0-rc.1)
|
||||
|
||||
* Support for lazy loading members. This should improve performance for
|
||||
users who joined big rooms a lot. Pass to `lazyLoadMembers = true` option when calling `startClient`.
|
||||
|
||||
BREAKING CHANGES
|
||||
----------------
|
||||
|
||||
* `MatrixClient::startClient` now returns a Promise. No method should be called on the client before that promise resolves. Before this method didn't return anything.
|
||||
* A new `CATCHUP` sync state, emitted by `MatrixClient#"sync"` and returned by `MatrixClient::getSyncState()`, when doing initial sync after the `ERROR` state. See `MatrixClient` documentation for details.
|
||||
* `RoomState::maySendEvent('m.room.message', userId)` & `RoomState::maySendMessage(userId)` do not check the membership of the user anymore, only the power level. To check if the syncing user is allowed to write in a room, use `Room::maySendMessage()` as `RoomState` is not always aware of the syncing user's membership anymore, in case lazy loading of members is enabled.
|
||||
|
||||
All Changes
|
||||
-----------
|
||||
|
||||
* Only emit CATCHUP if recovering from conn error
|
||||
[\#727](https://github.com/matrix-org/matrix-js-sdk/pull/727)
|
||||
* Fix docstring for sync data.error
|
||||
[\#725](https://github.com/matrix-org/matrix-js-sdk/pull/725)
|
||||
* Re-apply "Don't rely on members to query if syncing user can post to room"
|
||||
[\#723](https://github.com/matrix-org/matrix-js-sdk/pull/723)
|
||||
* Revert "Don't rely on members to query if syncing user can post to room"
|
||||
[\#721](https://github.com/matrix-org/matrix-js-sdk/pull/721)
|
||||
* Don't rely on members to query if syncing user can post to room
|
||||
[\#717](https://github.com/matrix-org/matrix-js-sdk/pull/717)
|
||||
* Fixes for room.guessDMUserId
|
||||
[\#719](https://github.com/matrix-org/matrix-js-sdk/pull/719)
|
||||
* Fix filepanel also filtering main timeline with LL turned on.
|
||||
[\#716](https://github.com/matrix-org/matrix-js-sdk/pull/716)
|
||||
* Remove lazy loaded members when leaving room
|
||||
[\#711](https://github.com/matrix-org/matrix-js-sdk/pull/711)
|
||||
* Fix: show spinner again while recovering from connection error
|
||||
[\#702](https://github.com/matrix-org/matrix-js-sdk/pull/702)
|
||||
* Add method to query LL state in client
|
||||
[\#714](https://github.com/matrix-org/matrix-js-sdk/pull/714)
|
||||
* Fix: also load invited members when lazy loading members
|
||||
[\#707](https://github.com/matrix-org/matrix-js-sdk/pull/707)
|
||||
* Pass through function to discard megolm session
|
||||
[\#704](https://github.com/matrix-org/matrix-js-sdk/pull/704)
|
||||
|
||||
Changes in [0.10.9](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.10.9) (2018-09-03)
|
||||
==================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.10.9-rc.2...v0.10.9)
|
||||
|
||||
* No changes since rc.2
|
||||
|
||||
Changes in [0.10.9-rc.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.10.9-rc.2) (2018-08-31)
|
||||
============================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.10.9-rc.1...v0.10.9-rc.2)
|
||||
@@ -30,18 +88,6 @@ Changes in [0.10.9-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/ta
|
||||
* Lazy loading of room members
|
||||
[\#691](https://github.com/matrix-org/matrix-js-sdk/pull/691)
|
||||
|
||||
BREAKING CHANGE
|
||||
---------------
|
||||
|
||||
* `MatrixClient::startClient` now returns a Promise. No method should be called on the client before that promise resolves. Before this method didn't return anything.
|
||||
|
||||
Changes in [0.11.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.11.0) (TDB)
|
||||
==================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.10.6...v0.11.0)
|
||||
|
||||
* Support for lazy loading members. This should improve performance for
|
||||
users who joined big rooms a lot. Pass to `lazyLoadMembers = true` option when calling `startClient`.
|
||||
|
||||
Changes in [0.10.8](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.10.8) (2018-08-20)
|
||||
==================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.10.8-rc.1...v0.10.8)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "matrix-js-sdk",
|
||||
"version": "0.10.9-rc.2",
|
||||
"version": "0.11.0",
|
||||
"description": "Matrix Client-Server SDK for Javascript",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -380,7 +380,7 @@ describe("MatrixClient", function() {
|
||||
client.startClient();
|
||||
});
|
||||
|
||||
it("should transition ERROR -> PREPARED after /sync if prev failed",
|
||||
it("should transition ERROR -> CATCHUP after /sync if prev failed",
|
||||
function(done) {
|
||||
const expectedStates = [];
|
||||
acceptKeepalives = false;
|
||||
@@ -403,7 +403,7 @@ describe("MatrixClient", function() {
|
||||
|
||||
expectedStates.push(["RECONNECTING", null]);
|
||||
expectedStates.push(["ERROR", "RECONNECTING"]);
|
||||
expectedStates.push(["PREPARED", "ERROR"]);
|
||||
expectedStates.push(["CATCHUP", "ERROR"]);
|
||||
client.on("sync", syncChecker(expectedStates, done));
|
||||
client.startClient();
|
||||
});
|
||||
|
||||
@@ -447,13 +447,6 @@ describe("RoomState", function() {
|
||||
});
|
||||
|
||||
describe("maySendStateEvent", function() {
|
||||
it("should say non-joined members may not send state",
|
||||
function() {
|
||||
expect(state.maySendStateEvent(
|
||||
'm.room.name', "@nobody:nowhere",
|
||||
)).toEqual(false);
|
||||
});
|
||||
|
||||
it("should say any member may send state with no power level event",
|
||||
function() {
|
||||
expect(state.maySendStateEvent('m.room.name', userA)).toEqual(true);
|
||||
@@ -640,14 +633,6 @@ describe("RoomState", function() {
|
||||
});
|
||||
|
||||
describe("maySendEvent", function() {
|
||||
it("should say non-joined members may not send events",
|
||||
function() {
|
||||
expect(state.maySendEvent(
|
||||
'm.room.message', "@nobody:nowhere",
|
||||
)).toEqual(false);
|
||||
expect(state.maySendMessage("@nobody:nowhere")).toEqual(false);
|
||||
});
|
||||
|
||||
it("should say any member may send events with no power level event",
|
||||
function() {
|
||||
expect(state.maySendEvent('m.room.message', userA)).toEqual(true);
|
||||
|
||||
+37
-1
@@ -1400,7 +1400,7 @@ describe("Room", function() {
|
||||
|
||||
describe("getMyMembership", function() {
|
||||
it("should return synced membership if membership isn't available yet",
|
||||
async function() {
|
||||
function() {
|
||||
const room = new Room(roomId, null, userA);
|
||||
room.setSyncedMembership("invite");
|
||||
expect(room.getMyMembership()).toEqual("invite");
|
||||
@@ -1411,4 +1411,40 @@ describe("Room", function() {
|
||||
expect(room.getMyMembership()).toEqual("join");
|
||||
});
|
||||
});
|
||||
|
||||
describe("guessDMUserId", function() {
|
||||
it("should return first hero id",
|
||||
function() {
|
||||
const room = new Room(roomId, null, userA);
|
||||
room.setSummary({'m.heroes': [userB]});
|
||||
expect(room.guessDMUserId()).toEqual(userB);
|
||||
});
|
||||
it("should return first member that isn't self",
|
||||
function() {
|
||||
const room = new Room(roomId, null, userA);
|
||||
room.addLiveEvents([utils.mkMembership({
|
||||
user: userB, mship: "join",
|
||||
room: roomId, event: true,
|
||||
})]);
|
||||
expect(room.guessDMUserId()).toEqual(userB);
|
||||
});
|
||||
it("should return self if only member present",
|
||||
function() {
|
||||
const room = new Room(roomId, null, userA);
|
||||
expect(room.guessDMUserId()).toEqual(userA);
|
||||
});
|
||||
});
|
||||
|
||||
describe("maySendMessage", function() {
|
||||
it("should return false if synced membership not join",
|
||||
function() {
|
||||
const room = new Room(roomId, null, userA);
|
||||
room.setSyncedMembership("invite");
|
||||
expect(room.maySendMessage()).toEqual(false);
|
||||
room.setSyncedMembership("leave");
|
||||
expect(room.maySendMessage()).toEqual(false);
|
||||
room.setSyncedMembership("join");
|
||||
expect(room.maySendMessage()).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+38
-7
@@ -702,6 +702,21 @@ MatrixClient.prototype.isRoomEncrypted = function(roomId) {
|
||||
return this._roomList.isRoomEncrypted(roomId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Forces the current outbound group session to be discarded such
|
||||
* that another one will be created next time an event is sent.
|
||||
*
|
||||
* @param {string} roomId The ID of the room to discard the session for
|
||||
*
|
||||
* This should not normally be necessary.
|
||||
*/
|
||||
MatrixClient.prototype.forceDiscardSession = function(roomId) {
|
||||
if (!this._crypto) {
|
||||
throw new Error("End-to-End encryption disabled");
|
||||
}
|
||||
this._crypto.forceDiscardSession(roomId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a list containing all of the room keys
|
||||
*
|
||||
@@ -2130,7 +2145,9 @@ function(roomId, fromToken, limit, dir, timelineFilter = undefined) {
|
||||
|
||||
let filter = null;
|
||||
if (this._clientOpts.lazyLoadMembers) {
|
||||
filter = LAZY_LOADING_MESSAGES_FILTER;
|
||||
// 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);
|
||||
}
|
||||
if (timelineFilter) {
|
||||
// XXX: it's horrific that /messages' filter parameter doesn't match
|
||||
@@ -3159,6 +3176,14 @@ MatrixClient.prototype.doesServerSupportLazyLoading = async function() {
|
||||
return this._serverSupportsLazyLoading;
|
||||
};
|
||||
|
||||
/*
|
||||
* Get if lazy loading members is being used.
|
||||
* @return {boolean} Whether or not members are lazy loaded by this client
|
||||
*/
|
||||
MatrixClient.prototype.hasLazyLoadMembersEnabled = function() {
|
||||
return !!this._clientOpts.lazyLoadMembers;
|
||||
};
|
||||
|
||||
/*
|
||||
* Set a function which is called when /sync returns a 'limited' response.
|
||||
* It is called with a room ID and returns a boolean. It should return 'true' if the SDK
|
||||
@@ -3504,6 +3529,12 @@ module.exports.CRYPTO_ENABLED = CRYPTO_ENABLED;
|
||||
* a state of SYNCING. <i>This is the equivalent of "syncComplete" in the
|
||||
* previous API.</i></li>
|
||||
*
|
||||
* <li>CATCHUP: The client has detected the connection to the server might be
|
||||
* available again and will now try to do a sync again. As this sync might take
|
||||
* a long time (depending how long ago was last synced, and general server
|
||||
* performance) the client is put in this mode so the UI can reflect trying
|
||||
* to catch up with the server after losing connection.</li>
|
||||
*
|
||||
* <li>SYNCING : The client is currently polling for new events from the server.
|
||||
* This will be called <i>after</i> processing latest events from a sync.</li>
|
||||
*
|
||||
@@ -3527,11 +3558,11 @@ module.exports.CRYPTO_ENABLED = CRYPTO_ENABLED;
|
||||
* +---->STOPPED
|
||||
* |
|
||||
* +----->PREPARED -------> SYNCING <--+
|
||||
* | ^ | ^ |
|
||||
* | | | | |
|
||||
* | | V | |
|
||||
* null ------+ | +--------RECONNECTING |
|
||||
* | | V |
|
||||
* | ^ | ^ |
|
||||
* | CATCHUP ----------+ | | |
|
||||
* | ^ V | |
|
||||
* null ------+ | +------- RECONNECTING |
|
||||
* | V V |
|
||||
* +------->ERROR ---------------------+
|
||||
*
|
||||
* NB: 'null' will never be emitted by this event.
|
||||
@@ -3581,7 +3612,7 @@ module.exports.CRYPTO_ENABLED = CRYPTO_ENABLED;
|
||||
*
|
||||
* @param {?Object} data Data about this transition.
|
||||
*
|
||||
* @param {MatrixError} data.err The matrix error if <code>state=ERROR</code>.
|
||||
* @param {MatrixError} data.error The matrix error if <code>state=ERROR</code>.
|
||||
*
|
||||
* @param {String} data.oldSyncToken The 'since' token passed to /sync.
|
||||
* <code>null</code> for the first successful sync since this client was
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -498,6 +499,16 @@ MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Forces the current outbound group session to be discarded such
|
||||
* that another one will be created next time an event is sent.
|
||||
*
|
||||
* This should not normally be necessary.
|
||||
*/
|
||||
MegolmEncryption.prototype.forceDiscardSession = function() {
|
||||
this._setupPromise = this._setupPromise.then(() => null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks the devices we're about to send to and see if any are entirely
|
||||
* unknown to the user. If so, warn the user, and mark them as known to
|
||||
|
||||
@@ -621,6 +621,23 @@ Crypto.prototype.getEventSenderDeviceInfo = function(event) {
|
||||
return device;
|
||||
};
|
||||
|
||||
/**
|
||||
* Forces the current outbound group session to be discarded such
|
||||
* that another one will be created next time an event is sent.
|
||||
*
|
||||
* @param {string} roomId The ID of the room to discard the session for
|
||||
*
|
||||
* This should not normally be necessary.
|
||||
*/
|
||||
Crypto.prototype.forceDiscardSession = function(roomId) {
|
||||
const alg = this._roomEncryptors[roomId];
|
||||
if (alg === undefined) throw new Error("Room not encrypted");
|
||||
if (alg.forceDiscardSession === undefined) {
|
||||
throw new Error("Room encryption algorithm doesn't support session discarding");
|
||||
}
|
||||
alg.forceDiscardSession();
|
||||
};
|
||||
|
||||
/**
|
||||
* Configure a room to use encryption (ie, save a flag in the sessionstore).
|
||||
*
|
||||
|
||||
@@ -443,6 +443,22 @@ RoomState.prototype.markOutOfBandMembersFailed = function() {
|
||||
this._oobMemberFlags.status = OOB_STATUS_NOTSTARTED;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the loaded out-of-band members
|
||||
*/
|
||||
RoomState.prototype.clearOutOfBandMembers = function() {
|
||||
let count = 0;
|
||||
Object.keys(this.members).forEach((userId) => {
|
||||
const member = this.members[userId];
|
||||
if (member.isOutOfBand()) {
|
||||
++count;
|
||||
delete this.members[userId];
|
||||
}
|
||||
});
|
||||
console.log(`LL: RoomState removed ${count} members...`);
|
||||
this._oobMemberFlags.status = OOB_STATUS_NOTSTARTED;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the loaded out-of-band members.
|
||||
* @param {MatrixEvent[]} stateEvents array of membership state events
|
||||
@@ -651,11 +667,6 @@ RoomState.prototype.maySendStateEvent = function(stateEventType, userId) {
|
||||
* according to the room's state.
|
||||
*/
|
||||
RoomState.prototype._maySendEventOfType = function(eventType, userId, state) {
|
||||
const member = this.getMember(userId);
|
||||
if (!member || member.membership == 'leave') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const power_levels_event = this.getStateEvents('m.room.power_levels', '');
|
||||
|
||||
let power_levels;
|
||||
@@ -663,25 +674,34 @@ RoomState.prototype._maySendEventOfType = function(eventType, userId, state) {
|
||||
|
||||
let state_default = 0;
|
||||
let events_default = 0;
|
||||
let powerLevel = 0;
|
||||
if (power_levels_event) {
|
||||
power_levels = power_levels_event.getContent();
|
||||
events_levels = power_levels.events || {};
|
||||
|
||||
if (utils.isNumber(power_levels.state_default)) {
|
||||
if (Number.isFinite(power_levels.state_default)) {
|
||||
state_default = power_levels.state_default;
|
||||
} else {
|
||||
state_default = 50;
|
||||
}
|
||||
if (utils.isNumber(power_levels.events_default)) {
|
||||
|
||||
const userPowerLevel = power_levels.users && power_levels.users[userId];
|
||||
if (Number.isFinite(userPowerLevel)) {
|
||||
powerLevel = userPowerLevel;
|
||||
} else if(Number.isFinite(power_levels.users_default)) {
|
||||
powerLevel = power_levels.users_default;
|
||||
}
|
||||
|
||||
if (Number.isFinite(power_levels.events_default)) {
|
||||
events_default = power_levels.events_default;
|
||||
}
|
||||
}
|
||||
|
||||
let required_level = state ? state_default : events_default;
|
||||
if (utils.isNumber(events_levels[eventType])) {
|
||||
if (Number.isFinite(events_levels[eventType])) {
|
||||
required_level = events_levels[eventType];
|
||||
}
|
||||
return member.powerLevel >= required_level;
|
||||
return powerLevel >= required_level;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+42
-18
@@ -299,27 +299,17 @@ Room.prototype.guessDMUserId = function() {
|
||||
return inviterId;
|
||||
}
|
||||
}
|
||||
const fallbackMember = this.getAvatarFallbackMember();
|
||||
if (fallbackMember) {
|
||||
return fallbackMember.userId;
|
||||
}
|
||||
// now we're getting into sketchy territory,
|
||||
// but we're assuming this room is marked as a DM
|
||||
// so we're going to make a wild-ish guess with whom
|
||||
if (this._summaryHeroes.length) {
|
||||
// remember, we're assuming this room is a DM,
|
||||
// so returning the first member we find should be fine
|
||||
const hasHeroes = Array.isArray(this._summaryHeroes) &&
|
||||
this._summaryHeroes.length;
|
||||
if (hasHeroes) {
|
||||
return this._summaryHeroes[0];
|
||||
}
|
||||
const members = this.currentState.getMembers();
|
||||
const anyMember = members.filter((m) => m.userId !== this.myUserId);
|
||||
const anyMember = members.find((m) => m.userId !== this.myUserId);
|
||||
if (anyMember) {
|
||||
return anyMember;
|
||||
}
|
||||
const createEvent = this.currentState.getStateEvents("m.room.create", "");
|
||||
if (createEvent) {
|
||||
const sender = createEvent.getSender();
|
||||
if (sender !== this.myUserId) {
|
||||
return sender;
|
||||
}
|
||||
return anyMember.userId;
|
||||
}
|
||||
// it really seems like I'm the only user in the room
|
||||
// so I probably created a room with just me in it
|
||||
@@ -379,7 +369,6 @@ Room.prototype.setSyncedMembership = function(membership) {
|
||||
Room.prototype._loadMembersFromServer = async function() {
|
||||
const lastSyncToken = this._client.store.getSyncToken();
|
||||
const queryString = utils.encodeParams({
|
||||
membership: "join",
|
||||
not_membership: "leave",
|
||||
at: lastSyncToken,
|
||||
});
|
||||
@@ -457,6 +446,30 @@ Room.prototype.loadMembersIfNeeded = function() {
|
||||
return this._membersPromise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the lazily loaded members from storage if needed
|
||||
*/
|
||||
Room.prototype.clearLoadedMembersIfNeeded = async function() {
|
||||
if (this._opts.lazyLoadMembers && this._membersPromise) {
|
||||
await this.loadMembersIfNeeded();
|
||||
await this._client.store.clearOutOfBandMembers(this.roomId);
|
||||
this.currentState.clearOutOfBandMembers();
|
||||
this._membersPromise = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* called when sync receives this room in the leave section
|
||||
* to do cleanup after leaving a room. Possibly called multiple times.
|
||||
*/
|
||||
Room.prototype.onLeft = function() {
|
||||
this.clearLoadedMembersIfNeeded().catch((err) => {
|
||||
console.error(`error after clearing loaded members from ` +
|
||||
`room ${this.roomId} after leaving`);
|
||||
console.dir(err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset the live timeline of all timelineSets, and start new ones.
|
||||
*
|
||||
@@ -1472,6 +1485,17 @@ Room.prototype.getAccountData = function(type) {
|
||||
return this.accountData[type];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns wheter the syncing user has permission to send a message in the room
|
||||
* @return {boolean} true if the user should be permitted to send
|
||||
* message events into the room.
|
||||
*/
|
||||
Room.prototype.maySendMessage = function() {
|
||||
return this.getMyMembership() === 'join' &&
|
||||
this.currentState.maySendEvent('m.room.message', this.myUserId);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is an internal method. Calculates the name of the room from the current
|
||||
* room state.
|
||||
|
||||
@@ -71,7 +71,7 @@ function selectQuery(store, keyRange, resultMapper) {
|
||||
});
|
||||
}
|
||||
|
||||
function promiseifyTxn(txn) {
|
||||
function txnAsPromise(txn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
txn.oncomplete = function(event) {
|
||||
resolve(event);
|
||||
@@ -82,7 +82,7 @@ function promiseifyTxn(txn) {
|
||||
});
|
||||
}
|
||||
|
||||
function promiseifyRequest(req) {
|
||||
function reqAsEventPromise(req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
req.onsuccess = function(event) {
|
||||
resolve(event);
|
||||
@@ -93,6 +93,17 @@ function promiseifyRequest(req) {
|
||||
});
|
||||
}
|
||||
|
||||
function reqAsPromise(req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
req.onsuccess = () => resolve(req);
|
||||
req.onerror = (err) => reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
function reqAsCursorPromise(req) {
|
||||
return reqAsEventPromise(req).then((event) => event.target.result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the actual reading from and writing to the indexeddb
|
||||
*
|
||||
@@ -159,7 +170,7 @@ LocalIndexedDBStoreBackend.prototype = {
|
||||
console.log(
|
||||
`LocalIndexedDBStoreBackend.connect: awaiting connection...`,
|
||||
);
|
||||
return promiseifyRequest(req).then((ev) => {
|
||||
return reqAsEventPromise(req).then((ev) => {
|
||||
console.log(
|
||||
`LocalIndexedDBStoreBackend.connect: connected`,
|
||||
);
|
||||
@@ -265,7 +276,7 @@ LocalIndexedDBStoreBackend.prototype = {
|
||||
const tx = this.db.transaction(["oob_membership_events"], "readwrite");
|
||||
const store = tx.objectStore("oob_membership_events");
|
||||
const eventPuts = membershipEvents.map((e) => {
|
||||
const putPromise = promiseifyRequest(store.put(e));
|
||||
const putPromise = reqAsEventPromise(store.put(e));
|
||||
// ignoring the result makes sure we discard the IDB success event
|
||||
// ASAP, and not create a potentially big array containing them
|
||||
// unneccesarily later on by calling Promise.all.
|
||||
@@ -281,7 +292,7 @@ LocalIndexedDBStoreBackend.prototype = {
|
||||
oob_written: true,
|
||||
state_key: 0,
|
||||
};
|
||||
const markerPut = promiseifyRequest(store.put(markerObject));
|
||||
const markerPut = reqAsEventPromise(store.put(markerObject));
|
||||
const allPuts = eventPuts.concat(markerPut);
|
||||
// ignore the empty array Promise.all creates
|
||||
// as this method should just resolve
|
||||
@@ -292,6 +303,44 @@ LocalIndexedDBStoreBackend.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
clearOutOfBandMembers: async function(roomId) {
|
||||
// the approach to delete all members for a room
|
||||
// is to get the min and max state key from the index
|
||||
// for that room, and then delete between those
|
||||
// keys in the store.
|
||||
// this should be way faster than deleting every member
|
||||
// individually for a large room.
|
||||
const readTx = this.db.transaction(
|
||||
["oob_membership_events"],
|
||||
"readonly");
|
||||
const store = readTx.objectStore("oob_membership_events");
|
||||
const roomIndex = store.index("room");
|
||||
const roomRange = IDBKeyRange.only(roomId);
|
||||
|
||||
const minStateKeyProm = reqAsCursorPromise(
|
||||
roomIndex.openKeyCursor(roomRange, "next"),
|
||||
).then((cursor) => cursor && cursor.primaryKey[1]);
|
||||
const maxStateKeyProm = reqAsCursorPromise(
|
||||
roomIndex.openKeyCursor(roomRange, "prev"),
|
||||
).then((cursor) => cursor && cursor.primaryKey[1]);
|
||||
const [minStateKey, maxStateKey] = await Promise.all(
|
||||
[minStateKeyProm, maxStateKeyProm]);
|
||||
|
||||
const writeTx = this.db.transaction(
|
||||
["oob_membership_events"],
|
||||
"readwrite");
|
||||
const writeStore = writeTx.objectStore("oob_membership_events");
|
||||
const membersKeyRange = IDBKeyRange.bound(
|
||||
[roomId, minStateKey],
|
||||
[roomId, maxStateKey],
|
||||
);
|
||||
|
||||
console.log(`LL: Deleting all users + marker in storage for ` +
|
||||
`room ${roomId}, with key range:`,
|
||||
[roomId, minStateKey], [roomId, maxStateKey]);
|
||||
await reqAsPromise(writeStore.delete(membersKeyRange));
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the entire database. This should be used when logging out of a client
|
||||
* to prevent mixing data between accounts.
|
||||
@@ -389,7 +438,7 @@ LocalIndexedDBStoreBackend.prototype = {
|
||||
roomsData: roomsData,
|
||||
groupsData: groupsData,
|
||||
}); // put == UPSERT
|
||||
return promiseifyTxn(txn);
|
||||
return txnAsPromise(txn);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -406,7 +455,7 @@ LocalIndexedDBStoreBackend.prototype = {
|
||||
for (let i = 0; i < accountData.length; i++) {
|
||||
store.put(accountData[i]); // put == UPSERT
|
||||
}
|
||||
return promiseifyTxn(txn);
|
||||
return txnAsPromise(txn);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -428,7 +477,7 @@ LocalIndexedDBStoreBackend.prototype = {
|
||||
event: tuple[1],
|
||||
}); // put == UPSERT
|
||||
}
|
||||
return promiseifyTxn(txn);
|
||||
return txnAsPromise(txn);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -110,6 +110,10 @@ RemoteIndexedDBStoreBackend.prototype = {
|
||||
return this._doCmd('setOutOfBandMembers', [roomId, membershipEvents]);
|
||||
},
|
||||
|
||||
clearOutOfBandMembers: function(roomId) {
|
||||
return this._doCmd('clearOutOfBandMembers', [roomId]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load all user presence events from the database. This is not cached.
|
||||
* @return {Promise<Object[]>} A list of presence events in their raw form.
|
||||
|
||||
@@ -95,6 +95,9 @@ class IndexedDBStoreWorker {
|
||||
case 'getOutOfBandMembers':
|
||||
prom = this.backend.getOutOfBandMembers(msg.args[0]);
|
||||
break;
|
||||
case 'clearOutOfBandMembers':
|
||||
prom = this.backend.clearOutOfBandMembers(msg.args[0]);
|
||||
break;
|
||||
case 'setOutOfBandMembers':
|
||||
prom = this.backend.setOutOfBandMembers(msg.args[0], msg.args[1]);
|
||||
break;
|
||||
|
||||
@@ -242,4 +242,8 @@ IndexedDBStore.prototype.setOutOfBandMembers = function(roomId, membershipEvents
|
||||
return this.backend.setOutOfBandMembers(roomId, membershipEvents);
|
||||
};
|
||||
|
||||
IndexedDBStore.prototype.clearOutOfBandMembers = function(roomId) {
|
||||
return this.backend.clearOutOfBandMembers(roomId);
|
||||
};
|
||||
|
||||
module.exports.IndexedDBStore = IndexedDBStore;
|
||||
|
||||
@@ -272,6 +272,10 @@ StubStore.prototype = {
|
||||
setOutOfBandMembers: function() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
clearOutOfBandMembers: function() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
};
|
||||
|
||||
/** Stub Store class. */
|
||||
|
||||
+23
-6
@@ -779,7 +779,18 @@ SyncApi.prototype._onSyncError = function(err, syncOptions) {
|
||||
// erroneous. We set the state to 'reconnecting'
|
||||
// instead, so that clients can observe this state
|
||||
// if they wish.
|
||||
this._startKeepAlives().then(() => {
|
||||
this._startKeepAlives().then((connDidFail) => {
|
||||
// Only emit CATCHUP if we detected a connectivity error: if we didn't,
|
||||
// it's quite likely the sync will fail again for the same reason and we
|
||||
// want to stay in ERROR rather than keep flip-flopping between ERROR
|
||||
// and CATCHUP.
|
||||
if (connDidFail && this.getSyncState() === 'ERROR') {
|
||||
this._updateSyncState("CATCHUP", {
|
||||
oldSyncToken: null,
|
||||
nextSyncToken: null,
|
||||
catchingUp: true,
|
||||
});
|
||||
}
|
||||
this._sync(syncOptions);
|
||||
});
|
||||
|
||||
@@ -1134,6 +1145,8 @@ SyncApi.prototype._processSyncResponse = async function(
|
||||
accountDataEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
});
|
||||
|
||||
room.onLeft();
|
||||
});
|
||||
|
||||
// update the notification timeline, if appropriate.
|
||||
@@ -1206,13 +1219,16 @@ SyncApi.prototype._startKeepAlives = function(delay) {
|
||||
*
|
||||
* On failure, schedules a call back to itself. On success, resolves
|
||||
* this._connectionReturnedDefer.
|
||||
*
|
||||
* @param {bool} connDidFail True if a connectivity failure has been detected. Optional.
|
||||
*/
|
||||
SyncApi.prototype._pokeKeepAlive = function() {
|
||||
SyncApi.prototype._pokeKeepAlive = function(connDidFail) {
|
||||
if (connDidFail === undefined) connDidFail = false;
|
||||
const self = this;
|
||||
function success() {
|
||||
clearTimeout(self._keepAliveTimer);
|
||||
if (self._connectionReturnedDefer) {
|
||||
self._connectionReturnedDefer.resolve();
|
||||
self._connectionReturnedDefer.resolve(connDidFail);
|
||||
self._connectionReturnedDefer = null;
|
||||
}
|
||||
}
|
||||
@@ -1229,7 +1245,7 @@ SyncApi.prototype._pokeKeepAlive = function() {
|
||||
).done(function() {
|
||||
success();
|
||||
}, function(err) {
|
||||
if (err.httpStatus == 400) {
|
||||
if (err.httpStatus == 400 || err.httpStatus == 404) {
|
||||
// treat this as a success because the server probably just doesn't
|
||||
// support /versions: point is, we're getting a response.
|
||||
// We wait a short time though, just in case somehow the server
|
||||
@@ -1237,8 +1253,9 @@ SyncApi.prototype._pokeKeepAlive = function() {
|
||||
// responses fail, this will mean we don't hammer in a loop.
|
||||
self._keepAliveTimer = setTimeout(success, 2000);
|
||||
} else {
|
||||
connDidFail = true;
|
||||
self._keepAliveTimer = setTimeout(
|
||||
self._pokeKeepAlive.bind(self),
|
||||
self._pokeKeepAlive.bind(self, connDidFail),
|
||||
5000 + Math.floor(Math.random() * 5000),
|
||||
);
|
||||
// A keepalive has failed, so we emit the
|
||||
@@ -1246,7 +1263,7 @@ SyncApi.prototype._pokeKeepAlive = function() {
|
||||
// first failure).
|
||||
// Note we do this after setting the timer:
|
||||
// this lets the unit tests advance the mock
|
||||
// clock when the get the error.
|
||||
// clock when they get the error.
|
||||
self._updateSyncState("ERROR", { error: err });
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user