use serde::{Deserialize, Serialize};
use crate::{
data::library::{LibraryItem, LibraryKind},
load_state::LoadState,
providers::ProviderId,
types::Track,
};
#[derive(Debug, Default)]
pub struct RequestIdGenerator(u64);
impl RequestIdGenerator {
pub fn next(&mut self) -> u64 {
self.0 += 1;
self.0
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ViewData {
pub kind: ViewKind,
pub content: LoadState<Vec<Track>>,
pub selection: Vec<usize>,
pub scroll: f32,
#[serde(skip)]
pub request_id: u64,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct SearchData {
pub query: String,
pub provider: ProviderId,
pub tab: crate::providers::SearchTab,
pub exhausted: bool,
#[serde(skip)]
pub append_in_flight: bool,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct AlbumRef {
pub id: String,
pub name: String,
#[serde(default)]
pub badge: String,
#[serde(default)]
pub date: String,
#[serde(default)]
pub thumbnail: String,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct PlaylistRef {
pub id: String,
pub name: String,
#[serde(default)]
pub thumbnail: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PlaylistEntry {
pub index: usize,
pub name: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ArtistEntry {
pub id: String,
pub name: String,
pub source: ProviderId,
pub page: Box<crate::providers::ArtistPageState>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ViewKind {
Search(SearchData),
SongRadio(String),
ArtistRadio(String),
Artist(ArtistEntry),
Album(AlbumRef),
PlaylistView(PlaylistRef),
Playlist(PlaylistEntry),
Downloads,
Settings,
}
impl From<LibraryItem> for ViewKind {
fn from(item: LibraryItem) -> Self {
match item.kind {
LibraryKind::Artist => ViewKind::Artist(ArtistEntry {
id: item.id.clone(),
name: item.title,
source: item.provider,
page: Box::new(crate::providers::ArtistPageState::new(
item.provider,
&item.id,
)),
}),
LibraryKind::Album => ViewKind::Album(AlbumRef {
id: item.id,
name: item.title,
thumbnail: item.thumbnail,
..Default::default()
}),
LibraryKind::Playlist => ViewKind::PlaylistView(PlaylistRef {
id: item.id,
name: item.title,
thumbnail: item.thumbnail,
}),
}
}
}
impl Default for ViewKind {
fn default() -> Self {
ViewKind::Search(SearchData {
query: String::new(),
provider: ProviderId::YouTube,
tab: crate::providers::SearchTab::Songs,
..Default::default()
})
}
}
impl ViewKind {
pub fn browse_params(&self) -> Option<(&str, &'static str, &str)> {
match self {
ViewKind::Album(r) => Some((&r.id, "album", &r.name)),
ViewKind::PlaylistView(r) => Some((&r.id, "playlist", &r.name)),
_ => None,
}
}
}
impl ViewData {
pub fn is_search_like(&self) -> bool {
matches!(
self.kind,
ViewKind::Search(_)
| ViewKind::SongRadio(_)
| ViewKind::ArtistRadio(_)
| ViewKind::Artist(_)
| ViewKind::Album(_)
| ViewKind::PlaylistView(_)
)
}
pub fn same_kind(&self, other: &Self) -> bool {
match (&self.kind, &other.kind) {
(ViewKind::Search(a), ViewKind::Search(b)) => {
a.query == b.query && a.provider == b.provider && a.tab == b.tab
}
(ViewKind::SongRadio(a), ViewKind::SongRadio(b))
| (ViewKind::ArtistRadio(a), ViewKind::ArtistRadio(b)) => a == b,
(ViewKind::Artist(a), ViewKind::Artist(b)) => a.id == b.id && a.source == b.source,
(ViewKind::Playlist(a), ViewKind::Playlist(b)) => a == b,
(ViewKind::Downloads, ViewKind::Downloads)
| (ViewKind::Settings, ViewKind::Settings) => true,
_ => false,
}
}
pub fn new_search(
query: String,
provider: ProviderId,
scope: crate::providers::SearchScope,
) -> Self {
Self {
kind: ViewKind::Search(SearchData {
query,
provider,
tab: crate::providers::SearchTab::from_scope(scope),
..Default::default()
}),
content: LoadState::Loading,
..Default::default()
}
}
pub fn new_radio(kind: ViewKind) -> Self {
Self {
kind,
content: LoadState::Loading,
..Default::default()
}
}
pub fn new_playlist(index: usize, name: String) -> Self {
Self {
kind: ViewKind::Playlist(PlaylistEntry { index, name }),
..Default::default()
}
}
pub fn new_downloads(tracks: Vec<Track>) -> Self {
Self {
kind: ViewKind::Downloads,
content: LoadState::Ready(tracks),
..Default::default()
}
}
pub fn new_settings() -> Self {
Self {
kind: ViewKind::Settings,
..Default::default()
}
}
pub fn tracks(&self) -> &[Track] {
match &self.content {
LoadState::Ready(tracks) => tracks.as_slice(),
_ => &[],
}
}
pub fn set_tracks(&mut self, tracks: Vec<Track>) {
self.content = LoadState::Ready(tracks);
}
pub fn set_failed(&mut self, msg: String) {
self.content = LoadState::Failed(msg);
}
pub fn tracks_mut(&mut self) -> Option<&mut Vec<Track>> {
match &mut self.content {
LoadState::Ready(tracks) => Some(tracks),
_ => None,
}
}
}