Skip to content

Commit

Permalink
Uri handler (xou816#465)
Browse files Browse the repository at this point in the history
* Added spotify URI handler

* continue uri handler

* very slightly better error handling

* uh fix something?

* various state management fixes

Co-authored-by: bbb651 <[email protected]>
  • Loading branch information
xou816 and bbb651 authored Feb 7, 2022
1 parent cccc9ad commit 917fdee
Show file tree
Hide file tree
Showing 18 changed files with 249 additions and 177 deletions.
3 changes: 2 additions & 1 deletion AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ Niklas Sauter
Nicolas Fella
Fridolin Weisser
Jan Przebor
Warren Hu
Warren Hu
bbb651
3 changes: 2 additions & 1 deletion data/dev.alextren.Spot.desktop
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
[Desktop Entry]
Version=1.0
Name=Spot
Exec=spot %u
GenericName=Music Player
Exec=spot
Icon=dev.alextren.Spot
Terminal=false
Type=Application
Categories=GTK;GNOME;Music;AudioVideo;
MimeType=x-scheme-handler/spotify;
StartupNotify=true
X-Purism-FormFactor=Workstation;Mobile;
6 changes: 5 additions & 1 deletion po/spot.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: spot\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-01-29 17:34-0500\n"
"POT-Creation-Date: 2022-02-06 17:30-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down Expand Up @@ -141,6 +141,10 @@ msgstr ""
msgid "Connection restored"
msgstr ""

#: src/main.rs:77
msgid "Failed to open link!"
msgstr ""

#. A title that is shown when the user has not saved any playlists.
#: src/app/components/saved_playlists/saved_playlists.ui:26
msgid "You have no saved playlists."
Expand Down
15 changes: 11 additions & 4 deletions src/app/components/artist_details/artist_details_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use gio::SimpleActionGroup;
use std::ops::Deref;
use std::rc::Rc;

use crate::api::SpotifyApiError;
use crate::app::components::SimpleHeaderBarModel;
use crate::app::components::{labels, PlaylistModel};
use crate::app::models::*;
Expand Down Expand Up @@ -41,9 +42,15 @@ impl ArtistDetailsModel {
let api = self.app_model.get_spotify();
self.dispatcher
.call_spotify_and_dispatch(move || async move {
api.get_artist(&id)
.await
.map(|artist| BrowserAction::SetArtistDetails(Box::new(artist)).into())
let artist = api.get_artist(&id).await;
match artist {
Ok(artist) => Ok(BrowserAction::SetArtistDetails(Box::new(artist)).into()),
Err(SpotifyApiError::BadStatus(400, _))
| Err(SpotifyApiError::BadStatus(404, _)) => {
Ok(BrowserAction::NavigationPop.into())
}
Err(e) => Err(e),
}
});
}

Expand All @@ -64,7 +71,7 @@ impl ArtistDetailsModel {
.call_spotify_and_dispatch(move || async move {
api.get_artist_albums(&id, offset, batch_size)
.await
.map(|albums| BrowserAction::AppendArtistReleases(albums).into())
.map(|albums| BrowserAction::AppendArtistReleases(id, albums).into())
});

Some(())
Expand Down
13 changes: 10 additions & 3 deletions src/app/components/details/details_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::cell::Ref;
use std::ops::Deref;
use std::rc::Rc;

use crate::api::SpotifyApiError;
use crate::app::components::labels;
use crate::app::components::HeaderBarModel;
use crate::app::components::PlaylistModel;
Expand Down Expand Up @@ -56,9 +57,15 @@ impl DetailsModel {
let api = self.app_model.get_spotify();
self.dispatcher
.call_spotify_and_dispatch(move || async move {
api.get_album(&id)
.await
.map(|album| BrowserAction::SetAlbumDetails(Box::new(album)).into())
let album = api.get_album(&id).await;
match album {
Ok(album) => Ok(BrowserAction::SetAlbumDetails(Box::new(album)).into()),
Err(SpotifyApiError::BadStatus(400, _))
| Err(SpotifyApiError::BadStatus(404, _)) => {
Ok(BrowserAction::NavigationPop.into())
}
Err(e) => Err(e),
}
});
}

Expand Down
3 changes: 2 additions & 1 deletion src/app/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ impl dyn ActionDispatcher {
{
self.dispatch_many_async(Box::pin(async move {
let first_call = call.clone();
match first_call().await {
let result = first_call().await;
match result {
Ok(actions) => actions,
Err(SpotifyApiError::NoToken) => vec![],
Err(SpotifyApiError::InvalidToken) => {
Expand Down
15 changes: 12 additions & 3 deletions src/app/components/playlist_details/playlist_details_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use gio::SimpleActionGroup;
use std::ops::Deref;
use std::rc::Rc;

use crate::api::SpotifyApiError;
use crate::app::components::SimpleHeaderBarModel;
use crate::app::components::{labels, PlaylistModel};
use crate::app::models::*;
Expand Down Expand Up @@ -46,9 +47,17 @@ impl PlaylistDetailsModel {
let id = self.id.clone();
self.dispatcher
.call_spotify_and_dispatch(move || async move {
api.get_playlist(&id)
.await
.map(|playlist| BrowserAction::SetPlaylistDetails(Box::new(playlist)).into())
let playlist = api.get_playlist(&id).await;
match playlist {
Ok(playlist) => {
Ok(BrowserAction::SetPlaylistDetails(Box::new(playlist)).into())
}
Err(SpotifyApiError::BadStatus(400, _))
| Err(SpotifyApiError::BadStatus(404, _)) => {
Ok(BrowserAction::NavigationPop.into())
}
Err(e) => Err(e),
}
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/app/components/selection/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ impl SelectionToolbarModel {
.call_spotify_and_dispatch_many(move || async move {
api.remove_from_playlist(&id, uris.clone()).await?;
Ok(vec![
BrowserAction::RemoveTracksFromPlaylist(uris).into(),
BrowserAction::RemoveTracksFromPlaylist(id, uris).into(),
SelectionAction::Clear.into(),
])
})
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/user_details/user_details_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl UserDetailsModel {
.call_spotify_and_dispatch(move || async move {
api.get_user_playlists(&id, offset, batch_size)
.await
.map(|playlists| BrowserAction::AppendUserPlaylists(playlists).into())
.map(|playlists| BrowserAction::AppendUserPlaylists(id, playlists).into())
});

Some(())
Expand Down
8 changes: 5 additions & 3 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,14 @@ impl App {
}

fn handle(&mut self, message: AppAction) {
if let AppAction::Start = message {
self.add_ui_components();
}
let starting = matches!(&message, &AppAction::Start);

let events = self.model.update_state(message);

if !events.is_empty() && starting {
self.add_ui_components();
}

for event in events.iter() {
for component in self.components.iter_mut() {
component.on_event(event);
Expand Down
98 changes: 60 additions & 38 deletions src/app/state/app_state.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::borrow::Cow;

use crate::app::state::{
browser_state::{BrowserAction, BrowserEvent, BrowserState},
login_state::{LoginAction, LoginEvent, LoginState},
Expand Down Expand Up @@ -28,6 +30,30 @@ pub enum AppAction {
}

impl AppAction {
#[allow(non_snake_case)]
pub fn OpenURI(uri: String) -> Option<Self> {
debug!("parsing {}", &uri);
let mut parts = uri.split(':');
if parts.next()? != "spotify" {
return None;
}

// Might start with /// because of https://gitlab.gnome.org/GNOME/glib/-/issues/1886/
let action = parts
.next()?
.strip_prefix("///")
.filter(|p| !p.is_empty())?;
let data = parts.next()?;

match action {
"album" => Some(Self::ViewAlbum(data.to_string())),
"artist" => Some(Self::ViewArtist(data.to_string())),
"playlist" => Some(Self::ViewPlaylist(data.to_string())),
"user" => Some(Self::ViewUser(data.to_string())),
_ => None,
}
}

#[allow(non_snake_case)]
pub fn ViewAlbum(id: String) -> Self {
BrowserAction::NavigationPush(ScreenName::AlbumDetails(id)).into()
Expand Down Expand Up @@ -67,6 +93,7 @@ pub enum AppEvent {
}

pub struct AppState {
started: bool,
pub playback: PlaybackState,
pub browser: BrowserState,
pub selection: SelectionState,
Expand All @@ -76,6 +103,7 @@ pub struct AppState {
impl AppState {
pub fn new() -> Self {
Self {
started: false,
playback: Default::default(),
browser: BrowserState::new(),
selection: Default::default(),
Expand All @@ -85,7 +113,10 @@ impl AppState {

pub fn update_state(&mut self, message: AppAction) -> Vec<AppEvent> {
match message {
AppAction::Start => vec![AppEvent::Started],
AppAction::Start if !self.started => {
self.started = true;
vec![AppEvent::Started]
}
AppAction::ShowNotification(c) => vec![AppEvent::NotificationShown(c)],
AppAction::ViewNowPlaying => vec![AppEvent::NowPlayingShown],
AppAction::Raise => vec![AppEvent::Raised],
Expand Down Expand Up @@ -130,15 +161,10 @@ impl AppState {
}
AppAction::SaveSelection => {
let tracks = self.selection.take_selection();
let mut events: Vec<AppEvent> = self
.browser
.home_state_mut()
.into_iter()
.flat_map(move |home| {
home.update_with(BrowserAction::SaveTracks(tracks.clone()))
})
.map(AppEvent::BrowserEvent)
.collect();
let mut events: Vec<AppEvent> = forward_action(
BrowserAction::SaveTracks(tracks),
self.browser.home_state_mut().unwrap(),
);
events.push(SelectionEvent::SelectionModeChanged(false).into());
events
}
Expand All @@ -149,15 +175,10 @@ impl AppState {
.into_iter()
.map(|s| s.id)
.collect();
let mut events: Vec<AppEvent> = self
.browser
.home_state_mut()
.into_iter()
.flat_map(move |home| {
home.update_with(BrowserAction::RemoveSavedTracks(tracks.clone()))
})
.map(AppEvent::BrowserEvent)
.collect();
let mut events: Vec<AppEvent> = forward_action(
BrowserAction::RemoveSavedTracks(tracks),
self.browser.home_state_mut().unwrap(),
);
events.push(SelectionEvent::SelectionModeChanged(false).into());
events
}
Expand All @@ -175,25 +196,26 @@ impl AppState {
vec![]
}
}
AppAction::PlaybackAction(a) => self
.playback
.update_with(a)
.into_iter()
.map(AppEvent::PlaybackEvent)
.collect(),
AppAction::BrowserAction(a) => self
.browser
.update_with(a)
.into_iter()
.map(AppEvent::BrowserEvent)
.collect(),
AppAction::SelectionAction(a) => self
.selection
.update_with(a)
.into_iter()
.map(AppEvent::SelectionEvent)
.collect(),
AppAction::LoginAction(a) => self.logged_user.update_with(a).into_iter().collect(),
AppAction::PlaybackAction(a) => forward_action(a, &mut self.playback),
AppAction::BrowserAction(a) => forward_action(a, &mut self.browser),
AppAction::SelectionAction(a) => forward_action(a, &mut self.selection),
AppAction::LoginAction(a) => forward_action(a, &mut self.logged_user),
_ => vec![],
}
}
}

fn forward_action<A, E>(
action: A,
target: &mut impl UpdatableState<Action = A, Event = E>,
) -> Vec<AppEvent>
where
A: Clone,
E: Into<AppEvent>,
{
target
.update_with(Cow::Owned(action))
.into_iter()
.map(|e| e.into())
.collect()
}
Loading

0 comments on commit 917fdee

Please sign in to comment.