1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-28 05:03:59 +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.
}
// TODO:
// Internal: rate limiting
var OLM_ALGORITHM = "m.olm.v1.curve25519-aes-sha2";
/**
@@ -147,7 +144,6 @@ function MatrixClient(opts) {
this.callList = {
// callId: MatrixCall
};
this._config = {}; // see startClient()
// 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.
@@ -2197,6 +2193,7 @@ MatrixClient.prototype.startClient = function(opts) {
// client is already running.
return;
}
this.clientRunning = true;
// backwards compat for when 'opts' was 'historyLen'.
if (typeof opts === "number") {
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) {
this.uploadKeys(5);
}
@@ -2219,11 +2208,8 @@ MatrixClient.prototype.startClient = function(opts) {
// periodically poll for turn servers if we support voip
checkTurnServers(this);
var syncApi = new SyncApi(this);
syncApi.sync({
historyLen: this._config.initialSyncLimit,
includeArchived: this._config.includeArchivedRooms
});
var syncApi = new SyncApi(this, opts);
syncApi.sync();
};
/**
@@ -2233,6 +2219,7 @@ MatrixClient.prototype.startClient = function(opts) {
MatrixClient.prototype.stopClient = function() {
this.clientRunning = false;
// TODO: f.e. Room => self.store.storeRoom(room) ?
// TODO: Actually stop the SyncApi
};
function setupCallEventHandler(client) {

View File

@@ -81,7 +81,9 @@ module.exports.createClient = function(opts) {
};
}
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();
return new module.exports.MatrixClient(opts);
};

View File

@@ -8,8 +8,13 @@
/**
* Construct a new in-memory data store for the Matrix Client.
* @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 = {
// roomId: Room
};
@@ -22,6 +27,7 @@ module.exports.MatrixInMemoryStore = function MatrixInMemoryStore() {
// filterId: Filter
// }
};
this.localStorage = opts.localStorage;
};
module.exports.MatrixInMemoryStore.prototype = {
@@ -139,6 +145,37 @@ module.exports.MatrixInMemoryStore.prototype = {
return null;
}
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

View File

@@ -113,6 +113,15 @@ StubStore.prototype = {
*/
getFilter: function(userId, filterId) {
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

View File

@@ -14,6 +14,10 @@ var User = require("./models/user");
var Room = require("./models/room");
var utils = require("./utils");
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>
@@ -21,9 +25,15 @@ var MatrixEvent = require("./models/event").MatrixEvent;
* @constructor
* @param {MatrixClient} client The matrix client instance to use.
*/
function SyncApi(client) {
function SyncApi(client, opts) {
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
* @param {Object} opts
* @param {Number} opts.historyLen
* @param {Boolean} opts.includeArchived
*/
SyncApi.prototype.sync = function(opts) {
SyncApi.prototype.sync = function() {
console.log("SyncApi.sync");
this.opts = opts || {};
this._prepareForSync();
};
/**
* @param {Number=} attempt
*/
SyncApi.prototype._prepareForSync = function(attempt) {
var client = this.client;
var self = this;
if (client.isGuest()) {
// no push rules for guests
this._sync();
// 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;
}
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;
// 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() {
self._prepareForSync(attempt);
fnToRun(attempt);
});
updateSyncState(client, "ERROR", { error: err });
});
};
}
if (client.isGuest()) {
// no push rules for guests
getFilter();
}
else {
getPushRules();
}
};
/**
* Invoke me to do /sync calls
* @param {Object} syncOptions
* @param {string} syncOptions.filterId
* @param {boolean} syncOptions.hasSyncedBefore
* @param {Number=} attempt
*/
SyncApi.prototype._sync = function(attempt) {
var opts = this.opts;
SyncApi.prototype._sync = function(syncOptions, attempt) {
var client = this.client;
var self = this;
var historyLen = opts.historyLen;
var includeArchived = opts.includeArchived;
attempt = attempt || 1;
var qps = { limit: historyLen };
if (includeArchived) {
qps.archived = true;
}
// TODO include archived rooms flag.
var qps = {
filter: syncOptions.filterId,
timeout: this.opts.pollTimeout,
since: client.store.getSyncToken() || undefined // do not send 'null'
};
if (client._guestRooms && client._isGuest) {
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) {
// 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;
// intercept the results and put them into our store
if (!(client.store instanceof StubStore)) {
@@ -180,7 +253,6 @@ SyncApi.prototype._sync = function(attempt) {
}
if (data) {
client.store.setSyncToken(data.end);
var events = [];
for (i = 0; i < data.presence.length; i++) {
events.push(new MatrixEvent(data.presence[i]));
@@ -204,16 +276,22 @@ SyncApi.prototype._sync = function(attempt) {
});
}
client.clientRunning = true;
// assume success until we fail which may be 30+ secs */
client.store.setSyncToken(data.next_batch);
if (!syncOptions.hasSyncedBefore) {
updateSyncState(client, "PREPARED");
// assume success until we fail which may be 30+ secs
syncOptions.hasSyncedBefore = true;
}
updateSyncState(client, "SYNCING");
self._pollForEvents();
self._sync(syncOptions);
}, function(err) {
console.error("/initialSync error (%s attempts): %s", attempt, err);
console.error("/sync error (%s attempts): %s", attempt, err);
attempt += 1;
startSyncingRetryTimer(client, attempt, function() {
self._sync(attempt);
self._sync(syncOptions, attempt);
});
updateSyncState(client, "ERROR", { error: err });
});