From 66d661561dbfcecddc82179d2bb5085636a75d55 Mon Sep 17 00:00:00 2001 From: epicsam123 <92618898+epicsam123@users.noreply.github.com> Date: Sat, 1 Feb 2025 21:07:52 -0500 Subject: [PATCH 1/5] feature: include reply count when loading replies --- assets/js/comments.js | 90 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 13 deletions(-) diff --git a/assets/js/comments.js b/assets/js/comments.js index 35ffa96e..986eca9b 100644 --- a/assets/js/comments.js +++ b/assets/js/comments.js @@ -1,5 +1,13 @@ var video_data = JSON.parse(document.getElementById('video_data').textContent); +var isRTL = (() => { + var ltrChars = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF'+'\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF', + rtlChars = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC', + rtlDirCheck = new RegExp('^[^'+ltrChars+']*['+rtlChars+']'); + + return rtlDirCheck.test(video_data.hide_replies_text); +})(); + var spinnerHTML = '

'; var spinnerHTMLwithHR = spinnerHTML + '
'; @@ -65,7 +73,7 @@ function get_youtube_comments() { '&thin_mode=' + video_data.preferences.thin_mode; if (video_data.ucid) { - url += '&ucid=' + video_data.ucid + url += '&ucid=' + video_data.ucid; } var onNon200 = function (xhr) { comments.innerHTML = fallback; }; @@ -119,13 +127,45 @@ function get_youtube_comments() { }); } +function format_count_load_more(content, current_count, total_count) { + var load_more_end_str = content.split('data-load-more'); + if (load_more_end_str.length === 1) + return [content, false]; // no Load More button, return false for has_more_replies + load_more_end_str = load_more_end_str[1].split('\n')[0]; // ' >("Load more" translated string)' + var slice_index = content.indexOf(load_more_end_str) + load_more_end_str.length - 4; // backtrace + var num_remaining = total_count - current_count; + return [ + // More replies may have been added since initally loading parent comment + content.slice(0, slice_index) + ' (+' + (num_remaining > 0 ? num_remaining : '?') + ')' + content.slice(slice_index), + true // Load More button present, return true for has_more_replies + ]; +} + +function format_count_toggle_replies_button(toggle_reply_button, current_count, total_count, has_more_replies) { + if (has_more_replies) { + if (current_count >= total_count) total_count = '?'; + } else { + // Accept the final current count as the total (comments may have been added or removed since loading) + total_count = current_count; + } + + if (isRTL) [current_count, total_count] = [total_count, current_count]; + ['data-sub-text', 'data-inner-text'].forEach(attr => { + toggle_reply_button.setAttribute(attr, + toggle_reply_button.getAttribute(attr) + .replace(/\(\d+\/\d+\)/, ' (' + current_count + '/' + total_count + ')') + ); + }); + toggle_reply_button.textContent = toggle_reply_button.getAttribute('data-sub-text'); +} + function get_youtube_replies(target, load_more, load_replies) { var continuation = target.getAttribute('data-continuation'); var body = target.parentNode.parentNode; - var fallback = body.innerHTML; + var originalHTML = body.innerHTML; body.innerHTML = spinnerHTML; - var baseUrl = video_data.base_url || '/api/v1/comments/'+ video_data.id + var baseUrl = video_data.base_url || '/api/v1/comments/' + video_data.id var url = baseUrl + '?format=html' + '&hl=' + video_data.preferences.locale + @@ -133,18 +173,35 @@ function get_youtube_replies(target, load_more, load_replies) { '&continuation=' + continuation; if (video_data.ucid) { - url += '&ucid=' + video_data.ucid + url += '&ucid=' + video_data.ucid; } if (load_replies) url += '&action=action_get_comment_replies'; helpers.xhr('GET', url, {}, { on200: function (response) { + var num_incoming_replies = response.contentHtml.split('channel-profile').length - 1; if (load_more) { body = body.parentNode.parentNode; - body.removeChild(body.lastElementChild); - body.insertAdjacentHTML('beforeend', response.contentHtml); + body.removeChild(body.lastElementChild); // Remove spinner + + var toggle_replies_button = body.parentNode.firstChild.firstChild; + if (!toggle_replies_button) { + body.insertAdjacentHTML('beforeend', response.contentHtml);  + return; + } + + var [prev_num_replies, num_total_replies] = toggle_replies_button.textContent.match(/\d+/g); + if (isRTL) [prev_num_replies, num_total_replies] = [num_total_replies, prev_num_replies]; + prev_num_replies -= 0; num_total_replies -= 0; // convert to integers + var num_current_replies = prev_num_replies + num_incoming_replies; + + var [newHTML, has_more_replies] = format_count_load_more(response.contentHtml, num_current_replies, num_total_replies); + format_count_toggle_replies_button(toggle_replies_button, num_current_replies, num_total_replies, has_more_replies); + + body.insertAdjacentHTML('beforeend', newHTML); } else { - body.removeChild(body.lastElementChild); + // loads only once for each comment when first opening their replies + body.removeChild(body.lastElementChild); // Remove spinner var p = document.createElement('p'); var a = document.createElement('a'); @@ -152,23 +209,30 @@ function get_youtube_replies(target, load_more, load_replies) { a.href = 'javascript:void(0)'; a.onclick = hide_youtube_replies; - a.setAttribute('data-sub-text', video_data.hide_replies_text); - a.setAttribute('data-inner-text', video_data.show_replies_text); - a.textContent = video_data.hide_replies_text; + + var num_total_replies = originalHTML.split('data-load-replies')[1].match(/\d+/)[0] - 0; + var num_replies_text = ' (0/0)'; // replace later + var hide_replies_text = video_data.hide_replies_text + num_replies_text; + a.setAttribute('data-sub-text', hide_replies_text); + a.setAttribute('data-inner-text', video_data.show_replies_text + num_replies_text); + a.textContent = hide_replies_text; var div = document.createElement('div'); - div.innerHTML = response.contentHtml; + var [newHTML, has_more_replies] = format_count_load_more(response.contentHtml, num_incoming_replies, num_total_replies); + format_count_toggle_replies_button(a, num_incoming_replies, num_total_replies, has_more_replies); + + div.innerHTML = newHTML; body.appendChild(p); body.appendChild(div); } }, onNon200: function (xhr) { - body.innerHTML = fallback; + body.innerHTML = originalHTML; }, onTimeout: function (xhr) { console.warn('Pulling comments failed'); - body.innerHTML = fallback; + body.innerHTML = originalHTML; } }); } \ No newline at end of file From 814267bd1c3e93f6c3c5788c42b732a5f2eac054 Mon Sep 17 00:00:00 2001 From: epicsam123 <92618898+epicsam123@users.noreply.github.com> Date: Mon, 3 Feb 2025 19:29:31 -0500 Subject: [PATCH 2/5] refactor condition, fallback -> originalHTML --- assets/js/comments.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/js/comments.js b/assets/js/comments.js index 986eca9b..79cc25ae 100644 --- a/assets/js/comments.js +++ b/assets/js/comments.js @@ -63,7 +63,7 @@ function show_youtube_replies(event) { function get_youtube_comments() { var comments = document.getElementById('comments'); - var fallback = comments.innerHTML; + var originalHTML = comments.innerHTML; comments.innerHTML = spinnerHTML; var baseUrl = video_data.base_url || '/api/v1/comments/'+ video_data.id @@ -76,7 +76,7 @@ function get_youtube_comments() { url += '&ucid=' + video_data.ucid; } - var onNon200 = function (xhr) { comments.innerHTML = fallback; }; + var onNon200 = function (xhr) { comments.innerHTML = originalHTML; }; if (video_data.params.comments[1] === 'youtube') onNon200 = function (xhr) {}; @@ -142,11 +142,11 @@ function format_count_load_more(content, current_count, total_count) { } function format_count_toggle_replies_button(toggle_reply_button, current_count, total_count, has_more_replies) { - if (has_more_replies) { - if (current_count >= total_count) total_count = '?'; - } else { + if (!has_more_replies) { // Accept the final current count as the total (comments may have been added or removed since loading) total_count = current_count; + } else if (current_count >= total_count) { + total_count = '?'; } if (isRTL) [current_count, total_count] = [total_count, current_count]; From d8ac5e47828943e3de2fa46ddc79296d4e40d2e9 Mon Sep 17 00:00:00 2001 From: epicsam123 <92618898+epicsam123@users.noreply.github.com> Date: Tue, 4 Feb 2025 21:45:22 -0500 Subject: [PATCH 3/5] condense locale extraction for isRTL --- assets/js/comments.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/assets/js/comments.js b/assets/js/comments.js index 79cc25ae..abe423ce 100644 --- a/assets/js/comments.js +++ b/assets/js/comments.js @@ -1,12 +1,7 @@ var video_data = JSON.parse(document.getElementById('video_data').textContent); -var isRTL = (() => { - var ltrChars = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF'+'\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF', - rtlChars = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC', - rtlDirCheck = new RegExp('^[^'+ltrChars+']*['+rtlChars+']'); - - return rtlDirCheck.test(video_data.hide_replies_text); -})(); +// Arabic, Persian, Hebrew +var isRTL = ["ar", "fa", "he"].includes(video_data.preferences.locale); var spinnerHTML = '

'; var spinnerHTMLwithHR = spinnerHTML + '
'; From efb6d3fe70d3dbe3f7d36dfea614558fb002552f Mon Sep 17 00:00:00 2001 From: epicsam123 <92618898+epicsam123@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:54:35 -0400 Subject: [PATCH 4/5] Added loaded text for replies, added title for hover, show only final total count --- assets/js/comments.js | 33 ++++++++++++++++++------------- locales/en-US.json | 1 + src/invidious/views/community.ecr | 1 + src/invidious/views/post.ecr | 1 + src/invidious/views/watch.ecr | 4 +++- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/assets/js/comments.js b/assets/js/comments.js index abe423ce..72d17d9b 100644 --- a/assets/js/comments.js +++ b/assets/js/comments.js @@ -123,17 +123,17 @@ function get_youtube_comments() { } function format_count_load_more(content, current_count, total_count) { - var load_more_end_str = content.split('data-load-more'); - if (load_more_end_str.length === 1) - return [content, false]; // no Load More button, return false for has_more_replies - load_more_end_str = load_more_end_str[1].split('\n')[0]; // ' >("Load more" translated string)' - var slice_index = content.indexOf(load_more_end_str) + load_more_end_str.length - 4; // backtrace - var num_remaining = total_count - current_count; - return [ - // More replies may have been added since initally loading parent comment - content.slice(0, slice_index) + ' (+' + (num_remaining > 0 ? num_remaining : '?') + ')' + content.slice(slice_index), - true // Load More button present, return true for has_more_replies - ]; + var load_more_end_str = content.split('data-load-more'); + if (load_more_end_str.length === 1) + return [content, false]; // no Load More button, return false for has_more_replies + load_more_end_str = load_more_end_str[1].split('\n')[0]; // ' >("Load more" translated string)' + var slice_index = content.indexOf(load_more_end_str) + load_more_end_str.length - 4; // backtrace + var num_remaining = total_count - current_count; + return [ + // More replies may have been added since initally loading parent comment + content.slice(0, slice_index) + ' (+' + (num_remaining > 0 ? num_remaining : '?') + ')' + content.slice(slice_index), + true // Load More button present, return true for has_more_replies + ]; } function format_count_toggle_replies_button(toggle_reply_button, current_count, total_count, has_more_replies) { @@ -141,6 +141,7 @@ function format_count_toggle_replies_button(toggle_reply_button, current_count, // Accept the final current count as the total (comments may have been added or removed since loading) total_count = current_count; } else if (current_count >= total_count) { + // An unknown number of new replies have been added since retrieving data total_count = '?'; } @@ -148,10 +149,13 @@ function format_count_toggle_replies_button(toggle_reply_button, current_count, ['data-sub-text', 'data-inner-text'].forEach(attr => { toggle_reply_button.setAttribute(attr, toggle_reply_button.getAttribute(attr) - .replace(/\(\d+\/\d+\)/, ' (' + current_count + '/' + total_count + ')') + .replace(/\d+\/\d+/, total_count === current_count + ? total_count + : current_count + '/' + total_count + ) ); }); - toggle_reply_button.textContent = toggle_reply_button.getAttribute('data-sub-text'); + toggle_reply_button.textContent = toggle_reply_button.title = toggle_reply_button.getAttribute('data-sub-text'); } function get_youtube_replies(target, load_more, load_replies) { @@ -206,7 +210,8 @@ function get_youtube_replies(target, load_more, load_replies) { a.onclick = hide_youtube_replies; var num_total_replies = originalHTML.split('data-load-replies')[1].match(/\d+/)[0] - 0; - var num_replies_text = ' (0/0)'; // replace later + // Replaced later with real counts + var num_replies_text = ' (0/0 ' + video_data.replies_loaded_text + ')'; var hide_replies_text = video_data.hide_replies_text + num_replies_text; a.setAttribute('data-sub-text', hide_replies_text); a.setAttribute('data-inner-text', video_data.show_replies_text + num_replies_text); diff --git a/locales/en-US.json b/locales/en-US.json index c23f6bc3..fdeffb56 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -219,6 +219,7 @@ "View Reddit comments": "View Reddit comments", "Hide replies": "Hide replies", "Show replies": "Show replies", + "Replies loaded": "loaded", "Incorrect password": "Incorrect password", "Wrong answer": "Wrong answer", "Erroneous CAPTCHA": "Erroneous CAPTCHA", diff --git a/src/invidious/views/community.ecr b/src/invidious/views/community.ecr index d2a305d3..0976cea9 100644 --- a/src/invidious/views/community.ecr +++ b/src/invidious/views/community.ecr @@ -39,6 +39,7 @@ "comments_text" => HTML.escape(translate(locale, "View `x` comments", "{commentCount}")), "hide_replies_text" => HTML.escape(translate(locale, "Hide replies")), "show_replies_text" => HTML.escape(translate(locale, "Show replies")), + "replies_loaded_text" => HTML.escape(translate(locale, "Replies loaded")), "preferences" => env.get("preferences").as(Preferences) }.to_pretty_json %> diff --git a/src/invidious/views/post.ecr b/src/invidious/views/post.ecr index fb03a44c..07f96529 100644 --- a/src/invidious/views/post.ecr +++ b/src/invidious/views/post.ecr @@ -35,6 +35,7 @@ "comments_text" => HTML.escape(translate(locale, "View `x` comments", "{commentCount}")), "hide_replies_text" => HTML.escape(translate(locale, "Hide replies")), "show_replies_text" => HTML.escape(translate(locale, "Show replies")), + "replies_loaded_text" => HTML.escape(translate(locale, "Replies loaded")), "params" => { "comments": ["youtube"] }, diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 6f9ced6f..1d2377a7 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -59,6 +59,7 @@ we're going to need to do it here in order to allow for translations. "comments_text" => HTML.escape(translate(locale, "View `x` comments", "{commentCount}")), "hide_replies_text" => HTML.escape(translate(locale, "Hide replies")), "show_replies_text" => HTML.escape(translate(locale, "Show replies")), + "replies_loaded_text" => HTML.escape(translate(locale, "Replies loaded")), "params" => params, "preferences" => preferences, "premiere_timestamp" => video.premiere_timestamp.try &.to_unix, @@ -158,7 +159,7 @@ we're going to need to do it here in order to allow for translations. <% if user %> <% playlists = Invidious::Database::Playlists.select_user_created_playlists(user.email) %> <% if !playlists.empty? %> -
+
"> +