displaynames, unselect thread, other fixes
parent
8ef0172d11
commit
7deeb82a08
|
@ -17,16 +17,20 @@ function setVisibility() {
|
||||||
function chooseThread() {
|
function chooseThread() {
|
||||||
const edit = document.getElementById('edit');
|
const edit = document.getElementById('edit');
|
||||||
if (window.currentThread) {
|
if (window.currentThread) {
|
||||||
if (window.currentThread.id === this.thread.id)
|
document.getElementById(`thread${window.currentThread.id}`).classList.remove('active');
|
||||||
return;
|
|
||||||
document.getElementById(`thread${window.currentThread.id}`)
|
|
||||||
.classList.remove('active');
|
|
||||||
let editform = document.getElementById('editthread');
|
let editform = document.getElementById('editthread');
|
||||||
if (editform) {
|
if (editform) {
|
||||||
editform.remove();
|
editform.remove();
|
||||||
edit.textContent = 'edit';
|
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)
|
if (this.thread.permissions.admin)
|
||||||
edit.classList.remove('hidden');
|
edit.classList.remove('hidden');
|
||||||
else
|
else
|
||||||
|
@ -43,7 +47,7 @@ function chooseThread() {
|
||||||
switchTab(document.getElementById(this.tab = 'messagetab'));
|
switchTab(document.getElementById(this.tab = 'messagetab'));
|
||||||
else
|
else
|
||||||
loadSpace(spans => {
|
loadSpace(spans => {
|
||||||
if (spans)
|
if (spans.length)
|
||||||
switchTab(document.getElementById(this.tab = 'spacetab'));
|
switchTab(document.getElementById(this.tab = 'spacetab'));
|
||||||
else if (window.currentThread.streams.length)
|
else if (window.currentThread.streams.length)
|
||||||
switchTab(document.getElementById(this.tab = 'streamtab'));
|
switchTab(document.getElementById(this.tab = 'streamtab'));
|
||||||
|
@ -246,6 +250,21 @@ function clickedTab(event) {
|
||||||
document.getElementById(`thread${window.currentThread.id}`).tab = event.target.id;
|
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
|
// main app html
|
||||||
document.body.append(html.node`
|
document.body.append(html.node`
|
||||||
<div id='home' class='column'>
|
<div id='home' class='column'>
|
||||||
|
@ -263,7 +282,17 @@ document.body.append(html.node`
|
||||||
<!-- create thread column goes here -->
|
<!-- create thread column goes here -->
|
||||||
<hr class='separator' color='#505050'>
|
<hr class='separator' color='#505050'>
|
||||||
<div id='profile' class='column hidden'>
|
<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 id='authrequests'></div>
|
||||||
</div>
|
</div>
|
||||||
<hr class='separator' color='#505050'>
|
<hr class='separator' color='#505050'>
|
||||||
|
@ -298,6 +327,8 @@ document.body.append(html.node`
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
document.getElementById('newname').value = window.displayname;
|
||||||
|
|
||||||
function makeThread(thread) {
|
function makeThread(thread) {
|
||||||
let node = html.node`
|
let node = html.node`
|
||||||
<div class='thread' onclick=${chooseThread}>${
|
<div class='thread' onclick=${chooseThread}>${
|
||||||
|
@ -313,7 +344,7 @@ window.socket.on('thread', thread => {
|
||||||
if (el) {
|
if (el) {
|
||||||
el.thread = thread;
|
el.thread = thread;
|
||||||
el.textContent = thread.name;
|
el.textContent = thread.name;
|
||||||
if (window.currentThread.id === thread.id) {
|
if (window.currentThread?.id === thread.id) {
|
||||||
Object.assign(window.currentThread, thread);
|
Object.assign(window.currentThread, thread);
|
||||||
document.getElementById('threadname').textContent = thread.name;
|
document.getElementById('threadname').textContent = thread.name;
|
||||||
setVisibility();
|
setVisibility();
|
||||||
|
@ -323,21 +354,32 @@ window.socket.on('thread', thread => {
|
||||||
document.getElementById('threadlist').prepend(makeThread(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 => {
|
window.emit('list_threads', {}, msg => {
|
||||||
const threadlist = document.getElementById('threadlist');
|
const threadlist = document.getElementById('threadlist');
|
||||||
threadlist.replaceChildren(...msg.threads.map(makeThread));
|
threadlist.replaceChildren(...msg.threads.map(makeThread));
|
||||||
chooseThread.call(threadlist.firstChild);
|
chooseThread.call(threadlist.firstChild);
|
||||||
});
|
});
|
||||||
|
|
||||||
window.socket.on('authrequest', (msg, respond) => {
|
export {
|
||||||
const div = html.node`
|
authRequest
|
||||||
<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);
|
|
||||||
});
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ async function auth() {
|
||||||
message: sig,
|
message: sig,
|
||||||
pubkey: window.keys.armored.publicKey
|
pubkey: window.keys.armored.publicKey
|
||||||
},
|
},
|
||||||
msg => {
|
async msg => {
|
||||||
let register = document.getElementById('register');
|
let register = document.getElementById('register');
|
||||||
if (!msg.success) {
|
if (!msg.success) {
|
||||||
console.log('authenticate failed', msg);
|
console.log('authenticate failed', msg);
|
||||||
|
@ -34,7 +34,9 @@ async function auth() {
|
||||||
localStorage.setItem('keys', JSON.stringify(window.keys.armored));
|
localStorage.setItem('keys', JSON.stringify(window.keys.armored));
|
||||||
localStorage.setItem('name', window.name);
|
localStorage.setItem('name', window.name);
|
||||||
register.classList.add('hidden');
|
register.classList.add('hidden');
|
||||||
import('/app.js');
|
window.displayname = msg.displayname;
|
||||||
|
const { authRequest } = await import('/app.js');
|
||||||
|
msg.authrequests.forEach(authRequest);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@
|
||||||
}
|
}
|
||||||
.column {
|
.column {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin: 2px;
|
margin: 3px;
|
||||||
}
|
}
|
||||||
.separator {
|
.separator {
|
||||||
margin: 8px 2px;
|
margin: 8px 2px;
|
||||||
|
@ -116,7 +116,6 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
#threads {
|
#threads {
|
||||||
margin: 3px;
|
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -130,15 +129,9 @@
|
||||||
}
|
}
|
||||||
#profile {
|
#profile {
|
||||||
max-width: 250px;
|
max-width: 250px;
|
||||||
> * {
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.authrequest {
|
|
||||||
margin-block: 3px;
|
|
||||||
}
|
}
|
||||||
.thread {
|
.thread {
|
||||||
padding: 2px 4px;
|
padding: 2px 3px;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
}
|
}
|
||||||
#newthread {
|
#newthread {
|
||||||
|
|
|
@ -16,7 +16,7 @@ function sendMessage(e) {
|
||||||
let earliestMessage;
|
let earliestMessage;
|
||||||
|
|
||||||
window.socket.on('new_message', message => {
|
window.socket.on('new_message', message => {
|
||||||
if (message.thread !== window.currentThread.id)
|
if (message.thread !== window.currentThread?.id)
|
||||||
return;
|
return;
|
||||||
const messages = document.getElementById('messages');
|
const messages = document.getElementById('messages');
|
||||||
let scroll = messages.scrollHeight - messages.scrollTop <= messages.clientHeight;
|
let scroll = messages.scrollHeight - messages.scrollTop <= messages.clientHeight;
|
||||||
|
@ -73,7 +73,7 @@ function loadMessages(firstRender, callback) {
|
||||||
for (let message of msg.messages)
|
for (let message of msg.messages)
|
||||||
messages.prepend(html.node`
|
messages.prepend(html.node`
|
||||||
<div class='message'>
|
<div class='message'>
|
||||||
<strong>${message.name}: </strong>
|
<strong>${message.displayname}: </strong>
|
||||||
${message.message}
|
${message.message}
|
||||||
</div>`);
|
</div>`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,7 +132,7 @@ function loadStreams() {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.socket.on('stream', async msg => {
|
window.socket.on('stream', async msg => {
|
||||||
if (msg.thread !== window.currentThread.id)
|
if (msg.thread !== window.currentThread?.id)
|
||||||
return;
|
return;
|
||||||
let p = document.getElementById('stream' + msg.id);
|
let p = document.getElementById('stream' + msg.id);
|
||||||
if (p) {
|
if (p) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
create table user (
|
create table user (
|
||||||
id integer primary key asc,
|
id integer primary key asc,
|
||||||
name text,
|
name text,
|
||||||
|
displayname text,
|
||||||
created timestamp default current_timestamp
|
created timestamp default current_timestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -60,11 +61,3 @@ create table span (
|
||||||
scale decimal,
|
scale decimal,
|
||||||
foreign key(thread) references thread(id)
|
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");
|
|
||||||
|
|
|
@ -11,10 +11,7 @@ const authwrap = (fn) => async (msg, respond, socket) => {
|
||||||
}
|
}
|
||||||
return await fn({
|
return await fn({
|
||||||
...msg,
|
...msg,
|
||||||
auth_user: {
|
auth_user: vybe.users[socket.username]
|
||||||
id: vybe.users[socket.username].id,
|
|
||||||
name: socket.username
|
|
||||||
}
|
|
||||||
}, respond, socket);
|
}, respond, socket);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,8 @@ async function send_message(msg, respond) {
|
||||||
for (let s of vybe.users[username].sockets) {
|
for (let s of vybe.users[username].sockets) {
|
||||||
s.emit('new_message', {
|
s.emit('new_message', {
|
||||||
id: id.rows[0].id,
|
id: id.rows[0].id,
|
||||||
name: msg.auth_user.name,
|
username: msg.auth_user.name,
|
||||||
|
displayname: msg.auth_user.displayname,
|
||||||
message: msg.message,
|
message: msg.message,
|
||||||
thread: msg.thread
|
thread: msg.thread
|
||||||
});
|
});
|
||||||
|
|
|
@ -341,7 +341,9 @@ async function edit_thread(msg, respond) {
|
||||||
permissions: {
|
permissions: {
|
||||||
is_member: username in members,
|
is_member: username in members,
|
||||||
view: true,
|
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
|
...permissions
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -47,7 +47,7 @@ async function create_user(msg, respond) {
|
||||||
[insert.rows[0].id, msg.pubkey]
|
[insert.rows[0].id, msg.pubkey]
|
||||||
)
|
)
|
||||||
// respond
|
// respond
|
||||||
return respond({
|
respond({
|
||||||
success: true,
|
success: true,
|
||||||
id: insert.rows[0].id
|
id: insert.rows[0].id
|
||||||
});
|
});
|
||||||
|
@ -60,9 +60,9 @@ async function authenticate(msg, respond, socket) {
|
||||||
message: 'invalid message'
|
message: 'invalid message'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let userid = await db.query(
|
let user = (await db.query(
|
||||||
`select user.id from user where name = ?`, [msg.name]);
|
`select id, displayname from user where name = ?`, [msg.name])).rows[0];
|
||||||
if (userid.rows.length === 0) {
|
if (!user) {
|
||||||
return respond({
|
return respond({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'user not found'
|
message: 'user not found'
|
||||||
|
@ -91,13 +91,14 @@ async function authenticate(msg, respond, socket) {
|
||||||
message: 'bad auth message'
|
message: 'bad auth message'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let user = vybe.users[msg.name];
|
if (!user.displayname)
|
||||||
if (!user)
|
user.displayname = msg.name;
|
||||||
user = vybe.users[msg.name] = {
|
user = vybe.users[msg.name] || (vybe.users[msg.name] = {
|
||||||
id: userid.rows[0].id,
|
...user,
|
||||||
sockets: [],
|
name: msg.name,
|
||||||
authrequests: {}
|
sockets: [],
|
||||||
};
|
authrequests: {}
|
||||||
|
});
|
||||||
if (result.rows.length === 0) {
|
if (result.rows.length === 0) {
|
||||||
// request auth from logged in sessions
|
// request auth from logged in sessions
|
||||||
let id = key.getFingerprint().slice(0, 8);
|
let id = key.getFingerprint().slice(0, 8);
|
||||||
|
@ -105,7 +106,7 @@ async function authenticate(msg, respond, socket) {
|
||||||
if (!await new Promise(resolve => {
|
if (!await new Promise(resolve => {
|
||||||
user.authrequests[id] = { time, callback: resolve };
|
user.authrequests[id] = { time, callback: resolve };
|
||||||
for (let s of user.sockets)
|
for (let s of user.sockets)
|
||||||
s.emit('authrequest', { id, time }, resolve);
|
s.emit('authrequest', { id, time });
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
delete user.authrequests[id];
|
delete user.authrequests[id];
|
||||||
resolve(false);
|
resolve(false);
|
||||||
|
@ -124,27 +125,45 @@ async function authenticate(msg, respond, socket) {
|
||||||
socket.username = msg.name;
|
socket.username = msg.name;
|
||||||
user.sockets.push(socket);
|
user.sockets.push(socket);
|
||||||
respond({
|
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) {
|
catch (err) {
|
||||||
console.error('error in authentication: ' + err);
|
console.error('error in authentication: ' + err);
|
||||||
return respond({
|
respond({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'message signature verification failed'
|
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) {
|
async function get_keys(msg, respond) {
|
||||||
// validate inputs
|
// validate inputs
|
||||||
if (!msg.names) {
|
if (!msg.names) {
|
||||||
|
@ -165,7 +184,7 @@ async function get_keys(msg, respond) {
|
||||||
msg.names
|
msg.names
|
||||||
);
|
);
|
||||||
// respond
|
// respond
|
||||||
return respond({
|
respond({
|
||||||
success: true,
|
success: true,
|
||||||
keys: keys.rows
|
keys: keys.rows
|
||||||
});
|
});
|
||||||
|
@ -174,5 +193,7 @@ async function get_keys(msg, respond) {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
create_user,
|
create_user,
|
||||||
authenticate,
|
authenticate,
|
||||||
|
authorize_key: authwrap(authorize_key),
|
||||||
|
update_user: authwrap(update_user),
|
||||||
get_keys: authwrap(get_keys)
|
get_keys: authwrap(get_keys)
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue