diff --git a/src/api/auth.rs b/src/api/auth.rs index 68e66d2..7d4c03a 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -1,19 +1,6 @@ use crate::api::client::{ApiClient, Endpoint}; use keyring::KeyringEntry; -use serde::Deserialize; - -#[derive(Deserialize)] -pub struct UserInfo { - pub username: String, - pub display_name: Option, -} - -impl UserInfo { - /// Returns the display name if set, otherwise falls back to the username. - pub fn name(&self) -> &str { - self.display_name.as_deref().unwrap_or(&self.username) - } -} +use serde_json::Value; pub struct Auth { pub token_entry: KeyringEntry, @@ -35,12 +22,13 @@ impl Auth { }) } - pub async fn validate_token(&self, token: &str) -> Result { + pub async fn validate_token(&self, token: &str) -> Result { let client = ApiClient::new(token.to_string()); client - .get::(Endpoint::CurrentUser) + .get::(Endpoint::CurrentUser) .await + .map(|_| client) .map_err(|e| { let err_msg = e.to_string(); if err_msg.contains("401") { diff --git a/src/app.rs b/src/app.rs index 3e705e3..ea1741f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,6 +2,7 @@ use ratatui::crossterm::event::{KeyCode, KeyEvent}; use crate::action::Action; use crate::api::auth::Auth; +use crate::api::client::ApiClient; use crate::input::InputState; use crate::{Result, cache::CacheStore}; @@ -16,9 +17,9 @@ pub struct App { pub state: AppState, pub input_text: String, pub auth: Auth, - pub username: String, pub should_quit: bool, pub input_state: InputState, + pub client: ApiClient, #[allow(unused)] pub cache: CacheStore, } @@ -27,12 +28,12 @@ impl App { pub async fn new() -> Result { let auth = Auth::new().map_err(|e| anyhow::anyhow!(e))?; - let mut username = String::new(); + let mut client = ApiClient::new(String::new()); let state = if let Ok(token) = auth.token_entry.get_secret().await { match auth.validate_token(&token).await { - Ok(user_info) => { - username = user_info.name().to_string(); + Ok(authenticated_client) => { + client = authenticated_client; AppState::LoggedIn } Err(e) => AppState::Error(format!("Stored token is invalid: {}", e)), @@ -47,8 +48,8 @@ impl App { state, input_text: String::new(), auth, - username, should_quit: false, + client, cache, input_state: InputState::default(), }) @@ -61,9 +62,9 @@ impl App { if !self.input_text.is_empty() { self.state = AppState::ValidatingToken; match self.auth.validate_token(&self.input_text).await { - Ok(user_info) => match self.auth.store_token(&self.input_text).await { + Ok(client) => match self.auth.store_token(&self.input_text).await { Ok(_) => { - self.username = user_info.name().to_string(); + self.client = client; self.state = AppState::LoggedIn; } Err(detailed_err) => { diff --git a/src/main.rs b/src/main.rs index 71dadf2..af3f991 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,13 +4,12 @@ mod app; mod cache; mod error; mod input; -mod state; +mod ui; use std::{fs, path::PathBuf}; use app::App; use ratatui::crossterm::event::{self, Event}; -use state::ui; pub const LOG_FILE: &str = "logs"; diff --git a/src/state/mod.rs b/src/state/mod.rs deleted file mode 100644 index 6bae95d..0000000 --- a/src/state/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod ui; diff --git a/src/state/ui.rs b/src/state/ui.rs deleted file mode 100644 index c468b1f..0000000 --- a/src/state/ui.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::app::{App, AppState}; -use ratatui::{ - Frame, - layout::{Constraint, Direction, Layout}, - style::{Color, Style}, - widgets::{Block, Borders, Paragraph}, -}; - -pub fn render(f: &mut Frame, app: &App) { - let area = f.area(); - - match &app.state { - AppState::InputToken => { - let chunks = Layout::default() - .direction(Direction::Vertical) - .margin(2) - .constraints([ - Constraint::Length(2), - Constraint::Length(3), - Constraint::Min(0), - ]) - .split(area); - - let explanation = Paragraph::new( - "We couldn't find your user_token. Please paste it below and press Enter to save it securely:", - ) - .style(Style::default().fg(Color::Yellow)); - f.render_widget(explanation, chunks[0]); - - let input_block = Paragraph::new(app.input_text.as_str()) - .block(Block::default().title(" User Token ").borders(Borders::ALL)); - f.render_widget(input_block, chunks[1]); - } - AppState::ValidatingToken => { - let msg = Paragraph::new("Validating token with Stoat API...") - .style(Style::default().fg(Color::Cyan)) - .block( - Block::default() - .title(" Authenticating ") - .borders(Borders::ALL), - ); - f.render_widget(msg, area); - } - AppState::LoggedIn => { - let success_msg = Paragraph::new(format!( - "Logged in as: {}\n\nPress 'q' to quit.", - app.username - )) - .block(Block::default().title(" VimStoat ").borders(Borders::ALL)); - f.render_widget(success_msg, area); - } - AppState::Error(err_msg) => { - let error_text = format!( - "CRITICAL SYSTEM ERROR:\n\n{}\n\nPress any key to return...", - err_msg - ); - let error_msg = Paragraph::new(error_text) - .style(Style::default().fg(Color::Red)) - .block( - Block::default() - .title(" Keyring Failure ") - .borders(Borders::ALL), - ); - f.render_widget(error_msg, area); - } - } -} diff --git a/src/ui/error.rs b/src/ui/error.rs new file mode 100644 index 0000000..19b7b7f --- /dev/null +++ b/src/ui/error.rs @@ -0,0 +1,17 @@ +use ratatui::{ + Frame, + style::{Color, Style}, + widgets::{Block, Borders, Paragraph}, +}; + +pub fn render(f: &mut Frame, message: &str) { + let error_text = format!("CRITICAL SYSTEM ERROR:\n\n{message}\n\nPress any key to return..."); + let error_msg = Paragraph::new(error_text) + .style(Style::default().fg(Color::Red)) + .block( + Block::default() + .title(" Keyring Failure ") + .borders(Borders::ALL), + ); + f.render_widget(error_msg, f.area()); +} diff --git a/src/ui/input_token.rs b/src/ui/input_token.rs new file mode 100644 index 0000000..5bf321d --- /dev/null +++ b/src/ui/input_token.rs @@ -0,0 +1,30 @@ +use crate::app::App; +use ratatui::{ + Frame, + layout::{Constraint, Direction, Layout}, + style::{Color, Style}, + widgets::{Block, Borders, Paragraph}, +}; + +pub fn render(f: &mut Frame, app: &App) { + let area = f.area(); + let chunks = Layout::default() + .direction(Direction::Vertical) + .margin(2) + .constraints([ + Constraint::Length(2), + Constraint::Length(3), + Constraint::Min(0), + ]) + .split(area); + + let explanation = Paragraph::new( + "We couldn't find your user_token. Please paste it below and press Enter to save it securely:", + ) + .style(Style::default().fg(Color::Yellow)); + f.render_widget(explanation, chunks[0]); + + let input_block = Paragraph::new(app.input_text.as_str()) + .block(Block::default().title(" User Token ").borders(Borders::ALL)); + f.render_widget(input_block, chunks[1]); +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs new file mode 100644 index 0000000..db8968f --- /dev/null +++ b/src/ui/mod.rs @@ -0,0 +1,8 @@ +mod error; +mod input_token; +mod server_list; +mod validating_token; + +mod render; + +pub use render::render; diff --git a/src/ui/render.rs b/src/ui/render.rs new file mode 100644 index 0000000..c58799c --- /dev/null +++ b/src/ui/render.rs @@ -0,0 +1,13 @@ +use crate::app::{App, AppState}; +use ratatui::Frame; + +use super::{error, input_token, server_list, validating_token}; + +pub fn render(f: &mut Frame, app: &App) { + match &app.state { + AppState::InputToken => input_token::render(f, app), + AppState::ValidatingToken => validating_token::render(f), + AppState::LoggedIn => server_list::render(f, app), + AppState::Error(message) => error::render(f, message), + } +} diff --git a/src/ui/server_list.rs b/src/ui/server_list.rs new file mode 100644 index 0000000..7cb4e79 --- /dev/null +++ b/src/ui/server_list.rs @@ -0,0 +1,13 @@ +use crate::app::App; +use ratatui::{ + Frame, + style::{Color, Style}, + widgets::{Block, Borders, Paragraph}, +}; + +pub fn render(f: &mut Frame, _app: &App) { + let msg = Paragraph::new("Server list is not yet implemented.") + .style(Style::default().fg(Color::Yellow)) + .block(Block::default().title(" Servers ").borders(Borders::ALL)); + f.render_widget(msg, f.area()); +} diff --git a/src/ui/validating_token.rs b/src/ui/validating_token.rs new file mode 100644 index 0000000..b093143 --- /dev/null +++ b/src/ui/validating_token.rs @@ -0,0 +1,16 @@ +use ratatui::{ + Frame, + style::{Color, Style}, + widgets::{Block, Borders, Paragraph}, +}; + +pub fn render(f: &mut Frame) { + let msg = Paragraph::new("Validating token with Stoat API...") + .style(Style::default().fg(Color::Cyan)) + .block( + Block::default() + .title(" Authenticating ") + .borders(Borders::ALL), + ); + f.render_widget(msg, f.area()); +}