2019-05-12 20:26:03 +02:00
|
|
|
import { ObservableArray } from "../../../observable/index.js";
|
|
|
|
import sortedIndex from "../../../utils/sortedIndex.js";
|
2019-06-01 15:40:21 +02:00
|
|
|
import GapWriter from "./persistence/GapWriter.js";
|
2019-05-19 20:49:46 +02:00
|
|
|
import TimelineReader from "./persistence/TimelineReader.js";
|
2019-02-27 22:50:08 +01:00
|
|
|
|
|
|
|
export default class Timeline {
|
2019-05-12 20:26:03 +02:00
|
|
|
constructor({roomId, storage, closeCallback, fragmentIdComparer}) {
|
2019-02-27 22:50:08 +01:00
|
|
|
this._roomId = roomId;
|
|
|
|
this._storage = storage;
|
|
|
|
this._closeCallback = closeCallback;
|
|
|
|
this._entriesList = new ObservableArray();
|
2019-05-12 20:26:03 +02:00
|
|
|
this._fragmentIdComparer = fragmentIdComparer;
|
2019-05-19 20:49:46 +02:00
|
|
|
this._timelineReader = new TimelineReader({
|
|
|
|
roomId: this._roomId,
|
|
|
|
storage: this._storage,
|
|
|
|
fragmentIdComparer: this._fragmentIdComparer
|
|
|
|
});
|
2019-02-27 22:50:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @package */
|
|
|
|
async load() {
|
2019-06-01 17:04:05 +02:00
|
|
|
const entries = this._timelineReader.readFromEnd(100);
|
2019-02-27 22:50:08 +01:00
|
|
|
for (const entry of entries) {
|
|
|
|
this._entriesList.append(entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @package */
|
|
|
|
appendLiveEntries(newEntries) {
|
|
|
|
for (const entry of newEntries) {
|
|
|
|
this._entriesList.append(entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-08 20:03:18 +01:00
|
|
|
/** @public */
|
2019-05-12 20:26:03 +02:00
|
|
|
async fillGap(fragmentEntry, amount) {
|
2019-03-09 00:41:06 +01:00
|
|
|
const response = await this._hsApi.messages(this._roomId, {
|
2019-05-12 20:26:03 +02:00
|
|
|
from: fragmentEntry.token,
|
|
|
|
dir: fragmentEntry.direction.asApiString(),
|
2019-03-08 20:03:18 +01:00
|
|
|
limit: amount
|
|
|
|
});
|
2019-03-09 00:41:06 +01:00
|
|
|
|
2019-06-01 15:40:21 +02:00
|
|
|
const gapWriter = new GapWriter({
|
2019-05-12 20:26:03 +02:00
|
|
|
roomId: this._roomId,
|
|
|
|
storage: this._storage,
|
|
|
|
fragmentIdComparer: this._fragmentIdComparer
|
|
|
|
});
|
2019-06-01 15:40:21 +02:00
|
|
|
const newEntries = await gapWriter.writerFragmentFill(fragmentEntry, response);
|
2019-03-08 20:03:18 +01:00
|
|
|
// find where to replace existing gap with newEntries by doing binary search
|
2019-05-12 20:26:03 +02:00
|
|
|
const gapIdx = sortedIndex(this._entriesList.array, fragmentEntry, (fragmentEntry, entry) => {
|
|
|
|
return fragmentEntry.compare(entry);
|
2019-03-09 00:41:06 +01:00
|
|
|
});
|
|
|
|
// only replace the gap if it's currently in the timeline
|
2019-05-12 20:26:03 +02:00
|
|
|
if (this._entriesList.at(gapIdx) === fragmentEntry) {
|
2019-03-09 00:41:06 +01:00
|
|
|
this._entriesList.removeAt(gapIdx);
|
|
|
|
this._entriesList.insertMany(gapIdx, newEntries);
|
|
|
|
}
|
|
|
|
}
|
2019-03-08 20:03:18 +01:00
|
|
|
|
2019-03-09 00:41:06 +01:00
|
|
|
async loadAtTop(amount) {
|
2019-05-19 20:49:46 +02:00
|
|
|
// TODO: use TimelineReader::readFrom here, and insert returned array at location for first and last entry.
|
2019-03-09 00:41:06 +01:00
|
|
|
const firstEntry = this._entriesList.at(0);
|
|
|
|
if (firstEntry) {
|
2019-05-12 20:26:03 +02:00
|
|
|
const txn = await this._storage.readTxn([this._storage.storeNames.timelineEvents]);
|
2019-03-09 00:41:06 +01:00
|
|
|
const topEntries = await txn.roomTimeline.eventsBefore(this._roomId, firstEntry.sortKey, amount);
|
|
|
|
this._entriesList.insertMany(0, topEntries);
|
|
|
|
return topEntries.length;
|
|
|
|
}
|
2019-03-08 20:03:18 +01:00
|
|
|
}
|
|
|
|
|
2019-02-27 22:50:08 +01:00
|
|
|
/** @public */
|
|
|
|
get entries() {
|
|
|
|
return this._entriesList;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @public */
|
|
|
|
close() {
|
|
|
|
this._closeCallback();
|
|
|
|
}
|
|
|
|
}
|