Compare commits
3 commits
8d23f17f76
...
d3a10b062d
Author | SHA1 | Date | |
---|---|---|---|
|
d3a10b062d | ||
|
7f9bddb00f | ||
|
4243b4f213 |
14 changed files with 1395 additions and 112 deletions
10
client-solid/.eslintrc.cjs
Normal file
10
client-solid/.eslintrc.cjs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/* eslint-env node */
|
||||||
|
module.exports = {
|
||||||
|
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: ['@typescript-eslint'],
|
||||||
|
root: true,
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "warn"
|
||||||
|
}
|
||||||
|
};
|
1255
client-solid/package-lock.json
generated
1255
client-solid/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -13,8 +13,11 @@
|
||||||
"solid-js": "^1.8.5"
|
"solid-js": "^1.8.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||||
|
"@typescript-eslint/parser": "^6.10.0",
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.16",
|
||||||
"daisyui": "^4.0.1",
|
"daisyui": "^4.0.1",
|
||||||
|
"eslint": "^8.53.0",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
"tailwindcss": "^3.3.5",
|
"tailwindcss": "^3.3.5",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
export function Arrow() {
|
import { JSXElement } from "solid-js";
|
||||||
|
|
||||||
|
export function Arrow(): JSXElement {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { onCleanup, useContext } from "solid-js";
|
import { JSXElement, onCleanup, useContext } from "solid-js";
|
||||||
import { ModalContext } from "./Root";
|
import { ModalContext } from "./Root";
|
||||||
import { LoginForm } from "./RegLogin/Login";
|
import { LoginForm } from "./RegLogin/Login";
|
||||||
import { RegisterForm } from "./RegLogin/Register";
|
import { RegisterForm } from "./RegLogin/Register";
|
||||||
|
|
||||||
export function LoginModal() {
|
export function LoginModal(): JSXElement {
|
||||||
const modal_ctx = useContext(ModalContext);
|
const modal_ctx = useContext(ModalContext);
|
||||||
|
|
||||||
if (!modal_ctx) {
|
if (!modal_ctx) {
|
||||||
|
@ -11,7 +11,7 @@ export function LoginModal() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = (): void => {
|
||||||
modal_ctx.setLoginModalOpen(false);
|
modal_ctx.setLoginModalOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { useContext } from "solid-js";
|
import { JSXElement, createEffect, createSignal, useContext } from "solid-js";
|
||||||
import { A } from "@solidjs/router";
|
import { A } from "@solidjs/router";
|
||||||
import { LoginContext } from "./Root";
|
import { LoginContext } from "./Root";
|
||||||
import { ModalContext } from "./Root";
|
import { ModalContext } from "./Root";
|
||||||
|
|
||||||
function MenuItem(props: { href: string; children: any }) {
|
// Represents a single list item in the menu bar
|
||||||
|
function MenuItem(props: { href: string; children: JSXElement }): JSXElement {
|
||||||
return (
|
return (
|
||||||
<li>
|
<li>
|
||||||
<A class="justify-center" href={props.href} end>
|
<A class="justify-center" href={props.href} end>
|
||||||
|
@ -13,8 +14,9 @@ function MenuItem(props: { href: string; children: any }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Menu() {
|
// Represents the menu bar at the top of the page
|
||||||
let login_ctx = useContext(LoginContext);
|
function Menu(): JSXElement {
|
||||||
|
const login_ctx = useContext(LoginContext);
|
||||||
return (
|
return (
|
||||||
<ul class="menu md:menu-horizontal rounded-box">
|
<ul class="menu md:menu-horizontal rounded-box">
|
||||||
<MenuItem href="/">Home</MenuItem>
|
<MenuItem href="/">Home</MenuItem>
|
||||||
|
@ -23,23 +25,33 @@ function Menu() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Navbar() {
|
export function Navbar(): JSXElement {
|
||||||
let modal_ctx = useContext(ModalContext);
|
const modal_ctx = useContext(ModalContext);
|
||||||
let login_ctx = useContext(LoginContext);
|
const login_ctx = useContext(LoginContext);
|
||||||
|
const [buttonText, setButtonText] = createSignal("Login");
|
||||||
|
|
||||||
const logout = () => {
|
const logout = (): void => {
|
||||||
localStorage.setItem("token", "");
|
localStorage.removeItem("token");
|
||||||
localStorage.setItem("username", "");
|
localStorage.removeItem("username");
|
||||||
login_ctx?.setToken("");
|
login_ctx?.setToken("");
|
||||||
login_ctx?.setUsername("");
|
login_ctx?.setUsername("");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Capitalize the first letter of the username
|
// Function to instantly elect 50 cent as president and declare war on north korea
|
||||||
const username =
|
const capitalizeFirst = (str: string): string => {
|
||||||
login_ctx && login_ctx.username()
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
? login_ctx.username().charAt(0).toUpperCase() +
|
};
|
||||||
login_ctx.username().slice(1)
|
|
||||||
: "Guest";
|
createEffect(() => {
|
||||||
|
if (login_ctx?.token() != "")
|
||||||
|
setButtonText(capitalizeFirst(login_ctx?.username() ?? "") + " (Logout)");
|
||||||
|
else setButtonText("Login");
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
const clickHandler = (): void => {
|
||||||
|
if (login_ctx?.token() != "") logout();
|
||||||
|
else modal_ctx?.setLoginModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="navbar text-neutral-content max-w-3xl max-w rounded-box my-4">
|
<div class="navbar text-neutral-content max-w-3xl max-w rounded-box my-4">
|
||||||
|
@ -55,12 +67,9 @@ export function Navbar() {
|
||||||
<A
|
<A
|
||||||
href="#"
|
href="#"
|
||||||
class="btn btn-ghost normal-case text-sm"
|
class="btn btn-ghost normal-case text-sm"
|
||||||
onClick={() => {
|
onClick={clickHandler}
|
||||||
if (login_ctx?.token() != "") logout();
|
|
||||||
else modal_ctx?.setLoginModalOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{login_ctx?.token() != "" ? username : "Login"}
|
{buttonText()}
|
||||||
</A>
|
</A>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
59
client-solid/src/NewPost.tsx
Normal file
59
client-solid/src/NewPost.tsx
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { JSXElement, Show, createSignal, useContext } from "solid-js";
|
||||||
|
import { createPost } from "./api";
|
||||||
|
import { NewPost } from "./api";
|
||||||
|
import { useNavigate } from "@solidjs/router";
|
||||||
|
import { LoginContext } from "./Root";
|
||||||
|
|
||||||
|
export function NewPostInputArea(): JSXElement {
|
||||||
|
const [content, setContent] = createSignal("");
|
||||||
|
const [waiting, setWaiting] = createSignal(false);
|
||||||
|
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("/");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Show
|
||||||
|
when={!waiting()}
|
||||||
|
fallback={
|
||||||
|
<span class="loading loading-spinner loading-lg self-center"></span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div class="flex flex-col w-full space-y-2">
|
||||||
|
<textarea
|
||||||
|
class="textarea textarea-bordered h-32"
|
||||||
|
placeholder="Speak your mind..."
|
||||||
|
maxLength={500}
|
||||||
|
oninput={(input): void => {
|
||||||
|
setContent(input.target.value);
|
||||||
|
}}
|
||||||
|
></textarea>
|
||||||
|
<button
|
||||||
|
class={
|
||||||
|
"btn btn-primary self-end btn-sm" +
|
||||||
|
(content() == "" ? " btn-disabled" : "")
|
||||||
|
}
|
||||||
|
onclick={sendPost}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
import { createSignal } from "solid-js";
|
import { JSXElement, createSignal } from "solid-js";
|
||||||
import { getPosts } from "./api";
|
import { getPosts } from "./api";
|
||||||
import { Post } from "./api";
|
import { Post } from "./api";
|
||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
import { Arrow } from "./Icons";
|
import { Arrow } from "./Icons";
|
||||||
|
|
||||||
export function Posts() {
|
export function Posts(): JSXElement {
|
||||||
const [posts, setPosts] = createSignal([] as Post[]);
|
const [posts, setPosts] = createSignal([] as Post[]);
|
||||||
const [loading, setLoading] = createSignal(true);
|
const [loading, setLoading] = createSignal(true);
|
||||||
|
|
||||||
getPosts().then((posts) => {
|
getPosts().then((posts) => {
|
||||||
setPosts(posts as any);
|
setPosts(posts);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -27,14 +27,17 @@ export function Posts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the card container for a post
|
// This is the card container for a post
|
||||||
export function PostSegment({ post }: { post: Post }) {
|
export function PostSegment({ post }: { post: Post }): JSXElement {
|
||||||
const nav = useNavigate();
|
const nav = useNavigate();
|
||||||
return (
|
return (
|
||||||
<div class="card border-b-2 flex-grow border-b-base-300 bg-base-200 hover:bg-base-300 compact text-base-content w-full">
|
<div class="card border-b-2 flex-grow border-b-base-300 bg-base-200 hover:bg-base-300 compact text-base-content w-full">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p class="text-base-content break-words">{post?.content}</p>
|
<p class="text-base-content break-words">{post?.content}</p>
|
||||||
<div class="card-actions justify-end">
|
<div class="card-actions justify-end">
|
||||||
<button onClick={() => nav("/post/" + post?.id)} class="btn btn-xs">
|
<button
|
||||||
|
onClick={(): void => nav("/post/" + post?.id)}
|
||||||
|
class="btn btn-xs"
|
||||||
|
>
|
||||||
<Arrow />
|
<Arrow />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -42,5 +45,3 @@ export function PostSegment({ post }: { post: Post }) {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Route, Routes } from "@solidjs/router";
|
import { Route, Routes } from "@solidjs/router";
|
||||||
import { Posts } from "./Posts";
|
import { Posts } from "./Posts";
|
||||||
import { SinglePost } from "./SinglePost";
|
import { SinglePost } from "./SinglePost";
|
||||||
import { NewPostInputArea } from "./Root";
|
import { NewPostInputArea } from "./NewPost";
|
||||||
|
import { JSXElement } from "solid-js";
|
||||||
|
|
||||||
// Primary is the section of the page that holds the main content
|
// Primary is the section of the page that holds the main content
|
||||||
export function Primary() {
|
export function Primary(): JSXElement {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Posts />} />
|
<Route path="/" element={<Posts />} />
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { createSignal, useContext } from "solid-js";
|
import { JSXElement, createSignal, useContext } from "solid-js";
|
||||||
import { LoginContext, ModalContext } from "../Root";
|
import { LoginContext, ModalContext } from "../Root";
|
||||||
|
|
||||||
export function LoginForm() {
|
export function LoginForm(): JSXElement {
|
||||||
const modal_ctx = useContext(ModalContext);
|
const modal_ctx = useContext(ModalContext);
|
||||||
const login_ctx = useContext(LoginContext);
|
const login_ctx = useContext(LoginContext);
|
||||||
const [username, setUsername] = createSignal<string>("");
|
const [username, setUsername] = createSignal<string>("");
|
||||||
|
@ -9,7 +9,7 @@ export function LoginForm() {
|
||||||
const [waiting, setWaiting] = createSignal(false);
|
const [waiting, setWaiting] = createSignal(false);
|
||||||
const [error, setError] = createSignal(false);
|
const [error, setError] = createSignal(false);
|
||||||
|
|
||||||
async function loginFailed() {
|
async function loginFailed(): Promise<void> {
|
||||||
setError(true);
|
setError(true);
|
||||||
setWaiting(false);
|
setWaiting(false);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -17,7 +17,7 @@ export function LoginForm() {
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loginPress(e: Event) {
|
async function loginPress(e: Event): Promise<void> {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setWaiting(true);
|
setWaiting(true);
|
||||||
submitLogin(username(), password()).then((token) => {
|
submitLogin(username(), password()).then((token) => {
|
||||||
|
@ -42,7 +42,7 @@ export function LoginForm() {
|
||||||
placeholder="Username"
|
placeholder="Username"
|
||||||
value={username()}
|
value={username()}
|
||||||
class="input input-bordered"
|
class="input input-bordered"
|
||||||
onChange={(e) => {
|
onChange={(e): void => {
|
||||||
setUsername(e.target.value);
|
setUsername(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -52,14 +52,14 @@ export function LoginForm() {
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
value={password()}
|
value={password()}
|
||||||
class="input input-bordered"
|
class="input input-bordered"
|
||||||
onChange={(e) => {
|
onChange={(e): void => {
|
||||||
setPassword(e.target.value);
|
setPassword(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class={"btn btn-primary" + (error() ? " btn-error" : "")}
|
class={"btn btn-primary" + (error() ? " btn-error" : "")}
|
||||||
onClick={ loginPress }
|
onClick={loginPress}
|
||||||
>
|
>
|
||||||
{waiting() ? "Logging in..." : "Login"}
|
{waiting() ? "Logging in..." : "Login"}
|
||||||
</button>
|
</button>
|
||||||
|
@ -73,7 +73,7 @@ export async function submitLogin(
|
||||||
username: string,
|
username: string,
|
||||||
password: string
|
password: string
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if(username == "" || password == "") return "";
|
if (username == "" || password == "") return "";
|
||||||
const response = await fetch("/api/login", {
|
const response = await fetch("/api/login", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { createSignal, useContext } from "solid-js";
|
import { JSXElement, createSignal, useContext } from "solid-js";
|
||||||
import { LoginContext, ModalContext } from "../Root";
|
import { LoginContext, ModalContext } from "../Root";
|
||||||
|
|
||||||
export function RegisterForm() {
|
export function RegisterForm(): JSXElement {
|
||||||
const modal_ctx = useContext(ModalContext);
|
const modal_ctx = useContext(ModalContext);
|
||||||
const login_ctx = useContext(LoginContext);
|
const login_ctx = useContext(LoginContext);
|
||||||
const [username, setUsername] = createSignal<string>("");
|
const [username, setUsername] = createSignal<string>("");
|
||||||
|
@ -10,7 +10,7 @@ export function RegisterForm() {
|
||||||
const [waiting, setWaiting] = createSignal(false);
|
const [waiting, setWaiting] = createSignal(false);
|
||||||
const [error, setError] = createSignal(false);
|
const [error, setError] = createSignal(false);
|
||||||
|
|
||||||
async function loginFailed() {
|
async function loginFailed(): Promise<void> {
|
||||||
setError(true);
|
setError(true);
|
||||||
setWaiting(false);
|
setWaiting(false);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -18,7 +18,7 @@ export function RegisterForm() {
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function regPress(e: Event) {
|
async function regPress(e: Event): Promise<void> {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setWaiting(true);
|
setWaiting(true);
|
||||||
submitRegistration(username(), password(), captcha()).then((token) => {
|
submitRegistration(username(), password(), captcha()).then((token) => {
|
||||||
|
@ -43,7 +43,7 @@ export function RegisterForm() {
|
||||||
placeholder="Username"
|
placeholder="Username"
|
||||||
value={username()}
|
value={username()}
|
||||||
class="input input-bordered"
|
class="input input-bordered"
|
||||||
onChange={(e) => {
|
onChange={(e): void => {
|
||||||
setUsername(e.target.value);
|
setUsername(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -53,7 +53,7 @@ export function RegisterForm() {
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
value={password()}
|
value={password()}
|
||||||
class="input input-bordered"
|
class="input input-bordered"
|
||||||
onChange={(e) => {
|
onChange={(e): void => {
|
||||||
setPassword(e.target.value);
|
setPassword(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -63,7 +63,7 @@ export function RegisterForm() {
|
||||||
placeholder="Captcha"
|
placeholder="Captcha"
|
||||||
value={password()}
|
value={password()}
|
||||||
class="input input-bordered"
|
class="input input-bordered"
|
||||||
onChange={(e) => {
|
onChange={(e): void => {
|
||||||
setCaptcha(e.target.value);
|
setCaptcha(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
import { Accessor, Show, createSignal, useContext } from "solid-js";
|
import { Accessor, JSXElement, createSignal } from "solid-js";
|
||||||
import { createContext } from "solid-js";
|
import { createContext } from "solid-js";
|
||||||
|
|
||||||
import { createPost } from "./api";
|
|
||||||
import { NewPost } from "./api";
|
|
||||||
import { Navbar } from "./Navbar";
|
import { Navbar } from "./Navbar";
|
||||||
import { Primary } from "./Primary";
|
import { Primary } from "./Primary";
|
||||||
// import { Login } from "./Navbar";
|
|
||||||
import { LoginModal } from "./LoginModal";
|
import { LoginModal } from "./LoginModal";
|
||||||
import { useNavigate } from "@solidjs/router";
|
|
||||||
|
|
||||||
// Representing the state of varoious modals.
|
// Representing the state of varoious modals.
|
||||||
// So far we only have one modal, but we can add more later
|
// So far we only have one modal, but we can add more later
|
||||||
|
@ -28,7 +24,7 @@ interface LoginContextType {
|
||||||
export const ModalContext = createContext<ModalContextType>();
|
export const ModalContext = createContext<ModalContextType>();
|
||||||
export const LoginContext = createContext<LoginContextType>();
|
export const LoginContext = createContext<LoginContextType>();
|
||||||
|
|
||||||
function Root() {
|
function Root(): JSXElement {
|
||||||
// All of these are passed into context providers
|
// All of these are passed into context providers
|
||||||
const [loginModalOpen, setLoginModalOpen] = createSignal(false);
|
const [loginModalOpen, setLoginModalOpen] = createSignal(false);
|
||||||
const [token, setToken] = createSignal("");
|
const [token, setToken] = createSignal("");
|
||||||
|
@ -48,7 +44,7 @@ function Root() {
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<LoginModal />
|
<LoginModal />
|
||||||
<div class="flex flex-col items-center md:w-96 space-y-2">
|
<div class="flex flex-col items-center w-full md:w-96 px-2 space-y-2">
|
||||||
<Primary />
|
<Primary />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,58 +54,4 @@ function Root() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NewPostInputArea() {
|
|
||||||
const [content, setContent] = createSignal("");
|
|
||||||
const [waiting, setWaiting] = createSignal(false);
|
|
||||||
const login_ctx = useContext(LoginContext);
|
|
||||||
|
|
||||||
const nav = useNavigate();
|
|
||||||
|
|
||||||
const sendPost = () => {
|
|
||||||
setWaiting(true);
|
|
||||||
|
|
||||||
const response = createPost({
|
|
||||||
content: content(),
|
|
||||||
token: login_ctx?.token(),
|
|
||||||
} as NewPost);
|
|
||||||
|
|
||||||
if (response) {
|
|
||||||
response.then(() => {
|
|
||||||
setWaiting(false);
|
|
||||||
setContent("");
|
|
||||||
nav("/");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Show
|
|
||||||
when={!waiting()}
|
|
||||||
fallback={
|
|
||||||
<span class="loading loading-spinner loading-lg self-center"></span>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div class="flex flex-col space-y-2">
|
|
||||||
<textarea
|
|
||||||
class="textarea textarea-bordered"
|
|
||||||
placeholder="Speak your mind..."
|
|
||||||
maxLength={500}
|
|
||||||
oninput={(input) => {
|
|
||||||
setContent(input.target.value);
|
|
||||||
}}
|
|
||||||
></textarea>
|
|
||||||
<button
|
|
||||||
class={
|
|
||||||
"btn btn-primary self-end btn-sm" +
|
|
||||||
(content() == "" ? " btn-disabled" : "")
|
|
||||||
}
|
|
||||||
onclick={sendPost}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Root;
|
export default Root;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { useParams } from "@solidjs/router";
|
import { useParams } from "@solidjs/router";
|
||||||
import { Show, Suspense, createResource } from "solid-js";
|
import { JSXElement, Show, Suspense, createResource } from "solid-js";
|
||||||
import { getPost } from "./api";
|
import { getPost } from "./api";
|
||||||
import { PostSegment } from "./Posts";
|
import { PostSegment } from "./Posts";
|
||||||
|
|
||||||
export function SinglePost() {
|
export function SinglePost(): JSXElement {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const [post] = createResource(params.postid, getPost);
|
const [post] = createResource(params.postid, getPost);
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ export default {
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
plugins: [require("daisyui")],
|
plugins: [require("daisyui")],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue