Skip to content

Commit

Permalink
Merge pull request #16020 from opf/feature/40437-emoji-reactions-to-w…
Browse files Browse the repository at this point in the history
…ork-package-comments

Feature/40437 emoji reactions to work package comments
  • Loading branch information
akabiru authored Oct 25, 2024
2 parents a618078 + 840deff commit 87a8334
Show file tree
Hide file tree
Showing 37 changed files with 1,783 additions and 81 deletions.
1 change: 1 addition & 0 deletions app/components/_index.sass
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@import "work_packages/activities_tab/journals/index_component"
@import "work_packages/activities_tab/journals/item_component"
@import "work_packages/activities_tab/journals/item_component/details"
@import "work_packages/activities_tab/journals/item_component/reactions"
@import "shares/modal_body_component"
@import "shares/invite_user_form_component"
@import "work_packages/details/tab_component"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
<%=
component_wrapper(class: "work-packages-activities-tab-journals-index-component") do
flex_layout do |journals_index_wrapper_container|
journals_index_wrapper_container.with_row(classes: "work-packages-activities-tab-journals-index-component--journals-inner-container", mb: inner_container_margin_bottom) do
flex_layout(id: insert_target_modifier_id, data: { "test_selector": "op-wp-journals-container" }) do |journals_index_container|
journals_index_wrapper_container.with_row(
classes: "work-packages-activities-tab-journals-index-component--journals-inner-container",
mb: inner_container_margin_bottom
) do
flex_layout(id: insert_target_modifier_id,
data: { test_selector: "op-wp-journals-container" }) do |journals_index_container|
if empty_state?
journals_index_container.with_row(mt: 2, mb: 3) do
render(
WorkPackages::ActivitiesTab::Journals::EmptyComponent.new()
WorkPackages::ActivitiesTab::Journals::EmptyComponent.new
)
end
end

journals.each do |journal|
journals_index_container.with_row do
render(
WorkPackages::ActivitiesTab::Journals::ItemComponent.new(journal:, filter:)
)
render(WorkPackages::ActivitiesTab::Journals::ItemComponent.new(
journal:, filter:,
grouped_emoji_reactions: wp_journals_grouped_emoji_reactions[journal.id]
))
end
end
end
end
journals_index_wrapper_container.with_row(classes: "work-packages-activities-tab-journals-index-component--stem-connection") unless empty_state? || journal_sorting == "desc"

unless empty_state? || journal_sorting_desc?
journals_index_wrapper_container
.with_row(classes: "work-packages-activities-tab-journals-index-component--stem-connection")
end
end
end
%>
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ def journal_sorting
User.current.preference&.comments_sorting || "desc"
end

def journal_sorting_desc?
journal_sorting == "desc"
end

def journals
work_package.journals.includes(:user, :notifications).reorder(version: journal_sorting)
end
Expand All @@ -67,12 +71,16 @@ def journal_with_notes
journals.where.not(notes: "")
end

def wp_journals_grouped_emoji_reactions
@wp_journals_grouped_emoji_reactions ||= Journal.grouped_work_package_journals_emoji_reactions(work_package)
end

def empty_state?
filter == :only_comments && journal_with_notes.empty?
end

def inner_container_margin_bottom
if journal_sorting == "desc"
if journal_sorting_desc?
3
else
0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
<%=
component_wrapper(data: wrapper_data_attributes, class: "work-packages-activities-tab-journals-item-component") do
flex_layout(data: { "test_selector": "op-wp-journal-entry-#{journal.id}" }) do |journal_container|
flex_layout(data: { test_selector: "op-wp-journal-entry-#{journal.id}" }) do |journal_container|
if show_comment_container?
journal_container.with_row do
render(border_box_container(
id: "activity-anchor-#{journal.version}",
padding: :condensed,
"aria-label": I18n.t("activities.work_packages.activity_tab.commented")
)) do |border_box_component|
id: "activity-anchor-#{journal.version}",
padding: :condensed,
"aria-label": I18n.t("activities.work_packages.activity_tab.commented")
)) do |border_box_component|
border_box_component.with_header(px: 2, py: 1, data: { test_selector: "op-journal-notes-header" }) do
flex_layout(align_items: :center, justify_content: :space_between) do |header_container|
header_container.with_column(flex_layout: true, classes: "work-packages-activities-tab-journals-item-component--header-start-container ellipsis") do |header_start_container|
header_container.with_column(flex_layout: true,
classes: "work-packages-activities-tab-journals-item-component--header-start-container ellipsis") do |header_start_container|
header_start_container.with_column(mr: 2) do
render Users::AvatarComponent.new(user: journal.user, show_name: false, size: :mini)
end
header_start_container.with_column(mr: 1, flex_layout: true, classes: "work-packages-activities-tab-journals-item-component--user-name-container hidden-for-desktop") do |user_name_container|
header_start_container.with_column(mr: 1, flex_layout: true,
classes: "work-packages-activities-tab-journals-item-component--user-name-container hidden-for-desktop") do |user_name_container|
user_name_container.with_row(classes: "work-packages-activities-tab-journals-item-component--user-name ellipsis") do
truncated_user_name(journal.user)
end
user_name_container.with_row do
render(Primer::Beta::Text.new(font_size: :small, color: :subtle, mt: 1)) { format_time(journal.created_at) }
end
end
end
header_start_container.with_column(mr: 1, classes: "work-packages-activities-tab-journals-item-component--user-name ellipsis hidden-for-mobile") do
header_start_container.with_column(mr: 1,
classes: "work-packages-activities-tab-journals-item-component--user-name ellipsis hidden-for-mobile") do
truncated_user_name(journal.user)
end
header_start_container.with_column(mr: 1, classes: "hidden-for-mobile") do
Expand All @@ -33,32 +36,33 @@
if has_unread_notifications?
header_end_container.with_column(mr: 2, pt: 1) do
render(Primer::Beta::Octicon.new(
:"dot-fill", # color is set via CSS as requested by UI/UX Team
classes: "work-packages-activities-tab-journals-item-component--notification-dot-icon",
size: :medium,
data: { test_selector: "op-journal-unread-notification", "op-ian-center-update-immediate": true }
))
:"dot-fill", # color is set via CSS as requested by UI/UX Team
classes: "work-packages-activities-tab-journals-item-component--notification-dot-icon",
size: :medium,
data: { test_selector: "op-journal-unread-notification", "op-ian-center-update-immediate": true }
))
end
end
header_end_container.with_column do
render(Primer::Beta::Link.new(
href: "#",
scheme: :secondary,
underline: false,
font_size: :small,
data: {
turbo: false,
action: "click->work-packages--activities-tab--index#setAnchor:prevent",
"work-packages--activities-tab--index-id-param": journal.version
}
)) do
href: "#",
scheme: :secondary,
underline: false,
font_size: :small,
data: {
turbo: false,
action: "click->work-packages--activities-tab--index#setAnchor:prevent",
"work-packages--activities-tab--index-id-param": journal.version
}
)) do
"##{journal.version}"
end
end
header_end_container.with_column(ml: 1, classes: "work-packages-activities-tab-journals-item-component--action-menu") do
render(Primer::Alpha::ActionMenu.new(data: { "test_selector": "op-wp-journal-#{journal.id}-action-menu" })) do |menu|
header_end_container.with_column(ml: 1,
classes: "work-packages-activities-tab-journals-item-component--action-menu") do
render(Primer::Alpha::ActionMenu.new(data: { test_selector: "op-wp-journal-#{journal.id}-action-menu" })) do |menu|
menu.with_show_button(icon: "kebab-horizontal",
'aria-label': I18n.t(:button_actions),
"aria-label": I18n.t(:button_actions),
scheme: :invisible)
copy_url_action_item(menu)
edit_action_item(menu) if allowed_to_edit?
Expand All @@ -70,24 +74,28 @@
end
border_box_component.with_body(
classes: "work-packages-activities-tab-journals-item-component--journal-notes-body",
data: { "test_selector": "op-journal-notes-body" }
data: { test_selector: "op-journal-notes-body" }
) do
unless noop?
if noop?
render(Primer::Beta::Text.new(font_style: :italic, color: :subtle, mt: 1)) do
I18n.t(:"journals.changes_retracted")
end
else
case state
when :show
render(WorkPackages::ActivitiesTab::Journals::ItemComponent::Show.new(journal:, filter:))
render(WorkPackages::ActivitiesTab::Journals::ItemComponent::Show.new(journal:, filter:,
grouped_emoji_reactions:))
when :edit
render(WorkPackages::ActivitiesTab::Journals::ItemComponent::Edit.new(journal:, filter:))
end
else
render(Primer::Beta::Text.new(font_style: :italic, color: :subtle, mt: 1)) { I18n.t(:"journals.changes_retracted") }
end
end
end
end
end
journal_container.with_row do
render(WorkPackages::ActivitiesTab::Journals::ItemComponent::Details.new(journal:, has_unread_notifications: notification_on_details?, filter:))
render(WorkPackages::ActivitiesTab::Journals::ItemComponent::Details.new(journal:,
has_unread_notifications: notification_on_details?, filter:))
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,18 @@ class ItemComponent < ApplicationComponent
include OpPrimer::ComponentHelpers
include OpTurbo::Streamable

def initialize(journal:, filter:, state: :show)
def initialize(journal:, filter:, grouped_emoji_reactions:, state: :show)
super

@journal = journal
@state = state
@filter = filter
@grouped_emoji_reactions = grouped_emoji_reactions
@state = state
end

private

attr_reader :journal, :state, :filter
attr_reader :journal, :state, :filter, :grouped_emoji_reactions

def wrapper_uniq_by
journal.id
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<%=
component_wrapper do
if journal.available_emoji_reactions.any?
render(Primer::Alpha::Overlay.new(
title: I18n.t("reactions.action_title"),
padding: :condensed,
anchor_side: :outside_top,
visually_hide_title: true
)) do |overlay|
overlay.with_show_button(
icon: "smiley",
"aria-label": I18n.t("reactions.add_reaction"),
title: I18n.t("reactions.add_reaction"),
mr: 2,
test_selector: "add-reactions-button"
)

overlay.with_body(pt: 2) do
flex_layout do |add_reactions_container|
journal.available_emoji_reactions.each do |emoji, reaction|
add_reactions_container.with_column(mr: 2) do
render(Primer::Beta::Button.new(
scheme: :invisible,
id: "#{journal.id}-#{reaction}",
tag: :a,
href: toggle_reaction_work_package_activity_path(journal.journable.id, id: journal.id, reaction:),
data: { "turbo-stream": true, "turbo-method": :put },
"aria-label": I18n.t("reactions.react_with", reaction: reaction.to_s.humanize(capitalize: false))
)) do
emoji
end
end
end
end
end
end
end
end
%>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module WorkPackages
module ActivitiesTab
module Journals
class ItemComponent::AddReactions < ApplicationComponent
include ApplicationHelper
include OpPrimer::ComponentHelpers
include OpTurbo::Streamable

def initialize(journal:)
super

@journal = journal
end

def render?
User.current.allowed_in_work_package?(:add_work_package_notes, work_package)
end

private

attr_reader :journal

def work_package = journal.journable
def wrapper_uniq_by = journal.id
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<%=
component_wrapper do
if grouped_emoji_reactions.present?
flex_layout(test_selector: "emoji-reactions") do |reactions_container|
grouped_emoji_reactions.each do |reaction, data|
reactions_container.with_column(mr: 2) do
render(Primer::Beta::Button.new(
scheme: button_scheme(data[:users]),
color: :default,
bg: counter_color(data[:users]),
id: "#{journal.id}-#{reaction}",
test_selector: "reaction-#{reaction}",
tag: :a,
href: href(reaction:),
data: { turbo_stream: true, turbo_method: :put },
aria: { label: aria_label_text(reaction, data[:users]) },
disabled: current_user_cannot_react?,
classes: "op-reactions-button"
)) do |button|
button.with_tooltip(text: number_of_user_reactions_text(data[:users]),
test_selector: "reaction-tooltip-#{reaction}") do
button.with_icon(EmojiReactions.emoji(reaction), size: :small)
end
"#{EmojiReaction.emoji(reaction)} #{data[:count]}"
end
end
end
end
end
end
%>
Loading

0 comments on commit 87a8334

Please sign in to comment.