mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-08 11:01:35 +00:00
Merge branch 'test' of github.com:storytold/GottaGoFast into test
This commit is contained in:
commit
cbae93f939
11 files changed
+178
-69
No files matched your search
@@ -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" : "",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) => (
|
||||
|
||||
@@ -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,
|
||||
}}>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
@@ -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
@@ -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 />
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export const kebabCase = (str:string) => str
|
||||
.replace(/([a-z])([A-Z])/g, "$1-$2")
|
||||
.replace(/[\s_]+/g, '-')
|
||||
.toLowerCase();
|
||||
Reference in new issue
Block a user