From 9679058081b92c531eae1eca57634200f1e3b482 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 11:05:31 +0200 Subject: [PATCH 01/11] allow to join room by id or alias --- src/matrix/Session.js | 7 +++++++ src/matrix/net/HomeServerApi.js | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/src/matrix/Session.js b/src/matrix/Session.js index 38771c87..a2c77752 100644 --- a/src/matrix/Session.js +++ b/src/matrix/Session.js @@ -717,6 +717,13 @@ export class Session { } }); } + + joinRoom(roomIdOrAlias, log = null) { + return this._platform.logger.wrapOrRun(log, "joinRoom", async log => { + const body = await this._hsApi.joinIdOrAlias(roomIdOrAlias).response(); + return body.room_id; + }); + } } export function tests() { diff --git a/src/matrix/net/HomeServerApi.js b/src/matrix/net/HomeServerApi.js index a3fe1bc9..f834b94b 100644 --- a/src/matrix/net/HomeServerApi.js +++ b/src/matrix/net/HomeServerApi.js @@ -190,6 +190,10 @@ export class HomeServerApi { return this._post(`/rooms/${encodeURIComponent(roomId)}/join`, null, null, options); } + joinIdOrAlias(roomIdOrAlias, options = null) { + return this._post(`/join/${encodeURIComponent(roomIdOrAlias)}`, null, null, options); + } + leave(roomId, options = null) { return this._post(`/rooms/${encodeURIComponent(roomId)}/leave`, null, null, options); } From 3acb3bb48ceae1c679c17f6ec56492af939c8f74 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 11:06:48 +0200 Subject: [PATCH 02/11] fix memory leak when switching room ids --- src/domain/session/RoomViewModelObservable.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/domain/session/RoomViewModelObservable.js b/src/domain/session/RoomViewModelObservable.js index 9252ee81..8a8254e7 100644 --- a/src/domain/session/RoomViewModelObservable.js +++ b/src/domain/session/RoomViewModelObservable.js @@ -36,6 +36,7 @@ This is also why there is an explicit initialize method, see comment there. export class RoomViewModelObservable extends ObservableValue { constructor(sessionViewModel, roomId) { super(null); + this._statusSubscription = null; this._sessionViewModel = sessionViewModel; this.id = roomId; } @@ -48,9 +49,9 @@ export class RoomViewModelObservable extends ObservableValue { */ async initialize() { const {session} = this._sessionViewModel._sessionContainer; - this._statusObservable = await session.observeRoomStatus(this.id); - this.set(await this._statusToViewModel(this._statusObservable.get())); - this._statusObservable.subscribe(async status => { + const statusObservable = await session.observeRoomStatus(this.id); + this.set(await this._statusToViewModel(statusObservable.get())); + this._statusSubscription = statusObservable.subscribe(async status => { // first dispose existing VM, if any this.get()?.dispose(); this.set(await this._statusToViewModel(status)); From 03be63572d1531962ca5f6182757a11d136096dd Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 11:07:19 +0200 Subject: [PATCH 03/11] ask to join when room id is unknown --- src/domain/session/RoomViewModelObservable.js | 2 + src/domain/session/SessionViewModel.js | 8 +++ .../session/room/UnknownRoomViewModel.js | 59 +++++++++++++++++++ src/platform/web/ui/session/SessionView.js | 5 +- .../web/ui/session/room/UnknownRoomView.js | 38 ++++++++++++ 5 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/domain/session/room/UnknownRoomViewModel.js create mode 100644 src/platform/web/ui/session/room/UnknownRoomView.js diff --git a/src/domain/session/RoomViewModelObservable.js b/src/domain/session/RoomViewModelObservable.js index 8a8254e7..a7aeea07 100644 --- a/src/domain/session/RoomViewModelObservable.js +++ b/src/domain/session/RoomViewModelObservable.js @@ -65,6 +65,8 @@ export class RoomViewModelObservable extends ObservableValue { return this._sessionViewModel._createRoomViewModel(this.id); } else if (status.archived) { return await this._sessionViewModel._createArchivedRoomViewModel(this.id); + } else { + return this._sessionViewModel._createUnknownRoomViewModel(this.id); } return null; } diff --git a/src/domain/session/SessionViewModel.js b/src/domain/session/SessionViewModel.js index ae697c72..6f2c9797 100644 --- a/src/domain/session/SessionViewModel.js +++ b/src/domain/session/SessionViewModel.js @@ -17,6 +17,7 @@ limitations under the License. import {LeftPanelViewModel} from "./leftpanel/LeftPanelViewModel.js"; import {RoomViewModel} from "./room/RoomViewModel.js"; +import {UnknownRoomViewModel} from "./room/UnknownRoomViewModel.js"; import {InviteViewModel} from "./room/InviteViewModel.js"; import {LightboxViewModel} from "./room/LightboxViewModel.js"; import {SessionStatusViewModel} from "./SessionStatusViewModel.js"; @@ -162,6 +163,13 @@ export class SessionViewModel extends ViewModel { return null; } + _createUnknownRoomViewModel(roomIdOrAlias) { + return new UnknownRoomViewModel(this.childOptions({ + roomIdOrAlias, + session: this._sessionContainer.session, + })); + } + async _createArchivedRoomViewModel(roomId) { const room = await this._sessionContainer.session.loadArchivedRoom(roomId); if (room) { diff --git a/src/domain/session/room/UnknownRoomViewModel.js b/src/domain/session/room/UnknownRoomViewModel.js new file mode 100644 index 00000000..d896d4cf --- /dev/null +++ b/src/domain/session/room/UnknownRoomViewModel.js @@ -0,0 +1,59 @@ +/* +Copyright 2020 Bruno Windels +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import {ViewModel} from "../../ViewModel.js"; + +export class UnknownRoomViewModel extends ViewModel { + constructor(options) { + super(options); + const {roomIdOrAlias, session} = options; + this._session = session; + this._roomIdOrAlias = roomIdOrAlias; + this._error = null; + this._busy = false; + } + + get error() { + return this._error?.message; + } + + get description() { + return this.i18n`You are currently not in ${this._roomIdOrAlias}. Want to join it?`; + } + + async join() { + this._busy = true; + this.emitChange("busy"); + try { + const roomId = await this._session.joinRoom(this._roomIdOrAlias); + // navigate to roomId if we were at the alias + this.navigation.push("room", roomId); + } catch (err) { + this._error = err; + this._busy = false; + this.emitChange("error"); + } + } + + get busy() { + return this._busy; + } + + get kind() { + return "unknown"; + } +} \ No newline at end of file diff --git a/src/platform/web/ui/session/SessionView.js b/src/platform/web/ui/session/SessionView.js index 214db2a3..c8a77a1b 100644 --- a/src/platform/web/ui/session/SessionView.js +++ b/src/platform/web/ui/session/SessionView.js @@ -17,6 +17,7 @@ limitations under the License. import {LeftPanelView} from "./leftpanel/LeftPanelView.js"; import {RoomView} from "./room/RoomView.js"; +import {UnknownRoomView} from "./room/UnknownRoomView.js"; import {InviteView} from "./room/InviteView.js"; import {LightboxView} from "./room/LightboxView.js"; import {TemplateView} from "../general/TemplateView.js"; @@ -43,8 +44,10 @@ export class SessionView extends TemplateView { } else if (vm.currentRoomViewModel) { if (vm.currentRoomViewModel.kind === "invite") { return new InviteView(vm.currentRoomViewModel); - } else { + } else if (vm.currentRoomViewModel.kind === "room") { return new RoomView(vm.currentRoomViewModel); + } else { + return new UnknownRoomView(vm.currentRoomViewModel); } } else { return new StaticView(t => t.div({className: "room-placeholder"}, t.h2(vm.i18n`Choose a room on the left side.`))); diff --git a/src/platform/web/ui/session/room/UnknownRoomView.js b/src/platform/web/ui/session/room/UnknownRoomView.js new file mode 100644 index 00000000..03076885 --- /dev/null +++ b/src/platform/web/ui/session/room/UnknownRoomView.js @@ -0,0 +1,38 @@ +/* +Copyright 2020 Bruno Windels +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +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"; +import {RoomArchivedView} from "./RoomArchivedView.js"; +import {AvatarView} from "../../avatar.js"; + +export class UnknownRoomView extends TemplateView { + render(t, vm) { + return t.main({className: "UnknownRoomView middle"}, [ + t.h2(vm.description), + t.div(t.button({ + className: "button-action primary", + onClick: () => vm.join(), + disabled: vm => vm.busy, + }, vm.i18n`Join room`)) + ]); + } +} From c04a8140a3e2e2785fa32fea9324789974f36a4a Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 11:07:46 +0200 Subject: [PATCH 04/11] offer to rejoin archived room from menu --- src/domain/session/room/RoomViewModel.js | 8 ++++++++ src/matrix/room/ArchivedRoom.js | 6 ++++++ src/platform/web/ui/session/room/RoomView.js | 3 +++ 3 files changed, 17 insertions(+) diff --git a/src/domain/session/room/RoomViewModel.js b/src/domain/session/room/RoomViewModel.js index 1d2f3725..5d08f903 100644 --- a/src/domain/session/room/RoomViewModel.js +++ b/src/domain/session/room/RoomViewModel.js @@ -147,6 +147,14 @@ export class RoomViewModel extends ViewModel { forgetRoom() { this._room.forget(); } + + get canRejoin() { + return this._room.isArchived; + } + + rejoinRoom() { + this._room.join(); + } async _sendMessage(message) { if (!this._room.isArchived && message) { diff --git a/src/matrix/room/ArchivedRoom.js b/src/matrix/room/ArchivedRoom.js index a87bd774..f975191e 100644 --- a/src/matrix/room/ArchivedRoom.js +++ b/src/matrix/room/ArchivedRoom.js @@ -162,6 +162,12 @@ export class ArchivedRoom extends BaseRoom { this._forgetCallback(this.id); }); } + + join(log = null) { + return this._platform.logger.wrapOrRun(log, "rejoin archived room", async log => { + await this._hsApi.join(this.id, {log}).response(); + }); + } } function findKickDetails(roomResponse, ownUserId) { diff --git a/src/platform/web/ui/session/room/RoomView.js b/src/platform/web/ui/session/room/RoomView.js index e2ef0de7..3d8c58a5 100644 --- a/src/platform/web/ui/session/room/RoomView.js +++ b/src/platform/web/ui/session/room/RoomView.js @@ -73,6 +73,9 @@ export class RoomView extends TemplateView { if (vm.canForget) { options.push(Menu.option(vm.i18n`Forget room`, () => vm.forgetRoom())); } + if (vm.canRejoin) { + options.push(Menu.option(vm.i18n`Rejoin room`, () => vm.rejoinRoom())); + } if (!options.length) { return; } From 2cf100efaaa9dd11e6b2a9c2a1c5278112220bd3 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 11:46:55 +0200 Subject: [PATCH 05/11] refine unknown room view --- .../session/room/UnknownRoomViewModel.js | 8 ++------ .../web/ui/css/themes/element/theme.css | 18 ++++++++++++++++++ .../web/ui/session/room/UnknownRoomView.js | 15 ++++++++++----- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/domain/session/room/UnknownRoomViewModel.js b/src/domain/session/room/UnknownRoomViewModel.js index d896d4cf..9f1b5d2b 100644 --- a/src/domain/session/room/UnknownRoomViewModel.js +++ b/src/domain/session/room/UnknownRoomViewModel.js @@ -22,7 +22,7 @@ export class UnknownRoomViewModel extends ViewModel { super(options); const {roomIdOrAlias, session} = options; this._session = session; - this._roomIdOrAlias = roomIdOrAlias; + this.roomIdOrAlias = roomIdOrAlias; this._error = null; this._busy = false; } @@ -31,15 +31,11 @@ export class UnknownRoomViewModel extends ViewModel { return this._error?.message; } - get description() { - return this.i18n`You are currently not in ${this._roomIdOrAlias}. Want to join it?`; - } - async join() { this._busy = true; this.emitChange("busy"); try { - const roomId = await this._session.joinRoom(this._roomIdOrAlias); + const roomId = await this._session.joinRoom(this.roomIdOrAlias); // navigate to roomId if we were at the alias this.navigation.push("room", roomId); } catch (err) { diff --git a/src/platform/web/ui/css/themes/element/theme.css b/src/platform/web/ui/css/themes/element/theme.css index 00fdefd0..706bd90d 100644 --- a/src/platform/web/ui/css/themes/element/theme.css +++ b/src/platform/web/ui/css/themes/element/theme.css @@ -910,4 +910,22 @@ button.link { .RoomArchivedView h3 { margin: 0; +} + +.UnknownRoomView { + align-items: center; + justify-content: center; + text-align: center; + padding: 16px; + box-sizing: border-box; +} + +.UnknownRoomView h2 { + word-break: break-all; + word-break: break-word; +} + +.UnknownRoomView button { + max-width: 200px; + width: 100%; } \ No newline at end of file diff --git a/src/platform/web/ui/session/room/UnknownRoomView.js b/src/platform/web/ui/session/room/UnknownRoomView.js index 03076885..6e4cb284 100644 --- a/src/platform/web/ui/session/room/UnknownRoomView.js +++ b/src/platform/web/ui/session/room/UnknownRoomView.js @@ -26,13 +26,18 @@ import {AvatarView} from "../../avatar.js"; export class UnknownRoomView extends TemplateView { render(t, vm) { - return t.main({className: "UnknownRoomView middle"}, [ - t.h2(vm.description), - t.div(t.button({ + return t.main({className: "UnknownRoomView middle"}, t.div([ + t.h2([ + vm.i18n`You are currently not in ${vm.roomIdOrAlias}.`, + t.br(), + vm.i18n`Want to join it?` + ]), + t.button({ className: "button-action primary", onClick: () => vm.join(), disabled: vm => vm.busy, - }, vm.i18n`Join room`)) - ]); + }, vm.i18n`Join room`), + t.if(vm => vm.error, t => t.p({className: "error"}, vm.error)) + ])); } } From 3724fc6765c22ce887aafbe1c05d4a1d01511834 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 11:49:16 +0200 Subject: [PATCH 06/11] log join network call --- src/matrix/Session.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/matrix/Session.js b/src/matrix/Session.js index a2c77752..5a046117 100644 --- a/src/matrix/Session.js +++ b/src/matrix/Session.js @@ -720,7 +720,7 @@ export class Session { joinRoom(roomIdOrAlias, log = null) { return this._platform.logger.wrapOrRun(log, "joinRoom", async log => { - const body = await this._hsApi.joinIdOrAlias(roomIdOrAlias).response(); + const body = await this._hsApi.joinIdOrAlias(roomIdOrAlias, {log}).response(); return body.room_id; }); } From 32e47bc6605741ca6096731f2fba1c3562e013c4 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 11:49:25 +0200 Subject: [PATCH 07/11] remove unused imports --- src/platform/web/ui/session/room/UnknownRoomView.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/platform/web/ui/session/room/UnknownRoomView.js b/src/platform/web/ui/session/room/UnknownRoomView.js index 6e4cb284..bd65417c 100644 --- a/src/platform/web/ui/session/room/UnknownRoomView.js +++ b/src/platform/web/ui/session/room/UnknownRoomView.js @@ -16,13 +16,6 @@ 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"; -import {RoomArchivedView} from "./RoomArchivedView.js"; -import {AvatarView} from "../../avatar.js"; export class UnknownRoomView extends TemplateView { render(t, vm) { From 54798d3079aa3b254799402ea703b27c3f0ed4d2 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 11:50:02 +0200 Subject: [PATCH 08/11] unreachable now --- src/domain/session/RoomViewModelObservable.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/domain/session/RoomViewModelObservable.js b/src/domain/session/RoomViewModelObservable.js index a7aeea07..b9549905 100644 --- a/src/domain/session/RoomViewModelObservable.js +++ b/src/domain/session/RoomViewModelObservable.js @@ -68,7 +68,6 @@ export class RoomViewModelObservable extends ObservableValue { } else { return this._sessionViewModel._createUnknownRoomViewModel(this.id); } - return null; } dispose() { From f92b1df4fcc35afd710ab86b52d80b7389e10d6e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 11:54:10 +0200 Subject: [PATCH 09/11] update copyright comments --- src/domain/session/room/UnknownRoomViewModel.js | 3 +-- src/platform/web/ui/session/room/UnknownRoomView.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/domain/session/room/UnknownRoomViewModel.js b/src/domain/session/room/UnknownRoomViewModel.js index 9f1b5d2b..5dc8cd65 100644 --- a/src/domain/session/room/UnknownRoomViewModel.js +++ b/src/domain/session/room/UnknownRoomViewModel.js @@ -1,6 +1,5 @@ /* -Copyright 2020 Bruno Windels -Copyright 2020 The Matrix.org Foundation C.I.C. +Copyright 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/platform/web/ui/session/room/UnknownRoomView.js b/src/platform/web/ui/session/room/UnknownRoomView.js index bd65417c..32569d6f 100644 --- a/src/platform/web/ui/session/room/UnknownRoomView.js +++ b/src/platform/web/ui/session/room/UnknownRoomView.js @@ -1,6 +1,5 @@ /* -Copyright 2020 Bruno Windels -Copyright 2020 The Matrix.org Foundation C.I.C. +Copyright 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 754e7e7bb28263101793f5b523a56a2b7fe4fc28 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 11:54:18 +0200 Subject: [PATCH 10/11] clarify with comments --- src/domain/session/room/UnknownRoomViewModel.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/domain/session/room/UnknownRoomViewModel.js b/src/domain/session/room/UnknownRoomViewModel.js index 5dc8cd65..e7969298 100644 --- a/src/domain/session/room/UnknownRoomViewModel.js +++ b/src/domain/session/room/UnknownRoomViewModel.js @@ -36,7 +36,11 @@ export class UnknownRoomViewModel extends ViewModel { try { const roomId = await this._session.joinRoom(this.roomIdOrAlias); // navigate to roomId if we were at the alias + // so we're subscribed to the right room status + // and we'll switch to the room view model once + // the join is synced this.navigation.push("room", roomId); + // keep busy on true while waiting for the join to sync } catch (err) { this._error = err; this._busy = false; From 4c5be997fe6eba80f17c4c447e2edcda10073742 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 11:58:06 +0200 Subject: [PATCH 11/11] fix menu alignment with extra option we'll need to have a closer look how these alignment options make sense but for now this will do --- src/platform/web/ui/session/room/RoomView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/web/ui/session/room/RoomView.js b/src/platform/web/ui/session/room/RoomView.js index 3d8c58a5..fff875b8 100644 --- a/src/platform/web/ui/session/room/RoomView.js +++ b/src/platform/web/ui/session/room/RoomView.js @@ -89,8 +89,8 @@ export class RoomView extends TemplateView { }, vertical: { relativeTo: "start", - align: "start", - after: 40 + 4 + align: "end", + before: -32 - 4 } }); }