use futures::stream::TryStreamExt; use kkdisp::{ component::{Plan, Widget}, theme::Color, token::Token, view::{Event, Key}, Action, }; use misskey::{ websocket::WebSocketClientBuilder, ClientExt, StreamingClientExt, WebSocketClient, }; use crate::{AuthDetail, Message}; pub enum LoginFocus { Hostname, Token, OK, } impl LoginFocus { fn ok(&self) -> bool { match self { LoginFocus::OK => true, _ => false, } } } pub struct LoginPrompt { hostname: String, token: String, error: Option, focus: LoginFocus, client: Option, } impl LoginPrompt { pub fn new() -> Self { Self { hostname: String::new(), token: String::new(), error: None, focus: LoginFocus::Hostname, client: None, } } pub fn plan(&self) -> Plan { Plan::start() .fill(vec![]) .fixed(8, vec![Widget::new(100, self.tokens())]) .fill(vec![]) } fn backspace(&mut self) { match self.focus { LoginFocus::Hostname => { self.hostname.pop(); } LoginFocus::Token => { self.token.pop(); } LoginFocus::OK => {} } } pub fn query(&mut self) -> Option { self.client.take() } pub async fn attempt(&mut self) { let hostname = self.hostname.trim_end_matches('/').to_string(); if hostname.len() == 0 { self.error = Some("empty hostname".into()); self.focus = LoginFocus::Hostname; return; } let token = self.token.clone(); if token.len() == 0 { self.error = Some("empty token".into()); self.focus = LoginFocus::Token; return; } let auth = AuthDetail { hostname, token }; match auth.normal().await { Ok(client) => { match client.recommended_users().try_next().await { Ok(next) => { eprintln!("next: {:#?}", next); self.client = Some(auth); } Err(err) => { self.error = Some(err.to_string()); } }; } Err(err) => { self.error = Some(err.to_string()); } }; } fn down(&mut self) { self.focus = match self.focus { LoginFocus::Hostname => LoginFocus::Token, LoginFocus::Token => LoginFocus::OK, LoginFocus::OK => LoginFocus::Hostname, }; } pub async fn update( &mut self, event: Event, ) -> Result { match event { Event::Link(lnk) => { if lnk == "ok" { self.attempt().await; Ok(Action::ReplaceAll(vec![self.plan()])) } else if lnk == "token" { self.focus = LoginFocus::Token; Ok(Action::ReplaceAll(vec![self.plan()])) } else if lnk == "hostname" { self.focus = LoginFocus::Hostname; Ok(Action::ReplaceAll(vec![self.plan()])) } else { Ok(Action::Nothing) } } Event::Input(input) => { match input { Key::Return => match self.focus { LoginFocus::Hostname => { self.focus = LoginFocus::Token; } LoginFocus::Token => { self.focus = LoginFocus::OK; } LoginFocus::OK => self.attempt().await, }, Key::Backspace => self.backspace(), Key::Tab => self.down(), Key::Up => { self.focus = match self.focus { LoginFocus::Hostname => LoginFocus::OK, LoginFocus::Token => LoginFocus::Hostname, LoginFocus::OK => LoginFocus::Token, }; } Key::Down => self.down(), Key::Char(c) => match self.focus { LoginFocus::Hostname => self.hostname.push(c), LoginFocus::Token => self.token.push(c), LoginFocus::OK => {} }, Key::Ctrl(c) => { if c == 'u' || c == 'U' { match self.focus { LoginFocus::Hostname => { self.hostname = String::new(); } LoginFocus::Token => { self.token = String::new() } LoginFocus::OK => {} } } } Key::Esc => { self.focus = LoginFocus::OK; } _ => { return Ok(Action::Nothing); } }; Ok(Action::ReplaceAll(vec![self.plan()])) } Event::Message(_) => todo!(), } } fn tokens(&self) -> Vec { const HOSTNAME_PROMPT: &str = "hostname: "; const TOKEN_PROMPT: &str = "token: "; let mut hostname = Token::text(self.hostname.clone()); let mut token = Token::text(self.token.clone()); let mut ok = Token::text("[ok]"); match self.focus { LoginFocus::Hostname => { hostname = hostname.bg(Color::BLUE) } LoginFocus::Token => token = token.bg(Color::BLUE), LoginFocus::OK => ok = ok.bg(Color::BLUE), }; vec![ Token::text("kkx cfg") .bg(Color::BLUE) .fg(Color::WHITE) .centered(), Token::End, match &self.error { Some(e) => Token::text(e) .fg(Color::RED) .pad_percent(80) .centered(), None => Token::End, }, hostname .string(HOSTNAME_PROMPT) .link("hostname") .pad_percent(80) .centered(), token .string(TOKEN_PROMPT) .link("token") .pad_percent(80) .centered(), ok.link("ok").centered(), ] } }