Merge pull request #715 from vector-im/bwindels/rename-viewclassfortile
Some timeline refactoring and also make reply tiles of correct custom view class
This commit is contained in:
commit
4cbd149c25
13 changed files with 56 additions and 32 deletions
|
@ -311,7 +311,7 @@ export function tests() {
|
||||||
}
|
}
|
||||||
const entries = new ObservableArray([{n: 5}, {n: 10}]);
|
const entries = new ObservableArray([{n: 5}, {n: 10}]);
|
||||||
const tileOptions = {
|
const tileOptions = {
|
||||||
tileClassForEntry: entry => UpdateOnSiblingTile,
|
tileClassForEntry: () => UpdateOnSiblingTile,
|
||||||
};
|
};
|
||||||
const tiles = new TilesCollection(entries, tileOptions);
|
const tiles = new TilesCollection(entries, tileOptions);
|
||||||
let receivedAdd = false;
|
let receivedAdd = false;
|
||||||
|
@ -337,7 +337,7 @@ export function tests() {
|
||||||
}
|
}
|
||||||
const entries = new ObservableArray([{n: 5}, {n: 10}, {n: 15}]);
|
const entries = new ObservableArray([{n: 5}, {n: 10}, {n: 15}]);
|
||||||
const tileOptions = {
|
const tileOptions = {
|
||||||
tileClassForEntry: entry => UpdateOnSiblingTile,
|
tileClassForEntry: () => UpdateOnSiblingTile,
|
||||||
};
|
};
|
||||||
const tiles = new TilesCollection(entries, tileOptions);
|
const tiles = new TilesCollection(entries, tileOptions);
|
||||||
const events = [];
|
const events = [];
|
||||||
|
|
|
@ -44,7 +44,7 @@ export {MissingAttachmentTile} from "./domain/session/room/timeline/tiles/Missin
|
||||||
export {SimpleTile} from "./domain/session/room/timeline/tiles/SimpleTile.js";
|
export {SimpleTile} from "./domain/session/room/timeline/tiles/SimpleTile.js";
|
||||||
|
|
||||||
export {TimelineView} from "./platform/web/ui/session/room/TimelineView";
|
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 type {TileViewConstructor, ViewClassForEntryFn} from "./platform/web/ui/session/room/TimelineView";
|
||||||
// export timeline tile views
|
// export timeline tile views
|
||||||
export {AnnouncementView} from "./platform/web/ui/session/room/timeline/AnnouncementView.js";
|
export {AnnouncementView} from "./platform/web/ui/session/room/timeline/AnnouncementView.js";
|
||||||
|
|
|
@ -21,6 +21,11 @@ import {TemplateView} from "../general/TemplateView";
|
||||||
import {StaticView} from "../general/StaticView.js";
|
import {StaticView} from "../general/StaticView.js";
|
||||||
|
|
||||||
export class RoomGridView extends TemplateView {
|
export class RoomGridView extends TemplateView {
|
||||||
|
constructor(vm, viewClassForTile) {
|
||||||
|
super(vm);
|
||||||
|
this._viewClassForTile = viewClassForTile;
|
||||||
|
}
|
||||||
|
|
||||||
render(t, vm) {
|
render(t, vm) {
|
||||||
const children = [];
|
const children = [];
|
||||||
for (let i = 0; i < (vm.height * vm.width); i+=1) {
|
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") {
|
} else if (roomVM.kind === "invite") {
|
||||||
return new InviteView(roomVM);
|
return new InviteView(roomVM);
|
||||||
} else {
|
} else {
|
||||||
return new RoomView(roomVM);
|
return new RoomView(roomVM, this._viewClassForTile);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return new StaticView(t => t.div({className: "room-placeholder"}, [
|
return new StaticView(t => t.div({className: "room-placeholder"}, [
|
||||||
|
|
|
@ -28,6 +28,7 @@ import {RoomGridView} from "./RoomGridView.js";
|
||||||
import {SettingsView} from "./settings/SettingsView.js";
|
import {SettingsView} from "./settings/SettingsView.js";
|
||||||
import {CreateRoomView} from "./CreateRoomView.js";
|
import {CreateRoomView} from "./CreateRoomView.js";
|
||||||
import {RightPanelView} from "./rightpanel/RightPanelView.js";
|
import {RightPanelView} from "./rightpanel/RightPanelView.js";
|
||||||
|
import {viewClassForTile} from "./room/common";
|
||||||
|
|
||||||
export class SessionView extends TemplateView {
|
export class SessionView extends TemplateView {
|
||||||
render(t, vm) {
|
render(t, vm) {
|
||||||
|
@ -42,7 +43,7 @@ export class SessionView extends TemplateView {
|
||||||
t.view(new LeftPanelView(vm.leftPanelViewModel)),
|
t.view(new LeftPanelView(vm.leftPanelViewModel)),
|
||||||
t.mapView(vm => vm.activeMiddleViewModel, () => {
|
t.mapView(vm => vm.activeMiddleViewModel, () => {
|
||||||
if (vm.roomGridViewModel) {
|
if (vm.roomGridViewModel) {
|
||||||
return new RoomGridView(vm.roomGridViewModel);
|
return new RoomGridView(vm.roomGridViewModel, viewClassForTile);
|
||||||
} else if (vm.settingsViewModel) {
|
} else if (vm.settingsViewModel) {
|
||||||
return new SettingsView(vm.settingsViewModel);
|
return new SettingsView(vm.settingsViewModel);
|
||||||
} else if (vm.createRoomViewModel) {
|
} else if (vm.createRoomViewModel) {
|
||||||
|
@ -51,7 +52,7 @@ export class SessionView extends TemplateView {
|
||||||
if (vm.currentRoomViewModel.kind === "invite") {
|
if (vm.currentRoomViewModel.kind === "invite") {
|
||||||
return new InviteView(vm.currentRoomViewModel);
|
return new InviteView(vm.currentRoomViewModel);
|
||||||
} else if (vm.currentRoomViewModel.kind === "room") {
|
} else if (vm.currentRoomViewModel.kind === "room") {
|
||||||
return new RoomView(vm.currentRoomViewModel);
|
return new RoomView(vm.currentRoomViewModel, viewClassForTile);
|
||||||
} else if (vm.currentRoomViewModel.kind === "roomBeingCreated") {
|
} else if (vm.currentRoomViewModel.kind === "roomBeingCreated") {
|
||||||
return new RoomBeingCreatedView(vm.currentRoomViewModel);
|
return new RoomBeingCreatedView(vm.currentRoomViewModel);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -17,11 +17,11 @@ limitations under the License.
|
||||||
import {TemplateView} from "../../general/TemplateView";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {Popup} from "../../general/Popup.js";
|
import {Popup} from "../../general/Popup.js";
|
||||||
import {Menu} from "../../general/Menu.js";
|
import {Menu} from "../../general/Menu.js";
|
||||||
import {viewClassForEntry} from "./common"
|
|
||||||
|
|
||||||
export class MessageComposer extends TemplateView {
|
export class MessageComposer extends TemplateView {
|
||||||
constructor(viewModel) {
|
constructor(viewModel, viewClassForTile) {
|
||||||
super(viewModel);
|
super(viewModel);
|
||||||
|
this._viewClassForTile = viewClassForTile;
|
||||||
this._input = null;
|
this._input = null;
|
||||||
this._attachmentPopup = null;
|
this._attachmentPopup = null;
|
||||||
this._focusInput = null;
|
this._focusInput = null;
|
||||||
|
@ -45,8 +45,8 @@ export class MessageComposer extends TemplateView {
|
||||||
this._focusInput = () => this._input.focus();
|
this._focusInput = () => this._input.focus();
|
||||||
this.value.on("focus", this._focusInput);
|
this.value.on("focus", this._focusInput);
|
||||||
const replyPreview = t.map(vm => vm.replyViewModel, (rvm, t) => {
|
const replyPreview = t.map(vm => vm.replyViewModel, (rvm, t) => {
|
||||||
const View = rvm && viewClassForEntry(rvm);
|
const TileView = rvm && this._viewClassForTile(rvm);
|
||||||
if (!View) { return null; }
|
if (!TileView) { return null; }
|
||||||
return t.div({
|
return t.div({
|
||||||
className: "MessageComposer_replyPreview"
|
className: "MessageComposer_replyPreview"
|
||||||
}, [
|
}, [
|
||||||
|
@ -55,8 +55,8 @@ export class MessageComposer extends TemplateView {
|
||||||
className: "cancel",
|
className: "cancel",
|
||||||
onClick: () => this._clearReplyingTo()
|
onClick: () => this._clearReplyingTo()
|
||||||
}, "Close"),
|
}, "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"}, [
|
const input = t.div({className: "MessageComposer_input"}, [
|
||||||
this._input,
|
this._input,
|
||||||
|
|
|
@ -23,18 +23,18 @@ import {TimelineLoadingView} from "./TimelineLoadingView.js";
|
||||||
import {MessageComposer} from "./MessageComposer.js";
|
import {MessageComposer} from "./MessageComposer.js";
|
||||||
import {RoomArchivedView} from "./RoomArchivedView.js";
|
import {RoomArchivedView} from "./RoomArchivedView.js";
|
||||||
import {AvatarView} from "../../AvatarView.js";
|
import {AvatarView} from "../../AvatarView.js";
|
||||||
import {viewClassForEntry} from "./common";
|
|
||||||
|
|
||||||
export class RoomView extends TemplateView {
|
export class RoomView extends TemplateView {
|
||||||
constructor(options) {
|
constructor(vm, viewClassForTile) {
|
||||||
super(options);
|
super(vm);
|
||||||
|
this._viewClassForTile = viewClassForTile;
|
||||||
this._optionsPopup = null;
|
this._optionsPopup = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
render(t, vm) {
|
render(t, vm) {
|
||||||
let bottomView;
|
let bottomView;
|
||||||
if (vm.composerViewModel.kind === "composer") {
|
if (vm.composerViewModel.kind === "composer") {
|
||||||
bottomView = new MessageComposer(vm.composerViewModel);
|
bottomView = new MessageComposer(vm.composerViewModel, this._viewClassForTile);
|
||||||
} else if (vm.composerViewModel.kind === "archived") {
|
} else if (vm.composerViewModel.kind === "archived") {
|
||||||
bottomView = new RoomArchivedView(vm.composerViewModel);
|
bottomView = new RoomArchivedView(vm.composerViewModel);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ export class RoomView extends TemplateView {
|
||||||
t.div({className: "RoomView_error"}, vm => vm.error),
|
t.div({className: "RoomView_error"}, vm => vm.error),
|
||||||
t.mapView(vm => vm.timelineViewModel, timelineViewModel => {
|
t.mapView(vm => vm.timelineViewModel, timelineViewModel => {
|
||||||
return timelineViewModel ?
|
return timelineViewModel ?
|
||||||
new TimelineView(timelineViewModel, viewClassForEntry) :
|
new TimelineView(timelineViewModel, this._viewClassForTile) :
|
||||||
new TimelineLoadingView(vm); // vm is just needed for i18n
|
new TimelineLoadingView(vm); // vm is just needed for i18n
|
||||||
}),
|
}),
|
||||||
t.view(bottomView),
|
t.view(bottomView),
|
||||||
|
|
|
@ -28,7 +28,11 @@ export interface TileView extends IView {
|
||||||
readonly value: SimpleTile;
|
readonly value: SimpleTile;
|
||||||
onClick(event: UIEvent);
|
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;
|
export type ViewClassForEntryFn = (tile: SimpleTile) => TileViewConstructor;
|
||||||
|
|
||||||
//import {TimelineViewModel} from "../../../../../domain/session/room/timeline/TimelineViewModel.js";
|
//import {TimelineViewModel} from "../../../../../domain/session/room/timeline/TimelineViewModel.js";
|
||||||
|
@ -61,7 +65,7 @@ export class TimelineView extends TemplateView<TimelineViewModel> {
|
||||||
private tilesView?: TilesListView;
|
private tilesView?: TilesListView;
|
||||||
private resizeObserver?: ResizeObserver;
|
private resizeObserver?: ResizeObserver;
|
||||||
|
|
||||||
constructor(vm: TimelineViewModel, private readonly viewClassForEntry: ViewClassForEntryFn) {
|
constructor(vm: TimelineViewModel, private readonly viewClassForTile: ViewClassForEntryFn) {
|
||||||
super(vm);
|
super(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +75,7 @@ export class TimelineView extends TemplateView<TimelineViewModel> {
|
||||||
// do initial scroll positioning
|
// do initial scroll positioning
|
||||||
this.restoreScrollPosition();
|
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"}, [
|
const root = t.div({className: "Timeline"}, [
|
||||||
t.div({
|
t.div({
|
||||||
className: "Timeline_scroller bottom-aligned-scroll",
|
className: "Timeline_scroller bottom-aligned-scroll",
|
||||||
|
@ -184,13 +188,13 @@ class TilesListView extends ListView<SimpleTile, TileView> {
|
||||||
|
|
||||||
private onChanged: () => void;
|
private onChanged: () => void;
|
||||||
|
|
||||||
constructor(tiles: ObservableList<SimpleTile>, onChanged: () => void, private readonly viewClassForEntry: ViewClassForEntryFn) {
|
constructor(tiles: ObservableList<SimpleTile>, onChanged: () => void, private readonly viewClassForTile: ViewClassForEntryFn) {
|
||||||
super({
|
super({
|
||||||
list: tiles,
|
list: tiles,
|
||||||
onItemClick: (tileView, evt) => tileView.onClick(evt),
|
onItemClick: (tileView, evt) => tileView.onClick(evt),
|
||||||
}, entry => {
|
}, tile => {
|
||||||
const View = viewClassForEntry(entry);
|
const TileView = viewClassForTile(tile);
|
||||||
return new View(entry);
|
return new TileView(tile, viewClassForTile);
|
||||||
});
|
});
|
||||||
this.onChanged = onChanged;
|
this.onChanged = onChanged;
|
||||||
}
|
}
|
||||||
|
@ -202,7 +206,7 @@ class TilesListView extends ListView<SimpleTile, TileView> {
|
||||||
|
|
||||||
onUpdate(index: number, value: SimpleTile, param: any) {
|
onUpdate(index: number, value: SimpleTile, param: any) {
|
||||||
if (param === "shape") {
|
if (param === "shape") {
|
||||||
const ExpectedClass = this.viewClassForEntry(value);
|
const ExpectedClass = this.viewClassForTile(value);
|
||||||
const child = this.getChildInstanceByIndex(index);
|
const child = this.getChildInstanceByIndex(index);
|
||||||
if (!ExpectedClass || !(child instanceof ExpectedClass)) {
|
if (!ExpectedClass || !(child instanceof ExpectedClass)) {
|
||||||
// shape was updated, so we need to recreate the tile view,
|
// shape was updated, so we need to recreate the tile view,
|
||||||
|
|
|
@ -26,7 +26,7 @@ import {SimpleTile} from "../../../../../domain/session/room/timeline/tiles/Simp
|
||||||
import {GapView} from "./timeline/GapView.js";
|
import {GapView} from "./timeline/GapView.js";
|
||||||
import type {TileViewConstructor, ViewClassForEntryFn} from "./TimelineView";
|
import type {TileViewConstructor, ViewClassForEntryFn} from "./TimelineView";
|
||||||
|
|
||||||
export function viewClassForEntry(vm: SimpleTile): TileViewConstructor {
|
export function viewClassForTile(vm: SimpleTile): TileViewConstructor {
|
||||||
switch (vm.shape) {
|
switch (vm.shape) {
|
||||||
case "gap":
|
case "gap":
|
||||||
return GapView;
|
return GapView;
|
||||||
|
|
|
@ -17,6 +17,11 @@ limitations under the License.
|
||||||
import {TemplateView} from "../../../general/TemplateView";
|
import {TemplateView} from "../../../general/TemplateView";
|
||||||
|
|
||||||
export class AnnouncementView extends TemplateView {
|
export class AnnouncementView extends TemplateView {
|
||||||
|
// ignore other arguments
|
||||||
|
constructor(vm) {
|
||||||
|
super(vm);
|
||||||
|
}
|
||||||
|
|
||||||
render(t) {
|
render(t) {
|
||||||
return t.li({className: "AnnouncementView"}, t.div(vm => vm.announcement));
|
return t.li({className: "AnnouncementView"}, t.div(vm => vm.announcement));
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,11 @@ import {Menu} from "../../../general/Menu.js";
|
||||||
import {ReactionsView} from "./ReactionsView.js";
|
import {ReactionsView} from "./ReactionsView.js";
|
||||||
|
|
||||||
export class BaseMessageView extends TemplateView {
|
export class BaseMessageView extends TemplateView {
|
||||||
constructor(value, renderFlags, tagName = "li") {
|
constructor(value, viewClassForTile, renderFlags, tagName = "li") {
|
||||||
super(value);
|
super(value);
|
||||||
this._menuPopup = null;
|
this._menuPopup = null;
|
||||||
this._tagName = tagName;
|
this._tagName = tagName;
|
||||||
|
this._viewClassForTile = viewClassForTile;
|
||||||
// TODO An enum could be nice to make code easier to read at call sites.
|
// TODO An enum could be nice to make code easier to read at call sites.
|
||||||
this._renderFlags = renderFlags;
|
this._renderFlags = renderFlags;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,11 @@ import {TemplateView} from "../../../general/TemplateView";
|
||||||
import {spinner} from "../../../common.js";
|
import {spinner} from "../../../common.js";
|
||||||
|
|
||||||
export class GapView extends TemplateView {
|
export class GapView extends TemplateView {
|
||||||
|
// ignore other argument
|
||||||
|
constructor(vm) {
|
||||||
|
super(vm);
|
||||||
|
}
|
||||||
|
|
||||||
render(t) {
|
render(t) {
|
||||||
const className = {
|
const className = {
|
||||||
GapView: true,
|
GapView: true,
|
||||||
|
|
|
@ -16,15 +16,18 @@ limitations under the License.
|
||||||
|
|
||||||
import {renderStaticAvatar} from "../../../avatar";
|
import {renderStaticAvatar} from "../../../avatar";
|
||||||
import {TemplateView} from "../../../general/TemplateView";
|
import {TemplateView} from "../../../general/TemplateView";
|
||||||
import {viewClassForEntry} from "../common";
|
|
||||||
|
|
||||||
export class ReplyPreviewView extends TemplateView {
|
export class ReplyPreviewView extends TemplateView {
|
||||||
|
constructor(vm, viewClassForTile) {
|
||||||
|
super(vm);
|
||||||
|
this._viewClassForTile = viewClassForTile;
|
||||||
|
}
|
||||||
render(t, vm) {
|
render(t, vm) {
|
||||||
const viewClass = viewClassForEntry(vm);
|
const TileView = this._viewClassForTile(vm);
|
||||||
if (!viewClass) {
|
if (!TileView) {
|
||||||
throw new Error(`Shape ${vm.shape} is unrecognized.`)
|
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(
|
return t.div(
|
||||||
{ className: "ReplyPreviewView" },
|
{ className: "ReplyPreviewView" },
|
||||||
t.blockquote([
|
t.blockquote([
|
||||||
|
|
|
@ -35,7 +35,7 @@ export class TextMessageView extends BaseMessageView {
|
||||||
return new ReplyPreviewError();
|
return new ReplyPreviewError();
|
||||||
}
|
}
|
||||||
else if (replyTile) {
|
else if (replyTile) {
|
||||||
return new ReplyPreviewView(replyTile);
|
return new ReplyPreviewView(replyTile, this._viewClassForTile);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return null;
|
return null;
|
||||||
|
|
Reference in a new issue