1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-26 17:03:12 +03:00

Do all prep for /sync calls

This includes managing filters in localStorage. The /sync response
is not yet parsed.
This commit is contained in:
Kegan Dougal
2015-12-09 15:25:09 +00:00
parent 06f927aa22
commit b622960b32
5 changed files with 180 additions and 67 deletions

View File

@@ -31,9 +31,6 @@ try {
// Olm not installed. // Olm not installed.
} }
// TODO:
// Internal: rate limiting
var OLM_ALGORITHM = "m.olm.v1.curve25519-aes-sha2"; var OLM_ALGORITHM = "m.olm.v1.curve25519-aes-sha2";
/** /**
@@ -147,7 +144,6 @@ function MatrixClient(opts) {
this.callList = { this.callList = {
// callId: MatrixCall // callId: MatrixCall
}; };
this._config = {}; // see startClient()
// try constructing a MatrixCall to see if we are running in an environment // try constructing a MatrixCall to see if we are running in an environment
// which has WebRTC. If we are, listen for and handle m.call.* events. // which has WebRTC. If we are, listen for and handle m.call.* events.
@@ -2197,6 +2193,7 @@ MatrixClient.prototype.startClient = function(opts) {
// client is already running. // client is already running.
return; return;
} }
this.clientRunning = true;
// backwards compat for when 'opts' was 'historyLen'. // backwards compat for when 'opts' was 'historyLen'.
if (typeof opts === "number") { if (typeof opts === "number") {
opts = { opts = {
@@ -2204,14 +2201,6 @@ MatrixClient.prototype.startClient = function(opts) {
}; };
} }
opts = opts || {};
opts.initialSyncLimit = opts.initialSyncLimit || 8;
opts.includeArchivedRooms = opts.includeArchivedRooms || false;
opts.resolveInvitesToProfiles = opts.resolveInvitesToProfiles || false;
opts.pollTimeout = opts.pollTimeout || (30 * 1000);
opts.pendingEventOrdering = opts.pendingEventOrdering || "chronological";
this._config = opts;
if (CRYPTO_ENABLED && this.sessionStore !== null) { if (CRYPTO_ENABLED && this.sessionStore !== null) {
this.uploadKeys(5); this.uploadKeys(5);
} }
@@ -2219,11 +2208,8 @@ MatrixClient.prototype.startClient = function(opts) {
// periodically poll for turn servers if we support voip // periodically poll for turn servers if we support voip
checkTurnServers(this); checkTurnServers(this);
var syncApi = new SyncApi(this); var syncApi = new SyncApi(this, opts);
syncApi.sync({ syncApi.sync();
historyLen: this._config.initialSyncLimit,
includeArchived: this._config.includeArchivedRooms
});
}; };
/** /**
@@ -2233,6 +2219,7 @@ MatrixClient.prototype.startClient = function(opts) {
MatrixClient.prototype.stopClient = function() { MatrixClient.prototype.stopClient = function() {
this.clientRunning = false; this.clientRunning = false;
// TODO: f.e. Room => self.store.storeRoom(room) ? // TODO: f.e. Room => self.store.storeRoom(room) ?
// TODO: Actually stop the SyncApi
}; };
function setupCallEventHandler(client) { function setupCallEventHandler(client) {

View File

@@ -81,7 +81,9 @@ module.exports.createClient = function(opts) {
}; };
} }
opts.request = opts.request || request; opts.request = opts.request || request;
opts.store = opts.store || new module.exports.MatrixInMemoryStore(); opts.store = opts.store || new module.exports.MatrixInMemoryStore({
localStorage: global.localStorage
});
opts.scheduler = opts.scheduler || new module.exports.MatrixScheduler(); opts.scheduler = opts.scheduler || new module.exports.MatrixScheduler();
return new module.exports.MatrixClient(opts); return new module.exports.MatrixClient(opts);
}; };

View File

@@ -8,8 +8,13 @@
/** /**
* Construct a new in-memory data store for the Matrix Client. * Construct a new in-memory data store for the Matrix Client.
* @constructor * @constructor
* @param {Object=} opts Config options
* @param {LocalStorage} opts.localStorage The local storage instance to persist some forms
* of data such as tokens. Rooms will NOT be stored. See {@link WebStorageStore} to
* persist rooms.
*/ */
module.exports.MatrixInMemoryStore = function MatrixInMemoryStore() { module.exports.MatrixInMemoryStore = function MatrixInMemoryStore(opts) {
opts = opts || {};
this.rooms = { this.rooms = {
// roomId: Room // roomId: Room
}; };
@@ -22,6 +27,7 @@ module.exports.MatrixInMemoryStore = function MatrixInMemoryStore() {
// filterId: Filter // filterId: Filter
// } // }
}; };
this.localStorage = opts.localStorage;
}; };
module.exports.MatrixInMemoryStore.prototype = { module.exports.MatrixInMemoryStore.prototype = {
@@ -139,6 +145,37 @@ module.exports.MatrixInMemoryStore.prototype = {
return null; return null;
} }
return this.filters[userId][filterId]; return this.filters[userId][filterId];
},
/**
* Retrieve a filter ID with the given name.
* @param {string} filterName The filter name.
* @return {?string} The filter ID or null.
*/
getFilterIdByName: function(filterName) {
if (!this.localStorage) {
return null;
}
try {
return this.localStorage.getItem("mxjssdk_memory_filter_" + filterName);
}
catch(e) {}
return null;
},
/**
* Set a filter name to ID mapping.
* @param {string} filterName
* @param {string} filterId
*/
setFilterIdByName: function(filterName, filterId) {
if (!this.localStorage) {
return;
}
try {
this.localStorage.setItem("mxjssdk_memory_filter_" + filterName, filterId);
}
catch (e) {}
} }
// TODO // TODO

View File

@@ -113,6 +113,15 @@ StubStore.prototype = {
*/ */
getFilter: function(userId, filterId) { getFilter: function(userId, filterId) {
return null; return null;
},
/**
* Retrieve a filter ID with the given name.
* @param {string} filterName The filter name.
* @return {?string} The filter ID or null.
*/
getFilterIdByName: function(filterName) {
return null;
} }
// TODO // TODO

View File

@@ -14,6 +14,10 @@ var User = require("./models/user");
var Room = require("./models/room"); var Room = require("./models/room");
var utils = require("./utils"); var utils = require("./utils");
var MatrixEvent = require("./models/event").MatrixEvent; var MatrixEvent = require("./models/event").MatrixEvent;
var httpApi = require("./http-api");
var Filter = require("./filter");
var FILTER_SYNC = "FILTER_SYNC";
/** /**
* <b>Internal class - unstable.</b> * <b>Internal class - unstable.</b>
@@ -21,9 +25,15 @@ var MatrixEvent = require("./models/event").MatrixEvent;
* @constructor * @constructor
* @param {MatrixClient} client The matrix client instance to use. * @param {MatrixClient} client The matrix client instance to use.
*/ */
function SyncApi(client) { function SyncApi(client, opts) {
this.client = client; this.client = client;
this.opts = {}; opts = opts || {};
opts.initialSyncLimit = opts.initialSyncLimit || 8;
opts.includeArchivedRooms = opts.includeArchivedRooms || false;
opts.resolveInvitesToProfiles = opts.resolveInvitesToProfiles || false;
opts.pollTimeout = opts.pollTimeout || (30 * 1000);
opts.pendingEventOrdering = opts.pendingEventOrdering || "chronological";
this.opts = opts;
} }
/** /**
@@ -44,65 +54,128 @@ SyncApi.prototype.syncRoom = function(room) {
/** /**
* Main entry point * Main entry point
* @param {Object} opts
* @param {Number} opts.historyLen
* @param {Boolean} opts.includeArchived
*/ */
SyncApi.prototype.sync = function(opts) { SyncApi.prototype.sync = function() {
console.log("SyncApi.sync"); console.log("SyncApi.sync");
this.opts = opts || {};
this._prepareForSync();
};
/**
* @param {Number=} attempt
*/
SyncApi.prototype._prepareForSync = function(attempt) {
var client = this.client; var client = this.client;
var self = this; var self = this;
// We need to do one-off checks before we can begin the /sync loop.
// These are:
// 1) We need to get push rules so we can check if events should bing as we get
// them from /sync.
// 2) We need to get/create a filter which we can use for /sync.
function getPushRules(attempt) {
attempt = attempt || 0;
attempt += 1;
client.pushRules().done(function(result) {
console.log("Got push rules");
client.pushRules = result;
getFilter(); // Now get the filter
}, retryHandler(attempt, getPushRules));
}
function getFilter(attempt) {
attempt = attempt || 0;
attempt += 1;
// Get or create filter
var filterId = client.store.getFilterIdByName(FILTER_SYNC);
if (filterId) {
// super, just use that.
console.log("Using existing filter ID %s", filterId);
self._sync({ filterId: filterId });
return;
}
// create a filter
var filter = new Filter(client.credentials.userId);
filter.setTimelineLimit(self.opts.initialSyncLimit);
client.createFilter(filter.getDefinition()).done(function(filter) {
client.store.setFilterIdByName(FILTER_SYNC, filter.filterId);
console.log("Created filter ", filter.filterId);
self._sync({ filterId: filter.filterId }); // Now start the /sync loop
}, retryHandler(attempt, getFilter));
}
// sets the sync state to error and waits a bit before re-invoking the function.
function retryHandler(attempt, fnToRun) {
return function(err) {
startSyncingRetryTimer(client, attempt, function() {
fnToRun(attempt);
});
updateSyncState(client, "ERROR", { error: err });
};
}
if (client.isGuest()) { if (client.isGuest()) {
// no push rules for guests // no push rules for guests
this._sync(); getFilter();
return; }
else {
getPushRules();
} }
attempt = attempt || 1;
// we do push rules before syncing so when we gets events down we know immediately
// whether they are bing-worthy.
client.pushRules().done(function(result) {
client.pushRules = result;
self._sync();
}, function(err) {
attempt += 1;
startSyncingRetryTimer(client, attempt, function() {
self._prepareForSync(attempt);
});
updateSyncState(client, "ERROR", { error: err });
});
}; };
/** /**
* Invoke me to do /sync calls
* @param {Object} syncOptions
* @param {string} syncOptions.filterId
* @param {boolean} syncOptions.hasSyncedBefore
* @param {Number=} attempt * @param {Number=} attempt
*/ */
SyncApi.prototype._sync = function(attempt) { SyncApi.prototype._sync = function(syncOptions, attempt) {
var opts = this.opts;
var client = this.client; var client = this.client;
var self = this; var self = this;
var historyLen = opts.historyLen;
var includeArchived = opts.includeArchived;
attempt = attempt || 1; attempt = attempt || 1;
var qps = { limit: historyLen }; // TODO include archived rooms flag.
if (includeArchived) {
qps.archived = true; var qps = {
} filter: syncOptions.filterId,
timeout: this.opts.pollTimeout,
since: client.store.getSyncToken() || undefined // do not send 'null'
};
if (client._guestRooms && client._isGuest) { if (client._guestRooms && client._isGuest) {
qps.room_id = JSON.stringify(client._guestRooms); qps.room_id = JSON.stringify(client._guestRooms);
} }
client._http.authedRequest(
undefined, "GET", "/initialSync", qps client._http.authedRequestWithPrefix(
undefined, "GET", "/sync", qps, undefined, httpApi.PREFIX_V2_ALPHA
).done(function(data) { ).done(function(data) {
// data looks like:
// {
// next_batch: $token,
// presence: [PresencEvents],
// rooms: {
// invite: {
// $roomid: {
// invite_state: { events: [] }
// }
// },
// join: {
// $roomid: {
// state: { events: [] },
// timeline: { events: [], prev_batch: $token, limited: true },
// ephemeral: { events: [] },
// account_data: { events: [] }
// }
// },
// leave: {
// $roomid: {
// state: { events: [] },
// timeline: { events: [], prev_batch: $token }
// }
// }
// }
// }
console.log("Got data %s", data);
/*
var i, j; var i, j;
// intercept the results and put them into our store // intercept the results and put them into our store
if (!(client.store instanceof StubStore)) { if (!(client.store instanceof StubStore)) {
@@ -180,7 +253,6 @@ SyncApi.prototype._sync = function(attempt) {
} }
if (data) { if (data) {
client.store.setSyncToken(data.end);
var events = []; var events = [];
for (i = 0; i < data.presence.length; i++) { for (i = 0; i < data.presence.length; i++) {
events.push(new MatrixEvent(data.presence[i])); events.push(new MatrixEvent(data.presence[i]));
@@ -204,16 +276,22 @@ SyncApi.prototype._sync = function(attempt) {
}); });
} }
client.clientRunning = true;
updateSyncState(client, "PREPARED"); // assume success until we fail which may be 30+ secs */
// assume success until we fail which may be 30+ secs
client.store.setSyncToken(data.next_batch);
if (!syncOptions.hasSyncedBefore) {
updateSyncState(client, "PREPARED");
syncOptions.hasSyncedBefore = true;
}
updateSyncState(client, "SYNCING"); updateSyncState(client, "SYNCING");
self._pollForEvents(); self._sync(syncOptions);
}, function(err) { }, function(err) {
console.error("/initialSync error (%s attempts): %s", attempt, err); console.error("/sync error (%s attempts): %s", attempt, err);
attempt += 1; attempt += 1;
startSyncingRetryTimer(client, attempt, function() { startSyncingRetryTimer(client, attempt, function() {
self._sync(attempt); self._sync(syncOptions, attempt);
}); });
updateSyncState(client, "ERROR", { error: err }); updateSyncState(client, "ERROR", { error: err });
}); });