diff --git a/src/main.rs b/src/main.rs index a935365..9e1077a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ mod auth; use std::collections::HashMap; -use std::sync::mpsc::channel; +use std::sync::mpsc::{channel, Receiver}; use std::thread; use log::{debug, info, LevelFilter}; @@ -11,11 +11,11 @@ use oauth2::url::Url; use wry::application::accelerator::{Accelerator, SysMods}; use wry::application::event::{Event, WindowEvent}; -use wry::application::event_loop::{ControlFlow, EventLoop}; +use wry::application::event_loop::{ControlFlow, EventLoop, EventLoopProxy}; use wry::application::keyboard::KeyCode; -use wry::application::menu::{MenuBar as Menu, MenuItem, MenuItemAttributes, MenuType}; +use wry::application::menu::{CustomMenuItem, MenuBar, MenuItem, MenuItemAttributes, MenuType}; use wry::application::window::{Window, WindowBuilder}; -use wry::http::ResponseBuilder; +use wry::http::{Request, Response, ResponseBuilder}; use wry::webview::{RpcRequest, WebViewBuilder}; use wry::Value; @@ -44,36 +44,9 @@ fn main() -> wry::Result<()> { .init() .unwrap(); - let mut client = auth::Client::new(); - let auth_url = client.authorization_url(); - - debug!("Opening {} ...", auth_url); - let event_loop = EventLoop::::with_user_event(); let event_proxy = event_loop.create_proxy(); - let mut menu_bar_menu = Menu::new(); - let mut menu = Menu::new(); - - menu.add_native_item(MenuItem::About("Todos".to_string())); - menu.add_native_item(MenuItem::Services); - menu.add_native_item(MenuItem::Separator); - menu.add_native_item(MenuItem::Hide); - let quit_item = menu.add_item( - MenuItemAttributes::new("Quit") - .with_accelerators(&Accelerator::new(SysMods::Cmd, KeyCode::KeyQ)), - ); - menu.add_native_item(MenuItem::Copy); - menu.add_native_item(MenuItem::Paste); - - menu_bar_menu.add_submenu("First menu", true, menu); - - let window = WindowBuilder::new() - .with_title("Tesla Auth") - .with_menu(menu_bar_menu) - .build(&event_loop) - .unwrap(); - let (tx, rx) = channel(); let handler = move |_window: &Window, req: RpcRequest| { @@ -85,59 +58,29 @@ fn main() -> wry::Result<()> { None }; + let mut client = auth::Client::new(); + let auth_url = client.authorization_url(); + thread::spawn(move || { - let mut tokens_retrieved = false; - - while let Ok(url) = rx.recv() { - if !auth::is_redirect_url(&url) || tokens_retrieved { - debug!("URL changed: {}", &url); - continue; - } - - let query: HashMap<_, _> = url.query_pairs().collect(); - - let state = query.get("state").expect("No state parameter found"); - let code = query.get("code").expect("No code parameter found"); - - client.verify_csrf_state(state.to_string()); - - let tokens = client.retrieve_tokens(code); - - tokens_retrieved = true; - - event_proxy.send_event(CustomEvent::Tokens(tokens)).unwrap(); - } + handle_url_changes(rx, client, event_proxy); }); - let webview = WebViewBuilder::new(window) - .unwrap() + let (menu, quit_item) = build_menu(); + + let window = WindowBuilder::new() + .with_title("Tesla Auth") + .with_menu(menu) + .build(&event_loop)?; + + let webview = WebViewBuilder::new(window)? .with_initialization_script(INITIALIZATION_SCRIPT) - .with_custom_protocol("wry".into(), move |request| { - let url: Url = request.uri().parse()?; - - match url.domain() { - Some("index.html") => { - let query = url.query_pairs().collect::>(); - - let (access, refresh) = - (query.get("access").unwrap(), query.get("refresh").unwrap()); - - let content = include_str!("../views/index.html") - .replace("{access_token}", access) - .replace("{refresh_token}", refresh); - - ResponseBuilder::new() - .mimetype("text/html") - .body(content.as_bytes().to_vec()) - } - - domain => unimplemented!("Cannot open {:?}", domain), - } - }) + .with_custom_protocol("wry".into(), protocol_handler) .with_url(auth_url.as_str())? .with_rpc_handler(handler) .build()?; + debug!("Opening {} ...", auth_url); + event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; @@ -171,6 +114,76 @@ fn main() -> wry::Result<()> { }); } +fn build_menu() -> (MenuBar, CustomMenuItem) { + let mut menu_bar_menu = MenuBar::new(); + let mut menu = MenuBar::new(); + + menu.add_native_item(MenuItem::About("Todos".to_string())); + menu.add_native_item(MenuItem::Services); + menu.add_native_item(MenuItem::Separator); + menu.add_native_item(MenuItem::Hide); + let quit_item = menu.add_item( + MenuItemAttributes::new("Quit") + .with_accelerators(&Accelerator::new(SysMods::Cmd, KeyCode::KeyQ)), + ); + menu.add_native_item(MenuItem::Copy); + menu.add_native_item(MenuItem::Paste); + + menu_bar_menu.add_submenu("First menu", true, menu); + + (menu_bar_menu, quit_item) +} + +fn handle_url_changes( + rx: Receiver, + mut client: auth::Client, + event_proxy: EventLoopProxy, +) { + let mut tokens_retrieved = false; + + while let Ok(url) = rx.recv() { + if !auth::is_redirect_url(&url) || tokens_retrieved { + debug!("URL changed: {}", &url); + continue; + } + + let query: HashMap<_, _> = url.query_pairs().collect(); + + let state = query.get("state").expect("No state parameter found"); + let code = query.get("code").expect("No code parameter found"); + + client.verify_csrf_state(state.to_string()); + + let tokens = client.retrieve_tokens(code); + + tokens_retrieved = true; + + event_proxy.send_event(CustomEvent::Tokens(tokens)).unwrap(); + } +} + +fn protocol_handler(request: &Request) -> wry::Result { + let url: Url = request.uri().parse()?; + + match url.domain() { + Some("index.html") => { + let query = url.query_pairs().collect::>(); + + let (access, refresh) = (query.get("access").unwrap(), query.get("refresh").unwrap()); + + let content = include_str!("../views/index.html") + .replace("{access_token}", access) + .replace("{refresh_token}", refresh); + + ResponseBuilder::new() + .mimetype("text/html") + .body(content.as_bytes().to_vec()) + } + + domain => unimplemented!("Cannot open {:?}", domain), + } +} + fn parse_url(params: Value) -> Url { let args = serde_json::from_value::>(params).unwrap(); let url = args.first().unwrap();