Compare commits

..

No commits in common. "d3a10b062d0a04a0e5ca21a8bc5d8683b209cdbe" and "8d23f17f76a4a00dee8bb47031b87263258e27a3" have entirely different histories.

14 changed files with 112 additions and 1395 deletions

View file

@ -1,10 +0,0 @@
/* 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"
}
};

File diff suppressed because it is too large Load diff

View file

@ -13,11 +13,8 @@
"solid-js": "^1.8.5"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^6.10.0",
"@typescript-eslint/parser": "^6.10.0",
"autoprefixer": "^10.4.16",
"daisyui": "^4.0.1",
"eslint": "^8.53.0",
"postcss": "^8.4.31",
"tailwindcss": "^3.3.5",
"typescript": "^5.2.2",

View file

@ -1,6 +1,4 @@
import { JSXElement } from "solid-js";
export function Arrow(): JSXElement {
export function Arrow() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"

View file

@ -1,9 +1,9 @@
import { JSXElement, onCleanup, useContext } from "solid-js";
import { onCleanup, useContext } from "solid-js";
import { ModalContext } from "./Root";
import { LoginForm } from "./RegLogin/Login";
import { RegisterForm } from "./RegLogin/Register";
export function LoginModal(): JSXElement {
export function LoginModal() {
const modal_ctx = useContext(ModalContext);
if (!modal_ctx) {
@ -11,7 +11,7 @@ export function LoginModal(): JSXElement {
return null;
}
const closeModal = (): void => {
const closeModal = () => {
modal_ctx.setLoginModalOpen(false);
};

View file

@ -1,10 +1,9 @@
import { JSXElement, createEffect, createSignal, useContext } from "solid-js";
import { useContext } from "solid-js";
import { A } from "@solidjs/router";
import { LoginContext } from "./Root";
import { ModalContext } from "./Root";
// Represents a single list item in the menu bar
function MenuItem(props: { href: string; children: JSXElement }): JSXElement {
function MenuItem(props: { href: string; children: any }) {
return (
<li>
<A class="justify-center" href={props.href} end>
@ -14,9 +13,8 @@ function MenuItem(props: { href: string; children: JSXElement }): JSXElement {
);
}
// Represents the menu bar at the top of the page
function Menu(): JSXElement {
const login_ctx = useContext(LoginContext);
function Menu() {
let login_ctx = useContext(LoginContext);
return (
<ul class="menu md:menu-horizontal rounded-box">
<MenuItem href="/">Home</MenuItem>
@ -25,33 +23,23 @@ function Menu(): JSXElement {
);
}
export function Navbar(): JSXElement {
const modal_ctx = useContext(ModalContext);
const login_ctx = useContext(LoginContext);
const [buttonText, setButtonText] = createSignal("Login");
export function Navbar() {
let modal_ctx = useContext(ModalContext);
let login_ctx = useContext(LoginContext);
const logout = (): void => {
localStorage.removeItem("token");
localStorage.removeItem("username");
const logout = () => {
localStorage.setItem("token", "");
localStorage.setItem("username", "");
login_ctx?.setToken("");
login_ctx?.setUsername("");
};
// Function to instantly elect 50 cent as president and declare war on north korea
const capitalizeFirst = (str: string): string => {
return str.charAt(0).toUpperCase() + str.slice(1);
};
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);
};
// Capitalize the first letter of the username
const username =
login_ctx && login_ctx.username()
? login_ctx.username().charAt(0).toUpperCase() +
login_ctx.username().slice(1)
: "Guest";
return (
<div class="navbar text-neutral-content max-w-3xl max-w rounded-box my-4">
@ -67,9 +55,12 @@ export function Navbar(): JSXElement {
<A
href="#"
class="btn btn-ghost normal-case text-sm"
onClick={clickHandler}
onClick={() => {
if (login_ctx?.token() != "") logout();
else modal_ctx?.setLoginModalOpen(true);
}}
>
{buttonText()}
{login_ctx?.token() != "" ? username : "Login"}
</A>
</div>
</div>

View file

@ -1,59 +0,0 @@
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>
);
}

View file

@ -1,15 +1,15 @@
import { JSXElement, createSignal } from "solid-js";
import { createSignal } from "solid-js";
import { getPosts } from "./api";
import { Post } from "./api";
import { useNavigate } from "@solidjs/router";
import { Arrow } from "./Icons";
export function Posts(): JSXElement {
export function Posts() {
const [posts, setPosts] = createSignal([] as Post[]);
const [loading, setLoading] = createSignal(true);
getPosts().then((posts) => {
setPosts(posts);
setPosts(posts as any);
setLoading(false);
});
@ -27,17 +27,14 @@ export function Posts(): JSXElement {
}
// This is the card container for a post
export function PostSegment({ post }: { post: Post }): JSXElement {
export function PostSegment({ post }: { post: Post }) {
const nav = useNavigate();
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-body">
<p class="text-base-content break-words">{post?.content}</p>
<div class="card-actions justify-end">
<button
onClick={(): void => nav("/post/" + post?.id)}
class="btn btn-xs"
>
<button onClick={() => nav("/post/" + post?.id)} class="btn btn-xs">
<Arrow />
</button>
</div>
@ -45,3 +42,5 @@ export function PostSegment({ post }: { post: Post }): JSXElement {
</div>
);
}

View file

@ -1,11 +1,10 @@
import { Route, Routes } from "@solidjs/router";
import { Posts } from "./Posts";
import { SinglePost } from "./SinglePost";
import { NewPostInputArea } from "./NewPost";
import { JSXElement } from "solid-js";
import { NewPostInputArea } from "./Root";
// Primary is the section of the page that holds the main content
export function Primary(): JSXElement {
export function Primary() {
return (
<Routes>
<Route path="/" element={<Posts />} />

View file

@ -1,7 +1,7 @@
import { JSXElement, createSignal, useContext } from "solid-js";
import { createSignal, useContext } from "solid-js";
import { LoginContext, ModalContext } from "../Root";
export function LoginForm(): JSXElement {
export function LoginForm() {
const modal_ctx = useContext(ModalContext);
const login_ctx = useContext(LoginContext);
const [username, setUsername] = createSignal<string>("");
@ -9,7 +9,7 @@ export function LoginForm(): JSXElement {
const [waiting, setWaiting] = createSignal(false);
const [error, setError] = createSignal(false);
async function loginFailed(): Promise<void> {
async function loginFailed() {
setError(true);
setWaiting(false);
setTimeout(() => {
@ -17,7 +17,7 @@ export function LoginForm(): JSXElement {
}, 1000);
}
async function loginPress(e: Event): Promise<void> {
async function loginPress(e: Event) {
e.preventDefault();
setWaiting(true);
submitLogin(username(), password()).then((token) => {
@ -42,7 +42,7 @@ export function LoginForm(): JSXElement {
placeholder="Username"
value={username()}
class="input input-bordered"
onChange={(e): void => {
onChange={(e) => {
setUsername(e.target.value);
}}
/>
@ -52,14 +52,14 @@ export function LoginForm(): JSXElement {
placeholder="Password"
value={password()}
class="input input-bordered"
onChange={(e): void => {
onChange={(e) => {
setPassword(e.target.value);
}}
/>
<button
class={"btn btn-primary" + (error() ? " btn-error" : "")}
onClick={loginPress}
onClick={ loginPress }
>
{waiting() ? "Logging in..." : "Login"}
</button>
@ -73,7 +73,7 @@ export async function submitLogin(
username: string,
password: string
): Promise<string> {
if (username == "" || password == "") return "";
if(username == "" || password == "") return "";
const response = await fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },

View file

@ -1,7 +1,7 @@
import { JSXElement, createSignal, useContext } from "solid-js";
import { createSignal, useContext } from "solid-js";
import { LoginContext, ModalContext } from "../Root";
export function RegisterForm(): JSXElement {
export function RegisterForm() {
const modal_ctx = useContext(ModalContext);
const login_ctx = useContext(LoginContext);
const [username, setUsername] = createSignal<string>("");
@ -10,7 +10,7 @@ export function RegisterForm(): JSXElement {
const [waiting, setWaiting] = createSignal(false);
const [error, setError] = createSignal(false);
async function loginFailed(): Promise<void> {
async function loginFailed() {
setError(true);
setWaiting(false);
setTimeout(() => {
@ -18,7 +18,7 @@ export function RegisterForm(): JSXElement {
}, 1000);
}
async function regPress(e: Event): Promise<void> {
async function regPress(e: Event) {
e.preventDefault();
setWaiting(true);
submitRegistration(username(), password(), captcha()).then((token) => {
@ -43,7 +43,7 @@ export function RegisterForm(): JSXElement {
placeholder="Username"
value={username()}
class="input input-bordered"
onChange={(e): void => {
onChange={(e) => {
setUsername(e.target.value);
}}
/>
@ -53,7 +53,7 @@ export function RegisterForm(): JSXElement {
placeholder="Password"
value={password()}
class="input input-bordered"
onChange={(e): void => {
onChange={(e) => {
setPassword(e.target.value);
}}
/>
@ -63,7 +63,7 @@ export function RegisterForm(): JSXElement {
placeholder="Captcha"
value={password()}
class="input input-bordered"
onChange={(e): void => {
onChange={(e) => {
setCaptcha(e.target.value);
}}
/>

View file

@ -1,9 +1,13 @@
import { Accessor, JSXElement, createSignal } from "solid-js";
import { Accessor, Show, createSignal, useContext } from "solid-js";
import { createContext } from "solid-js";
import { createPost } from "./api";
import { NewPost } from "./api";
import { Navbar } from "./Navbar";
import { Primary } from "./Primary";
// import { Login } from "./Navbar";
import { LoginModal } from "./LoginModal";
import { useNavigate } from "@solidjs/router";
// Representing the state of varoious modals.
// So far we only have one modal, but we can add more later
@ -24,7 +28,7 @@ interface LoginContextType {
export const ModalContext = createContext<ModalContextType>();
export const LoginContext = createContext<LoginContextType>();
function Root(): JSXElement {
function Root() {
// All of these are passed into context providers
const [loginModalOpen, setLoginModalOpen] = createSignal(false);
const [token, setToken] = createSignal("");
@ -44,7 +48,7 @@ function Root(): JSXElement {
<div class="flex flex-col items-center">
<Navbar />
<LoginModal />
<div class="flex flex-col items-center w-full md:w-96 px-2 space-y-2">
<div class="flex flex-col items-center md:w-96 space-y-2">
<Primary />
</div>
</div>
@ -54,4 +58,58 @@ function Root(): JSXElement {
);
}
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;

View file

@ -1,9 +1,9 @@
import { useParams } from "@solidjs/router";
import { JSXElement, Show, Suspense, createResource } from "solid-js";
import { Show, Suspense, createResource } from "solid-js";
import { getPost } from "./api";
import { PostSegment } from "./Posts";
export function SinglePost(): JSXElement {
export function SinglePost() {
const params = useParams();
const [post] = createResource(params.postid, getPost);

View file

@ -26,7 +26,6 @@ export default {
theme: {
extend: {},
},
// eslint-disable-next-line no-undef
plugins: [require("daisyui")],
}