mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 03:25:12 +01:00
log login & loading a session
This commit is contained in:
parent
ef1e867dee
commit
57bb75e864
@ -96,7 +96,6 @@ export class Session {
|
|||||||
|
|
||||||
// called once this._e2eeAccount is assigned
|
// called once this._e2eeAccount is assigned
|
||||||
_setupEncryption() {
|
_setupEncryption() {
|
||||||
console.log("loaded e2ee account with keys", this._e2eeAccount.identityKeys);
|
|
||||||
// TODO: this should all go in a wrapper in e2ee/ that is bootstrapped by passing in the account
|
// TODO: this should all go in a wrapper in e2ee/ that is bootstrapped by passing in the account
|
||||||
// and can create RoomEncryption objects and handle encrypted to_device messages and device list changes.
|
// and can create RoomEncryption objects and handle encrypted to_device messages and device list changes.
|
||||||
const senderKeyLock = new LockMap();
|
const senderKeyLock = new LockMap();
|
||||||
@ -228,7 +227,7 @@ export class Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
async createIdentity() {
|
async createIdentity(log) {
|
||||||
if (this._olm) {
|
if (this._olm) {
|
||||||
if (!this._e2eeAccount) {
|
if (!this._e2eeAccount) {
|
||||||
this._e2eeAccount = await E2EEAccount.create({
|
this._e2eeAccount = await E2EEAccount.create({
|
||||||
@ -240,15 +239,16 @@ export class Session {
|
|||||||
olmWorker: this._olmWorker,
|
olmWorker: this._olmWorker,
|
||||||
storage: this._storage,
|
storage: this._storage,
|
||||||
});
|
});
|
||||||
|
log.set("keys", this._e2eeAccount.identityKeys);
|
||||||
this._setupEncryption();
|
this._setupEncryption();
|
||||||
}
|
}
|
||||||
await this._e2eeAccount.generateOTKsIfNeeded(this._storage);
|
await this._e2eeAccount.generateOTKsIfNeeded(this._storage, log);
|
||||||
await this._e2eeAccount.uploadKeys(this._storage);
|
await log.wrap("uploadKeys", log => this._e2eeAccount.uploadKeys(this._storage, log));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
async load() {
|
async load(log) {
|
||||||
const txn = this._storage.readTxn([
|
const txn = this._storage.readTxn([
|
||||||
this._storage.storeNames.session,
|
this._storage.storeNames.session,
|
||||||
this._storage.storeNames.roomSummary,
|
this._storage.storeNames.roomSummary,
|
||||||
@ -271,6 +271,7 @@ export class Session {
|
|||||||
txn
|
txn
|
||||||
});
|
});
|
||||||
if (this._e2eeAccount) {
|
if (this._e2eeAccount) {
|
||||||
|
log.set("keys", this._e2eeAccount.identityKeys);
|
||||||
this._setupEncryption();
|
this._setupEncryption();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,7 +280,7 @@ export class Session {
|
|||||||
const rooms = await txn.roomSummary.getAll();
|
const rooms = await txn.roomSummary.getAll();
|
||||||
await Promise.all(rooms.map(summary => {
|
await Promise.all(rooms.map(summary => {
|
||||||
const room = this.createRoom(summary.roomId, pendingEventsByRoomId.get(summary.roomId));
|
const room = this.createRoom(summary.roomId, pendingEventsByRoomId.get(summary.roomId));
|
||||||
return room.load(summary, txn);
|
return log.wrap("room", log => room.load(summary, txn, log));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,7 +298,7 @@ export class Session {
|
|||||||
* and useful to store so we can later tell what capabilities
|
* and useful to store so we can later tell what capabilities
|
||||||
* our homeserver has.
|
* our homeserver has.
|
||||||
*/
|
*/
|
||||||
async start(lastVersionResponse) {
|
async start(lastVersionResponse, log) {
|
||||||
if (lastVersionResponse) {
|
if (lastVersionResponse) {
|
||||||
// store /versions response
|
// store /versions response
|
||||||
const txn = this._storage.readWriteTxn([
|
const txn = this._storage.readWriteTxn([
|
||||||
@ -334,7 +335,7 @@ export class Session {
|
|||||||
if (roomOperations) {
|
if (roomOperations) {
|
||||||
roomOperationsByType = groupBy(roomOperations, r => r.type);
|
roomOperationsByType = groupBy(roomOperations, r => r.type);
|
||||||
}
|
}
|
||||||
room.start(roomOperationsByType);
|
room.start(roomOperationsByType, log);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,7 +439,7 @@ export class Session {
|
|||||||
// are about to receive messages for
|
// are about to receive messages for
|
||||||
// (https://github.com/vector-im/riot-web/issues/2782).
|
// (https://github.com/vector-im/riot-web/issues/2782).
|
||||||
if (!isCatchupSync) {
|
if (!isCatchupSync) {
|
||||||
const needsToUploadOTKs = await this._e2eeAccount.generateOTKsIfNeeded(this._storage);
|
const needsToUploadOTKs = await this._e2eeAccount.generateOTKsIfNeeded(this._storage, log);
|
||||||
if (needsToUploadOTKs) {
|
if (needsToUploadOTKs) {
|
||||||
promises.push(log.wrap("uploadKeys", log => this._e2eeAccount.uploadKeys(this._storage, log)));
|
promises.push(log.wrap("uploadKeys", log => this._e2eeAccount.uploadKeys(this._storage, log)));
|
||||||
}
|
}
|
||||||
|
@ -73,68 +73,79 @@ export class SessionContainer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._status.set(LoadStatus.Loading);
|
this._status.set(LoadStatus.Loading);
|
||||||
try {
|
this._platform.logger.run("load session", async log => {
|
||||||
const sessionInfo = await this._platform.sessionInfoStorage.get(sessionId);
|
log.set("id", sessionId);
|
||||||
if (!sessionInfo) {
|
try {
|
||||||
throw new Error("Invalid session id: " + sessionId);
|
const sessionInfo = await this._platform.sessionInfoStorage.get(sessionId);
|
||||||
|
if (!sessionInfo) {
|
||||||
|
throw new Error("Invalid session id: " + sessionId);
|
||||||
|
}
|
||||||
|
await this._loadSessionInfo(sessionInfo, false, log);
|
||||||
|
log.set("status", this._status.get());
|
||||||
|
} catch (err) {
|
||||||
|
log.catch(err);
|
||||||
|
this._error = err;
|
||||||
|
this._status.set(LoadStatus.Error);
|
||||||
}
|
}
|
||||||
await this._loadSessionInfo(sessionInfo, false);
|
});
|
||||||
} catch (err) {
|
|
||||||
this._error = err;
|
|
||||||
this._status.set(LoadStatus.Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async startWithLogin(homeServer, username, password) {
|
async startWithLogin(homeServer, username, password) {
|
||||||
if (this._status.get() !== LoadStatus.NotLoading) {
|
if (this._status.get() !== LoadStatus.NotLoading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._status.set(LoadStatus.Login);
|
this._platform.logger.run("login", async log => {
|
||||||
const clock = this._platform.clock;
|
this._status.set(LoadStatus.Login);
|
||||||
let sessionInfo;
|
const clock = this._platform.clock;
|
||||||
try {
|
let sessionInfo;
|
||||||
const request = this._platform.request;
|
try {
|
||||||
const hsApi = new HomeServerApi({homeServer, request, createTimeout: clock.createTimeout});
|
const request = this._platform.request;
|
||||||
const loginData = await hsApi.passwordLogin(username, password, "Hydrogen").response();
|
const hsApi = new HomeServerApi({homeServer, request, createTimeout: clock.createTimeout});
|
||||||
const sessionId = this.createNewSessionId();
|
const loginData = await hsApi.passwordLogin(username, password, "Hydrogen", {log}).response();
|
||||||
sessionInfo = {
|
const sessionId = this.createNewSessionId();
|
||||||
id: sessionId,
|
sessionInfo = {
|
||||||
deviceId: loginData.device_id,
|
id: sessionId,
|
||||||
userId: loginData.user_id,
|
deviceId: loginData.device_id,
|
||||||
homeServer: homeServer,
|
userId: loginData.user_id,
|
||||||
accessToken: loginData.access_token,
|
homeServer: homeServer,
|
||||||
lastUsed: clock.now()
|
accessToken: loginData.access_token,
|
||||||
};
|
lastUsed: clock.now()
|
||||||
await this._platform.sessionInfoStorage.add(sessionInfo);
|
};
|
||||||
} catch (err) {
|
log.set("id", sessionId);
|
||||||
this._error = err;
|
await this._platform.sessionInfoStorage.add(sessionInfo);
|
||||||
if (err instanceof HomeServerError) {
|
} catch (err) {
|
||||||
if (err.errcode === "M_FORBIDDEN") {
|
this._error = err;
|
||||||
this._loginFailure = LoginFailure.Credentials;
|
if (err instanceof HomeServerError) {
|
||||||
|
if (err.errcode === "M_FORBIDDEN") {
|
||||||
|
this._loginFailure = LoginFailure.Credentials;
|
||||||
|
} else {
|
||||||
|
this._loginFailure = LoginFailure.Unknown;
|
||||||
|
}
|
||||||
|
log.set("loginFailure", this._loginFailure);
|
||||||
|
this._status.set(LoadStatus.LoginFailed);
|
||||||
|
} else if (err instanceof ConnectionError) {
|
||||||
|
this._loginFailure = LoginFailure.Connection;
|
||||||
|
this._status.set(LoadStatus.LoginFailed);
|
||||||
} else {
|
} else {
|
||||||
this._loginFailure = LoginFailure.Unknown;
|
this._status.set(LoadStatus.Error);
|
||||||
}
|
}
|
||||||
this._status.set(LoadStatus.LoginFailed);
|
return;
|
||||||
} else if (err instanceof ConnectionError) {
|
}
|
||||||
this._loginFailure = LoginFailure.Connection;
|
// loading the session can only lead to
|
||||||
this._status.set(LoadStatus.LoginFailed);
|
// LoadStatus.Error in case of an error,
|
||||||
} else {
|
// so separate try/catch
|
||||||
|
try {
|
||||||
|
await this._loadSessionInfo(sessionInfo, true, log);
|
||||||
|
log.set("status", this._status.get());
|
||||||
|
} catch (err) {
|
||||||
|
log.catch(err);
|
||||||
|
this._error = err;
|
||||||
this._status.set(LoadStatus.Error);
|
this._status.set(LoadStatus.Error);
|
||||||
}
|
}
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
// loading the session can only lead to
|
|
||||||
// LoadStatus.Error in case of an error,
|
|
||||||
// so separate try/catch
|
|
||||||
try {
|
|
||||||
await this._loadSessionInfo(sessionInfo, true);
|
|
||||||
} catch (err) {
|
|
||||||
this._error = err;
|
|
||||||
this._status.set(LoadStatus.Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async _loadSessionInfo(sessionInfo, isNewLogin) {
|
async _loadSessionInfo(sessionInfo, isNewLogin, log) {
|
||||||
const clock = this._platform.clock;
|
const clock = this._platform.clock;
|
||||||
this._sessionStartedByReconnector = false;
|
this._sessionStartedByReconnector = false;
|
||||||
this._status.set(LoadStatus.Loading);
|
this._status.set(LoadStatus.Loading);
|
||||||
@ -178,24 +189,26 @@ export class SessionContainer {
|
|||||||
mediaRepository,
|
mediaRepository,
|
||||||
platform: this._platform,
|
platform: this._platform,
|
||||||
});
|
});
|
||||||
await this._session.load();
|
await this._session.load(log);
|
||||||
if (isNewLogin) {
|
if (isNewLogin) {
|
||||||
this._status.set(LoadStatus.SessionSetup);
|
this._status.set(LoadStatus.SessionSetup);
|
||||||
await this._session.createIdentity();
|
await log.wrap("createIdentity", log => this._session.createIdentity(log));
|
||||||
}
|
}
|
||||||
|
|
||||||
this._sync = new Sync({hsApi: this._requestScheduler.hsApi, storage: this._storage, session: this._session, logger: this._platform.logger});
|
this._sync = new Sync({hsApi: this._requestScheduler.hsApi, storage: this._storage, session: this._session, logger: this._platform.logger});
|
||||||
// notify sync and session when back online
|
// notify sync and session when back online
|
||||||
this._reconnectSubscription = this._reconnector.connectionStatus.subscribe(state => {
|
this._reconnectSubscription = this._reconnector.connectionStatus.subscribe(state => {
|
||||||
if (state === ConnectionStatus.Online) {
|
if (state === ConnectionStatus.Online) {
|
||||||
// needs to happen before sync and session or it would abort all requests
|
this._platform.logger.runDetached("reconnect", async log => {
|
||||||
this._requestScheduler.start();
|
// needs to happen before sync and session or it would abort all requests
|
||||||
this._sync.start();
|
this._requestScheduler.start();
|
||||||
this._sessionStartedByReconnector = true;
|
this._sync.start();
|
||||||
this._session.start(this._reconnector.lastVersionsResponse);
|
this._sessionStartedByReconnector = true;
|
||||||
|
await log.wrap("session start", log => this._session.start(this._reconnector.lastVersionsResponse, log));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await this._waitForFirstSync();
|
await log.wrap("wait first sync", log => this._waitForFirstSync(log));
|
||||||
|
|
||||||
this._status.set(LoadStatus.Ready);
|
this._status.set(LoadStatus.Ready);
|
||||||
|
|
||||||
@ -204,12 +217,13 @@ export class SessionContainer {
|
|||||||
// started to session, so check first
|
// started to session, so check first
|
||||||
// to prevent an extra /versions request
|
// to prevent an extra /versions request
|
||||||
if (!this._sessionStartedByReconnector) {
|
if (!this._sessionStartedByReconnector) {
|
||||||
const lastVersionsResponse = await hsApi.versions({timeout: 10000}).response();
|
const lastVersionsResponse = await hsApi.versions({timeout: 10000, log}).response();
|
||||||
this._session.start(lastVersionsResponse);
|
// log as ref as we don't want to await it
|
||||||
|
await log.wrap("session start", log => this._session.start(lastVersionsResponse, log));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _waitForFirstSync() {
|
async _waitForFirstSync(log) {
|
||||||
try {
|
try {
|
||||||
this._sync.start();
|
this._sync.start();
|
||||||
this._status.set(LoadStatus.FirstSync);
|
this._status.set(LoadStatus.FirstSync);
|
||||||
|
@ -80,22 +80,24 @@ export class Account {
|
|||||||
return this._identityKeys;
|
return this._identityKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadKeys(storage) {
|
async uploadKeys(storage, log) {
|
||||||
const oneTimeKeys = JSON.parse(this._account.one_time_keys());
|
const oneTimeKeys = JSON.parse(this._account.one_time_keys());
|
||||||
// only one algorithm supported by olm atm, so hardcode its name
|
// only one algorithm supported by olm atm, so hardcode its name
|
||||||
const oneTimeKeysEntries = Object.entries(oneTimeKeys.curve25519);
|
const oneTimeKeysEntries = Object.entries(oneTimeKeys.curve25519);
|
||||||
if (oneTimeKeysEntries.length || !this._areDeviceKeysUploaded) {
|
if (oneTimeKeysEntries.length || !this._areDeviceKeysUploaded) {
|
||||||
const payload = {};
|
const payload = {};
|
||||||
if (!this._areDeviceKeysUploaded) {
|
if (!this._areDeviceKeysUploaded) {
|
||||||
|
log.set("identity", true);
|
||||||
const identityKeys = JSON.parse(this._account.identity_keys());
|
const identityKeys = JSON.parse(this._account.identity_keys());
|
||||||
payload.device_keys = this._deviceKeysPayload(identityKeys);
|
payload.device_keys = this._deviceKeysPayload(identityKeys);
|
||||||
}
|
}
|
||||||
if (oneTimeKeysEntries.length) {
|
if (oneTimeKeysEntries.length) {
|
||||||
|
log.set("otks", true);
|
||||||
payload.one_time_keys = this._oneTimeKeysPayload(oneTimeKeysEntries);
|
payload.one_time_keys = this._oneTimeKeysPayload(oneTimeKeysEntries);
|
||||||
}
|
}
|
||||||
const response = await this._hsApi.uploadKeys(payload, /*{log}*/).response();
|
const response = await this._hsApi.uploadKeys(payload, {log}).response();
|
||||||
this._serverOTKCount = response?.one_time_key_counts?.signed_curve25519;
|
this._serverOTKCount = response?.one_time_key_counts?.signed_curve25519;
|
||||||
// log.set("serverOTKCount", this._serverOTKCount);
|
log.set("serverOTKCount", this._serverOTKCount);
|
||||||
// TODO: should we not modify this in the txn like we do elsewhere?
|
// TODO: should we not modify this in the txn like we do elsewhere?
|
||||||
// we'd have to pickle and unpickle the account to clone it though ...
|
// we'd have to pickle and unpickle the account to clone it though ...
|
||||||
// and the upload has succeed at this point, so in-memory would be correct
|
// and the upload has succeed at this point, so in-memory would be correct
|
||||||
@ -114,7 +116,7 @@ export class Account {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async generateOTKsIfNeeded(storage) {
|
async generateOTKsIfNeeded(storage, log) {
|
||||||
const maxOTKs = this._account.max_number_of_one_time_keys();
|
const maxOTKs = this._account.max_number_of_one_time_keys();
|
||||||
const limit = maxOTKs / 2;
|
const limit = maxOTKs / 2;
|
||||||
if (this._serverOTKCount < limit) {
|
if (this._serverOTKCount < limit) {
|
||||||
@ -128,11 +130,16 @@ export class Account {
|
|||||||
if (totalOTKCount < limit) {
|
if (totalOTKCount < limit) {
|
||||||
// we could in theory also generated the keys and store them in
|
// we could in theory also generated the keys and store them in
|
||||||
// writeSync, but then we would have to clone the account to avoid side-effects.
|
// writeSync, but then we would have to clone the account to avoid side-effects.
|
||||||
await this._updateSessionStorage(storage, sessionStore => {
|
await log.wrap("generate otks", log => this._updateSessionStorage(storage, sessionStore => {
|
||||||
const newKeyCount = maxOTKs - totalOTKCount;
|
const newKeyCount = maxOTKs - totalOTKCount;
|
||||||
|
log.set("max", maxOTKs);
|
||||||
|
log.set("server", this._serverOTKCount);
|
||||||
|
log.set("unpublished", unpublishedOTKCount);
|
||||||
|
log.set("new", newKeyCount);
|
||||||
|
log.set("limit", limit);
|
||||||
this._account.generate_one_time_keys(newKeyCount);
|
this._account.generate_one_time_keys(newKeyCount);
|
||||||
sessionStore.set(ACCOUNT_SESSION_KEY, this._account.pickle(this._pickleKey));
|
sessionStore.set(ACCOUNT_SESSION_KEY, this._account.pickle(this._pickleKey));
|
||||||
});
|
}));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,6 +167,7 @@ export class Room extends EventEmitter {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
await writeTxn.complete();
|
await writeTxn.complete();
|
||||||
|
// TODO: log decryption errors here
|
||||||
decryption.applyToEntries(entries);
|
decryption.applyToEntries(entries);
|
||||||
if (this._observedEvents) {
|
if (this._observedEvents) {
|
||||||
this._observedEvents.updateEvents(entries);
|
this._observedEvents.updateEvents(entries);
|
||||||
@ -318,30 +319,29 @@ export class Room extends EventEmitter {
|
|||||||
async afterSyncCompleted(changes, log) {
|
async afterSyncCompleted(changes, log) {
|
||||||
log.set("id", this.id);
|
log.set("id", this.id);
|
||||||
if (this._roomEncryption) {
|
if (this._roomEncryption) {
|
||||||
// TODO: pass log to flushPendingRoomKeyShares once we also have a logger in `start`
|
await this._roomEncryption.flushPendingRoomKeyShares(this._hsApi, null, log);
|
||||||
await this._roomEncryption.flushPendingRoomKeyShares(this._hsApi, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @package */
|
/** @package */
|
||||||
async start(pendingOperations) {
|
start(pendingOperations, parentLog) {
|
||||||
if (this._roomEncryption) {
|
if (this._roomEncryption) {
|
||||||
try {
|
const roomKeyShares = pendingOperations?.get("share_room_key");
|
||||||
const roomKeyShares = pendingOperations?.get("share_room_key");
|
if (roomKeyShares) {
|
||||||
if (roomKeyShares) {
|
// if we got interrupted last time sending keys to newly joined members
|
||||||
// if we got interrupted last time sending keys to newly joined members
|
parentLog.wrapDetached("flush room keys", log => {
|
||||||
await this._roomEncryption.flushPendingRoomKeyShares(this._hsApi, roomKeyShares);
|
log.set("id", this.id);
|
||||||
}
|
return this._roomEncryption.flushPendingRoomKeyShares(this._hsApi, roomKeyShares, log);
|
||||||
} catch (err) {
|
});
|
||||||
// we should not throw here
|
|
||||||
console.error(`could not send out (all) pending room keys for room ${this.id}`, err.stack);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._sendQueue.resumeSending();
|
|
||||||
|
this._sendQueue.resumeSending(parentLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @package */
|
/** @package */
|
||||||
async load(summary, txn) {
|
async load(summary, txn, log) {
|
||||||
|
log.set("id", this.id);
|
||||||
try {
|
try {
|
||||||
this._summary.load(summary);
|
this._summary.load(summary);
|
||||||
if (this._summary.data.encryption) {
|
if (this._summary.data.encryption) {
|
||||||
@ -354,7 +354,7 @@ export class Room extends EventEmitter {
|
|||||||
const changes = await this._heroes.calculateChanges(this._summary.data.heroes, [], txn);
|
const changes = await this._heroes.calculateChanges(this._summary.data.heroes, [], txn);
|
||||||
this._heroes.applyChanges(changes, this._summary.data);
|
this._heroes.applyChanges(changes, this._summary.data);
|
||||||
}
|
}
|
||||||
return this._syncWriter.load(txn);
|
return this._syncWriter.load(txn, log);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new WrappedError(`Could not load room ${this._roomId}`, err);
|
throw new WrappedError(`Could not load room ${this._roomId}`, err);
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ export class SyncWriter {
|
|||||||
this._lastLiveKey = null;
|
this._lastLiveKey = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async load(txn) {
|
async load(txn, log) {
|
||||||
const liveFragment = await txn.timelineFragments.liveFragment(this._roomId);
|
const liveFragment = await txn.timelineFragments.liveFragment(this._roomId);
|
||||||
if (liveFragment) {
|
if (liveFragment) {
|
||||||
const [lastEvent] = await txn.timelineEvents.lastEvents(this._roomId, liveFragment.id, 1);
|
const [lastEvent] = await txn.timelineEvents.lastEvents(this._roomId, liveFragment.id, 1);
|
||||||
@ -53,7 +53,9 @@ export class SyncWriter {
|
|||||||
}
|
}
|
||||||
// if there is no live fragment, we don't create it here because load gets a readonly txn.
|
// if there is no live fragment, we don't create it here because load gets a readonly txn.
|
||||||
// this is on purpose, load shouldn't modify the store
|
// this is on purpose, load shouldn't modify the store
|
||||||
console.log("room persister load", this._roomId, this._lastLiveKey && this._lastLiveKey.toString());
|
if (this._lastLiveKey) {
|
||||||
|
log.set("live key", this._lastLiveKey.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _createLiveFragment(txn, previousToken) {
|
async _createLiveFragment(txn, previousToken) {
|
||||||
|
Loading…
Reference in New Issue
Block a user