diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..4a4726a --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use_nix diff --git a/app/app.css b/app/app.css index 9b4c3ef..1f98ecc 100644 --- a/app/app.css +++ b/app/app.css @@ -1,8 +1,10 @@ +@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap"); @import "tailwindcss" source("."); @theme { --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-roboto: "Roboto", sans-serif } html, diff --git a/app/components/navbar.tsx b/app/components/navbar.tsx new file mode 100644 index 0000000..f357bea --- /dev/null +++ b/app/components/navbar.tsx @@ -0,0 +1,17 @@ +import { Link } from "react-router"; + +export default function Navbar() { + return ( +
+
+
+

anum

+ feed + create +
+
+
+ Login +
+ ) +} diff --git a/app/components/post.tsx b/app/components/post.tsx new file mode 100644 index 0000000..f9f8d6b --- /dev/null +++ b/app/components/post.tsx @@ -0,0 +1,58 @@ +import { useState, useEffect } from 'react'; +import StaticPost from "./staticPost"; + +export interface PostContent { + id: number, + title: string, + content: string, + userId: number, + parentId: number, + createdAt: string, + author: { + name: string, + display_name: string, + id: number + }, + replies: any[]; + _count: { + replies: number; + }; +} + +export interface PostProps { + postId: number; +} + + +export default function Post({ postId }: PostProps) { + const [postContent, setPostContent] = useState(); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const fetchPost = async () => { + const response = await fetch(`http://localhost:8787/posts/${postId}/view`); + const jsonContent: Array = await response.json(); + + let newPostContent: PostContent = jsonContent[0] as PostContent; + + if (newPostContent) { + setPostContent(newPostContent); + setIsLoading(false); + } + }; + fetchPost(); + }, []); + + if (isLoading) { + return
Loading...
; + } + + if (!postContent) { + return

Failed to load!

; + } + + + return ( + + ) +} diff --git a/app/components/staticPost.tsx b/app/components/staticPost.tsx new file mode 100644 index 0000000..48ee3d0 --- /dev/null +++ b/app/components/staticPost.tsx @@ -0,0 +1,44 @@ +import type { PostContent } from "./post"; +import { Link } from "react-router"; + +export interface StaticPostProps { + postContent: PostContent +} + +// This is a version of Post without logic that just takes the content directly. +// Doing this cuts down on loading time since we don't have to manually view every reply. +export default function StaticPost({ postContent }: StaticPostProps) { + let name = ""; + if (postContent.author?.display_name) { + name = postContent.author?.display_name + } else { + name = postContent.author?.name + } + console.debug(postContent.author?.name); + + return ( +
+
+
+

#{postContent.id}

+

{postContent.title}

+

{name}

+

Created at: {postContent.createdAt}

+

Parent: #{postContent.parentId}

+
+

{postContent.content}

+
+ Reply + View +
+
+
+ {postContent.replies && postContent.replies.length > 0 && ( + postContent.replies.map((reply) => ( + + )) + )} +
+
+ ) +} diff --git a/app/entry.server.tsx b/app/entry.server.tsx index 0d843db..8c96db4 100644 --- a/app/entry.server.tsx +++ b/app/entry.server.tsx @@ -36,6 +36,7 @@ export default async function handleRequest( } responseHeaders.set("Content-Type", "text/html"); + responseHeaders.set("Access-Control-Allow-Origin", "*"); return new Response(body, { headers: responseHeaders, status: responseStatusCode, diff --git a/app/root.tsx b/app/root.tsx index 9fc6636..5fb4ad4 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -7,6 +7,7 @@ import { ScrollRestoration, } from "react-router"; +import Navbar from "./components/navbar"; import type { Route } from "./+types/root"; import "./app.css"; @@ -25,15 +26,18 @@ export const links: Route.LinksFunction = () => [ export function Layout({ children }: { children: React.ReactNode }) { return ( - + - - {children} + +
+ + {children} +
diff --git a/app/routes.ts b/app/routes.ts index 102b402..d33cfab 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -1,3 +1,8 @@ -import { type RouteConfig, index } from "@react-router/dev/routes"; +import { type RouteConfig, index, route } from "@react-router/dev/routes"; -export default [index("routes/home.tsx")] satisfies RouteConfig; +export default [ + index("routes/home.tsx"), + route("login", "routes/login.tsx"), + route("create", "routes/create.tsx"), + route("view", "routes/view.tsx") +] satisfies RouteConfig; diff --git a/app/routes/create.tsx b/app/routes/create.tsx new file mode 100644 index 0000000..eb11e4d --- /dev/null +++ b/app/routes/create.tsx @@ -0,0 +1,92 @@ +import { useState } from "react"; +import { useNavigate } from "react-router"; +import { useLocation } from "react-router"; + +export default function CreatePostPage() { + const location = useLocation(); + const stateParentId = location.state?.parentId; + const [postTitle, setPostTitle] = useState(""); + const [postContent, setPostContent] = useState(""); + const [parentId, setParentId] = useState(stateParentId || ""); + const navigate = useNavigate(); + + console.log(stateParentId); + console.log(parentId); + + async function createPost() { + if (parentId) { + const response = await fetch(`http://localhost:8787/posts/${parentId}/reply`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + title: postTitle, + content: postContent // god bless HTTPS + }), + credentials: 'include' + }); + if (!response.ok) { + throw new Error(`Got ${response.status} when trying to log in.`) + } + navigate("/"); + } else { + const response = await fetch("http://localhost:8787/posts/add", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + title: postTitle, + content: postContent // god bless HTTPS + }), + credentials: 'include' + }); + if (!response.ok) { + throw new Error(`Got ${response.status} when trying to log in.`) + } + navigate("/"); + } + } + + + return ( +
+

Create Post

+ +
+
+ + setPostTitle(e.target.value)} + /> +
+ +
+ +