diff --git a/packages/components/src/api/account/CreateSession.ts b/packages/components/src/api/account/CreateSession.ts new file mode 100644 index 0000000000..3a25f928ab --- /dev/null +++ b/packages/components/src/api/account/CreateSession.ts @@ -0,0 +1,17 @@ +import MakeRequest from "../MakeRequest"; + +export interface CreateSessionRequest { + username_or_email: string, + password: string, +} + +export interface CreateSessionResponse { + success: boolean, + error_type?: string, + error_message?: string, +} + +export const CreateSession = MakeRequest({ + method: "POST", + routingFunction: () => "/v1/login", +}); diff --git a/packages/fakeyou.com/src/App.tsx b/packages/fakeyou.com/src/App.tsx index e3e5dcdff4..532d5c733f 100644 --- a/packages/fakeyou.com/src/App.tsx +++ b/packages/fakeyou.com/src/App.tsx @@ -842,6 +842,7 @@ class App extends React.Component { { label } : null } - { invalidReason ? + { invalidReason ? { invalidReason } : null } ; diff --git a/packages/fakeyou.com/src/components/layout/AccountModal/AccountModal.scss b/packages/fakeyou.com/src/components/layout/AccountModal/AccountModal.scss index a9039f6d74..2716f9a150 100644 --- a/packages/fakeyou.com/src/components/layout/AccountModal/AccountModal.scss +++ b/packages/fakeyou.com/src/components/layout/AccountModal/AccountModal.scss @@ -48,12 +48,19 @@ transition: opacity .25s ease-in-out; } + .error-message { + color: $primary; + margin-bottom: 1rem; + text-align: center; + } + } .modal-spinner { height: 300px; display: flex; + flex-direction: column; align-items: center; justify-content: center; } diff --git a/packages/fakeyou.com/src/components/layout/AccountModal/AccountModal.tsx b/packages/fakeyou.com/src/components/layout/AccountModal/AccountModal.tsx index 2bbb673e22..fa6f067ac9 100644 --- a/packages/fakeyou.com/src/components/layout/AccountModal/AccountModal.tsx +++ b/packages/fakeyou.com/src/components/layout/AccountModal/AccountModal.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import { a, useTransition } from "@react-spring/web"; import { FetchStatus } from "@storyteller/components/src/api/_common/SharedFetchTypes"; import { Spinner } from "components/common"; +import useLogin from "./useLogin"; import useSignup from "./useSignup"; import LoginView from "./LoginView"; import SignupView from "./SignupView"; @@ -22,13 +23,26 @@ const AniMod = ({ animating, isLeaving, render: Render, style, ...rest }: AniPro ; -const Loader = () =>
; +const Loader = ({ viewLogin }: { viewLogin: boolean }) =>
+

{ + viewLogin ? "Logging you in" : "Creating your account" + }

+ +
; export default function LoginModal({ handleClose }: { handleClose: any }) { const [status,statusSet] = useState(FetchStatus.paused); const [viewLogin,viewLoginSet] = useState(false); const [animating,animatingSet] = useState(false); - const { setProps, signup } = useSignup({ + const { errorType, setProps: loginProps, login } = useLogin({ + onSuccess: () => { + Analytics.accountLoginSuccess(); + handleClose(); + }, + status, + statusSet + }); + const { setProps: signupProps, signup } = useSignup({ onSuccess: () => { Analytics.accountSignupAttempt(); handleClose(); @@ -54,13 +68,13 @@ export default function LoginModal({ handleClose }: { handleClose: any }) { { transitions((style: any, i: number, state: any) => { let isLeaving = state.phase === "leave"; + let sharedProps = { animating, handleClose, isLeaving, style, viewLogin, viewLoginSet }; switch(i) { - case 0: return ; - case 1: return ; - case 2: return ; + case 0: return ; + case 1: return ; + case 2: return ; } - }) } diff --git a/packages/fakeyou.com/src/components/layout/AccountModal/LoginView.tsx b/packages/fakeyou.com/src/components/layout/AccountModal/LoginView.tsx index 6282ff11bc..3aee97dea8 100644 --- a/packages/fakeyou.com/src/components/layout/AccountModal/LoginView.tsx +++ b/packages/fakeyou.com/src/components/layout/AccountModal/LoginView.tsx @@ -1,16 +1,27 @@ import React from 'react'; import { TempInput } from "components/common"; -import { faUser, faEnvelope, faKey } from "@fortawesome/free-solid-svg-icons"; +import { faUser, faKey } from "@fortawesome/free-solid-svg-icons"; + +enum ErrorTypes { + InvalidCredentials = "abc", + ServerError = "abc" +} interface Props { animating: boolean, + errorType?: string, handleClose: (x:any) => any, - setProps: (x:any) => any, - signup: (x:any) => any, + login: (x:any) => any, + loginProps: (x:any) => any, viewLoginSet: (x:any) => void } -export default function LoginView({ animating, handleClose, setProps, signup, viewLoginSet }: Props) { +export default function LoginView({ animating, errorType = "", handleClose, login, loginProps, viewLoginSet }: Props) { + const errorStrings = [ + "Could not login, check credentials and try again.", + "A sever error occured. Try again." + ]; + return
@@ -25,8 +36,9 @@ export default function LoginView({ animating, handleClose, setProps, signup, vi }}>Signup instead
- - - + { errorType ?

{ errorStrings[Object.keys(ErrorTypes).indexOf(errorType)] }

: null } + + +
; }; \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/layout/AccountModal/SignupView.tsx b/packages/fakeyou.com/src/components/layout/AccountModal/SignupView.tsx index 6257e396b9..d906c95a6e 100644 --- a/packages/fakeyou.com/src/components/layout/AccountModal/SignupView.tsx +++ b/packages/fakeyou.com/src/components/layout/AccountModal/SignupView.tsx @@ -5,12 +5,12 @@ import { faUser, faEnvelope, faKey } from "@fortawesome/free-solid-svg-icons"; interface Props { animating: boolean, handleClose: (x:any) => any, - setProps: (x:any) => any, + signupProps: (x:any) => any, signup: (x:any) => any, viewLoginSet: (x:any) => void } -export default function LoginView({ animating, handleClose, setProps, signup, viewLoginSet }: Props) { +export default function LoginView({ animating, handleClose, signupProps, signup, viewLoginSet }: Props) { return
@@ -22,10 +22,10 @@ export default function LoginView({ animating, handleClose, setProps, signup, vi { if (!animating) viewLoginSet(true); } }}>Or login instead
- - - - - + + + + +
; }; \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/layout/AccountModal/useLogin.tsx b/packages/fakeyou.com/src/components/layout/AccountModal/useLogin.tsx index 8e65d16d09..e29927c915 100644 --- a/packages/fakeyou.com/src/components/layout/AccountModal/useLogin.tsx +++ b/packages/fakeyou.com/src/components/layout/AccountModal/useLogin.tsx @@ -1,143 +1,7 @@ +import { useState } from "react"; import { useChanger, useSession } from "hooks"; import { FetchStatus } from "@storyteller/components/src/api/_common/SharedFetchTypes"; -import { CreateAccount, CreateAccountResponse } from "@storyteller/components/src/api/account/CreateAccount"; -import { InputValidation, ValidatorCallbacks } from "common/InputValidation"; - - -enum EmailReasons { - EmailTaken = "emailTaken", - Invalid = "invalid", - TooShort = "tooShort", - BadInput = "badInput" -} - -enum PasswordReasons { - TooShort = "tooShort" -} - -enum PasswordConfirmReasons { - PasswordMismatch = "passwordMismatch", - TooShort = "tooShort" -} - -enum UsernameReasons { - ContainsSlurs = "containsSlurs", - InvalidCharacters = "invalidCharacters", - IsReserved = "isReserved", - IsTaken = "isTaken", - TooLong = "tooLong", - TooShort = "tooShort" -} - -const emailErrors = { - emailTaken: "Email is taken", - invalid: "Email is invalid", - tooShort: "Email is too short", - badInput: "Bad email input, try another" -}; - -const passwordErrors = { - tooShort: "Password is too short", -}; - -const passwordConfirmErrors = { - passwordMismatch: "Passwords do not match", - tooShort: "Password is too short" -}; - -const usernameErrors = { - containsSlurs: "Username contains slurs", - invalidCharacters: "Username has invalid characters", - isReserved: "Username is reserved", - isTaken: "Username is taken", - tooLong: "Username is too long", - tooShort: "Username is too short" -}; - -const emailValidator = ({ inputValue }: ValidatorCallbacks) => { - let reason = ""; - let validation = InputValidation.Neutral; - - if (inputValue.length > 1) { - if (inputValue.length < 3) { - validation = InputValidation.Invalid; - reason = EmailReasons.TooShort; - } else if (!inputValue.includes("@")) { - validation = InputValidation.Invalid; - reason = EmailReasons.Invalid; - } else { - validation = InputValidation.Valid; - } - } - - return { value: inputValue, reason, validation }; -}; - -const passwordConfirmValidator = ({ inputValue, state }: ValidatorCallbacks) => { - const password = state.password; - let validation = InputValidation.Neutral; - let reason = ""; - - if (inputValue.length > 1) { - if (inputValue !== password.value) { - validation = InputValidation.Invalid; - reason = PasswordConfirmReasons.PasswordMismatch; - } else { - validation = InputValidation.Valid; - reason = ""; - } - } - - return { value: inputValue, reason, validation }; -}; - -const passwordValidator = ({ inputValue, state }: ValidatorCallbacks) => { - const passwordConfirm = state.passwordConfirm; - let validation = InputValidation.Neutral; - let reason = ""; - let confirmValidation = InputValidation.Neutral; - let confirmReason = ""; - - if (inputValue.length > 1) { - if (inputValue.length < 6) { - validation = InputValidation.Invalid; - reason = PasswordReasons.TooShort; - } else { - validation = InputValidation.Valid; - } - - if (inputValue !== passwordConfirm.value) { - confirmValidation = InputValidation.Invalid; - confirmReason = PasswordConfirmReasons.PasswordMismatch - } else { - confirmValidation = InputValidation.Valid; - confirmReason = ""; - } - } - - const additional = { passwordConfirm: { ...passwordConfirm, reason: confirmReason, validation: confirmValidation } } - - return { additional, value: inputValue, reason, validation }; -}; - -const usernameValidator = ({ inputValue }: ValidatorCallbacks) => { - let reason = ""; - let validation = InputValidation.Neutral; - - if (inputValue.length > 1) { - if (inputValue.length < 3) { - validation = InputValidation.Invalid; - reason = UsernameReasons.TooShort - } else if (inputValue.length > 15) { - validation = InputValidation.Invalid; - reason = UsernameReasons.TooLong - } else { - validation = InputValidation.Valid; - } - } - - return { value: inputValue, reason, validation }; -}; +import { CreateSession, CreateSessionResponse } from "@storyteller/components/src/api/account/CreateSession"; interface Props { onSuccess?: (x?:any) => any, @@ -146,71 +10,34 @@ interface Props { } export default function useLogin({ onSuccess, status, statusSet }: Props) { - const { querySession } = useSession(); + const [errorType,errorTypeSet] = useState(""); + const { querySession, querySubscriptions } = useSession(); const { allAreValid, setProps, state, update } = useChanger({ - errorStrings: { - email: emailErrors, - password: passwordErrors, - passwordConfirm: passwordConfirmErrors, - username: usernameErrors, - }, - state: { - email: "", - password: "", - passwordConfirm: "", - username: "" - }, - validators: { - email: emailValidator, - password: passwordValidator, - passwordConfirm: passwordConfirmValidator, - username: usernameValidator - } + password: { value: "" }, + usernameOrEmail: { value: "" } }); - const signup = () => { + const login = () => { console.log("🐊",); + errorTypeSet(""); statusSet(FetchStatus.in_progress); // if (allAreValid()) { - CreateAccount("",{ - username: state.username.value, - email_address: state.email.value, + CreateSession("",{ + username_or_email: state.usernameOrEmail.value, password: state.password.value, - password_confirmation: state.passwordConfirm.value, - }).then((res: CreateAccountResponse) => { + }).then((res: CreateSessionResponse) => { console.log("🦋",res); - // let updates = {} - if (res && res.error_fields && res.error_type) { - statusSet(FetchStatus.error); - if (res.error_fields.email_address) { - console.log("🍟",res.error_type); - switch (res.error_type) { - case "BadInput": return update({ name: "email", reason: EmailReasons.BadInput, validation: InputValidation.Invalid }); - case "EmailTaken": return update({ name: "email", reason: EmailReasons.EmailTaken, validation: InputValidation.Invalid }); - } - return; - } else if (res.error_fields.password) { - switch (res.error_type) { - case "TooShort": update({ name: "password", reason: PasswordReasons.TooShort, validation: InputValidation.Invalid }); - } - } else if (res.error_fields?.username) { - let updateUsername = (reason: UsernameReasons) => update({ reason, name: "username", validation: InputValidation.Invalid }); - switch (res.error_fields.username) { - case "invalid username characters": return updateUsername(UsernameReasons.InvalidCharacters); - case "username is too long": return updateUsername(UsernameReasons.TooLong); - case "username is taken": return updateUsername(UsernameReasons.IsTaken); - case "username is reserved": return updateUsername(UsernameReasons.IsReserved); - case "username contains slurs": return updateUsername(UsernameReasons.ContainsSlurs); - } - } + if (!res.success && res.error_type) { + statusSet(FetchStatus.error); + errorTypeSet(res.error_type); } else if (res.success) { statusSet(FetchStatus.success); querySession(); + querySubscriptions(); if (onSuccess) onSuccess(res); } }); - // } - }; + }; - return { allAreValid, setProps, signup, state, update }; + return { allAreValid, errorType, errorTypeSet, login, setProps, state, update }; }; \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/layout/AccountModal/useSignup.tsx b/packages/fakeyou.com/src/components/layout/AccountModal/useSignup.tsx index 8e65d16d09..4f7136a67e 100644 --- a/packages/fakeyou.com/src/components/layout/AccountModal/useSignup.tsx +++ b/packages/fakeyou.com/src/components/layout/AccountModal/useSignup.tsx @@ -145,27 +145,29 @@ interface Props { statusSet: (x: FetchStatus) => void, } -export default function useLogin({ onSuccess, status, statusSet }: Props) { +export default function useSignup({ onSuccess, status, statusSet }: Props) { const { querySession } = useSession(); const { allAreValid, setProps, state, update } = useChanger({ - errorStrings: { - email: emailErrors, - password: passwordErrors, - passwordConfirm: passwordConfirmErrors, - username: usernameErrors, + email: { + errorText: emailErrors, + validator: emailValidator, + value: "" }, - state: { - email: "", - password: "", - passwordConfirm: "", - username: "" + password: { + errorText: passwordErrors, + validator: passwordValidator, + value: "" + }, + passwordConfirm: { + errorText: passwordConfirmErrors, + validator: passwordConfirmValidator, + value: "" + }, + username: { + errorText: usernameErrors, + validator: usernameValidator, + value: "" }, - validators: { - email: emailValidator, - password: passwordValidator, - passwordConfirm: passwordConfirmValidator, - username: usernameValidator - } }); const signup = () => { diff --git a/packages/fakeyou.com/src/components/providers/Session/Session.tsx b/packages/fakeyou.com/src/components/providers/Session/Session.tsx index a252d134eb..82cb03a39a 100644 --- a/packages/fakeyou.com/src/components/providers/Session/Session.tsx +++ b/packages/fakeyou.com/src/components/providers/Session/Session.tsx @@ -4,12 +4,13 @@ import { SessionContext } from 'context'; interface Props { children?: any; querySession: any; + querySubscriptions: any; sessionFetched: boolean; sessionWrapper?: any; } -export default function Session({ children, querySession, sessionFetched, sessionWrapper }: Props) { - return +export default function Session({ children, querySession, querySubscriptions, sessionFetched, sessionWrapper }: Props) { + return { children } }; \ No newline at end of file diff --git a/packages/fakeyou.com/src/context/SessionContext.tsx b/packages/fakeyou.com/src/context/SessionContext.tsx index a8e8e43be1..8f2db81151 100644 --- a/packages/fakeyou.com/src/context/SessionContext.tsx +++ b/packages/fakeyou.com/src/context/SessionContext.tsx @@ -2,6 +2,7 @@ import { createContext } from "react"; interface SessionContextType { querySession?: any, + querySubscriptions?: any, sessionFetched: boolean, sessionWrapper?: any } diff --git a/packages/fakeyou.com/src/hooks/useChanger/useChanger.tsx b/packages/fakeyou.com/src/hooks/useChanger/useChanger.tsx index 561998e61b..826031484e 100644 --- a/packages/fakeyou.com/src/hooks/useChanger/useChanger.tsx +++ b/packages/fakeyou.com/src/hooks/useChanger/useChanger.tsx @@ -1,27 +1,36 @@ import { useState } from "react"; import { InputValidation, InputStateLibrary, ValidatorCallbacks } from "common/InputValidation"; -interface Props { - errorStrings: { [key:string]: { [error:string]: string } }, - state: { [key:string]: any }, - validators: { [key: string]: (value:any) => any } +// interface Props { +// errorStrings: { [key:string]: { [error:string]: string } }, +// state: { [key:string]: any }, +// validators: { [key: string]: (value:any) => any } +// } + +interface InputInput { + errorText?: { [key: string]: string }, + validator?: (value: ValidatorCallbacks) => ValidatorOutput, + value: any, } -interface ValidatorOutput { additional: any, value: any, reason: string, validation: any } +interface Props { + [key:string]: InputInput +} + +interface ValidatorOutput { additional?: any, value: any, reason: string, validation: any } const noValidation = ({ inputValue }: ValidatorCallbacks) => ({ value: inputValue, reason: "", validation: InputValidation.Neutral }); -export default function useChanger({ errorStrings, state: inputState, validators }: Props) { - const baseValidation = Object.keys(inputState).reduce((obj,key) => ({ ...obj, [key]: { +export default function useChanger(input: Props) { + const baseValidation = Object.keys(input).reduce((obj,key) => ({ ...obj, [key]: { reason: "", validation: InputValidation.Neutral, - value: inputState[key] + value: input[key].value } }),{}) const [state,stateSet] = useState(baseValidation); const changer = (name: string) => ({ target }: { target: { value: any } }) => { - // const setter = state[name][1]; - const validator = validators[name] ? validators[name] : noValidation; + const validator = input[name]?.validator || noValidation; const { additional, value, reason, validation, } : ValidatorOutput = validator({ inputValue: target.value, name, state }); const updateValidations = ( initial: InputStateLibrary ) => ({ ...initial, @@ -36,12 +45,19 @@ export default function useChanger({ errorStrings, state: inputState, validators const allAreValid = () => !Object.values(state).find((item,i) => ((item.validation === InputValidation.Neutral) || (item.validation === InputValidation.Invalid)) ); - const setProps = (name: string) => ({ - name, - value: state[name].value, - invalidReason: errorStrings[name][state[name].reason] || "", - onChange: changer(name) - }); + const setProps = (name: string) => { + const config = input[name]; + const current = state[name]; + if (config && current) { + console.log("🍇",config.errorText,current); + return { + name, + value: current.value, + invalidReason: config.errorText ? config.errorText[current.reason] : "", + onChange: changer(name) + } + } + }; const update = ({ name, reason, validation }: { name: string, reason: string, validation: number }) => stateSet((lib: InputStateLibrary) => ({ diff --git a/packages/fakeyou.com/src/hooks/useSession/useSession.tsx b/packages/fakeyou.com/src/hooks/useSession/useSession.tsx index 37e85caaa8..6ea4e554ce 100644 --- a/packages/fakeyou.com/src/hooks/useSession/useSession.tsx +++ b/packages/fakeyou.com/src/hooks/useSession/useSession.tsx @@ -2,12 +2,13 @@ import { useContext } from 'react'; import { SessionContext } from 'context'; export default function useInferenceJobs() { - const { querySession, sessionFetched, sessionWrapper } = useContext(SessionContext); + const { querySession, querySubscriptions, sessionFetched, sessionWrapper } = useContext(SessionContext); const { logged_in: loggedIn, user } = sessionWrapper?.sessionStateResponse || { logged_in: false }; return { loggedIn, querySession, + querySubscriptions, sessionFetched, user };