Handle SongListItem play/pause clicks
This commit is contained in:
parent
f633cffe69
commit
a3b3174306
@ -1,3 +1,5 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos::logging::*;
|
use leptos::logging::*;
|
||||||
use leptos_icons::*;
|
use leptos_icons::*;
|
||||||
@ -5,6 +7,7 @@ use leptos_icons::*;
|
|||||||
use crate::api::songs::*;
|
use crate::api::songs::*;
|
||||||
use crate::songdata::SongData;
|
use crate::songdata::SongData;
|
||||||
use crate::models::{Album, Artist};
|
use crate::models::{Album, Artist};
|
||||||
|
use crate::util::state::GlobalState;
|
||||||
|
|
||||||
const LIKE_DISLIKE_BTN_SIZE: &str = "2em";
|
const LIKE_DISLIKE_BTN_SIZE: &str = "2em";
|
||||||
|
|
||||||
@ -24,18 +27,52 @@ pub fn SongListExtra<T>(songs: Vec<(SongData, T)>) -> impl IntoView where
|
|||||||
fn SongListInner<T>(songs: Vec<(SongData, T)>, show_extra: bool) -> impl IntoView where
|
fn SongListInner<T>(songs: Vec<(SongData, T)>, show_extra: bool) -> impl IntoView where
|
||||||
T: Clone + IntoView + 'static
|
T: Clone + IntoView + 'static
|
||||||
{
|
{
|
||||||
|
let songs = Rc::new(songs);
|
||||||
|
let songs_2 = songs.clone();
|
||||||
|
|
||||||
|
// Signal that acts as a callback for a song list item to queue songs after it in the list
|
||||||
|
let (handle_queue_remaining, do_queue_remaining) = create_signal(None);
|
||||||
|
create_effect(move |_| {
|
||||||
|
let clicked_index = handle_queue_remaining.get();
|
||||||
|
|
||||||
|
if let Some(index) = clicked_index {
|
||||||
|
GlobalState::play_status().update(|status| {
|
||||||
|
let song: &(SongData, T) = songs.get(index).expect("Invalid song list item index");
|
||||||
|
|
||||||
|
if status.queue.front().map(|song| song.id) == Some(song.0.id) {
|
||||||
|
// If the clicked song is already at the front of the queue, just play it
|
||||||
|
status.playing = true;
|
||||||
|
} else {
|
||||||
|
// Otherwise, add the currently playing song to the history,
|
||||||
|
// clear the queue, and queue the clicked song and other after it
|
||||||
|
if let Some(last_playing) = status.queue.pop_front() {
|
||||||
|
status.history.push_back(last_playing);
|
||||||
|
}
|
||||||
|
|
||||||
|
status.queue.clear();
|
||||||
|
status.queue.extend(songs.iter().skip(index).map(|(song, _)| song.clone()));
|
||||||
|
status.playing = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<table class="song-list">
|
<table class="song-list">
|
||||||
{
|
{
|
||||||
let mut first_song = true;
|
songs_2.iter().enumerate().map(|(list_index, (song, extra))| {
|
||||||
|
let song_id = song.id;
|
||||||
|
let playing = create_rw_signal(false);
|
||||||
|
|
||||||
songs.iter().map(|(song, extra)| {
|
create_effect(move |_| {
|
||||||
let playing = first_song.into();
|
GlobalState::play_status().with(|status| {
|
||||||
first_song = false;
|
playing.set(status.queue.front().map(|song| song.id) == Some(song_id) && status.playing);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<SongListItem song={song.clone()} song_playing=playing
|
<SongListItem song={song.clone()} song_playing=playing.into()
|
||||||
extra={if show_extra { Some(extra.clone()) } else { None }} />
|
extra={if show_extra { Some(extra.clone()) } else { None }} list_index do_queue_remaining/>
|
||||||
}
|
}
|
||||||
}).collect::<Vec<_>>()
|
}).collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
@ -44,7 +81,8 @@ fn SongListInner<T>(songs: Vec<(SongData, T)>, show_extra: bool) -> impl IntoVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn SongListItem<T>(song: SongData, song_playing: MaybeSignal<bool>, extra: Option<T>) -> impl IntoView where
|
pub fn SongListItem<T>(song: SongData, song_playing: MaybeSignal<bool>, extra: Option<T>,
|
||||||
|
list_index: usize, do_queue_remaining: WriteSignal<Option<usize>>) -> impl IntoView where
|
||||||
T: IntoView + 'static
|
T: IntoView + 'static
|
||||||
{
|
{
|
||||||
let liked = create_rw_signal(song.like_dislike.map(|(liked, _)| liked).unwrap_or(false));
|
let liked = create_rw_signal(song.like_dislike.map(|(liked, _)| liked).unwrap_or(false));
|
||||||
@ -52,7 +90,8 @@ pub fn SongListItem<T>(song: SongData, song_playing: MaybeSignal<bool>, extra: O
|
|||||||
|
|
||||||
view! {
|
view! {
|
||||||
<tr class="song-list-item">
|
<tr class="song-list-item">
|
||||||
<td class="song-image"><SongImage image_path=song.image_path song_playing /></td>
|
<td class="song-image"><SongImage image_path=song.image_path song_playing
|
||||||
|
list_index do_queue_remaining /></td>
|
||||||
<td class="song-title"><p>{song.title}</p></td>
|
<td class="song-title"><p>{song.title}</p></td>
|
||||||
<td class="song-list-spacer"></td>
|
<td class="song-list-spacer"></td>
|
||||||
<td class="song-artists"><SongArtists artists=song.artists /></td>
|
<td class="song-artists"><SongArtists artists=song.artists /></td>
|
||||||
@ -72,13 +111,27 @@ pub fn SongListItem<T>(song: SongData, song_playing: MaybeSignal<bool>, extra: O
|
|||||||
/// Display the song's image, with an overlay if the song is playing
|
/// Display the song's image, with an overlay if the song is playing
|
||||||
/// When the song list item is hovered, the overlay will show the play button
|
/// When the song list item is hovered, the overlay will show the play button
|
||||||
#[component]
|
#[component]
|
||||||
fn SongImage(image_path: String, song_playing: MaybeSignal<bool>) -> impl IntoView {
|
fn SongImage(image_path: String, song_playing: MaybeSignal<bool>, list_index: usize,
|
||||||
|
do_queue_remaining: WriteSignal<Option<usize>>) -> impl IntoView
|
||||||
|
{
|
||||||
|
let play_song = move |_| {
|
||||||
|
do_queue_remaining.set(Some(list_index));
|
||||||
|
};
|
||||||
|
|
||||||
|
let pause_song = move |_| {
|
||||||
|
GlobalState::play_status().update(|status| {
|
||||||
|
status.playing = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<img class="song-image" src={image_path}/>
|
<img class="song-image" src={image_path}/>
|
||||||
{if song_playing.get() {
|
{move || if song_playing.get() {
|
||||||
view! { <Icon class="song-image-overlay song-playing-overlay" icon=icondata::BsPauseFill /> }.into_view()
|
view! { <Icon class="song-image-overlay song-playing-overlay"
|
||||||
|
icon=icondata::BsPauseFill on:click=pause_song /> }.into_view()
|
||||||
} else {
|
} else {
|
||||||
view! { <Icon class="song-image-overlay hide-until-hover" icon=icondata::BsPlayFill /> }.into_view()
|
view! { <Icon class="song-image-overlay hide-until-hover"
|
||||||
|
icon=icondata::BsPlayFill on:click=play_song /> }.into_view()
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user