use db::{Page, ReadDb, Source, TrackFilter};
use dioxus::prelude::*;
use server::source::TrackFavorite;
use tracing::Instrument;
use crate::db_reactivity::{Table, use_generations};
#[derive(Clone, Default, PartialEq)]
pub struct WindowRows {
pub offset: u32,
pub rows: Vec<reader::Track>,
}
#[derive(Clone, Copy)]
pub struct TracksWindow {
pub rows: Resource<WindowRows>,
pub total: Resource<u32>,
}
pub fn use_tracks_window(filter: Memo<TrackFilter>, page: Memo<Page>) -> TracksWindow {
let db = use_context::<ReadDb>();
let gens = use_generations();
let rows = use_resource({
let db = db.clone();
move || {
let _ = gens.generation(Table::Tracks);
let (db, f, p) = (db.clone(), filter(), page());
let span = tracing::info_span!(
"query.tracks_page",
filter = ?f,
offset = p.offset,
limit = p.limit,
rows = tracing::field::Empty,
);
async move {
let rows = db.tracks_page(&f, p).await.unwrap_or_default();
tracing::Span::current().record("rows", rows.len());
WindowRows {
offset: p.offset,
rows,
}
}
.instrument(span)
}
});
let total = use_resource({
let db = db.clone();
move || {
let _ = gens.generation(Table::Tracks);
let (db, f) = (db.clone(), filter());
let span = tracing::info_span!("query.tracks_count", filter = ?f, total = tracing::field::Empty);
async move {
let total = db.tracks_count(&f).await.unwrap_or(0);
tracing::Span::current().record("total", total);
total
}
.instrument(span)
}
});
TracksWindow { rows, total }
}
pub fn use_album_tracks(
source: Memo<Source>,
album_id: Memo<String>,
) -> Resource<Vec<reader::Track>> {
let db = use_context::<ReadDb>();
let gens = use_generations();
use_resource(move || {
let _ = gens.generation(Table::Tracks);
let (db, s, id) = (db.clone(), source(), album_id());
let span = tracing::info_span!(
"query.album_tracks",
source = s.as_str(),
album_id = %id,
rows = tracing::field::Empty,
);
async move {
if id.is_empty() {
tracing::Span::current().record("rows", 0);
return Vec::new();
}
let rows = db.album_tracks(&s, &id).await.unwrap_or_default();
tracing::Span::current().record("rows", rows.len());
rows
}
.instrument(span)
})
}
pub fn use_artist_tracks(
source: Memo<Source>,
artist: Memo<String>,
) -> Resource<Vec<reader::Track>> {
let db = use_context::<ReadDb>();
let gens = use_generations();
use_resource(move || {
let _ = gens.generation(Table::Tracks);
let (db, s, a) = (db.clone(), source(), artist());
let span = tracing::info_span!(
"query.artist_tracks",
source = s.as_str(),
artist = %a,
rows = tracing::field::Empty,
);
async move {
let rows = db.artist_tracks(&s, &a).await.unwrap_or_default();
tracing::Span::current().record("rows", rows.len());
rows
}
.instrument(span)
})
}
pub fn use_genre_tracks(source: Memo<Source>, genre: Memo<String>) -> Resource<Vec<reader::Track>> {
let db = use_context::<ReadDb>();
let gens = use_generations();
use_resource(move || {
let _ = gens.generation(Table::Tracks);
let (db, s, g) = (db.clone(), source(), genre());
let span = tracing::info_span!(
"query.genre_tracks",
source = s.as_str(),
genre = %g,
rows = tracing::field::Empty,
);
async move {
if g.is_empty() {
tracing::Span::current().record("rows", 0);
return Vec::new();
}
let rows = db.genre_tracks(&s, &g).await.unwrap_or_default();
tracing::Span::current().record("rows", rows.len());
rows
}
.instrument(span)
})
}
pub fn use_artist_sample_tracks(source: Memo<Source>, limit: u32) -> Resource<Vec<reader::Track>> {
let db = use_context::<ReadDb>();
let gens = use_generations();
use_resource(move || {
let _ = gens.generation(Table::Tracks);
let (db, s) = (db.clone(), source());
let span = tracing::info_span!(
"query.artist_samples",
source = s.as_str(),
limit,
rows = tracing::field::Empty,
);
async move {
let rows = db.artist_sample_tracks(&s, limit).await.unwrap_or_default();
tracing::Span::current().record("rows", rows.len());
rows
}
.instrument(span)
})
}
pub fn use_top_genre(source: Memo<Source>) -> Resource<Option<String>> {
let db = use_context::<ReadDb>();
let gens = use_generations();
use_resource(move || {
let _ = gens.generation(Table::Tracks);
let (db, s) = (db.clone(), source());
let span = tracing::info_span!("query.top_genre", source = s.as_str());
async move { db.top_genre(&s).await.unwrap_or_default() }.instrument(span)
})
}
pub fn use_tracks_by_keys(
source: Memo<Source>,
keys: Memo<Vec<String>>,
) -> Resource<Vec<reader::Track>> {
let db = use_context::<ReadDb>();
let gens = use_generations();
use_resource(move || {
let _ = gens.generation(Table::Tracks);
let (db, s, k) = (db.clone(), source(), keys());
let span = tracing::info_span!(
"query.tracks_by_keys",
source = s.as_str(),
keys = k.len(),
rows = tracing::field::Empty,
);
async move {
if k.is_empty() {
tracing::Span::current().record("rows", 0);
return Vec::new();
}
let rows = db.tracks_by_keys(&s, &k).await.unwrap_or_default();
tracing::Span::current().record("rows", rows.len());
rows
}
.instrument(span)
})
}
pub fn use_recently_played(source: Memo<Source>) -> Resource<Vec<reader::Track>> {
let db = use_context::<ReadDb>();
let gens = use_generations();
use_resource(move || {
let _ = gens.generation(Table::Recents);
let (db, s) = (db.clone(), source());
let span = tracing::info_span!("query.recently_played", source = s.as_str());
async move {
let keys = db.recently_played(&s, 50).await.unwrap_or_default();
if keys.is_empty() {
return Vec::new();
}
db.tracks_by_keys(&s, &keys).await.unwrap_or_default()
}
.instrument(span)
})
}
pub fn use_album(source: Memo<Source>, album_id: Memo<String>) -> Resource<Option<reader::Album>> {
let db = use_context::<ReadDb>();
let gens = use_generations();
use_resource(move || {
let _ = gens.generation(Table::Albums);
let (db, s, id) = (db.clone(), source(), album_id());
let span = tracing::info_span!("query.album", source = s.as_str(), album_id = %id);
async move { db.album(&s, &id).await.unwrap_or_default() }.instrument(span)
})
}
pub fn use_artists(source: Memo<Source>) -> Resource<Vec<(String, u32)>> {
let db = use_context::<ReadDb>();
let gens = use_generations();
use_resource(move || {
let _ = gens.generation(Table::Tracks);
let (db, s) = (db.clone(), source());
let span = tracing::info_span!(
"query.artists",
source = s.as_str(),
rows = tracing::field::Empty
);
async move {
let rows = db.artists(&s).await.unwrap_or_default();
tracing::Span::current().record("rows", rows.len());
rows
}
.instrument(span)
})
}
pub fn use_active_source() -> Memo<config::Source> {
let config = use_context::<Signal<config::AppConfig>>();
use_memo(move || config.read().active_source.clone())
}
pub fn use_playlists() -> Resource<reader::PlaylistStore> {
let db = use_context::<ReadDb>();
let gens = use_generations();
let source = use_active_source();
use_resource(move || {
let _ = gens.generation(Table::Playlists);
let _ = gens.generation(Table::Folders);
let (db, src) = (db.clone(), source());
let span = tracing::info_span!("query.playlists", source = %src.as_str());
async move { db.load_playlists(&src).await.unwrap_or_default() }.instrument(span)
})
}
pub fn use_artist_images() -> Resource<db::ArtistImages> {
let db = use_context::<ReadDb>();
let gens = use_generations();
use_resource(move || {
let _ = gens.generation(Table::Tracks);
let db = db.clone();
let span = tracing::info_span!("query.artist_images");
async move { db.artist_images().await.unwrap_or_default() }.instrument(span)
})
}
pub fn use_albums(source: Memo<Source>) -> Resource<Vec<reader::Album>> {
let db = use_context::<ReadDb>();
let gens = use_generations();
use_resource(move || {
let _ = gens.generation(Table::Albums);
let (db, s) = (db.clone(), source());
let span = tracing::info_span!(
"query.albums",
source = s.as_str(),
rows = tracing::field::Empty
);
async move {
let rows = db.albums(&s).await.unwrap_or_default();
tracing::Span::current().record("rows", rows.len());
rows
}
.instrument(span)
})
}
pub fn use_cover_resolver(max_width: u32) -> impl Fn(&reader::Track) -> Option<utils::CoverUrl> {
let config = use_context::<Signal<config::AppConfig>>();
move |track: &reader::Track| ::server::cover::track(&config.read(), track, max_width)
}
pub fn use_favorites() -> Resource<Vec<String>> {
let active_source = use_context::<Signal<::server::source::ActiveSource>>();
let gens = use_generations();
use_resource(move || {
let _ = gens.generation(Table::Favorites);
let source = active_source.read().clone();
async move { source.favorites().await.unwrap_or_default() }
})
}
pub fn use_track_is_favorite(track: Memo<Option<reader::Track>>) -> Memo<bool> {
let active_source = use_context::<Signal<::server::source::ActiveSource>>();
let gens = use_generations();
let res = use_resource(move || {
let _ = gens.generation(Table::Favorites);
let source = active_source.read().clone();
let track = track();
async move {
match track {
Some(t) => t.is_favorite(&source).await,
None => false,
}
}
});
use_memo(move || res.read().unwrap_or(false))
}