decidim-posts 1.0.0
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 +7 -0
- data/LICENSE-AGPLv3.txt +661 -0
- data/README.md +40 -0
- data/Rakefile +9 -0
- data/app/cells/decidim/posts/comments/add_comment.erb +15 -0
- data/app/cells/decidim/posts/comments/comments_loading.erb +1 -0
- data/app/cells/decidim/posts/comments/order_control.erb +13 -0
- data/app/cells/decidim/posts/comments/show.erb +31 -0
- data/app/cells/decidim/posts/comments_cell.rb +158 -0
- data/app/cells/decidim/posts/content_blocks/posts/show.erb +3 -0
- data/app/cells/decidim/posts/content_blocks/posts_cell.rb +50 -0
- data/app/cells/decidim/posts/content_blocks/posts_settings_form/show.erb +3 -0
- data/app/cells/decidim/posts/content_blocks/posts_settings_form_cell.rb +23 -0
- data/app/cells/decidim/posts/feed_dropdown_metadata_cell.rb +19 -0
- data/app/cells/decidim/posts/meeting/show.erb +65 -0
- data/app/cells/decidim/posts/meeting_cell.rb +44 -0
- data/app/cells/decidim/posts/post/show.erb +30 -0
- data/app/cells/decidim/posts/post/survey.erb +68 -0
- data/app/cells/decidim/posts/post_attachments/show.erb +40 -0
- data/app/cells/decidim/posts/post_attachments_cell.rb +17 -0
- data/app/cells/decidim/posts/post_cell.rb +44 -0
- data/app/cells/decidim/posts/post_comment/show.erb +17 -0
- data/app/cells/decidim/posts/post_comment_cell.rb +49 -0
- data/app/cells/decidim/posts/post_header/show.erb +53 -0
- data/app/cells/decidim/posts/post_header_cell.rb +79 -0
- data/app/cells/decidim/posts/post_host/show.erb +74 -0
- data/app/cells/decidim/posts/post_host_cell.rb +73 -0
- data/app/cells/decidim/posts/post_metadata/show.erb +9 -0
- data/app/cells/decidim/posts/post_metadata_cell.rb +67 -0
- data/app/cells/decidim/posts/reaction_menu/show.erb +18 -0
- data/app/cells/decidim/posts/reaction_menu/styles.erb +7 -0
- data/app/cells/decidim/posts/reaction_menu_cell.rb +26 -0
- data/app/cells/decidim/posts/reactions/show.erb +8 -0
- data/app/cells/decidim/posts/reactions_cell.rb +22 -0
- data/app/commands/decidim/posts/add_reaction_to_resource.rb +90 -0
- data/app/commands/decidim/posts/create_post.rb +112 -0
- data/app/commands/decidim/posts/destroy_post.rb +20 -0
- data/app/commands/decidim/posts/remove_reaction_from_resource.rb +41 -0
- data/app/commands/decidim/posts/update_post.rb +72 -0
- data/app/controllers/concerns/decidim/posts/admin/filterable.rb +46 -0
- data/app/controllers/concerns/decidim/posts/reactionable.rb +31 -0
- data/app/controllers/decidim/posts/admin/application_controller.rb +26 -0
- data/app/controllers/decidim/posts/admin/posts_controller.rb +93 -0
- data/app/controllers/decidim/posts/application_controller.rb +18 -0
- data/app/controllers/decidim/posts/meetings_controller.rb +176 -0
- data/app/controllers/decidim/posts/posts_controller.rb +202 -0
- data/app/controllers/decidim/posts/reactions_controller.rb +54 -0
- data/app/controllers/decidim/posts/user_answers_controller.rb +36 -0
- data/app/events/decidim/posts/resource_reacted_event.rb +38 -0
- data/app/forms/decidim/posts/answer_form.rb +13 -0
- data/app/forms/decidim/posts/post_form.rb +121 -0
- data/app/forms/decidim/posts/question_form.rb +20 -0
- data/app/helpers/decidim/posts/admin/posts_helper.rb +8 -0
- data/app/helpers/decidim/posts/application_helper.rb +20 -0
- data/app/helpers/decidim/posts/post_cells_helper.rb +35 -0
- data/app/helpers/decidim/posts/posts_helper.rb +34 -0
- data/app/helpers/decidim/posts/reaction_helper.rb +22 -0
- data/app/models/decidim/posts/answer.rb +13 -0
- data/app/models/decidim/posts/application_record.rb +10 -0
- data/app/models/decidim/posts/post.rb +116 -0
- data/app/models/decidim/posts/question.rb +17 -0
- data/app/models/decidim/posts/reaction.rb +22 -0
- data/app/models/decidim/posts/reaction_type.rb +13 -0
- data/app/models/decidim/posts/user_answer.rb +11 -0
- data/app/packs/entrypoints/decidim_posts.js +5 -0
- data/app/packs/entrypoints/decidim_posts.scss +1 -0
- data/app/packs/images/decidim/posts/icon.svg +1 -0
- data/app/packs/src/decidim/posts/carousel.js +112 -0
- data/app/packs/src/decidim/posts/host_status.js +75 -0
- data/app/packs/src/decidim/posts/newFeeds.js +98 -0
- data/app/packs/src/decidim/posts/posts.js +272 -0
- data/app/packs/src/decidim/posts/submenu.js +46 -0
- data/app/packs/src/decidim/posts/survey.js +94 -0
- data/app/packs/stylesheets/decidim/posts/_variables.scss +10 -0
- data/app/packs/stylesheets/decidim/posts/posts.scss +415 -0
- data/app/permissions/decidim/posts/admin/permissions.rb +23 -0
- data/app/permissions/decidim/posts/permissions.rb +101 -0
- data/app/presenters/decidim/posts/post_presenter.rb +45 -0
- data/app/views/decidim/posts/admin/posts/_post-tr.html.erb +45 -0
- data/app/views/decidim/posts/admin/posts/index.html.erb +54 -0
- data/app/views/decidim/posts/meetings/edit.html.erb +24 -0
- data/app/views/decidim/posts/posts/_admin_options.html.erb +30 -0
- data/app/views/decidim/posts/posts/_attachment.html.erb +24 -0
- data/app/views/decidim/posts/posts/_edit_form.html.erb +16 -0
- data/app/views/decidim/posts/posts/_feed.html.erb +8 -0
- data/app/views/decidim/posts/posts/_form.html.erb +104 -0
- data/app/views/decidim/posts/posts/_index.html.erb +49 -0
- data/app/views/decidim/posts/posts/_meeting_form.erb +18 -0
- data/app/views/decidim/posts/posts/_new.html.erb +33 -0
- data/app/views/decidim/posts/posts/_new_survey.html.erb +20 -0
- data/app/views/decidim/posts/posts/_new_survey_answer.html.erb +11 -0
- data/app/views/decidim/posts/posts/_new_survey_question.html.erb +17 -0
- data/app/views/decidim/posts/posts/_post.html.erb +11 -0
- data/app/views/decidim/posts/posts/_post_form.html.erb +5 -0
- data/app/views/decidim/posts/posts/_sidebar.html.erb +36 -0
- data/app/views/decidim/posts/posts/edit.html.erb +34 -0
- data/app/views/decidim/posts/posts/index.html.erb +1 -0
- data/app/views/decidim/posts/posts/show.html.erb +12 -0
- data/app/views/decidim/posts/reactions/_update_buttons_and_counters.html.erb +1 -0
- data/config/assets.rb +9 -0
- data/config/i18n-tasks.yml +10 -0
- data/config/locales/bs.yml +215 -0
- data/config/locales/de.yml +217 -0
- data/config/locales/en.yml +216 -0
- data/config/locales/hr.yml +219 -0
- data/config/locales/it.yml +215 -0
- data/config/locales/sr.yml +220 -0
- data/config/locales/tr.yml +219 -0
- data/lib/decidim/posts/admin.rb +10 -0
- data/lib/decidim/posts/admin_engine.rb +25 -0
- data/lib/decidim/posts/component.rb +59 -0
- data/lib/decidim/posts/content_blocks/content_blocks_homepage.rb +19 -0
- data/lib/decidim/posts/engine.rb +84 -0
- data/lib/decidim/posts/test/factories.rb +14 -0
- data/lib/decidim/posts/version.rb +9 -0
- data/lib/decidim/posts.rb +13 -0
- metadata +183 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Posts
|
|
5
|
+
class PostCell < Decidim::ViewModel
|
|
6
|
+
include PostCellsHelper
|
|
7
|
+
include Cell::ViewModel::Partial
|
|
8
|
+
|
|
9
|
+
def show
|
|
10
|
+
render :show
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def post
|
|
14
|
+
model
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def post_body
|
|
18
|
+
simple_format(post_presenter.body)
|
|
19
|
+
# translated_attribute model.body
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def post_presenter
|
|
23
|
+
@post_presenter ||= present(model)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def post_category
|
|
27
|
+
model.category
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def post_commentable?
|
|
31
|
+
model.enable_comments?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def resource_image_path
|
|
35
|
+
model.attachments.first&.url
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def has_image?
|
|
39
|
+
resource_image_path.present?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<% if post_commentable? %>
|
|
2
|
+
<div class="posts__post_comments" data-post-id="<%= post.id %>">
|
|
3
|
+
<button class="button button__sm button__transparent-primary absolute newCommentBtn" data-post-id="<%= post.id %>" data-model-type="<%= model_type %>" aria-expanded="false" aria-controls="new_comment_for_<%= model_type %>_<%= post.id %> comments-for-<%= model_type %>-<%= post.id %>-threads"><%= t(".comment") %></button>
|
|
4
|
+
<%#= comments_for post %>
|
|
5
|
+
<%= cell(
|
|
6
|
+
"decidim/posts/comments",
|
|
7
|
+
post,
|
|
8
|
+
machine_translations: machine_translations_toggled?,
|
|
9
|
+
single_comment: params.fetch("commentId", nil),
|
|
10
|
+
order: options[:order],
|
|
11
|
+
polymorphic: options[:polymorphic]
|
|
12
|
+
).to_s %>
|
|
13
|
+
</div>
|
|
14
|
+
<% end %>
|
|
15
|
+
<style>
|
|
16
|
+
.comment__opinion-label, .comment__opinion-container{display:none;}
|
|
17
|
+
</style>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Posts
|
|
5
|
+
class PostCommentCell < Decidim::ViewModel
|
|
6
|
+
include Cell::ViewModel::Partial
|
|
7
|
+
include Decidim::ResourceHelper
|
|
8
|
+
include Decidim::Comments::CommentsHelper
|
|
9
|
+
def show
|
|
10
|
+
render :show
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def post
|
|
14
|
+
model
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def model_type
|
|
18
|
+
if model.is_a?(Decidim::Posts::Post)
|
|
19
|
+
"Post"
|
|
20
|
+
elsif model.is_a?(Decidim::Meetings::Meeting)
|
|
21
|
+
"Meeting"
|
|
22
|
+
else
|
|
23
|
+
"Post"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def post_commentable?
|
|
28
|
+
if model.respond_to?(:enable_comments?)
|
|
29
|
+
model.enable_comments?
|
|
30
|
+
elsif model.respond_to?(:comments_enabled?)
|
|
31
|
+
model.comments_enabled?
|
|
32
|
+
else
|
|
33
|
+
false # Default to false if neither method exists
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def machine_translations_toggled?
|
|
38
|
+
RequestStore.store[:toggle_machine_translations]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def comments_count
|
|
42
|
+
model.comments_count
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<div class="card__grid-metadata">
|
|
2
|
+
<% if post.is_a?(Decidim::Posts::Post) && post.highlighted %>
|
|
3
|
+
<div></div>
|
|
4
|
+
<% else %>
|
|
5
|
+
<% if post.author.is_a? Decidim::User %>
|
|
6
|
+
<%= cell("decidim/author", Decidim::UserPresenter.new(post.author), layout: :compact) %>
|
|
7
|
+
<% else %>
|
|
8
|
+
<%= cell("decidim/author", Decidim::Meetings::OfficialAuthorPresenter.new, layout: :compact) %>
|
|
9
|
+
<% end %>
|
|
10
|
+
<% end %>
|
|
11
|
+
<div class="posts__post_actions">
|
|
12
|
+
<div class="posts__post_actions_submenu relative self-start">
|
|
13
|
+
<% if show_menu? %>
|
|
14
|
+
<button type="button" aria-expanded="false" aria-haspopup="true" aria-controls="<%= dom_id(post) %>_posts__post_actions_submenu" id="<%= dom_id(post) %>_posts__post_actions_submenuButton" class="flex justify-center items-start min-w-[24px] min-h-[24px] openButton">
|
|
15
|
+
<span class="sr-only"><%= t('decidim.components.posts.actions.openMenu') %></span>
|
|
16
|
+
<span aria-hidden="true">...</span>
|
|
17
|
+
</button>
|
|
18
|
+
|
|
19
|
+
<ul id="<%= dom_id(post) %>_posts__post_actions_submenu" role="menu" aria-orientation="vertical" aria-labelledby="<%= dom_id(post) %>_posts__post_actions_submenuButton" class="absolute right-0 mt-2 px-4 py-2 bg-white divide-y divide-dashed divide-gray-100 rounded-md shadow-feedMenu overflow-hidden hidden z-10">
|
|
20
|
+
<% if show_report_button? %>
|
|
21
|
+
<li class="text-right py-1 text-feeds-sidebar" role="menuitem">
|
|
22
|
+
<%= cell("decidim/report_button", post, button_classes: "hover:text-black dropdown__item flex-row-reverse min-h-[24px]", modal_id: "flagModalPost#{post.id}") %>
|
|
23
|
+
</li>
|
|
24
|
+
<% end %>
|
|
25
|
+
<% if show_translate_button? %>
|
|
26
|
+
<li class="text-right py-1 text-feeds-sidebar" role="menuitem">
|
|
27
|
+
<%= link_to '#', class:'hover:text-black dropdown__item min-h-[24px]' do %>
|
|
28
|
+
<%= icon "translate" %>
|
|
29
|
+
<span><%= t('decidim.components.posts.actions.translate') %></span>
|
|
30
|
+
<% end %>
|
|
31
|
+
</li>
|
|
32
|
+
<% end %>
|
|
33
|
+
<% if post_editable? %>
|
|
34
|
+
<li class="text-right py-1 text-feeds-sidebar" role="menuitem">
|
|
35
|
+
<%= link_to edit_post_path, class: "hover:text-black dropdown__item min-h-[24px]" do %>
|
|
36
|
+
<%= icon "pencil-line" %>
|
|
37
|
+
<span><%= t('decidim.components.posts.actions.edit') %></span>
|
|
38
|
+
<% end %>
|
|
39
|
+
</li>
|
|
40
|
+
<% end %>
|
|
41
|
+
<% if post_deleteable? %>
|
|
42
|
+
<li class="text-right py-1 text-feeds-sidebar" role="menuitem">
|
|
43
|
+
<%= button_to delete_post_path, method: delete_method, class: "hover:text-black dropdown__item min-h-[24px]", data: { confirm: t('decidim.components.posts.actions.deleteConfirmation') } do %>
|
|
44
|
+
<%= icon "delete-bin-line" %>
|
|
45
|
+
<span><%= t('decidim.components.posts.actions.delete') %></span>
|
|
46
|
+
<% end %>
|
|
47
|
+
</li>
|
|
48
|
+
<% end %>
|
|
49
|
+
</ul>
|
|
50
|
+
<% end %>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Posts
|
|
5
|
+
class PostHeaderCell < Decidim::ViewModel
|
|
6
|
+
include Cell::ViewModel::Partial
|
|
7
|
+
include Decidim::EndorsableHelper
|
|
8
|
+
def show
|
|
9
|
+
render :show
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def post
|
|
13
|
+
model
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def edit_post_path
|
|
17
|
+
if post.component.manifest_name == "meetings"
|
|
18
|
+
# use MeetingsController from posts module
|
|
19
|
+
Decidim::EngineRouter.main_proxy(posts_component_for_meeting(post)).edit_meeting_path(post)
|
|
20
|
+
else
|
|
21
|
+
Decidim::EngineRouter.main_proxy(post.component).edit_post_path(post)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def delete_post_path
|
|
26
|
+
if post.component.manifest_name == "meetings"
|
|
27
|
+
# use MeetingsController from posts module
|
|
28
|
+
Decidim::EngineRouter.main_proxy(posts_component_for_meeting(post)).withdraw_meeting_path(assembly_slug: post.component.participatory_space.slug, component_id: 1, id: post)
|
|
29
|
+
else
|
|
30
|
+
Decidim::EngineRouter.main_proxy(post.component).post_path(post)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def delete_method
|
|
35
|
+
if post.component.manifest_name == "meetings"
|
|
36
|
+
:put
|
|
37
|
+
else
|
|
38
|
+
:delete
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def post_editable?
|
|
43
|
+
if post.component.manifest_name == "meetings"
|
|
44
|
+
post.authored_by?(current_user)
|
|
45
|
+
else
|
|
46
|
+
# Don't allow editing of survey posts
|
|
47
|
+
return false if post.category == "survey"
|
|
48
|
+
post.editable_by?(current_user)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def post_deleteable?
|
|
53
|
+
if post.component.manifest_name == "meetings"
|
|
54
|
+
post.withdrawable_by?(current_user)
|
|
55
|
+
else
|
|
56
|
+
post.deleteable_by?(current_user)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def show_report_button?
|
|
61
|
+
post.author != current_user && post.class == Decidim::Posts::Post
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def show_translate_button?
|
|
65
|
+
false # disbled for now
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def show_menu?
|
|
69
|
+
show_report_button? || post_editable? || post_deleteable? || show_translate_button?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def posts_component_for_meeting(meeting)
|
|
75
|
+
@posts_component ||= meeting.component.participatory_space.components.find_by(manifest_name: "posts")
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<%= cell("decidim/posts/post_header", post) %>
|
|
2
|
+
|
|
3
|
+
<% unless post_highlighted %>
|
|
4
|
+
<% if official_post %>
|
|
5
|
+
<%= icon "home-gear-line", class: 'host-icon' %>
|
|
6
|
+
<% end %>
|
|
7
|
+
<% end %>
|
|
8
|
+
|
|
9
|
+
<%= cell("decidim/posts/post_attachments", post) %>
|
|
10
|
+
<div class="card__grid-text">
|
|
11
|
+
<% should_truncate = strip_tags(post_body).length > 300 %>
|
|
12
|
+
<div class="content-block__description editor-content" <%= "data-component='accordion'" if should_truncate %>>
|
|
13
|
+
<% if should_truncate %>
|
|
14
|
+
<% seed = SecureRandom.hex(4) %>
|
|
15
|
+
<div id="panel-view-more-<%= seed %>" aria-hidden="true">
|
|
16
|
+
<%= post_body %>
|
|
17
|
+
</div>
|
|
18
|
+
<button class="button button__sm button__text-secondary mt-2" data-controls="panel-view-more-<%= seed %>" aria-expanded="false">
|
|
19
|
+
<span>
|
|
20
|
+
<%= t("view_more", scope: "layouts.decidim.announcements") %>
|
|
21
|
+
</span>
|
|
22
|
+
<%= icon "arrow-down-s-line" %>
|
|
23
|
+
<span>
|
|
24
|
+
<%= t("view_less", scope: "layouts.decidim.announcements") %>
|
|
25
|
+
</span>
|
|
26
|
+
<%= icon "arrow-up-s-line" %>
|
|
27
|
+
</button>
|
|
28
|
+
<% else %>
|
|
29
|
+
<%= post_body %>
|
|
30
|
+
<% end %>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
<% if status_class %>
|
|
34
|
+
<div class="bg-white rounded-2xl border-4 border-<%= status_class %> flex-col justify-between items-center inline-flex mx-2">
|
|
35
|
+
<div class="self-stretch px-2 py-1 justify-start items-center gap-2 inline-flex">
|
|
36
|
+
<time class="p-2 font-bold text-lg"><%= I18n.l(post.updated_at, format: :long) %></time>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="self-stretch grow shrink basis-0 bg-<%= status_class %> rounded-lg justify-center items-center gap-2 inline-flex">
|
|
39
|
+
<div class="p-2 text-xl font-bold uppercase text-black"><%= post_status_text %></div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<% end %>
|
|
43
|
+
|
|
44
|
+
<% unless post_highlighted || status_class %>
|
|
45
|
+
<% if post_commentable? %>
|
|
46
|
+
<%= cell("decidim/posts/post_comment", post) %>
|
|
47
|
+
<% end %>
|
|
48
|
+
<% end %>
|
|
49
|
+
<% if participatory_space.admins.exists?(id:current_user.id) && !official_post %>
|
|
50
|
+
<div class="flex flex-col flex-wrap py-4 px-2 justify-center items-center gap-4">
|
|
51
|
+
<% if post_status && post_status != 0 %>
|
|
52
|
+
<% modalDialog = t('decidim.posts.posts.host.status.dialogBodyMsg') %>
|
|
53
|
+
<% else %>
|
|
54
|
+
<% modalDialog = t('decidim.posts.posts.host.status.dialogBodyMsg') + "<br>" + t('decidim.posts.posts.host.status.dialogBodyMsgComments') %>
|
|
55
|
+
<% end %>
|
|
56
|
+
<% if post_status && post_status != 0 %>
|
|
57
|
+
<button class="button button__sm button__transparent-primary changeStatusButton" data-post-status="0" data-post-id="<%= post.id %>" data-dialogmsg="<%=modalDialog%>" aria-controls="confirmationModal" aria-haspopup="dialog">
|
|
58
|
+
<%= t('decidim.posts.posts.host.status.undone') %>
|
|
59
|
+
<%= icon('close-line') %>
|
|
60
|
+
</button>
|
|
61
|
+
<% end %>
|
|
62
|
+
<button class="button button__sm button__transparent-primary changeStatusButton" data-post-status="1" data-post-id="<%= post.id %>" data-dialogmsg="<%=modalDialog%>" aria-controls="confirmationModal" aria-haspopup="dialog">
|
|
63
|
+
<%= t('decidim.posts.posts.host.status.processing') %>
|
|
64
|
+
<%= icon('arrow-right-line') %>
|
|
65
|
+
</button>
|
|
66
|
+
<button class="button button__sm button__transparent-primary changeStatusButton" data-post-status="2" data-post-id="<%= post.id %>" data-dialogmsg="<%=modalDialog%>" aria-controls="confirmationModal" aria-haspopup="dialog">
|
|
67
|
+
<%= t('decidim.posts.posts.host.status.done') %>
|
|
68
|
+
<%= icon('arrow-right-line') %>
|
|
69
|
+
</button>
|
|
70
|
+
</div>
|
|
71
|
+
<% end %>
|
|
72
|
+
<% unless post_highlighted %>
|
|
73
|
+
<%= cell("decidim/posts/post_metadata", post) %>
|
|
74
|
+
<% end %>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Posts
|
|
5
|
+
class PostHostCell < Decidim::ViewModel
|
|
6
|
+
include Cell::ViewModel::Partial
|
|
7
|
+
include ActiveModel::Conversion
|
|
8
|
+
include PostCellsHelper
|
|
9
|
+
|
|
10
|
+
delegate :allowed_to?, to: :controller, prefix: false
|
|
11
|
+
|
|
12
|
+
def show
|
|
13
|
+
render :show
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def post
|
|
17
|
+
model
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def post_body
|
|
21
|
+
translated_attribute model.body
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def post_category
|
|
25
|
+
model.category
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# post status / for host post
|
|
29
|
+
# 0 default
|
|
30
|
+
# 1 bearbeitung
|
|
31
|
+
# 2 erledigt
|
|
32
|
+
|
|
33
|
+
def post_status
|
|
34
|
+
model.status
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def status_class
|
|
38
|
+
case post_status
|
|
39
|
+
when 0
|
|
40
|
+
nil
|
|
41
|
+
when 1
|
|
42
|
+
'warning'
|
|
43
|
+
when 2
|
|
44
|
+
'success'
|
|
45
|
+
else
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def post_status_text
|
|
51
|
+
case post_status
|
|
52
|
+
when 1
|
|
53
|
+
I18n.t('decidim.posts.posts.host.status.processing')
|
|
54
|
+
when 2
|
|
55
|
+
I18n.t('decidim.posts.posts.host.status.done')
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def post_highlighted
|
|
60
|
+
model.highlighted
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def post_commentable?
|
|
64
|
+
model.enable_comments?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def official_post
|
|
68
|
+
participatory_space.admins.exists?(id: post.author.id)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<div class="posts__post_metadata px-2 py-1 text-sm border-t-[1px] border-feeds-<%=category%> flex justify-between items-end ">
|
|
2
|
+
<div class="flex flex-col gap-1">
|
|
3
|
+
<div id="feeds_post-<%= post.id %>_reactions" class="flex gap-2 items-center" aria-live="polite">
|
|
4
|
+
<%= cell("decidim/posts/reactions", post) if post.is_a?(Decidim::Posts::Post) %>
|
|
5
|
+
</div>
|
|
6
|
+
<%= cell("decidim/posts/reaction_menu", post) if post.is_a?(Decidim::Posts::Post) %>
|
|
7
|
+
</div>
|
|
8
|
+
<time><%= @time_since_update %></time>
|
|
9
|
+
</div>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Posts
|
|
5
|
+
class PostMetadataCell < Decidim::ViewModel
|
|
6
|
+
include Cell::ViewModel::Partial
|
|
7
|
+
include Decidim::EndorsableHelper
|
|
8
|
+
def show
|
|
9
|
+
@time_since_update = time_since_update
|
|
10
|
+
@category_info = category_info
|
|
11
|
+
render :show
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def post
|
|
15
|
+
model
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def time_since_update
|
|
21
|
+
time_difference = Time.now - post.created_at
|
|
22
|
+
minutes = (time_difference / 60).to_i
|
|
23
|
+
hours = (time_difference / 3600).to_i
|
|
24
|
+
days = (time_difference / 86400).to_i
|
|
25
|
+
months = (time_difference / 2592000).to_i # Approximate month duration
|
|
26
|
+
years = (time_difference / 31536000).to_i
|
|
27
|
+
|
|
28
|
+
if minutes < 60
|
|
29
|
+
minutes.to_s + ' ' + t("decidim.posts.time.minutes", count: minutes)
|
|
30
|
+
elsif hours < 24
|
|
31
|
+
hours.to_s + ' ' + t("decidim.posts.time.hours", count: hours)
|
|
32
|
+
elsif days < 30
|
|
33
|
+
days.to_s + ' ' + t("decidim.posts.time.days", count: days)
|
|
34
|
+
elsif months < 12
|
|
35
|
+
months.to_s + ' ' + t("decidim.posts.time.months", count: months)
|
|
36
|
+
else
|
|
37
|
+
years.to_s + ' ' + t("decidim.posts.time.years", count: years)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def category_info
|
|
42
|
+
filters = [
|
|
43
|
+
{ filter: 'post', icon: 'home-5-line' },
|
|
44
|
+
{ filter: 'sharecare', icon: 'shake-hands-line' },
|
|
45
|
+
{ filter: 'calendar', icon: 'calendar-line' },
|
|
46
|
+
{ filter: 'survey', icon: 'draft-line' },
|
|
47
|
+
{ filter: 'host', icon: 'home-gear-line' }
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
current_filter = filters.find { |f| f[:filter] == category }
|
|
51
|
+
{
|
|
52
|
+
icon: current_filter[:icon],
|
|
53
|
+
label: t("decidim.components.posts.filter.#{current_filter[:filter]}")
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def category
|
|
58
|
+
if post.is_a?(Decidim::Meetings::Meeting)
|
|
59
|
+
'calendar'
|
|
60
|
+
else
|
|
61
|
+
post.category
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<div class="relative posts__post_reactions_submenu">
|
|
2
|
+
<% user_reaction = post.reactions.find_by(author: current_user) %>
|
|
3
|
+
<% data_icon = user_reaction ? "data-reaction=\"#{user_reaction.reaction_type.name}\"" : 'data-reaction=""' %>
|
|
4
|
+
<button type="button" aria-expanded="false" aria-haspopup="true" aria-controls="<%= dom_id(post) %>_posts__post_reactions_submenu" id="<%= dom_id(post) %>_posts__post_reactions_submenuButton" class="openButton flex gap-1 items-center text-xl" <%=data_icon%> aria-live="polite">
|
|
5
|
+
<span class="text-sm"><%= t("decidim.endorsement_buttons_cell.already_endorsed") %></span>
|
|
6
|
+
<span class="reaction_icon text-black"><%= icon('heart-add-line', class: 'text-black') %></span>
|
|
7
|
+
</button>
|
|
8
|
+
<ul id="<%= dom_id(post) %>_posts__post_reactions_submenu" role="menu" aria-orientation="vertical" aria-labelledby="<%= dom_id(post) %>_posts__post_reactions_submenuButton" class="absolute left-[60px] -top-[40px] p-2 flex gap-2 bg-white rounded-md shadow-feedMenu overflow-hidden hidden" aria-label="<%= t("decidim.components.posts.reactions.title") %>">
|
|
9
|
+
<% reaction_types.each do |reaction_type| %>
|
|
10
|
+
<li role="menuitem">
|
|
11
|
+
<a href="#" class="reaction_btn block text-xl w-[24px] h-[24px]" data-reaction="<%= reaction_type.name %>" data-reaction-id="<%= reaction_type.id %>" data-reaction-url="<%= reactions_path %>" data-reactionable-id="<%= post.to_global_id.to_s %>">
|
|
12
|
+
<%= reaction_icon(reaction_type.icon_name) %>
|
|
13
|
+
<span class="sr-only"><%= t("decidim.components.posts.reactions.#{reaction_type.name}") %></span>
|
|
14
|
+
</a>
|
|
15
|
+
</li>
|
|
16
|
+
<% end %>
|
|
17
|
+
</ul>
|
|
18
|
+
</div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Decidim
|
|
2
|
+
module Posts
|
|
3
|
+
class ReactionMenuCell < Decidim::ViewModel
|
|
4
|
+
include PostCellsHelper
|
|
5
|
+
include ReactionHelper
|
|
6
|
+
include Cell::ViewModel::Partial
|
|
7
|
+
|
|
8
|
+
def show
|
|
9
|
+
render :show
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def styles
|
|
13
|
+
render :styles
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def post
|
|
17
|
+
model
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def reaction_types
|
|
21
|
+
ReactionType.order(:position)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<div class="flex pl-1">
|
|
2
|
+
<% grouped_reactions_for(post).each do |reaction_type, reactions| %>
|
|
3
|
+
<span class="text-sm ml-[-0.25rem]"><%= reaction_icon(reaction_type.icon_name) %></span>
|
|
4
|
+
<% end %>
|
|
5
|
+
<span class="total-reactions ml-1">
|
|
6
|
+
<%= post.reactions_count if post.reactions_count > 0 %>
|
|
7
|
+
</span>
|
|
8
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Decidim
|
|
2
|
+
module Posts
|
|
3
|
+
class ReactionsCell < Decidim::ViewModel
|
|
4
|
+
include PostCellsHelper
|
|
5
|
+
include ReactionHelper
|
|
6
|
+
include Cell::ViewModel::Partial
|
|
7
|
+
|
|
8
|
+
def show
|
|
9
|
+
render :show
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def post
|
|
13
|
+
model
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def grouped_reactions_for(resource)
|
|
17
|
+
resource.reactions.group_by(&:reaction_type)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Decidim
|
|
4
|
+
module Posts
|
|
5
|
+
# A command with all the business logic related with a user reacting to a resource.
|
|
6
|
+
class AddReactionToResource < Decidim::Command
|
|
7
|
+
# Public: Initializes the command.
|
|
8
|
+
#
|
|
9
|
+
# resource - An instance of Decidim::Posts::Reactionable.
|
|
10
|
+
# current_user - The current user.
|
|
11
|
+
# current_group_id- (optional) The current_grup that is reacting to the Resource.
|
|
12
|
+
# reaction_type_id - The id of the reaction type (emoji) that the user is reacting with.
|
|
13
|
+
def initialize(resource, current_user, current_group_id = nil, reaction_type_id)
|
|
14
|
+
@resource = resource
|
|
15
|
+
@current_user = current_user
|
|
16
|
+
@current_group_id = current_group_id
|
|
17
|
+
@reaction_type = ReactionType.find(reaction_type_id)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Executes the command. Broadcasts these events:
|
|
21
|
+
#
|
|
22
|
+
# - :ok when everything is valid, together with the resource reaction.
|
|
23
|
+
# - :invalid if the form was not valid and we could not proceed.
|
|
24
|
+
#
|
|
25
|
+
# Returns nothing.
|
|
26
|
+
def call
|
|
27
|
+
return broadcast(:invalid) if existing_group_reaction?
|
|
28
|
+
|
|
29
|
+
reaction = @resource.reactions.find_by(author: @current_user)
|
|
30
|
+
if reaction.present?
|
|
31
|
+
if reaction.reaction_type == @reaction_type
|
|
32
|
+
if delete_reaction(reaction)
|
|
33
|
+
broadcast(:ok, nil)
|
|
34
|
+
else
|
|
35
|
+
broadcast(:invalid)
|
|
36
|
+
end
|
|
37
|
+
else
|
|
38
|
+
if reaction.update(reaction_type: @reaction_type)
|
|
39
|
+
broadcast(:ok, reaction)
|
|
40
|
+
else
|
|
41
|
+
broadcast(:invalid)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
reaction = build_resource_reaction unless reaction
|
|
46
|
+
if reaction.save
|
|
47
|
+
notify_reactor_followers
|
|
48
|
+
broadcast(:ok, reaction)
|
|
49
|
+
else
|
|
50
|
+
broadcast(:invalid)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
rescue ActiveRecord::RecordNotUnique
|
|
54
|
+
broadcast(:invalid)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def existing_group_reaction?
|
|
60
|
+
@current_group_id.present? && @resource.reactions.exists?(decidim_user_group_id: @current_group_id)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def build_resource_reaction
|
|
64
|
+
reaction = @resource.reactions.build(author: @current_user, reaction_type: @reaction_type)
|
|
65
|
+
reaction.user_group = user_groups.find(@current_group_id) if @current_group_id.present?
|
|
66
|
+
reaction
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def delete_reaction(reaction)
|
|
70
|
+
reaction.destroy
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def user_groups
|
|
74
|
+
Decidim::UserGroups::ManageableUserGroups.for(@current_user).verified
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def notify_reactor_followers
|
|
78
|
+
Decidim::EventsManager.publish(
|
|
79
|
+
event: "decidim.events.resource_reacted",
|
|
80
|
+
event_class: Decidim::Posts::ResourceReactedEvent,
|
|
81
|
+
resource: @resource,
|
|
82
|
+
followers: @current_user.followers,
|
|
83
|
+
extra: {
|
|
84
|
+
reactor_id: @current_user.id
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|