From 9c76002bc141e9d88bd22a3cface029f9cbee073 Mon Sep 17 00:00:00 2001 From: Mike Dalessio Date: Fri, 16 Jan 2026 09:56:10 -0500 Subject: [PATCH] Completing a step removes stalled status from UI Add `updatedAt` to stalled bubble options so JS can check if the card was recently updated, matching the `Card::Stallable#stalled?` logic. Refresh the bubble via Turbo Stream when a step is updated. This should have been part of #1625 Fixes https://app.fizzy.do/5986089/cards/3668 --- app/helpers/entropy_helper.rb | 1 + .../controllers/bubble_controller.js | 5 +++- app/models/card/stallable.rb | 1 + app/views/cards/steps/update.turbo_stream.erb | 4 +++ test/helpers/entropy_helper_test.rb | 30 +++++++++++++++++++ 5 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 test/helpers/entropy_helper_test.rb diff --git a/app/helpers/entropy_helper.rb b/app/helpers/entropy_helper.rb index f1a50bbe9..4329dab88 100644 --- a/app/helpers/entropy_helper.rb +++ b/app/helpers/entropy_helper.rb @@ -16,6 +16,7 @@ module EntropyHelper { stalledAfterDays: card.entropy.days_before_reminder, lastActivitySpikeAt: card.last_activity_spike_at.iso8601, + updatedAt: card.updated_at.iso8601, action: "Stalled" } end diff --git a/app/javascript/controllers/bubble_controller.js b/app/javascript/controllers/bubble_controller.js index 714977c66..b8b7f6545 100644 --- a/app/javascript/controllers/bubble_controller.js +++ b/app/javascript/controllers/bubble_controller.js @@ -52,8 +52,11 @@ export default class extends Controller { this.#show() } + // Keep in sync with Card::Stallable#stalled? in app/models/card/stallable.rb get #isStalled() { - return this.stalledValue.lastActivitySpikeAt && signedDifferenceInDays(new Date(this.stalledValue.lastActivitySpikeAt), new Date()) > this.stalledValue.stalledAfterDays + return this.stalledValue.lastActivitySpikeAt && + signedDifferenceInDays(new Date(this.stalledValue.lastActivitySpikeAt), new Date()) > this.stalledValue.stalledAfterDays && + signedDifferenceInDays(new Date(this.stalledValue.updatedAt), new Date()) > this.stalledValue.stalledAfterDays } #showStalled() { diff --git a/app/models/card/stallable.rb b/app/models/card/stallable.rb index 03348dfde..9e82b3fa1 100644 --- a/app/models/card/stallable.rb +++ b/app/models/card/stallable.rb @@ -13,6 +13,7 @@ module Card::Stallable after_update_commit :detect_activity_spikes_later, if: :should_detect_activity_spikes? end + # Keep in sync with #isStalled in app/javascript/controllers/bubble_controller.js def stalled? if activity_spike.present? open? && last_activity_spike_at < STALLED_AFTER_LAST_SPIKE_PERIOD.ago && updated_at < STALLED_AFTER_LAST_SPIKE_PERIOD.ago diff --git a/app/views/cards/steps/update.turbo_stream.erb b/app/views/cards/steps/update.turbo_stream.erb index 24fd7f934..5cde975c9 100644 --- a/app/views/cards/steps/update.turbo_stream.erb +++ b/app/views/cards/steps/update.turbo_stream.erb @@ -1,3 +1,7 @@ <%= turbo_stream.replace @step do %> <%= render "cards/steps/step", step: @step %> <% end %> + +<%= turbo_stream.replace dom_id(@card, "bubble") do %> + <%= render "cards/display/preview/bubble", card: @card %> +<% end %> diff --git a/test/helpers/entropy_helper_test.rb b/test/helpers/entropy_helper_test.rb new file mode 100644 index 000000000..144c523ea --- /dev/null +++ b/test/helpers/entropy_helper_test.rb @@ -0,0 +1,30 @@ +require "test_helper" + +class EntropyHelperTest < ActionView::TestCase + test "stalled_bubble_options_for returns nil when card has no activity spike" do + assert_nil stalled_bubble_options_for(cards(:logo)) + end + + test "stalled_bubble_options_for returns options when card has activity spike" do + card = cards(:logo) + card.create_activity_spike! + + options = stalled_bubble_options_for(card) + assert_not_nil options + assert_equal card.last_activity_spike_at.iso8601, options[:lastActivitySpikeAt] + end + + test "stalled_bubble_options_for includes updatedAt for client-side staleness check" do + card = cards(:logo) + card.create_activity_spike! + + travel_to 3.months.from_now + + # Touch the card to simulate step completion + card.touch + + options = stalled_bubble_options_for(card) + # The helper must include updatedAt so JS can check if card was recently updated + assert_equal card.updated_at.iso8601, options[:updatedAt] + end +end