UI Optimizations, Basic Multi Panel system
This commit is contained in:
parent
befc35bbfd
commit
beea361971
14 changed files with 608 additions and 618 deletions
|
|
@ -23,20 +23,17 @@ pub struct AuthUser {
|
||||||
pub sub_end: i32,
|
pub sub_end: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AuthConnector;
|
fn client() -> Client {
|
||||||
|
|
||||||
impl AuthConnector {
|
|
||||||
fn client() -> Client {
|
|
||||||
Client::builder()
|
Client::builder()
|
||||||
.connect_timeout(Duration::from_secs(100))
|
.connect_timeout(Duration::from_secs(100))
|
||||||
.timeout(Duration::from_secs(150))
|
.timeout(Duration::from_secs(150))
|
||||||
.build()
|
.build()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn unregister_user(user_id: Uuid, reset_token: &str) -> Option<bool> {
|
pub async fn unregister_user(user_id: Uuid, reset_token: &str) -> Option<bool> {
|
||||||
let url = format!("https:/auth.tensamin.methanium.net/api/delete/{}/", user_id);
|
let url = format!("https:/auth.tensamin.methanium.net/api/delete/{}/", user_id);
|
||||||
let client = Self::client();
|
let client = client();
|
||||||
|
|
||||||
let mut payload = JsonValue::new_object();
|
let mut payload = JsonValue::new_object();
|
||||||
payload["reset_token"] = reset_token.into();
|
payload["reset_token"] = reset_token.into();
|
||||||
|
|
@ -51,14 +48,14 @@ impl AuthConnector {
|
||||||
let json = res.text().await.ok()?;
|
let json = res.text().await.ok()?;
|
||||||
let cv = CommunicationValue::from_json(&json);
|
let cv = CommunicationValue::from_json(&json);
|
||||||
Option::from(cv.is_type(CommunicationType::success))
|
Option::from(cv.is_type(CommunicationType::success))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_uuid(username: &str) -> Option<Uuid> {
|
pub async fn get_uuid(username: &str) -> Option<Uuid> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"https://auth.tensamin.methanium.net/api/get/uuid/{}/",
|
"https://auth.tensamin.methanium.net/api/get/uuid/{}/",
|
||||||
username
|
username
|
||||||
);
|
);
|
||||||
let client = Self::client();
|
let client = client();
|
||||||
let res = client.get(&url).send().await.ok()?;
|
let res = client.get(&url).send().await.ok()?;
|
||||||
let json = res.text().await.ok()?;
|
let json = res.text().await.ok()?;
|
||||||
let mut cv = CommunicationValue::from_json(&json);
|
let mut cv = CommunicationValue::from_json(&json);
|
||||||
|
|
@ -66,11 +63,11 @@ impl AuthConnector {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Uuid::parse_str(&*cv.get_data(DataTypes::user_id).unwrap().to_string()).ok()
|
Uuid::parse_str(&*cv.get_data(DataTypes::user_id).unwrap().to_string()).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_user(user_id: Uuid) -> Option<AuthUser> {
|
pub async fn get_user(user_id: Uuid) -> Option<AuthUser> {
|
||||||
let url = format!("https://auth.tensamin.methanium.net/api/get/{}/", user_id);
|
let url = format!("https://auth.tensamin.methanium.net/api/get/{}/", user_id);
|
||||||
let client = Self::client();
|
let client = client();
|
||||||
let res = client.get(&url).send().await.ok()?;
|
let res = client.get(&url).send().await.ok()?;
|
||||||
let json = res.text().await.ok()?;
|
let json = res.text().await.ok()?;
|
||||||
|
|
||||||
|
|
@ -105,21 +102,21 @@ impl AuthConnector {
|
||||||
.parse::<i32>()
|
.parse::<i32>()
|
||||||
.unwrap_or(-1),
|
.unwrap_or(-1),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_register() -> Option<Uuid> {
|
pub async fn get_register() -> Option<Uuid> {
|
||||||
let url = "https://auth.tensamin.methanium.net/api/register/init/".to_string();
|
let url = "https://auth.tensamin.methanium.net/api/register/init/".to_string();
|
||||||
let client = Self::client();
|
let client = client();
|
||||||
let res = client.get(&url).send().await.ok()?;
|
let res = client.get(&url).send().await.ok()?;
|
||||||
let json = res.text().await.ok()?;
|
let json = res.text().await.ok()?;
|
||||||
|
|
||||||
let mut cv = CommunicationValue::from_json(&json);
|
let mut cv = CommunicationValue::from_json(&json);
|
||||||
Uuid::parse_str(&*cv.get_data(DataTypes::user_id).unwrap().to_string()).ok()
|
Uuid::parse_str(&*cv.get_data(DataTypes::user_id).unwrap().to_string()).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn complete_register(user_profile: &UserProfile, iota_id: &str) -> bool {
|
pub async fn complete_register(user_profile: &UserProfile, iota_id: &str) -> bool {
|
||||||
let url = "https://auth.tensamin.methanium.net/api/register/complete/";
|
let url = "https://auth.tensamin.methanium.net/api/register/complete/";
|
||||||
let client = Self::client();
|
let client = client();
|
||||||
|
|
||||||
let mut payload = JsonValue::new_object();
|
let mut payload = JsonValue::new_object();
|
||||||
payload["uuid"] = user_profile.user_id.to_string().into();
|
payload["uuid"] = user_profile.user_id.to_string().into();
|
||||||
|
|
@ -142,14 +139,14 @@ impl AuthConnector {
|
||||||
println!("Response body:\n{}", body);
|
println!("Response body:\n{}", body);
|
||||||
|
|
||||||
CommunicationValue::from_json(&body).is_type(CommunicationType::success)
|
CommunicationValue::from_json(&body).is_type(CommunicationType::success)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn migrate_user(user_profile: &mut UserProfile) -> bool {
|
pub async fn migrate_user(user_profile: &mut UserProfile) -> bool {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"https://auth.tensamin.methanium.net/api/change/iota-id/{}",
|
"https://auth.tensamin.methanium.net/api/change/iota-id/{}",
|
||||||
user_profile.user_id
|
user_profile.user_id
|
||||||
);
|
);
|
||||||
let client = Self::client();
|
let client = client();
|
||||||
|
|
||||||
let mut payload = JsonValue::new_object();
|
let mut payload = JsonValue::new_object();
|
||||||
payload["iota_id"] = JsonValue::String(CONFIG.lock().unwrap().get_iota_id().to_string());
|
payload["iota_id"] = JsonValue::String(CONFIG.lock().unwrap().get_iota_id().to_string());
|
||||||
|
|
@ -164,18 +161,17 @@ impl AuthConnector {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(resp) => Self::handle_response(resp).await,
|
Ok(resp) => handle_response(resp).await,
|
||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_response(resp: Response) -> bool {
|
async fn handle_response(resp: Response) -> bool {
|
||||||
match resp.text().await {
|
match resp.text().await {
|
||||||
Ok(text) => {
|
Ok(text) => {
|
||||||
let cv = CommunicationValue::from_json(&text);
|
let cv = CommunicationValue::from_json(&text.to_string());
|
||||||
cv.comm_type == CommunicationType::success
|
cv.comm_type == CommunicationType::success
|
||||||
}
|
}
|
||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ pub enum DataTypes {
|
||||||
accepted,
|
accepted,
|
||||||
accepted_profiles,
|
accepted_profiles,
|
||||||
denied_profiles,
|
denied_profiles,
|
||||||
message_content,
|
content,
|
||||||
message_chunk,
|
messages,
|
||||||
send_time,
|
send_time,
|
||||||
get_time,
|
get_time,
|
||||||
get_variant,
|
get_variant,
|
||||||
|
|
@ -50,8 +50,8 @@ pub enum DataTypes {
|
||||||
ping_clients,
|
ping_clients,
|
||||||
matches,
|
matches,
|
||||||
omikron,
|
omikron,
|
||||||
loaded_messages,
|
offset,
|
||||||
message_amount,
|
amount,
|
||||||
position,
|
position,
|
||||||
name,
|
name,
|
||||||
path,
|
path,
|
||||||
|
|
@ -97,8 +97,8 @@ impl DataTypes {
|
||||||
"accepted" => DataTypes::accepted,
|
"accepted" => DataTypes::accepted,
|
||||||
"acceptedprofiles" => DataTypes::accepted_profiles,
|
"acceptedprofiles" => DataTypes::accepted_profiles,
|
||||||
"deniedprofiles" => DataTypes::denied_profiles,
|
"deniedprofiles" => DataTypes::denied_profiles,
|
||||||
"messagecontent" => DataTypes::message_content,
|
"content" => DataTypes::content,
|
||||||
"messagechunk" => DataTypes::message_chunk,
|
"messages" => DataTypes::messages,
|
||||||
"sendtime" => DataTypes::send_time,
|
"sendtime" => DataTypes::send_time,
|
||||||
"gettime" => DataTypes::get_time,
|
"gettime" => DataTypes::get_time,
|
||||||
"getvariant" => DataTypes::get_variant,
|
"getvariant" => DataTypes::get_variant,
|
||||||
|
|
@ -123,8 +123,8 @@ impl DataTypes {
|
||||||
"pingclients" => DataTypes::ping_clients,
|
"pingclients" => DataTypes::ping_clients,
|
||||||
"matches" => DataTypes::matches,
|
"matches" => DataTypes::matches,
|
||||||
"omikron" => DataTypes::omikron,
|
"omikron" => DataTypes::omikron,
|
||||||
"loadedmessages" => DataTypes::loaded_messages,
|
"offset" => DataTypes::offset,
|
||||||
"messageamount" => DataTypes::message_amount,
|
"amount" => DataTypes::amount,
|
||||||
"position" => DataTypes::position,
|
"position" => DataTypes::position,
|
||||||
"name" => DataTypes::name,
|
"name" => DataTypes::name,
|
||||||
"path" => DataTypes::path,
|
"path" => DataTypes::path,
|
||||||
|
|
@ -158,10 +158,11 @@ pub enum CommunicationType {
|
||||||
error,
|
error,
|
||||||
success,
|
success,
|
||||||
message,
|
message,
|
||||||
|
message_send,
|
||||||
message_live,
|
message_live,
|
||||||
message_other_iota,
|
message_other_iota,
|
||||||
message_chunk,
|
message_chunk,
|
||||||
message_get,
|
messages_get,
|
||||||
change_confirm,
|
change_confirm,
|
||||||
confirm_receive,
|
confirm_receive,
|
||||||
confirm_read,
|
confirm_read,
|
||||||
|
|
@ -210,7 +211,8 @@ impl CommunicationType {
|
||||||
"messagelive" => CommunicationType::message_live,
|
"messagelive" => CommunicationType::message_live,
|
||||||
"messageotheriota" => CommunicationType::message_other_iota,
|
"messageotheriota" => CommunicationType::message_other_iota,
|
||||||
"messagechunk" => CommunicationType::message_chunk,
|
"messagechunk" => CommunicationType::message_chunk,
|
||||||
"messageget" => CommunicationType::message_get,
|
"messagesget" => CommunicationType::messages_get,
|
||||||
|
"message_send" => CommunicationType::message_send,
|
||||||
"changeconfirm" => CommunicationType::change_confirm,
|
"changeconfirm" => CommunicationType::change_confirm,
|
||||||
"confirmreceive" => CommunicationType::confirm_receive,
|
"confirmreceive" => CommunicationType::confirm_receive,
|
||||||
"confirmread" => CommunicationType::confirm_read,
|
"confirmread" => CommunicationType::confirm_read,
|
||||||
|
|
@ -348,11 +350,10 @@ impl CommunicationValue {
|
||||||
let mut data = HashMap::new();
|
let mut data = HashMap::new();
|
||||||
if parsed["data"].is_object() {
|
if parsed["data"].is_object() {
|
||||||
for (k, v) in parsed["data"].entries() {
|
for (k, v) in parsed["data"].entries() {
|
||||||
if let Some(val) = v.as_str() {
|
|
||||||
data.insert(DataTypes::parse(k.to_string()), v.clone());
|
data.insert(DataTypes::parse(k.to_string()), v.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Self {
|
Self {
|
||||||
id: uuid,
|
id: uuid,
|
||||||
comm_type,
|
comm_type,
|
||||||
|
|
@ -389,13 +390,8 @@ impl CommunicationValue {
|
||||||
.with_receiver(receiver.unwrap())
|
.with_receiver(receiver.unwrap())
|
||||||
.add_data(DataTypes::send_time, JsonValue::String(now_ms.to_string()))
|
.add_data(DataTypes::send_time, JsonValue::String(now_ms.to_string()))
|
||||||
.add_data(
|
.add_data(
|
||||||
DataTypes::message_content,
|
DataTypes::content,
|
||||||
JsonValue::String(
|
JsonValue::String(original.get_data(DataTypes::content).unwrap().to_string()),
|
||||||
original
|
|
||||||
.get_data(DataTypes::message_content)
|
|
||||||
.unwrap()
|
|
||||||
.to_string(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// include sender_id if the original had one
|
// include sender_id if the original had one
|
||||||
|
|
|
||||||
70
src/gui/app_state.rs
Normal file
70
src/gui/app_state.rs
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AppState {
|
||||||
|
pub logs: VecDeque<String>,
|
||||||
|
pub cpu: Vec<(f64, f64)>,
|
||||||
|
pub ram: Vec<(f64, f64)>,
|
||||||
|
pub ping: Vec<(f64, f64)>,
|
||||||
|
pub net_up: Vec<(f64, f64)>,
|
||||||
|
pub net_down: Vec<(f64, f64)>,
|
||||||
|
pub sys_info: String,
|
||||||
|
}
|
||||||
|
const MAX_POINTS: usize = 1000;
|
||||||
|
const MAX_LOGS: usize = 100;
|
||||||
|
|
||||||
|
impl AppState {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
logs: VecDeque::new(),
|
||||||
|
cpu: Vec::new(),
|
||||||
|
ram: Vec::new(),
|
||||||
|
ping: Vec::new(),
|
||||||
|
net_up: Vec::new(),
|
||||||
|
net_down: Vec::new(),
|
||||||
|
sys_info: String::from("Loading..."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_log(&mut self, msg: String) {
|
||||||
|
if self.logs.len() >= MAX_LOGS {
|
||||||
|
self.logs.pop_front();
|
||||||
|
}
|
||||||
|
self.logs.push_back(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_cpu(&mut self, pt: (f64, f64)) {
|
||||||
|
self.cpu.push(pt);
|
||||||
|
if self.cpu.len() > MAX_POINTS {
|
||||||
|
self.cpu.remove(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_ram(&mut self, pt: (f64, f64)) {
|
||||||
|
self.ram.push(pt);
|
||||||
|
if self.ram.len() > MAX_POINTS {
|
||||||
|
self.ram.remove(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_ping_val(&mut self, pt: f64) {
|
||||||
|
self.ping.push((self.ping.len() as f64, pt));
|
||||||
|
if self.ping.len() > MAX_POINTS {
|
||||||
|
self.ping.remove(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_net_up(&mut self, pt: (f64, f64)) {
|
||||||
|
self.net_up.push(pt);
|
||||||
|
if self.net_up.len() > MAX_POINTS {
|
||||||
|
self.net_up.remove(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_net_down(&mut self, pt: (f64, f64)) {
|
||||||
|
self.net_down.push(pt);
|
||||||
|
if self.net_down.len() > MAX_POINTS {
|
||||||
|
self.net_down.remove(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,16 @@
|
||||||
use crate::APP_STATE;
|
use crate::APP_STATE;
|
||||||
|
use crate::gui::ratatui_interface::TERMINAL;
|
||||||
use crate::gui::widgets::betterblock::draw_block_joins;
|
use crate::gui::widgets::betterblock::draw_block_joins;
|
||||||
use crate::langu::language_manager::from_key;
|
use crate::langu::language_manager::from_key;
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
ExecutableCommand,
|
ExecutableCommand, execute,
|
||||||
terminal::{LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
terminal::{
|
||||||
|
Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode,
|
||||||
|
enable_raw_mode,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use futures_util::lock::Mutex;
|
||||||
use ratatui::widgets::{
|
use ratatui::widgets::{
|
||||||
BorderType,
|
BorderType,
|
||||||
canvas::{Canvas, Line},
|
canvas::{Canvas, Line},
|
||||||
|
|
@ -13,83 +19,20 @@ use ratatui::{
|
||||||
Terminal,
|
Terminal,
|
||||||
backend::CrosstermBackend,
|
backend::CrosstermBackend,
|
||||||
layout::{Constraint, Direction, Layout},
|
layout::{Constraint, Direction, Layout},
|
||||||
style::Color,
|
style::{Color, Style},
|
||||||
symbols,
|
symbols,
|
||||||
widgets::{Block, Borders, List, ListItem, Paragraph},
|
widgets::{Block, Borders, List, ListItem, Paragraph},
|
||||||
};
|
};
|
||||||
use std::{collections::VecDeque, io::stdout, process::Command, thread, time::Duration};
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
io::{Stdout, Write, stdout},
|
||||||
|
process::Command,
|
||||||
|
sync::{Arc, LazyLock},
|
||||||
|
thread,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
use sysinfo::{RefreshKind, System};
|
use sysinfo::{RefreshKind, System};
|
||||||
|
|
||||||
const MAX_POINTS: usize = 1000;
|
|
||||||
const MAX_LOGS: usize = 100;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct AppState {
|
|
||||||
logs: VecDeque<String>,
|
|
||||||
cpu: Vec<(f64, f64)>,
|
|
||||||
ram: Vec<(f64, f64)>,
|
|
||||||
ping: Vec<(f64, f64)>,
|
|
||||||
net_up: Vec<(f64, f64)>,
|
|
||||||
net_down: Vec<(f64, f64)>,
|
|
||||||
sys_info: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppState {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
logs: VecDeque::new(),
|
|
||||||
cpu: Vec::new(),
|
|
||||||
ram: Vec::new(),
|
|
||||||
ping: Vec::new(),
|
|
||||||
net_up: Vec::new(),
|
|
||||||
net_down: Vec::new(),
|
|
||||||
sys_info: String::from("Loading..."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_log(&mut self, msg: String) {
|
|
||||||
if self.logs.len() >= MAX_LOGS {
|
|
||||||
self.logs.pop_front();
|
|
||||||
}
|
|
||||||
self.logs.push_back(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_cpu(&mut self, pt: (f64, f64)) {
|
|
||||||
self.cpu.push(pt);
|
|
||||||
if self.cpu.len() > MAX_POINTS {
|
|
||||||
self.cpu.remove(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_ram(&mut self, pt: (f64, f64)) {
|
|
||||||
self.ram.push(pt);
|
|
||||||
if self.ram.len() > MAX_POINTS {
|
|
||||||
self.ram.remove(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_ping_val(&mut self, pt: f64) {
|
|
||||||
self.ping.push((self.ping.len() as f64, pt));
|
|
||||||
if self.ping.len() > MAX_POINTS {
|
|
||||||
self.ping.remove(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_net_up(&mut self, pt: (f64, f64)) {
|
|
||||||
self.net_up.push(pt);
|
|
||||||
if self.net_up.len() > MAX_POINTS {
|
|
||||||
self.net_up.remove(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_net_down(&mut self, pt: (f64, f64)) {
|
|
||||||
self.net_down.push(pt);
|
|
||||||
if self.net_down.len() > MAX_POINTS {
|
|
||||||
self.net_down.remove(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn log_message_trans(key: impl Into<String>) {
|
pub fn log_message_trans(key: impl Into<String>) {
|
||||||
APP_STATE.lock().unwrap().push_log(from_key(&key.into()));
|
APP_STATE.lock().unwrap().push_log(from_key(&key.into()));
|
||||||
}
|
}
|
||||||
|
|
@ -146,36 +89,7 @@ fn downsample_to_fit_width(data: &[(f64, f64)], width: u16) -> Vec<(f64, f64)> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn measure_ping_ms(host: &str) -> Option<f64> {
|
|
||||||
// adapt for Windows
|
|
||||||
let output = Command::new("ping")
|
|
||||||
.arg("-c")
|
|
||||||
.arg("1")
|
|
||||||
.arg("-W")
|
|
||||||
.arg("1")
|
|
||||||
.arg(host)
|
|
||||||
.output()
|
|
||||||
.ok()?;
|
|
||||||
let out = String::from_utf8_lossy(&output.stdout);
|
|
||||||
for line in out.lines() {
|
|
||||||
if line.contains("time=") {
|
|
||||||
if let Some(idx) = line.find("time=") {
|
|
||||||
let substr = &line[idx + 5..];
|
|
||||||
if let Some(end) = substr.find(" ms") {
|
|
||||||
let num = &substr[..end];
|
|
||||||
if let Ok(f) = num.parse::<f64>() {
|
|
||||||
return Some(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn setup() {
|
pub fn setup() {
|
||||||
enable_raw_mode().unwrap();
|
|
||||||
|
|
||||||
// Start a background thread to sample metrics
|
// Start a background thread to sample metrics
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut sys = System::new_with_specifics(RefreshKind::new());
|
let mut sys = System::new_with_specifics(RefreshKind::new());
|
||||||
|
|
@ -222,19 +136,16 @@ pub fn setup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
counter += 1.0;
|
counter += 1.0;
|
||||||
thread::sleep(Duration::from_millis(250));
|
thread::sleep(Duration::from_millis(1000));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// UI rendering loop in a separate thread
|
pub fn render() {
|
||||||
thread::spawn(move || {
|
tokio::spawn(async move {
|
||||||
let stdout = stdout();
|
TERMINAL
|
||||||
let mut terminal = Terminal::new(CrosstermBackend::new(stdout)).unwrap();
|
.lock()
|
||||||
|
.await
|
||||||
enable_raw_mode().unwrap();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
terminal
|
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
let sys = System::new_all();
|
let sys = System::new_all();
|
||||||
|
|
||||||
|
|
@ -293,11 +204,11 @@ pub fn setup() {
|
||||||
let st = APP_STATE.lock().unwrap();
|
let st = APP_STATE.lock().unwrap();
|
||||||
let w = grid_chunks[0].width.saturating_sub(2);
|
let w = grid_chunks[0].width.saturating_sub(2);
|
||||||
|
|
||||||
let ping_ds = downsample_to_fit_width(&smooth_data(&st.ping, 3), w + 1);
|
let ping_ds = downsample_to_fit_width(&smooth_data(&st.ping, 3), w + 4);
|
||||||
let cpu_ds = downsample_to_fit_width(&smooth_data(&st.cpu, 3), w);
|
let cpu_ds = downsample_to_fit_width(&smooth_data(&st.cpu, 3), w);
|
||||||
let ram_ds = downsample_to_fit_width(&smooth_data(&st.ram, 3), w);
|
let ram_ds = downsample_to_fit_width(&smooth_data(&st.ram, 3), w);
|
||||||
let down_ds = downsample_to_fit_width(&st.net_down, w + 1);
|
let down_ds = downsample_to_fit_width(&st.net_down, w + 4);
|
||||||
let up_ds = downsample_to_fit_width(&st.net_up, w + 1);
|
let up_ds = downsample_to_fit_width(&st.net_up, w + 4);
|
||||||
|
|
||||||
// ---- Render CPU ----
|
// ---- Render CPU ----
|
||||||
{
|
{
|
||||||
|
|
@ -423,11 +334,12 @@ pub fn setup() {
|
||||||
max_sum = max_sum.max(up + down);
|
max_sum = max_sum.max(up + down);
|
||||||
}
|
}
|
||||||
|
|
||||||
let canvas =
|
let canvas = Canvas::default()
|
||||||
Canvas::default()
|
.block(
|
||||||
.block(Block::default().title("Network Up/Down").borders(
|
Block::default()
|
||||||
Borders::TOP.union(Borders::RIGHT).union(Borders::BOTTOM),
|
.title("Network Up/Down")
|
||||||
))
|
.borders(Borders::TOP.union(Borders::RIGHT).union(Borders::BOTTOM)),
|
||||||
|
)
|
||||||
.x_bounds([min_x, max_x])
|
.x_bounds([min_x, max_x])
|
||||||
.y_bounds([0.0, max_sum])
|
.y_bounds([0.0, max_sum])
|
||||||
.paint(|ctx| {
|
.paint(|ctx| {
|
||||||
|
|
@ -467,11 +379,5 @@ pub fn setup() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(100));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
// Clean up terminal before exit
|
|
||||||
disable_raw_mode().unwrap();
|
|
||||||
stdout().execute(LeaveAlternateScreen).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
pub mod app_state;
|
||||||
pub mod log_panel;
|
pub mod log_panel;
|
||||||
|
pub mod nav_bar;
|
||||||
pub mod ratatui_interface;
|
pub mod ratatui_interface;
|
||||||
pub mod widgets {
|
pub mod widgets {
|
||||||
pub mod betterblock;
|
pub mod betterblock;
|
||||||
|
|
|
||||||
34
src/gui/nav_bar.rs
Normal file
34
src/gui/nav_bar.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
use crate::gui::log_panel;
|
||||||
|
|
||||||
|
pub struct Screen {
|
||||||
|
pub title: String,
|
||||||
|
pub render: Box<dyn Fn() + Send + Sync + 'static>,
|
||||||
|
}
|
||||||
|
impl Screen {
|
||||||
|
pub fn new(title: &str, render: Box<dyn Fn() + Send + Sync + 'static>) -> Self {
|
||||||
|
Screen {
|
||||||
|
title: String::from(title),
|
||||||
|
render,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn renderf(&self) {
|
||||||
|
(self.render)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub struct NavBar {
|
||||||
|
pub current_screen: Screen,
|
||||||
|
pub screens: Vec<Screen>,
|
||||||
|
}
|
||||||
|
impl NavBar {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
NavBar {
|
||||||
|
current_screen: Screen {
|
||||||
|
title: String::from("Main"),
|
||||||
|
render: Box::new(|| log_panel::render()),
|
||||||
|
},
|
||||||
|
screens: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,58 +1,45 @@
|
||||||
|
use std::{
|
||||||
|
io::Stdout,
|
||||||
|
sync::{Arc, LazyLock},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::gui::nav_bar::NavBar;
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use ratatui::Frame;
|
use crossterm::{
|
||||||
use ratatui::layout::{Constraint, Direction, Layout};
|
execute,
|
||||||
use ratatui::widgets::{Block, Paragraph};
|
terminal::{EnterAlternateScreen, enable_raw_mode},
|
||||||
use sys_info;
|
};
|
||||||
|
use futures_util::lock::Mutex;
|
||||||
|
use ratatui::{Terminal, prelude::CrosstermBackend};
|
||||||
|
use std::io::stdout;
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
|
|
||||||
pub fn launch(connection_status: bool) -> Result<()> {
|
pub static TERMINAL: LazyLock<Arc<Mutex<Terminal<CrosstermBackend<Stdout>>>>> =
|
||||||
|
LazyLock::new(|| {
|
||||||
|
Arc::new(Mutex::new(
|
||||||
|
Terminal::new(CrosstermBackend::new(stdout())).unwrap(),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
pub static NAV_BAR: LazyLock<Arc<Mutex<NavBar>>> =
|
||||||
|
LazyLock::new(|| Arc::new(Mutex::new(NavBar::new())));
|
||||||
|
|
||||||
|
pub fn launch() -> Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
task::spawn(run(connection_status));
|
task::spawn(run());
|
||||||
ratatui::restore();
|
ratatui::restore();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(connection_status: bool) -> Result<()> {
|
fn init_terminal() {
|
||||||
|
let mut stdout = stdout();
|
||||||
|
execute!(stdout, EnterAlternateScreen).unwrap();
|
||||||
|
enable_raw_mode().unwrap();
|
||||||
|
}
|
||||||
|
async fn run() -> Result<()> {
|
||||||
|
init_terminal();
|
||||||
loop {
|
loop {
|
||||||
ratatui::init().draw(|frame| render(frame, connection_status))?;
|
NAV_BAR.lock().await.current_screen.renderf();
|
||||||
|
tokio::time::sleep(Duration::from_millis(1000)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(frame: &mut Frame, connection_status: bool) {
|
|
||||||
let main_layout = Layout::default()
|
|
||||||
.direction(Direction::Horizontal)
|
|
||||||
.margin(1)
|
|
||||||
.constraints([Constraint::Percentage((100))].as_ref());
|
|
||||||
let main_block = Block::bordered().title("Tensamin - Iota");
|
|
||||||
let main_chunks = main_layout.split(frame.area());
|
|
||||||
|
|
||||||
let [left, right] = Layout::horizontal([Constraint::Fill(1); 2]).areas(main_chunks[0]);
|
|
||||||
let [top_right, bottom_right] = Layout::vertical([Constraint::Fill(1); 2]).areas(right);
|
|
||||||
|
|
||||||
let block_logs = Block::bordered().title("Logs");
|
|
||||||
let block_systeminfo = Block::bordered().title("System Info");
|
|
||||||
let block_ping = Block::bordered().title("Ping");
|
|
||||||
|
|
||||||
let mut operating_system: String;
|
|
||||||
match sys_info::os_type() {
|
|
||||||
Ok(os) => operating_system = os,
|
|
||||||
Err(error) => operating_system = error.to_string(),
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut text_content: String = String::from("");
|
|
||||||
text_content.push_str("Operating System: ");
|
|
||||||
text_content.push_str(operating_system.as_str());
|
|
||||||
text_content.push_str("\nConnection: ");
|
|
||||||
text_content.push_str(if connection_status {
|
|
||||||
"Connected"
|
|
||||||
} else {
|
|
||||||
"Disconnected"
|
|
||||||
});
|
|
||||||
|
|
||||||
let paragraph_systeminfo = Paragraph::new(text_content).block(block_systeminfo);
|
|
||||||
|
|
||||||
frame.render_widget(Block::bordered().title("Tensamin - Iota"), frame.area());
|
|
||||||
frame.render_widget(Block::bordered().title("Logs"), left);
|
|
||||||
frame.render_widget(paragraph_systeminfo, top_right);
|
|
||||||
frame.render_widget(Block::bordered().title("Ping"), bottom_right);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
|
|
@ -15,8 +15,9 @@ mod users;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
||||||
use crate::gui::log_panel;
|
use crate::gui::app_state::AppState;
|
||||||
use crate::gui::log_panel::{AppState, log_message, log_message_trans};
|
use crate::gui::log_panel::{log_message, log_message_trans};
|
||||||
|
use crate::gui::{log_panel, ratatui_interface};
|
||||||
use crate::langu::language_creator;
|
use crate::langu::language_creator;
|
||||||
use crate::omikron::omikron_connection::OmikronConnection;
|
use crate::omikron::omikron_connection::OmikronConnection;
|
||||||
use crate::users::user_manager::UserManager;
|
use crate::users::user_manager::UserManager;
|
||||||
|
|
@ -34,8 +35,8 @@ async fn main() {
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
|
|
||||||
log_panel::setup();
|
log_panel::setup();
|
||||||
|
ratatui_interface::launch();
|
||||||
// LANGUAGE PACK
|
// LANGUAGE PACK
|
||||||
language_creator::create_languages();
|
language_creator::create_languages();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,13 @@
|
||||||
use crate::APP_STATE;
|
|
||||||
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
||||||
use crate::gui::log_panel::log_message;
|
use crate::gui::log_panel::log_message;
|
||||||
use crate::gui::log_panel::log_message_trans;
|
use crate::gui::log_panel::log_message_trans;
|
||||||
use crate::omikron::ping_pong_task::*;
|
|
||||||
use crate::users::contact::Contact;
|
use crate::users::contact::Contact;
|
||||||
use crate::users::user_community_util::UserCommunityUtil;
|
use crate::users::user_community_util::UserCommunityUtil;
|
||||||
use crate::util::chat_files::ChatFiles;
|
use crate::util::chat_files::ChatFiles;
|
||||||
use crate::util::chats_util::{get_user, get_users, mod_user};
|
use crate::util::chats_util::{get_user, get_users, mod_user};
|
||||||
use futures_util::{SinkExt, StreamExt};
|
use futures_util::{SinkExt, StreamExt};
|
||||||
use json::JsonValue;
|
use json::JsonValue;
|
||||||
use json::number::Number;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
@ -121,7 +117,6 @@ impl OmikronConnection {
|
||||||
// ************************************************ //
|
// ************************************************ //
|
||||||
// Direct messages //
|
// Direct messages //
|
||||||
// ************************************************ //
|
// ************************************************ //
|
||||||
|
|
||||||
log_message_trans(format!("{:?}", &cv.comm_type));
|
log_message_trans(format!("{:?}", &cv.comm_type));
|
||||||
if cv.is_type(CommunicationType::message_other_iota) {
|
if cv.is_type(CommunicationType::message_other_iota) {
|
||||||
let sender_id = &cv.get_sender();
|
let sender_id = &cv.get_sender();
|
||||||
|
|
@ -135,10 +130,7 @@ impl OmikronConnection {
|
||||||
false,
|
false,
|
||||||
*receiver_id,
|
*receiver_id,
|
||||||
*sender_id,
|
*sender_id,
|
||||||
cv.get_data(DataTypes::message_content)
|
cv.get_data(DataTypes::content).unwrap().as_str().unwrap(),
|
||||||
.unwrap()
|
|
||||||
.as_str()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
);
|
||||||
let response = CommunicationValue::new(CommunicationType::message_live)
|
let response = CommunicationValue::new(CommunicationType::message_live)
|
||||||
.with_id(cv.get_id())
|
.with_id(cv.get_id())
|
||||||
|
|
@ -149,7 +141,7 @@ impl OmikronConnection {
|
||||||
)
|
)
|
||||||
.add_data(
|
.add_data(
|
||||||
DataTypes::message,
|
DataTypes::message,
|
||||||
cv.get_data(DataTypes::message_content).unwrap().clone(),
|
cv.get_data(DataTypes::content).unwrap().clone(),
|
||||||
)
|
)
|
||||||
.add_data(
|
.add_data(
|
||||||
DataTypes::sender_id,
|
DataTypes::sender_id,
|
||||||
|
|
@ -163,7 +155,7 @@ impl OmikronConnection {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if cv.is_type(CommunicationType::message) {
|
if cv.is_type(CommunicationType::message_send) {
|
||||||
/* DATA CONTAINER:
|
/* DATA CONTAINER:
|
||||||
"sent_by_self": true,
|
"sent_by_self": true,
|
||||||
"timestamp": unixTimestamp,
|
"timestamp": unixTimestamp,
|
||||||
|
|
@ -189,8 +181,14 @@ impl OmikronConnection {
|
||||||
true,
|
true,
|
||||||
my_id,
|
my_id,
|
||||||
other_id,
|
other_id,
|
||||||
&*cv.get_data(DataTypes::message_content).unwrap().to_string(),
|
&*cv.get_data(DataTypes::content).unwrap().to_string(),
|
||||||
);
|
);
|
||||||
|
log_message(format!(
|
||||||
|
"Received a message from {} reading {} to {}",
|
||||||
|
my_id,
|
||||||
|
&*cv.get_data(DataTypes::content).unwrap().to_string(),
|
||||||
|
other_id
|
||||||
|
));
|
||||||
let ack = CommunicationValue::ack_message(cv.get_id(), my_id);
|
let ack = CommunicationValue::ack_message(cv.get_id(), my_id);
|
||||||
Self::send_message_static(&writer.clone(), ack.to_json().to_string())
|
Self::send_message_static(&writer.clone(), ack.to_json().to_string())
|
||||||
.await;
|
.await;
|
||||||
|
|
@ -203,30 +201,32 @@ impl OmikronConnection {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if cv.is_type(CommunicationType::message_get) {
|
if cv.is_type(CommunicationType::messages_get) {
|
||||||
let my_id = cv.get_sender();
|
let my_id = cv.get_sender();
|
||||||
let partner_id = Uuid::from_str(
|
let partner_id = Uuid::from_str(
|
||||||
&*cv.get_data(DataTypes::user_id).unwrap().to_string(),
|
&*cv.get_data(DataTypes::user_id).unwrap().to_string(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let offset = cv
|
let offset = cv
|
||||||
.get_data(DataTypes::loaded_messages)
|
.get_data(DataTypes::offset)
|
||||||
.unwrap_or(&JsonValue::Null)
|
.unwrap_or(&JsonValue::Null)
|
||||||
.to_string()
|
.to_string()
|
||||||
.parse::<i64>()
|
.parse::<i64>()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let amount = cv
|
let amount = cv
|
||||||
.get_data(DataTypes::message_amount)
|
.get_data(DataTypes::amount)
|
||||||
.unwrap_or(&JsonValue::Null)
|
.unwrap_or(&JsonValue::Null)
|
||||||
.to_string()
|
.to_string()
|
||||||
.parse::<i64>()
|
.parse::<i64>()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
log_message(format!("A:{} O:{}, P:{}", amount, offset, partner_id));
|
||||||
let messages =
|
let messages =
|
||||||
ChatFiles::get_messages(my_id, partner_id, offset, amount);
|
ChatFiles::get_messages(my_id, partner_id, offset, amount);
|
||||||
let resp = CommunicationValue::new(CommunicationType::message_chunk)
|
log_message(messages.to_string());
|
||||||
|
let resp = CommunicationValue::new(CommunicationType::messages_get)
|
||||||
.with_id(cv.get_id())
|
.with_id(cv.get_id())
|
||||||
.with_receiver(my_id)
|
.with_receiver(my_id)
|
||||||
.add_data(DataTypes::message_chunk, messages);
|
.add_data(DataTypes::messages, messages);
|
||||||
|
|
||||||
Self::send_message_static(&writer.clone(), resp.to_json().to_string())
|
Self::send_message_static(&writer.clone(), resp.to_json().to_string())
|
||||||
.await;
|
.await;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::auth::auth_connector::AuthConnector;
|
use crate::auth::auth_connector;
|
||||||
use crate::users::user_profile::UserProfile;
|
use crate::users::user_profile::UserProfile;
|
||||||
use crate::users::user_profile_full::UserProfileFull;
|
use crate::users::user_profile_full::UserProfileFull;
|
||||||
use crate::util::config_util::CONFIG;
|
use crate::util::config_util::CONFIG;
|
||||||
|
|
@ -29,7 +29,7 @@ static UNIQUE: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
|
||||||
|
|
||||||
impl UserManager {
|
impl UserManager {
|
||||||
pub async fn create_user(username: &str) -> Option<UserProfileFull> {
|
pub async fn create_user(username: &str) -> Option<UserProfileFull> {
|
||||||
let user_id = AuthConnector::get_register().await.unwrap();
|
let user_id = auth_connector::get_register().await.unwrap();
|
||||||
let mut buf = [0u8; 56];
|
let mut buf = [0u8; 56];
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
rng.fill_bytes(&mut buf);
|
rng.fill_bytes(&mut buf);
|
||||||
|
|
@ -63,7 +63,7 @@ impl UserManager {
|
||||||
private_key: general_purpose::STANDARD.encode(&private_key.as_bytes()),
|
private_key: general_purpose::STANDARD.encode(&private_key.as_bytes()),
|
||||||
};
|
};
|
||||||
|
|
||||||
AuthConnector::complete_register(&up, &CONFIG.lock().unwrap().get_iota_id().to_string())
|
auth_connector::complete_register(&up, &CONFIG.lock().unwrap().get_iota_id().to_string())
|
||||||
.await;
|
.await;
|
||||||
save_file(
|
save_file(
|
||||||
"",
|
"",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::auth::auth_connector::AuthConnector;
|
use crate::auth::auth_connector;
|
||||||
use crate::gui::log_panel::log_message;
|
use crate::gui::log_panel::log_message;
|
||||||
use crate::users::user_manager::UserManager;
|
use crate::users::user_manager::UserManager;
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
|
|
@ -79,7 +79,7 @@ impl UserProfile {
|
||||||
|| j.has_key("move")
|
|| j.has_key("move")
|
||||||
|| j.has_key("moving")
|
|| j.has_key("moving")
|
||||||
{
|
{
|
||||||
if AuthConnector::migrate_user(&mut up).await {
|
if auth_connector::migrate_user(&mut up).await {
|
||||||
log_message(format!("[INFO] Migration triggered for {}", up.username));
|
log_message(format!("[INFO] Migration triggered for {}", up.username));
|
||||||
UserManager::set_unique(true);
|
UserManager::set_unique(true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
|
use crate::util::file_util::{get_children, load_file, save_file};
|
||||||
use json::{self, JsonValue, array, object};
|
use json::{self, JsonValue, array, object};
|
||||||
use sha2::digest::typenum::Add1;
|
use sha2::digest::typenum::Add1;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use sys_info::loadavg;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::gui::log_panel::log_message;
|
||||||
|
use crate::langu::language_manager::format;
|
||||||
|
|
||||||
pub enum MessageState {
|
pub enum MessageState {
|
||||||
Read,
|
Read,
|
||||||
Received,
|
Received,
|
||||||
|
|
@ -26,23 +31,6 @@ impl MessageState {
|
||||||
pub struct ChatFiles;
|
pub struct ChatFiles;
|
||||||
|
|
||||||
impl ChatFiles {
|
impl ChatFiles {
|
||||||
fn load_file(dir: &str, file_name: &str) -> String {
|
|
||||||
let path = Path::new(dir).join(file_name);
|
|
||||||
if let Ok(mut f) = File::open(&path) {
|
|
||||||
let mut content = String::new();
|
|
||||||
let _ = f.read_to_string(&mut content);
|
|
||||||
content
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn save_file(dir: &str, file_name: &str, content: &str) {
|
|
||||||
fs::create_dir_all(dir);
|
|
||||||
let path = Path::new(dir).join(file_name);
|
|
||||||
let mut file = File::create(path).unwrap();
|
|
||||||
file.write_all(content.as_bytes());
|
|
||||||
}
|
|
||||||
pub fn add_message(
|
pub fn add_message(
|
||||||
send_time: i64,
|
send_time: i64,
|
||||||
storage_owner_is_sender: bool,
|
storage_owner_is_sender: bool,
|
||||||
|
|
@ -52,25 +40,38 @@ impl ChatFiles {
|
||||||
) {
|
) {
|
||||||
let user_dir = format!("users/{}/chats/{}", storage_owner, external_user);
|
let user_dir = format!("users/{}/chats/{}", storage_owner, external_user);
|
||||||
|
|
||||||
|
if let Err(e) = fs::create_dir_all(&user_dir) {
|
||||||
|
log_message(format!("Failed to create chat directory: {}", e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let mut chunk_index = 0;
|
let mut chunk_index = 0;
|
||||||
let mut message_chunk = array![];
|
let mut message_chunk = array![];
|
||||||
|
|
||||||
// find latest chunk not full (max 800 msgs)
|
// find latest chunk not full (max 800 msgs)
|
||||||
loop {
|
loop {
|
||||||
let file_name = format!("msgs_{}.json", chunk_index);
|
let file_name = format!("msgs_{}.json", chunk_index);
|
||||||
let file_content = Self::load_file(&user_dir, &file_name);
|
let file_content = load_file(&user_dir, &file_name);
|
||||||
|
|
||||||
if !file_content.is_empty() {
|
if !file_content.is_empty() {
|
||||||
if let Ok(current_chunk) = json::parse(&file_content) {
|
if let Ok(current_chunk) = json::parse(&file_content) {
|
||||||
if current_chunk.len() < 800 {
|
if current_chunk.is_array() && current_chunk.len() < 800 {
|
||||||
message_chunk = current_chunk;
|
message_chunk = current_chunk;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log_message(format!("Failed to parse existing JSON file: {}", file_name));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// New file, use empty array
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk_index += 1;
|
chunk_index += 1;
|
||||||
|
if chunk_index > 1000 {
|
||||||
|
log_message(format!("Too many message chunks. Aborting add."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let json_obj = object! {
|
let json_obj = object! {
|
||||||
|
|
@ -79,15 +80,16 @@ impl ChatFiles {
|
||||||
"sender_is_me" => storage_owner_is_sender,
|
"sender_is_me" => storage_owner_is_sender,
|
||||||
"message_state" => MessageState::Sending.as_str()
|
"message_state" => MessageState::Sending.as_str()
|
||||||
};
|
};
|
||||||
message_chunk.push(json_obj).unwrap();
|
|
||||||
|
|
||||||
Self::save_file(
|
if let Err(e) = message_chunk.push(json_obj) {
|
||||||
&user_dir,
|
log_message(format!("Failed to push new message into JSON array: {}", e));
|
||||||
&format!("msgs_{}.json", chunk_index),
|
return;
|
||||||
&message_chunk.dump(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let file_name = format!("msgs_{}.json", chunk_index);
|
||||||
|
log_message(format!("Saving message to {}/{}", user_dir, file_name));
|
||||||
|
save_file(&user_dir, &file_name, &message_chunk.dump());
|
||||||
|
}
|
||||||
pub fn change_message_state(
|
pub fn change_message_state(
|
||||||
storage_owner: Uuid,
|
storage_owner: Uuid,
|
||||||
external_user: Uuid,
|
external_user: Uuid,
|
||||||
|
|
@ -108,7 +110,7 @@ impl ChatFiles {
|
||||||
let fname_str = fname.to_string_lossy();
|
let fname_str = fname.to_string_lossy();
|
||||||
|
|
||||||
if fname_str.starts_with("msgs_") && fname_str.ends_with(".json") {
|
if fname_str.starts_with("msgs_") && fname_str.ends_with(".json") {
|
||||||
let file_content = Self::load_file(&user_dir, &fname_str);
|
let file_content = load_file(&user_dir, &fname_str);
|
||||||
if file_content.is_empty() {
|
if file_content.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +126,7 @@ impl ChatFiles {
|
||||||
}
|
}
|
||||||
|
|
||||||
if modified {
|
if modified {
|
||||||
Self::save_file(&user_dir, &fname_str, &chunk.dump());
|
save_file(&user_dir, &fname_str, &chunk.dump());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -139,24 +141,17 @@ impl ChatFiles {
|
||||||
loaded_messages: i64,
|
loaded_messages: i64,
|
||||||
amount: i64,
|
amount: i64,
|
||||||
) -> JsonValue {
|
) -> JsonValue {
|
||||||
let user_dir = format!("users/{}/chats/{}", storage_owner, external_user);
|
|
||||||
let path = Path::new(&user_dir);
|
|
||||||
let mut messages = array![];
|
let mut messages = array![];
|
||||||
|
|
||||||
if !path.exists() {
|
|
||||||
return messages;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut latest_chunk_index: i32 = -1;
|
let mut latest_chunk_index: i32 = -1;
|
||||||
if let Ok(entries) = fs::read_dir(path) {
|
let files = get_children(&format!("users/{}/chats/{}", storage_owner, external_user));
|
||||||
for entry in entries.flatten() {
|
|
||||||
let fname = entry.file_name();
|
for entry in files {
|
||||||
let fname_str = fname.to_string_lossy();
|
if let Some(num) = {
|
||||||
if fname_str.starts_with("msgs_") && fname_str.ends_with(".json") {
|
entry
|
||||||
if let Some(num) = fname_str
|
|
||||||
.strip_prefix("msgs_")
|
.strip_prefix("msgs_")
|
||||||
.and_then(|s| s.strip_suffix(".json"))
|
.and_then(|s| s.strip_suffix(".json"))
|
||||||
{
|
} {
|
||||||
if let Ok(index) = num.parse::<i32>() {
|
if let Ok(index) = num.parse::<i32>() {
|
||||||
if index > latest_chunk_index {
|
if index > latest_chunk_index {
|
||||||
latest_chunk_index = index;
|
latest_chunk_index = index;
|
||||||
|
|
@ -164,8 +159,6 @@ impl ChatFiles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if latest_chunk_index == -1 {
|
if latest_chunk_index == -1 {
|
||||||
return messages;
|
return messages;
|
||||||
|
|
@ -179,7 +172,10 @@ impl ChatFiles {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let file_name = format!("msgs_{}.json", chunk_index);
|
let file_name = format!("msgs_{}.json", chunk_index);
|
||||||
let file_content = Self::load_file(&user_dir, &file_name);
|
let file_content = load_file(
|
||||||
|
&format!("users/{}/chats/{}", storage_owner, external_user),
|
||||||
|
&file_name,
|
||||||
|
);
|
||||||
if file_content.is_empty() {
|
if file_content.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue