Merge pull request #1135 from vector-im/invite/implement-invite-ui

Invite - PR [2/2] - Add UI for inviting users to room
This commit is contained in:
R Midhun Suresh 2023-09-04 12:53:09 +05:30 committed by GitHub
commit 20106ca742
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 207 additions and 7 deletions

View File

@ -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<SegmentType> | undefined, child: Segment<Se
case "room":
return type === "lightbox" || type === "right-panel";
case "right-panel":
return type === "details"|| type === "members" || type === "member" || type === "verification";
return type === "details"|| type === "members" || type === "member" || type === "verification" || type === "invite";
case "logout":
return type === "forced";
default:
@ -177,7 +178,7 @@ export function parseUrlPath(urlPath: string, currentNavPath: Path<SegmentType>,
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;

View File

@ -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<SegmentType, Options> {
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);
});
}
}

View File

@ -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);
}
}

View File

@ -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(),
() => {

View File

@ -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 {

View File

@ -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);
}
@ -1123,12 +1171,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;

View File

@ -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<InvitePanelViewModel> {
render(t: Builder<InvitePanelViewModel>, 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!)),
]),
]);
}
}

View File

@ -16,13 +16,26 @@ 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",
onClick: () => vm.openInvitePanel(),
},
vm.i18n`Invite to this room`
),
]),
t.view(list),
]);
}
}

View File

@ -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: