From a53a74e3d3d0b5e3e9e6c016fcbbc4e10b592528 Mon Sep 17 00:00:00 2001 From: echicken <echicken@bbs.electronicchicken.com> Date: Fri, 26 Feb 2021 14:12:00 -0500 Subject: [PATCH] Client-side things for the XJS forum page. --- webv4/root/js/xjs-forum.js | 500 +++++++++++++++++++++++++++++++++++++ 1 file changed, 500 insertions(+) create mode 100644 webv4/root/js/xjs-forum.js diff --git a/webv4/root/js/xjs-forum.js b/webv4/root/js/xjs-forum.js new file mode 100644 index 0000000000..ace33e9779 --- /dev/null +++ b/webv4/root/js/xjs-forum.js @@ -0,0 +1,500 @@ +let __v4_forum_offset = 0; // Feels hacky + +async function addNew(sub) { + + if (document.getElementById('newmessage') !== null) return; + + const elem = document.getElementById('forum-new-message-template').cloneNode(true); + elem.id = 'newmessage'; + elem.innerHTML = elem.innerHTML.replace(/SUB/g, sub); + + const li = document.createElement('li'); + li.id = 'newmessage-li'; + li.className = 'list-group-item'; + li.appendChild(elem); + document.getElementById('forum-list-container').appendChild(li); + + elem.removeAttribute('hidden'); + + const data = await v4_get('./api/forum.ssjs?call=get-signature'); + const nmb = elem.getElementsByTagName('textarea')[0]; + nmb.value += `\r\n${data.signature}`; + nmb.setSelectionRange(0, 0); + window.location.hash = '#newmessage'; + nmb.onkeydown = evt => evt.stopImmediatePropagation(); + +} + +async function postNew(sub) { + + document.getElementById('newmessage').getElementsByTagName('input')[2].setAttribute('disabled', true); + + const data = await v4_post('./api/forum.ssjs', { + call: 'post', + sub, + to: document.getElementById('newmessage-to').value, + subject: document.getElementById('newmessage-subject').value, + body: document.getElementById('newmessage-body').value, + }); + + document.getElementById('newmessage').getElementsByTagName('input')[2].setAttribute('disabled', true); + + if (data.success) { + const li = document.getElementById('newmessage-li'); + li.parentNode.removeChild(li); + insertParam('notice', 'Your message has been posted.'); // This is stupid. + } + +} + +async function addReply(sub, id) { + + if (document.getElementById(`replybox-${id}`) !== null) return; + + const elem = document.getElementById('forum-message-reply-template').cloneNode(true); + elem.id = `replybox-${id}`; + elem.innerHTML = elem.innerHTML.replace(/SUB/g, sub); + elem.innerHTML = elem.innerHTML.replace(/ID/g, id); + elem.removeAttribute('hidden'); + + const data = await v4_get('./api/forum.ssjs?call=get-signature'); + const nmb = elem.getElementsByTagName('textarea')[0]; + nmb.value += `\r\n${data.signature}`; + nmb.setSelectionRange(0, 0); + nmb.onkeydown = evt => evt.stopImmediatePropagation(); + +} + +async function postReply(sub, id) { + document.getElementById(`reply-button-${id}`).setAttribute('disabled', true); + const data = await v4_post('./api/forum.ssjs', { + call: 'post-reply', + sub, + body: body = document.getElementById(`reply-button-${id}`).value, + pid: id, + }); + if (data.success) { + document.getElementById(`quote-${id}`).setAttribute('disabled', false); + const rb = document.getElementById(`replybox-${id}`); + rb.parentNode.removeChild(rb); + insertParam('notice', 'Your message has been posted.'); // This is stupid. + } else { + document.getElementById(`reply-button-${id}`).setAttribute('disabled', false); + } +} + +async function postNewPoll(sub) { + + document.getElementById('newpoll-submit').setAttribute('disabled', true); + + if (document.querySelectorAll('input[name="newpoll-answers"]:checked').length !== 1) return; + + const subject = document.getElementById('newpoll-subject').value; + if (subject.length < 1) return; + + let answerCount = document.querySelector('input[name="newpoll-answers"]:checked:first').value; + if (answerCount == 2) answerCount = document.querySelector('input[name="newpoll-answer-count"]').value; + if (answerCount < 0 || answerCount > 15) return; + + const results = parseInt(document.querySelector('input[name="newpoll-results"]:checked').value); + if (results < 0 || results > 3) return; + + const answers = Array.from(document.querySelectorAll('input[name="forum-new-poll-field-answer"]')).reduce((a, c) => { + if (c.value !== '') a.push(c.value); + return a; + }, []); + if (!answers.length) return; + + const comments = Array.from(document.querySelectorAll('input[name="forum-new-poll-field-comment"]')).reduce((a, c) => { + if (c.value !== '') a.push(c); + return a; + }, []); + + const post_data = { + sub, + subject, + votes: answerCount, + results, + answer: answers + }; + if (comments.length) post_data.comment = comments; + const res = await v4_post('./api/forum.ssjs?call=submit-poll', post_data); + document.getElementById('newpoll-submit').setAttribute('disabled', false); + if (res.success) { + const np = document.getElementById('forum-new-poll'); + np.parentNode.removeChild(np); + insertParam('notice', 'Your poll has been posted.'); // This is stupid + } + +} + +function addPoll(sub) { + + if (document.getElementById('forum-new-poll') !== null) return; + + const elem = document.getElementById('forum-new-poll-template').cloneNode(true); + elem.id = 'forum-new-poll'; + elem.innerHTML = elem.innerHTML.replace(/\-template/g, ''); + elem.innerHTML = elem.innerHTML.replace(/SUB/g, sub); + elem.removeAttribute('hidden'); + + const li = document.createElement('li'); + li.id = 'newpoll-li'; + li.className = 'list-group-item'; + li.appendChild(elem); + document.getElementById('forum-list-container').appendChild(li); + + addPollField('comment', 'newpoll-comment-group'); + addPollField('answer', 'newpoll-answer-group'); + addPollField('answer', 'newpoll-answer-group'); + window.location.hash = '#newpoll'; + +} + +function addPollField(type, target) { + + const prefix = `forum-new-poll-field-${type}`; + const count = document.querySelectorAll(`div[name="${prefix}"]`).length; + if (type === 'answer' && count > 15) return; + const number = count + 1; + + const elem = document.getElementById(`forum-new-poll-field-container-template`).cloneNode(true); + elem.id = `${prefix}-${number}`; + elem.innerHTML = elem.innerHTML.replace(/\-template/g, ''); + elem.innerHTML = elem.innerHTML.replace(/TYPE/g, type); + elem.innerHTML = elem.innerHTML.replace(/NUMBER/g, number); + elem.onkeydown = evt => evt.stopImmediatePropagation(); + elem.removeAttribute('hidden'); + + document.getElementById(target).appendChild(elem); + +} + +async function setScanCfg(sub, cfg) { + var opts = [ + 'scan-cfg-off', + 'scan-cfg-new', + 'scan-cfg-youonly', + ]; + const data = await v4_get(`./api/forum.ssjs?call=set-scan-cfg&sub=${sub}&cfg=${cfg}`); + if (!data.success) return; + opts.forEach((e, i) => { + const elem = document.getElementById(`${e}`); + if (cfg == i) { + elem.classList.add('btn-primary'); + elem.classList.remove('btn-default'); + } else { + elem.classList.add('btn-default'); + elem.classList.remove('btn-primary'); + } + }); +} + +function LoadingMessage() { + + let pos = 0; + let cursor = ['|', '/', '—', '\\' ]; + let evt; + + const flc = document.getElementById('forum-list-container'); + + this.start = function () { + const elem = document.querySelector('div[data-loading-template]').cloneNode(true); + const sc = elem.querySelector('span[data-spinning-cursor]'); + elem.removeAttribute('hidden'); + flc.appendChild(elem); + evt = setInterval(() => { + sc.innerHTML = cursor[pos % cursor.length]; + pos++; + }, 250); + } + + this.stop = function () { + flc.removeChild(flc.querySelector('div[data-loading-template]')); + clearInterval(evt); + } + +} + +function loading(l) { + var flc = document.getElementById('forum-list-container'); + if (l) { + var elem = document.querySelector('div[data-loading-template]').cloneNode(true); + elem.removeAttribute('hidden'); + flc.appendChild(elem); + } else { + flc.removeChild(flc.querySelector('div[data-loading-template]')); + } +} + +async function listMessages(sub, thread, count, after) { + + const dlmm = document.getElementById('forum-load-more-messages'); + const blmm = document.getElementById('load-more-messages'); + dlmm.setAttribute('hidden', true); + blmm.setAttribute('disabled', true); + + const lm = new LoadingMessage(); + lm.start(); + let _data; + let data = JSON.parse(localStorage.getItem(`${sub}-${thread}`)); + if (data === null) { + data = await v4_fetch_jsonl(`./api/forum.ssjs?call=get-thread&sub=${sub}&thread=${thread}&count=${count}`); + } else if (after) { + _data = await v4_fetch_jsonl(`./api/forum.ssjs?call=get-thread&sub=${sub}&thread=${thread}&count=${count}&after=${data[data.length - 1].number}`); + data = data.concat(_data); + } + localStorage.setItem(`${sub}-${thread}`, JSON.stringify(data)); + lm.stop(); + + const users = []; + (_data || data).forEach((e, i) => { + let akey; + const elem = document.getElementById('forum-message-template').cloneNode(true); + elem.id = elem.id.replace(/template$/, e.number); + elem.querySelector('a[data-message-anchor]').id = e.number; + if (i == 0 && !after && e.subject !== undefined) elem.querySelector('strong[data-message-subject]').innerHTML = e.subject; + elem.querySelector('strong[data-message-from]').innerHTML = e.from; + if (e.from_net_addr) { + akey = `${e.from}@${e.from_net_addr}`; + elem.querySelector('span[data-message-from-address]').innerHTML = `@${e.from_net_addr}`; + elem.querySelector('div[data-avatar]').setAttribute('data-avatar', `${e.from}@${e.from_net_addr}`); + } else { + akey = e.from; + elem.querySelector('div[data-avatar]').setAttribute('data-avatar', `${e.from}`); + } + elem.querySelector('strong[data-message-to]').innerHTML = e.to; + elem.querySelector('strong[data-message-date]').innerHTML = formatMessageDate(e.when_written_time); + elem.querySelector('span[data-upvote-count]').innerHTML = e.upvotes; + elem.querySelector('span[data-downvote-count]').innerHTML = e.downvotes; + elem.querySelector('div[data-message-body]').innerHTML = e.body; + elem.querySelector('a[data-direct-link]').setAttribute('href', `#${e.number}`); + elem.removeAttribute('hidden'); + document.getElementById('forum-list-container').appendChild(elem); + if (users.indexOf(akey) < 0) users.push(akey); + }); + + dlmm.removeAttribute('hidden'); + blmm.removeAttribute('disabled'); + + if (Avatars) Avatars.draw(users); + +} + +function onThreadStats(data) { + + Object.entries(data).forEach(([k, v]) => { + + if (k == 'sub' || k == 'scan_cfg') return; + + let cache = JSON.parse(localStorage.getItem(`${data.sub}-threadList`)); + if (cache) { + const idx = cache.threads.findIndex(e => e.id == k); + if (idx > -1) { + cache.total += (v.messages - cache.threads[idx].messages); + cache.threads[idx].last.from = v.last.from; + cache.threads[idx].last.when_written_time = v.last.when_written_time; + cache.threads[idx].messages = v.messages; + cache.threads[idx].unread = v.unread; + cache.threads[idx].votes = v.votes; + } + localStorage.setItem(`${data.sub}-threadList`, JSON.stringify(cache)); + } + + const elem = document.getElementById(`forum-thread-link-${k}`); + if (elem === null) return; + + const replies = elem.querySelector('div[data-replies]'); + if (v.total > 1) { + replies.querySelector('strong[data-message-count]').innerHTML = v.messages - 1; + if (v.messages == 2) { + replies.querySelector('span[data-suffix-reply]').removeAttribute('hidden'); + } else { + replies.querySelector('span[data-suffix-replies]').removeAttribute('hidden'); + } + replies.querySelector('strong[data-last-from]').innerHTML = v.last.from; + replies.querySelector('span[data-last-time]').innerHTML = formatMessageDate(v.last.when_written_time); + replies.removeAttribute('hidden'); + } + + const stats = elem.querySelector('div[data-stats]'); + if (v.unread) { + const urm = stats.querySelector('span[data-unread-messages]'); + if (urm !== null) { // If user is guest, this element will not exist + urm.innerHTML = v.unread; + if (data.scan_cfg&(1<<1) || data.scan_cfg&5 || data.scan_cfg&(1<<8)) { + urm.classList.add('scanned'); + } else { + urm.classList.remove('scanned'); + } + urm.removeAttribute('hidden'); + stats.removeAttribute('hidden'); + } + } + + if (v.votes.total) { + if (v.votes.up.t) { + stats.querySelector('span[data-upvotes]').innerHTML = `${v.votes.up.p}/${v.votes.up.t}`; + stats.querySelector('span[data-upvotes-badge]').style.setProperty('display', ''); + } + if (v.votes.down.t) { + stats.querySelector('span[data-downvotes]').innerHTML = `${v.votes.down.p}/${v.votes.down.t}`; + stats.querySelector('span[data-downvotes-badge]').style.setProperty('display', ''); + } + stats.removeAttribute('hidden'); + } + + }); + +} + +async function listThreads(sub, count, after) { + + const dlmt = document.getElementById('forum-load-more-threads'); + const blmt = document.getElementById('load-more-threads'); + dlmt.setAttribute('hidden', true); + blmt.setAttribute('disabled', true); + + const lm = new LoadingMessage(); + lm.start(); + let _data; + let data = JSON.parse(localStorage.getItem(`${sub}-threadList`)); + if (data === null) { + data = await v4_get(`./api/forum.ssjs?call=list-threads&sub=${sub}&count=${count}`); + } else if (after) { + _data = await v4_get(`./api/forum.ssjs?call=list-threads&sub=${sub}&count=${count}&after=${data.threads[data.threads.length - 1].id}`); + data.total = _data.total; + data.threads = data.threads.concat(_data.threads); + } + localStorage.setItem(`${sub}-threadList`, JSON.stringify(data)); + lm.stop(); + + (_data || data).threads.forEach(e => { + + const elem = document.getElementById('forum-thread-link-template').cloneNode(true); + elem.id = elem.id.replace(/template$/, e.id); + elem.setAttribute('href', `${elem.getAttribute('href')}&thread=${e.id}`); + elem.querySelector('strong[data-thread-subject]').innerHTML = e.subject; + elem.querySelector('strong[data-thread-from]').innerHTML = e.first.from; + elem.querySelector('span[data-thread-date-start]').innerHTML = formatMessageDate(e.first.when_written_time); + + if (e.messages > 1) { + elem.querySelector('strong[data-message-count]').innerHTML = e.messages - 1; + if (e.messages == 2) { + elem.querySelector('span[data-suffix-reply]').removeAttribute('hidden'); + } else { + elem.querySelector('span[data-suffix-replies]').removeAttribute('hidden'); + } + elem.querySelector('strong[data-last-from]').innerHTML = e.last.from; + elem.querySelector('span[data-last-time]').innerHTML = formatMessageDate(e.last.when_written_time); + elem.querySelector('div[data-replies]').removeAttribute('hidden'); + } + + const stats = elem.querySelector('div[data-stats]'); + if (e.unread) { + const urm = stats.querySelector('span[data-unread-messages]'); + if (urm !== null) { // If user is guest, this element will not exist + urm.innerHTML = e.unread; + if (data.scan_cfg&(1<<1) || data.scan_cfg&5 || data.scan_cfg&(1<<8)) { + urm.classList.add('scanned'); + } else { + urm.classList.remove('scanned'); + } + urm.removeAttribute('hidden'); + stats.removeAttribute('hidden'); + } + } + if (e.votes.total) { + if (e.votes.up.t) { + stats.querySelector('span[data-upvotes]').innerHTML = `${e.votes.up.p}/${e.votes.up.t}`; + stats.querySelector('span[data-upvotes-badge]').style.setProperty('display', ''); + } + if (e.votes.down.t) { + stats.querySelector('span[data-downvotes]').innerHTML = `${e.votes.down.p}/${e.votes.down.t}`; + stats.querySelector('span[data-downvotes-badge]').style.setProperty('display', ''); + } + stats.removeAttribute('hidden'); + } + + elem.removeAttribute('hidden'); + document.getElementById('forum-list-container').appendChild(elem); + + }); + + dlmt.removeAttribute('hidden'); + blmt.removeAttribute('disabled'); + +} + +async function listGroups() { + + loading(true); + const data = await v4_get(`./api/forum.ssjs?call=list-groups`); + loading(false); + + data.forEach(e => { + const elem = document.getElementById('forum-group-link-template').cloneNode(true); + elem.id = elem.id.replace(/template$/, e.index); + elem.setAttribute('href', `${elem.getAttribute('href')}&group=${e.index}`); + elem.querySelector('strong[data-group-name]').innerHTML = e.name; + elem.querySelector('span[data-unread-unscanned]').innerHTML = ''; + elem.querySelector('span[data-unread-scanned]').innerHTML = ''; + elem.querySelector('span[data-group-description]').innerHTML = e.description; + elem.querySelector('span[data-group-sub-count]').innerHTML = e.sub_count; + document.getElementById('forum-list-container').appendChild(elem); + }); + +} + +async function listSubs(group) { + + loading(true); + const data = await v4_get(`./api/forum.ssjs?call=list-subs&group=${group}`); + loading(false); + + data.forEach(e => { + const elem = document.getElementById('forum-sub-link-template').cloneNode(true); + elem.id = elem.id.replace(/template$/, e.code); + elem.setAttribute('href', `${elem.getAttribute('href')}&sub=${e.code}`); + elem.querySelector('strong[data-sub-name]').innerHTML = e.name; + elem.querySelector('p[data-sub-description]').innerHTML = e.description; + document.getElementById('forum-list-container').appendChild(elem); + }); + +} + +function onNewestSubMessage(sub, msg) { + const elem = document.getElementById(`forum-sub-link-${sub}`); + elem.querySelector('strong[data-newest-message-subject]').innerHTML = msg.subject; + elem.querySelector('span[data-newest-message-from]').innerHTML = msg.from; + elem.querySelector('span[data-newest-message-date]').innerHTML = formatMessageDate(msg.date); + elem.querySelector('span[data-newest-message-container]').removeAttribute('hidden'); +} + +async function getNewestMessagePerSub(group) { + const data = await v4_get(`./api/forum.ssjs?call=get-newest-message-per-sub&group=${group}`); + Object.entries(data).forEach(([k, v]) => { + onNewestSubMessage(k, v) + }); +} + +function formatMessageDate(t) { + return (new Date(t * 1000)).toLocaleString(); +} + +function onGroupUnreadCount(data) { + Object.entries(data).forEach(([k, v]) => { + const elem = document.getElementById(`forum-group-link-${k}`); + if (v.total - v.scanned > 0) elem.querySelector('span[data-unread-unscanned]').innerHTML = v.total - v.scanned; + if (v.scanned > 0) elem.querySelector('span[data-unread-scanned]').innerHTML = v.scanned; + }); +} + +function onSubUnreadCount(data) { + Object.entries(data).forEach(([k, v]) => { + const elem = document.getElementById(`forum-sub-link-${k}`); + if (v.total - v.scanned > 0) elem.querySelector('span[data-unread-unscanned]').innerHTML = v.total - v.scanned; + if (v.scanned > 0) elem.querySelector('span[data-unread-scanned]').innerHTML = v.scanned; + if (v.newest) onNewestSubMessage(k, v.newest); + }); +} -- GitLab