~nicohman/signal-rs

edbf12498bd33cf134713119a61c83d019f8d9e8 — nicohman 1 year, 13 days ago f72f101
More gtk work
4 files changed, 162 insertions(+), 68 deletions(-)

M Cargo.toml
M src/main.rs
M src/signal.rs
A src/style.css
M Cargo.toml => Cargo.toml +5 -5
@@ 18,12 18,12 @@ tokio-tungstenite = "*"
enum_variant_type = "0.2.0"
glium = { version = "0.27", default-features = true }
crossbeam-channel = "0.4.4"
glib = "^0"
gio = "^0"
gdk = "^0"
glib = "0.10"
gio = "0.9"
gdk = "0.13"
gdk-pixbuf = "^0"
gtk = "^0"
gtk = "0.9"
once_cell = "^0"
pango = "^0"
pango = "0.9"
pangocairo = "^0"
cairo-rs = { version = "^0", features = ["png"] }
\ No newline at end of file

M src/main.rs => src/main.rs +135 -41
@@ 22,70 22,159 @@ use serde::*;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::prelude::*;
use tokio::sync::mpsc::*;
use tokio_tungstenite::{connect_async};
use tokio_tungstenite::connect_async;
const CONNECTION: &'static str = "ws://localhost:9080/ws";
mod signal;
use gio::prelude::*;
use glib::clone;
use glib::prelude::*;
use gtk::prelude::*;
use glib::clone;
use gtk::*;
use signal::*;
use std::borrow::*;
use std::boxed::Box;
use std::cell::*;
use std::collections::*;
use std::env::args;
use std::rc::*;
use tungstenite::*;
use url::Url;
pub struct SignalState {
    pub chats: HashMap<i32, Chat>,
    pub message_box: Rc<gtk::Box>,
    pub app_box: gtk::Box,
    pub contacts: HashMap<String, Contact>,
    pub messages: HashMap<String, (HashMap<i32, Message>, gtk::Box)>,
    pub current: Option<String>,
}
impl SignalState {
    pub fn new() -> SignalState {
    pub fn new(app: &gtk::Box) -> SignalState {
        let mut message_box = Rc::new(gtk::Box::new(Orientation::Vertical, 8));
        let mut app_box = gtk::Box::new(Orientation::Vertical, 8);
        app_box.add(&(*message_box));
        SignalState {
            chats: HashMap::new(),
            contacts: HashMap::new(),
            message_box: message_box,
            app_box: app_box,
            messages: HashMap::new(),
            current: None,
        }
    }
    pub fn show_messages(&mut self, tel: String) {
        if let Some(cur) = &self.current {
            self.messages.get(cur).unwrap().1.hide();
        }
        self.current = Some(tel.clone());
        if let Some(b) = self.messages.get(&tel) {
            b.1.show();
        } else {
            let b = gtk::Box::new(Orientation::Vertical, 8);
            b.show();
            self.message_box.add(&b);
            self.messages.insert(tel.clone(), (HashMap::new(), b));
        }
    }
    pub fn add_message(&mut self, message: SignalMessage) {
        if !self.messages.contains_key(&message.source) {
            let b = gtk::Box::new(Orientation::Vertical, 8);
            self.message_box.add(&b);
            self.messages
                .insert(message.source.clone(), (HashMap::new(), b));
        }
        let (msg_p, msg_box) = self.messages.get_mut(&message.source).unwrap();
        if msg_p.contains_key(&message.ID) {
            let msg = msg_p.get_mut(&message.ID).unwrap();
            msg.label.set_text(&message.message);
            msg.msg = message;
        } else {
            let construct = Message::new(message.clone());
            msg_p.insert(message.ID, construct);
            let ui_ref = &msg_p.get(&message.ID).unwrap().ui;
            msg_box.add(ui_ref);
        }
        msg_box.show_all();
    }
    pub fn add_messages(&mut self, messages: Vec<SignalMessage>) {
        for msg in messages.into_iter() {
            self.add_message(msg);
        }
    }
    pub fn process_response(&mut self, response: SignalResponse) {
        match response {
            SignalResponse::ChatList(chats) => {
                let mut new_messages = vec![];
                for chat in chats.into_iter() {
                    if let Some(mut chat_p) = self.chats.get_mut(&chat.ID) {
                    if let Some(chat_p) = self.chats.get_mut(&chat.ID) {
                        let mut inter = chat
                            .messages
                            .iter()
                            .filter(|x| !chat_p.messages.contains(x))
                            .cloned()
                            .collect::<Vec<SignalMessage>>();
                        new_messages.append(&mut inter);
                        *chat_p = chat;
                    } else {
                        self.chats.insert(chat.ID, chat);
                        new_messages.append(&mut chat.messages.clone());
                        self.chats.insert(chat.ID, chat.clone());
                    }
                }
            }
            SignalResponse::ContactList(contacts) => {
                for contact in contacts.into_iter() {
                    if let Some(contact_p) = self.contacts.get_mut(&contact.tel) {
                        *contact_p = contact;
                    } else {
                        self.contacts.insert(contact.tel.clone(), contact.clone());
                    }
                }
            }
            SignalResponse::MessageList(message_list) => {
                self.add_messages(message_list.messages);
            }
            _ => {}
        };
    }
    pub fn chat_by_tel<'a>(&'a self, tel: String) -> Option<&'a Chat> {
        self.chats.values().filter(|x| {
            x.tel == tel
        }).next()
        self.chats.values().filter(|x| x.tel == tel).next()
    }
}
pub struct Message {
    pub msg: SignalMessage,
    pub ui: gtk::Box,
    pub label: Label,
}
impl Message {
    pub fn build_ui(&self) -> gtk::Box {
    pub fn new(msg: SignalMessage) -> Message {
        let msg_box = gtk::Box::new(Orientation::Vertical, 6);
        let label = Label::new(Some(&self.msg.message));
        let label = Label::new(Some(&msg.message));
        msg_box.add(&label);
        msg_box
        let context = msg_box.get_style_context();
        context.add_class("msg");
        if msg.outgoing {
            context.add_class("outgoing");
        }
        Message {
            msg,
            ui: msg_box,
            label,
        }
    }
}
#[tokio::main]
async fn main() -> Result<()> {
    let css = include_str!("style.css");
    let mut total: Vec<SignalResponse> = vec![];
    let mut manager = SignalManager::new();
    let application = gtk::Application::new(Some("com.nicohman"), Default::default()).unwrap();
    application.connect_activate(move |app| {
        let (resSender, resReceiver) =
        let provider = CssProvider::new();
        provider.load_from_data(css.as_bytes()).unwrap();
        let (resSender, mut resReceiver) =
            glib::MainContext::channel::<SignalResponse>(glib::PRIORITY_DEFAULT);
        let l_res_sender = resSender.clone();
        let (reqSender, mut reqReceiver) = unbounded_channel::<SignalRequest>();
        let handle = tokio::spawn(async move {
            let (mut socket, response) = connect_async(Url::parse(CONNECTION).unwrap())
        tokio::spawn(async move {
            let (socket, _response) = connect_async(Url::parse(CONNECTION).unwrap())
                .await
                .unwrap();
            let mut session = SignalSession::from_socket(socket);


@@ 96,50 185,55 @@ async fn main() -> Result<()> {
                        resSender.send(next.unwrap());
                    },
                    inc = reqReceiver.recv() => {
                        println!("Sending reqest");
                        session.send(inc.unwrap()).await;
                        if let Some(v) = inc {
                            session.send(v).await;
                        }
                    },
                };
            }
            let req = SignalRequest::GetContactList;
            let req2 = SignalRequest::GetChatList;
            let req3 = SignalRequest::AddContact {
                name: "Nico".to_string(),
                phone: "".to_string(),
            };
            let res: ContactList = session.send_res(req).await.unwrap();
            //let res2 : ChatList = session.send_res(req2).await.unwrap();
            session.send(req3).await.unwrap();
            println!("{}", serde_json::to_string(&res).unwrap());
            //println!("{}", serde_json::to_string(&res2).unwrap());
        });

        let mut state = SignalState::new();
        StyleContext::add_provider_for_screen(
            &gdk::Screen::get_default().expect("Error initializing gtk css provider."),
            &provider,
            gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
        );
        let window = gtk::ApplicationWindow::new(app);
        let mut app_box = gtk::Box::new(Orientation::Vertical, 6);
        let mut state = SignalState::new(&app_box);
        window.set_title("Signal");
        window.set_border_width(10);
        window.set_position(gtk::WindowPosition::Center);
        window.set_default_size(350, 70);
        let mut input = gtk::EntryBuilder::new().build();
        let input = gtk::EntryBuilder::new().build();
        let show_input = gtk::Entry::new();
        show_input.set_placeholder_text(Some("Number to show messages from"));
        let show_button = Button::with_label("Show messages");
        let button = gtk::Button::with_label("Send debug text");
        show_button.connect_clicked(
            clone!( @strong l_res_sender, @weak show_input, @strong reqSender => move |_| {
                let inp = show_input.get_buffer().get_text().clone();
                reqSender.send(SignalRequest::GetMessageList{ id: inp.clone()});
                l_res_sender.send(SignalResponse::ShowMessages(inp));
            }),
        );
        button.connect_clicked(clone!( @weak input => move |_| {
            reqSender.send(SignalRequest::SendMessage {
                to: "".to_owned(),
                message: input.get_buffer().get_text().clone(),
            });
        }));
        let mut app_box = gtk::Box::new(Orientation::Vertical, 6);
        let mut message_box = gtk::Box::new(Orientation::Vertical, 8);
        let mut text = gtk::TextViewBuilder::new().wrap_mode(WrapMode::Word).build();
        //app_box.add(&text);
        app_box.add(&message_box);
        app_box.add(&input);
        window.add(&app_box);
        app_box.add(&button);
        let text = gtk::TextViewBuilder::new()
            .wrap_mode(WrapMode::Word)
            .build();
        app_box.add(&text);
        state.app_box.add(&show_input);
        state.app_box.add(&show_button);
        state.app_box.add(&input);
        state.app_box.add(&button);
        window.add(&state.app_box);
        resReceiver.attach(None, move |value| {
            state.process_response(value);
            text.get_buffer().unwrap().set_text(&format!("{:?}", state.chats));
            Continue(true)
            glib::Continue(true)
        });
        window.show_all();
    });

M src/signal.rs => src/signal.rs +17 -22
@@ 11,6 11,7 @@ use serde::*;
use serde::{Deserialize, Serialize};
use std::collections::*;
use std::convert::TryFrom;
use std::rc::*;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::prelude::*;
use tokio::stream::*;


@@ 41,6 42,17 @@ pub enum SignalResponse {
    ContactList(Vec<Contact>),
    #[evt(derive(Clone, Serialize, Deserialize, Debug))]
    ChatList(Vec<Chat>),
    #[evt(derive(Clone, Serialize, Deserialize, Debug))]
    MessageList(SignalMessageList),
    ShowMessages(String),
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct SignalMessageList {
    #[serde(rename = "ID")]
    pub ID: String,
    pub messages: Vec<SignalMessage>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "PascalCase")]


@@ 86,6 98,11 @@ pub struct SignalMessage {
    pub receipt: bool,
    pub status_message: bool,
}
impl PartialEq for SignalMessage {
    fn eq(&self, other: &Self) -> bool {
        self.ID == other.ID && self.message == other.message
    }
}
trait SignalResponseTrait {}
impl SignalResponseTrait for SignalResponse {}
#[serde(rename_all = "PascalCase")]


@@ 145,25 162,3 @@ impl<'a> SignalSession<'a> {
            .unwrap())
    }
}
pub struct SignalManager {
    pub contacts: HashMap<String, Contact>,
}
impl SignalManager {
    pub fn new() -> SignalManager {
        SignalManager {
            contacts: HashMap::new(),
        }
    }
    pub fn process_responses(&mut self, responses: Vec<SignalResponse>) {
        for res in responses.into_iter() {
            match res {
                SignalResponse::ContactList(v) => {
                    for contact in v.into_iter() {
                        self.contacts.insert(contact.tel.clone(), contact);
                    }
                }
                _ => {}
            }
        }
    }
}

A src/style.css => src/style.css +5 -0
@@ 0,0 1,5 @@
button {
}
.msg {
    border: 1px solid black;
}