if we don't have our own member yet, fetch it from the server and store

This commit is contained in:
Bruno Windels 2021-04-07 22:43:08 +02:00
parent d94aeff98c
commit be3d734319
4 changed files with 62 additions and 10 deletions

View file

@ -189,6 +189,10 @@ export class HomeServerApi {
return this._get(`/rooms/${encodeURIComponent(roomId)}/messages`, params, null, options); return this._get(`/rooms/${encodeURIComponent(roomId)}/messages`, params, null, options);
} }
roomState(roomId, eventType, stateKey, options = null) {
return this._get(`/rooms/${encodeURIComponent(roomId)}/state/${encodeURIComponent(eventType)}/${encodeURIComponent(stateKey)}`, null, null, options);
}
// params is at, membership and not_membership // params is at, membership and not_membership
members(roomId, params, options = null) { members(roomId, params, options = null) {
return this._get(`/rooms/${encodeURIComponent(roomId)}/members`, params, null, options); return this._get(`/rooms/${encodeURIComponent(roomId)}/members`, params, null, options);

View file

@ -425,8 +425,13 @@ export class Room extends EventEmitter {
/** @public */ /** @public */
sendEvent(eventType, content, attachments, log = null) { sendEvent(eventType, content, attachments, log = null) {
this._platform.logger.wrapOrRun(log, "send", log => { this._platform.logger.wrapOrRun(log, "send", async log => {
log.set("id", this.id); log.set("id", this.id);
if (this._timeline) {
// ensure we have our own member loaded for the local echo
await this._timeline.ensureOwnMember(
this._user, this._summary.data.membership, this._hsApi, log);
}
return this._sendQueue.enqueueEvent(eventType, content, attachments, log); return this._sendQueue.enqueueEvent(eventType, content, attachments, log);
}); });
} }
@ -678,7 +683,12 @@ export class Room extends EventEmitter {
if (this._roomEncryption) { if (this._roomEncryption) {
this._timeline.enableEncryption(this._decryptEntries.bind(this, DecryptionSource.Timeline)); this._timeline.enableEncryption(this._decryptEntries.bind(this, DecryptionSource.Timeline));
} }
await this._timeline.load(this._user, log); if (this._sendQueue.pendingEvents.length !== 0) {
// ensure we have our own member loaded for the local echo
await this._timeline.ensureOwnMember(
this._user, this._summary.data.membership, this._hsApi, log);
}
await this._timeline.load(log);
return this._timeline; return this._timeline;
}); });
} }

View file

@ -23,6 +23,15 @@ export class RoomMember {
this._data = data; this._data = data;
} }
static async fromFetch(roomId, userId, hsApi, log) {
const memberEvent = await hsApi.roomState(this._roomId, EVENT_TYPE, userId, {log}).response();
return RoomMember.fromMemberEvent(memberEvent);
}
static fromUserId(roomId, userId, membership) {
return new RoomMember({roomId, userId, membership});
}
static fromMemberEvent(roomId, memberEvent) { static fromMemberEvent(roomId, memberEvent) {
const userId = memberEvent?.state_key; const userId = memberEvent?.state_key;
if (typeof userId !== "string") { if (typeof userId !== "string") {

View file

@ -45,14 +45,8 @@ export class Timeline {
} }
/** @package */ /** @package */
async load(user, log) { async load(log) {
const txn = await this._storage.readTxn(this._timelineReader.readTxnStores.concat(this._storage.storeNames.roomMembers)); const txn = await this._storage.readTxn(this._timelineReader.readTxnStores);
const memberData = await txn.roomMembers.get(this._roomId, user.id);
this._ownMember = new RoomMember(memberData);
// it should be fine to not update the local entries,
// as they should only populate once the view subscribes to it
// if they are populated already, the sender profile would be empty
// 30 seems to be a good amount to fill the entire screen // 30 seems to be a good amount to fill the entire screen
const readerRequest = this._disposables.track(this._timelineReader.readFromEnd(30, txn, log)); const readerRequest = this._disposables.track(this._timelineReader.readFromEnd(30, txn, log));
try { try {
@ -63,6 +57,41 @@ export class Timeline {
} }
} }
async ensureOwnMember(user, myMembership, hsApi, log) {
if (this._ownMember) {
return;
}
const txn = await this._storage.readTxn(this._storage.storeNames.roomMembers);
const memberData = await txn.roomMembers.get(this._roomId, user.id);
if (memberData) {
this._ownMember = new RoomMember(memberData);
} else {
await log.wrap("own member missing", async log => {
// important this *never* throws, not even when offline,
// as it would prevent enqueueing a message, or opening the timeline
try {
this._ownMember = await RoomMember.fromFetch(this._roomId, user.id, hsApi, log);
const writeTxn = await this._storage.readWriteTxn(this._storage.storeNames.roomMembers);
// check we still don't have the member after the fetch (e.g. from sync)
// just to make sure we don't overwrite it
const memberData = await writeTxn.roomMembers.get(this._roomId, user.id);
if(!memberData) {
writeTxn.roomMembers.set(this._roomId, this._ownMember.serialize());
await writeTxn.complete();
} else {
this._ownMember = new RoomMember(memberData);
}
} catch (err) {
log.error = err;
if (!this._ownMember) {
log.set("fallback", true);
this._ownMember = RoomMember.fromUserId(this._roomId, user.id, myMembership);
}
}
});
}
}
updateOwnMember(member) { updateOwnMember(member) {
this._ownMember = member; this._ownMember = member;
} }