use std::sync::{atomic::AtomicBool, Arc};
use async_std::task;
use futures::{channel::mpsc::Sender, sink::SinkExt};
use iced::{
widget::{button, scrollable, Column, Row, Text, TextInput},
Command, Element, Length,
};
use log::error;
use koibumi_box::Contact;
use koibumi_common::boxes::{Boxes, DEFAULT_USER_ID};
use koibumi_core::identity::Private as PrivateIdentity;
use koibumi_node::Command as NodeCommand;
use crate::{config::Config as GuiConfig, gui, log::Logger, style};
#[derive(Clone, Debug, Default)]
pub(crate) struct Tab {
deterministic_password_value: String,
chan_password_value: String,
}
impl Tab {
pub(crate) fn update(
&mut self,
message: gui::Message,
boxes: &mut Option<Boxes>,
command_sender: &mut Sender<NodeCommand>,
logger: &mut Logger,
) -> Command<gui::Message> {
if boxes.is_none() {
return Command::none();
}
let boxes = boxes.as_mut().unwrap();
match message {
gui::Message::IdentitiesIdentitySelected(index) => {
boxes.set_selected_identity_index(Some(index));
Command::none()
}
gui::Message::IdentitiesCopyPressed => {
if boxes.selected_identity_index().is_none() {
return Command::none();
}
let index = boxes.selected_identity_index().unwrap();
let address = boxes.user().private_identities()[index].address();
iced::clipboard::write(address.to_string())
}
gui::Message::IdentitiesSubscribePressed => {
if boxes.selected_identity_index().is_none() {
return Command::none();
}
let index = boxes.selected_identity_index().unwrap();
let address = boxes.user().private_identities()[index].address();
if boxes.user().subscriptions().contains(&address) {
return Command::none();
}
if let Err(err) =
task::block_on(boxes.manager().subscribe(DEFAULT_USER_ID, &address))
{
error!("{}", err);
return Command::none();
}
if let Err(err) = task::block_on(command_sender.send(NodeCommand::Subscribe {
id: DEFAULT_USER_ID.to_vec(),
address: address.clone(),
})) {
error!("{}", err);
return Command::none();
}
boxes.user_mut().subscriptions_mut().push(address);
Command::none()
}
gui::Message::IdentitiesGenerateRandomPressed => {
let cancel = Arc::new(AtomicBool::new(false));
let identity = PrivateIdentity::random_builder().build(cancel);
if let Err(err) = identity {
error!("{}", err);
return Command::none();
}
let identity = identity.unwrap();
if let Err(err) = task::block_on(
boxes
.manager()
.add_private_identity(DEFAULT_USER_ID, identity.clone()),
) {
error!("{}", err);
return Command::none();
}
if let Err(err) = task::block_on(command_sender.send(NodeCommand::AddIdentity {
id: DEFAULT_USER_ID.to_vec(),
identity: identity.clone(),
})) {
error!("{}", err);
return Command::none();
}
boxes
.user_mut()
.private_identities_mut()
.insert(0, identity);
boxes.set_selected_identity_index(None);
logger.info("Random address generated");
Command::none()
}
gui::Message::IdentitiesDeterministicPasswordChanged(password) => {
self.deterministic_password_value = password;
Command::none()
}
gui::Message::IdentitiesGenerateDeterministicPressed => {
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);
return Command::none();
}
let identities = identities.unwrap();
let identity = identities[0].clone();
if let Err(err) = task::block_on(
boxes
.manager()
.add_private_identity(DEFAULT_USER_ID, identity.clone()),
) {
error!("{}", err);
return Command::none();
}
if let Err(err) = task::block_on(command_sender.send(NodeCommand::AddIdentity {
id: DEFAULT_USER_ID.to_vec(),
identity: identity.clone(),
})) {
error!("{}", err);
return Command::none();
}
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");
Command::none()
}
gui::Message::IdentitiesChanPasswordChanged(password) => {
self.chan_password_value = password;
Command::none()
}
gui::Message::IdentitiesGenerateChanPressed => {
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);
return Command::none();
}
let identity = identity.unwrap();
if let Err(err) = task::block_on(
boxes
.manager()
.add_private_identity(DEFAULT_USER_ID, identity.clone()),
) {
error!("{}", err);
return Command::none();
}
if let Err(err) = task::block_on(command_sender.send(NodeCommand::AddIdentity {
id: DEFAULT_USER_ID.to_vec(),
identity: identity.clone(),
})) {
error!("{}", err);
return Command::none();
}
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) =
task::block_on(boxes.manager().add_contact(DEFAULT_USER_ID, &contact))
{
error!("{}", err);
return Command::none();
}
boxes.user_mut().contacts_mut().push(contact);
let alias = format!("[chan] {}", self.chan_password_value);
if let Err(err) =
task::block_on(boxes.manager().add_alias(DEFAULT_USER_ID, &address, &alias))
{
error!("{}", err);
return Command::none();
}
boxes
.user_mut()
.aliases_mut()
.insert(address.to_string(), alias);
self.chan_password_value = String::new();
logger.info("chan address generated");
Command::none()
}
_ => panic!("Program error"),
}
}
pub(crate) fn view(
&self,
config: &GuiConfig,
identities: &[PrivateIdentity],
selected_index: Option<usize>,
boxes: &Option<Boxes>,
) -> Element<gui::Message> {
let text_size = config.text_size();
if boxes.is_none() {
return Column::new()
.push(Text::new("inbox/outbox database error").size(text_size))
.into();
}
let boxes = boxes.as_ref().unwrap();
let address_button = |label, i, selected| {
let label = Text::new(label).size(text_size);
let style = if selected {
iced::theme::Button::Custom(Box::new(style::AddressButton::Selected))
} else {
iced::theme::Button::Custom(Box::new(style::AddressButton::Normal))
};
button(label)
.style(style)
.on_press(gui::Message::IdentitiesIdentitySelected(i))
.padding(2)
};
let mut list = Column::new();
let mut i = 0;
for identity in identities {
let alias = boxes.user().rich_alias(&identity.to_string());
let mut selected = false;
if let Some(index) = selected_index {
selected = index == i;
}
let row = Row::new().push(address_button(alias, i, selected));
list = list.push(row);
i += 1;
}
let list = scrollable(list).height(Length::Fill);
let buttons = Row::new()
.spacing(text_size / 4)
.push(
button(Text::new("Copy to clipboard").size(text_size))
.on_press(gui::Message::IdentitiesCopyPressed),
)
.push(
button(Text::new("Subscribe").size(text_size))
.on_press(gui::Message::IdentitiesSubscribePressed),
);
let generate_random = Row::new().spacing(text_size / 4).push(
button(Text::new("Generate random").size(text_size))
.on_press(gui::Message::IdentitiesGenerateRandomPressed),
);
let generate_deterministic = Row::new()
.spacing(text_size / 4)
.push(
TextInput::new("Password", &self.deterministic_password_value)
.on_input(gui::Message::IdentitiesDeterministicPasswordChanged)
.size(text_size)
.padding(text_size / 4),
)
.push(
button(Text::new("Generate deterministic").size(text_size))
.on_press(gui::Message::IdentitiesGenerateDeterministicPressed),
);
let generate_chan = Row::new()
.spacing(text_size / 4)
.push(
TextInput::new("Password", &self.chan_password_value)
.on_input(gui::Message::IdentitiesChanPasswordChanged)
.size(text_size)
.padding(text_size / 4),
)
.push(
button(Text::new("Generate chan").size(text_size))
.on_press(gui::Message::IdentitiesGenerateChanPressed),
);
Column::new()
.spacing(text_size / 4)
.push(list)
.push(buttons)
.push(generate_random)
.push(generate_deterministic)
.push(generate_chan)
.into()
}
}