A tool to sync music with your favorite devices
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

*: commit gui thing, shelve it for now

Gee Sawra 1ea0f3f8 d5d10603

+358 -36
+2
.cargo/config.toml
··· 1 + [target.'cfg(target_os = "linux")'] 2 + rustflags = ["-C", "link-arg=-fuse-ld=mold"]
+36 -15
Cargo.lock
··· 1175 1175 [[package]] 1176 1176 name = "collections" 1177 1177 version = "0.1.0" 1178 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 1178 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 1179 1179 dependencies = [ 1180 1180 "indexmap", 1181 1181 "rustc-hash 2.1.1", ··· 1587 1587 [[package]] 1588 1588 name = "derive_refineable" 1589 1589 version = "0.1.0" 1590 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 1590 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 1591 1591 dependencies = [ 1592 1592 "proc-macro2", 1593 1593 "quote", ··· 2582 2582 [[package]] 2583 2583 name = "gpui" 2584 2584 version = "0.2.2" 2585 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 2585 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 2586 2586 dependencies = [ 2587 2587 "anyhow", 2588 2588 "as-raw-xcb-connection", ··· 2720 2720 ] 2721 2721 2722 2722 [[package]] 2723 + name = "gpui-component-assets" 2724 + version = "0.5.0" 2725 + source = "git+https://github.com/longbridge/gpui-component?rev=1a19df56caeb5db8520d478727a19c33264370f0#1a19df56caeb5db8520d478727a19c33264370f0" 2726 + dependencies = [ 2727 + "anyhow", 2728 + "gpui", 2729 + "rust-embed", 2730 + ] 2731 + 2732 + [[package]] 2723 2733 name = "gpui-component-macros" 2724 2734 version = "0.5.0" 2725 2735 source = "git+https://github.com/longbridge/gpui-component?rev=1a19df56caeb5db8520d478727a19c33264370f0#1a19df56caeb5db8520d478727a19c33264370f0" ··· 2744 2754 [[package]] 2745 2755 name = "gpui_macros" 2746 2756 version = "0.1.0" 2747 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 2757 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 2748 2758 dependencies = [ 2749 2759 "heck 0.5.0", 2750 2760 "proc-macro2", ··· 2918 2928 [[package]] 2919 2929 name = "http_client" 2920 2930 version = "0.1.0" 2921 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 2931 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 2922 2932 dependencies = [ 2923 2933 "anyhow", 2924 2934 "async-compression", ··· 3729 3739 [[package]] 3730 3740 name = "media" 3731 3741 version = "0.1.0" 3732 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 3742 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 3733 3743 dependencies = [ 3734 3744 "anyhow", 3735 3745 "bindgen 0.71.1", ··· 4447 4457 [[package]] 4448 4458 name = "perf" 4449 4459 version = "0.1.0" 4450 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 4460 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 4451 4461 dependencies = [ 4452 4462 "collections", 4453 4463 "serde", ··· 5089 5099 [[package]] 5090 5100 name = "refineable" 5091 5101 version = "0.1.0" 5092 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 5102 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 5093 5103 dependencies = [ 5094 5104 "derive_refineable", 5095 5105 ] ··· 5248 5258 "proc-macro2", 5249 5259 "quote", 5250 5260 "rust-embed-utils", 5261 + "shellexpand", 5251 5262 "syn 2.0.114", 5252 5263 "walkdir", 5253 5264 ] ··· 5443 5454 [[package]] 5444 5455 name = "scheduler" 5445 5456 version = "0.1.0" 5446 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 5457 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 5447 5458 dependencies = [ 5448 5459 "async-task", 5449 5460 "backtrace", ··· 5716 5727 checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 5717 5728 dependencies = [ 5718 5729 "lazy_static", 5730 + ] 5731 + 5732 + [[package]] 5733 + name = "shellexpand" 5734 + version = "3.1.1" 5735 + source = "registry+https://github.com/rust-lang/crates.io-index" 5736 + checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" 5737 + dependencies = [ 5738 + "dirs 5.0.1", 5719 5739 ] 5720 5740 5721 5741 [[package]] ··· 6262 6282 [[package]] 6263 6283 name = "sum_tree" 6264 6284 version = "0.1.0" 6265 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 6285 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 6266 6286 dependencies = [ 6267 6287 "arrayvec", 6268 6288 "log", ··· 6906 6926 "glib", 6907 6927 "gpui", 6908 6928 "gpui-component", 6929 + "gpui-component-assets", 6909 6930 "indicatif", 6910 6931 "infer", 6911 6932 "libc", ··· 7121 7142 [[package]] 7122 7143 name = "util" 7123 7144 version = "0.1.0" 7124 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 7145 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 7125 7146 dependencies = [ 7126 7147 "anyhow", 7127 7148 "async-fs", ··· 7157 7178 [[package]] 7158 7179 name = "util_macros" 7159 7180 version = "0.1.0" 7160 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 7181 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 7161 7182 dependencies = [ 7162 7183 "perf", 7163 7184 "quote", ··· 8459 8480 [[package]] 8460 8481 name = "zlog" 8461 8482 version = "0.1.0" 8462 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 8483 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 8463 8484 dependencies = [ 8464 8485 "anyhow", 8465 8486 "chrono", ··· 8476 8497 [[package]] 8477 8498 name = "ztracing" 8478 8499 version = "0.1.0" 8479 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 8500 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 8480 8501 dependencies = [ 8481 8502 "tracing", 8482 8503 "tracing-subscriber", ··· 8487 8508 [[package]] 8488 8509 name = "ztracing_macro" 8489 8510 version = "0.1.0" 8490 - source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#1321aa932ca5975e968023f698b8b610711d48be" 8511 + source = "git+https://github.com/zed-industries/zed?rev=b4d1ba7a#189c9f41246708baa950b41cc9f90b9d6f4d3aaa" 8491 8512 8492 8513 [[package]] 8493 8514 name = "zune-core"
+2
Cargo.toml
··· 51 51 sysinfo = "0.37.2" 52 52 lofty = "0.22.4" 53 53 gpui-component = { git = "https://github.com/longbridge/gpui-component", rev = "1a19df56caeb5db8520d478727a19c33264370f0" } 54 + gpui-component-assets = { git = "https://github.com/longbridge/gpui-component", rev = "1a19df56caeb5db8520d478727a19c33264370f0" } 54 55 gpui = { git = "https://github.com/zed-industries/zed", rev = "b4d1ba7a" } 56 + 55 57 56 58 [patch."git+https://github.com/zed-industries/zed"] 57 59 gpui = { git = "https://github.com/zed-industries/zed?rev=b4d1ba7a" }
+136 -18
src/gui/gui.rs
··· 1 - use anyhow::Result; 2 - use gpui::*; 3 - use gpui_component::{button::*, *}; 1 + use crate::{ 2 + db, 3 + gui::{sync::SyncView, track_table::TrackTableDelegate}, 4 + model, 5 + }; 6 + use anyhow::{Context as AnyhowContext, Result}; 7 + use gpui::{prelude::FluentBuilder, *}; 8 + use gpui_component::{ 9 + dialog::Dialog, 10 + menu::AppMenuBar, 11 + popover::Popover, 12 + table::{Table, TableState}, 13 + *, 14 + }; 15 + 16 + actions!([Sync, Quit]); 4 17 5 18 pub async fn run() -> Result<()> { 6 - let app = Application::new(); 19 + let db = db::Instance::new(db::default_database_dir().to_str().unwrap(), false) 20 + .await 21 + .with_context(|| "Cannot open local database instance")?; 22 + 23 + let tracks = db.tracks_by_state(model::FileState::Copied).await?; 24 + 25 + let app = Application::new().with_assets(gpui_component_assets::Assets); 7 26 8 27 app.run(move |cx| { 9 - // This must be called before using any GPUI Component features. 10 28 gpui_component::init(cx); 11 29 30 + cx.on_action(|_: &Quit, cx: &mut App| { 31 + cx.quit(); 32 + }); 33 + 34 + cx.set_menus(vec![Menu { 35 + name: "File".into(), 36 + items: vec![ 37 + MenuItem::action("Sync", Sync), 38 + MenuItem::Separator, 39 + MenuItem::action("Quit", Quit), 40 + ], 41 + }]); 42 + 43 + let mb = AppMenuBar::new(cx); 44 + 45 + let window_options = WindowOptions { 46 + titlebar: Some(TitleBar::title_bar_options()), 47 + window_bounds: Some(WindowBounds::centered(size(px(680.), px(600.)), cx)), 48 + ..Default::default() 49 + }; 50 + 12 51 cx.spawn(async move |cx| { 13 - cx.open_window(WindowOptions::default(), |window, cx| { 14 - let view = cx.new(|_| HelloWorld); 52 + cx.open_window(window_options, |window, cx| { 53 + window.activate_window(); 54 + window.set_window_title("tunz"); 55 + 56 + Theme::change(ThemeMode::Dark, Some(window), cx); 57 + 58 + let view = cx.new(|cx| Tunz::new(window, cx, tracks, mb)); 59 + 15 60 // This first level on the window, should be a Root. 16 61 cx.new(|cx| Root::new(view, window, cx)) 17 62 })?; ··· 23 68 Ok(()) 24 69 } 25 70 26 - pub struct HelloWorld; 27 - impl Render for HelloWorld { 28 - fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement { 71 + pub struct Tunz { 72 + tracks_table: Entity<TableState<TrackTableDelegate>>, 73 + menu_bar: Entity<AppMenuBar>, 74 + show_sync: bool, 75 + } 76 + 77 + impl Tunz { 78 + fn new( 79 + window: &mut Window, 80 + cx: &mut Context<Self>, 81 + tracks: Vec<model::Track>, 82 + mb: Entity<AppMenuBar>, 83 + ) -> Self { 84 + let td = TrackTableDelegate::new(tracks); 85 + let td = cx.new(|cx| { 86 + TableState::new(td, window, cx) 87 + .col_selectable(false) 88 + .col_movable(false) 89 + }); 90 + 91 + Self { 92 + tracks_table: td, 93 + menu_bar: mb, 94 + show_sync: false, 95 + } 96 + } 97 + } 98 + 99 + impl Render for Tunz { 100 + fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { 29 101 div() 30 - .v_flex() 31 - .gap_2() 102 + .size_full() 103 + .on_action(cx.listener(|this, _: &Sync, window, cx| { 104 + this.show_sync = true; 105 + window.open_dialog(cx, |d, _, _| d.title("test")); 106 + log::info!("sync pressed!"); 107 + })) 32 108 .size_full() 109 + .child( 110 + TitleBar::new().child( 111 + div() 112 + .flex() 113 + .w_full() 114 + .items_center() 115 + .justify_between() 116 + .child(self.menu_bar.clone()) 117 + // Spacer for window controls 118 + .child(div().w_6()), 119 + ), 120 + ) 121 + .bg(cx.theme().background) 122 + // .child(self.render_menu(cx)) 123 + .child(self.render_track_list(cx)) 124 + .child(self.render_status_bar(cx)) 125 + } 126 + } 127 + 128 + impl Tunz { 129 + fn render_track_list(&mut self, cx: &mut Context<Self>) -> impl IntoElement { 130 + v_flex().size_full().child(Table::new(&self.tracks_table)) 131 + } 132 + 133 + fn render_status_bar(&self, cx: &Context<Self>) -> impl IntoElement { 134 + let tracks = self.tracks_table.read(cx); 135 + 136 + h_flex() 137 + .px_3() 138 + .gap_4() 139 + .h_7() 140 + .text_sm() 33 141 .items_center() 34 - .justify_center() 35 - .child("Hello, World!") 142 + .justify_between() 143 + .border_t_1() 144 + .border_color(cx.theme().border) 145 + .bg(cx.theme().tab_bar) 146 + .text_color(cx.theme().muted_foreground) 36 147 .child( 37 - Button::new("ok") 38 - .primary() 39 - .label("Let's Go!") 40 - .on_click(|_, _, _| println!("Clicked!")), 148 + h_flex() 149 + .gap_4() 150 + // Memory info 151 + .child({ 152 + h_flex() 153 + .gap_2() 154 + .w(px(135.)) 155 + .items_center() 156 + .child(Icon::new(IconName::HardDrive)) 157 + .child(format!("{} tracks", tracks.delegate().tracks_amount())) 158 + }), 41 159 ) 42 160 } 43 161 }
+2
src/gui/mod.rs
··· 1 1 mod gui; 2 2 pub use gui::run; 3 + mod sync; 4 + mod track_table;
+15
src/gui/sync.rs
··· 1 + use std::any::TypeId; 2 + 3 + use gpui::{Context, InteractiveElement, IntoElement, Render, Styled, Window, actions, div, rgb}; 4 + use gpui_component::dialog::Dialog; 5 + 6 + pub struct SyncView {} 7 + impl Render for SyncView { 8 + fn render( 9 + &mut self, 10 + window: &mut gpui::Window, 11 + cx: &mut gpui::Context<Self>, 12 + ) -> impl gpui::IntoElement { 13 + Dialog::new(window, cx) 14 + } 15 + }
+159
src/gui/track_table.rs
··· 1 + use gpui::{Context, IntoElement, ParentElement, Styled, Window, div}; 2 + use gpui_component::{ 3 + ActiveTheme, StyledExt, 4 + table::{Column, ColumnSort, TableDelegate, TableState}, 5 + }; 6 + 7 + use crate::model; 8 + 9 + enum TrackSort { 10 + Title, 11 + Album, 12 + Artist, 13 + Format, 14 + } 15 + 16 + pub struct TrackTableDelegate { 17 + tracks: Vec<model::Track>, 18 + columns: Vec<Column>, 19 + sort_type: TrackSort, 20 + sort_order: ColumnSort, 21 + } 22 + 23 + impl TrackTableDelegate { 24 + pub fn new(tracks: Vec<model::Track>) -> Self { 25 + let mut s = Self { 26 + tracks: tracks, 27 + columns: vec![ 28 + Column::new("title", "Title") 29 + .min_width(500.) 30 + .max_width(800.) 31 + .sortable(), 32 + Column::new("artist", "Artist") 33 + .min_width(200.) 34 + .max_width(500.) 35 + .sortable(), 36 + Column::new("album", "Album") 37 + .min_width(300.) 38 + .max_width(500.) 39 + .sortable() 40 + .sortable(), 41 + Column::new("format", "Format") 42 + .min_width(60.) 43 + .max_width(500.) 44 + .sortable() 45 + .sortable(), 46 + ], 47 + sort_type: TrackSort::Title, 48 + sort_order: ColumnSort::Ascending, 49 + }; 50 + 51 + s.sort(); 52 + 53 + s 54 + } 55 + 56 + pub fn tracks_amount(&self) -> usize { 57 + self.tracks.len() 58 + } 59 + 60 + fn sort(&mut self) { 61 + let is_descending = matches!(self.sort_order, ColumnSort::Descending); 62 + 63 + match self.sort_type { 64 + TrackSort::Title => { 65 + self.tracks.sort_by(|a, b| { 66 + let cmp = a.title.to_lowercase().cmp(&b.title.to_lowercase()); 67 + if is_descending { cmp.reverse() } else { cmp } 68 + }); 69 + } 70 + TrackSort::Album => { 71 + self.tracks.sort_by(|a, b| { 72 + let cmp = a.album.to_lowercase().cmp(&b.album.to_lowercase()); 73 + if is_descending { cmp.reverse() } else { cmp } 74 + }); 75 + } 76 + TrackSort::Artist => { 77 + self.tracks.sort_by(|a, b| { 78 + let cmp = a.artist.to_lowercase().cmp(&b.artist.to_lowercase()); 79 + if is_descending { cmp.reverse() } else { cmp } 80 + }); 81 + } 82 + TrackSort::Format => { 83 + self.tracks.sort_by(|a, b| { 84 + let cmp = a.extension.cmp(&b.extension); 85 + if is_descending { cmp.reverse() } else { cmp } 86 + }); 87 + } 88 + } 89 + } 90 + } 91 + 92 + impl TableDelegate for TrackTableDelegate { 93 + fn columns_count(&self, cx: &gpui::App) -> usize { 94 + self.columns.len() 95 + } 96 + 97 + fn rows_count(&self, cx: &gpui::App) -> usize { 98 + self.tracks.len() 99 + } 100 + 101 + fn column(&self, col_ix: usize, cx: &gpui::App) -> gpui_component::table::Column { 102 + self.columns[col_ix].clone() 103 + } 104 + 105 + fn render_td( 106 + &mut self, 107 + row_ix: usize, 108 + col_ix: usize, 109 + window: &mut gpui::Window, 110 + cx: &mut gpui::Context<gpui_component::table::TableState<Self>>, 111 + ) -> impl gpui::IntoElement { 112 + let Some(track) = self.tracks.get(row_ix) else { 113 + return div().into_any_element(); 114 + }; 115 + 116 + match col_ix { 117 + 0 => div() 118 + .text_xs() 119 + .text_color(cx.theme().accent_foreground) 120 + .child(track.title.clone()) 121 + .into_any_element(), 122 + 1 => div() 123 + .text_sm() 124 + .text_color(cx.theme().foreground) 125 + .truncate() 126 + .child(track.artist.clone()) 127 + .into_any_element(), 128 + 2 => div() 129 + .text_xs() 130 + .child(track.album.clone()) 131 + .into_any_element(), 132 + 3 => div() 133 + .text_xs() 134 + .font_bold() 135 + .text_color(cx.theme().muted_foreground) 136 + .child(track.extension.clone().to_uppercase()) 137 + .into_any_element(), 138 + _ => div().into_any_element(), 139 + } 140 + } 141 + 142 + fn perform_sort( 143 + &mut self, 144 + col_ix: usize, 145 + sort: ColumnSort, 146 + _: &mut Window, 147 + _: &mut Context<TableState<Self>>, 148 + ) { 149 + self.sort_order = sort; 150 + self.sort_type = match col_ix { 151 + 0 => TrackSort::Title, 152 + 1 => TrackSort::Album, 153 + 2 => TrackSort::Artist, 154 + 3 => TrackSort::Format, 155 + _ => TrackSort::Title, 156 + }; 157 + self.sort(); 158 + } 159 + }
+6 -3
src/main.rs
··· 13 13 async fn main() -> anyhow::Result<()> { 14 14 log_setup(); 15 15 16 - if std::env::args().count() == 1 { 17 - return gui::run().await; 18 - } 16 + // One day I may get back to this. 17 + // Today is not one of those days. 18 + // 19 + // if std::env::args().count() == 1 { 20 + // return gui::run().await; 21 + // } 19 22 20 23 let c = cli::Cli::parse(); 21 24