auth.rs
1 use gpui::{prelude::*, *}; 2 use gpui_component::{ 3 form::{field, v_form}, 4 input::{Input, InputState}, 5 *, 6 }; 7 8 use crate::{ 9 api::api_v9_post_auth_login, 10 components::branding::Button, 11 database::{self, pool::Pool}, 12 stores::{ 13 http_manager::HttpManager, 14 router::{Route, Router}, 15 }, 16 }; 17 18 pub struct AuthView { 19 email: Entity<InputState>, 20 password: Entity<InputState>, 21 loading: bool, 22 } 23 24 impl Render for AuthView { 25 fn render( 26 &mut self, 27 _: &mut Window, 28 cx: &mut Context<Self>, 29 ) -> impl IntoElement { 30 div() 31 .size_full() 32 .bg(gpui::white()) 33 .flex() 34 .justify_center() 35 .items_center() 36 .child( 37 div() 38 .max_w(px(480.)) 39 .w_full() 40 .flex() 41 .flex_col() 42 .items_center() 43 .rounded_lg() 44 .bg(rgb(0x323339)) 45 .border_1() 46 .border_color(rgb(0xadaeb4)) 47 .p_8() 48 .gap_5() 49 .child( 50 div() 51 .flex() 52 .flex_col() 53 .items_center() 54 .gap_1() 55 .child( 56 div() 57 .text_color(gpui::white()) 58 .text_size(px(24.)) 59 .font_weight(FontWeight::SEMIBOLD) 60 .child("Welcome back!"), 61 ) 62 .child( 63 div() 64 .text_color(rgb(0xdfe0e2)) 65 .text_size(px(16.)) 66 .child("We're so excited to see you again!"), 67 ), 68 ) 69 .child( 70 v_form() 71 .child( 72 field() 73 .label("Email or Phone Number") 74 .required(true) 75 .child(Input::new(&self.email).large()), 76 ) 77 .child( 78 field() 79 .label("Password") 80 .required(true) 81 .child(Input::new(&self.password).mask_toggle().large()), 82 ) 83 .child( 84 field().label_indent(false).child( 85 Button::new("login_button") 86 .w_full() 87 .label(if self.loading { 88 "Logging in..." 89 } else { 90 "Log In" 91 }) 92 .on_click( 93 cx.listener(|this, _, window, cx| this.login(window, cx)), 94 ), 95 ), 96 ), 97 ), 98 ) 99 } 100 } 101 102 impl AuthView { 103 pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self { 104 let email = cx.new(|cx| InputState::new(window, cx)); 105 let password = cx.new(|cx| InputState::new(window, cx).masked(true)); 106 107 Self { 108 email, 109 password, 110 loading: false, 111 } 112 } 113 114 fn login(&mut self, _window: &mut Window, cx: &mut Context<Self>) { 115 println!("starting login sequence"); 116 117 if self.loading { 118 return; 119 } 120 121 let email = self.email.read(cx).value().to_string(); 122 let password = self.password.read(cx).value().to_string(); 123 124 if email.is_empty() || password.is_empty() { 125 return; 126 } 127 128 self.set_loading(cx, true); 129 130 let client = cx.global_mut::<HttpManager>().unauth().unwrap(); 131 let response = 132 crate::RUNTIME.spawn(api_v9_post_auth_login(client, email, password)); 133 134 cx.spawn(async move |this, cx| match response.await? { 135 Ok(response) => { 136 if let Some(token) = response.token { 137 this.update(cx, |this, cx| { 138 let pool: &Pool = cx.global(); 139 crate::RUNTIME.block_on(database::accounts::insert( 140 &pool.0, 141 response.user_id, 142 token.clone(), 143 ))?; 144 145 let http: &mut HttpManager = cx.global_mut(); 146 http.token = Some(token); 147 148 let router: &mut Router = cx.global_mut(); 149 router.current = Route::App; 150 151 this.loading = false; 152 cx.notify(); 153 154 Ok(()) 155 })? 156 } else { 157 this.update(cx, |this, cx| { 158 this.loading = false; 159 cx.notify(); 160 161 Ok(()) 162 })? 163 } 164 } 165 Err(err) => { 166 this.update(cx, |this, cx| { 167 this.loading = false; 168 cx.notify(); 169 })?; 170 171 Err(err) 172 } 173 }) 174 .detach_and_log_err(cx); 175 } 176 177 fn set_loading(&mut self, cx: &mut Context<Self>, status: bool) { 178 self.loading = status; 179 cx.notify(); 180 } 181 }