diff --git a/app/assets/stylesheets/views/comments.scss b/app/assets/stylesheets/views/comments.scss
index 68bdb5c3a..6f4717811 100644
--- a/app/assets/stylesheets/views/comments.scss
+++ b/app/assets/stylesheets/views/comments.scss
@@ -275,3 +275,24 @@
.moderator-template-button:not(.active) {
@extend .crayons-btn--outlined;
}
+
+#comments-sort-dropdown-container {
+ .comment-sort-option {
+ &__header {
+ color: var(--base-90);
+ font-weight: var(--fw-medium);
+ }
+
+ .comment-sort-option__header {
+ .crayons-icon {
+ position: absolute;
+ left: var(--su-4);
+ color: var(--link-branded-color);
+ }
+
+ &[aria-current='page'] {
+ font-weight: var(--fw-bold);
+ }
+ }
+ }
+}
diff --git a/app/controllers/stories_controller.rb b/app/controllers/stories_controller.rb
index d823fb867..001ea4594 100644
--- a/app/controllers/stories_controller.rb
+++ b/app/controllers/stories_controller.rb
@@ -256,7 +256,7 @@ class StoriesController < ApplicationController
@discussion_lock = @article.discussion_lock
@user = @article.user
@organization = @article.organization
-
+ @comments_order = fetch_sort_order
if @article.collection
@collection = @article.collection
@@ -414,4 +414,10 @@ class StoriesController < ApplicationController
@user.profile.website_url,
].compact_blank
end
+
+ def fetch_sort_order
+ return params[:comments_sort] if Comment::VALID_SORT_OPTIONS.include? params[:comments_sort]
+
+ "top"
+ end
end
diff --git a/app/javascript/packs/commentDropdowns.js b/app/javascript/packs/commentDropdowns.js
index a68d2f384..a4cbeb24f 100644
--- a/app/javascript/packs/commentDropdowns.js
+++ b/app/javascript/packs/commentDropdowns.js
@@ -21,7 +21,7 @@ const handleCopyPermalink = (closeDropdown) => {
const initializeArticlePageDropdowns = () => {
// Gather all dropdown triggers for comment options and profile previews
const dropdownTriggers = document.querySelectorAll(
- 'button[id^=comment-dropdown-trigger], button[id^=comment-profile-preview-trigger-]',
+ 'button[id^=comment-dropdown-trigger], button[id^=comment-profile-preview-trigger-], button[id^=toggle-comments-sort-dropdown]',
);
for (const dropdownTrigger of dropdownTriggers) {
@@ -58,7 +58,11 @@ const initializeArticlePageDropdowns = () => {
'.report-abuse-link-wrapper',
);
if (reportAbuseWrapper) {
- reportAbuseWrapper.innerHTML = `${locale('core.report_abuse')}`;
+ reportAbuseWrapper.innerHTML = `${locale(
+ 'core.report_abuse',
+ )}`;
}
// Initialize the "Copy link" functionality
diff --git a/app/javascript/utilities/dropdownUtils.js b/app/javascript/utilities/dropdownUtils.js
index 4ab8bd402..eb68531d2 100644
--- a/app/javascript/utilities/dropdownUtils.js
+++ b/app/javascript/utilities/dropdownUtils.js
@@ -165,7 +165,10 @@ export const initializeDropdown = ({
// Close the dropdown if user has clicked outside
const clickOutsideListener = ({ target }) => {
+ // Get fresh handle every time, resulting in more streamlined functionality for cypress
+ const triggerButton = document.getElementById(triggerElementId);
if (
+ triggerButton &&
target !== triggerButton &&
!dropdownContent.contains(target) &&
!triggerButton.contains(target)
diff --git a/app/models/comment.rb b/app/models/comment.rb
index f72dcfbb5..8b96612c9 100644
--- a/app/models/comment.rb
+++ b/app/models/comment.rb
@@ -9,6 +9,8 @@ class Comment < ApplicationRecord
COMMENTABLE_TYPES = %w[Article PodcastEpisode].freeze
+ VALID_SORT_OPTIONS = %w[top latest oldest].freeze
+
URI_REGEXP = %r{
\A
(?:https?://)? # optional scheme
@@ -87,10 +89,10 @@ class Comment < ApplicationRecord
alias touch_by_reaction save
- def self.tree_for(commentable, limit = 0)
+ def self.tree_for(commentable, limit = 0, order = nil)
commentable.comments
.includes(user: %i[setting profile])
- .arrange(order: "score DESC")
+ .arrange(order: build_sort_query(order))
.to_a[0..limit - 1]
.to_h
end
@@ -172,6 +174,19 @@ class Comment < ApplicationRecord
ancestry && Comment.exists?(id: ancestry)
end
+ def self.build_sort_query(order)
+ case order
+ when "latest"
+ "created_at DESC"
+ when "oldest"
+ "created_at ASC"
+ else
+ "score DESC"
+ end
+ end
+
+ private_class_method :build_sort_query
+
private
def remove_notifications?
diff --git a/app/views/articles/_full_comment_area.html.erb b/app/views/articles/_full_comment_area.html.erb
index 6c3b1efc6..f7bbd05bb 100644
--- a/app/views/articles/_full_comment_area.html.erb
+++ b/app/views/articles/_full_comment_area.html.erb
@@ -2,10 +2,37 @@
- <%= t("views.articles.comments.subtitle_html", - num: tag.span(t("views.articles.comments.num", num: @article.comments_count), class: "js-comments-count", data: { comments_count: @article.comments_count })) %> -
++ <%= t("views.articles.comments.subtitle.#{@comments_order}_html", + num: tag.span(t("views.articles.comments.num", num: @article.comments_count), class: "js-comments-count", data: { comments_count: @article.comments_count })) %> +
+ +