2019-02-04 23:26:45 +01:00
|
|
|
import {RequestAbortError} from "./network.js";
|
|
|
|
import {HomeServerError, StorageError} from "./error.js";
|
2018-12-21 14:35:24 +01:00
|
|
|
|
2019-02-03 22:17:24 +01:00
|
|
|
const INCREMENTAL_TIMEOUT = 30;
|
2018-12-21 14:35:24 +01:00
|
|
|
|
2019-02-04 23:26:45 +01:00
|
|
|
function parseRooms(responseSections, roomMapper) {
|
|
|
|
return ["join", "invite", "leave"].map(membership => {
|
|
|
|
const membershipSection = responseSections[membership];
|
|
|
|
const results = Object.entries(membershipSection).map(([roomId, roomResponse]) => {
|
|
|
|
const room = roomMapper(roomId, membership);
|
|
|
|
return room.processInitialSync(roomResponse);
|
|
|
|
});
|
|
|
|
return results;
|
|
|
|
}).reduce((allResults, sectionResults) => allResults.concat(sectionResults), []);
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Sync {
|
2019-02-03 22:17:24 +01:00
|
|
|
constructor(network, session, storage) {
|
2018-12-21 14:35:24 +01:00
|
|
|
this._network = network;
|
|
|
|
this._session = session;
|
2019-02-03 22:17:24 +01:00
|
|
|
this._storage = storage;
|
2018-12-21 14:35:24 +01:00
|
|
|
this._isSyncing = false;
|
|
|
|
this._currentRequest = null;
|
|
|
|
}
|
2019-02-03 22:17:24 +01:00
|
|
|
// returns when initial sync is done
|
|
|
|
async start() {
|
2018-12-21 14:35:24 +01:00
|
|
|
if (this._isSyncing) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this._isSyncing = true;
|
|
|
|
try {
|
2019-02-03 22:17:24 +01:00
|
|
|
let syncToken = session.syncToken;
|
|
|
|
// do initial sync if needed
|
|
|
|
if (!syncToken) {
|
|
|
|
syncToken = await this._syncRequest();
|
|
|
|
}
|
2018-12-21 14:35:24 +01:00
|
|
|
} catch(err) {
|
|
|
|
//expected when stop is called
|
|
|
|
if (err instanceof RequestAbortError) {
|
|
|
|
|
|
|
|
} else if (err instanceof HomeServerError) {
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// something threw something
|
|
|
|
}
|
|
|
|
}
|
2019-02-03 22:17:24 +01:00
|
|
|
this._syncLoop(syncToken);
|
2018-12-21 14:35:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
async _syncLoop(syncToken) {
|
2019-02-03 22:17:24 +01:00
|
|
|
// if syncToken is falsy, it will first do an initial sync ...
|
2018-12-21 14:35:24 +01:00
|
|
|
while(this._isSyncing) {
|
2019-02-03 22:17:24 +01:00
|
|
|
try {
|
|
|
|
syncToken = await this._syncRequest(INCREMENTAL_TIMEOUT, syncToken);
|
|
|
|
} catch (err) {
|
|
|
|
this.emit("error", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async _syncRequest(timeout, syncToken) {
|
|
|
|
this._currentRequest = this._network.sync(timeout, syncToken);
|
|
|
|
const response = await this._currentRequest.response;
|
|
|
|
syncToken = response.next_batch;
|
2019-02-05 00:21:50 +01:00
|
|
|
const storeNames = this._storage.storeNames;
|
|
|
|
const txn = this._storage.startReadWriteTxn([
|
|
|
|
storeNames.timeline,
|
|
|
|
storeNames.sync,
|
|
|
|
storeNames.state
|
|
|
|
]);
|
2019-02-03 22:17:24 +01:00
|
|
|
try {
|
|
|
|
session.applySync(syncToken, response.account_data, txn);
|
2018-12-21 14:35:24 +01:00
|
|
|
// to_device
|
|
|
|
// presence
|
2019-02-03 22:17:24 +01:00
|
|
|
parseRooms(response.rooms, async (roomId, roomResponse, membership) => {
|
2018-12-21 14:35:24 +01:00
|
|
|
let room = session.getRoom(roomId);
|
|
|
|
if (!room) {
|
2019-02-06 23:06:47 +01:00
|
|
|
room = session.createRoom(roomId);
|
2018-12-21 14:35:24 +01:00
|
|
|
}
|
2019-02-03 22:17:24 +01:00
|
|
|
room.applySync(roomResponse, membership, txn);
|
2018-12-21 14:35:24 +01:00
|
|
|
});
|
2019-02-03 22:17:24 +01:00
|
|
|
} catch(err) {
|
|
|
|
// avoid corrupting state by only
|
|
|
|
// storing the sync up till the point
|
|
|
|
// the exception occurred
|
|
|
|
txn.abort();
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
await txn.complete();
|
|
|
|
} catch (err) {
|
|
|
|
throw new StorageError("unable to commit sync tranaction", err);
|
2018-12-21 14:35:24 +01:00
|
|
|
}
|
2019-02-03 22:17:24 +01:00
|
|
|
return syncToken;
|
2018-12-21 14:35:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
stop() {
|
|
|
|
if (!this._isSyncing) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this._isSyncing = false;
|
|
|
|
if (this._currentRequest) {
|
|
|
|
this._currentRequest.abort();
|
|
|
|
this._currentRequest = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|