Skip to content

Fix stale state and formatting in sync dialog#2299

Merged
myieye merged 7 commits into
developfrom
claude/refresh-sync-dialog-on-bg-sync
Jun 3, 2026
Merged

Fix stale state and formatting in sync dialog#2299
myieye merged 7 commits into
developfrom
claude/refresh-sync-dialog-on-bg-sync

Conversation

@myieye

@myieye myieye commented May 26, 2026

Copy link
Copy Markdown
Collaborator

Four small fixes:

  1. formatRelativeDate — fall back to a forced-display zero so the "Last sync:" label doesn't flash empty when the diff is below the smallest requested unit.
  2. Blazor toast filter — ignore the benign "no pending async call with ID" error from MAUI WebView page-refresh races.
  3. Sticky "Last sync: Never" — dropped the 500ms onClose state-wipe that, combined with the refresh dedupe, was stranding the dialog without data after a fast close→reopen.
  4. Multi-device staleness — when a sync event arrives during an in-flight refresh, queue a fresh fetch instead of returning the pre-sync snapshot.

claude and others added 5 commits May 26, 2026 17:03
The dialog was only refreshed on open and on user-triggered sync, so
state (last sync date, pending counts) went stale when another client
pushed changes and a background sync arrived while the dialog was open.

Subscribe to projectEventBus.onSync and refresh on Success events.
Extracted the existing refresh trio into refreshStatus() since it's
now used in three places.
onSync fires a cached event immediately on subscribe, doubling up with
onOpen's refresh; user-clicked sync similarly races the resulting sync
event. Reuse the in-flight promise so overlapping calls share one
round-trip.
Intl.DurationFormat hides zero-valued fields by default, so an absDiffMs
below the smallest requested unit (e.g. <1s for seconds) was emitting an
empty string and the "Last sync: " label briefly showed nothing right
after a fresh sync. Fall back to a forced-display zero so Intl handles
plurals correctly across styles and locales.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Benign race during page refresh: .NET completes a JS->.NET call whose
JS-side registry was already torn down. Add to the existing ignore-list.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The 500ms onClose timer wiped state to undefined; combined with the
refreshStatus dedupe (a784c90) it permanently stranded the dialog
without data when the user reopened during the close-animation window.
Removing onClose: data persists in memory while the dialog is closed
(bits-ui keeps content mounted but hidden), so the next open shows the
previous values until refreshStatus overwrites them.

Also moved the projectEventBus.onSync subscription below the
pendingRefresh declaration: onSync invokes its callback synchronously
with a cached event, which can reach refreshStatus and hit the TDZ.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions github-actions Bot added the 💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related label May 26, 2026
@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f44dda34-6e71-42b4-b390-0a02b1a42906

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR enhances sync status display and error handling. It fixes relative date formatting to treat zero time differences as "past," adds comprehensive tests, refactors SyncDialog with event-driven status refresh using a cached helper, and filters WebView teardown errors.

Changes

Sync & Date Formatting Enhancements

Layer / File(s) Summary
Relative date formatting logic and tests
frontend/viewer/src/lib/components/ui/format/format-relative-date-fn.svelte.ts, frontend/viewer/src/lib/components/ui/format/format-relative-date.test.ts
formatRelativeDate now treats zero time difference as "past," ensures zero-value duration fallback when formatDuration omits fields, and includes comprehensive test coverage for nullish inputs, past/future output, smallest-unit boundary behavior, and zero pluralization.
SyncDialog event-driven refresh mechanism
frontend/viewer/src/project/SyncDialog.svelte
SyncDialog subscribes to project sync events via useProjectEventBus and implements a cached refreshStatus() helper that fetches local/remote/latest-synced state. The projectEventBus.onSync handler triggers refresh when dialog is open and sync succeeds. onOpen and post-CRDT sync flows now delegate to refreshStatus() instead of inline Promise.all.
WebView error filtering
frontend/viewer/src/lib/errors/global-errors.ts
shouldIgnoreError now filters out "There is no pending async call with ID" errors to prevent WebView JS→.NET teardown messages from being processed into notifications/logging.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

💻 FW Lite

Suggested reviewers

  • hahn-kev

Poem

🐰 A zero that's past, not future bright,
Dates now format just right, oh what a sight!
Sync dialogs listen to the event's sweet call,
Status refreshes clean—no duplicate at all,
WebView tears fade, the flow doth endure. 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title directly summarizes the main changes: fixing stale state and formatting issues in the SyncDialog component, which aligns with the multiple fixes described in the PR.
Description check ✅ Passed The description clearly outlines four distinct fixes related to the changeset: formatRelativeDate formatting, Blazor toast filtering, sticky dialog state, and multi-device staleness handling.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/refresh-sync-dialog-on-bg-sync

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

UI unit Tests

  1 files   60 suites   32s ⏱️
183 tests 183 ✅ 0 💤 0 ❌
252 runs  252 ✅ 0 💤 0 ❌

Results for commit df417c3.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

C# Unit Tests

165 tests   165 ✅  20s ⏱️
 23 suites    0 💤
  1 files      0 ❌

Results for commit df417c3.

♻️ This comment has been updated with latest results.

@argos-ci

argos-ci Bot commented May 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - May 28, 2026, 9:11 AM

@myieye myieye marked this pull request as draft May 27, 2026 07:00
Convert the cfg helper from an arrow expression to a function declaration
to match the project's ESLint config.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@myieye myieye force-pushed the claude/refresh-sync-dialog-on-bg-sync branch from 5320fe3 to 424a5bb Compare May 27, 2026 08:17
The pendingRefresh dedupe returned the same promise to all overlapping
callers, including onSync handlers triggered by another client's push.
That meant the in-flight refresh (started before the push landed) would
resolve with pre-sync data and no follow-up would fire.

Mark refreshStatus calls from the onSync handler as forceFresh so they
queue a rerun after the in-flight refresh finishes. Other overlapping
calls (cached-event-on-subscribe, post-syncLexboxToLocal) still dedupe,
preserving the original round-trip savings in those flows.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@myieye myieye changed the title Fix sticky 'Last sync: Never' and surrounding SyncDialog cleanups Fix stale state and formatting in sync dialog May 27, 2026
@myieye myieye closed this May 28, 2026
@myieye myieye reopened this May 28, 2026
@myieye myieye marked this pull request as ready for review June 3, 2026 12:08
@myieye myieye merged commit 7c3a324 into develop Jun 3, 2026
48 checks passed
@myieye myieye deleted the claude/refresh-sync-dialog-on-bg-sync branch June 3, 2026 12:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FW Lite issues related to the fw lite application, not miniLcm or crdt related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants