imageboard/app/routes/threads/$threadId.jsx

116 lines
4.5 KiB
React
Raw Normal View History

2023-01-12 18:26:01 +01:00
import { redirect, unstable_createFileUploadHandler, unstable_parseMultipartFormData, json } from "@remix-run/node";
import { useLoaderData, Form, useActionData, Link } from "@remix-run/react";
import { useState } from "react";
import Overlay from "~/components/Overlay";
2023-01-12 16:03:38 +01:00
import prisma from "~/utils/db.server";
export async function action({ request, params }) {
const threadId = params.threadId;
const clonedData = request.clone();
const formData = await clonedData.formData();
const post = formData.get("post");
const replying = formData.get("replying");
2023-01-12 16:03:38 +01:00
const fileUploadHandler = unstable_createFileUploadHandler({
directory: './public/uploads/',
maxPartSize: 500000,
file: ({ filename }) => filename,
});
const errors = {};
2023-01-12 16:03:38 +01:00
let imageName;
let multiPartformdata;
try {
multiPartformdata = await unstable_parseMultipartFormData(request, fileUploadHandler);
multiPartformdata.get("image") !== null ? imageName = multiPartformdata.get("image").name : imageName = null;
} catch (err) {
errors.image = "Image size too big or something elase happened";
}
2023-01-12 16:03:38 +01:00
if (typeof post !== "string" || post.length > 50 || post.length < 3) {
errors.post = "Post too long or short";
2023-01-12 18:26:01 +01:00
};
2023-01-12 18:26:01 +01:00
if (Object.keys(errors).length) {
return json(errors, { status: 422 });
}
2023-01-12 16:03:38 +01:00
const createPost = await prisma.post.create({
data: {
comment: post,
imageName: imageName,
2023-01-12 21:58:35 +01:00
replyingTo: replying ? parseInt(replying) : null,
2023-01-12 16:03:38 +01:00
postId: parseInt(threadId),
},
});
return redirect(`/threads/${threadId}`);
};
export async function loader({ params }) {
const threadId = params.threadId;
const thread = await prisma.thread.findUnique({
where: {
2023-01-12 21:58:35 +01:00
id: parseInt(threadId),
2023-01-12 16:03:38 +01:00
},
include: {
posts: true,
},
});
return thread;
}
export default function Thread() {
const data = useLoaderData();
2023-01-12 18:26:01 +01:00
const actionData = useActionData();
const [replying, setReplying] = useState();
2023-01-12 16:03:38 +01:00
return (
<Overlay>
<div className="flex flex-col p-12 bg-ctp-crust outline rounded">
<div className="">
<span><strong>{data.title}</strong> Post id: <strong>{data.id}</strong> Created at: <strong>{data.createdAt}</strong> Reply count: <strong>{data.posts.length}</strong></span>
<br />
<a href={`/uploads/${data.imageName}`} target="_blank" referrerPolicy="no-referrer"><img src={`/uploads/${data.imageName}`} alt="post image" width={240} /></a>
<p>{data.post}</p>
</div>
<ul className="flex flex-col m-8 ">
2023-01-12 16:03:38 +01:00
{data.posts.map(post =>
<li id={`${post.id}`} className="rounded shadow p-4 m-4 odd:bg-ctp-base even:bg-ctp-mantle" key={post.id}>
<span id={`${post.id}`}>Reply id: <strong>{post.id}</strong> Replied at: <strong>{post.createdAt} </strong></span>
<Link onClick={(event) => {
event.preventDefault();
document.getElementById(`bottom`).scrollIntoView(true);
setReplying(post.id);
}} to={`#bottom`}
>Reply
</Link>
2023-01-12 16:03:38 +01:00
<br />
{post.replyingTo ? <Link onClick={event => {
event.preventDefault();
document.getElementById(`${post.replyingTo}`).scrollIntoView(true);
}} className="font-semibold text-sm hover:underline" to={`#${post.replyingTo}`}>Replying to id: {post.replyingTo}</Link> : ''}
{post.imageName !== null ? <a href={`/uploads/${post.imageName}`} target="_blank" referrerPolicy="no-referrer"><img width={240} src={`/uploads/${post.imageName}`} alt="post image" /></a> : ''}
2023-01-12 16:03:38 +01:00
<p>{post.comment}</p>
</li>
)}
</ul>
</div>
<Form id="bottom" className=" bg-ctp-crust p-4 space-y-2 shadow-ctp-flamingo shadow-lg outline m-16 rounded flex flex-col justify-center items-center" method="post" encType="multipart/form-data">
2023-01-12 16:03:38 +01:00
<label htmlFor="text-input">
{replying ? <p>Replying to reply id: {replying}</p> : ''}
2023-01-12 16:03:38 +01:00
Post: <input required minLength={3} name="post" type="text" />
2023-01-12 18:26:01 +01:00
{actionData ? <p>{actionData.post}</p> : ''}
2023-01-12 16:03:38 +01:00
</label>
<label htmlFor="image-upload">
Image: <input accept="image/*" type="file" name="image" id="image-upload" />
2023-01-12 16:03:38 +01:00
</label>
<input className="hidden" type="number" name="replying" value={replying} />
2023-01-12 21:58:35 +01:00
<button className="bg-white px-4 py-2 shadow rounded-full" type="submit">Submit</button>
2023-01-12 16:03:38 +01:00
</Form>
</Overlay>
2023-01-12 16:03:38 +01:00
);
}