decidim-comments 0.31.6 → 0.32.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -16
  3. data/app/cells/decidim/comments/comment/deletion_data.erb +3 -7
  4. data/app/cells/decidim/comments/comment/replies.erb +23 -3
  5. data/app/cells/decidim/comments/comment/show.erb +5 -19
  6. data/app/cells/decidim/comments/comment_cell.rb +10 -6
  7. data/app/cells/decidim/comments/comment_form/show.erb +1 -1
  8. data/app/cells/decidim/comments/comments/inline.erb +13 -10
  9. data/app/cells/decidim/comments/comments_cell.rb +7 -2
  10. data/app/cells/decidim/comments/two_columns_comments/column.erb +7 -2
  11. data/app/cells/decidim/comments/two_columns_comments/show.erb +7 -4
  12. data/app/cells/decidim/comments/two_columns_comments_cell.rb +30 -31
  13. data/app/controllers/decidim/comments/comments_controller.rb +32 -7
  14. data/app/models/decidim/comments/seed.rb +17 -7
  15. data/app/packs/entrypoints/decidim_comments.js +6 -0
  16. data/app/packs/src/decidim/comments/comments.component.js +1 -43
  17. data/app/packs/src/decidim/comments/comments.component.test.js +65 -63
  18. data/app/packs/src/decidim/comments/comments.js +1 -1
  19. data/app/packs/src/decidim/comments/controllers/load_more_comments/controller.js +201 -0
  20. data/app/packs/src/decidim/comments/controllers/show_replies/controller.js +240 -0
  21. data/app/packs/stylesheets/comments.scss +63 -2
  22. data/app/queries/decidim/comments/sorted_comments.rb +77 -33
  23. data/app/views/decidim/comments/admin/shared/_availability_fields.html.erb +1 -1
  24. data/app/views/decidim/comments/comments/_load_more_comments.html.erb +22 -0
  25. data/app/views/decidim/comments/comments/create.js.erb +38 -4
  26. data/app/views/decidim/comments/comments/index.js.erb +58 -23
  27. data/app/views/decidim/comments/comments/load_more_comments.js.erb +74 -0
  28. data/app/views/decidim/comments/comments/update.js.erb +3 -13
  29. data/config/locales/ar.yml +0 -1
  30. data/config/locales/bg.yml +0 -1
  31. data/config/locales/bs-BA.yml +0 -1
  32. data/config/locales/ca-IT.yml +6 -4
  33. data/config/locales/ca.yml +6 -4
  34. data/config/locales/cs.yml +3 -6
  35. data/config/locales/de.yml +6 -4
  36. data/config/locales/el.yml +0 -1
  37. data/config/locales/en.yml +6 -3
  38. data/config/locales/es-MX.yml +6 -4
  39. data/config/locales/es-PY.yml +6 -4
  40. data/config/locales/es.yml +6 -4
  41. data/config/locales/eu.yml +14 -12
  42. data/config/locales/fi-plain.yml +6 -4
  43. data/config/locales/fi.yml +6 -4
  44. data/config/locales/fr-CA.yml +3 -4
  45. data/config/locales/fr.yml +3 -4
  46. data/config/locales/ga-IE.yml +0 -1
  47. data/config/locales/gl.yml +0 -1
  48. data/config/locales/hu.yml +0 -1
  49. data/config/locales/id-ID.yml +0 -1
  50. data/config/locales/is-IS.yml +1 -2
  51. data/config/locales/it.yml +0 -1
  52. data/config/locales/ja.yml +5 -3
  53. data/config/locales/lb.yml +0 -1
  54. data/config/locales/lt.yml +0 -1
  55. data/config/locales/lv.yml +0 -1
  56. data/config/locales/nl.yml +0 -1
  57. data/config/locales/no.yml +0 -1
  58. data/config/locales/pl.yml +1 -2
  59. data/config/locales/pt-BR.yml +6 -4
  60. data/config/locales/pt.yml +0 -1
  61. data/config/locales/ro-RO.yml +0 -5
  62. data/config/locales/ru.yml +0 -1
  63. data/config/locales/si-LK.yml +0 -1
  64. data/config/locales/sk.yml +0 -125
  65. data/config/locales/sl.yml +0 -1
  66. data/config/locales/sq-AL.yml +0 -1
  67. data/config/locales/sr-CS.yml +0 -1
  68. data/config/locales/sv.yml +0 -4
  69. data/config/locales/tr-TR.yml +0 -1
  70. data/config/locales/uk.yml +0 -1
  71. data/config/locales/zh-CN.yml +0 -1
  72. data/config/locales/zh-TW.yml +0 -1
  73. data/db/migrate/20260208201401_remove_user_group_comments.rb +13 -0
  74. data/decidim-comments.gemspec +6 -9
  75. data/lib/decidim/api/comment_mutation_type.rb +12 -4
  76. data/lib/decidim/api/commentable_interface.rb +1 -0
  77. data/lib/decidim/api/commentable_mutation_type.rb +4 -0
  78. data/lib/decidim/comments/commentable.rb +5 -0
  79. data/lib/decidim/comments/commentable_with_component.rb +9 -0
  80. data/lib/decidim/comments/engine.rb +11 -0
  81. data/lib/decidim/comments/test/factories.rb +1 -1
  82. data/lib/decidim/comments/test/shared_examples/translatable_comment.rb +1 -0
  83. data/lib/decidim/comments/version.rb +1 -1
  84. metadata +15 -14
  85. data/app/resolvers/decidim/comments/vote_comment_resolver.rb +0 -24
@@ -1,4 +1,4 @@
1
- @use "stylesheets/decidim/editor_suggestions";
1
+ @use "stylesheets/decidim/tribute";
2
2
 
3
3
  .comments {
4
4
  &__container {
@@ -6,7 +6,7 @@
6
6
  }
7
7
 
8
8
  .add-comment {
9
- @apply mt-12 md:bg-gray-5 p-0 md:px-4 md:pb-6 md:pt-1 rounded-lg w-full;
9
+ @apply mt-2 md:bg-gray-5 p-0 md:px-4 md:pb-6 md:pt-1 rounded-lg w-full;
10
10
 
11
11
  .new_comment {
12
12
  @apply w-full;
@@ -461,10 +461,71 @@
461
461
  }
462
462
  }
463
463
 
464
+ .show-replies-button,
465
+ .load-more-comments {
466
+ @apply flex items-center justify-center my-2;
467
+
468
+ .button {
469
+ @apply border-0 rounded px-2 py-1 transition-colors;
470
+
471
+ &:hover:not(:disabled) {
472
+ @apply bg-secondary/10 no-underline;
473
+ }
474
+
475
+ &:disabled {
476
+ @apply opacity-100 bg-transparent text-secondary no-underline;
477
+ }
478
+ }
479
+ }
480
+
481
+ .show-replies-button {
482
+ @apply my-0 relative z-10 h-0 overflow-visible;
483
+
484
+ transform: translateY(calc(0.75rem + 1px));
485
+
486
+ .button {
487
+ @apply bg-white border-0;
488
+
489
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);
490
+
491
+ &:hover:not(:disabled) {
492
+ @apply bg-white no-underline;
493
+ }
494
+
495
+ &:disabled {
496
+ @apply bg-white opacity-100;
497
+
498
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);
499
+ }
500
+
501
+ svg:first-of-type {
502
+ @apply block;
503
+ }
504
+
505
+ svg:last-of-type {
506
+ @apply hidden;
507
+ }
508
+
509
+ &[aria-expanded="true"] {
510
+ svg:first-of-type {
511
+ @apply hidden;
512
+ }
513
+
514
+ svg:last-of-type {
515
+ @apply block;
516
+ }
517
+ }
518
+ }
519
+ }
520
+
464
521
  .publish-comment-button {
465
522
  @apply ml-auto md:relative fixed inset-x-0 bottom-0 z-30 md:z-0 flex flex-row items-center md:items-end md:justify-end font-semibold justify-around bg-white md:bg-transparent gap-4 md:gap-0 p-4 md:p-0 shadow-inner md:shadow-none first:[&>*]:grow md:first:[&>*]:grow-0 h-14 md:h-fit first:[&>button]:py-3;
466
523
  }
467
524
 
525
+ [data-additional-reply] .comment__form-submit {
526
+ @apply flex justify-end;
527
+ }
528
+
468
529
  .fullscreen {
469
530
  @apply fixed md:relative inset-0 w-screen md:w-full h-screen bg-white z-50 p-0 overflow-auto md:overflow-hidden rounded-none;
470
531
  }
@@ -4,6 +4,8 @@ module Decidim
4
4
  module Comments
5
5
  # A class used to find comments for a commentable resource
6
6
  class SortedComments < Decidim::Query
7
+ DEFAULT_COMMENTS_LIMIT = 20
8
+
7
9
  attr_reader :commentable
8
10
 
9
11
  # Syntactic sugar to initialize the class and return the queried objects.
@@ -11,6 +13,8 @@ module Decidim
11
13
  # commentable - a resource that can have comments
12
14
  # options - The Hash options is used to refine the selection ( default: {}):
13
15
  # :order_by - The string order_by to sort by ( optional )
16
+ # :limit - The number of items to load ( optional )
17
+ # :offset - The number of items to skip ( optional )
14
18
  def self.for(commentable, options = {})
15
19
  new(commentable, options).query
16
20
  end
@@ -20,6 +24,9 @@ module Decidim
20
24
  # commentable = a resource that can have comments
21
25
  # options - The Hash options is used to refine the selection ( default: {}):
22
26
  # :order_by - The string order_by to sort by ( optional )
27
+ # :limit - The number of items to load ( optional )
28
+ # :offset - The number of items to skip ( optional )
29
+ # :alignment - Filter by alignment: 1 (in_favor), -1 (against), 0 (neutral) ( optional )
23
30
  def initialize(commentable, options = {})
24
31
  options[:order_by] ||= "older"
25
32
  @commentable = commentable
@@ -31,43 +38,63 @@ module Decidim
31
38
  # level of nested replies.
32
39
  def query
33
40
  scope = base_scope
34
- .includes(:author, :up_votes, :down_votes)
35
-
36
- case @options[:order_by]
37
- when "recent"
38
- order_by_recent(scope)
39
- when "best_rated"
40
- order_by_best_rated(scope)
41
- when "most_discussed"
42
- order_by_most_discussed(scope)
43
- else
44
- order_by_older(scope)
45
- end
41
+ .includes(:author)
42
+
43
+ sorted_scope = case @options[:order_by]
44
+ when "recent"
45
+ order_by_recent(scope)
46
+ when "best_rated"
47
+ order_by_best_rated(scope)
48
+ when "most_discussed"
49
+ order_by_most_discussed(scope)
50
+ else
51
+ order_by_older(scope)
52
+ end
53
+
54
+ apply_limit(sorted_scope)
55
+ end
56
+
57
+ def total_count
58
+ base_scope.count
59
+ end
60
+
61
+ def has_more?
62
+ return false unless limited?
63
+
64
+ total_count > offset + limit
65
+ end
66
+
67
+ def offset
68
+ @options[:offset].to_i
46
69
  end
47
70
 
48
- def count_replies(comment)
49
- if comment.comment_threads.size.positive?
50
- comment.comment_threads.size + comment.comment_threads.sum { |reply| count_replies(reply) }
51
- else
52
- 0
53
- end
71
+ def limit
72
+ @options[:limit]&.to_i || default_limit
54
73
  end
55
74
 
56
75
  private
57
76
 
77
+ def limited?
78
+ @options[:limit].present? || @options[:offset].present?
79
+ end
80
+
81
+ def default_limit
82
+ DEFAULT_COMMENTS_LIMIT
83
+ end
84
+
85
+ def apply_limit(scope)
86
+ return scope unless limited?
87
+
88
+ scope.limit(limit).offset(offset)
89
+ end
90
+
58
91
  def base_scope
59
92
  id = @options[:id]
60
93
  return Comment.where(root_commentable: commentable, id:) if id.present?
61
94
 
62
- after = @options[:after]
63
- if after.present?
64
- return Comment.where(root_commentable: commentable).where(
65
- "decidim_comments_comments.id > ?",
66
- after
67
- )
68
- end
69
-
70
- Comment.where(commentable:)
95
+ scope = Comment.where(commentable:)
96
+ scope = scope.where(alignment: @options[:alignment]) if @options[:alignment].present?
97
+ scope
71
98
  end
72
99
 
73
100
  def order_by_older(scope)
@@ -79,15 +106,32 @@ module Decidim
79
106
  end
80
107
 
81
108
  def order_by_best_rated(scope)
82
- scope.sort_by do |comment|
83
- comment.up_votes.size - comment.down_votes.size
84
- end.reverse
109
+ scope.order(Arel.sql("up_votes_count - down_votes_count DESC, created_at DESC"))
85
110
  end
86
111
 
87
112
  def order_by_most_discussed(scope)
88
- scope.sort_by do |comment|
89
- count_replies(comment)
90
- end.reverse
113
+ scope
114
+ .select("decidim_comments_comments.*, COALESCE(descendants.total, 0) as descendants_count")
115
+ .joins(<<~SQL.squish)
116
+ LEFT JOIN LATERAL (
117
+ WITH RECURSIVE comment_tree AS (
118
+ SELECT id, decidim_commentable_id
119
+ FROM decidim_comments_comments AS replies
120
+ WHERE replies.decidim_commentable_id = decidim_comments_comments.id
121
+ AND replies.decidim_commentable_type = 'Decidim::Comments::Comment'
122
+
123
+ UNION ALL
124
+
125
+ SELECT r.id, r.decidim_commentable_id
126
+ FROM decidim_comments_comments AS r
127
+ INNER JOIN comment_tree ct ON r.decidim_commentable_id = ct.id
128
+ AND r.decidim_commentable_type = 'Decidim::Comments::Comment'
129
+ )
130
+ SELECT COUNT(*) as total
131
+ FROM comment_tree
132
+ ) descendants ON true
133
+ SQL
134
+ .order(Arel.sql("descendants_count DESC, decidim_comments_comments.created_at DESC"))
91
135
  end
92
136
  end
93
137
  end
@@ -2,7 +2,7 @@
2
2
  <%= form.check_box :comments_enabled, label: t(".enabled"), data: { controller: :toggle, "toggle-toggle-value": "customize_comments_times-div" } %>
3
3
  </div>
4
4
 
5
- <div id="customize_comments_times-div" class="row column <%= @form.comments_enabled ? nil : "hide" %>">
5
+ <div id="customize_comments_times-div" data-toggler=".hide" class="row column <%= @form.comments_enabled ? nil : "hide" %>">
6
6
  <div class="mb-4">
7
7
  <%= form.datetime_field :comments_start_time, label: t(".start_time") %>
8
8
  </div>
@@ -0,0 +1,22 @@
1
+ <div data-load-more-comments-wrapper
2
+ data-controller="load-more-comments"
3
+ data-load-more-comments-url-value="<%= decidim_comments.comments_path %>"
4
+ data-load-more-comments-commentable-gid-value="<%= commentable.to_signed_global_id.to_s %>"
5
+ data-load-more-comments-order-value="<%= order %>"
6
+ data-load-more-comments-offset-value="<%= offset %>"
7
+ data-load-more-comments-per-page-value="<%= Decidim::Comments::SortedComments::DEFAULT_COMMENTS_LIMIT %>"
8
+ <% if local_assigns[:alignment].present? %>
9
+ data-load-more-comments-alignment-value="<%= alignment %>"
10
+ <% end %>
11
+ class="load-more-comments">
12
+ <button type="button"
13
+ data-load-more-comments-target="button"
14
+ data-action="click->load-more-comments#loadMore"
15
+ class="button button__xs button__text-secondary">
16
+ <span class="font-normal"><%= t("decidim.components.comments.load_more_comments") %></span>
17
+ <%= icon "arrow-down-s-line" %>
18
+ </button>
19
+ <span data-load-more-comments-target="spinner" class="hidden">
20
+ <%= icon "loader-3-line", class: "animate-spin fill-secondary" %>
21
+ </span>
22
+ </div>
@@ -13,12 +13,46 @@
13
13
  hideButton.find("[data-show-comment-reply]").first().html('<%= t("decidim.components.comment.show_replies", count: Decidim::Comments::SortedComments.for(root_comment.reload).size) %>');
14
14
 
15
15
  } else {
16
- component.addThread(commentHtml, alignment, true);
16
+ var params = new URLSearchParams({
17
+ "commentable_gid": component.commentableGid,
18
+ "root_depth": component.rootDepth,
19
+ "order": component.order,
20
+ "reload": 1
21
+ });
22
+
23
+ var csrfToken = document.querySelector('meta[name="csrf-token"]');
24
+ var headers = {
25
+ "Accept": "text/javascript",
26
+ "X-Requested-With": "XMLHttpRequest"
27
+ };
28
+ if (csrfToken) { headers["X-CSRF-Token"] = csrfToken.getAttribute("content"); }
29
+
30
+ fetch(component.commentsUrl + "?" + params.toString(), {
31
+ method: "GET",
32
+ headers: headers,
33
+ credentials: "same-origin"
34
+ }).then(function(response) {
35
+ if (!response.ok) { throw new Error("Network response was not ok"); }
36
+ return response.text();
37
+ }).then(function(script) {
38
+ var scriptElement = document.createElement("script");
39
+ scriptElement.textContent = script;
40
+ document.body.appendChild(scriptElement);
41
+ document.body.removeChild(scriptElement);
42
+ component._finalizeCommentCreation(null, true);
43
+ }).catch(function() {
44
+ var commentsContainer = document.getElementById(rootCommentableId);
45
+ var loadingElement = commentsContainer.querySelector(".loading-comments");
46
+ var commentsElement = commentsContainer.querySelector(".comments");
47
+ if (loadingElement) { loadingElement.classList.add("hidden"); }
48
+ if (commentsElement) { commentsElement.classList.remove("hidden"); }
49
+ component._finalizeCommentCreation(null, true);
50
+ });
17
51
  }
18
52
 
19
- Rails.fire(document, "comments:loaded", {
20
- commentsIds: [<%= @comment.id %>, <%= @comment.commentable.id %>]
21
- });
53
+ document.dispatchEvent(new CustomEvent("comments:loaded", {
54
+ detail: { commentsIds: [<%= @comment.id %>, <%= @comment.commentable.id %>] }
55
+ }));
22
56
 
23
57
  // Update the comments count
24
58
  $(".comments-count", $comments).text(<%== t("decidim.components.comments.title", count: @comments_count).to_json %>);
@@ -1,35 +1,70 @@
1
1
  (function() {
2
- var rootCommentableId = <%== "comments-for-#{commentable.commentable_type.demodulize}-#{commentable.id}".to_json %>;
2
+ <% root_resource = commentable.is_a?(Decidim::Comments::Comment) ? commentable.root_commentable : commentable %>
3
3
 
4
- var $comments = $("#" + rootCommentableId);
4
+ var commentsContainer = document.getElementById(<%== "comments-for-#{root_resource.commentable_type.demodulize}-#{root_resource.id}".to_json %>);
5
+ var $comments = $(commentsContainer);
5
6
  var component = $comments.data("comments");
6
7
 
7
- $(".loading-comments").addClass("hidden");
8
+ // Find target container: desktop column, mobile container, or standard single-column
9
+ <% if alignment.present? %>
10
+ var targetContainer = commentsContainer.querySelector(<%== ".comments-section__#{alignment == 1 ? "in-favor" : "against"}".to_json %>);
11
+ <% else %>
12
+ var targetContainer = commentsContainer.querySelector("#mobileContainer") || commentsContainer.querySelector(".comment-threads");
13
+ <% end %>
8
14
 
9
- var commentsIds = [];
10
- <% @comments.each do |comment| %>
11
- var commentId = <%= comment.id.to_json %>;
12
- commentsIds.push(commentId);
13
- var commentHtml = '<%== j(render comment).strip %>';
14
- var inReplyTo = <%== (reply?(comment) ? comment.commentable.id : nil).to_json %>;
15
-
16
- var $comment = $("#comment_" + commentId);
17
- if (1 > $comment.length) {
18
- if (inReplyTo) {
19
- component.addReply(inReplyTo, commentHtml);
20
- } else {
21
- component.addThread(commentHtml);
15
+ var loadingElement = commentsContainer.querySelector(".loading-comments");
16
+ var commentsElement = commentsContainer.querySelector(".comments");
17
+ if (loadingElement) { loadingElement.classList.add("hidden"); }
18
+ if (commentsElement) { commentsElement.classList.remove("hidden"); }
19
+
20
+ <% unless alignment.present? %>
21
+ if (targetContainer) {
22
+ targetContainer.querySelectorAll(".comment-thread").forEach(function(el) { el.remove(); });
23
+ var existingLoadMore = targetContainer.querySelector("[data-load-more-comments-wrapper]");
24
+ if (existingLoadMore) { existingLoadMore.remove(); }
22
25
  }
23
- }
24
- <% end %>
26
+ <% end %>
27
+
28
+ // Insert comments
29
+ var commentsIds = [];
30
+ <% @comments.each do |comment| %>
31
+ (function() {
32
+ var commentId = <%= comment.id.to_json %>;
33
+ var commentHtml = '<%== j(render comment).strip %>';
34
+ var inReplyTo = <%== (reply?(comment) ? comment.commentable.id : nil).to_json %>;
35
+
36
+ commentsIds.push(commentId);
37
+ if (targetContainer && !targetContainer.querySelector("#comment_" + commentId)) {
38
+ if (inReplyTo) {
39
+ component.addReply(inReplyTo, commentHtml);
40
+ } else {
41
+ component.addThread(commentHtml, <%= alignment.presence || "null" %>);
42
+ }
43
+ }
44
+ })();
45
+ <% end %>
46
+
25
47
  if (commentsIds.length) {
26
48
  component.lastCommentId = <%= @comments.last&.id || 0 %>;
49
+ Rails.fire(document, "comments:loaded", { commentsIds: commentsIds });
50
+ }
27
51
 
28
- Rails.fire(document, "comments:loaded", {
29
- commentsIds: commentsIds
30
- });
52
+ // Update comments count
53
+ var countElement = commentsContainer.querySelector(".comments-count");
54
+ if (countElement) {
55
+ countElement.textContent = <%== t("decidim.components.comments.title", count: @comments_count).to_json %>;
31
56
  }
32
57
 
33
- // Update the comments count
34
- $(".comments-count", $comments).text(<%== t("decidim.components.comments.title", count: @comments_count).to_json %>);
58
+ // Handle load more button
59
+ <% if @has_more_comments %>
60
+ if (targetContainer && !targetContainer.querySelector("[data-load-more-comments-wrapper]")) {
61
+ var loadMoreHtml = '<%== j(render partial: "decidim/comments/comments/load_more_comments", locals: { commentable:, order:, offset: @comments.size, alignment: alignment.presence }).strip %>';
62
+ targetContainer.insertAdjacentHTML("beforeend", loadMoreHtml);
63
+ }
64
+ <% else %>
65
+ if (targetContainer) {
66
+ var loadMoreToRemove = targetContainer.querySelector("[data-load-more-comments-wrapper]");
67
+ if (loadMoreToRemove) { loadMoreToRemove.remove(); }
68
+ }
69
+ <% end %>
35
70
  }());
@@ -0,0 +1,74 @@
1
+ (function() {
2
+ <% root_resource = commentable.is_a?(Decidim::Comments::Comment) ? commentable.root_commentable : commentable %>
3
+ <% is_loading_replies = commentable.is_a?(Decidim::Comments::Comment) %>
4
+ <% new_offset = comments_offset + @comments.size %>
5
+
6
+ var commentsContainer = document.getElementById(<%== "comments-for-#{root_resource.commentable_type.demodulize}-#{root_resource.id}".to_json %>);
7
+ var $comments = $(commentsContainer);
8
+ var component = $comments.data("comments");
9
+
10
+ <% if is_loading_replies %>
11
+ var targetContainer = document.getElementById("comment-<%= commentable.id %>-replies");
12
+ var loadMoreWrapper = null;
13
+ <% else %>
14
+ <% if alignment.present? %>
15
+ var targetContainer = commentsContainer.querySelector(<%== ".comments-section__#{alignment == 1 ? "in-favor" : "against"}".to_json %>);
16
+ <% else %>
17
+ var targetContainer = commentsContainer.querySelector("#mobileContainer") || commentsContainer.querySelector(".comment-threads");
18
+ <% end %>
19
+ var loadMoreWrapper = targetContainer ? targetContainer.querySelector("[data-load-more-comments-wrapper]") : null;
20
+ <% end %>
21
+
22
+ // Insert comments
23
+ var commentsIds = [];
24
+ <% @comments.each do |comment| %>
25
+ (function() {
26
+ var commentId = <%= comment.id.to_json %>;
27
+ var commentHtml = '<%== j(render comment).strip %>';
28
+ var inReplyTo = <%== (reply?(comment) ? comment.commentable.id : nil).to_json %>;
29
+
30
+ commentsIds.push(commentId);
31
+ var existingComment = targetContainer ? targetContainer.querySelector("#comment_" + commentId) : null;
32
+ if (!existingComment) {
33
+ if (inReplyTo) {
34
+ component.addReply(inReplyTo, commentHtml);
35
+ } else if (loadMoreWrapper) {
36
+ loadMoreWrapper.insertAdjacentHTML("beforebegin", commentHtml);
37
+ component._initializeComments($(loadMoreWrapper.previousElementSibling));
38
+ } else {
39
+ component.addThread(commentHtml, <%= alignment.presence || "null" %>);
40
+ }
41
+ }
42
+ })();
43
+ <% end %>
44
+
45
+ if (commentsIds.length) {
46
+ component.lastCommentId = <%= @comments.last&.id || 0 %>;
47
+ Rails.fire(document, "comments:loaded", { commentsIds: commentsIds });
48
+ }
49
+
50
+ <% if is_loading_replies %>
51
+ if (targetContainer) {
52
+ targetContainer.querySelectorAll(".comment-reply.hidden").forEach(function(el) {
53
+ el.classList.remove("hidden");
54
+ });
55
+ targetContainer.querySelectorAll(".show-replies-button").forEach(function(el) {
56
+ el.remove();
57
+ });
58
+ }
59
+ <% end %>
60
+
61
+ <% unless is_loading_replies %>
62
+ <% if @has_more_comments %>
63
+ if (loadMoreWrapper) {
64
+ loadMoreWrapper.setAttribute("data-load-more-comments-offset-value", <%= new_offset %>);
65
+ var btn = loadMoreWrapper.querySelector("[data-load-more-comments-target='button']");
66
+ var spin = loadMoreWrapper.querySelector("[data-load-more-comments-target='spinner']");
67
+ if (btn) { btn.disabled = false; btn.classList.remove("loading"); }
68
+ if (spin) { spin.classList.add("hidden"); }
69
+ }
70
+ <% else %>
71
+ if (loadMoreWrapper) { loadMoreWrapper.remove(); }
72
+ <% end %>
73
+ <% end %>
74
+ }());
@@ -1,26 +1,16 @@
1
1
  document.addEventListener("turbo:load", () => {
2
- var rootCommentableId = <%== "comments-for-#{@commentable.class.name.demodulize}-#{@commentable.id}".to_json %>;
3
- var $comments = $("#" + rootCommentableId);
4
- var config = $comments.data("decidim-comments");
5
- var component = $comments.data("comments");
6
-
7
- component.unmountComponent();
8
-
9
2
  var commentHtml = '<%== j(render partial: "edited_comment", locals: { comment: @comment }).strip %>';
10
3
  var commentId = <%= @comment.id.to_json %>;
11
4
  var $comment = $("#comment_<%= @comment.id %>");
12
5
 
13
- var $edit_modal = document.getElementById(`editCommentModal${commentId}`)
6
+ var $edit_modal = document.getElementById(`editCommentModal${commentId}`);
14
7
 
15
- if ($edit_modal !== undefined) {
16
- $edit_modal.remove()
8
+ if ($edit_modal) {
9
+ $edit_modal.remove();
17
10
  }
18
11
 
19
12
  $comment.replaceWith(commentHtml);
20
13
 
21
- // Re-create the component
22
- component.mountComponent();
23
-
24
14
  Rails.fire(document, "comments:loaded", {
25
15
  commentsIds: [commentId]
26
16
  });
@@ -1,4 +1,3 @@
1
- ---
2
1
  ar:
3
2
  activemodel:
4
3
  models:
@@ -1,4 +1,3 @@
1
- ---
2
1
  bg:
3
2
  activemodel:
4
3
  models:
@@ -1,4 +1,3 @@
1
- ---
2
1
  bs:
3
2
  decidim:
4
3
  components:
@@ -1,4 +1,3 @@
1
- ---
2
1
  ca-IT:
3
2
  activemodel:
4
3
  models:
@@ -68,9 +67,6 @@ ca-IT:
68
67
  alignment:
69
68
  against: En contra
70
69
  in_favor: A favor
71
- answers:
72
- one: "%{count} resposta"
73
- other: "%{count} respuestas"
74
70
  cancel_reply: Cancel·lar resposta
75
71
  comment_label: Comentari %{comment_id}
76
72
  comment_label_reply: Comentari %{comment_id} (respon al comentari %{parent_comment_id})
@@ -84,7 +80,11 @@ ca-IT:
84
80
  hide_replies:
85
81
  one: Amagar resposta
86
82
  other: Amagar %{count} respostes
83
+ load_replies: Carregar les respostes
87
84
  moderated_at: Comentari moderat el %{date}
85
+ replies_count:
86
+ one: "%{count} respuesta"
87
+ other: "%{count} respostes"
88
88
  reply: Respondre
89
89
  report:
90
90
  action: Denúncia
@@ -116,7 +116,9 @@ ca-IT:
116
116
  blocked_comments_warning: Els comentaris estan deshabilitats en aquests moments, només les administradores poden respondre o publicar-ne de nous.
117
117
  comment_details_title: Detalls del comentari
118
118
  in_favor: A favor
119
+ load_more_comments: Carregar més comentaris
119
120
  loading: Carregant els comentaris ...
121
+ no_comments_yet: Encara no hi ha comentaris
120
122
  single_comment_warning: <a href="%{url}">Mostra tots els comentaris</a>
121
123
  single_comment_warning_title: Estàs veient un sol comentari
122
124
  title:
@@ -1,4 +1,3 @@
1
- ---
2
1
  ca:
3
2
  activemodel:
4
3
  models:
@@ -68,9 +67,6 @@ ca:
68
67
  alignment:
69
68
  against: En contra
70
69
  in_favor: A favor
71
- answers:
72
- one: "%{count} resposta"
73
- other: "%{count} respuestas"
74
70
  cancel_reply: Cancel·lar resposta
75
71
  comment_label: Comentari %{comment_id}
76
72
  comment_label_reply: Comentari %{comment_id} (respon al comentari %{parent_comment_id})
@@ -84,7 +80,11 @@ ca:
84
80
  hide_replies:
85
81
  one: Amagar resposta
86
82
  other: Amagar %{count} respostes
83
+ load_replies: Carregar les respostes
87
84
  moderated_at: Comentari moderat el %{date}
85
+ replies_count:
86
+ one: "%{count} respuesta"
87
+ other: "%{count} respostes"
88
88
  reply: Respondre
89
89
  report:
90
90
  action: Denúncia
@@ -116,7 +116,9 @@ ca:
116
116
  blocked_comments_warning: Els comentaris estan deshabilitats en aquests moments, només les administradores poden respondre o publicar-ne de nous.
117
117
  comment_details_title: Detalls del comentari
118
118
  in_favor: A favor
119
+ load_more_comments: Carregar més comentaris
119
120
  loading: Carregant els comentaris ...
121
+ no_comments_yet: Encara no hi ha comentaris
120
122
  single_comment_warning: <a href="%{url}">Mostra tots els comentaris</a>
121
123
  single_comment_warning_title: Estàs veient un sol comentari
122
124
  title:
@@ -1,4 +1,3 @@
1
- ---
2
1
  cs:
3
2
  activemodel:
4
3
  models:
@@ -72,11 +71,6 @@ cs:
72
71
  alignment:
73
72
  against: Proti
74
73
  in_favor: Ve prospěch
75
- answers:
76
- one: "%{count} odpověď"
77
- few: "%{count} odpovědi"
78
- many: "%{count} odpovědí"
79
- other: "%{count} odpovědí"
80
74
  cancel_reply: Zrušit odpověď
81
75
  comment_label: Komentář %{comment_id}
82
76
  comment_label_reply: Komentář %{comment_id} (odpověď na komentář %{parent_comment_id})
@@ -92,6 +86,7 @@ cs:
92
86
  few: Skrýt {count} odpovědi
93
87
  many: Skrýt {count} odpovědí
94
88
  other: Skrýt {count} odpovědí
89
+ load_replies: Načíst odpovědi
95
90
  moderated_at: Komentář moderován dne %{date}
96
91
  reply: Odpověď
97
92
  report:
@@ -126,7 +121,9 @@ cs:
126
121
  blocked_comments_warning: Komentáře jsou momentálně zakázány, pouze administrátoři mohou odpovědět nebo psát nové.
127
122
  comment_details_title: Podrobnosti komentáře
128
123
  in_favor: Líbí se mi
124
+ load_more_comments: Načíst další komentáře
129
125
  loading: Načítání komentářů ...
126
+ no_comments_yet: Zatím žádné komentáře
130
127
  single_comment_warning: <a href="%{url}">Zobrazit všechny komentáře</a>
131
128
  single_comment_warning_title: Vidíte jeden komentář
132
129
  title: