Clean up registration page

This commit is contained in:
Robert Long
2022-01-04 17:09:27 -08:00
parent ef8c28f274
commit f4936f221f
13 changed files with 299 additions and 341 deletions

63
src/home/CallList.jsx Normal file
View File

@@ -0,0 +1,63 @@
import React from "react";
import { Link } from "react-router-dom";
import { CopyButton } from "../button";
import { Facepile } from "../Facepile";
import { Avatar } from "../Avatar";
import { ReactComponent as VideoIcon } from "../icons/Video.svg";
import styles from "./CallList.module.css";
import { getRoomUrl } from "../ConferenceCallManagerHooks";
import { Body, Caption } from "../typography/Typography";
export function CallList({ rooms, client }) {
return (
<>
<div className={styles.callList}>
{rooms.map(({ roomId, roomName, avatarUrl, participants }) => (
<CallTile
key={roomId}
client={client}
name={roomName}
avatarUrl={avatarUrl}
roomId={roomId}
participants={participants}
/>
))}
</div>
</>
);
}
function CallTile({ name, avatarUrl, roomId, participants, client }) {
return (
<div className={styles.callTile}>
<Link to={`/room/${roomId}`} className={styles.callTileLink}>
<Avatar
size="lg"
bgKey={name}
src={avatarUrl}
fallback={<VideoIcon width={16} height={16} />}
className={styles.avatar}
/>
<div className={styles.callInfo}>
<Body overflowEllipsis fontWeight="semiBold">
{name}
</Body>
<Caption overflowEllipsis>{getRoomUrl(roomId)}</Caption>
{participants && (
<Facepile
className={styles.facePile}
client={client}
participants={participants}
/>
)}
</div>
<div className={styles.copyButtonSpacer} />
</Link>
<CopyButton
className={styles.copyButton}
variant="icon"
value={getRoomUrl(roomId)}
/>
</div>
);
}

View File

@@ -0,0 +1,62 @@
.callTile {
width: 329px;
height: 95px;
padding: 12px;
background-color: var(--bgColor2);
border-radius: 8px;
overflow: hidden;
box-sizing: border-box;
position: relative;
user-select: none;
}
.callTileLink {
display: flex;
text-decoration: none;
width: 100%;
}
.avatar,
.copyButtonSpacer {
flex-shrink: 0;
}
.callInfo {
display: flex;
flex-direction: column;
flex: 1;
padding: 0 16px;
color: var(--textColor1);
min-width: 0;
}
.callInfo > * {
margin-bottom: 0;
}
.facePile {
margin-top: 8px;
}
.copyButtonSpacer,
.copyButton {
width: 16px;
height: 16px;
}
.copyButton {
position: absolute;
top: 12px;
right: 12px;
}
.callList {
display: grid;
grid-template-columns: repeat(auto-fill, 329px);
max-width: calc((329px + 24px) * 3);
width: calc(100% - 48px);
gap: 24px;
padding: 0 24px;
justify-content: center;
margin-bottom: 24px;
}

38
src/home/HomePage.jsx Normal file
View File

@@ -0,0 +1,38 @@
/*
Copyright 2021 New Vector Ltd
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 React from "react";
import { useClient } from "../ConferenceCallManagerHooks";
import { ErrorView, LoadingView } from "../FullScreenView";
import { UnauthenticatedView } from "./UnauthenticatedView";
import { RegisteredView } from "./RegisteredView";
export function HomePage() {
const { isAuthenticated, isPasswordlessUser, loading, error, client } =
useClient();
if (loading) {
return <LoadingView />;
} else if (error) {
return <ErrorView error={error} />;
} else {
return isAuthenticated ? (
<RegisteredView isPasswordlessUser={isPasswordlessUser} client={client} />
) : (
<UnauthenticatedView />
);
}
}

View File

@@ -1,131 +1,117 @@
import React from "react";
import { Link } from "react-router-dom";
import React, { useState, useCallback } from "react";
import {
createRoom,
useGroupCallRooms,
usePublicRooms,
roomAliasFromRoomName,
} from "../ConferenceCallManagerHooks";
import { Header, HeaderLogo, LeftNav, RightNav } from "../Header";
import styles from "../Home.module.css";
import commonStyles from "./common.module.css";
import styles from "./RegisteredView.module.css";
import { FieldRow, InputField, ErrorMessage } from "../Input";
import { Button } from "../button";
import { CallList } from "../CallList";
import classNames from "classnames";
import { CallList } from "./CallList";
import { UserMenuContainer } from "../UserMenuContainer";
import { useModalTriggerState } from "../Modal";
import { JoinExistingCallModal } from "../JoinExistingCallModal";
import { useHistory } from "react-router-dom";
import { Headline, Title } from "../typography/Typography";
import { Form } from "../form/Form";
export function RegisteredView({
client,
isPasswordlessUser,
onCreateRoom,
createRoomError,
creatingRoom,
onJoinRoom,
}) {
const publicRooms = usePublicRooms(
client,
import.meta.env.VITE_PUBLIC_SPACE_ROOM_ID
export function RegisteredView({ client }) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState();
const onSubmit = useCallback(
(e) => {
e.preventDefault();
const data = new FormData(e.target);
const roomName = data.get("roomName");
async function submit() {
setError(undefined);
setLoading(true);
const roomIdOrAlias = await createRoom(client, roomName);
if (roomIdOrAlias) {
history.push(`/room/${roomIdOrAlias}`);
}
}
submit().catch((error) => {
if (error.errcode === "M_ROOM_IN_USE") {
setExistingRoomId(roomAliasFromRoomName(roomName));
setError(undefined);
modalState.open();
} else {
console.error(error);
setLoading(false);
setError(error);
reset();
}
});
},
[client]
);
const recentRooms = useGroupCallRooms(client);
const hideCallList = publicRooms.length === 0 && recentRooms.length === 0;
const { modalState, modalProps } = useModalTriggerState();
const [existingRoomId, setExistingRoomId] = useState();
const history = useHistory();
const onJoinExistingRoom = useCallback(() => {
history.push(`/${existingRoomId}`);
}, [history, existingRoomId]);
return (
<div
className={classNames(styles.home, {
[styles.fullWidth]: hideCallList,
})}
>
<Header className={styles.header}>
<LeftNav className={styles.leftNav}>
<>
<Header>
<LeftNav>
<HeaderLogo />
</LeftNav>
<RightNav>
<UserMenuContainer />
</RightNav>
</Header>
<div className={styles.splitContainer}>
<div className={styles.left}>
<div className={styles.content}>
<div className={styles.centered}>
<form onSubmit={onJoinRoom}>
<h1>Join a call</h1>
<FieldRow className={styles.fieldRow}>
<InputField
id="roomId"
name="roomId"
label="Call ID"
type="text"
required
autoComplete="off"
placeholder="Call ID"
/>
</FieldRow>
<FieldRow className={styles.fieldRow}>
<Button className={styles.button} type="submit">
Join call
</Button>
</FieldRow>
</form>
<hr />
<form onSubmit={onCreateRoom}>
<h1>Create a call</h1>
<FieldRow className={styles.fieldRow}>
<InputField
id="roomName"
name="roomName"
label="Room Name"
type="text"
required
autoComplete="off"
placeholder="Room Name"
/>
</FieldRow>
{createRoomError && (
<FieldRow className={styles.fieldRow}>
<ErrorMessage>{createRoomError.message}</ErrorMessage>
</FieldRow>
)}
<FieldRow className={styles.fieldRow}>
<Button
className={styles.button}
type="submit"
disabled={creatingRoom}
>
{creatingRoom ? "Creating call..." : "Create call"}
</Button>
</FieldRow>
</form>
{isPasswordlessUser && (
<div className={styles.authLinks}>
<p>
Not registered yet?{" "}
<Link to="/register">Create an account</Link>
</p>
</div>
)}
</div>
</div>
</div>
{!hideCallList && (
<div className={styles.right}>
<div className={styles.content}>
{publicRooms.length > 0 && (
<CallList
title="Public Calls"
rooms={publicRooms}
client={client}
/>
)}
{recentRooms.length > 0 && (
<CallList
title="Recent Calls"
rooms={recentRooms}
client={client}
/>
)}
</div>
</div>
)}
<div className={commonStyles.container}>
<main className={commonStyles.main}>
<HeaderLogo className={commonStyles.logo} />
<Headline className={commonStyles.headline}>
Enter a call name
</Headline>
<Form className={styles.form} onSubmit={onSubmit}>
<FieldRow className={styles.fieldRow}>
<InputField
id="callName"
name="callName"
label="Call name"
placeholder="Call name"
type="text"
required
autoComplete="off"
/>
<Button type="submit" size="lg" disabled={loading}>
{loading ? "Loading..." : "Go"}
</Button>
</FieldRow>
{error && (
<FieldRow>
<ErrorMessage>{error.message}</ErrorMessage>
</FieldRow>
)}
</Form>
{recentRooms.length > 0 && (
<>
<Title className={styles.recentCallsTitle}>
Your recent Calls
</Title>
<CallList rooms={recentRooms} client={client} />
</>
)}
</main>
</div>
</div>
{modalState.isOpen && (
<JoinExistingCallModal onJoin={onJoinExistingRoom} {...modalProps} />
)}
</>
);
}

View File

@@ -0,0 +1,14 @@
.form {
padding: 0 24px;
justify-content: center;
width: 409px;
margin-bottom: 72px;
}
.fieldRow {
margin-bottom: 0;
}
.recentCallsTitle {
margin-bottom: 32px;
}

View File

@@ -8,13 +8,15 @@ import { randomString } from "matrix-js-sdk/src/randomstring";
import {
createRoom,
useInteractiveRegistration,
roomAliasFromRoomName,
} from "../ConferenceCallManagerHooks";
import { useModalTriggerState } from "../Modal";
import { JoinExistingCallModal } from "../JoinExistingCallModal";
import { useRecaptcha } from "../useRecaptcha";
import { Body, Caption, Title, Link, Headline } from "../typography/Typography";
import { Body, Caption, Link, Headline } from "../typography/Typography";
import { Form } from "../form/Form";
import styles from "./UnauthenticatedView.module.css";
import commonStyles from "./common.module.css";
export function UnauthenticatedView() {
const [loading, setLoading] = useState(false);
@@ -80,10 +82,12 @@ export function UnauthenticatedView() {
<UserMenuContainer />
</RightNav>
</Header>
<div className={styles.container}>
<main className={styles.main}>
<HeaderLogo className={styles.logo} />
<Headline className={styles.headline}>Enter a call name</Headline>
<div className={commonStyles.container}>
<main className={commonStyles.main}>
<HeaderLogo className={commonStyles.logo} />
<Headline className={commonStyles.headline}>
Enter a call name
</Headline>
<Form className={styles.form} onSubmit={onSubmit}>
<FieldRow>
<InputField

View File

@@ -1,18 +1,3 @@
.container {
display: flex;
min-height: calc(100% - 64px);
flex-direction: column;
justify-content: space-between;
}
.main {
display: flex;
flex: 1;
flex-direction: column;
align-items: center;
justify-content: center;
}
.footer {
display: flex;
flex-direction: column;
@@ -29,15 +14,6 @@
margin-bottom: 24px;
}
.logo {
display: flex;
margin-bottom: 54px;
}
.headline {
margin-bottom: 40px;
}
.form {
padding: 0 24px;
justify-content: center;
@@ -49,15 +25,7 @@
}
@media (min-width: 800px) {
.logo {
display: none;
}
.mobileLoginLink {
display: none;
}
.container {
min-height: calc(100% - 76px);
}
}

View File

@@ -0,0 +1,33 @@
.container {
display: flex;
min-height: calc(100% - 64px);
flex-direction: column;
justify-content: space-between;
}
.main {
display: flex;
flex: 1;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
display: flex;
margin-bottom: 54px;
}
.headline {
margin-bottom: 40px;
}
@media (min-width: 800px) {
.logo {
display: none;
}
.container {
min-height: calc(100% - 76px);
}
}