2023-06-03 14:17:58 +00:00
|
|
|
use neveko_core::*;
|
2023-05-09 21:28:07 +00:00
|
|
|
use std::sync::mpsc::{
|
|
|
|
Receiver,
|
|
|
|
Sender,
|
|
|
|
};
|
2023-04-30 15:55:41 +00:00
|
|
|
|
2023-06-13 02:54:13 +00:00
|
|
|
use crate::ADD_CONTACT_TIMEOUT_SECS;
|
2023-04-30 15:55:41 +00:00
|
|
|
|
|
|
|
// TODO(c2m): better error handling with and error_tx/error_rx channel
|
|
|
|
// hook into the error thread and show toast messages as required
|
|
|
|
|
|
|
|
/// Maintain the message for sending in this struct
|
|
|
|
struct Compose {
|
|
|
|
message: String,
|
|
|
|
to: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Compose {
|
|
|
|
fn default() -> Self {
|
|
|
|
Compose {
|
|
|
|
message: utils::empty_string(),
|
2023-05-01 14:09:33 +00:00
|
|
|
to: utils::empty_string(),
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-04 07:08:23 +00:00
|
|
|
|
2023-04-30 15:55:41 +00:00
|
|
|
/// The AddressBookApp unfornuately does more than that.
|
2023-05-01 14:09:33 +00:00
|
|
|
///
|
2023-04-30 15:55:41 +00:00
|
|
|
/// Herein lies the logic for filtering contacts, generating JWPs,
|
2023-05-01 14:09:33 +00:00
|
|
|
///
|
2023-04-30 15:55:41 +00:00
|
|
|
/// transaction proofs, etc. Once a contact has a valid JWP that has
|
2023-05-01 14:09:33 +00:00
|
|
|
///
|
2023-04-30 15:55:41 +00:00
|
|
|
/// not yet expired the `Compose` button will appear by their i2p address.
|
2023-05-01 14:09:33 +00:00
|
|
|
///
|
2023-04-30 15:55:41 +00:00
|
|
|
/// NOTE: the `Sign Key` must be pressed for trusted contacts before a
|
2023-05-01 14:09:33 +00:00
|
|
|
///
|
2023-04-30 15:55:41 +00:00
|
|
|
/// message can be composed.
|
|
|
|
pub struct AddressBookApp {
|
|
|
|
add_nick: String,
|
|
|
|
approve_contact: bool,
|
|
|
|
approve_payment: bool,
|
|
|
|
added: bool,
|
2023-05-12 08:51:20 +00:00
|
|
|
can_transfer: bool,
|
|
|
|
can_transfer_tx: Sender<bool>,
|
|
|
|
can_transfer_rx: Receiver<bool>,
|
2023-04-30 15:55:41 +00:00
|
|
|
compose: Compose,
|
|
|
|
contact: String,
|
|
|
|
find_contact: String,
|
|
|
|
contacts: Vec<models::Contact>,
|
|
|
|
contacts_init: bool,
|
|
|
|
contact_add_tx: Sender<models::Contact>,
|
|
|
|
contact_add_rx: Receiver<models::Contact>,
|
|
|
|
contact_info_tx: Sender<models::Contact>,
|
|
|
|
contact_info_rx: Receiver<models::Contact>,
|
|
|
|
contact_timeout_tx: Sender<bool>,
|
|
|
|
contact_timeout_rx: Receiver<bool>,
|
|
|
|
invoice_tx: Sender<reqres::Invoice>,
|
|
|
|
invoice_rx: Receiver<reqres::Invoice>,
|
|
|
|
is_adding: bool,
|
|
|
|
is_composing: bool,
|
2023-06-12 15:37:39 +00:00
|
|
|
is_approving_jwp: bool,
|
2023-05-12 08:51:20 +00:00
|
|
|
is_estimating_fee: bool,
|
2023-04-30 15:55:41 +00:00
|
|
|
is_pinging: bool,
|
|
|
|
is_loading: bool,
|
|
|
|
is_message_sent: bool,
|
|
|
|
is_payment_processed: bool,
|
|
|
|
is_timeout: bool,
|
|
|
|
payment_tx: Sender<bool>,
|
|
|
|
payment_rx: Receiver<bool>,
|
|
|
|
showing_status: bool,
|
2023-06-08 21:22:13 +00:00
|
|
|
status: utils::ContactStatus,
|
2023-04-30 15:55:41 +00:00
|
|
|
send_message_tx: Sender<bool>,
|
|
|
|
send_message_rx: Receiver<bool>,
|
|
|
|
s_contact: models::Contact,
|
|
|
|
s_invoice: reqres::Invoice,
|
|
|
|
s_added_contact: models::Contact,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for AddressBookApp {
|
|
|
|
fn default() -> Self {
|
2023-05-12 08:51:20 +00:00
|
|
|
let (can_transfer_tx, can_transfer_rx) = std::sync::mpsc::channel();
|
2023-04-30 15:55:41 +00:00
|
|
|
let (contact_add_tx, contact_add_rx) = std::sync::mpsc::channel();
|
|
|
|
let (contact_info_tx, contact_info_rx) = std::sync::mpsc::channel();
|
|
|
|
let (contact_timeout_tx, contact_timeout_rx) = std::sync::mpsc::channel();
|
|
|
|
let (invoice_tx, invoice_rx) = std::sync::mpsc::channel();
|
|
|
|
let (payment_tx, payment_rx) = std::sync::mpsc::channel();
|
|
|
|
let (send_message_tx, send_message_rx) = std::sync::mpsc::channel();
|
|
|
|
AddressBookApp {
|
|
|
|
add_nick: utils::empty_string(),
|
|
|
|
approve_contact: false,
|
|
|
|
approve_payment: false,
|
|
|
|
added: false,
|
2023-05-12 08:51:20 +00:00
|
|
|
can_transfer: false,
|
|
|
|
can_transfer_rx,
|
|
|
|
can_transfer_tx,
|
2023-04-30 15:55:41 +00:00
|
|
|
compose: Default::default(),
|
|
|
|
contact: utils::empty_string(),
|
|
|
|
contacts: Vec::new(),
|
|
|
|
contacts_init: false,
|
|
|
|
contact_add_tx,
|
|
|
|
contact_add_rx,
|
|
|
|
contact_info_tx,
|
|
|
|
contact_info_rx,
|
|
|
|
contact_timeout_tx,
|
|
|
|
contact_timeout_rx,
|
|
|
|
find_contact: utils::empty_string(),
|
|
|
|
invoice_tx,
|
|
|
|
invoice_rx,
|
|
|
|
is_adding: false,
|
|
|
|
is_composing: false,
|
2023-06-12 15:37:39 +00:00
|
|
|
is_approving_jwp: false,
|
2023-05-12 08:51:20 +00:00
|
|
|
is_estimating_fee: false,
|
2023-04-30 15:55:41 +00:00
|
|
|
is_loading: false,
|
|
|
|
is_message_sent: false,
|
|
|
|
is_pinging: false,
|
|
|
|
is_payment_processed: false,
|
|
|
|
is_timeout: false,
|
|
|
|
payment_rx,
|
|
|
|
payment_tx,
|
|
|
|
send_message_tx,
|
|
|
|
send_message_rx,
|
|
|
|
status: Default::default(),
|
|
|
|
showing_status: false,
|
|
|
|
s_contact: Default::default(),
|
|
|
|
s_added_contact: Default::default(),
|
|
|
|
s_invoice: Default::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl eframe::App for AddressBookApp {
|
|
|
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
|
|
|
// Hook into async channel threads
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
if let Ok(contact_info) = self.contact_info_rx.try_recv() {
|
|
|
|
self.s_contact = contact_info;
|
|
|
|
if self.s_contact.xmr_address != utils::empty_string() && !self.showing_status {
|
|
|
|
self.approve_contact = true;
|
|
|
|
}
|
|
|
|
if self.showing_status {
|
|
|
|
self.is_pinging = false;
|
|
|
|
}
|
|
|
|
}
|
2023-05-13 00:39:49 +00:00
|
|
|
|
2023-04-30 15:55:41 +00:00
|
|
|
if let Ok(added_contact) = self.contact_add_rx.try_recv() {
|
|
|
|
self.s_added_contact = added_contact;
|
|
|
|
if self.s_added_contact.cid != utils::empty_string() {
|
|
|
|
self.added = true;
|
|
|
|
self.is_loading = false;
|
|
|
|
}
|
|
|
|
}
|
2023-05-13 00:39:49 +00:00
|
|
|
|
2023-04-30 15:55:41 +00:00
|
|
|
if let Ok(timeout) = self.contact_timeout_rx.try_recv() {
|
|
|
|
self.is_timeout = true;
|
|
|
|
if timeout {
|
|
|
|
self.is_loading = false;
|
|
|
|
self.is_adding = false;
|
|
|
|
self.approve_contact = false;
|
|
|
|
self.contact = utils::empty_string();
|
|
|
|
}
|
|
|
|
}
|
2023-05-13 00:39:49 +00:00
|
|
|
|
2023-04-30 15:55:41 +00:00
|
|
|
if let Ok(invoice) = self.invoice_rx.try_recv() {
|
|
|
|
self.s_invoice = invoice;
|
2023-05-12 08:51:20 +00:00
|
|
|
if self.s_invoice.pay_threshold > 0 {
|
|
|
|
send_can_transfer_req(
|
|
|
|
self.can_transfer_tx.clone(),
|
2023-05-13 00:39:49 +00:00
|
|
|
ctx.clone(),
|
|
|
|
self.s_invoice.pay_threshold,
|
2023-05-12 08:51:20 +00:00
|
|
|
);
|
|
|
|
self.is_estimating_fee = true;
|
|
|
|
}
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
2023-05-13 00:39:49 +00:00
|
|
|
|
2023-04-30 15:55:41 +00:00
|
|
|
if let Ok(payment) = self.payment_rx.try_recv() {
|
|
|
|
self.is_payment_processed = payment;
|
|
|
|
if self.is_payment_processed {
|
|
|
|
self.is_loading = false;
|
|
|
|
self.approve_payment = false;
|
|
|
|
self.showing_status = false;
|
|
|
|
}
|
|
|
|
}
|
2023-05-13 00:39:49 +00:00
|
|
|
|
2023-04-30 15:55:41 +00:00
|
|
|
if let Ok(message) = self.send_message_rx.try_recv() {
|
|
|
|
self.is_message_sent = message;
|
|
|
|
if self.is_message_sent {
|
|
|
|
self.is_loading = false;
|
|
|
|
self.is_composing = false;
|
|
|
|
self.compose.message = utils::empty_string();
|
|
|
|
}
|
|
|
|
}
|
2023-05-13 00:39:49 +00:00
|
|
|
|
2023-05-12 08:51:20 +00:00
|
|
|
if let Ok(can_transfer) = self.can_transfer_rx.try_recv() {
|
|
|
|
self.can_transfer = can_transfer;
|
|
|
|
self.is_estimating_fee = false;
|
|
|
|
}
|
2023-05-01 14:09:33 +00:00
|
|
|
|
2023-04-30 15:55:41 +00:00
|
|
|
// initial contact load
|
|
|
|
if !self.contacts_init {
|
|
|
|
self.contacts = contact::find_all();
|
|
|
|
self.contacts_init = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compose window
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
let mut is_composing = self.is_composing;
|
2023-06-27 08:56:15 +00:00
|
|
|
egui::Window::new("compose")
|
2023-04-30 15:55:41 +00:00
|
|
|
.open(&mut is_composing)
|
2023-06-27 08:56:15 +00:00
|
|
|
.title_bar(false)
|
2023-04-30 15:55:41 +00:00
|
|
|
.vscroll(true)
|
|
|
|
.show(&ctx, |ui| {
|
2023-06-27 08:56:15 +00:00
|
|
|
ui.heading("Compose Message");
|
2023-04-30 15:55:41 +00:00
|
|
|
if self.is_loading {
|
|
|
|
ui.add(egui::Spinner::new());
|
2023-06-10 19:34:04 +00:00
|
|
|
ui.label("sending message...");
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
2023-05-01 14:09:33 +00:00
|
|
|
ui.horizontal(|ui| ui.label(format!("to: {}", self.status.i2p)));
|
2023-04-30 15:55:41 +00:00
|
|
|
ui.horizontal(|ui| {
|
|
|
|
let message_label = ui.label("msg: ");
|
|
|
|
ui.text_edit_multiline(&mut self.compose.message)
|
|
|
|
.labelled_by(message_label.id);
|
|
|
|
});
|
|
|
|
if !self.is_loading {
|
|
|
|
self.compose.to = self.status.i2p.clone();
|
|
|
|
if self.status.jwp != utils::empty_string() {
|
|
|
|
if ui.button("Send").clicked() {
|
|
|
|
self.is_loading = true;
|
2023-05-01 14:09:33 +00:00
|
|
|
send_message_req(
|
|
|
|
self.send_message_tx.clone(),
|
|
|
|
ctx.clone(),
|
|
|
|
self.compose.message.clone(),
|
|
|
|
self.compose.to.clone(),
|
|
|
|
self.status.jwp.clone(),
|
|
|
|
);
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if ui.button("Exit").clicked() {
|
|
|
|
self.is_composing = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Payment approval window
|
|
|
|
//-----------------------------------------------------------------------------------
|
2023-05-01 14:09:33 +00:00
|
|
|
let mut is_approving_payment =
|
|
|
|
self.approve_payment && self.s_invoice.address != utils::empty_string();
|
2023-04-30 15:55:41 +00:00
|
|
|
let address = self.s_invoice.address.clone();
|
|
|
|
let amount = self.s_invoice.pay_threshold;
|
|
|
|
let expire = self.s_invoice.conf_threshold;
|
2023-06-27 08:56:15 +00:00
|
|
|
egui::Window::new("approve payment")
|
2023-04-30 15:55:41 +00:00
|
|
|
.open(&mut is_approving_payment)
|
2023-06-27 08:56:15 +00:00
|
|
|
.title_bar(false)
|
2023-04-30 15:55:41 +00:00
|
|
|
.vscroll(true)
|
|
|
|
.show(&ctx, |ui| {
|
2023-06-27 08:56:15 +00:00
|
|
|
ui.heading("Approve Payment for JWP");
|
2023-04-30 15:55:41 +00:00
|
|
|
if self.is_loading {
|
|
|
|
ui.add(egui::Spinner::new());
|
2023-06-10 19:34:04 +00:00
|
|
|
ui.label("creating jwp. please wait...");
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
2023-05-12 08:51:20 +00:00
|
|
|
if self.is_estimating_fee {
|
|
|
|
ui.add(egui::Spinner::new());
|
2023-06-03 14:17:58 +00:00
|
|
|
ui.label("running neveko jwp fee estimator...");
|
2023-05-12 08:51:20 +00:00
|
|
|
}
|
2023-04-30 15:55:41 +00:00
|
|
|
ui.heading(self.status.i2p.clone());
|
|
|
|
ui.label(format!("pay to: {}", address));
|
|
|
|
ui.label(format!("amount: {} piconero(s)", amount));
|
|
|
|
ui.label(format!("expiration: {} blocks", expire));
|
2023-11-21 02:14:36 +00:00
|
|
|
let show_approve = self.s_invoice.address != utils::empty_string()
|
2023-11-25 12:39:54 +00:00
|
|
|
&& self.can_transfer
|
|
|
|
&& !self.is_estimating_fee;
|
2023-04-30 15:55:41 +00:00
|
|
|
if !self.is_loading {
|
2023-11-21 02:14:36 +00:00
|
|
|
if show_approve {
|
2023-04-30 15:55:41 +00:00
|
|
|
if ui.button("Approve").clicked() {
|
|
|
|
// activate xmr "transfer", check the hash, update db and refresh
|
2023-05-13 00:39:49 +00:00
|
|
|
// Note it is simply disabled on insufficient funds as calcd by fee
|
|
|
|
// estimator
|
2023-04-30 15:55:41 +00:00
|
|
|
let d: reqres::Destination = reqres::Destination { address, amount };
|
2023-05-01 14:09:33 +00:00
|
|
|
send_payment_req(
|
|
|
|
self.payment_tx.clone(),
|
|
|
|
ctx.clone(),
|
|
|
|
d,
|
|
|
|
self.status.i2p.clone(),
|
|
|
|
expire,
|
2023-06-12 15:37:39 +00:00
|
|
|
false,
|
2023-05-01 14:09:33 +00:00
|
|
|
);
|
2023-04-30 15:55:41 +00:00
|
|
|
self.is_loading = true;
|
2023-06-12 15:37:39 +00:00
|
|
|
self.is_approving_jwp = false;
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
}
|
2023-07-30 09:47:27 +00:00
|
|
|
}
|
|
|
|
// TODO(c2m): add payment timeout error handling to prevent infinite loading window
|
|
|
|
if ui.button("Exit").clicked() {
|
|
|
|
self.approve_payment = false;
|
|
|
|
self.is_loading = false;
|
2023-11-21 02:14:36 +00:00
|
|
|
self.is_approving_jwp = false;
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Contact status window
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
let mut is_showing_status = self.showing_status;
|
2023-06-27 08:56:15 +00:00
|
|
|
egui::Window::new("contact status")
|
2023-04-30 15:55:41 +00:00
|
|
|
.open(&mut is_showing_status)
|
2023-06-27 08:56:15 +00:00
|
|
|
.title_bar(false)
|
2023-04-30 15:55:41 +00:00
|
|
|
.vscroll(true)
|
|
|
|
.title_bar(false)
|
|
|
|
.id(egui::Id::new(self.status.i2p.clone()))
|
|
|
|
.show(&ctx, |ui| {
|
2023-06-27 08:56:15 +00:00
|
|
|
ui.heading(&self.status.i2p);
|
2023-06-12 15:37:39 +00:00
|
|
|
if self.is_pinging || self.is_loading {
|
|
|
|
let spinner_text = if self.is_loading {
|
|
|
|
"retrying payment proof... "
|
|
|
|
} else {
|
|
|
|
"pinging..."
|
|
|
|
};
|
2023-04-30 15:55:41 +00:00
|
|
|
ui.add(egui::Spinner::new());
|
2023-06-12 15:37:39 +00:00
|
|
|
ui.label(spinner_text);
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
2023-05-01 14:09:33 +00:00
|
|
|
let status = if self.s_contact.xmr_address != utils::empty_string() {
|
|
|
|
"online"
|
|
|
|
} else {
|
|
|
|
"offline"
|
|
|
|
};
|
2023-04-30 15:55:41 +00:00
|
|
|
ui.label(format!("status: {}", status));
|
|
|
|
ui.label(format!("nick: {}", self.status.nick));
|
|
|
|
ui.label(format!("tx proof: {}", self.status.txp));
|
|
|
|
ui.label(format!("jwp: {}", self.status.jwp));
|
|
|
|
ui.label(format!("expiration: {}", self.status.h_exp));
|
|
|
|
ui.label(format!("signed key: {}", self.status.signed_key));
|
|
|
|
if self.status.jwp == utils::empty_string()
|
2023-05-01 14:09:33 +00:00
|
|
|
&& !self.is_pinging
|
|
|
|
&& status == "online"
|
|
|
|
&& self.status.txp == utils::empty_string()
|
|
|
|
{
|
2023-04-30 15:55:41 +00:00
|
|
|
if ui.button("Create JWP").clicked() {
|
2023-11-21 02:14:36 +00:00
|
|
|
self.s_invoice = Default::default();
|
2023-05-01 14:09:33 +00:00
|
|
|
send_invoice_req(
|
|
|
|
self.invoice_tx.clone(),
|
|
|
|
ctx.clone(),
|
|
|
|
self.status.i2p.clone(),
|
|
|
|
);
|
2023-04-30 15:55:41 +00:00
|
|
|
self.approve_payment = true;
|
|
|
|
self.showing_status = false;
|
2023-06-12 15:37:39 +00:00
|
|
|
self.is_approving_jwp = true;
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if !self.status.signed_key {
|
|
|
|
if ui.button("Sign Key").clicked() {
|
|
|
|
contact::trust_gpg(self.status.i2p.clone());
|
2023-06-08 21:22:13 +00:00
|
|
|
utils::write_gui_db(
|
2023-06-25 21:48:52 +00:00
|
|
|
String::from(crate::GUI_SIGNED_GPG_DB_KEY),
|
2023-05-01 14:09:33 +00:00
|
|
|
self.status.i2p.clone(),
|
2023-06-25 21:48:52 +00:00
|
|
|
String::from(crate::SIGNED_GPG_KEY),
|
2023-05-01 14:09:33 +00:00
|
|
|
);
|
2023-04-30 15:55:41 +00:00
|
|
|
self.showing_status = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let failed_to_prove = self.status.txp != utils::empty_string()
|
|
|
|
&& self.status.jwp == utils::empty_string();
|
|
|
|
if self.status.jwp != utils::empty_string() || failed_to_prove {
|
|
|
|
if ui.button("Clear stale JWP").clicked() {
|
2023-06-08 21:22:13 +00:00
|
|
|
utils::clear_gui_db(String::from("gui-txp"), self.status.i2p.clone());
|
|
|
|
utils::clear_gui_db(String::from("gui-jwp"), self.status.i2p.clone());
|
|
|
|
utils::clear_gui_db(String::from("gui-exp"), self.status.i2p.clone());
|
2023-04-30 15:55:41 +00:00
|
|
|
self.showing_status = false;
|
|
|
|
}
|
|
|
|
}
|
2023-06-12 15:37:39 +00:00
|
|
|
if self.status.txp != utils::empty_string()
|
|
|
|
&& self.status.jwp == utils::empty_string()
|
2023-06-12 16:12:13 +00:00
|
|
|
&& status == "online"
|
|
|
|
{
|
2023-06-13 02:54:13 +00:00
|
|
|
if ui.button("Prove Retry").clicked() {
|
|
|
|
send_payment_req(
|
|
|
|
self.payment_tx.clone(),
|
|
|
|
ctx.clone(),
|
|
|
|
Default::default(),
|
|
|
|
self.status.i2p.clone(),
|
|
|
|
expire as u64,
|
|
|
|
true,
|
|
|
|
);
|
|
|
|
self.is_loading = true;
|
2023-06-12 15:37:39 +00:00
|
|
|
}
|
2023-06-13 02:54:13 +00:00
|
|
|
}
|
2023-04-30 15:55:41 +00:00
|
|
|
ui.horizontal(|ui| {
|
|
|
|
let nick_label = ui.label("nick: ");
|
2023-05-01 14:09:33 +00:00
|
|
|
ui.text_edit_singleline(&mut self.add_nick)
|
|
|
|
.labelled_by(nick_label.id);
|
2023-04-30 15:55:41 +00:00
|
|
|
});
|
|
|
|
if ui.button("Change nick").clicked() {
|
|
|
|
change_nick_req(self.status.i2p.clone(), self.add_nick.clone());
|
|
|
|
self.add_nick = utils::empty_string();
|
|
|
|
}
|
|
|
|
if ui.button("Exit").clicked() {
|
|
|
|
self.showing_status = false;
|
2023-06-12 15:37:39 +00:00
|
|
|
self.is_loading = false;
|
2023-11-21 02:14:36 +00:00
|
|
|
self.is_approving_jwp = false;
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Main panel for adding contacts
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
egui::CentralPanel::default().show(ctx, |ui| {
|
2023-06-12 15:37:39 +00:00
|
|
|
if self.is_approving_jwp {
|
|
|
|
ui.add(egui::Spinner::new());
|
|
|
|
}
|
2023-04-30 15:55:41 +00:00
|
|
|
ui.heading("Add Contact");
|
|
|
|
ui.label(
|
|
|
|
"____________________________________________________________________________\n",
|
|
|
|
);
|
|
|
|
ui.horizontal(|ui| {
|
|
|
|
let contact_label = ui.label("contact: ");
|
2023-05-01 14:09:33 +00:00
|
|
|
ui.text_edit_singleline(&mut self.contact)
|
|
|
|
.labelled_by(contact_label.id);
|
2023-04-30 15:55:41 +00:00
|
|
|
});
|
|
|
|
let mut is_approved = self.approve_contact;
|
|
|
|
let mut is_added = self.added;
|
|
|
|
let is_loading = self.is_loading;
|
|
|
|
let i2p_address = self.s_contact.i2p_address.clone();
|
2023-06-03 05:13:56 +00:00
|
|
|
let is_vendor = self.s_contact.is_vendor;
|
2023-04-30 15:55:41 +00:00
|
|
|
let xmr_address = self.s_contact.xmr_address.clone();
|
|
|
|
let gpg_key = self.s_contact.gpg_key.iter().cloned().collect();
|
2023-05-01 14:09:33 +00:00
|
|
|
|
2023-04-30 15:55:41 +00:00
|
|
|
// Contact added confirmation screen
|
|
|
|
//-----------------------------------------------------------------------------------
|
2023-06-27 08:56:15 +00:00
|
|
|
egui::Window::new("added contact")
|
2023-04-30 15:55:41 +00:00
|
|
|
.open(&mut is_added)
|
2023-06-27 08:56:15 +00:00
|
|
|
.title_bar(false)
|
2023-04-30 15:55:41 +00:00
|
|
|
.vscroll(true)
|
|
|
|
.show(ctx, |ui| {
|
2023-06-27 08:56:15 +00:00
|
|
|
ui.heading("Added contact");
|
2023-04-30 15:55:41 +00:00
|
|
|
ui.label(format!("i2p address: {}", self.s_added_contact.i2p_address));
|
|
|
|
if ui.button("Exit").clicked() {
|
|
|
|
self.added = false;
|
|
|
|
self.contact = utils::empty_string();
|
|
|
|
self.is_adding = false;
|
|
|
|
self.approve_contact = false;
|
|
|
|
self.contacts = contact::find_all();
|
2023-05-01 14:09:33 +00:00
|
|
|
for c in &self.contacts {
|
|
|
|
ui.label(format!("{}", c.i2p_address));
|
|
|
|
}
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
});
|
2023-05-01 14:09:33 +00:00
|
|
|
|
2023-04-30 15:55:41 +00:00
|
|
|
// Contact approval screen
|
|
|
|
//-----------------------------------------------------------------------------------
|
2023-06-27 08:56:15 +00:00
|
|
|
egui::Window::new("approve contact")
|
2023-04-30 15:55:41 +00:00
|
|
|
.open(&mut is_approved)
|
2023-06-27 08:56:15 +00:00
|
|
|
.title_bar(false)
|
2023-04-30 15:55:41 +00:00
|
|
|
.vscroll(true)
|
|
|
|
.show(ctx, |ui| {
|
2023-06-27 08:56:15 +00:00
|
|
|
ui.heading("Approve Contact");
|
2023-04-30 15:55:41 +00:00
|
|
|
if is_loading {
|
|
|
|
ui.add(egui::Spinner::new());
|
|
|
|
ui.label("adding contact...");
|
|
|
|
}
|
|
|
|
ui.label(format!("i2p: {}", i2p_address));
|
|
|
|
ui.label(format!("xmr: {}", xmr_address));
|
2023-05-01 14:09:33 +00:00
|
|
|
ui.label(format!(
|
|
|
|
"gpg: {}",
|
|
|
|
String::from_utf8(gpg_key).unwrap_or(utils::empty_string())
|
|
|
|
));
|
2023-04-30 15:55:41 +00:00
|
|
|
ui.horizontal(|ui| {
|
|
|
|
if !is_loading {
|
|
|
|
if ui.button("Approve").clicked() {
|
|
|
|
self.is_loading = true;
|
|
|
|
self.approve_contact = false;
|
2023-06-19 04:36:31 +00:00
|
|
|
self.is_adding = false;
|
2023-04-30 15:55:41 +00:00
|
|
|
let c_contact: models::Contact = models::Contact {
|
|
|
|
cid: self.s_contact.cid.clone(),
|
2023-05-01 14:09:33 +00:00
|
|
|
i2p_address,
|
2023-06-03 05:13:56 +00:00
|
|
|
is_vendor,
|
2023-05-01 14:09:33 +00:00
|
|
|
xmr_address,
|
|
|
|
gpg_key: self.s_contact.gpg_key.iter().cloned().collect(),
|
2023-04-30 15:55:41 +00:00
|
|
|
};
|
2023-05-01 14:09:33 +00:00
|
|
|
send_create_contact_req(
|
|
|
|
self.contact_add_tx.clone(),
|
|
|
|
ctx.clone(),
|
|
|
|
c_contact,
|
|
|
|
);
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
if ui.button("Exit").clicked() {
|
|
|
|
self.approve_contact = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
if self.is_adding {
|
|
|
|
ui.add(egui::Spinner::new());
|
|
|
|
}
|
|
|
|
if !self.is_adding && self.contact.contains(".b32.i2p") {
|
|
|
|
if ui.button("Add").clicked() {
|
|
|
|
// Get the contacts information from the /share API
|
|
|
|
let contact = self.contact.clone();
|
2023-06-11 22:28:28 +00:00
|
|
|
let prune = contact::Prune::Full.value();
|
|
|
|
send_contact_info_req(
|
|
|
|
self.contact_info_tx.clone(),
|
|
|
|
ctx.clone(),
|
|
|
|
contact,
|
|
|
|
prune,
|
|
|
|
);
|
2023-04-30 15:55:41 +00:00
|
|
|
add_contact_timeout(self.contact_timeout_tx.clone(), ctx.clone());
|
|
|
|
self.is_adding = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Contact filter
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
ui.heading("\nFind Contact");
|
|
|
|
ui.label(
|
|
|
|
"____________________________________________________________________________\n",
|
|
|
|
);
|
|
|
|
ui.horizontal(|ui| {
|
|
|
|
let find_contact_label = ui.label("filter contacts: ");
|
2023-05-01 14:09:33 +00:00
|
|
|
ui.text_edit_singleline(&mut self.find_contact)
|
|
|
|
.labelled_by(find_contact_label.id);
|
2023-04-30 15:55:41 +00:00
|
|
|
});
|
|
|
|
ui.label("\n");
|
2023-05-09 21:28:07 +00:00
|
|
|
use egui_extras::{
|
|
|
|
Column,
|
|
|
|
TableBuilder,
|
|
|
|
};
|
2023-05-01 14:09:33 +00:00
|
|
|
let table = TableBuilder::new(ui)
|
|
|
|
.striped(true)
|
|
|
|
.resizable(true)
|
|
|
|
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
|
|
|
.column(Column::auto())
|
|
|
|
.column(Column::initial(100.0).at_least(40.0).clip(true))
|
|
|
|
.column(Column::initial(100.0).at_least(40.0).clip(true))
|
|
|
|
.column(Column::initial(100.0).at_least(40.0).clip(true))
|
|
|
|
.column(Column::remainder())
|
|
|
|
.min_scrolled_height(0.0);
|
2023-04-30 15:55:41 +00:00
|
|
|
|
2023-05-01 14:09:33 +00:00
|
|
|
table
|
|
|
|
.header(20.0, |mut header| {
|
|
|
|
header.col(|ui| {
|
|
|
|
ui.strong("Nickname");
|
|
|
|
});
|
|
|
|
header.col(|ui| {
|
|
|
|
ui.strong(".b32.i2p");
|
|
|
|
});
|
|
|
|
header.col(|ui| {
|
|
|
|
ui.strong("");
|
|
|
|
});
|
|
|
|
header.col(|ui| {
|
|
|
|
ui.strong("");
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.body(|mut body| {
|
|
|
|
for c in &self.contacts {
|
|
|
|
if c.i2p_address.contains(&self.find_contact) {
|
|
|
|
let row_height = 20.0;
|
|
|
|
body.row(row_height, |mut row| {
|
|
|
|
row.col(|ui| {
|
|
|
|
ui.label("anon");
|
|
|
|
});
|
|
|
|
row.col(|ui| {
|
|
|
|
ui.label(format!("{}", c.i2p_address));
|
|
|
|
});
|
|
|
|
row.col(|ui| {
|
|
|
|
if ui.button("Check Status").clicked() {
|
2023-06-08 21:22:13 +00:00
|
|
|
let nick_db = utils::search_gui_db(
|
2023-06-25 21:48:52 +00:00
|
|
|
String::from(crate::GUI_NICK_DB_KEY),
|
2023-05-01 14:09:33 +00:00
|
|
|
String::from(&c.i2p_address),
|
|
|
|
);
|
|
|
|
let nick = if nick_db == utils::empty_string() {
|
|
|
|
String::from("anon")
|
|
|
|
} else {
|
|
|
|
nick_db
|
|
|
|
};
|
|
|
|
self.status.nick = nick;
|
|
|
|
self.status.i2p = String::from(&c.i2p_address);
|
|
|
|
// get the txp
|
2023-06-08 21:22:13 +00:00
|
|
|
self.status.txp = utils::search_gui_db(
|
2023-06-25 21:48:52 +00:00
|
|
|
String::from(crate::GUI_TX_PROOF_DB_KEY),
|
2023-05-01 14:09:33 +00:00
|
|
|
String::from(&c.i2p_address),
|
|
|
|
);
|
|
|
|
// get the jwp
|
2023-06-08 21:22:13 +00:00
|
|
|
self.status.jwp = utils::search_gui_db(
|
2023-06-25 21:48:52 +00:00
|
|
|
String::from(crate::GUI_JWP_DB_KEY),
|
2023-05-01 14:09:33 +00:00
|
|
|
String::from(&c.i2p_address),
|
|
|
|
);
|
2023-06-08 21:22:13 +00:00
|
|
|
let r_exp = utils::search_gui_db(
|
2023-06-25 21:48:52 +00:00
|
|
|
String::from(crate::GUI_EXP_DB_KEY),
|
2023-05-01 14:09:33 +00:00
|
|
|
String::from(&c.i2p_address),
|
|
|
|
);
|
|
|
|
self.status.exp = r_exp;
|
2023-04-30 15:55:41 +00:00
|
|
|
let expire = match self.status.exp.parse::<i64>() {
|
|
|
|
Ok(n) => n,
|
|
|
|
Err(_e) => 0,
|
|
|
|
};
|
2023-05-01 14:09:33 +00:00
|
|
|
self.status.h_exp =
|
|
|
|
chrono::NaiveDateTime::from_timestamp_opt(expire, 0)
|
|
|
|
.unwrap()
|
|
|
|
.to_string();
|
|
|
|
// MESSAGES WON'T BE SENT UNTIL KEY IS SIGNED AND TRUSTED!
|
|
|
|
self.status.signed_key =
|
|
|
|
check_signed_key(self.status.i2p.clone());
|
2023-06-11 22:28:28 +00:00
|
|
|
let prune = contact::Prune::Pruned.value();
|
2023-05-01 14:09:33 +00:00
|
|
|
send_contact_info_req(
|
|
|
|
self.contact_info_tx.clone(),
|
|
|
|
ctx.clone(),
|
|
|
|
self.status.i2p.clone(),
|
2023-06-11 22:28:28 +00:00
|
|
|
prune,
|
2023-05-01 14:09:33 +00:00
|
|
|
);
|
|
|
|
self.showing_status = true;
|
|
|
|
self.is_pinging = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
row.col(|ui| {
|
|
|
|
let now = chrono::offset::Utc::now().timestamp();
|
|
|
|
let expire = match self.status.exp.parse::<i64>() {
|
|
|
|
Ok(n) => n,
|
|
|
|
Err(_e) => 0,
|
|
|
|
};
|
|
|
|
if now < expire
|
|
|
|
&& self.status.signed_key
|
|
|
|
&& self.status.jwp != utils::empty_string()
|
|
|
|
&& c.i2p_address == self.status.i2p
|
|
|
|
{
|
|
|
|
if ui.button("Compose").clicked() {
|
|
|
|
self.is_composing = true;
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
2023-05-01 14:09:33 +00:00
|
|
|
}
|
2023-04-30 15:55:41 +00:00
|
|
|
});
|
2023-05-01 14:09:33 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2023-04-30 15:55:41 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-03 14:17:58 +00:00
|
|
|
// Send asyc requests to neveko-core
|
2023-04-30 15:55:41 +00:00
|
|
|
//------------------------------------------------------------------------------
|
2023-06-11 07:56:53 +00:00
|
|
|
fn send_contact_info_req(
|
|
|
|
tx: Sender<models::Contact>,
|
|
|
|
ctx: egui::Context,
|
|
|
|
contact: String,
|
|
|
|
prune: u32,
|
|
|
|
) {
|
2023-04-30 15:55:41 +00:00
|
|
|
log::debug!("async send_contact_info_req");
|
|
|
|
tokio::spawn(async move {
|
2023-06-11 07:56:53 +00:00
|
|
|
match contact::add_contact_request(contact, prune).await {
|
2023-04-30 15:55:41 +00:00
|
|
|
Ok(contact) => {
|
|
|
|
let _ = tx.send(contact);
|
|
|
|
ctx.request_repaint();
|
|
|
|
}
|
|
|
|
_ => log::debug!("failed to request invoice"),
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-05-01 14:09:33 +00:00
|
|
|
fn send_create_contact_req(tx: Sender<models::Contact>, ctx: egui::Context, c: models::Contact) {
|
2023-04-30 15:55:41 +00:00
|
|
|
log::debug!("async send_create_contact_req");
|
|
|
|
tokio::spawn(async move {
|
|
|
|
let j_contact = utils::contact_to_json(&c);
|
|
|
|
let a_contact: models::Contact = contact::create(&j_contact).await;
|
|
|
|
let _ = tx.send(a_contact);
|
|
|
|
ctx.request_repaint();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-05-01 14:09:33 +00:00
|
|
|
fn add_contact_timeout(tx: Sender<bool>, ctx: egui::Context) {
|
2023-04-30 15:55:41 +00:00
|
|
|
tokio::spawn(async move {
|
|
|
|
tokio::time::sleep(std::time::Duration::from_secs(ADD_CONTACT_TIMEOUT_SECS)).await;
|
|
|
|
log::error!("add contact timeout");
|
|
|
|
let _ = tx.send(true);
|
|
|
|
ctx.request_repaint();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-05-01 14:09:33 +00:00
|
|
|
fn send_invoice_req(tx: Sender<reqres::Invoice>, ctx: egui::Context, contact: String) {
|
2023-04-30 15:55:41 +00:00
|
|
|
log::debug!("async send_invoice_req");
|
|
|
|
tokio::spawn(async move {
|
|
|
|
match contact::request_invoice(contact).await {
|
|
|
|
Ok(contact) => {
|
|
|
|
let _ = tx.send(contact);
|
|
|
|
ctx.request_repaint();
|
|
|
|
}
|
|
|
|
_ => log::debug!("failed to request invoice"),
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-05-01 14:09:33 +00:00
|
|
|
fn send_payment_req(
|
|
|
|
tx: Sender<bool>,
|
|
|
|
ctx: egui::Context,
|
|
|
|
d: reqres::Destination,
|
|
|
|
contact: String,
|
|
|
|
expire: u64,
|
2023-06-12 15:37:39 +00:00
|
|
|
retry: bool,
|
2023-05-01 14:09:33 +00:00
|
|
|
) {
|
2023-04-30 15:55:41 +00:00
|
|
|
log::debug!("async send_payment_req");
|
|
|
|
log::debug!("cleaning stale jwp values");
|
|
|
|
tokio::spawn(async move {
|
2023-06-12 15:37:39 +00:00
|
|
|
if !retry {
|
|
|
|
utils::clear_gui_db(String::from("gui-txp"), String::from(&contact));
|
|
|
|
utils::clear_gui_db(String::from("gui-jwp"), String::from(&contact));
|
|
|
|
utils::clear_gui_db(String::from("gui-exp"), String::from(&contact));
|
|
|
|
let ptxp_address = String::from(&d.address);
|
|
|
|
let ftxp_address = String::from(&d.address);
|
|
|
|
log::debug!("sending {} piconero(s) to: {}", &d.amount, &d.address);
|
|
|
|
let wallet_name = String::from(neveko_core::APP_NAME);
|
2023-06-12 16:12:13 +00:00
|
|
|
let wallet_password = std::env::var(neveko_core::MONERO_WALLET_PASSWORD)
|
|
|
|
.unwrap_or(String::from("password"));
|
2023-06-12 15:37:39 +00:00
|
|
|
monero::open_wallet(&wallet_name, &wallet_password).await;
|
|
|
|
let transfer: reqres::XmrRpcTransferResponse = monero::transfer(d).await;
|
|
|
|
// in order to keep the jwp creation process transparent to the user
|
|
|
|
// we will process all logic in one shot here.
|
2023-04-30 15:55:41 +00:00
|
|
|
|
2023-06-12 15:37:39 +00:00
|
|
|
// use the hash to create a PENDING transaction proof
|
|
|
|
let ptxp_hash = String::from(&transfer.result.tx_hash);
|
|
|
|
let ftxp_hash = String::from(&transfer.result.tx_hash);
|
|
|
|
let ptxp: proof::TxProof = proof::TxProof {
|
|
|
|
subaddress: ptxp_address,
|
|
|
|
confirmations: 0,
|
|
|
|
hash: ptxp_hash,
|
|
|
|
message: utils::empty_string(),
|
|
|
|
signature: utils::empty_string(),
|
|
|
|
};
|
|
|
|
log::debug!("creating transaction proof for: {}", &ptxp.hash);
|
2023-06-30 08:59:18 +00:00
|
|
|
// if we made it this far we can now request a JWP from our friend
|
|
|
|
// wait a bit for the tx to propogate, i2p takes longer
|
|
|
|
let wait = if std::env::var(neveko_core::GUI_REMOTE_NODE)
|
|
|
|
.unwrap_or(utils::empty_string())
|
|
|
|
== String::from(neveko_core::GUI_SET_REMOTE_NODE)
|
|
|
|
{
|
|
|
|
crate::I2P_PROPAGATION_TIME_IN_SECS_EST
|
|
|
|
} else {
|
|
|
|
crate::PROPAGATION_TIME_IN_SECS_EST
|
|
|
|
};
|
|
|
|
tokio::time::sleep(std::time::Duration::from_secs(wait)).await;
|
2023-06-12 15:37:39 +00:00
|
|
|
let get_txp: reqres::XmrRpcGetTxProofResponse = monero::get_tx_proof(ptxp).await;
|
2023-06-12 16:12:13 +00:00
|
|
|
// TODO(c2m): error handling on failed tx proof generation
|
2023-06-12 15:37:39 +00:00
|
|
|
// use the signature to create the FINALIZED transaction proof
|
|
|
|
let ftxp: proof::TxProof = proof::TxProof {
|
|
|
|
subaddress: ftxp_address,
|
|
|
|
confirmations: 0,
|
|
|
|
hash: ftxp_hash,
|
|
|
|
message: utils::empty_string(),
|
|
|
|
signature: get_txp.result.signature,
|
|
|
|
};
|
|
|
|
utils::write_gui_db(
|
2023-06-25 21:48:52 +00:00
|
|
|
String::from(crate::GUI_TX_PROOF_DB_KEY),
|
2023-06-12 15:37:39 +00:00
|
|
|
String::from(&contact),
|
|
|
|
String::from(&ftxp.signature),
|
|
|
|
);
|
|
|
|
utils::write_gui_db(
|
2023-06-25 21:48:52 +00:00
|
|
|
String::from(crate::GUI_TX_HASH_DB_KEY),
|
2023-06-12 15:37:39 +00:00
|
|
|
String::from(&contact),
|
|
|
|
String::from(&ftxp.hash),
|
|
|
|
);
|
|
|
|
utils::write_gui_db(
|
2023-06-25 21:48:52 +00:00
|
|
|
String::from(crate::GUI_TX_SIGNATURE_DB_KEY),
|
2023-06-12 15:37:39 +00:00
|
|
|
String::from(&contact),
|
|
|
|
String::from(&ftxp.signature),
|
|
|
|
);
|
|
|
|
utils::write_gui_db(
|
2023-06-25 21:48:52 +00:00
|
|
|
String::from(crate::GUI_TX_SUBADDRESS_DB_KEY),
|
2023-06-12 15:37:39 +00:00
|
|
|
String::from(&contact),
|
|
|
|
String::from(&ftxp.subaddress),
|
|
|
|
);
|
|
|
|
log::debug!(
|
|
|
|
"proving payment to {} for: {}",
|
|
|
|
String::from(&contact),
|
|
|
|
&ftxp.hash
|
|
|
|
);
|
|
|
|
match proof::prove_payment(String::from(&contact), &ftxp).await {
|
|
|
|
Ok(result) => {
|
|
|
|
utils::write_gui_db(
|
2023-06-25 21:48:52 +00:00
|
|
|
String::from(crate::GUI_JWP_DB_KEY),
|
2023-06-12 15:37:39 +00:00
|
|
|
String::from(&contact),
|
|
|
|
String::from(&result.jwp),
|
|
|
|
);
|
|
|
|
// this is just an estimate expiration but should suffice
|
|
|
|
let seconds: i64 = expire as i64 * 2 * 60;
|
2023-06-12 16:12:13 +00:00
|
|
|
let unix: i64 = chrono::offset::Utc::now().timestamp() + seconds;
|
2023-06-12 15:37:39 +00:00
|
|
|
utils::write_gui_db(
|
2023-06-25 21:48:52 +00:00
|
|
|
String::from(crate::GUI_EXP_DB_KEY),
|
2023-06-12 15:37:39 +00:00
|
|
|
String::from(&contact),
|
|
|
|
format!("{}", unix),
|
|
|
|
);
|
|
|
|
ctx.request_repaint();
|
|
|
|
}
|
|
|
|
_ => log::error!("failed to obtain jwp"),
|
|
|
|
}
|
|
|
|
monero::close_wallet(&wallet_name, &wallet_password).await;
|
|
|
|
}
|
|
|
|
if retry {
|
2023-06-25 21:48:52 +00:00
|
|
|
let k_hash = String::from(crate::GUI_TX_HASH_DB_KEY);
|
|
|
|
let k_sig = String::from(crate::GUI_TX_SIGNATURE_DB_KEY);
|
|
|
|
let k_subaddress = String::from(crate::GUI_TX_SUBADDRESS_DB_KEY);
|
2023-06-12 15:37:39 +00:00
|
|
|
let hash = utils::search_gui_db(k_hash, String::from(&contact));
|
|
|
|
let signature = utils::search_gui_db(k_sig, String::from(&contact));
|
|
|
|
let subaddress = utils::search_gui_db(k_subaddress, String::from(&contact));
|
|
|
|
let ftxp: proof::TxProof = proof::TxProof {
|
|
|
|
subaddress,
|
|
|
|
confirmations: 0,
|
|
|
|
hash: String::from(&hash),
|
|
|
|
message: utils::empty_string(),
|
|
|
|
signature,
|
|
|
|
};
|
|
|
|
log::debug!(
|
|
|
|
"proving payment to {} for: {}",
|
|
|
|
String::from(&contact),
|
|
|
|
&ftxp.hash
|
|
|
|
);
|
|
|
|
match proof::prove_payment(String::from(&contact), &ftxp).await {
|
|
|
|
Ok(result) => {
|
|
|
|
utils::write_gui_db(
|
2023-06-25 21:48:52 +00:00
|
|
|
String::from(crate::GUI_JWP_DB_KEY),
|
2023-06-12 15:37:39 +00:00
|
|
|
String::from(&contact),
|
|
|
|
String::from(&result.jwp),
|
|
|
|
);
|
|
|
|
ctx.request_repaint();
|
|
|
|
}
|
|
|
|
_ => log::error!("failed to obtain jwp"),
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
|
|
|
}
|
2023-05-01 14:09:33 +00:00
|
|
|
let _ = tx.send(true);
|
2023-04-30 15:55:41 +00:00
|
|
|
ctx.request_repaint();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-05-01 14:09:33 +00:00
|
|
|
fn send_message_req(tx: Sender<bool>, ctx: egui::Context, body: String, to: String, jwp: String) {
|
2023-04-30 15:55:41 +00:00
|
|
|
log::debug!("constructing message");
|
|
|
|
let m: models::Message = models::Message {
|
|
|
|
body: body.into_bytes(),
|
|
|
|
to,
|
|
|
|
mid: utils::empty_string(),
|
|
|
|
uid: utils::empty_string(),
|
|
|
|
created: 0,
|
2023-06-04 16:30:30 +00:00
|
|
|
from: i2p::get_destination(None),
|
2023-04-30 15:55:41 +00:00
|
|
|
};
|
|
|
|
let j_message = utils::message_to_json(&m);
|
|
|
|
tokio::spawn(async move {
|
2023-06-01 14:14:15 +00:00
|
|
|
let m_type = message::MessageType::Normal;
|
|
|
|
let result = message::create(j_message, jwp, m_type).await;
|
2023-04-30 15:55:41 +00:00
|
|
|
if result.mid != utils::empty_string() {
|
|
|
|
log::info!("sent message: {}", result.mid);
|
2023-05-01 14:09:33 +00:00
|
|
|
let _ = tx.send(true);
|
2023-04-30 15:55:41 +00:00
|
|
|
ctx.request_repaint();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_signed_key(contact: String) -> bool {
|
2023-06-25 21:48:52 +00:00
|
|
|
let v = utils::search_gui_db(String::from(crate::GUI_SIGNED_GPG_DB_KEY), contact);
|
2023-04-30 15:55:41 +00:00
|
|
|
v != utils::empty_string()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn change_nick_req(contact: String, nick: String) {
|
|
|
|
log::debug!("change nick");
|
2023-06-25 21:48:52 +00:00
|
|
|
utils::clear_gui_db(String::from(crate::GUI_NICK_DB_KEY), String::from(&contact));
|
|
|
|
utils::write_gui_db(
|
|
|
|
String::from(crate::GUI_NICK_DB_KEY),
|
|
|
|
String::from(&contact),
|
|
|
|
nick,
|
|
|
|
);
|
2023-04-30 15:55:41 +00:00
|
|
|
}
|
2023-05-12 08:51:20 +00:00
|
|
|
|
|
|
|
fn send_can_transfer_req(tx: Sender<bool>, ctx: egui::Context, invoice: u128) {
|
|
|
|
log::debug!("async send_can_transfer_req");
|
|
|
|
tokio::spawn(async move {
|
|
|
|
let can_transfer = utils::can_transfer(invoice).await;
|
|
|
|
log::debug!("can transfer: {}", can_transfer);
|
|
|
|
let _ = tx.send(can_transfer);
|
|
|
|
ctx.request_repaint();
|
|
|
|
});
|
|
|
|
}
|