diff options
Diffstat (limited to 'view/js/main.js')
-rw-r--r-- | view/js/main.js | 356 |
1 files changed, 312 insertions, 44 deletions
diff --git a/view/js/main.js b/view/js/main.js index 7043c8577..8e369f62a 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -28,6 +28,8 @@ var expanded_items = []; var updateTimeout = []; const singlethread_modules = ['display', 'hq']; const redirect_modules = ['display', 'notify']; +let b64mids = []; + var page_cache = {}; @@ -87,6 +89,124 @@ $(document).ready(function() { } }); + document.addEventListener('click', function(event) { + const targetElement = event.target.closest('.wall-item-reaction'); + if (!targetElement) return; + + const userClick = event.isTrusted; + const id = targetElement.dataset.itemId; + const mid = targetElement.dataset.itemMid; + const parent = targetElement.dataset.itemParent; + const uuid = targetElement.dataset.itemUuid; + const verb = targetElement.dataset.itemVerb; + + if (targetElement.classList.contains('wall-item-comment')) { + const threadWrapper = document.getElementById(`thread-wrapper-${id}`); + const subWrapper = document.getElementById(`wall-item-sub-thread-wrapper-${id}`); + const parentSubWrapper = document.querySelectorAll(`#wall-item-sub-thread-wrapper-${parent} .wall-item-sub-thread-wrapper`); + const parentSubWrapperIndented = document.querySelector(`#wall-item-sub-thread-wrapper-${parent} .wall-item-sub-thread-wrapper.item-indent`); + + subWrapper.style.setProperty('--hz-item-indent', stringToHslColor(uuid)); + threadWrapper.style.setProperty('--hz-item-highlight', stringToHslColor(uuid)); + + document.querySelectorAll('.thread-wrapper.item-highlight').forEach(el => { + el.classList.remove('item-highlight'); + }); + + if (!parentSubWrapperIndented && userClick) { + threadWrapper.classList.add('item-highlight'); + } + else { + subWrapper.classList.add('item-indent'); + } + + if (userClick && targetElement.classList.contains('expanded')) { + document.querySelectorAll('.thread-wrapper.item-highlight').forEach(el => { + el.classList.remove('item-highlight'); + }); + + parentSubWrapper.forEach(el => { + if (el.children.length > 0) { + el.classList.add('item-indent'); + } + }); + + targetElement.classList.add('disabled'); + return; + } + + targetElement.classList.add('expanded'); + + if (!userClick) { + targetElement.classList.add('disabled'); + } + } + + request(id, mid, verb, parent, uuid, userClick); + }); + + document.addEventListener('click', function(event) { + const targetElement = event.target.closest('.dropdown-item-expand'); + if (!targetElement) return; + + // Disable the button we just clicked + targetElement.classList.add('disabled'); + event.preventDefault(); + + document.querySelectorAll('.thread-wrapper.item-highlight').forEach(el => { + el.classList.remove('item-highlight'); + }); + + const id = targetElement.dataset.itemId; + const uuid = targetElement.dataset.itemUuid; + const subWrapper = document.getElementById(`wall-item-sub-thread-wrapper-${id}`); + + const loading = document.getElementById('like-rotator-' + id); + + const wrapper = document.getElementById('thread-wrapper-' + id); + const parent = wrapper.closest('.generic-content-wrapper'); + + parent.querySelectorAll('.wall-item-sub-thread-wrapper.item-indent').forEach(el => { + el.classList.remove('item-indent'); + }); + + parent.querySelectorAll('.thread-wrapper.wall-item-expanded').forEach(el => { + el.classList.remove('wall-item-expanded'); + }); + + parent.classList.add('wall-item-backdrop'); + wrapper.classList.add('wall-item-expanded', 'shadow'); + + if (!wrapper.classList.contains('toplevel_item')) { + document.documentElement.style.setProperty('--hz-item-highlight', stringToHslColor(uuid)); + } + + autoExpand(id); + + // Close and reset if dbl clicked + wrapper.addEventListener('dblclick', function(event) { + + parent.classList.remove('wall-item-backdrop'); + wrapper.classList.remove('wall-item-expanded', 'shadow'); + + wrapper.querySelectorAll('.dropdown-item-expand').forEach(el => { + el.classList.remove('disabled'); + }); + + wrapper.querySelectorAll('.wall-item-comment').forEach(el => { + el.classList.remove('disabled', 'expanded'); + }); + + subWrapper.querySelectorAll('.wall-item-comment').forEach(el => { + el.classList.remove('disabled', 'expanded'); + }); + + subWrapper.innerHTML = ''; + subWrapper.classList.remove('item-indent'); + }) + + }); + // @hilmar |-> if ( typeof(window.tao) == 'undefined' ) { window.tao = {}; @@ -756,7 +876,7 @@ function updateConvItems(mode, data) { if (data_json.includes(bParam_mid) && elem.parentNode.classList.contains('wall-item-sub-thread-wrapper')) { if (!elem.parentNode.parentNode.classList.contains('toplevel_item')) { elem.parentNode.parentNode.classList.add('item-highlight'); - document.documentElement.style.setProperty('--hz-item-highlight', stringToHlsColor(JSON.parse(elem.parentNode.parentNode.dataset.b64mids)[0])); + document.documentElement.style.setProperty('--hz-item-highlight', stringToHslColor(JSON.parse(elem.parentNode.parentNode.dataset.b64mids)[0])); } } @@ -1292,27 +1412,64 @@ function justifyPhotosAjax(id) { $('#' + id).justifiedGallery('norewind').on('jg.complete', function(e){ justifiedGalleryActive = false; }); } -function request(id, mid, verb, parent, uuid) { +function request(id, mid, verb, parent, uuid, userClick) { - const loading = document.getElementById('like-rotator-' + id); - loading.style.display = 'block'; + if (verb === 'load') { + const dots = document.getElementById('load-more-dots-' + parent); + dots.classList.add('jumping-dots'); - if (verb === 'comment') { + const parent_sub = document.getElementById('wall-item-sub-thread-wrapper-' + parent); + const offset = parent_sub.children.length; - if (singlethread_modules.indexOf(module) !== -1) { - let stateObj = { b64mid: uuid }; - history.pushState(stateObj, '', module + '/' + uuid); - } + fetch('/request?offset=' + offset + '&verb=' + verb + '&mid=' + mid + '&parent=' + parent + '&module=' + module) + .then(response => response.json()) + .then(obj => { + let parser = new DOMParser(); + let doc = parser.parseFromString(obj.html, 'text/html'); + let b64mids = []; - document.querySelectorAll('.thread-wrapper.item-highlight').forEach(el => { - el.classList.remove('item-highlight'); - el.style.boxShadow = ''; + doc.querySelectorAll('.thread-wrapper').forEach(function (e) { + let data = JSON.parse(e.dataset.b64mids); + b64mids.push(...data); + }); + + imagesLoaded(doc.querySelectorAll('.wall-item-body img'), function () { + injectWithAnimation('wall-item-sub-thread-wrapper-' + parent, doc); + dots.classList.remove('jumping-dots'); + + const loadmore_progress = document.getElementById('load-more-progress-' + parent); + loadmore_progress.style.width = Math.round(100 * parent_sub.children.length / loadmore_progress.dataset.commentsTotal) + '%'; + + if (Number(parent_sub.children.length) === Number(loadmore_progress.dataset.commentsTotal)) { + const loadmore = document.getElementById('load-more-' + parent); + loadmore.remove(); + } + + updateRelativeTime('.autotime'); + collapseHeight(); + + document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids })); + document.dispatchEvent(new Event('hz:sse_bs_counts')); + }); + + }) + .catch(error => { + console.error('Error fetching data:', error); }); - const wrapper = document.getElementById('thread-wrapper-' + id); - if (!wrapper.classList.contains('toplevel_item')) { - wrapper.classList.add('item-highlight'); - document.documentElement.style.setProperty('--hz-item-highlight', stringToHlsColor(uuid)); + return; + } + + const loading = document.getElementById('like-rotator-' + id); + + if (userClick) { + loading.style.display = 'block'; + } + + if (verb === 'comment') { + if (userClick && singlethread_modules.indexOf(module) !== -1) { + let stateObj = { b64mid: uuid }; + history.pushState(stateObj, '', module + '/' + uuid); } fetch('/request?verb=' + verb + '&mid=' + mid + '&parent=' + parent + '&module=' + module) @@ -1320,7 +1477,6 @@ function request(id, mid, verb, parent, uuid) { .then(obj => { let parser = new DOMParser(); let doc = parser.parseFromString(obj.html, 'text/html'); - let b64mids = []; doc.querySelectorAll('.thread-wrapper').forEach(function (e) { let data = JSON.parse(e.dataset.b64mids); @@ -1328,13 +1484,15 @@ function request(id, mid, verb, parent, uuid) { }); imagesLoaded(doc.querySelectorAll('.wall-item-body img'), function () { - injectWithAnimation('wall-item-sub-thread-wrapper-' + id, obj.html); + injectWithAnimation('wall-item-sub-thread-wrapper-' + id, doc, true); updateRelativeTime('.autotime'); - loading.style.display = 'none'; collapseHeight(); - document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids })); - document.dispatchEvent(new Event('hz:sse_bs_counts')); + if (userClick) { + loading.style.display = 'none'; + document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids })); + document.dispatchEvent(new Event('hz:sse_bs_counts')); + } }); }) @@ -1377,32 +1535,142 @@ function request(id, mid, verb, parent, uuid) { } -function injectWithAnimation(container, html) { - const target = document.getElementById(container); - target.innerHTML = html; +function injectWithAnimation(containerId, parsedDoc, overwrite = false) { + const container = document.getElementById(containerId); + if (!container) return; + if (overwrite) container.innerHTML = ''; - target.animate([ - { opacity: 0, transform: 'translateY(-20px)' }, - { opacity: 1, transform: 'translateY(0)' } - ], { - duration: 300, - easing: 'ease-out' - }); + const newElements = Array.from(parsedDoc.body.children); - // For children animation - Array.from(target.children).forEach((el, i) => { - el.animate([ - { opacity: 0, transform: 'scale(.7)' }, - { opacity: 1, transform: 'scale(1)' } - ], { - duration: 300, - delay: i * 50, - fill: 'both', - easing: 'ease-out' - }); - }); + for (let i = newElements.length - 1; i >= 0; i--) { + const el = newElements[i].cloneNode(true); + el.classList.add('item-fade-in'); + container.insertBefore(el, container.firstChild); + + // Remove classes after transition ends + const onTransitionEnd = (event) => { + el.classList.remove('item-fade-in', 'show'); + el.removeEventListener('transitionend', onTransitionEnd); + }; + el.addEventListener('transitionend', onTransitionEnd); + + setTimeout(() => { + el.classList.add('show'); + }, (newElements.length - 1 - i) * 30); + } } +const autoExpand = (function () { + const clickedElements = new Set(); // Stores clicked button references + + // We wait 10 seconds for images. Set the timeout here slightly higher. + function waitForElement(selector, timeout = 11000) { + return new Promise((resolve, reject) => { + // Check if the element already exists + const element = document.querySelector(selector); + + if (element) { + resolve(element); + return; + } + + // Set up a timeout to reject the promise if the element doesn't appear in time + const timer = setTimeout(() => { + observer.disconnect(); + reject(new Error(`Element "${selector}" not found within ${timeout}ms`)); + }, timeout); + + // Create a MutationObserver to watch for DOM changes + const observer = new MutationObserver(() => { + const el = document.querySelector(selector); + if (el) { + clearTimeout(timer); + observer.disconnect(); + resolve(el); + } + }); + + // Start observing the document for changes + observer.observe(document.documentElement, { + childList: true, + subtree: true + }); + }); + } + + async function autoExpand(id) { + const loading = document.getElementById('like-rotator-' + id); + const maxIterations = 3; + clickedElements.clear(); + + try { + // Step 1: Ensure initial button is clicked + const initBtnSelector = '#wall-item-comment-' + id; + const initBtn = await waitForElement(initBtnSelector); + + if (!clickedElements.has(initBtn)) { + initBtn.click(); + clickedElements.add(initBtn); + } + + // Step 2: Loop until no new buttons are found + let newButtonsFound; + + const commentSelector = `#wall-item-sub-thread-wrapper-${id} .thread-wrapper`; + const commentBtnSelector = `#wall-item-sub-thread-wrapper-${id} .wall-item-comment`; + const subsSelector = `#wall-item-sub-thread-wrapper-${id}, #wall-item-sub-thread-wrapper-${id} .wall-item-sub-thread-wrapper`; + + let iteration = 1; + + do { + newButtonsFound = false; + + // Wait for any comment to appear + await waitForElement(commentSelector); + + document.querySelectorAll(subsSelector).forEach(el => { + if (el.children.length > 0) { + el.classList.add('item-indent'); + } + }); + + const expandButtons = document.querySelectorAll(commentBtnSelector); + + for (const btn of expandButtons) { + if (!clickedElements.has(btn)) { + btn.click(); + clickedElements.add(btn); + newButtonsFound = true; + // Optional: await waitForElement(...) to wait for new content + } + } + + // Wait between iterations to allow UI to update + if (newButtonsFound) { + await new Promise(res => setTimeout(res, 700)); + } + + iteration++; + + } while (newButtonsFound && iteration < maxIterations); + + console.log('All replies loaded!'); + + loading.style.display = 'none'; + + document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids })); + document.dispatchEvent(new Event('hz:sse_bs_counts')); + + } catch (error) { + loading.style.display = 'none'; + console.error("autoExpand failed:", error.message); + } + } + + return autoExpand; +})(); + + function stringToHexColor(str) { let hash = 0; for (let i = 0; i < str.length; i++) { @@ -1416,7 +1684,7 @@ function stringToHexColor(str) { return color; } -function stringToHlsColor(str) { +function stringToHslColor(str) { let stringUniqueHash = [...str].reduce((acc, char) => { return char.charCodeAt(0) + ((acc << 5) - acc); }, 0); |