displaynames, unselect thread, other fixes

main
jerl 2024-05-27 18:39:16 -07:00
parent 8ef0172d11
commit 7deeb82a08
10 changed files with 122 additions and 71 deletions

View File

@ -17,16 +17,20 @@ function setVisibility() {
function chooseThread() {
const edit = document.getElementById('edit');
if (window.currentThread) {
if (window.currentThread.id === this.thread.id)
return;
document.getElementById(`thread${window.currentThread.id}`)
.classList.remove('active');
document.getElementById(`thread${window.currentThread.id}`).classList.remove('active');
let editform = document.getElementById('editthread');
if (editform) {
editform.remove();
edit.textContent = 'edit';
}
if (window.currentThread.id === this.thread.id) {
document.getElementById('thread').classList.add('hidden');
window.currentThread = null;
return;
}
}
else
document.getElementById('thread').classList.remove('hidden');
if (this.thread.permissions.admin)
edit.classList.remove('hidden');
else
@ -43,7 +47,7 @@ function chooseThread() {
switchTab(document.getElementById(this.tab = 'messagetab'));
else
loadSpace(spans => {
if (spans)
if (spans.length)
switchTab(document.getElementById(this.tab = 'spacetab'));
else if (window.currentThread.streams.length)
switchTab(document.getElementById(this.tab = 'streamtab'));
@ -246,6 +250,21 @@ function clickedTab(event) {
document.getElementById(`thread${window.currentThread.id}`).tab = event.target.id;
}
function changeName(e) {
let displayname = document.getElementById('newname').value;
window.emit('update_user', {
displayname
}, msg => {
if (!msg.success) {
console.log('update_user error: ', msg.message);
document.getElementById('savename').classList.remove('hidden');
return;
}
window.displayname = displayname;
document.getElementById('savename').classList.add('hidden');
})
}
// main app html
document.body.append(html.node`
<div id='home' class='column'>
@ -263,7 +282,17 @@ document.body.append(html.node`
<!-- create thread column goes here -->
<hr class='separator' color='#505050'>
<div id='profile' class='column hidden'>
<p><strong>authentication requests</strong></p>
<label class='heading'>display name</label>
<input id='newname' onkeyup=${function(event) {
if (window.displayname === this.value)
document.getElementById('savename').classList.add('hidden');
else if (event.key === 'Enter')
changeName();
else
document.getElementById('savename').classList.remove('hidden');
}}>
<button class='hidden' id='savename' onclick=${changeName}>save</button>
<label class='heading'>authentication requests</label>
<div id='authrequests'></div>
</div>
<hr class='separator' color='#505050'>
@ -298,6 +327,8 @@ document.body.append(html.node`
</div>
`);
document.getElementById('newname').value = window.displayname;
function makeThread(thread) {
let node = html.node`
<div class='thread' onclick=${chooseThread}>${
@ -313,7 +344,7 @@ window.socket.on('thread', thread => {
if (el) {
el.thread = thread;
el.textContent = thread.name;
if (window.currentThread.id === thread.id) {
if (window.currentThread?.id === thread.id) {
Object.assign(window.currentThread, thread);
document.getElementById('threadname').textContent = thread.name;
setVisibility();
@ -323,21 +354,32 @@ window.socket.on('thread', thread => {
document.getElementById('threadlist').prepend(makeThread(thread));
});
function authRequest(authrequest) {
const p = html.node`
<p>
<button onclick=${() => {
window.emit('authorize_key', {
id: authrequest.id
}, msg => {
if (!msg.success)
console.log('authorize_key failed: ', msg.message);
});
p.remove();
}}>approve</button>
session <strong>${authrequest.id}</strong>
</p>`;
document.getElementById('authrequests').append(p);
setTimeout(() => p.remove(), Date.now() - authrequest.time + 60000 * 5);
}
window.socket.on('authrequest', authRequest);
window.emit('list_threads', {}, msg => {
const threadlist = document.getElementById('threadlist');
threadlist.replaceChildren(...msg.threads.map(makeThread));
chooseThread.call(threadlist.firstChild);
});
window.socket.on('authrequest', (msg, respond) => {
const div = html.node`
<div class='authrequest'>
<button onclick=${() => {
respond(true);
div.remove();
}}>approve</button>
session <strong>${msg.id}</strong>
</div>`;
document.getElementById('authrequests').append(div);
setTimeout(() => div.remove(), Date.now() - msg.time + 60000 * 5);
});
export {
authRequest
};

View File

@ -23,7 +23,7 @@ async function auth() {
message: sig,
pubkey: window.keys.armored.publicKey
},
msg => {
async msg => {
let register = document.getElementById('register');
if (!msg.success) {
console.log('authenticate failed', msg);
@ -34,7 +34,9 @@ async function auth() {
localStorage.setItem('keys', JSON.stringify(window.keys.armored));
localStorage.setItem('name', window.name);
register.classList.add('hidden');
import('/app.js');
window.displayname = msg.displayname;
const { authRequest } = await import('/app.js');
msg.authrequests.forEach(authRequest);
}
);
}

View File

@ -99,7 +99,7 @@
}
.column {
flex: 1;
margin: 2px;
margin: 3px;
}
.separator {
margin: 8px 2px;
@ -116,7 +116,6 @@
justify-content: space-between;
}
#threads {
margin: 3px;
min-height: 0;
display: flex;
flex-direction: column;
@ -130,15 +129,9 @@
}
#profile {
max-width: 250px;
> * {
margin: 4px;
}
}
.authrequest {
margin-block: 3px;
}
.thread {
padding: 2px 4px;
padding: 2px 3px;
white-space: pre;
}
#newthread {

View File

@ -16,7 +16,7 @@ function sendMessage(e) {
let earliestMessage;
window.socket.on('new_message', message => {
if (message.thread !== window.currentThread.id)
if (message.thread !== window.currentThread?.id)
return;
const messages = document.getElementById('messages');
let scroll = messages.scrollHeight - messages.scrollTop <= messages.clientHeight;
@ -73,7 +73,7 @@ function loadMessages(firstRender, callback) {
for (let message of msg.messages)
messages.prepend(html.node`
<div class='message'>
<strong>${message.name}: </strong>
<strong>${message.displayname}: </strong>
${message.message}
</div>`);
}

View File

@ -132,7 +132,7 @@ function loadStreams() {
}
window.socket.on('stream', async msg => {
if (msg.thread !== window.currentThread.id)
if (msg.thread !== window.currentThread?.id)
return;
let p = document.getElementById('stream' + msg.id);
if (p) {

View File

@ -1,6 +1,7 @@
create table user (
id integer primary key asc,
name text,
displayname text,
created timestamp default current_timestamp
);
@ -60,11 +61,3 @@ create table span (
scale decimal,
foreign key(thread) references thread(id)
);
insert into thread (name) values ("meow");
insert into permission
(thread, type, permission, value) values
(1, "everyone", "view", "true");
insert into permission
(thread, type, permission, value) values
(1, "everyone", "post", "true");

View File

@ -11,10 +11,7 @@ const authwrap = (fn) => async (msg, respond, socket) => {
}
return await fn({
...msg,
auth_user: {
id: vybe.users[socket.username].id,
name: socket.username
}
auth_user: vybe.users[socket.username]
}, respond, socket);
};

View File

@ -36,7 +36,8 @@ async function send_message(msg, respond) {
for (let s of vybe.users[username].sockets) {
s.emit('new_message', {
id: id.rows[0].id,
name: msg.auth_user.name,
username: msg.auth_user.name,
displayname: msg.auth_user.displayname,
message: msg.message,
thread: msg.thread
});

View File

@ -341,7 +341,9 @@ async function edit_thread(msg, respond) {
permissions: {
is_member: username in members,
view: true,
post: !msg.permissions || !msg.permissions.post_limited,
post: !msg.permissions || !msg.permissions.post_limited
|| username in members,
admin: username === msg.auth_user.name && perms.admin,
...permissions
}
});

View File

@ -47,7 +47,7 @@ async function create_user(msg, respond) {
[insert.rows[0].id, msg.pubkey]
)
// respond
return respond({
respond({
success: true,
id: insert.rows[0].id
});
@ -60,9 +60,9 @@ async function authenticate(msg, respond, socket) {
message: 'invalid message'
});
}
let userid = await db.query(
`select user.id from user where name = ?`, [msg.name]);
if (userid.rows.length === 0) {
let user = (await db.query(
`select id, displayname from user where name = ?`, [msg.name])).rows[0];
if (!user) {
return respond({
success: false,
message: 'user not found'
@ -91,13 +91,14 @@ async function authenticate(msg, respond, socket) {
message: 'bad auth message'
});
}
let user = vybe.users[msg.name];
if (!user)
user = vybe.users[msg.name] = {
id: userid.rows[0].id,
sockets: [],
authrequests: {}
};
if (!user.displayname)
user.displayname = msg.name;
user = vybe.users[msg.name] || (vybe.users[msg.name] = {
...user,
name: msg.name,
sockets: [],
authrequests: {}
});
if (result.rows.length === 0) {
// request auth from logged in sessions
let id = key.getFingerprint().slice(0, 8);
@ -105,7 +106,7 @@ async function authenticate(msg, respond, socket) {
if (!await new Promise(resolve => {
user.authrequests[id] = { time, callback: resolve };
for (let s of user.sockets)
s.emit('authrequest', { id, time }, resolve);
s.emit('authrequest', { id, time });
setTimeout(() => {
delete user.authrequests[id];
resolve(false);
@ -124,27 +125,45 @@ async function authenticate(msg, respond, socket) {
socket.username = msg.name;
user.sockets.push(socket);
respond({
success: true
success: true,
displayname: user.displayname,
authrequests: Object.entries(user.authrequests).map(authrequest => ({
id: authrequest[0],
time: authrequest[1].time
}))
});
// send authenticated session any auth requests
if (result.rows.length)
setTimeout(() => {
for (let id in user.authrequests)
socket.emit('authrequest', {
id,
time: user.authrequests[id].time
}, user.authrequests[id].callback);
}, 1000); // todo: do this better
}
catch (err) {
console.error('error in authentication: ' + err);
return respond({
respond({
success: false,
message: 'message signature verification failed'
});
}
}
async function authorize_key(msg, respond) {
let authrequest = vybe.users[msg.auth_user.name].authrequests[msg.id];
if (!authrequest) {
return respond({
success: false,
message: 'auth request not found'
});
}
authrequest.callback(true);
respond({
success: true
});
}
async function update_user(msg, respond) {
await db.query(`update user set displayname = ?`, msg.displayname);
vybe.users[msg.auth_user.name].displayname = msg.displayname;
respond({
success: true
});
}
async function get_keys(msg, respond) {
// validate inputs
if (!msg.names) {
@ -165,7 +184,7 @@ async function get_keys(msg, respond) {
msg.names
);
// respond
return respond({
respond({
success: true,
keys: keys.rows
});
@ -174,5 +193,7 @@ async function get_keys(msg, respond) {
module.exports = {
create_user,
authenticate,
authorize_key: authwrap(authorize_key),
update_user: authwrap(update_user),
get_keys: authwrap(get_keys)
};