Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions psst-gui/src/controller/nav.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use crate::{
cmd,
data::{AppState, Nav, SpotifyUrl},
ui::{album, artist, library, lyrics, playlist, recommend, search, show},
ui::{album, artist, library, lyrics, playlist, public_user, recommend, search, show},
};
use druid::{
widget::{prelude::*, Controller},
Code,
};
use druid::widget::{prelude::*, Controller};
use druid::Code;

pub struct NavController;

Expand Down Expand Up @@ -52,6 +54,13 @@ impl NavController {
);
}
}
Nav::PublicUserDetail(public_user) => {
if !data.public_user_detail.info.contains(public_user) {
ctx.submit_command(
public_user::LOAD_DETAIL.with((public_user.to_owned(), data.to_owned())),
);
}
}
Nav::ShowDetail(link) => {
if !data.show_detail.show.contains(link) {
ctx.submit_command(show::LOAD_DETAIL.with(link.to_owned()));
Expand Down Expand Up @@ -136,7 +145,8 @@ where
env: &Env,
) {
if let LifeCycle::WidgetAdded = event {
// Loads the library's saved tracks without the user needing to click on the tab.
// Loads the library's saved tracks without the user needing to click on the
// tab.
ctx.submit_command(cmd::NAVIGATE.with(Nav::SavedTracks));
// Load the last route, or the default.
ctx.submit_command(
Expand Down
7 changes: 6 additions & 1 deletion psst-gui/src/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod nav;
mod playback;
mod playlist;
mod promise;
pub mod public_user;
mod recommend;
mod search;
mod show;
Expand Down Expand Up @@ -61,7 +62,7 @@ pub use crate::data::{
user::{PublicUser, UserProfile},
utils::{Cached, Float64, Image, Page},
};
use crate::ui::credits::TrackCredits;
use crate::{data::public_user::PublicUserDetail, ui::credits::TrackCredits};

pub const ALERT_DURATION: Duration = Duration::from_secs(5);

Expand All @@ -79,6 +80,7 @@ pub struct AppState {
pub album_detail: AlbumDetail,
pub artist_detail: ArtistDetail,
pub playlist_detail: PlaylistDetail,
pub public_user_detail: PublicUserDetail,
pub show_detail: ShowDetail,
pub library: Arc<Library>,
pub common_ctx: Arc<CommonCtx>,
Expand Down Expand Up @@ -170,6 +172,9 @@ impl AppState {
finder: Finder::new(),
lyrics: Promise::Empty,
credits: None,
public_user_detail: PublicUserDetail {
info: Promise::Empty,
},
Comment on lines +175 to +177
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there a reason to create a struct to hold only the info? Otherwise this could be just a Promise?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is in case we wanted to add the ability to view all for different elements, so for example you can have all of the user playlists or artists or friends there, and that imo would be better split into different parts?

}
}
}
Expand Down
8 changes: 6 additions & 2 deletions psst-gui/src/data/nav.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use druid::Data;
use serde::{Deserialize, Serialize};
use url::Url;

use crate::data::track::TrackId;
use crate::data::{AlbumLink, ArtistLink, PlaylistLink, ShowLink};
use crate::data::{track::TrackId, AlbumLink, ArtistLink, PlaylistLink, PublicUser, ShowLink};

use super::RecommendationsRequest;

Expand All @@ -16,6 +15,7 @@ pub enum Route {
SavedTracks,
SavedAlbums,
Shows,
PublicUser,
SearchResults,
ArtistDetail,
AlbumDetail,
Expand All @@ -36,6 +36,7 @@ pub enum Nav {
AlbumDetail(AlbumLink, Option<TrackId>),
ArtistDetail(ArtistLink),
PlaylistDetail(PlaylistLink),
PublicUserDetail(PublicUser),
ShowDetail(ShowLink),
Recommendations(Arc<RecommendationsRequest>),
}
Expand All @@ -49,6 +50,7 @@ impl Nav {
Nav::SavedAlbums => Route::SavedAlbums,
Nav::Shows => Route::Shows,
Nav::SearchResults(_) => Route::SearchResults,
Nav::PublicUserDetail(_) => Route::PublicUser,
Nav::AlbumDetail(_, _) => Route::AlbumDetail,
Nav::ArtistDetail(_) => Route::ArtistDetail,
Nav::PlaylistDetail(_) => Route::PlaylistDetail,
Expand All @@ -65,6 +67,7 @@ impl Nav {
Nav::SavedAlbums => "Saved Albums".to_string(),
Nav::Shows => "Podcasts".to_string(),
Nav::SearchResults(query) => query.to_string(),
Nav::PublicUserDetail(usr) => usr.display_name.to_string(),
Nav::AlbumDetail(link, _) => link.name.to_string(),
Nav::ArtistDetail(link) => link.name.to_string(),
Nav::PlaylistDetail(link) => link.name.to_string(),
Expand All @@ -80,6 +83,7 @@ impl Nav {
Nav::SavedTracks => "Saved Tracks".to_string(),
Nav::SavedAlbums => "Saved Albums".to_string(),
Nav::Shows => "Saved Shows".to_string(),
Nav::PublicUserDetail(usr) => format!("User \"{}\"", usr.display_name),
Nav::SearchResults(query) => format!("Search \"{query}\""),
Nav::AlbumDetail(link, _) => format!("Album \"{}\"", link.name),
Nav::ArtistDetail(link) => format!("Artist \"{}\"", link.name),
Expand Down
26 changes: 26 additions & 0 deletions psst-gui/src/data/public_user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::sync::Arc;

use druid::{im::Vector, Data, Lens};

use crate::data::{Cached, MixedView, Promise, PublicUser};

#[derive(Clone, Data, Lens)]
pub struct PublicUserDetail {
pub info: Promise<Cached<Arc<PublicUserInformation>>, PublicUser>,
}

#[derive(Clone, Data, Lens)]
pub struct PublicUserInformation {
pub uri: String,
pub name: String,
pub image_url: Option<String>,
pub followers_count: i64,
pub following_count: i64,
pub is_following: Option<bool>,
pub is_current_user: Option<bool>,
pub recently_played_artists: MixedView,
pub public_playlists: MixedView,
pub allow_follows: bool,
pub followers: Vector<PublicUser>,
pub following: Vector<PublicUser>,
}
33 changes: 30 additions & 3 deletions psst-gui/src/data/user.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,44 @@
use std::sync::Arc;

use druid::{Data, Lens};
use serde::Deserialize;
use serde::{Deserialize, Serialize};

#[derive(Clone, Data, Lens, Deserialize)]
pub struct UserProfile {
#[serde(alias = "name")]
pub display_name: Arc<str>,
pub email: Arc<str>,
pub id: Arc<str>,
}

#[derive(Clone, Data, Lens, Deserialize, Debug)]
#[derive(Clone, Data, Lens, Serialize, Deserialize, Debug, Eq, PartialEq, Default)]
pub struct PublicUser {
pub display_name: Arc<str>,
#[serde(default)]
pub id: Arc<str>,
#[serde(default, alias = "name")]
pub display_name: Arc<str>,
// Extended profile fields (optional, for detailed responses)
#[serde(default)]
pub uri: Option<Arc<str>>,
#[serde(default)]
pub image_url: Option<Arc<str>>,
#[serde(default)]
pub followers_count: Option<i64>,
#[serde(default)]
pub is_following: Option<bool>,
}

impl PublicUser {
/// Get the user ID, extracting from URI if `id` is empty.
/// URI format is "spotify:user:abc123", extracts "abc123".
pub fn get_id(&self) -> Arc<str> {
if self.id.is_empty() {
self.uri
.as_ref()
.and_then(|uri| uri.split(':').nth(2).map(Arc::from))
.unwrap_or_default()
} else {
self.id.clone()
}
}
}
30 changes: 17 additions & 13 deletions psst-gui/src/ui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,31 @@
use crate::data::config::SortCriteria;
use crate::data::Track;
use crate::error::Error;
use crate::{
cmd,
controller::{
AfterDelay, AlertCleanupController, NavController, SessionController, SortController,
},
data::{
config::SortOrder, Alert, AlertStyle, AppState, Config, Nav, Playable, Playback, Route,
ALERT_DURATION,
config::{SortCriteria, SortOrder},
Alert, AlertStyle, AppState, Config, Nav, Playable, Playback, Route, Track, ALERT_DURATION,
},
error::Error,
webapi::WebApi,
widget::{
icons, icons::SvgIcon, Border, Empty, MyWidgetExt, Overlay, RemoteImage, ThemeScope,
ViewDispatcher,
},
};
use credits::TrackCredits;
use druid::widget::Controller;
use druid::KbKey;
use druid::{
im::Vector,
widget::{
CrossAxisAlignment, Either, Flex, Label, LineBreaking, List, Scroll, Slider, Split,
ViewSwitcher,
Controller, CrossAxisAlignment, Either, Flex, Label, LineBreaking, List, Scroll, Slider,
Split, ViewSwitcher,
},
Color, Env, Insets, Key, LensExt, Menu, MenuItem, Selector, Widget, WidgetExt, WindowDesc,
Color, Env, Insets, KbKey, Key, LensExt, Menu, MenuItem, Selector, Widget, WidgetExt,
WindowDesc,
};
use druid_shell::Cursor;
use std::sync::Arc;
use std::time::Duration;
use std::{sync::Arc, time::Duration};

pub mod album;
pub mod artist;
Expand All @@ -44,6 +40,7 @@ pub mod playable;
pub mod playback;
pub mod playlist;
pub mod preferences;
pub mod public_user;
pub mod recommend;
pub mod search;
pub mod show;
Expand Down Expand Up @@ -109,7 +106,8 @@ pub fn account_setup_window() -> WindowDesc<AppState> {
pub fn artwork_window() -> WindowDesc<AppState> {
let win_size = (theme::grid(50.0), theme::grid(50.0));

// On Windows, the window size includes the titlebar, so we need to account for it
// On Windows, the window size includes the titlebar, so we need to account for
// it
let win_size = if cfg!(target_os = "windows") {
const WINDOWS_TITLEBAR_OFFSET: f64 = 24.0; // Standard Windows titlebar height
(win_size.0, win_size.1 + WINDOWS_TITLEBAR_OFFSET)
Expand Down Expand Up @@ -358,6 +356,11 @@ fn route_widget() -> impl Widget<AppState> {
.vertical()
.boxed()
}
Route::PublicUser => {
Scroll::new(public_user::detail_widget().padding(theme::grid(1.0)))
.vertical()
.boxed()
}
Route::Shows => Scroll::new(library::saved_shows_widget().padding(theme::grid(1.0)))
.vertical()
.boxed(),
Expand Down Expand Up @@ -624,6 +627,7 @@ fn route_icon_widget() -> impl Widget<Nav> {
Empty.boxed()
}
Nav::SearchResults(_) | Nav::Recommendations(_) => icon(&icons::SEARCH).boxed(),
Nav::PublicUserDetail(_) => icon(&icons::ACCOUNT).boxed(),
Nav::AlbumDetail(_, _) => icon(&icons::ALBUM).boxed(),
Nav::ArtistDetail(_) => icon(&icons::ARTIST).boxed(),
Nav::PlaylistDetail(_) => icon(&icons::PLAYLIST).boxed(),
Expand Down
5 changes: 4 additions & 1 deletion psst-gui/src/ui/playlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,10 @@ fn playlist_info_widget() -> impl Widget<WithCtx<Playlist>> {
.clip(Size::new(size, size).to_rounded_rect(4.0))
.context_menu(playlist_menu_ctx);

let owner_label = Label::dynamic(|p: &Playlist, _| p.owner.display_name.as_ref().to_string());
let owner_label = Label::dynamic(|p: &Playlist, _| p.owner.display_name.as_ref().to_string())
.on_click(|ctx, p: &mut Playlist, _env| {
ctx.submit_command(cmd::NAVIGATE.with(Nav::PublicUserDetail(p.owner.clone())));
});

let track_count_label = Label::dynamic(|p: &Playlist, _| {
let count = p.track_count.unwrap_or(0);
Expand Down
Loading