From 4d1859b331f2ddb6079dd4d64084192b1a300918 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Tue, 6 May 2025 01:34:53 +0000 Subject: [PATCH] Add playlist page --- src/app.rs | 2 + src/pages/mod.rs | 1 + src/pages/playlist.rs | 218 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 src/pages/playlist.rs diff --git a/src/app.rs b/src/app.rs index c985c3d..c56c1bd 100644 --- a/src/app.rs +++ b/src/app.rs @@ -6,6 +6,7 @@ use crate::pages::album::*; use crate::pages::artist::*; use crate::pages::dashboard::*; use crate::pages::login::*; +use crate::pages::playlist::*; use crate::pages::profile::*; use crate::pages::search::*; use crate::pages::signup::*; @@ -73,6 +74,7 @@ pub fn App() -> impl IntoView { + diff --git a/src/pages/mod.rs b/src/pages/mod.rs index dc0b635..2cb0ec4 100644 --- a/src/pages/mod.rs +++ b/src/pages/mod.rs @@ -2,6 +2,7 @@ pub mod album; pub mod artist; pub mod dashboard; pub mod login; +pub mod playlist; pub mod profile; pub mod search; pub mod signup; diff --git a/src/pages/playlist.rs b/src/pages/playlist.rs new file mode 100644 index 0000000..dc3fc7b --- /dev/null +++ b/src/pages/playlist.rs @@ -0,0 +1,218 @@ +use crate::api::playlists::*; +use crate::components::error::*; +use crate::components::loading::*; +use crate::components::song_list::*; +use crate::models::backend; +use crate::util::state::GlobalState; +use leptos::either::*; +use leptos::ev::{keydown, KeyboardEvent}; +use leptos::html::{Button, Input}; +use leptos::logging::*; +use leptos::prelude::*; +use leptos::task::spawn_local; +use leptos_icons::*; +use leptos_router::components::Form; +use leptos_router::hooks::{use_navigate, use_params_map}; +use leptos_use::{on_click_outside, use_event_listener}; +use std::sync::Arc; +use web_sys::Response; + +#[component] +pub fn PlaylistPage() -> impl IntoView { + let params = use_params_map(); + + view! { + {move || params.with(|params| { + match params.get("id").map(|id| id.parse::()) { + Some(Ok(id)) => { + Either::Left(view! { }) + }, + Some(Err(e)) => { + Either::Right(view! { + + title="Invalid Playlist ID" + error=e.to_string() + /> + }) + }, + None => { + Either::Right(view! { + + title="No Playlist ID" + message="You must specify a playlist ID to view its page." + /> + }) + } + } + })} + } +} + +#[component] +fn PlaylistIdPage(#[prop(into)] id: Signal) -> impl IntoView { + let playlist_songs = Resource::new(id, get_playlist_songs); + + view! { + } + > + {move || GlobalState::playlists().get().map(|playlists| { + let playlist = playlists.map(|playlists| { + playlists.into_iter().find(|playlist| playlist.id == id.get()) + }); + + match playlist { + Ok(Some(playlist)) => { + Either::Left(view! { }) + }, + Ok(None) => { + Either::Right(view! { + + title="Playlist not found" + message="The playlist you are looking for does not exist." + /> + }) + } + Err(e) => Either::Right(view! { + + title="Error loading playlist" + error=e.to_string() + /> + }), + } + })} + {move || playlist_songs.get().map(|playlist_songs| { + match playlist_songs { + Ok(playlist_songs) => { + Either::Left(view! { }) + }, + Err(e) => Either::Right(view! { + + title="Error loading playlist songs" + error=e.to_string() + /> + }), + } + })} + + } +} + +#[component] +fn PlaylistInfo(playlist: backend::Playlist) -> impl IntoView { + let on_img_edit_response = Arc::new(move |response: &Response| { + if response.ok() { + // TODO inform browser that image has changed + } else { + error!("Error editing playlist image: {}", response.status()); + // TODO toast + } + }); + + let playing = RwSignal::new(false); + + let editing_name = RwSignal::new(false); + let playlist_name = RwSignal::new(playlist.name.clone()); + + let name_edit_input = NodeRef::::new(); + + let edit_complete = move || { + editing_name.set(false); + + spawn_local(async move { + if let Err(e) = rename_playlist(playlist.id, playlist_name.get_untracked()).await { + error!("Error editing playlist name: {}", e); + // TODO toast + } else { + GlobalState::playlists().refetch(); + } + }); + }; + + let _edit_close_handler = on_click_outside(name_edit_input, move |_| edit_complete()); + + let _edit_enter_handler = + use_event_listener(name_edit_input, keydown, move |event: KeyboardEvent| { + if event.key() == "Enter" { + event.prevent_default(); + edit_complete(); + } + }); + + let on_play = move |_| { + playing.set(!playing.get()); + }; + + let delete_btn = NodeRef:: + + + + + } +}