/ src / layouts / auth.rs
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  }