-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRoom.tsx
133 lines (126 loc) · 4.28 KB
/
Room.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"use client";
import { FormEventHandler, useEffect, useState } from "react";
import usePartySocket from "partysocket/react";
import type { User } from "@/party/utils/auth";
import type { Message, ChatMessage } from "@/party/utils/message";
import { useSession } from "next-auth/react";
import PartySocket from "partysocket";
import Link from "next/link";
import RoomMessage from "./components/RoomMessage";
import ConnectionStatus from "@/app/components/ConnectionStatus";
const identify = async (socket: PartySocket) => {
// the ./auth route will authenticate the connection to the partykit room
const url = `${window.location.pathname}/auth?_pk=${socket._pk}`;
const req = await fetch(url, { method: "POST" });
if (!req.ok) {
const res = await req.text();
console.error("Failed to authenticate connection to PartyKit room", res);
}
};
export const Room: React.FC<{
room: string;
host: string;
user: User | null;
party: string;
messages: Message[];
}> = ({ room, host, user: initialUser, party, messages: initialMessages }) => {
// render with initial data, update from websocket as messages arrive
const session = useSession();
const [messages, setMessages] = useState(initialMessages);
const [user, setUser] = useState(initialUser);
const socket = usePartySocket({
host,
party,
room,
onOpen(e) {
// identify user upon connection
if (session.status === "authenticated" && e.target) {
identify(e.target as PartySocket);
if (session?.data?.user) setUser(session.data.user as User);
}
},
onMessage(event: MessageEvent<string>) {
const message = JSON.parse(event.data) as ChatMessage;
// upon connection, the server will send all messages in the room
if (message.type === "sync") setMessages(message.messages);
// after that, the server will send updates as they arrive
if (message.type === "new") setMessages((prev) => [...prev, message]);
if (message.type === "clear") setMessages([]);
if (message.type === "edit") {
setMessages((prev) =>
prev.map((m) => (m.id === message.id ? message : m))
);
}
scrollToBottom();
},
});
// authenticate connection to the partykit room if session status changes
useEffect(() => {
if (
session.status === "authenticated" &&
socket?.readyState === socket.OPEN
) {
identify(socket);
}
}, [session.status, socket]);
const handleSubmit: FormEventHandler<HTMLFormElement> = (event) => {
event.preventDefault();
const text = event.currentTarget.message.value;
if (text?.trim()) {
socket.send(JSON.stringify({ type: "new", text }));
event.currentTarget.message.value = "";
scrollToBottom();
}
};
function scrollToBottom() {
window.scrollTo({
top: document.body.scrollHeight,
left: 0,
behavior: "smooth",
});
}
return (
<>
<div className="h-full w-full flex flex-col gap-6">
{messages.length > 0 ? (
<ul className="flex flex-col gap-3">
{messages.map((message) => (
<RoomMessage
key={message.id}
message={message}
isMe={message.from.id === user?.username}
/>
))}
</ul>
) : (
<p className="italic">No messages yet</p>
)}
{session.status === "authenticated" ? (
<form onSubmit={handleSubmit} className="sticky bottom-4 sm:bottom-6">
<input
placeholder="Send message..."
className="border border-stone-400 p-3 bg-stone-100 min-w-full rounded"
type="text"
name="message"
></input>
</form>
) : session.status === "unauthenticated" ? (
<div className="sticky left-4 sm:left-6 bottom-4 sm:bottom-6 pt-2 rounded-sm flex items-start">
<p className="bg-red-100 p-3">
You must be signed in to post messages.{" "}
<Link
className="underline"
href={`/api/auth/signin?callbackUrl=${window.location.href}`}
>
Sign in
</Link>
</p>
</div>
) : (
<span />
)}
</div>
<ConnectionStatus socket={socket} />
</>
);
};