From cda96a35eebe2158d4d9627281fd00ebedc66715 Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Fri, 8 Apr 2022 15:01:06 +0200 Subject: [PATCH 1/6] rename viewClassForEntry to viewClassForTile --- src/lib.ts | 2 +- src/platform/web/ui/session/room/MessageComposer.js | 4 ++-- src/platform/web/ui/session/room/RoomView.js | 4 ++-- src/platform/web/ui/session/room/TimelineView.ts | 10 +++++----- src/platform/web/ui/session/room/common.ts | 2 +- .../web/ui/session/room/timeline/ReplyPreviewView.js | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) 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/room/MessageComposer.js b/src/platform/web/ui/session/room/MessageComposer.js index 9c67fa9f..7f822d37 100644 --- a/src/platform/web/ui/session/room/MessageComposer.js +++ b/src/platform/web/ui/session/room/MessageComposer.js @@ -17,7 +17,7 @@ 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" +import {viewClassForTile} from "./common" export class MessageComposer extends TemplateView { constructor(viewModel) { @@ -45,7 +45,7 @@ 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); + const View = rvm && viewClassForTile(rvm); if (!View) { return null; } return t.div({ className: "MessageComposer_replyPreview" diff --git a/src/platform/web/ui/session/room/RoomView.js b/src/platform/web/ui/session/room/RoomView.js index 2190f1f1..961be704 100644 --- a/src/platform/web/ui/session/room/RoomView.js +++ b/src/platform/web/ui/session/room/RoomView.js @@ -23,7 +23,7 @@ import {TimelineLoadingView} from "./TimelineLoadingView.js"; import {MessageComposer} from "./MessageComposer.js"; import {RoomArchivedView} from "./RoomArchivedView.js"; import {AvatarView} from "../../AvatarView.js"; -import {viewClassForEntry} from "./common"; +import {viewClassForTile} from "./common"; export class RoomView extends TemplateView { constructor(options) { @@ -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, 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..6dd52466 100644 --- a/src/platform/web/ui/session/room/TimelineView.ts +++ b/src/platform/web/ui/session/room/TimelineView.ts @@ -61,7 +61,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 +71,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,12 +184,12 @@ 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); + const View = viewClassForTile(entry); return new View(entry); }); this.onChanged = onChanged; @@ -202,7 +202,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/ReplyPreviewView.js b/src/platform/web/ui/session/room/timeline/ReplyPreviewView.js index 3c52fc71..bddcc8fe 100644 --- a/src/platform/web/ui/session/room/timeline/ReplyPreviewView.js +++ b/src/platform/web/ui/session/room/timeline/ReplyPreviewView.js @@ -16,11 +16,11 @@ limitations under the License. import {renderStaticAvatar} from "../../../avatar"; import {TemplateView} from "../../../general/TemplateView"; -import {viewClassForEntry} from "../common"; +import {viewClassForTile} from "../common"; export class ReplyPreviewView extends TemplateView { render(t, vm) { - const viewClass = viewClassForEntry(vm); + const viewClass = viewClassForTile(vm); if (!viewClass) { throw new Error(`Shape ${vm.shape} is unrecognized.`) } From 57f50cc4160a7e35b4cb2df1c9299166fff6cf53 Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Fri, 8 Apr 2022 15:01:27 +0200 Subject: [PATCH 2/6] fix lint warnings --- src/domain/session/room/timeline/TilesCollection.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 = []; From 1f0cb542c88e53831424155f282b01315aef88e4 Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Fri, 8 Apr 2022 15:02:07 +0200 Subject: [PATCH 3/6] pass viewClassForTile to tile views, so they can create reply view with correct subtile --- src/platform/web/ui/session/room/TimelineView.ts | 12 ++++++++---- .../web/ui/session/room/timeline/BaseMessageView.js | 3 ++- .../web/ui/session/room/timeline/ReplyPreviewView.js | 11 +++++++---- .../web/ui/session/room/timeline/TextMessageView.js | 2 +- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/platform/web/ui/session/room/TimelineView.ts b/src/platform/web/ui/session/room/TimelineView.ts index 6dd52466..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"; @@ -188,9 +192,9 @@ class TilesListView extends ListView { super({ list: tiles, onItemClick: (tileView, evt) => tileView.onClick(evt), - }, entry => { - const View = viewClassForTile(entry); - return new View(entry); + }, tile => { + const TileView = viewClassForTile(tile); + return new TileView(tile, viewClassForTile); }); this.onChanged = onChanged; } 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/ReplyPreviewView.js b/src/platform/web/ui/session/room/timeline/ReplyPreviewView.js index bddcc8fe..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 {viewClassForTile} from "../common"; export class ReplyPreviewView extends TemplateView { + constructor(vm, viewClassForTile) { + super(vm); + this._viewClassForTile = viewClassForTile; + } render(t, vm) { - const viewClass = viewClassForTile(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; From 1fea14dd10a1b830cc9be35d0077c478b2cb5a5e Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Fri, 8 Apr 2022 15:04:38 +0200 Subject: [PATCH 4/6] ensure other parameters don't get passed to TemplateView parent ctors --- .../web/ui/session/room/timeline/AnnouncementView.js | 5 +++++ src/platform/web/ui/session/room/timeline/GapView.js | 5 +++++ 2 files changed, 10 insertions(+) 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/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, From d21d10e4f25afa82c7ff41658d6fc7f79e8b42fa Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Fri, 8 Apr 2022 15:15:21 +0200 Subject: [PATCH 5/6] pass in viewClassForTile from SessionView so you can also use custom tiles when using the grid view --- src/platform/web/ui/session/RoomGridView.js | 7 ++++++- src/platform/web/ui/session/SessionView.js | 5 +++-- src/platform/web/ui/session/room/RoomView.js | 8 ++++---- 3 files changed, 13 insertions(+), 7 deletions(-) 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/RoomView.js b/src/platform/web/ui/session/room/RoomView.js index 961be704..0bc85e83 100644 --- a/src/platform/web/ui/session/room/RoomView.js +++ b/src/platform/web/ui/session/room/RoomView.js @@ -23,11 +23,11 @@ import {TimelineLoadingView} from "./TimelineLoadingView.js"; import {MessageComposer} from "./MessageComposer.js"; import {RoomArchivedView} from "./RoomArchivedView.js"; import {AvatarView} from "../../AvatarView.js"; -import {viewClassForTile} from "./common"; export class RoomView extends TemplateView { - constructor(options) { - super(options); + constructor(vm, viewClassForTile) { + super(vm); + this._viewClassForTile = viewClassForTile; this._optionsPopup = null; } @@ -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, viewClassForTile) : + new TimelineView(timelineViewModel, this._viewClassForTile) : new TimelineLoadingView(vm); // vm is just needed for i18n }), t.view(bottomView), From cf780ce259bdcf7a6c74b759bc513a0f3f45e8c3 Mon Sep 17 00:00:00 2001 From: Bruno Windels <274386+bwindels@users.noreply.github.com> Date: Fri, 8 Apr 2022 15:16:22 +0200 Subject: [PATCH 6/6] also apply custom tiles in reply preview in composer --- src/platform/web/ui/session/room/MessageComposer.js | 12 ++++++------ src/platform/web/ui/session/room/RoomView.js | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/platform/web/ui/session/room/MessageComposer.js b/src/platform/web/ui/session/room/MessageComposer.js index 7f822d37..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 {viewClassForTile} 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 && viewClassForTile(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 0bc85e83..76e26eab 100644 --- a/src/platform/web/ui/session/room/RoomView.js +++ b/src/platform/web/ui/session/room/RoomView.js @@ -34,7 +34,7 @@ export class RoomView extends TemplateView { 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); }