diff --git a/src/components/AuthComponents.tsx b/src/components/AuthComponents.tsx
new file mode 100644
index 0000000..106f981
--- /dev/null
+++ b/src/components/AuthComponents.tsx
@@ -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 Link = 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;
+`;
diff --git a/src/components/GuildItem.tsx b/src/components/GuildItem.tsx
index 0872609..80b92ae 100644
--- a/src/components/GuildItem.tsx
+++ b/src/components/GuildItem.tsx
@@ -13,7 +13,7 @@ import GuildSidebarListItem from "./GuildSidebarListItem";
import SidebarPill, { PillType } from "./SidebarPill";
import Tooltip from "./Tooltip";
-const Wrapper = styled(Container)<{ active?: boolean }>`
+const Wrapper = styled(Container)<{ active?: boolean; hasImage?: boolean }>`
display: flex;
align-items: center;
justify-content: center;
@@ -21,13 +21,18 @@ const Wrapper = styled(Container)<{ active?: boolean }>`
height: 48px;
border-radius: ${(props) => (props.active ? "30%" : "50%")};
background-color: ${(props) =>
- props.active ? "var(--primary)" : "var(--background-secondary)"};
+ props.hasImage
+ ? "transparent"
+ : props.active
+ ? "var(--primary)"
+ : "var(--background-secondary)"};
transition: border-radius 0.2s ease, background-color 0.2s ease;
cursor: pointer;
&:hover {
border-radius: 30%;
- background-color: var(--primary);
+ background-color: ${(props) =>
+ props.hasImage ? "transparent" : "var(--primary)"};
}
`;
@@ -71,6 +76,7 @@ function GuildItem(props: Props) {
setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
@@ -87,7 +93,13 @@ function GuildItem(props: Props) {
height={48}
/>
) : (
-
+
{guild?.acronym}
)}
diff --git a/src/components/MFA.tsx b/src/components/MFA.tsx
index 461069b..5845bf7 100644
--- a/src/components/MFA.tsx
+++ b/src/components/MFA.tsx
@@ -2,7 +2,7 @@ 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 { ReactComponent as SpacebarLogoBlue } from "../assets/images/logo/Logo-Blue.svg";
import { useAppStore } from "../stores/AppStore";
import {
IAPIError,
@@ -11,154 +11,23 @@ import {
IAPITOTPRequest,
} from "../utils/interfaces/api";
import { messageFromFieldError } from "../utils/messageFromFieldError";
-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-secondary);
-`;
-
-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;
- margin-bottom: 40px;
-`;
-
-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 Link = 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;
-`;
+import {
+ AuthContainer,
+ Divider,
+ FormContainer,
+ Header,
+ HeaderContainer,
+ Input,
+ InputContainer,
+ InputErrorText,
+ InputLabel,
+ InputWrapper,
+ LabelWrapper,
+ Link,
+ SubHeader,
+ SubmitButton,
+ Wrapper,
+} from "./AuthComponents";
type FormValues = {
code: string;
@@ -224,8 +93,9 @@ function MFA(props: IAPILoginResponseMFARequired) {
return (
-
+
+
Two-factor authentication
You can use a backup code or your two-factor
@@ -238,9 +108,7 @@ function MFA(props: IAPILoginResponseMFARequired) {
style={{ marginTop: 0 }}
>
-
- Enter Spacebar Auth/Backup Code
-
+ Enter 2FA/Backup Code
{errors.code && (
<>
@@ -262,13 +130,13 @@ function MFA(props: IAPILoginResponseMFARequired) {
-
Log In
-
+
{/* {
@@ -295,7 +163,7 @@ function MFA(props: IAPILoginResponseMFARequired) {
-
+
);
}
diff --git a/src/components/modals/ForgotPasswordModal.tsx b/src/components/modals/ForgotPasswordModal.tsx
new file mode 100644
index 0000000..badbc28
--- /dev/null
+++ b/src/components/modals/ForgotPasswordModal.tsx
@@ -0,0 +1,81 @@
+import { useModals } from "@mattjennings/react-modal-stack";
+import styled from "styled-components";
+import Icon from "../Icon";
+import {
+ ModalActionItem,
+ ModalCloseWrapper,
+ ModalContainer,
+ ModalFooter,
+ ModalHeaderText,
+ ModalWrapper,
+ ModelContentContainer,
+} from "./ModalComponents";
+
+export const ModalHeader = styled.div`
+ padding: 16px;
+`;
+
+const SubmitButton = styled(ModalActionItem)`
+ transition: background-color 0.2s ease-in-out;
+ font-size: 16px;
+
+ &:hover {
+ background-color: var(--background-secondary-highlight);
+ }
+`;
+
+function ForgotPasswordModal() {
+ const { openModal, closeModal } = useModals();
+
+ if (!open) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+
+ Instructions Sent
+
+
+
+ We sent instructions to change your password to
+ user@example.com, please check both your inbox and spam
+ folder.
+
+
+
+
+ Okay
+
+
+
+
+ );
+}
+
+export default ForgotPasswordModal;
diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx
index 0fdfd2d..00c06fd 100644
--- a/src/pages/LoginPage.tsx
+++ b/src/pages/LoginPage.tsx
@@ -1,14 +1,34 @@
import HCaptchaLib from "@hcaptcha/react-hcaptcha";
+import { useModals } from "@mattjennings/react-modal-stack";
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 { ReactComponent as SpacebarLogoBlue } from "../assets/images/logo/Logo-Blue.svg";
+import {
+ AuthContainer,
+ AuthSwitchPageContainer,
+ AuthSwitchPageLabel,
+ AuthSwitchPageLink,
+ Divider,
+ FormContainer,
+ Header,
+ Input,
+ InputContainer,
+ InputErrorText,
+ InputLabel,
+ InputWrapper,
+ LabelWrapper,
+ SubHeader,
+ SubmitButton,
+ Wrapper,
+} from "../components/AuthComponents";
+import HCaptcha, { HeaderContainer } from "../components/HCaptcha";
import MFA from "../components/MFA";
-import { useAppStore } from "../stores/AppStore";
+import ForgotPasswordModal from "../components/modals/ForgotPasswordModal";
+import { AUTH_NO_BRANDING, useAppStore } from "../stores/AppStore";
+import { Globals } from "../utils/Globals";
+import REST from "../utils/REST";
import {
IAPILoginRequest,
IAPILoginResponse,
@@ -16,153 +36,6 @@ import {
IAPILoginResponseMFARequired,
} from "../utils/interfaces/api";
import { messageFromFieldError } from "../utils/messageFromFieldError";
-import REST from "../utils/REST";
-import { Globals } from "../utils/Globals";
-
-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;
@@ -180,6 +53,7 @@ function LoginPage() {
React.useState();
const captchaRef = React.useRef(null);
const [debounce, setDebounce] = React.useState(null);
+ const { openModal } = useModals();
const {
register,
@@ -297,6 +171,10 @@ function LoginPage() {
onSubmit();
};
+ const forgotPassword = () => {
+ openModal(ForgotPasswordModal);
+ };
+
if (captchaSiteKey) {
return (
-
+
-
- We're so excited to see you again!
+ {AUTH_NO_BRANDING ? (
+ <>
+
+ >
+ ) : (
+ <>
+
+ Log into Spacebar
+ >
+ )}
@@ -394,6 +280,7 @@ function LoginPage() {
-
+
Password
{errors.password && (
@@ -417,6 +304,7 @@ function LoginPage() {
- {
- window.open(
- "https://youtu.be/dQw4w9WgXcQ",
- "_blank",
- );
- }}
- type="button"
- >
+ {/* TODO: I need to figure this out, clicking this should submit the form or even a different function with only email being required */}
+ {/*
Forgot your password?
-
+ */}
-
- Log In
-
+ Login
+
-
-
- Don't have an account?
-
-
+
+ New to Spacebar?
+
+ {
navigate("/register");
}}
type="button"
>
- Sign Up
-
-
+ Register
+
+
-
+
);
}
diff --git a/src/pages/RegistrationPage.tsx b/src/pages/RegistrationPage.tsx
index 33f3735..ac2e48b 100644
--- a/src/pages/RegistrationPage.tsx
+++ b/src/pages/RegistrationPage.tsx
@@ -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 { ReactComponent as SpacebarLogoBlue } from "../assets/images/logo/Logo-Blue.svg";
+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 +32,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 +172,17 @@ function RegistrationPage() {
return (
-
-
-
- {/* We're so excited to see you again! */}
-
+
+ {AUTH_NO_BRANDING ? (
+ <>
+
+ >
+ ) : (
+ <>
+
+ Create an account
+ >
+ )}
@@ -351,6 +252,7 @@ function RegistrationPage() {
-
Create Account
-
+
- {
- navigate("/login", { replace: true });
- }}
- type="button"
- >
- Already have an account?
-
+
+
+ Already have an account?
+
+ {
+ navigate("/login");
+ }}
+ type="button"
+ >
+ Login
+
+
-
+
);
}
diff --git a/src/stores/AppStore.ts b/src/stores/AppStore.ts
index 05d80b5..71d2b00 100644
--- a/src/stores/AppStore.ts
+++ b/src/stores/AppStore.ts
@@ -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;