vybe/client/app.js

203 lines
5.8 KiB
JavaScript

import { render, html } from '/uhtml.js';
import loadMessages from '/message.js';
import loadSpace from '/space.js';
function chooseThread() {
if (window.threadId) {
if (window.threadId === this.thread.id)
return;
document.getElementById(`thread${window.threadId}`)
.classList.remove('active');
}
document.getElementById('threadname').textContent = this.thread.name;
this.classList.add('active');
window.threadId = this.thread.id;
loadMessages();
if (this.thread.permissions.post)
document.getElementById('msginput').classList.remove('hidden');
else
document.getElementById('msginput').classList.add('hidden');
switchTab(document.getElementById(this.tab));
if (this.tab === 'spacetab')
loadSpace();
}
function switchTab(tab) {
for (let tab of document.querySelectorAll('.tab'))
tab.classList.remove('active');
for (let tab of document.querySelectorAll('.tabcontent'))
tab.classList.add('hidden');
tab.classList.add('active');
document
.getElementById(tab.id.slice(0, -3))
.classList.remove('hidden');
if (tab.id === 'spacetab')
loadSpace();
}
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').value;
if (!name) {
document.getElementById('nameempty').classList.remove('hidden');
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,
permissions,
members
},
msg => {
chooseThread.call(document.getElementById('thread' + msg.id));
// since the form exists, this will perform cleanup
newThread();
document.getElementById('loadmore').classList.add('hidden');
}
);
}
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>
<p id='nameempty' class='hidden'>name cannot be empty</p>
<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 clickedTab(event) {
switchTab(event.target);
document.getElementById(`thread${window.threadId}`).tab = event.target.id;
}
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>
<hr class='separator' color='#666'>
<div id='thread' class='column'>
<div id='title'>
thread: <strong id='threadname'>meow</strong>
</div>
<div id='tabs'>
<button id='messagetab' class='tab active' onclick=${clickedTab}>messages</button><button id='spacetab' class='tab' onclick=${clickedTab}>space</button>
</div>
<div id='message' class='tabcontent'></div>
<div id='space' class='tabcontent hidden'></div>
</div>
`);
function makeThread(thread) {
let node = html.node`
<div class='thread' onclick=${chooseThread}>${
thread.name
}</div>`;
node.id = 'thread' + thread.id;
node.thread = thread;
node.tab = 'messagetab';
return node;
}
window.socket.on('new_thread', thread => {
document.getElementById('threadlist').prepend(makeThread(thread));
});
window.emit('list_threads', {}, msg => {
const threadlist = document.getElementById('threadlist')
threadlist.innerHTML = '';
for (let thread of msg.threads)
threadlist.appendChild(makeThread(thread));
chooseThread.call(threadlist.firstChild);
});