convert TimelineView to typescript

This commit is contained in:
Bruno Windels 2021-09-06 17:51:32 +02:00
parent 632d29795a
commit ad4ec5f04c
4 changed files with 51 additions and 31 deletions

View file

@ -24,7 +24,7 @@ interface IOptions<T, V> {
onItemClick?: (childView: V, evt: UIEvent) => void,
className?: string,
tagName?: string,
parentProvidesUpdates: boolean
parentProvidesUpdates?: boolean
}
type SubscriptionHandle = () => undefined;
@ -122,7 +122,7 @@ export class ListView<T, V extends UIView> implements UIView {
this._childInstances = undefined;
}
private loadList() {
protected loadList() {
if (!this._list) {
return;
}
@ -184,4 +184,8 @@ export class ListView<T, V extends UIView> implements UIView {
protected onBeforeListChanged() {}
protected onListChanged() {}
protected getChildInstanceByIndex(idx: number): V | undefined {
return this._childInstances?.[idx];
}
}

View file

@ -17,7 +17,7 @@ limitations under the License.
import {TemplateView} from "../../general/TemplateView.js";
import {Popup} from "../../general/Popup.js";
import {Menu} from "../../general/Menu.js";
import {viewClassForEntry} from "./TimelineView.js"
import {viewClassForEntry} from "./TimelineView"
export class MessageComposer extends TemplateView {
constructor(viewModel) {

View file

@ -18,7 +18,7 @@ limitations under the License.
import {TemplateView} from "../../general/TemplateView.js";
import {Popup} from "../../general/Popup.js";
import {Menu} from "../../general/Menu.js";
import {TimelineView} from "./TimelineView.js";
import {TimelineView} from "./TimelineView";
import {TimelineLoadingView} from "./TimelineLoadingView.js";
import {MessageComposer} from "./MessageComposer.js";
import {RoomArchivedView} from "./RoomArchivedView.js";

View file

@ -23,8 +23,14 @@ import {FileView} from "./timeline/FileView.js";
import {MissingAttachmentView} from "./timeline/MissingAttachmentView.js";
import {AnnouncementView} from "./timeline/AnnouncementView.js";
import {RedactedView} from "./timeline/RedactedView.js";
import {SimpleTile} from "../../../../../domain/session/room/timeline/tiles/SimpleTile.js";
import {TimelineViewModel} from "../../../../../domain/session/room/timeline/TimelineViewModel.js";
export function viewClassForEntry(entry) {
type TileView = GapView | AnnouncementView | TextMessageView |
ImageView | VideoView | FileView | MissingAttachmentView | RedactedView;
type TileViewConstructor = (this: TileView, SimpleTile) => void;
export function viewClassForEntry(entry: SimpleTile): TileViewConstructor | undefined {
switch (entry.shape) {
case "gap": return GapView;
case "announcement": return AnnouncementView;
@ -40,13 +46,18 @@ export function viewClassForEntry(entry) {
}
}
export class TimelineView extends ListView {
constructor(viewModel) {
export class TimelineView extends ListView<SimpleTile, TileView> {
private _atBottom: boolean;
private _topLoadingPromise?: Promise<boolean>;
private _viewModel: TimelineViewModel;
constructor(viewModel: TimelineViewModel) {
const options = {
className: "Timeline bottom-aligned-scroll",
list: viewModel.tiles,
onItemClick: (tileView, evt) => tileView.onClick(evt),
}
};
super(options, entry => {
const View = viewClassForEntry(entry);
if (View) {
@ -54,12 +65,19 @@ export class TimelineView extends ListView {
}
});
this._atBottom = false;
this._onScroll = this._onScroll.bind(this);
this._topLoadingPromise = null;
this._topLoadingPromise = undefined;
this._viewModel = viewModel;
}
async _loadAtTopWhile(predicate) {
override handleEvent(evt: Event) {
if (evt.type === "scroll") {
this._handleScroll(evt);
} else {
super.handleEvent(evt);
}
}
async _loadAtTopWhile(predicate: () => boolean) {
if (this._topLoadingPromise) {
return;
}
@ -78,11 +96,11 @@ export class TimelineView extends ListView {
//ignore error, as it is handled in the VM
}
finally {
this._topLoadingPromise = null;
this._topLoadingPromise = undefined;
}
}
async _onScroll() {
async _handleScroll(evt: Event) {
const PAGINATE_OFFSET = 100;
const root = this.root();
if (root.scrollTop < PAGINATE_OFFSET && !this._topLoadingPromise && this._viewModel) {
@ -102,18 +120,18 @@ export class TimelineView extends ListView {
}
}
mount() {
override mount() {
const root = super.mount();
root.addEventListener("scroll", this._onScroll);
root.addEventListener("scroll", this);
return root;
}
unmount() {
this.root().removeEventListener("scroll", this._onScroll);
override unmount() {
this.root().removeEventListener("scroll", this);
super.unmount();
}
async loadList() {
override async loadList() {
super.loadList();
const root = this.root();
// yield so the browser can render the list
@ -130,7 +148,7 @@ export class TimelineView extends ListView {
});
}
onBeforeListChanged() {
override onBeforeListChanged() {
const fromBottom = this._distanceFromBottom();
this._atBottom = fromBottom < 1;
}
@ -140,25 +158,23 @@ export class TimelineView extends ListView {
return root.scrollHeight - root.scrollTop - root.clientHeight;
}
onListChanged() {
override onListChanged() {
const root = this.root();
if (this._atBottom) {
root.scrollTop = root.scrollHeight;
}
}
onUpdate(index, value, param) {
override onUpdate(index: number, value: SimpleTile, param: any) {
if (param === "shape") {
if (this._childInstances) {
const ExpectedClass = viewClassForEntry(value);
const child = this._childInstances[index];
if (!ExpectedClass || !(child instanceof ExpectedClass)) {
// shape was updated, so we need to recreate the tile view,
// the shape parameter is set in EncryptedEventTile.updateEntry
// (and perhaps elsewhere by the time you read this)
super.recreateItem(index, value);
return;
}
const ExpectedClass = viewClassForEntry(value);
const child = this.getChildInstanceByIndex(index);
if (!ExpectedClass || !(child instanceof ExpectedClass)) {
// shape was updated, so we need to recreate the tile view,
// the shape parameter is set in EncryptedEventTile.updateEntry
// (and perhaps elsewhere by the time you read this)
super.recreateItem(index, value);
return;
}
}
super.onUpdate(index, value, param);