diff --git a/src/api.rs b/src/api.rs index 9b9d38a..f480f16 100644 --- a/src/api.rs +++ b/src/api.rs @@ -44,14 +44,14 @@ impl RequestContext { pub fn get>(&self, url: S) -> reqwest::RequestBuilder { self.client - .get(&format!("https://{}{}", self.instance, url.as_ref())) + .get(&format!("{}{}", self.instance, url.as_ref())) .header(reqwest::header::AUTHORIZATION, format!("JWT {}", self.token)) } /// Warning: no authentication, since it is only used for login pub fn post>(&self, url: S) -> reqwest::RequestBuilder { self.client - .post(&format!("https://{}{}", self.instance, url.as_ref())) + .post(&format!("{}{}", self.instance, url.as_ref())) } pub fn to_json(&self) -> serde_json::Value { diff --git a/src/main.rs b/src/main.rs index 2962979..ddae0a0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,6 +70,7 @@ mod ui; #[derive(Debug)] pub struct AppState { + window: Rc>, stack: Stack, error: InfoBar, header: HeaderBar, @@ -156,6 +157,24 @@ fn main() { window.set_title("Mobydick"); window.set_default_size(1080, 720); + window.connect_delete_event(move |_, _| { + gtk::main_quit(); + + fs::create_dir_all(dirs::config_dir().unwrap().join("mobydick")).unwrap(); + fs::write( + dirs::config_dir().unwrap().join("mobydick").join("data.json"), + serde_json::to_string(&client!().to_json()).unwrap() + ).unwrap(); + + Inhibit(false) + }); + + init(Rc::new(RefCell::new(window))); + + gtk::main(); +} + +fn init(window: Rc>) { let connected = fs::read(dirs::config_dir().unwrap().join("mobydick").join("data.json")).ok().and_then(|f| { let json: serde_json::Value = serde_json::from_slice(&f).ok()?; let mut api_ctx = crate::api::API.lock().ok()?; @@ -167,6 +186,7 @@ fn main() { }).is_some(); let state = Rc::new(RefCell::new(AppState { + window: window.clone(), stack: { let s = Stack::new(); s.set_vexpand(true); @@ -196,16 +216,20 @@ fn main() { let scrolled = ScrolledWindow::new(None, None); scrolled.add(&main_box); - window.add(&scrolled); - window.set_titlebar(&state.borrow().header); - window.show_all(); + window.borrow().add(&scrolled); + window.borrow().set_titlebar(&state.borrow().header); + window.borrow().show_all(); if connected { - let main_page = ui::main_page::render(&state.borrow().header, &{ - let s = StackSwitcher::new(); - s.set_stack(&state.borrow().stack); - s - }); + let main_page = ui::main_page::render( + state.borrow().window.clone(), + &state.borrow().header, + &{ + let s = StackSwitcher::new(); + s.set_stack(&state.borrow().stack); + s + } + ); state.borrow().stack.add_titled(&main_page, "main", "Search Music"); state.borrow().stack.add_titled(&*ui::dl_list::render().borrow(), "downloads", "Downloads"); state.borrow().stack.set_visible_child_name("main"); @@ -213,20 +237,6 @@ fn main() { let login_page = ui::login_page::render(state.clone()); state.borrow().stack.add_named(&login_page, "login"); } - - window.connect_delete_event(move |_, _| { - gtk::main_quit(); - - fs::create_dir_all(dirs::config_dir().unwrap().join("mobydick")).unwrap(); - fs::write( - dirs::config_dir().unwrap().join("mobydick").join("data.json"), - serde_json::to_string(&client!().to_json()).unwrap() - ).unwrap(); - - Inhibit(false) - }); - - gtk::main(); } fn show_error(state: State, msg: &str) { @@ -238,3 +248,16 @@ fn show_error(state: State, msg: &str) { state.borrow().error.show_all(); state.borrow().error.set_revealed(true); } + +fn logout(window: Rc>) { + fs::remove_file(dirs::config_dir().unwrap().join("mobydick").join("data.json")).ok(); + *api::API.lock().unwrap() = None; + *DOWNLOADS.lock().unwrap() = HashMap::new(); + { + let window = window.borrow(); + for ch in window.get_children() { + window.remove(&ch); + } + } + init(window) +} \ No newline at end of file diff --git a/src/ui/login_page.rs b/src/ui/login_page.rs index 77ecc89..0700e89 100644 --- a/src/ui/login_page.rs +++ b/src/ui/login_page.rs @@ -25,8 +25,12 @@ pub fn render(state: State) -> gtk::Box { ))); login_bt.connect_clicked(clone!(state, widgets => move |_| { let mut api_ctx = crate::api::API.lock().unwrap(); + let mut instance_url = widgets.borrow().0.get_text().unwrap().trim_end_matches('/').to_string(); + if !(instance_url.starts_with("http://") || instance_url.starts_with("https://")) { + instance_url = format!("https://{}", instance_url) + } *api_ctx = Some(RequestContext::new( - widgets.borrow().0.get_text().unwrap() + instance_url )); let state = state.clone(); @@ -45,11 +49,16 @@ pub fn render(state: State) -> gtk::Box { let state = state.borrow(); state.error.set_revealed(false); - state.stack.add_titled(&crate::ui::main_page::render(&state.header, &{ - let s = StackSwitcher::new(); - s.set_stack(&state.stack); - s - }), "main", "Search Music"); + state.stack.add_titled(&crate::ui::main_page::render( + state.window.clone(), + &state.header, + &{ + let s = StackSwitcher::new(); + s.set_stack(&state.stack); + s + } + ), + "main", "Search Music"); state.stack.set_visible_child_name("main"); state.stack.add_titled(&*crate::ui::dl_list::render().borrow(), "downloads", "Downloads"); state.stack.remove(&state.stack.get_child_by_name("login").unwrap()); // To avoid having a "Login" tab in the header @@ -59,11 +68,25 @@ pub fn render(state: State) -> gtk::Box { }); })); - cont.add(&title); - cont.add(&widgets.borrow().0.render()); - cont.add(&widgets.borrow().1.render()); - cont.add(&widgets.borrow().2.render()); - cont.add(&login_bt); + { + + let (ref instance, ref username, ref password) = *widgets.borrow(); + cont.add(&title); + cont.add(&instance.render()); + cont.add(&username.render()); + cont.add(&password.render()); + cont.add(&login_bt); + } + + widgets.borrow().0.entry.connect_activate(clone!(widgets => move |_| { + widgets.borrow().1.entry.grab_focus(); + })); + widgets.borrow().1.entry.connect_activate(clone!(widgets => move |_| { + widgets.borrow().2.entry.grab_focus(); + })); + widgets.borrow().2.entry.connect_activate(move |_| { + login_bt.clicked(); + }); cont.show_all(); cont diff --git a/src/ui/main_page.rs b/src/ui/main_page.rs index ffcd221..73cd25c 100644 --- a/src/ui/main_page.rs +++ b/src/ui/main_page.rs @@ -2,11 +2,13 @@ use gdk::ContextExt; use gdk_pixbuf::PixbufExt; use gtk::*; use std::{ + cell::RefCell, fs, + rc::Rc, }; use crate::{api::{self, execute}, ui::{title, card}}; -pub fn render(header: &HeaderBar, switcher: &StackSwitcher) -> gtk::Box { +pub fn render(window: Rc>, header: &HeaderBar, switcher: &StackSwitcher) -> gtk::Box { let cont = gtk::Box::new(Orientation::Vertical, 12); cont.set_margin_top(48); cont.set_margin_bottom(48); @@ -50,6 +52,12 @@ pub fn render(header: &HeaderBar, switcher: &StackSwitcher) -> gtk::Box { })); header.pack_start(&avatar); header.set_custom_title(&*switcher); + + let logout_bt = Button::new_from_icon_name("system-log-out", IconSize::LargeToolbar.into()); + logout_bt.connect_clicked(clone!(window => move |_| { + crate::logout(window.clone()); + })); + header.pack_end(&logout_bt); header.show_all(); let search = SearchEntry::new(); @@ -63,17 +71,23 @@ pub fn render(header: &HeaderBar, switcher: &StackSwitcher) -> gtk::Box { rc!(avatar, results); clone!(avatar, results, avatar_path); wait!(execute(client!().get("/api/v1/users/users/me")) => |res| { - let res: api::UserInfo = res.json().unwrap(); + let res: Result = res.json(); + match res { + Ok(res) => { + avatar.borrow().set_tooltip_text(format!("Connected as {}.", res.username).as_ref()); - avatar.borrow().set_tooltip_text(format!("Connected as {}.", res.username).as_ref()); - - clone!(avatar_path, avatar); - wait!(execute(client!().get(&res.avatar.medium_square_crop.unwrap_or_default())) => |avatar_dl| { - fs::create_dir_all(avatar_path.parent().unwrap()).unwrap(); - let mut avatar_file = fs::File::create(avatar_path.clone()).unwrap(); - avatar_dl.copy_to(&mut avatar_file).unwrap(); - avatar.borrow().queue_draw(); - }); + clone!(avatar_path, avatar); + wait!(execute(client!().get(&res.avatar.medium_square_crop.unwrap_or_default())) => |avatar_dl| { + fs::create_dir_all(avatar_path.parent().unwrap()).unwrap(); + let mut avatar_file = fs::File::create(avatar_path.clone()).unwrap(); + avatar_dl.copy_to(&mut avatar_file).unwrap(); + avatar.borrow().queue_draw(); + }); + }, + Err(_) => { + crate::logout(window.clone()); + } + } }); search.connect_activate(move |s| {