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.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +40 -0
  4. data/Rakefile +9 -0
  5. data/app/cells/decidim/posts/comments/add_comment.erb +15 -0
  6. data/app/cells/decidim/posts/comments/comments_loading.erb +1 -0
  7. data/app/cells/decidim/posts/comments/order_control.erb +13 -0
  8. data/app/cells/decidim/posts/comments/show.erb +31 -0
  9. data/app/cells/decidim/posts/comments_cell.rb +158 -0
  10. data/app/cells/decidim/posts/content_blocks/posts/show.erb +3 -0
  11. data/app/cells/decidim/posts/content_blocks/posts_cell.rb +50 -0
  12. data/app/cells/decidim/posts/content_blocks/posts_settings_form/show.erb +3 -0
  13. data/app/cells/decidim/posts/content_blocks/posts_settings_form_cell.rb +23 -0
  14. data/app/cells/decidim/posts/feed_dropdown_metadata_cell.rb +19 -0
  15. data/app/cells/decidim/posts/meeting/show.erb +65 -0
  16. data/app/cells/decidim/posts/meeting_cell.rb +44 -0
  17. data/app/cells/decidim/posts/post/show.erb +30 -0
  18. data/app/cells/decidim/posts/post/survey.erb +68 -0
  19. data/app/cells/decidim/posts/post_attachments/show.erb +40 -0
  20. data/app/cells/decidim/posts/post_attachments_cell.rb +17 -0
  21. data/app/cells/decidim/posts/post_cell.rb +44 -0
  22. data/app/cells/decidim/posts/post_comment/show.erb +17 -0
  23. data/app/cells/decidim/posts/post_comment_cell.rb +49 -0
  24. data/app/cells/decidim/posts/post_header/show.erb +53 -0
  25. data/app/cells/decidim/posts/post_header_cell.rb +79 -0
  26. data/app/cells/decidim/posts/post_host/show.erb +74 -0
  27. data/app/cells/decidim/posts/post_host_cell.rb +73 -0
  28. data/app/cells/decidim/posts/post_metadata/show.erb +9 -0
  29. data/app/cells/decidim/posts/post_metadata_cell.rb +67 -0
  30. data/app/cells/decidim/posts/reaction_menu/show.erb +18 -0
  31. data/app/cells/decidim/posts/reaction_menu/styles.erb +7 -0
  32. data/app/cells/decidim/posts/reaction_menu_cell.rb +26 -0
  33. data/app/cells/decidim/posts/reactions/show.erb +8 -0
  34. data/app/cells/decidim/posts/reactions_cell.rb +22 -0
  35. data/app/commands/decidim/posts/add_reaction_to_resource.rb +90 -0
  36. data/app/commands/decidim/posts/create_post.rb +112 -0
  37. data/app/commands/decidim/posts/destroy_post.rb +20 -0
  38. data/app/commands/decidim/posts/remove_reaction_from_resource.rb +41 -0
  39. data/app/commands/decidim/posts/update_post.rb +72 -0
  40. data/app/controllers/concerns/decidim/posts/admin/filterable.rb +46 -0
  41. data/app/controllers/concerns/decidim/posts/reactionable.rb +31 -0
  42. data/app/controllers/decidim/posts/admin/application_controller.rb +26 -0
  43. data/app/controllers/decidim/posts/admin/posts_controller.rb +93 -0
  44. data/app/controllers/decidim/posts/application_controller.rb +18 -0
  45. data/app/controllers/decidim/posts/meetings_controller.rb +176 -0
  46. data/app/controllers/decidim/posts/posts_controller.rb +202 -0
  47. data/app/controllers/decidim/posts/reactions_controller.rb +54 -0
  48. data/app/controllers/decidim/posts/user_answers_controller.rb +36 -0
  49. data/app/events/decidim/posts/resource_reacted_event.rb +38 -0
  50. data/app/forms/decidim/posts/answer_form.rb +13 -0
  51. data/app/forms/decidim/posts/post_form.rb +121 -0
  52. data/app/forms/decidim/posts/question_form.rb +20 -0
  53. data/app/helpers/decidim/posts/admin/posts_helper.rb +8 -0
  54. data/app/helpers/decidim/posts/application_helper.rb +20 -0
  55. data/app/helpers/decidim/posts/post_cells_helper.rb +35 -0
  56. data/app/helpers/decidim/posts/posts_helper.rb +34 -0
  57. data/app/helpers/decidim/posts/reaction_helper.rb +22 -0
  58. data/app/models/decidim/posts/answer.rb +13 -0
  59. data/app/models/decidim/posts/application_record.rb +10 -0
  60. data/app/models/decidim/posts/post.rb +116 -0
  61. data/app/models/decidim/posts/question.rb +17 -0
  62. data/app/models/decidim/posts/reaction.rb +22 -0
  63. data/app/models/decidim/posts/reaction_type.rb +13 -0
  64. data/app/models/decidim/posts/user_answer.rb +11 -0
  65. data/app/packs/entrypoints/decidim_posts.js +5 -0
  66. data/app/packs/entrypoints/decidim_posts.scss +1 -0
  67. data/app/packs/images/decidim/posts/icon.svg +1 -0
  68. data/app/packs/src/decidim/posts/carousel.js +112 -0
  69. data/app/packs/src/decidim/posts/host_status.js +75 -0
  70. data/app/packs/src/decidim/posts/newFeeds.js +98 -0
  71. data/app/packs/src/decidim/posts/posts.js +272 -0
  72. data/app/packs/src/decidim/posts/submenu.js +46 -0
  73. data/app/packs/src/decidim/posts/survey.js +94 -0
  74. data/app/packs/stylesheets/decidim/posts/_variables.scss +10 -0
  75. data/app/packs/stylesheets/decidim/posts/posts.scss +415 -0
  76. data/app/permissions/decidim/posts/admin/permissions.rb +23 -0
  77. data/app/permissions/decidim/posts/permissions.rb +101 -0
  78. data/app/presenters/decidim/posts/post_presenter.rb +45 -0
  79. data/app/views/decidim/posts/admin/posts/_post-tr.html.erb +45 -0
  80. data/app/views/decidim/posts/admin/posts/index.html.erb +54 -0
  81. data/app/views/decidim/posts/meetings/edit.html.erb +24 -0
  82. data/app/views/decidim/posts/posts/_admin_options.html.erb +30 -0
  83. data/app/views/decidim/posts/posts/_attachment.html.erb +24 -0
  84. data/app/views/decidim/posts/posts/_edit_form.html.erb +16 -0
  85. data/app/views/decidim/posts/posts/_feed.html.erb +8 -0
  86. data/app/views/decidim/posts/posts/_form.html.erb +104 -0
  87. data/app/views/decidim/posts/posts/_index.html.erb +49 -0
  88. data/app/views/decidim/posts/posts/_meeting_form.erb +18 -0
  89. data/app/views/decidim/posts/posts/_new.html.erb +33 -0
  90. data/app/views/decidim/posts/posts/_new_survey.html.erb +20 -0
  91. data/app/views/decidim/posts/posts/_new_survey_answer.html.erb +11 -0
  92. data/app/views/decidim/posts/posts/_new_survey_question.html.erb +17 -0
  93. data/app/views/decidim/posts/posts/_post.html.erb +11 -0
  94. data/app/views/decidim/posts/posts/_post_form.html.erb +5 -0
  95. data/app/views/decidim/posts/posts/_sidebar.html.erb +36 -0
  96. data/app/views/decidim/posts/posts/edit.html.erb +34 -0
  97. data/app/views/decidim/posts/posts/index.html.erb +1 -0
  98. data/app/views/decidim/posts/posts/show.html.erb +12 -0
  99. data/app/views/decidim/posts/reactions/_update_buttons_and_counters.html.erb +1 -0
  100. data/config/assets.rb +9 -0
  101. data/config/i18n-tasks.yml +10 -0
  102. data/config/locales/bs.yml +215 -0
  103. data/config/locales/de.yml +217 -0
  104. data/config/locales/en.yml +216 -0
  105. data/config/locales/hr.yml +219 -0
  106. data/config/locales/it.yml +215 -0
  107. data/config/locales/sr.yml +220 -0
  108. data/config/locales/tr.yml +219 -0
  109. data/lib/decidim/posts/admin.rb +10 -0
  110. data/lib/decidim/posts/admin_engine.rb +25 -0
  111. data/lib/decidim/posts/component.rb +59 -0
  112. data/lib/decidim/posts/content_blocks/content_blocks_homepage.rb +19 -0
  113. data/lib/decidim/posts/engine.rb +84 -0
  114. data/lib/decidim/posts/test/factories.rb +14 -0
  115. data/lib/decidim/posts/version.rb +9 -0
  116. data/lib/decidim/posts.rb +13 -0
  117. metadata +183 -0
@@ -0,0 +1,116 @@
1
+ module Decidim
2
+ module Posts
3
+ class Post < Feeds::ApplicationRecord
4
+ include Decidim::Resourceable
5
+ include Decidim::Authorable
6
+ include Decidim::Reportable
7
+ include Decidim::HasAttachments
8
+ include Decidim::Comments::Commentable
9
+ include Decidim::Searchable
10
+ include Decidim::Endorsable
11
+ include Decidim::TranslatableResource
12
+ include Decidim::TranslatableAttributes
13
+ include Decidim::Fingerprintable
14
+ include Decidim::HasComponent
15
+ include Decidim::FilterableResource
16
+ include Decidim::Posts::Reactionable
17
+ # include Decidim::Loggable
18
+ # include Decidim::DownloadYourData
19
+ # include Decidim::Randomable
20
+ # include Decidim::ScopableResource
21
+ # include Decidim::HasReference
22
+ # include Decidim::HasCategory
23
+ # include Decidim::Followable
24
+ # include Decidim::Traceable
25
+ # include Decidim::NewsletterParticipant
26
+
27
+ # belongs_to :organization, class_name: "Decidim::Organization"
28
+
29
+ has_many :questions, class_name: "Decidim::Posts::Question", dependent: :destroy, foreign_key: "decidim_posts_post_id"
30
+ # accepts_nested_attributes_for :questions, reject_if: ->(attributes){ attributes['name'].blank? }, allow_destroy: true
31
+
32
+ component_manifest_name "posts"
33
+
34
+ translatable_fields :body
35
+
36
+ fingerprint fields: [:body]
37
+
38
+ validates :body, presence: true
39
+
40
+ searchable_fields(
41
+ {
42
+ participatory_space: { component: :participatory_space },
43
+ A: :body,
44
+ datetime: :created_at
45
+ },
46
+ index_on_create: true,
47
+ index_on_update: true
48
+ )
49
+
50
+ scope :filter_category, ->(category) { where(category: category) if category.present? }
51
+
52
+ # Posts don't have a title, but Commentable requires it, so let's extract the first words of each translation of the body
53
+ def title
54
+ truncated_body = {}
55
+ body.each do |locale, translations|
56
+ if locale == "machine_translations"
57
+ truncated_body[locale] = {}
58
+ translations.each do |key, value|
59
+ next if value.class != String
60
+ truncated_body[locale][key] = value.truncate_words(4)
61
+ end
62
+ else
63
+ next if translations.class != String
64
+ truncated_body[locale] = translations.truncate_words(4)
65
+ end
66
+ end
67
+ truncated_body
68
+ end
69
+
70
+ def editable_by?(user)
71
+ user == author
72
+ end
73
+
74
+ def deleteable_by?(user)
75
+ user == author || user.admin?
76
+ end
77
+
78
+ # Public: Overrides the `reported_content_url` Reportable concern method.
79
+ def reported_content_url
80
+ ResourceLocatorPresenter.new(self).url
81
+ end
82
+
83
+ # Public: Overrides the `reported_attributes` Reportable concern method.
84
+ def reported_attributes
85
+ [:body]
86
+ end
87
+
88
+ # Public: Overrides the `reported_searchable_content_extras` Reportable concern method.
89
+ def reported_searchable_content_extras
90
+ [author.name]
91
+ end
92
+
93
+ def survey_responses_count
94
+ # questions.includes(answers: :user_answers).map(&:answers).flatten.map(&:user_answers).flatten.pluck(:decidim_user_id).uniq.count
95
+ answer_ids = questions.includes(:answers).map(&:answers).flatten.pluck(:id)
96
+ @survey_responses_count ||= UserAnswer.where(decidim_posts_answer_id: answer_ids).distinct.count(:decidim_user_id)
97
+ end
98
+
99
+ # Public: Overrides the `comments_have_alignment?` Commentable concern method.
100
+ # Whether the object's comments can have alignment or not. It enables the
101
+ # alignment selector in the add new comment form.
102
+ def comments_have_alignment?
103
+ false
104
+ end
105
+
106
+ # Public: Overrides the `comments_have_votes?` Commentable concern method.
107
+ def comments_have_votes?
108
+ true
109
+ end
110
+
111
+ def users_to_notify_on_comment_created
112
+ [author]
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,17 @@
1
+ module Decidim
2
+ module Posts
3
+ class Question < Feeds::ApplicationRecord
4
+ include Decidim::TranslatableResource
5
+ include Decidim::TranslatableAttributes
6
+
7
+ belongs_to :post, class_name: "Decidim::Posts::Post", foreign_key: "decidim_posts_post_id"
8
+ has_many :answers, class_name: "Decidim::Posts::Answer", dependent: :destroy, foreign_key: "decidim_posts_question_id"
9
+ # accepts_nested_attributes_for :answers, allow_destroy: true
10
+
11
+ enum question_type: { single_choice: 0, multiple_choice: 1 }
12
+
13
+ translatable_fields :title
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Posts
5
+ class Reaction < ApplicationRecord
6
+ include Decidim::Authorable
7
+
8
+ belongs_to :resource, polymorphic: true, counter_cache: true#, touch: true
9
+ belongs_to :reaction_type, class_name: "Decidim::Posts::ReactionType"
10
+
11
+ validates :reaction_type, presence: true
12
+
13
+ def self.reaction_types
14
+ Decidim::Posts::ReactionType.all
15
+ end
16
+
17
+ def organization
18
+ resource&.component&.organization
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Posts
5
+ # A reaction type for posts.
6
+ class ReactionType < Decidim::ApplicationRecord
7
+ has_many :reactions, dependent: :destroy
8
+
9
+ validates :name, presence: true, uniqueness: true
10
+ validates :icon_name, presence: true
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module Decidim
2
+ module Posts
3
+ class UserAnswer < Feeds::ApplicationRecord
4
+ belongs_to :decidim_posts_answer, class_name: 'Decidim::Posts::Answer', foreign_key: "decidim_posts_answer_id"
5
+ belongs_to :decidim_user, class_name: 'Decidim::User', foreign_key: "decidim_user_id"
6
+
7
+ # validate combination of user and answer
8
+ validates :decidim_user_id, uniqueness: { scope: :decidim_posts_answer_id }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ import 'src/decidim/posts/posts.js';
2
+
3
+ require.context('../images', true);
4
+
5
+ import 'entrypoints/decidim_posts.scss';
@@ -0,0 +1 @@
1
+ @import 'stylesheets/decidim/posts/posts';
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35 35"><path d="M17.5 35A17.5 17.5 0 1 1 35 17.5 17.52 17.52 0 0 1 17.5 35zm0-33.06A15.56 15.56 0 1 0 33.06 17.5 15.57 15.57 0 0 0 17.5 1.94zm9.5 13.7H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zm0 3.68H8a1 1 0 0 1 0-1.94h19a1 1 0 0 1 0 1.94zM22.26 23H8a1 1 0 0 1 0-1.94h14.26a1 1 0 0 1 0 1.94z"/></svg>
@@ -0,0 +1,112 @@
1
+ const carousel = (() => {
2
+ let startX;
3
+
4
+ const setActiveItem = (galleryId, index, direction = null) => {
5
+ const galleryItems = document.querySelectorAll(`#${galleryId} li`);
6
+ const navDots = document.querySelectorAll(
7
+ `.posts__post_gallery_nav_dot[data-target="${galleryId}"]`
8
+ );
9
+ let activeIndex = index;
10
+
11
+ if (direction) {
12
+ const currentIndex = Array.from(galleryItems).findIndex((item) =>
13
+ item.classList.contains('active')
14
+ );
15
+
16
+ if (direction === 'left' && currentIndex < galleryItems.length - 1) {
17
+ activeIndex = currentIndex + 1;
18
+ } else if (direction === 'right' && currentIndex > 0) {
19
+ activeIndex = currentIndex - 1;
20
+ }
21
+ }
22
+
23
+ galleryItems.forEach((item, idx) => {
24
+ if (idx === activeIndex) {
25
+ item.classList.add('active');
26
+ } else {
27
+ item.classList.remove('active');
28
+ }
29
+ });
30
+
31
+ navDots.forEach((dot, idx) => {
32
+ const innerSpan = dot.querySelector('span');
33
+ if (innerSpan) {
34
+ if (idx === activeIndex) {
35
+ innerSpan.classList.add('bg-feeds-notification');
36
+ } else {
37
+ innerSpan.classList.remove('bg-feeds-notification');
38
+ }
39
+ }
40
+ });
41
+
42
+ // Show only the first 8 dots and update the visible range as the user navigates
43
+ const visibleRange = 8;
44
+ let start = 0;
45
+ let end = visibleRange;
46
+
47
+ if (activeIndex >= end - 1) {
48
+ start = activeIndex - visibleRange + 2;
49
+ end = activeIndex + 2;
50
+ }
51
+
52
+ start = Math.max(0, start);
53
+ end = Math.min(navDots.length, end);
54
+
55
+ navDots.forEach((dot, idx) => {
56
+ if (idx >= start && idx < end) {
57
+ dot.style.display = 'flex';
58
+ } else {
59
+ dot.style.display = 'none';
60
+ }
61
+ });
62
+ };
63
+
64
+ const attachSwipeListeners = (galleryId) => {
65
+ const gallery = document.getElementById(galleryId);
66
+
67
+ gallery.addEventListener('touchstart', (e) => {
68
+ startX = e.touches[0].clientX;
69
+ });
70
+
71
+ gallery.addEventListener('touchend', (e) => {
72
+ const endX = e.changedTouches[0].clientX;
73
+ const galleryItems = document.querySelectorAll(`#${galleryId} li`);
74
+ const currentIndex = Array.from(galleryItems).findIndex((item) =>
75
+ item.classList.contains('active')
76
+ );
77
+
78
+ if (startX - endX > 50 && currentIndex < galleryItems.length - 1) {
79
+ setActiveItem(galleryId, null, 'left');
80
+ } else if (startX - endX < -50 && currentIndex > 0) {
81
+ setActiveItem(galleryId, null, 'right');
82
+ }
83
+ });
84
+ };
85
+
86
+ const attachEventListenersToDots = () => {
87
+ document.querySelectorAll('.posts__post_gallery_nav_dot').forEach((dot) => {
88
+ dot.addEventListener('click', function () {
89
+ const targetGalleryId = this.getAttribute('data-target');
90
+ const targetIndex = parseInt(this.getAttribute('data-index'), 10);
91
+ setActiveItem(targetGalleryId, targetIndex);
92
+ });
93
+ });
94
+ };
95
+
96
+ const init = () => {
97
+ attachEventListenersToDots();
98
+ // Initialize swipe listeners for each gallery
99
+ document.querySelectorAll('.posts__post_gallery').forEach((gallery) => {
100
+ attachSwipeListeners(gallery.id);
101
+ });
102
+ // Initialize the visible dots for each gallery
103
+ document.querySelectorAll('.posts__post_gallery_nav_dot').forEach((dot) => {
104
+ const targetGalleryId = dot.getAttribute('data-target');
105
+ setActiveItem(targetGalleryId, 0);
106
+ });
107
+ };
108
+
109
+ return { init };
110
+ })();
111
+
112
+ export default carousel;
@@ -0,0 +1,75 @@
1
+ export function host_status() {
2
+ const changeStatusButtons = document.querySelectorAll('.changeStatusButton');
3
+
4
+ changeStatusButtons.forEach(function (button) {
5
+ button.addEventListener('click', handleStatusChange);
6
+ });
7
+
8
+ const cancelButton = document.getElementById(
9
+ 'confirmationModal__cancelButton'
10
+ );
11
+ cancelButton.addEventListener('click', function () {
12
+ const confirmationModal = document.getElementById('confirmationModal');
13
+ confirmationModal.close();
14
+ });
15
+
16
+ function handleStatusChange(event) {
17
+ const button = event.currentTarget;
18
+ const postId = button.dataset.postId;
19
+ const newStatus = button.dataset.postStatus;
20
+ const confirmationModalMsg = button.dataset.dialogmsg;
21
+
22
+ const confirmationModal = document.getElementById('confirmationModal');
23
+ const confirmButton = document.getElementById(
24
+ 'confirmationModal__confirmButton'
25
+ );
26
+ const confirmButtons = confirmationModal.querySelector(
27
+ '.confirmationModal__buttons'
28
+ );
29
+ const confirmationModalResponse = confirmationModal.querySelector(
30
+ '.confirmationModal__response'
31
+ );
32
+
33
+ confirmationModalResponse.innerHTML = confirmationModalMsg;
34
+ confirmationModal.showModal();
35
+
36
+ const confirmClickHandler = function () {
37
+ confirmButtons.style.display = 'none';
38
+ confirmationModalResponse.innerHTML =
39
+ window.translations.dialog.dialogBodyResponse;
40
+ const params = new URLSearchParams({
41
+ id: postId,
42
+ status: newStatus,
43
+ });
44
+ Rails.ajax({
45
+ url: 'change_status/?' + params.toString(),
46
+ type: 'GET',
47
+ success: function (response) {
48
+ confirmationModal.close();
49
+ confirmButtons.style.display = 'flex';
50
+ confirmationModalResponse.innerText =
51
+ window.translations.dialog.dialogBodyMsg;
52
+
53
+ const postElement = document.getElementById(`feeds_post-${postId}`);
54
+ postElement.innerHTML = 'updating status...';
55
+ postElement.innerHTML = response.new_content;
56
+
57
+ const updatedButtons = postElement.querySelectorAll(
58
+ '.changeStatusButton'
59
+ );
60
+ updatedButtons.forEach(function (updatedButton) {
61
+ updatedButton.addEventListener('click', handleStatusChange);
62
+ });
63
+ },
64
+ error: function (error) {
65
+ console.error('Error changing status:', error);
66
+ },
67
+ });
68
+
69
+ confirmButton.removeEventListener('click', confirmClickHandler);
70
+ };
71
+
72
+ confirmButton.removeEventListener('click', confirmClickHandler);
73
+ confirmButton.addEventListener('click', confirmClickHandler);
74
+ }
75
+ }
@@ -0,0 +1,98 @@
1
+ const newFeed = document.getElementById('posts__post_newElement');
2
+ const newFeedOpener = document.querySelectorAll(
3
+ '.posts__post_newElement-opener'
4
+ );
5
+ const newFeedCloser = newFeed.querySelector('.posts__post_newElement-closer');
6
+ let lastFocusedButton = null;
7
+
8
+ newFeedOpener.forEach(function (opener) {
9
+ opener.addEventListener('click', function () {
10
+ let isExpanded = opener.getAttribute('aria-expanded') === 'true';
11
+ opener.setAttribute('aria-expanded', !isExpanded);
12
+ const activeFilter = newFeed.getAttribute('data-active-filter');
13
+ const buttonToActivate = activeFilter
14
+ ? Array.from(categoryButtons).find(
15
+ (button) => button.getAttribute('data-category') === activeFilter
16
+ )
17
+ : categoryButtons[0];
18
+ activateCategory(buttonToActivate || categoryButtons[0]);
19
+ newFeed.classList.toggle('open');
20
+ lastFocusedButton = this;
21
+ document.getElementById('post_body').focus();
22
+ document.body.style.overflow = 'hidden';
23
+ });
24
+ });
25
+
26
+ newFeed.addEventListener('click', function (event) {
27
+ if (event.target === newFeed) {
28
+ closeDialog();
29
+ }
30
+ });
31
+
32
+ newFeedCloser.addEventListener('click', closeDialog);
33
+
34
+ document.addEventListener('keydown', function (event) {
35
+ if (event.key === 'Escape') {
36
+ closeDialog();
37
+ }
38
+ });
39
+
40
+ const categoryButtons = document.querySelectorAll('.category-selection button');
41
+ const meetingForm = document.querySelector('.meetings_form');
42
+ const postForm = document.querySelector('.posts_form');
43
+ const surveyDiv = document.getElementById('extraFieldsForSurvey');
44
+ const newFeedLiveRegion = document.getElementById(
45
+ 'posts__post_newElement_Form-LiveRegion'
46
+ );
47
+
48
+ categoryButtons.forEach((button) => {
49
+ button.addEventListener('click', function () {
50
+ activateCategory(this);
51
+ });
52
+ });
53
+
54
+ function hideAllForms() {
55
+ meetingForm.classList.remove('open');
56
+ postForm.classList.remove('open');
57
+ surveyDiv.classList.remove('open');
58
+ newFeedLiveRegion.innerHTML = '';
59
+ categoryButtons.forEach(function (button) {
60
+ button.classList.remove('active');
61
+ button.setAttribute('aria-pressed', 'false');
62
+ });
63
+ }
64
+
65
+ function closeDialog() {
66
+ newFeedOpener.forEach(function (opener) {
67
+ opener.setAttribute('aria-expanded', 'false');
68
+ newFeed.classList.remove('open');
69
+ lastFocusedButton.focus();
70
+ document.body.style.overflow = 'auto';
71
+ });
72
+ }
73
+
74
+ function activateCategory(button) {
75
+ hideAllForms();
76
+ button.classList.add('active');
77
+ button.setAttribute('aria-pressed', 'true');
78
+ const category = button.getAttribute('data-category');
79
+ newFeedLiveRegion.innerHTML = window.translations.newFeedLiveRegion[category];
80
+
81
+ const postBody = document.getElementById('post_body');
82
+ const meetingTitle = document.getElementById('meeting_title');
83
+ if (category === 'calendar') {
84
+ meetingForm.classList.add('open');
85
+ meetingForm.querySelector('.form-error').classList.remove('is-visible');
86
+ meetingTitle.classList.remove('is-invalid-input');
87
+ document.getElementById('meeting_title').focus();
88
+ } else {
89
+ postForm.classList.add('open');
90
+ postForm.querySelector('.form-error').classList.remove('is-visible');
91
+ postBody.classList.remove('is-invalid-input');
92
+ postBody.focus();
93
+ document.getElementById('post_category').value = category;
94
+ if (category === 'survey') surveyDiv.classList.add('open');
95
+ }
96
+ }
97
+
98
+ export { closeDialog, activateCategory, hideAllForms };