Implement lazy-loading from placeholder to room

In placeholder-rooms.html
This commit is contained in:
Kegan Dougal 2021-11-24 15:12:38 +00:00
parent 080be2554b
commit 6140301d9e
8 changed files with 114 additions and 14 deletions

View file

@ -57,6 +57,10 @@ export class BaseTileViewModel extends ViewModel {
}
compare(other) {
// don't use KIND_ORDER for placeholder|room kinds as they are comparable
if (this.kind !== "invite" && other.kind !== "invite") {
return 0;
}
if (other.kind !== this.kind) {
return KIND_ORDER.indexOf(this.kind) - KIND_ORDER.indexOf(other.kind);
}

View file

@ -43,7 +43,7 @@ export class LeftPanelViewModel extends ViewModel {
if (roomOrInvite.isInvite) {
vm = new InviteTileViewModel(this.childOptions({invite: roomOrInvite, emitChange}));
} else if (roomOrInvite.isPlaceholder) {
vm = new PlaceholderRoomTileViewModel(this.childOptions({emitChange}));
vm = new PlaceholderRoomTileViewModel(this.childOptions({room: roomOrInvite, emitChange}));
} else {
vm = new RoomTileViewModel(this.childOptions({room: roomOrInvite, emitChange}));
}
@ -140,4 +140,9 @@ export class LeftPanelViewModel extends ViewModel {
return startFiltering;
}
}
// TODO: used in sync v3
loadRoomRange(range) {
}
}

View file

@ -20,6 +20,11 @@ import {BaseTileViewModel} from "./BaseTileViewModel.js";
export class PlaceholderRoomTileViewModel extends BaseTileViewModel {
constructor(options) {
super(options);
// Placeholder tiles can be sorted with Room tiles, so we need to ensure we have the same
// fields else the comparison needs to take into account the kind().
// We need a fake room so we can do compare(other) with RoomTileViewModels
const {room} = options;
this._room = room;
}
get busy() {
@ -31,11 +36,14 @@ export class PlaceholderRoomTileViewModel extends BaseTileViewModel {
}
compare(other) {
if (other._room.index !== undefined) {
return this._room.index > other._room.index ? 1 : -1;
}
return super.compare(other);
}
get name() {
return "Placeholder";
return "Placeholder " + this._room.index;
}
get avatarLetter() {

View file

@ -38,6 +38,12 @@ export class RoomTileViewModel extends BaseTileViewModel {
if (parentComparison !== 0) {
return parentComparison;
}
// sync v3 has its own ordering, use it if we have an index
if (this._room.index !== undefined && other._room.index !== undefined) {
return this._room.index > other._room.index ? 1 : -1;;
}
/*
put unread rooms first
then put rooms with a timestamp first, and sort by name

View file

@ -401,8 +401,6 @@ export class Session {
room.setInvite(invite);
}
}
const room = this.createRoom("!temp:localhost");
room.isPlaceholder = true;
this._rooms.add(room.id, room);
}

View file

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="platform/web/ui/css/main.css">
<link rel="stylesheet" type="text/css" href="platform/web/ui/css/themes/element/theme.css">
<style type="text/css">
.LeftPanel{
height: 100%;
}
</style>
</head>
<body class="not-ie11">
<div id="session-status" class="hydrogen" style="height: 500px;"></div>
<script id="main" type="module">
import {LeftPanelView} from "./platform/web/ui/session/leftpanel/LeftPanelView.js";
import {LeftPanelViewModel} from "./domain/session/leftpanel/LeftPanelViewModel";
import {Navigation} from "./domain/navigation/Navigation.js";
import {URLRouter} from "./domain/navigation/URLRouter.js";
import {parseUrlPath, stringifyPath} from "./domain/navigation/index.js";
import {ObservableMap} from "./observable/index.js";
import {History} from "./platform/web/dom/History.js";
const navigation = new Navigation(() => false);
const rooms = new ObservableMap();
for (let i = 0; i < 1000; i++) {
let r = {
id: "!placeholder-" + i,
isPlaceholder: true,
index: i,
};
rooms.add(r.id, r);
}
const leftPanel = new LeftPanelViewModel({
invites: new ObservableMap(),
rooms: rooms,
navigation: navigation,
urlCreator: new URLRouter({
navigation: navigation,
history: new History(),
stringifyPath: stringifyPath,
parseUrlPath: parseUrlPath,
}),
platform: null,
});
const sleep = (ms) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
leftPanel.loadRoomRange = async (range) => {
// pretend to load something
await sleep(200);
for (let i = range.start; i <= range.end; i++) {
const fakeRoomId = "!placeholder-" + i;
let room = rooms.get(fakeRoomId);
if (room && room.isPlaceholder) {
rooms.remove(fakeRoomId);
}
const actualRoomId = "!" + i + ":localhost";
room = room || {};
room.isPlaceholder = false;
room.id = actualRoomId;
room.avatarColorId = 1;
room.name = "Room " + i;
rooms.set(room.id, room);
}
};
const view = new LeftPanelView(leftPanel);
document.getElementById("session-status").appendChild(view.mount());
</script>
</body>
</html>

View file

@ -14,15 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {tag} from "./html";
import {removeChildren, mountView} from "./utils";
import {ListRange, ResultType, AddRemoveResult} from "./ListRange";
import {ListView, IOptions as IParentOptions} from "./ListView";
import {IView} from "./types";
import { tag } from "./html";
import { removeChildren, mountView } from "./utils";
import { ListRange, ResultType, AddRemoveResult } from "./ListRange";
import { ListView, IOptions as IParentOptions } from "./ListView";
import { IView } from "./types";
export interface IOptions<T, V> extends IParentOptions<T, V> {
itemHeight: number;
overflowItems?: number;
onRangeVisible?: (range: ListRange) => void;
}
export class LazyListView<T, V extends IView> extends ListView<T, V> {
@ -31,14 +32,16 @@ export class LazyListView<T, V extends IView> extends ListView<T, V> {
private itemHeight: number;
private overflowItems: number;
private scrollContainer?: HTMLElement;
private onRangeVisible?: (range: ListRange) => void;
constructor(
{itemHeight, overflowItems = 20, ...options}: IOptions<T, V>,
{ itemHeight, onRangeVisible, overflowItems = 20, ...options }: IOptions<T, V>,
childCreator: (value: T) => V
) {
super(options, childCreator);
this.itemHeight = itemHeight;
this.overflowItems = overflowItems;
this.onRangeVisible = onRangeVisible; // function(ItemRange)
}
handleEvent(e: Event) {
@ -82,7 +85,7 @@ export class LazyListView<T, V extends IView> extends ListView<T, V> {
}
private _getVisibleRange() {
const {clientHeight, scrollTop} = this.root()!;
const { clientHeight, scrollTop } = this.root()!;
if (clientHeight === 0) {
throw new Error("LazyListView height is 0");
}
@ -101,6 +104,9 @@ export class LazyListView<T, V extends IView> extends ListView<T, V> {
});
this._listElement!.appendChild(fragment);
this.adjustPadding(range);
if (this.onRangeVisible) {
this.onRangeVisible(range);
}
}
private renderUpdate(prevRange: ListRange, newRange: ListRange) {
@ -121,6 +127,9 @@ export class LazyListView<T, V extends IView> extends ListView<T, V> {
}
});
this.adjustPadding(newRange);
if (this.onRangeVisible) {
this.onRangeVisible(newRange);
}
} else {
this.reRenderFullRange(newRange);
}
@ -136,7 +145,7 @@ export class LazyListView<T, V extends IView> extends ListView<T, V> {
mount() {
const listElement = super.mount();
this.scrollContainer = tag.div({className: "LazyListParent"}, listElement) as HTMLElement;
this.scrollContainer = tag.div({ className: "LazyListParent" }, listElement) as HTMLElement;
this.scrollContainer.addEventListener("scroll", this);
return this.scrollContainer;
}

View file

@ -65,7 +65,7 @@ export class LeftPanelView extends TemplateView {
itemHeight: 44,
list: vm.tileViewModels,
onRangeVisible: (range) => {
console.log(range);
vm.loadRoomRange(range);
},
},
tileVM => {