103 lines
2.5 KiB
TypeScript
103 lines
2.5 KiB
TypeScript
const main = document.querySelector("main")!;
|
|
|
|
async function nav() {
|
|
const nav = <nav />;
|
|
|
|
const conversations = await fetch("/api/conversation").then(r => r.json());
|
|
for (const conversation of conversations) {
|
|
const button = <button type="button">{conversation.name}</button>;
|
|
button.addEventListener("click", e => {
|
|
e.preventDefault();
|
|
|
|
main.append(conversationUI(conversation.id));
|
|
nav.remove();
|
|
});
|
|
|
|
nav.append(button);
|
|
}
|
|
|
|
nav.append(
|
|
<button
|
|
type="button"
|
|
_tap={b =>
|
|
b.addEventListener("click", e => {
|
|
e.preventDefault();
|
|
main.append(conversationUI("new"));
|
|
nav.remove();
|
|
})
|
|
}
|
|
>
|
|
new
|
|
</button>,
|
|
);
|
|
|
|
return nav;
|
|
}
|
|
|
|
function conversationUI(id: string) {
|
|
window.location.hash = `#${id}`;
|
|
|
|
const socket = new WebSocket(`/api/conversation/${id}/connect`);
|
|
|
|
const chatlog = <section className="chatlog" />;
|
|
const inFlightMessages = new Map<string, Element>();
|
|
|
|
socket.addEventListener("message", event => {
|
|
if (typeof event.data !== "string") return;
|
|
const message = JSON.parse(event.data);
|
|
|
|
const scrolledToBottom =
|
|
chatlog.scrollTop + 16 >= chatlog.scrollHeight - chatlog.clientHeight;
|
|
|
|
if ("u" in message) {
|
|
chatlog.append(<article className="user">{message.u}</article>);
|
|
} else if ("f" in message) {
|
|
chatlog.append(<article className="assistant">{message.f}</article>);
|
|
} else if ("s" in message) {
|
|
const article = <article className="assistant" />;
|
|
inFlightMessages.set(message.s, article);
|
|
chatlog.append(article);
|
|
} else if ("r" in message && "c" in message) {
|
|
const article = inFlightMessages.get(message.r)!;
|
|
article.append(message.c);
|
|
} else if ("d" in message) {
|
|
inFlightMessages.delete(message.d);
|
|
}
|
|
|
|
if (scrolledToBottom) chatlog.scrollTop = chatlog.scrollHeight - chatlog.clientHeight;
|
|
});
|
|
|
|
const form = (
|
|
<form>
|
|
<input type="text" required />
|
|
</form>
|
|
);
|
|
const input = form.querySelector("input")!;
|
|
form.addEventListener("submit", e => {
|
|
e.preventDefault();
|
|
socket.send(input.value);
|
|
input.value = "";
|
|
});
|
|
|
|
return (
|
|
<section className="conversation">
|
|
{chatlog}
|
|
{form}
|
|
</section>
|
|
);
|
|
}
|
|
|
|
const showUI = async () => {
|
|
main.innerHTML = "";
|
|
|
|
if (window.location.hash) {
|
|
main.append(conversationUI(window.location.hash.substring(1)));
|
|
} else {
|
|
main.append(await nav());
|
|
}
|
|
};
|
|
|
|
await showUI();
|
|
window.addEventListener("hashchange", async () => {
|
|
await showUI();
|
|
});
|