stuff n tabs

main
jerl 2023-12-28 11:54:16 -08:00
parent 8cd970e4f8
commit 0b830174d3
7 changed files with 185 additions and 148 deletions

View File

@ -58,10 +58,14 @@ function addMember() {
async function createThread(e) { async function createThread(e) {
e.preventDefault(); e.preventDefault();
let members = window.threadmembers.map(name => { name }); 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( const perms = document.querySelector(
'input[name="permissions"]:checked' 'input[name="permissions"]:checked').value;
).value;
if (perms === "private_view") if (perms === "private_view")
members = (await new Promise(resolve => members = (await new Promise(resolve =>
window.emit("get_keys", { names: window.threadmembers }, resolve) window.emit("get_keys", { names: window.threadmembers }, resolve)
@ -98,12 +102,12 @@ async function createThread(e) {
*/ */
} }
window.emit("create_thread", { window.emit("create_thread", {
name: document.getElementById("newthreadname").value, name: name.value,
permissions, permissions,
members members
}, msg => { }, msg => {
chooseThread({ chooseThread({
name: document.getElementById("newthreadname").value, name: name.value,
id: msg.id id: msg.id
}); });
document.getElementById('createthread').remove(); document.getElementById('createthread').remove();
@ -138,12 +142,12 @@ function newThread(e) {
<label for="newthreadname">thread name</label> <label for="newthreadname">thread name</label>
<input type="text" id="newthreadname" /> <input type="text" id="newthreadname" />
<p id='permissions'>thread permissions</p> <p id='permissions'>thread permissions</p>
<input type="radio" id="public" name="permissions" value="public" /> <input type="radio" id="public" name="permissions" value="public" checked />
<label for="public">anyone can view and post</label><br /> <label for="public">anyone can view and post</label><br />
<input type="radio" id="private_post" <input type="radio" id="private_post"
name="permissions" value="private_post" name="permissions" value="private_post"
/> />
<label for="private_post">only members can post, anyone can view</label><br/> <label for="private_post">anyone can view, only members can post</label><br/>
<input type="radio" id="private_view" <input type="radio" id="private_view"
name="permissions" value="private_view" name="permissions" value="private_view"
/> />
@ -168,27 +172,41 @@ function newThread(e) {
} }
} }
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` render(document.body, html`
<div id="app"> <div id="threads" class="column">
<div id="threads" class="column"> <h3>vybe</h3>
<h1>vybe</h1> <h4>threads</h4>
<h3>threads</h3> <div id="threadlist">loading...</div>
<div id="threadlist">loading...</div> <button id='newthread' onclick=${newThread}>create</button>
<button id='newthread' onclick=${newThread}>create</button> </div>
<div id="thread" class="column">
<div id='title'>
thread: <strong id="threadname">meow</strong>
</div> </div>
<div id="thread" class="column"> <button id='messagetab' class='tab active' onclick=${switchTab}>messages</button>
<h3> <button id='spacetab' class='tab' onclick=${switchTab}>space</button>
thread: <strong id="threadname">meow</strong> <button id="loadmore" class="hidden" onclick=${loadMessages}>load more messages</button>
</h3> <div id='message' class='tabcontent'>
<h3>messages</h3> <div id='messages'>
<button id="loadmore" class="hidden" onclick=${loadMessages}>load more messages</button> </div>
<div id="messages"></div>
<form id="msginput" onsubmit=${sendMessage}> <form id="msginput" onsubmit=${sendMessage}>
<input type="text" placeholder="write a message..." id="msg" /> <input type="text" placeholder="write a message..." id="msg" />
<button type="submit" class="hidden" id="sendmsg"></button> <button type="submit" class="hidden" id="sendmsg"></button>
</form> </form>
</div> </div>
</div>`); <div id='space' class='tabcontent'></div>
</div>
`);
window.socket.on("new_message", (msg) => { window.socket.on("new_message", (msg) => {
if (msg.thread !== window.currentThreadId) if (msg.thread !== window.currentThreadId)

View File

@ -46,15 +46,16 @@ render(document.body, html`
}); });
const priv = await openpgp.readKey({ armoredKey: keys.privateKey }); const priv = await openpgp.readKey({ armoredKey: keys.privateKey });
const pub = await openpgp.readKey({ armoredKey: keys.publicKey }); const pub = await openpgp.readKey({ armoredKey: keys.publicKey });
window.keys = { priv, pub };
localStorage.setItem("keys", JSON.stringify(keys));
localStorage.setItem("name", name);
window.name = name;
window.emit("create_user", { name, pubkey: keys.publicKey }, msg => { window.emit("create_user", { name, pubkey: keys.publicKey }, msg => {
if (!msg.success) { if (!msg.success) {
console.log('create user failed'); document.querySelector('#registerform').insertAdjacentHTML('afterend', `
<p>${msg.message}</p>`);
return; return;
} }
window.keys = { priv, pub };
localStorage.setItem("keys", JSON.stringify(keys));
localStorage.setItem("name", name);
window.name = name;
auth(); auth();
}); });
}} id="registerform"> }} id="registerform">

View File

@ -27,11 +27,8 @@
margin: 0; margin: 0;
min-width: min-content; min-width: min-content;
} }
h1 { h3, h4 {
margin: 0; margin: 10px 0;
}
h3 {
margin: 11px 0;
} }
button { button {
border-color: #767676; border-color: #767676;
@ -39,12 +36,6 @@
.hidden { .hidden {
display: none; display: none;
} }
#app {
display: contents;
}
#app.hidden {
display: none;
}
.column { .column {
flex: 1; flex: 1;
margin: 5px; margin: 5px;
@ -53,18 +44,41 @@
#threads { #threads {
max-width: 250px; max-width: 250px;
} }
.thread:hover {
background-color: #3b3b3b;
}
.thread.selected { .thread.selected {
background-color: #4f4f4f; background-color: #4f4f4f;
} }
.thread:hover {
background-color: #3b3b3b;
}
#newthread { #newthread {
margin-top: 5px; margin-top: 5px;
} }
#createthread {
max-width: 350px;
}
#permissions { #permissions {
margin-bottom: 5px; margin-bottom: 5px;
} }
#title {
margin: 4px 2px;
}
.tab {
border: 0;
margin-top: 2px;
padding: 5px 7px;
color: #ccc;
font-weight: 500;
}
.tab.active {
background-color: #4f4f4f;
color: #fff;
}
.tab:hover {
background-color:#3b3b3b;
}
#messages {
margin: 4px 2px;
}
#msginput { #msginput {
margin-top: 15px; margin-top: 15px;
} }

View File

@ -32,6 +32,11 @@ io.on("connection", (socket) => {
events[event](msg, callback, socket, io) events[event](msg, callback, socket, io)
); );
} }
socket.on('disconnect', reason => {
let sockets = io.cache[socket.username];
if (sockets)
sockets.splice(sockets.indexOf(socket.id), 1);
})
}); });
server.listen(PORT, () => { server.listen(PORT, () => {

View File

@ -42,7 +42,7 @@ const authenticate = async (msg, respond, socket, io) => {
result.rows[0].id, result.rows[0].id,
data[1], data[1],
]); ]);
socket.userid = result.rows[0].id; socket.username = msg.name;
if (io.cache[msg.name]) { if (io.cache[msg.name]) {
io.cache[msg.name].push(socket.id); io.cache[msg.name].push(socket.id);
} else { } else {

View File

@ -2,109 +2,108 @@ const db = require("../db");
const authwrap = require("./authwrap"); const authwrap = require("./authwrap");
const create_thread = async (msg, respond, socket, io) => { const create_thread = async (msg, respond, socket, io) => {
// validate inputs // validate inputs
if (!msg.name) { if (typeof msg.name !== 'string') {
return respond({ return respond({
success: false, success: false,
message: "thread name required", message: "thread name required",
}); });
} }
if (msg.name.length > 200) { if (msg.name.length > 200) {
return respond({ return respond({
success: false, success: false,
message: "thread name 200 chars max", message: "thread name 200 chars max",
}); });
} }
// add to db // add to db
const insert = await db.query( const insert = await db.query(
"insert into threads (name, creator) values (?, ?) returning id", "insert into threads (name, creator) values (?, ?) returning id",
[msg.name, msg.auth_user.id] [msg.name, msg.auth_user.id]
); );
const thread_id = insert.rows[0].id; const thread_id = insert.rows[0].id;
// set up permissions // set up permissions
if (!msg.permissions || !msg.permissions.view_limited) { if (!msg.permissions || !msg.permissions.view_limited) {
await db.query( await db.query(
`insert into permissions (thread, type, mutable, permission, value) `insert into permissions (thread, type, mutable, permission, value)
values (?, ?, ?, ?, ?)`, values (?, ?, ?, ?, ?)`,
[thread_id, "everyone", false, "view", "true"] [thread_id, "everyone", false, "view", "true"]
); );
if (!msg.permissions || !msg.permissions.post_limited) { if (!msg.permissions || !msg.permissions.post_limited) {
await db.query( await db.query(
`insert into permissions (thread, type, mutable, permission, value) `insert into permissions (thread, type, mutable, permission, value)
values (?, ?, ?, ?, ?)`, values (?, ?, ?, ?, ?)`,
[thread_id, "everyone", false, "post", "true"] [thread_id, "everyone", false, "post", "true"]
); );
} else { } else {
await db.query( await db.query(
`insert into permissions (thread, type, mutable, permission, value) `insert into permissions (thread, type, mutable, permission, value)
values (?, ?, ?, ?, ?)`, values (?, ?, ?, ?, ?)`,
[thread_id, "members", false, "post", "true"] [thread_id, "members", false, "post", "true"]
); );
} }
} else { } else {
await db.query( await db.query(
`insert into permissions (thread, type, mutable, permission, value) `insert into permissions (thread, type, mutable, permission, value)
values (?, ?, ?, ?, ?)`, values (?, ?, ?, ?, ?)`,
[thread_id, "members", false, "view", "true"] [thread_id, "members", false, "view", "true"]
); );
await db.query( await db.query(
`insert into permissions (thread, type, mutable, permission, value) `insert into permissions (thread, type, mutable, permission, value)
values (?, ?, ?, ?, ?)`, values (?, ?, ?, ?, ?)`,
[thread_id, "members", false, "post", "true"] [thread_id, "members", false, "post", "true"]
); );
} }
// add members // add members
for (let user of msg.members) { for (let user of msg.members) {
// get user id if (!user) continue;
const id = await db.query("select id from users where name = ?", [ // get user id
user.name, const id = await db.query("select id from users where name = ?", [
]); user.name,
if (id.rows.length > 0) { ]);
const user_id = id.rows[0].id; if (id.rows.length > 0) {
await db.query( const user_id = id.rows[0].id;
"insert into members (thread, user, key_delivery) values (?, ?, ?)", await db.query(
[thread_id, user_id, user.key] "insert into members (thread, user, key_delivery) values (?, ?, ?)",
); [thread_id, user_id, user.key]
} );
} }
const member_perms = { }
is_member: true, if (!msg.permissions || !msg.permissions.view_limited) {
view: true, for (let username in io.cache) {
post: true, for (let socket of io.cache[username]) {
}; io.to(socket).emit("new_thread", {
const general_perms = { name: msg.name,
is_member: false, id: insert.rows[0].id,
view: !msg.permissions || !msg.permissions.view_limited, permissions: {
post: !msg.permissions || !msg.permissions.post_limited, is_member: false,
}; view: true,
for (let username in io.cache) { post: !msg.permissions || !msg.permissions.post_limited
let member = msg.members.filter((i) => i.user === username)[0]; },
if (member) { });
const sockets = io.cache[username]; }
for (let s of sockets) { }
io.to(s).emit("new_thread", { }
name: msg.name, else {
id: insert.rows[0].id, for (let member of msg.members) {
permissions: member_perms, for (let socket of io.cache[member.user]) {
key: member.key, io.to(socket).emit("new_thread", {
}); name: msg.name,
} id: insert.rows[0].id,
} else if (general_perms.view) { permissions: {
const sockets = io.cache[username]; is_member: true,
for (let s of sockets) { view: true,
io.to(s).emit("new_thread", { post: true
name: msg.name, },
id: insert.rows[0].id, key: member.key
permissions: general_perms, });
}); }
} }
} }
} // respond
// respond return respond({
return respond({ success: true,
success: true, id: insert.rows[0].id,
id: insert.rows[0].id, });
});
}; };
module.exports = authwrap(create_thread); module.exports = authwrap(create_thread);

View File

@ -6,7 +6,7 @@ const create_user = async (msg, respond) => {
if (!msg.name) { if (!msg.name) {
return respond({ return respond({
success: false, success: false,
message: "Username required", message: "username required",
}); });
} }
// ensure username is not taken // ensure username is not taken
@ -17,14 +17,14 @@ const create_user = async (msg, respond) => {
console.log(`username already exists: ${result}`); console.log(`username already exists: ${result}`);
return respond({ return respond({
success: false, success: false,
message: "A user with this name already exists on this server", message: "a user with this name already exists on this server",
}); });
} }
// validate public key // validate public key
if (!msg.pubkey) { if (!msg.pubkey) {
return respond({ return respond({
success: false, success: false,
message: "Public key required", message: "public key required",
}); });
} }
try { try {
@ -33,7 +33,7 @@ const create_user = async (msg, respond) => {
console.err("error in create_user readkey: " + err); console.err("error in create_user readkey: " + err);
return respond({ return respond({
success: false, success: false,
message: "Public key invalid", message: "public key invalid",
}); });
} }
// add to db // add to db