EARHORNMUSIC UPnP · Android TV · Phones · Android Auto · GNU/Linux
Release 0.5.0

UiEvents.kt

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)
    }
}