view users
parent
b8b7a7edd6
commit
62816d255f
|
@ -1,10 +1,11 @@
|
|||
import { render, html } from '/uhtml.js';
|
||||
import loadThreads from '/thread.js';
|
||||
|
||||
function changeName(e) {
|
||||
function saveProfile(e) {
|
||||
let displayname = document.getElementById('newname').value;
|
||||
window.emit('update_user', {
|
||||
displayname
|
||||
displayname,
|
||||
public: document.getElementById('profilepublic').checked
|
||||
}, msg => {
|
||||
if (!msg.success) {
|
||||
console.log('update_user error: ', msg.message);
|
||||
|
@ -13,7 +14,7 @@ function changeName(e) {
|
|||
}
|
||||
window.displayname = displayname;
|
||||
document.getElementById('savename').classList.add('hidden');
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
window.connectInstance = async url => {
|
||||
|
@ -59,12 +60,31 @@ window.getUser = async (id, name) => {
|
|||
return new Promise((resolve, reject) =>
|
||||
instance.socket.emit('get_user', { id, name }, msg => {
|
||||
if (!msg.success)
|
||||
return reject('get_user failed: ' + msg.message);
|
||||
if (msg.message === 'user not found')
|
||||
return resolve();
|
||||
else
|
||||
return reject(msg.message);
|
||||
resolve(msg.user);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
window.makeUser = (user, url) => {
|
||||
let span = html.node`
|
||||
<span class='user' onclick=${function(e) {
|
||||
let member = document.getElementById('member');
|
||||
if (member.user === user)
|
||||
member.classList.toggle('hidden');
|
||||
else
|
||||
member.classList.remove('hidden');
|
||||
member.user = user;
|
||||
document.getElementById('membername').textContent = user.displayname;
|
||||
document.getElementById('memberusername').textContent = `${user.name}@${url}`;
|
||||
}}>${user.displayname}</span>`;
|
||||
span.user = user;
|
||||
return span;
|
||||
};
|
||||
|
||||
async function authenticateInstance(div, select) {
|
||||
return new Promise((resolve, reject) =>
|
||||
div.instance.socket.emit('authenticate', {
|
||||
|
@ -107,8 +127,11 @@ function instanceClicked(event) {
|
|||
let userlist = document.getElementById('userlist');
|
||||
userlist.innerHTML = '';
|
||||
instance.emit('list_users', {}, msg =>
|
||||
userlist.innerHTML = msg.users.map(user =>
|
||||
`<p>${user.displayname}</p>`).join('\n')
|
||||
userlist.replaceChildren(...msg.users.map(user => {
|
||||
let p = document.createElement('p');
|
||||
p.append(window.makeUser(user, instance.url));
|
||||
return p;
|
||||
}))
|
||||
);
|
||||
div.instance = instance;
|
||||
}
|
||||
|
@ -155,11 +178,15 @@ document.body.append(html.node`
|
|||
if (window.displayname === this.value)
|
||||
document.getElementById('savename').classList.add('hidden');
|
||||
else if (event.key === 'Enter')
|
||||
changeName();
|
||||
saveProfile();
|
||||
else
|
||||
document.getElementById('savename').classList.remove('hidden');
|
||||
}}>
|
||||
<button class='hidden' id='savename' onclick=${changeName}>save</button>
|
||||
<button class='hidden' id='savename' onclick=${saveProfile}>save</button>
|
||||
<p>
|
||||
<input id='profilepublic' type='checkbox' oninput=${saveProfile}>
|
||||
<label for='profilepublic'>show profile in instance users</label>
|
||||
</p>
|
||||
<label class='heading'>authentication requests</label>
|
||||
<div id='authrequests'></div>
|
||||
</div>
|
||||
|
@ -207,6 +234,17 @@ document.body.append(html.node`
|
|||
<hr class='separator' color='#505050'>
|
||||
<!-- create thread column goes here -->
|
||||
<hr class='separator' color='#505050'>
|
||||
<div id='thread' class='column hidden'></div>
|
||||
<hr class='separator' color='#505050'>
|
||||
<div id='member' class='column hidden'>
|
||||
<p>
|
||||
<button onclick=${e =>
|
||||
document.getElementById('member').classList.add('hidden')
|
||||
}>close</button>
|
||||
</p>
|
||||
<p>user: <strong id='membername'></strong></p>
|
||||
<p id='memberusername'></p>
|
||||
</div>
|
||||
`);
|
||||
|
||||
for (let i = 0; i < instancelist.length; ++i) {
|
||||
|
|
|
@ -32,17 +32,16 @@ async function auth() {
|
|||
pubkey: window.keys.armored.publicKey
|
||||
},
|
||||
async msg => {
|
||||
let register = document.getElementById('register');
|
||||
if (!msg.success) {
|
||||
console.log('authenticate failed:', msg.message);
|
||||
if (document.getElementById('app'))
|
||||
location.reload();
|
||||
document.getElementById('result').innerText = msg.message;
|
||||
register.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
localStorage.setItem('keys', JSON.stringify(window.keys.armored));
|
||||
localStorage.setItem('name', window.name = msg.name);
|
||||
localStorage.setItem('id', window.id = msg.id);
|
||||
register.classList.add('hidden');
|
||||
window.displayname = msg.displayname;
|
||||
if (window.instancelist) {
|
||||
if (window.instancelist[0].url !== location.host
|
||||
|
@ -62,8 +61,10 @@ async function auth() {
|
|||
instancelist[0].socket = window.socket;
|
||||
instancelist[0].emit = window.emit;
|
||||
window.instances = Object.fromEntries(instancelist.map(i => [i.url, i]));
|
||||
document.getElementById('register')?.remove();
|
||||
const { authRequest } = await import('/app.js');
|
||||
msg.authrequests.forEach(authRequest);
|
||||
document.getElementById('profilepublic').checked = msg.public;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
&::placeholder {
|
||||
color: #aaa;
|
||||
}
|
||||
&[type='radio'] {
|
||||
&[type='radio'], &[type='checkbox'] {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
|
@ -107,7 +107,7 @@
|
|||
margin: 8px 2px;
|
||||
}
|
||||
.separator:has(+ .separator),
|
||||
*.hidden + .separator,
|
||||
*.hidden:first-child + .separator,
|
||||
.separator:has(+ *.hidden),
|
||||
.separator:last-child {
|
||||
display: none;
|
||||
|
@ -171,6 +171,12 @@
|
|||
padding: 6px;
|
||||
background-color: #222;
|
||||
}
|
||||
.user {
|
||||
white-space: pre-wrap;
|
||||
&:hover {
|
||||
background-color: #444;
|
||||
}
|
||||
}
|
||||
#profile {
|
||||
max-width: 250px;
|
||||
}
|
||||
|
@ -257,6 +263,9 @@
|
|||
min-width: 140px;
|
||||
max-width: 250px;
|
||||
}
|
||||
#member {
|
||||
max-width: 250px;
|
||||
}
|
||||
#editthread {
|
||||
max-width: fit-content;
|
||||
}
|
||||
|
|
|
@ -53,22 +53,27 @@ function loadMessages(callback) {
|
|||
earliestMessage = msg.messages[msg.messages.length - 1].id;
|
||||
let users = {};
|
||||
for (let message of msg.messages) {
|
||||
if (!message.name) {
|
||||
let span;
|
||||
if (message.name)
|
||||
span = window.makeUser({
|
||||
displayname: message.displayname,
|
||||
name: message.name,
|
||||
id: message.user
|
||||
}, instance.url);
|
||||
else {
|
||||
try {
|
||||
message.name = (users[message.userid] || (
|
||||
span = window.makeUser(users[message.userid] || (
|
||||
users[message.userid] = await window.getUser(message.userid)
|
||||
)).displayname;
|
||||
), message.userid.split('@')[1]);
|
||||
}
|
||||
catch (e) {
|
||||
console.log(`error getting user ${message.userid}`, e);
|
||||
message.name = message.userid;
|
||||
span = html.node`<span>${message.userid}</span>`;
|
||||
}
|
||||
}
|
||||
messages.prepend(html.node`
|
||||
<div class='message'>
|
||||
<strong>${message.name}: </strong>
|
||||
${message.content}
|
||||
</div>`);
|
||||
message = html.node`<div class='message'>: ${message.content}</div>`;
|
||||
message.prepend(span);
|
||||
messages.prepend(message);
|
||||
}
|
||||
}
|
||||
if (msg.more)
|
||||
|
@ -84,11 +89,13 @@ function loadMessages(callback) {
|
|||
return;
|
||||
const messages = document.getElementById('messages');
|
||||
let scroll = messages.scrollTop + 10 >= messages.scrollHeight - messages.clientHeight;
|
||||
messages.appendChild(html.node`
|
||||
<div class='message'>
|
||||
<strong>${message.name}: </strong>
|
||||
${message.content}
|
||||
</div>`);
|
||||
let div = html.node`<div class='message'>: ${message.content}</div>`;
|
||||
div.prepend(window.makeUser({
|
||||
name: window.name,
|
||||
displayname: window.displayname,
|
||||
id: window.id
|
||||
}, location.host));
|
||||
messages.append(div);
|
||||
if (scroll)
|
||||
messages.scroll(0, messages.scrollHeight - messages.clientHeight);
|
||||
if (!earliestMessage)
|
||||
|
|
|
@ -72,10 +72,12 @@ function chooseThread() {
|
|||
loadStreams();
|
||||
setVisibility();
|
||||
document.getElementById('memberlist').replaceChildren(
|
||||
...await Promise.all(msg.thread.members.map(async member =>
|
||||
html.node`<p class='member'>${
|
||||
member.name || (await window.getUser(member.id)).name
|
||||
}</p>`))
|
||||
...await Promise.all(msg.thread.members.map(async member => {
|
||||
let p = document.createElement('p');
|
||||
p.append(member.name ? window.makeUser(member, window.currentInstance.url)
|
||||
: makeUser(await window.getUser(member.id), member.id.split('@')[1]));
|
||||
return p;
|
||||
}))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -111,12 +113,19 @@ function newThread() {
|
|||
}
|
||||
|
||||
async function addMember() {
|
||||
const name = document.getElementById('membername');
|
||||
const name = document.getElementById('newmembername');
|
||||
if (!name.value)
|
||||
return;
|
||||
let at = name.value.split('@');
|
||||
let url = at[1] || instancediv.instance.url;
|
||||
let error = document.getElementById('membererror');
|
||||
let user = await window.getUser('@' + url, at[0]);
|
||||
if (!user) {
|
||||
error.innerText = 'user not found';
|
||||
return;
|
||||
}
|
||||
error.innerText = '';
|
||||
user.id = String(user.id);
|
||||
if (instancediv.instance.url !== url) {
|
||||
user.id += '@' + url;
|
||||
user.name += '@' + url;
|
||||
|
@ -178,14 +187,15 @@ function newThread() {
|
|||
<input type='radio' name='newpermissions'
|
||||
id='private_view' value='private_view' />
|
||||
<label for='private_view'>only members can view and post</label><br>
|
||||
<label class='heading' for='membername'>members</label>
|
||||
<input type='text' id='membername' placeholder='username' onkeydown=${event => {
|
||||
<label class='heading' for='newmembername'>members</label>
|
||||
<input type='text' id='newmembername' placeholder='username' onkeydown=${event => {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
addMember();
|
||||
}
|
||||
}} />
|
||||
<button id='addmember' type='button' onclick=${addMember}>add</button>
|
||||
<p id='membererror'></p>
|
||||
<div id='newmembers'>
|
||||
<p class='member'>${members[0].name}</p>
|
||||
</div>
|
||||
|
@ -261,7 +271,7 @@ function editThread() {
|
|||
form['private_post'].checked = true;
|
||||
else
|
||||
form['private_view'].checked = true;
|
||||
document.body.append(form);
|
||||
document.getElementById('thread').append(form);
|
||||
document.getElementById('edit').textContent = 'cancel';
|
||||
}
|
||||
|
||||
|
@ -287,9 +297,9 @@ async function loadThreads(instancediv, select) {
|
|||
return node;
|
||||
}
|
||||
|
||||
if (!document.getElementById('thread'))
|
||||
document.body.append(html.node`
|
||||
<div id='thread' class='column hidden'>
|
||||
let thread = document.getElementById('thread');
|
||||
if (!thread.hasChildNodes())
|
||||
thread.append(html.node`
|
||||
<div id='content' class='content'>
|
||||
<div id='titlebar'>
|
||||
<span id='title'>thread: <strong id='threadname'>meow</strong></span>
|
||||
|
@ -316,7 +326,6 @@ async function loadThreads(instancediv, select) {
|
|||
<div id='memberlist'>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class='separator' color='#505050'>`);
|
||||
|
||||
if (!instancediv.children['threads']) {
|
||||
|
|
|
@ -2,6 +2,7 @@ create table user (
|
|||
id integer primary key asc,
|
||||
name text,
|
||||
displayname text,
|
||||
public boolean default true,
|
||||
created timestamp default current_timestamp
|
||||
);
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ async function get_history(msg, respond) {
|
|||
});
|
||||
}
|
||||
const messages = (await db.query(
|
||||
`select coalesce(displayname, name) as name, user as userid, post.id, content
|
||||
`select coalesce(displayname, name) as displayname, name, user as userid, post.id, content
|
||||
from post
|
||||
left join user on post.user = user.id
|
||||
where ${msg.before ? 'post.id < ? and' : ''}
|
||||
|
|
|
@ -39,8 +39,8 @@ async function create_user(msg, respond) {
|
|||
}
|
||||
// add to db
|
||||
const insert = await db.query(
|
||||
'insert into user (name) values (?) returning id',
|
||||
[msg.name]
|
||||
'insert into user (name, public) values (?, ?) returning id',
|
||||
[msg.name, true]
|
||||
);
|
||||
await db.query(
|
||||
'insert into key (user, pubkey, active) values (?, ?, true)',
|
||||
|
@ -55,9 +55,9 @@ async function create_user(msg, respond) {
|
|||
|
||||
async function getUser(id, name) {
|
||||
return (id ? await db.query(
|
||||
`select name, id, displayname from user where id = ?`, id)
|
||||
`select name, id, displayname, public from user where id = ?`, id)
|
||||
: await db.query(
|
||||
`select name, id, displayname from user where name = ?`, name)
|
||||
`select name, id, displayname, public from user where name = ?`, name)
|
||||
).rows[0];
|
||||
}
|
||||
|
||||
|
@ -184,6 +184,7 @@ async function authenticate(msg, respond, socket) {
|
|||
id: user.id,
|
||||
name: user.name,
|
||||
displayname: user.displayname,
|
||||
public: user.public,
|
||||
authrequests: Object.entries(user.authrequests).map(authrequest => ({
|
||||
id: authrequest[0],
|
||||
time: authrequest[1].time
|
||||
|
@ -221,8 +222,8 @@ async function update_user(msg, respond) {
|
|||
message: 'user not found'
|
||||
});
|
||||
await db.query(
|
||||
`update user set displayname = ? where id = ?`,
|
||||
[msg.displayname, msg.auth_user.id]);
|
||||
`update user set displayname = ?, public = ? where id = ?`,
|
||||
[msg.displayname, !!msg.public, msg.auth_user.id]);
|
||||
vybe.users[msg.auth_user.id].displayname = msg.displayname;
|
||||
respond({
|
||||
success: true
|
||||
|
@ -254,7 +255,8 @@ async function list_users(msg, respond) {
|
|||
success: true,
|
||||
users: (await db.query(`
|
||||
select id, name, coalesce(displayname, name) as displayname
|
||||
from user`)).rows
|
||||
from user
|
||||
where public = true`)).rows
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue