UI Utility
This commit is contained in:
parent
a5ade7dd03
commit
cbd357ce14
14 changed files with 962 additions and 622 deletions
126
Cargo.lock
generated
126
Cargo.lock
generated
|
|
@ -334,6 +334,31 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.28.1"
|
||||
|
|
@ -913,7 +938,7 @@ dependencies = [
|
|||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
"windows-core 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1281,25 +1306,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-foundation"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-io-kit"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.7"
|
||||
|
|
@ -1534,6 +1540,26 @@ dependencies = [
|
|||
"unicode-width 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.17"
|
||||
|
|
@ -1964,15 +1990,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.37.0"
|
||||
version = "0.30.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753"
|
||||
checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"memchr",
|
||||
"ntapi",
|
||||
"objc2-core-foundation",
|
||||
"objc2-io-kit",
|
||||
"once_cell",
|
||||
"rayon",
|
||||
"windows",
|
||||
]
|
||||
|
||||
|
|
@ -2531,24 +2558,21 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.3"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
||||
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core",
|
||||
"windows-future",
|
||||
"windows-link 0.1.3",
|
||||
"windows-numerics",
|
||||
"windows-core 0.52.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.2.0"
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2564,17 +2588,6 @@ dependencies = [
|
|||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link 0.1.3",
|
||||
"windows-threading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.0"
|
||||
|
|
@ -2609,16 +2622,6 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.5.3"
|
||||
|
|
@ -2717,15 +2720,6 @@ dependencies = [
|
|||
"windows_x86_64_msvc 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
|
||||
dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ cmake = "*"
|
|||
tokio = { version = "*", features = ["full"] }
|
||||
uuid = { version = "*", features = ["v4"] }
|
||||
walkdir = "2.5.0"
|
||||
sysinfo = "*"
|
||||
sysinfo = "0.30"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
ratatui = { version = "*", features = ["all-widgets"]}
|
||||
bytes = "*"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use crate::CONFIG;
|
||||
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
||||
use crate::gui::log_panel::log_message;
|
||||
use crate::users::user_profile::UserProfile;
|
||||
use hex;
|
||||
use json::JsonValue;
|
||||
|
|
@ -48,7 +50,7 @@ impl AuthConnector {
|
|||
.ok()?;
|
||||
let json = res.text().await.ok()?;
|
||||
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> {
|
||||
|
|
@ -60,10 +62,10 @@ impl AuthConnector {
|
|||
let res = client.get(&url).send().await.ok()?;
|
||||
let json = res.text().await.ok()?;
|
||||
let mut cv = CommunicationValue::from_json(&json);
|
||||
if !cv.is_type(CommunicationType::Success) {
|
||||
if !cv.is_type(CommunicationType::success) {
|
||||
return None;
|
||||
}
|
||||
Uuid::parse_str(&*cv.get_data(DataTypes::UserId).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> {
|
||||
|
|
@ -73,31 +75,31 @@ impl AuthConnector {
|
|||
let json = res.text().await.ok()?;
|
||||
|
||||
let mut cv = CommunicationValue::from_json(&json);
|
||||
if cv.comm_type != CommunicationType::Success {
|
||||
if cv.comm_type != CommunicationType::success {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(AuthUser {
|
||||
created_at: cv
|
||||
.get_data(DataTypes::CreatedAt)
|
||||
.get_data(DataTypes::created_at)
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.parse::<i64>()
|
||||
.unwrap_or(-1),
|
||||
username: cv.get_data(DataTypes::Username).unwrap().to_string(),
|
||||
display: cv.get_data(DataTypes::Display).unwrap().to_string(),
|
||||
avatar: cv.get_data(DataTypes::Avatar).unwrap().to_string(),
|
||||
about: cv.get_data(DataTypes::About).unwrap().to_string(),
|
||||
status: cv.get_data(DataTypes::Status).unwrap().to_string(),
|
||||
public_key: cv.get_data(DataTypes::PublicKey).unwrap().to_string(),
|
||||
username: cv.get_data(DataTypes::username).unwrap().to_string(),
|
||||
display: cv.get_data(DataTypes::display).unwrap().to_string(),
|
||||
avatar: cv.get_data(DataTypes::avatar).unwrap().to_string(),
|
||||
about: cv.get_data(DataTypes::about).unwrap().to_string(),
|
||||
status: cv.get_data(DataTypes::status).unwrap().to_string(),
|
||||
public_key: cv.get_data(DataTypes::public_key).unwrap().to_string(),
|
||||
sub_level: cv
|
||||
.get_data(DataTypes::SubLevel)
|
||||
.get_data(DataTypes::sub_level)
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.parse::<i32>()
|
||||
.unwrap_or(-1),
|
||||
sub_end: cv
|
||||
.get_data(DataTypes::SubEnd)
|
||||
.get_data(DataTypes::sub_end)
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.parse::<i32>()
|
||||
|
|
@ -112,7 +114,7 @@ impl AuthConnector {
|
|||
let json = res.text().await.ok()?;
|
||||
|
||||
let mut cv = CommunicationValue::from_json(&json);
|
||||
Uuid::parse_str(&*cv.get_data(DataTypes::UserId).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 {
|
||||
|
|
@ -139,10 +141,10 @@ impl AuthConnector {
|
|||
let body = res.text().await.unwrap();
|
||||
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, iota_id: &str) -> bool {
|
||||
pub async fn migrate_user(user_profile: &mut UserProfile) -> bool {
|
||||
let url = format!(
|
||||
"https://auth.tensamin.methanium.net/api/change/iota-id/{}",
|
||||
user_profile.user_id
|
||||
|
|
@ -150,7 +152,7 @@ impl AuthConnector {
|
|||
let client = Self::client();
|
||||
|
||||
let mut payload = JsonValue::new_object();
|
||||
payload["iota_id"] = iota_id.into();
|
||||
payload["iota_id"] = JsonValue::String(CONFIG.lock().unwrap().get_iota_id().to_string());
|
||||
payload["reset_token"] = user_profile.reset_token.clone().into();
|
||||
payload["new_token"] = user_profile.randomize_reset_token().into();
|
||||
|
||||
|
|
@ -171,7 +173,7 @@ impl AuthConnector {
|
|||
match resp.text().await {
|
||||
Ok(text) => {
|
||||
let cv = CommunicationValue::from_json(&text);
|
||||
cv.comm_type == CommunicationType::Success
|
||||
cv.comm_type == CommunicationType::success
|
||||
}
|
||||
Err(_) => false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use axum::Json;
|
||||
use json::number::Number;
|
||||
use json::{JsonValue, object, parse, stringify};
|
||||
use json::{Array, JsonValue, object, parse, stringify};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::env::VarsOs;
|
||||
|
|
@ -8,73 +8,73 @@ use std::str::FromStr;
|
|||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Eq, Hash, PartialEq, Debug)]
|
||||
#[derive(Eq, Hash, PartialEq, Clone, Debug)]
|
||||
pub enum DataTypes {
|
||||
ErrorType,
|
||||
ChatPartnerId,
|
||||
IotaId,
|
||||
UserId,
|
||||
UserIds,
|
||||
UserState,
|
||||
UserStates,
|
||||
UserPings,
|
||||
CallState,
|
||||
ScreenShare,
|
||||
PrivateKeyHash,
|
||||
Accepted,
|
||||
AcceptedProfiles,
|
||||
DeniedProfiles,
|
||||
MessageContent,
|
||||
MessageChunk,
|
||||
SendTime,
|
||||
GetTime,
|
||||
GetVariant,
|
||||
SharedSecretOwn,
|
||||
SharedSecretOther,
|
||||
SharedSecretSign,
|
||||
SharedSecret,
|
||||
CallId,
|
||||
CallName,
|
||||
CallSecretSha,
|
||||
CallSecret,
|
||||
SharedCallSecret,
|
||||
StartDate,
|
||||
EndDate,
|
||||
ReceiverId,
|
||||
SenderId,
|
||||
Signature,
|
||||
Signed,
|
||||
Message,
|
||||
LastPing,
|
||||
PingIota,
|
||||
PingClients,
|
||||
Matches,
|
||||
Omikron,
|
||||
LoadedMessages,
|
||||
MessageAmount,
|
||||
Position,
|
||||
Name,
|
||||
Path,
|
||||
Codec,
|
||||
Function,
|
||||
Payload,
|
||||
Result,
|
||||
Interactables,
|
||||
WantToWatch,
|
||||
Watcher,
|
||||
CreatedAt,
|
||||
Username,
|
||||
Display,
|
||||
Avatar,
|
||||
About,
|
||||
Status,
|
||||
PublicKey,
|
||||
SubLevel,
|
||||
SubEnd,
|
||||
CommunityAddress,
|
||||
Challenge,
|
||||
CommunityTitle,
|
||||
Communities,
|
||||
error_type,
|
||||
chat_partner_id,
|
||||
iota_id,
|
||||
user_id,
|
||||
user_ids,
|
||||
user_state,
|
||||
user_states,
|
||||
user_pings,
|
||||
call_state,
|
||||
screen_share,
|
||||
private_key_hash,
|
||||
accepted,
|
||||
accepted_profiles,
|
||||
denied_profiles,
|
||||
message_content,
|
||||
message_chunk,
|
||||
send_time,
|
||||
get_time,
|
||||
get_variant,
|
||||
shared_secret_own,
|
||||
shared_secret_other,
|
||||
shared_secret_sign,
|
||||
shared_secret,
|
||||
call_id,
|
||||
call_name,
|
||||
call_secret_sha,
|
||||
call_secret,
|
||||
shared_call_secret,
|
||||
start_date,
|
||||
end_date,
|
||||
receiver_id,
|
||||
sender_id,
|
||||
signature,
|
||||
signed,
|
||||
message,
|
||||
last_ping,
|
||||
ping_iota,
|
||||
ping_clients,
|
||||
matches,
|
||||
omikron,
|
||||
loaded_messages,
|
||||
message_amount,
|
||||
position,
|
||||
name,
|
||||
path,
|
||||
codec,
|
||||
function,
|
||||
payload,
|
||||
result,
|
||||
interactables,
|
||||
want_to_watch,
|
||||
watcher,
|
||||
created_at,
|
||||
username,
|
||||
display,
|
||||
avatar,
|
||||
about,
|
||||
status,
|
||||
public_key,
|
||||
sub_level,
|
||||
sub_end,
|
||||
community_address,
|
||||
challenge,
|
||||
community_title,
|
||||
communities,
|
||||
}
|
||||
|
||||
impl DataTypes {
|
||||
|
|
@ -83,209 +83,179 @@ impl DataTypes {
|
|||
let normalized = p0.to_lowercase().replace('_', "");
|
||||
|
||||
match normalized.as_str() {
|
||||
"errortype" => DataTypes::ErrorType,
|
||||
"chatpartnerid" => DataTypes::ChatPartnerId,
|
||||
"iotaid" => DataTypes::IotaId,
|
||||
"userid" => DataTypes::UserId,
|
||||
"userids" => DataTypes::UserIds,
|
||||
"userstate" => DataTypes::UserState,
|
||||
"userstates" => DataTypes::UserStates,
|
||||
"userpings" => DataTypes::UserPings,
|
||||
"callstate" => DataTypes::CallState,
|
||||
"screenshare" => DataTypes::ScreenShare,
|
||||
"privatekeyhash" => DataTypes::PrivateKeyHash,
|
||||
"accepted" => DataTypes::Accepted,
|
||||
"acceptedprofiles" => DataTypes::AcceptedProfiles,
|
||||
"deniedprofiles" => DataTypes::DeniedProfiles,
|
||||
"messagecontent" => DataTypes::MessageContent,
|
||||
"messagechunk" => DataTypes::MessageChunk,
|
||||
"sendtime" => DataTypes::SendTime,
|
||||
"gettime" => DataTypes::GetTime,
|
||||
"getvariant" => DataTypes::GetVariant,
|
||||
"sharedsecretown" => DataTypes::SharedSecretOwn,
|
||||
"sharedsecretother" => DataTypes::SharedSecretOther,
|
||||
"sharedsecretsign" => DataTypes::SharedSecretSign,
|
||||
"sharedsecret" => DataTypes::SharedSecret,
|
||||
"callid" => DataTypes::CallId,
|
||||
"callname" => DataTypes::CallName,
|
||||
"callsecretsha" => DataTypes::CallSecretSha,
|
||||
"callsecret" => DataTypes::CallSecret,
|
||||
"sharedcallsecret" => DataTypes::SharedCallSecret,
|
||||
"startdate" => DataTypes::StartDate,
|
||||
"enddate" => DataTypes::EndDate,
|
||||
"receiverid" => DataTypes::ReceiverId,
|
||||
"senderid" => DataTypes::SenderId,
|
||||
"signature" => DataTypes::Signature,
|
||||
"signed" => DataTypes::Signed,
|
||||
"message" => DataTypes::Message,
|
||||
"lastping" => DataTypes::LastPing,
|
||||
"pingiota" => DataTypes::PingIota,
|
||||
"pingclients" => DataTypes::PingClients,
|
||||
"matches" => DataTypes::Matches,
|
||||
"omikron" => DataTypes::Omikron,
|
||||
"loadedmessages" => DataTypes::LoadedMessages,
|
||||
"messageamount" => DataTypes::MessageAmount,
|
||||
"position" => DataTypes::Position,
|
||||
"name" => DataTypes::Name,
|
||||
"path" => DataTypes::Path,
|
||||
"codec" => DataTypes::Codec,
|
||||
"function" => DataTypes::Function,
|
||||
"payload" => DataTypes::Payload,
|
||||
"result" => DataTypes::Result,
|
||||
"interactables" => DataTypes::Interactables,
|
||||
"wanttowatch" => DataTypes::WantToWatch,
|
||||
"watcher" => DataTypes::Watcher,
|
||||
"createdat" => DataTypes::CreatedAt,
|
||||
"username" => DataTypes::Username,
|
||||
"display" => DataTypes::Display,
|
||||
"avatar" => DataTypes::Avatar,
|
||||
"about" => DataTypes::About,
|
||||
"status" => DataTypes::Status,
|
||||
"publickey" => DataTypes::PublicKey,
|
||||
"sublevel" => DataTypes::SubLevel,
|
||||
"subend" => DataTypes::SubEnd,
|
||||
"communityaddress" => DataTypes::CommunityAddress,
|
||||
"challenge" => DataTypes::Challenge,
|
||||
"communitytitle" => DataTypes::CommunityTitle,
|
||||
"communities" => DataTypes::Communities,
|
||||
_ => DataTypes::ErrorType, // fallback if unknown
|
||||
"errortype" => DataTypes::error_type,
|
||||
"chatpartnerid" => DataTypes::chat_partner_id,
|
||||
"iotaid" => DataTypes::iota_id,
|
||||
"userid" => DataTypes::user_id,
|
||||
"userids" => DataTypes::user_ids,
|
||||
"userstate" => DataTypes::user_state,
|
||||
"userstates" => DataTypes::user_states,
|
||||
"userpings" => DataTypes::user_pings,
|
||||
"callstate" => DataTypes::call_state,
|
||||
"screenshare" => DataTypes::screen_share,
|
||||
"privatekeyhash" => DataTypes::private_key_hash,
|
||||
"accepted" => DataTypes::accepted,
|
||||
"acceptedprofiles" => DataTypes::accepted_profiles,
|
||||
"deniedprofiles" => DataTypes::denied_profiles,
|
||||
"messagecontent" => DataTypes::message_content,
|
||||
"messagechunk" => DataTypes::message_chunk,
|
||||
"sendtime" => DataTypes::send_time,
|
||||
"gettime" => DataTypes::get_time,
|
||||
"getvariant" => DataTypes::get_variant,
|
||||
"sharedsecretown" => DataTypes::shared_secret_own,
|
||||
"sharedsecretother" => DataTypes::shared_secret_other,
|
||||
"sharedsecretsign" => DataTypes::shared_secret_sign,
|
||||
"sharedsecret" => DataTypes::shared_secret,
|
||||
"callid" => DataTypes::call_id,
|
||||
"callname" => DataTypes::call_name,
|
||||
"callsecretsha" => DataTypes::call_secret_sha,
|
||||
"callsecret" => DataTypes::call_secret,
|
||||
"sharedcallsecret" => DataTypes::shared_call_secret,
|
||||
"startdate" => DataTypes::start_date,
|
||||
"enddate" => DataTypes::end_date,
|
||||
"receiverid" => DataTypes::receiver_id,
|
||||
"senderid" => DataTypes::sender_id,
|
||||
"signature" => DataTypes::signature,
|
||||
"signed" => DataTypes::signed,
|
||||
"message" => DataTypes::message,
|
||||
"lastping" => DataTypes::last_ping,
|
||||
"pingiota" => DataTypes::ping_iota,
|
||||
"pingclients" => DataTypes::ping_clients,
|
||||
"matches" => DataTypes::matches,
|
||||
"omikron" => DataTypes::omikron,
|
||||
"loadedmessages" => DataTypes::loaded_messages,
|
||||
"messageamount" => DataTypes::message_amount,
|
||||
"position" => DataTypes::position,
|
||||
"name" => DataTypes::name,
|
||||
"path" => DataTypes::path,
|
||||
"codec" => DataTypes::codec,
|
||||
"function" => DataTypes::function,
|
||||
"payload" => DataTypes::payload,
|
||||
"result" => DataTypes::result,
|
||||
"interactables" => DataTypes::interactables,
|
||||
"wanttowatch" => DataTypes::want_to_watch,
|
||||
"watcher" => DataTypes::watcher,
|
||||
"createdat" => DataTypes::created_at,
|
||||
"username" => DataTypes::username,
|
||||
"display" => DataTypes::display,
|
||||
"avatar" => DataTypes::avatar,
|
||||
"about" => DataTypes::about,
|
||||
"status" => DataTypes::status,
|
||||
"publickey" => DataTypes::public_key,
|
||||
"sublevel" => DataTypes::sub_level,
|
||||
"subend" => DataTypes::sub_end,
|
||||
"communityaddress" => DataTypes::community_address,
|
||||
"challenge" => DataTypes::challenge,
|
||||
"communitytitle" => DataTypes::community_title,
|
||||
"communities" => DataTypes::communities,
|
||||
_ => DataTypes::error_type, // fallback if unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum CommunicationType {
|
||||
Error,
|
||||
Success,
|
||||
Message,
|
||||
MessageLive,
|
||||
MessageOtherIota,
|
||||
MessageChunk,
|
||||
MessageGet,
|
||||
ChangeConfirm,
|
||||
ConfirmReceive,
|
||||
ConfirmRead,
|
||||
GetChats,
|
||||
GetStates,
|
||||
AddCommunity,
|
||||
RemoveCommunity,
|
||||
GetCommunities,
|
||||
Challenge,
|
||||
ChallengeResponse,
|
||||
Register,
|
||||
RegisterResponse,
|
||||
Identification,
|
||||
IdentificationResponse,
|
||||
Ping,
|
||||
Pong,
|
||||
AddChat,
|
||||
SendChat,
|
||||
IotaConnected,
|
||||
IotaClosed,
|
||||
ClientChanged,
|
||||
ClientConnected,
|
||||
ClientClosed,
|
||||
PublicKey,
|
||||
PrivateKey,
|
||||
WebrtcSdp,
|
||||
WebrtcIce,
|
||||
StartStream,
|
||||
EndStream,
|
||||
WatchStream,
|
||||
GetCall,
|
||||
NewCall,
|
||||
CallInvite,
|
||||
EndCall,
|
||||
Function,
|
||||
Update,
|
||||
error,
|
||||
success,
|
||||
message,
|
||||
message_live,
|
||||
message_other_iota,
|
||||
message_chunk,
|
||||
message_get,
|
||||
change_confirm,
|
||||
confirm_receive,
|
||||
confirm_read,
|
||||
get_chats,
|
||||
get_states,
|
||||
add_community,
|
||||
remove_community,
|
||||
get_communities,
|
||||
challenge,
|
||||
challenge_response,
|
||||
register,
|
||||
register_response,
|
||||
identification,
|
||||
identification_response,
|
||||
ping,
|
||||
pong,
|
||||
add_chat,
|
||||
send_chat,
|
||||
iota_connected,
|
||||
iota_closed,
|
||||
client_changed,
|
||||
client_connected,
|
||||
client_closed,
|
||||
public_key,
|
||||
private_key,
|
||||
webrtc_sdp,
|
||||
webrtc_ice,
|
||||
start_stream,
|
||||
end_stream,
|
||||
watch_stream,
|
||||
get_call,
|
||||
new_call,
|
||||
call_invite,
|
||||
end_call,
|
||||
function,
|
||||
update,
|
||||
}
|
||||
impl CommunicationType {
|
||||
pub fn parse(p0: String) -> CommunicationType {
|
||||
let normalized = p0.to_lowercase().replace('_', "");
|
||||
|
||||
match normalized.as_str() {
|
||||
"error" => CommunicationType::Error,
|
||||
"success" => CommunicationType::Success,
|
||||
"message" => CommunicationType::Message,
|
||||
"messagelive" => CommunicationType::MessageLive,
|
||||
"messageotheriota" => CommunicationType::MessageOtherIota,
|
||||
"messagechunk" => CommunicationType::MessageChunk,
|
||||
"messageget" => CommunicationType::MessageGet,
|
||||
"changeconfirm" => CommunicationType::ChangeConfirm,
|
||||
"confirmreceive" => CommunicationType::ConfirmReceive,
|
||||
"confirmread" => CommunicationType::ConfirmRead,
|
||||
"getchats" => CommunicationType::GetChats,
|
||||
"getstates" => CommunicationType::GetStates,
|
||||
"addcommunity" => CommunicationType::AddCommunity,
|
||||
"removecommunity" => CommunicationType::RemoveCommunity,
|
||||
"getcommunities" => CommunicationType::GetCommunities,
|
||||
"challenge" => CommunicationType::Challenge,
|
||||
"challengeresponse" => CommunicationType::ChallengeResponse,
|
||||
"register" => CommunicationType::Register,
|
||||
"registerresponse" => CommunicationType::RegisterResponse,
|
||||
"identification" => CommunicationType::Identification,
|
||||
"identificationresponse" => CommunicationType::IdentificationResponse,
|
||||
"ping" => CommunicationType::Ping,
|
||||
"pong" => CommunicationType::Pong,
|
||||
"addchat" => CommunicationType::AddChat,
|
||||
"sendchat" => CommunicationType::SendChat,
|
||||
"iotaconnected" => CommunicationType::IotaConnected,
|
||||
"iotaclosed" => CommunicationType::IotaClosed,
|
||||
"clientchanged" => CommunicationType::ClientChanged,
|
||||
"clientconnected" => CommunicationType::ClientConnected,
|
||||
"clientclosed" => CommunicationType::ClientClosed,
|
||||
"publickey" => CommunicationType::PublicKey,
|
||||
"privatekey" => CommunicationType::PrivateKey,
|
||||
"webrtcsdp" => CommunicationType::WebrtcSdp,
|
||||
"webrtcice" => CommunicationType::WebrtcIce,
|
||||
"startstream" => CommunicationType::StartStream,
|
||||
"endstream" => CommunicationType::EndStream,
|
||||
"watchstream" => CommunicationType::WatchStream,
|
||||
"getcall" => CommunicationType::GetCall,
|
||||
"newcall" => CommunicationType::NewCall,
|
||||
"callinvite" => CommunicationType::CallInvite,
|
||||
"endcall" => CommunicationType::EndCall,
|
||||
"function" => CommunicationType::Function,
|
||||
"update" => CommunicationType::Update,
|
||||
_ => CommunicationType::Error, // fallback
|
||||
"error" => CommunicationType::error,
|
||||
"success" => CommunicationType::success,
|
||||
"message" => CommunicationType::message,
|
||||
"messagelive" => CommunicationType::message_live,
|
||||
"messageotheriota" => CommunicationType::message_other_iota,
|
||||
"messagechunk" => CommunicationType::message_chunk,
|
||||
"messageget" => CommunicationType::message_get,
|
||||
"changeconfirm" => CommunicationType::change_confirm,
|
||||
"confirmreceive" => CommunicationType::confirm_receive,
|
||||
"confirmread" => CommunicationType::confirm_read,
|
||||
"getchats" => CommunicationType::get_chats,
|
||||
"getstates" => CommunicationType::get_states,
|
||||
"addcommunity" => CommunicationType::add_community,
|
||||
"removecommunity" => CommunicationType::remove_community,
|
||||
"getcommunities" => CommunicationType::get_communities,
|
||||
"challenge" => CommunicationType::challenge,
|
||||
"challengeresponse" => CommunicationType::challenge_response,
|
||||
"register" => CommunicationType::register,
|
||||
"registerresponse" => CommunicationType::register_response,
|
||||
"identification" => CommunicationType::identification,
|
||||
"identificationresponse" => CommunicationType::identification_response,
|
||||
"ping" => CommunicationType::ping,
|
||||
"pong" => CommunicationType::pong,
|
||||
"addchat" => CommunicationType::add_chat,
|
||||
"sendchat" => CommunicationType::send_chat,
|
||||
"iotaconnected" => CommunicationType::iota_connected,
|
||||
"iotaclosed" => CommunicationType::iota_closed,
|
||||
"clientchanged" => CommunicationType::client_changed,
|
||||
"clientconnected" => CommunicationType::client_connected,
|
||||
"clientclosed" => CommunicationType::client_closed,
|
||||
"publickey" => CommunicationType::public_key,
|
||||
"privatekey" => CommunicationType::private_key,
|
||||
"webrtcsdp" => CommunicationType::webrtc_sdp,
|
||||
"webrtcice" => CommunicationType::webrtc_ice,
|
||||
"startstream" => CommunicationType::start_stream,
|
||||
"endstream" => CommunicationType::end_stream,
|
||||
"watchstream" => CommunicationType::watch_stream,
|
||||
"getcall" => CommunicationType::get_call,
|
||||
"newcall" => CommunicationType::new_call,
|
||||
"callinvite" => CommunicationType::call_invite,
|
||||
"endcall" => CommunicationType::end_call,
|
||||
"function" => CommunicationType::function,
|
||||
"update" => CommunicationType::update,
|
||||
_ => CommunicationType::error,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum LogLevel {
|
||||
Important = 2,
|
||||
Normal = 1,
|
||||
Debug = 0,
|
||||
None = -1,
|
||||
DebugOnly = -2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LogValue {
|
||||
pub message: String,
|
||||
pub log_level: LogLevel,
|
||||
}
|
||||
|
||||
impl LogValue {
|
||||
pub fn new(message: impl Into<String>, log_level: LogLevel) -> Self {
|
||||
Self {
|
||||
message: message.into(),
|
||||
log_level,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_json(self) -> JsonValue {
|
||||
object! {
|
||||
message: self.message.clone(),
|
||||
log_level: self.log_level as i32
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct CommunicationValue {
|
||||
pub id: Uuid,
|
||||
pub comm_type: CommunicationType,
|
||||
pub log_value: Option<LogValue>,
|
||||
pub sender: Uuid,
|
||||
pub receiver: Uuid,
|
||||
pub data: HashMap<DataTypes, JsonValue>,
|
||||
|
|
@ -296,7 +266,6 @@ impl CommunicationValue {
|
|||
Self {
|
||||
id: Uuid::new_v4(),
|
||||
comm_type,
|
||||
log_value: None,
|
||||
sender: Uuid::new_v4(),
|
||||
receiver: Uuid::new_v4(),
|
||||
data: HashMap::new(),
|
||||
|
|
@ -309,13 +278,6 @@ impl CommunicationValue {
|
|||
pub fn get_id(&self) -> Uuid {
|
||||
self.id.clone()
|
||||
}
|
||||
pub fn with_log(mut self, log: LogValue) -> Self {
|
||||
self.log_value = Some(log);
|
||||
self
|
||||
}
|
||||
pub fn get_log(&self) -> &Option<LogValue> {
|
||||
&self.log_value
|
||||
}
|
||||
pub fn with_sender(mut self, sender: Uuid) -> Self {
|
||||
self.sender = sender;
|
||||
self
|
||||
|
|
@ -342,6 +304,10 @@ impl CommunicationValue {
|
|||
self.data.insert(key, value);
|
||||
self
|
||||
}
|
||||
pub fn add_array(mut self, key: DataTypes, value: Array) -> Self {
|
||||
self.data.insert(key, JsonValue::Array(value));
|
||||
self
|
||||
}
|
||||
pub fn get_data(&mut self, key: DataTypes) -> Option<&JsonValue> {
|
||||
self.data.get(&key)
|
||||
}
|
||||
|
|
@ -360,7 +326,6 @@ impl CommunicationValue {
|
|||
type: format!("{:?}", self.comm_type),
|
||||
sender: self.sender.to_string(),
|
||||
receiver: self.receiver.to_string(),
|
||||
log: self.log_value.as_ref().map(|l| l.clone().to_json()).unwrap_or(JsonValue::Null),
|
||||
data: jdata
|
||||
}
|
||||
}
|
||||
|
|
@ -379,14 +344,6 @@ impl CommunicationValue {
|
|||
.and_then(|s| Uuid::parse_str(s).ok())
|
||||
.unwrap_or(Uuid::new_v4());
|
||||
|
||||
let log_value = if parsed["log"].is_object() {
|
||||
Some(LogValue::new(
|
||||
parsed["log"]["message"].as_str().unwrap_or("").to_string(),
|
||||
LogLevel::Normal,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let uuid = Uuid::parse_str(parsed["id"].as_str().unwrap_or("")).unwrap_or(Uuid::new_v4());
|
||||
let mut data = HashMap::new();
|
||||
if parsed["data"].is_object() {
|
||||
|
|
@ -399,24 +356,23 @@ impl CommunicationValue {
|
|||
Self {
|
||||
id: uuid,
|
||||
comm_type,
|
||||
log_value,
|
||||
sender,
|
||||
receiver,
|
||||
data,
|
||||
}
|
||||
}
|
||||
pub fn ack_message(message_id: Uuid, sender: Uuid) -> CommunicationValue {
|
||||
let mut cv = CommunicationValue::new(CommunicationType::Message).with_id(message_id);
|
||||
let mut cv = CommunicationValue::new(CommunicationType::message).with_id(message_id);
|
||||
|
||||
if let s = sender {
|
||||
cv = cv.add_data(DataTypes::SenderId, JsonValue::String(s.to_string()));
|
||||
cv = cv.add_data(DataTypes::send_time, JsonValue::String(s.to_string()));
|
||||
}
|
||||
cv
|
||||
}
|
||||
pub fn forward_to_other_iota(original: &mut CommunicationValue) -> CommunicationValue {
|
||||
let receiver = Uuid::from_str(
|
||||
&*original
|
||||
.get_data(DataTypes::ReceiverId)
|
||||
.get_data(DataTypes::receiver_id)
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
)
|
||||
|
|
@ -428,15 +384,15 @@ impl CommunicationValue {
|
|||
.unwrap()
|
||||
.as_millis() as i64;
|
||||
|
||||
let cv = CommunicationValue::new(CommunicationType::MessageOtherIota)
|
||||
let cv = CommunicationValue::new(CommunicationType::message_other_iota)
|
||||
.with_id(original.get_id())
|
||||
.with_receiver(receiver.unwrap())
|
||||
.add_data(DataTypes::SendTime, JsonValue::String(now_ms.to_string()))
|
||||
.add_data(DataTypes::send_time, JsonValue::String(now_ms.to_string()))
|
||||
.add_data(
|
||||
DataTypes::MessageContent,
|
||||
DataTypes::message_content,
|
||||
JsonValue::String(
|
||||
original
|
||||
.get_data(DataTypes::MessageContent)
|
||||
.get_data(DataTypes::message_content)
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
),
|
||||
|
|
@ -444,6 +400,6 @@ impl CommunicationValue {
|
|||
|
||||
// include sender_id if the original had one
|
||||
let sender = original.get_sender();
|
||||
cv.add_data(DataTypes::SenderId, JsonValue::String(sender.to_string()))
|
||||
cv.add_data(DataTypes::sender_id, JsonValue::String(sender.to_string()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,405 @@
|
|||
use crate::langu::language_manager::from_key;
|
||||
use crate::{APP_STATE, omikron::ping_pong_task::PingPongTask};
|
||||
use crossterm::{
|
||||
ExecutableCommand,
|
||||
terminal::{LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
|
||||
};
|
||||
use ratatui::widgets::canvas::{Canvas, Line};
|
||||
use ratatui::{
|
||||
Terminal,
|
||||
backend::CrosstermBackend,
|
||||
layout::{Constraint, Direction, Layout},
|
||||
style::Color,
|
||||
widgets::{Block, Borders, List, ListItem, Paragraph},
|
||||
};
|
||||
use std::{collections::VecDeque, io::stdout, process::Command, thread, time::Duration};
|
||||
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>) {
|
||||
APP_STATE.lock().unwrap().push_log(from_key(&key.into()));
|
||||
}
|
||||
pub fn log_message(msg: impl Into<String>) {
|
||||
APP_STATE.lock().unwrap().push_log(msg.into());
|
||||
}
|
||||
fn smooth_data(data: &[(f64, f64)], window_size: usize) -> Vec<(f64, f64)> {
|
||||
if data.len() < window_size {
|
||||
return data.to_vec();
|
||||
}
|
||||
let mut smoothed = Vec::with_capacity(data.len());
|
||||
for i in 0..data.len() {
|
||||
let start = if i + 1 >= window_size {
|
||||
i + 1 - window_size
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let window = &data[start..=i];
|
||||
let avg = window.iter().map(|(_, y)| y).sum::<f64>() / window.len() as f64;
|
||||
smoothed.push((data[i].0, avg));
|
||||
}
|
||||
smoothed
|
||||
}
|
||||
|
||||
fn downsample_to_fit_width(data: &[(f64, f64)], width: u16) -> Vec<(f64, f64)> {
|
||||
let width_usize = (width as usize) * 2;
|
||||
let len = data.len();
|
||||
if len == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
let slice = if len <= width_usize {
|
||||
data.to_vec()
|
||||
} else {
|
||||
data[len - width_usize..].to_vec()
|
||||
};
|
||||
slice
|
||||
}
|
||||
|
||||
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() {
|
||||
enable_raw_mode().unwrap();
|
||||
|
||||
// Start a background thread to sample metrics
|
||||
thread::spawn(move || {
|
||||
let mut sys = System::new_with_specifics(RefreshKind::new());
|
||||
let mut last_total_received = 0u64;
|
||||
let mut last_total_transmitted = 0u64;
|
||||
let mut counter = 0.0;
|
||||
loop {
|
||||
sys.refresh_all();
|
||||
|
||||
let mut tcpu = 0;
|
||||
for cpu in sys.cpus() {
|
||||
tcpu += cpu.cpu_usage() as i64;
|
||||
tcpu /= 2;
|
||||
}
|
||||
let ram = (sys.used_memory() as f64 / sys.total_memory() as f64) * 100.0;
|
||||
|
||||
let mut total_received = 0u64;
|
||||
let mut total_transmitted = 0u64;
|
||||
|
||||
let delta_received = if last_total_received == 0 {
|
||||
0
|
||||
} else {
|
||||
total_received.saturating_sub(last_total_received)
|
||||
};
|
||||
let delta_transmitted = if last_total_transmitted == 0 {
|
||||
0
|
||||
} else {
|
||||
total_transmitted.saturating_sub(last_total_transmitted)
|
||||
};
|
||||
last_total_received = total_received;
|
||||
last_total_transmitted = total_transmitted;
|
||||
|
||||
let net_down = delta_received as f64;
|
||||
let net_up = delta_transmitted as f64;
|
||||
|
||||
{
|
||||
let mut st = APP_STATE.lock().unwrap();
|
||||
st.push_cpu((counter, tcpu as f64));
|
||||
st.push_ram((counter, ram));
|
||||
st.push_net_down((counter, net_down));
|
||||
st.push_net_up((counter, net_up));
|
||||
|
||||
st.sys_info = format!(
|
||||
"CPU: {:.1}% RAM: {:.1}%\nNetDown: {} NetUp: {}",
|
||||
tcpu, ram, delta_received, delta_transmitted
|
||||
);
|
||||
}
|
||||
|
||||
counter += 1.0;
|
||||
thread::sleep(Duration::from_millis(250));
|
||||
}
|
||||
});
|
||||
|
||||
// UI rendering loop in a separate thread
|
||||
thread::spawn(move || {
|
||||
let stdout = stdout();
|
||||
let mut terminal = Terminal::new(CrosstermBackend::new(stdout)).unwrap();
|
||||
|
||||
enable_raw_mode().unwrap();
|
||||
|
||||
loop {
|
||||
terminal
|
||||
.draw(|f| {
|
||||
let size = f.size();
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(40), Constraint::Percentage(60)])
|
||||
.split(size);
|
||||
|
||||
let left = chunks[0];
|
||||
let right = chunks[1];
|
||||
|
||||
{
|
||||
let st = APP_STATE.lock().unwrap();
|
||||
let items: Vec<ListItem> = st
|
||||
.logs
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|s| ListItem::new(s.clone()))
|
||||
.collect();
|
||||
let list = List::new(items)
|
||||
.block(Block::default().title("Logs").borders(Borders::ALL));
|
||||
f.render_widget(list, left);
|
||||
}
|
||||
|
||||
let right_chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Length(5), Constraint::Min(0)])
|
||||
.split(right);
|
||||
|
||||
{
|
||||
let st = APP_STATE.lock().unwrap();
|
||||
let header = Paragraph::new(st.sys_info.clone())
|
||||
.block(Block::default().title("System Info").borders(Borders::ALL));
|
||||
f.render_widget(header, right_chunks[0]);
|
||||
}
|
||||
|
||||
let grid_chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||
.split(right_chunks[1]);
|
||||
|
||||
let left_column = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||
.split(grid_chunks[0]);
|
||||
|
||||
let right_column = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||
.split(grid_chunks[1]);
|
||||
|
||||
let st = APP_STATE.lock().unwrap();
|
||||
let w = grid_chunks[0].width.saturating_sub(2);
|
||||
|
||||
let ping_ds = downsample_to_fit_width(&smooth_data(&st.ping, 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 down_ds = downsample_to_fit_width(&st.net_down, w);
|
||||
let up_ds = downsample_to_fit_width(&st.net_up, w);
|
||||
|
||||
// ---- Render CPU ----
|
||||
{
|
||||
let min_x = cpu_ds.first().map(|(x, _)| *x).unwrap_or(0.0);
|
||||
let max_x = cpu_ds.last().map(|(x, _)| *x).unwrap_or(100.0);
|
||||
|
||||
let canvas = Canvas::default()
|
||||
.block(Block::default().title("CPU %").borders(Borders::ALL))
|
||||
.x_bounds([min_x, max_x])
|
||||
.y_bounds([0.0, 100.0])
|
||||
.paint(|ctx| {
|
||||
for (x, y) in &cpu_ds {
|
||||
ctx.draw(&Line {
|
||||
x1: *x,
|
||||
y1: 0.0,
|
||||
x2: *x,
|
||||
y2: *y,
|
||||
color: Color::Cyan,
|
||||
});
|
||||
}
|
||||
});
|
||||
f.render_widget(canvas, left_column[0]);
|
||||
}
|
||||
|
||||
// ---- Render RAM ----
|
||||
{
|
||||
let min_x = ram_ds.first().map(|(x, _)| *x).unwrap_or(0.0);
|
||||
let max_x = ram_ds.last().map(|(x, _)| *x).unwrap_or(100.0);
|
||||
let canvas = Canvas::default()
|
||||
.block(Block::default().title("RAM %").borders(Borders::ALL))
|
||||
.x_bounds([min_x, max_x])
|
||||
.y_bounds([0.0, 100.0])
|
||||
.paint(|ctx| {
|
||||
for (x, y) in &ram_ds {
|
||||
ctx.draw(&Line {
|
||||
x1: *x,
|
||||
y1: 0.0,
|
||||
x2: *x,
|
||||
y2: *y,
|
||||
color: Color::Magenta,
|
||||
});
|
||||
}
|
||||
});
|
||||
f.render_widget(canvas, left_column[1]);
|
||||
}
|
||||
|
||||
// ---- Render Ping ----
|
||||
{
|
||||
let min_x = ping_ds.first().map(|(x, _)| *x).unwrap_or(0.0);
|
||||
let max_x = ping_ds.last().map(|(x, _)| *x).unwrap_or(100.0);
|
||||
let max_y = ping_ds.iter().map(|(_, y)| *y).fold(1.0, f64::max);
|
||||
|
||||
let canvas = Canvas::default()
|
||||
.block(
|
||||
Block::default()
|
||||
.title(format!("Ping {}(ms)", max_y))
|
||||
.borders(Borders::ALL),
|
||||
)
|
||||
.x_bounds([min_x, max_x])
|
||||
.y_bounds([0.0, max_y])
|
||||
.paint(|ctx| {
|
||||
for (x, y) in &ping_ds {
|
||||
ctx.draw(&Line {
|
||||
x1: *x,
|
||||
y1: 0.0,
|
||||
x2: *x,
|
||||
y2: *y,
|
||||
color: Color::Yellow,
|
||||
});
|
||||
}
|
||||
});
|
||||
f.render_widget(canvas, right_column[0]);
|
||||
}
|
||||
|
||||
// ---- Render Network ----
|
||||
{
|
||||
let min_x = up_ds.first().map(|(x, _)| *x).unwrap_or(0.0);
|
||||
let max_x = up_ds.last().map(|(x, _)| *x).unwrap_or(100.0);
|
||||
let mut max_sum: f64 = 1.0;
|
||||
for ((_, up), (_, down)) in up_ds.iter().zip(down_ds.iter()) {
|
||||
max_sum = max_sum.max(up + down);
|
||||
}
|
||||
|
||||
let canvas = Canvas::default()
|
||||
.block(
|
||||
Block::default()
|
||||
.title("Network Up/Down")
|
||||
.borders(Borders::ALL),
|
||||
)
|
||||
.x_bounds([min_x, max_x])
|
||||
.y_bounds([0.0, max_sum])
|
||||
.paint(|ctx| {
|
||||
for (x, dval) in &down_ds {
|
||||
ctx.draw(&Line {
|
||||
x1: *x,
|
||||
y1: 0.0,
|
||||
x2: *x,
|
||||
y2: *dval,
|
||||
color: Color::Red,
|
||||
});
|
||||
}
|
||||
for (x, uval) in &up_ds {
|
||||
let dval = down_ds
|
||||
.iter()
|
||||
.find(|(xx, _)| (*xx - *x).abs() < f64::EPSILON)
|
||||
.map(|(_, y)| *y)
|
||||
.unwrap_or(0.0);
|
||||
|
||||
ctx.draw(&Line {
|
||||
x1: *x,
|
||||
y1: dval,
|
||||
x2: *x,
|
||||
y2: dval + *uval,
|
||||
color: Color::Green,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
f.render_widget(canvas, right_column[1]);
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
});
|
||||
// Clean up terminal before exit
|
||||
disable_raw_mode().unwrap();
|
||||
stdout().execute(LeaveAlternateScreen).unwrap();
|
||||
}
|
||||
|
|
@ -1 +1,2 @@
|
|||
pub mod ratatui_interface;
|
||||
pub mod log_panel;
|
||||
pub mod ratatui_interface;
|
||||
|
|
|
|||
|
|
@ -9,19 +9,15 @@ pub fn create_languages() {
|
|||
let mut debug_messages = JsonValue::new_object();
|
||||
|
||||
// FRONTEND
|
||||
frontend_messages.insert(
|
||||
"USER_CONTEXT_GET_CONVERSATIONS",
|
||||
"User {} is loading conversations",
|
||||
);
|
||||
frontend_messages.insert(
|
||||
"USER_CONTEXT_GET_COMMUNITIES",
|
||||
"User {} is loading communities",
|
||||
);
|
||||
frontend_messages.insert("ADD_CONVERSATION", "User {} added {}");
|
||||
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 {}");
|
||||
|
||||
// OMIKRON
|
||||
omikron_messages.insert(
|
||||
"IdentificationResponse",
|
||||
"identification_response",
|
||||
"IOTA identified on Omikron, {} users!",
|
||||
);
|
||||
|
||||
|
|
@ -32,6 +28,7 @@ pub fn create_languages() {
|
|||
general_texts.insert("IOTA_ID", "IOTA ID: {}-####-####-####-############");
|
||||
general_texts.insert("USER_ID", "USER ID: {}");
|
||||
general_texts.insert("USER_IDS", "USER IDS: {}");
|
||||
general_texts.insert("SETUP_COMPLETED", "Launched");
|
||||
|
||||
// DEBUG
|
||||
debug_messages.insert("", "");
|
||||
|
|
@ -55,4 +52,9 @@ pub fn create_languages() {
|
|||
"debug.json",
|
||||
&debug_messages.to_string(),
|
||||
);
|
||||
save_file(
|
||||
"languages/en_INT",
|
||||
"general.json",
|
||||
&general_texts.to_string(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,9 +32,7 @@ pub fn from_key(key: &str) -> String {
|
|||
LANGUAGE_PACK
|
||||
.lock()
|
||||
.unwrap()
|
||||
.language
|
||||
.get(key)
|
||||
.unwrap_or(&String::new())
|
||||
.get_translation(key)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
|
|
@ -90,9 +88,18 @@ impl LanguagePack {
|
|||
self.language
|
||||
.insert(key.to_string(), value.as_str().unwrap().to_string());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_translation(&self, key: &str) -> &String {
|
||||
self.language.get(key).unwrap()
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
53
src/main.rs
53
src/main.rs
|
|
@ -1,41 +1,30 @@
|
|||
use base64::Engine;
|
||||
use base64::engine::general_purpose;
|
||||
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
|
||||
use futures_util::SinkExt;
|
||||
use json::JsonValue::String;
|
||||
use json::{self, JsonValue};
|
||||
use rand::Rng;
|
||||
use rand_core::OsRng;
|
||||
use rand_core::RngCore;
|
||||
use reqwest::header::PUBLIC_KEY_PINS_REPORT_ONLY;
|
||||
use sha2::{Digest, Sha256};
|
||||
use json::{self};
|
||||
use std::sync::Arc;
|
||||
use std::sync::LazyLock;
|
||||
use std::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
use x448::{PublicKey, Secret};
|
||||
|
||||
mod auth;
|
||||
mod data;
|
||||
mod eula;
|
||||
mod gui;
|
||||
mod langu;
|
||||
mod omikron;
|
||||
mod users;
|
||||
mod util;
|
||||
mod gui {
|
||||
pub mod ratatui_interface;
|
||||
}
|
||||
|
||||
mod auth;
|
||||
mod langu;
|
||||
use crate::auth::auth_connector::AuthConnector;
|
||||
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
||||
use crate::eula::*;
|
||||
use crate::gui::log_panel;
|
||||
use crate::gui::log_panel::{AppState, log_message, log_message_trans};
|
||||
use crate::langu::language_creator;
|
||||
use crate::langu::language_manager;
|
||||
use crate::omikron::omikron_connection::OmikronConnection;
|
||||
use crate::omikron::ping_pong_task::PingPongTask;
|
||||
use crate::users::user_manager::UserManager;
|
||||
use crate::users::user_profile::UserProfile;
|
||||
use crate::users::user_profile_full::UserProfileFull;
|
||||
use crate::util::config_util::{self, CONFIG, ConfigUtil};
|
||||
use crate::util::file_util;
|
||||
use crate::util::config_util::CONFIG;
|
||||
|
||||
pub static APP_STATE: LazyLock<Arc<Mutex<AppState>>> =
|
||||
LazyLock::new(|| Arc::new(Mutex::new(AppState::new())));
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
|
|
@ -46,8 +35,8 @@ async fn main() {
|
|||
//}
|
||||
|
||||
// UI
|
||||
gui::ratatui_interface::launch(false);
|
||||
|
||||
log_panel::setup();
|
||||
// LANGUAGE PACK
|
||||
language_creator::create_languages();
|
||||
|
||||
|
|
@ -68,9 +57,10 @@ async fn main() {
|
|||
|
||||
if !sb.is_empty() {
|
||||
sb.remove(0);
|
||||
sb = sb + ",";
|
||||
}
|
||||
println!(
|
||||
"IOTA ID: {}-####-####-####-############",
|
||||
log_message(format!(
|
||||
"IOTA ID: {}-####-####-####-############",
|
||||
CONFIG
|
||||
.lock()
|
||||
.unwrap()
|
||||
|
|
@ -79,8 +69,8 @@ async fn main() {
|
|||
.split("-")
|
||||
.next()
|
||||
.unwrap()
|
||||
);
|
||||
println!("User ID: {}", sb);
|
||||
));
|
||||
log_message(format!("User IDS: {}", sb));
|
||||
|
||||
// IDENTIFICATION ON OMIKRON
|
||||
let omikron: OmikronConnection = OmikronConnection::new();
|
||||
|
|
@ -88,10 +78,10 @@ async fn main() {
|
|||
let _ping_pong_task = PingPongTask::new(Arc::new(OmikronConnection::new()));
|
||||
omikron
|
||||
.send_message(
|
||||
CommunicationValue::new(CommunicationType::Identification)
|
||||
.add_data(DataTypes::UserIds, String(sb.to_string()))
|
||||
CommunicationValue::new(CommunicationType::identification)
|
||||
.add_data(DataTypes::user_ids, String(sb.to_string()))
|
||||
.add_data(
|
||||
DataTypes::IotaId,
|
||||
DataTypes::iota_id,
|
||||
String(CONFIG.lock().unwrap().get_iota_id().to_string()),
|
||||
)
|
||||
.to_json()
|
||||
|
|
@ -100,6 +90,7 @@ async fn main() {
|
|||
.to_string(),
|
||||
)
|
||||
.await;
|
||||
log_message_trans("SETUP_COMPLETED");
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
use crate::APP_STATE;
|
||||
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
||||
use crate::omikron::ping_pong_task::PingPongTask;
|
||||
use crate::gui::log_panel::log_message;
|
||||
use crate::gui::log_panel::log_message_trans;
|
||||
use crate::omikron::ping_pong_task::{self, PingPongTask};
|
||||
use crate::users::contact::Contact;
|
||||
use crate::users::user_community_util::UserCommunityUtil;
|
||||
use crate::util::chat_files::ChatFiles;
|
||||
|
|
@ -8,6 +11,7 @@ use futures_util::{SinkExt, StreamExt};
|
|||
use json::JsonValue;
|
||||
use json::number::Number;
|
||||
use std::collections::HashMap;
|
||||
use std::pin::Pin;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
|
@ -31,8 +35,9 @@ pub struct OmikronConnection {
|
|||
>,
|
||||
>,
|
||||
>,
|
||||
waiting: Arc<Mutex<HashMap<Uuid, Box<dyn Fn(CommunicationValue) + Send>>>>, // waiting for responses
|
||||
pingpong: Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>, // ping-pong handler
|
||||
waiting: Arc<Mutex<HashMap<Uuid, Box<dyn Fn(CommunicationValue) + Send + Sync>>>>, // waiting for responses
|
||||
pingpong: Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>, // ping-pong handler
|
||||
ping_pong_task: Arc<Mutex<Option<Arc<PingPongTask>>>>,
|
||||
}
|
||||
|
||||
impl OmikronConnection {
|
||||
|
|
@ -41,18 +46,27 @@ impl OmikronConnection {
|
|||
writer: Arc::new(Mutex::new(None)),
|
||||
waiting: Arc::new(Mutex::new(HashMap::new())),
|
||||
pingpong: Arc::new(Mutex::new(None)),
|
||||
ping_pong_task: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Connect loop with retry
|
||||
pub async fn connect<'a>(&'a self) {
|
||||
pub async fn connect(&self) {
|
||||
loop {
|
||||
match connect_async("wss://tensamin.methanium.net/ws/iota/").await {
|
||||
Ok((ws_stream, _)) => {
|
||||
let (write_half, read_half) = ws_stream.split();
|
||||
*self.writer.lock().await = Some(write_half);
|
||||
self.spawn_listener(read_half);
|
||||
self.start_ping_pong_task().await;
|
||||
self.spawn_listener(read_half).await;
|
||||
|
||||
let ppt = PingPongTask::new(Arc::new(self.clone()));
|
||||
*self.ping_pong_task.lock().await = Some(Arc::new(ppt.clone()));
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
ppt.send_ping();
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
@ -61,18 +75,6 @@ impl OmikronConnection {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start the PingPongTask
|
||||
pub async fn start_ping_pong_task(&self) {
|
||||
let ping_pong_task = PingPongTask::new(Arc::new(self.clone()));
|
||||
|
||||
// Store the task handle in `pingpong` so we can manage it
|
||||
let mut pingpong_handle = self.pingpong.lock().await;
|
||||
*pingpong_handle = Some(tokio::spawn(async move {
|
||||
ping_pong_task.run_ping_loop();
|
||||
}));
|
||||
}
|
||||
|
||||
pub async fn send_message(&self, msg: String) {
|
||||
Self::send_message_static(&self.writer, msg).await;
|
||||
}
|
||||
|
|
@ -87,7 +89,7 @@ impl OmikronConnection {
|
|||
}
|
||||
|
||||
/// Listener for all incoming messages
|
||||
fn spawn_listener(
|
||||
async fn spawn_listener(
|
||||
&self,
|
||||
mut read_half: futures_util::stream::SplitStream<
|
||||
WebSocketStream<MaybeTlsStream<TcpStream>>,
|
||||
|
|
@ -95,57 +97,60 @@ impl OmikronConnection {
|
|||
) {
|
||||
let waiting = self.waiting.clone();
|
||||
let writer = self.writer.clone();
|
||||
|
||||
let sel = self.clone();
|
||||
tokio::spawn(async move {
|
||||
while let Some(msg) = read_half.next().await {
|
||||
match msg {
|
||||
Ok(Message::Close(Some(frame))) => {
|
||||
println!("[Omikron] Closed: {:?}", frame);
|
||||
log_message(format!("[Omikron] Closed: {:?}", frame));
|
||||
break;
|
||||
}
|
||||
Ok(Message::Text(text)) => {
|
||||
let mut cv = CommunicationValue::from_json(&text); // needs CommunicationValue parser
|
||||
if cv.is_type(CommunicationType::Pong) {
|
||||
// handle pingpong reset here
|
||||
continue;
|
||||
if cv.is_type(CommunicationType::pong) {
|
||||
sel.ping_pong_task
|
||||
.lock()
|
||||
.await
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.handle_pong(&cv, true);
|
||||
return;
|
||||
}
|
||||
// ************************************************ //
|
||||
// Direct messages //
|
||||
// ************************************************ //
|
||||
println!("[Omikron] Received message: {:?}", cv);
|
||||
if cv.is_type(CommunicationType::MessageOtherIota) {
|
||||
|
||||
log_message_trans(format!("{:?}", &cv.comm_type));
|
||||
if cv.is_type(CommunicationType::message_other_iota) {
|
||||
let sender_id = &cv.get_sender();
|
||||
let receiver_id = &cv.get_receiver();
|
||||
|
||||
ChatFiles::add_message(
|
||||
cv.get_data(DataTypes::SendTime)
|
||||
cv.get_data(DataTypes::send_time)
|
||||
.unwrap()
|
||||
.as_i64()
|
||||
.unwrap_or(-1),
|
||||
false,
|
||||
*receiver_id,
|
||||
*sender_id,
|
||||
cv.get_data(DataTypes::MessageContent)
|
||||
cv.get_data(DataTypes::message_content)
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap(),
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
);
|
||||
let response = CommunicationValue::new(CommunicationType::MessageLive)
|
||||
let response = CommunicationValue::new(CommunicationType::message_live)
|
||||
.with_id(cv.get_id())
|
||||
.with_receiver(cv.get_receiver())
|
||||
.add_data(
|
||||
DataTypes::SendTime,
|
||||
cv.get_data(DataTypes::SendTime).unwrap().clone(),
|
||||
DataTypes::send_time,
|
||||
cv.get_data(DataTypes::send_time).unwrap().clone(),
|
||||
)
|
||||
.add_data(
|
||||
DataTypes::Message,
|
||||
cv.get_data(DataTypes::MessageContent).unwrap().clone(),
|
||||
DataTypes::message,
|
||||
cv.get_data(DataTypes::message_content).unwrap().clone(),
|
||||
)
|
||||
.add_data(
|
||||
DataTypes::SenderId,
|
||||
DataTypes::sender_id,
|
||||
JsonValue::String(cv.get_sender().clone().to_string()),
|
||||
);
|
||||
Self::send_message_static(
|
||||
|
|
@ -156,26 +161,24 @@ impl OmikronConnection {
|
|||
continue;
|
||||
}
|
||||
|
||||
if cv.is_type(CommunicationType::Message) {
|
||||
/*
|
||||
"user": {
|
||||
"avatar": true,
|
||||
"display": true,
|
||||
"timestamp": true
|
||||
},
|
||||
"body": {
|
||||
"tint": "<hex color>",
|
||||
"content": "<markdown (encrypted)>",
|
||||
"files": [
|
||||
{
|
||||
"name": "<cool name>",
|
||||
"id": "<uuid>",
|
||||
"type": "[ image | image_top_right | file ]"
|
||||
}
|
||||
]
|
||||
}
|
||||
if cv.is_type(CommunicationType::message) {
|
||||
/* DATA CONTAINER:
|
||||
"sent_by_self": true,
|
||||
"timestamp": unixTimestamp,
|
||||
"files": [ // wenn keine files dann weglassen
|
||||
{
|
||||
"name": "<name>",
|
||||
"id": "<uuid>",
|
||||
"type": "[ image | image_top_right | file ]"
|
||||
}
|
||||
],
|
||||
"content": "<enc markdown>"
|
||||
*/
|
||||
let my_id = cv.get_sender();
|
||||
let other_id = Uuid::from_str(
|
||||
&*cv.get_data(DataTypes::receiver_id).unwrap().to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
ChatFiles::add_message(
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
|
|
@ -183,20 +186,12 @@ impl OmikronConnection {
|
|||
.as_millis() as i64,
|
||||
true,
|
||||
my_id,
|
||||
Uuid::from_str(
|
||||
&*cv.get_data(DataTypes::ReceiverId).unwrap().to_string(),
|
||||
)
|
||||
.unwrap(),
|
||||
&*cv.get_data(DataTypes::MessageContent).unwrap().to_string(),
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
other_id,
|
||||
&*cv.get_data(DataTypes::message_content).unwrap().to_string(),
|
||||
);
|
||||
// ack
|
||||
let ack = CommunicationValue::ack_message(cv.get_id(), my_id);
|
||||
Self::send_message_static(&writer.clone(), ack.to_json().to_string())
|
||||
.await;
|
||||
// forward
|
||||
let forward = CommunicationValue::forward_to_other_iota(&mut cv);
|
||||
Self::send_message_static(
|
||||
&writer.clone(),
|
||||
|
|
@ -206,55 +201,52 @@ impl OmikronConnection {
|
|||
continue;
|
||||
}
|
||||
|
||||
if cv.is_type(CommunicationType::MessageGet) {
|
||||
if cv.is_type(CommunicationType::message_get) {
|
||||
let my_id = cv.get_sender();
|
||||
let partner_id = Uuid::from_str(
|
||||
&*cv.get_data(DataTypes::ChatPartnerId).unwrap().to_string(),
|
||||
&*cv.get_data(DataTypes::user_id).unwrap().to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
let offset = cv
|
||||
.get_data(DataTypes::LoadedMessages)
|
||||
.get_data(DataTypes::loaded_messages)
|
||||
.unwrap_or(&JsonValue::Null)
|
||||
.to_string()
|
||||
.parse::<i64>()
|
||||
.unwrap_or(0);
|
||||
let amount = cv
|
||||
.get_data(DataTypes::MessageAmount)
|
||||
.get_data(DataTypes::message_amount)
|
||||
.unwrap_or(&JsonValue::Null)
|
||||
.to_string()
|
||||
.parse::<i64>()
|
||||
.unwrap_or(0);
|
||||
|
||||
let messages =
|
||||
ChatFiles::get_messages(my_id, partner_id, offset, amount); // needs ChatFiles
|
||||
let mut resp = CommunicationValue::new(CommunicationType::MessageChunk)
|
||||
ChatFiles::get_messages(my_id, partner_id, offset, amount);
|
||||
let resp = CommunicationValue::new(CommunicationType::message_chunk)
|
||||
.with_id(cv.get_id())
|
||||
.with_receiver(my_id);
|
||||
if !messages.is_empty() {
|
||||
resp = resp.add_data(DataTypes::MessageChunk, messages);
|
||||
}
|
||||
.with_receiver(my_id)
|
||||
.add_data(DataTypes::message_chunk, messages);
|
||||
|
||||
Self::send_message_static(&writer.clone(), resp.to_json().to_string())
|
||||
.await;
|
||||
continue;
|
||||
}
|
||||
|
||||
if cv.is_type(CommunicationType::GetChats) {
|
||||
if cv.is_type(CommunicationType::get_chats) {
|
||||
let user_id = cv.get_sender();
|
||||
let users = get_users(user_id); // needs ChatsUtil
|
||||
let resp = CommunicationValue::new(CommunicationType::GetChats)
|
||||
let users = get_users(user_id);
|
||||
let resp = CommunicationValue::new(CommunicationType::get_chats)
|
||||
.with_id(cv.get_id())
|
||||
.with_receiver(user_id)
|
||||
.add_data(DataTypes::UserIds, users);
|
||||
println!("ALARM: {}", resp.to_json().to_string());
|
||||
.add_data(DataTypes::user_ids, users);
|
||||
Self::send_message_static(&writer.clone(), resp.to_json().to_string())
|
||||
.await;
|
||||
continue;
|
||||
}
|
||||
|
||||
if cv.is_type(CommunicationType::AddChat) {
|
||||
if cv.is_type(CommunicationType::add_chat) {
|
||||
let user_id = cv.get_sender();
|
||||
let other_id = Uuid::from_str(
|
||||
&*cv.get_data(DataTypes::UserId).unwrap().to_string(),
|
||||
&*cv.get_data(DataTypes::user_id).unwrap().to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut contact =
|
||||
|
|
@ -266,7 +258,7 @@ impl OmikronConnection {
|
|||
.as_millis() as i64,
|
||||
);
|
||||
mod_user(user_id, &contact);
|
||||
let resp = CommunicationValue::new(CommunicationType::AddChat)
|
||||
let resp = CommunicationValue::new(CommunicationType::add_chat)
|
||||
.with_id(cv.get_id())
|
||||
.with_receiver(user_id);
|
||||
Self::send_message_static(&writer.clone(), resp.to_json().to_string())
|
||||
|
|
@ -274,16 +266,16 @@ impl OmikronConnection {
|
|||
continue;
|
||||
}
|
||||
|
||||
if cv.is_type(CommunicationType::AddCommunity) {
|
||||
if cv.is_type(CommunicationType::add_community) {
|
||||
UserCommunityUtil::add_community(
|
||||
cv.get_sender(),
|
||||
cv.get_data(DataTypes::CommunityAddress)
|
||||
cv.get_data(DataTypes::community_address)
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
cv.get_data(DataTypes::CommunityTitle).unwrap().to_string(),
|
||||
cv.get_data(DataTypes::Position).unwrap().to_string(),
|
||||
cv.get_data(DataTypes::community_title).unwrap().to_string(),
|
||||
cv.get_data(DataTypes::position).unwrap().to_string(),
|
||||
);
|
||||
let resp = CommunicationValue::new(CommunicationType::AddCommunity)
|
||||
let resp = CommunicationValue::new(CommunicationType::add_community)
|
||||
.with_id(cv.get_id())
|
||||
.with_receiver(cv.get_sender());
|
||||
Self::send_message_static(&writer.clone(), resp.to_json().to_string())
|
||||
|
|
@ -291,12 +283,12 @@ impl OmikronConnection {
|
|||
continue;
|
||||
}
|
||||
|
||||
if cv.is_type(CommunicationType::GetCommunities) {
|
||||
let resp = CommunicationValue::new(CommunicationType::GetCommunities)
|
||||
if cv.is_type(CommunicationType::get_communities) {
|
||||
let resp = CommunicationValue::new(CommunicationType::get_communities)
|
||||
.with_id(cv.get_id())
|
||||
.with_receiver(cv.get_sender())
|
||||
.add_data(
|
||||
DataTypes::Communities,
|
||||
.add_array(
|
||||
DataTypes::communities,
|
||||
UserCommunityUtil::get_communities(cv.get_sender()),
|
||||
); // needs UserCommunityUtil
|
||||
Self::send_message_static(&writer.clone(), resp.to_json().to_string())
|
||||
|
|
@ -304,14 +296,14 @@ impl OmikronConnection {
|
|||
continue;
|
||||
}
|
||||
|
||||
if cv.is_type(CommunicationType::RemoveCommunity) {
|
||||
if cv.is_type(CommunicationType::remove_community) {
|
||||
UserCommunityUtil::remove_community(
|
||||
cv.get_sender(),
|
||||
cv.get_data(DataTypes::CommunityAddress)
|
||||
cv.get_data(DataTypes::community_address)
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
); // needs UserCommunityUtil
|
||||
let resp = CommunicationValue::new(CommunicationType::RemoveCommunity)
|
||||
let resp = CommunicationValue::new(CommunicationType::remove_community)
|
||||
.with_id(cv.get_id())
|
||||
.with_receiver(cv.get_sender());
|
||||
Self::send_message_static(&writer.clone(), resp.to_json().to_string())
|
||||
|
|
@ -320,7 +312,7 @@ impl OmikronConnection {
|
|||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[Omikron] Error: {}", e);
|
||||
log_message(format!("[Omikron] Error: {}", e));
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -351,23 +343,11 @@ impl OmikronConnection {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn on_answer<F>(&self, message_id: Uuid, callback: F)
|
||||
where
|
||||
F: Fn(CommunicationValue) + Send + 'static,
|
||||
{
|
||||
tokio::spawn({
|
||||
let waiting = self.waiting.clone();
|
||||
async move {
|
||||
waiting.lock().await.insert(message_id, Box::new(callback));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn send_ping_message(&self, uuid: Uuid) {
|
||||
// Send the ping message over the connection
|
||||
let ping_message = CommunicationValue::new(CommunicationType::Ping)
|
||||
let ping_message = CommunicationValue::new(CommunicationType::ping)
|
||||
.with_id(uuid)
|
||||
.add_data_num(DataTypes::LastPing, Number::from(2))
|
||||
.add_data_num(DataTypes::last_ping, Number::from(2))
|
||||
.to_json()
|
||||
.to_string();
|
||||
self.send_message(ping_message).await;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
use crate::APP_STATE;
|
||||
use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes};
|
||||
use crate::gui::log_panel::AppState;
|
||||
use crate::omikron::omikron_connection::OmikronConnection;
|
||||
use color_eyre::owo_colors::OwoColorize;
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use std::arch::x86_64::_SIDD_MASKED_NEGATIVE_POLARITY;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
|
@ -10,12 +13,22 @@ use uuid::Uuid;
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct PingPongTask {
|
||||
pub parent: Arc<OmikronConnection>, // assuming OmikronConnection is your connection type
|
||||
pub parent: Arc<OmikronConnection>,
|
||||
pub message_send_times: Arc<Mutex<HashMap<Uuid, Instant>>>,
|
||||
pub no_ping_in: Arc<Mutex<i32>>,
|
||||
pub last_ping: Arc<Mutex<Option<u64>>>,
|
||||
}
|
||||
fn assert_send_sync<T: Send + Sync>() {}
|
||||
|
||||
#[test]
|
||||
fn check_omikron_connection_send_sync() {
|
||||
assert_send_sync::<OmikronConnection>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_pingpong_task_send_sync() {
|
||||
assert_send_sync::<PingPongTask>();
|
||||
}
|
||||
impl PingPongTask {
|
||||
pub fn new(parent: Arc<OmikronConnection>) -> Self {
|
||||
let message_send_times = Arc::new(Mutex::new(HashMap::new()));
|
||||
|
|
@ -29,50 +42,32 @@ impl PingPongTask {
|
|||
last_ping: last_ping.clone(),
|
||||
};
|
||||
|
||||
// Spawn the periodic ping task
|
||||
tokio::spawn({
|
||||
let task = task.clone(); // Clone the task (Arc) so that it lives long enough for the async task
|
||||
async move {
|
||||
task.run_ping_loop().await;
|
||||
}
|
||||
});
|
||||
|
||||
task
|
||||
}
|
||||
|
||||
pub async fn run_ping_loop(&self) {
|
||||
loop {
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
self.send_ping().await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_ping(&self) {
|
||||
let uuid = Uuid::new_v4();
|
||||
let send_time = Instant::now();
|
||||
|
||||
{
|
||||
let mut message_send_times = self.message_send_times.lock().await;
|
||||
pub fn send_ping(&self) {
|
||||
let sel = self.clone();
|
||||
tokio::spawn(async move {
|
||||
let uuid = Uuid::new_v4();
|
||||
let send_time = Instant::now();
|
||||
let mut message_send_times = sel.message_send_times.lock().await;
|
||||
message_send_times.insert(uuid, send_time);
|
||||
}
|
||||
|
||||
let no_ping_in = {
|
||||
let no_ping_in = self.no_ping_in.lock().await;
|
||||
*no_ping_in
|
||||
};
|
||||
let no_ping_in_val = {
|
||||
let no_ping_in = sel.no_ping_in.lock().await;
|
||||
*no_ping_in
|
||||
};
|
||||
|
||||
if no_ping_in != -1 {
|
||||
// Connection slow or disconnected
|
||||
self.handle_slow_connection(no_ping_in).await;
|
||||
} else {
|
||||
// Connection is fine
|
||||
self.parent.send_ping_message(uuid).await;
|
||||
}
|
||||
if no_ping_in_val != -1 {
|
||||
sel.handle_slow_connection(no_ping_in_val).await;
|
||||
} else {
|
||||
sel.parent.send_ping_message(uuid).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn handle_slow_connection(&self, no_ping_in: i32) {
|
||||
if no_ping_in > 8 {
|
||||
// Attempt reconnection if ping times out
|
||||
self.parent.reconnect().await;
|
||||
self.reconnect().await;
|
||||
}
|
||||
|
|
@ -80,37 +75,39 @@ impl PingPongTask {
|
|||
|
||||
pub async fn reconnect(&self) {
|
||||
let mut no_ping_in = self.no_ping_in.lock().await;
|
||||
*no_ping_in = -1; // Reset slow count
|
||||
*no_ping_in = -1;
|
||||
}
|
||||
|
||||
pub async fn handle_pong(&self, cv: &CommunicationValue) {
|
||||
let send_time = {
|
||||
let message_send_times = self.message_send_times.lock().await;
|
||||
message_send_times.get(&cv.get_id()).cloned()
|
||||
};
|
||||
pub fn handle_pong(&self, cv: &CommunicationValue, log: bool) {
|
||||
let sel = self.clone();
|
||||
let cv = cv.clone();
|
||||
tokio::spawn(async move {
|
||||
let send_time = {
|
||||
let message_send_times = sel.message_send_times.lock().await;
|
||||
message_send_times.get(&cv.get_id()).cloned()
|
||||
};
|
||||
|
||||
if let Some(send_time) = send_time {
|
||||
let receive_time = Instant::now();
|
||||
let ping = receive_time.duration_since(send_time).as_millis() as u64;
|
||||
if let Some(send_time) = send_time {
|
||||
let receive_time = Instant::now();
|
||||
let ping = receive_time.duration_since(send_time).as_millis() as u64;
|
||||
|
||||
{
|
||||
let mut last_ping = self.last_ping.lock().await;
|
||||
let mut last_ping = sel.last_ping.lock().await;
|
||||
*last_ping = Some(ping);
|
||||
}
|
||||
|
||||
{
|
||||
let mut no_ping_in = self.no_ping_in.lock().await;
|
||||
let mut no_ping_in = sel.no_ping_in.lock().await;
|
||||
*no_ping_in = -1;
|
||||
let mut message_send_times = sel.message_send_times.lock().await;
|
||||
message_send_times.remove(&cv.get_id());
|
||||
|
||||
if log {
|
||||
APP_STATE.lock().unwrap().push_log("12.0".to_string());
|
||||
APP_STATE.lock().unwrap().push_ping_val(12.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn cancel(&self) {
|
||||
// Cancel or stop the task
|
||||
let mut no_ping_in = self.no_ping_in.lock().await;
|
||||
*no_ping_in = -1; // Reset the counter
|
||||
|
||||
// Example of stopping the ping task gracefully
|
||||
// If using a task join handle or similar
|
||||
*no_ping_in = -1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use json::{self, JsonValue};
|
||||
use crate::util::file_util::save_file;
|
||||
use json::{self, Array, JsonValue};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use uuid::Uuid;
|
||||
|
|
@ -7,8 +8,8 @@ pub struct UserCommunityUtil;
|
|||
|
||||
impl UserCommunityUtil {
|
||||
pub fn add_community(storage_owner: Uuid, address: String, title: String, position: String) {
|
||||
let path = format!("users/{}/communities.json", storage_owner);
|
||||
let mut communities: JsonValue = Self::load_array(&path);
|
||||
let file_path = format!("users/{}/", storage_owner);
|
||||
let mut communities = Self::load_array(&file_path);
|
||||
|
||||
let mut community = JsonValue::new_object();
|
||||
community["title"] = JsonValue::String(title);
|
||||
|
|
@ -16,44 +17,52 @@ impl UserCommunityUtil {
|
|||
community["position"] = JsonValue::String(position);
|
||||
|
||||
communities.push(community);
|
||||
Self::save_array(&path, communities);
|
||||
|
||||
save_file(
|
||||
&file_path,
|
||||
"communities.json",
|
||||
&JsonValue::Array(communities).to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn remove_community(storage_owner: Uuid, community_address: String) {
|
||||
let path = format!("users/{}/communities.json", storage_owner);
|
||||
let mut communities = Self::load_array(&path);
|
||||
let file_path = format!("users/{}/", storage_owner);
|
||||
let communities = Self::load_array(&file_path);
|
||||
|
||||
let mut new_array = JsonValue::new_array();
|
||||
for entry in communities.members() {
|
||||
if entry["address"].as_str() != Some(&community_address) {
|
||||
new_array.push(entry.clone()).unwrap();
|
||||
let filtered: Array = communities
|
||||
.iter()
|
||||
.filter(|entry| entry["address"].as_str() != Some(&community_address))
|
||||
.cloned()
|
||||
.collect();
|
||||
save_file(
|
||||
&file_path,
|
||||
"communities.json",
|
||||
&JsonValue::Array(filtered).to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn get_communities(storage_owner: Uuid) -> Array {
|
||||
let file_path = format!("users/{}/communities.json", storage_owner);
|
||||
Self::load_array(&file_path)
|
||||
}
|
||||
|
||||
fn load_array(file_path: &str) -> Array {
|
||||
if !Path::new(file_path).exists() {
|
||||
return Array::new();
|
||||
}
|
||||
|
||||
match fs::read_to_string(file_path) {
|
||||
Ok(content) => {
|
||||
let parsed = json::parse(&content);
|
||||
match parsed {
|
||||
Ok(JsonValue::Array(arr)) => arr,
|
||||
_ => Array::new(),
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Failed to read file {}: {}", file_path, err);
|
||||
Array::new()
|
||||
}
|
||||
}
|
||||
|
||||
Self::save_array(&path, new_array);
|
||||
}
|
||||
|
||||
pub fn get_communities(storage_owner: Uuid) -> JsonValue {
|
||||
let path = format!("users/{}/communities.json", storage_owner);
|
||||
Self::load_array(&path)
|
||||
}
|
||||
|
||||
fn load_array(path: &str) -> JsonValue {
|
||||
if !Path::new(path).exists() {
|
||||
return JsonValue::new_object();
|
||||
}
|
||||
|
||||
match fs::read_to_string(path) {
|
||||
Ok(content) => json::parse(&content).unwrap_or_else(|_| JsonValue::new_object()),
|
||||
Err(_) => JsonValue::new_object(),
|
||||
}
|
||||
}
|
||||
|
||||
fn save_array(path: &str, arr: JsonValue) {
|
||||
if let Some(parent) = Path::new(path).parent() {
|
||||
let _ = fs::create_dir_all(parent);
|
||||
}
|
||||
|
||||
let _ = fs::write(path, arr.pretty(3));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::auth::auth_connector::AuthConnector;
|
||||
use crate::gui::log_panel::log_message;
|
||||
use crate::users::user_manager::UserManager;
|
||||
use base64::{Engine as _, engine::general_purpose};
|
||||
use json::{JsonValue, object, stringify};
|
||||
|
|
@ -72,15 +73,14 @@ impl UserProfile {
|
|||
reset_token,
|
||||
);
|
||||
|
||||
// Migration hook (stubbed, since AuthConnector isn’t implemented here)
|
||||
if j.has_key("migrate")
|
||||
|| j.has_key("migrating")
|
||||
|| j.has_key("changing")
|
||||
|| j.has_key("move")
|
||||
|| j.has_key("moving")
|
||||
{
|
||||
if AuthConnector::migrate_user(&mut up, stringify!("{}", Uuid::new_v4())).await {
|
||||
println!("[INFO] Migration triggered for {}", up.username);
|
||||
if AuthConnector::migrate_user(&mut up).await {
|
||||
log_message(format!("[INFO] Migration triggered for {}", up.username));
|
||||
UserManager::set_unique(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -93,7 +93,6 @@ impl UserProfile {
|
|||
OsRng.fill(bytes.as_mut());
|
||||
let new_token = general_purpose::STANDARD.encode(&bytes);
|
||||
self.reset_token = new_token.clone();
|
||||
UserManager::save_users().ok();
|
||||
new_token
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ impl ChatFiles {
|
|||
}
|
||||
}
|
||||
|
||||
fn save_file(dir: &str, file_name: &str, content: &str) -> std::io::Result<()> {
|
||||
fs::create_dir_all(dir)?;
|
||||
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)?;
|
||||
file.write_all(content.as_bytes())
|
||||
let mut file = File::create(path).unwrap();
|
||||
file.write_all(content.as_bytes());
|
||||
}
|
||||
pub fn add_message(
|
||||
send_time: i64,
|
||||
|
|
@ -49,10 +49,7 @@ impl ChatFiles {
|
|||
storage_owner: Uuid,
|
||||
external_user: Uuid,
|
||||
message: &str,
|
||||
avatar: bool,
|
||||
timestamp: bool,
|
||||
tint: i64,
|
||||
) -> std::io::Result<()> {
|
||||
) {
|
||||
let user_dir = format!("users/{}/chats/{}", storage_owner, external_user);
|
||||
|
||||
let mut chunk_index = 0;
|
||||
|
|
@ -88,7 +85,7 @@ impl ChatFiles {
|
|||
&user_dir,
|
||||
&format!("msgs_{}.json", chunk_index),
|
||||
&message_chunk.dump(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
pub fn change_message_state(
|
||||
|
|
@ -127,7 +124,7 @@ impl ChatFiles {
|
|||
}
|
||||
|
||||
if modified {
|
||||
Self::save_file(&user_dir, &fname_str, &chunk.dump())?;
|
||||
Self::save_file(&user_dir, &fname_str, &chunk.dump());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue