App.tsx•2.43 kB
import { useState, FormEvent } from "react";
import { useMutation, useQuery } from "convex/react";
import { Message } from "./common";
import { api } from "../convex/_generated/api";
const randomName = "User " + Math.floor(Math.random() * 10000);
// Render a chat message.
function MessageView(props: { message: Message }) {
const message = props.message;
return (
<div>
<strong>{message.author}:</strong> {message.body}
</div>
);
}
export default function App() {
// Dynamically update `messages` in response to the output of
// `listMessages.ts`.
// @snippet start listMessages
const messages = useQuery(api.listMessages.default) || [];
// @snippet end listMessages
// Run `sendMessage.ts` as a mutation to record a chat message when
// `handleSendMessage` triggered.
const [newMessageText, setNewMessageText] = useState("");
// @snippet start sendMessageHook
const sendMessage = useMutation(api.sendMessage.default);
// @snippet end sendMessageHook
async function handleSendMessage(event: FormEvent) {
event.preventDefault();
setNewMessageText(""); // reset text entry box
// @snippet start sendMessage
await sendMessage({ body: newMessageText, author: randomName });
// @snippet end sendMessage
}
return (
<main className="py-4">
<h1 className="text-center">Convex Chat</h1>
<p className="text-center">
<span className="badge bg-dark">{randomName}</span>
</p>
<ul className="list-group shadow-sm my-3">
{messages.slice(-10).map((message) => (
<li
key={message._id}
className="list-group-item d-flex justify-content-between"
>
<MessageView message={message} />
<div className="ml-auto text-secondary text-nowrap">
{new Date(message._creationTime).toLocaleTimeString()}
</div>
</li>
))}
</ul>
<form
onSubmit={handleSendMessage}
className="d-flex justify-content-center"
>
<input
value={newMessageText}
onChange={(event) => setNewMessageText(event.target.value)}
className="form-control w-50"
placeholder="Write a message…"
/>
<input
type="submit"
value="Send"
className="ms-2 btn btn-primary"
disabled={!newMessageText}
/>
</form>
</main>
);
}