2023-01-12 18:26:01 +01:00
|
|
|
import { redirect, unstable_createFileUploadHandler, unstable_parseMultipartFormData, json } from "@remix-run/node";
|
2023-01-14 00:51:52 +01:00
|
|
|
import { useLocation } from "@remix-run/react";
|
2023-01-14 00:16:01 +01:00
|
|
|
import { useLoaderData, Form, useActionData, Link } from "@remix-run/react";
|
2023-01-14 00:51:52 +01:00
|
|
|
import { useEffect, useState } from "react";
|
2023-01-12 17:48:00 +01:00
|
|
|
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");
|
2023-01-12 21:18:11 +01:00
|
|
|
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,
|
|
|
|
});
|
|
|
|
|
2023-01-12 21:18:11 +01:00
|
|
|
const errors = {};
|
2023-01-12 16:03:38 +01:00
|
|
|
let imageName;
|
2023-01-12 21:18:11 +01:00
|
|
|
let multiPartformdata;
|
|
|
|
try {
|
|
|
|
multiPartformdata = await unstable_parseMultipartFormData(request, fileUploadHandler);
|
|
|
|
multiPartformdata.get("image") !== null ? imageName = multiPartformdata.get("image").name : imageName = null;
|
|
|
|
} catch (err) {
|
2023-01-14 00:51:52 +01:00
|
|
|
errors.image = "Image size too big";
|
2023-01-12 21:18:11 +01:00
|
|
|
}
|
2023-01-12 16:03:38 +01:00
|
|
|
|
2023-01-12 21:18:11 +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 21:18:11 +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),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2023-01-14 15:05:47 +01:00
|
|
|
return createPost;
|
2023-01-12 16:03:38 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
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: {
|
2023-01-14 12:23:40 +01:00
|
|
|
posts: {
|
|
|
|
include: {
|
|
|
|
replies: true,
|
|
|
|
},
|
|
|
|
},
|
2023-01-12 16:03:38 +01:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2023-01-14 15:05:47 +01:00
|
|
|
if (!thread) {
|
|
|
|
return redirect('/')
|
|
|
|
}
|
|
|
|
|
2023-01-12 16:03:38 +01:00
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
|
|
|
export default function Thread() {
|
|
|
|
const data = useLoaderData();
|
2023-01-12 18:26:01 +01:00
|
|
|
const actionData = useActionData();
|
2023-01-12 21:18:11 +01:00
|
|
|
const [replying, setReplying] = useState();
|
2023-01-12 16:03:38 +01:00
|
|
|
|
|
|
|
return (
|
2023-01-12 17:48:00 +01:00
|
|
|
<Overlay>
|
2023-01-14 15:05:47 +01:00
|
|
|
<div className="flex flex-col p-12 bg-ctp-crust border outline rounded shadow-ctp-green shadow-lg">
|
2023-01-14 01:00:57 +01:00
|
|
|
<div className="bg-ctp-base rounded p-4">
|
2023-01-14 13:19:09 +01:00
|
|
|
<span><strong>{data.title}</strong> Post id: <strong>{data.id}</strong> Created at: <strong>{new Date(data.createdAt).toLocaleString()}</strong> Reply count: <strong>{data.posts.length}</strong></span>
|
2023-01-12 17:48:00 +01:00
|
|
|
<br />
|
2023-01-14 14:16:45 +01:00
|
|
|
<div className="flex flex-col">
|
2023-01-14 15:05:47 +01:00
|
|
|
<img className="w-60 h-60" src={`/uploads/${data.imageName}`} alt="post image" />
|
2023-01-14 14:16:45 +01:00
|
|
|
<Link className=" text-xs text-ctp-surface0" to={`/uploads/${data.imageName}`} target="_blank" referrerPolicy="no-referrer">Click to show full image</Link>
|
|
|
|
</div>
|
2023-01-12 17:48:00 +01:00
|
|
|
<p>{data.post}</p>
|
|
|
|
</div>
|
2023-01-13 17:40:07 +01:00
|
|
|
<ul className="flex flex-col m-8 ">
|
2023-01-12 16:03:38 +01:00
|
|
|
{data.posts.map(post =>
|
2023-01-14 01:00:57 +01:00
|
|
|
<li id={`${post.id}`} className="rounded shadow p-4 m-4 odd:bg-ctp-mantle even:bg-ctp-base" key={post.id}>
|
2023-01-14 15:05:47 +01:00
|
|
|
<div className="flex flex-wrap">
|
2023-01-14 13:19:09 +01:00
|
|
|
<span>Reply id: <strong>{post.id}</strong> Replied at: <strong>{new Date(post.createdAt).toLocaleString()} </strong></span>
|
2023-01-14 12:23:40 +01:00
|
|
|
<Link className="mx-2 text-ctp-rosewater hover:text-ctp-maroon hover:underline" onClick={(event) => {
|
|
|
|
event.preventDefault();
|
|
|
|
document.getElementById(`bottom`).scrollIntoView(true);
|
|
|
|
setReplying(post.id);
|
|
|
|
}} to={`#bottom`}
|
|
|
|
>Reply
|
|
|
|
</Link>
|
|
|
|
<ul className="flex flex-wrap space-x-1">
|
|
|
|
{post?.replies?.map(reply =>
|
|
|
|
<li key={reply.id}>
|
|
|
|
<Link onClick={event => {
|
|
|
|
event.preventDefault();
|
|
|
|
document.getElementById(`${reply.id}`).scrollIntoView(true);
|
2023-01-14 13:19:09 +01:00
|
|
|
}} className="text-ctp-teal hover:text-ctp-sky hover:underline" to={`#${reply.id}`}>#{reply.id}</Link>
|
2023-01-14 12:23:40 +01:00
|
|
|
</li>
|
|
|
|
)}
|
|
|
|
</ul>
|
|
|
|
</div>
|
2023-01-14 00:16:01 +01:00
|
|
|
{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> : ''}
|
2023-01-14 14:16:45 +01:00
|
|
|
{post.imageName !== null ?
|
|
|
|
<div className="flex flex-col">
|
2023-01-14 15:05:47 +01:00
|
|
|
<img className="w-60 h-60" src={`/uploads/${post.imageName}`} alt="post image" />
|
2023-01-14 14:16:45 +01:00
|
|
|
<Link className=" text-xs text-ctp-surface0" to={`/uploads/${post.imageName}`} target="_blank" referrerPolicy="no-referrer">Click to show full image</Link>
|
|
|
|
</div> : ''}
|
2023-01-12 16:03:38 +01:00
|
|
|
<p>{post.comment}</p>
|
|
|
|
</li>
|
|
|
|
)}
|
|
|
|
</ul>
|
|
|
|
</div>
|
2023-01-14 15:05:47 +01:00
|
|
|
<Form id="bottom" className="outline bg-ctp-crust p-4 space-y-2 shadow-ctp-flamingo shadow-lg m-16 rounded flex flex-col justify-center items-center" method="post" encType="multipart/form-data">
|
2023-01-14 00:51:52 +01:00
|
|
|
<label className="flex flex-col m-2 space-y-2" htmlFor="text-input">
|
|
|
|
{replying ? <p className="text-center font-semibold text-ctp-black" >Replying to reply id: {replying}</p> : ''}
|
|
|
|
<span className="font-semibold text-center text-ctp-subtext0" >Post</span> <textarea className=" bg-ctp-surface0 m-1 p-1 rounded shadow shadow-ctp-overlay0" 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>
|
2023-01-14 00:51:52 +01:00
|
|
|
<label className="flex flex-col m-2" htmlFor="image-upload"><span className="font-semibold text-center text-ctp-subtext0">Image</span>
|
|
|
|
<input className="block w-full px-3 py-1.5 text-base font-semibold text-ctp-text bg-ctp-surface0 bg-clip-padding rounded transition ease-in-out m-0" accept="image/*" type="file" id="image-upload" name="image" />
|
|
|
|
<p>{actionData?.image}</p>
|
2023-01-12 16:03:38 +01:00
|
|
|
</label>
|
2023-01-14 14:16:45 +01:00
|
|
|
<details class="flex items-center justify-center duration-300">
|
|
|
|
<summary className="text-center text-ctp-subtext0 font-semibold cursor-pointer">Manual replying (needed when JS is disabled)</summary>
|
|
|
|
<div class="flex items-center justify-center">
|
|
|
|
<input className="bg-ctp-surface0 m-1 p-1 rounded shadow shadow-ctp-overlay0" placeholder="Enter a Reply ID" type="number" name="replying" value={replying} />
|
|
|
|
</div>
|
|
|
|
</details>
|
|
|
|
|
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>
|
2023-01-12 17:48:00 +01:00
|
|
|
</Overlay>
|
2023-01-12 16:03:38 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|