2026-06-10 16:11:09 +01:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
|
|
use crate::art::ArtImage;
|
2026-07-16 20:29:51 +03:00
|
|
|
use crate::library::models::{
|
|
|
|
|
ArtistDetail, ArtistsPage, PlaylistCard, PlaylistDetail, ReleaseDetail, SearchResults,
|
|
|
|
|
TrackItem,
|
|
|
|
|
};
|
2026-06-10 16:11:09 +01:00
|
|
|
|
2026-07-16 20:29:51 +03:00
|
|
|
/// Events delivered to the main loop by background tasks (library queries,
|
|
|
|
|
/// the playback engine, imports). Tasks never touch AppState directly.
|
2026-06-10 16:11:09 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub enum AppEvent {
|
|
|
|
|
StatusMessage(String),
|
2026-07-16 20:29:51 +03:00
|
|
|
/// A page of the artists list arrived (or failed).
|
2026-06-10 16:11:09 +01:00
|
|
|
ArtistsLoaded(Result<ArtistsPage, String>),
|
2026-07-16 20:29:51 +03:00
|
|
|
/// A full reload after a library change: replaces the loaded artist
|
|
|
|
|
/// list wholesale, so the grid never flashes empty.
|
|
|
|
|
ArtistsReloaded {
|
|
|
|
|
page: ArtistsPage,
|
|
|
|
|
limit: i64,
|
|
|
|
|
},
|
2026-06-10 16:11:09 +01:00
|
|
|
ArtistViewLoaded {
|
|
|
|
|
id: i64,
|
|
|
|
|
result: Result<ArtistDetail, String>,
|
|
|
|
|
},
|
|
|
|
|
ReleaseViewLoaded {
|
|
|
|
|
id: i64,
|
|
|
|
|
result: Result<ReleaseDetail, String>,
|
|
|
|
|
},
|
|
|
|
|
/// Live search results; `seq` drops responses that are already stale.
|
|
|
|
|
SearchLoaded {
|
|
|
|
|
seq: u64,
|
|
|
|
|
result: Result<SearchResults, String>,
|
|
|
|
|
},
|
2026-07-16 20:29:51 +03:00
|
|
|
/// Artwork loaded and decoded for the shared art cache.
|
2026-06-10 16:11:09 +01:00
|
|
|
ArtLoaded {
|
|
|
|
|
key: String,
|
|
|
|
|
art: Option<Arc<ArtImage>>,
|
|
|
|
|
},
|
|
|
|
|
Player(crate::player::PlayerEvent),
|
|
|
|
|
/// A command from the OS media keys.
|
|
|
|
|
Media(crate::media::MediaCommand),
|
2026-07-16 20:29:51 +03:00
|
|
|
/// Gapless prefetch could not open the file; the normal track-switch
|
2026-06-10 16:11:09 +01:00
|
|
|
/// path takes over when the current track ends.
|
|
|
|
|
PrefetchFailed {
|
|
|
|
|
pos: usize,
|
|
|
|
|
},
|
|
|
|
|
PlaylistsLoaded(Result<Vec<PlaylistCard>, String>),
|
|
|
|
|
PlaylistViewLoaded {
|
|
|
|
|
id: i64,
|
|
|
|
|
result: Result<PlaylistDetail, String>,
|
|
|
|
|
},
|
|
|
|
|
/// Liked track ids for the ♥ markers.
|
|
|
|
|
LikesLoaded(Result<Vec<i64>, String>),
|
|
|
|
|
LikeToggled {
|
|
|
|
|
track_id: i64,
|
|
|
|
|
liked: bool,
|
|
|
|
|
},
|
2026-07-17 15:29:10 +03:00
|
|
|
/// Liked federated item ids for the ♥ markers.
|
|
|
|
|
FedLikesLoaded(Result<Vec<String>, String>),
|
|
|
|
|
FedLikeToggled {
|
|
|
|
|
item_id: String,
|
|
|
|
|
liked: bool,
|
|
|
|
|
},
|
2026-06-10 16:11:09 +01:00
|
|
|
/// A release fetched for queueing (a / shift-a on a release).
|
|
|
|
|
EnqueueTracks {
|
|
|
|
|
tracks: Vec<TrackItem>,
|
|
|
|
|
next: bool,
|
|
|
|
|
},
|
2026-06-10 23:30:03 +01:00
|
|
|
PlaylistCreated {
|
|
|
|
|
result: Result<PlaylistCard, String>,
|
2026-07-17 01:43:09 +03:00
|
|
|
/// Add this target to the new playlist right away (Shift-P flow).
|
|
|
|
|
add_target: Option<crate::app::state::PlaylistAddTarget>,
|
2026-06-10 23:30:03 +01:00
|
|
|
},
|
|
|
|
|
PlaylistTracksAdded {
|
|
|
|
|
playlist_id: i64,
|
|
|
|
|
playlist_title: String,
|
|
|
|
|
result: Result<(), String>,
|
|
|
|
|
},
|
2026-07-16 20:29:51 +03:00
|
|
|
/// The library was mutated (import, edit, delete): cached views must be
|
|
|
|
|
/// dropped and reloaded lazily.
|
|
|
|
|
LibraryChanged {
|
|
|
|
|
message: Option<String>,
|
|
|
|
|
},
|
|
|
|
|
/// Progress of a running import, shown in the status bar.
|
|
|
|
|
ImportProgress {
|
|
|
|
|
done: usize,
|
|
|
|
|
total: usize,
|
|
|
|
|
current: String,
|
|
|
|
|
},
|
|
|
|
|
/// Fresh copies of the queued tracks after a library change. Tracks
|
|
|
|
|
/// missing from the result were deleted and leave the queue.
|
|
|
|
|
QueueTracksRefreshed {
|
|
|
|
|
tracks: Vec<TrackItem>,
|
|
|
|
|
},
|
|
|
|
|
/// A status snapshot for the Federation tab.
|
|
|
|
|
FederationStatus(crate::federation::FedStatus),
|
2026-07-17 01:17:54 +03:00
|
|
|
/// Federated live-search results (artists a card can be opened for,
|
|
|
|
|
/// plus matching tracks).
|
2026-07-16 20:29:51 +03:00
|
|
|
FedSearchLoaded {
|
|
|
|
|
seq: u64,
|
2026-07-17 01:17:54 +03:00
|
|
|
result: Result<crate::federation::FedSearchResults, String>,
|
|
|
|
|
},
|
|
|
|
|
/// A federated artist card finished assembling.
|
|
|
|
|
FedArtistLoaded {
|
|
|
|
|
name: String,
|
|
|
|
|
result: Result<crate::federation::FedArtistCard, String>,
|
2026-07-16 20:29:51 +03:00
|
|
|
},
|
2026-07-17 01:43:09 +03:00
|
|
|
/// A streamed image for the open card arrived (artist image when
|
|
|
|
|
/// `release` is None, a release cover otherwise).
|
|
|
|
|
FedCardArt {
|
|
|
|
|
name: String,
|
|
|
|
|
release: Option<String>,
|
|
|
|
|
path: String,
|
|
|
|
|
},
|
2026-07-17 15:29:10 +03:00
|
|
|
/// A pending federated track finished downloading (or failed); the
|
|
|
|
|
/// queue swaps the placeholder for the resolved track.
|
|
|
|
|
FedTrackResolved {
|
|
|
|
|
placeholder_id: i64,
|
|
|
|
|
result: Result<Box<crate::federation::FedPlayable>, String>,
|
2026-07-16 20:29:51 +03:00
|
|
|
},
|
2026-07-21 00:12:45 +03:00
|
|
|
/// Rich metadata for a federated track-info preview arrived without
|
|
|
|
|
/// downloading the audio file.
|
|
|
|
|
FedTrackInfoLoaded {
|
|
|
|
|
placeholder_id: i64,
|
|
|
|
|
item_id: String,
|
|
|
|
|
result: Result<TrackItem, String>,
|
|
|
|
|
},
|
2026-07-16 20:29:51 +03:00
|
|
|
/// This peer's connection ticket, requested from the Federation tab.
|
|
|
|
|
FedTicket(Result<String, String>),
|
2026-06-10 16:11:09 +01:00
|
|
|
}
|