diff --git a/src/domain/session/room/timeline/TilesCollection.js b/src/domain/session/room/timeline/TilesCollection.js index 75af5b09..173b0cf6 100644 --- a/src/domain/session/room/timeline/TilesCollection.js +++ b/src/domain/session/room/timeline/TilesCollection.js @@ -311,7 +311,7 @@ export function tests() { } const entries = new ObservableArray([{n: 5}, {n: 10}]); const tileOptions = { - tileClassForEntry: entry => UpdateOnSiblingTile, + tileClassForEntry: () => UpdateOnSiblingTile, }; const tiles = new TilesCollection(entries, tileOptions); let receivedAdd = false; @@ -337,7 +337,7 @@ export function tests() { } const entries = new ObservableArray([{n: 5}, {n: 10}, {n: 15}]); const tileOptions = { - tileClassForEntry: entry => UpdateOnSiblingTile, + tileClassForEntry: () => UpdateOnSiblingTile, }; const tiles = new TilesCollection(entries, tileOptions); const events = []; diff --git a/src/lib.ts b/src/lib.ts index 7f4e5316..90bf597c 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -44,7 +44,7 @@ export {MissingAttachmentTile} from "./domain/session/room/timeline/tiles/Missin export {SimpleTile} from "./domain/session/room/timeline/tiles/SimpleTile.js"; export {TimelineView} from "./platform/web/ui/session/room/TimelineView"; -export {viewClassForEntry} from "./platform/web/ui/session/room/common"; +export {viewClassForTile} from "./platform/web/ui/session/room/common"; export type {TileViewConstructor, ViewClassForEntryFn} from "./platform/web/ui/session/room/TimelineView"; // export timeline tile views export {AnnouncementView} from "./platform/web/ui/session/room/timeline/AnnouncementView.js"; diff --git a/src/platform/web/ui/session/RoomGridView.js b/src/platform/web/ui/session/RoomGridView.js index 79fc3d21..65289bea 100644 --- a/src/platform/web/ui/session/RoomGridView.js +++ b/src/platform/web/ui/session/RoomGridView.js @@ -21,6 +21,11 @@ import {TemplateView} from "../general/TemplateView"; import {StaticView} from "../general/StaticView.js"; export class RoomGridView extends TemplateView { + constructor(vm, viewClassForTile) { + super(vm); + this._viewClassForTile = viewClassForTile; + } + render(t, vm) { const children = []; for (let i = 0; i < (vm.height * vm.width); i+=1) { @@ -39,7 +44,7 @@ export class RoomGridView extends TemplateView { } else if (roomVM.kind === "invite") { return new InviteView(roomVM); } else { - return new RoomView(roomVM); + return new RoomView(roomVM, this._viewClassForTile); } } else { return new StaticView(t => t.div({className: "room-placeholder"}, [ diff --git a/src/platform/web/ui/session/SessionView.js b/src/platform/web/ui/session/SessionView.js index e7cc406a..ef63b29b 100644 --- a/src/platform/web/ui/session/SessionView.js +++ b/src/platform/web/ui/session/SessionView.js @@ -28,6 +28,7 @@ import {RoomGridView} from "./RoomGridView.js"; import {SettingsView} from "./settings/SettingsView.js"; import {CreateRoomView} from "./CreateRoomView.js"; import {RightPanelView} from "./rightpanel/RightPanelView.js"; +import {viewClassForTile} from "./room/common"; export class SessionView extends TemplateView { render(t, vm) { @@ -42,7 +43,7 @@ export class SessionView extends TemplateView { t.view(new LeftPanelView(vm.leftPanelViewModel)), t.mapView(vm => vm.activeMiddleViewModel, () => { if (vm.roomGridViewModel) { - return new RoomGridView(vm.roomGridViewModel); + return new RoomGridView(vm.roomGridViewModel, viewClassForTile); } else if (vm.settingsViewModel) { return new SettingsView(vm.settingsViewModel); } else if (vm.createRoomViewModel) { @@ -51,7 +52,7 @@ export class SessionView extends TemplateView { if (vm.currentRoomViewModel.kind === "invite") { return new InviteView(vm.currentRoomViewModel); } else if (vm.currentRoomViewModel.kind === "room") { - return new RoomView(vm.currentRoomViewModel); + return new RoomView(vm.currentRoomViewModel, viewClassForTile); } else if (vm.currentRoomViewModel.kind === "roomBeingCreated") { return new RoomBeingCreatedView(vm.currentRoomViewModel); } else { diff --git a/src/platform/web/ui/session/room/MessageComposer.js b/src/platform/web/ui/session/room/MessageComposer.js index 9c67fa9f..6ce8148f 100644 --- a/src/platform/web/ui/session/room/MessageComposer.js +++ b/src/platform/web/ui/session/room/MessageComposer.js @@ -17,11 +17,11 @@ limitations under the License. import {TemplateView} from "../../general/TemplateView"; import {Popup} from "../../general/Popup.js"; import {Menu} from "../../general/Menu.js"; -import {viewClassForEntry} from "./common" export class MessageComposer extends TemplateView { - constructor(viewModel) { + constructor(viewModel, viewClassForTile) { super(viewModel); + this._viewClassForTile = viewClassForTile; this._input = null; this._attachmentPopup = null; this._focusInput = null; @@ -45,8 +45,8 @@ export class MessageComposer extends TemplateView { this._focusInput = () => this._input.focus(); this.value.on("focus", this._focusInput); const replyPreview = t.map(vm => vm.replyViewModel, (rvm, t) => { - const View = rvm && viewClassForEntry(rvm); - if (!View) { return null; } + const TileView = rvm && this._viewClassForTile(rvm); + if (!TileView) { return null; } return t.div({ className: "MessageComposer_replyPreview" }, [ @@ -55,8 +55,8 @@ export class MessageComposer extends TemplateView { className: "cancel", onClick: () => this._clearReplyingTo() }, "Close"), - t.view(new View(rvm, { interactive: false }, "div")) - ]) + t.view(new TileView(rvm, this._viewClassForTile, { interactive: false }, "div")) + ]); }); const input = t.div({className: "MessageComposer_input"}, [ this._input, diff --git a/src/platform/web/ui/session/room/RoomView.js b/src/platform/web/ui/session/room/RoomView.js index 2190f1f1..76e26eab 100644 --- a/src/platform/web/ui/session/room/RoomView.js +++ b/src/platform/web/ui/session/room/RoomView.js @@ -23,18 +23,18 @@ import {TimelineLoadingView} from "./TimelineLoadingView.js"; import {MessageComposer} from "./MessageComposer.js"; import {RoomArchivedView} from "./RoomArchivedView.js"; import {AvatarView} from "../../AvatarView.js"; -import {viewClassForEntry} from "./common"; export class RoomView extends TemplateView { - constructor(options) { - super(options); + constructor(vm, viewClassForTile) { + super(vm); + this._viewClassForTile = viewClassForTile; this._optionsPopup = null; } render(t, vm) { let bottomView; if (vm.composerViewModel.kind === "composer") { - bottomView = new MessageComposer(vm.composerViewModel); + bottomView = new MessageComposer(vm.composerViewModel, this._viewClassForTile); } else if (vm.composerViewModel.kind === "archived") { bottomView = new RoomArchivedView(vm.composerViewModel); } @@ -55,7 +55,7 @@ export class RoomView extends TemplateView { t.div({className: "RoomView_error"}, vm => vm.error), t.mapView(vm => vm.timelineViewModel, timelineViewModel => { return timelineViewModel ? - new TimelineView(timelineViewModel, viewClassForEntry) : + new TimelineView(timelineViewModel, this._viewClassForTile) : new TimelineLoadingView(vm); // vm is just needed for i18n }), t.view(bottomView), diff --git a/src/platform/web/ui/session/room/TimelineView.ts b/src/platform/web/ui/session/room/TimelineView.ts index 0c893847..5a04991f 100644 --- a/src/platform/web/ui/session/room/TimelineView.ts +++ b/src/platform/web/ui/session/room/TimelineView.ts @@ -28,7 +28,11 @@ export interface TileView extends IView { readonly value: SimpleTile; onClick(event: UIEvent); } -export type TileViewConstructor = new (tile: SimpleTile) => TileView; +export type TileViewConstructor = new ( + tile: SimpleTile, + viewClassForTile: ViewClassForEntryFn, + renderFlags?: { reply?: boolean, interactive?: boolean } +) => TileView; export type ViewClassForEntryFn = (tile: SimpleTile) => TileViewConstructor; //import {TimelineViewModel} from "../../../../../domain/session/room/timeline/TimelineViewModel.js"; @@ -61,7 +65,7 @@ export class TimelineView extends TemplateView { private tilesView?: TilesListView; private resizeObserver?: ResizeObserver; - constructor(vm: TimelineViewModel, private readonly viewClassForEntry: ViewClassForEntryFn) { + constructor(vm: TimelineViewModel, private readonly viewClassForTile: ViewClassForEntryFn) { super(vm); } @@ -71,7 +75,7 @@ export class TimelineView extends TemplateView { // do initial scroll positioning this.restoreScrollPosition(); }); - this.tilesView = new TilesListView(vm.tiles, () => this.restoreScrollPosition(), this.viewClassForEntry); + this.tilesView = new TilesListView(vm.tiles, () => this.restoreScrollPosition(), this.viewClassForTile); const root = t.div({className: "Timeline"}, [ t.div({ className: "Timeline_scroller bottom-aligned-scroll", @@ -184,13 +188,13 @@ class TilesListView extends ListView { private onChanged: () => void; - constructor(tiles: ObservableList, onChanged: () => void, private readonly viewClassForEntry: ViewClassForEntryFn) { + constructor(tiles: ObservableList, onChanged: () => void, private readonly viewClassForTile: ViewClassForEntryFn) { super({ list: tiles, onItemClick: (tileView, evt) => tileView.onClick(evt), - }, entry => { - const View = viewClassForEntry(entry); - return new View(entry); + }, tile => { + const TileView = viewClassForTile(tile); + return new TileView(tile, viewClassForTile); }); this.onChanged = onChanged; } @@ -202,7 +206,7 @@ class TilesListView extends ListView { onUpdate(index: number, value: SimpleTile, param: any) { if (param === "shape") { - const ExpectedClass = this.viewClassForEntry(value); + const ExpectedClass = this.viewClassForTile(value); const child = this.getChildInstanceByIndex(index); if (!ExpectedClass || !(child instanceof ExpectedClass)) { // shape was updated, so we need to recreate the tile view, diff --git a/src/platform/web/ui/session/room/common.ts b/src/platform/web/ui/session/room/common.ts index 201f14d0..7b62630f 100644 --- a/src/platform/web/ui/session/room/common.ts +++ b/src/platform/web/ui/session/room/common.ts @@ -26,7 +26,7 @@ import {SimpleTile} from "../../../../../domain/session/room/timeline/tiles/Simp import {GapView} from "./timeline/GapView.js"; import type {TileViewConstructor, ViewClassForEntryFn} from "./TimelineView"; -export function viewClassForEntry(vm: SimpleTile): TileViewConstructor { +export function viewClassForTile(vm: SimpleTile): TileViewConstructor { switch (vm.shape) { case "gap": return GapView; diff --git a/src/platform/web/ui/session/room/timeline/AnnouncementView.js b/src/platform/web/ui/session/room/timeline/AnnouncementView.js index 268bf0fa..5ae92daa 100644 --- a/src/platform/web/ui/session/room/timeline/AnnouncementView.js +++ b/src/platform/web/ui/session/room/timeline/AnnouncementView.js @@ -17,6 +17,11 @@ limitations under the License. import {TemplateView} from "../../../general/TemplateView"; export class AnnouncementView extends TemplateView { + // ignore other arguments + constructor(vm) { + super(vm); + } + render(t) { return t.li({className: "AnnouncementView"}, t.div(vm => vm.announcement)); } diff --git a/src/platform/web/ui/session/room/timeline/BaseMessageView.js b/src/platform/web/ui/session/room/timeline/BaseMessageView.js index 7356cd2b..74b96ecf 100644 --- a/src/platform/web/ui/session/room/timeline/BaseMessageView.js +++ b/src/platform/web/ui/session/room/timeline/BaseMessageView.js @@ -24,10 +24,11 @@ import {Menu} from "../../../general/Menu.js"; import {ReactionsView} from "./ReactionsView.js"; export class BaseMessageView extends TemplateView { - constructor(value, renderFlags, tagName = "li") { + constructor(value, viewClassForTile, renderFlags, tagName = "li") { super(value); this._menuPopup = null; this._tagName = tagName; + this._viewClassForTile = viewClassForTile; // TODO An enum could be nice to make code easier to read at call sites. this._renderFlags = renderFlags; } diff --git a/src/platform/web/ui/session/room/timeline/GapView.js b/src/platform/web/ui/session/room/timeline/GapView.js index 2d3bd6e8..db6cda59 100644 --- a/src/platform/web/ui/session/room/timeline/GapView.js +++ b/src/platform/web/ui/session/room/timeline/GapView.js @@ -18,6 +18,11 @@ import {TemplateView} from "../../../general/TemplateView"; import {spinner} from "../../../common.js"; export class GapView extends TemplateView { + // ignore other argument + constructor(vm) { + super(vm); + } + render(t) { const className = { GapView: true, diff --git a/src/platform/web/ui/session/room/timeline/ReplyPreviewView.js b/src/platform/web/ui/session/room/timeline/ReplyPreviewView.js index 3c52fc71..219e4357 100644 --- a/src/platform/web/ui/session/room/timeline/ReplyPreviewView.js +++ b/src/platform/web/ui/session/room/timeline/ReplyPreviewView.js @@ -16,15 +16,18 @@ limitations under the License. import {renderStaticAvatar} from "../../../avatar"; import {TemplateView} from "../../../general/TemplateView"; -import {viewClassForEntry} from "../common"; export class ReplyPreviewView extends TemplateView { + constructor(vm, viewClassForTile) { + super(vm); + this._viewClassForTile = viewClassForTile; + } render(t, vm) { - const viewClass = viewClassForEntry(vm); - if (!viewClass) { + const TileView = this._viewClassForTile(vm); + if (!TileView) { throw new Error(`Shape ${vm.shape} is unrecognized.`) } - const view = new viewClass(vm, { reply: true, interactive: false }); + const view = new TileView(vm, this._viewClassForTile, { reply: true, interactive: false }); return t.div( { className: "ReplyPreviewView" }, t.blockquote([ diff --git a/src/platform/web/ui/session/room/timeline/TextMessageView.js b/src/platform/web/ui/session/room/timeline/TextMessageView.js index c0c0cfb0..8d6cb4dc 100644 --- a/src/platform/web/ui/session/room/timeline/TextMessageView.js +++ b/src/platform/web/ui/session/room/timeline/TextMessageView.js @@ -35,7 +35,7 @@ export class TextMessageView extends BaseMessageView { return new ReplyPreviewError(); } else if (replyTile) { - return new ReplyPreviewView(replyTile); + return new ReplyPreviewView(replyTile, this._viewClassForTile); } else { return null;