diff --git a/src/domain/session/room/timeline/tiles/RoomMemberTile.js b/src/domain/session/room/timeline/tiles/RoomMemberTile.js index cc9796e9..e3492882 100644 --- a/src/domain/session/room/timeline/tiles/RoomMemberTile.js +++ b/src/domain/session/room/timeline/tiles/RoomMemberTile.js @@ -6,7 +6,7 @@ export default class RoomNameTile extends SimpleTile { return "announcement"; } - get label() { + get announcement() { const event = this._entry.event; const content = event.content; return `${event.sender} changed membership to ${content.membership}`; diff --git a/src/domain/session/room/timeline/tiles/RoomNameTile.js b/src/domain/session/room/timeline/tiles/RoomNameTile.js index f415efe6..32cd5adf 100644 --- a/src/domain/session/room/timeline/tiles/RoomNameTile.js +++ b/src/domain/session/room/timeline/tiles/RoomNameTile.js @@ -6,9 +6,9 @@ export default class RoomNameTile extends SimpleTile { return "announcement"; } - get label() { + get announcement() { const event = this._entry.event; const content = event.content; - return `${event.sender} changed the room name to "${content.name}"` + return `${event.sender} named the room "${content.name}"` } } diff --git a/src/domain/session/room/timeline/tilesCreator.js b/src/domain/session/room/timeline/tilesCreator.js index 60ce42ed..71009576 100644 --- a/src/domain/session/room/timeline/tilesCreator.js +++ b/src/domain/session/room/timeline/tilesCreator.js @@ -22,7 +22,8 @@ export default function ({timeline}) { case "m.emote": return new TextTile(options); case "m.image": - return new ImageTile(options); + return null; // not supported yet + // return new ImageTile(options); case "m.location": return new LocationTile(options); default: diff --git a/src/ui/web/ListView.js b/src/ui/web/general/ListView.js similarity index 100% rename from src/ui/web/ListView.js rename to src/ui/web/general/ListView.js diff --git a/src/ui/web/SwitchView.js b/src/ui/web/general/SwitchView.js similarity index 100% rename from src/ui/web/SwitchView.js rename to src/ui/web/general/SwitchView.js diff --git a/src/ui/web/Template.js b/src/ui/web/general/Template.js similarity index 100% rename from src/ui/web/Template.js rename to src/ui/web/general/Template.js diff --git a/src/ui/web/TemplateView.js b/src/ui/web/general/TemplateView.js similarity index 56% rename from src/ui/web/TemplateView.js rename to src/ui/web/general/TemplateView.js index 96d1a500..a697a9a7 100644 --- a/src/ui/web/TemplateView.js +++ b/src/ui/web/general/TemplateView.js @@ -1,8 +1,9 @@ import Template from "./Template.js"; export default class TemplateView { - constructor(value) { - this.viewModel = value; + constructor(vm, bindToChangeEvent) { + this.viewModel = vm; + this._changeEventHandler = bindToChangeEvent ? this.update.bind(this, this.viewModel) : null; this._template = null; } @@ -11,6 +12,9 @@ export default class TemplateView { } mount() { + if (this._changeEventHandler) { + this.viewModel.on("change", this._changeEventHandler); + } this._template = new Template(this.viewModel, (t, value) => this.render(t, value)); return this.root(); } @@ -20,6 +24,9 @@ export default class TemplateView { } unmount() { + if (this._changeEventHandler) { + this.viewModel.off("change", this._changeEventHandler); + } this._template.dispose(); this._template = null; } diff --git a/src/ui/web/html.js b/src/ui/web/general/html.js similarity index 94% rename from src/ui/web/html.js rename to src/ui/web/general/html.js index 0816a689..a4f816c9 100644 --- a/src/ui/web/html.js +++ b/src/ui/web/general/html.js @@ -1,6 +1,7 @@ // DOM helper functions export function isChildren(children) { + // children should be an not-object (that's the attributes), or a domnode, or an array return typeof children !== "object" || !!children.nodeType || Array.isArray(children); } diff --git a/src/ui/web/login/LoginView.js b/src/ui/web/login/LoginView.js new file mode 100644 index 00000000..a9ccf667 --- /dev/null +++ b/src/ui/web/login/LoginView.js @@ -0,0 +1,19 @@ +import TemplateView from "./general/TemplateView.js"; + +export default class LoginView extends TemplateView { + render(t, vm) { + const username = t.input({type: "text", placeholder: vm.usernamePlaceholder}); + const password = t.input({type: "password", placeholder: vm.usernamePlaceholder}); + const homeserver = t.input({type: "text", placeholder: vm.hsPlaceholder, value: vm.defaultHS}); + return t.div({className: "login form"}, [ + t.if(vm => vm.error, t => t.div({className: "error"}, vm => vm.error)), + t.div(username), + t.div(password), + t.div(homeserver), + t.div(t.button({ + onClick: () => vm.login(username.value, password.value, homeserver.value), + disabled: vm => vm.isBusy + }, "Log In")) + ]); + } +} diff --git a/src/ui/web/RoomPlaceholderView.js b/src/ui/web/session/RoomPlaceholderView.js similarity index 88% rename from src/ui/web/RoomPlaceholderView.js rename to src/ui/web/session/RoomPlaceholderView.js index eaeadb57..fb5f9b61 100644 --- a/src/ui/web/RoomPlaceholderView.js +++ b/src/ui/web/session/RoomPlaceholderView.js @@ -1,4 +1,4 @@ -import {tag} from "./html.js"; +import {tag} from "../general/html.js"; export default class RoomPlaceholderView { constructor() { diff --git a/src/ui/web/RoomTile.js b/src/ui/web/session/RoomTile.js similarity index 78% rename from src/ui/web/RoomTile.js rename to src/ui/web/session/RoomTile.js index a68049ab..cd880593 100644 --- a/src/ui/web/RoomTile.js +++ b/src/ui/web/session/RoomTile.js @@ -1,4 +1,4 @@ -import TemplateView from "./TemplateView.js"; +import TemplateView from "../general/TemplateView.js"; export default class RoomTile extends TemplateView { render(t) { diff --git a/src/ui/web/SessionView.js b/src/ui/web/session/SessionView.js similarity index 68% rename from src/ui/web/SessionView.js rename to src/ui/web/session/SessionView.js index 12ef2e1c..be19ca30 100644 --- a/src/ui/web/SessionView.js +++ b/src/ui/web/session/SessionView.js @@ -1,9 +1,10 @@ -import ListView from "./ListView.js"; +import ListView from "../general/ListView.js"; import RoomTile from "./RoomTile.js"; -import RoomView from "./RoomView.js"; -import SwitchView from "./SwitchView.js"; +import RoomView from "./room/RoomView.js"; +import SwitchView from "../general/SwitchView.js"; import RoomPlaceholderView from "./RoomPlaceholderView.js"; -import {tag} from "./html.js"; +import SyncStatusBar from "./SyncStatusBar.js"; +import {tag} from "../general/html.js"; export default class SessionView { constructor(viewModel) { @@ -21,8 +22,7 @@ export default class SessionView { mount() { this._viewModel.on("change", this._onViewModelChange); - - this._root = tag.div({className: "SessionView"}); + this._syncStatusBar = new SyncStatusBar(this._viewModel.syncStatusViewModel); this._roomList = new ListView( { list: this._viewModel.roomList, @@ -30,9 +30,16 @@ export default class SessionView { }, (room) => new RoomTile(room) ); - this._root.appendChild(this._roomList.mount()); this._middleSwitcher = new SwitchView(new RoomPlaceholderView()); - this._root.appendChild(this._middleSwitcher.mount()); + + this._root = tag.div({className: "SessionView"}, [ + this._syncStatusBar.mount(), + tag.div({className: "main"}, [ + this._roomList.mount(), + this._middleSwitcher.mount() + ]) + ]); + return this._root; } diff --git a/src/ui/web/session/SyncStatusBar.js b/src/ui/web/session/SyncStatusBar.js new file mode 100644 index 00000000..7630cbe1 --- /dev/null +++ b/src/ui/web/session/SyncStatusBar.js @@ -0,0 +1,16 @@ +import TemplateView from "../general/TemplateView.js"; + +export default class SyncStatusBar extends TemplateView { + constructor(vm) { + super(vm, true); + } + + render(t, vm) { + return t.div({className: { + "SyncStatusBar": true, + }}, [ + vm => vm.status, + t.if(vm => !vm.isSyncing, t => t.button({onClick: () => vm.trySync()}, "Try syncing")) + ]); + } +} diff --git a/src/ui/web/RoomView.js b/src/ui/web/session/room/RoomView.js similarity index 91% rename from src/ui/web/RoomView.js rename to src/ui/web/session/room/RoomView.js index 35523c9f..03f3ddd5 100644 --- a/src/ui/web/RoomView.js +++ b/src/ui/web/session/room/RoomView.js @@ -1,6 +1,6 @@ -import TimelineTile from "./TimelineTile.js"; -import ListView from "./ListView.js"; -import {tag} from "./html.js"; +import TimelineTile from "./timeline/TimelineTile.js"; +import ListView from "../../general/ListView.js"; +import {tag} from "../../general/html.js"; import GapView from "./timeline/GapView.js"; export default class RoomView { diff --git a/src/ui/web/session/room/timeline/AnnoucementView.js b/src/ui/web/session/room/timeline/AnnoucementView.js new file mode 100644 index 00000000..1bb08db0 --- /dev/null +++ b/src/ui/web/session/room/timeline/AnnoucementView.js @@ -0,0 +1,7 @@ +import TemplateView from "../../../general/TemplateView.js"; + +export default class AnnouncementView extends TemplateView { + render(t) { + return t.li({className: "AnnouncementView"}, vm => vm.announcement); + } +} diff --git a/src/ui/web/timeline/GapView.js b/src/ui/web/session/room/timeline/GapView.js similarity index 60% rename from src/ui/web/timeline/GapView.js rename to src/ui/web/session/room/timeline/GapView.js index 258cc167..62cde6a6 100644 --- a/src/ui/web/timeline/GapView.js +++ b/src/ui/web/session/room/timeline/GapView.js @@ -1,14 +1,17 @@ -import TemplateView from "../TemplateView.js"; +import TemplateView from "../../../general/TemplateView.js"; export default class GapView extends TemplateView { render(t, vm) { const className = { - gap: true, + GapView: true, isLoading: vm => vm.isLoading }; const label = (vm.isUp ? "🠝" : "🠟") + " fill gap"; //no binding return t.li({className}, [ - t.button({onClick: () => this.viewModel.fill(), disabled: vm => vm.isLoading}, label), + t.button({ + onClick: () => this.viewModel.fill(), + disabled: vm => vm.isLoading + }, label), t.if(vm => vm.error, t => t.strong(vm => vm.error)) ]); } diff --git a/src/ui/web/session/room/timeline/TextMessageView.js b/src/ui/web/session/room/timeline/TextMessageView.js new file mode 100644 index 00000000..86f2813b --- /dev/null +++ b/src/ui/web/session/room/timeline/TextMessageView.js @@ -0,0 +1,13 @@ +import TemplateView from "../../../general/TemplateView.js"; + +export default class TextMessageView extends TemplateView { + render(t, vm) { + return t.li( + {className: {"TextMessageView": true, own: vm.isOwn}}, + t.div({className: "message-container"}, [ + t.div({className: "sender"}, vm.sender), + t.p([vm.text, t.time(vm.time)]), + ]) + ); + } +} diff --git a/src/ui/web/session/room/timeline/TimelineTile.js b/src/ui/web/session/room/timeline/TimelineTile.js new file mode 100644 index 00000000..4e87f182 --- /dev/null +++ b/src/ui/web/session/room/timeline/TimelineTile.js @@ -0,0 +1,33 @@ +import {tag} from "../../../general/html.js"; + +export default class TimelineTile { + constructor(tileVM) { + this._tileVM = tileVM; + this._root = null; + } + + root() { + return this._root; + } + + mount() { + this._root = renderTile(this._tileVM); + return this._root; + } + + unmount() {} + + update(vm, paramName) { + } +} + +function renderTile(tile) { + switch (tile.shape) { + case "message": + return tag.li([tag.strong(tile.internalId+" "), tile.label]); + case "announcement": + return tag.li([tag.strong(tile.internalId+" "), tile.announcement]); + default: + return tag.li([tag.strong(tile.internalId+" "), "unknown tile shape: " + tile.shape]); + } +}