diff --git a/src/matrix/room/timeline/persistence/GapWriter.js b/src/matrix/room/timeline/persistence/GapWriter.js index 837d445f..20ba1fa7 100644 --- a/src/matrix/room/timeline/persistence/GapWriter.js +++ b/src/matrix/room/timeline/persistence/GapWriter.js @@ -205,6 +205,59 @@ export class GapWriter { return changedFragments; } + async _storeAndUpdate(fragmentEntry, events, key, end, state, txn, log) { + const { + nonOverlappingEvents, + neighbourFragmentEntry + } = await this._findOverlappingEvents(fragmentEntry, events, txn, log); + const {entries, updatedEntries} = await this._storeEvents(nonOverlappingEvents, key, fragmentEntry.direction, state, txn, log); + const fragments = await this._updateFragments(fragmentEntry, neighbourFragmentEntry, end, entries, txn); + return {entries, updatedEntries, fragments}; + } + + async writeContext(response, txn, log) { + const { + events_before: eventsBefore, + events_after: eventsAfter, + event, state, start, end + } = response; + + if (!Array.isArray(eventsBefore) || !Array.isArray(eventsAfter)) { + throw new Error("Invalid chunks in response"); + } + + const eventEntry = await txn.timelineEvents.getByEventId(this._roomId, event.event_id); + if (eventEntry) { + // If we have the current event, eary return. + return { entries: [], updatedEntries: [], fragments: [] } + } + + const maxFragmentKey = await tnx.timelineFragments.getMaxFragmentId(this._roomId); + const newFragment = { + roomId: this._roomId, + id: maxFragmentKey + 1, + previousId: null, + nextId: null, + previousToken: null, + nextToken: null + }; + const eventKey = EventKey.defaultFragmentKey(newFragment.id); + + const startEntry = FragmentBoundaryEntry.start(newFragment, this._fragmentIdComparer); + const startFill = this._storeAndUpdate(startEntry, eventsBefore, eventKey, start, state, txn, log); + + eventsAfter.unshift(event); + const endEntry = FragmentBoundaryEntry.end(newFragment, this._fragmentIdComparer); + const endFill = this._storeAndUpdate(endEntry, eventsAfter, eventKey, end, state, txn, log); + + // Both startFill and endFill are throwaway objects, so we + // may as well modify one of them in-place instead of returning a third. + startFill.entries.push(...endFill.entries); + startFill.updatedEntries.push(...endFill.updatedEntries); + startFill.fragments.push(...endFill.fragments); + return startFill; + } + async writeFragmentFill(fragmentEntry, response, txn, log) { const {fragmentId, direction} = fragmentEntry; // chunk is in reverse-chronological order when backwards diff --git a/src/matrix/storage/idb/stores/TimelineFragmentStore.ts b/src/matrix/storage/idb/stores/TimelineFragmentStore.ts index 813fc3f3..802d6e98 100644 --- a/src/matrix/storage/idb/stores/TimelineFragmentStore.ts +++ b/src/matrix/storage/idb/stores/TimelineFragmentStore.ts @@ -16,7 +16,7 @@ limitations under the License. import { StorageError } from "../../common"; import {KeyLimits} from "../../common"; -import { encodeUint32 } from "../utils"; +import { encodeUint32, decodeUint32 } from "../utils"; import {Store} from "../Store"; interface Fragment { @@ -34,6 +34,11 @@ function encodeKey(roomId: string, fragmentId: number): string { return `${roomId}|${encodeUint32(fragmentId)}`; } +function decodeKey(key) { + const [roomId, fragmentId] = key.split("|"); + return { roomId, fragmentId: decodeUint32(fragmentId) }; +} + export class TimelineFragmentStore { private _store: Store; @@ -71,6 +76,14 @@ export class TimelineFragmentStore { }); } + async getMaxFragmentId(roomId) { + const maxKey = await this._store.findMaxKey(this._allRange(roomId)); + if (!maxKey) { + return null; + } + return decodeKey(maxKey).fragmentId; + } + // should generate an id an return it? // depends if we want to do anything smart with fragment ids, // like give them meaning depending on range. not for now probably ...