spaces working real
							parent
							
								
									26530690b9
								
							
						
					
					
						commit
						59d0b9eab2
					
				
							
								
								
									
										169
									
								
								client/app.js
								
								
								
								
							
							
						
						
									
										169
									
								
								client/app.js
								
								
								
								
							|  | @ -1,28 +1,34 @@ | |||
| import { render, html } from '/uhtml.js'; | ||||
| import loadSpace from '/space.js'; | ||||
| 
 | ||||
| window.currentThreadId = 1; | ||||
| 
 | ||||
| function chooseThread(thread) { | ||||
| 	if (window.currentThreadId) | ||||
| 		document | ||||
| 			.getElementById(`thread${window.currentThreadId}`) | ||||
| 			.classList.remove("active"); | ||||
| 	document.getElementById(`thread${thread.id}`).classList.add("active"); | ||||
| 	if (window.currentThreadId) { | ||||
| 		if (window.currentThreadId === thread.id) | ||||
| 			return; | ||||
| 		document.getElementById(`thread${window.currentThreadId}`) | ||||
| 			.classList.remove('active'); | ||||
| 	} | ||||
| 	const el = document.getElementById(`thread${thread.id}`); | ||||
| 	el.classList.add('active'); | ||||
| 	window.currentThreadId = thread.id; | ||||
| 	window.earliestMessage = null; | ||||
| 	document.getElementById("messages").innerHTML = ""; | ||||
| 	document.getElementById("threadname").textContent = thread.name; | ||||
| 	if (!thread.permissions.post) { | ||||
| 		document.getElementById("msginput").classList.add("hidden"); | ||||
| 	} else { | ||||
| 		document.getElementById("msginput").classList.remove("hidden"); | ||||
| 	} | ||||
| 	document.getElementById('threadname').textContent = thread.name; | ||||
| 	if (thread.permissions.post) | ||||
| 		document.getElementById('msginput').classList.remove('hidden'); | ||||
| 	else | ||||
| 		document.getElementById('msginput').classList.add('hidden'); | ||||
| 	switchTab(document.getElementById(el.tab)); | ||||
| 	loadMessages(); | ||||
| 	if (el.tab === 'spacetab') | ||||
| 		loadSpace(); | ||||
| } | ||||
| 
 | ||||
| function loadMessages() { | ||||
| 	document.getElementById('messages').innerHTML = ''; | ||||
| 	window.emit( | ||||
| 		"get_history", | ||||
| 		'get_history', | ||||
| 		{ | ||||
| 			before: window.earliestMessage, | ||||
| 			thread: window.currentThreadId, | ||||
|  | @ -31,16 +37,16 @@ function loadMessages() { | |||
| 			if (msg.messages.length > 0) { | ||||
| 				window.earliestMessage = msg.messages[msg.messages.length - 1].id; | ||||
| 				for (let message of msg.messages) | ||||
| 					document.getElementById("messages").prepend(html.node` | ||||
| 					document.getElementById('messages').prepend(html.node` | ||||
| 					<div class='message'> | ||||
| 						<strong>${message.name}: </strong> | ||||
| 						${message.message} | ||||
| 					</div>`); | ||||
| 			} | ||||
| 			if (!msg.more) | ||||
| 				document.getElementById("loadmore").classList.add("hidden"); | ||||
| 				document.getElementById('loadmore').classList.add('hidden'); | ||||
| 			else | ||||
| 				document.getElementById("loadmore").classList.remove("hidden"); | ||||
| 				document.getElementById('loadmore').classList.remove('hidden'); | ||||
| 		} | ||||
| 	); | ||||
| } | ||||
|  | @ -51,21 +57,35 @@ function addThread(thread, top) { | |||
| 		thread.name | ||||
| 	}</div>`; | ||||
| 	node.id = `thread${thread.id}`; | ||||
| 	document.getElementById("threadlist")[top ? "prepend" : "appendChild"](node); | ||||
| 	node.tab = 'messagetab'; | ||||
| 	document.getElementById('threadlist')[top ? 'prepend' : 'appendChild'](node); | ||||
| } | ||||
| 
 | ||||
| function switchTab(tab) { | ||||
| 	for (let tab of document.querySelectorAll('.tab')) | ||||
| 		tab.classList.remove('active'); | ||||
| 	for (let tab of document.querySelectorAll('.tabcontent')) | ||||
| 		tab.classList.add('hidden'); | ||||
| 	tab.classList.add('active'); | ||||
| 	document | ||||
| 		.getElementById(tab.id.slice(0, -3)) | ||||
| 		.classList.remove('hidden'); | ||||
| 	if (tab.id === 'spacetab') | ||||
| 		loadSpace(); | ||||
| } | ||||
| 
 | ||||
| function addMember() { | ||||
| 	const name = document.getElementById("membername").value; | ||||
| 	const name = document.getElementById('membername').value; | ||||
| 	window.threadmembers.push(name); | ||||
| 	document | ||||
| 		.getElementById("memberlist") | ||||
| 		.getElementById('memberlist') | ||||
| 		.appendChild(html.node`<p class='member'>${name}</p>`); | ||||
| 	document.getElementById("membername").value = ""; | ||||
| 	document.getElementById('membername').value = ''; | ||||
| } | ||||
| 
 | ||||
| async function createThread(e) { | ||||
| 	e.preventDefault(); | ||||
| 	let name = document.getElementById("newthreadname"); | ||||
| 	let name = document.getElementById('newthreadname'); | ||||
| 	if (!name.value) { | ||||
| 		name.insertAdjacentHTML('afterend', `<p>name cannot be empty</p>`); | ||||
| 		return; | ||||
|  | @ -74,24 +94,24 @@ async function createThread(e) { | |||
| 	const perms = document.querySelector( | ||||
| 		'input[name="permissions"]:checked' | ||||
| 	).value; | ||||
| 	if (perms === "private_view") | ||||
| 	if (perms === 'private_view') | ||||
| 		members = ( | ||||
| 			await new Promise(resolve => | ||||
| 				window.emit("get_keys", { names: window.threadmembers }, resolve) | ||||
| 				window.emit('get_keys', { names: window.threadmembers }, resolve) | ||||
| 			) | ||||
| 		).keys; | ||||
| 	let permissions; | ||||
| 	if (perms === "public") { | ||||
| 	if (perms === 'public') { | ||||
| 		permissions = { | ||||
| 			view_limited: false, | ||||
| 			post_limited: false | ||||
| 		}; | ||||
| 	} else if (perms === "private_post") { | ||||
| 	} else if (perms === 'private_post') { | ||||
| 		permissions = { | ||||
| 			view_limited: false, | ||||
| 			post_limited: true | ||||
| 		}; | ||||
| 	} else if (perms === "private_view") { | ||||
| 	} else if (perms === 'private_view') { | ||||
| 		permissions = { | ||||
| 			view_limited: true, | ||||
| 			post_limited: true | ||||
|  | @ -112,11 +132,11 @@ async function createThread(e) { | |||
| 		*/ | ||||
| 	} | ||||
| 	window.emit( | ||||
| 		"create_thread", | ||||
| 		'create_thread', | ||||
| 		{ | ||||
| 			name: name.value, | ||||
| 			permissions, | ||||
| 			members, | ||||
| 			members | ||||
| 		}, | ||||
| 		msg => { | ||||
| 			chooseThread({ | ||||
|  | @ -130,113 +150,106 @@ async function createThread(e) { | |||
| 			}); | ||||
| 			// since the form exists, this will perform cleanup
 | ||||
| 			newThread(); | ||||
| 			document.getElementById("loadmore").classList.add("hidden"); | ||||
| 			document.getElementById("msginput").classList.remove("hidden"); | ||||
| 			document.getElementById('loadmore').classList.add('hidden'); | ||||
| 			document.getElementById('msginput').classList.remove('hidden'); | ||||
| 		} | ||||
| 	); | ||||
| } | ||||
| 
 | ||||
| function sendMessage(e) { | ||||
| 	e.preventDefault(); | ||||
| 	const msg = document.getElementById("msg").value; | ||||
| 	const msg = document.getElementById('msg').value; | ||||
| 	if (!msg) | ||||
| 		return; | ||||
| 	window.emit("send_message", { | ||||
| 	window.emit('send_message', { | ||||
| 		message: msg, | ||||
| 		thread: window.currentThreadId | ||||
| 	}); | ||||
| 	document.getElementById("msg").value = ""; | ||||
| 	document.getElementById('msg').value = ''; | ||||
| } | ||||
| 
 | ||||
| function newThread() { | ||||
| 	let form = document.getElementById('createthread'); | ||||
| 	if (form) { | ||||
| 		form.remove(); | ||||
| 		document.getElementById("newthread").textContent = 'create'; | ||||
| 		document.getElementById('newthread').textContent = 'create'; | ||||
| 	} else { | ||||
| 		window.threadmembers = [window.name]; | ||||
| 		document.getElementById('threads').insertAdjacentElement('afterend', html.node` | ||||
| 			<form id="createthread" class='column' onsubmit=${createThread}> | ||||
| 			<form id='createthread' class='column' onsubmit=${createThread}> | ||||
| 				<h3>create thread</h3> | ||||
| 				<label for="newthreadname" class="heading">thread name</label> | ||||
| 				<input type="text" id="newthreadname" /> | ||||
| 				<label for='newthreadname' class='heading'>thread name</label> | ||||
| 				<input type='text' id='newthreadname' /> | ||||
| 				<p id='permissions'>thread permissions</p> | ||||
| 				<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" | ||||
| 				<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">anyone can view, only members can post</label><br/> | ||||
| 				<input type="radio" id="private_view" | ||||
| 					name="permissions" value="private_view" | ||||
| 				<label for='private_post'>anyone can view, only members can post</label><br/> | ||||
| 				<input type='radio' id='private_view' | ||||
| 					name='permissions' value='private_view' | ||||
| 				/> | ||||
| 				<label for="private_view">only members can view and post</label | ||||
| 				<label for='private_view'>only members can view and post</label | ||||
| 				><br /><br /> | ||||
| 				<label class="heading" for="membername">members</label> | ||||
| 				<input type="text" id="membername" placeholder="username" onkeydown=${(e) => { | ||||
| 					if (e.key == "Enter") { | ||||
| 				<label class='heading' for='membername'>members</label> | ||||
| 				<input type='text' id='membername' placeholder='username' onkeydown=${(e) => { | ||||
| 					if (e.key == 'Enter') { | ||||
| 						e.preventDefault(); | ||||
| 						addMember(); | ||||
| 					} | ||||
| 				}}/> | ||||
| 				<button id="addmember" onclick=${addMember}>add</button> | ||||
| 				<div id="memberlist"> | ||||
| 				<button id='addmember' onclick=${addMember}>add</button> | ||||
| 				<div id='memberlist'> | ||||
| 					<p class='member'>${window.name}</p> | ||||
| 				</div> | ||||
| 				<br /> | ||||
| 				<button id="submitthread" type="submit">create</button> | ||||
| 				<button id='submitthread' type='submit'>create</button> | ||||
| 			</form> | ||||
| 		` | ||||
| 		); | ||||
| 		document.getElementById("newthread").textContent = 'cancel'; | ||||
| 		document.getElementById('newthread').textContent = 'cancel'; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 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'); | ||||
| function clickedTab(event) { | ||||
| 	switchTab(event.target); | ||||
| 	document.getElementById(`thread${window.currentThreadId}`).tab = event.target.id; | ||||
| } | ||||
| 
 | ||||
| render(document.body, html` | ||||
| 	<div id="threads" class="column"> | ||||
| 	<div id='threads' class='column'> | ||||
| 		<h3>vybe</h3> | ||||
| 		<h4>threads</h4> | ||||
| 		<div id="threadlist">loading...</div> | ||||
| 		<div id='threadlist'>loading...</div> | ||||
| 		<button id='newthread' onclick=${newThread}>create</button> | ||||
| 	</div> | ||||
| 	<div id="thread" class="column"> | ||||
| 	<hr class='separator' color='#666'> | ||||
| 	<div id='thread' class='column'> | ||||
| 		<div id='title'> | ||||
| 			thread: <strong id="threadname">meow</strong> | ||||
| 			thread: <strong id='threadname'>meow</strong> | ||||
| 		</div> | ||||
| 		<div id='tabs'> | ||||
| 			<button id='messagetab' class='tab active' onclick=${switchTab}> | ||||
| 			messages | ||||
| 			</button><button id='spacetab' class='tab' onclick=${switchTab}>space</button> | ||||
| 			<button id='messagetab' class='tab active' onclick=${clickedTab}>messages</button><button id='spacetab' class='tab' onclick=${clickedTab}>space</button> | ||||
| 		</div> | ||||
| 		<div id='message' class='tabcontent'> | ||||
| 			<button id="loadmore" class="hidden" onclick=${loadMessages}> | ||||
| 			<button id='loadmore' class='hidden' onclick=${loadMessages}> | ||||
| 			load more messages | ||||
| 			</button> | ||||
| 			<div id='messages'></div> | ||||
| 			<form id="msginput" onsubmit=${sendMessage}> | ||||
| 				<input type="text" placeholder="write a message..." id="msg" /> | ||||
| 				<button type="submit" id="sendmsg">send</button> | ||||
| 			<form id='msginput' onsubmit=${sendMessage}> | ||||
| 				<input type='text' placeholder='write a message...' id='msg' /> | ||||
| 				<button type='submit' id='sendmsg'>send</button> | ||||
| 			</form> | ||||
| 		</div> | ||||
| 		<div id='space' class='tabcontent hidden'></div> | ||||
| 	</div> | ||||
| `);
 | ||||
| 
 | ||||
| window.socket.on("new_message", msg => { | ||||
| window.socket.on('new_message', msg => { | ||||
| 	if (msg.thread !== window.currentThreadId) | ||||
| 		return; | ||||
| 	document.getElementById("messages").appendChild(html.node` | ||||
| 	document.getElementById('messages').appendChild(html.node` | ||||
| 		<div class='message'> | ||||
| 			<strong>${msg.name}: </strong> | ||||
| 			${msg.message} | ||||
|  | @ -244,13 +257,11 @@ window.socket.on("new_message", msg => { | |||
| 	if (!window.earliestMessage) | ||||
| 		window.earliestMessage = msg.id; | ||||
| }); | ||||
| window.socket.on("new_thread", thread => addThread(thread, true)); | ||||
| window.socket.on('new_thread', thread => addThread(thread, true)); | ||||
| 
 | ||||
| window.emit("list_threads", {}, msg => { | ||||
| 	document.getElementById("threadlist").innerHTML = ""; | ||||
| window.emit('list_threads', {}, msg => { | ||||
| 	document.getElementById('threadlist').innerHTML = ''; | ||||
| 	for (let thread of msg.threads) | ||||
| 		addThread(thread); | ||||
| 	chooseThread(msg.threads[0]); | ||||
| }); | ||||
| 
 | ||||
| import('/space.js'); | ||||
|  |  | |||
|  | @ -91,11 +91,14 @@ | |||
| 				overflow: hidden; | ||||
| 				margin: 5px; | ||||
| 			} | ||||
| 			.separator { | ||||
| 				margin: 8px 2px; | ||||
| 			} | ||||
| 			#threads { | ||||
| 				max-width: 250px; | ||||
| 			} | ||||
| 			.thread { | ||||
| 				padding: 2px; | ||||
| 				padding: 2px 4px; | ||||
| 			} | ||||
| 			#newthread { | ||||
| 				margin-top: 5px; | ||||
|  | @ -139,6 +142,7 @@ | |||
| 			#msginput { | ||||
| 				display: flex; | ||||
| 				flex-direction: row; | ||||
| 				margin: 2px; | ||||
| 			} | ||||
| 			#msg { | ||||
| 				flex-grow: 1; | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| let space = document.getElementById('space'); | ||||
| let space; | ||||
| 
 | ||||
| let scale = 1; | ||||
| 
 | ||||
|  | @ -13,23 +13,32 @@ function mousemove(event) { | |||
| 	dragging.style.left = `${left < 0 ? 0 : left}px`; | ||||
| 	dragging.style.top = `${top < 0 ? 0 : top}px`; | ||||
| 	moved = true; | ||||
| 	//save
 | ||||
| 	save(dragging); | ||||
| } | ||||
| 
 | ||||
| function remove(span) { | ||||
| 	//remove
 | ||||
| 	span.remove(); | ||||
| function save(span) { | ||||
| 	window.emit('save_span', { | ||||
| 		thread: window.space, | ||||
| 		id: span.id ? span.id.slice(4) : '', | ||||
| 		content: span.innerText, | ||||
| 		x: span.style.left.slice(0, -2), | ||||
| 		y: span.style.top.slice(0, -2), | ||||
| 		scale: span.scale | ||||
| 	}, msg => { | ||||
| 		if (!span.id) | ||||
| 			span.id = 'span' + msg.id; | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| function add(x, y) { | ||||
| function add(x, y, scale = 1) { | ||||
| 	let span = document.createElement('span'); | ||||
| 	span.classList.add('span'); | ||||
| 	span.contentEditable = true; | ||||
| 	span.spellcheck = false; | ||||
| 	span.scale = 1; | ||||
| 	span.scale = scale; | ||||
| 	span.style.left = `${x}px`; | ||||
| 	span.style.top = `${y}px`; | ||||
| 	span.style.transform = 'translate(-50%, -50%)'; | ||||
| 	span.style.transform = `translate(-50%, -50%) scale(${scale})`; | ||||
| 	span.onkeydown = function(event) { | ||||
| 		if (event.key === 'Enter' && !event.getModifierState('Shift')) { | ||||
| 			event.preventDefault(); | ||||
|  | @ -38,12 +47,13 @@ function add(x, y) { | |||
| 		} | ||||
| 	}; | ||||
| 	span.oninput = function(event) { | ||||
| 		this.time = Date.now(); | ||||
| 		//save
 | ||||
| 		save(this); | ||||
| 	}; | ||||
| 	span.onblur = function(event) { | ||||
| 		if (!this.innerText) | ||||
| 			remove(this); | ||||
| 		if (this.innerText) | ||||
| 			return; | ||||
| 		save(this); | ||||
| 		this.remove(); | ||||
| 	}; | ||||
| 	span.onwheel = function(event) { | ||||
| 		event.preventDefault(); | ||||
|  | @ -76,18 +86,32 @@ function add(x, y) { | |||
| 	return span; | ||||
| } | ||||
| 
 | ||||
| space.onmouseup = event => { | ||||
| 	if (dragging) { | ||||
| 		dragging.onmouseup(event); | ||||
| 		return; | ||||
| export default function loadSpace() { | ||||
| 	if (!space) { | ||||
| 		space = document.getElementById('space'); | ||||
| 		space.onmouseup = event => { | ||||
| 			if (dragging) { | ||||
| 				dragging.onmouseup(event); | ||||
| 				return; | ||||
| 			} | ||||
| 			if (editing) { | ||||
| 				if (event.target !== editing) | ||||
| 					editing = null; | ||||
| 				return; | ||||
| 			} | ||||
| 			editing = add(event.offsetX + space.scrollLeft, event.offsetY + space.scrollTop); | ||||
| 			editing.focus(); | ||||
| 		}; | ||||
| 	} | ||||
| 	if (editing) { | ||||
| 		if (!editing.innerText) | ||||
| 			remove(editing); | ||||
| 		if (event.target !== editing) | ||||
| 			editing = null; | ||||
| 	if (window.space === window.currentThreadId) | ||||
| 		return; | ||||
| 	} | ||||
| 	editing = add(event.offsetX + space.scrollLeft, event.offsetY + space.scrollTop); | ||||
| 	editing.focus(); | ||||
| 	window.space = window.currentThreadId; | ||||
| 	space.innerHTML = ''; | ||||
| 	window.emit('get_space', { thread: window.space }, msg => { | ||||
| 		for (const span of msg.spans) { | ||||
| 			let el = add(span.x, span.y, span.scale); | ||||
| 			el.innerText = span.content; | ||||
| 			el.id = 'span' + span.id; | ||||
| 		} | ||||
| 	}); | ||||
| }; | ||||
|  |  | |||
|  | @ -54,13 +54,11 @@ create table post ( | |||
| create table span ( | ||||
| 	id integer primary key asc, | ||||
| 	thread integer, | ||||
| 	creator integer, | ||||
| 	created timestamp default current_timestamp, | ||||
| 	deleted bool default false, | ||||
| 	content text, | ||||
| 	x integer, | ||||
| 	y integer, | ||||
| 	x decimal, | ||||
| 	y decimal, | ||||
| 	scale decimal, | ||||
| 	foreign key(creator) references user(id), | ||||
| 	foreign key(thread) references thread(id) | ||||
| ); | ||||
| 
 | ||||
|  | @ -70,4 +68,4 @@ insert into permission | |||
| 	(1, "everyone", "view", "true"); | ||||
| insert into permission  | ||||
| 	(thread, type, permission, value) values | ||||
| 	(1, "everyone", "post", "true"); | ||||
| 	(1, "everyone", "post", "true"); | ||||
|  |  | |||
							
								
								
									
										24
									
								
								index.js
								
								
								
								
							
							
						
						
									
										24
									
								
								index.js
								
								
								
								
							|  | @ -1,17 +1,19 @@ | |||
| const express = require("express"); | ||||
| const http = require("http"); | ||||
| const { Server } = require("socket.io"); | ||||
| const compression = require("compression"); | ||||
| const express = require('express'); | ||||
| const http = require('http'); | ||||
| const { Server } = require('socket.io'); | ||||
| const compression = require('compression'); | ||||
| 
 | ||||
| const events = Object.fromEntries( | ||||
| 	[ | ||||
| 		'create_user', | ||||
| 		'get_history', | ||||
| 		'send_message', | ||||
| 		'authenticate', | ||||
| 		'create_thread', | ||||
| 		'list_threads', | ||||
| 		'create_user', | ||||
| 		'get_history', | ||||
| 		'get_keys', | ||||
| 		'get_space', | ||||
| 		'list_threads', | ||||
| 		'save_span', | ||||
| 		'send_message' | ||||
| 	].map(event => [event, require('./src/' + event)]) | ||||
| ); | ||||
| 
 | ||||
|  | @ -28,7 +30,7 @@ const PORT = process.env.PORT || 3435; | |||
| 
 | ||||
| io.cache = {}; | ||||
| 
 | ||||
| io.on("connection", (socket) => { | ||||
| io.on('connection', (socket) => { | ||||
| 	for (let event in events) { | ||||
| 		socket.on(event, (msg, callback) => | ||||
| 			events[event](msg, callback, socket, io) | ||||
|  | @ -42,7 +44,7 @@ io.on("connection", (socket) => { | |||
| }); | ||||
| 
 | ||||
| server.listen(PORT, () => { | ||||
| 	console.log("server running on port " + PORT); | ||||
| 	console.log('server running on port ' + PORT); | ||||
| }); | ||||
| 
 | ||||
| app.use(express.static("client")); | ||||
| app.use(express.static('client')); | ||||
|  |  | |||
|  | @ -0,0 +1,28 @@ | |||
| const db = require('../db'); | ||||
| const authwrap = require('./authwrap'); | ||||
| const check_permission = require('./helpers/check_permission'); | ||||
| 
 | ||||
| const get_space = async (msg, respond) => { | ||||
| 	if (!msg.thread) { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: 'thread ID required', | ||||
| 		}); | ||||
| 	} | ||||
| 	if (!(await check_permission(msg.auth_user.id, msg.thread)).view) { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: "you can't view this thread", | ||||
| 		}); | ||||
| 	} | ||||
| 	const spans = await db.query( | ||||
| 		'select id, content, x, y, scale from span where thread=? and deleted=false', | ||||
| 		[msg.thread] | ||||
| 	); | ||||
| 	return respond({ | ||||
| 		success: true, | ||||
| 		spans: spans.rows | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| module.exports = authwrap(get_space); | ||||
|  | @ -30,7 +30,7 @@ const check_permission = async (user_id, thread_id) => { | |||
| 	return { | ||||
| 		is_member, | ||||
| 		view: get_permission("view"), | ||||
| 		post: get_permission("post"), | ||||
| 		post: get_permission("post") | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,67 @@ | |||
| const db = require('../db'); | ||||
| const authwrap = require('./authwrap'); | ||||
| const check_permission = require('./helpers/check_permission'); | ||||
| 
 | ||||
| const save_span = async (msg, respond, socket, io) => { | ||||
| 	if (!msg.thread) { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: 'thread ID required', | ||||
| 		}); | ||||
| 	} | ||||
| 	if (!(await check_permission(msg.auth_user.id, msg.thread)).post) { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
| 			message: "you can't post to this thread", | ||||
| 		}); | ||||
| 	} | ||||
| 	// save span and send it to everyone
 | ||||
| 	let id; | ||||
| 	if (msg.id) { | ||||
| 		id = msg.id; | ||||
| 		if (msg.content) | ||||
| 			await db.query('update span set content=?, x=?, y=?, scale=?, deleted=false where id=?', | ||||
| 				[msg.content, msg.x, msg.y, msg.scale, msg.id]); | ||||
| 		else | ||||
| 			await db.query('update span set deleted=true where id=?', [msg.id]); | ||||
| 	} | ||||
| 	else { | ||||
| 		id = (await db.query( | ||||
| 			'insert into span (thread, content, x, y, scale) values (?, ?, ?, ?, ?) returning id', | ||||
| 			[msg.thread, msg.content, msg.x, msg.y, msg.scale] | ||||
| 		)).rows[0].id; | ||||
| 	} | ||||
| 	// get thread members
 | ||||
| 	const members = ( | ||||
| 		await db.query( | ||||
| 			'select name from user join member on member.user = user.id where member.thread = ?', | ||||
| 			[msg.thread] | ||||
| 		) | ||||
| 	).rows.map(i => i.name); | ||||
| 	// get perms
 | ||||
| 	const permissions = await db.query( | ||||
| 		"select * from permission where thread = ? and type = 'everyone' and value = 'true' and permission = 'view'", | ||||
| 		[msg.thread] | ||||
| 	); | ||||
| 	for (let username in io.cache) { | ||||
| 		if (permissions.rows.length > 0 || members.includes(username)) { | ||||
| 			const sockets = io.cache[username]; | ||||
| 			for (let s of sockets) { | ||||
| 				io.to(s).emit('span', { | ||||
| 					id, | ||||
| 					thread: msg.thread, | ||||
| 					content: msg.content, | ||||
| 					x: msg.x, | ||||
| 					y: msg.y, | ||||
| 					scale: msg.scale | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return respond({ | ||||
| 		success: true, | ||||
| 		id | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| module.exports = authwrap(save_span); | ||||
|  | @ -33,17 +33,7 @@ const send_message = async (msg, respond, socket, io) => { | |||
| 		[msg.thread] | ||||
| 	); | ||||
| 	for (let username in io.cache) { | ||||
| 		if (members.includes(username)) { | ||||
| 			const sockets = io.cache[username]; | ||||
| 			for (let s of sockets) { | ||||
| 				io.to(s).emit("new_message", { | ||||
| 					id: id.rows[0].id, | ||||
| 					name: msg.auth_user.name, | ||||
| 					message: msg.message, | ||||
| 					thread: msg.thread, | ||||
| 				}); | ||||
| 			} | ||||
| 		} else if (permissions.rows.length > 0) { | ||||
| 		if (permissions.rows.length > 0 || members.includes(username)) { | ||||
| 			const sockets = io.cache[username]; | ||||
| 			for (let s of sockets) { | ||||
| 				io.to(s).emit("new_message", { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue