use crate::countries::country_utils::get_flag_tooltip;
use crate::countries::flags_pictures::ICONS_SIZE_BIG;
use crate::gui::components::header::get_button_settings;
use crate::gui::components::tab::get_pages_tabs;
use crate::gui::components::types::my_modal::MyModal;
use crate::gui::pages::overview_page::item_bar;
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::styles::container::ContainerType;
use crate::gui::styles::rule::RuleType;
use crate::gui::styles::scrollbar::ScrollbarType;
use crate::gui::styles::style_constants::{FONT_SIZE_FOOTER, TOOLTIP_DELAY};
use crate::gui::styles::text::TextType;
use crate::gui::types::message::Message;
use crate::gui::types::settings::Settings;
use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::data_representation::DataRepr;
use crate::networking::types::host::Host;
use crate::networking::types::program_lookup::ProgramLookup;
use crate::networking::types::service::Service;
use crate::notifications::types::logged_notification::{
BlacklistedTransmitted, DataThresholdExceeded, FavoriteTransmitted, LoggedNotification,
};
use crate::report::types::sort_type::SortType;
use crate::translations::translations::{
clear_all_translation, favorite_transmitted_translation, no_notifications_received_translation,
no_notifications_set_translation, only_last_30_translation, per_second_translation,
threshold_translation,
};
use crate::translations::translations_5::blacklisted_transmitted_translation;
use crate::utils::types::icon::Icon;
use crate::{Language, RunningPage, Sniffer, StyleType};
use iced::Length::FillPortion;
use iced::widget::scrollable::Direction;
use iced::widget::text::LineHeight;
use iced::widget::tooltip::Position;
use iced::widget::{Column, Container, Row, Scrollable, Text, Tooltip};
use iced::widget::{Space, button};
use iced::{Alignment, Element, Length, Padding};
use std::cmp::max;
pub fn notifications_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
let Settings {
language,
ref notifications,
..
} = sniffer.conf.settings;
let mut tab_and_body = Column::new()
.align_x(Alignment::Center)
.height(Length::Fill);
let tabs = get_pages_tabs(
RunningPage::Notifications,
language,
sniffer.unread_notifications,
);
tab_and_body = tab_and_body.push(tabs);
if notifications.data_notification.threshold.is_none()
&& !notifications.favorite_notification.is_active
&& !notifications.ip_blacklist_notification.is_active
&& sniffer.logged_notifications.is_empty()
{
let body = body_no_notifications_set(language);
tab_and_body = tab_and_body.push(body);
} else if sniffer.logged_notifications.is_empty() {
let body = body_no_notifications_received(language, &sniffer.dots_pulse.0);
tab_and_body = tab_and_body.push(body);
} else {
let logged_notifications = logged_notifications(sniffer);
let body_row = Row::new()
.spacing(10)
.padding(Padding::new(10.0).bottom(0))
.push(
Container::new(if sniffer.logged_notifications.len() < 30 {
Text::new("")
} else {
Text::new(only_last_30_translation(language))
})
.width(150)
.height(Length::Fill)
.align_x(Alignment::Center)
.align_y(Alignment::Center),
)
.push(Scrollable::with_direction(
logged_notifications,
Direction::Vertical(ScrollbarType::properties()),
))
.push(
Container::new(get_button_clear_all(language))
.width(150)
.height(Length::Fill)
.align_x(Alignment::Center)
.align_y(Alignment::Center),
);
tab_and_body = tab_and_body.push(body_row);
}
Container::new(Column::new().push(tab_and_body)).height(Length::Fill)
}
fn body_no_notifications_set<'a>(language: Language) -> Column<'a, Message, StyleType> {
Column::new()
.padding(5)
.spacing(5)
.align_x(Alignment::Center)
.width(Length::Fill)
.push(Space::new().height(Length::Fill))
.push(no_notifications_set_translation(language).align_x(Alignment::Center))
.push(get_button_settings(language, SettingsPage::Notifications))
.push(Space::new().height(FillPortion(2)))
}
fn body_no_notifications_received(
language: Language,
dots: &str,
) -> Column<'_, Message, StyleType> {
Column::new()
.padding(5)
.spacing(5)
.align_x(Alignment::Center)
.width(Length::Fill)
.push(Space::new().height(Length::Fill))
.push(no_notifications_received_translation(language).align_x(Alignment::Center))
.push(Text::new(dots).size(50))
.push(Space::new().height(FillPortion(2)))
}
fn data_notification_log<'a>(
logged_notification: &DataThresholdExceeded,
first_entry_data_info: DataInfo,
language: Language,
) -> Container<'a, Message, StyleType> {
let data_info = logged_notification.data_info;
let data_repr = logged_notification.data_repr;
let threshold_bar = item_bar(
Space::new().width(ICONS_SIZE_BIG),
String::new(),
&data_info,
data_repr,
first_entry_data_info,
);
let data_string = data_repr.formatted_string(logged_notification.threshold.into());
let icon = if data_repr == DataRepr::Packets {
Icon::PacketsThreshold
} else {
Icon::BytesThreshold
}
.to_text()
.size(80)
.line_height(LineHeight::Relative(1.0));
let threshold_str = format!(
"{}: {data_string} {}",
threshold_translation(language),
per_second_translation(language)
);
let content = Row::new()
.align_y(Alignment::Center)
.spacing(30)
.push(icon)
.push(
Column::new()
.spacing(7)
.width(250)
.push(
Row::new()
.spacing(8)
.push(Icon::Clock.to_text())
.push(Text::new(logged_notification.timestamp.clone())),
)
.push(
Text::new(data_repr.data_exceeded_translation(language).to_string())
.class(TextType::Title),
)
.push(
Text::new(threshold_str)
.class(TextType::Subtitle)
.size(FONT_SIZE_FOOTER),
),
)
.push(threshold_bar);
let content_and_extra = Column::new()
.spacing(10)
.push(content)
.push(button_expand(
logged_notification.id,
logged_notification.is_expanded,
))
.push(data_notification_extra(logged_notification, language));
Container::new(content_and_extra)
.width(Length::Fill)
.padding(15)
.class(ContainerType::BorderedRound)
}
fn favorite_notification_log<'a>(
logged_notification: &'a FavoriteTransmitted,
first_entry_data_info: DataInfo,
data_repr: DataRepr,
language: Language,
program_lookup: Option<&'a ProgramLookup>,
) -> Container<'a, Message, StyleType> {
let favorite = &logged_notification.favorite;
let icon = favorite.icon(language, program_lookup, true, 1.0);
let item_bar = item_bar(
icon,
favorite.to_entry_string(),
&favorite.data_info(),
data_repr,
first_entry_data_info,
);
let content = Row::new()
.spacing(30)
.align_y(Alignment::Center)
.push(
Icon::StarEmpty
.to_text()
.size(80)
.line_height(LineHeight::Relative(1.0)),
)
.push(
Column::new()
.width(250)
.spacing(7)
.push(
Row::new()
.spacing(8)
.push(Icon::Clock.to_text())
.push(Text::new(logged_notification.timestamp.clone())),
)
.push(Text::new(favorite_transmitted_translation(language)).class(TextType::Title)),
)
.push(item_bar);
Container::new(content)
.width(Length::Fill)
.padding(15)
.class(ContainerType::BorderedRound)
}
fn blacklisted_notification_log<'a>(
logged_notification: &BlacklistedTransmitted,
first_entry_data_info: DataInfo,
data_repr: DataRepr,
language: Language,
) -> Container<'a, Message, StyleType> {
let host = &logged_notification.host;
let data_info_host = logged_notification.data_info_host;
let icon = get_flag_tooltip(host.country, &data_info_host, language, false, 1.0);
let blacklisted_bar = item_bar(
icon,
host.to_blacklist_string(logged_notification.ip),
&data_info_host.data_info,
data_repr,
first_entry_data_info,
);
let content = Row::new()
.spacing(30)
.align_y(Alignment::Center)
.push(
Icon::Forbidden
.to_text()
.size(80)
.line_height(LineHeight::Relative(1.0)),
)
.push(
Column::new()
.width(250)
.spacing(7)
.push(
Row::new()
.spacing(8)
.push(Icon::Clock.to_text())
.push(Text::new(logged_notification.timestamp.clone())),
)
.push(
Text::new(blacklisted_transmitted_translation(language)).class(TextType::Title),
),
)
.push(blacklisted_bar);
Container::new(content)
.width(Length::Fill)
.padding(15)
.class(ContainerType::BorderedRound)
}
fn get_button_clear_all<'a>(language: Language) -> Tooltip<'a, Message, StyleType> {
let content = button(
Icon::Bin
.to_text()
.size(20)
.align_x(Alignment::Center)
.align_y(Alignment::Center),
)
.padding(10)
.height(50)
.width(75)
.on_press(Message::ShowModal(MyModal::ClearAll));
Tooltip::new(
content,
Text::new(clear_all_translation(language)),
Position::Top,
)
.gap(5)
.class(ContainerType::Tooltip)
.delay(TOOLTIP_DELAY)
}
fn logged_notifications(sniffer: &Sniffer) -> Column<'_, Message, StyleType> {
let Settings { language, .. } = sniffer.conf.settings;
let data_repr = sniffer.conf.data_repr;
let mut ret_val = Column::new()
.padding(Padding::ZERO.right(15).bottom(10))
.spacing(10)
.align_x(Alignment::Center);
let first_entry_data_info = sniffer
.logged_notifications
.notifications()
.iter()
.map(LoggedNotification::data_info)
.max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr))
.unwrap_or_default();
for logged_notification in sniffer.logged_notifications.notifications() {
ret_val = ret_val.push(match logged_notification {
LoggedNotification::DataThresholdExceeded(data_threshold_exceeded) => {
data_notification_log(data_threshold_exceeded, first_entry_data_info, language)
}
LoggedNotification::FavoriteTransmitted(favorite_transmitted) => {
favorite_notification_log(
favorite_transmitted,
first_entry_data_info,
data_repr,
language,
sniffer.program_lookup.as_ref(),
)
}
LoggedNotification::BlacklistedTransmitted(blacklisted_transmitted) => {
blacklisted_notification_log(
blacklisted_transmitted,
first_entry_data_info,
data_repr,
language,
)
}
});
}
ret_val
}
fn button_expand<'a>(
notification_id: usize,
is_expanded: bool,
) -> Container<'a, Message, StyleType> {
let button = button(
if is_expanded {
Icon::SortAscending
} else {
Icon::SortDescending
}
.to_text()
.size(11)
.align_x(Alignment::Center)
.align_y(Alignment::Center),
)
.padding(Padding::ZERO.top(if is_expanded { 0 } else { 2 }))
.width(25)
.height(25)
.on_press(Message::ExpandNotification(notification_id, !is_expanded));
Container::new(button)
.padding(Padding::ZERO.left(427))
.align_y(Alignment::Center)
}
fn data_notification_extra<'a>(
logged_notification: &DataThresholdExceeded,
language: Language,
) -> Option<Row<'a, Message, StyleType>> {
let max_entries = max(
logged_notification.hosts.len(),
logged_notification.services.len(),
);
if !logged_notification.is_expanded || max_entries == 0 {
return None;
}
let spacing = 10.0;
#[allow(clippy::cast_precision_loss)]
let height = (ICONS_SIZE_BIG + spacing) * max_entries as f32;
let mut hosts_col = Column::new().spacing(spacing).width(Length::FillPortion(5));
let first_data_info = logged_notification
.hosts
.first()
.unwrap_or(&(Host::default(), DataInfoHost::default()))
.1
.data_info;
for (host, data_info_host) in &logged_notification.hosts {
let icon = get_flag_tooltip(host.country, data_info_host, language, false, 1.0);
let host_bar = item_bar(
icon,
host.to_entry_string(),
&data_info_host.data_info,
logged_notification.data_repr,
first_data_info,
);
hosts_col = hosts_col.push(host_bar);
}
let mut services_col = Column::new().spacing(spacing).width(Length::FillPortion(2));
let first_data_info_service = logged_notification
.services
.first()
.unwrap_or(&(Service::default(), DataInfo::default()))
.1;
for (service, data_info) in &logged_notification.services {
let service_bar = item_bar(
None::<Element<Message, StyleType>>,
service.to_string(),
data_info,
logged_notification.data_repr,
first_data_info_service,
);
services_col = services_col.push(service_bar);
}
Some(
Row::new()
.push(hosts_col)
.push(Container::new(RuleType::Standard.vertical(30)).height(height))
.push(services_col),
)
}