This commit is contained in:
Alex Emmet 2026-04-05 00:03:19 +02:00
commit 1ea0b97f6d
49 changed files with 869 additions and 289 deletions

View file

@ -1,4 +1,9 @@
use crossterm::event::{KeyCode, KeyEvent};
use iota_logger::{log, log_command, log_cv};
use iota_state::{ACTIVE_TASKS, RELOAD, SHUTDOWN};
use iota_storage::users::{user_manager, user_profile::UserProfile};
use iota_util::file_util;
use omikron_connector::omikron_connection::OMIKRON_CONNECTION;
use ratatui::{
Frame,
layout::Rect,
@ -9,19 +14,6 @@ use ratatui::{
use ttp_core::{CommunicationType, CommunicationValue};
use uuid::Uuid;
use crate::{
ACTIVE_TASKS, RELOAD, SHUTDOWN,
gui::{
elements::elements::{Element, InteractableElement, JoinableElement},
interaction_result::InteractionResult,
ui::FPS,
util::borders::draw_block_joins,
},
log, log_command, log_cv,
omikron::omikron_connection::OMIKRON_CONNECTION,
users::{user_manager, user_profile::UserProfile},
util::file_util,
};
use std::{
any::Any,
sync::{Arc, Mutex},
@ -29,6 +21,13 @@ use std::{
};
use tokio::time::Instant;
use crate::{
elements::elements::{Element, InteractableElement, JoinableElement},
interaction_result::InteractionResult,
ui::FPS,
util::borders::draw_block_joins,
};
pub struct ConsoleCard {
focused: bool,
pub title: String,
@ -425,7 +424,8 @@ pub async fn run_command(command: &str) {
ping(time).await;
}
["user", "add", username] => {
if let (Some(user), Some(_)) = user_manager::create_user(username).await {
if let (Some(user), Some(_)) = omikron_connector::user_ops::create_user(username).await
{
log!("Created user {}", user.user_id);
} else {
log!("Failed to create user");

View file

@ -3,7 +3,7 @@ use std::any::Any;
use crossterm::event::KeyEvent;
use ratatui::{Frame, layout::Rect, widgets::Borders};
use crate::gui::{interaction_result::InteractionResult, screens::screens::Screen};
use crate::{interaction_result::InteractionResult, screens::screens::Screen};
#[allow(unused)]
pub trait Element: Send + Sync + Any {

View file

@ -1,6 +1,7 @@
use std::{any::Any, sync::Arc};
use crossterm::event::KeyEvent;
use iota_state::APP_STATE;
use ratatui::{
Frame,
layout::Rect,
@ -12,13 +13,10 @@ use ratatui::{
};
use crate::{
APP_STATE,
gui::{
elements::elements::{Element, InteractableElement, JoinableElement},
interaction_result::InteractionResult,
ui::UI,
util::borders::draw_block_joins,
},
elements::elements::{Element, InteractableElement, JoinableElement},
interaction_result::InteractionResult,
ui::UI,
util::borders::draw_block_joins,
};
pub enum GRAPHS {

View file

@ -1,9 +1,9 @@
use crate::APP_STATE;
use crate::gui::elements::elements::{Element, InteractableElement, JoinableElement};
use crate::gui::interaction_result::InteractionResult;
use crate::gui::util::borders::draw_block_joins;
use crate::util::logger::PrintType;
use crate::app_state::APP_STATE;
use crate::elements::elements::{Element, InteractableElement, JoinableElement};
use crate::interaction_result::InteractionResult;
use crate::util::borders::draw_block_joins;
use crossterm::event::{KeyCode, KeyEvent};
use iota_logger::PrintType;
use ratatui::{
Frame,
layout::Rect,
@ -90,7 +90,24 @@ impl LogCard {
fn get_logs(&self) -> Vec<UiLogEntry> {
let state = APP_STATE.lock().unwrap();
state.get_logs().iter().cloned().collect()
state
.get_logs()
.iter()
.map(|e| UiLogEntry {
timestamp_ms: e.timestamp_ms,
sender: match e.sender.as_str() {
"Call" => PrintType::Call,
"Client" => PrintType::Client,
"Iota" => PrintType::Iota,
"Omikron" => PrintType::Omikron,
"Omega" => PrintType::Omega,
"Command" => PrintType::Command,
_ => PrintType::General,
},
message: e.message.clone(),
is_error: e.is_error,
})
.collect()
}
fn find_split_point(s: &str, max_width: usize) -> usize {

View file

@ -1,6 +1,6 @@
use crate::ui::{UI, UNIQUE};
use crate::{RELOAD, SHUTDOWN};
use crate::ui::UI;
use crossterm::event::{Event, KeyEvent, KeyEventKind, KeyModifiers, poll, read};
use iota_state::{RELOAD, SHUTDOWN, UNIQUE};
use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::time::Duration;

View file

@ -1,80 +0,0 @@
use crate::util::file_util::save_file;
use json::{self, JsonError, JsonValue};
pub fn create_languages() -> Result<(), JsonError> {
let mut frontend_messages = JsonValue::new_object();
let mut omikron_messages = JsonValue::new_object();
let mut button_texts = JsonValue::new_object();
let mut general_texts = JsonValue::new_object();
let mut debug_messages = JsonValue::new_object();
frontend_messages.insert("error", "An error occurred")?;
// FRONTEND
frontend_messages.insert("get_chats", "User {} is loading conversations")?;
frontend_messages.insert("message_get", "User {} is loading messages")?;
frontend_messages.insert("get_communities", "User {} is loading communities")?;
frontend_messages.insert("client_connected", "Client {} connected")?;
frontend_messages.insert("add_conversation", "User {} added {}")?;
frontend_messages.insert("message_send", "User {} sent a message")?;
// OMIKRON
omikron_messages.insert(
"identification_response",
"IOTA identified on Omikron, {} users!",
)?;
omikron_messages.insert(
"send_message_failed",
"Failed to send message to Omikron: {}",
)?;
omikron_messages.insert("connection_failed", "Failed to connect to Omikron: {}")?;
// BUTTONS
button_texts.insert("exit", "Exit")?;
// GENERAL
general_texts.insert("iota_id", "IOTA ID: {}-####-####-####-############")?;
general_texts.insert("user_id", "USER ID: {}")?;
general_texts.insert("user_ids", "USER IDS: {}")?;
general_texts.insert("user_load_failed", "Failed to load user data")?;
general_texts.insert("setup_completed", "Launched")?;
general_texts.insert(
"community_active",
"Communities active on ws://{}:{}/community/...",
)?;
general_texts.insert(
"community_start_error",
"Failed to start community socket on port {}!",
)?;
general_texts.insert(
"community_start_error_admin",
"Failed to start community socket on port {}! Run with admin privileges",
)?;
// DEBUG
debug_messages.insert("", "")?;
save_file(
"languages/en_INT",
"frontend.json",
&frontend_messages.to_string(),
);
save_file(
"languages/en_INT",
"omikron.json",
&omikron_messages.to_string(),
);
save_file(
"languages/en_INT",
"buttons.json",
&button_texts.to_string(),
);
save_file(
"languages/en_INT",
"debug.json",
&debug_messages.to_string(),
);
save_file(
"languages/en_INT",
"general.json",
&general_texts.to_string(),
);
Ok(())
}

View file

@ -1,105 +0,0 @@
use crate::util::file_util::{self};
use json::parse;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::sync::Mutex;
#[derive(Clone)]
pub struct LanguagePack {
language: HashMap<String, String>,
}
pub static LANGUAGE_PACK: Lazy<Mutex<LanguagePack>> =
Lazy::new(|| Mutex::new(LanguagePack::new("en_INT")));
#[allow(dead_code)]
pub fn get_language() -> LanguagePack {
LANGUAGE_PACK.lock().unwrap().clone()
}
#[allow(dead_code)]
pub fn get_languages() -> Vec<String> {
file_util::get_children("languages")
}
#[allow(dead_code)]
pub fn set_language(language: &str) {
LANGUAGE_PACK.lock().unwrap().language.clear();
LANGUAGE_PACK.lock().unwrap().load_language(language);
}
pub fn from_key(key: &str) -> String {
LANGUAGE_PACK
.lock()
.unwrap()
.get_translation(key)
.to_string()
}
pub fn format(key: &str, args: &[&str]) -> String {
let message = from_key(key);
let mut formatted = String::new();
let parts = message.split("{}");
for (i, part) in parts.enumerate() {
formatted.push_str(part);
if i < args.len() {
formatted.push_str(args[i]);
}
}
formatted
}
impl LanguagePack {
pub fn new(language: &str) -> Self {
let mut pack = LanguagePack {
language: HashMap::new(),
};
pack.load_language(language);
pack
}
pub fn load_language(&mut self, language: &str) {
let path = format!("languages/{}/", language);
let frontend_messages = file_util::load_file(&path, "frontend.json");
let frontend_messages = parse(&frontend_messages).unwrap();
for (key, value) in frontend_messages.entries() {
self.language
.insert(key.to_string(), value.as_str().unwrap().to_string());
}
let omikron_messages = file_util::load_file(&path, "omikron.json");
let omikron_messages = parse(&omikron_messages).unwrap();
for (key, value) in omikron_messages.entries() {
self.language
.insert(key.to_string(), value.as_str().unwrap().to_string());
}
let button_texts = file_util::load_file(&path, "buttons.json");
let button_texts = parse(&button_texts).unwrap();
for (key, value) in button_texts.entries() {
self.language
.insert(key.to_string(), value.as_str().unwrap().to_string());
}
let debug_messages = file_util::load_file(&path, "debug.json");
let debug_messages = parse(&debug_messages).unwrap();
for (key, value) in debug_messages.entries() {
self.language
.insert(key.to_string(), value.as_str().unwrap().to_string());
}
let general_messages = file_util::load_file(&path, "general.json");
let general_messages = parse(&general_messages).unwrap();
for (key, value) in general_messages.entries() {
self.language
.insert(key.to_string(), value.as_str().unwrap().to_string());
}
}
pub fn get_translation(&self, key: &str) -> String {
match self.language.get(key) {
Some(v) if !v.is_empty() => v.clone(),
_ => key.to_uppercase(),
}
}
}

View file

@ -1,2 +0,0 @@
pub mod language_creator;
pub mod language_manager;

View file

@ -2,8 +2,13 @@ use crate::{
interaction_result::InteractionResult,
screens::{md_viewer::FileViewer, screens::Screen},
ui::UI,
util::{
buttons::{checkbox, draw_buttons},
terms_focus::Focus,
},
};
use crossterm::event::{KeyCode, KeyEvent};
use iota_terms::{TermsType, get_link, get_terms};
use ratatui::{
Frame,
layout::{Alignment, Constraint, Direction, Layout, Rect},
@ -14,6 +19,13 @@ use ratatui::{
use std::{any::Any, pin::Pin, sync::Arc};
use tokio::sync::oneshot;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UserChoice {
Deny,
AcceptEULA,
AcceptAll,
}
pub struct TermsCheckerScreen {
ui: Arc<UI>,
sender: Option<oneshot::Sender<UserChoice>>,
@ -268,9 +280,9 @@ impl Screen for TermsCheckerScreen {
}
KeyCode::Char('o') | KeyCode::Char('O') => {
let terms_type = match self.focus {
Focus::Eula => Some(Type::EULA),
Focus::Tos => Some(Type::TOS),
Focus::Pp => Some(Type::PP),
Focus::Eula => Some(TermsType::EULA),
Focus::Tos => Some(TermsType::TOS),
Focus::Pp => Some(TermsType::PP),
_ => None,
};
if let Some(terms_type) = terms_type {
@ -288,15 +300,15 @@ impl Screen for TermsCheckerScreen {
}
KeyCode::Char('l') | KeyCode::Char('L') => match self.focus {
Focus::Eula => {
let _ = open::that(get_link(Type::EULA));
let _ = open::that(get_link(TermsType::EULA));
InteractionResult::Handled
}
Focus::Tos => {
let _ = open::that(get_link(Type::TOS));
let _ = open::that(get_link(TermsType::TOS));
InteractionResult::Handled
}
Focus::Pp => {
let _ = open::that(get_link(Type::PP));
let _ = open::that(get_link(TermsType::PP));
InteractionResult::Handled
}
_ => InteractionResult::Unhandled,

View file

@ -1,9 +1,15 @@
use crate::screens::terms_checker::UserChoice;
use crate::{
interaction_result::InteractionResult,
screens::{md_viewer::FileViewer, screens::Screen},
util::{
buttons::{checkbox, draw_buttons},
terms_focus::Focus,
},
};
use chrono::{Local, TimeZone, Utc};
use crossterm::event::{KeyCode, KeyEvent};
use iota_terms::{Doc, TermsType, get_newest_link, get_terms};
use ratatui::{
Frame,
layout::{Alignment, Constraint, Direction, Layout, Rect},
@ -14,6 +20,13 @@ use ratatui::{
use std::any::Any;
use tokio::sync::oneshot;
#[derive(Debug, Clone)]
pub enum UpdateDecision {
NoChange,
Future { newest: Doc },
Forced(Doc),
}
pub struct TermsUpdaterScreen {
sender: Option<oneshot::Sender<UserChoice>>,
@ -635,9 +648,9 @@ impl Screen for TermsUpdaterScreen {
},
KeyCode::Char('o') | KeyCode::Char('O') => {
let terms_type = match self.focus {
Focus::Eula => Some(Type::EULA),
Focus::Tos => Some(Type::TOS),
Focus::Pp => Some(Type::PP),
Focus::Eula => Some(TermsType::EULA),
Focus::Tos => Some(TermsType::TOS),
Focus::Pp => Some(TermsType::PP),
_ => None,
};
@ -655,15 +668,15 @@ impl Screen for TermsUpdaterScreen {
}
KeyCode::Char('l') | KeyCode::Char('L') => match self.focus {
Focus::Eula => {
let _ = open::that(get_newest_link(Type::EULA));
let _ = open::that(get_newest_link(TermsType::EULA));
InteractionResult::Handled
}
Focus::Tos => {
let _ = open::that(get_newest_link(Type::TOS));
let _ = open::that(get_newest_link(TermsType::TOS));
InteractionResult::Handled
}
Focus::Pp => {
let _ = open::that(get_newest_link(Type::PP));
let _ = open::that(get_newest_link(TermsType::PP));
InteractionResult::Handled
}
_ => InteractionResult::Unhandled,

View file

@ -3,6 +3,7 @@ use crate::{
screens::screens::Screen,
};
use crossterm::event::KeyEvent;
use iota_state::{ACTIVE_TASKS, SHUTDOWN, UNIQUE};
use once_cell::sync::Lazy;
use ratatui::{Terminal, backend::CrosstermBackend, init};
use std::{
@ -17,7 +18,6 @@ use std::{
use tokio::{sync::RwLock, time::Instant};
/// UI state and rendering
pub static UNIQUE: AtomicBool = AtomicBool::new(true);
pub static FPS: Lazy<RwLock<(f64, f64)>> = Lazy::new(|| RwLock::new((0.0, 0.0)));