use std::sync::{atomic::AtomicBool, Arc};
use copypasta::ClipboardProvider;
use crossbeam_channel::Sender;
use log::error;
use koibumi_box_sync::Contact;
use koibumi_common_sync::boxes::{Boxes, DEFAULT_USER_ID};
use koibumi_core::identity::Private as PrivateIdentity;
use koibumi_node_sync::Command as NodeCommand;
use crate::{config::Config as GuiConfig, ids::Ids, log::Logger};
#[derive(Clone, Debug, Default)]
pub(crate) struct Tab {
deterministic_password_value: String,
chan_password_value: String,
}
impl Tab {
#[allow(clippy::too_many_arguments)]
pub(crate) fn set_widgets(
&mut self,
ui: &mut conrod_core::UiCell,
ids: &mut Ids,
config: &GuiConfig,
identities: &[PrivateIdentity],
selected_index: Option<usize>,
boxes: &mut Option<Boxes>,
command_sender: &mut Sender<NodeCommand>,
logger: &mut Logger,
) {
let text_size = config.text_size();
let font_id = ui.fonts.ids().next().unwrap();
if boxes.is_none() {
return;
}
let boxes = boxes.as_mut().unwrap();
use conrod_core::{
color,
position::{Place, Relative},
text, widget, Colorable, Labelable, Positionable, Sizeable, Widget,
};
widget::Canvas::new()
.flow_down(&[
(
ids.identities_list_canvas,
widget::Canvas::new().pad_bottom(2.0),
),
(
ids.identities_buttons_canvas,
widget::Canvas::new()
.length((text_size as f64 + 8.0 + 4.0) * 4.0 + 4.0)
.pad_bottom(2.0),
),
])
.top_left_of(ids.tabs_body)
.wh_of(ids.tabs_body)
.set(ids.identities, ui);
let (mut items, scrollbar) = widget::List::flow_down(identities.len())
.top_left_of(ids.identities_list_canvas)
.wh_of(ids.identities_list_canvas)
.item_size(text_size as f64 + 8.0)
.scrollbar_on_top()
.set(ids.identities_list, ui);
while let Some(item) = items.next(ui) {
let i = item.i;
let label = boxes
.user()
.rich_alias(&identities[i].address().to_string());
let c = if let Some(index) = selected_index {
if i == index {
color::DARK_YELLOW
} else {
color::DARK_CHARCOAL
}
} else {
color::DARK_CHARCOAL
};
for _v in item.set(
widget::Button::new()
.label(&label)
.label_color(color::WHITE)
.label_font_size(text_size as u32)
.label_x(Relative::Place(Place::Start(Some(2.0))))
.color(c),
ui,
) {
boxes.set_selected_identity_index(Some(i));
}
}
if let Some(s) = scrollbar {
s.set(ui)
}
let label = "Copy to clipboard";
let width = text::line::width(label, ui.fonts.get(font_id).unwrap(), text_size as u32);
for _click in widget::Button::new()
.top_left_with_margins_on(ids.identities_buttons_canvas, 2.0, 2.0)
.w_h(width + 8.0, text_size as f64 + 8.0)
.label_font_size(text_size as u32)
.label(label)
.color(color::DARK_GRAY)
.label_color(color::WHITE)
.set(ids.identities_copy_button, ui)
{
if boxes.selected_identity_index().is_none() {
continue;
}
let index = boxes.selected_identity_index().unwrap();
let address = boxes.user().private_identities()[index].address();
let ctx = copypasta::ClipboardContext::new();
if let Err(err) = ctx {
error!("{}", err);
continue;
}
let mut ctx = ctx.unwrap();
if let Err(err) = ctx.set_contents(address.to_string()) {
error!("{}", err);
}
}
let label = "Subscribe";
let width = text::line::width(label, ui.fonts.get(font_id).unwrap(), text_size as u32);
for _click in widget::Button::new()
.right_from(ids.identities_copy_button, 4.0)
.w_h(width + 8.0, text_size as f64 + 8.0)
.label_font_size(text_size as u32)
.label(label)
.color(color::DARK_GRAY)
.label_color(color::WHITE)
.set(ids.identities_subscribe_button, ui)
{
if boxes.selected_identity_index().is_none() {
continue;
}
let index = boxes.selected_identity_index().unwrap();
let address = boxes.user().private_identities()[index].address();
if boxes.user().subscriptions().contains(&address) {
continue;
}
if let Err(err) = boxes.manager().subscribe(DEFAULT_USER_ID, &address) {
error!("{}", err);
continue;
}
if let Err(err) = command_sender.send(NodeCommand::Subscribe {
id: DEFAULT_USER_ID.to_vec(),
address: address.clone(),
}) {
error!("{}", err);
continue;
}
boxes.user_mut().subscriptions_mut().push(address);
}
let label = "Generate random";
let width = text::line::width(label, ui.fonts.get(font_id).unwrap(), text_size as u32);
for _click in widget::Button::new()
.down_from(ids.identities_copy_button, 4.0)
.w_h(width + 8.0, text_size as f64 + 8.0)
.label_font_size(text_size as u32)
.label(label)
.color(color::DARK_GRAY)
.label_color(color::WHITE)
.set(ids.identities_generate_random_button, ui)
{
let cancel = Arc::new(AtomicBool::new(false));
let identity = PrivateIdentity::random_builder().build(cancel);
if let Err(err) = identity {
error!("{}", err);
continue;
}
let identity = identity.unwrap();
if let Err(err) = boxes
.manager()
.add_private_identity(DEFAULT_USER_ID, identity.clone())
{
error!("{}", err);
continue;
}
if let Err(err) = command_sender.send(NodeCommand::AddIdentity {
id: DEFAULT_USER_ID.to_vec(),
identity: identity.clone(),
}) {
error!("{}", err);
continue;
}
boxes
.user_mut()
.private_identities_mut()
.insert(0, identity);
boxes.set_selected_identity_index(None);
logger.info("Random address generated");
}
let label = "Generate deterministic";
let width = text::line::width(label, ui.fonts.get(font_id).unwrap(), text_size as u32);
widget::Canvas::new()
.flow_right(&[
(
ids.identities_deterministic_edit_canvas,
widget::Canvas::new()
.scroll_kids_vertically()
.color(color::LIGHT_GRAY)
.pad_right(2.0),
),
(
ids.identities_deterministic_button_canvas,
widget::Canvas::new().length(width + 8.0),
),
])
.down_from(ids.identities_generate_random_button, 4.0)
.w_of(ids.identities_buttons_canvas)
.h(text_size as f64 + 8.0)
.set(ids.identities_deterministic_canvas, ui);
if let Some(edit) = widget::TextEdit::new(&self.deterministic_password_value)
.top_left_with_margins_on(ids.identities_deterministic_edit_canvas, 2.0, 2.0)
.padded_w_of(ids.identities_deterministic_edit_canvas, 2.0)
.parent(ids.identities_deterministic_edit_canvas)
.restrict_to_height(false)
.font_size(text_size as u32)
.wrap_by_character()
.color(color::BLACK)
.line_spacing((text_size / 2) as f64)
.set(ids.identities_deterministic_edit, ui)
{
self.deterministic_password_value = edit;
}
widget::Scrollbar::y_axis(ids.identities_deterministic_edit_canvas)
.auto_hide(true)
.set(ids.identities_deterministic_edit_scrollbar, ui);
for _click in widget::Button::new()
.top_left_of(ids.identities_deterministic_button_canvas)
.w_h(width + 8.0, text_size as f64 + 8.0)
.label_font_size(text_size as u32)
.label(label)
.color(color::DARK_GRAY)
.label_color(color::WHITE)
.set(ids.identities_deterministic_button, ui)
{
let cancel = Arc::new(AtomicBool::new(false));
let identities = PrivateIdentity::deterministic_builder(
self.deterministic_password_value.as_bytes().to_vec(),
)
.build(1, cancel);
if let Err(err) = identities {
error!("{}", err);
continue;
}
let identities = identities.unwrap();
let identity = identities[0].clone();
if let Err(err) = boxes
.manager()
.add_private_identity(DEFAULT_USER_ID, identity.clone())
{
error!("{}", err);
continue;
}
if let Err(err) = command_sender.send(NodeCommand::AddIdentity {
id: DEFAULT_USER_ID.to_vec(),
identity: identity.clone(),
}) {
error!("{}", err);
continue;
}
boxes
.user_mut()
.private_identities_mut()
.insert(0, identity);
boxes.set_selected_identity_index(None);
self.deterministic_password_value = String::new();
logger.info("Deterministic address generated");
}
let label = "Generate chan";
let width = text::line::width(label, ui.fonts.get(font_id).unwrap(), text_size as u32);
widget::Canvas::new()
.flow_right(&[
(
ids.identities_chan_edit_canvas,
widget::Canvas::new()
.scroll_kids_vertically()
.color(color::LIGHT_GRAY)
.pad_right(2.0),
),
(
ids.identities_chan_button_canvas,
widget::Canvas::new().length(width + 8.0),
),
])
.down_from(ids.identities_deterministic_canvas, 4.0)
.w_of(ids.identities_buttons_canvas)
.h(text_size as f64 + 8.0)
.set(ids.identities_chan_canvas, ui);
if let Some(edit) = widget::TextEdit::new(&self.chan_password_value)
.top_left_with_margins_on(ids.identities_chan_edit_canvas, 2.0, 2.0)
.padded_w_of(ids.identities_chan_edit_canvas, 2.0)
.parent(ids.identities_chan_edit_canvas)
.restrict_to_height(false)
.font_size(text_size as u32)
.wrap_by_character()
.color(color::BLACK)
.line_spacing((text_size / 2) as f64)
.set(ids.identities_chan_edit, ui)
{
self.chan_password_value = edit;
}
widget::Scrollbar::y_axis(ids.identities_chan_edit_canvas)
.auto_hide(true)
.set(ids.identities_chan_edit_scrollbar, ui);
for _click in widget::Button::new()
.top_left_of(ids.identities_chan_button_canvas)
.w_h(width + 8.0, text_size as f64 + 8.0)
.label_font_size(text_size as u32)
.label(label)
.color(color::DARK_GRAY)
.label_color(color::WHITE)
.set(ids.identities_chan_button, ui)
{
let cancel = Arc::new(AtomicBool::new(false));
let identity =
PrivateIdentity::chan_builder(self.chan_password_value.as_bytes().to_vec())
.build(cancel);
if let Err(err) = identity {
error!("{}", err);
continue;
}
let identity = identity.unwrap();
if let Err(err) = boxes
.manager()
.add_private_identity(DEFAULT_USER_ID, identity.clone())
{
error!("{}", err);
continue;
}
if let Err(err) = command_sender.send(NodeCommand::AddIdentity {
id: DEFAULT_USER_ID.to_vec(),
identity: identity.clone(),
}) {
error!("{}", err);
continue;
}
let address = identity.address();
boxes
.user_mut()
.private_identities_mut()
.insert(0, identity);
boxes.set_selected_identity_index(None);
let contact = Contact::new(address.clone());
if let Err(err) = boxes.manager().add_contact(DEFAULT_USER_ID, &contact) {
error!("{}", err);
continue;
}
boxes.user_mut().contacts_mut().push(contact);
let alias = format!("[chan] {}", self.chan_password_value);
if let Err(err) = boxes.manager().add_alias(DEFAULT_USER_ID, &address, &alias) {
error!("{}", err);
continue;
}
boxes
.user_mut()
.aliases_mut()
.insert(address.to_string(), alias);
self.chan_password_value = String::new();
logger.info("chan address generated");
}
}
}