Redesign homepage WIP

This commit is contained in:
Robert Long
2022-01-04 16:00:13 -08:00
parent eb620e9220
commit ef8c28f274
18 changed files with 697 additions and 437 deletions

View File

@@ -14,399 +14,25 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { useCallback, useState, useRef, useEffect } from "react";
import { useHistory, Link } from "react-router-dom";
import {
useClient,
useGroupCallRooms,
usePublicRooms,
createRoom,
roomAliasFromRoomName,
useInteractiveRegistration,
} from "./ConferenceCallManagerHooks";
import { Header, HeaderLogo, LeftNav, RightNav } from "./Header";
import styles from "./Home.module.css";
import { FieldRow, InputField, ErrorMessage } from "./Input";
import { UserMenu } from "./UserMenu";
import { Button } from "./button";
import { CallList } from "./CallList";
import classNames from "classnames";
import React from "react";
import { useClient } from "./ConferenceCallManagerHooks";
import { ErrorView, LoadingView } from "./FullScreenView";
import { useModalTriggerState } from "./Modal";
import { randomString } from "matrix-js-sdk/src/randomstring";
import { JoinExistingCallModal } from "./JoinExistingCallModal";
import { RecaptchaInput } from "./RecaptchaInput";
import { UserMenuContainer } from "./UserMenuContainer";
import { UnauthenticatedView } from "./home/UnauthenticatedView";
import { RegisteredView } from "./home/RegisteredView";
export function Home() {
const {
isAuthenticated,
isGuest,
isPasswordlessUser,
loading,
error,
client,
} = useClient();
const [{ privacyPolicyUrl, recaptchaKey }, register] =
useInteractiveRegistration();
const [recaptchaResponse, setRecaptchaResponse] = useState();
const history = useHistory();
const [creatingRoom, setCreatingRoom] = useState(false);
const [createRoomError, setCreateRoomError] = useState();
const { modalState, modalProps } = useModalTriggerState();
const [existingRoomId, setExistingRoomId] = useState();
const onCreateRoom = useCallback(
(e) => {
e.preventDefault();
const data = new FormData(e.target);
const roomName = data.get("roomName");
const userName = data.get("userName");
async function onCreateRoom() {
let _client = client;
if (!recaptchaResponse) {
return;
}
if (!_client || isGuest) {
_client = await register(
userName,
randomString(16),
recaptchaResponse,
true
);
}
const roomIdOrAlias = await createRoom(_client, roomName);
if (roomIdOrAlias) {
history.push(`/room/${roomIdOrAlias}`);
}
}
setCreateRoomError(undefined);
setCreatingRoom(true);
return onCreateRoom().catch((error) => {
if (error.errcode === "M_ROOM_IN_USE") {
setExistingRoomId(roomAliasFromRoomName(roomName));
setCreateRoomError(undefined);
modalState.open();
} else {
setCreateRoomError(error);
}
setCreatingRoom(false);
});
},
[client, history, register, isGuest, recaptchaResponse]
);
const onJoinRoom = useCallback(
(e) => {
e.preventDefault();
const data = new FormData(e.target);
const roomId = data.get("roomId");
history.push(`/${roomId}`);
},
[history]
);
const onJoinExistingRoom = useCallback(() => {
history.push(`/${existingRoomId}`);
}, [history, existingRoomId]);
const { isAuthenticated, isPasswordlessUser, loading, error, client } =
useClient();
if (loading) {
return <LoadingView />;
} else if (error) {
return <ErrorView error={error} />;
} else {
return (
<>
{!isAuthenticated || isGuest ? (
<UnregisteredView
onCreateRoom={onCreateRoom}
createRoomError={createRoomError}
creatingRoom={creatingRoom}
onJoinRoom={onJoinRoom}
privacyPolicyUrl={privacyPolicyUrl}
recaptchaKey={recaptchaKey}
setRecaptchaResponse={setRecaptchaResponse}
/>
) : (
<RegisteredView
client={client}
isPasswordlessUser={isPasswordlessUser}
isGuest={isGuest}
onCreateRoom={onCreateRoom}
createRoomError={createRoomError}
creatingRoom={creatingRoom}
onJoinRoom={onJoinRoom}
/>
)}
{modalState.isOpen && (
<JoinExistingCallModal onJoin={onJoinExistingRoom} {...modalProps} />
)}
</>
return isAuthenticated ? (
<RegisteredView isPasswordlessUser={isPasswordlessUser} client={client} />
) : (
<UnauthenticatedView />
);
}
}
function UnregisteredView({
onCreateRoom,
createRoomError,
creatingRoom,
onJoinRoom,
privacyPolicyUrl,
recaptchaKey,
setRecaptchaResponse,
}) {
const acceptTermsRef = useRef();
const [acceptTerms, setAcceptTerms] = useState(false);
useEffect(() => {
if (!acceptTermsRef.current) {
return;
}
if (!acceptTerms) {
acceptTermsRef.current.setCustomValidity(
"You must accept the terms to continue."
);
} else {
acceptTermsRef.current.setCustomValidity("");
}
}, [acceptTerms]);
return (
<div className={classNames(styles.home, styles.fullWidth)}>
<Header className={styles.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="userName"
name="userName"
label="Username"
type="text"
required
autoComplete="off"
placeholder="Username"
/>
</FieldRow>
<FieldRow className={styles.fieldRow}>
<InputField
id="roomName"
name="roomName"
label="Room Name"
type="text"
required
autoComplete="off"
placeholder="Room Name"
/>
</FieldRow>
<FieldRow>
<InputField
id="acceptTerms"
type="checkbox"
name="acceptTerms"
onChange={(e) => setAcceptTerms(e.target.checked)}
checked={acceptTerms}
label="Accept Privacy Policy"
ref={acceptTermsRef}
/>
<a target="_blank" href={privacyPolicyUrl}>
Privacy Policy
</a>
</FieldRow>
{recaptchaKey && (
<FieldRow>
<RecaptchaInput
publicKey={recaptchaKey}
onResponse={setRecaptchaResponse}
/>
</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>
<div className={styles.authLinks}>
<p>
Not registered yet?{" "}
<Link to="/register">Create an account</Link>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
function RegisteredView({
client,
isPasswordlessUser,
isGuest,
onCreateRoom,
createRoomError,
creatingRoom,
onJoinRoom,
}) {
const publicRooms = usePublicRooms(
client,
import.meta.env.VITE_PUBLIC_SPACE_ROOM_ID
);
const recentRooms = useGroupCallRooms(client);
const hideCallList = publicRooms.length === 0 && recentRooms.length === 0;
return (
<div
className={classNames(styles.home, {
[styles.fullWidth]: hideCallList,
})}
>
<Header className={styles.header}>
<LeftNav className={styles.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 || isGuest) && (
<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>
</div>
);
}