vybe/client/app.js

247 lines
7.4 KiB
JavaScript

import { render, html } from "/uhtml.js";
window.currentThreadId = 1;
function chooseThread(thread) {
if (window.currentThreadId)
document
.getElementById(`thread${window.currentThreadId}`)
.classList.remove("active");
document.getElementById(`thread${thread.id}`).classList.add("active");
window.currentThreadId = thread.id;
window.earliestMessage = null;
document.getElementById("messages").innerHTML = "";
document.getElementById("threadname").textContent = thread.name;
if (!thread.permissions.post) {
document.getElementById("msginput").classList.add("hidden");
} else {
document.getElementById("msginput").classList.remove("hidden");
}
loadMessages();
}
function loadMessages() {
window.emit(
"get_history",
{
before: window.earliestMessage,
thread: window.currentThreadId,
},
(msg) => {
if (msg.messages.length > 0) {
window.earliestMessage = msg.messages[msg.messages.length - 1].id;
for (let message of msg.messages)
document.getElementById("messages").prepend(html.node`
<div class='message'>
<strong>${message.name}: </strong>
${message.message}
</div>`);
}
if (!msg.more)
document.getElementById("loadmore").classList.add("hidden");
else document.getElementById("loadmore").classList.remove("hidden");
}
);
}
function addThread(thread, top) {
let node = html.node`
<button class='thread tab' onclick=${() => chooseThread(thread)}>${
thread.name
}</button>`;
node.id = `thread${thread.id}`;
document.getElementById("threadlist")[top ? "prepend" : "appendChild"](node);
}
function addMember() {
const name = document.getElementById("membername").value;
window.threadmembers.push(name);
document
.getElementById("memberlist")
.appendChild(html.node`<p class='member'>${name}</p>`);
document.getElementById("membername").value = "";
}
async function createThread(e) {
e.preventDefault();
let name = document.getElementById("newthreadname");
if (!name.value) {
name.insertAdjacentHTML("afterend", `<p>name cannot be empty</p>`);
return;
}
let members = window.threadmembers.map((name) => ({ name }));
const perms = document.querySelector(
'input[name="permissions"]:checked'
).value;
if (perms === "private_view")
members = (
await new Promise((resolve) =>
window.emit("get_keys", { names: window.threadmembers }, resolve)
)
).keys;
let permissions;
if (perms === "public") {
permissions = {
view_limited: false,
post_limited: false,
};
} else if (perms === "private_post") {
permissions = {
view_limited: false,
post_limited: true,
};
} else if (perms === "private_view") {
permissions = {
view_limited: true,
post_limited: true,
};
// generate key
/* wip
var buf = new Uint8Array(32);
crypto.getRandomValues(buf);
const key = aesjs.utils.hex.fromBytes(Array.from(buf));
// sign it to each of the members
for (let i = 0; i < newmembers.length; i++) {
const member = newmembers[i];
const sig = await openpgp.encrypt({
message: await openpgp.createMessage({ text: key }),
signingKeys: window.keys.priv,
});
}
*/
}
window.emit(
"create_thread",
{
name: name.value,
permissions,
members,
},
(msg) => {
chooseThread({
name: name.value,
id: msg.id,
});
// since the form exists, this will perform cleanup
newThread();
document.getElementById("loadmore").classList.add("hidden");
document.getElementById("msginput").classList.remove("hidden");
}
);
}
function sendMessage(e) {
e.preventDefault();
const msg = document.getElementById("msg").value;
if (!msg) return;
window.emit("send_message", {
message: msg,
thread: window.currentThreadId,
});
document.getElementById("msg").value = "";
}
function newThread() {
let form = document.getElementById("createthread");
if (form) {
form.remove();
document.getElementById("newthread").textContent = "create";
} else {
window.threadmembers = [window.name];
document.getElementById("threads").insertAdjacentElement(
"afterend",
html.node`
<form id="createthread" class='column' onsubmit=${createThread}>
<h3>create thread</h3>
<label for="newthreadname" class="heading">thread name</label>
<input type="text" id="newthreadname" />
<p id='permissions'>thread permissions</p>
<input type="radio" id="public" name="permissions" value="public" checked />
<label for="public">anyone can view and post</label><br />
<input type="radio" id="private_post"
name="permissions" value="private_post"
/>
<label for="private_post">anyone can view, only members can post</label><br/>
<input type="radio" id="private_view"
name="permissions" value="private_view"
/>
<label for="private_view">only members can view and post</label
><br /><br />
<label class="heading" for="membername">members</label>
<input type="text" id="membername" placeholder="username" onkeydown=${(e) => {
if (e.key == "Enter") {
e.preventDefault();
addMember();
}
}}/>
<button id="addmember" onclick=${addMember}>add</button>
<div id="memberlist">
<p class='member'>${window.name}</p>
</div>
<br />
<button id="submitthread" type="submit">create</button>
</form>
`
);
document.getElementById("newthread").textContent = "cancel";
}
}
function switchTab(event) {
for (let tab of document.querySelectorAll(".tab"))
tab.classList.remove("active");
for (let tab of document.querySelectorAll(".tabcontent"))
tab.classList.add("hidden");
event.target.classList.add("active");
document
.getElementById(event.target.id.substring(0, event.target.id.length - 3))
.classList.remove("hidden");
}
render(
document.body,
html`
<div id="threads" class="column">
<h3>vybe</h3>
<h4>threads</h4>
<div id="threadlist">loading...</div>
<button id="newthread" onclick=${newThread}>create</button>
</div>
<div id="thread" class="column">
<div id="title">thread: <strong id="threadname">meow</strong></div>
<button id="messagetab" class="tab active" onclick=${switchTab}>
messages
</button>
<button id="spacetab" class="tab" onclick=${switchTab}>space</button>
<button id="loadmore" class="hidden" onclick=${loadMessages}>
load more messages
</button>
<div id="message" class="tabcontent">
<div id="messages"></div>
<form id="msginput" onsubmit=${sendMessage}>
<input type="text" placeholder="write a message..." id="msg" />
<button type="submit" class="hidden" id="sendmsg"></button>
</form>
</div>
<div id="space" class="tabcontent"></div>
</div>
`
);
window.socket.on("new_message", (msg) => {
if (msg.thread !== window.currentThreadId) return;
document.getElementById("messages").appendChild(html.node`
<div class='message'>
<strong>${msg.name}: </strong>
${msg.message}
</div>`);
if (!window.earliestMessage) window.earliestMessage = msg.id;
});
window.socket.on("new_thread", (thread) => addThread(thread, true));
window.emit("list_threads", {}, (msg) => {
document.getElementById("threadlist").innerHTML = "";
for (let thread of msg.threads) addThread(thread);
chooseThread(msg.threads[0]);
});