Merge branch 'test' of github.com:storytold/GottaGoFast into test

This commit is contained in:
Michael Chung committed 2024-04-04 17:52:05 -04:00
commit cbae93f939
11 files changed
+178 -69

No files matched your search

+10 -3
View File
@@ -2,17 +2,23 @@ import { twMerge } from "tailwind-merge";
import { IconDefinition } from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Label } from "./Typography";
import { kebabCase } from "~/utilities";
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string;
className?: string;
icon?: IconDefinition
}
export function Input({label, icon, className, ...rest }: InputProps) {
export function Input({
label, icon, className, id, ...rest
}: InputProps) {
return (
<div className={twMerge("flex flex-col gap-2", className)}>
{label && <Label>{label}</Label>}
{label &&
<Label htmlFor={id ? id : kebabCase(label)}>
{label}
</Label>
}
<div className="relative w-full">
{icon && (
@@ -22,6 +28,7 @@ export function Input({label, icon, className, ...rest }: InputProps) {
/>
)}
<input
id={id ? id : (label ? kebabCase(label) : undefined)}
className={twMerge(
"w-full h-10 rounded-md bg-brand-secondary p-3 text-white transition-all duration-150 ease-in-out outline-none outline-offset-0 focus:outline-brand-primary",
icon? "pl-12" : "",
+9 -1
View File
@@ -1,6 +1,7 @@
import { TextareaHTMLAttributes } from "react";
import { twMerge } from "tailwind-merge";
import { Label } from "./Typography";
import { kebabCase } from "~/utilities";
type ResizeType =
| "none"
@@ -15,6 +16,7 @@ export const Textarea = ({
className,
label,
resize = "vertical",
id,
...rest
}: TextareaHTMLAttributes<HTMLTextAreaElement> & {
label?: string;
@@ -22,8 +24,14 @@ export const Textarea = ({
}) => {
return (
<div className="flex flex-col">
{label && <Label>{label}</Label>}
{label &&
<Label htmlFor={id ? id : kebabCase(label)}>
{label}
</Label>
}
<textarea
id={id ? id : (label ? kebabCase(label) : undefined)}
className={twMerge(
"rounded-lg border border-ui-panel-border bg-ui-controls p-3",
className,
+10 -2
View File
@@ -1,4 +1,5 @@
import { NavLink, NavLinkProps } from "@remix-run/react";
import { LabelHTMLAttributes } from "react";
import { twMerge } from "tailwind-merge";
interface TypoProps {
@@ -23,8 +24,15 @@ export const H4 = ({ className, children }: TypoProps) => (
<h4 className={twMerge(baseTypo, "text-base font-medium", className)}>{children}</h4>
);
export const Label = ({ className, children }: TypoProps) => (
<label className={twMerge(baseTypo, "mb-2 text-base font-medium", className)}>{children}</label>
export const Label = ({
className, children, ...rest
}: LabelHTMLAttributes<HTMLLabelElement>) => (
<label
className={twMerge(baseTypo, "mb-2 text-base font-medium", className)}
{...rest}
>
{children}
</label>
);
export const H5 = ({ className, children }: TypoProps) => (
+73 -42
View File
@@ -1,7 +1,7 @@
import {
createContext,
useEffect,
useCallback,
useEffect,
useState,
ReactNode,
Dispatch,
@@ -12,52 +12,43 @@ import { useCookies } from 'react-cookie';
import {
SessionResponse,
AuthState,
STORAGE_KEYS
} from "./types";
import { CreateSession, DestroySession, GetSession } from "./utilities";
export const AuthenticationContext = createContext<{
authState?: AuthState,
setAuthState?: Dispatch<SetStateAction<AuthState | undefined>>,
persistLoginOrOut?: ()=>void,
authState: AuthState,
setAuthState?: Dispatch<SetStateAction<AuthState>>,
loginAndGetUserInfo?: (
usernameOrEmail: string,
password:string,
failureCallback?:()=>void
)=>void;
logout?: ()=>void;
}>({});
}>({
authState:{}
});
export const AuthenticationProvider = ({children}:{children:ReactNode})=>{
const [authCookies, setAuthCookie, removeAuthCookie] = useCookies(['userInfo']);
const [authState, setAuthState] = useState<AuthState | undefined>(undefined);
console.log('auth provider rerender');
const [authCookies, setAuthCookie, removeAuthCookie] = useCookies([STORAGE_KEYS.USER_INFO]);
const [authState, setAuthState] = useState<AuthState>({});
const persistLoginOrOut = useCallback(()=>{
GetSession().then((
res: SessionResponse
)=>{
if(res.success && res.user){
setAuthCookie('userInfo', res.user);
setAuthState({
isLoggedIn: true,
userInfo: res.user
});
}else{
removeAuthCookie('userInfo');
}
});
}, []);
const loginAndGetUserInfo = (usernameOrEmail: string, password:string, failureCallback?:()=>void)=>{
const loginAndGetUserInfo = useCallback((
usernameOrEmail: string,
password:string,
failureCallback?:()=>void
)=>{
CreateSession({usernameOrEmail, password})
.then((respond)=>{
GetSession().then((
res: SessionResponse
)=>{
if(res.success && res.user && setAuthState){
setAuthCookie('userInfo', res.user);
setAuthState({
isLoggedIn: true,
sessionData: JSON.stringify(res.user),
userInfo: res.user
});
}else{
@@ -65,29 +56,69 @@ export const AuthenticationProvider = ({children}:{children:ReactNode})=>{
}
});
});
}
const logout = ()=>{
DestroySession().then((res)=>{
if (res && setAuthState) {
setAuthState({
isLoggedIn: false,
userInfo: undefined
});
}
});
}
useEffect(()=>{
if (authCookies !== undefined && authState===undefined){
persistLoginOrOut();
}
},[]);
const setLogoutState = useCallback(()=>{
setAuthState({
isLoggedIn: false,
sessionData: null,
userInfo: undefined
});
},[]);
const logout = useCallback(()=>{
DestroySession().then((res)=>{
if (res) setLogoutState();
});
},[]);
//Set Session upon Auth State Changes
useEffect(()=>{
//On First Load
if(authState.isLoggedIn === undefined){
//Persist Login if Session Data exist
const sessionData = localStorage.getItem(STORAGE_KEYS.USER_INFO);
console.log(`Session Data: ${sessionData?.substring(0, 30)}${sessionData ? '...':''}`);
if (sessionData !== null){
console.log('Getting session to persist login');
GetSession().then((
res: SessionResponse
)=>{
console.log('Session Returned');
console.log(res);
if(res.success && res.user){
console.log('Setting Auth State');
setAuthState({
isLoggedIn: true,
sessionData: JSON.stringify(res.user),
userInfo: res.user
});
}else{
console.log('Session expired, setting State');
setLogoutState();
}
});
}else{
setLogoutState();
}
}
// Propagate session data to localstorage
if(authState.sessionData !== null && authState.sessionData !== undefined){
localStorage.setItem(STORAGE_KEYS.USER_INFO, authState.sessionData);
setAuthCookie(STORAGE_KEYS.USER_INFO, authState.userInfo);
}
// Propagate Logout
else if (authState.sessionData === null){
localStorage.removeItem(STORAGE_KEYS.USER_INFO);
removeAuthCookie(STORAGE_KEYS.USER_INFO);
}
},[authState]);
return(
<AuthenticationContext.Provider value={{
authState,
setAuthState,
persistLoginOrOut,
loginAndGetUserInfo,
logout,
}}>
+5 -1
View File
@@ -1,3 +1,6 @@
export enum STORAGE_KEYS{
USER_INFO = "user_info",
}
export enum AUTH_STATUS {
LOGGED_IN = "logged_in",
LOGGING = "logging",
@@ -5,7 +8,8 @@ export enum AUTH_STATUS {
}
export type AuthState = {
isLoggedIn: boolean;
isLoggedIn?: boolean;
sessionData?: string | null;
userInfo?: UserInfo;
}
export interface UserInfo {
+26
View File
@@ -0,0 +1,26 @@
import { useContext, useEffect, ComponentType } from "react";
import { useNavigate, useLocation } from "@remix-run/react";
import { AuthenticationContext } from "~/contexts/Authentication";
export const withProtectionRoute = <P extends {}>(Component: ComponentType<P>) => (rest:P) => {
const { authState } = useContext(AuthenticationContext);
console.log(authState);
if (authState.isLoggedIn === undefined ) {
return <>waiting</>;
}
if (authState.isLoggedIn === false) {
return <RedirectToLogin />
}
return (
<Component {...rest}/>
);
}
const RedirectToLogin = ()=>{
const navigate = useNavigate();
const {pathname} = useLocation();
useEffect(()=>{
navigate(`/login?redirect=${pathname}`);
},[]);
return <>redirecting</>
}
@@ -1,6 +1,6 @@
import { useContext, useState } from "react";
import { Button, ItemPicker, Label, Textarea } from "~/components";
import { Button, ItemPicker, Label, H4, Textarea } from "~/components";
import { AppUiContext } from "../../contexts/AppUiContext";
import { APPUI_ACTION_TYPES, APPUI_VIEW_MODES } from "../../reducers";
@@ -62,7 +62,7 @@ export const TabStylization = () => {
return (
<div className="flex h-full w-full flex-col gap-3">
<div className="flex flex-col">
<Label>Select Base Style</Label>
<H4>Select Base Style</H4>
<div className="grid grid-cols-4 gap-2">
<ItemPicker
label="Anime"
@@ -98,6 +98,7 @@ export const TabStylization = () => {
<Textarea
label="Positive Prompt"
className="h-32 w-full"
name="positive-prompt"
placeholder="Type here to describe your scene"
onChange={onChangeHandlerPositive}
resize="none"
@@ -105,6 +106,7 @@ export const TabStylization = () => {
<Textarea
label="Negative Prompt"
className="h-32 w-full"
name="negative-prompt"
placeholder="Type here to filter out the things you don't want in the scene"
onChange={onChangeHandlerNegative}
resize="none"
+27
View File
@@ -0,0 +1,27 @@
import { json } from "@remix-run/router";
import { withProtectionRoute } from "~/modules/withProtectedRoute";
import { faChevronLeft } from "@fortawesome/pro-solid-svg-icons";
import { P, H1, ButtonLink } from "~/components";
export const loader = () => {
return json(null, { status: 404 });
};
const Page404 = withProtectionRoute(()=>{
return(
<div
className="fixed w-full overflow-scroll"
style={{height: "calc(100% - 72px)"}}
>
<div
className='bg-ui-panel w-10/12 max-w-7xl h-1/2 mx-auto my-24 rounded-lg p-6 flex flex-col items-center justify-center gap-8 text-center'
>
<P>Oops, seems like this is not a page that exists!</P>
<H1>Server Error: 404</H1>
<ButtonLink to="/" icon={faChevronLeft}>Back to Dashboard</ButtonLink>
</div>
</div>
)
});
export default Page404;
+4 -4
View File
@@ -1,5 +1,5 @@
import { PageEnigma } from "../pages/PageEnigma";
import { withProtectionRoute } from "~/modules/withProtectedRoute";
import { PageEnigma } from "~/pages/PageEnigma";
export default function IdealEnigma() {
return <PageEnigma />;
}
const IdealEnigma = withProtectionRoute(()=><PageEnigma />);
export default IdealEnigma;
+6 -14
View File
@@ -1,29 +1,19 @@
import { useContext, useEffect, useState, useRef } from "react";
import { useNavigate } from "@remix-run/react";
import { useCookies } from 'react-cookie';
import { useNavigate, useSearchParams } from "@remix-run/react";
import { faKey, faUser } from "@fortawesome/pro-solid-svg-icons";
import { AuthenticationContext } from "~/contexts/Authentication";
import {
Button,
H1,
Input,
Link,
P,
} from '~/components';
import { Button,H1,Input,Link,P } from '~/components';
import { LoadingDots } from "~/components";
export default function LoginScreen() {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const [showLoader, setShowLoader] = useState<string|undefined>(undefined);
const formRef = useRef<HTMLFormElement | null>(null);
const [authCookies, setAuthCookie, removeAuthCookie] = useCookies(['userInfo']);
const {authState, loginAndGetUserInfo} = useContext(AuthenticationContext);
const handleOnSumbit = (ev: React.FormEvent<HTMLFormElement>)=>{
ev.preventDefault();
if(formRef.current){
@@ -43,8 +33,9 @@ export default function LoginScreen() {
}// end handleOnSubmit
useEffect(()=>{
const redirectPath = searchParams.get('redirect');
if(authState && authState.isLoggedIn)
navigate("/");
navigate(redirectPath ? redirectPath:'/');
},[authState]);
return (
@@ -64,6 +55,7 @@ export default function LoginScreen() {
icon={faUser}
name="usernameOrEmail"
placeholder="Username or Email"
autoComplete="username"
required
/>
<br />
+4
View File
@@ -0,0 +1,4 @@
export const kebabCase = (str:string) => str
.replace(/([a-z])([A-Z])/g, "$1-$2")
.replace(/[\s_]+/g, '-')
.toLowerCase();