Skip to content

feat(ui): replace the sync modal with a corner toast - #221

Merged
romaintb merged 4 commits into
mainfrom
feat/sync-toast
Sep 4, 2026
Merged

romaintb merged 4 commits into
mainfrom
feat/sync-toast

Conversation

@romaintb

@romaintb romaintb commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Every successful sync fired a centered Info dialog, and every failure a centered Error one. That was tolerable while sync blocked the UI, but sync now runs in the background and, since the auto-sync interval was wired up, fires on a timer. The modal had started interrupting the user every five minutes on its own.

Sync outcomes now land in a small box in the bottom-right of the task list: success in theme green, gone after three seconds; failure in theme red, staying until any keypress dismisses it. The in-flight indicator moves to the same corner instead of covering the middle of the screen. Task operation errors are untouched and still get a modal, since those are user-initiated.

The event loop only repaints on input or background work, so a toast with an expiry would otherwise sit on screen until the next keystroke. toast_expiring() forces a repaint on tick, but only while something is counting down, so a failure toast cannot pin the TUI at tick rate.

Changes

  • Code changes
  • Tests added/updated
  • Docs updated (README/CHANGELOG)

Checklist

  • cargo fmt passes
  • cargo clippy -- -D warnings passes
  • cargo test passes

Related issues

Taken from #209

@romaintb romaintb self-assigned this Sep 4, 2026
@romaintb romaintb added the feat label Sep 4, 2026
@romaintb romaintb added this to the v0.6.0 milestone Sep 4, 2026
Every successful sync fired a centered Info dialog, and every failure a centered
Error one. That was tolerable while sync blocked the UI, but sync now runs in the
background and, since the auto-sync interval was wired up, fires on a timer. The
modal had started interrupting the user every five minutes on its own.

Sync outcomes now land in a small box in the bottom-right of the task list:
success in theme green, gone after three seconds; failure in theme red, given
fifteen because it carries something the user has to actually read. The in-flight
indicator moves to the same corner instead of covering the middle of the screen.
Task operation errors are untouched and still get a modal, since those are
user-initiated.

Nothing dismisses a toast by hand. Clearing it on any keypress meant a failure
notice died to the next `j`, and Esc is already the quit key, so the TTL is the
only thing that takes a toast down.

The event loop only repaints on input or background work, so an expired toast
would otherwise sit on screen until the next keystroke. `toast_expired()` asks
for a repaint on tick once the TTL has passed, and that single repaint is what
sweeps the toast away.

The box is sized from the line's display width rather than its character count,
because the status emoji occupy two columns and this is the same measure ratatui
centers by. Getting that wrong put the leftover column entirely on one side.

`info_message`, `error_message` and `clear_messages` are gone from `AppState`:
they existed only to feed these dialogs, and `clear_messages` had no callers at
all.
is_busy, active_task_count and task_count had zero callers; the lib
target keeps the dead_code lint quiet about pub items.
The corner toast lived in app_component.rs as a private struct plus four
methods, with its layout helper made pub only so a test could reach it.
It now sits in src/ui/components/toast.rs next to badge.rs, and its test
in the mirroring tests/ui/components/toast.rs.

Toast::success, Toast::error and Toast::spinner replace the ad-hoc
constructors, and `expires` became Option<Instant>: a spinner is a toast
with no deadline, so expired() no longer has to rely on nobody asking.

app_component.rs keeps only what the app owns, the Option<Toast> field
and sweep_toast(), which is an event-loop concern rather than a widget
one. current_toast() and render_toast() are gone, the render site is a
plain if/else.
@romaintb
romaintb merged commit 12e0c41 into main Sep 4, 2026
9 checks passed
@romaintb
romaintb deleted the feat/sync-toast branch September 4, 2026 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant