Skip to content

feat(ui): add in-app notices#185

Merged
thewrz merged 9 commits into
mainfrom
feat/issue-156
Jul 6, 2026
Merged

feat(ui): add in-app notices#185
thewrz merged 9 commits into
mainfrom
feat/issue-156

Conversation

@thewrz

@thewrz thewrz commented Jun 27, 2026

Copy link
Copy Markdown
Collaborator

Closes #156

Summary

  • Add an app-owned Notice / NoticeQueue model for info, warning, and error messages.
  • Add MVU messages to raise, dismiss, and expire notices from app-owned state.
  • Replace the bespoke source_notice storage path with the general notice mechanism.
  • Add a reusable Iced notice layer rendered as an in-window stack across app views.

Design decisions

  • Used a small top-right in-window notice stack for v1 instead of a modal/dialog system, so notices do not block ordinary soundboard workflows.
  • Info and warning notices auto-dismiss after six seconds through app state; error notices have no expiry and persist until dismissed.
  • Kept notice state in src/app/ and the Iced component in src/ui/, so view code stays presentational and business logic stays in the MVU update path.
  • Surfaced AudioEvent::Error as a persistent Audio error notice while preserving the existing structured log call.
  • Did not add OS-level notification dependencies; this PR is strictly in-window UI.

Test plan

  • cargo fmt -- --check
  • cargo test notice
  • cargo test
  • cargo clippy -- -D warnings

Summary by CodeRabbit

  • New Features

    • Added an in-app notice system with stacked “card” messages (Info, Warning, Error) that can auto-expire or be dismissed.
    • Notices now render as an overlay layer above the main UI without altering the base layout.
    • Notice styling adapts to light and dark themes, with level labels and close controls.
  • Bug Fixes

    • Error and first-run (“persistent vs this session”) messages now consistently appear via the notice area.
    • Identical notices are consolidated to reduce repeated clutter.

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 882bde53-8105-4224-93a2-545d667fb84f

📥 Commits

Reviewing files that changed from the base of the PR and between 25baaa0 and 792e372.

📒 Files selected for processing (2)
  • src/app/mod.rs
  • src/app/notices.rs

📝 Walkthrough

Walkthrough

Introduces a generalized in-app notice system replacing the ad hoc source_notice field. It adds queue-backed notice types and lifecycle handling, routes app events into the queue, and renders queued notices in an overlay with dismiss controls.

Changes

Notice queue feature

Layer / File(s) Summary
Notice data model and queue implementation
src/app/notices.rs
Defines NoticeLevel, NoticeId, Notice, QueuedNotice, and NoticeQueue with push, coalescing, expiry, dismissal, inspection, and queue behavior tests.
App state, message handling, and subscription wiring
src/app/mod.rs, src/app/notice_tests.rs
Replaces source_notice with notices: NoticeQueue, adds notice lifecycle messages, routes audio events into the queue, conditionally schedules expiry ticks, and updates tests to cover queued notice behavior.
Notice overlay UI
src/ui/mod.rs, src/ui/notice.rs
Adds the notice UI module and renders queued notices as styled cards in a scrollable overlay with dismiss actions and theme-aware styling.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AudioEvent
  participant HonkHonk
  participant NoticeQueue
  participant Subscription
  participant NoticeLayer

  AudioEvent->>HonkHonk: Error / SourceFirstRun
  HonkHonk->>NoticeQueue: push(Notice, Instant::now())
  Subscription->>HonkHonk: NoticeTick(Instant)
  HonkHonk->>NoticeQueue: expire(now)
  HonkHonk->>NoticeLayer: view_notice_layer(notices)
  NoticeLayer->>HonkHonk: Message::DismissNotice(id)
  HonkHonk->>NoticeQueue: dismiss(id)
Loading

Possibly related PRs

  • wrzonance/HonkHonk#1: Touches the same Iced app update/subscription plumbing, including tray polling and event-loop wiring adjacent to the new notice tick handling.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding in-app notices.
Linked Issues check ✅ Passed The PR implements a queued in-app notice system with levels, app messages, auto-dismissal, persistent errors, and replaces source_notice as requested.
Out of Scope Changes check ✅ Passed The changes stay focused on the notice system and UI layer, with no obvious unrelated features introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-156

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

Persistent error notices never auto-expire, and the queue had no cap, no
dedup, and rendered in a plain Column. A re-emitting fault (a broken sink
honked repeatedly, device churn) grew the queue and the on-screen stack
without limit, pushing notices and their close buttons off-screen with no
way to clear them.

- Coalesce identical notices (same level/title/body), refreshing the expiry
  in place instead of stacking duplicates.
- Cap the queue at NoticeQueue::MAX_NOTICES, evicting the oldest beyond it.
- Wrap the toast stack in a height-bounded scrollable so overflow stays
  reachable and dismissable.

Addresses the CRITICAL flagged in the #185 review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@thewrz thewrz marked this pull request as ready for review July 6, 2026 02:27

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/ui/notice.rs (1)

132-157: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Tests only assert Some/None, not rendered content — light coverage against the "no view-rendering tests" intent.

Both tests just check that view_notice_layer returns Some/None based on queue state rather than exercising app-state behavior directly on NoticeQueue (which is already covered thoroughly in src/app/notices.rs). Consider whether this duplicate smoke-check pulls its weight here, given the guideline to keep tests focused on audio engine/config/library/app-state and avoid Iced view-rendering tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/notice.rs` around lines 132 - 157, The tests in
notice_layer_is_absent_for_empty_queue and notice_layer_builds_for_queued_notice
only smoke-check view_notice_layer returning Some/None, which duplicates
NoticeQueue coverage without validating rendered UI. Remove or replace these
Iced view-rendering assertions in src/ui/notice.rs, and keep tests focused on
app-state behavior in crate::app::notices::NoticeQueue or other non-view logic
instead.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/notices.rs`:
- Line 76: cargo fmt is failing in this file, so reformat the affected sections
to match rustfmt expectations. Update the long find closure and the multi-line
assert_eq!/assert! blocks in the notice-related tests and helpers so they follow
standard formatting, using the existing notice handling code around the
coalescing logic in the relevant functions as the reference points. After
adjusting those spots, run cargo fmt again to ensure the file passes the
formatting check.
- Around line 75-97: The notice coalescing logic in `NoticeQueue::push`
refreshes `expires_at` in place but leaves the existing entry at its original
deque position. Update the `push` path so that when
`self.entries.iter_mut().find(...)` matches an existing notice, you remove that
`QueuedNotice` and reinsert it at the back with the refreshed expiry, keeping
recency aligned with the cap eviction behavior.

---

Nitpick comments:
In `@src/ui/notice.rs`:
- Around line 132-157: The tests in notice_layer_is_absent_for_empty_queue and
notice_layer_builds_for_queued_notice only smoke-check view_notice_layer
returning Some/None, which duplicates NoticeQueue coverage without validating
rendered UI. Remove or replace these Iced view-rendering assertions in
src/ui/notice.rs, and keep tests focused on app-state behavior in
crate::app::notices::NoticeQueue or other non-view logic instead.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 711fe8b9-6901-4bd9-84aa-5ceabb9a5fd1

📥 Commits

Reviewing files that changed from the base of the PR and between 1278248 and 25baaa0.

📒 Files selected for processing (5)
  • src/app/mod.rs
  • src/app/notice_tests.rs
  • src/app/notices.rs
  • src/ui/mod.rs
  • src/ui/notice.rs

Comment thread src/app/notices.rs
Comment thread src/app/notices.rs
A re-emitted notice refreshed its expiry in place but kept its original
deque position, so oldest-first cap eviction could victimize the most
actively re-emitting notice while stale ones survived. Coalescing now
moves the refreshed notice to the back, aligning recency with eviction.

Also applies the cargo fmt cleanup that was failing the CI lint job.

Addresses CodeRabbit review on PR #185.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@thewrz

thewrz commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

Remaining review items:

  • CodeRabbit nitpick — src/ui/notice.rs tests only assert Some/None: declining. These two tests pin the overlay's presence contract at the module boundary (empty queue ⇒ no overlay layer, queued notice ⇒ layer exists) without asserting any rendered content, so they don't violate the no-view-rendering-tests guideline — and CodeRabbit itself rated the item Trivial/Low value. Removing them would leave view_notice_layer's gating uncovered for ~0 savings.
  • Second-reviewer pass (Codex GPT-5.5, adversarial) over the full diff: no findings — queue, expiry, and overlay behavior judged consistent with intent.

The CI lint failure was a pure cargo fmt diff in src/app/notices.rs, fixed in 13d53ba along with the coalescing-recency fix from the review thread. Verified locally: fmt clean, clippy -D warnings clean, full test suite green (hermetic XDG override).

thewrz and others added 6 commits July 5, 2026 23:36
Both advisories are DoS-via-untrusted-XML in quick-xml < 0.41. The sole
consumer is wayland-scanner, a build-time proc-macro parsing vendored
Wayland protocol XML; no untrusted XML is parsed at runtime, so neither
issue is reachable in the shipped binary. wayland-scanner 0.31.10
(latest) still pins quick-xml ^0.39 — drop these ignores when it moves
to 0.41.

Closes #192.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
anyhow 1.0.102 is flagged unsound (Error::downcast_mut UB after
Error::context); 1.0.103 is the patched release already on main.
Regenerates the Flatpak cargo-sources.json for the lock change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…opped)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…opped)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@thewrz thewrz merged commit f9837b8 into main Jul 6, 2026
1 check was pending
@thewrz thewrz deleted the feat/issue-156 branch July 6, 2026 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(ui): universal in-app notification/dialog helper for info & error surfacing

1 participant