Transient Feedback Elements
Relevant source files
The following files were used as context for generating this wiki page:
Transient Feedback Elements provide temporary visual status updates to the user following specific actions or system events. These elements ensure the user receives immediate confirmation or error details without permanently altering the application's primary interface. The system primary consists of a presenter that manages the lifecycle of feedback messages and a banner view that renders them.
In the Spotty architecture, transient feedback is explicitly distinguished from persistent playback state or generic event buses. It specifically handles mutation banners, such as confirmation that a track was added to a queue or notification that a network request failed.
Sources: Sources/Spotty/AGENTS.md:16-18, Sources/Spotty/Spotify/AGENTS.md:20-22
Architecture and Components
The feedback system uses a centralized presenter to coordinate message timing and a specialized view for display. The PlaybackStore interacts with this presenter to surface feedback originating from playback commands or catalog mutations.
TransientFeedbackPresenter
The TransientFeedbackPresenter is a @MainActor class that manages the current active feedback message. It enforces a "last-in-wins" policy where new messages replace existing ones. Each message is assigned a unique UUID to prevent stale dismissal tasks from removing newer, relevant messages.
Sources: Sources/Spotty/TransientFeedback.swift:23-28, Tests/SpottyBoundaryTests/TransientFeedbackChecks.swift:214-230
TransientFeedbackBanner
The TransientFeedbackBanner is a SwiftUI view that observes the presenter. It renders a floating banner at the top of the interface. The banner uses specific colors and icons based on the TransientFeedbackKind to denote the status type.
Sources: Sources/Spotty/Views/TransientFeedbackBanner.swift:11-20
Data Models
The system relies on the following internal structures:
| Structure | Description |
|---|---|
TransientFeedbackMessage | Represents a single feedback instance, containing a unique ID, text, and kind. |
TransientFeedbackKind | Defines the category of feedback: .success, .informational, or .failure. |
Sources: Sources/Spotty/TransientFeedback.swift:10-21
Logic and Data Flow
The feedback lifecycle follows a strict sequence of presentation, automated delay, and dismissal.
Message Lifecycle
- Trigger: A feature calls
success(_:),informational(_:), orfailure(_:)on the presenter. - Replacement: The presenter generates a new
TransientFeedbackMessagewith a newUUID. This immediately replaces any existing message in the@Publishedmessageproperty. - Scheduling: A new asynchronous task is created. It sleeps for a predefined duration (defaulting to 4 seconds).
- Dismissal: After the sleep concludes, the task checks if its local message
UUIDmatches the currentmessage.idin the presenter. If they match, the message is cleared. If the message was replaced by a newer one during the sleep, the stale dismissal is ignored.
Sources: Sources/Spotty/TransientFeedback.swift:42-70, Tests/SpottyBoundaryTests/TransientFeedbackChecks.swift:245-265
Flow Diagram
The following diagram illustrates the lifecycle of a feedback message and the protection against stale dismissals.
Integration with Playback and Catalog
The PlaybackStore utilizes the TransientFeedbackPresenter to communicate outcomes of commands that do not result in a permanent state change.
- Queue Operations: Adding tracks to the queue triggers success feedback ("Added to Queue") or failure feedback if the operation fails or occurs while disconnected.
- Session State: If the user attempts to add to a queue while the app is still joining Spotify Connect, the system provides feedback: "Spotty is still joining Spotify Connect."
- Disconnected States: Attempting mutations without an active session triggers a failure banner: "Connect Spotify before adding to the queue."
Sources: Tests/SpottyBoundaryTests/TransientFeedbackChecks.swift:273-285, Tests/SpottyBoundaryTests/TransientFeedbackChecks.swift:317-325
Sequence Diagram
This diagram shows the interaction between the PlaybackStore, the TransientFeedbackPresenter, and the UI when a user adds a track.
Presentation and Appearance
The TransientFeedbackBanner uses standard macOS styling for native integration.
- Placement: It is positioned with a top padding of 24 and an internal padding of 12.
- Visual Indicators:
- Success: Uses
Color.accentColorand thecheck_circlesymbol. - Informational: Uses
Color.secondaryand theinfo_circlesymbol. - Failure: Uses
Color.redand theexclamationmark_trianglesymbol.
- Success: Uses
- Interaction: Users can dismiss the banner manually by clicking it, which calls the
dismiss()method on the presenter.
Sources: Sources/Spotty/Views/TransientFeedbackBanner.swift:25-65, Sources/Spotty/TransientFeedback.swift:72-75
Summary
Transient Feedback Elements provide a robust mechanism for non-persistent user communication. By centralizing management in the TransientFeedbackPresenter and using UUID-based identity for messages, the system ensures that feedback remains accurate and prevents race conditions during rapid message updates. The TransientFeedbackBanner provides a consistent, native-feeling UI component that integrates with the project's broader playback and catalog systems.