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;
|
"details": true;
|
||||||
"members": true;
|
"members": true;
|
||||||
"member": string;
|
"member": string;
|
||||||
|
"invite": true;
|
||||||
"device-verification": string | boolean;
|
"device-verification": string | boolean;
|
||||||
"verification": string | boolean;
|
"verification": string | boolean;
|
||||||
"join-room": true;
|
"join-room": true;
|
||||||
@ -61,7 +62,7 @@ function allowsChild(parent: Segment<SegmentType> | undefined, child: Segment<Se
|
|||||||
case "room":
|
case "room":
|
||||||
return type === "lightbox" || type === "right-panel";
|
return type === "lightbox" || type === "right-panel";
|
||||||
case "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":
|
case "logout":
|
||||||
return type === "forced";
|
return type === "forced";
|
||||||
default:
|
default:
|
||||||
@ -177,7 +178,7 @@ export function parseUrlPath(urlPath: string, currentNavPath: Path<SegmentType>,
|
|||||||
if (sessionSegment) {
|
if (sessionSegment) {
|
||||||
segments.push(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);
|
pushRightPanelSegment(segments, type);
|
||||||
} else if (type === "member") {
|
} else if (type === "member") {
|
||||||
let userId = iterator.next().value;
|
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);
|
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 {RoomDetailsViewModel} from "./RoomDetailsViewModel.js";
|
||||||
import {MemberListViewModel} from "./MemberListViewModel.js";
|
import {MemberListViewModel} from "./MemberListViewModel.js";
|
||||||
import {MemberDetailsViewModel} from "./MemberDetailsViewModel.js";
|
import {MemberDetailsViewModel} from "./MemberDetailsViewModel.js";
|
||||||
|
import {InvitePanelViewModel} from "./InvitePanelViewModel";
|
||||||
import {DeviceVerificationViewModel} from "../verification/DeviceVerificationViewModel";
|
import {DeviceVerificationViewModel} from "../verification/DeviceVerificationViewModel";
|
||||||
|
|
||||||
export class RightPanelViewModel extends ViewModel {
|
export class RightPanelViewModel extends ViewModel {
|
||||||
@ -61,6 +62,7 @@ export class RightPanelViewModel extends ViewModel {
|
|||||||
|
|
||||||
_setupNavigation() {
|
_setupNavigation() {
|
||||||
this._hookUpdaterToSegment("details", RoomDetailsViewModel, () => { return {room: this._room}; });
|
this._hookUpdaterToSegment("details", RoomDetailsViewModel, () => { return {room: this._room}; });
|
||||||
|
this._hookUpdaterToSegment("invite", InvitePanelViewModel, () => { return {room: this._room}; });
|
||||||
this._hookUpdaterToSegment("members", MemberListViewModel, () => this._getMemberListArguments());
|
this._hookUpdaterToSegment("members", MemberListViewModel, () => this._getMemberListArguments());
|
||||||
this._hookUpdaterToSegment("member", MemberDetailsViewModel, () => this._getMemberDetailsArguments(),
|
this._hookUpdaterToSegment("member", MemberDetailsViewModel, () => this._getMemberDetailsArguments(),
|
||||||
() => {
|
() => {
|
||||||
|
@ -217,6 +217,8 @@ the layout viewport up without resizing it when the keyboard shows */
|
|||||||
|
|
||||||
.LazyListParent {
|
.LazyListParent {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
flex-basis: 0;
|
||||||
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.LoadingView {
|
.LoadingView {
|
||||||
|
@ -1013,6 +1013,54 @@ button.link {
|
|||||||
|
|
||||||
/* Right Panel */
|
/* 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 {
|
.RightPanelView {
|
||||||
background: var(--background-color-secondary);
|
background: var(--background-color-secondary);
|
||||||
}
|
}
|
||||||
@ -1123,12 +1171,32 @@ button.RoomDetailsView_row::after {
|
|||||||
|
|
||||||
/* Memberlist Panel */
|
/* Memberlist Panel */
|
||||||
|
|
||||||
.MemberListView {
|
.MemberListView__list {
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
margin: 0;
|
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 {
|
.MemberTileView {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
list-style: none;
|
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 {LazyListView} from "../../general/LazyListView";
|
||||||
import {MemberTileView} from "./MemberTileView.js";
|
import {MemberTileView} from "./MemberTileView.js";
|
||||||
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
|
|
||||||
export class MemberListView extends LazyListView {
|
export class MemberListView extends TemplateView {
|
||||||
constructor(vm) {
|
render(t, vm) {
|
||||||
super({
|
const list = new LazyListView({
|
||||||
list: vm.memberTileViewModels,
|
list: vm.memberTileViewModels,
|
||||||
className: "MemberListView",
|
className: "MemberListView__list",
|
||||||
itemHeight: 40
|
itemHeight: 40
|
||||||
}, tileViewModel => new MemberTileView(tileViewModel));
|
}, 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 {LoadingView} from "../../general/LoadingView.js";
|
||||||
import {MemberDetailsView} from "./MemberDetailsView.js";
|
import {MemberDetailsView} from "./MemberDetailsView.js";
|
||||||
import {DeviceVerificationView} from "../verification/DeviceVerificationView";
|
import {DeviceVerificationView} from "../verification/DeviceVerificationView";
|
||||||
|
import {InvitePanelView} from "./InvitePanelView";
|
||||||
|
|
||||||
export class RightPanelView extends TemplateView {
|
export class RightPanelView extends TemplateView {
|
||||||
render(t) {
|
render(t) {
|
||||||
@ -40,6 +41,8 @@ export class RightPanelView extends TemplateView {
|
|||||||
return new MemberListView(vm);
|
return new MemberListView(vm);
|
||||||
case "member-details":
|
case "member-details":
|
||||||
return new MemberDetailsView(vm);
|
return new MemberDetailsView(vm);
|
||||||
|
case "invite":
|
||||||
|
return new InvitePanelView(vm);
|
||||||
case "verification":
|
case "verification":
|
||||||
return new DeviceVerificationView(vm);
|
return new DeviceVerificationView(vm);
|
||||||
default:
|
default:
|
||||||
|
Loading…
Reference in New Issue
Block a user