mirror of
https://github.com/spacebarchat/client.git
synced 2024-11-22 10:22:30 +01:00
auth layout changes
This commit is contained in:
parent
d2a4b4f3cf
commit
be4595ddce
156
src/components/AuthComponents.tsx
Normal file
156
src/components/AuthComponents.tsx
Normal file
@ -0,0 +1,156 @@
|
||||
import styled from "styled-components";
|
||||
import Button from "./Button";
|
||||
import Container from "./Container";
|
||||
|
||||
export const Wrapper = styled(Container)`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background-color: var(--background-tertiary);
|
||||
`;
|
||||
|
||||
export const AuthContainer = styled(Container)`
|
||||
background-color: var(--background-primary-alt);
|
||||
padding: 32px;
|
||||
font-size: 18px;
|
||||
color: var(--text-muted);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
@media (max-width: 480px) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 480px) {
|
||||
width: 480px;
|
||||
border-radius: 18px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const HeaderContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const Header = styled.h1`
|
||||
margin-bottom: 3px;
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
font-size: 24px;
|
||||
`;
|
||||
|
||||
export const SubHeader = styled.h2<{ noBranding?: boolean }>`
|
||||
margin-top: 3px;
|
||||
color: var(--text-muted);
|
||||
font-weight: 400;
|
||||
font-size: ${(props) => (props.noBranding ? "20px" : "16px")};
|
||||
`;
|
||||
|
||||
export const FormContainer = styled.form`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const InputContainer = styled.h1<{ marginBottom: boolean }>`
|
||||
margin-bottom: ${(props) => (props.marginBottom ? "20px" : "0")};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
`;
|
||||
|
||||
export const LabelWrapper = styled.div<{ error?: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 8px;
|
||||
color: ${(props) => (props.error ? "var(--error)" : "var(--text)")};
|
||||
`;
|
||||
|
||||
export const InputErrorText = styled.label`
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
`;
|
||||
|
||||
export const InputLabel = styled.label`
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
`;
|
||||
|
||||
export const InputWrapper = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
// TODO: Fix border hover causing small layout shift
|
||||
export const Input = styled.input<{ error?: boolean }>`
|
||||
outline: none;
|
||||
background: var(--background-secondary);
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
flex: 1;
|
||||
border-radius: 12px;
|
||||
color: var(--text);
|
||||
margin: 0;
|
||||
border: none;
|
||||
aria-invalid: ${(props) => (props.error ? "true" : "false")};
|
||||
border: ${(props) => (props.error ? "1px solid var(--error)" : "none")};
|
||||
|
||||
&:focus {
|
||||
border: 1px solid var(--primary);
|
||||
}
|
||||
`;
|
||||
|
||||
export const PasswordResetLink = styled.button`
|
||||
margin-bottom: 20px;
|
||||
margin-top: 4px;
|
||||
padding: 2px 0;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
color: var(--text-link);
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export const SubmitButton = styled(Button)`
|
||||
margin-bottom: 8px;
|
||||
width: 100%;
|
||||
min-width: 130px;
|
||||
min-height: 44px;
|
||||
font-size: 14px;
|
||||
`;
|
||||
|
||||
export const AuthSwitchPageContainer = styled.div`
|
||||
margin-top: 4px;
|
||||
text-align: initial;
|
||||
`;
|
||||
|
||||
export const AuthSwitchPageLabel = styled.label`
|
||||
font-size: 14px;
|
||||
`;
|
||||
|
||||
export const AuthSwitchPageLink = styled.button`
|
||||
font-size: 14px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-link);
|
||||
|
||||
@media (max-width: 480px) {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Divider = styled.span`
|
||||
padding: 0 4px;
|
||||
`;
|
@ -3,12 +3,28 @@ import { Routes } from "@spacebarchat/spacebar-api-types/v9";
|
||||
import React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import Button from "../components/Button";
|
||||
import Container from "../components/Container";
|
||||
import HCaptcha from "../components/HCaptcha";
|
||||
import {
|
||||
AuthContainer,
|
||||
AuthSwitchPageContainer,
|
||||
AuthSwitchPageLabel,
|
||||
AuthSwitchPageLink,
|
||||
Divider,
|
||||
FormContainer,
|
||||
Header,
|
||||
Input,
|
||||
InputContainer,
|
||||
InputErrorText,
|
||||
InputLabel,
|
||||
InputWrapper,
|
||||
LabelWrapper,
|
||||
PasswordResetLink,
|
||||
SubHeader,
|
||||
SubmitButton,
|
||||
Wrapper,
|
||||
} from "../components/AuthComponents";
|
||||
import HCaptcha, { HeaderContainer } from "../components/HCaptcha";
|
||||
import MFA from "../components/MFA";
|
||||
import { useAppStore } from "../stores/AppStore";
|
||||
import { AUTH_NO_BRANDING, useAppStore } from "../stores/AppStore";
|
||||
import {
|
||||
IAPILoginRequest,
|
||||
IAPILoginResponse,
|
||||
@ -17,151 +33,6 @@ import {
|
||||
} from "../utils/interfaces/api";
|
||||
import { messageFromFieldError } from "../utils/messageFromFieldError";
|
||||
|
||||
export const Wrapper = styled(Container)`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background-color: var(--background-tertiary);
|
||||
`;
|
||||
|
||||
export const AuthBox = styled(Container)`
|
||||
background-color: var(--background-primary-alt);
|
||||
padding: 32px;
|
||||
font-size: 18px;
|
||||
color: var(--text-muted);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
@media (max-width: 480px) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 480px) {
|
||||
width: 480px;
|
||||
border-radius: 18px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const HeaderContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const Header = styled.h1`
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
font-size: 24px;
|
||||
color: var(--text);
|
||||
`;
|
||||
|
||||
export const SubHeader = styled.h2`
|
||||
color: var(--text-muted);
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
`;
|
||||
|
||||
export const FormContainer = styled.form`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const InputContainer = styled.h1<{ marginBottom: boolean }>`
|
||||
margin-bottom: ${(props) => (props.marginBottom ? "20px" : "0")};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
`;
|
||||
|
||||
export const LabelWrapper = styled.div<{ error?: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 8px;
|
||||
color: ${(props) => (props.error ? "var(--error)" : "#b1b5bc")};
|
||||
`;
|
||||
|
||||
export const InputErrorText = styled.label`
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
`;
|
||||
|
||||
export const InputLabel = styled.label`
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
`;
|
||||
|
||||
export const InputWrapper = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const Input = styled.input<{ error?: boolean }>`
|
||||
outline: none;
|
||||
background: var(--background-secondary);
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
flex: 1;
|
||||
border-radius: 12px;
|
||||
color: var(--text);
|
||||
margin: 0;
|
||||
border: none;
|
||||
aria-invalid: ${(props) => (props.error ? "true" : "false")};
|
||||
`;
|
||||
|
||||
export const PasswordResetLink = styled.button`
|
||||
margin-bottom: 20px;
|
||||
margin-top: 4px;
|
||||
padding: 2px 0;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
color: var(--text-link);
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export const LoginButton = styled(Button)`
|
||||
margin-bottom: 8px;
|
||||
width: 100%;
|
||||
min-width: 130px;
|
||||
min-height: 44px;
|
||||
`;
|
||||
|
||||
export const RegisterContainer = styled.div`
|
||||
margin-top: 4px;
|
||||
text-align: initial;
|
||||
`;
|
||||
|
||||
export const RegisterLabel = styled.label`
|
||||
font-size: 14px;
|
||||
`;
|
||||
|
||||
export const RegisterLink = styled.button`
|
||||
font-size: 14px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-link);
|
||||
|
||||
@media (max-width: 480px) {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Divider = styled.span`
|
||||
padding: 0 4px;
|
||||
`;
|
||||
|
||||
type FormValues = {
|
||||
login: string;
|
||||
password: string;
|
||||
@ -299,10 +170,22 @@ function LoginPage() {
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<AuthBox>
|
||||
<AuthContainer>
|
||||
<HeaderContainer>
|
||||
<Header>Welcome Back!</Header>
|
||||
<SubHeader>We're so excited to see you again!</SubHeader>
|
||||
{AUTH_NO_BRANDING ? (
|
||||
<>
|
||||
<Header>Login to Spacebar</Header>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/* Note: This would need to change depending on the theme */}
|
||||
<img
|
||||
src="https://github.com/spacebarchat/spacebarchat/blob/master/branding/png/Spacebar__Logo-White.png?raw=true"
|
||||
height={48}
|
||||
/>
|
||||
<SubHeader noBranding>Log into Spacebar</SubHeader>
|
||||
</>
|
||||
)}
|
||||
</HeaderContainer>
|
||||
|
||||
<FormContainer onSubmit={onSubmit}>
|
||||
@ -324,6 +207,7 @@ function LoginPage() {
|
||||
<InputWrapper>
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
autoFocus
|
||||
{...register("login", { required: true })}
|
||||
error={!!errors.login}
|
||||
@ -347,6 +231,7 @@ function LoginPage() {
|
||||
<InputWrapper>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
{...register("password", { required: true })}
|
||||
error={!!errors.password}
|
||||
disabled={loading}
|
||||
@ -366,29 +251,29 @@ function LoginPage() {
|
||||
Forgot your password?
|
||||
</PasswordResetLink>
|
||||
|
||||
<LoginButton
|
||||
<SubmitButton
|
||||
variant="primary"
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
>
|
||||
Log In
|
||||
</LoginButton>
|
||||
Login
|
||||
</SubmitButton>
|
||||
|
||||
<RegisterContainer>
|
||||
<RegisterLabel>
|
||||
Don't have an account?
|
||||
</RegisterLabel>
|
||||
<RegisterLink
|
||||
<AuthSwitchPageContainer>
|
||||
<AuthSwitchPageLabel>
|
||||
New to Spacebar?
|
||||
</AuthSwitchPageLabel>
|
||||
<AuthSwitchPageLink
|
||||
onClick={() => {
|
||||
navigate("/register");
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
Sign Up
|
||||
</RegisterLink>
|
||||
</RegisterContainer>
|
||||
Register
|
||||
</AuthSwitchPageLink>
|
||||
</AuthSwitchPageContainer>
|
||||
</FormContainer>
|
||||
</AuthBox>
|
||||
</AuthContainer>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
@ -3,12 +3,27 @@ import { Routes } from "@spacebarchat/spacebar-api-types/v9";
|
||||
import React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import Button from "../components/Button";
|
||||
import Container from "../components/Container";
|
||||
import {
|
||||
AuthContainer,
|
||||
AuthSwitchPageContainer,
|
||||
AuthSwitchPageLabel,
|
||||
AuthSwitchPageLink,
|
||||
Divider,
|
||||
FormContainer,
|
||||
Header,
|
||||
Input,
|
||||
InputContainer,
|
||||
InputErrorText,
|
||||
InputLabel,
|
||||
InputWrapper,
|
||||
LabelWrapper,
|
||||
SubHeader,
|
||||
SubmitButton,
|
||||
Wrapper,
|
||||
} from "../components/AuthComponents";
|
||||
import DOBInput from "../components/DOBInput";
|
||||
import HCaptcha from "../components/HCaptcha";
|
||||
import { useAppStore } from "../stores/AppStore";
|
||||
import { AUTH_NO_BRANDING, useAppStore } from "../stores/AppStore";
|
||||
import {
|
||||
IAPILoginResponseSuccess,
|
||||
IAPIRegisterRequest,
|
||||
@ -16,129 +31,6 @@ import {
|
||||
} from "../utils/interfaces/api";
|
||||
import { messageFromFieldError } from "../utils/messageFromFieldError";
|
||||
|
||||
const Wrapper = styled(Container)`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background-color: var(--background-tertiary);
|
||||
`;
|
||||
|
||||
const AuthBox = styled(Container)`
|
||||
background-color: var(--background-primary-alt);
|
||||
padding: 32px;
|
||||
font-size: 18px;
|
||||
color: var(--text-muted);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
@media (max-width: 480px) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 480px) {
|
||||
width: 480px;
|
||||
border-radius: 18px;
|
||||
}
|
||||
`;
|
||||
|
||||
const HeaderContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const Header = styled.h1`
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
font-size: 24px;
|
||||
color: var(--text);
|
||||
`;
|
||||
|
||||
// const SubHeader = styled.h2`
|
||||
// color: var(--text-muted);
|
||||
// font-weight: 400;
|
||||
// font-size: 16px;
|
||||
// `;
|
||||
|
||||
const FormContainer = styled.form`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const InputContainer = styled.h1<{ marginBottom: boolean }>`
|
||||
margin-bottom: ${(props) => (props.marginBottom ? "20px" : "0")};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
`;
|
||||
|
||||
const LabelWrapper = styled.div<{ error?: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 8px;
|
||||
color: ${(props) => (props.error ? "var(--error)" : "#b1b5bc")};
|
||||
`;
|
||||
|
||||
const InputErrorText = styled.label`
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
`;
|
||||
|
||||
const InputLabel = styled.label`
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
`;
|
||||
|
||||
const InputWrapper = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const Input = styled.input<{ error?: boolean }>`
|
||||
outline: none;
|
||||
background: var(--background-secondary);
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
border-radius: 12px;
|
||||
color: var(--text);
|
||||
margin: 0;
|
||||
border: none;
|
||||
aria-invalid: ${(props) => (props.error ? "true" : "false")};
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const LoginButton = styled(Button)`
|
||||
margin-bottom: 8px;
|
||||
width: 100%;
|
||||
min-width: 130px;
|
||||
min-height: 44px;
|
||||
`;
|
||||
|
||||
const LoginLink = styled.button`
|
||||
margin-top: 4px;
|
||||
float: left;
|
||||
font-size: 14px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-link);
|
||||
|
||||
@media (max-width: 480px) {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
const Divider = styled.span`
|
||||
padding: 0 4px;
|
||||
`;
|
||||
|
||||
type FormValues = {
|
||||
email: string;
|
||||
username: string;
|
||||
@ -279,11 +171,21 @@ function RegistrationPage() {
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<AuthBox>
|
||||
<HeaderContainer>
|
||||
<Header>Create an account</Header>
|
||||
{/* <SubHeader>We're so excited to see you again!</SubHeader> */}
|
||||
</HeaderContainer>
|
||||
<AuthContainer>
|
||||
{AUTH_NO_BRANDING ? (
|
||||
<>
|
||||
<Header>Create an account</Header>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/* Note: This would need to change depending on the theme */}
|
||||
<img
|
||||
src="https://github.com/spacebarchat/spacebarchat/blob/master/branding/png/Spacebar__Logo-White.png?raw=true"
|
||||
height={48}
|
||||
/>
|
||||
<SubHeader noBranding>Create an account</SubHeader>
|
||||
</>
|
||||
)}
|
||||
|
||||
<FormContainer onSubmit={onSubmit}>
|
||||
<InputContainer
|
||||
@ -304,6 +206,7 @@ function RegistrationPage() {
|
||||
<InputWrapper>
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
autoFocus
|
||||
{...register("email", { required: true })}
|
||||
error={!!errors.email}
|
||||
@ -330,6 +233,7 @@ function RegistrationPage() {
|
||||
<InputWrapper>
|
||||
<Input
|
||||
{...register("username", { required: true })}
|
||||
placeholder="Username"
|
||||
error={!!errors.username}
|
||||
disabled={loading}
|
||||
/>
|
||||
@ -351,6 +255,7 @@ function RegistrationPage() {
|
||||
<InputWrapper>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
{...register("password", { required: true })}
|
||||
error={!!errors.password}
|
||||
disabled={loading}
|
||||
@ -396,24 +301,29 @@ function RegistrationPage() {
|
||||
</InputWrapper>
|
||||
</InputContainer>
|
||||
|
||||
<LoginButton
|
||||
<SubmitButton
|
||||
variant="primary"
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
>
|
||||
Create Account
|
||||
</LoginButton>
|
||||
</SubmitButton>
|
||||
|
||||
<LoginLink
|
||||
onClick={() => {
|
||||
navigate("/login", { replace: true });
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
Already have an account?
|
||||
</LoginLink>
|
||||
<AuthSwitchPageContainer>
|
||||
<AuthSwitchPageLabel>
|
||||
Already have an account?
|
||||
</AuthSwitchPageLabel>
|
||||
<AuthSwitchPageLink
|
||||
onClick={() => {
|
||||
navigate("/login");
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
Login
|
||||
</AuthSwitchPageLink>
|
||||
</AuthSwitchPageContainer>
|
||||
</FormContainer>
|
||||
</AuthBox>
|
||||
</AuthContainer>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
@ -12,6 +12,9 @@ import PrivateChannelStore from "./PrivateChannelStore";
|
||||
import ThemeStore from "./ThemeStore";
|
||||
import UserStore from "./UserStore";
|
||||
|
||||
// dev thing to force toggle branding on auth pages for testing.
|
||||
export const AUTH_NO_BRANDING = false;
|
||||
|
||||
export default class AppStore {
|
||||
// whether the gateway is ready
|
||||
@observable isGatewayReady = false;
|
||||
|
Loading…
Reference in New Issue
Block a user