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? %>
-