From eebd58f5a68e3416e54c42c06d91e1eb28144f02 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Sat, 11 May 2024 12:53:57 -0400 Subject: [PATCH 01/17] Add MediaType struct --- src/lib.rs | 2 ++ src/media_type.rs | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/media_type.rs diff --git a/src/lib.rs b/src/lib.rs index 89cd04e..23bc098 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,8 @@ pub mod users; pub mod search; pub mod fileserv; pub mod error_template; +pub mod media_type; + use cfg_if::cfg_if; cfg_if! { diff --git a/src/media_type.rs b/src/media_type.rs new file mode 100644 index 0000000..f97c5c2 --- /dev/null +++ b/src/media_type.rs @@ -0,0 +1,20 @@ +use serde::{Deserialize, Serialize}; + +/// Differentiates between different types of media +/// Used to display a short text near a corresponging image / title to indicate what type of media it is +#[derive(Serialize, Deserialize)] +pub enum MediaType { + Song, + Album, + Artist, +} + +impl ToString for MediaType { + fn to_string(&self) -> String { + match self { + MediaType::Song => "Song".to_string(), + MediaType::Album => "Album".to_string(), + MediaType::Artist => "Artist".to_string(), + } + } +} From ec43ab472d9896e6114ef95170471724cc32bf17 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Sat, 11 May 2024 13:08:05 -0400 Subject: [PATCH 02/17] Add DashboardTile component --- src/components.rs | 3 +- src/components/dashboard_tile.rs | 69 ++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 src/components/dashboard_tile.rs diff --git a/src/components.rs b/src/components.rs index 4d0c8a5..5f7fff7 100644 --- a/src/components.rs +++ b/src/components.rs @@ -1,4 +1,5 @@ pub mod sidebar; pub mod dashboard; pub mod search; -pub mod personal; \ No newline at end of file +pub mod personal; +pub mod dashboard_tile; diff --git a/src/components/dashboard_tile.rs b/src/components/dashboard_tile.rs new file mode 100644 index 0000000..899981f --- /dev/null +++ b/src/components/dashboard_tile.rs @@ -0,0 +1,69 @@ +use leptos::leptos_dom::*; +use leptos::*; +use serde::{Deserialize, Serialize}; +use crate::media_type::MediaType; + +/// Info representing what will be displayed in a dashboard tile +#[derive(Serialize, Deserialize)] +pub struct DashboardTile { + pub image_path: String, + pub title: String, + pub media_type: Option, + pub artist: Option, +} + +impl DashboardTile { + pub fn new(image_path: String, title: String, media_type: Option, artist: Option) -> Self { + Self { + image_path, + title, + media_type, + artist: artist.map(|artist| artist.to_string()), + } + } + + /// Get the description of the dashboard tile + /// Will display the media type, and the artist if it is available and relevant + pub fn description(&self) -> String { + match self.media_type { + Some(MediaType::Song) => { + if let Some(artist) = &self.artist { + format!("{} • {}", MediaType::Song.to_string(), artist) + } else { + MediaType::Song.to_string() + } + }, + Some(MediaType::Album) => { + if let Some(artist) = &self.artist { + format!("{} • {}", MediaType::Album.to_string(), artist) + } else { + MediaType::Album.to_string() + } + }, + Some(MediaType::Artist) => { + MediaType::Artist.to_string() + }, + None => { + if let Some(artist) = &self.artist { + artist.to_string() + } else { + "".to_string() + } + } + } + } +} + +impl IntoView for DashboardTile { + fn into_view(self) -> View { + let description = self.description(); + + view! { +
+ dashboard-tile +

{self.title}

+

{description}

+
+ }.into_view() + } +} From b1cc0f156c2bad7c2070b0da2e02011c8caeb914 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Sat, 11 May 2024 13:08:32 -0400 Subject: [PATCH 03/17] Add styling for dashboard tile --- style/dashboard_tile.scss | 23 +++++++++++++++++++++++ style/main.scss | 1 + style/theme.scss | 2 ++ 3 files changed, 26 insertions(+) create mode 100644 style/dashboard_tile.scss diff --git a/style/dashboard_tile.scss b/style/dashboard_tile.scss new file mode 100644 index 0000000..6a25a21 --- /dev/null +++ b/style/dashboard_tile.scss @@ -0,0 +1,23 @@ +.dashboard-tile { + img { + width: $dashboard-tile-size; + height: $dashboard-tile-size; + border-radius: 7px; + margin-right: 20px; + } + + p.dashboard-tile-title { + font-size: 16px; + font-weight: bold; + margin: 0; + padding: 0; + } + + p.dashboard-tile-description { + font-size: 12px; + margin: 0; + padding: 0; + } + + margin-right: 10px; +} diff --git a/style/main.scss b/style/main.scss index 7793e25..ea1d847 100644 --- a/style/main.scss +++ b/style/main.scss @@ -8,6 +8,7 @@ @import 'home.scss'; @import 'search.scss'; @import 'personal.scss'; +@import 'dashboard_tile.scss'; body { font-family: sans-serif; diff --git a/style/theme.scss b/style/theme.scss index a7ebc14..fe96046 100644 --- a/style/theme.scss +++ b/style/theme.scss @@ -14,3 +14,5 @@ $queue-background-color: $play-bar-background-color; $auth-inputs: #796dd4; $auth-containers: white; + +$dashboard-tile-size: 200px; From f4908ad3b301658b8e1df8bde71bb3f08c8c19fa Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Sat, 11 May 2024 13:16:02 -0400 Subject: [PATCH 04/17] Add DashboardRow component --- src/components.rs | 1 + src/components/dashboard_row.rs | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/components/dashboard_row.rs diff --git a/src/components.rs b/src/components.rs index 5f7fff7..6f919fa 100644 --- a/src/components.rs +++ b/src/components.rs @@ -3,3 +3,4 @@ pub mod dashboard; pub mod search; pub mod personal; pub mod dashboard_tile; +pub mod dashboard_row; diff --git a/src/components/dashboard_row.rs b/src/components/dashboard_row.rs new file mode 100644 index 0000000..2917b86 --- /dev/null +++ b/src/components/dashboard_row.rs @@ -0,0 +1,39 @@ +use leptos::leptos_dom::*; +use leptos::*; +use serde::{Deserialize, Serialize}; +use crate::components::dashboard_tile::DashboardTile; + +/// A row of dashboard tiles, with a title +#[derive(Serialize, Deserialize)] +pub struct DashboardRow { + pub title: String, + pub tiles: Vec, +} + +impl DashboardRow { + pub fn new(title: String, tiles: Vec) -> Self { + Self { + title, + tiles, + } + } +} + +impl IntoView for DashboardRow { + fn into_view(self) -> View { + view! { +
+

{self.title}

+
    + {self.tiles.into_iter().map(|tile_info| { + view! { +
  • + { tile_info } +
  • + } + }).collect::>()} +
+
+ }.into_view() + } +} From 683f979bc7ce26b0644be59d226e655c10468af2 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Sat, 11 May 2024 13:16:31 -0400 Subject: [PATCH 05/17] Add styling for dashboard row --- style/dashboard_row.scss | 12 ++++++++++++ style/main.scss | 1 + 2 files changed, 13 insertions(+) create mode 100644 style/dashboard_row.scss diff --git a/style/dashboard_row.scss b/style/dashboard_row.scss new file mode 100644 index 0000000..2eb1041 --- /dev/null +++ b/style/dashboard_row.scss @@ -0,0 +1,12 @@ +.dashboard-tile-row { + ul { + display: flex; + + li { + list-style-type: none; + } + + -webkit-mask-image: linear-gradient(90deg, #000000 95%, transparent); + mask-image: linear-gradient(90deg, #000000 95%, transparent); + } +} diff --git a/style/main.scss b/style/main.scss index ea1d847..841e88a 100644 --- a/style/main.scss +++ b/style/main.scss @@ -9,6 +9,7 @@ @import 'search.scss'; @import 'personal.scss'; @import 'dashboard_tile.scss'; +@import 'dashboard_row.scss'; body { font-family: sans-serif; From af66381f5f354afd6a9d9b44196292af2755be28 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Sat, 11 May 2024 15:23:49 -0400 Subject: [PATCH 06/17] Implement dashboard row sideways scrolling --- src/components/dashboard_row.rs | 61 +++++++++++++++++++++++++++++++-- style/dashboard_row.scss | 31 +++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/components/dashboard_row.rs b/src/components/dashboard_row.rs index 2917b86..4134161 100644 --- a/src/components/dashboard_row.rs +++ b/src/components/dashboard_row.rs @@ -1,7 +1,9 @@ +use leptos::html::Ul; use leptos::leptos_dom::*; use leptos::*; use serde::{Deserialize, Serialize}; use crate::components::dashboard_tile::DashboardTile; +use leptos_icons::*; /// A row of dashboard tiles, with a title #[derive(Serialize, Deserialize)] @@ -21,10 +23,65 @@ impl DashboardRow { impl IntoView for DashboardRow { fn into_view(self) -> View { + let list_ref = create_node_ref::
    (); + + // Scroll functions attempt to align the left edge of the scroll area with the left edge of a tile + // This is done by scrolling to the nearest multiple of the tile width, plus some for padding + + let scroll_left = move |_| { + if let Some(scroll_element) = list_ref.get() { + let client_width = scroll_element.client_width() as f64; + let current_pos = scroll_element.scroll_left() as f64; + let desired_pos = current_pos - client_width; + + if let Some(first_tile) = scroll_element.first_element_child() { + let tile_width = first_tile.client_width() as f64; + let scroll_pos = desired_pos + (tile_width - (desired_pos % tile_width)) + 15.0; + scroll_element.scroll_to_with_x_and_y(scroll_pos, 0.0); + } else { + warn!("Could not get first tile to scroll left"); + // Fall back to scrolling by the client width if we can't get the tile width + scroll_element.scroll_to_with_x_and_y(desired_pos, 0.0); + } + } else { + warn!("Could not get scroll element to scroll left"); + } + }; + + let scroll_right = move |_| { + if let Some(scroll_element) = list_ref.get() { + let client_width = scroll_element.client_width() as f64; + let current_pos = scroll_element.scroll_left() as f64; + let desired_pos = current_pos + client_width; + + if let Some(first_tile) = scroll_element.first_element_child() { + let tile_width = first_tile.client_width() as f64; + let scroll_pos = desired_pos - (desired_pos % tile_width) + 15.0; + scroll_element.scroll_to_with_x_and_y(scroll_pos, 0.0); + } else { + warn!("Could not get first tile to scroll right"); + // Fall back to scrolling by the client width if we can't get the tile width + scroll_element.scroll_to_with_x_and_y(desired_pos, 0.0); + } + } else { + warn!("Could not get scroll element to scroll right"); + } + }; + view! {
    -

    {self.title}

    -
      +
      +

      {self.title}

      +
      + + +
      +
      +
        {self.tiles.into_iter().map(|tile_info| { view! {
      • diff --git a/style/dashboard_row.scss b/style/dashboard_row.scss index 2eb1041..1dea8d9 100644 --- a/style/dashboard_row.scss +++ b/style/dashboard_row.scss @@ -1,6 +1,37 @@ .dashboard-tile-row { + .dashboard-tile-row-title-row { + display: flex; + + .dashboard-tile-row-scroll-btn { + margin-left: auto; + margin-top: auto; + margin-bottom: auto; + + button { + background-color: transparent; + border: none; + + .dashboard-tile-row-scroll { + color: $text-controls-color; + width: 2.5rem; + height: 2.5rem; + } + + .dashboard-tile-row-scroll:hover { + color: $controls-hover-color; + } + + .dashboard-tile-row-scroll:active { + color: $controls-click-color; + } + } + } + } + ul { display: flex; + overflow-x: hidden; + scroll-behavior: smooth; li { list-style-type: none; From 7f4108e1c5c539bfd85efd3784d20f5beb8d02f0 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Tue, 30 Jul 2024 21:02:48 -0400 Subject: [PATCH 07/17] Add conversion from SongData to DashboardTile --- src/songdata.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/songdata.rs b/src/songdata.rs index 23ce415..9e08942 100644 --- a/src/songdata.rs +++ b/src/songdata.rs @@ -1,4 +1,6 @@ use crate::models::{Album, Artist, Song}; +use crate::components::dashboard_tile::DashboardTile; +use crate::media_type::MediaType; use time::Date; @@ -95,3 +97,14 @@ impl TryInto for SongData { }) } } + +impl Into for SongData { + fn into(self) -> DashboardTile { + DashboardTile { + image_path: self.image_path, + title: self.title, + media_type: Some(MediaType::Song), + artist: Some(Artist::display_list(&self.artists)), + } + } +} From fa811350ae3b2f72faa8dd39b5759bdef59d23f8 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Tue, 30 Jul 2024 21:06:23 -0400 Subject: [PATCH 08/17] Add leptos_use crate --- Cargo.lock | 132 +++++++++++++++++++++++++++++++++++++++++++++++++---- Cargo.toml | 2 + 2 files changed, 124 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1bc5c0a..0a6bd5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -308,7 +308,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f" dependencies = [ - "darling", + "darling 0.14.4", "proc-macro2", "quote", "syn 1.0.109", @@ -377,6 +377,15 @@ dependencies = [ "half", ] +[[package]] +name = "codee" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af40247be877a1e3353fb406aa27ab3ef4bd3ff18cef91e75e667bfa3fde701d" +dependencies = [ + "thiserror", +] + [[package]] name = "collection_literals" version = "1.0.1" @@ -499,8 +508,18 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core 0.20.10", + "darling_macro 0.20.10", ] [[package]] @@ -513,21 +532,46 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.58", +] + [[package]] name = "darling_macro" version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ - "darling_core", + "darling_core 0.14.4", "quote", "syn 1.0.109", ] +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core 0.20.10", + "quote", + "syn 2.0.58", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -541,6 +585,18 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "default-struct-builder" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fa90da96b8fd491f5754d1f7a731f73921e3b7aa0ce333c821a0e43666ac14" +dependencies = [ + "darling 0.20.10", + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "deranged" version = "0.3.11" @@ -862,7 +918,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-sink", - "gloo-utils", + "gloo-utils 0.2.0 (git+https://github.com/rustwasm/gloo.git?rev=a823fab7ecc4068e9a28bd669da5eaf3f0a56380)", "http", "js-sys", "pin-project", @@ -874,6 +930,31 @@ dependencies = [ "web-sys", ] +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gloo-utils" version = "0.2.0" @@ -1338,6 +1419,30 @@ dependencies = [ "web-sys", ] +[[package]] +name = "leptos-use" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac79c02d0e2998569116aa36d26fd00bfa8cadbe8cb630eb771b4d1676412a16" +dependencies = [ + "async-trait", + "cfg-if", + "codee", + "cookie", + "default-struct-builder", + "futures-util", + "gloo-timers", + "gloo-utils 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys", + "lazy_static", + "leptos", + "paste", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "leptos_axum" version = "0.6.10" @@ -1587,6 +1692,7 @@ dependencies = [ "icondata", "lazy_static", "leptos", + "leptos-use", "leptos_axum", "leptos_icons", "leptos_meta", @@ -2477,6 +2583,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.5.0" @@ -2580,18 +2692,18 @@ checksum = "384595c11a4e2969895cad5a8c4029115f5ab956a9e5ef4de79d11a426e5f20c" [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 953da57..b162619 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ multer = { version = "3.0.0", optional = true } log = { version = "0.4.21", optional = true } flexi_logger = { version = "0.28.0", optional = true, default-features = false } web-sys = "0.3.69" +leptos-use = "0.11.3" [patch.crates-io] gloo-net = { git = "https://github.com/rustwasm/gloo.git", rev = "a823fab7ecc4068e9a28bd669da5eaf3f0a56380" } @@ -74,6 +75,7 @@ ssr = [ "multer", "log", "flexi_logger", + "leptos-use/ssr", ] # Defines a size-optimized profile for the WASM bundle in release mode From 9d6013b8a4d8a86d02782d49108fa6885afd0446 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Tue, 30 Jul 2024 21:35:44 -0400 Subject: [PATCH 09/17] Move dashboard tile row left spacing to margin-left --- src/components/dashboard_row.rs | 4 ++-- style/dashboard_row.scss | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/dashboard_row.rs b/src/components/dashboard_row.rs index 4134161..10f1ee9 100644 --- a/src/components/dashboard_row.rs +++ b/src/components/dashboard_row.rs @@ -36,7 +36,7 @@ impl IntoView for DashboardRow { if let Some(first_tile) = scroll_element.first_element_child() { let tile_width = first_tile.client_width() as f64; - let scroll_pos = desired_pos + (tile_width - (desired_pos % tile_width)) + 15.0; + let scroll_pos = desired_pos + (tile_width - (desired_pos % tile_width)); scroll_element.scroll_to_with_x_and_y(scroll_pos, 0.0); } else { warn!("Could not get first tile to scroll left"); @@ -56,7 +56,7 @@ impl IntoView for DashboardRow { if let Some(first_tile) = scroll_element.first_element_child() { let tile_width = first_tile.client_width() as f64; - let scroll_pos = desired_pos - (desired_pos % tile_width) + 15.0; + let scroll_pos = desired_pos - (desired_pos % tile_width); scroll_element.scroll_to_with_x_and_y(scroll_pos, 0.0); } else { warn!("Could not get first tile to scroll right"); diff --git a/style/dashboard_row.scss b/style/dashboard_row.scss index 1dea8d9..8bbb2c9 100644 --- a/style/dashboard_row.scss +++ b/style/dashboard_row.scss @@ -32,6 +32,8 @@ display: flex; overflow-x: hidden; scroll-behavior: smooth; + margin-left: 40px; + padding-inline-start: 0; li { list-style-type: none; From eab011070d7975b228347ef9cc91533293ce480a Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Tue, 30 Jul 2024 21:46:59 -0400 Subject: [PATCH 10/17] Don't track scroll_element in scroll button handlers --- src/components/dashboard_row.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/dashboard_row.rs b/src/components/dashboard_row.rs index 10f1ee9..a85159c 100644 --- a/src/components/dashboard_row.rs +++ b/src/components/dashboard_row.rs @@ -29,7 +29,7 @@ impl IntoView for DashboardRow { // This is done by scrolling to the nearest multiple of the tile width, plus some for padding let scroll_left = move |_| { - if let Some(scroll_element) = list_ref.get() { + if let Some(scroll_element) = list_ref.get_untracked() { let client_width = scroll_element.client_width() as f64; let current_pos = scroll_element.scroll_left() as f64; let desired_pos = current_pos - client_width; @@ -49,7 +49,7 @@ impl IntoView for DashboardRow { }; let scroll_right = move |_| { - if let Some(scroll_element) = list_ref.get() { + if let Some(scroll_element) = list_ref.get_untracked() { let client_width = scroll_element.client_width() as f64; let current_pos = scroll_element.scroll_left() as f64; let desired_pos = current_pos + client_width; From 53cde3bed6c4051f137574628e12aa820bd46eae Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Tue, 30 Jul 2024 21:47:55 -0400 Subject: [PATCH 11/17] Hide scroll buttons when at end of scroll bar --- src/components/dashboard_row.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/components/dashboard_row.rs b/src/components/dashboard_row.rs index a85159c..1690d6d 100644 --- a/src/components/dashboard_row.rs +++ b/src/components/dashboard_row.rs @@ -1,6 +1,7 @@ use leptos::html::Ul; use leptos::leptos_dom::*; use leptos::*; +use leptos_use::{use_element_size, UseElementSizeReturn, use_scroll, UseScrollReturn}; use serde::{Deserialize, Serialize}; use crate::components::dashboard_tile::DashboardTile; use leptos_icons::*; @@ -68,15 +69,38 @@ impl IntoView for DashboardRow { } }; + let UseElementSizeReturn { width: scroll_element_width, .. } = use_element_size(list_ref); + let UseScrollReturn { x: scroll_x, .. } = use_scroll(list_ref); + + let scroll_right_hidden = Signal::derive(move || { + if let Some(scroll_element) = list_ref.get() { + if scroll_element.scroll_width() as f64 - scroll_element_width.get() <= scroll_x.get() { + "visibility: hidden" + } else { + "" + } + } else { + "" + } + }); + + let scroll_left_hidden = Signal::derive(move || { + if scroll_x.get() <= 0.0 { + "visibility: hidden" + } else { + "" + } + }); + view! {

        {self.title}

        - -
        From 553e24800b84b56d5acf6709e10e397b95225017 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Sun, 6 Oct 2024 15:35:18 -0400 Subject: [PATCH 12/17] Refactor DashboardTile into trait --- src/components/dashboard_row.rs | 8 ++-- src/components/dashboard_tile.rs | 70 +++++++------------------------- 2 files changed, 17 insertions(+), 61 deletions(-) diff --git a/src/components/dashboard_row.rs b/src/components/dashboard_row.rs index 1690d6d..7abd258 100644 --- a/src/components/dashboard_row.rs +++ b/src/components/dashboard_row.rs @@ -2,19 +2,17 @@ use leptos::html::Ul; use leptos::leptos_dom::*; use leptos::*; use leptos_use::{use_element_size, UseElementSizeReturn, use_scroll, UseScrollReturn}; -use serde::{Deserialize, Serialize}; use crate::components::dashboard_tile::DashboardTile; use leptos_icons::*; /// A row of dashboard tiles, with a title -#[derive(Serialize, Deserialize)] pub struct DashboardRow { pub title: String, - pub tiles: Vec, + pub tiles: Vec>, } impl DashboardRow { - pub fn new(title: String, tiles: Vec) -> Self { + pub fn new(title: String, tiles: Vec>) -> Self { Self { title, tiles, @@ -109,7 +107,7 @@ impl IntoView for DashboardRow { {self.tiles.into_iter().map(|tile_info| { view! {
      • - { tile_info } + { tile_info.into_view() }
      • } }).collect::>()} diff --git a/src/components/dashboard_tile.rs b/src/components/dashboard_tile.rs index 899981f..354aad5 100644 --- a/src/components/dashboard_tile.rs +++ b/src/components/dashboard_tile.rs @@ -1,68 +1,26 @@ use leptos::leptos_dom::*; use leptos::*; -use serde::{Deserialize, Serialize}; -use crate::media_type::MediaType; -/// Info representing what will be displayed in a dashboard tile -#[derive(Serialize, Deserialize)] -pub struct DashboardTile { - pub image_path: String, - pub title: String, - pub media_type: Option, - pub artist: Option, +pub trait DashboardTile { + fn image_path(&self) -> String; + fn title(&self) -> String; + fn link(&self) -> String; + fn description(&self) -> Option { None } } -impl DashboardTile { - pub fn new(image_path: String, title: String, media_type: Option, artist: Option) -> Self { - Self { - image_path, - title, - media_type, - artist: artist.map(|artist| artist.to_string()), - } - } - - /// Get the description of the dashboard tile - /// Will display the media type, and the artist if it is available and relevant - pub fn description(&self) -> String { - match self.media_type { - Some(MediaType::Song) => { - if let Some(artist) = &self.artist { - format!("{} • {}", MediaType::Song.to_string(), artist) - } else { - MediaType::Song.to_string() - } - }, - Some(MediaType::Album) => { - if let Some(artist) = &self.artist { - format!("{} • {}", MediaType::Album.to_string(), artist) - } else { - MediaType::Album.to_string() - } - }, - Some(MediaType::Artist) => { - MediaType::Artist.to_string() - }, - None => { - if let Some(artist) = &self.artist { - artist.to_string() - } else { - "".to_string() - } - } - } - } -} - -impl IntoView for DashboardTile { +impl IntoView for &dyn DashboardTile { fn into_view(self) -> View { - let description = self.description(); + let link = self.link(); view! { }.into_view() } From f02a22d80597f918ec2a6cf60523fe45f1321c65 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Sun, 6 Oct 2024 15:35:52 -0400 Subject: [PATCH 13/17] Format text inside dashboard tiles normally --- style/dashboard_tile.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/style/dashboard_tile.scss b/style/dashboard_tile.scss index 6a25a21..925a985 100644 --- a/style/dashboard_tile.scss +++ b/style/dashboard_tile.scss @@ -6,6 +6,11 @@ margin-right: 20px; } + a { + text-decoration: none; + color: $text-controls-color; + } + p.dashboard-tile-title { font-size: 16px; font-weight: bold; From 097b1fc491f35d4fa8198cdd16e414ee1eacdb1d Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Sun, 6 Oct 2024 15:37:26 -0400 Subject: [PATCH 14/17] impl DashboardTile for Songdata --- src/songdata.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/songdata.rs b/src/songdata.rs index 99464c8..6851a85 100644 --- a/src/songdata.rs +++ b/src/songdata.rs @@ -1,6 +1,5 @@ use crate::models::{Album, Artist, Song}; use crate::components::dashboard_tile::DashboardTile; -use crate::media_type::MediaType; use time::Date; @@ -63,13 +62,20 @@ impl TryInto for SongData { } } -impl Into for SongData { - fn into(self) -> DashboardTile { - DashboardTile { - image_path: self.image_path, - title: self.title, - media_type: Some(MediaType::Song), - artist: Some(Artist::display_list(&self.artists)), - } +impl DashboardTile for SongData { + fn image_path(&self) -> String { + self.image_path.clone() + } + + fn title(&self) -> String { + self.title.clone() + } + + fn link(&self) -> String { + format!("/song/{}", self.id) + } + + fn description(&self) -> Option { + Some(format!("Song • {}", Artist::display_list(&self.artists))) } } From c18bf277b94995dab53c1fa08a93b59e6cb92d29 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Sun, 6 Oct 2024 15:49:08 -0400 Subject: [PATCH 15/17] Create AlbumData, impl DashboardTile --- src/albumdata.rs | 39 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 40 insertions(+) create mode 100644 src/albumdata.rs diff --git a/src/albumdata.rs b/src/albumdata.rs new file mode 100644 index 0000000..e42baea --- /dev/null +++ b/src/albumdata.rs @@ -0,0 +1,39 @@ +use crate::models::Artist; +use crate::components::dashboard_tile::DashboardTile; + +use time::Date; + +/// Holds information about an album +/// +/// Intended to be used in the front-end +pub struct AlbumData { + /// Album id + pub id: i32, + /// Album title + pub title: String, + /// Album artists + pub artists: Vec, + /// Album release date + pub release_date: Option, + /// Path to album image, relative to the root of the web server. + /// For example, `"/assets/images/Album.jpg"` + pub image_path: String, +} + +impl DashboardTile for AlbumData { + fn image_path(&self) -> String { + self.image_path.clone() + } + + fn title(&self) -> String { + self.title.clone() + } + + fn link(&self) -> String { + format!("/album/{}", self.id) + } + + fn description(&self) -> Option { + Some(format!("Album • {}", Artist::display_list(&self.artists))) + } +} diff --git a/src/lib.rs b/src/lib.rs index 19d61b2..a347039 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ pub mod app; pub mod auth; pub mod songdata; +pub mod albumdata; pub mod playstatus; pub mod playbar; pub mod database; From c678d93661d4852cbcdee06bdd75eee24fc9ec49 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Sun, 6 Oct 2024 15:49:19 -0400 Subject: [PATCH 16/17] Create ArtistData, impl DashboardTile --- src/artistdata.rs | 32 ++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 33 insertions(+) create mode 100644 src/artistdata.rs diff --git a/src/artistdata.rs b/src/artistdata.rs new file mode 100644 index 0000000..e799679 --- /dev/null +++ b/src/artistdata.rs @@ -0,0 +1,32 @@ +use crate::components::dashboard_tile::DashboardTile; + +/// Holds information about an artist +/// +/// Intended to be used in the front-end +pub struct ArtistData { + /// Artist id + pub id: i32, + /// Artist name + pub name: String, + /// Path to artist image, relative to the root of the web server. + /// For example, `"/assets/images/Artist.jpg"` + pub image_path: String, +} + +impl DashboardTile for ArtistData { + fn image_path(&self) -> String { + self.image_path.clone() + } + + fn title(&self) -> String { + self.name.clone() + } + + fn link(&self) -> String { + format!("/artist/{}", self.id) + } + + fn description(&self) -> Option { + Some("Artist".to_string()) + } +} diff --git a/src/lib.rs b/src/lib.rs index a347039..474fb99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ pub mod app; pub mod auth; pub mod songdata; pub mod albumdata; +pub mod artistdata; pub mod playstatus; pub mod playbar; pub mod database; From 134b425ce603e02930e4077499ac29f346f37032 Mon Sep 17 00:00:00 2001 From: Ethan Girouard Date: Sun, 6 Oct 2024 15:50:21 -0400 Subject: [PATCH 17/17] Remove unused MediaType --- src/lib.rs | 1 - src/media_type.rs | 20 -------------------- 2 files changed, 21 deletions(-) delete mode 100644 src/media_type.rs diff --git a/src/lib.rs b/src/lib.rs index 474fb99..95ac8ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,7 +15,6 @@ pub mod users; pub mod search; pub mod fileserv; pub mod error_template; -pub mod media_type; pub mod api; pub mod upload; pub mod util; diff --git a/src/media_type.rs b/src/media_type.rs deleted file mode 100644 index f97c5c2..0000000 --- a/src/media_type.rs +++ /dev/null @@ -1,20 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// Differentiates between different types of media -/// Used to display a short text near a corresponging image / title to indicate what type of media it is -#[derive(Serialize, Deserialize)] -pub enum MediaType { - Song, - Album, - Artist, -} - -impl ToString for MediaType { - fn to_string(&self) -> String { - match self { - MediaType::Song => "Song".to_string(), - MediaType::Album => "Album".to_string(), - MediaType::Artist => "Artist".to_string(), - } - } -}