Skip to content

Commit 20106ca

Browse files
Merge pull request #1135 from vector-im/invite/implement-invite-ui
Invite - PR [2/2] - Add UI for inviting users to room
2 parents 0cad1e5 + 20cc5b5 commit 20106ca

File tree

9 files changed

+207
-7
lines changed

9 files changed

+207
-7
lines changed

src/domain/navigation/index.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export type SegmentType = {
3434
"details": true;
3535
"members": true;
3636
"member": string;
37+
"invite": true;
3738
"device-verification": string | boolean;
3839
"verification": string | boolean;
3940
"join-room": true;
@@ -61,7 +62,7 @@ function allowsChild(parent: Segment<SegmentType> | undefined, child: Segment<Se
6162
case "room":
6263
return type === "lightbox" || type === "right-panel";
6364
case "right-panel":
64-
return type === "details"|| type === "members" || type === "member" || type === "verification";
65+
return type === "details"|| type === "members" || type === "member" || type === "verification" || type === "invite";
6566
case "logout":
6667
return type === "forced";
6768
default:
@@ -177,7 +178,7 @@ export function parseUrlPath(urlPath: string, currentNavPath: Path<SegmentType>,
177178
if (sessionSegment) {
178179
segments.push(sessionSegment);
179180
}
180-
} else if (type === "details" || type === "members" || type === "verification") {
181+
} else if (type === "details" || type === "members" || type === "verification" || type === "invite") {
181182
pushRightPanelSegment(segments, type);
182183
} else if (type === "member") {
183184
let userId = iterator.next().value;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
Copyright 2023 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import {ErrorReportViewModel} from "../../ErrorReportViewModel";
18+
import type {Options as BaseOptions} from "../../ViewModel";
19+
import type {SegmentType} from "../../navigation";
20+
import type {Room} from "../../../matrix/room/Room.js";
21+
import type {Session} from "../../../matrix/Session.js";
22+
23+
type Options = { room: Room, session: Session } & BaseOptions;
24+
25+
export class InvitePanelViewModel extends ErrorReportViewModel<SegmentType, Options> {
26+
constructor(options: Options) {
27+
super(options);
28+
}
29+
30+
get type() {
31+
return "invite";
32+
}
33+
34+
get shouldShowBackButton() {
35+
return true;
36+
}
37+
38+
get previousSegmentName() {
39+
return "members";
40+
}
41+
42+
get roomName() {
43+
return this.getOption("room").name;
44+
}
45+
46+
async invite(userId: string) {
47+
await this.logAndCatch("InvitePanelViewModel.invite", async () => {
48+
const room = this.getOption("room");
49+
await room.inviteUser(userId);
50+
const path = this.navigation.path.until("room");
51+
this.navigation.applyPath(path);
52+
});
53+
}
54+
}

src/domain/session/rightpanel/MemberListViewModel.js

+7
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,11 @@ export class MemberListViewModel extends ViewModel {
5454
return members.mapValues(mapper, updater);
5555
}
5656

57+
openInvitePanel() {
58+
let path = this.navigation.path.until("room");
59+
path = path.with(this.navigation.segment("right-panel", true));
60+
path = path.with(this.navigation.segment("invite", true));
61+
this.navigation.applyPath(path);
62+
}
63+
5764
}

src/domain/session/rightpanel/RightPanelViewModel.js

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {ViewModel} from "../../ViewModel";
1818
import {RoomDetailsViewModel} from "./RoomDetailsViewModel.js";
1919
import {MemberListViewModel} from "./MemberListViewModel.js";
2020
import {MemberDetailsViewModel} from "./MemberDetailsViewModel.js";
21+
import {InvitePanelViewModel} from "./InvitePanelViewModel";
2122
import {DeviceVerificationViewModel} from "../verification/DeviceVerificationViewModel";
2223

2324
export class RightPanelViewModel extends ViewModel {
@@ -61,6 +62,7 @@ export class RightPanelViewModel extends ViewModel {
6162

6263
_setupNavigation() {
6364
this._hookUpdaterToSegment("details", RoomDetailsViewModel, () => { return {room: this._room}; });
65+
this._hookUpdaterToSegment("invite", InvitePanelViewModel, () => { return {room: this._room}; });
6466
this._hookUpdaterToSegment("members", MemberListViewModel, () => this._getMemberListArguments());
6567
this._hookUpdaterToSegment("member", MemberDetailsViewModel, () => this._getMemberDetailsArguments(),
6668
() => {

src/platform/web/ui/css/layout.css

+2
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,8 @@ the layout viewport up without resizing it when the keyboard shows */
217217

218218
.LazyListParent {
219219
flex: 1;
220+
flex-basis: 0;
221+
margin-top: 15px;
220222
}
221223

222224
.LoadingView {

src/platform/web/ui/css/themes/element/theme.css

+69-1
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,54 @@ button.link {
10131013

10141014
/* Right Panel */
10151015

1016+
.InvitePanelView {
1017+
display: flex;
1018+
flex-direction: column;
1019+
justify-content: center;
1020+
align-items: center;
1021+
}
1022+
1023+
.InvitePanelView__form {
1024+
margin-top: 8px;
1025+
}
1026+
1027+
.InvitePanelView__input {
1028+
font-family: "Inter";
1029+
font-size: 1.3rem;
1030+
font-weight: 500;
1031+
line-height: 1.573rem;
1032+
outline: none;
1033+
border: none;
1034+
background-color: var(--icon-background);
1035+
color: var(--text-color);
1036+
height: 32px;
1037+
box-sizing: border-box;
1038+
margin: 5px;
1039+
border-radius: 16px;
1040+
padding: 15px;
1041+
width: 90%;
1042+
}
1043+
1044+
.InvitePanelView__form,
1045+
.InvitePanelView__btn {
1046+
display: flex;
1047+
flex-direction: column;
1048+
justify-content: center;
1049+
align-items: center;
1050+
}
1051+
1052+
.InvitePanelView__btn {
1053+
width: 100px;
1054+
height: 30px;
1055+
margin-top: 8px;
1056+
}
1057+
1058+
.InvitePanelView__heading {
1059+
width: 90%;
1060+
text-align: center;
1061+
margin: 0;
1062+
}
1063+
10161064
.RightPanelView {
10171065
background: var(--background-color-secondary);
10181066
}
@@ -1123,12 +1171,32 @@ button.RoomDetailsView_row::after {
11231171

11241172
/* Memberlist Panel */
11251173

1126-
.MemberListView {
1174+
.MemberListView__list {
11271175
padding-left: 16px;
11281176
padding-right: 16px;
11291177
margin: 0;
11301178
}
11311179

1180+
.MemberListView {
1181+
display: flex;
1182+
flex-direction: column;
1183+
height: 100%;
1184+
}
1185+
1186+
.MemberListView__invite-container {
1187+
display: flex;
1188+
justify-content: center;
1189+
align-items: center;
1190+
}
1191+
1192+
.MemberListView__invite-btn {
1193+
width: 80%;
1194+
height: 32px;
1195+
display: flex;
1196+
justify-content: center;
1197+
align-items: center;
1198+
}
1199+
11321200
.MemberTileView {
11331201
margin-bottom: 8px;
11341202
list-style: none;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
Copyright 2023 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import {Builder, TemplateView} from "../../general/TemplateView";
18+
import {ErrorView} from "../../general/ErrorView";
19+
import type {InvitePanelViewModel} from "../../../../../domain/session/rightpanel/InvitePanelViewModel";
20+
21+
export class InvitePanelView extends TemplateView<InvitePanelViewModel> {
22+
render(t: Builder<InvitePanelViewModel>, vm: InvitePanelViewModel) {
23+
const input = t.input({
24+
className: "InvitePanelView__input",
25+
type: "text",
26+
placeholder: "Enter user-id of user",
27+
onkeydown: (e: KeyboardEvent) => {
28+
if (e.key === "Enter") {
29+
vm.invite((input as HTMLInputElement).value);
30+
}
31+
}
32+
});
33+
return t.div({ className: "InvitePanelView" }, [
34+
t.h3({ className: "InvitePanelView__heading" },
35+
(vm: InvitePanelViewModel) => vm.i18n`Invite to ${vm.roomName}`
36+
),
37+
t.div({ className: "InvitePanelView__form" }, [
38+
input,
39+
t.button({
40+
className: "InvitePanelView__btn button-action primary",
41+
onClick: () => vm.invite((input as HTMLInputElement).value),
42+
}, "Invite"),
43+
]),
44+
t.div({ className: "InvitePanelView__error" }, [
45+
t.ifView(vm => !!vm.errorViewModel, vm => new ErrorView(vm.errorViewModel!)),
46+
]),
47+
]);
48+
}
49+
50+
}

src/platform/web/ui/session/rightpanel/MemberListView.js

+17-4
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,26 @@ limitations under the License.
1616

1717
import {LazyListView} from "../../general/LazyListView";
1818
import {MemberTileView} from "./MemberTileView.js";
19+
import {TemplateView} from "../../general/TemplateView";
1920

20-
export class MemberListView extends LazyListView {
21-
constructor(vm) {
22-
super({
21+
export class MemberListView extends TemplateView {
22+
render(t, vm) {
23+
const list = new LazyListView({
2324
list: vm.memberTileViewModels,
24-
className: "MemberListView",
25+
className: "MemberListView__list",
2526
itemHeight: 40
2627
}, tileViewModel => new MemberTileView(tileViewModel));
28+
return t.div({ className: "MemberListView" }, [
29+
t.div({ className: "MemberListView__invite-container" }, [
30+
t.button(
31+
{
32+
className: "MemberListView__invite-btn button-action primary",
33+
onClick: () => vm.openInvitePanel(),
34+
},
35+
vm.i18n`Invite to this room`
36+
),
37+
]),
38+
t.view(list),
39+
]);
2740
}
2841
}

src/platform/web/ui/session/rightpanel/RightPanelView.js

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {MemberListView} from "./MemberListView.js";
2020
import {LoadingView} from "../../general/LoadingView.js";
2121
import {MemberDetailsView} from "./MemberDetailsView.js";
2222
import {DeviceVerificationView} from "../verification/DeviceVerificationView";
23+
import {InvitePanelView} from "./InvitePanelView";
2324

2425
export class RightPanelView extends TemplateView {
2526
render(t) {
@@ -40,6 +41,8 @@ export class RightPanelView extends TemplateView {
4041
return new MemberListView(vm);
4142
case "member-details":
4243
return new MemberDetailsView(vm);
44+
case "invite":
45+
return new InvitePanelView(vm);
4346
case "verification":
4447
return new DeviceVerificationView(vm);
4548
default:

0 commit comments

Comments
 (0)