use iced::{
button, scrollable, text_input, Align, Application, Button, Clipboard, Column, Command,
Container, Element, Length, Row, Scrollable, Settings, Subscription, Text, TextInput,
};
use serde::{Deserialize, Serialize};
use std::sync::{Arc};
use crate::model::{message::*, subscribe_irc};
use crate::style;
use crate::util;
use irc::client::{self, prelude::{Client, Config, Sender}};
pub fn main() -> iced::Result {
App::run(Settings::default())
}
#[derive(Debug, Clone)]
pub struct State {
input_state: text_input::State,
input_value: String,
connecting_flag: bool,
display_value: String,
saving: bool,
dirty: bool,
current_channel: String,
irc_button_state: button::State,
post_button_state: button::State,
scrollable_state: scrollable::State,
sender: Option<Arc<futures::lock::Mutex<irc::client::Sender>>>,
client_stream: Option<Arc<futures::lock::Mutex<irc::client::ClientStream>>>,
}
impl Default for State {
fn default() -> Self {
Self {
input_state: text_input::State::new(),
input_value: String::from(""),
connecting_flag: false,
display_value: String::from(""),
saving: true,
dirty: true,
current_channel: String::from(""),
irc_button_state: button::State::new(),
post_button_state: button::State::new(),
scrollable_state: scrollable::State::new(),
sender: None,
client_stream: None
}
}
}
impl State {
pub fn new_display_val(s: String) -> Self {
let mut default: State = State::default();
default.display_value = String::from(s.to_string());
default
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SavedState {
pub input_value: String,
pub display_value: String
}
#[cfg(not(target_arch = "wasm32"))]
impl SavedState {
async fn load() -> Result<SavedState, LoadError> {
let contents = r#"
{
"display_value": "Push Start IRC button",
"input_value": "43"
}"#;
serde_json::from_str(&contents).map_err(|_| LoadError::FormatError)
}
async fn save(self) -> Result<(), SaveError> {
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum LoadError {
FileError,
FormatError,
}
#[derive(Debug, Clone)]
pub enum SaveError {
DirectoryError,
FileError,
WriteError,
FormatError
}
#[derive(Debug, Clone)]
pub enum IrcError {
IrcError,
}
pub struct IrcClient {
client_stream: irc::client::ClientStream,
sender: irc::client::Sender,
}
impl IrcClient {
async fn get_client() -> Result<IrcClient, failure::Error> {
let config = Config::load("config.toml").unwrap();
let mut client = Client::from_config(config).await?;
client.identify()?;
Ok(IrcClient{ client_stream: client.stream()?, sender: client.sender()})
}
}
pub enum App {
Loading,
Loaded(State),
IrcConnecting(State),
IrcFinished(State),
}
impl Application for App {
type Executor = iced::executor::Default;
type Message = Message;
type Flags = ();
fn new(_flags: ()) -> (App, Command<Self::Message>) {
(
App::Loading,
Command::perform(SavedState::load(), Message::Loaded),
)
}
fn title(&self) -> String {
String::from("Gelato")
}
fn update(
&mut self,
message: Self::Message,
_clipboard: &mut Clipboard,
) -> Command<Self::Message> {
match self {
App::Loading => {
match message {
Message::Loaded(Ok(saved_state)) => {
let mut current_state = State::default();
futures::executor::block_on(async {
let irc_client_struct = IrcClient::get_client().await.expect("get_client()");
current_state.client_stream = Some(Arc::new(futures::lock::Mutex::new(irc_client_struct.client_stream)));
current_state.sender = Some(Arc::new(futures::lock::Mutex::new(irc_client_struct.sender)));
});
*self = App::Loaded(current_state);
}
Message::Loaded(Err(_)) => {
*self = App::Loaded(State::default());
}
_ => {}
}
Command::none()
}
App::Loaded(state) => {
let mut saved = false;
let mut ircflag = false;
match message {
Message::Saved(_) => {
state.saving = false;
saved = true;
}
Message::IrcStart => {
ircflag = true;
}
Message::InputChanged(value) => {
state.input_value = value;
}
_ => {}
}
if !saved {
state.dirty = true;
}
if state.dirty && !state.saving {
state.dirty = false;
state.saving = true;
Command::none()
} else if ircflag {
*self = App::IrcConnecting(state.clone());
Command::none()
} else {
Command::none()
}
}
App::IrcConnecting(state) => {
state.connecting_flag = true;
let mut irc_finished = false;
let mut posted = false;
let mut input_word = String::from("");
match message {
Message::IrcProgressed(progress_state) => match progress_state {
subscribe_irc::Progress::Advanced(message_text) => {
let filtered_text: &str = util::filter(&message_text);
state.display_value.push_str(filtered_text);
}
subscribe_irc::Progress::Finished => {
irc_finished = true;
}
subscribe_irc::Progress::Errored => {
irc_finished = true;
}
_ => {}
},
Message::IrcFinished(_) => {
irc_finished = true;
state.connecting_flag = false;
}
Message::InputChanged(value) => {
state.input_value = value;
}
Message::PostMessage => {
posted = true;
input_word.push_str(&state.input_value.clone());
state.input_value = String::from("");
}
_ => {}
}
if posted && !input_word.is_empty() {
let sender_original = Arc::clone(&state.sender.as_ref().unwrap());
let call = async move {
let sender = sender_original.lock().await;
(*sender).send_privmsg("#test", input_word).unwrap();
};
Command::perform(call, Message::None)
} else if irc_finished {
*self = App::IrcFinished(state.clone());
Command::perform(Message::change(), Message::IrcFinished)
} else {
Command::none()
}
}
App::IrcFinished(state) => {
*self = App::Loaded(state.clone());
Command::none()
}
}
}
fn subscription(&self) -> Subscription<Message> {
match self {
App::IrcConnecting(State {
client_stream,
..
}) => {
let mut client_stream = client_stream.as_ref();
subscribe_irc::input( client_stream, "").map(Message::IrcProgressed)
},
_ => Subscription::none(),
}
}
fn view(&mut self) -> Element<Self::Message> {
match self {
App::Loading => util::loading_message(),
App::Loaded(state) | App::IrcFinished(state) | App::IrcConnecting(state) => {
let scrollable_state: Scrollable<Message> =
Scrollable::new(&mut state.scrollable_state)
.width(Length::Fill)
.height(Length::Fill)
.push(Text::new(state.display_value.to_string()));
let start_irc_button_control: Element<_> = {
let (label, toggle, style) = if state.connecting_flag {
(
"Stop IRC",
Message::IrcFinished(Ok(())),
style::Button::Stop,
)
} else {
("Start IRC", Message::IrcStart, style::Button::Start)
};
Button::new(&mut state.irc_button_state, Text::new(label))
.style(style)
.on_press(toggle)
.into()
};
let post_button: Element<_> = {
let (label, toggle, style) =
("Post", Message::PostMessage, style::Button::Post);
Button::new(&mut state.post_button_state, Text::new(label).size(25))
.style(style)
.on_press(toggle)
.into()
};
let input_box = TextInput::new(
&mut state.input_state,
"Input text...",
&mut state.input_value,
Message::InputChanged,
)
.padding(10)
.size(15)
.on_submit(Message::PostMessage);
let content = Column::new()
.padding(10)
.spacing(10)
.align_items(Align::Start)
.push(start_irc_button_control)
.push(Row::new().push(input_box).push(post_button))
.push(Row::new().align_items(Align::Center).push(scrollable_state));
Container::new(content)
.width(Length::FillPortion(2))
.height(Length::Fill)
.into()
}
}
}
}