at main 175 lines 5.5 kB view raw
1use dioxus::logger::tracing::{error, info}; 2use dioxus::prelude::*; 3use jacquard::oauth::client::OAuthClient; 4use jacquard::oauth::session::ClientData; 5use jacquard::{oauth::types::AuthorizeOptions, smol_str::SmolStr}; 6 7use crate::components::{ 8 button::{Button, ButtonVariant}, 9 dialog::{DialogContent, DialogRoot, DialogTitle}, 10 input::Input, 11}; 12use crate::fetch::Fetcher; 13use crate::CONFIG; 14 15fn handle_submit( 16 cached_route: String, 17 fetcher: Fetcher, 18 mut error: Signal<Option<String>>, 19 mut is_loading: Signal<bool>, 20 handle_input: Signal<String>, 21 mut open: Signal<bool>, 22) { 23 let handle = handle_input.read().clone(); 24 if handle.is_empty() { 25 error.set(Some("Please enter a handle".to_string())); 26 return; 27 } 28 29 is_loading.set(true); 30 error.set(None); 31 32 #[cfg(target_arch = "wasm32")] 33 { 34 use gloo_storage::Storage; 35 gloo_storage::LocalStorage::set("cached_route", cached_route).ok(); 36 spawn(async move { 37 match start_oauth_flow(handle, fetcher).await { 38 Ok(_) => { 39 open.set(false); 40 } 41 Err(e) => { 42 error!("Authentication failed: {}", e); 43 error.set(Some(format!("Authentication failed: {}", e))); 44 is_loading.set(false); 45 } 46 } 47 }); 48 } 49} 50 51#[component] 52pub fn LoginModal(open: Signal<bool>, cached_route: String) -> Element { 53 let mut handle_input = use_signal(|| String::new()); 54 let error = use_signal(|| Option::<String>::None); 55 let is_loading = use_signal(|| false); 56 let fetcher = use_context::<Fetcher>(); 57 let cached_route_clone = cached_route.clone(); 58 let submit_fetcher = fetcher.clone(); 59 let submit_closure1 = move || { 60 let cached_route = cached_route_clone.clone(); 61 let submit_fetcher = submit_fetcher.clone(); 62 handle_submit( 63 cached_route, 64 submit_fetcher, 65 error, 66 is_loading, 67 handle_input, 68 open, 69 ); 70 }; 71 72 let submit_closure2 = move || { 73 let cached_route = cached_route.clone(); 74 let submit_fetcher = fetcher.clone(); 75 handle_submit( 76 cached_route, 77 submit_fetcher, 78 error, 79 is_loading, 80 handle_input, 81 open, 82 ); 83 }; 84 85 rsx! { 86 DialogRoot { open: open(), on_open_change: move |v| open.set(v), 87 DialogContent { 88 button { 89 class: "dialog-close", 90 r#type: "button", 91 aria_label: "Close", 92 tabindex: if open() { "0" } else { "-1" }, 93 onclick: move |_| { 94 open.set(false) 95 }, 96 "×" 97 } 98 DialogTitle { "Sign In with AT Protocol" } 99 Input { 100 aria_label: "Handle", 101 oninput: move |e: FormEvent| handle_input.set(e.value()), 102 onkeypress: move |k: KeyboardEvent| { 103 if k.key() == Key::Enter { 104 submit_closure1(); 105 } 106 }, 107 placeholder: "Enter your handle", 108 value: "{handle_input}", 109 } 110 if let Some(err) = error() { 111 div { class: "error", "{err}" } 112 } 113 Button { 114 r#type: "submit", 115 disabled: is_loading(), 116 onclick: move |_| { 117 submit_closure2(); 118 }, 119 if is_loading() { "Authenticating..." } else { "Sign In" } 120 } 121 Button { 122 r#type: "button", 123 onclick: move |_| { 124 open.set(false) 125 }, 126 disabled: is_loading(), 127 variant: ButtonVariant::Secondary, 128 "Cancel" 129 } 130 131 132 } 133 } 134 } 135} 136 137async fn start_oauth_flow(handle: String, fetcher: Fetcher) -> Result<(), SmolStr> { 138 info!("Starting OAuth flow for handle: {}", handle); 139 140 let client_data = ClientData { 141 keyset: fetcher 142 .client 143 .oauth_client 144 .registry 145 .client_data 146 .keyset 147 .clone(), 148 config: CONFIG.oauth.clone(), 149 }; 150 151 // Build client using store and resolver 152 let flow_client = OAuthClient::new_with_shared( 153 fetcher.client.oauth_client.registry.store.clone(), 154 fetcher.client.oauth_client.client.clone(), 155 client_data.clone(), 156 ); 157 158 let auth_url = flow_client 159 .start_auth(handle, AuthorizeOptions::default()) 160 .await 161 .map_err(|e| format!("{:?}", e))?; 162 #[cfg(target_arch = "wasm32")] 163 { 164 let window = web_sys::window().ok_or("no window")?; 165 let location = window.location(); 166 location 167 .set_href(&auth_url) 168 .map_err(|e| format!("{:?}", e))?; 169 } 170 #[cfg(not(target_arch = "wasm32"))] 171 { 172 webbrowser::open(&auth_url).map_err(|e| format!("{:?}", e))?; 173 } 174 Ok(()) 175}