fingers crossed lol 4da42760
Steve · 2024-09-22 15:16 4 file(s) · +154 −12
bun.lockb (added) +0 −0

Binary file — no preview.

package.json +1 −0
50 50
		"@astrojs/rss": "^4.0.7",
51 51
		"@astrojs/ts-plugin": "^1.10.2",
52 52
		"@clerk/astro": "^1.3.6",
53 +
		"@nanostores/react": "^0.7.3",
53 54
		"@types/react": "^18.3.8",
54 55
		"@types/react-dom": "^18.3.0",
55 56
		"@vercel/analytics": "^0.1.10",
src/components/GuestbookFeed.tsx (added) +147 −0
1 +
import { useStore } from "@nanostores/react";
2 +
import { $sessionStore, $userStore } from "@clerk/astro/client";
3 +
import { useState, useEffect } from "react";
4 +
import {
5 +
	SignedIn,
6 +
	SignedOut,
7 +
	UserButton,
8 +
	SignInButton,
9 +
} from "@clerk/astro/react";
10 +
11 +
type Message = {
12 +
	id: number;
13 +
	note: string;
14 +
	author: string;
15 +
	user_id: string;
16 +
	pfp_url: string;
17 +
};
18 +
19 +
export function GuestbookFeed() {
20 +
	const [messages, setMessages] = useState<Message[]>([]);
21 +
	const [isLoading, setIsLoading] = useState(true);
22 +
	const [inputText, setInputText] = useState("");
23 +
	const session = useStore($sessionStore);
24 +
	const user = useStore($userStore);
25 +
26 +
	async function fetchMessages() {
27 +
		setIsLoading(true);
28 +
		try {
29 +
			const req = await fetch(`${import.meta.env.PUBLIC_API_URL}/messages`);
30 +
			const res = await req.json();
31 +
			console.log(res);
32 +
			setMessages(res);
33 +
		} catch (error) {
34 +
			console.log(error);
35 +
		} finally {
36 +
			setIsLoading(false);
37 +
		}
38 +
	}
39 +
40 +
	function inputHandeler(e) {
41 +
		setInputText(e.target.value);
42 +
	}
43 +
44 +
	async function sendMessage() {
45 +
		try {
46 +
			const req = await fetch(`${import.meta.env.PUBLIC_API_URL}/messages`, {
47 +
				method: "POST",
48 +
				headers: {
49 +
					Authorization: `Bearer ${await session.getToken()}`,
50 +
				},
51 +
				body: JSON.stringify({ note: inputText }),
52 +
			});
53 +
			const res = await req.json();
54 +
			console.log(res);
55 +
			setInputText("");
56 +
			await fetchMessages();
57 +
		} catch (error) {
58 +
			console.log(error);
59 +
		}
60 +
	}
61 +
62 +
	async function deleteMessage(id: number) {
63 +
		try {
64 +
			const req = await fetch(
65 +
				`${import.meta.env.PUBLIC_API_URL}/messages/${id}`,
66 +
				{
67 +
					method: "DELETE",
68 +
					headers: {
69 +
						Authorization: `Bearer ${await session.getToken()}`,
70 +
					},
71 +
				},
72 +
			);
73 +
			const res = await req.json();
74 +
			console.log(res);
75 +
			await fetchMessages();
76 +
		} catch (error) {
77 +
			console.log(error);
78 +
		}
79 +
	}
80 +
81 +
	useEffect(() => {
82 +
		fetchMessages();
83 +
	}, []);
84 +
85 +
	return (
86 +
		<div className="flex flex-col gap-6">
87 +
			<div className="">
88 +
				<SignedOut>
89 +
					<SignInButton
90 +
						as="button"
91 +
						mode="modal"
92 +
						className="border-2 border-current rounded-md p-1 cursor-pointer"
93 +
					>
94 +
						Sign in with Github
95 +
					</SignInButton>
96 +
				</SignedOut>
97 +
				<SignedIn>
98 +
					<div className="flex items-center gap-4 w-full">
99 +
						<UserButton />
100 +
						<input
101 +
							className="p-1 border-current border-2 rounded-md w-96"
102 +
							type="text"
103 +
							onChange={inputHandeler}
104 +
							value={inputText}
105 +
						/>
106 +
						<button
107 +
							className="border-2 border-current rounded-md py-1 px-2 cursor-pointer"
108 +
							onClick={sendMessage}
109 +
							type="button"
110 +
						>
111 +
							Send
112 +
						</button>
113 +
					</div>
114 +
				</SignedIn>
115 +
			</div>
116 +
			{isLoading ? (
117 +
				<p>Loading...</p>
118 +
			) : (
119 +
				<div className="flex flex-col gap-6">
120 +
					{messages.map((note: Message) => (
121 +
						<div className="flex flex-row justify-between" key={note.id}>
122 +
							<div className="flex flex-row gap-2 items-center">
123 +
								<img
124 +
									className="h-7 w-7 rounded-full"
125 +
									src={note.pfp_url}
126 +
									alt={note.author}
127 +
								/>
128 +
								<div className="flex flex-col justify-between">
129 +
									<p className="font-bold text-gray-400">{note.author}</p>
130 +
									<p>{note.note}</p>
131 +
								</div>
132 +
							</div>
133 +
							{user.id === note.user_id && (
134 +
								<button
135 +
									onClick={async () => deleteMessage(note.id)}
136 +
									type="button"
137 +
								>
138 +
									x
139 +
								</button>
140 +
							)}
141 +
						</div>
142 +
					))}
143 +
				</div>
144 +
			)}
145 +
		</div>
146 +
	);
147 +
}
src/pages/log.astro +6 −12
1 1
---
2 2
export const prerender = false;
3 3
import PageLayout from "@/layouts/Base";
4 -
import {
5 -
	SignedIn,
6 -
	SignedOut,
7 -
	UserButton,
8 -
	SignInButton,
9 -
} from "@clerk/astro/components";
4 +
import { GuestbookFeed } from "src/components/GuestbookFeed";
10 5
11 6
const meta = {
12 7
	title: "Log",
15 10
---
16 11
<PageLayout meta={meta}>
17 12
  <div class="space-y-6">
18 -
    <SignedOut isStatic={false}>
19 -
      <SignInButton path="/log" as="button" mode="modal" class="border-2 border-current rounded-md p-2 cursor-pointer">Sign in with Github</SignInButton>
20 -
    </SignedOut>
21 -
    <SignedIn isStatic={false}>
22 -
      <UserButton />
23 -
    </SignedIn>
13 +
  <div class="flex flex-col gap-2 mb-6">
14 +
    <h1 class="font-bold text-2xl">Guest Log</h1>
15 +
    <p>Come say hello!</p>
16 +
    </div>
17 +
    <GuestbookFeed client:load />
24 18
  </div>
25 19
</PageLayout>