Fix #5 + fix #6 + add a logout button

This commit is contained in:
Baptiste Gelez 2019-02-27 20:06:36 +01:00
parent e4191a744d
commit fa53f0ec76
4 changed files with 106 additions and 46 deletions

View File

@ -44,14 +44,14 @@ impl RequestContext {
pub fn get<S: AsRef<str>>(&self, url: S) -> reqwest::RequestBuilder { pub fn get<S: AsRef<str>>(&self, url: S) -> reqwest::RequestBuilder {
self.client 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)) .header(reqwest::header::AUTHORIZATION, format!("JWT {}", self.token))
} }
/// Warning: no authentication, since it is only used for login /// Warning: no authentication, since it is only used for login
pub fn post<S: AsRef<str>>(&self, url: S) -> reqwest::RequestBuilder { pub fn post<S: AsRef<str>>(&self, url: S) -> reqwest::RequestBuilder {
self.client 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 { pub fn to_json(&self) -> serde_json::Value {

View File

@ -70,6 +70,7 @@ mod ui;
#[derive(Debug)] #[derive(Debug)]
pub struct AppState { pub struct AppState {
window: Rc<RefCell<Window>>,
stack: Stack, stack: Stack,
error: InfoBar, error: InfoBar,
header: HeaderBar, header: HeaderBar,
@ -156,6 +157,24 @@ fn main() {
window.set_title("Mobydick"); window.set_title("Mobydick");
window.set_default_size(1080, 720); 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<RefCell<Window>>) {
let connected = fs::read(dirs::config_dir().unwrap().join("mobydick").join("data.json")).ok().and_then(|f| { 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 json: serde_json::Value = serde_json::from_slice(&f).ok()?;
let mut api_ctx = crate::api::API.lock().ok()?; let mut api_ctx = crate::api::API.lock().ok()?;
@ -167,6 +186,7 @@ fn main() {
}).is_some(); }).is_some();
let state = Rc::new(RefCell::new(AppState { let state = Rc::new(RefCell::new(AppState {
window: window.clone(),
stack: { stack: {
let s = Stack::new(); let s = Stack::new();
s.set_vexpand(true); s.set_vexpand(true);
@ -196,16 +216,20 @@ fn main() {
let scrolled = ScrolledWindow::new(None, None); let scrolled = ScrolledWindow::new(None, None);
scrolled.add(&main_box); scrolled.add(&main_box);
window.add(&scrolled); window.borrow().add(&scrolled);
window.set_titlebar(&state.borrow().header); window.borrow().set_titlebar(&state.borrow().header);
window.show_all(); window.borrow().show_all();
if connected { if connected {
let main_page = ui::main_page::render(&state.borrow().header, &{ let main_page = ui::main_page::render(
let s = StackSwitcher::new(); state.borrow().window.clone(),
s.set_stack(&state.borrow().stack); &state.borrow().header,
s &{
}); 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(&main_page, "main", "Search Music");
state.borrow().stack.add_titled(&*ui::dl_list::render().borrow(), "downloads", "Downloads"); state.borrow().stack.add_titled(&*ui::dl_list::render().borrow(), "downloads", "Downloads");
state.borrow().stack.set_visible_child_name("main"); state.borrow().stack.set_visible_child_name("main");
@ -213,20 +237,6 @@ fn main() {
let login_page = ui::login_page::render(state.clone()); let login_page = ui::login_page::render(state.clone());
state.borrow().stack.add_named(&login_page, "login"); 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) { 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.show_all();
state.borrow().error.set_revealed(true); state.borrow().error.set_revealed(true);
} }
fn logout(window: Rc<RefCell<Window>>) {
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)
}

View File

@ -25,8 +25,12 @@ pub fn render(state: State) -> gtk::Box {
))); )));
login_bt.connect_clicked(clone!(state, widgets => move |_| { login_bt.connect_clicked(clone!(state, widgets => move |_| {
let mut api_ctx = crate::api::API.lock().unwrap(); 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( *api_ctx = Some(RequestContext::new(
widgets.borrow().0.get_text().unwrap() instance_url
)); ));
let state = state.clone(); let state = state.clone();
@ -45,11 +49,16 @@ pub fn render(state: State) -> gtk::Box {
let state = state.borrow(); let state = state.borrow();
state.error.set_revealed(false); state.error.set_revealed(false);
state.stack.add_titled(&crate::ui::main_page::render(&state.header, &{ state.stack.add_titled(&crate::ui::main_page::render(
let s = StackSwitcher::new(); state.window.clone(),
s.set_stack(&state.stack); &state.header,
s &{
}), "main", "Search Music"); let s = StackSwitcher::new();
s.set_stack(&state.stack);
s
}
),
"main", "Search Music");
state.stack.set_visible_child_name("main"); state.stack.set_visible_child_name("main");
state.stack.add_titled(&*crate::ui::dl_list::render().borrow(), "downloads", "Downloads"); 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 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()); let (ref instance, ref username, ref password) = *widgets.borrow();
cont.add(&widgets.borrow().2.render()); cont.add(&title);
cont.add(&login_bt); 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.show_all();
cont cont

View File

@ -2,11 +2,13 @@ use gdk::ContextExt;
use gdk_pixbuf::PixbufExt; use gdk_pixbuf::PixbufExt;
use gtk::*; use gtk::*;
use std::{ use std::{
cell::RefCell,
fs, fs,
rc::Rc,
}; };
use crate::{api::{self, execute}, ui::{title, card}}; use crate::{api::{self, execute}, ui::{title, card}};
pub fn render(header: &HeaderBar, switcher: &StackSwitcher) -> gtk::Box { pub fn render(window: Rc<RefCell<Window>>, header: &HeaderBar, switcher: &StackSwitcher) -> gtk::Box {
let cont = gtk::Box::new(Orientation::Vertical, 12); let cont = gtk::Box::new(Orientation::Vertical, 12);
cont.set_margin_top(48); cont.set_margin_top(48);
cont.set_margin_bottom(48); cont.set_margin_bottom(48);
@ -50,6 +52,12 @@ pub fn render(header: &HeaderBar, switcher: &StackSwitcher) -> gtk::Box {
})); }));
header.pack_start(&avatar); header.pack_start(&avatar);
header.set_custom_title(&*switcher); 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(); header.show_all();
let search = SearchEntry::new(); let search = SearchEntry::new();
@ -63,17 +71,23 @@ pub fn render(header: &HeaderBar, switcher: &StackSwitcher) -> gtk::Box {
rc!(avatar, results); rc!(avatar, results);
clone!(avatar, results, avatar_path); clone!(avatar, results, avatar_path);
wait!(execute(client!().get("/api/v1/users/users/me")) => |res| { wait!(execute(client!().get("/api/v1/users/users/me")) => |res| {
let res: api::UserInfo = res.json().unwrap(); let res: Result<api::UserInfo, _> = 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| {
clone!(avatar_path, avatar); fs::create_dir_all(avatar_path.parent().unwrap()).unwrap();
wait!(execute(client!().get(&res.avatar.medium_square_crop.unwrap_or_default())) => |avatar_dl| { let mut avatar_file = fs::File::create(avatar_path.clone()).unwrap();
fs::create_dir_all(avatar_path.parent().unwrap()).unwrap(); avatar_dl.copy_to(&mut avatar_file).unwrap();
let mut avatar_file = fs::File::create(avatar_path.clone()).unwrap(); avatar.borrow().queue_draw();
avatar_dl.copy_to(&mut avatar_file).unwrap(); });
avatar.borrow().queue_draw(); },
}); Err(_) => {
crate::logout(window.clone());
}
}
}); });
search.connect_activate(move |s| { search.connect_activate(move |s| {