stuff n tabs
							parent
							
								
									8cd970e4f8
								
							
						
					
					
						commit
						0b830174d3
					
				|  | @ -58,10 +58,14 @@ function addMember() { | |||
| 
 | ||||
| async function createThread(e) { | ||||
| 	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( | ||||
| 		'input[name="permissions"]:checked' | ||||
| 	).value; | ||||
| 		'input[name="permissions"]:checked').value; | ||||
| 	if (perms === "private_view") | ||||
| 		members = (await new Promise(resolve => | ||||
| 			window.emit("get_keys", { names: window.threadmembers }, resolve) | ||||
|  | @ -98,12 +102,12 @@ async function createThread(e) { | |||
| 		*/ | ||||
| 	} | ||||
| 	window.emit("create_thread", { | ||||
| 		name: document.getElementById("newthreadname").value, | ||||
| 		name: name.value, | ||||
| 		permissions, | ||||
| 		members | ||||
| 	}, msg => { | ||||
| 		chooseThread({ | ||||
| 			name: document.getElementById("newthreadname").value, | ||||
| 			name: name.value, | ||||
| 			id: msg.id | ||||
| 		}); | ||||
| 		document.getElementById('createthread').remove(); | ||||
|  | @ -138,12 +142,12 @@ function newThread(e) { | |||
| 				<label for="newthreadname">thread name</label> | ||||
| 				<input type="text" id="newthreadname" /> | ||||
| 				<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 /> | ||||
| 				<input type="radio" id="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" | ||||
| 					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` | ||||
| 	<div id="app"> | ||||
| 		<div id="threads" class="column"> | ||||
| 			<h1>vybe</h1> | ||||
| 			<h3>threads</h3> | ||||
| 			<div id="threadlist">loading...</div> | ||||
| 			<button id='newthread' onclick=${newThread}>create</button> | ||||
| 	<div id="threads" class="column"> | ||||
| 		<h3>vybe</h3> | ||||
| 		<h4>threads</h4> | ||||
| 		<div id="threadlist">loading...</div> | ||||
| 		<button id='newthread' onclick=${newThread}>create</button> | ||||
| 	</div> | ||||
| 	<div id="thread" class="column"> | ||||
| 		<div id='title'> | ||||
| 			thread: <strong id="threadname">meow</strong> | ||||
| 		</div> | ||||
| 		<div id="thread" class="column"> | ||||
| 			<h3> | ||||
| 				thread: <strong id="threadname">meow</strong> | ||||
| 			</h3> | ||||
| 			<h3>messages</h3> | ||||
| 			<button id="loadmore" class="hidden" onclick=${loadMessages}>load more messages</button> | ||||
| 			<div id="messages"></div> | ||||
| 		<button id='messagetab' class='tab active' onclick=${switchTab}>messages</button> | ||||
| 		<button id='spacetab' class='tab' onclick=${switchTab}>space</button> | ||||
| 		<button id="loadmore" class="hidden" onclick=${loadMessages}>load more messages</button> | ||||
| 		<div id='message' class='tabcontent'> | ||||
| 			<div id='messages'> | ||||
| 			</div> | ||||
| 			<form id="msginput" onsubmit=${sendMessage}> | ||||
| 				<input type="text" placeholder="write a message..." id="msg" /> | ||||
| 				<button type="submit" class="hidden" id="sendmsg"></button> | ||||
| 			</form> | ||||
| 		</div> | ||||
| 	</div>`); | ||||
| 		<div id='space' class='tabcontent'></div> | ||||
| 	</div> | ||||
| `);
 | ||||
| 
 | ||||
| window.socket.on("new_message", (msg) => { | ||||
| 	if (msg.thread !== window.currentThreadId) | ||||
|  |  | |||
|  | @ -46,15 +46,16 @@ render(document.body, html` | |||
| 			}); | ||||
| 			const priv = await openpgp.readKey({ armoredKey: keys.privateKey }); | ||||
| 			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 => { | ||||
| 				if (!msg.success) { | ||||
| 					console.log('create user failed'); | ||||
| 					document.querySelector('#registerform').insertAdjacentHTML('afterend', ` | ||||
| 						<p>${msg.message}</p>`); | ||||
| 					return; | ||||
| 				} | ||||
| 				window.keys = { priv, pub }; | ||||
| 				localStorage.setItem("keys", JSON.stringify(keys)); | ||||
| 				localStorage.setItem("name", name); | ||||
| 				window.name = name; | ||||
| 				auth(); | ||||
| 			}); | ||||
| 		}} id="registerform"> | ||||
|  |  | |||
|  | @ -27,11 +27,8 @@ | |||
| 				margin: 0; | ||||
| 				min-width: min-content; | ||||
| 			} | ||||
| 			h1 { | ||||
| 				margin: 0; | ||||
| 			} | ||||
| 			h3 { | ||||
| 				margin: 11px 0; | ||||
| 			h3, h4 { | ||||
| 				margin: 10px 0; | ||||
| 			} | ||||
| 			button { | ||||
| 				border-color: #767676; | ||||
|  | @ -39,12 +36,6 @@ | |||
| 			.hidden { | ||||
| 				display: none; | ||||
| 			} | ||||
| 			#app { | ||||
| 				display: contents; | ||||
| 			} | ||||
| 			#app.hidden { | ||||
| 				display: none; | ||||
| 			} | ||||
| 			.column { | ||||
| 				flex: 1; | ||||
| 				margin: 5px; | ||||
|  | @ -53,18 +44,41 @@ | |||
| 			#threads { | ||||
| 				max-width: 250px; | ||||
| 			} | ||||
| 			.thread:hover { | ||||
| 				background-color: #3b3b3b; | ||||
| 			} | ||||
| 			.thread.selected { | ||||
| 				background-color: #4f4f4f; | ||||
| 			} | ||||
| 			.thread:hover { | ||||
| 				background-color: #3b3b3b; | ||||
| 			} | ||||
| 			#newthread { | ||||
| 				margin-top: 5px; | ||||
| 			} | ||||
| 			#createthread { | ||||
| 				max-width: 350px; | ||||
| 			} | ||||
| 			#permissions { | ||||
| 				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 { | ||||
| 				margin-top: 15px; | ||||
| 			} | ||||
|  |  | |||
							
								
								
									
										5
									
								
								index.js
								
								
								
								
							
							
						
						
									
										5
									
								
								index.js
								
								
								
								
							|  | @ -32,6 +32,11 @@ io.on("connection", (socket) => { | |||
| 			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, () => { | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ const authenticate = async (msg, respond, socket, io) => { | |||
|         result.rows[0].id, | ||||
|         data[1], | ||||
|       ]); | ||||
|       socket.userid = result.rows[0].id; | ||||
|       socket.username = msg.name; | ||||
|       if (io.cache[msg.name]) { | ||||
|         io.cache[msg.name].push(socket.id); | ||||
|       } else { | ||||
|  |  | |||
|  | @ -2,109 +2,108 @@ const db = require("../db"); | |||
| const authwrap = require("./authwrap"); | ||||
| 
 | ||||
| const create_thread = async (msg, respond, socket, io) => { | ||||
|   // validate inputs
 | ||||
|   if (!msg.name) { | ||||
|     return respond({ | ||||
|       success: false, | ||||
|       message: "thread name required", | ||||
|     }); | ||||
|   } | ||||
|   if (msg.name.length > 200) { | ||||
|     return respond({ | ||||
|       success: false, | ||||
|       message: "thread name 200 chars max", | ||||
|     }); | ||||
|   } | ||||
|   // add to db
 | ||||
|   const insert = await db.query( | ||||
|     "insert into threads (name, creator) values (?, ?) returning id", | ||||
|     [msg.name, msg.auth_user.id] | ||||
|   ); | ||||
|   const thread_id = insert.rows[0].id; | ||||
|   // set up permissions
 | ||||
|   if (!msg.permissions || !msg.permissions.view_limited) { | ||||
|     await db.query( | ||||
|       `insert into permissions (thread, type, mutable, permission, value) 
 | ||||
|       values (?, ?, ?, ?, ?)`,
 | ||||
|       [thread_id, "everyone", false, "view", "true"] | ||||
|     ); | ||||
|     if (!msg.permissions || !msg.permissions.post_limited) { | ||||
|       await db.query( | ||||
|         `insert into permissions (thread, type, mutable, permission, value) 
 | ||||
|         values (?, ?, ?, ?, ?)`,
 | ||||
|         [thread_id, "everyone", false, "post", "true"] | ||||
|       ); | ||||
|     } else { | ||||
|       await db.query( | ||||
|         `insert into permissions (thread, type, mutable, permission, value) 
 | ||||
|         values (?, ?, ?, ?, ?)`,
 | ||||
|         [thread_id, "members", false, "post", "true"] | ||||
|       ); | ||||
|     } | ||||
|   } else { | ||||
|     await db.query( | ||||
|       `insert into permissions (thread, type, mutable, permission, value) 
 | ||||
|       values (?, ?, ?, ?, ?)`,
 | ||||
|       [thread_id, "members", false, "view", "true"] | ||||
|     ); | ||||
|     await db.query( | ||||
|       `insert into permissions (thread, type, mutable, permission, value) 
 | ||||
|       values (?, ?, ?, ?, ?)`,
 | ||||
|       [thread_id, "members", false, "post", "true"] | ||||
|     ); | ||||
|   } | ||||
|   // add members
 | ||||
|   for (let user of msg.members) { | ||||
|     // get user id
 | ||||
|     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; | ||||
|       await db.query( | ||||
|         "insert into members (thread, user, key_delivery) values (?, ?, ?)", | ||||
|         [thread_id, user_id, user.key] | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|   const member_perms = { | ||||
|     is_member: true, | ||||
|     view: true, | ||||
|     post: true, | ||||
|   }; | ||||
|   const general_perms = { | ||||
|     is_member: false, | ||||
|     view: !msg.permissions || !msg.permissions.view_limited, | ||||
|     post: !msg.permissions || !msg.permissions.post_limited, | ||||
|   }; | ||||
|   for (let username in io.cache) { | ||||
|     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, | ||||
|           id: insert.rows[0].id, | ||||
|           permissions: member_perms, | ||||
|           key: member.key, | ||||
|         }); | ||||
|       } | ||||
|     } else if (general_perms.view) { | ||||
|       const sockets = io.cache[username]; | ||||
|       for (let s of sockets) { | ||||
|         io.to(s).emit("new_thread", { | ||||
|           name: msg.name, | ||||
|           id: insert.rows[0].id, | ||||
|           permissions: general_perms, | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   // respond
 | ||||
|   return respond({ | ||||
|     success: true, | ||||
|     id: insert.rows[0].id, | ||||
|   }); | ||||
| 	// validate inputs
 | ||||
| 	if (typeof msg.name !== 'string') { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: "thread name required", | ||||
| 		}); | ||||
| 	} | ||||
| 	if (msg.name.length > 200) { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: "thread name 200 chars max", | ||||
| 		}); | ||||
| 	} | ||||
| 	// add to db
 | ||||
| 	const insert = await db.query( | ||||
| 		"insert into threads (name, creator) values (?, ?) returning id", | ||||
| 		[msg.name, msg.auth_user.id] | ||||
| 	); | ||||
| 	const thread_id = insert.rows[0].id; | ||||
| 	// set up permissions
 | ||||
| 	if (!msg.permissions || !msg.permissions.view_limited) { | ||||
| 		await db.query( | ||||
| 			`insert into permissions (thread, type, mutable, permission, value) 
 | ||||
| 			values (?, ?, ?, ?, ?)`,
 | ||||
| 			[thread_id, "everyone", false, "view", "true"] | ||||
| 		); | ||||
| 		if (!msg.permissions || !msg.permissions.post_limited) { | ||||
| 			await db.query( | ||||
| 				`insert into permissions (thread, type, mutable, permission, value) 
 | ||||
| 				values (?, ?, ?, ?, ?)`,
 | ||||
| 				[thread_id, "everyone", false, "post", "true"] | ||||
| 			); | ||||
| 		} else { | ||||
| 			await db.query( | ||||
| 				`insert into permissions (thread, type, mutable, permission, value) 
 | ||||
| 				values (?, ?, ?, ?, ?)`,
 | ||||
| 				[thread_id, "members", false, "post", "true"] | ||||
| 			); | ||||
| 		} | ||||
| 	} else { | ||||
| 		await db.query( | ||||
| 			`insert into permissions (thread, type, mutable, permission, value) 
 | ||||
| 			values (?, ?, ?, ?, ?)`,
 | ||||
| 			[thread_id, "members", false, "view", "true"] | ||||
| 		); | ||||
| 		await db.query( | ||||
| 			`insert into permissions (thread, type, mutable, permission, value) 
 | ||||
| 			values (?, ?, ?, ?, ?)`,
 | ||||
| 			[thread_id, "members", false, "post", "true"] | ||||
| 		); | ||||
| 	} | ||||
| 	// add members
 | ||||
| 	for (let user of msg.members) { | ||||
| 		if (!user) continue; | ||||
| 		// get user id
 | ||||
| 		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; | ||||
| 			await db.query( | ||||
| 				"insert into members (thread, user, key_delivery) values (?, ?, ?)", | ||||
| 				[thread_id, user_id, user.key] | ||||
| 			); | ||||
| 		} | ||||
| 	} | ||||
| 	if (!msg.permissions || !msg.permissions.view_limited) { | ||||
| 		for (let username in io.cache) { | ||||
| 			for (let socket of io.cache[username]) { | ||||
| 				io.to(socket).emit("new_thread", { | ||||
| 					name: msg.name, | ||||
| 					id: insert.rows[0].id, | ||||
| 					permissions: { | ||||
| 						is_member: false, | ||||
| 						view: true, | ||||
| 						post: !msg.permissions || !msg.permissions.post_limited | ||||
| 					}, | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	else { | ||||
| 		for (let member of msg.members) { | ||||
| 			for (let socket of io.cache[member.user]) { | ||||
| 				io.to(socket).emit("new_thread", { | ||||
| 					name: msg.name, | ||||
| 					id: insert.rows[0].id, | ||||
| 					permissions: { | ||||
| 						is_member: true, | ||||
| 						view: true, | ||||
| 						post: true | ||||
| 					}, | ||||
| 					key: member.key | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	// respond
 | ||||
| 	return respond({ | ||||
| 		success: true, | ||||
| 		id: insert.rows[0].id, | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| module.exports = authwrap(create_thread); | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ const create_user = async (msg, respond) => { | |||
|   if (!msg.name) { | ||||
|     return respond({ | ||||
|       success: false, | ||||
|       message: "Username required", | ||||
|       message: "username required", | ||||
|     }); | ||||
|   } | ||||
|   // ensure username is not taken
 | ||||
|  | @ -17,14 +17,14 @@ const create_user = async (msg, respond) => { | |||
|     console.log(`username already exists: ${result}`); | ||||
|     return respond({ | ||||
|       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
 | ||||
|   if (!msg.pubkey) { | ||||
|     return respond({ | ||||
|       success: false, | ||||
|       message: "Public key required", | ||||
|       message: "public key required", | ||||
|     }); | ||||
|   } | ||||
|   try { | ||||
|  | @ -33,7 +33,7 @@ const create_user = async (msg, respond) => { | |||
|     console.err("error in create_user readkey: " + err); | ||||
|     return respond({ | ||||
|       success: false, | ||||
|       message: "Public key invalid", | ||||
|       message: "public key invalid", | ||||
|     }); | ||||
|   } | ||||
|   // add to db
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue