diff options
-rw-r--r-- | Zotlabs/Lib/Activity.php | 2 | ||||
-rw-r--r-- | Zotlabs/Lib/ThreadItem.php | 7 | ||||
-rw-r--r-- | Zotlabs/Module/Xref.php | 26 | ||||
-rw-r--r-- | Zotlabs/Widget/Album.php | 3 | ||||
-rw-r--r-- | Zotlabs/Widget/Portfolio.php | 4 | ||||
-rw-r--r-- | boot.php | 2 | ||||
-rw-r--r-- | include/conversation.php | 6 | ||||
-rw-r--r-- | include/items.php | 12 | ||||
-rw-r--r-- | include/js_strings.php | 1 | ||||
-rw-r--r-- | view/css/conversation.css | 47 | ||||
-rw-r--r-- | view/js/main.js | 221 | ||||
-rw-r--r-- | view/tpl/conv_frame.tpl | 3 | ||||
-rw-r--r-- | view/tpl/conv_item.tpl | 10 | ||||
-rw-r--r-- | view/tpl/jot-header.tpl | 64 | ||||
-rw-r--r-- | view/tpl/js_strings.tpl | 1 |
15 files changed, 252 insertions, 157 deletions
diff --git a/Zotlabs/Lib/Activity.php b/Zotlabs/Lib/Activity.php index 03b63a17b..ebf866a3b 100644 --- a/Zotlabs/Lib/Activity.php +++ b/Zotlabs/Lib/Activity.php @@ -2277,7 +2277,7 @@ class Activity { $s['edited'] = $s['created']; $s['title'] = (($response_activity) ? EMPTY_STR : html2plain($content['name'])); - $s['summary'] = html2plain($content['summary']); + $s['summary'] = (($content['summary'] !== $content['content']) ? html2plain($content['summary']) : ''); $s['body'] = ((self::bb_content($content, 'bbcode') && (!$response_activity)) ? self::bb_content($content, 'bbcode') : self::bb_content($content, 'content')); // peertube quirks diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index dee64caac..46fe6d815 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -353,13 +353,14 @@ class ThreadItem { $contact = App::$contacts[$item['author_xchan']]; } + $blog_mode = $this->get_display_mode() === 'list'; $load_more = false; $load_more_title = ''; $comments_total_percent = 0; - if ($conv->comments_total > $conv->comments_loaded) { + if (($conv->comments_total > $conv->comments_loaded) || ($blog_mode && $conv->comments_total > 3)) { // provide a load more comments button $load_more = true; - $load_more_title = sprintf(t('Load the next few of total %d replies'), $conv->comments_total); + $load_more_title = sprintf(t('Load the next few of total %d comments'), $conv->comments_total); $comments_total_percent = round(100 * 3 / $conv->comments_total); } @@ -491,7 +492,7 @@ class ThreadItem { 'tentativeaccept' => intval($item['observer_tentativeaccept_count'] ?? 0) ], 'threaded' => $this->threaded, - 'blog_mode' => $this->get_display_mode() === 'list', + 'blog_mode' => $blog_mode, 'collapse_comments' => t('show less'), 'expand_comments' => $this->threaded ? t('show more') : t('show all'), 'load_more' => $load_more, diff --git a/Zotlabs/Module/Xref.php b/Zotlabs/Module/Xref.php deleted file mode 100644 index e9d494da4..000000000 --- a/Zotlabs/Module/Xref.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php -namespace Zotlabs\Module; - - -class Xref extends \Zotlabs\Web\Controller { - - function init() { - // Sets a referral URL using an xchan directly - // Link format: example.com/xref/[xchan]/[TargetURL] - // Target URL is optional. - // Cookie lasts 24 hours to survive a browser restart. Contains no personal - // information at all - just somebody else's xchan. - $referrer = argv(1); - $expire=time()+60*60*2; - $path = 'xref'; - setcookie($path, $referrer, $expire, "/"); - $url = ''; - - if (argc() > 2) - $url = argv(2); - - goaway (z_root() . '/' . $url); - - } - -} diff --git a/Zotlabs/Widget/Album.php b/Zotlabs/Widget/Album.php index f1fa69182..667952360 100644 --- a/Zotlabs/Widget/Album.php +++ b/Zotlabs/Widget/Album.php @@ -59,6 +59,9 @@ class Album { //edit album name $album_edit = null; + $ph = photo_factory(''); + $phototypes = $ph->supportedTypes(); + $photos = array(); if($r) { $twist = 'rotright'; diff --git a/Zotlabs/Widget/Portfolio.php b/Zotlabs/Widget/Portfolio.php index bde1c7d6a..1c9dc162a 100644 --- a/Zotlabs/Widget/Portfolio.php +++ b/Zotlabs/Widget/Portfolio.php @@ -66,6 +66,10 @@ class Portfolio { //edit album name $album_edit = null; + + $ph = photo_factory(''); + $phototypes = $ph->supportedTypes(); + $photos = array(); if($r) { $twist = 'rotright'; @@ -70,7 +70,7 @@ require_once('include/security.php'); define('PLATFORM_NAME', 'hubzilla'); -define('STD_VERSION', '10.3.59'); +define('STD_VERSION', '10.3.71'); define('ZOT_REVISION', '6.0'); define('DB_UPDATE_VERSION', 1263); diff --git a/include/conversation.php b/include/conversation.php index bd1b7f863..07e4df088 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -1426,7 +1426,7 @@ function get_responses($response_verbs, $item) { } $ret[$v]['count'] = $item[$v . '_count'] ?? 0; - $ret[$v]['button'] = get_response_button_text($v, $ret[$v]['count']); + $ret[$v]['button'] = get_response_button_text($v, $ret[$v]['count'], $item['item_thread_top']); } //logger('ret: ' . print_r($ret,true)); @@ -1434,7 +1434,7 @@ function get_responses($response_verbs, $item) { return $ret; } -function get_response_button_text($v, $count = 0) { +function get_response_button_text($v, $count = 0, $top_level = 0) { switch($v) { case 'like': return ['label' => tt('Like','Likes',$count,'noun'), 'icon' => 'hand-thumbs-up', 'class' => 'like', 'action' => 'dolike']; @@ -1446,7 +1446,7 @@ function get_response_button_text($v, $count = 0) { return ['label' => tt('Dislike','Dislikes',$count,'noun'), 'icon' => 'hand-thumbs-down', 'class' => 'dislike', 'action' => 'dolike']; break; case 'comment': - return ['label' => tt('Reply','Replies',$count,'noun'), 'icon' => 'chat', 'class' => 'comment', 'action' => '']; + return ['label' => (($top_level) ? tt('Comment', 'Comments' ,$count, 'noun') : tt('Reply', 'Replies', $count, 'noun')), 'icon' => 'chat', 'class' => 'comment', 'action' => '']; break; case 'accept': return ['label' => tt('Attending','Attending',$count,'noun'), 'icon' => 'calendar-check', 'class' => 'accept', 'action' => 'dolike']; diff --git a/include/items.php b/include/items.php index 021d743a5..9e448e39a 100644 --- a/include/items.php +++ b/include/items.php @@ -5390,7 +5390,7 @@ function item_by_item_id(int $id, int $parent): array WHERE item.id = %d AND item.uid = %d - AND item.verb NOT IN ('Like', 'Dislike', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'Add', 'Remove', 'Follow', 'Ignore') + AND item.verb IN ('Create', 'Update', 'EmojiReact') $item_normal_sql", intval($id), intval(local_channel()) @@ -5474,7 +5474,7 @@ function items_by_parent_ids(array $parents, null|array $thr_parents = null, str ROW_NUMBER() OVER (PARTITION BY item.parent ORDER BY item.created DESC) AS rn FROM item WHERE item.parent IN ($ids) - AND item.verb NOT IN ('Like', 'Dislike', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'Add', 'Remove', 'Follow', 'Ignore') + AND item.verb IN ('Create', 'Update', 'EmojiReact') AND item.item_thread_top = 0 $thr_parent_sql $permission_sql @@ -5523,7 +5523,7 @@ function item_reaction_sql(string $ids, string $permission_sql = '', string $joi $thread_allow = ((local_channel()) ? PConfig::Get(local_channel(), 'system', 'thread_allow', true) : Config::Get('system', 'thread_allow', true)); if ($thread_allow) { - $verbs['comment'] = ['Create', 'Update']; + $verbs['comment'] = ['Create', 'Update', 'EmojiReact']; } $cte = ''; @@ -5604,7 +5604,7 @@ function items_by_thr_parent(string $mid, int $parent, int|null $offset = null): $order_sql = "ORDER BY item.created"; if (isset($offset)) { - $order_sql = "ORDER BY item.created DESC LIMIT 3 OFFSET $offset"; + $order_sql = "ORDER BY item.created DESC, item.received DESC LIMIT 3 OFFSET $offset"; } $owner_uid = intval($parent_item[0]['uid']); @@ -5626,7 +5626,7 @@ function items_by_thr_parent(string $mid, int $parent, int|null $offset = null): WHERE item.thr_parent = '%s' AND item.uid = %d - AND item.verb NOT IN ('Like', 'Dislike', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'Add', 'Remove', 'Follow', 'Ignore') + AND item.verb IN ('Create', 'Update', 'EmojiReact') AND item.item_thread_top = 0 $item_normal_sql $order_sql", @@ -5653,7 +5653,7 @@ function items_by_thr_parent(string $mid, int $parent, int|null $offset = null): WHERE item.thr_parent = '%s' AND item.uid = %d - AND item.verb NOT IN ('Like', 'Dislike', 'Announce', 'Accept', 'Reject', 'TentativeAccept', 'Add', 'Remove', 'Follow', 'Ignore') + AND item.verb IN ('Create', 'Update', 'EmojiReact') AND item.item_thread_top = 0 $permission_sql $item_normal_sql diff --git a/include/js_strings.php b/include/js_strings.php index 1772cb66b..6f2ffd351 100644 --- a/include/js_strings.php +++ b/include/js_strings.php @@ -36,6 +36,7 @@ function js_strings() { '$pinned' => t('Pinned'), '$pin_item' => t('Pin to the top'), '$unpin_item' => t('Unpin from the top'), + '$dblclick_to_exit_zoom' => t('Double click to exit zoom'), // translatable prefix and suffix strings for jquery.timeago - // using the defaults set below if left untranslated, empty strings if diff --git a/view/css/conversation.css b/view/css/conversation.css index b1b405c9b..3d206797f 100644 --- a/view/css/conversation.css +++ b/view/css/conversation.css @@ -210,7 +210,7 @@ a.wall-item-name-link { transform: scale(1) translateY(0); } -.item-highlight:after { +.item-highlight::after { content: ''; position: absolute; pointer-events: none; @@ -218,7 +218,7 @@ a.wall-item-name-link { width: 100%; top: 0; bottom: 0; - margin: var(--bs-border-radius) 0 var(--bs-border-radius) 0; +/* margin: var(--bs-border-radius) 0 var(--bs-border-radius) 0; */ } .item-highlight-fade { @@ -236,7 +236,7 @@ a.wall-item-name-link { padding-left: .75rem; } -.item-indent:before { +.item-indent::before { content: ''; position: absolute; height: 100%; @@ -253,6 +253,13 @@ a.wall-item-name-link { opacity: 0.5; } +.wall-item-comment.collapsed::before { + content: '\2212'; + position: absolute; + left: .85rem; + top: .45rem; +} + .wall-item-backdrop::before { content: ''; position: absolute; @@ -261,15 +268,45 @@ a.wall-item-name-link { z-index: 1; } - .wall-item-expanded { position: relative; - border: 1px solid var(--bs-danger); border-radius: var(--bs-border-radius); background-color: var(--bs-body-bg); z-index: 2; } +.wall-item-expanded::after { + content: ''; + position: absolute; + pointer-events: none; + box-shadow: 0 0 0 1px var(--bs-border-color); + border-radius: var(--bs-border-radius); + width: 100%; + top: 0; + bottom: 0; +} + +.wall-item-expanded::before { + content: var(--hz-wall-item-expanded-before-content, "");; + position: absolute; + top: 0; + left: 50%; + transform: translate(-50%, -50%); + background: var(--bs-info-bg-subtle); + color: var(--bs-info-text-emphasis); + border-radius: var(--bs-border-radius-pill); + padding: 0.35em 0.65em; + font-size: 0.6em; + font-weight: 700; + z-index: 3; + text-transform: uppercase; +} + +.load-more:hover .load-more-dots { + background: var(--bs-light-bg-subtle); + padding: 0 0.35em; +} + /* comment_item */ diff --git a/view/js/main.js b/view/js/main.js index 8e369f62a..ff8e61ec8 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -90,121 +90,171 @@ $(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; + // Only handle clicks on .wall-item-reaction or its children + const target = event.target.closest('.wall-item-reaction'); + if (!target) return; + + let doRequest = true; + const isUserClick = event.isTrusted; + + // Destructure relevant data attributes + const { itemId: id, itemMid: mid, itemParent: parentId, itemUuid: uuid, itemVerb: verb } = target.dataset; + const isCommentBtn = target.classList.contains('wall-item-comment'); + + if (isCommentBtn) { + if (id === parentId) { + // Handle blog mode + target.classList.add('disabled'); + document.getElementById(`load-more-progress-wrapper-${id}`).classList.remove('d-none'); + document.getElementById(`load-more-${id}`).classList.remove('d-none') + request(id, mid, 'load', parentId, uuid, isUserClick); + return; + } - if (targetElement.classList.contains('wall-item-comment')) { + // Get relevant DOM elements 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`); + const parentWrapper = document.getElementById(`thread-wrapper-${parentId}`); + const subThreadWrapper = document.getElementById(`wall-item-sub-thread-wrapper-${id}`); + const parentSubThreadWrapper = document.getElementById(`wall-item-sub-thread-wrapper-${parentId}`); + + // Query related sub-thread and highlight elements + const parentIndentedThreads = document.querySelectorAll(`#wall-item-sub-thread-wrapper-${parentId} .wall-item-sub-thread-wrapper.item-indent`); + + let ancestorIds = []; + + doRequest = !subThreadWrapper.children.length; - subWrapper.style.setProperty('--hz-item-indent', stringToHslColor(uuid)); + // Set visual styles using UUID + subThreadWrapper.style.setProperty('--hz-item-indent', stringToHslColor(uuid)); threadWrapper.style.setProperty('--hz-item-highlight', stringToHslColor(uuid)); + threadWrapper.style.setProperty('--hz-wall-item-expanded-before-content', '"' + aStr.dblclick_to_exit_zoom + '"'); - document.querySelectorAll('.thread-wrapper.item-highlight').forEach(el => { - el.classList.remove('item-highlight'); - }); + // Clear previous highlights + parentSubThreadWrapper.querySelectorAll('.thread-wrapper.item-highlight').forEach(el => el.classList.remove('item-highlight')); - if (!parentSubWrapperIndented && userClick) { + if (isUserClick && parentIndentedThreads.length === 0 && !subThreadWrapper.children.length) { + // Handle first-time expansion and highlighting but not for toplevels (blog mode) threadWrapper.classList.add('item-highlight'); - } - else { - subWrapper.classList.add('item-indent'); - } + } else { + // Handle indentation and zooming + let ancestor = subThreadWrapper.parentElement; + ancestorIds.push(ancestor.id.slice(15)); // thread-wrapper-1234 - if (userClick && targetElement.classList.contains('expanded')) { - document.querySelectorAll('.thread-wrapper.item-highlight').forEach(el => { - el.classList.remove('item-highlight'); - }); + while (ancestor) { + if (ancestor.classList.contains('item-indent') && ancestor.classList.contains('wall-item-sub-thread-wrapper')) { + ancestorIds.push(ancestor.parentElement.id.slice(15)); + } + ancestor = ancestor.parentElement; + } + + ancestorIds.reverse(); - parentSubWrapper.forEach(el => { - if (el.children.length > 0) { + if (ancestorIds.length > 3) { + // Handle zooming in + let firstWrapper = document.getElementById('thread-wrapper-' + ancestorIds[0]); + let firstSubWrapper = document.getElementById('wall-item-sub-thread-wrapper-' + ancestorIds[0]); + + firstWrapper.querySelector('.wall-item-comment').classList.remove('indented'); + firstWrapper.classList.remove('wall-item-expanded', 'shadow'); + firstSubWrapper.classList.remove('item-indent'); + + let newFirstWrapper = document.getElementById('thread-wrapper-' + ancestorIds[1]) + let newFirstSubWrapper = document.getElementById('wall-item-sub-thread-wrapper-' + ancestorIds[1]) + + newFirstWrapper.classList.add('wall-item-expanded', 'shadow'); + parentWrapper.classList.add('wall-item-backdrop'); + + // Exit zoom on double-click + newFirstWrapper.addEventListener('dblclick', function() { + parentWrapper.querySelectorAll('.wall-item-comment.indented').forEach(el => el.classList.remove('indented')); + parentWrapper.querySelectorAll('.wall-item-comment.collapsed').forEach(el => el.classList.remove('collapsed')); + parentWrapper.classList.remove('wall-item-backdrop'); + parentWrapper.querySelectorAll('.wall-item-sub-thread-wrapper.item-indent').forEach(el => el.classList.remove('item-indent')); + parentWrapper.querySelectorAll('.wall-item-sub-thread-wrapper.d-none').forEach(el => el.classList.remove('d-none')); + parentWrapper.querySelectorAll('.thread-wrapper.wall-item-expanded').forEach(el => el.classList.remove('wall-item-expanded', 'shadow')); + }, { once: true }); + } + + // Toggle sub-thread visibility if indented + if (isUserClick && target.classList.contains('indented')) { + doRequest = false; + subThreadWrapper.classList.toggle('d-none'); + target.classList.toggle('collapsed'); + } + + // Indenting of already expanded but flattened items + if (isUserClick && subThreadWrapper.classList.contains('item-expanded') && !subThreadWrapper.classList.contains('item-indent')) { + doRequest = false; + + threadWrapper.querySelectorAll('.wall-item-sub-thread-wrapper.item-expanded').forEach(function (el, i) { el.classList.add('item-indent'); - } - }); - targetElement.classList.add('disabled'); - return; - } + el.querySelectorAll('.wall-item-comment.expanded').forEach(function (el, i) { + el.classList.add('collapsed', 'indented'); + }); - targetElement.classList.add('expanded'); + // Collapse everything below the first level + if (i > 0) { + el.classList.add('d-none'); + } + }); + } + + // Indent the subthread + subThreadWrapper.classList.add('item-indent', 'item-expanded'); - if (!userClick) { - targetElement.classList.add('disabled'); + // Mark as indented after visibility toggle + target.classList.add('indented'); } + + // Mark as expanded + target.classList.add('expanded'); } - request(id, mid, verb, parent, uuid, userClick); + if (doRequest) { + request(id, mid, verb, parentId, uuid, isUserClick); + } }); 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 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'); - }); + subWrapper.innerHTML = ''; parent.querySelectorAll('.thread-wrapper.wall-item-expanded').forEach(el => { - el.classList.remove('wall-item-expanded'); + el.classList.remove('wall-item-expanded', 'shadow'); }); - 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)); - } + parent.querySelectorAll('.wall-item-sub-thread-wrapper.item-indent').forEach(el => { + el.classList.remove('item-indent'); + }); - autoExpand(id); + wrapper.classList.add('wall-item-expanded', 'shadow'); - // Close and reset if dbl clicked - wrapper.addEventListener('dblclick', function(event) { + parent.classList.add('wall-item-backdrop'); + // Exit zoom on double-click + wrapper.addEventListener('dblclick', function() { + parent.querySelectorAll('.wall-item-comment.indented').forEach(el => el.classList.remove('indented')); + parent.querySelectorAll('.wall-item-comment.collapsed').forEach(el => el.classList.remove('collapsed')); 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'); - }) + parent.querySelectorAll('.wall-item-sub-thread-wrapper.item-indent').forEach(el => el.classList.remove('item-indent')); + parent.querySelectorAll('.wall-item-sub-thread-wrapper.d-none').forEach(el => el.classList.remove('d-none')); + parent.querySelectorAll('.thread-wrapper.wall-item-expanded').forEach(el => el.classList.remove('wall-item-expanded', 'shadow')); + }, { once: true }); + autoExpand(id); }); // @hilmar |-> @@ -877,6 +927,9 @@ function updateConvItems(mode, data) { if (!elem.parentNode.parentNode.classList.contains('toplevel_item')) { elem.parentNode.parentNode.classList.add('item-highlight'); document.documentElement.style.setProperty('--hz-item-highlight', stringToHslColor(JSON.parse(elem.parentNode.parentNode.dataset.b64mids)[0])); + // Mark the comment button at the parent expanded + // TODO: should do that for all comments that have replies expanded + elem.parentNode.parentNode.querySelector('.wall-item-comment').classList.add('expanded'); } } @@ -1600,6 +1653,7 @@ const autoExpand = (function () { async function autoExpand(id) { const loading = document.getElementById('like-rotator-' + id); + let iteration = 0; const maxIterations = 3; clickedElements.clear(); @@ -1611,6 +1665,7 @@ const autoExpand = (function () { if (!clickedElements.has(initBtn)) { initBtn.click(); clickedElements.add(initBtn); + iteration++; } // Step 2: Loop until no new buttons are found @@ -1618,9 +1673,6 @@ const autoExpand = (function () { 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; @@ -1628,12 +1680,6 @@ const autoExpand = (function () { // 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) { @@ -1647,14 +1693,13 @@ const autoExpand = (function () { // Wait between iterations to allow UI to update if (newButtonsFound) { + iteration++; await new Promise(res => setTimeout(res, 700)); } - iteration++; - } while (newButtonsFound && iteration < maxIterations); - console.log('All replies loaded!'); + console.log('Replies loaded!'); loading.style.display = 'none'; @@ -1688,7 +1733,7 @@ function stringToHslColor(str) { let stringUniqueHash = [...str].reduce((acc, char) => { return char.charCodeAt(0) + ((acc << 5) - acc); }, 0); - return `hsl(${stringUniqueHash % 360}, 95%, 70%)`; + return `hsl(${stringUniqueHash % 360}, 65%, 65%)`; } function dolike(ident, verb) { diff --git a/view/tpl/conv_frame.tpl b/view/tpl/conv_frame.tpl index b40585a46..0acf7b24c 100644 --- a/view/tpl/conv_frame.tpl +++ b/view/tpl/conv_frame.tpl @@ -30,6 +30,9 @@ <div class="modal-body list-group" id="reactions_body"> {{$wait}} </div> + <div class="ps-3 pe-3" id="reactions_extra_top"></div> + <div class="ps-3 pe-3" id="reactions_extra_middle"></div> + <div class="ps-3 pe-3" id="reactions_extra_bottom"></div> </div><!-- /.modal-content --> </div><!-- /.modal-dialog --> </div><!-- /.modal --> diff --git a/view/tpl/conv_item.tpl b/view/tpl/conv_item.tpl index 39634f429..c97c53c41 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-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}}"> + <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}}" data-item-reaction-count="{{$response.count}}"> <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}} @@ -222,12 +222,12 @@ </div> </div> {{if $item.thread_level == 1}} - {{if $item.toplevel && $item.load_more && $item.threaded && !$item.blog_mode}} - <div id="load-more-progress-wrapper-{{$item.id}}" class="progress " role="progressbar" aria-valuenow="{{$item.comments_total_percent}}" aria-valuemin="0" aria-valuemax="100" style="height: 1px"> + {{if $item.toplevel && $item.load_more && $item.threaded}} + <div id="load-more-progress-wrapper-{{$item.id}}" class="progress{{if $item.blog_mode}} d-none{{/if}}" role="progressbar" aria-valuenow="{{$item.comments_total_percent}}" aria-valuemin="0" aria-valuemax="100" style="height: 1px"> <div id="load-more-progress-{{$item.id}}" class="progress-bar bg-info" style="width: {{$item.comments_total_percent}}%; margin-left: auto; margin-right: auto;" data-comments-total="{{$item.comments_total}}"></div> </div> - <div id="load-more-{{$item.id}}" class="text-center text-secondary cursor-pointer" title="{{$item.load_more_title}}" onclick="request(0, '{{$item.rawmid}}', 'load', {{$item.parent}}, ''); return false;"> - <span id="load-more-dots-{{$item.id}}" class=""><span class="dot-1">-</span> <span class="dot-2">-</span> <span class="dot-3">-</span></span> + <div id="load-more-{{$item.id}}" class="load-more text-center text-secondary cursor-pointer{{if $item.blog_mode}} d-none{{/if}}" title="{{$item.load_more_title}}" onclick="request(0, '{{$item.rawmid}}', 'load', {{$item.parent}}, ''); return false;"> + <span id="load-more-dots-{{$item.id}}" class="load-more-dots rounded"><span class="dot-1">-</span> <span class="dot-2">-</span> <span class="dot-3">-</span></span> </div> {{/if}} <div id="wall-item-sub-thread-wrapper-{{$item.id}}" class="wall-item-sub-thread-wrapper"> diff --git a/view/tpl/jot-header.tpl b/view/tpl/jot-header.tpl index b01fa6609..b9b8b3012 100644 --- a/view/tpl/jot-header.tpl +++ b/view/tpl/jot-header.tpl @@ -11,6 +11,8 @@ var activeCommentID = 0; var activeCommentText = ''; + var isModalAction = false; + var postSaveTimer = null; function initEditor(cb){ @@ -414,19 +416,41 @@ // Fetch the photo album list getPhotoAlbumList(); - // Remove any existing click event listeners on the modal body - const modalBodyAlbumDialog = document.getElementById('embedPhotoModalBodyAlbumDialog'); - modalBodyAlbumDialog.replaceWith(modalBodyAlbumDialog.cloneNode(true)); // This effectively removes all event listeners + if (activeCommentID) { + const modalEl = document.getElementById('reactions'); + isModalAction = modalEl.classList.contains('show'); + } - // Show the modal - const modalEl = document.getElementById('embedPhotoModal'); - const modal = new bootstrap.Modal(modalEl); - modal.show(); + if (isModalAction) { + // Remove any existing click event listeners on the modal body + const modalBodyAlbumDialog = document.getElementById('reactions_extra_middle'); + modalBodyAlbumDialog.replaceWith(modalBodyAlbumDialog.cloneNode(true)); // This effectively removes all event listeners - // Reset activeCommentID when the modal is closed - modalEl.addEventListener('hide.bs.modal', event => { - activeCommentID = 0; - }); + const modalEl = document.getElementById('reactions'); + + // Reset activeCommentID when the modal is closed + modalEl.addEventListener('hide.bs.modal', event => { + activeCommentID = 0; + isModalAction = false; + document.getElementById('reactions_extra_middle').innerHTML = ''; + document.getElementById('reactions_extra_top').innerHTML = ''; + }); + } + else { + // Remove any existing click event listeners on the modal body + const modalBodyAlbumDialog = document.getElementById('embedPhotoModalBodyAlbumDialog'); + modalBodyAlbumDialog.replaceWith(modalBodyAlbumDialog.cloneNode(true)); // This effectively removes all event listeners + + // Show the modal + const modalEl = document.getElementById('embedPhotoModal'); + const modal = new bootstrap.Modal(modalEl); + modal.show(); + + // Reset activeCommentID when the modal is closed + modalEl.addEventListener('hide.bs.modal', event => { + activeCommentID = 0; + }); + } }; const choosePhotoFromAlbum = (album) => { @@ -442,15 +466,16 @@ .then(data => { if (data.status) { - const modalLabel = document.getElementById('embedPhotoModalLabel'); - const modalBody = document.getElementById('embedPhotoModalBodyAlbumDialog'); + const modalLabel = isModalAction ? document.getElementById('reactions_extra_top') : document.getElementById('embedPhotoModalLabel'); + const modalBody = isModalAction ? document.getElementById('reactions_extra_middle') : document.getElementById('embedPhotoModalBodyAlbumDialog'); - modalLabel.innerHTML = '{{$modalchooseimages}}'; + modalLabel.innerHTML = '<h3>{{$modalchooseimages}}</h3>'; modalBody.innerHTML = '<div><div class="nav nav-pills flex-column"><li class="nav-item"><a class="nav-link" href="#" onclick="initializeEmbedPhotoDialog(); return false;"><i class="bi bi-chevron-left"></i> {{$modaldiffalbum}}</a></li></div><br></div>'; modalBody.innerHTML += data.content; // Make sure the loaded script is executed const scripts = modalBody.querySelectorAll('script'); + scripts.forEach(script => { const scriptContent = script.textContent || script.innerText; eval(scriptContent); // Execute the script @@ -475,7 +500,6 @@ .then(ddata => { if (ddata.status) { addActiveEditorText(ddata.photolink); - preview_post(); } else { console.error("{{$modalerrorlink}}: " + ddata.errormsg); } @@ -502,10 +526,10 @@ .then(data => { if (data.status) { const albums = data.albumlist; - const modalLabel = document.getElementById('embedPhotoModalLabel'); - const modalBodyList = document.getElementById('embedPhotoModalBodyAlbumList'); + const modalLabel = isModalAction ? document.getElementById('reactions_extra_top') : document.getElementById('embedPhotoModalLabel'); + const modalBodyList = isModalAction ? document.getElementById('reactions_extra_middle') : document.getElementById('embedPhotoModalBodyAlbumList'); - modalLabel.innerHTML = '{{$modalchoosealbum}}'; + modalLabel.innerHTML = '<h3>{{$modalchoosealbum}}</h3>'; modalBodyList.innerHTML = '<ul class="nav nav-pills flex-column"></ul>'; albums.forEach(album => { @@ -551,7 +575,9 @@ textarea.value = currentText + data; textarea.focus(); textarea.click(); - preview_comment(activeCommentID); + if (!isModalAction) { + preview_comment(activeCommentID); + } } } else { addeditortext(data); diff --git a/view/tpl/js_strings.tpl b/view/tpl/js_strings.tpl index 2a374fdf8..0e438f450 100644 --- a/view/tpl/js_strings.tpl +++ b/view/tpl/js_strings.tpl @@ -36,6 +36,7 @@ 'pinned' : "{{$pinned}}", 'pin_item' : "{{$pin_item}}", 'unpin_item' : "{{$unpin_item}}", + 'dblclick_to_exit_zoom' : "{{$dblclick_to_exit_zoom}}", 'monthNames' : [ "{{$January}}","{{$February}}","{{$March}}","{{$April}}","{{$May}}","{{$June}}","{{$July}}","{{$August}}","{{$September}}","{{$October}}","{{$November}}","{{$December}}" ], 'monthNamesShort' : [ "{{$Jan}}","{{$Feb}}","{{$Mar}}","{{$Apr}}","{{$MayShort}}","{{$Jun}}","{{$Jul}}","{{$Aug}}","{{$Sep}}","{{$Oct}}","{{$Nov}}","{{$Dec}}" ], |