some login prototyping

This commit is contained in:
emilis 2023-01-26 14:27:25 +00:00
parent 70168f9ed2
commit f8854de25c
6 changed files with 1916 additions and 89 deletions

1669
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -264,12 +264,10 @@ where
last_ctrlc = Some(Instant::now()); last_ctrlc = Some(Instant::now());
continue; continue;
} }
Some(Event::Input(e))
} else {
Some(Event::Input(e))
} }
Some(Event::Input(key.into()))
} }
_ => Some(Event::Input(e)), _ => None,
} }
} }
None => { None => {

View File

@ -1,6 +1,54 @@
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Event<T> { pub enum Event<T> {
Link(String), Link(String),
Input(termion::event::Event), Input(Key),
Message(T), Message(T),
} }
impl From<termion::event::Key> for Key {
fn from(value: termion::event::Key) -> Self {
match value {
termion::event::Key::Backspace => Self::Backspace,
termion::event::Key::Left => Self::Left,
termion::event::Key::Right => Self::Right,
termion::event::Key::Up => Self::Up,
termion::event::Key::Down => Self::Down,
termion::event::Key::Home => Self::Home,
termion::event::Key::End => Self::End,
termion::event::Key::PageUp => Self::PgUp,
termion::event::Key::PageDown => Self::PgDown,
termion::event::Key::BackTab => Self::Backtab,
termion::event::Key::Delete => Self::Delete,
termion::event::Key::Insert => Self::Insert,
termion::event::Key::F(f) => Self::F(f),
termion::event::Key::Char(c) => Self::Char(c),
termion::event::Key::Alt(c) => Self::Alt(c),
termion::event::Key::Ctrl(c) => Self::Ctrl(c),
termion::event::Key::Null => Self::Null,
termion::event::Key::Esc => Self::Esc,
_ => Self::Null,
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum Key {
Backspace,
Left,
Right,
Up,
Down,
Home,
End,
PgUp,
PgDown,
Backtab,
Delete,
Insert,
F(u8),
Char(char),
Alt(char),
Ctrl(char),
Null,
Esc,
}

View File

@ -12,3 +12,7 @@ kkdisp = { version = "0.1.0", path = "../kkdisp" }
[dependencies.tokio] [dependencies.tokio]
version = "1.24.2" version = "1.24.2"
features = ["full"] features = ["full"]
[dependencies.misskey]
version = "*"
features = ["websocket-client"]

167
kkx/src/login.rs Normal file
View File

@ -0,0 +1,167 @@
use kkdisp::{
component::{Plan, Widget},
theme::Color,
token::Token,
view::{Event, Key},
Action,
};
use crate::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<String>,
focus: LoginFocus,
}
impl From<&LoginPrompt> for Plan {
fn from(value: &LoginPrompt) -> Self {
Plan::start()
.fill(vec![])
.fixed(8, vec![Widget::new(100, value.tokens())])
.fill(vec![])
}
}
impl LoginPrompt {
pub fn new() -> Self {
Self {
hostname: String::new(),
token: String::new(),
error: None,
focus: LoginFocus::Hostname,
}
}
fn backspace(&mut self) {
match self.focus {
LoginFocus::Hostname => {
self.hostname.pop();
}
LoginFocus::Token => {
self.token.pop();
}
LoginFocus::OK => {}
}
}
pub fn attempt(&self) -> Result<(), anyhow::Error> {
todo!()
}
pub fn update(
&mut self,
event: Event<Message>,
) -> Result<Action, anyhow::Error> {
match event {
Event::Link(lnk) => {
if lnk == "ok" {
todo!()
} else {
Ok(Action::Nothing)
}
}
Event::Input(input) => {
match input {
Key::Backspace => self.backspace(),
Key::Up => {
self.focus = match self.focus {
LoginFocus::Hostname => LoginFocus::OK,
LoginFocus::Token => LoginFocus::Hostname,
LoginFocus::OK => LoginFocus::Token,
};
}
Key::Down => {
self.focus = match self.focus {
LoginFocus::Hostname => LoginFocus::Token,
LoginFocus::Token => LoginFocus::OK,
LoginFocus::OK => LoginFocus::Hostname,
};
}
Key::Char(c) => {
if c == '\n' {
if self.focus.ok() {
self.attempt()?;
}
} else if c == '\t' {
} else {
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).into()]))
}
Event::Message(_) => todo!(),
}
}
fn tokens(&self) -> Vec<Token> {
let mut hostname =
Token::text(self.hostname.clone()).padded(10);
let mut token = Token::text(self.token.clone()).padded(10);
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),
None => Token::End,
},
hostname.string("hostname: ").centered(),
token.string("token: ").centered(),
ok.link("ok").centered(),
]
}
}

View File

@ -5,98 +5,69 @@ use kkdisp::{
theme::Color, theme::Color,
token::Token, token::Token,
view::Event, view::Event,
Action, View, Action, PlanLayers, View,
}; };
use tokio::sync::mpsc::{self, Receiver}; use login::LoginPrompt;
use tokio::sync::mpsc::{self, Receiver, Sender};
mod login;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
kkdisp::run(App::default()).await.unwrap(); kkdisp::run(AppView::default()).await.unwrap();
std::process::exit(0); std::process::exit(0);
} }
pub struct App { pub enum Page {
states: Vec<Plan>, Login(LoginPrompt),
index: usize,
recv: Receiver<Token>,
} }
impl Default for App { impl Page {
fn init(&self) -> Result<PlanLayers, anyhow::Error> {
match self {
Page::Login(log) => Ok(vec![log.into()]),
}
}
}
pub struct AppView {
recv: Receiver<Message>,
page: Page,
}
impl Default for AppView {
fn default() -> Self { fn default() -> Self {
let (snd, recv) = mpsc::channel(256); let (snd, recv) = mpsc::channel(256);
tokio::spawn(async move { tokio::spawn(async move {
std::thread::sleep(Duration::from_secs(10)); // std::thread::sleep(Duration::from_secs(10));
snd.send(Token::text("this is an event").centered()) // snd.send(Token::text("this is an event").centered())
.await // .await
.unwrap(); // .unwrap();
}); });
Self { Self {
recv, recv,
index: 0, page: Page::Login(LoginPrompt::new()),
states: vec![
Plan::start().fill(vec![]).fixed(
4,
vec![Widget::new(
100,
vec![
Token::End,
Token::text("click me")
.bg(Color::BLUE)
.link("click"),
],
)],
),
Plan::start()
.fixed(4, vec![])
.fill(vec![Widget::scrolling(
100,
(1..=100)
.into_iter()
.map(|num| {
Token::text(format!(
"this is {}",
num
))
.padded(30)
.bg("#330033".try_into().unwrap())
.link(format!("num{}", num))
.centered()
})
.collect(),
)])
.fixed(4, vec![]),
],
} }
} }
} }
impl View for App { pub enum Message {}
type Message = Token;
impl View for AppView {
type Message = Message;
fn init( fn init(
&mut self, &mut self,
) -> std::result::Result<kkdisp::PlanLayers, anyhow::Error> { ) -> std::result::Result<kkdisp::PlanLayers, anyhow::Error> {
tokio::spawn(async { loop {} }); self.page.init()
Ok(vec![self.states[self.index].clone()])
} }
fn update( fn update(
&mut self, &mut self,
event: Event<Self::Message>, event: Event<Self::Message>,
) -> std::result::Result<Action, anyhow::Error> { ) -> std::result::Result<Action, anyhow::Error> {
match event { let page = &mut self.page;
Event::Link(lnk) => { match page {
eprintln!("recieved link: {}", lnk); Page::Login(login) => login.update(event),
self.index ^= 1;
Ok(Action::ReplaceAll(vec![
self.states[self.index].clone()
]))
}
Event::Message(tok) => {
Ok(Action::ReplaceAll(vec![Plan::start()
.fixed(3, vec![Widget::new(100, vec![tok])])]))
}
_ => Ok(Action::Nothing),
} }
} }