mirror of
https://github.com/creating2morrow/neveko.git
synced 2024-12-22 19:49:24 +00:00
add products view for customers
This commit is contained in:
parent
eb806fa36e
commit
9fc930fb5b
5 changed files with 375 additions and 81 deletions
|
@ -10,6 +10,7 @@ use log::{
|
||||||
info,
|
info,
|
||||||
};
|
};
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
/// Create a new product
|
/// Create a new product
|
||||||
pub fn create(d: Json<Product>) -> Product {
|
pub fn create(d: Json<Product>) -> Product {
|
||||||
|
@ -102,3 +103,32 @@ fn validate_product(p: &Json<Product>) -> bool {
|
||||||
&& p.name.len() < utils::string_limit()
|
&& p.name.len() < utils::string_limit()
|
||||||
&& p.image.len() < utils::image_limit()
|
&& p.image.len() < utils::image_limit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send the request to vendor for the products available
|
||||||
|
pub async fn get_vendor_products(
|
||||||
|
contact: String,
|
||||||
|
jwp: String,
|
||||||
|
) -> Result<Vec<Product>, Box<dyn Error>> {
|
||||||
|
let host = utils::get_i2p_http_proxy();
|
||||||
|
let proxy = reqwest::Proxy::http(&host)?;
|
||||||
|
let client = reqwest::Client::builder().proxy(proxy).build();
|
||||||
|
match client?
|
||||||
|
.get(format!("http://{}/market/products", contact))
|
||||||
|
.header("proof", jwp)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(response) => {
|
||||||
|
let res = response.json::<Vec<Product>>().await;
|
||||||
|
debug!("get vendor products response: {:?}", res);
|
||||||
|
match res {
|
||||||
|
Ok(r) => Ok(r),
|
||||||
|
_ => Ok(Default::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed to fetch products due to: {:?}", e);
|
||||||
|
Ok(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,38 @@ use rand_core::RngCore;
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
/// Struct for the vendor / contact status window
|
||||||
|
pub struct ContactStatus {
|
||||||
|
/// UNIX timestamp of expiration as string
|
||||||
|
pub exp: String,
|
||||||
|
/// human readable date of expiration as string
|
||||||
|
pub h_exp: String,
|
||||||
|
/// i2p address of current status check
|
||||||
|
pub i2p: String,
|
||||||
|
/// JSON Web Proof of current status check
|
||||||
|
pub jwp: String,
|
||||||
|
/// Alias for contact
|
||||||
|
pub nick: String,
|
||||||
|
/// Must sign key for contacts befor messages can be sent
|
||||||
|
pub signed_key: bool,
|
||||||
|
/// transaction proof signature of current status check
|
||||||
|
pub txp: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ContactStatus {
|
||||||
|
fn default() -> Self {
|
||||||
|
ContactStatus {
|
||||||
|
exp: utils::empty_string(),
|
||||||
|
h_exp: utils::empty_string(),
|
||||||
|
i2p: utils::empty_string(),
|
||||||
|
jwp: utils::empty_string(),
|
||||||
|
nick: String::from("anon"),
|
||||||
|
signed_key: false,
|
||||||
|
txp: utils::empty_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Enum for selecting hash validation
|
/// Enum for selecting hash validation
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum ExternalSoftware {
|
enum ExternalSoftware {
|
||||||
|
@ -698,6 +730,24 @@ pub fn toggle_vendor_enabled() -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn search_gui_db(f: String, data: String) -> String {
|
||||||
|
let s = db::Interface::open();
|
||||||
|
let k = format!("{}-{}", f, data);
|
||||||
|
db::Interface::read(&s.env, &s.handle, &k)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_gui_db(f: String, key: String, data: String) {
|
||||||
|
let s = db::Interface::open();
|
||||||
|
let k = format!("{}-{}", f, key);
|
||||||
|
db::Interface::write(&s.env, &s.handle, &k, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_gui_db(f: String, key: String) {
|
||||||
|
let s = db::Interface::open();
|
||||||
|
let k = format!("{}-{}", f, key);
|
||||||
|
db::Interface::delete(&s.env, &s.handle, &k);
|
||||||
|
}
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
//-------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -27,37 +27,6 @@ impl Default for Compose {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Struct for the contact status window
|
|
||||||
struct Status {
|
|
||||||
/// UNIX timestamp of expiration as string
|
|
||||||
exp: String,
|
|
||||||
/// human readable date of expiration as string
|
|
||||||
h_exp: String,
|
|
||||||
/// i2p address of current status check
|
|
||||||
i2p: String,
|
|
||||||
/// JSON Web Proof of current status check
|
|
||||||
jwp: String,
|
|
||||||
/// Alias for contact
|
|
||||||
nick: String,
|
|
||||||
signed_key: bool,
|
|
||||||
/// transaction proof signature of current status check
|
|
||||||
txp: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Status {
|
|
||||||
fn default() -> Self {
|
|
||||||
Status {
|
|
||||||
exp: utils::empty_string(),
|
|
||||||
h_exp: utils::empty_string(),
|
|
||||||
i2p: utils::empty_string(),
|
|
||||||
jwp: utils::empty_string(),
|
|
||||||
nick: String::from("anon"),
|
|
||||||
signed_key: false,
|
|
||||||
txp: utils::empty_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The AddressBookApp unfornuately does more than that.
|
/// The AddressBookApp unfornuately does more than that.
|
||||||
///
|
///
|
||||||
/// Herein lies the logic for filtering contacts, generating JWPs,
|
/// Herein lies the logic for filtering contacts, generating JWPs,
|
||||||
|
@ -101,7 +70,7 @@ pub struct AddressBookApp {
|
||||||
payment_tx: Sender<bool>,
|
payment_tx: Sender<bool>,
|
||||||
payment_rx: Receiver<bool>,
|
payment_rx: Receiver<bool>,
|
||||||
showing_status: bool,
|
showing_status: bool,
|
||||||
status: Status,
|
status: utils::ContactStatus,
|
||||||
send_message_tx: Sender<bool>,
|
send_message_tx: Sender<bool>,
|
||||||
send_message_rx: Receiver<bool>,
|
send_message_rx: Receiver<bool>,
|
||||||
s_contact: models::Contact,
|
s_contact: models::Contact,
|
||||||
|
@ -359,7 +328,7 @@ impl eframe::App for AddressBookApp {
|
||||||
if !self.status.signed_key {
|
if !self.status.signed_key {
|
||||||
if ui.button("Sign Key").clicked() {
|
if ui.button("Sign Key").clicked() {
|
||||||
contact::trust_gpg(self.status.i2p.clone());
|
contact::trust_gpg(self.status.i2p.clone());
|
||||||
write_gui_db(
|
utils::write_gui_db(
|
||||||
String::from("gui-signed-key"),
|
String::from("gui-signed-key"),
|
||||||
self.status.i2p.clone(),
|
self.status.i2p.clone(),
|
||||||
String::from("1"),
|
String::from("1"),
|
||||||
|
@ -371,9 +340,9 @@ impl eframe::App for AddressBookApp {
|
||||||
&& self.status.jwp == utils::empty_string();
|
&& self.status.jwp == utils::empty_string();
|
||||||
if self.status.jwp != utils::empty_string() || failed_to_prove {
|
if self.status.jwp != utils::empty_string() || failed_to_prove {
|
||||||
if ui.button("Clear stale JWP").clicked() {
|
if ui.button("Clear stale JWP").clicked() {
|
||||||
clear_gui_db(String::from("gui-txp"), self.status.i2p.clone());
|
utils::clear_gui_db(String::from("gui-txp"), self.status.i2p.clone());
|
||||||
clear_gui_db(String::from("gui-jwp"), self.status.i2p.clone());
|
utils::clear_gui_db(String::from("gui-jwp"), self.status.i2p.clone());
|
||||||
clear_gui_db(String::from("gui-exp"), self.status.i2p.clone());
|
utils::clear_gui_db(String::from("gui-exp"), self.status.i2p.clone());
|
||||||
self.showing_status = false;
|
self.showing_status = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -538,7 +507,7 @@ impl eframe::App for AddressBookApp {
|
||||||
});
|
});
|
||||||
row.col(|ui| {
|
row.col(|ui| {
|
||||||
if ui.button("Check Status").clicked() {
|
if ui.button("Check Status").clicked() {
|
||||||
let nick_db = search_gui_db(
|
let nick_db = utils::search_gui_db(
|
||||||
String::from("gui-nick"),
|
String::from("gui-nick"),
|
||||||
String::from(&c.i2p_address),
|
String::from(&c.i2p_address),
|
||||||
);
|
);
|
||||||
|
@ -550,16 +519,16 @@ impl eframe::App for AddressBookApp {
|
||||||
self.status.nick = nick;
|
self.status.nick = nick;
|
||||||
self.status.i2p = String::from(&c.i2p_address);
|
self.status.i2p = String::from(&c.i2p_address);
|
||||||
// get the txp
|
// get the txp
|
||||||
self.status.txp = search_gui_db(
|
self.status.txp = utils::search_gui_db(
|
||||||
String::from("gui-txp"),
|
String::from("gui-txp"),
|
||||||
String::from(&c.i2p_address),
|
String::from(&c.i2p_address),
|
||||||
);
|
);
|
||||||
// get the jwp
|
// get the jwp
|
||||||
self.status.jwp = search_gui_db(
|
self.status.jwp = utils::search_gui_db(
|
||||||
String::from("gui-jwp"),
|
String::from("gui-jwp"),
|
||||||
String::from(&c.i2p_address),
|
String::from(&c.i2p_address),
|
||||||
);
|
);
|
||||||
let r_exp = search_gui_db(
|
let r_exp = utils::search_gui_db(
|
||||||
String::from("gui-exp"),
|
String::from("gui-exp"),
|
||||||
String::from(&c.i2p_address),
|
String::from(&c.i2p_address),
|
||||||
);
|
);
|
||||||
|
@ -642,24 +611,6 @@ fn add_contact_timeout(tx: Sender<bool>, ctx: egui::Context) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_gui_db(f: String, data: String) -> String {
|
|
||||||
let s = db::Interface::open();
|
|
||||||
let k = format!("{}-{}", f, data);
|
|
||||||
db::Interface::read(&s.env, &s.handle, &k)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_gui_db(f: String, key: String, data: String) {
|
|
||||||
let s = db::Interface::open();
|
|
||||||
let k = format!("{}-{}", f, key);
|
|
||||||
db::Interface::write(&s.env, &s.handle, &k, &data);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_gui_db(f: String, key: String) {
|
|
||||||
let s = db::Interface::open();
|
|
||||||
let k = format!("{}-{}", f, key);
|
|
||||||
db::Interface::delete(&s.env, &s.handle, &k);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_invoice_req(tx: Sender<reqres::Invoice>, ctx: egui::Context, contact: String) {
|
fn send_invoice_req(tx: Sender<reqres::Invoice>, ctx: egui::Context, contact: String) {
|
||||||
log::debug!("async send_invoice_req");
|
log::debug!("async send_invoice_req");
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
@ -682,9 +633,9 @@ fn send_payment_req(
|
||||||
) {
|
) {
|
||||||
log::debug!("async send_payment_req");
|
log::debug!("async send_payment_req");
|
||||||
log::debug!("cleaning stale jwp values");
|
log::debug!("cleaning stale jwp values");
|
||||||
clear_gui_db(String::from("gui-txp"), String::from(&contact));
|
utils::clear_gui_db(String::from("gui-txp"), String::from(&contact));
|
||||||
clear_gui_db(String::from("gui-jwp"), String::from(&contact));
|
utils::clear_gui_db(String::from("gui-jwp"), String::from(&contact));
|
||||||
clear_gui_db(String::from("gui-exp"), String::from(&contact));
|
utils::clear_gui_db(String::from("gui-exp"), String::from(&contact));
|
||||||
let mut retry_count = 1;
|
let mut retry_count = 1;
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let ptxp_address = String::from(&d.address);
|
let ptxp_address = String::from(&d.address);
|
||||||
|
@ -733,7 +684,7 @@ fn send_payment_req(
|
||||||
.await;
|
.await;
|
||||||
retry_count += 1;
|
retry_count += 1;
|
||||||
}
|
}
|
||||||
write_gui_db(
|
utils::write_gui_db(
|
||||||
String::from("gui-txp"),
|
String::from("gui-txp"),
|
||||||
String::from(&contact),
|
String::from(&contact),
|
||||||
String::from(&ftxp.signature),
|
String::from(&ftxp.signature),
|
||||||
|
@ -747,7 +698,7 @@ fn send_payment_req(
|
||||||
// if we made it this far we can now request a JWP from our friend
|
// if we made it this far we can now request a JWP from our friend
|
||||||
match proof::prove_payment(String::from(&contact), &ftxp).await {
|
match proof::prove_payment(String::from(&contact), &ftxp).await {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
write_gui_db(
|
utils::write_gui_db(
|
||||||
String::from("gui-jwp"),
|
String::from("gui-jwp"),
|
||||||
String::from(&contact),
|
String::from(&contact),
|
||||||
String::from(&result.jwp),
|
String::from(&result.jwp),
|
||||||
|
@ -757,7 +708,7 @@ fn send_payment_req(
|
||||||
// subtract 120 seconds since we had to wait for one confirmation
|
// subtract 120 seconds since we had to wait for one confirmation
|
||||||
let grace: i64 = seconds - BLOCK_TIME_IN_SECS_EST as i64;
|
let grace: i64 = seconds - BLOCK_TIME_IN_SECS_EST as i64;
|
||||||
let unix: i64 = chrono::offset::Utc::now().timestamp() + grace;
|
let unix: i64 = chrono::offset::Utc::now().timestamp() + grace;
|
||||||
write_gui_db(
|
utils::write_gui_db(
|
||||||
String::from("gui-exp"),
|
String::from("gui-exp"),
|
||||||
String::from(&contact),
|
String::from(&contact),
|
||||||
format!("{}", unix),
|
format!("{}", unix),
|
||||||
|
@ -796,14 +747,14 @@ fn send_message_req(tx: Sender<bool>, ctx: egui::Context, body: String, to: Stri
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_signed_key(contact: String) -> bool {
|
fn check_signed_key(contact: String) -> bool {
|
||||||
let v = search_gui_db(String::from("gui-signed-key"), contact);
|
let v = utils::search_gui_db(String::from("gui-signed-key"), contact);
|
||||||
v != utils::empty_string()
|
v != utils::empty_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_nick_req(contact: String, nick: String) {
|
fn change_nick_req(contact: String, nick: String) {
|
||||||
log::debug!("change nick");
|
log::debug!("change nick");
|
||||||
clear_gui_db(String::from("gui-nick"), String::from(&contact));
|
utils::clear_gui_db(String::from("gui-nick"), String::from(&contact));
|
||||||
write_gui_db(String::from("gui-nick"), String::from(&contact), nick);
|
utils::write_gui_db(String::from("gui-nick"), String::from(&contact), nick);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_can_transfer_req(tx: Sender<bool>, ctx: egui::Context, invoice: u128) {
|
fn send_can_transfer_req(tx: Sender<bool>, ctx: egui::Context, invoice: u128) {
|
||||||
|
|
|
@ -5,12 +5,22 @@ use std::sync::mpsc::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct MarketApp {
|
pub struct MarketApp {
|
||||||
|
contact_info_tx: Sender<models::Contact>,
|
||||||
|
contact_info_rx: Receiver<models::Contact>,
|
||||||
|
find_vendor: String,
|
||||||
|
get_vendor_products_tx: Sender<Vec<models::Product>>,
|
||||||
|
get_vendor_products_rx: Receiver<Vec<models::Product>>,
|
||||||
|
is_ordering: bool,
|
||||||
|
is_pinging: bool,
|
||||||
is_product_image_set: bool,
|
is_product_image_set: bool,
|
||||||
is_showing_products: bool,
|
is_showing_products: bool,
|
||||||
is_showing_product_image: bool,
|
is_showing_product_image: bool,
|
||||||
is_showing_product_update: bool,
|
is_showing_product_update: bool,
|
||||||
is_showing_orders: bool,
|
is_showing_orders: bool,
|
||||||
|
is_showing_vendor_status: bool,
|
||||||
|
is_showing_vendors: bool,
|
||||||
is_vendor_enabled: bool,
|
is_vendor_enabled: bool,
|
||||||
|
is_window_shopping: bool,
|
||||||
orders: Vec<models::Order>,
|
orders: Vec<models::Order>,
|
||||||
product_image: egui_extras::RetainedImage,
|
product_image: egui_extras::RetainedImage,
|
||||||
products: Vec<models::Product>,
|
products: Vec<models::Product>,
|
||||||
|
@ -22,6 +32,10 @@ pub struct MarketApp {
|
||||||
new_product_qty: String,
|
new_product_qty: String,
|
||||||
_refresh_on_delete_product_tx: Sender<bool>,
|
_refresh_on_delete_product_tx: Sender<bool>,
|
||||||
_refresh_on_delete_product_rx: Receiver<bool>,
|
_refresh_on_delete_product_rx: Receiver<bool>,
|
||||||
|
s_contact: models::Contact,
|
||||||
|
showing_vendor_status: bool,
|
||||||
|
vendor_status: utils::ContactStatus,
|
||||||
|
vendors: Vec<models::Contact>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MarketApp {
|
impl Default for MarketApp {
|
||||||
|
@ -32,13 +46,25 @@ impl Default for MarketApp {
|
||||||
let s = db::Interface::open();
|
let s = db::Interface::open();
|
||||||
let r = db::Interface::read(&s.env, &s.handle, contact::NEVEKO_VENDOR_ENABLED);
|
let r = db::Interface::read(&s.env, &s.handle, contact::NEVEKO_VENDOR_ENABLED);
|
||||||
let is_vendor_enabled = r == contact::NEVEKO_VENDOR_MODE_ON;
|
let is_vendor_enabled = r == contact::NEVEKO_VENDOR_MODE_ON;
|
||||||
|
let (contact_info_tx, contact_info_rx) = std::sync::mpsc::channel();
|
||||||
|
let (get_vendor_products_tx, get_vendor_products_rx) = std::sync::mpsc::channel();
|
||||||
MarketApp {
|
MarketApp {
|
||||||
|
contact_info_rx,
|
||||||
|
contact_info_tx,
|
||||||
|
find_vendor: utils::empty_string(),
|
||||||
|
get_vendor_products_rx,
|
||||||
|
get_vendor_products_tx,
|
||||||
|
is_ordering: false,
|
||||||
|
is_pinging: false,
|
||||||
is_product_image_set: false,
|
is_product_image_set: false,
|
||||||
is_showing_orders: false,
|
is_showing_orders: false,
|
||||||
is_showing_products: false,
|
is_showing_products: false,
|
||||||
is_showing_product_image: false,
|
is_showing_product_image: false,
|
||||||
is_showing_product_update: false,
|
is_showing_product_update: false,
|
||||||
|
is_showing_vendor_status: false,
|
||||||
|
is_showing_vendors: false,
|
||||||
is_vendor_enabled,
|
is_vendor_enabled,
|
||||||
|
is_window_shopping: false,
|
||||||
orders: Vec::new(),
|
orders: Vec::new(),
|
||||||
product_image: egui_extras::RetainedImage::from_image_bytes(
|
product_image: egui_extras::RetainedImage::from_image_bytes(
|
||||||
"qr.png",
|
"qr.png",
|
||||||
|
@ -54,6 +80,10 @@ impl Default for MarketApp {
|
||||||
new_product_qty: utils::empty_string(),
|
new_product_qty: utils::empty_string(),
|
||||||
_refresh_on_delete_product_tx,
|
_refresh_on_delete_product_tx,
|
||||||
_refresh_on_delete_product_rx,
|
_refresh_on_delete_product_rx,
|
||||||
|
s_contact: Default::default(),
|
||||||
|
showing_vendor_status: false,
|
||||||
|
vendor_status: Default::default(),
|
||||||
|
vendors: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +92,188 @@ impl eframe::App for MarketApp {
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
// Hook into async channel threads
|
// 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.is_pinging = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(vendor_products) = self.get_vendor_products_rx.try_recv() {
|
||||||
|
self.products = vendor_products;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(c2m): create order form
|
||||||
|
|
||||||
|
// View vendors
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
let mut is_showing_vendors = self.is_showing_vendors;
|
||||||
|
egui::Window::new("Vendors")
|
||||||
|
.open(&mut is_showing_vendors)
|
||||||
|
.vscroll(true)
|
||||||
|
.show(&ctx, |ui| {
|
||||||
|
// Vendor filter
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
ui.heading("\nFind Vendor");
|
||||||
|
ui.label("\n");
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let find_vendor_label = ui.label("filter vendors: ");
|
||||||
|
ui.text_edit_singleline(&mut self.find_vendor)
|
||||||
|
.labelled_by(find_vendor_label.id);
|
||||||
|
});
|
||||||
|
ui.label("\n");
|
||||||
|
use egui_extras::{
|
||||||
|
Column,
|
||||||
|
TableBuilder,
|
||||||
|
};
|
||||||
|
let table = TableBuilder::new(ui)
|
||||||
|
.striped(true)
|
||||||
|
.resizable(true)
|
||||||
|
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
||||||
|
.column(Column::auto())
|
||||||
|
.column(Column::auto())
|
||||||
|
.column(Column::auto())
|
||||||
|
.column(Column::auto())
|
||||||
|
.column(Column::remainder())
|
||||||
|
.min_scrolled_height(0.0);
|
||||||
|
|
||||||
|
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 v in &self.vendors {
|
||||||
|
if v.i2p_address.contains(&self.find_vendor) && v.is_vendor {
|
||||||
|
let row_height = 20.0;
|
||||||
|
body.row(row_height, |mut row| {
|
||||||
|
row.col(|ui| {
|
||||||
|
ui.label("vendor");
|
||||||
|
});
|
||||||
|
row.col(|ui| {
|
||||||
|
ui.label(format!("{}", v.i2p_address));
|
||||||
|
});
|
||||||
|
row.col(|ui| {
|
||||||
|
if ui.button("Check Status").clicked() {
|
||||||
|
let nick_db = utils::search_gui_db(
|
||||||
|
String::from("gui-nick"),
|
||||||
|
String::from(&v.i2p_address),
|
||||||
|
);
|
||||||
|
let nick = if nick_db == utils::empty_string() {
|
||||||
|
String::from("anon")
|
||||||
|
} else {
|
||||||
|
nick_db
|
||||||
|
};
|
||||||
|
self.vendor_status.nick = nick;
|
||||||
|
self.vendor_status.i2p = String::from(&v.i2p_address);
|
||||||
|
// get the txp
|
||||||
|
self.vendor_status.txp = utils::search_gui_db(
|
||||||
|
String::from("gui-txp"),
|
||||||
|
String::from(&v.i2p_address),
|
||||||
|
);
|
||||||
|
// get the jwp
|
||||||
|
self.vendor_status.jwp = utils::search_gui_db(
|
||||||
|
String::from("gui-jwp"),
|
||||||
|
String::from(&v.i2p_address),
|
||||||
|
);
|
||||||
|
let r_exp = utils::search_gui_db(
|
||||||
|
String::from("gui-exp"),
|
||||||
|
String::from(&v.i2p_address),
|
||||||
|
);
|
||||||
|
self.vendor_status.exp = r_exp;
|
||||||
|
let expire = match self.vendor_status.exp.parse::<i64>()
|
||||||
|
{
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(_e) => 0,
|
||||||
|
};
|
||||||
|
self.vendor_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.vendor_status.signed_key =
|
||||||
|
check_signed_key(self.vendor_status.i2p.clone());
|
||||||
|
send_contact_info_req(
|
||||||
|
self.contact_info_tx.clone(),
|
||||||
|
ctx.clone(),
|
||||||
|
self.vendor_status.i2p.clone(),
|
||||||
|
);
|
||||||
|
self.showing_vendor_status = true;
|
||||||
|
self.is_pinging = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
row.col(|ui| {
|
||||||
|
let now = chrono::offset::Utc::now().timestamp();
|
||||||
|
let expire = match self.vendor_status.exp.parse::<i64>() {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(_e) => 0,
|
||||||
|
};
|
||||||
|
if now < expire
|
||||||
|
&& self.vendor_status.signed_key
|
||||||
|
&& self.vendor_status.jwp != utils::empty_string()
|
||||||
|
&& v.i2p_address == self.vendor_status.i2p
|
||||||
|
{
|
||||||
|
if ui.button("View Products").clicked() {
|
||||||
|
send_products_from_vendor_req(
|
||||||
|
self.get_vendor_products_tx.clone(),
|
||||||
|
ctx.clone(),
|
||||||
|
self.vendor_status.i2p.clone(),
|
||||||
|
self.vendor_status.jwp.clone(),
|
||||||
|
);
|
||||||
|
self.is_window_shopping = true;
|
||||||
|
self.is_showing_products = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if ui.button("Exit").clicked() {
|
||||||
|
self.is_showing_vendors = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Vendor status window
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
let mut is_showing_vendor_status = self.is_showing_vendor_status;
|
||||||
|
egui::Window::new(&self.vendor_status.i2p)
|
||||||
|
.open(&mut is_showing_vendor_status)
|
||||||
|
.vscroll(true)
|
||||||
|
.title_bar(false)
|
||||||
|
.id(egui::Id::new(self.vendor_status.i2p.clone()))
|
||||||
|
.show(&ctx, |ui| {
|
||||||
|
if self.is_pinging {
|
||||||
|
ui.add(egui::Spinner::new());
|
||||||
|
ui.label("pinging...");
|
||||||
|
}
|
||||||
|
let status = if self.s_contact.xmr_address != utils::empty_string() {
|
||||||
|
"online"
|
||||||
|
} else {
|
||||||
|
"offline"
|
||||||
|
};
|
||||||
|
ui.label(format!("status: {}", status));
|
||||||
|
ui.label(format!("nick: {}", self.vendor_status.nick));
|
||||||
|
ui.label(format!("tx proof: {}", self.vendor_status.txp));
|
||||||
|
ui.label(format!("jwp: {}", self.vendor_status.jwp));
|
||||||
|
ui.label(format!("expiration: {}", self.vendor_status.h_exp));
|
||||||
|
ui.label(format!("signed key: {}", self.vendor_status.signed_key));
|
||||||
|
if ui.button("Exit").clicked() {
|
||||||
|
self.is_showing_vendor_status = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Product image window
|
// Product image window
|
||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
|
@ -81,6 +293,8 @@ impl eframe::App for MarketApp {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Vendor specific
|
||||||
|
|
||||||
// Update Product window
|
// Update Product window
|
||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
let mut is_showing_product_update = self.is_showing_product_update;
|
let mut is_showing_product_update = self.is_showing_product_update;
|
||||||
|
@ -159,10 +373,9 @@ impl eframe::App for MarketApp {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Products window
|
// Vendor Products Management window
|
||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
let mut is_showing_products = self.is_showing_products;
|
let mut is_showing_products = self.is_showing_products;
|
||||||
let mut is_showing_orders = self.is_showing_orders;
|
|
||||||
egui::Window::new("Products")
|
egui::Window::new("Products")
|
||||||
.open(&mut is_showing_products)
|
.open(&mut is_showing_products)
|
||||||
.vscroll(true)
|
.vscroll(true)
|
||||||
|
@ -258,13 +471,20 @@ impl eframe::App for MarketApp {
|
||||||
row.col(|ui| {
|
row.col(|ui| {
|
||||||
ui.style_mut().wrap = Some(false);
|
ui.style_mut().wrap = Some(false);
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if ui.button("Update").clicked() {
|
if !self.is_window_shopping {
|
||||||
self.product_update_pid = p.pid.clone();
|
if ui.button("Update").clicked() {
|
||||||
self.new_product_desc = p.description.clone();
|
self.product_update_pid = p.pid.clone();
|
||||||
self.new_product_name = p.name.clone();
|
self.new_product_desc = p.description.clone();
|
||||||
self.new_product_price = format!("{}", p.price);
|
self.new_product_name = p.name.clone();
|
||||||
self.new_product_qty = format!("{}", p.qty);
|
self.new_product_price = format!("{}", p.price);
|
||||||
self.is_showing_product_update = true;
|
self.new_product_qty = format!("{}", p.qty);
|
||||||
|
self.is_showing_product_update = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ui.button("Create Order").clicked() {
|
||||||
|
self.product_update_pid = p.pid.clone();
|
||||||
|
self.is_ordering = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -278,6 +498,7 @@ impl eframe::App for MarketApp {
|
||||||
|
|
||||||
// TODO(c2m): Orders window
|
// TODO(c2m): Orders window
|
||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
|
let mut is_showing_orders = self.is_showing_orders;
|
||||||
egui::Window::new("Orders")
|
egui::Window::new("Orders")
|
||||||
.open(&mut is_showing_orders)
|
.open(&mut is_showing_orders)
|
||||||
.vscroll(true)
|
.vscroll(true)
|
||||||
|
@ -343,6 +564,10 @@ impl eframe::App for MarketApp {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// End Vendor specific
|
||||||
|
|
||||||
|
// Market Dashboard Main window
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
if ui.button("Refresh").clicked() {
|
if ui.button("Refresh").clicked() {
|
||||||
self.products = product::find_all();
|
self.products = product::find_all();
|
||||||
|
@ -360,7 +585,7 @@ impl eframe::App for MarketApp {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if ui.button("View Vendors").clicked() {
|
if ui.button("View Vendors").clicked() {
|
||||||
// TODO(c2m):
|
self.is_showing_vendors = true;
|
||||||
}
|
}
|
||||||
ui.label("\n");
|
ui.label("\n");
|
||||||
if ui.button("View Orders").clicked() {
|
if ui.button("View Orders").clicked() {
|
||||||
|
@ -437,11 +662,49 @@ impl eframe::App for MarketApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _refresh_on_delete_product_req(tx: Sender<bool>, ctx: egui::Context) {
|
fn _refresh_on_delete_product_req(_tx: Sender<bool>, _ctx: egui::Context) {
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||||
log::error!("refreshing products....");
|
log::error!("refreshing products....");
|
||||||
let _ = tx.send(true);
|
todo!();
|
||||||
ctx.request_repaint();
|
// let _ = tx.send(true);
|
||||||
|
// ctx.request_repaint();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
fn send_contact_info_req(tx: Sender<models::Contact>, ctx: egui::Context, contact: String) {
|
||||||
|
log::debug!("async send_contact_info_req");
|
||||||
|
tokio::spawn(async move {
|
||||||
|
match contact::add_contact_request(contact).await {
|
||||||
|
Ok(contact) => {
|
||||||
|
let _ = tx.send(contact);
|
||||||
|
ctx.request_repaint();
|
||||||
|
}
|
||||||
|
_ => log::debug!("failed to request invoice"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_signed_key(contact: String) -> bool {
|
||||||
|
let v = utils::search_gui_db(String::from("gui-signed-key"), contact);
|
||||||
|
v != utils::empty_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_products_from_vendor_req(
|
||||||
|
tx: Sender<Vec<models::Product>>,
|
||||||
|
ctx: egui::Context,
|
||||||
|
contact: String,
|
||||||
|
jwp: String,
|
||||||
|
) {
|
||||||
|
log::debug!("fetching products for vendor: {}", contact);
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let result = product::get_vendor_products(contact, jwp).await;
|
||||||
|
if result.is_ok() {
|
||||||
|
let products: Vec<models::Product> = result.unwrap();
|
||||||
|
log::info!("retreived {:?} products", products);
|
||||||
|
let _ = tx.send(products);
|
||||||
|
ctx.request_repaint();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,7 @@ impl Default for State {
|
||||||
login: Default::default(),
|
login: Default::default(),
|
||||||
mailbox: Default::default(),
|
mailbox: Default::default(),
|
||||||
market: Default::default(),
|
market: Default::default(),
|
||||||
selected_anchor: Default::default(),
|
selected_anchor: Anchor::Home,
|
||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
wallet: Default::default(),
|
wallet: Default::default(),
|
||||||
// async notifications
|
// async notifications
|
||||||
|
|
Loading…
Reference in a new issue