koibumi 0.0.10

An experimental Bitmessage client
use async_std::task;
use futures::{channel::mpsc::Sender, sink::SinkExt};
use iced::{
    widget::{button, scrollable, Column, Row, Text},
    Command, Element, Length,
};
use log::error;

use koibumi_box::Contact;
use koibumi_common::boxes::{Boxes, DEFAULT_USER_ID};
use koibumi_node::Command as NodeCommand;

use crate::{config::Config as GuiConfig, gui, style};

#[derive(Clone, Debug, Default)]
pub(crate) struct Tab {}

impl Tab {
    pub(crate) fn update(
        &mut self,
        message: gui::Message,
        boxes: &mut Option<Boxes>,
        command_sender: &mut Sender<NodeCommand>,
    ) -> Command<gui::Message> {
        if boxes.is_none() {
            return Command::none();
        }
        let boxes = boxes.as_mut().unwrap();
        match message {
            gui::Message::ContactsContactSelected(index) => {
                boxes.set_selected_contact_index(Some(index));
                Command::none()
            }

            gui::Message::ContactsCopyPressed => {
                if boxes.selected_contact_index().is_none() {
                    return Command::none();
                }
                let index = boxes.selected_contact_index().unwrap();
                let address = boxes.user().contacts()[index].address();
                iced::clipboard::write(address.to_string())
            }
            gui::Message::ContactsSubscribePressed => {
                if boxes.selected_contact_index().is_none() {
                    return Command::none();
                }
                let index = boxes.selected_contact_index().unwrap();
                let address = boxes.user().contacts()[index].address().clone();
                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()
            }
            _ => panic!("Program error"),
        }
    }

    pub(crate) fn view(
        &self,
        config: &GuiConfig,
        contacts: &[Contact],
        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::ContactsContactSelected(i))
                .padding(2)
        };

        let mut list = Column::new();
        let mut i = 0;
        for contact in contacts {
            let alias = boxes.user().rich_alias(&contact.address().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);
        // XXX .spacing(text_size / 4);

        let buttons = Row::new()
            .spacing(text_size / 4)
            .push(
                button(Text::new("Copy to clipboard").size(text_size))
                    .on_press(gui::Message::ContactsCopyPressed),
            )
            .push(
                button(Text::new("Subscribe").size(text_size))
                    .on_press(gui::Message::ContactsSubscribePressed),
            );

        Column::new()
            .spacing(text_size / 4)
            .push(list)
            .push(buttons)
            .into()
    }
}