Skip to content

Commit

Permalink
fix[Op#40437]: Reinstate emoji reactions streams
Browse files Browse the repository at this point in the history
Journalsa are updated on last updated_at; reactions cannot rely on this as they do not "touch"
the journal, so a full journal refresh would not be viable. We avoid "touching" the journal on emoji
reactions update to retain concurrent user actions. E.g. If a user has a journal in edit mode, we do not want to overrite
it on the next polling update- this is why it's safer to stream emoji reactions separately

This reverts commit fb419dd.
  • Loading branch information
akabiru committed Oct 25, 2024
1 parent e22d22d commit a302f3f
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
<%=
component_wrapper do
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)
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
"#{EmojiReaction.emoji(reaction)} #{data[:count]}"
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@ def initialize(journal:, grouped_emoji_reactions:)
@grouped_emoji_reactions = grouped_emoji_reactions
end

def render?
grouped_emoji_reactions.present?
end

private

attr_reader :journal, :grouped_emoji_reactions
Expand Down
13 changes: 13 additions & 0 deletions app/controllers/work_packages/activities_tab_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ def handle_other_filters_on_create(call)
def perform_update_streams_from_last_update_timestamp
if params[:last_update_timestamp].present? && (last_updated_at = Time.zone.parse(params[:last_update_timestamp]))
generate_time_based_update_streams(last_updated_at)
generate_work_package_journals_emoji_reactions_update_streams
else
@turbo_status = :bad_request
end
Expand Down Expand Up @@ -328,6 +329,18 @@ def generate_time_based_update_streams(last_update_timestamp)
end
end

def generate_work_package_journals_emoji_reactions_update_streams
# Current limitation: Only shows added reactions, not removed ones
Journal.grouped_work_package_journals_emoji_reactions(@work_package).each do |journal_id, grouped_emoji_reactions|
update_via_turbo_stream(
component: WorkPackages::ActivitiesTab::Journals::ItemComponent::Reactions.new(
journal: @work_package.journals.find(journal_id),
grouped_emoji_reactions:
)
)
end
end

def rerender_updated_journals(journals, last_update_timestamp, grouped_emoji_reactions)
journals.where("updated_at > ?", last_update_timestamp).find_each do |journal|
update_item_show_component(journal:, grouped_emoji_reactions: grouped_emoji_reactions.fetch(journal.id, {}))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,12 @@
end

context "with no reactions" do
it "does not render" do
it "renders just the component node" do
# NB: This is so that there is always a node to update during WorkPackages::ActivitiesTabController#update_streams polling
render_inline(described_class.new(journal:, grouped_emoji_reactions: {}))

expect(page.text).to be_empty
component = page.find("#work-packages-activities-tab-journals-item-component-reactions-#{journal.id}")
expect(component.text).to be_empty
end
end

Expand Down
42 changes: 42 additions & 0 deletions spec/features/activities/work_package/emoji_reactions_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,48 @@
end
end

describe "reactions updates" do
let(:work_package) { create(:work_package, project:, author: admin) }
let(:first_comment_by_member) do
create(:work_package_journal, user: member, notes: "Second comment by member", journable: work_package,
version: 2)
end

current_user { member }

before do
# set WORK_PACKAGES_ACTIVITIES_TAB_POLLING_INTERVAL_IN_MS to 1000
# to speed up the polling interval for test duration
ENV["WORK_PACKAGES_ACTIVITIES_TAB_POLLING_INTERVAL_IN_MS"] = "1000"

wp_page.visit!
wp_page.wait_for_activity_tab
end

after do
ENV.delete("WORK_PACKAGES_ACTIVITIES_TAB_POLLING_INTERVAL_IN_MS")
end

it "shows the updated reactions without reload", :aggregate_failures do
activity_tab.expect_journal_notes(text: first_comment_by_member.notes)

# Simulate another user adding a reaction
EmojiReactions::CreateService
.new(user: admin)
.call(user: admin, reactable: first_comment_by_member, reaction: :confused_face)

wait_for do
activity_tab.expect_emoji_reactions_for_journal(first_comment_by_member, "😕" => 1)
end

# Current user adds several reactions
activity_tab.add_first_emoji_reaction_for_journal(first_comment_by_member, "👍")
activity_tab.add_emoji_reaction_for_journal(first_comment_by_member, "😕")

activity_tab.expect_emoji_reactions_for_journal(first_comment_by_member, "👍" => 1, "😕" => 2)
end
end

def create_user_as_project_member
member_role = create(:project_role,
permissions: %i[view_work_packages edit_work_packages add_work_packages work_package_assigned
Expand Down

0 comments on commit a302f3f

Please sign in to comment.