diff --git a/src/domain/session/room/timeline/TilesCollection.js b/src/domain/session/room/timeline/TilesCollection.js index 10062af2..23d863d4 100644 --- a/src/domain/session/room/timeline/TilesCollection.js +++ b/src/domain/session/room/timeline/TilesCollection.js @@ -236,6 +236,22 @@ export class TilesCollection extends BaseObservableList { getFirst() { return this._tiles[0]; } + + getTileIndex(searchTile) { + const idx = sortedIndex(this._tiles, searchTile, (searchTile, tile) => { + // negate result because we're switching the order of the params + return searchTile.compare(tile); + }); + const foundTile = this._tiles[idx]; + if (foundTile?.compare(searchTile) === 0) { + return idx; + } + return -1; + } + + sliceIterator(start, end) { + return this._tiles.slice(start, end)[Symbol.iterator](); + } } import {ObservableArray} from "../../../../observable/list/ObservableArray.js"; diff --git a/src/domain/session/room/timeline/TimelineViewModel.js b/src/domain/session/room/timeline/TimelineViewModel.js index dd115e79..0e28cb6e 100644 --- a/src/domain/session/room/timeline/TimelineViewModel.js +++ b/src/domain/session/room/timeline/TimelineViewModel.js @@ -42,10 +42,41 @@ export class TimelineViewModel extends ViewModel { const {timeline, tilesCreator} = options; this._timeline = this.track(timeline); this._tiles = new TilesCollection(timeline.entries, tilesCreator); - this._timeline.loadAtTop(50); + this._startTile = null; + this._endTile = null; + this._topLoadingPromise = null; + this._bottomLoadingPromise = null; } - setVisibleTileRange(startTile, endTile) { + setVisibleTileRange(startTile, endTile, isViewportFilled) { + // we should async batch things here? + + // this will prevent a (small) inserted tile from being marked visible, won't it? + if (this._startTile === startTile && this._endTile === endTile) { + return; + } + + // old tiles could have been removed from tilescollection once we support unloading + const oldStartIndex = this._startTile ? this._tiles.getTileIndex(this._startTile) : Number.MAX_SAFE_INTEGER; + const oldEndIndex = this._endTile ? this._tiles.getTileIndex(this._endTile) : Number.MIN_SAFE_INTEGER; + const newStartIndex = this._tiles.getTileIndex(startTile); + const newEndIndex = this._tiles.getTileIndex(endTile); + + const minIndex = Math.min(oldStartIndex, newStartIndex); + const maxIndex = Math.max(oldEndIndex, newEndIndex); + + let index = minIndex; + for (const tile of this._tiles.sliceIterator(minIndex, maxIndex)) { + const isVisible = index >= newStartIndex && index <= newEndIndex; + tile.setVisible(isVisible); + index += 1; + } + + if (!isViewportFilled || (newStartIndex < 5 && !this._topLoadingPromise)) { + this._topLoadingPromise = this._timeline.loadAtTop(10).then(() => { + this._topLoadingPromise = null; + }); + } } get tiles() { diff --git a/src/domain/session/room/timeline/tiles/SimpleTile.js b/src/domain/session/room/timeline/tiles/SimpleTile.js index 412d9737..96679c97 100644 --- a/src/domain/session/room/timeline/tiles/SimpleTile.js +++ b/src/domain/session/room/timeline/tiles/SimpleTile.js @@ -83,6 +83,10 @@ export class SimpleTile extends ViewModel { return this._entry; } + compare(tile) { + return this.upperEntry.compare(tile.upperEntry); + } + compareEntry(entry) { return this._entry.compare(entry); } @@ -119,6 +123,10 @@ export class SimpleTile extends ViewModel { } + setVisible(isVisible) { + + } + dispose() { this.setUpdateEmit(null); super.dispose(); diff --git a/src/platform/web/ui/session/room/TimelineView.ts b/src/platform/web/ui/session/room/TimelineView.ts index 969cd254..1342d877 100644 --- a/src/platform/web/ui/session/room/TimelineView.ts +++ b/src/platform/web/ui/session/room/TimelineView.ts @@ -86,7 +86,7 @@ export class TimelineView extends TemplateView { tiles.style.setProperty("margin-top", `${missingTilesHeight}px`); // we don't have enough tiles to fill the viewport, so set all as visible const len = this.value.tiles.length; - this.updateVisibleRange(0, len - 1); + this.updateVisibleRange(0, len - 1, false); } else { tiles.style.removeProperty("margin-top"); if (this.stickToBottom) { @@ -123,14 +123,14 @@ export class TimelineView extends TemplateView { bottomNodeIndex = anchoredNodeIndex; } let topNodeIndex = findFirstNodeIndexAtOrBelow(tiles, scrollTop, bottomNodeIndex); - this.updateVisibleRange(topNodeIndex, bottomNodeIndex); + this.updateVisibleRange(topNodeIndex, bottomNodeIndex, true); } - private updateVisibleRange(startIndex: number, endIndex: number) { + private updateVisibleRange(startIndex: number, endIndex: number, isViewportFilled: boolean) { const firstVisibleChild = this.tilesView!.getChildInstanceByIndex(startIndex); const lastVisibleChild = this.tilesView!.getChildInstanceByIndex(endIndex); if (firstVisibleChild && lastVisibleChild) { - this.value.setVisibleTileRange(firstVisibleChild.value, lastVisibleChild.value); + this.value.setVisibleTileRange(firstVisibleChild.value, lastVisibleChild.value, isViewportFilled); } } }