Meta information, add reply receipts to where they were missing,class sorting and also some pfp stuff

This commit is contained in:
Joonas 2023-11-20 13:05:14 +02:00
parent 1e632c52be
commit 1e25c30cf3
12 changed files with 127 additions and 46 deletions

View File

@ -9,7 +9,7 @@ export function Card({
}) {
return (
<div
className={`flex z-10 flex-col bg-ctp-mantle/50 gap-5 p-8 border w-full rounded-md ${className}`}
className={`flex z-10 flex-col gap-5 p-8 w-full rounded-md border bg-ctp-mantle/50 ${className}`}
>
{children}
</div>

View File

@ -27,7 +27,7 @@ export function TextArea({
}) {
return (
<textarea
className="p-4 w-full bg-ctp-crust/50 text-ctp-subtext0 rounded-lg border"
className="p-4 w-full rounded-lg border bg-ctp-crust/50 text-ctp-subtext0"
placeholder={placeholder}
name={name}
></textarea>

View File

@ -22,13 +22,15 @@ export function Poster({
return style === "compact" ? (
<div>
<img
className={`peer/image rounded-full border w-6 h-6`}
className={`w-6 h-6 rounded-full border peer/image`}
src={
pfp
? `
/uploads/${encodeURIComponent(pfp)}`
: `/uploads/default.png`
}
width={24}
height={24}
alt="pfp"
/>
<Card className="!p-3 hidden peer-hover/image:flex hover:flex !absolute !w-fit">
@ -36,15 +38,17 @@ export function Poster({
</Card>
</div>
) : (
<div className={`flex break-all items-center gap-3 ${className}`}>
<div className={`flex gap-3 items-center break-all ${className}`}>
<img
className="rounded-full border w-12 h-12"
className="w-12 h-12 rounded-full border"
src={
pfp
? `
/uploads/${encodeURIComponent(pfp)}`
: `/uploads/default.png`
}
height={48}
width={48}
alt="pfp"
/>
<div>
@ -120,8 +124,8 @@ export function Post({
</>
}
/>
<div className="h-8 pl-[39px]">
<div className="border w-0 h-full"></div>
<div className="h-8 pl-[36px]">
<div className="w-0 h-full border"></div>
</div>
</>
) : (
@ -139,18 +143,31 @@ export function Post({
} px-4`}
>
<div className="flex gap-4">
<div className="flex flex-col justify-center items-center min-h-full">
<div className="flex flex-col items-center min-h-full">
<img
className="w-12 h-12 rounded-full"
className="w-12 h-11 rounded-full border"
src={`/uploads/${post.author.pfp}`}
alt=""
height={48}
width={48}
alt="pfp"
/>
{child ? <div className="w-0 border h-full"></div> : ""}
{child ? <div className="w-0 h-full border"></div> : ""}
</div>
<div className="flex flex-col gap-2">
<div className="flex relative flex-col gap-2 w-full">
<div className="absolute right-0">
<Text className="subtitle text-[12px] text-ctp-subtext1/50">
{new Date(post.createdAt).toLocaleDateString("en-fi", {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric",
})}
</Text>
</div>
<div className="flex flex-col gap-2">
{post.reposters?.length > 0 ? (
<div className="flex gap-2 items-center">
<div className="flex gap-1 items-center">
{post.reposters.map((user) => (
<Poster
style="compact"
@ -189,13 +206,19 @@ export function Post({
)}
</div>
</div>
<Link to={`/${rootPostId ? "reply" : "post"}/${post.id}`}>
{!enlarge ? (
<Link to={`/${rootPostId ? "reply" : "post"}/${post.id}`}>
<div>
<Text>{post.text}</Text>
</div>
</Link>
) : (
<div>
<Text>{post.text}</Text>
</div>
</Link>
)}
<div className="flex gap-2 text-sm">
<Text>{new Date(post.createdAt).toLocaleDateString()}</Text>
<fetcher.Form action={`/home`} method="POST">
<input
type="hidden"

View File

@ -5,10 +5,10 @@ export function Toast({ children }: { children: ReactNode }) {
return (
<>
<input className="peer/toast" type="checkbox" id="toast-toggle" hidden />
<div className="peer-checked/toast:hidden z-10 bg-ctp-base w-80 p-4 fixed bottom-10 right-10 border rounded-lg shadow-lg">
<div className="fixed right-10 bottom-10 z-10 p-4 w-80 rounded-lg border shadow-lg peer-checked/toast:hidden bg-ctp-base">
<div className="relative">
<label
className="absolute right-0 cursor-pointer text-gray-300 hover:text-gray-400"
className="absolute right-0 text-gray-300 cursor-pointer hover:text-gray-400"
htmlFor="toast-toggle"
>
X

View File

@ -1,8 +1,12 @@
import type { LoaderFunctionArgs } from "@remix-run/node";
import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
import { redirect } from "@remix-run/node";
import { Text } from "~/components/Typography";
import { getSession } from "~/utils/session.server";
export const meta: MetaFunction<typeof loader> = () => {
return [{ title: `Welcome | Twitter Clone` }];
};
export async function loader({ request }: LoaderFunctionArgs) {
const session = await getSession(request.headers.get("Cookie"));
if (session.has("userId")) {
@ -14,8 +18,8 @@ export async function loader({ request }: LoaderFunctionArgs) {
export default function Index() {
return (
<div className="bg-ctp-sky/10 rounded-lg">
<div className="flex flex-col gap-1 h-96 justify-center items-center">
<div className="rounded-lg bg-ctp-sky/10">
<div className="flex flex-col gap-1 justify-center items-center h-96">
<img src="/logo.png" alt="twitter" width={64} />
<h1 className="text-7xl font-black">
<span className="text-sky-400">Twitter</span>{" "}

View File

@ -4,10 +4,12 @@ import type {
ActionFunctionArgs,
LoaderFunction,
LoaderFunctionArgs,
MetaFunction,
} from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import {
Form,
Link,
useActionData,
useLoaderData,
useRouteLoaderData,
@ -24,6 +26,10 @@ import type { RootLoaderTypes } from "~/root";
import { repost, unRepost } from "~/models/repost.server";
import { reply } from "~/models/reply.server";
export const meta: MetaFunction<typeof loader> = () => {
return [{ title: `Home | Twitter Clone` }];
};
export async function action({ request }: ActionFunctionArgs) {
const session = await getSession(request.headers.get("Cookie"));
if (!session.has("userId")) {
@ -126,7 +132,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
where: { id: userId },
select: { following: { select: { id: true } } },
});
const followingArr = [...following.following.map((user) => user.id), userId];
const followingArr = [...following.following.map((user) => user.id)];
const feed = await prisma.post.findMany({
where: {
@ -218,18 +224,20 @@ export default function Index() {
return (
<div className="flex flex-col gap-5">
<Title>Home</Title>
<Card>
<Card className="!gap-0">
{rootData?.id ? (
<>
<SubTitle>New post</SubTitle>
<Form className="flex flex-col gap-3" method="POST">
<Form method="POST">
<input type="hidden" name="intent" value={"newpost"} />
<FormLabel>
<Text>Post body</Text>
<TextArea name="post" />
<Text type="error">{errors?.body ? errors.body : ""}</Text>
</FormLabel>
<Button type="submit">Post</Button>
<div className="flex flex-col gap-3">
<FormLabel>
<Text>Post body</Text>
<TextArea name="post" />
<Text type="error">{errors?.body ? errors.body : ""}</Text>
</FormLabel>
<Button type="submit">Post</Button>
</div>
</Form>
</>
) : (
@ -244,8 +252,12 @@ export default function Index() {
))}
</Card>
) : (
<Text type="error">
Feed is empty, follow someone so their recent posts will show up here!
<Text className="flex gap-1" type="error">
Feed is empty,{" "}
<Link to={`/users`}>
<Text type="link">follow someone</Text>
</Link>{" "}
so their recent posts will show up here!
</Text>
)}
</div>

View File

@ -3,6 +3,7 @@ import {
type ActionFunction,
type ActionFunctionArgs,
type LoaderFunctionArgs,
type MetaFunction,
} from "@remix-run/node";
import { Form, useActionData } from "@remix-run/react";
import { Button } from "~/components/Button";
@ -12,6 +13,10 @@ import { Text, Title } from "~/components/Typography";
import { checkUserLogin } from "~/models/user.server";
import { commitSession, getSession } from "~/utils/session.server";
export const meta: MetaFunction<typeof loader> = () => {
return [{ title: `Login | Twitter Clone` }];
};
export async function loader({ request }: LoaderFunctionArgs) {
const session = await getSession(request.headers.get("Cookie"));
if (session.has("userId")) {

View File

@ -3,8 +3,8 @@ import type {
ActionFunction,
ActionFunctionArgs,
LoaderFunctionArgs,
MetaFunction,
} from "@remix-run/node";
import { Form, useActionData } from "@remix-run/react";
import { commitSession, getSession } from "~/utils/session.server";
import { createUser } from "~/models/user.server";
@ -13,6 +13,10 @@ import { Button } from "~/components/Button";
import { Card } from "~/components/Card";
import { Text, Title } from "~/components/Typography";
export const meta: MetaFunction<typeof loader> = () => {
return [{ title: `Register | Twitter Clone` }];
};
export async function loader({ request }: LoaderFunctionArgs) {
const session = await getSession(request.headers.get("Cookie"));
if (session.has("userId")) {

View File

@ -1,8 +1,9 @@
import type { ActionFunction } from "@remix-run/node";
import {
json,
type LoaderFunction,
type LoaderFunctionArgs,
type ActionFunction,
type MetaFunction,
} from "@remix-run/node";
import {
Form,
@ -19,7 +20,11 @@ import type { RootLoaderTypes } from "~/root";
import { Button } from "~/components/Button";
import { FormLabel, TextArea } from "~/components/Form";
export async function loader({ request, params }: LoaderFunctionArgs) {
export const meta: MetaFunction<typeof loader> = ({ data, error }) => {
return [{ title: `${error ? "Not Found" : data.text} | Twitter Clone` }];
};
export async function loader({ params }: LoaderFunctionArgs) {
const reply = await prisma.reply.findUnique({
where: {
id: Number(params.id),
@ -341,7 +346,6 @@ export default function ReplyRoute() {
const rootData = useRouteLoaderData<RootLoaderTypes>("root");
const data: PostWithRelations = useLoaderData<LoaderFunction>();
const errors = useActionData<ActionFunction>();
console.log(data);
return (
<div className="flex flex-col gap-5">
@ -353,8 +357,12 @@ export default function ReplyRoute() {
child={true}
reply={true}
/>
<div className="h-8 pl-[39px]">
<div className="border w-0 h-full"></div>
<div className="h-8 pl-[36px]">
<div
className={`border ${
data?.parentReply?.parentReply?.parentReply ? "border-dashed" : ""
} w-0 h-full`}
></div>
</div>
<div>
<Post
@ -414,6 +422,17 @@ export default function ReplyRoute() {
userId={rootData?.id}
rootPostId={data.post.id}
reply={true}
topTitle={
<>
Replying to
<Poster
style="compact"
username={data.parentReply.author.username}
name={data.parentReply.author.name}
pfp={data.parentReply.author.pfp}
/>
</>
}
/>
</div>
))}

View File

@ -2,6 +2,7 @@ import type {
ActionFunction,
ActionFunctionArgs,
LoaderFunctionArgs,
MetaFunction,
} from "@remix-run/node";
import {
json,
@ -26,6 +27,10 @@ import { commitSession, getSession } from "~/utils/session.server";
const themes = ["latte", "frappe", "macchiato", "mocha", "none"];
export const meta: MetaFunction<typeof loader> = () => {
return [{ title: `Settings | Twitter Clone` }];
};
export async function action({ request }: ActionFunctionArgs) {
const cookieHeader = request.headers.get("Cookie");
const session = await getSession(cookieHeader);
@ -126,7 +131,7 @@ export default function Settings() {
<span className="text-ctp-text/75">Change username</span>
</summary>
<Card>
<Form method="POST" className="relative flex flex-col gap-3">
<Form method="POST" className="flex relative flex-col gap-3">
<input type="hidden" name="intent" value="username" />
<FormLabel>
<Text>Username</Text> <FormInput type="text" name="username" />{" "}
@ -148,7 +153,7 @@ export default function Settings() {
<span className="text-ctp-text/75">Change name</span>
</summary>
<Card>
<Form method="POST" className="relative flex flex-col gap-3">
<Form method="POST" className="flex relative flex-col gap-3">
<input type="hidden" name="intent" value="name" />
<FormLabel>
<Text>Name</Text> <FormInput type="text" name="name" />{" "}
@ -170,7 +175,7 @@ export default function Settings() {
<span className="text-ctp-text/75">Change description</span>
</summary>
<Card>
<Form method="POST" className="relative flex flex-col gap-3">
<Form method="POST" className="flex relative flex-col gap-3">
<input type="hidden" name="intent" value="desc" />
<FormLabel>
<Text>Description</Text> <FormInput type="text" name="desc" />{" "}
@ -192,7 +197,7 @@ export default function Settings() {
<span className="text-ctp-text/75">Change password</span>
</summary>
<Card>
<Form method="POST" className="relative flex flex-col gap-3">
<Form method="POST" className="flex relative flex-col gap-3">
<input type="hidden" name="intent" value="password" />
<FormLabel>
<Text>Old password</Text>{" "}
@ -225,7 +230,7 @@ export default function Settings() {
<Form
method="POST"
encType="multipart/form-data"
className="relative flex flex-col gap-3"
className="flex relative flex-col gap-3"
>
<input type="hidden" name="intent" value="pfp" />
<FormLabel>
@ -249,12 +254,12 @@ export default function Settings() {
<span className="text-ctp-text/75">Change theme</span>
</summary>
<Card>
<Form className="relative flex flex-col gap-3" method="POST">
<Form className="flex relative flex-col gap-3" method="POST">
<input type="hidden" name="intent" value="theme" />
<FormLabel>
<Text>Theme</Text>
<select
className="w-fit rounded-lg p-2 bg-ctp-crust border"
className="p-2 rounded-lg border w-fit bg-ctp-crust"
name="theme"
>
{themes.map((theme) => (

View File

@ -2,6 +2,7 @@ import type {
ActionFunction,
LoaderFunction,
LoaderFunctionArgs,
MetaFunction,
} from "@remix-run/node";
import {
Form,
@ -20,6 +21,10 @@ import type { RootLoaderTypes } from "~/root";
import { prisma, type UserWithRelations } from "~/utils/prisma.server";
import { getSession } from "~/utils/session.server";
export const meta: MetaFunction<typeof loader> = ({ data, error }) => {
return [{ title: `${error ? "Not Found" : data?.username} | Twitter Clone` }];
};
export async function action({ request }: LoaderFunctionArgs) {
const session = await getSession(request.headers.get("Cookie"));
if (!session.has("userId")) {

View File

@ -1,4 +1,4 @@
import type { LoaderFunction } from "@remix-run/node";
import type { LoaderFunction, MetaFunction } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { Card } from "~/components/Card";
import { Poster } from "~/components/Post";
@ -6,6 +6,10 @@ import { Title } from "~/components/Typography";
import { getUsers } from "~/models/user.server";
import type { UserWithRelations } from "~/utils/prisma.server";
export const meta: MetaFunction<typeof loader> = () => {
return [{ title: `Users | Twitter Clone` }];
};
export async function loader() {
return await getUsers(10);
}