You've already forked matrix-js-sdk
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:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
166
lib/sync.js
166
lib/sync.js
@@ -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 });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user