edit thread

main
jerl 2024-05-12 23:46:05 -07:00
parent e9f3b3f15a
commit 6531e692f7
6 changed files with 404 additions and 161 deletions

View File

@ -2,13 +2,34 @@ import { render, html } from '/uhtml.js';
import loadMessages from '/message.js'; import loadMessages from '/message.js';
import loadSpace from '/space.js'; import loadSpace from '/space.js';
function setVisibility() {
document.getElementById('visibility').innerText = `${
window.currentThread.permissions.everyone.view.value ?
'this thread is visible to everyone' :
'members can view this thread'}
${window.currentThread.permissions.everyone.post.value ?
'anyone can post' :
window.currentThread.permissions.members.post.value ?
'only members can post' : 'some members can post'}`;
}
function chooseThread() { function chooseThread() {
const edit = document.getElementById('edit');
if (window.currentThread) { if (window.currentThread) {
if (window.currentThread.id === this.thread.id) if (window.currentThread.id === this.thread.id)
return; return;
document.getElementById(`thread${window.currentThread.id}`) document.getElementById(`thread${window.currentThread.id}`)
.classList.remove('active'); .classList.remove('active');
let editform = document.getElementById('editthread');
if (editform) {
editform.remove();
edit.textContent = 'edit';
} }
}
if (this.thread.permissions.admin)
edit.classList.remove('hidden');
else
edit.classList.add('hidden');
document.getElementById('threadname').textContent = this.thread.name; document.getElementById('threadname').textContent = this.thread.name;
this.classList.add('active'); this.classList.add('active');
window.currentThread = this.thread; window.currentThread = this.thread;
@ -19,15 +40,8 @@ function chooseThread() {
document.getElementById('msginput').classList.add('hidden'); document.getElementById('msginput').classList.add('hidden');
switchTab(document.getElementById(this.tab)); switchTab(document.getElementById(this.tab));
window.emit('get_thread', { thread: this.thread.id }, msg => { window.emit('get_thread', { thread: this.thread.id }, msg => {
window.currentThread.members = msg.thread.members; window.currentThread = msg.thread;
document.getElementById('visibility').innerText = `${ setVisibility();
msg.thread.permissions.everyone?.view.value === 'true' ?
'this thread is visible to everyone' :
'members can view this thread'}
${msg.thread.permissions.everyone?.post.value === 'true' ?
'anyone can post' :
msg.thread.permissions.members?.post.view ?
'only members can post' : 'select members can post'}`;
document.getElementById('memberlist').replaceChildren( document.getElementById('memberlist').replaceChildren(
...msg.thread.members.map(member => ...msg.thread.members.map(member =>
html.node`<p class='member'>${member.name}</p>`) html.node`<p class='member'>${member.name}</p>`)
@ -48,48 +62,23 @@ function switchTab(tab) {
loadSpace(); loadSpace();
} }
function addMember() {
const name = document.getElementById('membername').value;
window.threadmembers.push(name);
document
.getElementById('newmembers')
.appendChild(html.node`<p class='member'>${name}</p>`);
document.getElementById('membername').value = '';
}
async function createThread(event) { async function createThread(event) {
event.preventDefault(); event.preventDefault();
let name = document.getElementById('newthreadname').value; let name = document.getElementById('newthreadname').value;
if (!name) { if (!name) {
document.getElementById('nameempty').classList.remove('hidden'); document.getElementById('newnameempty').classList.remove('hidden');
return; return;
} }
let members = window.threadmembers.map(name => ({ name }));
const perms = document.querySelector( const perms = document.querySelector(
'input[name="permissions"]:checked' 'input[name="newpermissions"]:checked'
).value; ).value;
if (perms === 'private_view')
members = (
await new Promise(resolve =>
window.emit('get_keys', { names: window.threadmembers }, resolve)
)
).keys;
let permissions; let permissions;
if (perms === 'public') { if (perms === 'public')
permissions = { permissions = { view_limited: false, post_limited: false };
view_limited: false, else if (perms === 'private_post')
post_limited: false permissions = { view_limited: false, post_limited: true };
}; else if (perms === 'private_view') {
} else if (perms === 'private_post') { permissions = { view_limited: true, post_limited: true };
permissions = {
view_limited: false,
post_limited: true
};
} else if (perms === 'private_view') {
permissions = {
view_limited: true,
post_limited: true
};
// generate key // generate key
/* wip /* wip
var buf = new Uint8Array(32); var buf = new Uint8Array(32);
@ -100,18 +89,13 @@ async function createThread(event) {
const member = newmembers[i]; const member = newmembers[i];
const sig = await openpgp.encrypt({ const sig = await openpgp.encrypt({
message: await openpgp.createMessage({ text: key }), message: await openpgp.createMessage({ text: key }),
signingKeys: window.keys.priv, signingKeys: window.keys.priv
}); });
} }
*/ */
} }
window.emit( window.emit('create_thread',
'create_thread', { name, permissions, members: window.threadmembers },
{
name,
permissions,
members
},
msg => { msg => {
chooseThread.call(document.getElementById('thread' + msg.id)); chooseThread.call(document.getElementById('thread' + msg.id));
// since the form exists, this will perform cleanup // since the form exists, this will perform cleanup
@ -121,35 +105,44 @@ async function createThread(event) {
); );
} }
function addMember() {
const name = document.getElementById('membername');
if (!name.value)
return;
window.threadmembers.push({ name: name.value });
document.getElementById('newmembers')
.appendChild(html.node`<p class='member'>${name.value}</p>`);
name.value = '';
}
function newThread() { function newThread() {
let form = document.getElementById('createthread'); let form = document.getElementById('createthread');
if (form) { if (form) {
form.remove(); form.remove();
document.getElementById('createseparator').remove();
document.getElementById('newthread').textContent = 'create'; document.getElementById('newthread').textContent = 'create';
} else { return;
window.threadmembers = [window.name]; }
document.getElementById('home') window.threadmembers = [{
.insertAdjacentElement('afterend', html.node` name: window.name,
<hr id='createseparator' class='separator' color='#505050'>`) permissions: { admin: 'true' }
.insertAdjacentElement('afterend', html.node` }];
document.querySelector('#home + .separator').insertAdjacentElement('afterend', html.node`
<form id='createthread' class='column' onsubmit=${createThread}> <form id='createthread' class='column' onsubmit=${createThread}>
<h3>create thread</h3> <h3>create thread</h3>
<label for='newthreadname' class='heading'>thread name</label> <label for='newthreadname' class='heading'>thread name</label>
<p id='nameempty' class='hidden'>name cannot be empty</p> <p id='newnameempty' class='hidden'>name cannot be empty</p>
<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' checked /> <input type='radio' name='newpermissions'
<label for='public'>anyone can view and post</label><br /> id='public' value='public' checked />
<input type='radio' id='private_post' <label for='public'>anyone can view and post</label><br>
name='permissions' value='private_post' <input type='radio' name='newpermissions'
/> id='private_post' value='private_post' />
<label for='private_post'>anyone can view, only members can post</label><br/> <label for='private_post'>anyone can view, only members can post</label><br>
<input type='radio' id='private_view' <input type='radio' name='newpermissions'
name='permissions' value='private_view' id='private_view' value='private_view' />
/> <label for='private_view'>only members can view and post</label><br>
<label for='private_view'>only members can view and post</label <br>
><br /><br />
<label class='heading' for='membername'>members</label> <label class='heading' for='membername'>members</label>
<input type='text' id='membername' placeholder='username' onkeydown=${event => { <input type='text' id='membername' placeholder='username' onkeydown=${event => {
if (event.key === 'Enter') { if (event.key === 'Enter') {
@ -157,16 +150,80 @@ function newThread() {
addMember(); addMember();
} }
}} /> }} />
<button id='addmember' onclick=${addMember}>add</button> <button id='addmember' type='button' onclick=${addMember}>add</button>
<div id='newmembers'> <div id='newmembers'>
<p class='member'>${window.name}</p> <p class='member'>${window.name}</p>
</div> </div>
<br /> <br>
<button id='submitthread' type='submit'>create</button> <button id='submitthread' type='submit'>create</button>
</form> </form>`);
`);
document.getElementById('newthread').textContent = 'cancel'; document.getElementById('newthread').textContent = 'cancel';
} }
async function saveThread(event) {
event.preventDefault();
let name = document.getElementById('editthreadname').value;
if (!name) {
document.getElementById('nameempty').classList.remove('hidden');
return;
}
const perms = document.querySelector(
'input[name="permissions"]:checked'
).value;
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 };
// todo: generate key and encrypt
}
window.emit('edit_thread',
{ id: window.currentThread.id, name, permissions },
msg => {
if (!msg.success) {
console.log('edit_thread failed: ', msg.message);
return;
}
editThread();
});
}
function editThread() {
let form = document.getElementById('editthread');
if (form) {
form.remove();
document.getElementById('edit').textContent = 'edit';
return;
}
form = html.node`
<form id='editthread' class='column' onsubmit=${saveThread}>
<label for='editthreadname' class='heading'>thread name</label>
<input type='text' id='editthreadname' />
<p id='nameempty' class='hidden'>name cannot be empty</p>
<p id='permissions'>thread permissions</p>
<input type='radio' name='permissions'
id='public' value='public' />
<label for='public'>anyone can view and post</label><br>
<input type='radio' name='permissions'
id='private_post' value='private_post' />
<label for='private_post'>anyone can view, only members can post</label><br>
<input type='radio' name='permissions'
id='private_view' value='private_view' />
<label for='private_view'>only members can view and post</label><br>
<br>
<button id='savethread'>save</button>
</form>`;
form['editthreadname'].value = window.currentThread.name;
if (window.currentThread.permissions.everyone.post.value)
form['public'].checked = true;
else if (window.currentThread.permissions.everyone.view.value)
form['private_post'].checked = true;
else
form['private_view'].checked = true;
document.querySelector('#thread').append(form);
document.getElementById('edit').textContent = 'cancel';
} }
function clickedTab(event) { function clickedTab(event) {
@ -188,6 +245,8 @@ document.body.append(html.node`
}>${window.name}</div> }>${window.name}</div>
</div> </div>
<hr class='separator' color='#505050'> <hr class='separator' color='#505050'>
<!-- create thread column goes here -->
<hr class='separator' color='#505050'>
<div id='profile' class='column hidden'> <div id='profile' class='column hidden'>
<p><strong>authentication requests</strong></p> <p><strong>authentication requests</strong></p>
<div id='authrequests'></div> <div id='authrequests'></div>
@ -196,7 +255,8 @@ document.body.append(html.node`
<div id='thread' class='column'> <div id='thread' class='column'>
<div id='content'> <div id='content'>
<div id='title'> <div id='title'>
thread: <strong id='threadname'>meow</strong> <span>thread: <strong id='threadname'>meow</strong></span>
<button id='edit' class='hidden' onclick=${editThread}>edit</button>
</div> </div>
<div id='buttons'> <div id='buttons'>
<div id='tabs'> <div id='tabs'>
@ -217,6 +277,7 @@ document.body.append(html.node`
<div id='memberlist'> <div id='memberlist'>
</div> </div>
</div> </div>
<hr class='separator' color='#505050'>
</div> </div>
`); `);
@ -231,7 +292,18 @@ function makeThread(thread) {
return node; return node;
} }
window.socket.on('new_thread', thread => { window.socket.on('thread', thread => {
let el = document.getElementById('thread' + thread.id);
if (el) {
el.thread = thread;
el.textContent = thread.name;
if (window.currentThread.id === thread.id) {
Object.assign(window.currentThread, thread);
document.getElementById('threadname').textContent = thread.name;
setVisibility();
}
return;
}
document.getElementById('threadlist').prepend(makeThread(thread)); document.getElementById('threadlist').prepend(makeThread(thread));
}); });

View File

@ -48,7 +48,7 @@
button, button,
input, input,
.tab { .tab {
padding: 5px 7px; padding: 4px 7px;
} }
input { input {
background: #1b1b1b; background: #1b1b1b;
@ -81,9 +81,11 @@
margin-bottom: 5px; margin-bottom: 5px;
display: block; display: block;
} }
h3, h3 {
margin: 0;
}
h4 { h4 {
margin: 10px 0; margin: 6px 0;
} }
.hidden { .hidden {
display: none !important; display: none !important;
@ -96,7 +98,8 @@
margin: 8px 2px; margin: 8px 2px;
} }
.separator:has(+ .separator), .separator:has(+ .separator),
.separator:has(+ *.hidden) { .separator:has(+ *.hidden),
.separator:last-child {
display: none; display: none;
} }
#home { #home {
@ -107,6 +110,12 @@
} }
#threads { #threads {
margin: 3px; margin: 3px;
min-height: 0;
display: flex;
flex-direction: column;
}
#threadlist {
overflow: auto;
} }
#user { #user {
padding: 6px; padding: 6px;
@ -127,25 +136,26 @@
} }
#newthread { #newthread {
margin-top: 5px; margin-top: 5px;
width: fit-content;
} }
#createthread { #createthread {
max-width: fit-content; max-width: fit-content;
overflow: auto;
} }
#permissions { #permissions {
margin-bottom: 5px; margin-bottom: 5px;
} }
#thread { #thread {
display: flex; display: flex;
margin: 0;
} }
#content { #content {
margin: 2px;
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
#title { #title, #buttons {
margin: 4px;
}
#buttons {
margin: 4px 2px; margin: 4px 2px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -194,8 +204,12 @@
margin: 4px; margin: 4px;
} }
} }
#editthread {
max-width: fit-content;
}
#visibility { #visibility {
white-space: pre-line; white-space: pre-line;
margin-bottom: 12px;
} }
.member { .member {
margin: 5px 0; margin: 5px 0;

View File

@ -7,31 +7,20 @@ const check_permission = async (user_id, thread_id) => {
[thread_id] [thread_id]
); );
// check if the user is a member // check if the user is a member
const is_member = const is_member = (
( await db.query('select * from member where thread = ? and user = ?',
await db.query('select * from member where thread = ? and user = ?', [ [thread_id, user_id])
thread_id,
user_id,
])
).rows.length > 0; ).rows.length > 0;
const get_permission = (permission) => { let perms = { is_member };
const relevant = permissions.rows.filter( for (let p of permissions.rows) {
(i) => i.permission === permission if (p.type === 'everyone' && p.value === 'true')
); perms[p.permission] = true;
for (let i of relevant) { else if (p.type === 'members' && is_member && p.value === 'true')
if (i.type === 'everyone' && i.value === 'true') { perms[p.permission] = true;
return true; else if (p.type === 'user' && p.user === user_id && p.value === 'true')
perms[p.permission] = true;
} }
if (i.type === 'members' && i.value === 'true' && is_member) { return perms;
return true;
}
}
};
return {
is_member,
view: get_permission('view'),
post: get_permission('post')
};
}; };
module.exports = check_permission; module.exports = check_permission;

View File

@ -1,7 +1,7 @@
const db = require('../db'); const db = require('../db');
const authwrap = require('../authwrap'); const authwrap = require('../authwrap');
const create_thread = async (msg, respond, socket) => { const create_thread = async (msg, respond) => {
// validate inputs // validate inputs
if (typeof msg.name !== 'string') { if (typeof msg.name !== 'string') {
return respond({ return respond({
@ -26,52 +26,61 @@ const create_thread = async (msg, respond, socket) => {
await db.query( await db.query(
`insert into permission (thread, type, mutable, permission, value) `insert into permission (thread, type, mutable, permission, value)
values (?, ?, ?, ?, ?)`, values (?, ?, ?, ?, ?)`,
[thread_id, 'everyone', false, 'view', 'true'] [thread_id, 'everyone', true, 'view', 'true']
); );
if (!msg.permissions || !msg.permissions.post_limited) { if (!msg.permissions || !msg.permissions.post_limited) {
await db.query( await db.query(
`insert into permission (thread, type, mutable, permission, value) `insert into permission (thread, type, mutable, permission, value)
values (?, ?, ?, ?, ?)`, values (?, ?, ?, ?, ?)`,
[thread_id, 'everyone', false, 'post', 'true'] [thread_id, 'everyone', true, 'post', 'true']
); );
} else { } else {
await db.query( await db.query(
`insert into permission (thread, type, mutable, permission, value) `insert into permission (thread, type, mutable, permission, value)
values (?, ?, ?, ?, ?)`, values (?, ?, ?, ?, ?)`,
[thread_id, 'members', false, 'post', 'true'] [thread_id, 'members', true, 'post', 'true']
); );
} }
} else { } else {
await db.query( await db.query(
`insert into permission (thread, type, mutable, permission, value) `insert into permission (thread, type, mutable, permission, value)
values (?, ?, ?, ?, ?)`, values (?, ?, ?, ?, ?)`,
[thread_id, 'members', false, 'view', 'true'] [thread_id, 'members', true, 'view', 'true']
); );
await db.query( await db.query(
`insert into permission (thread, type, mutable, permission, value) `insert into permission (thread, type, mutable, permission, value)
values (?, ?, ?, ?, ?)`, values (?, ?, ?, ?, ?)`,
[thread_id, 'members', false, 'post', 'true'] [thread_id, 'members', true, 'post', 'true']
); );
} }
// add members // add members
for (let user of msg.members) { if (Array.isArray(msg.members)) {
if (!user) continue; for (let member of msg.members) {
if (!member) continue;
// get user id // get user id
const id = await db.query('select id from user where name = ?', [ const id = await db.query('select id from user where name = ?', [
user.name, member.name,
]); ]);
if (id.rows.length > 0) { if (id.rows.length === 0) {
const user_id = id.rows[0].id; console.log('user not found: ' + member.name);
continue;
}
await db.query( await db.query(
'insert into member (thread, user) values (?, ?)', 'insert into member (thread, user) values (?, ?)',
[thread_id, user_id] [thread_id, id.rows[0].id]
); );
if (typeof member.permissions === 'object')
for (let permission in member.permissions)
await db.query(`
insert into permission (thread, type, user, mutable, permission, value)
values (?, ?, ?, ?, ?, ?)`,
[thread_id, 'user', id.rows[0].id, true, permission, member.permissions[permission]]);
} }
} }
if (!msg.permissions || !msg.permissions.view_limited) { if (!msg.permissions || !msg.permissions.view_limited) {
for (let username in vybe.users) { for (let username in vybe.users) {
for (let socket of vybe.users[username].sockets) { for (let socket of vybe.users[username].sockets) {
socket.emit('new_thread', { socket.emit('thread', {
name: msg.name, name: msg.name,
id: insert.rows[0].id, id: insert.rows[0].id,
permissions: { permissions: {
@ -83,10 +92,12 @@ const create_thread = async (msg, respond, socket) => {
} }
} }
} }
else { else if (Array.isArray(msg.members)) {
for (let member of msg.members) { for (let member of msg.members) {
if (!vybe.users[member.name])
continue;
for (let socket of vybe.users[member.name].sockets) { for (let socket of vybe.users[member.name].sockets) {
socket.emit('new_thread', { socket.emit('thread', {
name: msg.name, name: msg.name,
id: insert.rows[0].id, id: insert.rows[0].id,
permissions: { permissions: {
@ -101,7 +112,7 @@ const create_thread = async (msg, respond, socket) => {
// respond // respond
return respond({ return respond({
success: true, success: true,
id: insert.rows[0].id id: thread_id
}); });
}; };

145
src/event/edit_thread.js Normal file
View File

@ -0,0 +1,145 @@
const db = require('../db');
const authwrap = require('../authwrap');
const check_permission = require('../check_permission');
const edit_thread = async (msg, respond) => {
// validate inputs
if (!msg.id || typeof msg.name !== 'string') {
return respond({
success: false,
message: 'invalid msg'
});
}
if (msg.name.length > 200) {
return respond({
success: false,
message: 'thread name 200 chars max'
});
}
const perms = await check_permission(msg.auth_user.id, msg.id);
if (!perms.admin) {
return respond({
success: false,
message: "user doesn't have permission"
});
}
// update name
await db.query(
'update thread set name = ? where id = ?',
[msg.name, msg.id]
);
// update permissions
let permissions = {};
for (const p of (await db.query(
`select type, permission, value, mutable
from permission
where type != 'user' and thread = ?`,
[msg.id]
)).rows) {
(permissions[p.type] || (permissions[p.type] = {}))
[p.permission] = {
value: p.value,
mutable: p.mutable
};
}
async function setPermission(type, permission, value) {
if (permissions[type] && permissions[type][permission]) {
if (!permissions[type][permission].mutable) {
respond({
success: false,
message: 'permission not mutable'
});
return false;
}
if (permissions[type][permission].value !== value) {
await db.query(`
update permission set value = ?
where thread = ? and type = ? and permission = ?`,
[value, msg.id, type, permission]
);
permissions[type][permission].value = value;
}
}
else {
await db.query(
`insert into permission (thread, type, mutable, permission, value)
values (?, ?, ?, ?, ?)`,
[msg.id, type, true, permission, value]
);
(permissions[type] || (permissions[type] = {}))
[permission] = {
value,
mutable: true
};
}
return true;
}
if (!msg.permissions || !msg.permissions.view_limited) {
if (!await setPermission('everyone', 'view', 'true'))
return;
if (!msg.permissions || !msg.permissions.post_limited) {
if (!await setPermission('everyone', 'post', 'true'))
return;
}
else {
if (!await setPermission('members', 'post', 'true'))
return;
if (!await setPermission('everyone', 'post', 'false'))
return;
}
} else {
if (!await setPermission('members', 'view', 'true'))
return;
if (!await setPermission('members', 'post', 'true'))
return;
if (!await setPermission('everyone', 'view', 'false'))
return;
if (!await setPermission('everyone', 'post', 'false'))
return;
}
if (!msg.permissions || !msg.permissions.view_limited) {
for (let username in vybe.users) {
for (let socket of vybe.users[username].sockets) {
socket.emit('thread', {
name: msg.name,
id: msg.id,
permissions: {
is_member: false,
view: true,
post: !msg.permissions || !msg.permissions.post_limited,
...permissions
}
});
}
}
}
else {
for (let member of (await db.query(
`select user.name from thread
join member on thread.id = member.thread
join user on user.id = member.user
where thread.id = ?`,
[msg.id]
)).rows) {
if (!vybe.users[member.name])
continue;
for (let socket of vybe.users[member.name].sockets) {
socket.emit('thread', {
name: msg.name,
id: msg.id,
permissions: {
is_member: true,
view: true,
post: true,
...permissions
}
});
}
}
}
return respond({
success: true
});
};
module.exports = authwrap(edit_thread);

View File

@ -9,7 +9,8 @@ const get_thread = async (msg, respond) => {
message: 'thread ID required' message: 'thread ID required'
}); });
} }
if (!(await check_permission(msg.auth_user.id, msg.thread)).view) { let perms = await check_permission(msg.auth_user.id, msg.thread);
if (!perms.view) {
return respond({ return respond({
success: false, success: false,
message: "you can't view this thread" message: "you can't view this thread"
@ -23,39 +24,50 @@ const get_thread = async (msg, respond) => {
[msg.thread] [msg.thread]
); );
const permissions = await db.query( const permissions = await db.query(
`select permission.type, permission.user, permission.permission, permission.value, permission.mutable `select type, user, permission, value, mutable
from thread from permission where thread = ?`,
join permission on thread.id = permission.thread
where thread.id = ?`,
[msg.thread] [msg.thread]
); );
let threadperms = {}; let members = Object.fromEntries(thread.rows.map(member =>
let members = Object.fromEntries(thread.rows.map(member => [member.id, member.user])); [member.id, { name: member.user }]
));
for (let permission of permissions.rows) { for (let permission of permissions.rows) {
const member = members[permission.user]; const member = members[permission.user];
if (member) { if (member)
if (!member.permissions) (member.permissions || (member.permissions = {}))
member.permissions = {}; [permission.permission] = {
member.permissions[permission.permission] = {
value: permission.value, value: permission.value,
mutable: permission.mutable mutable: permission.mutable
}; };
}
else else
(threadperms[permission.type] || (threadperms[permission.type] = {})) (perms[permission.type] || (perms[permission.type] = {}))
[permission.permission] = { [permission.permission] = {
value: permission.value, value: permission.value,
mutable: permission.mutable mutable: permission.mutable
}; };
} }
function makeBool(type, permission) {
if (perms[type]) {
if (perms[type][permission])
perms[type][permission].value = perms[type][permission].value === 'true' ? true : false;
else
perms[type][permission] = { value: false, mutable: true };
} else
(perms[type] = {})[permission] = { value: false, mutable: true };
}
makeBool('everyone', 'view');
makeBool('everyone', 'post');
makeBool('members', 'view');
makeBool('members', 'post');
return respond({ return respond({
success: true, success: true,
thread: { thread: {
id: msg.thread,
name: thread.rows[0].name, name: thread.rows[0].name,
permissions: threadperms, permissions: perms,
members: Object.entries(members).map(member => ({ members: Object.entries(members).map(member => ({
id: member[0], id: member[0],
name: member[1] ...member[1]
})) }))
} }
}); });