Display playlists on sidebar
This commit is contained in:
@ -1,6 +1,15 @@
|
||||
use crate::components::error::Error;
|
||||
use crate::components::loading::*;
|
||||
use crate::components::menu::*;
|
||||
use crate::util::state::GlobalState;
|
||||
use leptos::html::Div;
|
||||
use leptos::prelude::*;
|
||||
use leptos_icons::*;
|
||||
use leptos_router::components::{Form, A};
|
||||
use leptos_router::hooks::use_location;
|
||||
use leptos_use::{on_click_outside_with_options, OnClickOutsideOptions};
|
||||
use std::sync::Arc;
|
||||
use web_sys::Response;
|
||||
|
||||
#[component]
|
||||
pub fn Sidebar(
|
||||
@ -9,7 +18,7 @@ pub fn Sidebar(
|
||||
add_album_open: RwSignal<bool>,
|
||||
) -> impl IntoView {
|
||||
view! {
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col w-[250px] min-w-[250px]">
|
||||
<Menu upload_open add_artist_open add_album_open />
|
||||
<Playlists />
|
||||
</div>
|
||||
@ -17,18 +26,143 @@ pub fn Sidebar(
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Playlists() -> impl IntoView {
|
||||
fn AddPlaylistDialog(open: RwSignal<bool>, node_ref: NodeRef<Div>) -> impl IntoView {
|
||||
let playlist_name = RwSignal::new("".to_string());
|
||||
let loading = RwSignal::new(false);
|
||||
let error_msg = RwSignal::new(None);
|
||||
|
||||
let handle_response = Arc::new(move |response: &Response| {
|
||||
loading.set(false);
|
||||
|
||||
if response.ok() {
|
||||
open.set(false);
|
||||
GlobalState::playlists().refetch();
|
||||
} else {
|
||||
error_msg.set(Some("Failed to create playlist".to_string()));
|
||||
}
|
||||
});
|
||||
|
||||
view! {
|
||||
<div class="home-card">
|
||||
<div class="flex">
|
||||
<h1 class="header">Playlists</h1>
|
||||
<button class="add-playlist">
|
||||
<div class="add-sign">
|
||||
<Icon icon={icondata::IoAddSharp} />
|
||||
<dialog class="fixed top-0 left-0 w-full h-full bg-black/50 flex items-center justify-center" class:open=open>
|
||||
<div node_ref=node_ref class="bg-neutral-800 rounded-lg p-4 w-1/3 text-white">
|
||||
<div class="flex items-center pb-3">
|
||||
<h1 class="text-2xl">"Create Playlist"</h1>
|
||||
<button id="add-playlist-dialog-btn" class="control ml-auto" on:click=move |_| open.set(false)>
|
||||
<Icon icon={icondata::IoClose} {..} class="w-7 h-7" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Form action="/api/playlists/create" on_response=handle_response.clone()
|
||||
method="POST" enctype="multipart/form-data".to_string()>
|
||||
<div class="grid grid-cols-[auto_1fr] gap-4">
|
||||
<label for="new-playlist-name">"Playlist Name"</label>
|
||||
<input id="new-playlist-name" name="name"
|
||||
class="bg-neutral-800 text-neutral-200 border border-neutral-600 rounded-lg p-2 outline-none"
|
||||
type="text" placeholder="My Playlist" bind:value=playlist_name required autocomplete="off" />
|
||||
|
||||
<label for="new-playlist-img">"Cover Image"</label>
|
||||
<input id="new-playlist-img" name="picture" type="file" accept="image/*" />
|
||||
</div>
|
||||
New Playlist
|
||||
</button>
|
||||
|
||||
{move || {
|
||||
error_msg.get().map(|error| {
|
||||
view! {
|
||||
<Error<String>
|
||||
message=error.clone()
|
||||
/>
|
||||
}
|
||||
})
|
||||
}}
|
||||
|
||||
<div class="flex justify-end">
|
||||
<button type="submit" class="control-solid" on:click=move |_| {
|
||||
error_msg.set(None);
|
||||
loading.set(true);
|
||||
}>
|
||||
"Create"
|
||||
</button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Playlists() -> impl IntoView {
|
||||
let location = use_location();
|
||||
|
||||
let add_playlist_open = RwSignal::new(false);
|
||||
|
||||
let create_playlist = move |_| {
|
||||
leptos::logging::log!("Creating playlist");
|
||||
add_playlist_open.set(true);
|
||||
};
|
||||
|
||||
let add_playlist_dialog = NodeRef::<Div>::new();
|
||||
|
||||
let _dialog_close_handler = on_click_outside_with_options(
|
||||
add_playlist_dialog,
|
||||
move |_| add_playlist_open.set(false),
|
||||
OnClickOutsideOptions::default().ignore(["#add-playlist-dialog-btn"]),
|
||||
);
|
||||
|
||||
view! {
|
||||
<div class="home-card">
|
||||
<div class="flex items-center mb-2">
|
||||
<h1 class="p-2 text-xl">"Playlists"</h1>
|
||||
<button class="control-solid ml-auto" on:click=create_playlist>
|
||||
<Icon icon={icondata::AiPlusOutlined} {..} class="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<Transition
|
||||
fallback=move || view! { <Loading /> }
|
||||
>
|
||||
<ErrorBoundary
|
||||
fallback=|errors| {
|
||||
errors.get().into_iter().map(|(_id, error)| {
|
||||
view! {
|
||||
<Error<String>
|
||||
message=error.to_string()
|
||||
/>
|
||||
}
|
||||
}).collect::<Vec<_>>()
|
||||
}
|
||||
>
|
||||
{move || GlobalState::playlists().get().map(|playlists| {
|
||||
playlists.map(|playlists| {
|
||||
|
||||
view! {
|
||||
{playlists.into_iter().map(|playlist| {
|
||||
let active = Signal::derive(move || {
|
||||
location.pathname.get().ends_with(&format!("/playlist/{}", playlist.id))
|
||||
});
|
||||
|
||||
view! {
|
||||
<A href={format!("/playlist/{}", playlist.id)} {..}
|
||||
style={move || if active() {"background-color: var(--color-neutral-700);"} else {""}}
|
||||
class="flex items-center hover:bg-neutral-700 rounded-md my-1" >
|
||||
<img class="w-15 h-15 rounded-xl p-2 object-cover"
|
||||
src={format!("/assets/images/playlist/{}.webp", playlist.id)}
|
||||
onerror={crate::util::img_fallback::MUSIC_IMG_FALLBACK} />
|
||||
<h2 class="pr-3 my-2">{playlist.name}</h2>
|
||||
</A>
|
||||
}
|
||||
}).collect::<Vec<_>>()}
|
||||
}
|
||||
})
|
||||
})}
|
||||
</ErrorBoundary>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Show
|
||||
when=add_playlist_open
|
||||
fallback=move || view! {}
|
||||
>
|
||||
<AddPlaylistDialog node_ref=add_playlist_dialog open=add_playlist_open />
|
||||
</Show>
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user