Skip to content

Commit

Permalink
feature: add basic console app using dioxus
Browse files Browse the repository at this point in the history
  • Loading branch information
shenek authored and Stepan Henek committed Sep 14, 2023
1 parent 4ebe553 commit 6d4e1a1
Show file tree
Hide file tree
Showing 9 changed files with 1,715 additions and 259 deletions.
1,555 changes: 1,296 additions & 259 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions ucelofka-data/src/identification/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ pub struct Identification {
pub name: String,
pub value: String,
}

impl ToString for Identification {
fn to_string(&self) -> String {
return format!("{}:{}", self.name, self.value)
}
}
2 changes: 2 additions & 0 deletions ucelofka/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ tera = "~1.6.1"
tokio = { version = "1", features = ["rt", "rt-multi-thread"]}
unic-langid = { version = "~0.9.0", features = ["macros"]}
ucelofka-data = { path="../ucelofka-data/" }
dioxus = "0.4"
dioxus-tui = "0.4"

[dev-dependencies]
assert_cmd = "~1.0.1"
Expand Down
21 changes: 21 additions & 0 deletions ucelofka/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod actions;
pub mod storage;
pub mod translations;
pub mod tui;
pub mod web;

use anyhow::{anyhow, Result};
Expand Down Expand Up @@ -338,6 +339,12 @@ fn prepare_ids_subcommand() -> Command {
.about("Print ids of all entities")
}

fn prepare_tui_subcommand() -> Command {
Command::new("tui")
.arg(prepare_data_dir())
.about("Start ucelofka's tui")
}

fn prepare_cmd() -> Command {
Command::new(crate_name!())
.author(crate_authors!())
Expand All @@ -353,6 +360,7 @@ fn prepare_cmd() -> Command {
.subcommand(prepare_web())
.subcommand(prepare_completions())
.subcommand(prepare_ids_subcommand())
.subcommand(prepare_tui_subcommand())
}

fn get_data_dir(matches: &ArgMatches) -> Result<PathBuf> {
Expand Down Expand Up @@ -600,6 +608,18 @@ fn process_ids(_cmd: Command, matches: &ArgMatches) -> Result<()> {
Ok(())
}

fn process_tui(_cmd: Command, matches: &ArgMatches) -> Result<()> {
let path = get_data_dir(matches)?;
dioxus_tui::launch_cfg_with_props(
tui::App,
tui::AppProps {
path
},
dioxus_tui::Config::new(),
);
Ok(())
}

fn main() -> Result<()> {
let cmd = prepare_cmd();

Expand All @@ -618,6 +638,7 @@ fn main() -> Result<()> {
process_completions(cmd.clone(), completions_matches)?
}
Some(("ids", ids_matches)) => process_ids(cmd.clone(), ids_matches)?,
Some(("tui", tui_matches)) => process_tui(cmd.clone(), tui_matches)?,
_ => exit_on_parse_error(cmd),
}
Ok(())
Expand Down
9 changes: 9 additions & 0 deletions ucelofka/src/tui.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#![allow(non_snake_case, deprecated)]

pub mod account;
pub mod app;
pub mod customer;

Check failure on line 5 in ucelofka/src/tui.rs

View workflow job for this annotation

GitHub Actions / Check

file not found for module `customer`

Check failure on line 5 in ucelofka/src/tui.rs

View workflow job for this annotation

GitHub Actions / Test Suite

file not found for module `customer`
pub mod list;
pub mod table;

pub use app::{App, AppProps};
86 changes: 86 additions & 0 deletions ucelofka/src/tui/account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#![allow(non_snake_case, deprecated)]

use dioxus::prelude::*;
use std::rc::Rc;
use ucelofka_data::account::Account;

use crate::actions::account::list;

use super::{app::UcelofkaTuiCfg, list::List, table::Table};

struct CurrentAccountPage(usize);

#[derive(Clone)]
enum SubPage {
Create,
Account(Account),
}

impl AsRef<str> for SubPage {
fn as_ref(&self) -> &str {
match self {
Self::Create => "<Create>",
Self::Account(account) => account.name.as_str(),
}
}
}

#[inline_props]
pub fn Accounts(cx: Scope) -> Element {
use_shared_state_provider(cx, || CurrentAccountPage(0));

let account_page = use_shared_state::<CurrentAccountPage>(cx).unwrap();

let cfg = use_shared_state::<UcelofkaTuiCfg>(cx).unwrap();
let accounts = list(cfg.read().path.as_path()).unwrap().accounts;
let items: Vec<SubPage> = vec![SubPage::Create]
.into_iter()
.chain(accounts.iter().map(|e| SubPage::Account(e.clone())))
.collect();

let items_str: Vec<String> = items.iter().map(|e| e.as_ref().to_string()).collect();
let selected_idx = account_page.read().0;
let selected_account = if selected_idx == 0 {
None
} else {
Some(accounts[selected_idx - 1].clone())
};

cx.render(rsx! {
List {
tabindex: 0,
width: "20%",
items: Rc::new(items_str),
dot: "❱",
idx: account_page.read().0.into(),
onindexupdate: move |i: usize| {
account_page.write().0 = i.into();
}
}
div {
width: "60%",
border_width: "1px",
height: "100%",
justify_content: "center",
if let Some(account) = selected_account {
rsx! {
Table {
width: "100%",
items: vec![
("Name:", account.name),
("Bank Name:", account.bank_name),
("Account Name:", account.account_name),
("Account number:", account.account_number),
("IBAN:", account.IBAN),
("BIC:", account.BIC),
("Currency:", account.currency),

]
}
}
} else {
rsx! {span { "TODO CREATE FORM"}}
}
}
})
}
164 changes: 164 additions & 0 deletions ucelofka/src/tui/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
#![allow(non_snake_case, deprecated)]

use dioxus::{
events::{KeyCode, KeyboardEvent},
prelude::*,
};
use dioxus_tui::TuiContext;
use std::{rc::Rc, path::PathBuf};

use super::{
list::List,
account::Accounts,
customer::Customers,

Check failure on line 13 in ucelofka/src/tui/app.rs

View workflow job for this annotation

GitHub Actions / Check

unresolved import `super::customer::Customers`

Check failure on line 13 in ucelofka/src/tui/app.rs

View workflow job for this annotation

GitHub Actions / Test Suite

unresolved import `super::customer::Customers`
};

struct CurrentPage(Page);

pub struct UcelofkaTuiCfg {
pub path: PathBuf
}

#[derive(Clone, Copy)]
enum Page {
Accounts,
Customers,
Entries,
Identities,
Invoices,
}
impl AsRef<str> for Page {
fn as_ref(&self) -> &str {
match self {
Self::Accounts => "Accounts",
Self::Customers => "Customers",
Self::Entries => "Entries",
Self::Identities => "Identities",
Self::Invoices => "Invoices",
}
}
}

impl From<usize> for Page {
fn from(page: usize) -> Self {
match page {
0 => Self::Accounts,
1 => Self::Customers,
2 => Self::Entries,
3 => Self::Identities,
4 => Self::Invoices,
_ => unreachable!(),
}
}
}

impl Into<usize> for Page {
fn into(self) -> usize {
match self {
Self::Accounts => 0,
Self::Customers => 1,
Self::Entries => 2,
Self::Identities => 3,
Self::Invoices => 4,
}
}
}

#[inline_props]
pub fn App(cx: Scope, path: PathBuf) -> Element {
let tui_ctx: TuiContext = cx.consume_context().unwrap();

use_shared_state_provider(cx, || CurrentPage(Page::Accounts));
use_shared_state_provider(cx, || UcelofkaTuiCfg {path: path.clone()});

let page = use_shared_state::<CurrentPage>(cx).unwrap();

let items = Rc::new(vec![
Page::Accounts.as_ref(),
Page::Customers.as_ref(),
Page::Entries.as_ref(),
Page::Identities.as_ref(),
Page::Invoices.as_ref(),
]);

let header_element = use_state(cx, || None::<Rc<MountedData>>);
let onkeydown = move |k: KeyboardEvent| {
match k.key_code {
KeyCode::Q => {
tui_ctx.quit();
}
KeyCode::P => {
if let Some(he) = header_element.as_ref() {
let inner = he.clone();
cx.spawn(async move {
let _ = inner.set_focus(true).await;
});
}
}
_ => {}
}
};

let onmounted = move |ctx: MountedEvent| {
header_element.set(Some(ctx.inner().clone()));
let inner = ctx.inner().clone();
cx.spawn(async move {
let _ = inner.set_focus(true).await;
});
};

cx.render(rsx! {
div {
width: "100%",
height: "100%",
justify_content: "center",
align_items: "center",
onkeydown: onkeydown,
onmounted: onmounted,
tabindex: -1,
List {
tabindex: 0,
width: "20%",
items: items,
dot: "❱",
idx: page.read().0.into(),
onindexupdate: move |i: usize| {
page.write().0 = i.into();
}
}
match page.read().0 {
Page::Accounts => {
rsx! {
Accounts {}
}

}
Page::Customers => {
rsx! {
Customers {}
}

}
_ => rsx! {
div { background_color: "green", width: "80%", "{page.read().0.as_ref()} is under construction" }
}
}
}
})
}
// div { background_color: "green", width: "50%", "Hello worlds! {page}" }
// input {
// background_color: "blue",
// r#type: "checkbox",
// value: "good",
// height: "1px",
// checked: "{bg_green}",
// width: "2px",
// oninput: move |data| {
// if &data.value == "good" {
// bg_green.set(true);
// } else {
// bg_green.set(false);
// }
// }
// }
Loading

0 comments on commit 6d4e1a1

Please sign in to comment.