|
|
@ -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() |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|