Files
sure/app/models/family/sync_complete_event.rb
Abhinav Dhiman cc070853b7 fix: Replace platform-wide broadcast_refresh with sync toast (#1964)
* fix: Replace platform-wide broadcast_refresh with sync toast

Instead of calling family.broadcast_refresh on every sync completion
(which reloads the page for all connected family members), broadcast
a lightweight static toast to the existing notification-tray.

A new sync-toast Stimulus controller handles two cases:
- User is idle (no focused form): auto-reloads after 500ms
- User is mid-form: toast stays visible with a manual Refresh button

This prevents in-progress form state from being wiped when a background
sync fires (e.g. adding a transaction, filling an import form).

The toast partial contains no user-scoped data, so the Current.user nil
constraint in background jobs is no longer a concern.

* fix(a11y): add explicit button types and aria-label to sync toast controls

* fix(sync-toast): improve interaction detection and replace broadcast strategy

- Increase auto-refresh delay from 500ms to 2000ms
- Expand interaction detection to include contentEditable, dialogs, and role="dialog" elements
- Switch from broadcast_append_to to broadcast_replace_to with dedicated #sync-toast target
- Add explicit id="sync-toast" to partial for targeted replacement
- Move sync_toast i18n keys from defaults/en.yml to views/shared/en.yml

* fix(sync-toast): replace hardcoded white icon color with inverse token
2026-05-31 16:05:54 +02:00

31 lines
1.1 KiB
Ruby

class Family::SyncCompleteEvent
attr_reader :family
def initialize(family)
@family = family
end
def broadcast
# Append a lightweight toast to the notification tray instead of a full
# page refresh. The sync-toast Stimulus controller handles two cases:
# - User is idle → auto-reloads after a short delay
# - User is mid-form → toast stays visible; user clicks "Refresh now"
#
# This avoids wiping in-progress form state when a background sync fires.
# The partial contains no user-scoped data (Current.user is nil here), so
# each browser re-fetches the page on its own authenticated request.
family.broadcast_replace_to(
family,
target: "sync-toast",
partial: "shared/notifications/sync_toast"
)
# Schedule recurring transaction pattern identification (debounced to run after all syncs complete)
begin
RecurringTransaction.identify_patterns_for(family)
rescue => e
Rails.logger.error("Family::SyncCompleteEvent recurring transaction identification failed: #{e.message}\n#{e.backtrace&.join("\n")}")
end
end
end