diff --git a/src/domain/session/room/RoomViewModel.js b/src/domain/session/room/RoomViewModel.js index 7e8591af..5616505c 100644 --- a/src/domain/session/room/RoomViewModel.js +++ b/src/domain/session/room/RoomViewModel.js @@ -131,6 +131,14 @@ export class RoomViewModel extends ViewModel { get avatarTitle() { return this.name; } + + get canLeave() { + return this._room.isJoined; + } + + leaveRoom() { + this._room.leave(); + } async _sendMessage(message) { if (!this._room.isArchived && message) { diff --git a/src/matrix/room/Room.js b/src/matrix/room/Room.js index ff13c0f4..93c580a3 100644 --- a/src/matrix/room/Room.js +++ b/src/matrix/room/Room.js @@ -367,6 +367,13 @@ export class Room extends BaseRoom { } } + leave(log = null) { + return this._platform.logger.wrapOrRun(log, "leave room", async log => { + log.set("id", this.id); + await this._hsApi.leave(this.id, {log}).response(); + }); + } + /* called by BaseRoom to pass pendingEvents when opening the timeline */ _getPendingEvents() { return this._sendQueue.pendingEvents; diff --git a/src/platform/web/ui/session/room/RoomView.js b/src/platform/web/ui/session/room/RoomView.js index db3f68da..0fdf4bb4 100644 --- a/src/platform/web/ui/session/room/RoomView.js +++ b/src/platform/web/ui/session/room/RoomView.js @@ -16,6 +16,8 @@ limitations under the License. */ import {TemplateView} from "../../general/TemplateView.js"; +import {Popup} from "../../general/Popup.js"; +import {Menu} from "../../general/Menu.js"; import {TimelineList} from "./TimelineList.js"; import {TimelineLoadingView} from "./TimelineLoadingView.js"; import {MessageComposer} from "./MessageComposer.js"; @@ -23,6 +25,11 @@ import {RoomArchivedView} from "./RoomArchivedView.js"; import {AvatarView} from "../../avatar.js"; export class RoomView extends TemplateView { + constructor(options) { + super(options); + this._optionsPopup = null; + } + render(t, vm) { let bottomView; if (vm.composerViewModel.kind === "composer") { @@ -37,6 +44,10 @@ export class RoomView extends TemplateView { t.div({className: "room-description"}, [ t.h2(vm => vm.name), ]), + t.button({ + className: "button-utility room-options", + onClick: evt => this._toggleOptionsMenu(evt) + }, "⋮") ]), t.div({className: "RoomView_body"}, [ t.div({className: "RoomView_error"}, vm => vm.error), @@ -49,4 +60,33 @@ export class RoomView extends TemplateView { ]) ]); } + + _toggleOptionsMenu(evt) { + if (this._optionsPopup && this._optionsPopup.isOpen) { + this._optionsPopup.close(); + } else { + const vm = this.value; + const options = []; + if (vm.canLeave) { + options.push(Menu.option(vm.i18n`Leave room`, () => vm.leaveRoom())); + } + if (!options.length) { + return; + } + this._optionsPopup = new Popup(new Menu(options)); + this._optionsPopup.trackInTemplateView(this); + this._optionsPopup.showRelativeTo(evt.target, { + horizontal: { + relativeTo: "end", + align: "start", + after: 0 + }, + vertical: { + relativeTo: "start", + align: "start", + after: 40 + 4 + } + }); + } + } }