Restructure

This commit is contained in:
Imbus 2023-11-22 15:29:27 +01:00
parent da48f005a3
commit 22a3ca1769
16 changed files with 44 additions and 34 deletions

View file

@ -0,0 +1,75 @@
import { JSXElement, Show, createSignal, useContext } from "solid-js";
import { LoginContext, ModalContext } from "../Context/GlobalState";
import { AuthResponse, submitLogin } from "../Util/api";
export function LoginForm(): JSXElement {
const modal_ctx = useContext(ModalContext)!;
const login_ctx = useContext(LoginContext)!;
const [username, setUsername] = createSignal<string>("");
const [password, setPassword] = createSignal<string>("");
const [waiting, setWaiting] = createSignal(false);
const [error, setError] = createSignal(false);
async function loginFailed(): Promise<void> {
setError(true);
setWaiting(false);
setTimeout(() => {
setError(false);
}, 1000);
}
const clearFields = (): void => {
setUsername("");
setPassword("");
};
const success = (response: AuthResponse): void => {
setWaiting(false);
setError(false);
clearFields();
login_ctx.logIn(response.username, response.token);
modal_ctx.setOpen(false);
};
async function loginPress(e: Event): Promise<void> {
e.preventDefault();
setWaiting(true);
const data = await submitLogin(username(), password());
if (data) success(data as AuthResponse);
else loginFailed();
}
return (
<form class="form-control space-y-2">
<input
type="text"
placeholder="Username"
value={username()}
class="input input-bordered"
onChange={(e): void => {
setUsername(e.target.value);
}}
/>
<input
type="password"
placeholder="Password"
value={password()}
class="input input-bordered"
onChange={(e): void => {
setPassword(e.target.value);
}}
/>
<button
classList={{ "btn btn-primary": !error(), "btn btn-error": error() }}
onClick={loginPress}
>
<Show when={waiting()} fallback="Login">
Logging in...
</Show>
</button>
</form>
);
}

View file

@ -0,0 +1,23 @@
import { JSXElement, Show, useContext } from "solid-js";
import { LoginContext, ModalContext } from "../Context/GlobalState";
import { UserCircle } from "../Util/Icons";
export function LoginButton(): JSXElement {
const modal_ctx = useContext(ModalContext)!;
const login_ctx = useContext(LoginContext)!;
const clickHandler = (): void => {
if (login_ctx.loggedIn()) login_ctx.logOut();
else modal_ctx.setOpen(true);
};
return (
<div class="btn btn-ghost text-sm capitalize" onClick={clickHandler}>
<Show when={login_ctx.loggedIn()} fallback="Login">
{login_ctx.username()}
</Show>
<UserCircle />
</div>
);
}

View file

@ -0,0 +1,36 @@
import { A } from "@solidjs/router";
import { JSXElement, Show, useContext } from "solid-js";
import { LoginContext } from "../Context/GlobalState";
import { Home, Plus } from "../Util/Icons";
// Represents a single list item in the menu bar
export function MenuItem(props: {
href: string;
children: JSXElement;
}): JSXElement {
return (
<li>
<A class="justify-center" href={props.href} end>
{props.children}
</A>
</li>
);
}
// Represents the menu bar at the top of the page
export function Menu(): JSXElement {
const login_ctx = useContext(LoginContext)!;
return (
<Show when={login_ctx.loggedIn()}>
<ul class="menu space-y-2 rounded-box md:menu-horizontal md:space-x-2 md:space-y-0">
<MenuItem href="/">
<Home />
</MenuItem>
<MenuItem href="/new">
<Plus />
</MenuItem>
</ul>
</Show>
);
}

View file

@ -0,0 +1,64 @@
import { useNavigate } from "@solidjs/router";
import { JSXElement, Show, createSignal, onMount, useContext } from "solid-js";
import { LoginContext } from "../Context/GlobalState";
import { NewPost, createPost } from "../Util/api";
export function NewPostInputArea(): JSXElement {
const [content, setContent] = createSignal("");
const [waiting, setWaiting] = createSignal(false);
// We assumte this context is always available
const login_ctx = useContext(LoginContext)!;
const nav = useNavigate();
const sendPost = (): void => {
setWaiting(true);
const response = createPost({
content: content(),
token: login_ctx.token(),
} as NewPost);
if (response) {
response.then(() => {
setWaiting(false);
setContent("");
nav("/");
});
}
};
// Bail out if not logged in
onMount(() => {
if (!login_ctx.loggedIn()) nav("/");
});
return (
<Show
when={!waiting()}
fallback={<span class="loading loading-spinner loading-lg self-center" />}
>
<div class="flex w-full flex-col space-y-2">
<textarea
class="textarea textarea-bordered h-32"
placeholder="Speak your mind..."
maxLength={500}
onInput={(input): void => {
setContent(input.target.value);
}}
/>
<button
class={
"btn btn-primary btn-sm self-end" +
(content() == "" ? " btn-disabled" : "")
}
onClick={sendPost}
>
Submit
</button>
</div>
</Show>
);
}

View file

@ -0,0 +1,43 @@
import { useNavigate } from "@solidjs/router";
import { For, JSXElement, Show, createSignal } from "solid-js";
import { Arrow, loadSpinner } from "../Util/Icons";
import { Post, getPosts } from "../Util/api";
export function Posts(): JSXElement {
const [posts, setPosts] = createSignal([] as Post[]);
const [loading, setLoading] = createSignal(true);
getPosts().then((posts) => {
setPosts(posts);
setLoading(false);
});
return (
<Show when={!loading()} fallback={loadSpinner()}>
<For each={posts()}>
{(post): JSXElement => <PostSegment post={post} />}
</For>
</Show>
);
}
// This is the card container for a post
export function PostSegment(props: { post: Post }): JSXElement {
const nav = useNavigate();
return (
<div class="card compact w-full flex-grow border-b-2 border-b-base-300 bg-base-200 text-base-content transition-all hover:bg-base-300">
<div class="card-body">
<p class="break-words text-base-content">{props.post?.content}</p>
<div class="card-actions justify-end">
<button
onClick={(): void => nav("/post/" + props.post?.id)}
class="btn btn-xs"
>
<Arrow />
</button>
</div>
</div>
</div>
);
}

View file

@ -0,0 +1,87 @@
import { JSXElement, Show, createSignal, useContext } from "solid-js";
import { LoginContext, ModalContext } from "../Context/GlobalState";
import { AuthResponse, submitRegistration } from "../Util/api";
export function RegisterForm(): JSXElement {
const modal_ctx = useContext(ModalContext)!;
const login_ctx = useContext(LoginContext)!;
const [username, setUsername] = createSignal<string>("");
const [password, setPassword] = createSignal<string>("");
const [captcha, setCaptcha] = createSignal<string>("");
const [waiting, setWaiting] = createSignal(false);
const [error, setError] = createSignal(false);
async function loginFailed(): Promise<void> {
setError(true);
setWaiting(false);
setTimeout(() => {
setError(false);
}, 1000);
}
const clearFields = (): void => {
setUsername("");
setPassword("");
setCaptcha("");
};
const success = (response: AuthResponse): void => {
setWaiting(false);
setError(false);
clearFields();
login_ctx.logIn(response.username, response.token);
modal_ctx.setOpen(false);
};
async function regPress(e: Event): Promise<void> {
e.preventDefault();
setWaiting(true);
const data = await submitRegistration(username(), password(), captcha());
if (data) success(data as AuthResponse);
else loginFailed();
}
return (
<form class="form-control space-y-2">
<input
type="text"
placeholder="Username"
value={username()}
class="input input-bordered"
onChange={(e): void => {
setUsername(e.target.value);
}}
/>
<input
type="password"
placeholder="Password"
value={password()}
class="input input-bordered"
onChange={(e): void => {
setPassword(e.target.value);
}}
/>
<input
type="text"
placeholder="Captcha"
value={captcha()}
class="input input-bordered"
onChange={(e): void => {
setCaptcha(e.target.value);
}}
/>
<button
classList={{ "btn btn-primary": !error(), "btn btn-error": error() }}
onClick={regPress}
>
<Show when={waiting()} fallback="Register">
Registering...
</Show>
</button>
</form>
);
}

View file

@ -0,0 +1,19 @@
import { useParams } from "@solidjs/router";
import { JSXElement, Show, Suspense, createResource } from "solid-js";
import { loadSpinner } from "../Util/Icons";
import { getPost } from "../Util/api";
import { PostSegment } from "./Posts";
export function SinglePost(): JSXElement {
const params = useParams();
const [post] = createResource(params.postid, getPost);
return (
<Suspense fallback={loadSpinner()}>
<Show when={post()}>
<PostSegment post={post()!} />
</Show>
</Suspense>
);
}