use std::convert::TryFrom;
use copypasta::ClipboardProvider;
use log::error;
use koibumi_common_sync::boxes::{Boxes, DEFAULT_USER_ID};
use koibumi_core::{
encoding::{Encoding, Simple},
message::InvHash,
time::Time,
};
use crate::{config::Config as GuiConfig, ids::Ids};
#[derive(Clone, Debug)]
pub(crate) struct Entry {
entry: koibumi_box_sync::MessageEntry,
}
impl Entry {
pub(crate) fn new(entry: koibumi_box_sync::MessageEntry) -> Self {
Self { entry }
}
}
#[derive(Clone, Debug, Default)]
pub(crate) struct Tab {
pub(crate) entries: Vec<Entry>,
selected: Option<InvHash>,
message: Option<koibumi_box_sync::Message>,
}
fn time_to_string(time: Time) -> String {
if time.as_secs() > i64::MAX as u64 {
return "(unsupported)".to_string();
}
let time = ::time::OffsetDateTime::from_unix_timestamp(time.as_secs() as i64);
if time.is_err() {
return "(error)".to_string();
}
let mut time = time.unwrap();
if let Ok(local_offset) = ::time::UtcOffset::local_offset_at(time) {
time = time.to_offset(local_offset);
}
let format = time::format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour sign:mandatory]:[offset_minute]").unwrap();
if let Ok(s) = time.format(&format) {
s
} else {
"(error)".to_string()
}
}
impl Tab {
pub(crate) fn set_widgets(
&mut self,
ui: &mut conrod_core::UiCell,
ids: &mut Ids,
config: &GuiConfig,
boxes: &mut Option<Boxes>,
) {
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.messages_list_canvas,
widget::Canvas::new().length(128.0).pad_bottom(2.0),
),
(
ids.messages_buttons_canvas,
widget::Canvas::new()
.length(text_size as f64 + 8.0 + 2.0 + 2.0)
.pad_bottom(2.0),
),
(
ids.messages_header_canvas,
widget::Canvas::new()
.length(text_size as f64 / 2.0 * 13.0)
.scroll_kids_vertically()
.color(color::DARK_CHARCOAL)
.pad_bottom(2.0),
),
(
ids.messages_body_canvas,
widget::Canvas::new()
.scroll_kids_vertically()
.pad_bottom(2.0),
),
])
.top_left_of(ids.tabs_body)
.wh_of(ids.tabs_body)
.set(ids.messages, ui);
let (mut items, scrollbar) = widget::List::flow_down(self.entries.len())
.top_left_of(ids.messages_list_canvas)
.wh_of(ids.messages_list_canvas)
.item_size(text_size as f64 + 8.0)
.scrollbar_on_top()
.set(ids.messages_list, ui);
while let Some(item) = items.next(ui) {
let i = item.i;
let label = self.entries[i].entry.subject();
let mut button = 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))));
if self.entries[i].entry.read() {
button = button.color(color::DARK_CHARCOAL);
} else {
button = button.color(color::DARK_YELLOW);
}
if let Some(hash) = &self.selected {
if self.entries[i].entry.hash() == hash {
button = button.color(color::GREEN);
}
}
for _v in item.set(button, ui) {
let hash = self.entries[i].entry.hash().clone();
let message = boxes.manager().get_message(DEFAULT_USER_ID, &hash);
if let Err(err) = message {
error!("{}", err);
continue;
}
let message = message.unwrap();
if message.is_none() {
continue;
}
let message = message.unwrap();
self.message = Some(message);
if let Some(entry) = self
.entries
.iter_mut()
.find(|item| item.entry.hash() == &hash)
{
if !entry.entry.read() {
entry.entry.set_read(true);
if let Err(err) = boxes.manager().set_read(DEFAULT_USER_ID, &hash, true) {
error!("{}", err);
}
boxes.decrement_unread_count();
}
}
self.selected = Some(hash);
}
}
if let Some(s) = scrollbar {
s.set(ui)
}
let label = "Unread";
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.messages_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.messages_button_unread, ui)
{
let hash = self.selected.clone();
if hash.is_none() {
continue;
}
let hash = hash.unwrap();
if let Some(entry) = self
.entries
.iter_mut()
.find(|item| item.entry.hash() == &hash)
{
if entry.entry.read() {
entry.entry.set_read(false);
if let Err(err) = boxes.manager().set_read(DEFAULT_USER_ID, &hash, false) {
error!("{}", err);
}
boxes.increment_unread_count();
}
}
}
let label = "Copy";
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.messages_button_unread, 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.messages_button_copy, ui)
{
if self.message.is_none() {
continue;
}
let message = self.message.as_ref().unwrap();
let from = boxes.user().rich_alias(&message.from_address().to_string());
let to = if let Some(address) = message.to_address() {
boxes.user().rich_alias(&address.to_string())
} else {
String::new()
};
let received = time_to_string(message.time());
let mut subject = String::new();
let mut body = String::new();
if message.encoding() == Encoding::Simple {
if let Ok(simple) = Simple::try_from(message.content()) {
subject = String::from_utf8_lossy(simple.subject()).to_string();
body = String::from_utf8_lossy(simple.body()).to_string();
}
}
let content = format!(
"Subject: {}\nFrom: {}\nTo: {}\nReceived: {}\n\n{}",
subject, from, to, received, body
);
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(content) {
error!("{}", err);
}
}
let mut subject = String::new();
let mut from = String::new();
let mut to = String::new();
let mut received = String::new();
let mut body = String::new();
if let Some(message) = &self.message {
from = boxes.user().rich_alias(&message.from_address().to_string());
to = if let Some(address) = message.to_address() {
boxes.user().rich_alias(&address.to_string())
} else {
String::new()
};
received = time_to_string(message.time());
if message.encoding() == Encoding::Simple {
if let Ok(simple) = Simple::try_from(message.content()) {
subject = String::from_utf8_lossy(simple.subject()).to_string();
body = String::from_utf8_lossy(simple.body()).to_string();
}
}
}
let header = format!(
"Subject: {}\nFrom: {}\nTo: {}\nReceived: {}",
subject, from, to, received
);
widget::TextEdit::new(&header)
.top_left_with_margins_on(
ids.messages_header_canvas,
text_size as f64 / 4.0 + 2.0,
2.0,
)
.padded_w_of(ids.messages_header_canvas, 2.0)
.parent(ids.messages_header_canvas)
.restrict_to_height(false)
.font_size(text_size as u32)
.line_spacing(text_size as f64 / 2.0)
.wrap_by_character()
.set(ids.messages_header, ui);
widget::Scrollbar::y_axis(ids.messages_header_canvas)
.auto_hide(true)
.set(ids.messages_header_scrollbar, ui);
widget::TextEdit::new(&body)
.top_left_with_margins_on(ids.messages_body_canvas, text_size as f64 / 4.0 + 2.0, 2.0)
.padded_w_of(ids.messages_body_canvas, 2.0)
.parent(ids.messages_body_canvas)
.restrict_to_height(false)
.font_size(text_size as u32)
.line_spacing(text_size as f64 / 2.0)
.wrap_by_character()
.set(ids.messages_body, ui);
widget::Scrollbar::y_axis(ids.messages_body_canvas)
.auto_hide(true)
.set(ids.messages_body_scrollbar, ui);
}
}