Browse Source

Documentation and serialization

master
Lev 3 years ago
parent
commit
84e1b5c879
  1. 16
      degeon/src/chat.rs
  2. 124
      degeon/src/degeon_worker.rs
  3. 5
      degeon/src/gui_events.rs
  4. 5
      degeon/src/main.rs
  5. 11
      degeon/src/message.rs
  6. 143
      degeon/src/state.rs
  7. 1
      degeon/src/styles.rs
  8. 6
      src/interfaces/ip.rs
  9. 17
      src/ironforce.rs

16
degeon/src/chat.rs

@ -5,17 +5,26 @@ use crate::gui_events::GuiEvent;
use crate::message::{DegMessage, DegMessageContent, Profile}; use crate::message::{DegMessage, DegMessageContent, Profile};
use crate::state; use crate::state;
use crate::styles::style; use crate::styles::style;
use serde::{Serialize, Deserialize};
#[derive(Clone, Debug)]
/// A chat in the messenger
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Chat { pub struct Chat {
/// Public key of the other user
pub pkey: PublicKey, pub pkey: PublicKey,
/// Messages in this chat
pub messages: Vec<DegMessage>, pub messages: Vec<DegMessage>,
/// Profile of the other user
pub profile: Profile, pub profile: Profile,
/// Scroll position
pub scrolled: f32, pub scrolled: f32,
/// Message in the field
pub input: String, pub input: String,
} }
impl Chat { impl Chat {
/// Create a new chat
pub fn new(pkey: PublicKey) -> Self { pub fn new(pkey: PublicKey) -> Self {
Self { Self {
pkey, pkey,
@ -28,6 +37,7 @@ impl Chat {
} }
impl Chat { impl Chat {
/// Render header of the chat
pub fn header<'a>(name: String) -> Element<'a, GuiEvent> { pub fn header<'a>(name: String) -> Element<'a, GuiEvent> {
iced::container::Container::new(Text::new(name.as_str()).color(iced::Color::WHITE)) iced::container::Container::new(Text::new(name.as_str()).color(iced::Color::WHITE))
.style(style::Container::Primary) .style(style::Container::Primary)
@ -37,6 +47,7 @@ impl Chat {
.into() .into()
} }
/// Render the sending field
pub fn send_field<'a>( pub fn send_field<'a>(
input: String, input: String,
text_input_state: &'a mut iced::text_input::State, text_input_state: &'a mut iced::text_input::State,
@ -64,6 +75,7 @@ impl Chat {
.into() .into()
} }
/// Render chat preview
pub fn preview<'a>( pub fn preview<'a>(
&'a self, &'a self,
state: &'a mut button::State, state: &'a mut button::State,
@ -82,6 +94,7 @@ impl Chat {
.into() .into()
} }
/// Render the chat view
pub fn view<'a>( pub fn view<'a>(
&'a self, &'a self,
text_input_state: &'a mut iced::text_input::State, text_input_state: &'a mut iced::text_input::State,
@ -110,6 +123,7 @@ impl Chat {
.into() .into()
} }
/// Create an example chat
pub fn example(i: usize, my_keys: &Keys) -> Chat { pub fn example(i: usize, my_keys: &Keys) -> Chat {
let pkey = Keys::generate().get_public(); let pkey = Keys::generate().get_public();
Self { Self {

124
degeon/src/degeon_worker.rs

@ -7,24 +7,46 @@ use ironforce::{IronForce, Keys, Message, MessageType, PublicKey};
use std::pin::Pin; use std::pin::Pin;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use serde::{Serialize, Deserialize};
/// The container for logic, data, IF and protocol interactions
#[derive(Clone)] #[derive(Clone)]
pub struct Degeon { pub struct Degeon {
/// The list of all chats for this instance
pub chats: Vec<Chat>, pub chats: Vec<Chat>,
pub my_name: String, /// Profile of this user
pub profile: Profile,
/// Keys of this user
pub keys: Keys, pub keys: Keys,
/// The IF worker
pub ironforce: Arc<Mutex<IronForce>>, pub ironforce: Arc<Mutex<IronForce>>,
} }
impl Default for Degeon { /// Data for serialization
fn default() -> Self { #[derive(Serialize, Deserialize)]
pub struct DegeonData {
pub chats: Vec<Chat>,
pub profile: Profile,
pub keys: Keys,
}
/// Load IF and launch the main loop
///
/// Returns ironforce and keys
fn get_initialized_ironforce() -> (Arc<Mutex<IronForce>>, Keys) {
let ironforce = IronForce::from_file("".to_string()).unwrap(); let ironforce = IronForce::from_file("".to_string()).unwrap();
let keys = ironforce.keys.clone(); let keys = ironforce.keys.clone();
println!("ID: {}", keys.get_public().get_short_id()); println!("ID: {}", keys.get_public().get_short_id());
let (_thread, ironforce) = ironforce.launch_main_loop(1000); let (_thread, ironforce) = ironforce.launch_main_loop(1000);
(ironforce, keys)
}
impl Default for Degeon {
fn default() -> Self {
let (ironforce, keys) = get_initialized_ironforce();
Self { Self {
chats: vec![], chats: vec![],
my_name: "".to_string(), profile: Profile::default(),
keys, keys,
ironforce, ironforce,
} }
@ -32,16 +54,17 @@ impl Default for Degeon {
} }
impl Degeon { impl Degeon {
/// Get profile for the current user
pub fn get_profile(&self) -> Profile { pub fn get_profile(&self) -> Profile {
Profile { self.profile.clone()
name: self.my_name.clone(),
}
} }
/// Find a chat in the list for a given public key
pub fn chat_with(&self, pkey: &PublicKey) -> Option<usize> { pub fn chat_with(&self, pkey: &PublicKey) -> Option<usize> {
self.chats.iter().position(|chat| &chat.pkey == pkey) self.chats.iter().position(|chat| &chat.pkey == pkey)
} }
/// Process the incoming message and act accordingly
pub fn process_message(&self, msg: ironforce::Message) -> IFResult<Option<GuiEvent>> { pub fn process_message(&self, msg: ironforce::Message) -> IFResult<Option<GuiEvent>> {
let deg_msg: ProtocolMsg = let deg_msg: ProtocolMsg =
match serde_json::from_slice(msg.get_decrypted(&self.keys)?.as_slice()) { match serde_json::from_slice(msg.get_decrypted(&self.keys)?.as_slice()) {
@ -49,7 +72,11 @@ impl Degeon {
Err(_) => return Ok(None), Err(_) => return Ok(None),
}; };
let sender = msg.get_sender(&self.keys).unwrap(); let sender = msg.get_sender(&self.keys).unwrap();
println!("check_rec: {:?}, sender==self: {:?}", msg.check_recipient(&self.keys), sender == self.keys.get_public()); println!(
"check_rec: {:?}, sender==self: {:?}",
msg.check_recipient(&self.keys),
sender == self.keys.get_public()
);
if !msg.check_recipient(&self.keys) || sender == self.keys.get_public() { if !msg.check_recipient(&self.keys) || sender == self.keys.get_public() {
return Ok(None); return Ok(None);
} }
@ -65,6 +92,7 @@ impl Degeon {
}) })
} }
/// Send a multicast message through the network
pub fn send_multicast(&self, msg: ProtocolMsg) -> IFResult<()> { pub fn send_multicast(&self, msg: ProtocolMsg) -> IFResult<()> {
self.ironforce.lock().unwrap().send_to_all( self.ironforce.lock().unwrap().send_to_all(
Message::build() Message::build()
@ -75,6 +103,7 @@ impl Degeon {
) )
} }
/// Send a message to a target through the network
pub fn send_message(&self, msg: ProtocolMsg, target: &PublicKey) -> IFResult<()> { pub fn send_message(&self, msg: ProtocolMsg, target: &PublicKey) -> IFResult<()> {
// if self.ironforce.lock().unwrap().get_tunnel(target).is_none() { // if self.ironforce.lock().unwrap().get_tunnel(target).is_none() {
// println!("Creating a tunnel"); // println!("Creating a tunnel");
@ -101,13 +130,14 @@ impl Degeon {
) )
} }
/// Created an iced command that sends a message to a target
pub fn get_send_command( pub fn get_send_command(
&self, &self,
msg: ProtocolMsg, msg: ProtocolMsg,
target: &PublicKey, target: &PublicKey,
) -> iced::Command<GuiEvent> { ) -> iced::Command<GuiEvent> {
let if_clone = self.ironforce.clone(); let if_clone = self.ironforce.clone();
let target = target.clone(); let _target = target.clone();
let keys = self.keys.clone(); let keys = self.keys.clone();
println!("Creating a send command: {:?}", msg); println!("Creating a send command: {:?}", msg);
@ -120,6 +150,7 @@ impl Degeon {
Message::build() Message::build()
.message_type(MessageType::Broadcast) .message_type(MessageType::Broadcast)
.content(serde_json::to_vec(&msg).unwrap()) .content(serde_json::to_vec(&msg).unwrap())
// todo:
// .recipient(&target) // .recipient(&target)
.sign(&keys) .sign(&keys)
.build() .build()
@ -130,6 +161,8 @@ impl Degeon {
}) })
} }
/// Create an iced command that sends a message through the network to a target
#[allow(dead_code)]
pub fn get_send_multicast_command(&self, msg: ProtocolMsg) -> iced::Command<GuiEvent> { pub fn get_send_multicast_command(&self, msg: ProtocolMsg) -> iced::Command<GuiEvent> {
let keys = self.keys.clone(); let keys = self.keys.clone();
let if_clone = self.ironforce.clone(); let if_clone = self.ironforce.clone();
@ -155,17 +188,84 @@ impl Degeon {
} }
} }
const DEFAULT_FILENAME: &str = ".degeon.json";
impl Degeon {
/// Store most of the necessary data to string
pub fn serialize_to_string(&self) -> serde_json::Result<String> {
let data = DegeonData {
chats: self.chats.clone(),
profile: self.get_profile(),
keys: self.keys.clone(),
};
serde_json::to_string(&data)
}
/// Restore `Degeon` from serialized data
pub fn restore_from_string(data: String) -> IFResult<Self> {
let data_res: serde_json::Result<DegeonData> = serde_json::from_str(data.as_str());
let data = match data_res {
Ok(r) => r,
Err(_) => return Ok(Self::default()),
};
let (ironforce, _keys) = get_initialized_ironforce();
ironforce.lock().unwrap().keys = data.keys.clone();
let deg = Degeon {
chats: data.chats,
profile: data.profile,
keys: data.keys,
ironforce
};
Ok(deg)
}
/// Save to a file. If no filename is provided, the default is used
pub fn save_to_file(&self, filename: String) -> IFResult<()> {
let data = self.serialize_to_string()?;
let filename = if filename.is_empty() {
DEFAULT_FILENAME.to_string()
} else {
filename
};
std::fs::write(filename, data)?;
Ok(())
}
/// Restore from a file. If no filename is provided, the default is used
pub fn restore_from_file(filename: String) -> IFResult<Self> {
let filename = if filename.is_empty() {
DEFAULT_FILENAME.to_string()
} else {
filename
};
let content = std::fs::read_to_string(filename).unwrap_or_default();
Self::restore_from_string(content)
}
}
impl Stream for Degeon { impl Stream for Degeon {
type Item = GuiEvent; type Item = GuiEvent;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let timestamp_0 = std::time::Instant::now(); let timestamp_0 = std::time::Instant::now();
let msg_raw = self.ironforce.lock().unwrap().read_message(); let msg_raw = self.ironforce.lock().unwrap().read_message();
let msg = msg_raw.as_ref().map(|msg| self.process_message(msg.clone()).unwrap()); let msg = msg_raw
.as_ref()
.map(|msg| self.process_message(msg.clone()).unwrap());
if msg_raw.is_some() { if msg_raw.is_some() {
let msg_deg: ProtocolMsg = match serde_json::from_slice(msg_raw.as_ref().unwrap().get_decrypted(&self.keys).unwrap().as_slice()) { let msg_deg: ProtocolMsg = match serde_json::from_slice(
msg_raw
.as_ref()
.unwrap()
.get_decrypted(&self.keys)
.unwrap()
.as_slice(),
) {
Ok(r) => r, Ok(r) => r,
Err(_) => {println!("Couldn't deserialize {:?}", msg_raw); return Poll::Ready(Some(GuiEvent::None))} Err(_) => {
println!("Couldn't deserialize {:?}", msg_raw);
return Poll::Ready(Some(GuiEvent::None));
}
}; };
println!("{:?} -> {:?}", msg_deg, msg); println!("{:?} -> {:?}", msg_deg, msg);
} }

5
degeon/src/gui_events.rs

@ -1,5 +1,6 @@
use crate::message::{DegMessage, Profile}; use crate::message::{DegMessage, Profile};
use ironforce::PublicKey; use ironforce::PublicKey;
use crate::state::AppScreen;
/// An enum with all possible events for this application /// An enum with all possible events for this application
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -10,14 +11,14 @@ pub enum GuiEvent {
Typed(String), Typed(String),
/// The user clicked "Send" /// The user clicked "Send"
SendMessage, SendMessage,
/// New chat shall be created because we have an incoming request
NewChat(PublicKey),
/// A new messaged arrived /// A new messaged arrived
NewMessageInChat(PublicKey, DegMessage), NewMessageInChat(PublicKey, DegMessage),
/// A profile response arrived and we should store it /// A profile response arrived and we should store it
SetProfile(PublicKey, Profile), SetProfile(PublicKey, Profile),
/// We should send profile (in response to profile request) /// We should send profile (in response to profile request)
WeHaveToSendProfile(PublicKey), WeHaveToSendProfile(PublicKey),
/// Go to another screen
ChangeScreen(AppScreen),
/// Nothing happened /// Nothing happened
None, None,
} }

5
degeon/src/main.rs

@ -7,13 +7,8 @@ mod styles;
mod degeon_worker; mod degeon_worker;
use iced::Application; use iced::Application;
use ironforce::res::IFResult;
use ironforce::{IronForce, Message, MessageType, PublicKey};
use crate::state::DegeonApp; use crate::state::DegeonApp;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
// let ironforce = IronForce::from_file("".to_string()).unwrap();
// let _if_keys = ironforce.keys.clone();
Ok(DegeonApp::run(iced::Settings::default())?) Ok(DegeonApp::run(iced::Settings::default())?)
} }

11
degeon/src/message.rs

@ -1,6 +1,7 @@
use ironforce::PublicKey; use ironforce::PublicKey;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// A message in the messenger
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DegMessage { pub struct DegMessage {
pub sender: PublicKey, pub sender: PublicKey,
@ -9,6 +10,7 @@ pub struct DegMessage {
} }
impl DegMessage { impl DegMessage {
/// Create a simple text message
pub fn new_text(text: String, my_key: &PublicKey) -> DegMessage { pub fn new_text(text: String, my_key: &PublicKey) -> DegMessage {
Self { Self {
sender: my_key.clone(), sender: my_key.clone(),
@ -18,6 +20,7 @@ impl DegMessage {
} }
} }
/// The content of the message
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum DegMessageContent { pub enum DegMessageContent {
Text(String), Text(String),
@ -25,15 +28,21 @@ pub enum DegMessageContent {
Service, Service,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] /// User's profile
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct Profile { pub struct Profile {
pub name: String, pub name: String,
} }
/// A protocol message (that's sent through IF)
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ProtocolMsg { pub enum ProtocolMsg {
/// Requesting profile
ProfileRequest, ProfileRequest,
/// Responding to the profile request with a profile
ProfileResponse(Profile), ProfileResponse(Profile),
/// A peer discovery
Ping, Ping,
/// A message is sent
NewMessage(DegMessage), NewMessage(DegMessage),
} }

143
degeon/src/state.rs

@ -1,21 +1,18 @@
use crate::chat::Chat; use crate::chat::Chat;
use crate::degeon_worker::Degeon; use crate::degeon_worker::Degeon;
use crate::gui_events::GuiEvent; use crate::gui_events::GuiEvent;
use crate::message::{DegMessage, DegMessageContent, Profile, ProtocolMsg}; use crate::message::{DegMessage, DegMessageContent, ProtocolMsg};
use crate::styles::style; use crate::styles::style;
use core::default::Default; use core::default::Default;
use futures::Stream; use iced::window::Mode;
use iced::{ use iced::{
button, Align, Application, Button, Column, Element, HorizontalAlignment, Length, Row, Text, button, Align, Application, Button, Color, Column, Element, HorizontalAlignment, Length, Row,
TextInput, VerticalAlignment, Settings, Text, TextInput, VerticalAlignment,
}; };
use ironforce::res::{IFError, IFResult}; use ironforce::res::{IFError, IFResult};
use ironforce::{IronForce, Keys, Message, MessageType, PublicKey}; use ironforce::PublicKey;
use std::hash::Hash;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
/// Render a message into an iced `Element`
pub fn view_message(msg: &DegMessage, pkey: PublicKey) -> Option<Element<GuiEvent>> { pub fn view_message(msg: &DegMessage, pkey: PublicKey) -> Option<Element<GuiEvent>> {
let is_from_me = pkey != msg.sender; let is_from_me = pkey != msg.sender;
match &msg.content { match &msg.content {
@ -44,18 +41,44 @@ pub fn view_message(msg: &DegMessage, pkey: PublicKey) -> Option<Element<GuiEven
} }
} }
/// The screens (pages) of the app
#[derive(Copy, Clone, Debug)]
pub enum AppScreen {
/// The one with the chats
Main,
/// Settings and profile
ProfileEditor,
/// The screen that appears if no peers are available and asks for the IP address of at least one peer
PeerInput,
}
impl Default for AppScreen {
fn default() -> Self {
AppScreen::Main
}
}
/// The main application struct (for iced)
#[derive(Default)] #[derive(Default)]
pub struct DegeonApp { pub struct DegeonApp {
/// The container for logic, data and IF
pub data: Degeon, pub data: Degeon,
/// Current screen
screen: AppScreen,
/// Selected chat (on the main screen)
selected_chat: usize, selected_chat: usize,
/// Send button
send_button_state: iced::button::State, send_button_state: iced::button::State,
/// Message input field
text_input_state: iced::text_input::State, text_input_state: iced::text_input::State,
/// Buttons for chat previews
preview_button_states: Vec<button::State>, preview_button_states: Vec<button::State>,
} }
impl DegeonApp { impl DegeonApp {
/// Render the list of chats with all previews
fn chat_list<'a>( fn chat_list<'a>(
chats: &'a Vec<Chat>, chats: &'a [Chat],
preview_button_states: &'a mut Vec<button::State>, preview_button_states: &'a mut Vec<button::State>,
selected: usize, selected: usize,
) -> Element<'a, GuiEvent> { ) -> Element<'a, GuiEvent> {
@ -77,8 +100,9 @@ impl DegeonApp {
.into() .into()
} }
/// Render active chat section
pub fn active_chat<'a>( pub fn active_chat<'a>(
chats: &'a Vec<Chat>, chats: &'a [Chat],
selected_chat: usize, selected_chat: usize,
send_button_state: &'a mut button::State, send_button_state: &'a mut button::State,
text_input_state: &'a mut iced::text_input::State, text_input_state: &'a mut iced::text_input::State,
@ -95,20 +119,52 @@ impl DegeonApp {
} }
} }
impl DegeonApp {
fn main_view(&mut self) -> Element<GuiEvent> {
let Self {
data: Degeon { chats, .. },
selected_chat,
send_button_state,
text_input_state,
preview_button_states,
..
} = self;
Row::new()
.padding(20)
.push(Self::chat_list(
chats,
preview_button_states,
*selected_chat,
))
.push(Self::active_chat(
chats,
*selected_chat,
send_button_state,
text_input_state,
))
.height(Length::Fill)
.into()
}
}
impl Application for DegeonApp { impl Application for DegeonApp {
type Executor = iced::executor::Default; type Executor = iced::executor::Default;
type Message = GuiEvent; type Message = GuiEvent;
type Flags = (); type Flags = ();
fn new(_: ()) -> (Self, iced::Command<GuiEvent>) { fn new(_: ()) -> (Self, iced::Command<GuiEvent>) {
let mut st = Self::default(); let mut data = Degeon::restore_from_file("".to_string()).unwrap();
st.data.chats = vec![ data.chats = vec![Chat::example(1, &data.keys), Chat::example(2, &data.keys)];
Chat::example(1, &st.data.keys), data.profile.name = "John".to_string();
Chat::example(2, &st.data.keys), data.send_multicast(ProtocolMsg::Ping).unwrap();
]; let st = DegeonApp {
st.preview_button_states = vec![Default::default(), Default::default()]; data,
st.data.my_name = "John".to_string(); screen: Default::default(),
st.data.send_multicast(ProtocolMsg::Ping).unwrap(); selected_chat: 0,
send_button_state: Default::default(),
text_input_state: Default::default(),
preview_button_states: vec![Default::default(), Default::default()],
};
let data_clone = st.data.clone(); let data_clone = st.data.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_secs(10)); std::thread::sleep(std::time::Duration::from_secs(10));
@ -142,19 +198,20 @@ impl Application for DegeonApp {
.push(new_msg.clone()); .push(new_msg.clone());
let data_cloned = self.data.clone(); let data_cloned = self.data.clone();
let target = self.data.chats[self.selected_chat].pkey.clone(); let target = self.data.chats[self.selected_chat].pkey.clone();
std::thread::spawn(move || data_cloned.send_message(ProtocolMsg::NewMessage(new_msg), &target).unwrap()); std::thread::spawn(move || {
} data_cloned
GuiEvent::NewChat(pkey) => { .send_message(ProtocolMsg::NewMessage(new_msg), &target)
if self.data.chat_with(&pkey).is_none() { .unwrap()
self.data.chats.push(Chat::new(pkey)) });
} self.data.save_to_file("".to_string()).unwrap();
} }
GuiEvent::NewMessageInChat(pkey, msg) => { GuiEvent::NewMessageInChat(pkey, msg) => {
if self.data.chat_with(&pkey).is_none() { if self.data.chat_with(&pkey).is_none() {
self.data.chats.push(Chat::new(pkey.clone())) self.data.chats.push(Chat::new(pkey.clone()))
} }
let ind = self.data.chat_with(&pkey).unwrap(); let ind = self.data.chat_with(&pkey).unwrap();
self.data.chats[ind].messages.push(msg) self.data.chats[ind].messages.push(msg);
self.data.save_to_file("".to_string()).unwrap();
} }
GuiEvent::SetProfile(pkey, name) => { GuiEvent::SetProfile(pkey, name) => {
if self.data.chat_with(&pkey).is_none() { if self.data.chat_with(&pkey).is_none() {
@ -162,6 +219,7 @@ impl Application for DegeonApp {
} }
let ind = self.data.chat_with(&pkey).unwrap(); let ind = self.data.chat_with(&pkey).unwrap();
self.data.chats[ind].profile = name; self.data.chats[ind].profile = name;
self.data.save_to_file("".to_string()).unwrap();
} }
GuiEvent::None => {} GuiEvent::None => {}
GuiEvent::WeHaveToSendProfile(target) => { GuiEvent::WeHaveToSendProfile(target) => {
@ -169,8 +227,9 @@ impl Application for DegeonApp {
return self.data.get_send_command( return self.data.get_send_command(
ProtocolMsg::ProfileResponse(self.data.get_profile()), ProtocolMsg::ProfileResponse(self.data.get_profile()),
&target, &target,
) );
} }
GuiEvent::ChangeScreen(sc) => self.screen = sc,
} }
iced::Command::none() iced::Command::none()
} }
@ -179,29 +238,11 @@ impl Application for DegeonApp {
iced::Subscription::from_recipe(self.data.clone()) iced::Subscription::from_recipe(self.data.clone())
} }
fn view(&mut self) -> Element<GuiEvent> { fn view(&mut self) -> Element<'_, Self::Message> {
let Self { match self.screen {
data: Degeon { chats, .. }, AppScreen::Main => self.main_view(),
selected_chat, AppScreen::ProfileEditor => todo!(),
send_button_state, AppScreen::PeerInput => todo!(),
text_input_state, }
preview_button_states,
..
} = self;
Row::new()
.padding(20)
.push(Self::chat_list(
chats,
preview_button_states,
*selected_chat,
))
.push(Self::active_chat(
chats,
*selected_chat,
send_button_state,
text_input_state,
))
.height(Length::Fill)
.into()
} }
} }

1
degeon/src/styles.rs

@ -6,6 +6,7 @@ pub mod style {
pub enum Button { pub enum Button {
Primary, Primary,
Secondary, Secondary,
#[allow(dead_code)]
Destructive, Destructive,
InactiveChat, InactiveChat,
} }

6
src/interfaces/ip.rs

@ -134,7 +134,7 @@ impl Interface for IPInterface {
.iter() .iter()
.find(|p| compare_addrs(p, connection_addr)) .find(|p| compare_addrs(p, connection_addr))
{ {
if let Some(Some(conn)) = IPInterface::new_connection(peer).ok() { if let Ok(Some(conn)) = IPInterface::new_connection(peer) {
new_connections.push(conn) new_connections.push(conn)
} }
} }
@ -276,7 +276,7 @@ impl Interface for IPInterface {
println!("Error while sending: {:?}", e); println!("Error while sending: {:?}", e);
e e
}, },
); ).unwrap_or_default();
} }
} }
} }
@ -401,7 +401,7 @@ impl IPInterface {
.connections .connections
.iter() .iter()
.filter_map(|conn| conn.peer_addr().ok()) .filter_map(|conn| conn.peer_addr().ok())
.position(|addr| compare_addrs(&peer, addr)) .position(|addr| compare_addrs(peer, addr))
{ {
self.connections.remove(ind); self.connections.remove(ind);
} }

17
src/ironforce.rs

@ -16,7 +16,7 @@ const TUNNEL_MAX_REPEAT_COUNT: u32 = 3;
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub const DEFAULT_FILE: &str = ".if_data.json"; pub const DEFAULT_FILE: &str = ".if_data.json";
/// Main worker /// Main IF worker
#[derive(Hash)] #[derive(Hash)]
pub struct IronForce { pub struct IronForce {
/// Keys for this instance /// Keys for this instance
@ -54,9 +54,13 @@ pub struct IronForce {
/// Data for the serialization of IF /// Data for the serialization of IF
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct IFSerializationData { pub struct IFSerializationData {
/// Worker's keys
pub keys: Keys, pub keys: Keys,
/// Saved tunnels that go through this node
pub tunnels: Vec<Tunnel>, pub tunnels: Vec<Tunnel>,
/// Peers for transport
pub peers: Vec<PeerInfo>, pub peers: Vec<PeerInfo>,
/// Data for all interfaces (in IP, for example, that's port and IPs of peers)
pub interfaces_data: Vec<alloc::string::String>, pub interfaces_data: Vec<alloc::string::String>,
} }
@ -325,6 +329,7 @@ impl IronForce {
self.messages.pop() self.messages.pop()
} }
/// Run one iteration of main loop: accepting incoming connections and messages, processing them
pub fn main_loop_iteration(&mut self) -> IFResult<()> { pub fn main_loop_iteration(&mut self) -> IFResult<()> {
self.transport.main_loop_iteration()?; self.transport.main_loop_iteration()?;
while let Some((msg, inc_peer)) = self.transport.receive() { while let Some((msg, inc_peer)) = self.transport.receive() {
@ -333,10 +338,12 @@ impl IronForce {
Ok(()) Ok(())
} }
/// Get an id for the public key of the worker
fn short_id(&self) -> alloc::string::String { fn short_id(&self) -> alloc::string::String {
self.keys.get_public().get_short_id() self.keys.get_public().get_short_id()
} }
/// Get `IFSerializationData` that can be stored in a file
pub fn get_serialization_data(&self) -> IFSerializationData { pub fn get_serialization_data(&self) -> IFSerializationData {
IFSerializationData { IFSerializationData {
keys: self.keys.clone(), keys: self.keys.clone(),
@ -346,6 +353,7 @@ impl IronForce {
} }
} }
/// Restore from `IFSerializationData`
pub fn from_serialization_data(data: IFSerializationData) -> IFResult<Self> { pub fn from_serialization_data(data: IFSerializationData) -> IFResult<Self> {
Ok(Self { Ok(Self {
keys: data.keys, keys: data.keys,
@ -361,6 +369,9 @@ impl IronForce {
}) })
} }
/// Load from file (`filename`) with `IFSerializationData`
///
/// If the filename is empty, the default filename is used
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn from_file(filename: alloc::string::String) -> IFResult<Self> { pub fn from_file(filename: alloc::string::String) -> IFResult<Self> {
let filename = if filename.is_empty() { let filename = if filename.is_empty() {
@ -377,6 +388,9 @@ impl IronForce {
} }
} }
/// Save `IFSerializationData` to a file with `filename`
///
/// If `filename` is None, the default filename is used
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn save_to_file(&self, filename: Option<alloc::string::String>) -> IFResult<()> { pub fn save_to_file(&self, filename: Option<alloc::string::String>) -> IFResult<()> {
std::fs::write( std::fs::write(
@ -386,6 +400,7 @@ impl IronForce {
Ok(()) Ok(())
} }
/// Spawn a thread with IF main loop and return `Arc<Mutex<IF>>`
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn launch_main_loop( pub fn launch_main_loop(
mut self, mut self,

Loading…
Cancel
Save