mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 03:25:12 +01:00
Merge pull request #21 from vector-im/bwindels/backfill-fixes
More backfill fixes
This commit is contained in:
commit
cf689e7643
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -42,13 +43,16 @@ export class TimelineViewModel {
|
|||||||
this._tiles = new TilesCollection(timeline.entries, tilesCreator({room, ownUserId}));
|
this._tiles = new TilesCollection(timeline.entries, tilesCreator({room, ownUserId}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// doesn't fill gaps, only loads stored entries/tiles
|
/**
|
||||||
loadAtTop() {
|
* @return {bool} startReached if the start of the timeline was reached
|
||||||
|
*/
|
||||||
|
async loadAtTop() {
|
||||||
const firstTile = this._tiles.getFirst();
|
const firstTile = this._tiles.getFirst();
|
||||||
if (firstTile.shape === "gap") {
|
if (firstTile.shape === "gap") {
|
||||||
return firstTile.fill();
|
return firstTile.fill();
|
||||||
} else {
|
} else {
|
||||||
return this._timeline.loadAtTop(50);
|
await this._timeline.loadAtTop(50);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
23
src/domain/session/room/timeline/tiles/EncryptedEventTile.js
Normal file
23
src/domain/session/room/timeline/tiles/EncryptedEventTile.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {MessageTile} from "./MessageTile.js";
|
||||||
|
|
||||||
|
export class EncryptedEventTile extends MessageTile {
|
||||||
|
get text() {
|
||||||
|
return this.i18n`**Encrypted message**`;
|
||||||
|
}
|
||||||
|
}
|
@ -41,6 +41,8 @@ export class GapTile extends SimpleTile {
|
|||||||
this.emitChange("isLoading");
|
this.emitChange("isLoading");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// edgeReached will have been updated by fillGap
|
||||||
|
return this._entry.edgeReached;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateEntry(entry, params) {
|
updateEntry(entry, params) {
|
||||||
@ -60,14 +62,6 @@ export class GapTile extends SimpleTile {
|
|||||||
return this._loading;
|
return this._loading;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isUp() {
|
|
||||||
return this._entry.direction.isBackward;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isDown() {
|
|
||||||
return this._entry.direction.isForward;
|
|
||||||
}
|
|
||||||
|
|
||||||
get error() {
|
get error() {
|
||||||
if (this._error) {
|
if (this._error) {
|
||||||
const dir = this._entry.prev_batch ? "previous" : "next";
|
const dir = this._entry.prev_batch ? "previous" : "next";
|
||||||
|
@ -20,6 +20,7 @@ import {ImageTile} from "./tiles/ImageTile.js";
|
|||||||
import {LocationTile} from "./tiles/LocationTile.js";
|
import {LocationTile} from "./tiles/LocationTile.js";
|
||||||
import {RoomNameTile} from "./tiles/RoomNameTile.js";
|
import {RoomNameTile} from "./tiles/RoomNameTile.js";
|
||||||
import {RoomMemberTile} from "./tiles/RoomMemberTile.js";
|
import {RoomMemberTile} from "./tiles/RoomMemberTile.js";
|
||||||
|
import {EncryptedEventTile} from "./tiles/EncryptedEventTile.js";
|
||||||
|
|
||||||
export function tilesCreator({room, ownUserId}) {
|
export function tilesCreator({room, ownUserId}) {
|
||||||
return function tilesCreator(entry, emitUpdate) {
|
return function tilesCreator(entry, emitUpdate) {
|
||||||
@ -49,6 +50,8 @@ export function tilesCreator({room, ownUserId}) {
|
|||||||
return new RoomNameTile(options);
|
return new RoomNameTile(options);
|
||||||
case "m.room.member":
|
case "m.room.member":
|
||||||
return new RoomMemberTile(options);
|
return new RoomMemberTile(options);
|
||||||
|
case "m.room.encrypted":
|
||||||
|
return new EncryptedEventTile(options);
|
||||||
default:
|
default:
|
||||||
// unknown type not rendered
|
// unknown type not rendered
|
||||||
return null;
|
return null;
|
||||||
|
@ -83,6 +83,9 @@ export class Room extends EventEmitter {
|
|||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
async fillGap(fragmentEntry, amount) {
|
async fillGap(fragmentEntry, amount) {
|
||||||
|
if (fragmentEntry.edgeReached) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const response = await this._hsApi.messages(this._roomId, {
|
const response = await this._hsApi.messages(this._roomId, {
|
||||||
from: fragmentEntry.token,
|
from: fragmentEntry.token,
|
||||||
dir: fragmentEntry.direction.asApiString(),
|
dir: fragmentEntry.direction.asApiString(),
|
||||||
|
@ -60,7 +60,7 @@ export class FragmentBoundaryEntry extends BaseEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isGap() {
|
get isGap() {
|
||||||
return !!this.token;
|
return !!this.token && !this.edgeReached;
|
||||||
}
|
}
|
||||||
|
|
||||||
get token() {
|
get token() {
|
||||||
@ -79,6 +79,25 @@ export class FragmentBoundaryEntry extends BaseEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get edgeReached() {
|
||||||
|
if (this.started) {
|
||||||
|
return this.fragment.startReached;
|
||||||
|
} else {
|
||||||
|
return this.fragment.endReached;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set edgeReached(reached) {
|
||||||
|
|
||||||
|
if (this.started) {
|
||||||
|
this.fragment.startReached = reached;
|
||||||
|
} else {
|
||||||
|
this.fragment.endReached = reached;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
get linkedFragmentId() {
|
get linkedFragmentId() {
|
||||||
if (this.started) {
|
if (this.started) {
|
||||||
return this.fragment.previousId;
|
return this.fragment.previousId;
|
||||||
|
@ -178,6 +178,14 @@ export class GapWriter {
|
|||||||
if (fragmentEntry.token !== start) {
|
if (fragmentEntry.token !== start) {
|
||||||
throw new Error("start is not equal to prev_batch or next_batch");
|
throw new Error("start is not equal to prev_batch or next_batch");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// begin (or end) of timeline reached
|
||||||
|
if (chunk.length === 0) {
|
||||||
|
fragmentEntry.edgeReached = true;
|
||||||
|
await txn.timelineFragments.update(fragmentEntry.fragment);
|
||||||
|
return {entries: [fragmentEntry], fragments: []};
|
||||||
|
}
|
||||||
|
|
||||||
// find last event in fragment so we get the eventIndex to begin creating keys at
|
// find last event in fragment so we get the eventIndex to begin creating keys at
|
||||||
let lastKey = await this._findFragmentEdgeEventKey(fragmentEntry, txn);
|
let lastKey = await this._findFragmentEdgeEventKey(fragmentEntry, txn);
|
||||||
// find out if any event in chunk is already present using findFirstOrLastOccurringEventId
|
// find out if any event in chunk is already present using findFirstOrLastOccurringEventId
|
||||||
|
@ -102,12 +102,14 @@ export class ListView {
|
|||||||
}
|
}
|
||||||
this._subscription = this._list.subscribe(this);
|
this._subscription = this._list.subscribe(this);
|
||||||
this._childInstances = [];
|
this._childInstances = [];
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
for (let item of this._list) {
|
for (let item of this._list) {
|
||||||
const child = this._childCreator(item);
|
const child = this._childCreator(item);
|
||||||
this._childInstances.push(child);
|
this._childInstances.push(child);
|
||||||
const childDomNode = child.mount(this._mountArgs);
|
const childDomNode = child.mount(this._mountArgs);
|
||||||
this._root.appendChild(childDomNode);
|
fragment.appendChild(childDomNode);
|
||||||
}
|
}
|
||||||
|
this._root.appendChild(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(idx, value) {
|
onAdd(idx, value) {
|
||||||
|
@ -41,11 +41,17 @@ export class TimelineList extends ListView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _loadAtTopWhile(predicate) {
|
async _loadAtTopWhile(predicate) {
|
||||||
|
if (this._topLoadingPromise) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
while (predicate()) {
|
while (predicate()) {
|
||||||
// fill, not enough content to fill timeline
|
// fill, not enough content to fill timeline
|
||||||
this._topLoadingPromise = this._viewModel.loadAtTop();
|
this._topLoadingPromise = this._viewModel.loadAtTop();
|
||||||
await this._topLoadingPromise;
|
const startReached = await this._topLoadingPromise;
|
||||||
|
if (startReached) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
@ -86,9 +92,12 @@ export class TimelineList extends ListView {
|
|||||||
super.unmount();
|
super.unmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadList() {
|
async loadList() {
|
||||||
super.loadList();
|
super.loadList();
|
||||||
const root = this.root();
|
const root = this.root();
|
||||||
|
// yield so the browser can render the list
|
||||||
|
// and we can measure the content below
|
||||||
|
await Promise.resolve();
|
||||||
const {scrollHeight, clientHeight} = root;
|
const {scrollHeight, clientHeight} = root;
|
||||||
if (scrollHeight > clientHeight) {
|
if (scrollHeight > clientHeight) {
|
||||||
root.scrollTop = root.scrollHeight;
|
root.scrollTop = root.scrollHeight;
|
||||||
|
Loading…
Reference in New Issue
Block a user