You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-12-23 22:42:10 +03:00
Merge branch 'develop' into rav/context
Conflicts: lib/models/room.js
This commit is contained in:
152
lib/client.js
152
lib/client.js
@@ -287,7 +287,7 @@ MatrixClient.prototype.retryImmediately = function() {
|
||||
// stop waiting
|
||||
clearTimeout(this._syncingRetry.timeoutId);
|
||||
// invoke immediately
|
||||
this._syncingRetry.fn();
|
||||
this._syncingRetry.fn(2); // FIXME: It shouldn't need to know about attempts :/
|
||||
this._syncingRetry = null;
|
||||
return true;
|
||||
};
|
||||
@@ -616,6 +616,14 @@ MatrixClient.prototype.getUser = function(userId) {
|
||||
return this.store.getUser(userId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve all known users.
|
||||
* @return {User[]} A list of users, or an empty list if there is no data store.
|
||||
*/
|
||||
MatrixClient.prototype.getUsers = function() {
|
||||
return this.store.getUsers();
|
||||
};
|
||||
|
||||
// Room operations
|
||||
// ===============
|
||||
|
||||
@@ -1466,6 +1474,23 @@ MatrixClient.prototype.createAlias = function(alias, roomId, callback) {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete an alias to room ID mapping. This alias must be on your local server
|
||||
* and you must have sufficient access to do this operation.
|
||||
* @param {string} alias The room alias to delete.
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @return {module:client.Promise} Resolves: TODO.
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixClient.prototype.deleteAlias = function(alias, callback) {
|
||||
var path = utils.encodeUri("/directory/room/$alias", {
|
||||
$alias: alias
|
||||
});
|
||||
return this._http.authedRequest(
|
||||
callback, "DELETE", path, undefined, undefined
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get room info for the given alias.
|
||||
* @param {string} alias The room alias to resolve.
|
||||
@@ -2384,7 +2409,7 @@ MatrixClient.prototype.loginWithToken = function(token, callback) {
|
||||
* @return {module:client.Promise} Resolves: TODO
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixClient.prototype.pushRules = function(callback) {
|
||||
MatrixClient.prototype.getPushRules = function(callback) {
|
||||
return this._http.authedRequest(callback, "GET", "/pushrules/");
|
||||
};
|
||||
|
||||
@@ -2425,6 +2450,129 @@ MatrixClient.prototype.deletePushRule = function(scope, kind, ruleId, callback)
|
||||
return this._http.authedRequest(callback, "DELETE", path);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable or disable a push notification rule.
|
||||
* @param {string} scope
|
||||
* @param {string} kind
|
||||
* @param {string} ruleId
|
||||
* @param {boolean} enabled
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @return {module:client.Promise} Resolves: result object
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixClient.prototype.setPushRuleEnabled = function(scope, kind,
|
||||
ruleId, enabled, callback) {
|
||||
var path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId/enabled", {
|
||||
$kind: kind,
|
||||
$ruleId: ruleId
|
||||
});
|
||||
return this._http.authedRequest(
|
||||
callback, "PUT", path, undefined, {"enabled": enabled}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the room-kind push rule associated with a room.
|
||||
* @param {string} scope "global" or device-specific.
|
||||
* @param {string} roomId the id of the room.
|
||||
* @return {object} the rule or undefined.
|
||||
*/
|
||||
MatrixClient.prototype.getRoomPushRule = function(scope, roomId) {
|
||||
// There can be only room-kind push rule per room
|
||||
// and its id is the room id.
|
||||
if (this.pushRules) {
|
||||
for (var i = 0; i < this.pushRules[scope].room.length; i++) {
|
||||
var rule = this.pushRules[scope].room[i];
|
||||
if (rule.rule_id === roomId) {
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error(
|
||||
"SyncApi.sync() must be done before accessing to push rules."
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a room-kind muting push rule in a room.
|
||||
* The operation also updates MatrixClient.pushRules at the end.
|
||||
* @param {string} scope "global" or device-specific.
|
||||
* @param {string} roomId the id of the room.
|
||||
* @param {string} mute the mute state.
|
||||
* @return {module:client.Promise} Resolves: result object
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixClient.prototype.setRoomMutePushRule = function(scope, roomId, mute) {
|
||||
var self = this;
|
||||
var deferred, hasDontNotifyRule;
|
||||
|
||||
// Get the existing room-kind push rule if any
|
||||
var roomPushRule = this.getRoomPushRule(scope, roomId);
|
||||
if (roomPushRule) {
|
||||
if (0 <= roomPushRule.actions.indexOf("dont_notify")) {
|
||||
hasDontNotifyRule = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mute) {
|
||||
// Remove the rule only if it is a muting rule
|
||||
if (hasDontNotifyRule) {
|
||||
deferred = this.deletePushRule(scope, "room", roomPushRule.rule_id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!roomPushRule) {
|
||||
deferred = this.addPushRule(scope, "room", roomId, {
|
||||
actions: ["dont_notify"]
|
||||
});
|
||||
}
|
||||
else if (!hasDontNotifyRule) {
|
||||
// Remove the existing one before setting the mute push rule
|
||||
// This is a workaround to SYN-590 (Push rule update fails)
|
||||
deferred = q.defer();
|
||||
this.deletePushRule(scope, "room", roomPushRule.rule_id)
|
||||
.done(function() {
|
||||
self.addPushRule(scope, "room", roomId, {
|
||||
actions: ["dont_notify"]
|
||||
}).done(function() {
|
||||
deferred.resolve();
|
||||
}, function(err) {
|
||||
deferred.reject(err);
|
||||
});
|
||||
}, function(err) {
|
||||
deferred.reject(err);
|
||||
});
|
||||
|
||||
deferred = deferred.promise;
|
||||
}
|
||||
}
|
||||
|
||||
if (deferred) {
|
||||
// Update this.pushRules when the operation completes
|
||||
var ruleRefreshDeferred = q.defer();
|
||||
deferred.done(function() {
|
||||
self.getPushRules().done(function(result) {
|
||||
self.pushRules = result;
|
||||
ruleRefreshDeferred.resolve();
|
||||
}, function(err) {
|
||||
ruleRefreshDeferred.reject(err);
|
||||
});
|
||||
}, function(err) {
|
||||
// Update it even if the previous operation fails. This can help the
|
||||
// app to recover when push settings has been modifed from another client
|
||||
self.getPushRules().done(function(result) {
|
||||
self.pushRules = result;
|
||||
ruleRefreshDeferred.reject(err);
|
||||
}, function(err2) {
|
||||
ruleRefreshDeferred.reject(err);
|
||||
});
|
||||
});
|
||||
return ruleRefreshDeferred.promise;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform a server-side search for messages containing the given text.
|
||||
* @param {Object} opts Options for the search.
|
||||
|
||||
@@ -43,6 +43,11 @@ module.exports.PREFIX_V2_ALPHA = "/_matrix/client/v2_alpha";
|
||||
*/
|
||||
module.exports.PREFIX_IDENTITY_V1 = "/_matrix/identity/api/v1";
|
||||
|
||||
/**
|
||||
* A constant representing the URI path for release 0 of the Client-Server HTTP API.
|
||||
*/
|
||||
module.exports.PREFIX_R0 = "/_matrix/client/r0";
|
||||
|
||||
/**
|
||||
* Construct a MatrixHttpApi.
|
||||
* @constructor
|
||||
@@ -416,8 +421,10 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
}, localTimeoutMs);
|
||||
}
|
||||
|
||||
var reqPromise = defer.promise;
|
||||
|
||||
try {
|
||||
this.opts.request(
|
||||
var req = this.opts.request(
|
||||
{
|
||||
uri: uri,
|
||||
method: method,
|
||||
@@ -425,6 +432,7 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
qs: queryParams,
|
||||
body: data,
|
||||
json: true,
|
||||
timeout: localTimeoutMs,
|
||||
_matrix_opts: this.opts
|
||||
},
|
||||
function(err, response, body) {
|
||||
@@ -438,6 +446,11 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
handlerFn(err, response, body);
|
||||
}
|
||||
);
|
||||
if (req && req.abort) {
|
||||
// FIXME: This is EVIL, but I can't think of a better way to expose
|
||||
// abort() operations on underlying HTTP requests :(
|
||||
reqPromise.abort = req.abort.bind(req);
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
defer.reject(ex);
|
||||
@@ -445,7 +458,7 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
callback(ex);
|
||||
}
|
||||
}
|
||||
return defer.promise;
|
||||
return reqPromise;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -139,6 +139,8 @@ function Room(roomId, opts) {
|
||||
// }]
|
||||
};
|
||||
|
||||
this._notificationCounts = {};
|
||||
|
||||
// just a list - *not* ordered.
|
||||
this._timelines = [];
|
||||
this._eventIdToTimeline = {};
|
||||
@@ -200,6 +202,26 @@ Room.prototype.getTimelineForEvent = function(eventId) {
|
||||
return this._eventIdToTimeline[eventId];
|
||||
};
|
||||
|
||||
/*
|
||||
* Get one of the notification counts for this room
|
||||
* @param {String} type The type of notification count to get. default: 'total'
|
||||
* @return {Number} The notification count, or undefined if there is no count
|
||||
* for this type.
|
||||
*/
|
||||
Room.prototype.getUnreadNotificationCount = function(type) {
|
||||
type = type || 'total';
|
||||
return this._notificationCounts[type];
|
||||
};
|
||||
|
||||
/**
|
||||
* Set one of the notification counts for this room
|
||||
* @param {String} type The type of notification count to set.
|
||||
* @param {Number} count The new count
|
||||
*/
|
||||
Room.prototype.setUnreadNotificationCount = function(type, count) {
|
||||
this._notificationCounts[type] = count;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the avatar URL for a room if one was set.
|
||||
* @param {String} baseUrl The homeserver base URL. See
|
||||
@@ -286,9 +308,11 @@ Room.prototype.getAvatarUrl = function(baseUrl, width, height, resizeMethod,
|
||||
* @return {boolean} True if this user_id has the given membership state.
|
||||
*/
|
||||
Room.prototype.hasMembershipState = function(userId, membership) {
|
||||
return utils.filter(this.currentState.getMembers(), function(m) {
|
||||
return m.membership === membership && m.userId === userId;
|
||||
}).length > 0;
|
||||
var member = this.getMember(userId);
|
||||
if (!member) {
|
||||
return false;
|
||||
}
|
||||
return member.membership === membership;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -941,7 +965,7 @@ function calculateRoomName(room, userId, ignoreRoomNameEvent) {
|
||||
return (m.membership !== "leave");
|
||||
});
|
||||
if (memberList.length === 1) {
|
||||
// we exist, but no one else... self-chat or invite.
|
||||
// self-chat, peeked room with 1 participant, or invite.
|
||||
if (memberList[0].membership === "invite") {
|
||||
if (memberList[0].events.member) {
|
||||
// extract who invited us to the room
|
||||
@@ -952,12 +976,17 @@ function calculateRoomName(room, userId, ignoreRoomNameEvent) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
return userId;
|
||||
if (memberList[0].userId === userId) {
|
||||
return "Empty room";
|
||||
}
|
||||
else {
|
||||
return memberList[0].name;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// there really isn't anyone in this room...
|
||||
return "?";
|
||||
return "Empty room";
|
||||
}
|
||||
}
|
||||
else if (members.length === 1) {
|
||||
|
||||
@@ -89,6 +89,32 @@ User.prototype.setPresenceEvent = function(event) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Manually set this user's display name. No event is emitted in response to this
|
||||
* as there is no underlying MatrixEvent to emit with.
|
||||
* @param {string} name The new display name.
|
||||
*/
|
||||
User.prototype.setDisplayName = function(name) {
|
||||
var oldName = this.displayName;
|
||||
this.displayName = name;
|
||||
if (name !== oldName) {
|
||||
this._updateModifiedTime();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Manually set this user's avatar URL. No event is emitted in response to this
|
||||
* as there is no underlying MatrixEvent to emit with.
|
||||
* @param {string} url The new avatar URL.
|
||||
*/
|
||||
User.prototype.setAvatarUrl = function(url) {
|
||||
var oldUrl = this.avatarUrl;
|
||||
this.avatarUrl = url;
|
||||
if (url !== oldUrl) {
|
||||
this._updateModifiedTime();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the last modified time to the current time.
|
||||
*/
|
||||
|
||||
@@ -19,6 +19,7 @@ limitations under the License.
|
||||
* @module store/memory
|
||||
*/
|
||||
var utils = require("../utils");
|
||||
var User = require("../models/user");
|
||||
|
||||
/**
|
||||
* Construct a new in-memory data store for the Matrix Client.
|
||||
@@ -55,6 +56,7 @@ module.exports.MatrixInMemoryStore.prototype = {
|
||||
return this.syncToken;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Set the token to stream from.
|
||||
* @param {string} token The token to stream from.
|
||||
@@ -69,6 +71,44 @@ module.exports.MatrixInMemoryStore.prototype = {
|
||||
*/
|
||||
storeRoom: function(room) {
|
||||
this.rooms[room.roomId] = room;
|
||||
// add listeners for room member changes so we can keep the room member
|
||||
// map up-to-date.
|
||||
room.currentState.on("RoomState.members", this._onRoomMember.bind(this));
|
||||
// add existing members
|
||||
var self = this;
|
||||
room.currentState.getMembers().forEach(function(m) {
|
||||
self._onRoomMember(null, room.currentState, m);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a room member in a room being tracked by this store has been
|
||||
* updated.
|
||||
* @param {MatrixEvent} event
|
||||
* @param {RoomState} state
|
||||
* @param {RoomMember} member
|
||||
*/
|
||||
_onRoomMember: function(event, state, member) {
|
||||
if (member.membership === "invite") {
|
||||
// We do NOT add invited members because people love to typo user IDs
|
||||
// which would then show up in these lists (!)
|
||||
return;
|
||||
}
|
||||
// We don't clobber any existing entry in the user map which has presence
|
||||
// so user entries with presence info are preferred. This does mean we will
|
||||
// clobber room member entries constantly, which is desirable to keep things
|
||||
// like display names and avatar URLs up-to-date.
|
||||
if (this.users[member.userId] && this.users[member.userId].events.presence) {
|
||||
return;
|
||||
}
|
||||
|
||||
var user = new User(member.userId);
|
||||
user.setDisplayName(member.name);
|
||||
var rawUrl = (
|
||||
member.events.member ? member.events.member.getContent().avatar_url : null
|
||||
);
|
||||
user.setAvatarUrl(rawUrl);
|
||||
this.users[user.userId] = user;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -93,6 +133,9 @@ module.exports.MatrixInMemoryStore.prototype = {
|
||||
* @param {string} roomId
|
||||
*/
|
||||
removeRoom: function(roomId) {
|
||||
if (this.rooms[roomId]) {
|
||||
this.rooms[roomId].removeListener("RoomState.members", this._onRoomMember);
|
||||
}
|
||||
delete this.rooms[roomId];
|
||||
},
|
||||
|
||||
@@ -123,6 +166,14 @@ module.exports.MatrixInMemoryStore.prototype = {
|
||||
return this.users[userId] || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve all known users.
|
||||
* @return {User[]} A list of users, which may be empty.
|
||||
*/
|
||||
getUsers: function() {
|
||||
return utils.values(this.users);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve scrollback for this room.
|
||||
* @param {Room} room The matrix room
|
||||
|
||||
@@ -101,6 +101,14 @@ StubStore.prototype = {
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* No-op.
|
||||
* @return {User[]}
|
||||
*/
|
||||
getUsers: function() {
|
||||
return [];
|
||||
},
|
||||
|
||||
/**
|
||||
* No-op.
|
||||
* @param {Room} room
|
||||
|
||||
92
lib/sync.js
92
lib/sync.js
@@ -66,7 +66,8 @@ function SyncApi(client, opts) {
|
||||
opts.pendingEventOrdering = opts.pendingEventOrdering || "chronological";
|
||||
this.opts = opts;
|
||||
this._peekRoomId = null;
|
||||
this._lowClientTimeouts = false;
|
||||
this._syncConnectionLost = false;
|
||||
this._currentSyncRequest = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -296,7 +297,7 @@ SyncApi.prototype.sync = function() {
|
||||
attempt = attempt || 0;
|
||||
attempt += 1;
|
||||
|
||||
client.pushRules().done(function(result) {
|
||||
client.getPushRules().done(function(result) {
|
||||
debuglog("Got push rules");
|
||||
client.pushRules = result;
|
||||
getFilter(); // Now get the filter
|
||||
@@ -370,25 +371,13 @@ SyncApi.prototype._sync = function(syncOptions, attempt) {
|
||||
// normal timeout= plus buffer time
|
||||
var clientSideTimeoutMs = this.opts.pollTimeout + BUFFER_PERIOD_MS;
|
||||
|
||||
if (self._lowClientTimeouts) {
|
||||
debuglog("_lowClientTimeouts flag set.");
|
||||
clientSideTimeoutMs = this.opts.pollTimeout;
|
||||
}
|
||||
|
||||
var dateStamp = new Date();
|
||||
dateStamp = dateStamp.getHours() + ":" + dateStamp.getMinutes() + ":" +
|
||||
dateStamp.getSeconds() + "." + dateStamp.getMilliseconds();
|
||||
debuglog("DEBUG[%s]: NEW _sync attempt=%s qp_timeout=%s cli_timeout=%s",
|
||||
dateStamp, attempt, qps.timeout, clientSideTimeoutMs);
|
||||
|
||||
|
||||
|
||||
client._http.authedRequestWithPrefix(
|
||||
this._currentSyncRequest = client._http.authedRequestWithPrefix(
|
||||
undefined, "GET", "/sync", qps, undefined, httpApi.PREFIX_V2_ALPHA,
|
||||
clientSideTimeoutMs
|
||||
).done(function(data) {
|
||||
debuglog("DEBUG[%s]: _sync RECV", dateStamp);
|
||||
self._lowClientTimeouts = false;
|
||||
);
|
||||
|
||||
this._currentSyncRequest.done(function(data) {
|
||||
self._syncConnectionLost = false;
|
||||
// data looks like:
|
||||
// {
|
||||
// next_batch: $token,
|
||||
@@ -405,7 +394,10 @@ SyncApi.prototype._sync = function(syncOptions, attempt) {
|
||||
// timeline: { events: [], prev_batch: $token, limited: true },
|
||||
// ephemeral: { events: [] },
|
||||
// account_data: { events: [] },
|
||||
// unread_notification_count: 0
|
||||
// unread_notifications: {
|
||||
// highlight_count: 0,
|
||||
// notification_count: 0,
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// leave: {
|
||||
@@ -487,7 +479,14 @@ SyncApi.prototype._sync = function(syncOptions, attempt) {
|
||||
var accountDataEvents = self._mapSyncEventsFormat(joinObj.account_data);
|
||||
|
||||
// we do this first so it's correct when any of the events fire
|
||||
room.unread_notification_count = joinObj.unread_notification_count;
|
||||
if (joinObj.unread_notifications) {
|
||||
room.setUnreadNotificationCount(
|
||||
'total', joinObj.unread_notifications.notification_count
|
||||
);
|
||||
room.setUnreadNotificationCount(
|
||||
'highlight', joinObj.unread_notifications.highlight_count
|
||||
);
|
||||
}
|
||||
|
||||
joinObj.timeline = joinObj.timeline || {};
|
||||
|
||||
@@ -554,22 +553,47 @@ SyncApi.prototype._sync = function(syncOptions, attempt) {
|
||||
|
||||
self._sync(syncOptions);
|
||||
}, function(err) {
|
||||
debuglog("DEBUG[%s]: RECV FAIL %s", dateStamp, require("util").inspect(err));
|
||||
if (!self._syncConnectionLost) {
|
||||
debuglog("Starting keep-alive");
|
||||
self._syncConnectionLost = true;
|
||||
retryPromise(self._pokeKeepAlive.bind(self), 2000).done(function() {
|
||||
debuglog("Keep-alive successful.");
|
||||
// blow away the current /sync request if the connection is still
|
||||
// dead. It may be black-holed.
|
||||
if (!self._syncConnectionLost) {
|
||||
return;
|
||||
}
|
||||
if (self._currentSyncRequest.abort) {
|
||||
// kill the current sync request
|
||||
debuglog("Aborting current /sync.");
|
||||
self._currentSyncRequest.abort();
|
||||
}
|
||||
// immediately retry if we were waiting
|
||||
debuglog(
|
||||
"Interrupted /sync backoff: %s", self.client.retryImmediately()
|
||||
);
|
||||
});
|
||||
}
|
||||
console.error("/sync error (%s attempts): %s", attempt, err);
|
||||
console.error(err);
|
||||
attempt += 1;
|
||||
startSyncingRetryTimer(client, attempt, function(newAttempt, extendedWait) {
|
||||
debuglog("DEBUG[%s]: Init new _sync new_attempt=%s extended_wait=%s",
|
||||
dateStamp, newAttempt, extendedWait);
|
||||
if (extendedWait) {
|
||||
self._lowClientTimeouts = true;
|
||||
}
|
||||
startSyncingRetryTimer(client, attempt, function(newAttempt) {
|
||||
self._sync(syncOptions, newAttempt);
|
||||
});
|
||||
updateSyncState(client, "ERROR", { error: err });
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Promise}
|
||||
*/
|
||||
SyncApi.prototype._pokeKeepAlive = function() {
|
||||
return this.client._http.requestWithPrefix(
|
||||
undefined, "GET", "/_matrix/client/versions", undefined,
|
||||
undefined, "", 5 * 1000
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} filterName
|
||||
* @param {Filter} filter
|
||||
@@ -749,6 +773,14 @@ function retryTimeMsForAttempt(attempt) {
|
||||
return Math.pow(2, Math.min(attempt, 5)) * 1000;
|
||||
}
|
||||
|
||||
function retryPromise(promiseFn, delay) {
|
||||
delay = delay || 0;
|
||||
return promiseFn().catch(function(reason) { // if it fails
|
||||
// retry after waiting the delay time
|
||||
return q.delay(delay).then(retryPromise.bind(null, promiseFn, delay));
|
||||
});
|
||||
}
|
||||
|
||||
function startSyncingRetryTimer(client, attempt, fn) {
|
||||
client._syncingRetry = {};
|
||||
client._syncingRetry.fn = fn;
|
||||
@@ -758,19 +790,17 @@ function startSyncingRetryTimer(client, attempt, fn) {
|
||||
client._syncingRetry.timeoutId = setTimeout(function() {
|
||||
var timeAfterWaitingMs = Date.now();
|
||||
var timeDeltaMs = timeAfterWaitingMs - timeBeforeWaitingMs;
|
||||
var extendedWait = false;
|
||||
if (timeDeltaMs > (2 * timeToWaitMs)) {
|
||||
// we've waited more than twice what we were supposed to. Reset the
|
||||
// attempt number back to 1. This can happen when the comp goes to
|
||||
// sleep whilst the timer is running.
|
||||
newAttempt = 1;
|
||||
extendedWait = true;
|
||||
console.warn(
|
||||
"Sync retry timer: Tried to wait %s ms but actually waited %s ms",
|
||||
timeToWaitMs, timeDeltaMs
|
||||
);
|
||||
}
|
||||
fn(newAttempt, extendedWait);
|
||||
fn(newAttempt);
|
||||
}, timeToWaitMs);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ describe("MatrixClient", function() {
|
||||
var identityServerDomain = "identity.server";
|
||||
var client, store, scheduler;
|
||||
|
||||
var KEEP_ALIVE_PATH = "/_matrix/client/versions";
|
||||
|
||||
var PUSH_RULES_RESPONSE = {
|
||||
method: "GET",
|
||||
path: "/pushrules/",
|
||||
@@ -51,6 +53,9 @@ describe("MatrixClient", function() {
|
||||
];
|
||||
var pendingLookup = null;
|
||||
function httpReq(cb, method, path, qp, data, prefix) {
|
||||
if (path === KEEP_ALIVE_PATH) {
|
||||
return q();
|
||||
}
|
||||
var next = httpLookups.shift();
|
||||
var logLine = (
|
||||
"MatrixClient[UT] RECV " + method + " " + path + " " +
|
||||
@@ -137,6 +142,7 @@ describe("MatrixClient", function() {
|
||||
]);
|
||||
client._http.authedRequest.andCallFake(httpReq);
|
||||
client._http.authedRequestWithPrefix.andCallFake(httpReq);
|
||||
client._http.requestWithPrefix.andCallFake(httpReq);
|
||||
|
||||
// set reasonable working defaults
|
||||
pendingLookup = null;
|
||||
|
||||
@@ -343,50 +343,40 @@ describe("Room", function() {
|
||||
|
||||
it("should return true for a matching userId and membership",
|
||||
function() {
|
||||
room.currentState.getMembers.andCallFake(function() {
|
||||
return [
|
||||
{ userId: "@alice:bar", membership: "join" },
|
||||
{ userId: "@bob:bar", membership: "invite" }
|
||||
];
|
||||
});
|
||||
room.currentState.members = {
|
||||
"@alice:bar": { userId: "@alice:bar", membership: "join" },
|
||||
"@bob:bar": { userId: "@bob:bar", membership: "invite" }
|
||||
};
|
||||
expect(room.hasMembershipState("@bob:bar", "invite")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false if match membership but no match userId",
|
||||
function() {
|
||||
room.currentState.getMembers.andCallFake(function() {
|
||||
return [
|
||||
{ userId: "@alice:bar", membership: "join" }
|
||||
];
|
||||
});
|
||||
room.currentState.members = {
|
||||
"@alice:bar": { userId: "@alice:bar", membership: "join" }
|
||||
};
|
||||
expect(room.hasMembershipState("@bob:bar", "join")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false if match userId but no match membership",
|
||||
function() {
|
||||
room.currentState.getMembers.andCallFake(function() {
|
||||
return [
|
||||
{ userId: "@alice:bar", membership: "join" }
|
||||
];
|
||||
});
|
||||
room.currentState.members = {
|
||||
"@alice:bar": { userId: "@alice:bar", membership: "join" }
|
||||
};
|
||||
expect(room.hasMembershipState("@alice:bar", "ban")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false if no match membership or userId",
|
||||
function() {
|
||||
room.currentState.getMembers.andCallFake(function() {
|
||||
return [
|
||||
{ userId: "@alice:bar", membership: "join" }
|
||||
];
|
||||
});
|
||||
room.currentState.members = {
|
||||
"@alice:bar": { userId: "@alice:bar", membership: "join" }
|
||||
};
|
||||
expect(room.hasMembershipState("@bob:bar", "invite")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false if no members exist",
|
||||
function() {
|
||||
room.currentState.getMembers.andCallFake(function() {
|
||||
return [];
|
||||
});
|
||||
room.currentState.members = {};
|
||||
expect(room.hasMembershipState("@foo:bar", "join")).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -622,29 +612,30 @@ describe("Room", function() {
|
||||
expect(name).toEqual(roomName);
|
||||
});
|
||||
|
||||
it("should show your name for private (invite join_rules) rooms if" +
|
||||
it("should return 'Empty room' for private (invite join_rules) rooms if" +
|
||||
" a room name and alias don't exist and it is a self-chat.", function() {
|
||||
setJoinRule("invite");
|
||||
addMember(userA);
|
||||
room.recalculate(userA);
|
||||
var name = room.name;
|
||||
expect(name).toEqual(userA);
|
||||
expect(name).toEqual("Empty room");
|
||||
});
|
||||
|
||||
it("should show your name for public (public join_rules) rooms if a" +
|
||||
it("should return 'Empty room' for public (public join_rules) rooms if a" +
|
||||
" room name and alias don't exist and it is a self-chat.", function() {
|
||||
setJoinRule("public");
|
||||
addMember(userA);
|
||||
room.recalculate(userA);
|
||||
var name = room.name;
|
||||
expect(name).toEqual(userA);
|
||||
expect(name).toEqual("Empty room");
|
||||
});
|
||||
|
||||
it("should return '?' if there is no name, alias or members in the room.",
|
||||
it("should return 'Empty room' if there is no name, " +
|
||||
"alias or members in the room.",
|
||||
function() {
|
||||
room.recalculate(userA);
|
||||
var name = room.name;
|
||||
expect(name).toEqual("?");
|
||||
expect(name).toEqual("Empty room");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user