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.
- checksums.yaml +4 -4
- data/README.md +18 -16
- data/app/cells/decidim/comments/comment/deletion_data.erb +3 -7
- data/app/cells/decidim/comments/comment/replies.erb +23 -3
- data/app/cells/decidim/comments/comment/show.erb +5 -19
- data/app/cells/decidim/comments/comment_cell.rb +10 -6
- data/app/cells/decidim/comments/comment_form/show.erb +1 -1
- data/app/cells/decidim/comments/comments/inline.erb +13 -10
- data/app/cells/decidim/comments/comments_cell.rb +7 -2
- data/app/cells/decidim/comments/two_columns_comments/column.erb +7 -2
- data/app/cells/decidim/comments/two_columns_comments/show.erb +7 -4
- data/app/cells/decidim/comments/two_columns_comments_cell.rb +30 -31
- data/app/controllers/decidim/comments/comments_controller.rb +32 -7
- data/app/models/decidim/comments/seed.rb +17 -7
- data/app/packs/entrypoints/decidim_comments.js +6 -0
- data/app/packs/src/decidim/comments/comments.component.js +1 -43
- data/app/packs/src/decidim/comments/comments.component.test.js +65 -63
- data/app/packs/src/decidim/comments/comments.js +1 -1
- data/app/packs/src/decidim/comments/controllers/load_more_comments/controller.js +201 -0
- data/app/packs/src/decidim/comments/controllers/show_replies/controller.js +240 -0
- data/app/packs/stylesheets/comments.scss +63 -2
- data/app/queries/decidim/comments/sorted_comments.rb +77 -33
- data/app/views/decidim/comments/admin/shared/_availability_fields.html.erb +1 -1
- data/app/views/decidim/comments/comments/_load_more_comments.html.erb +22 -0
- data/app/views/decidim/comments/comments/create.js.erb +38 -4
- data/app/views/decidim/comments/comments/index.js.erb +58 -23
- data/app/views/decidim/comments/comments/load_more_comments.js.erb +74 -0
- data/app/views/decidim/comments/comments/update.js.erb +3 -13
- data/config/locales/ar.yml +0 -1
- data/config/locales/bg.yml +0 -1
- data/config/locales/bs-BA.yml +0 -1
- data/config/locales/ca-IT.yml +6 -4
- data/config/locales/ca.yml +6 -4
- data/config/locales/cs.yml +3 -6
- data/config/locales/de.yml +6 -4
- data/config/locales/el.yml +0 -1
- data/config/locales/en.yml +6 -3
- data/config/locales/es-MX.yml +6 -4
- data/config/locales/es-PY.yml +6 -4
- data/config/locales/es.yml +6 -4
- data/config/locales/eu.yml +14 -12
- data/config/locales/fi-plain.yml +6 -4
- data/config/locales/fi.yml +6 -4
- data/config/locales/fr-CA.yml +3 -4
- data/config/locales/fr.yml +3 -4
- data/config/locales/ga-IE.yml +0 -1
- data/config/locales/gl.yml +0 -1
- data/config/locales/hu.yml +0 -1
- data/config/locales/id-ID.yml +0 -1
- data/config/locales/is-IS.yml +1 -2
- data/config/locales/it.yml +0 -1
- data/config/locales/ja.yml +5 -3
- data/config/locales/lb.yml +0 -1
- data/config/locales/lt.yml +0 -1
- data/config/locales/lv.yml +0 -1
- data/config/locales/nl.yml +0 -1
- data/config/locales/no.yml +0 -1
- data/config/locales/pl.yml +1 -2
- data/config/locales/pt-BR.yml +6 -4
- data/config/locales/pt.yml +0 -1
- data/config/locales/ro-RO.yml +0 -5
- data/config/locales/ru.yml +0 -1
- data/config/locales/si-LK.yml +0 -1
- data/config/locales/sk.yml +0 -125
- data/config/locales/sl.yml +0 -1
- data/config/locales/sq-AL.yml +0 -1
- data/config/locales/sr-CS.yml +0 -1
- data/config/locales/sv.yml +0 -4
- data/config/locales/tr-TR.yml +0 -1
- data/config/locales/uk.yml +0 -1
- data/config/locales/zh-CN.yml +0 -1
- data/config/locales/zh-TW.yml +0 -1
- data/db/migrate/20260208201401_remove_user_group_comments.rb +13 -0
- data/decidim-comments.gemspec +6 -9
- data/lib/decidim/api/comment_mutation_type.rb +12 -4
- data/lib/decidim/api/commentable_interface.rb +1 -0
- data/lib/decidim/api/commentable_mutation_type.rb +4 -0
- data/lib/decidim/comments/commentable.rb +5 -0
- data/lib/decidim/comments/commentable_with_component.rb +9 -0
- data/lib/decidim/comments/engine.rb +11 -0
- data/lib/decidim/comments/test/factories.rb +1 -1
- data/lib/decidim/comments/test/shared_examples/translatable_comment.rb +1 -0
- data/lib/decidim/comments/version.rb +1 -1
- metadata +15 -14
- data/app/resolvers/decidim/comments/vote_comment_resolver.rb +0 -24
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
@use "stylesheets/decidim/
|
|
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-
|
|
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
|
|
35
|
-
|
|
36
|
-
case @options[:order_by]
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
49
|
-
|
|
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
|
-
|
|
63
|
-
if
|
|
64
|
-
|
|
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.
|
|
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
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2
|
+
<% root_resource = commentable.is_a?(Decidim::Comments::Comment) ? commentable.root_commentable : commentable %>
|
|
3
3
|
|
|
4
|
-
var
|
|
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
|
-
|
|
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
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
//
|
|
34
|
-
|
|
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
|
|
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
|
});
|
data/config/locales/ar.yml
CHANGED
data/config/locales/bg.yml
CHANGED
data/config/locales/bs-BA.yml
CHANGED
data/config/locales/ca-IT.yml
CHANGED
|
@@ -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:
|
data/config/locales/ca.yml
CHANGED
|
@@ -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:
|
data/config/locales/cs.yml
CHANGED
|
@@ -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:
|