also pass timeline entries to summary after initial decryption failed

This commit is contained in:
Bruno Windels 2020-09-14 16:34:07 +02:00
parent f3f07a0672
commit 49f330279b
2 changed files with 77 additions and 14 deletions

View file

@ -70,8 +70,15 @@ export class Room extends EventEmitter {
await decryptRequest.complete(); await decryptRequest.complete();
this._timeline?.replaceEntries(retryEntries); this._timeline?.replaceEntries(retryEntries);
// we would ideally write the room summary in the same txn as the groupSessionDecryptions in the
// _decryptEntries entries and could even know which events have been decrypted for the first
// time from DecryptionChanges.write and only pass those to the summary. As timeline changes
// are not essential to the room summary, it's fine to write this in a separate txn for now.
const changes = this._summary.processTimelineEntries(retryEntries, false, this._isTimelineOpen);
if (changes) {
this._summary.writeAndApplyChanges(changes, this._storage);
this._emitUpdate();
} }
// pass decryptedEntries to roomSummary
} }
} }
} }

View file

@ -16,7 +16,19 @@ limitations under the License.
import {MEGOLM_ALGORITHM} from "../e2ee/common.js"; import {MEGOLM_ALGORITHM} from "../e2ee/common.js";
function applySyncResponse(data, roomResponse, timelineEntries, membership, isInitialSync, isTimelineOpen, ownUserId) {
function applyTimelineEntries(data, timelineEntries, isInitialSync, isTimelineOpen, ownUserId) {
if (timelineEntries.length) {
data = timelineEntries.reduce((data, entry) => {
return processTimelineEvent(data, entry,
isInitialSync, isTimelineOpen, ownUserId);
}, data);
}
return data;
}
function applySyncResponse(data, roomResponse, membership) {
if (roomResponse.summary) { if (roomResponse.summary) {
data = updateSummary(data, roomResponse.summary); data = updateSummary(data, roomResponse.summary);
} }
@ -31,13 +43,14 @@ function applySyncResponse(data, roomResponse, timelineEntries, membership, isIn
if (roomResponse.state) { if (roomResponse.state) {
data = roomResponse.state.events.reduce(processStateEvent, data); data = roomResponse.state.events.reduce(processStateEvent, data);
} }
if (timelineEntries.length) { const {timeline} = roomResponse;
data = timelineEntries.reduce((data, entry) => { // process state events in timeline
if (typeof entry.stateKey === "string") { // non-state events are handled by applyTimelineEntries
return processStateEvent(data, entry.event); // so decryption is handled properly
} else { if (timeline && Array.isArray(timeline.events)) {
return processTimelineEvent(data, entry, data = timeline.events.reduce((data, event) => {
isInitialSync, isTimelineOpen, ownUserId); if (typeof event.state_key === "string") {
return processStateEvent(data, event);
} }
}, data); }, data);
} }
@ -92,15 +105,19 @@ function processStateEvent(data, event) {
function processTimelineEvent(data, eventEntry, isInitialSync, isTimelineOpen, ownUserId) { function processTimelineEvent(data, eventEntry, isInitialSync, isTimelineOpen, ownUserId) {
if (eventEntry.eventType === "m.room.message") { if (eventEntry.eventType === "m.room.message") {
if (!data.lastMessageTimestamp || eventEntry.timestamp > data.lastMessageTimestamp) {
data = data.cloneIfNeeded(); data = data.cloneIfNeeded();
data.lastMessageTimestamp = eventEntry.timestamp; data.lastMessageTimestamp = eventEntry.timestamp;
}
if (!isInitialSync && eventEntry.sender !== ownUserId && !isTimelineOpen) { if (!isInitialSync && eventEntry.sender !== ownUserId && !isTimelineOpen) {
data = data.cloneIfNeeded();
data.isUnread = true; data.isUnread = true;
} }
const {content} = eventEntry; const {content} = eventEntry;
const body = content?.body; const body = content?.body;
const msgtype = content?.msgtype; const msgtype = content?.msgtype;
if (msgtype === "m.text" && !eventEntry.isEncrypted) { if (msgtype === "m.text" && !eventEntry.isEncrypted) {
data = data.cloneIfNeeded();
data.lastMessageBody = body; data.lastMessageBody = body;
} }
} }
@ -266,14 +283,34 @@ export class RoomSummary {
return data; return data;
} }
/**
* after retrying decryption
*/
processTimelineEntries(timelineEntries, isInitialSync, isTimelineOpen) {
// clear cloned flag, so cloneIfNeeded makes a copy and
// this._data is not modified if any field is changed.
processTimelineEvent
this._data.cloned = false;
const data = applyTimelineEntries(
this._data,
timelineEntries,
isInitialSync, isTimelineOpen,
this._ownUserId);
if (data !== this._data) {
return data;
}
}
writeSync(roomResponse, timelineEntries, membership, isInitialSync, isTimelineOpen, txn) { writeSync(roomResponse, timelineEntries, membership, isInitialSync, isTimelineOpen, txn) {
// clear cloned flag, so cloneIfNeeded makes a copy and // clear cloned flag, so cloneIfNeeded makes a copy and
// this._data is not modified if any field is changed. // this._data is not modified if any field is changed.
this._data.cloned = false; this._data.cloned = false;
const data = applySyncResponse( let data = applySyncResponse(this._data, roomResponse, membership);
this._data, roomResponse, data = applyTimelineEntries(
this._data,
timelineEntries, timelineEntries,
membership,
isInitialSync, isTimelineOpen, isInitialSync, isTimelineOpen,
this._ownUserId); this._ownUserId);
if (data !== this._data) { if (data !== this._data) {
@ -282,6 +319,25 @@ export class RoomSummary {
} }
} }
/**
* Only to be used with processTimelineEntries,
* other methods like writeSync, writeHasFetchedMembers,
* writeIsTrackingMembers, ... take a txn directly.
*/
async writeAndApplyChanges(data, storage) {
const txn = await storage.readTxn([
storage.storeNames.roomSummary,
]);
try {
txn.roomSummary.set(data.serialize());
} catch (err) {
txn.abort();
throw err;
}
await txn.complete();
this.applyChanges(data);
}
applyChanges(data) { applyChanges(data) {
this._data = data; this._data = data;
} }