Skip to content

Commit 50c4af5

Browse files
committed
threshold notifications: expand / collapse
1 parent c58f33c commit 50c4af5

7 files changed

Lines changed: 135 additions & 67 deletions

File tree

resources/fonts/subset/icons.ttf

1.27 KB
Binary file not shown.

src/gui/pages/notifications_page.rs

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@ use iced::widget::{Space, button, vertical_space};
77
use iced::{Alignment, Font, Length, Padding};
88

99
use crate::chart::types::chart_type::ChartType;
10-
use crate::countries::country_utils::get_computer_tooltip;
10+
use crate::countries::flags_pictures::FLAGS_WIDTH_BIG;
1111
use crate::gui::components::header::get_button_settings;
1212
use crate::gui::components::tab::get_pages_tabs;
1313
use crate::gui::components::types::my_modal::MyModal;
1414
use crate::gui::pages::overview_page::{get_bars, get_bars_length, host_bar};
1515
use crate::gui::pages::types::settings_page::SettingsPage;
16+
use crate::gui::styles::button::ButtonType;
1617
use crate::gui::styles::container::ContainerType;
1718
use crate::gui::styles::scrollbar::ScrollbarType;
1819
use crate::gui::styles::style_constants::FONT_SIZE_FOOTER;
1920
use crate::gui::styles::text::TextType;
2021
use crate::gui::types::message::Message;
2122
use crate::networking::types::data_info::DataInfo;
22-
use crate::networking::types::traffic_type::TrafficType;
2323
use crate::notifications::types::logged_notification::{
2424
DataThresholdExceeded, FavoriteTransmitted, LoggedNotification,
2525
};
@@ -61,11 +61,11 @@ pub fn notifications_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
6161
if notifications.packets_notification.threshold.is_none()
6262
&& notifications.bytes_notification.threshold.is_none()
6363
&& !notifications.favorite_notification.notify_on_favorite
64-
&& sniffer.logged_notifications.is_empty()
64+
&& sniffer.logged_notifications.0.is_empty()
6565
{
6666
let body = body_no_notifications_set(font, language);
6767
tab_and_body = tab_and_body.push(body);
68-
} else if sniffer.logged_notifications.is_empty() {
68+
} else if sniffer.logged_notifications.0.is_empty() {
6969
let body = body_no_notifications_received(font, language, &sniffer.dots_pulse.0);
7070
tab_and_body = tab_and_body.push(body);
7171
} else {
@@ -74,7 +74,7 @@ pub fn notifications_page(sniffer: &Sniffer) -> Container<Message, StyleType> {
7474
.spacing(10)
7575
.padding(Padding::new(10.0).bottom(0))
7676
.push(
77-
Container::new(if sniffer.logged_notifications.len() < 30 {
77+
Container::new(if sniffer.logged_notifications.0.len() < 30 {
7878
Text::new("")
7979
} else {
8080
Text::new(only_last_30_translation(language)).font(font)
@@ -168,7 +168,6 @@ fn data_notification_log<'a>(
168168
);
169169
let content = Row::new()
170170
.align_y(Alignment::Center)
171-
.height(Length::Fill)
172171
.spacing(30)
173172
.push(icon)
174173
.push(
@@ -179,7 +178,7 @@ fn data_notification_log<'a>(
179178
Row::new()
180179
.spacing(8)
181180
.push(Icon::Clock.to_text())
182-
.push(Text::new(logged_notification.timestamp).font(font)),
181+
.push(Text::new(logged_notification.timestamp.clone()).font(font)),
183182
)
184183
.push(
185184
Text::new(if chart_type == ChartType::Bytes {
@@ -198,16 +197,21 @@ fn data_notification_log<'a>(
198197
),
199198
)
200199
.push(threshold_bar(
201-
logged_notification.data_info,
200+
&logged_notification,
202201
chart_type,
203202
first_entry_data_info,
204203
font,
205-
language,
206204
));
207-
Container::new(content)
208-
.height(120)
205+
let content_and_extra = Column::new()
206+
.push(content)
207+
.push(if logged_notification.is_expanded {
208+
Row::new().push(get_button_clear_all(font, language))
209+
} else {
210+
Row::new()
211+
});
212+
Container::new(content_and_extra)
209213
.width(Length::Fill)
210-
.padding(10)
214+
.padding(15)
211215
.class(ContainerType::BorderedRound)
212216
}
213217

@@ -230,7 +234,6 @@ fn favorite_notification_log<'a>(
230234
let content = Row::new()
231235
.spacing(30)
232236
.align_y(Alignment::Center)
233-
.height(Length::Fill)
234237
.push(
235238
Icon::Star
236239
.to_text()
@@ -257,9 +260,8 @@ fn favorite_notification_log<'a>(
257260
.push(host_bar);
258261

259262
Container::new(content)
260-
.height(120)
261263
.width(Length::Fill)
262-
.padding(10)
264+
.padding(15)
263265
.class(ContainerType::BorderedRound)
264266
}
265267

@@ -298,12 +300,13 @@ fn logged_notifications<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType>
298300

299301
let first_entry_data_info = sniffer
300302
.logged_notifications
303+
.0
301304
.iter()
302305
.map(LoggedNotification::data_info)
303306
.max_by(|d1, d2| d1.compare(d2, SortType::Ascending, chart_type))
304307
.unwrap_or_default();
305308

306-
for logged_notification in &sniffer.logged_notifications {
309+
for logged_notification in &sniffer.logged_notifications.0 {
307310
ret_val = ret_val.push(match logged_notification {
308311
LoggedNotification::DataThresholdExceeded(data_threshold_exceeded) => {
309312
data_notification_log(
@@ -328,26 +331,22 @@ fn logged_notifications<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType>
328331
}
329332

330333
fn threshold_bar<'a>(
331-
data_info: DataInfo,
334+
logged_notification: &DataThresholdExceeded,
332335
chart_type: ChartType,
333336
first_entry_data_info: DataInfo,
334337
font: Font,
335-
language: Language,
336338
) -> Row<'a, Message, StyleType> {
339+
let data_info = logged_notification.data_info;
340+
let id = logged_notification.id;
341+
let is_expanded = logged_notification.is_expanded;
342+
337343
let (incoming_bar_len, outgoing_bar_len) =
338344
get_bars_length(chart_type, &first_entry_data_info, &data_info);
339345

340346
Row::new()
341347
.align_y(Alignment::Center)
342348
.spacing(5)
343-
.push(get_computer_tooltip(
344-
true,
345-
true,
346-
None,
347-
TrafficType::Unicast,
348-
language,
349-
font,
350-
))
349+
.push(button_expand(id, is_expanded))
351350
.push(
352351
Column::new()
353352
.spacing(1)
@@ -364,3 +363,28 @@ fn threshold_bar<'a>(
364363
.push(get_bars(incoming_bar_len, outgoing_bar_len)),
365364
)
366365
}
366+
367+
fn button_expand<'a>(
368+
notification_id: usize,
369+
is_expanded: bool,
370+
) -> Container<'a, Message, StyleType> {
371+
let button = button(
372+
if is_expanded {
373+
Icon::Collapse
374+
} else {
375+
Icon::Expand
376+
}
377+
.to_text()
378+
.size(25)
379+
.align_x(Alignment::Center)
380+
.align_y(Alignment::Center),
381+
)
382+
.width(FLAGS_WIDTH_BIG)
383+
.padding(Padding::ZERO)
384+
.class(ButtonType::SortArrows)
385+
.on_press(Message::ExpandNotification(notification_id, !is_expanded));
386+
387+
Container::new(button)
388+
.align_x(Alignment::Center)
389+
.align_y(Alignment::Center)
390+
}

src/gui/sniffer.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ pub struct Sniffer {
8787
pub addresses_resolved: HashMap<IpAddr, (String, Host)>,
8888
/// Collection of the favorite hosts
8989
pub favorite_hosts: HashSet<Host>,
90-
/// Log of the received notifications
91-
pub logged_notifications: VecDeque<LoggedNotification>,
90+
/// Log of the displayed notifications, with the total number of notifications for this capture
91+
pub logged_notifications: (VecDeque<LoggedNotification>, usize),
9292
/// Reports if a newer release of the software is available on GitHub
9393
pub newer_release_available: Option<bool>,
9494
/// Network device to be analyzed, or PCAP file to be imported
@@ -155,7 +155,7 @@ impl Sniffer {
155155
info_traffic: InfoTraffic::default(),
156156
addresses_resolved: HashMap::new(),
157157
favorite_hosts: HashSet::new(),
158-
logged_notifications: VecDeque::new(),
158+
logged_notifications: (VecDeque::new(), 0),
159159
newer_release_available: None,
160160
capture_source: CaptureSource::Device(device),
161161
my_devices: Vec::new(),
@@ -364,7 +364,7 @@ impl Sniffer {
364364
self.configs.settings.notifications.volume = volume;
365365
}
366366
Message::ClearAllNotifications => {
367-
self.logged_notifications = VecDeque::new();
367+
self.logged_notifications.0 = VecDeque::new();
368368
self.modal = None;
369369
}
370370
Message::SwitchPage(next) => {
@@ -552,6 +552,16 @@ impl Sniffer {
552552
self.update_waiting_dots();
553553
self.fetch_devices();
554554
}
555+
Message::ExpandNotification(id, expand) => {
556+
if let Some(n) = self
557+
.logged_notifications
558+
.0
559+
.iter_mut()
560+
.find(|n| n.id() == id)
561+
{
562+
n.expand(expand);
563+
}
564+
}
555565
}
556566
Task::none()
557567
}
@@ -798,7 +808,7 @@ impl Sniffer {
798808
self.info_traffic = InfoTraffic::default();
799809
self.addresses_resolved = HashMap::new();
800810
self.favorite_hosts = HashSet::new();
801-
self.logged_notifications = VecDeque::new();
811+
self.logged_notifications = (VecDeque::new(), 0);
802812
self.pcap_error = None;
803813
self.traffic_chart = TrafficChart::new(style, language);
804814
self.report_sort_type = ReportSortType::default();
@@ -1058,7 +1068,7 @@ impl Sniffer {
10581068

10591069
fn shortcut_ctrl_d(&mut self) -> Task<Message> {
10601070
if self.running_page.eq(&RunningPage::Notifications)
1061-
&& !self.logged_notifications.is_empty()
1071+
&& !self.logged_notifications.0.is_empty()
10621072
{
10631073
return Task::done(Message::ShowModal(MyModal::ClearAll));
10641074
}

src/gui/types/message.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,6 @@ pub enum Message {
133133
OfflineGap(usize, u32),
134134
/// Emitted every second to repeat certain tasks (such as fetching the network devices)
135135
Periodic,
136+
/// Expand or collapse the given logged notification
137+
ExpandNotification(usize, bool),
136138
}

src/notifications/notify_and_log.rs

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,36 @@ use std::collections::{HashSet, VecDeque};
1515
///
1616
/// It returns the number of new notifications emitted
1717
pub fn notify_and_log(
18-
logged_notifications: &mut VecDeque<LoggedNotification>,
18+
logged_notifications: &mut (VecDeque<LoggedNotification>, usize),
1919
notifications: Notifications,
2020
info_traffic_msg: &InfoTraffic,
2121
favorites: &HashSet<Host>,
2222
cs: &CaptureSource,
2323
) -> usize {
2424
let mut sound_to_play = Sound::None;
25-
let mut emitted_notifications = 0;
25+
let emitted_notifications_prev = logged_notifications.1;
2626
let timestamp = info_traffic_msg.last_packet_timestamp;
2727
let data_info = info_traffic_msg.tot_data_info;
2828
// packets threshold
2929
if let Some(threshold) = notifications.packets_notification.threshold {
3030
if data_info.tot_packets() > u128::from(threshold) {
3131
// log this notification
32-
emitted_notifications += 1;
33-
if logged_notifications.len() >= 30 {
34-
logged_notifications.pop_back();
32+
logged_notifications.1 += 1;
33+
if logged_notifications.0.len() >= 30 {
34+
logged_notifications.0.pop_back();
3535
}
36-
logged_notifications.push_front(LoggedNotification::DataThresholdExceeded(
37-
DataThresholdExceeded {
38-
chart_type: ChartType::Packets,
39-
threshold: notifications.packets_notification.previous_threshold,
40-
data_info,
41-
timestamp: get_formatted_timestamp(timestamp),
42-
},
43-
));
36+
logged_notifications
37+
.0
38+
.push_front(LoggedNotification::DataThresholdExceeded(
39+
DataThresholdExceeded {
40+
id: logged_notifications.1,
41+
chart_type: ChartType::Packets,
42+
threshold: notifications.packets_notification.previous_threshold,
43+
data_info,
44+
timestamp: get_formatted_timestamp(timestamp),
45+
is_expanded: false,
46+
},
47+
));
4448
if sound_to_play.eq(&Sound::None) {
4549
sound_to_play = notifications.packets_notification.sound;
4650
}
@@ -50,18 +54,22 @@ pub fn notify_and_log(
5054
if let Some(threshold) = notifications.bytes_notification.threshold {
5155
if data_info.tot_bytes() > u128::from(threshold) {
5256
//log this notification
53-
emitted_notifications += 1;
54-
if logged_notifications.len() >= 30 {
55-
logged_notifications.pop_back();
57+
logged_notifications.1 += 1;
58+
if logged_notifications.0.len() >= 30 {
59+
logged_notifications.0.pop_back();
5660
}
57-
logged_notifications.push_front(LoggedNotification::DataThresholdExceeded(
58-
DataThresholdExceeded {
59-
chart_type: ChartType::Bytes,
60-
threshold: notifications.bytes_notification.previous_threshold,
61-
data_info,
62-
timestamp: get_formatted_timestamp(timestamp),
63-
},
64-
));
61+
logged_notifications
62+
.0
63+
.push_front(LoggedNotification::DataThresholdExceeded(
64+
DataThresholdExceeded {
65+
id: logged_notifications.1,
66+
chart_type: ChartType::Bytes,
67+
threshold: notifications.bytes_notification.previous_threshold,
68+
data_info,
69+
timestamp: get_formatted_timestamp(timestamp),
70+
is_expanded: false,
71+
},
72+
));
6573
if sound_to_play.eq(&Sound::None) {
6674
sound_to_play = notifications.bytes_notification.sound;
6775
}
@@ -78,18 +86,21 @@ pub fn notify_and_log(
7886
if !favorites_last_interval.is_empty() {
7987
for (host, data_info_host) in favorites_last_interval {
8088
//log this notification
81-
emitted_notifications += 1;
82-
if logged_notifications.len() >= 30 {
83-
logged_notifications.pop_back();
89+
logged_notifications.1 += 1;
90+
if logged_notifications.0.len() >= 30 {
91+
logged_notifications.0.pop_back();
8492
}
8593

86-
logged_notifications.push_front(LoggedNotification::FavoriteTransmitted(
87-
FavoriteTransmitted {
88-
host,
89-
data_info_host,
90-
timestamp: get_formatted_timestamp(timestamp),
91-
},
92-
));
94+
logged_notifications
95+
.0
96+
.push_front(LoggedNotification::FavoriteTransmitted(
97+
FavoriteTransmitted {
98+
id: logged_notifications.1,
99+
host,
100+
data_info_host,
101+
timestamp: get_formatted_timestamp(timestamp),
102+
},
103+
));
93104
}
94105
if sound_to_play.eq(&Sound::None) {
95106
sound_to_play = notifications.favorite_notification.sound;
@@ -102,5 +113,5 @@ pub fn notify_and_log(
102113
play(sound_to_play, notifications.volume);
103114
}
104115

105-
emitted_notifications
116+
logged_notifications.1 - emitted_notifications_prev
106117
}

0 commit comments

Comments
 (0)