mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-11-20 03:25:52 +01:00
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:
commit
20106ca742
@ -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;
|
||||
|
54
src/domain/session/rightpanel/InvitePanelViewModel.ts
Normal file
54
src/domain/session/rightpanel/InvitePanelViewModel.ts
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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(),
|
||||
() => {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
50
src/platform/web/ui/session/rightpanel/InvitePanelView.ts
Normal file
50
src/platform/web/ui/session/rightpanel/InvitePanelView.ts
Normal 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!)),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
@ -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),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user