bio and fixes
							parent
							
								
									8f51958dca
								
							
						
					
					
						commit
						4ccaffcf1e
					
				|  | @ -1,19 +1,22 @@ | |||
| import { render, html } from '/uhtml.js'; | ||||
| import loadThreads from '/thread.js'; | ||||
| 
 | ||||
| let member, membername, memberusername, memberbio, newname, newbio; | ||||
| 
 | ||||
| function saveProfile(e) { | ||||
| 	let displayname = document.getElementById('newname').value; | ||||
| 	window.emit('update_user', { | ||||
| 		displayname, | ||||
| 		displayname: newname.value, | ||||
| 		bio: newbio.value, | ||||
| 		public: document.getElementById('profilepublic').checked | ||||
| 	}, msg => { | ||||
| 		if (!msg.success) { | ||||
| 			console.log('update_user error: ', msg.message); | ||||
| 			document.getElementById('savename').classList.remove('hidden'); | ||||
| 			document.getElementById('saveprofile').classList.remove('hidden'); | ||||
| 			return; | ||||
| 		} | ||||
| 		window.displayname = displayname; | ||||
| 		document.getElementById('savename').classList.add('hidden'); | ||||
| 		window.displayname = newname.value; | ||||
| 		window.bio = newbio.value; | ||||
| 		document.getElementById('saveprofile').classList.add('hidden'); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
|  | @ -75,19 +78,26 @@ window.makeUser = (user, url, context) => { | |||
| 		(context === 'member' ? true : Array.from(document.getElementById('memberlist').children) | ||||
| 			.find(p => p.children[0].user?.id == user.id)); | ||||
| 	let span = html.node` | ||||
| 		<span class='user' onclick=${function(e) { | ||||
| 			let member = document.getElementById('member'); | ||||
| 		<span class='user' onclick=${async function(event) { | ||||
| 			if (member.user === user) | ||||
| 				member.classList.toggle('hidden'); | ||||
| 			else | ||||
| 				member.classList.remove('hidden'); | ||||
| 			document.body.scrollLeft = document.body.scrollWidth - document.body.clientWidth; | ||||
| 			member.user = user; | ||||
| 			document.getElementById('membername').textContent = user.displayname; | ||||
| 			document.getElementById('memberusername').textContent = `${user.name}@${url}`; | ||||
| 			membername.textContent = user.displayname; | ||||
| 			memberusername.textContent = `${user.name}@${url}`; | ||||
| 			memberbio.textContent = user.bio || ''; | ||||
| 			if (removable) | ||||
| 				document.getElementById('removemember').classList.remove('hidden'); | ||||
| 			else | ||||
| 				document.getElementById('removemember').classList.add('hidden'); | ||||
| 			let id = user.id; | ||||
| 			Object.assign(user, await getUser(user.id, user.name)); | ||||
| 			user.id = id; // keep @
 | ||||
| 			membername.textContent = user.displayname; | ||||
| 			memberusername.textContent = `${user.name}@${url}`; | ||||
| 			memberbio.textContent = user.bio; | ||||
| 		}}>${user.displayname}</span>`; | ||||
| 	span.user = user; | ||||
| 	return span; | ||||
|  | @ -180,31 +190,37 @@ async function addInstance() { | |||
| 	this.onclick = instanceClicked; | ||||
| } | ||||
| 
 | ||||
| function profileEdit(event) { | ||||
| 	if (window.displayname === newname.value && window.bio === newbio.value) | ||||
| 		document.getElementById('saveprofile').classList.add('hidden'); | ||||
| 	else if (event.key === 'Enter' && this === newname) | ||||
| 		saveProfile(); | ||||
| 	else | ||||
| 		document.getElementById('saveprofile').classList.remove('hidden'); | ||||
| } | ||||
| 
 | ||||
| // main app html
 | ||||
| document.body.append(html.node` | ||||
| 	<div id='profile' class='column hidden'> | ||||
| 		<p><strong>profile</strong></p> | ||||
| 		<p class='header'> | ||||
| 			<strong>profile</strong> | ||||
| 			<button class='hidden' id='saveprofile' onclick=${saveProfile}>save</button> | ||||
| 		</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') | ||||
| 				saveProfile(); | ||||
| 			else | ||||
| 				document.getElementById('savename').classList.remove('hidden'); | ||||
| 		}}> | ||||
| 		<button class='hidden' id='savename' onclick=${saveProfile}>save</button> | ||||
| 		<input id='newname' onkeyup=${profileEdit}> | ||||
| 		<p> | ||||
| 			<input id='profilepublic' type='checkbox' oninput=${saveProfile}> | ||||
| 			<label for='profilepublic'>show profile in instance users</label> | ||||
| 			<label for='profilepublic'>show profile in instance user list</label> | ||||
| 		</p> | ||||
| 		<label class='heading'>bio</label> | ||||
| 		<textarea id='bio' onkeyup=${profileEdit}></textarea> | ||||
| 		<label class='heading'>authentication requests:</label> | ||||
| 		<div id='authrequests'></div> | ||||
| 	</div> | ||||
| 	<hr class='separator' color='#505050'> | ||||
| 	<div id='home' class='column'> | ||||
| 		<h3>vybe</h3> | ||||
| 		<p id='instances'>instances:<button onclick=${e => { | ||||
| 		<p id='instances' class='header'>instances:<button onclick=${e => { | ||||
| 			let div = html.node` | ||||
| 				<div> | ||||
| 					<div class='instancetitle'> | ||||
|  | @ -249,17 +265,18 @@ document.body.append(html.node` | |||
| 	<hr class='separator' color='#505050'> | ||||
| 	<div id='member' class='column hidden'> | ||||
| 		<div class='content'> | ||||
| 			<p> | ||||
| 			<p class='header'> | ||||
| 				user: | ||||
| 				<button onclick=${e => | ||||
| 					document.getElementById('member').classList.add('hidden') | ||||
| 					member.classList.add('hidden') | ||||
| 				}>close</button> | ||||
| 			</p> | ||||
| 			<p>user: <strong id='membername'></strong></p> | ||||
| 			<p id='membername'></p> | ||||
| 			<p id='memberusername'></p> | ||||
| 			<p id='memberbio'></p> | ||||
| 		</div> | ||||
| 		<p> | ||||
| 			<button id='removemember' onclick=${e => { | ||||
| 				let member = document.getElementById('member'); | ||||
| 				window.currentInstance.emit('remove_member', { | ||||
| 					thread: window.currentThread.id, | ||||
| 					id: member.user.id | ||||
|  | @ -277,6 +294,15 @@ document.body.append(html.node` | |||
| 	</div> | ||||
| `);
 | ||||
| 
 | ||||
| member = document.getElementById('member'); | ||||
| membername = document.getElementById('membername'); | ||||
| memberusername = document.getElementById('memberusername'); | ||||
| memberbio = document.getElementById('memberbio'); | ||||
| newname = document.getElementById('newname'); | ||||
| newbio = document.getElementById('bio'); | ||||
| newname.value = window.displayname; | ||||
| newbio.value = window.bio; | ||||
| 
 | ||||
| for (let i = 0; i < instancelist.length; ++i) { | ||||
| 	let instance = instancelist[i]; | ||||
| 	let div = html.node` | ||||
|  | @ -299,8 +325,6 @@ for (let i = 0; i < instancelist.length; ++i) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| document.getElementById('newname').value = window.displayname; | ||||
| 
 | ||||
| function authRequest(authrequest) { | ||||
| 	const p = html.node` | ||||
| 		<p> | ||||
|  |  | |||
|  | @ -43,6 +43,7 @@ async function auth() { | |||
| 		localStorage.setItem('name', window.name = msg.name); | ||||
| 		localStorage.setItem('id', window.id = msg.id); | ||||
| 		window.displayname = msg.displayname; | ||||
| 		window.bio = msg.bio; | ||||
| 		if (window.instancelist) { | ||||
| 			if (window.instancelist[0].url !== location.host | ||||
| 				|| window.instancelist[0].id !== msg.instance.id) { | ||||
|  |  | |||
|  | @ -27,18 +27,20 @@ | |||
| 			} | ||||
| 			html { | ||||
| 				height: 100%; | ||||
| 				overflow: hidden; | ||||
| 			} | ||||
| 			body { | ||||
| 				height: 100%; | ||||
| 				overflow: auto; | ||||
| 				background: #020202; | ||||
| 				display: flex; | ||||
| 				align-items: stretch; | ||||
| 				margin: 0; | ||||
| 				min-width: min-content; | ||||
| 			} | ||||
| 			body, | ||||
| 			button, | ||||
| 			input { | ||||
| 			input, | ||||
| 			textarea { | ||||
| 				color: #eaeaea; | ||||
| 			} | ||||
| 			button { | ||||
|  | @ -47,13 +49,19 @@ | |||
| 				width: fit-content; | ||||
| 			} | ||||
| 			button, | ||||
| 			input:not([type]), input[type='text'], | ||||
| 			.tab { | ||||
| 				padding: 4px 7px; | ||||
| 			} | ||||
| 			input:not([type]), input[type='text'], | ||||
| 			textarea { | ||||
| 				padding: 4px 4px; | ||||
| 			} | ||||
| 			input, | ||||
| 			textarea { | ||||
| 				outline: none; | ||||
| 			} | ||||
| 			input { | ||||
| 				background: #1b1b1b; | ||||
| 				outline: none; | ||||
| 				&::placeholder { | ||||
| 					color: #aaa; | ||||
| 				} | ||||
|  | @ -75,9 +83,6 @@ | |||
| 					background-color: #4f4f4f; | ||||
| 					border-radius: 1rem; | ||||
| 					height: 0.5rem; | ||||
| 					&:focus { | ||||
| 						outline: none; | ||||
| 					} | ||||
| 					&::-webkit-slider-thumb { | ||||
| 						-webkit-appearance: none; | ||||
| 						appearance: none; | ||||
|  | @ -103,6 +108,17 @@ | |||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			textarea { | ||||
| 				background: #111; | ||||
| 				border: 1px solid #444; | ||||
| 				width: 240px; | ||||
| 				height: 200px; | ||||
| 				resize: none; | ||||
| 				&:focus { | ||||
| 					padding-bottom: 2px; | ||||
| 					border-bottom: 3px solid #777; | ||||
| 				} | ||||
| 			} | ||||
| 			#register { | ||||
| 				margin-inline: 14px; | ||||
| 				max-width: 800px; | ||||
|  | @ -137,7 +153,8 @@ | |||
| 				display: none !important; | ||||
| 			} | ||||
| 			.column { | ||||
| 				flex: 1; | ||||
| 				width: 260px; | ||||
| 				min-width: 260px; | ||||
| 				margin: 2px; | ||||
| 			} | ||||
| 			.separator { | ||||
|  | @ -179,9 +196,14 @@ | |||
| 					left: -3px; | ||||
| 				} | ||||
| 			} | ||||
| 			.header { | ||||
| 				display: flex; | ||||
| 				justify-content: space-between; | ||||
| 				align-items: center; | ||||
| 				min-height: 25px; | ||||
| 			} | ||||
| 			#home { | ||||
| 				margin: 0; | ||||
| 				max-width: 256px; | ||||
| 				display: flex; | ||||
| 				flex-direction: column; | ||||
| 				justify-content: space-between; | ||||
|  | @ -195,18 +217,13 @@ | |||
| 					margin: 3px; | ||||
| 				} | ||||
| 			} | ||||
| 			#instances, #membershead, #threadshead { | ||||
| 				display: flex; | ||||
| 				justify-content: space-between; | ||||
| 				align-items: center; | ||||
| 			} | ||||
| 			#threads { | ||||
| 				margin: 0 2px 6px; | ||||
| 				min-height: 0; | ||||
| 				display: flex; | ||||
| 				flex-direction: column; | ||||
| 			} | ||||
| 			#threadshead { | ||||
| 			#threadshead, #membershead { | ||||
| 				margin-block: 2px 4px; | ||||
| 			} | ||||
| 			#threadlist { | ||||
|  | @ -226,14 +243,10 @@ | |||
| 					border-bottom: 1px #bbb solid; | ||||
| 				} | ||||
| 			} | ||||
| 			#profile { | ||||
| 				max-width: 250px; | ||||
| 			} | ||||
| 			#instance, #member { | ||||
| 				display: flex; | ||||
| 				flex-direction: column; | ||||
| 				justify-content: space-between; | ||||
| 				max-width: 250px; | ||||
| 			} | ||||
| 			#userlist { | ||||
| 				flex: 1; | ||||
|  | @ -259,12 +272,14 @@ | |||
| 				margin-bottom: 5px; | ||||
| 			} | ||||
| 			#thread { | ||||
| 				flex: 1; | ||||
| 				min-width: min-content; | ||||
| 				display: flex; | ||||
| 				margin: 0; | ||||
| 			} | ||||
| 			#content { | ||||
| 				margin: 2px; | ||||
| 				min-width: 300px; | ||||
| 				min-width: 340px; | ||||
| 				display: flex; | ||||
| 				flex-direction: column; | ||||
| 			} | ||||
|  | @ -319,13 +334,6 @@ | |||
| 			#loadmore { | ||||
| 				margin-bottom: 10px; | ||||
| 			} | ||||
| 			#members { | ||||
| 				min-width: 140px; | ||||
| 				max-width: 250px; | ||||
| 			} | ||||
| 			#member { | ||||
| 				max-width: 250px; | ||||
| 			} | ||||
| 			#editthread { | ||||
| 				max-width: fit-content; | ||||
| 			} | ||||
|  | @ -335,6 +343,7 @@ | |||
| 			} | ||||
| 			#space { | ||||
| 				margin: -2px; /* offset column margin */ | ||||
| 				margin-top: 0; | ||||
| 				overflow: auto; | ||||
| 			} | ||||
| 			#spacediv { | ||||
|  |  | |||
|  | @ -159,7 +159,8 @@ export default function loadSpace(callback) { | |||
| 					editing = null; | ||||
| 				return; | ||||
| 			} | ||||
| 			if (moved && (event.offsetX - movedFrom.x) * (event.offsetX - movedFrom.x) | ||||
| 			if (event.target !== this || !clicked || | ||||
| 				(event.offsetX - movedFrom.x) * (event.offsetX - movedFrom.x) | ||||
| 				+ (event.offsetY - movedFrom.y) * (event.offsetY - movedFrom.y) > 100) | ||||
| 				return; | ||||
| 			editing = add({ | ||||
|  |  | |||
|  | @ -403,6 +403,7 @@ async function loadThreads(instancediv, select) { | |||
| 	} | ||||
| 
 | ||||
| 	let thread = document.getElementById('thread'); | ||||
| 	let members; | ||||
| 	if (!thread.hasChildNodes()) | ||||
| 		thread.append(html.node` | ||||
| 			<div id='content' class='content'> | ||||
|  | @ -416,9 +417,11 @@ async function loadThreads(instancediv, select) { | |||
| 						</button><button id='spacetab' class='tab' onclick=${clickedTab}>space | ||||
| 						</button><button id='calltab' class='tab' onclick=${clickedTab}>call / stream</button> | ||||
| 					</div> | ||||
| 					<button id='showmembers' onclick=${() => | ||||
| 						document.getElementById('members').classList.toggle('hidden') | ||||
| 					}>members</button> | ||||
| 					<button id='showmembers' onclick=${e => { | ||||
| 						members.classList.toggle('hidden'); | ||||
| 						if (!members.classList.contains('hidden')) | ||||
| 							document.body.scrollLeft = document.body.scrollWidth - document.body.clientWidth; | ||||
| 					}}>members</button> | ||||
| 				</div> | ||||
| 				<div id='message' class='tabcontent'></div> | ||||
| 				<div id='space' class='tabcontent hidden'> | ||||
|  | @ -429,7 +432,7 @@ async function loadThreads(instancediv, select) { | |||
| 			<hr class='separator' color='#505050'> | ||||
| 			<div id='members' class='column hidden'> | ||||
| 				<p id='visibility'></p> | ||||
| 				<div id='membershead'> | ||||
| 				<div id='membershead' class='header'> | ||||
| 					<strong>members</strong> | ||||
| 					<button id='membersadd' onclick=${e => { | ||||
| 						document.getElementById('addmember').classList.remove('hidden'); | ||||
|  | @ -447,11 +450,12 @@ async function loadThreads(instancediv, select) { | |||
| 				</div> | ||||
| 			</div> | ||||
| 			<hr class='separator' color='#505050'>`);
 | ||||
| 	members = document.getElementById('members'); | ||||
| 
 | ||||
| 	if (!instancediv.children['threads']) { | ||||
| 		instancediv.append(html.node` | ||||
| 			<div id='threads'> | ||||
| 				<div id='threadshead'> | ||||
| 				<div id='threadshead' class='header'> | ||||
| 					<strong>threads</strong> | ||||
| 					<button id='newthread' onclick=${function(e) { | ||||
| 						newThread.call(this, instancediv.instance); | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ create table user ( | |||
| 	id integer primary key asc, | ||||
| 	name text, | ||||
| 	displayname text, | ||||
| 	bio text, | ||||
| 	public boolean default true, | ||||
| 	created timestamp default current_timestamp | ||||
| ); | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ const authwrap = require('../authwrap'); | |||
| const openpgp = require('openpgp'); | ||||
| 
 | ||||
| async function create_user(msg, respond) { | ||||
| 	// validate inputs
 | ||||
| 	if (typeof msg.name !== 'string') { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
|  | @ -36,7 +35,6 @@ async function create_user(msg, respond) { | |||
| 			message: 'public key invalid' | ||||
| 		}); | ||||
| 	} | ||||
| 	// add to db
 | ||||
| 	const insert = await db.query( | ||||
| 		'insert into user (name, public) values (?, ?) returning id', | ||||
| 		[msg.name, true] | ||||
|  | @ -45,7 +43,6 @@ async function create_user(msg, respond) { | |||
| 		'insert into key (user, pubkey, active) values (?, ?, true)', | ||||
| 		[insert.rows[0].id, msg.pubkey] | ||||
| 	) | ||||
| 	// respond
 | ||||
| 	respond({ | ||||
| 		success: true, | ||||
| 		id: insert.rows[0].id | ||||
|  | @ -54,9 +51,9 @@ async function create_user(msg, respond) { | |||
| 
 | ||||
| async function getUser(id, name) { | ||||
| 	return (id ? await db.query( | ||||
| 			`select name, id, displayname, public from user where id = ?`, id) | ||||
| 			`select name, id, displayname, bio, public from user where id = ?`, id) | ||||
| 		: await db.query( | ||||
| 			`select name, id, displayname, public from user where name = ?`, name) | ||||
| 			`select name, id, displayname, bio, public from user where name = ?`, name) | ||||
| 		).rows[0]; | ||||
| } | ||||
| 
 | ||||
|  | @ -190,6 +187,7 @@ async function authenticate(msg, respond, socket) { | |||
| 			id: user.id, | ||||
| 			name: user.name, | ||||
| 			displayname: user.displayname, | ||||
| 			bio: user.bio, | ||||
| 			public: user.public, | ||||
| 			authrequests: Object.entries(user.authrequests).map(authrequest => ({ | ||||
| 				id: authrequest[0], | ||||
|  | @ -215,9 +213,7 @@ async function authorize_key(msg, respond) { | |||
| 		}); | ||||
| 	} | ||||
| 	authrequest.callback(true); | ||||
| 	respond({ | ||||
| 		success: true | ||||
| 	}); | ||||
| 	respond({ success: true }); | ||||
| } | ||||
| 
 | ||||
| async function update_user(msg, respond) { | ||||
|  | @ -228,12 +224,11 @@ async function update_user(msg, respond) { | |||
| 			message: 'user not found' | ||||
| 		}); | ||||
| 	await db.query( | ||||
| 		`update user set displayname = ?, public = ? where id = ?`, | ||||
| 		[msg.displayname, !!msg.public, msg.auth_user.id]); | ||||
| 		`update user set displayname = ?, bio = ?, public = ? where id = ?`, | ||||
| 		[msg.displayname, msg.bio, !!msg.public, msg.auth_user.id]); | ||||
| 	vybe.users[msg.auth_user.id].displayname = msg.displayname; | ||||
| 	respond({ | ||||
| 		success: true | ||||
| 	}); | ||||
| 	vybe.users[msg.auth_user.id].bio = msg.bio; | ||||
| 	respond({ success: true }); | ||||
| } | ||||
| 
 | ||||
| async function get_user(msg, respond) { | ||||
|  | @ -267,7 +262,6 @@ async function list_users(msg, respond) { | |||
| } | ||||
| 
 | ||||
| async function get_keys(msg, respond) { | ||||
| 	// validate inputs
 | ||||
| 	if (!Array.isArray(msg.ids)) { | ||||
| 		return respond({ | ||||
| 			success: false, | ||||
|  | @ -280,7 +274,6 @@ async function get_keys(msg, respond) { | |||
| 			(${msg.ids.map(i => '?').join(',')})`,
 | ||||
| 		msg.ids | ||||
| 	)).rows.forEach(key => keys[key.user].push(key.pubkey)); | ||||
| 	// respond
 | ||||
| 	respond({ | ||||
| 		success: true, | ||||
| 		keys | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue