From 68092c5f628b920486eec605f62fa62e9a4e5f89 Mon Sep 17 00:00:00 2001 From: RMidhunSuresh Date: Sat, 19 Aug 2023 23:01:23 +0530 Subject: [PATCH 1/6] Implement invite button in memberlist --- src/platform/web/ui/css/layout.css | 2 ++ .../web/ui/css/themes/element/theme.css | 22 ++++++++++++++++++- .../ui/session/rightpanel/MemberListView.js | 15 +++++++++---- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/platform/web/ui/css/layout.css b/src/platform/web/ui/css/layout.css index f7fbe732a7..7d785c9fb2 100644 --- a/src/platform/web/ui/css/layout.css +++ b/src/platform/web/ui/css/layout.css @@ -217,6 +217,8 @@ the layout viewport up without resizing it when the keyboard shows */ .LazyListParent { flex: 1; + flex-basis: 0; + margin-top: 15px; } .LoadingView { diff --git a/src/platform/web/ui/css/themes/element/theme.css b/src/platform/web/ui/css/themes/element/theme.css index 1f7f2f13cc..7e54dc2fcc 100644 --- a/src/platform/web/ui/css/themes/element/theme.css +++ b/src/platform/web/ui/css/themes/element/theme.css @@ -1123,12 +1123,32 @@ button.RoomDetailsView_row::after { /* Memberlist Panel */ -.MemberListView { +.MemberListView__list { padding-left: 16px; padding-right: 16px; margin: 0; } +.MemberListView { + display: flex; + flex-direction: column; + height: 100%; +} + +.MemberListView__invite-container { + display: flex; + justify-content: center; + align-items: center; +} + +.MemberListView__invite-btn { + width: 80%; + height: 32px; + display: flex; + justify-content: center; + align-items: center; +} + .MemberTileView { margin-bottom: 8px; list-style: none; diff --git a/src/platform/web/ui/session/rightpanel/MemberListView.js b/src/platform/web/ui/session/rightpanel/MemberListView.js index a4a4e78c4a..fdb7001dda 100644 --- a/src/platform/web/ui/session/rightpanel/MemberListView.js +++ b/src/platform/web/ui/session/rightpanel/MemberListView.js @@ -16,13 +16,20 @@ limitations under the License. import {LazyListView} from "../../general/LazyListView"; import {MemberTileView} from "./MemberTileView.js"; +import {TemplateView} from "../../general/TemplateView"; -export class MemberListView extends LazyListView { - constructor(vm) { - super({ +export class MemberListView extends TemplateView { + render(t, vm) { + const list = new LazyListView({ list: vm.memberTileViewModels, - className: "MemberListView", + className: "MemberListView__list", itemHeight: 40 }, tileViewModel => new MemberTileView(tileViewModel)); + return t.div({ className: "MemberListView" }, [ + t.div({ className: "MemberListView__invite-container" }, [ + t.button({ className: "MemberListView__invite-btn button-action primary" }, vm.i18n`Invite to this room`), + ]), + t.view(list), + ]); } } From b766215ae132762b64fac522ca0f984ce87fa3dc Mon Sep 17 00:00:00 2001 From: RMidhunSuresh Date: Mon, 21 Aug 2023 17:09:14 +0530 Subject: [PATCH 2/6] Create segment "invite" --- src/domain/navigation/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/domain/navigation/index.ts b/src/domain/navigation/index.ts index 59bb1cccc2..5e63ef16bc 100644 --- a/src/domain/navigation/index.ts +++ b/src/domain/navigation/index.ts @@ -34,6 +34,7 @@ export type SegmentType = { "details": true; "members": true; "member": string; + "invite": true; "device-verification": string | boolean; "verification": string | boolean; "join-room": true; @@ -61,7 +62,7 @@ function allowsChild(parent: Segment | undefined, child: Segment, if (sessionSegment) { segments.push(sessionSegment); } - } else if (type === "details" || type === "members" || type === "verification") { + } else if (type === "details" || type === "members" || type === "verification" || type === "invite") { pushRightPanelSegment(segments, type); } else if (type === "member") { let userId = iterator.next().value; From f4e73c25d5d3a31a11f25d2c7c551bd68f3a1d15 Mon Sep 17 00:00:00 2001 From: RMidhunSuresh Date: Mon, 21 Aug 2023 17:11:46 +0530 Subject: [PATCH 3/6] Create vm/view for InvitePanel --- .../rightpanel/InvitePanelViewModel.ts | 54 +++++++++++++++++++ .../ui/session/rightpanel/InvitePanelView.ts | 50 +++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/domain/session/rightpanel/InvitePanelViewModel.ts create mode 100644 src/platform/web/ui/session/rightpanel/InvitePanelView.ts diff --git a/src/domain/session/rightpanel/InvitePanelViewModel.ts b/src/domain/session/rightpanel/InvitePanelViewModel.ts new file mode 100644 index 0000000000..4a4409c251 --- /dev/null +++ b/src/domain/session/rightpanel/InvitePanelViewModel.ts @@ -0,0 +1,54 @@ +/* +Copyright 2023 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 {ErrorReportViewModel} from "../../ErrorReportViewModel"; +import type {Options as BaseOptions} from "../../ViewModel"; +import type {SegmentType} from "../../navigation"; +import type {Room} from "../../../matrix/room/Room.js"; +import type {Session} from "../../../matrix/Session.js"; + +type Options = { room: Room, session: Session } & BaseOptions; + +export class InvitePanelViewModel extends ErrorReportViewModel { + constructor(options: Options) { + super(options); + } + + get type() { + return "invite"; + } + + get shouldShowBackButton() { + return true; + } + + get previousSegmentName() { + return "members"; + } + + get roomName() { + return this.getOption("room").name; + } + + async invite(userId: string) { + await this.logAndCatch("InvitePanelViewModel.invite", async () => { + const room = this.getOption("room"); + await room.inviteUser(userId); + const path = this.navigation.path.until("room"); + this.navigation.applyPath(path); + }); + } +} diff --git a/src/platform/web/ui/session/rightpanel/InvitePanelView.ts b/src/platform/web/ui/session/rightpanel/InvitePanelView.ts new file mode 100644 index 0000000000..4c3c9d4f84 --- /dev/null +++ b/src/platform/web/ui/session/rightpanel/InvitePanelView.ts @@ -0,0 +1,50 @@ +/* +Copyright 2023 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 {Builder, TemplateView} from "../../general/TemplateView"; +import {ErrorView} from "../../general/ErrorView"; +import type {InvitePanelViewModel} from "../../../../../domain/session/rightpanel/InvitePanelViewModel"; + +export class InvitePanelView extends TemplateView { + render(t: Builder, vm: InvitePanelViewModel) { + const input = t.input({ + className: "InvitePanelView__input", + type: "text", + placeholder: "Enter user-id of user", + onkeydown: (e: KeyboardEvent) => { + if (e.key === "Enter") { + vm.invite((input as HTMLInputElement).value); + } + } + }); + return t.div({ className: "InvitePanelView" }, [ + t.h3({ className: "InvitePanelView__heading" }, + (vm: InvitePanelViewModel) => vm.i18n`Invite to ${vm.roomName}` + ), + t.div({ className: "InvitePanelView__form" }, [ + input, + t.button({ + className: "InvitePanelView__btn button-action primary", + onClick: () => vm.invite((input as HTMLInputElement).value), + }, "Invite"), + ]), + t.div({ className: "InvitePanelView__error" }, [ + t.ifView(vm => !!vm.errorViewModel, vm => new ErrorView(vm.errorViewModel!)), + ]), + ]); + } + +} From bf32f2cc290b4340d847e8c39f9020ff5eae6bec Mon Sep 17 00:00:00 2001 From: RMidhunSuresh Date: Mon, 21 Aug 2023 17:12:06 +0530 Subject: [PATCH 4/6] Open invite panel from memberlist panel --- src/domain/session/rightpanel/MemberListViewModel.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/domain/session/rightpanel/MemberListViewModel.js b/src/domain/session/rightpanel/MemberListViewModel.js index 1c878c858b..b20ac92973 100644 --- a/src/domain/session/rightpanel/MemberListViewModel.js +++ b/src/domain/session/rightpanel/MemberListViewModel.js @@ -54,4 +54,11 @@ export class MemberListViewModel extends ViewModel { return members.mapValues(mapper, updater); } + openInvitePanel() { + let path = this.navigation.path.until("room"); + path = path.with(this.navigation.segment("right-panel", true)); + path = path.with(this.navigation.segment("invite", true)); + this.navigation.applyPath(path); + } + } From 1a16728d47a780be4c58d5a59511c78d5485ad05 Mon Sep 17 00:00:00 2001 From: RMidhunSuresh Date: Mon, 21 Aug 2023 17:12:39 +0530 Subject: [PATCH 5/6] Open panel when button is clicked --- src/domain/session/rightpanel/RightPanelViewModel.js | 2 ++ src/platform/web/ui/session/rightpanel/MemberListView.js | 8 +++++++- src/platform/web/ui/session/rightpanel/RightPanelView.js | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/domain/session/rightpanel/RightPanelViewModel.js b/src/domain/session/rightpanel/RightPanelViewModel.js index 5a8c2079b2..df06b88aa1 100644 --- a/src/domain/session/rightpanel/RightPanelViewModel.js +++ b/src/domain/session/rightpanel/RightPanelViewModel.js @@ -18,6 +18,7 @@ import {ViewModel} from "../../ViewModel"; import {RoomDetailsViewModel} from "./RoomDetailsViewModel.js"; import {MemberListViewModel} from "./MemberListViewModel.js"; import {MemberDetailsViewModel} from "./MemberDetailsViewModel.js"; +import {InvitePanelViewModel} from "./InvitePanelViewModel"; import {DeviceVerificationViewModel} from "../verification/DeviceVerificationViewModel"; export class RightPanelViewModel extends ViewModel { @@ -61,6 +62,7 @@ export class RightPanelViewModel extends ViewModel { _setupNavigation() { this._hookUpdaterToSegment("details", RoomDetailsViewModel, () => { return {room: this._room}; }); + this._hookUpdaterToSegment("invite", InvitePanelViewModel, () => { return {room: this._room}; }); this._hookUpdaterToSegment("members", MemberListViewModel, () => this._getMemberListArguments()); this._hookUpdaterToSegment("member", MemberDetailsViewModel, () => this._getMemberDetailsArguments(), () => { diff --git a/src/platform/web/ui/session/rightpanel/MemberListView.js b/src/platform/web/ui/session/rightpanel/MemberListView.js index fdb7001dda..1835542858 100644 --- a/src/platform/web/ui/session/rightpanel/MemberListView.js +++ b/src/platform/web/ui/session/rightpanel/MemberListView.js @@ -27,7 +27,13 @@ export class MemberListView extends TemplateView { }, tileViewModel => new MemberTileView(tileViewModel)); return t.div({ className: "MemberListView" }, [ t.div({ className: "MemberListView__invite-container" }, [ - t.button({ className: "MemberListView__invite-btn button-action primary" }, vm.i18n`Invite to this room`), + t.button( + { + className: "MemberListView__invite-btn button-action primary", + onClick: () => vm.openInvitePanel(), + }, + vm.i18n`Invite to this room` + ), ]), t.view(list), ]); diff --git a/src/platform/web/ui/session/rightpanel/RightPanelView.js b/src/platform/web/ui/session/rightpanel/RightPanelView.js index 738f8cb6e3..6e4f5e25d4 100644 --- a/src/platform/web/ui/session/rightpanel/RightPanelView.js +++ b/src/platform/web/ui/session/rightpanel/RightPanelView.js @@ -20,6 +20,7 @@ import {MemberListView} from "./MemberListView.js"; import {LoadingView} from "../../general/LoadingView.js"; import {MemberDetailsView} from "./MemberDetailsView.js"; import {DeviceVerificationView} from "../verification/DeviceVerificationView"; +import {InvitePanelView} from "./InvitePanelView"; export class RightPanelView extends TemplateView { render(t) { @@ -40,6 +41,8 @@ export class RightPanelView extends TemplateView { return new MemberListView(vm); case "member-details": return new MemberDetailsView(vm); + case "invite": + return new InvitePanelView(vm); case "verification": return new DeviceVerificationView(vm); default: From 20cc5b5c702a3125b71f3ff2d53cfdfc82695e21 Mon Sep 17 00:00:00 2001 From: RMidhunSuresh Date: Mon, 21 Aug 2023 17:13:19 +0530 Subject: [PATCH 6/6] Style views --- .../web/ui/css/themes/element/theme.css | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/platform/web/ui/css/themes/element/theme.css b/src/platform/web/ui/css/themes/element/theme.css index 7e54dc2fcc..e932a9e707 100644 --- a/src/platform/web/ui/css/themes/element/theme.css +++ b/src/platform/web/ui/css/themes/element/theme.css @@ -1013,6 +1013,54 @@ button.link { /* Right Panel */ +.InvitePanelView { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.InvitePanelView__form { + margin-top: 8px; +} + +.InvitePanelView__input { + font-family: "Inter"; + font-size: 1.3rem; + font-weight: 500; + line-height: 1.573rem; + outline: none; + border: none; + background-color: var(--icon-background); + color: var(--text-color); + height: 32px; + box-sizing: border-box; + margin: 5px; + border-radius: 16px; + padding: 15px; + width: 90%; +} + +.InvitePanelView__form, +.InvitePanelView__btn { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.InvitePanelView__btn { + width: 100px; + height: 30px; + margin-top: 8px; +} + +.InvitePanelView__heading { + width: 90%; + text-align: center; + margin: 0; +} + .RightPanelView { background: var(--background-color-secondary); }