diff options
Diffstat (limited to 'view')
-rw-r--r-- | view/js/main.js | 191 | ||||
-rw-r--r-- | view/tpl/conv_item.tpl | 2 |
2 files changed, 179 insertions, 14 deletions
diff --git a/view/js/main.js b/view/js/main.js index f9ed5059c..1ace4b56b 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -29,6 +29,11 @@ var updateTimeout = []; const singlethread_modules = ['display', 'hq']; const redirect_modules = ['display', 'notify']; +let clickTimer = null; +const clickTimerDelay = 300; // Adjust as needed +let b64mids = []; + + var page_cache = {}; // take care of tab/window reloads on channel change @@ -87,6 +92,51 @@ $(document).ready(function() { } }); + document.addEventListener('click', function(event) { + const targetElement = event.target.closest('.wall-item-reaction'); + if (!targetElement) return; + + let userClick = event.isTrusted; + + clearTimeout(clickTimer); + clickTimer = setTimeout(() => { + 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; + + request(id, mid, verb, parent, uuid, userClick); + + }, clickTimerDelay); + }); + + document.addEventListener('dblclick', function(event) { + const targetElement = event.target.closest('.wall-item-reaction.wall-item-comment'); + if (!targetElement) return; + + clearTimeout(clickTimer); + + const id = targetElement.dataset.itemId; + const uuid = targetElement.dataset.itemUuid; + + const loading = document.getElementById('like-rotator-' + id); + loading.style.display = 'block'; + + document.querySelectorAll('.thread-wrapper.item-highlight').forEach(el => { + el.classList.remove('item-highlight'); + el.style.boxShadow = ''; + }); + + 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)); + } + + autoExpand(id); + }); + // @hilmar |-> if ( typeof(window.tao) == 'undefined' ) { window.tao = {}; @@ -1292,7 +1342,7 @@ 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) { if (verb === 'load') { const dots = document.getElementById('load-more-dots-' + parent); @@ -1341,23 +1391,29 @@ function request(id, mid, verb, parent, uuid) { } const loading = document.getElementById('like-rotator-' + id); - loading.style.display = 'block'; + + if (userClick) { + loading.style.display = 'block'; + } if (verb === 'comment') { - if (singlethread_modules.indexOf(module) !== -1) { + if (userClick && singlethread_modules.indexOf(module) !== -1) { let stateObj = { b64mid: uuid }; history.pushState(stateObj, '', module + '/' + uuid); } - document.querySelectorAll('.thread-wrapper.item-highlight').forEach(el => { - el.classList.remove('item-highlight'); - el.style.boxShadow = ''; - }); - const wrapper = document.getElementById('thread-wrapper-' + id); - if (!wrapper.classList.contains('toplevel_item')) { + const subWrapper = document.getElementById('wall-item-sub-thread-wrapper-' + id); + + if (userClick && !wrapper.classList.contains('toplevel_item')) { + document.querySelectorAll('.thread-wrapper.item-highlight').forEach(el => { + el.classList.remove('item-highlight'); + el.style.boxShadow = ''; + }); + wrapper.classList.add('item-highlight'); + subWrapper.classList.remove('ms-3'); document.documentElement.style.setProperty('--hz-item-highlight', stringToHlsColor(uuid)); } @@ -1366,7 +1422,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); @@ -1376,11 +1431,16 @@ function request(id, mid, verb, parent, uuid) { imagesLoaded(doc.querySelectorAll('.wall-item-body img'), function () { 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')); + const icon = document.querySelector('#wall-item-comment-' + id + ' i'); + icon.classList.replace('bi-chat-dots', 'bi-chat'); + + if (userClick) { + loading.style.display = 'none'; + document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids })); + document.dispatchEvent(new Event('hz:sse_bs_counts')); + } }); }) @@ -1448,6 +1508,111 @@ function injectWithAnimation(containerId, parsedDoc, overwrite = false) { } } +const autoExpand = (function () { + const clickedElements = new Set(); // Stores clicked button references + + function waitForElement(selector, timeout = 3000) { + 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) { + 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 + const commentSelector = `#wall-item-sub-thread-wrapper-${id} .wall-item-comment`; + let newButtonsFound; + + const subs = `#wall-item-sub-thread-wrapper-${id}, #wall-item-sub-thread-wrapper-${id} .wall-item-sub-thread-wrapper`; + + do { + newButtonsFound = false; + + // Wait for any comment to appear + await waitForElement(commentSelector); + + document.querySelectorAll(subs).forEach(el => { + el.classList.add('ms-3'); + el.classList.add('border-start'); + }); + + const expandButtons = document.querySelectorAll(commentSelector); + + + for (const btn of expandButtons) { + if (!clickedElements.has(btn)) { + // Wait between iterations to allow UI to update + + await new Promise(res0 => setTimeout(res0, 500)); + 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(res1 => setTimeout(res1, 500)); + } + + } while (newButtonsFound); + + console.log('All replies loaded!'); + + const loading = document.getElementById('like-rotator-' + id); + loading.style.display = 'none'; + + document.dispatchEvent(new CustomEvent('hz:sse_setNotificationsStatus', { detail: b64mids })); + document.dispatchEvent(new Event('hz:sse_bs_counts')); + + } catch (error) { + console.error("autoExpand failed:", error.message); + } + } + + return autoExpand; +})(); + + function stringToHexColor(str) { let hash = 0; for (let i = 0; i < str.length; i++) { diff --git a/view/tpl/conv_item.tpl b/view/tpl/conv_item.tpl index b201a4748..007f48241 100644 --- a/view/tpl/conv_item.tpl +++ b/view/tpl/conv_item.tpl @@ -111,7 +111,7 @@ <div class="wall-item-tools-left hstack gap-1" id="wall-item-tools-left-{{$item.id}}"> {{foreach $item.responses as $verb=>$response}} {{if !($verb == 'comment' && (($item.toplevel && !$item.blog_mode) || $response.count == 0))}} - <button type="button" title="{{$response.count}} {{$response.button.label}}" class="btn btn-sm btn-link{{if !$item.observer_activity.$verb}} link-secondary{{/if}} wall-item-{{$response.button.class}}" onclick="request({{$item.id}}, '{{$item.rawmid}}', '{{$verb}}', {{$item.parent}}, '{{$item.mid}}'); return false;" id="wall-item-{{$verb}}-{{$item.id}}"> + <button type="button" title="{{$response.count}} {{$response.button.label}}" class="btn btn-sm btn-link{{if !$item.observer_activity.$verb}} link-secondary{{/if}} wall-item-reaction wall-item-{{$response.button.class}}" id="wall-item-{{$verb}}-{{$item.id}}" data-item-id="{{$item.id}}" data-item-mid="{{$item.rawmid}}" data-item-verb="{{$verb}}" data-item-parent="{{$item.parent}}" data-item-uuid="{{$item.mid}}"> <i class="bi bi-{{$response.button.icon}} generic-icons"></i>{{if $response.count}}<span style="display: inline-block; margin-top: -.25rem;" class="align-top">{{$response.count}}</span>{{/if}} </button> {{/if}} |