decidim-posts 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|