From 885651a6e5ce671d222294e6528e9c3dc5daeede Mon Sep 17 00:00:00 2001 From: Kubat <mael.martin31@gmail.com> Date: Mon, 23 Oct 2023 06:49:05 +0200 Subject: [PATCH] AMADEUS: Update the playlists component, use a vector where we control how we search for elements (buggy behaviour for the HashMap...) + Handle the case where the user deletes the playlist that is displayed in the main panel --- amadeus/src/app.rs | 10 +- amadeus/src/components/mainpanel/playlists.rs | 121 ++++++++++++++---- amadeus/src/components/sidebar.rs | 17 ++- 3 files changed, 116 insertions(+), 32 deletions(-) diff --git a/amadeus/src/app.rs b/amadeus/src/app.rs index e1eba7fa..5bd13df5 100644 --- a/amadeus/src/app.rs +++ b/amadeus/src/app.rs @@ -243,6 +243,8 @@ impl Amadeus { self.handle_refresh_request(RefreshRequest::Playlist(plt)) } playlists::Request::Delete(plt) => Command::batch([ + self.sidebar + .update(sidebar::Message::DeletePlaylist(plt.clone())), self.mainpanel .update(playlists::Message::DeletePlaylist(plt.clone()).into()) .map(Message::from), @@ -257,6 +259,9 @@ impl Amadeus { playlists::Request::RemoveFrom(name, id) => { send(remove_from_playlist(cfg, name, KaraFilter::KId(id))) } + playlists::Request::ChangeView => { + Command::perform(async {}, |_| Message::MainPanelDisplay(Default::default())) + } }, // Search/Database @@ -676,14 +681,11 @@ impl Application for Amadeus { } // A message for the side panel. + Message::SidebarMessage(message) => self.sidebar.update(message), Message::SideBarResize(size) => { self.sidebar_size = Some(size); Command::none() } - Message::SidebarMessage(message) => { - self.sidebar.update(message); - Command::none() - } // Refresh from lektord. Message::RefreshRequest(req) => self.handle_refresh_request(req), diff --git a/amadeus/src/components/mainpanel/playlists.rs b/amadeus/src/components/mainpanel/playlists.rs index 9b11ea3c..1f3b77aa 100644 --- a/amadeus/src/components/mainpanel/playlists.rs +++ b/amadeus/src/components/mainpanel/playlists.rs @@ -1,5 +1,4 @@ use crate::components::{self, icon, karalist, tip}; -use hashbrown::HashMap; use iced::{widget::row, Command, Element}; use lektor_payloads::{KId, Kara, PlaylistInfo}; use lektor_utils::log; @@ -7,7 +6,7 @@ use std::sync::Arc; #[derive(Default)] pub struct State { - playlists: HashMap<Arc<str>, (PlaylistInfo, karalist::State)>, + playlists: Vec<(Arc<str>, PlaylistInfo, karalist::State)>, to_show: Option<Arc<str>>, } @@ -16,16 +15,22 @@ pub enum Message { /// Reload the content of a playlist. Reload(Arc<str>, Vec<Arc<Kara>>), + /// Update informations about a playlist. UpdatePlaylistInfos(Arc<str>, PlaylistInfo), + /// Delete a playlist by its name. DeletePlaylist(Arc<str>), + /// Keep playlists, delete all the others. KeepPlaylists(Vec<Arc<str>>), + /// Remove a kara from a playlist by its Id. RemoveKaraFromPlaylist(Arc<str>, KId), + /// Add a kara to a playlist. AddKaraToPlaylist(Arc<str>, Arc<Kara>), + /// Message to notify which playlist to show. ShowPlaylist(Arc<str>), } @@ -45,55 +50,120 @@ pub enum Request { /// Inner playlist event, wrapper around the [karalist::Request] Inner(Arc<str>, karalist::Request), + + /// Message to tell Amadeus to change view. This can be the consequence of deleting a playlist. + ChangeView, } impl State { + /// Get a mut view to a playlist info, content, etc. If the playlist was not found returns + /// [None], otherwise returns [Some]. + fn get_mut( + &mut self, + name: impl AsRef<str>, + ) -> Option<&mut (Arc<str>, PlaylistInfo, karalist::State)> { + self.playlists + .iter_mut() + .find(|(plt, ..)| plt.as_ref().eq(name.as_ref())) + } + + /// Get a const view to a playlist info, content, etc. If the playlist was not found returns + /// [None], otherwise returns [Some]. + fn get(&self, name: impl AsRef<str>) -> Option<&(Arc<str>, PlaylistInfo, karalist::State)> { + self.playlists + .iter() + .find(|(plt, ..)| plt.as_ref().eq(name.as_ref())) + } + + /// Remove playlists from the list of playlists, returns whever the playlist was found and was + /// removed from the vector. + fn remove(&mut self, names: &[impl AsRef<str>]) -> bool { + let count = self.playlists.len(); + self.playlists + .retain(|(name, ..)| names.iter().any(|n| n.as_ref().ne(name.as_ref()))); + count != self.playlists.len() + } + + /// Keep playlists from the list of playlists. + fn keep(&mut self, names: &[impl AsRef<str>]) { + self.playlists + .retain(|(name, ..)| names.iter().any(|n| n.as_ref().eq(name.as_ref()))); + } + + /// Returns whever the container contains the asked playlist or not. + fn contains(&self, name: impl AsRef<str>) -> bool { + self.playlists + .iter() + .find(|(plt, ..)| name.as_ref().eq(plt.as_ref())) + .is_some() + } + pub fn update(&mut self, message: Message) -> Command<Request> { match message { Message::KeepPlaylists(plts) => { - self.playlists.retain(|name, _| plts.contains(name)); - Command::none() + self.keep(&plts); + match self.to_show { + Some(ref show) if !self.contains(show) => { + Command::perform(async {}, |_| Request::ChangeView) + } + _ => Command::none(), + } } + Message::Reload(plt, karas) => { - match self.playlists.get_mut(&plt) { - Some((_, plt)) => plt.update(karalist::Message::Reload(karas)), - None => log::error!("failed to update playlist {}", plt.as_ref()), + match self.get_mut(&plt) { + Some((.., plt)) => plt.update(karalist::Message::Reload(karas)), + None => log::error!("failed to update playlist {plt}"), } Command::none() } + Message::UpdatePlaylistInfos(plt, infos) => { - match self.playlists.get_mut(&plt) { - Some((old, _)) => *old = infos, - None => log::error!("failed to update playlist {}", plt.as_ref()), + match self.get_mut(&plt) { + Some((_, old, _)) => *old = infos, + None => log::error!("failed to update playlist {plt}"), } Command::none() } + Message::RemoveKaraFromPlaylist(plt, id) => { - match self.playlists.get_mut(&plt) { - Some((_, plt)) => plt.update(karalist::Message::RemoveId(id)), - None => log::error!( - "failed to delete kara {id:?} from playlist {}", - plt.as_ref() - ), + match self.get_mut(&plt) { + Some((.., plt)) => plt.update(karalist::Message::RemoveId(id)), + None => log::error!("failed to delete kara {id:?} from playlist {plt}"), } Command::none() } + Message::DeletePlaylist(plt) => { - if self.playlists.remove(&plt).is_none() { - log::error!("failed do delete playlist {}", plt.as_ref()) + if !self.remove(&[plt.as_ref()]) { + log::error!("failed do delete playlist {plt}"); + Command::none() + } else if self + .to_show + .as_ref() + .map(|show| show.as_ref().eq(plt.as_ref())) + .unwrap_or_default() + { + Command::perform(async {}, |_| Request::ChangeView) + } else { + Command::none() } - Command::none() } + Message::AddKaraToPlaylist(plt, kara) => { - match self.playlists.get_mut(&plt) { - Some(plt) => plt.1.update(karalist::Message::Add(kara)), - None => log::error!("can't add kara {} to playlist {}", kara.id, plt.as_ref()), + match self.get_mut(&plt) { + Some((.., plt)) => plt.update(karalist::Message::Add(kara)), + None => log::error!("can't add kara {} to playlist {plt}", kara.id), } Command::none() } Message::ShowPlaylist(name) => { - self.to_show = Some(name); + if self.contains(&name) { + self.to_show = Some(name); + } else { + log::error!("asked to show playlist {name}, but it wasn't found"); + } Command::none() } } @@ -107,9 +177,8 @@ impl State { } pub fn view(&self, plt: &Arc<str>) -> Element<'_, Request> { - self.playlists - .get_key_value(plt) - .map(|(plt, (_, content))| content.view().map(|req| Request::Inner(plt.clone(), req))) + self.get(plt) + .map(|(plt, _, content)| content.view().map(|req| Request::Inner(plt.clone(), req))) .unwrap_or(components::loading()) } } diff --git a/amadeus/src/components/sidebar.rs b/amadeus/src/components/sidebar.rs index 235c6cae..26c7ce98 100644 --- a/amadeus/src/components/sidebar.rs +++ b/amadeus/src/components/sidebar.rs @@ -11,9 +11,10 @@ use iced::{ scrollable::{Direction, Viewport}, text, vertical_space, Column, Space, }, - Element, Length, + Command, Element, Length, }; use lektor_payloads::PlaylistName; +use lektor_utils::log; use std::sync::Arc; pub struct State { @@ -38,6 +39,7 @@ pub enum Request { pub enum Message { Scrolled(Viewport), Playlists(Vec<Arc<str>>), + DeletePlaylist(Arc<str>), } impl FromIterator<PlaylistName> for Message { @@ -57,14 +59,25 @@ impl Default for State { } impl State { - pub fn update(&mut self, message: Message) { + pub fn update<T>(&mut self, message: Message) -> Command<T> { match message { Message::Scrolled(viewport) => { self.current_scroll_offset = viewport.relative_offset(); + Command::none() } Message::Playlists(playlists) => { let _ = std::mem::replace(&mut self.playlists, playlists); + Command::none() + } + + Message::DeletePlaylist(plt) => { + let count = self.playlists.len(); + self.playlists.retain(|name| name.as_ref().ne(plt.as_ref())); + if count == self.playlists.len() { + log::error!("failed to delete playlist {plt} from the sidebar"); + } + Command::none() } } } -- GitLab