package dev.stave.ui.events
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
/**
* App-wide bus for transient user-facing notifications. Emitters post strings;
* [dev.stave.desktop.ui.ToastHost] in StaveApp collects them into a small stack of
* Material Snackbar-style banners.
*
* **Design.** MutableSharedFlow with replay=0 (a toast that arrives before a
* collector is ready is dropped — we don't want last week's error flashing up on
* today's launch) and extraBufferCapacity=4 so a brief burst (e.g. three playlist
* loads failing in quick succession) queues rather than dropping.
*
* **What goes here.** Non-blocking informational messages that the user can safely
* ignore. Examples: "Lyrics unavailable for this track", "Couldn't reach LRCLIB",
* "Playlist saved to library", "Tag reader timed out". **Not** here: fatal errors
* that should prevent a user action from completing (those deserve a dialog), or
* status that's not time-bounded (those belong in the relevant screen's persistent
* UI).
*/
class UiEvents {
private val _messages = MutableSharedFlow<String>(
replay = 0,
extraBufferCapacity = 4,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
/** Observe toast messages. Collectors are fire-and-forget; missed emissions
* while no collector is attached stay missed, which is what we want. */
val messages: SharedFlow<String> = _messages.asSharedFlow()
/** Post a toast. Non-suspending for convenience in non-coroutine call sites
* (IO error handlers, VLCJ callbacks). Drops on overflow — at which point the
* user probably has bigger problems than one dropped toast. */
fun post(message: String) {
_messages.tryEmit(message)
}
}