Browse Source

Fixed the messenger

master
Lev 3 years ago
parent
commit
0ccd0bbced
  1. 31
      Cargo.lock
  2. 3
      degeon/Cargo.toml
  3. 135
      degeon/src/chat.rs
  4. 209
      degeon/src/degeon_worker.rs
  5. 17
      degeon/src/gui_events.rs
  6. 7
      degeon/src/main.rs
  7. 34
      degeon/src/message.rs
  8. 385
      degeon/src/state.rs
  9. 55
      degeon/src/styles.rs

31
Cargo.lock generated

@ -229,6 +229,19 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "clipboard-win" name = "clipboard-win"
version = "4.2.2" version = "4.2.2"
@ -553,6 +566,7 @@ name = "degeon"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"base64", "base64",
"chrono",
"futures", "futures",
"iced", "iced",
"iced_native", "iced_native",
@ -992,7 +1006,7 @@ checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"libc", "libc",
"wasi 0.10.2+wasi-snapshot-preview1", "wasi 0.10.0+wasi-snapshot-preview1",
] ]
[[package]] [[package]]
@ -2795,6 +2809,17 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7572415bd688d401c52f6e36f4c8e805b9ae1622619303b9fa835d531db0acae" checksum = "7572415bd688d401c52f6e36f4c8e805b9ae1622619303b9fa835d531db0acae"
[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.5.1" version = "1.5.1"
@ -2950,9 +2975,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.10.2+wasi-snapshot-preview1" version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"

3
degeon/Cargo.toml

@ -12,4 +12,5 @@ base64 = "0.13.0"
serde = { version = "1.0" } serde = { version = "1.0" }
serde_json = "1.0.72" serde_json = "1.0.72"
futures = "0.3.18" futures = "0.3.18"
iced_native = "0.4.0" iced_native = "0.4.0"
chrono = "0.4.19"

135
degeon/src/chat.rs

@ -0,0 +1,135 @@
use iced::{Align, Button, Column, Element, Length, Row, Text, TextInput};
use iced_native::button;
use ironforce::{Keys, PublicKey};
use crate::gui_events::GuiEvent;
use crate::message::{DegMessage, DegMessageContent, Profile};
use crate::state;
use crate::styles::style;
#[derive(Clone, Debug)]
pub struct Chat {
pub pkey: PublicKey,
pub messages: Vec<DegMessage>,
pub profile: Profile,
pub scrolled: f32,
pub input: String,
}
impl Chat {
pub fn new(pkey: PublicKey) -> Self {
Self {
pkey,
messages: vec![],
profile: Profile { name: "".to_string() },
scrolled: 0.0,
input: "".to_string(),
}
}
}
impl Chat {
pub fn header<'a>(name: String) -> Element<'a, GuiEvent> {
iced::container::Container::new(Text::new(name.as_str()).color(iced::Color::WHITE))
.style(style::Container::Primary)
.width(Length::Fill)
.height(Length::Units(50))
.padding(10)
.into()
}
pub fn send_field<'a>(
input: String,
text_input_state: &'a mut iced::text_input::State,
send_button_state: &'a mut iced::button::State,
) -> Element<'a, GuiEvent> {
Row::new()
.width(Length::Fill)
.padding(15)
.push(
TextInput::new(text_input_state, "Message", input.as_str(), |st| {
GuiEvent::Typed(st)
})
.padding(8)
.width(Length::Fill),
)
.push(
Button::new(send_button_state, Text::new("Send"))
.on_press(GuiEvent::SendMessage)
.style(style::Button::Secondary)
.padding(20)
.width(Length::Units(80)),
)
.spacing(25)
.height(Length::Units(100))
.into()
}
pub fn preview<'a>(
&'a self,
state: &'a mut button::State,
i: usize,
is_selected: bool,
) -> Element<'a, GuiEvent> {
Button::new(state, Text::new(self.profile.name.as_str()))
.width(Length::Fill)
.padding(10)
.style(if is_selected {
style::Button::Primary
} else {
style::Button::InactiveChat
})
.on_press(GuiEvent::ChatSelect(i))
.into()
}
pub fn view<'a>(
&'a self,
text_input_state: &'a mut iced::text_input::State,
send_button_state: &'a mut iced::button::State,
) -> Element<'a, GuiEvent> {
let pkey_clone = self.pkey.clone();
let msgs = self.messages.iter().filter_map(move |msg| state::view_message(msg, pkey_clone.clone())).collect();
Column::new()
.align_items(Align::End)
.height(Length::Fill)
.width(Length::FillPortion(4))
.push(Self::header(self.profile.name.clone()))
.push(
Column::with_children(msgs)
.padding(20)
.spacing(10)
.align_items(Align::End)
.height(Length::FillPortion(9)),
)
.spacing(10)
.push(Self::send_field(
self.input.to_string(),
text_input_state,
send_button_state,
))
.into()
}
pub fn example(i: usize, my_keys: &Keys) -> Chat {
let pkey = Keys::generate().get_public();
Self {
messages: vec![
DegMessage {
sender: pkey.clone(),
timestamp: chrono::Utc::now().timestamp(),
content: DegMessageContent::Text(format!("Example message {}", 2 * i))
},
DegMessage {
sender: my_keys.get_public(),
timestamp: chrono::Utc::now().timestamp(),
content: DegMessageContent::Text(format!("Example message {}", 2 * i + 1))
},
],
profile: Profile { name: format!("Example user ({})", i) },
scrolled: 0.0,
pkey,
input: "".to_string(),
}
}
}

209
degeon/src/degeon_worker.rs

@ -0,0 +1,209 @@
use crate::chat::Chat;
use crate::gui_events::GuiEvent;
use crate::message::{Profile, ProtocolMsg};
use futures::Stream;
use ironforce::res::IFResult;
use ironforce::{IronForce, Keys, Message, MessageType, PublicKey};
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
#[derive(Clone)]
pub struct Degeon {
pub chats: Vec<Chat>,
pub my_name: String,
pub keys: Keys,
pub ironforce: Arc<Mutex<IronForce>>,
}
impl Default for Degeon {
fn default() -> Self {
let ironforce = IronForce::from_file("".to_string()).unwrap();
let keys = ironforce.keys.clone();
println!("ID: {}", keys.get_public().get_short_id());
let (_thread, ironforce) = ironforce.launch_main_loop(1000);
Self {
chats: vec![],
my_name: "".to_string(),
keys,
ironforce,
}
}
}
impl Degeon {
pub fn get_profile(&self) -> Profile {
Profile {
name: self.my_name.clone(),
}
}
pub fn chat_with(&self, pkey: &PublicKey) -> Option<usize> {
self.chats.iter().position(|chat| &chat.pkey == pkey)
}
pub fn process_message(&self, msg: ironforce::Message) -> IFResult<Option<GuiEvent>> {
let deg_msg: ProtocolMsg =
match serde_json::from_slice(msg.get_decrypted(&self.keys)?.as_slice()) {
Ok(r) => r,
Err(_) => return Ok(None),
};
let sender = msg.get_sender(&self.keys).unwrap();
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() {
return Ok(None);
}
println!("{:?}", deg_msg);
Ok(match &deg_msg {
ProtocolMsg::NewMessage(deg_msg) => {
Some(GuiEvent::NewMessageInChat(sender, deg_msg.clone()))
}
ProtocolMsg::ProfileRequest | ProtocolMsg::Ping => {
Some(GuiEvent::WeHaveToSendProfile(sender))
}
ProtocolMsg::ProfileResponse(prof) => Some(GuiEvent::SetProfile(sender, prof.clone())),
})
}
pub fn send_multicast(&self, msg: ProtocolMsg) -> IFResult<()> {
self.ironforce.lock().unwrap().send_to_all(
Message::build()
.message_type(MessageType::Broadcast)
.content(serde_json::to_vec(&msg)?)
.sign(&self.keys)
.build()?,
)
}
pub fn send_message(&self, msg: ProtocolMsg, target: &PublicKey) -> IFResult<()> {
// if self.ironforce.lock().unwrap().get_tunnel(target).is_none() {
// println!("Creating a tunnel");
// self.ironforce
// .lock()
// .unwrap()
// .initialize_tunnel_creation(target)?;
// let mut counter = 0;
// while self.ironforce.lock().unwrap().get_tunnel(target).is_none() {
// std::thread::sleep(std::time::Duration::from_millis(350));
// counter += 1;
// if counter > 100 {
// return Err(IFError::TunnelNotFound);
// }
// }
// }
self.ironforce.lock().unwrap().send_to_all(
Message::build()
.message_type(MessageType::Broadcast)
.content(serde_json::to_vec(&msg)?)
.recipient(target)
.sign(&self.keys)
.build()?,
)
}
pub fn get_send_command(
&self,
msg: ProtocolMsg,
target: &PublicKey,
) -> iced::Command<GuiEvent> {
let if_clone = self.ironforce.clone();
let target = target.clone();
let keys = self.keys.clone();
println!("Creating a send command: {:?}", msg);
iced::Command::perform(async {}, move |_| {
println!("Sending message: {:?}", msg);
if_clone
.lock()
.unwrap()
.send_to_all(
Message::build()
.message_type(MessageType::Broadcast)
.content(serde_json::to_vec(&msg).unwrap())
// .recipient(&target)
.sign(&keys)
.build()
.unwrap(),
)
.unwrap();
GuiEvent::None
})
}
pub fn get_send_multicast_command(&self, msg: ProtocolMsg) -> iced::Command<GuiEvent> {
let keys = self.keys.clone();
let if_clone = self.ironforce.clone();
println!("Created a send command");
iced::Command::perform(
async move {
println!("Sending message: {:?}", msg);
if_clone
.lock()
.unwrap()
.send_to_all(
Message::build()
.message_type(MessageType::Broadcast)
.content(serde_json::to_vec(&msg).unwrap())
.sign(&keys)
.build()
.unwrap(),
)
.unwrap()
},
|_| GuiEvent::None,
)
}
}
impl Stream for Degeon {
type Item = GuiEvent;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let timestamp_0 = std::time::Instant::now();
let msg_raw = self.ironforce.lock().unwrap().read_message();
let msg = msg_raw.as_ref().map(|msg| self.process_message(msg.clone()).unwrap());
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()) {
Ok(r) => r,
Err(_) => {println!("Couldn't deserialize {:?}", msg_raw); return Poll::Ready(Some(GuiEvent::None))}
};
println!("{:?} -> {:?}", msg_deg, msg);
}
if timestamp_0.elapsed() < std::time::Duration::from_millis(5) {
std::thread::sleep(std::time::Duration::from_millis(5));
}
match msg {
None => Poll::Ready(Some(GuiEvent::None)),
Some(None) => Poll::Ready(Some(GuiEvent::None)),
Some(Some(msg)) => Poll::Ready(Some(msg)),
}
}
}
impl<H, I> iced_native::subscription::Recipe<H, I> for Degeon
where
H: std::hash::Hasher,
{
type Output = GuiEvent;
fn hash(&self, state: &mut H) {
use std::hash::Hash;
std::any::TypeId::of::<Self>().hash(state);
self.ironforce.lock().unwrap().hash(state);
// std::time::SystemTime::now().hash(state);
// std::time::UNIX_EPOCH
// .elapsed()
// .unwrap()
// .as_secs()
// .hash(state);
}
fn stream(
self: Box<Self>,
_input: futures::stream::BoxStream<'static, I>,
) -> futures::stream::BoxStream<'static, Self::Output> {
Box::pin(self)
}
}

17
degeon/src/gui_events.rs

@ -1,12 +1,23 @@
use crate::message::DegMessage; use crate::message::{DegMessage, Profile};
use ironforce::PublicKey; use ironforce::PublicKey;
/// An enum with all possible events for this application
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum GuiEvent { pub enum GuiEvent {
/// Selection of a chat
ChatSelect(usize), ChatSelect(usize),
/// The user changed the value of "new message" field
Typed(String), Typed(String),
SendClick, /// The user clicked "Send"
SendMessage,
/// New chat shall be created because we have an incoming request
NewChat(PublicKey), NewChat(PublicKey),
/// A new messaged arrived
NewMessageInChat(PublicKey, DegMessage), NewMessageInChat(PublicKey, DegMessage),
SetName(PublicKey, String), /// A profile response arrived and we should store it
SetProfile(PublicKey, Profile),
/// We should send profile (in response to profile request)
WeHaveToSendProfile(PublicKey),
/// Nothing happened
None,
} }

7
degeon/src/main.rs

@ -2,15 +2,18 @@ extern crate serde;
mod message; mod message;
mod state; mod state;
mod gui_events; mod gui_events;
mod chat;
mod styles;
mod degeon_worker;
use iced::Application; use iced::Application;
use ironforce::res::IFResult; use ironforce::res::IFResult;
use ironforce::{IronForce, Message, MessageType, PublicKey}; use ironforce::{IronForce, Message, MessageType, PublicKey};
use crate::state::State; 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 ironforce = IronForce::from_file("".to_string()).unwrap();
// let _if_keys = ironforce.keys.clone(); // let _if_keys = ironforce.keys.clone();
Ok(State::run(iced::Settings::default())?) Ok(DegeonApp::run(iced::Settings::default())?)
} }

34
degeon/src/message.rs

@ -1,15 +1,39 @@
use ironforce::PublicKey;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum DegMessage { pub struct DegMessage {
pub sender: PublicKey,
pub timestamp: i64,
pub content: DegMessageContent,
}
impl DegMessage {
pub fn new_text(text: String, my_key: &PublicKey) -> DegMessage {
Self {
sender: my_key.clone(),
timestamp: chrono::Utc::now().timestamp(),
content: DegMessageContent::Text(text),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum DegMessageContent {
Text(String), Text(String),
File(Vec<u8>), File(Vec<u8>),
Service(ServiceMsg), Service,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Profile {
pub name: String,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ServiceMsg { pub enum ProtocolMsg {
NameRequest, ProfileRequest,
NameStatement(String), ProfileResponse(Profile),
Ping, Ping,
NewMessage(DegMessage),
} }

385
degeon/src/state.rs

@ -1,5 +1,8 @@
use crate::chat::Chat;
use crate::degeon_worker::Degeon;
use crate::gui_events::GuiEvent; use crate::gui_events::GuiEvent;
use crate::message::{DegMessage, ServiceMsg}; use crate::message::{DegMessage, DegMessageContent, Profile, ProtocolMsg};
use crate::styles::style;
use core::default::Default; use core::default::Default;
use futures::Stream; use futures::Stream;
use iced::{ use iced::{
@ -13,326 +16,36 @@ use std::pin::Pin;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::task::{Context, Poll}; use std::task::{Context, Poll};
#[derive(Clone, Debug)] pub fn view_message(msg: &DegMessage, pkey: PublicKey) -> Option<Element<GuiEvent>> {
pub struct Chat { let is_from_me = pkey != msg.sender;
pkey: PublicKey, match &msg.content {
messages: Vec<(bool, DegMessage)>, DegMessageContent::Text(t) => Some(
name: String, iced::Row::new()
scrolled: f32, .push(
pub input: String, iced::Container::new(Text::new(t.as_str()))
} .padding(10)
.style(if is_from_me {
impl Chat { style::Container::MyMessage
pub fn new(pkey: PublicKey) -> Self { } else {
Self { style::Container::NotMyMessage
pkey, })
messages: vec![], .align_x(if is_from_me { Align::End } else { Align::Start }),
name: "".to_string(), )
scrolled: 0.0, .align_items(if is_from_me { Align::End } else { Align::Start })
input: "".to_string(), .width(if is_from_me {
} Length::Shrink
}
}
pub fn view_message(msg: &(bool, DegMessage)) -> Option<Element<GuiEvent>> {
let msg = &msg.1;
match msg {
DegMessage::Text(t) => Some(
iced::Container::new(Text::new(t.as_str()))
.padding(10)
.style(style::Container::Message)
.into(),
),
DegMessage::File(_) => None,
DegMessage::Service(_) => None,
}
}
mod style {
use iced::container::Style;
use iced::{button, Background, Color, Vector};
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Button {
Primary,
Secondary,
Destructive,
InactiveChat,
}
impl button::StyleSheet for Button {
fn active(&self) -> button::Style {
button::Style {
background: Some(Background::Color(match self {
Button::Primary => Color::from_rgb(0.11, 0.35, 0.75),
Button::Secondary => Color::from_rgb(0.3, 0.1, 0.7),
Button::Destructive => Color::from_rgb(0.8, 0.2, 0.2),
Button::InactiveChat => Color::from_rgb(0.3, 0.52, 0.9),
})),
border_radius: 5.0,
shadow_offset: Vector::new(1.0, 1.0),
text_color: if self != &Button::InactiveChat {
Color::WHITE
} else { } else {
Color::BLACK Length::Fill
},
..button::Style::default()
}
}
}
pub enum Container {
Primary,
Message,
}
impl iced::container::StyleSheet for Container {
fn style(&self) -> Style {
iced::container::Style {
text_color: Some(Color::WHITE),
background: Some(Background::Color(match self {
Container::Primary => Color::from_rgb(18. / 256., 25. / 256., 70. / 256.),
Container::Message => Color::from_rgb(0., 0.1, 0.8),
})),
border_radius: 5.0,
border_width: 0.6,
border_color: Color::TRANSPARENT,
}
}
}
}
impl Chat {
pub fn header<'a>(name: String) -> Element<'a, GuiEvent> {
iced::container::Container::new(Text::new(name.as_str()).color(iced::Color::WHITE))
.style(style::Container::Primary)
.width(Length::Fill)
.height(Length::Units(50))
.padding(10)
.into()
}
pub fn send_field<'a>(
input: String,
text_input_state: &'a mut iced::text_input::State,
send_button_state: &'a mut iced::button::State,
) -> Element<'a, GuiEvent> {
Row::new()
.width(Length::Fill)
.padding(15)
.push(
TextInput::new(text_input_state, "Message", input.as_str(), |st| {
GuiEvent::Typed(st)
}) })
.padding(8) .into(),
.width(Length::Fill), ),
) DegMessageContent::File(_) => None,
.push( DegMessageContent::Service => None,
Button::new(send_button_state, Text::new("Send"))
.on_press(GuiEvent::SendClick)
.style(style::Button::Secondary)
.padding(20)
.width(Length::Units(80)),
)
.spacing(25)
.height(Length::Units(100))
.into()
}
pub fn preview<'a>(
&'a self,
state: &'a mut button::State,
i: usize,
is_selected: bool,
) -> Element<'a, GuiEvent> {
Button::new(state, Text::new(self.name.as_str()))
.width(Length::Fill)
.padding(10)
.style(if is_selected {
style::Button::Primary
} else {
style::Button::InactiveChat
})
.on_press(GuiEvent::ChatSelect(i))
.into()
}
pub fn view<'a>(
&'a self,
text_input_state: &'a mut iced::text_input::State,
send_button_state: &'a mut iced::button::State,
) -> Element<'a, GuiEvent> {
let msgs = self.messages.iter().filter_map(view_message).collect();
Column::new()
.align_items(Align::End)
.height(Length::Fill)
.width(Length::FillPortion(4))
.push(Self::header(self.name.clone()))
.push(
Column::with_children(msgs)
.padding(20)
.spacing(10)
.align_items(Align::End)
.height(Length::FillPortion(9)),
)
.spacing(10)
.push(Self::send_field(
self.input.to_string(),
text_input_state,
send_button_state,
))
.into()
}
pub fn example(i: usize) -> Chat {
Self {
pkey: Keys::generate().get_public(),
messages: vec![(false, DegMessage::Text(format!("Example message {}", i)))],
name: format!("Example user ({})", i),
scrolled: 0.0,
input: "".to_string(),
}
}
}
#[derive(Clone)]
pub struct Degeon {
pub chats: Vec<Chat>,
pub my_name: String,
pub keys: Keys,
pub ironforce: Arc<Mutex<IronForce>>,
}
impl Default for Degeon {
fn default() -> Self {
let ironforce = IronForce::from_file("".to_string()).unwrap();
let keys = ironforce.keys.clone();
let (_thread, ironforce) = ironforce.launch_main_loop(500);
Self {
chats: vec![],
my_name: "".to_string(),
keys,
ironforce,
}
}
}
impl Degeon {
pub fn chat_with(&self, pkey: &PublicKey) -> Option<usize> {
self.chats.iter().position(|chat| &chat.pkey == pkey)
}
pub fn process_message(&self, msg: ironforce::Message) -> IFResult<Option<GuiEvent>> {
let deg_msg: DegMessage =
match serde_json::from_slice(msg.get_decrypted(&self.keys)?.as_slice()) {
Ok(r) => r,
Err(_) => return Ok(None)
};
let sender = msg.get_sender(&self.keys).unwrap();
Ok(match &deg_msg {
DegMessage::Text(_) | DegMessage::File(_) => {
Some(GuiEvent::NewMessageInChat(sender, deg_msg))
}
DegMessage::Service(msg) => match msg {
ServiceMsg::NameRequest => self
.send_message(
DegMessage::Service(ServiceMsg::NameStatement(self.my_name.clone())),
&sender,
)
.map(|_| None)?,
ServiceMsg::NameStatement(name) => {
Some(GuiEvent::SetName(sender, name.to_string()))
}
ServiceMsg::Ping => self
.send_message(DegMessage::Service(ServiceMsg::NameStatement(self.my_name.clone())), &sender)
.map(|_| None)?,
},
})
}
pub fn send_multicast(&self, msg: DegMessage) -> IFResult<()> {
self.ironforce.lock().unwrap().send_to_all(
Message::build()
.message_type(MessageType::Broadcast)
.content(serde_json::to_vec(&msg)?)
.sign(&self.keys)
.build()?,
)
}
pub fn send_message(&self, msg: DegMessage, target: &PublicKey) -> IFResult<()> {
// if self.ironforce.lock().unwrap().get_tunnel(target).is_none() {
// println!("Creating a tunnel");
// self.ironforce
// .lock()
// .unwrap()
// .initialize_tunnel_creation(target)?;
// let mut counter = 0;
// while self.ironforce.lock().unwrap().get_tunnel(target).is_none() {
// std::thread::sleep(std::time::Duration::from_millis(350));
// counter += 1;
// if counter > 100 {
// return Err(IFError::TunnelNotFound);
// }
// }
// }
self.ironforce.lock().unwrap().send_to_all(
Message::build()
.message_type(MessageType::Broadcast)
.content(serde_json::to_vec(&msg)?)
.recipient(target)
.sign(&self.keys)
.build()?,
)
}
}
impl Stream for Degeon {
type Item = GuiEvent;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
println!("Degeon worker is being polled");
let msg = self.ironforce.lock().unwrap().read_message();
println!("Msg: {:?}", msg);
match msg.map(|msg| self.process_message(msg).unwrap()) {
None | Some(None) => Poll::Pending,
Some(Some(msg)) => {
Poll::Ready(Some(msg))
}
}
}
}
impl<H, I> iced_native::subscription::Recipe<H, I> for Degeon
where
H: std::hash::Hasher,
{
type Output = GuiEvent;
fn hash(&self, state: &mut H) {
use std::hash::Hash;
std::any::TypeId::of::<Self>().hash(state);
self.ironforce.lock().unwrap().hash(state);
// std::time::SystemTime::now().hash(state);
std::time::UNIX_EPOCH
.elapsed()
.unwrap()
.as_secs()
.hash(state);
}
fn stream(
self: Box<Self>,
_input: futures::stream::BoxStream<'static, I>,
) -> futures::stream::BoxStream<'static, Self::Output> {
Box::pin(self)
} }
} }
#[derive(Default)] #[derive(Default)]
pub struct State { pub struct DegeonApp {
pub data: Degeon, pub data: Degeon,
selected_chat: usize, selected_chat: usize,
send_button_state: iced::button::State, send_button_state: iced::button::State,
@ -340,7 +53,7 @@ pub struct State {
preview_button_states: Vec<button::State>, preview_button_states: Vec<button::State>,
} }
impl State { impl DegeonApp {
fn chat_list<'a>( fn chat_list<'a>(
chats: &'a Vec<Chat>, chats: &'a Vec<Chat>,
preview_button_states: &'a mut Vec<button::State>, preview_button_states: &'a mut Vec<button::State>,
@ -382,26 +95,25 @@ impl State {
} }
} }
impl Application for State { 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 st = Self::default();
st.data.chats = vec![Chat::example(1), Chat::example(2)]; st.data.chats = vec![
Chat::example(1, &st.data.keys),
Chat::example(2, &st.data.keys),
];
st.preview_button_states = vec![Default::default(), Default::default()]; st.preview_button_states = vec![Default::default(), Default::default()];
st.data.my_name = "John".to_string(); st.data.my_name = "John".to_string();
st.data st.data.send_multicast(ProtocolMsg::Ping).unwrap();
.send_multicast(DegMessage::Service(ServiceMsg::Ping))
.unwrap();
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));
loop { loop {
data_clone data_clone.send_multicast(ProtocolMsg::Ping).unwrap();
.send_multicast(DegMessage::Service(ServiceMsg::Ping))
.unwrap();
std::thread::sleep(std::time::Duration::from_secs(120)); std::thread::sleep(std::time::Duration::from_secs(120));
} }
}); });
@ -416,18 +128,21 @@ impl Application for State {
match message { match message {
GuiEvent::ChatSelect(i) => self.selected_chat = i, GuiEvent::ChatSelect(i) => self.selected_chat = i,
GuiEvent::Typed(st) => self.data.chats[self.selected_chat].input = st, GuiEvent::Typed(st) => self.data.chats[self.selected_chat].input = st,
GuiEvent::SendClick => { GuiEvent::SendMessage => {
if self.data.chats[self.selected_chat].input.is_empty() { if self.data.chats[self.selected_chat].input.is_empty() {
return iced::Command::none(); return iced::Command::none();
} }
let new_msg = DegMessage::Text(self.data.chats[self.selected_chat].input.clone()); let new_msg = DegMessage::new_text(
self.data.chats[self.selected_chat].input.clone(),
&self.data.keys.get_public(),
);
self.data.chats[self.selected_chat].input = String::new(); self.data.chats[self.selected_chat].input = String::new();
self.data.chats[self.selected_chat] self.data.chats[self.selected_chat]
.messages .messages
.push((true, 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(new_msg, &target).unwrap()); std::thread::spawn(move || data_cloned.send_message(ProtocolMsg::NewMessage(new_msg), &target).unwrap());
} }
GuiEvent::NewChat(pkey) => { GuiEvent::NewChat(pkey) => {
if self.data.chat_with(&pkey).is_none() { if self.data.chat_with(&pkey).is_none() {
@ -439,14 +154,22 @@ impl Application for State {
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((false, msg)) self.data.chats[ind].messages.push(msg)
} }
GuiEvent::SetName(pkey, name) => { GuiEvent::SetProfile(pkey, name) => {
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].name = name; self.data.chats[ind].profile = name;
}
GuiEvent::None => {}
GuiEvent::WeHaveToSendProfile(target) => {
println!("WHTSP");
return self.data.get_send_command(
ProtocolMsg::ProfileResponse(self.data.get_profile()),
&target,
)
} }
} }
iced::Command::none() iced::Command::none()

55
degeon/src/styles.rs

@ -0,0 +1,55 @@
pub mod style {
use iced::container::Style;
use iced::{Background, button, Color, Vector};
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Button {
Primary,
Secondary,
Destructive,
InactiveChat,
}
impl button::StyleSheet for Button {
fn active(&self) -> button::Style {
button::Style {
background: Some(Background::Color(match self {
Button::Primary => Color::from_rgb(0.11, 0.35, 0.75),
Button::Secondary => Color::from_rgb(0.3, 0.1, 0.7),
Button::Destructive => Color::from_rgb(0.8, 0.2, 0.2),
Button::InactiveChat => Color::from_rgb(0.3, 0.52, 0.9),
})),
border_radius: 5.0,
shadow_offset: Vector::new(1.0, 1.0),
text_color: if self != &Button::InactiveChat {
Color::WHITE
} else {
Color::BLACK
},
..button::Style::default()
}
}
}
pub enum Container {
Primary,
MyMessage,
NotMyMessage,
}
impl iced::container::StyleSheet for Container {
fn style(&self) -> Style {
iced::container::Style {
text_color: Some(Color::WHITE),
background: Some(Background::Color(match self {
Container::Primary => Color::from_rgb(18. / 256., 25. / 256., 70. / 256.),
Container::MyMessage => Color::from_rgb(0., 0.1, 0.8),
Container::NotMyMessage => Color::from_rgb(0., 0.1, 0.4),
})),
border_radius: 5.0,
border_width: 0.6,
border_color: Color::TRANSPARENT,
}
}
}
}
Loading…
Cancel
Save