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