make navigating threads work with browser back/forward/url
							parent
							
								
									8fb55749eb
								
							
						
					
					
						commit
						bb31b56929
					
				|  | @ -56,6 +56,8 @@ function loadMessages(callback) { | |||
| 				let user; | ||||
| 				if (message.user.name) | ||||
| 					user = message.user; | ||||
| 				else if (message.user.id.indexOf('@') === -1) | ||||
| 					message.user.id = 'deleted user'; | ||||
| 				else { | ||||
| 					user = users[message.user.id]; | ||||
| 					if (user === undefined) | ||||
|  |  | |||
							
								
								
									
										138
									
								
								client/thread.js
								
								
								
								
							
							
						
						
									
										138
									
								
								client/thread.js
								
								
								
								
							|  | @ -14,49 +14,35 @@ function setVisibility() { | |||
| 			'only members can post' : 'some members can post'}`;
 | ||||
| } | ||||
| 
 | ||||
| function chooseThread() { | ||||
| function openThread(div, pushState) { | ||||
| 	if (!document.getElementById('removemember').classList.contains('hidden')) | ||||
| 		document.getElementById('member').classList.add('hidden'); | ||||
| 	const edit = document.getElementById('edit'); | ||||
| 	let thread = this.thread; | ||||
| 	let url = new URL(location); | ||||
| 	if (window.currentThread) { | ||||
| 		window.currentThread.div.classList.remove('active'); | ||||
| 		let editform = document.getElementById('editthread'); | ||||
| 		if (editform) { | ||||
| 			editform.remove(); | ||||
| 			edit.textContent = 'edit'; | ||||
| 		} | ||||
| 		url.searchParams.delete('tab'); | ||||
| 		if (window.currentThread.id === thread.id) | ||||
| 			url.searchParams.delete('thread'); | ||||
| 		window.history.pushState(null, '', url.toString()); | ||||
| 		if (window.currentThread.id === thread.id) { | ||||
| 			document.getElementById('thread').classList.add('hidden'); | ||||
| 			window.currentThread = null; | ||||
| 			return; | ||||
| 		} | ||||
| 	if (!div) { | ||||
| 		document.getElementById('thread').classList.add('hidden'); | ||||
| 		window.currentThread = null; | ||||
| 		return; | ||||
| 	} | ||||
| 	else | ||||
| 		document.getElementById('thread').classList.remove('hidden'); | ||||
| 	if (thread.permissions.admin) { | ||||
| 	document.getElementById('thread').classList.remove('hidden'); | ||||
| 	const edit = document.getElementById('edit'); | ||||
| 	if (div.thread.permissions.admin) { | ||||
| 		edit.classList.remove('hidden'); | ||||
| 		document.getElementById('membersadd').classList.remove('hidden'); | ||||
| 	} else { | ||||
| 		edit.classList.add('hidden'); | ||||
| 		document.getElementById('membersadd').classList.add('hidden'); | ||||
| 	} | ||||
| 	document.getElementById('threadname').textContent = thread.name; | ||||
| 	this.classList.add('active'); | ||||
| 	window.currentThread = thread; | ||||
| 	window.currentInstance = thread.instance; | ||||
| 	let div = this; | ||||
| 	window.currentInstance.emit('get_thread', { thread: thread.id }, async msg => { | ||||
| 	document.getElementById('threadname').textContent = div.thread.name; | ||||
| 	if (window.currentThread) | ||||
| 		window.currentThread.div.classList.remove('active'); | ||||
| 	div.classList.add('active'); | ||||
| 	window.currentThread = div.thread; | ||||
| 	window.currentInstance = div.thread.instance; | ||||
| 	window.currentInstance.emit('get_thread', { thread: div.thread.id }, async msg => { | ||||
| 		if (!msg.success) { | ||||
| 			console.log('get_thread failed:', msg.message); | ||||
| 			return; | ||||
| 		} | ||||
| 		Object.assign(thread, msg.thread); | ||||
| 		Object.assign(div.thread, msg.thread); | ||||
| 		setVisibility(); | ||||
| 		document.getElementById('memberlist').replaceChildren( | ||||
| 			...await Promise.all(msg.thread.members.map(async member => { | ||||
|  | @ -78,16 +64,11 @@ function chooseThread() { | |||
| 			})) | ||||
| 		); | ||||
| 		loadCall(); | ||||
| 		let tab = url.searchParams.get('tab'); | ||||
| 		if (!['message', 'space', 'call'].includes(tab)) | ||||
| 			tab = null; | ||||
| 		if (tab || this.tab) { | ||||
| 			if (tab) | ||||
| 				this.tab = tab + 'tab'; | ||||
| 			switchTab(document.getElementById(this.tab)); | ||||
| 			if (this.tab === 'messagetab') | ||||
| 		if (div.tab) { | ||||
| 			switchTab(document.getElementById(div.tab)); | ||||
| 			if (div.tab === 'messagetab') | ||||
| 				loadMessages(); | ||||
| 			else if (this.tab === 'spacetab') | ||||
| 			else if (div.tab === 'spacetab') | ||||
| 				loadSpace(); | ||||
| 		} | ||||
| 		else // load first tab that has any content
 | ||||
|  | @ -100,27 +81,51 @@ function chooseThread() { | |||
| 						loadSpace(spans => { | ||||
| 							if (spans.length) | ||||
| 								switchTab(document.getElementById(div.tab = 'spacetab')); | ||||
| 							else if (window.currentThread.streams.length) | ||||
| 							else if (Object.keys(currentThread.call).length || currentThread.streams.length) | ||||
| 								switchTab(document.getElementById(div.tab = 'calltab')); | ||||
| 							else | ||||
| 								switchTab(document.getElementById( | ||||
| 									div.tab = spans.length ? 'spacetab' : | ||||
| 										window.currentThread.streams.length ? 'calltab' : 'messagetab')); | ||||
| 										Object.keys(currentThread.call).length || currentThread.streams.length | ||||
| 											? 'calltab' : 'messagetab')); | ||||
| 							resolve(); | ||||
| 						}); | ||||
| 				}); | ||||
| 			}); | ||||
| 		if (window.currentInstance === window.instancelist[0]) { | ||||
| 			url.searchParams.set('thread', thread.id); | ||||
| 			url.searchParams.set('tab', div.tab.substring(0, div.tab.length - 3)); | ||||
| 		} else { | ||||
| 		if (pushState) { | ||||
| 			let url = new URL(window.location); | ||||
| 			// put instance before thread and tab
 | ||||
| 			url.searchParams.delete('thread'); | ||||
| 			url.searchParams.delete('tab'); | ||||
| 			if (currentInstance === instancelist[0]) | ||||
| 				url.searchParams.delete('instance'); | ||||
| 			else | ||||
| 				url.searchParams.set('instance', currentInstance.url); | ||||
| 			url.searchParams.set('thread', div.thread.id); | ||||
| 			url.searchParams.set('tab', div.tab.substring(0, div.tab.length - 3)); | ||||
| 			window.history.pushState(null, '', url.toString()); | ||||
| 		} | ||||
| 		window.history.pushState(null, '', url.toString()); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| window.onpopstate = event => { | ||||
| 	let params = new URLSearchParams(window.location.search); | ||||
| 	let thread = params.get('thread'); | ||||
| 	if (!thread) { | ||||
| 		openThread(); | ||||
| 		return; | ||||
| 	} | ||||
| 	let url = params.get('instance'); | ||||
| 	let div = document.querySelector( | ||||
| 		`#instance${(url ? instancelist.find(i => i.url === url) : instancelist[0])?.id} #thread${thread}`); | ||||
| 	if (!div) | ||||
| 		return; | ||||
| 	let tab = params.get('tab'); | ||||
| 	if (['message', 'space', 'call'].includes(tab)) | ||||
| 		div.tab = tab + 'tab'; | ||||
| 	openThread(div); | ||||
| }; | ||||
| 
 | ||||
| function switchTab(tab) { | ||||
| 	document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active')); | ||||
| 	document.querySelectorAll('.tabcontent').forEach(tab => tab.classList.add('hidden')); | ||||
|  | @ -204,8 +209,8 @@ function newThread(instance) { | |||
| 					} | ||||
| 					// since the form exists, this will perform cleanup
 | ||||
| 					newThread(); | ||||
| 					chooseThread.call(this.parentElement.parentElement.children['threadlist'] | ||||
| 						.children['thread' + msg.id]); | ||||
| 					openThread(this.parentElement.parentElement.children['threadlist'] | ||||
| 						.children['thread' + msg.id], true); | ||||
| 				} | ||||
| 			); | ||||
| 		}}> | ||||
|  | @ -366,7 +371,27 @@ async function loadThreads(instancediv, select) { | |||
| 	function makeThread(thread) { | ||||
| 		thread.instance = instancediv.instance; | ||||
| 		let node = html.node` | ||||
| 			<div class='thread' onclick=${chooseThread}>${ | ||||
| 			<div class='thread' onclick=${function(event) { | ||||
| 				let url = new URL(window.location); | ||||
| 				if (window.currentThread) { | ||||
| 					let editform = document.getElementById('editthread'); | ||||
| 					if (editform) { | ||||
| 						editform.remove(); | ||||
| 						edit.textContent = 'edit'; | ||||
| 					} | ||||
| 					url.searchParams.delete('tab'); | ||||
| 					if (window.currentThread.id === this.thread.id) { | ||||
| 						openThread(); | ||||
| 						url.searchParams.delete('instance'); | ||||
| 						url.searchParams.delete('thread'); | ||||
| 						window.history.pushState(null, '', url.toString()); | ||||
| 						return; | ||||
| 					} | ||||
| 				} | ||||
| 				else | ||||
| 					document.getElementById('thread').classList.remove('hidden'); | ||||
| 				openThread(this, true); | ||||
| 			}}>${ | ||||
| 				thread.name | ||||
| 			}</div>`; | ||||
| 		node.id = 'thread' + thread.id; | ||||
|  | @ -434,12 +459,19 @@ async function loadThreads(instancediv, select) { | |||
| 
 | ||||
| 		instancediv.instance.emit('list_threads', {}, msg => { | ||||
| 			threadlist.replaceChildren(...msg.threads.map(makeThread)); | ||||
| 			let params = new URLSearchParams(window.location.search); | ||||
| 			let instance = params.get('instance'); | ||||
| 			instance = instance ? instancelist.find(i => i.url === instance) : instancelist[0]; | ||||
| 			let thread = msg.threads.find(thread => | ||||
| 				thread.id == (new URLSearchParams(location.search)).get('thread'))?.div; | ||||
| 			if (!window.currentThread && thread) | ||||
| 				chooseThread.call(thread); | ||||
| 				thread.id == params.get('thread'))?.div; | ||||
| 			if (thread && (instance === instancediv.instance || !window.currentThread)) { | ||||
| 				let tab = params.get('tab'); | ||||
| 				if (['message', 'space', 'call'].includes(tab)) | ||||
| 					thread.tab = tab + 'tab'; | ||||
| 				openThread(thread); | ||||
| 			} | ||||
| 			else if (select && msg.threads.length) | ||||
| 				chooseThread.call(threadlist.firstChild); | ||||
| 				openThread(threadlist.firstChild); | ||||
| 		}); | ||||
| 
 | ||||
| 		instancediv.instance.socket.on('thread', thread => { | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ for (let file of fs.readdirSync('./src/events')) { | |||
| 
 | ||||
| function rand32() { | ||||
| 	let str = ''; | ||||
| 	const lookups = 'abcdefghjklmnpqrstvwxyz0123456789'.split(''); | ||||
| 	const lookups = 'bcdefghjklmnpqrstvwxyz0123456789'.split(''); | ||||
| 	while (str.length < 16) { | ||||
| 		const n = Math.random() * lookups.length; | ||||
| 		str += lookups[Math.floor(n)]; | ||||
|  |  | |||
|  | @ -122,6 +122,7 @@ async function create_thread(msg, respond) { | |||
| 		streams: [], | ||||
| 		call: {} | ||||
| 	}; | ||||
| 	vybe.calls[thread_id] = {}; | ||||
| 	if (!msg.permissions?.view_limited) { | ||||
| 		for (let id in vybe.users) { | ||||
| 			for (let socket of vybe.users[id].sockets) { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue