types: Change UIEvent::Notification structure

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
pull/312/head
Manos Pitsidianakis 6 months ago
parent 458258e1aa
commit 3495ffd61b
No known key found for this signature in database
GPG Key ID: 7729C7707F7E09D0

@ -932,16 +932,18 @@ impl Account {
return Some(UIEvent::MailboxUpdate((self.hash, mailbox_hash))); return Some(UIEvent::MailboxUpdate((self.hash, mailbox_hash)));
} }
return Some(Notification( return Some(Notification {
Some(format!("new e-mail from: {}", from)), title: Some(format!("new e-mail from: {}", from).into()),
format!( body: format!(
"{}\n{} {}", "{}\n{} {}",
subject, subject,
self.name, self.name,
self.mailbox_entries[&mailbox_hash].name() self.mailbox_entries[&mailbox_hash].name()
), )
Some(crate::types::NotificationType::NewMail), .into(),
)); source: None,
kind: Some(crate::types::NotificationType::NewMail),
});
} }
RefreshEventKind::Remove(env_hash) => { RefreshEventKind::Remove(env_hash) => {
if !self.collection.contains_key(&env_hash) { if !self.collection.contains_key(&env_hash) {
@ -987,21 +989,13 @@ impl Account {
let j = self.active_jobs.remove(&job_id); let j = self.active_jobs.remove(&job_id);
drop(j); drop(j);
} }
/*
context
.1
.send(ThreadEvent::UIEvent(UIEvent::Notification(
Some(format!("{} watcher exited with error", &self.name)),
e.to_string(),
Some(crate::types::NotificationType::Error(err.kind)),
)));
*/
self.watch(); self.watch();
return Some(Notification( return Some(Notification {
Some("Account watch failed".into()), title: Some("Account watch failed".into()),
err.to_string(), body: err.to_string().into(),
Some(crate::types::NotificationType::Error(err.kind)), kind: Some(crate::types::NotificationType::Error(err.kind)),
)); source: Some(err),
});
} }
RefreshEventKind::MailboxCreate(_new_mailbox) => {} RefreshEventKind::MailboxCreate(_new_mailbox) => {}
RefreshEventKind::MailboxDelete(_mailbox_hash) => {} RefreshEventKind::MailboxDelete(_mailbox_hash) => {}
@ -1699,11 +1693,14 @@ impl Account {
if let Err(err) = mailboxes.and_then(|mailboxes| self.init(mailboxes)) { if let Err(err) = mailboxes.and_then(|mailboxes| self.init(mailboxes)) {
if err.kind.is_authentication() { if err.kind.is_authentication() {
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
UIEvent::Notification( UIEvent::Notification {
Some(format!("{}: authentication error", &self.name)), title: Some(
err.to_string(), format!("{}: authentication error", &self.name).into(),
Some(crate::types::NotificationType::Error(err.kind)), ),
), source: None,
body: err.to_string().into(),
kind: Some(crate::types::NotificationType::Error(err.kind)),
},
)); ));
self.is_online.set_err(err); self.is_online.set_err(err);
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
@ -1771,11 +1768,14 @@ impl Account {
.job_executor .job_executor
.set_job_success(job_id, false); .set_job_success(job_id, false);
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
UIEvent::Notification( UIEvent::Notification {
Some(format!("{}: could not fetch mailbox", &self.name)), title: Some(
err.to_string(), format!("{}: could not fetch mailbox", &self.name).into(),
Some(crate::types::NotificationType::Error(err.kind)), ),
), source: None,
body: err.to_string().into(),
kind: Some(crate::types::NotificationType::Error(err.kind)),
},
)); ));
self.mailbox_entries self.mailbox_entries
.entry(mailbox_hash) .entry(mailbox_hash)
@ -1895,11 +1895,12 @@ impl Account {
.job_executor .job_executor
.set_job_success(job_id, false); .set_job_success(job_id, false);
self.main_loop_handler self.main_loop_handler
.send(ThreadEvent::UIEvent(UIEvent::Notification( .send(ThreadEvent::UIEvent(UIEvent::Notification {
Some(format!("{}: could not set flag", &self.name)), title: Some(format!("{}: could not set flag", &self.name).into()),
err.to_string(), source: None,
Some(crate::types::NotificationType::Error(err.kind)), body: err.to_string().into(),
))); kind: Some(crate::types::NotificationType::Error(err.kind)),
}));
} }
Ok(Some(Ok(()))) => { Ok(Some(Ok(()))) => {
for env_hash in env_hashes.iter() { for env_hash in env_hashes.iter() {
@ -1972,15 +1973,20 @@ impl Account {
file.path().display() file.path().display()
); );
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
UIEvent::Notification( UIEvent::Notification {
Some(format!("{}: could not save message", &self.name)), title: Some(
format!( format!("{}: could not save message", &self.name)
.into(),
),
source: None,
body: format!(
"Message was stored in {} so that you can restore it \ "Message was stored in {} so that you can restore it \
manually.", manually.",
file.path().display() file.path().display()
), )
Some(crate::types::NotificationType::Info), .into(),
), kind: Some(crate::types::NotificationType::Info),
},
)); ));
} }
Err(err) => log::error!("Could not save message: {err}"), Err(err) => log::error!("Could not save message: {err}"),
@ -1994,11 +2000,12 @@ impl Account {
.job_executor .job_executor
.set_job_success(job_id, false); .set_job_success(job_id, false);
self.main_loop_handler self.main_loop_handler
.send(ThreadEvent::UIEvent(UIEvent::Notification( .send(ThreadEvent::UIEvent(UIEvent::Notification {
Some("Could not send message".to_string()), title: Some("Could not send message".into()),
err.to_string(), source: None,
Some(crate::types::NotificationType::Error(err.kind)), body: err.to_string().into(),
))); kind: Some(crate::types::NotificationType::Error(err.kind)),
}));
} }
} }
JobRequest::DeleteMessages { ref mut handle, .. } => { JobRequest::DeleteMessages { ref mut handle, .. } => {
@ -2007,11 +2014,14 @@ impl Account {
.job_executor .job_executor
.set_job_success(job_id, false); .set_job_success(job_id, false);
self.main_loop_handler self.main_loop_handler
.send(ThreadEvent::UIEvent(UIEvent::Notification( .send(ThreadEvent::UIEvent(UIEvent::Notification {
Some(format!("{}: could not delete message", &self.name)), title: Some(
err.to_string(), format!("{}: could not delete message", &self.name).into(),
Some(crate::types::NotificationType::Error(err.kind)), ),
))); source: None,
body: err.to_string().into(),
kind: Some(crate::types::NotificationType::Error(err.kind)),
}));
} }
} }
JobRequest::CreateMailbox { JobRequest::CreateMailbox {
@ -2026,14 +2036,18 @@ impl Account {
.job_executor .job_executor
.set_job_success(job_id, false); .set_job_success(job_id, false);
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
UIEvent::Notification( UIEvent::Notification {
Some(format!( title: Some(
"{}: could not create mailbox {}", format!(
&self.name, path "{}: could not create mailbox {}",
)), &self.name, path
err.to_string(), )
Some(crate::types::NotificationType::Error(err.kind)), .into(),
), ),
source: None,
body: err.to_string().into(),
kind: Some(crate::types::NotificationType::Error(err.kind)),
},
)); ));
} }
Ok((mailbox_hash, mut mailboxes)) => { Ok((mailbox_hash, mut mailboxes)) => {
@ -2114,11 +2128,14 @@ impl Account {
.job_executor .job_executor
.set_job_success(job_id, false); .set_job_success(job_id, false);
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
UIEvent::Notification( UIEvent::Notification {
Some(format!("{}: could not delete mailbox", &self.name)), title: Some(
err.to_string(), format!("{}: could not delete mailbox", &self.name).into(),
Some(crate::types::NotificationType::Error(err.kind)), ),
), source: None,
body: err.to_string().into(),
kind: Some(crate::types::NotificationType::Error(err.kind)),
},
)); ));
} }
Ok(Some(Ok(mut mailboxes))) => { Ok(Some(Ok(mut mailboxes))) => {
@ -2167,11 +2184,15 @@ impl Account {
// [ref:FIXME] remove from settings as well // [ref:FIXME] remove from settings as well
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
UIEvent::Notification( UIEvent::Notification {
Some(format!("{}: mailbox deleted successfully", &self.name)), title: Some(
String::new(), format!("{}: mailbox deleted successfully", &self.name)
Some(crate::types::NotificationType::Info), .into(),
), ),
source: None,
body: "".into(),
kind: Some(crate::types::NotificationType::Info),
},
)); ));
} }
} }
@ -2186,26 +2207,34 @@ impl Account {
.job_executor .job_executor
.set_job_success(job_id, false); .set_job_success(job_id, false);
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
UIEvent::Notification( UIEvent::Notification {
Some(format!( title: Some(
"{}: could not set mailbox permissions", format!(
&self.name "{}: could not set mailbox permissions",
)), &self.name
err.to_string(), )
Some(crate::types::NotificationType::Error(err.kind)), .into(),
), ),
source: None,
body: err.to_string().into(),
kind: Some(crate::types::NotificationType::Error(err.kind)),
},
)); ));
} }
Ok(Some(Ok(_))) => { Ok(Some(Ok(_))) => {
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
UIEvent::Notification( UIEvent::Notification {
Some(format!( title: Some(
"{}: mailbox permissions set successfully", format!(
&self.name "{}: mailbox permissions set successfully",
)), &self.name
String::new(), )
Some(crate::types::NotificationType::Info), .into(),
), ),
source: None,
body: "".into(),
kind: Some(crate::types::NotificationType::Info),
},
)); ));
} }
} }
@ -2223,14 +2252,18 @@ impl Account {
.job_executor .job_executor
.set_job_success(job_id, false); .set_job_success(job_id, false);
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
UIEvent::Notification( UIEvent::Notification {
Some(format!( title: Some(
"{}: could not set mailbox subscription", format!(
&self.name "{}: could not set mailbox subscription",
)), &self.name
err.to_string(), )
Some(crate::types::NotificationType::Error(err.kind)), .into(),
), ),
source: None,
body: err.to_string().into(),
kind: Some(crate::types::NotificationType::Error(err.kind)),
},
)); ));
} }
Ok(Some(Ok(()))) if self.mailbox_entries.contains_key(mailbox_hash) => { Ok(Some(Ok(()))) if self.mailbox_entries.contains_key(mailbox_hash) => {
@ -2243,16 +2276,20 @@ impl Account {
let _ = m.ref_mailbox.set_is_subscribed(*new_value); let _ = m.ref_mailbox.set_is_subscribed(*new_value);
}); });
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
UIEvent::Notification( UIEvent::Notification {
Some(format!( title: Some(
"{}: `{}` has been {}subscribed.", format!(
&self.name, "{}: `{}` has been {}subscribed.",
self.mailbox_entries[mailbox_hash].name(), &self.name,
if *new_value { "" } else { "un" } self.mailbox_entries[mailbox_hash].name(),
)), if *new_value { "" } else { "un" }
String::new(), )
Some(crate::types::NotificationType::Info), .into(),
), ),
source: None,
body: "".into(),
kind: Some(crate::types::NotificationType::Info),
},
)); ));
} }
Ok(Some(Ok(()))) => {} Ok(Some(Ok(()))) => {}
@ -2269,11 +2306,14 @@ impl Account {
.set_job_success(job_id, false); .set_job_success(job_id, false);
// [ref:TODO]: relaunch watch job with ratelimit for failure // [ref:TODO]: relaunch watch job with ratelimit for failure
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
UIEvent::Notification( UIEvent::Notification {
Some(format!("{}: watch thread failed", &self.name)), title: Some(
err.to_string(), format!("{}: watch thread failed", &self.name).into(),
Some(crate::types::NotificationType::Error(err.kind)), ),
), source: None,
body: err.to_string().into(),
kind: Some(crate::types::NotificationType::Error(err.kind)),
},
)); ));
} }
} }
@ -2290,21 +2330,25 @@ impl Account {
.job_executor .job_executor
.set_job_success(job_id, false); .set_job_success(job_id, false);
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
UIEvent::Notification( UIEvent::Notification {
Some(format!("{}: {} failed", &self.name, name,)), title: Some(format!("{}: {} failed", &self.name, name,).into()),
err.to_string(), source: None,
Some(crate::types::NotificationType::Error(err.kind)), body: err.to_string().into(),
), kind: Some(crate::types::NotificationType::Error(err.kind)),
},
)); ));
} }
Ok(Some(Ok(()))) if on_finish.is_none() => { Ok(Some(Ok(()))) if on_finish.is_none() => {
if log_level <= LogLevel::INFO { if log_level <= LogLevel::INFO {
self.main_loop_handler.send(ThreadEvent::UIEvent( self.main_loop_handler.send(ThreadEvent::UIEvent(
UIEvent::Notification( UIEvent::Notification {
Some(format!("{}: {} succeeded", &self.name, name,)), title: Some(
String::new(), format!("{}: {} succeeded", &self.name, name,).into(),
Some(crate::types::NotificationType::Info), ),
), source: None,
body: "".into(),
kind: Some(crate::types::NotificationType::Info),
},
)); ));
} }
} }

@ -843,11 +843,14 @@ To: {}
true true
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Could not parse draft headers correctly.".to_string()), title: Some("Could not parse draft headers correctly.".into()),
format!("{err}\nThe invalid text has been set as the body of your draft",), source: None,
Some(NotificationType::Error(melib::error::ErrorKind::None)), body:
)); format!("{err}\nThe invalid text has been set as the body of your draft",)
.into(),
kind: Some(NotificationType::Error(melib::error::ErrorKind::None)),
});
self.has_changes = true; self.has_changes = true;
false false
} }
@ -1209,11 +1212,12 @@ impl Component for Composer {
); );
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
err.to_string(), source: None,
Some(NotificationType::Error(err.kind)), body: err.to_string().into(),
)); kind: Some(NotificationType::Error(err.kind)),
});
save_draft( save_draft(
self.draft.clone().finalise().unwrap().as_bytes(), self.draft.clone().finalise().unwrap().as_bytes(),
context, context,
@ -1355,11 +1359,12 @@ impl Component for Composer {
}) { }) {
Err(err) | Ok(Some(Err(err))) => { Err(err) | Ok(Some(Err(err))) => {
self.mode = ViewMode::Edit; self.mode = ViewMode::Edit;
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
err.to_string(), source: None,
Some(NotificationType::Error(err.kind)), body: err.to_string().into(),
)); kind: Some(NotificationType::Error(err.kind)),
});
self.set_dirty(true); self.set_dirty(true);
} }
Ok(None) | Ok(Some(Ok(()))) => { Ok(None) | Ok(Some(Ok(()))) => {
@ -1482,11 +1487,12 @@ impl Component for Composer {
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
{ {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
err.to_string(), source: None,
None, body: err.to_string().into(),
)); kind: None,
});
} }
} }
self.mode = ViewMode::Send(UIConfirmationDialog::new( self.mode = ViewMode::Send(UIConfirmationDialog::new(
@ -1529,16 +1535,17 @@ impl Component for Composer {
drop(embedded_guard); drop(embedded_guard);
let embedded_pty = self.embedded_pty.take(); let embedded_pty = self.embedded_pty.take();
if exit_code != 0 { if exit_code != 0 {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
format!( source: None,
"Subprocess has exited with exit code {}", body: format!(
exit_code "Subprocess has exited with exit code {exit_code}",
), )
Some(NotificationType::Error( .into(),
kind: Some(NotificationType::Error(
melib::error::ErrorKind::External, melib::error::ErrorKind::External,
)), )),
)); });
} else if let Some(EmbeddedPty { } else if let Some(EmbeddedPty {
running: true, running: true,
file, file,
@ -1608,13 +1615,15 @@ impl Component for Composer {
} }
Ok(WaitStatus::Signaled(_, signal, _)) => { Ok(WaitStatus::Signaled(_, signal, _)) => {
drop(embedded_guard); drop(embedded_guard);
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
format!("Subprocess was killed by {} signal", signal), source: None,
Some(NotificationType::Error( body: format!("Subprocess was killed by {signal} signal")
.into(),
kind: Some(NotificationType::Error(
melib::error::ErrorKind::External, melib::error::ErrorKind::External,
)), )),
)); });
self.initialized = false; self.initialized = false;
self.embedded_pty = None; self.embedded_pty = None;
self.mode = ViewMode::Edit; self.mode = ViewMode::Edit;
@ -1623,13 +1632,14 @@ impl Component for Composer {
.push_back(UIEvent::ChangeMode(UIMode::Normal)); .push_back(UIEvent::ChangeMode(UIMode::Normal));
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Embedded editor crashed.".to_string()), title: Some("Embedded editor crashed.".into()),
format!("Subprocess has exited with reason {}", &err), source: None,
Some(NotificationType::Error( body: format!("Subprocess has exited with reason {err}").into(),
kind: Some(NotificationType::Error(
melib::error::ErrorKind::External, melib::error::ErrorKind::External,
)), )),
)); });
drop(embedded_guard); drop(embedded_guard);
self.initialized = false; self.initialized = false;
self.embedded_pty = None; self.embedded_pty = None;
@ -1674,11 +1684,12 @@ impl Component for Composer {
self.mode = ViewMode::SelectEncryptKey(false, widget); self.mode = ViewMode::SelectEncryptKey(false, widget);
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Could not list keys.".to_string()), title: Some("Could not list keys.".into()),
format!("libgpgme error: {}", &err), source: None,
Some(NotificationType::Error(melib::error::ErrorKind::External)), body: format!("libgpgme error: {err}").into(),
)); kind: Some(NotificationType::Error(melib::error::ErrorKind::External)),
});
} }
} }
self.set_dirty(true); self.set_dirty(true);
@ -1714,11 +1725,12 @@ impl Component for Composer {
self.mode = ViewMode::SelectEncryptKey(true, widget); self.mode = ViewMode::SelectEncryptKey(true, widget);
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Could not list keys.".to_string()), title: Some("Could not list keys.".into()),
format!("libgpgme error: {}", &err), source: None,
Some(NotificationType::Error(melib::error::ErrorKind::External)), body: format!("libgpgme error: {err}").into(),
)); kind: Some(NotificationType::Error(melib::error::ErrorKind::External)),
});
} }
} }
self.set_dirty(true); self.set_dirty(true);
@ -1775,11 +1787,12 @@ impl Component for Composer {
guard.terminate(); guard.terminate();
self.update_from_file(file, context); self.update_from_file(file, context);
} }
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
"Subprocess was killed by SIGTERM signal".to_string(), source: None,
Some(NotificationType::Error(melib::error::ErrorKind::External)), body: "Subprocess was killed by SIGTERM signal".into(),
)); kind: Some(NotificationType::Error(melib::error::ErrorKind::External)),
});
self.initialized = false; self.initialized = false;
self.mode = ViewMode::Edit; self.mode = ViewMode::Edit;
context context
@ -1800,13 +1813,15 @@ impl Component for Composer {
} else { } else {
match std::env::var("EDITOR") { match std::env::var("EDITOR") {
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some(err.to_string()), title: Some(err.to_string().into()),
"$EDITOR is not set. You can change an envvar's value with setenv \ source: None,
or set composing.editor_command setting in your configuration." body: "$EDITOR is not set. You can change an envvar's value with \
.to_string(), setenv or set composing.editor_command setting in your \
Some(NotificationType::Error(melib::error::ErrorKind::None)), configuration."
)); .into(),
kind: Some(NotificationType::Error(melib::error::ErrorKind::None)),
});
self.set_dirty(true); self.set_dirty(true);
return true; return true;
} }
@ -1829,11 +1844,12 @@ impl Component for Composer {
) { ) {
Ok(f) => f, Ok(f) => f,
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
err.to_string(), source: None,
Some(NotificationType::Error(err.kind)), body: err.to_string().into(),
)); kind: Some(NotificationType::Error(err.kind)),
});
self.set_dirty(true); self.set_dirty(true);
return true; return true;
} }
@ -1866,11 +1882,16 @@ impl Component for Composer {
self.mode = ViewMode::EmbeddedPty; self.mode = ViewMode::EmbeddedPty;
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some(format!("Failed to create pseudoterminal: {}", err)), title: Some(
err.to_string(), format!("Failed to create pseudoterminal: {}", err).into(),
Some(NotificationType::Error(melib::error::ErrorKind::External)), ),
)); source: None,
body: err.to_string().into(),
kind: Some(NotificationType::Error(
melib::error::ErrorKind::External,
)),
});
} }
} }
self.set_dirty(true); self.set_dirty(true);
@ -1896,11 +1917,12 @@ impl Component for Composer {
let _ = child.wait(); let _ = child.wait();
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some(format!("Failed to execute {}: {}", editor, err)), title: Some(format!("Failed to execute {}: {}", editor, err).into()),
err.to_string(), source: None,
Some(NotificationType::Error(melib::error::ErrorKind::External)), body: err.to_string().into(),
)); kind: Some(NotificationType::Error(melib::error::ErrorKind::External)),
});
context.replies.push_back(UIEvent::Fork(ForkType::Finished)); context.replies.push_back(UIEvent::Fork(ForkType::Finished));
context.restore_input(); context.restore_input();
self.set_dirty(true); self.set_dirty(true);
@ -1918,13 +1940,15 @@ impl Component for Composer {
self.has_changes = has_changes; self.has_changes = has_changes;
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Could not parse draft headers correctly.".to_string()), title: Some("Could not parse draft headers correctly.".into()),
format!( source: None,
body: format!(
"{err}\nThe invalid text has been set as the body of your draft", "{err}\nThe invalid text has been set as the body of your draft",
), )
Some(NotificationType::Error(melib::error::ErrorKind::None)), .into(),
)); kind: Some(NotificationType::Error(melib::error::ErrorKind::None)),
});
self.has_changes = true; self.has_changes = true;
} }
} }
@ -1935,11 +1959,12 @@ impl Component for Composer {
UIEvent::Action(ref a) => match a { UIEvent::Action(ref a) => match a {
Action::Compose(ComposeAction::AddAttachmentPipe(ref command)) => { Action::Compose(ComposeAction::AddAttachmentPipe(ref command)) => {
if command.is_empty() { if command.is_empty() {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
format!("pipe command value is invalid: {}", command), source: None,
Some(NotificationType::Error(melib::error::ErrorKind::None)), body: format!("pipe command value is invalid: {command}").into(),
)); kind: Some(NotificationType::Error(melib::error::ErrorKind::None)),
});
return false; return false;
} }
match File::create_temp_file(&[], None, None, None, true) match File::create_temp_file(&[], None, None, None, true)
@ -1969,13 +1994,14 @@ impl Component for Composer {
match melib::email::compose::attachment_from_file(&f.path()) { match melib::email::compose::attachment_from_file(&f.path()) {
Ok(a) => a, Ok(a) => a,
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("could not add attachment".to_string()), title: Some("could not add attachment".into()),
err.to_string(), source: None,
Some(NotificationType::Error( body: err.to_string().into(),
kind: Some(NotificationType::Error(
melib::error::ErrorKind::None, melib::error::ErrorKind::None,
)), )),
)); });
self.set_dirty(true); self.set_dirty(true);
return true; return true;
} }
@ -1986,11 +2012,15 @@ impl Component for Composer {
return true; return true;
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
format!("could not execute pipe command {}: {}", command, &err), source: None,
Some(NotificationType::Error(melib::error::ErrorKind::External)), body: format!("could not execute pipe command {command}: {err}")
)); .into(),
kind: Some(NotificationType::Error(
melib::error::ErrorKind::External,
)),
});
self.set_dirty(true); self.set_dirty(true);
return true; return true;
} }
@ -2000,11 +2030,12 @@ impl Component for Composer {
let attachment = match melib::email::compose::attachment_from_file(path) { let attachment = match melib::email::compose::attachment_from_file(path) {
Ok(a) => a, Ok(a) => a,
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("could not add attachment".to_string()), title: Some("could not add attachment".into()),
err.to_string(), source: None,
Some(NotificationType::Error(melib::error::ErrorKind::None)), body: err.to_string().into(),
)); kind: Some(NotificationType::Error(melib::error::ErrorKind::None)),
});
self.set_dirty(true); self.set_dirty(true);
return true; return true;
} }
@ -2022,13 +2053,14 @@ impl Component for Composer {
{ {
cmd.as_str() cmd.as_str()
} else { } else {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
"You haven't defined any command to launch in \ source: None,
[terminal.file_picker_command]." body: "You haven't defined any command to launch in \
[terminal.file_picker_command]."
.into(), .into(),
Some(NotificationType::Error(melib::error::ErrorKind::None)), kind: Some(NotificationType::Error(melib::error::ErrorKind::None)),
)); });
self.set_dirty(true); self.set_dirty(true);
return true; return true;
}; };
@ -2057,27 +2089,36 @@ impl Component for Composer {
self.has_changes = true; self.has_changes = true;
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some(format!( title: Some(
"could not add attachment: {}", format!(
String::from_utf8_lossy(path) "could not add attachment: {}",
)), String::from_utf8_lossy(path)
err.to_string(), )
Some(NotificationType::Error( .into(),
),
source: None,
body: err.to_string().into(),
kind: Some(NotificationType::Error(
melib::error::ErrorKind::None, melib::error::ErrorKind::None,
)), )),
)); });
} }
}; };
} }
} }
Err(err) => { Err(err) => {
let command = command.to_string(); let command = command.to_string();
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some(format!("Failed to execute {}: {}", command, err)), title: Some(
err.to_string(), format!("Failed to execute {}: {}", command, err).into(),
Some(NotificationType::Error(melib::error::ErrorKind::External)), ),
)); source: None,
body: err.to_string().into(),
kind: Some(NotificationType::Error(
melib::error::ErrorKind::External,
)),
});
context.restore_input(); context.restore_input();
self.set_dirty(true); self.set_dirty(true);
return true; return true;
@ -2409,21 +2450,24 @@ pub fn save_draft(
kind, kind,
.. ..
}) => { }) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
details.map(|s| s.into()), title: details,
summary.to_string(), source: None,
Some(NotificationType::Error(kind)), body: summary,
)); kind: Some(NotificationType::Error(kind)),
});
} }
Ok(mailbox_hash) => { Ok(mailbox_hash) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Message saved".into()), title: Some("Message saved".into()),
format!( source: None,
body: format!(
"Message saved in `{}`", "Message saved in `{}`",
&context.accounts[&account_hash].mailbox_entries[&mailbox_hash].name &context.accounts[&account_hash].mailbox_entries[&mailbox_hash].name
), )
Some(NotificationType::Info), .into(),
)); kind: Some(NotificationType::Info),
});
} }
} }
} }

@ -780,21 +780,24 @@ pub trait MailListingTrait: ListingTrait {
handle, handle,
on_finish: Some(CallbackFn(Box::new(move |context: &mut Context| { on_finish: Some(CallbackFn(Box::new(move |context: &mut Context| {
context.replies.push_back(match receiver.try_recv() { context.replies.push_back(match receiver.try_recv() {
Err(_) | Ok(None) => UIEvent::Notification( Err(_) | Ok(None) => UIEvent::Notification {
Some("Could not export mbox".to_string()), title: Some("Could not export mbox".into()),
"Job was canceled.".to_string(), source: None,
Some(NotificationType::Info), body: "Job was canceled.".into(),
), kind: Some(NotificationType::Info),
Ok(Some(Err(err))) => UIEvent::Notification( },
Some("Could not export mbox".to_string()), Ok(Some(Err(err))) => UIEvent::Notification {
err.to_string(), title: Some("Could not export mbox".into()),
Some(NotificationType::Error(err.kind)), source: None,
), body: err.to_string().into(),
Ok(Some(Ok(path))) => UIEvent::Notification( kind: Some(NotificationType::Error(err.kind)),
Some("Succesfully exported mbox".to_string()), },
format!("Wrote to file {}", path.display()), Ok(Some(Ok(path))) => UIEvent::Notification {
Some(NotificationType::Info), title: Some("Succesfully exported mbox".into()),
), source: None,
body: format!("Wrote to file {}", path.display()).into(),
kind: Some(NotificationType::Info),
},
}); });
}))), }))),
log_level: LogLevel::INFO, log_level: LogLevel::INFO,
@ -1827,11 +1830,12 @@ impl Component for Listing {
if let MenuEntryCursor::Mailbox(idx) = self.cursor_pos.menu { if let MenuEntryCursor::Mailbox(idx) = self.cursor_pos.menu {
if let Some(&mailbox_hash) = account.mailboxes_order.get(idx) { if let Some(&mailbox_hash) = account.mailboxes_order.get(idx) {
if let Err(err) = account.refresh(mailbox_hash) { if let Err(err) = account.refresh(mailbox_hash) {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Could not refresh.".to_string()), title: Some("Could not refresh.".into()),
err.to_string(), source: None,
Some(NotificationType::Error(err.kind)), body: err.to_string().into(),
)); kind: Some(NotificationType::Error(err.kind)),
});
} }
} }
} }

@ -1563,11 +1563,12 @@ impl CompactListing {
search_term, &err search_term, &err
); );
log::error!("{}", message); log::error!("{}", message);
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Could not perform search".to_string()), title: Some("Could not perform search".into()),
message, source: None,
Some(crate::types::NotificationType::Error(err.kind)), body: message.into(),
)); kind: Some(crate::types::NotificationType::Error(err.kind)),
});
} }
} }
} }
@ -2033,11 +2034,12 @@ impl Component for CompactListing {
self.search_job = Some((filter_term.to_string(), handle)); self.search_job = Some((filter_term.to_string(), handle));
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Could not perform search".to_string()), title: Some("Could not perform search".into()),
err.to_string(), source: None,
Some(crate::types::NotificationType::Error(err.kind)), body: err.to_string().into(),
)); kind: Some(crate::types::NotificationType::Error(err.kind)),
});
} }
}; };
self.set_dirty(true); self.set_dirty(true);
@ -2061,11 +2063,12 @@ impl Component for CompactListing {
} }
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Could not perform search".to_string()), title: Some("Could not perform search".into()),
err.to_string(), source: None,
Some(crate::types::NotificationType::Error(err.kind)), body: err.to_string().into(),
)); kind: Some(crate::types::NotificationType::Error(err.kind)),
});
} }
}; };
self.set_dirty(true); self.set_dirty(true);
@ -2084,11 +2087,12 @@ impl Component for CompactListing {
Ok(None) => { /* something happened, perhaps a worker thread panicked */ } Ok(None) => { /* something happened, perhaps a worker thread panicked */ }
Ok(Some(Ok(results))) => self.filter(filter_term, results, context), Ok(Some(Ok(results))) => self.filter(filter_term, results, context),
Ok(Some(Err(err))) => { Ok(Some(Err(err))) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Could not perform search".to_string()), title: Some("Could not perform search".into()),
err.to_string(), source: None,
Some(crate::types::NotificationType::Error(err.kind)), body: err.to_string().into(),
)); kind: Some(crate::types::NotificationType::Error(err.kind)),
});
} }
} }
self.set_dirty(true); self.set_dirty(true);

@ -1476,11 +1476,7 @@ impl Component for ConversationsListing {
self.search_job = Some((filter_term.to_string(), handle)); self.search_job = Some((filter_term.to_string(), handle));
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification { title: Some("Could not perform search".into()), source: None, body: err.to_string().into(), kind: Some(crate::types::NotificationType::Error(err.kind)), });
Some("Could not perform search".to_string()),
err.to_string(),
Some(crate::types::NotificationType::Error(err.kind)),
));
} }
}; };
self.set_dirty(true); self.set_dirty(true);
@ -1522,11 +1518,7 @@ impl Component for ConversationsListing {
Ok(None) => { /* something happened, perhaps a worker thread panicked */ } Ok(None) => { /* something happened, perhaps a worker thread panicked */ }
Ok(Some(Ok(results))) => self.filter(filter_term, results, context), Ok(Some(Ok(results))) => self.filter(filter_term, results, context),
Ok(Some(Err(err))) => { Ok(Some(Err(err))) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification { title: Some("Could not perform search".into()), source: None, body: err.to_string().into(), kind: Some(crate::types::NotificationType::Error(err.kind)), });
Some("Could not perform search".to_string()),
err.to_string(),
Some(crate::types::NotificationType::Error(err.kind)),
));
} }
} }
self.set_dirty(true); self.set_dirty(true);

@ -1313,11 +1313,12 @@ impl PlainListing {
search_term, &err search_term, &err
); );
log::error!("{}", message); log::error!("{}", message);
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Could not perform search".to_string()), title: Some("Could not perform search".into()),
message, source: None,
Some(crate::types::NotificationType::Error(err.kind)), body: message.into(),
)); kind: Some(crate::types::NotificationType::Error(err.kind)),
});
} }
} }
} }
@ -1756,11 +1757,12 @@ impl Component for PlainListing {
self.search_job = Some((filter_term.to_string(), handle)); self.search_job = Some((filter_term.to_string(), handle));
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Could not perform search".to_string()), title: Some("Could not perform search".into()),
err.to_string(), source: None,
Some(crate::types::NotificationType::Error(err.kind)), body: err.to_string().into(),
)); kind: Some(crate::types::NotificationType::Error(err.kind)),
});
} }
}; };
self.set_dirty(true); self.set_dirty(true);
@ -1784,11 +1786,12 @@ impl Component for PlainListing {
} }
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Could not perform search".to_string()), title: Some("Could not perform search".into()),
err.to_string(), source: None,
Some(crate::types::NotificationType::Error(err.kind)), body: err.to_string().into(),
)); kind: Some(crate::types::NotificationType::Error(err.kind)),
});
} }
}; };
self.set_dirty(true); self.set_dirty(true);
@ -1807,11 +1810,12 @@ impl Component for PlainListing {
Ok(None) => { /* something happened, perhaps a worker thread panicked */ } Ok(None) => { /* something happened, perhaps a worker thread panicked */ }
Ok(Some(Ok(results))) => self.filter(filter_term, results, context), Ok(Some(Ok(results))) => self.filter(filter_term, results, context),
Ok(Some(Err(err))) => { Ok(Some(Err(err))) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Could not perform search".to_string()), title: Some("Could not perform search".into()),
err.to_string(), source: None,
Some(crate::types::NotificationType::Error(err.kind)), body: err.to_string().into(),
)); kind: Some(crate::types::NotificationType::Error(err.kind)),
});
} }
} }
self.set_dirty(true); self.set_dirty(true);

@ -1613,11 +1613,7 @@ impl Component for ThreadListing {
self.search_job = Some((filter_term.to_string(), handle)); self.search_job = Some((filter_term.to_string(), handle));
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification { title: Some("Could not perform search".into()), source: None, body: err.to_string().into(), kind: Some(crate::types::NotificationType::Error(err.kind)), });
Some("Could not perform search".to_string()),
err.to_string(),
Some(crate::types::NotificationType::Error(err.kind)),
));
} }
}; };
self.set_dirty(true); self.set_dirty(true);
@ -1638,11 +1634,7 @@ impl Component for ThreadListing {
Ok(None) => { /* something happened, perhaps a worker thread panicked */ } Ok(None) => { /* something happened, perhaps a worker thread panicked */ }
Ok(Some(Ok(results))) => self.filter(filter_term, results, context), Ok(Some(Ok(results))) => self.filter(filter_term, results, context),
Ok(Some(Err(err))) => { Ok(Some(Err(err))) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification { title: Some("Could not perform search".into()), source: None, body: err.to_string().into(), kind: Some(crate::types::NotificationType::Error(err.kind)), });
Some("Could not perform search".to_string()),
err.to_string(),
Some(crate::types::NotificationType::Error(err.kind)),
));
} }
} }
self.set_dirty(true); self.set_dirty(true);

@ -327,11 +327,12 @@ impl Component for MailView {
} else if let MailViewState::Error { ref err } = self.state { } else if let MailViewState::Error { ref err } = self.state {
grid.clear_area(area, self.theme_default); grid.clear_area(area, self.theme_default);
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Failed to open e-mail".to_string()), title: Some("Failed to open e-mail".into()),
err.to_string(), source: None,
Some(NotificationType::Error(err.kind)), body: err.to_string().into(),
)); kind: Some(NotificationType::Error(err.kind)),
});
log::error!("Failed to open envelope: {err}"); log::error!("Failed to open envelope: {err}");
self.init_futures(context); self.init_futures(context);
return; return;
@ -575,11 +576,12 @@ impl Component for MailView {
err err
); );
log::error!("{err_string}"); log::error!("{err_string}");
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Failed to open e-mail".to_string()), title: Some("Failed to open e-mail".into()),
err_string, source: None,
Some(NotificationType::Error(err.kind)), body: err_string.into(),
)); kind: Some(NotificationType::Error(err.kind)),
});
} }
} }
} }

@ -144,14 +144,15 @@ impl EnvelopeView {
}); });
match command_obj { match command_obj {
Err(err) => { Err(err) => {
main_loop_handler.send(ThreadEvent::UIEvent(UIEvent::Notification( main_loop_handler.send(ThreadEvent::UIEvent(UIEvent::Notification {
Some(format!( title: Some(
"Failed to start html filter process: {}", format!("Failed to start html filter process: {}", filter_invocation,)
filter_invocation, .into(),
)), ),
err.to_string(), source: None,
Some(NotificationType::Error(melib::ErrorKind::External)), body: err.to_string().into(),
))); kind: Some(NotificationType::Error(melib::ErrorKind::External)),
}));
// [ref:FIXME]: add `v` configurable shortcut // [ref:FIXME]: add `v` configurable shortcut
let comment = Some(format!( let comment = Some(format!(
"Failed to start html filter process: `{}`. Press `v` to open in web \ "Failed to start html filter process: `{}`. Press `v` to open in web \
@ -1385,20 +1386,24 @@ impl Component for EnvelopeView {
} }
match save_attachment(&path, &self.mail.bytes) { match save_attachment(&path, &self.mail.bytes) {
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some(format!("Failed to create file at {}", path.display())), title: Some(
err.to_string(), format!("Failed to create file at {}", path.display()).into(),
Some(NotificationType::Error(melib::ErrorKind::External)), ),
)); source: None,
body: err.to_string().into(),
kind: Some(NotificationType::Error(melib::ErrorKind::External)),
});
log::error!("Failed to create file at {}: {err}", path.display()); log::error!("Failed to create file at {}: {err}", path.display());
return true; return true;
} }
Ok(()) => { Ok(()) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
format!("Saved at {}", &path.display()), source: None,
Some(NotificationType::Info), body: format!("Saved at {}", &path.display()).into(),
)); kind: Some(NotificationType::Info),
});
} }
} }
@ -1423,19 +1428,23 @@ impl Component for EnvelopeView {
} }
match save_attachment(&path, &u.decode(Default::default())) { match save_attachment(&path, &u.decode(Default::default())) {
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some(format!("Failed to create file at {}", path.display())), title: Some(
err.to_string(), format!("Failed to create file at {}", path.display()).into(),
Some(NotificationType::Error(melib::ErrorKind::External)), ),
)); source: None,
body: err.to_string().into(),
kind: Some(NotificationType::Error(melib::ErrorKind::External)),
});
log::error!("Failed to create file at {}: {err}", path.display()); log::error!("Failed to create file at {}: {err}", path.display());
} }
Ok(()) => { Ok(()) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
format!("Saved at {}", path.display()), source: None,
Some(NotificationType::Info), body: format!("Saved at {}", path.display()).into(),
)); kind: Some(NotificationType::Info),
});
} }
} }
} else if a_i == 0 { } else if a_i == 0 {
@ -1448,20 +1457,24 @@ impl Component for EnvelopeView {
} }
match save_attachment(&path, &self.mail.bytes) { match save_attachment(&path, &self.mail.bytes) {
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some(format!("Failed to create file at {}", path.display())), title: Some(
err.to_string(), format!("Failed to create file at {}", path.display()).into(),
Some(NotificationType::Error(melib::ErrorKind::External)), ),
)); source: None,
body: err.to_string().into(),
kind: Some(NotificationType::Error(melib::ErrorKind::External)),
});
log::error!("Failed to create file at {}: {err}", path.display()); log::error!("Failed to create file at {}: {err}", path.display());
return true; return true;
} }
Ok(()) => { Ok(()) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
format!("Saved at {}", &path.display()), source: None,
Some(NotificationType::Info), body: format!("Saved at {}", &path.display()).into(),
)); kind: Some(NotificationType::Info),
});
} }
} }
@ -1651,11 +1664,12 @@ impl Component for EnvelopeView {
context.children.push(child); context.children.push(child);
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some(format!("Failed to launch {:?}", url_launcher)), title: Some(format!("Failed to launch {:?}", url_launcher).into()),
err.to_string(), source: None,
Some(NotificationType::Error(melib::ErrorKind::External)), body: err.to_string().into(),
)); kind: Some(NotificationType::Error(melib::ErrorKind::External)),
});
} }
} }
return true; return true;

@ -61,14 +61,15 @@ impl HtmlView {
.spawn(); .spawn();
match command_obj { match command_obj {
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some(format!( title: Some(
"Failed to start html filter process: {}", format!("Failed to start html filter process: {}", filter_invocation,)
filter_invocation, .into(),
)), ),
err.to_string(), source: None,
Some(NotificationType::Error(melib::ErrorKind::External)), body: err.to_string().into(),
)); kind: Some(NotificationType::Error(melib::ErrorKind::External)),
});
String::from_utf8_lossy(&bytes).to_string() String::from_utf8_lossy(&bytes).to_string()
} }
Ok(mut html_filter) => { Ok(mut html_filter) => {
@ -108,11 +109,12 @@ impl HtmlView {
display_text display_text
} else { } else {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Failed to find any application to use as html filter".to_string()), title: Some("Failed to find any application to use as html filter".into()),
String::new(), source: None,
Some(NotificationType::Error(melib::error::ErrorKind::None)), body: "".into(),
)); kind: Some(NotificationType::Error(melib::error::ErrorKind::None)),
});
String::from_utf8_lossy(&bytes).to_string() String::from_utf8_lossy(&bytes).to_string()
}; };
if body.count_attachments() > 1 { if body.count_attachments() > 1 {

@ -373,11 +373,7 @@ impl Component for MailboxManager {
.to_string(), .to_string(),
)) ))
{ {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification { title: None, source: None, body: err.to_string().into(), kind: Some(crate::types::NotificationType::Error(err.kind)), });
None,
err.to_string(),
Some(crate::types::NotificationType::Error(err.kind)),
));
} }
} }
MailboxAction::Unsubscribe => { MailboxAction::Unsubscribe => {
@ -389,11 +385,7 @@ impl Component for MailboxManager {
.to_string(), .to_string(),
)) ))
{ {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification { title: None, source: None, body: err.to_string().into(), kind: Some(crate::types::NotificationType::Error(err.kind)), });
None,
err.to_string(),
Some(crate::types::NotificationType::Error(err.kind)),
));
} }
} }
} }

@ -68,7 +68,13 @@ mod dbus {
return false; return false;
} }
if let UIEvent::Notification(ref title, ref body, ref kind) = event { if let UIEvent::Notification {
ref title,
source: _,
ref body,
ref kind,
} = event
{
if !self.rate_limit.tick() { if !self.rate_limit.tick() {
return false; return false;
} }
@ -77,7 +83,7 @@ mod dbus {
let mut notification = notify_rust::Notification::new(); let mut notification = notify_rust::Notification::new();
notification notification
.appname("meli") .appname("meli")
.summary(title.as_ref().map(String::as_str).unwrap_or("meli")) .summary(title.as_ref().map(<_>::as_ref).unwrap_or("meli"))
.body(&escape_str(body)); .body(&escape_str(body));
match *kind { match *kind {
Some(NotificationType::NewMail) => { Some(NotificationType::NewMail) => {
@ -191,7 +197,13 @@ impl Component for NotificationCommand {
fn draw(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {} fn draw(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {}
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
if let UIEvent::Notification(ref title, ref body, ref kind) = event { if let UIEvent::Notification {
ref title,
source: _,
ref body,
ref kind,
} = event
{
if context.settings.notifications.enable { if context.settings.notifications.enable {
if *kind == Some(NotificationType::NewMail) { if *kind == Some(NotificationType::NewMail) {
if let Some(ref path) = context.settings.notifications.xbiff_file_path { if let Some(ref path) = context.settings.notifications.xbiff_file_path {
@ -211,8 +223,8 @@ impl Component for NotificationCommand {
if let Some(ref bin) = script { if let Some(ref bin) = script {
match Command::new(bin) match Command::new(bin)
.arg(&kind.map(|k| k.to_string()).unwrap_or_default()) .arg(&kind.map(|k| k.to_string()).unwrap_or_default())
.arg(title.as_ref().map(String::as_str).unwrap_or("meli")) .arg(title.as_ref().map(<_>::as_ref).unwrap_or("meli"))
.arg(body) .arg(body.as_ref())
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()

@ -491,7 +491,7 @@ impl State {
} = &mut *self.context; } = &mut *self.context;
if let Some(notification) = accounts[&account_hash].reload(event, mailbox_hash) { if let Some(notification) = accounts[&account_hash].reload(event, mailbox_hash) {
if let UIEvent::Notification(_, _, _) = notification { if matches!(notification, UIEvent::Notification { .. }) {
self.rcv_event(UIEvent::MailboxUpdate((account_hash, mailbox_hash))); self.rcv_event(UIEvent::MailboxUpdate((account_hash, mailbox_hash)));
} }
self.rcv_event(notification); self.rcv_event(notification);
@ -775,11 +775,12 @@ impl State {
{ {
a a
} else { } else {
self.context.replies.push_back(UIEvent::Notification( self.context.replies.push_back(UIEvent::Notification {
None, title: None,
format!("Account {} was not found.", account_name), source: None,
Some(NotificationType::Error(ErrorKind::None)), body: format!("Account {account_name} was not found.").into(),
)); kind: Some(NotificationType::Error(ErrorKind::None)),
});
return; return;
}; };
if *self.context.accounts[account_index] if *self.context.accounts[account_index]
@ -788,14 +789,15 @@ impl State {
.search_backend() .search_backend()
!= crate::conf::SearchBackend::Sqlite3 != crate::conf::SearchBackend::Sqlite3
{ {
self.context.replies.push_back(UIEvent::Notification( self.context.replies.push_back(UIEvent::Notification {
None, title: None,
format!( source: None,
"Account {} doesn't have an sqlite3 search backend.", body: format!(
account_name "Account {account_name} doesn't have an sqlite3 search backend.",
), )
Some(NotificationType::Error(ErrorKind::None)), .into(),
)); kind: Some(NotificationType::Error(ErrorKind::None)),
});
return; return;
} }
match crate::sqlite3::index(&mut self.context, account_index) { match crate::sqlite3::index(&mut self.context, account_index) {
@ -814,29 +816,32 @@ impl State {
log_level: LogLevel::INFO, log_level: LogLevel::INFO,
}, },
); );
self.context.replies.push_back(UIEvent::Notification( self.context.replies.push_back(UIEvent::Notification {
None, title: None,
"Message index rebuild started.".to_string(), source: None,
Some(NotificationType::Info), body: "Message index rebuild started.".into(),
)); kind: Some(NotificationType::Info),
});
} }
Err(err) => { Err(err) => {
self.context.replies.push_back(UIEvent::Notification( self.context.replies.push_back(UIEvent::Notification {
Some("Message index rebuild failed".to_string()), title: Some("Message index rebuild failed".into()),
err.to_string(), source: None,
Some(NotificationType::Error(err.kind)), body: err.to_string().into(),
)); kind: Some(NotificationType::Error(err.kind)),
});
} }
} }
} }
#[cfg(not(feature = "sqlite3"))] #[cfg(not(feature = "sqlite3"))]
AccountAction(_, ReIndex) => { AccountAction(_, ReIndex) => {
self.context.replies.push_back(UIEvent::Notification( self.context.replies.push_back(UIEvent::Notification {
None, title: None,
"Message index rebuild failed: meli is not built with sqlite3 support." source: None,
body: "Message index rebuild failed: meli is not built with sqlite3 support."
.to_string(), .to_string(),
Some(NotificationType::Error(ErrorKind::None)), kind: Some(NotificationType::Error(ErrorKind::None)),
)); });
} }
AccountAction(ref account_name, PrintAccountSetting(ref setting)) => { AccountAction(ref account_name, PrintAccountSetting(ref setting)) => {
let path = setting.split('.').collect::<SmallVec<[&str; 16]>>(); let path = setting.split('.').collect::<SmallVec<[&str; 16]>>();
@ -855,11 +860,12 @@ impl State {
), ),
)); ));
} else { } else {
self.context.replies.push_back(UIEvent::Notification( self.context.replies.push_back(UIEvent::Notification {
None, title: None,
format!("Account {} was not found.", account_name), source: None,
Some(NotificationType::Error(ErrorKind::None)), body: format!("Account {account_name} was not found.").into(),
)); kind: Some(NotificationType::Error(ErrorKind::None)),
});
} }
} }
PrintSetting(ref setting) => { PrintSetting(ref setting) => {

@ -424,11 +424,12 @@ impl Component for SVGScreenshotFilter {
.unwrap() .unwrap()
.write_all(&res) .write_all(&res)
.unwrap(); .unwrap();
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Screenshot saved".into()), title: Some("Screenshot saved".into()),
format!("Screenshot saved to {}", filename), source: None,
None, body: format!("Screenshot saved to {filename}").into(),
)); kind: None,
});
} }
fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool { fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool {
if let UIEvent::Input(Key::F(6)) = event { if let UIEvent::Input(Key::F(6)) = event {

@ -41,6 +41,7 @@ use std::{borrow::Cow, sync::Arc};
pub use helpers::*; pub use helpers::*;
use melib::{ use melib::{
backends::{AccountHash, BackendEvent, MailboxHash}, backends::{AccountHash, BackendEvent, MailboxHash},
error::Error,
EnvelopeHash, RefreshEvent, ThreadHash, EnvelopeHash, RefreshEvent, ThreadHash,
}; };
use nix::unistd::Pid; use nix::unistd::Pid;
@ -137,7 +138,12 @@ pub enum UIEvent {
ChangeMailbox(usize), ChangeMailbox(usize),
ChangeMode(UIMode), ChangeMode(UIMode),
Command(String), Command(String),
Notification(Option<String>, String, Option<NotificationType>), Notification {
title: Option<Cow<'static, str>>,
source: Option<Error>,
body: Cow<'static, str>,
kind: Option<NotificationType>,
},
Action(Action), Action(Action),
StatusEvent(StatusEvent), StatusEvent(StatusEvent),
MailboxUpdate((AccountHash, MailboxHash)), // (account_idx, mailbox_idx) MailboxUpdate((AccountHash, MailboxHash)), // (account_idx, mailbox_idx)

@ -185,11 +185,12 @@ impl Component for EmbeddedContainer {
))); )));
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some(format!("Failed to create pseudoterminal: {}", err)), title: Some("Failed to create pseudoterminal".into()),
err.to_string(), source: None,
Some(NotificationType::Error(melib::error::ErrorKind::External)), body: err.to_string().into(),
)); kind: Some(NotificationType::Error(melib::error::ErrorKind::External)),
});
} }
} }
} }
@ -225,16 +226,17 @@ impl Component for EmbeddedContainer {
drop(embedded_pty_guard); drop(embedded_pty_guard);
_ = self.embedded_pty.take(); _ = self.embedded_pty.take();
if exit_code != 0 { if exit_code != 0 {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
format!( source: None,
"Subprocess has exited with exit code {}", body: format!(
exit_code "Subprocess has exited with exit code {exit_code}",
), )
Some(NotificationType::Error( .into(),
kind: Some(NotificationType::Error(
melib::error::ErrorKind::External, melib::error::ErrorKind::External,
)), )),
)); });
} }
self.set_dirty(true); self.set_dirty(true);
context context
@ -281,26 +283,29 @@ impl Component for EmbeddedContainer {
} }
Ok(WaitStatus::Signaled(_, signal, _)) => { Ok(WaitStatus::Signaled(_, signal, _)) => {
drop(embedded_pty_guard); drop(embedded_pty_guard);
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
format!("Subprocess was killed by {} signal", signal), source: None,
Some(NotificationType::Error( body: format!("Subprocess was killed by {signal} signal")
.into(),
kind: Some(NotificationType::Error(
melib::error::ErrorKind::External, melib::error::ErrorKind::External,
)), )),
)); });
self.embedded_pty = None; self.embedded_pty = None;
context context
.replies .replies
.push_back(UIEvent::ChangeMode(UIMode::Normal)); .push_back(UIEvent::ChangeMode(UIMode::Normal));
} }
Err(err) => { Err(err) => {
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
Some("Embedded editor crashed.".to_string()), title: Some("Embedded editor crashed.".into()),
format!("Subprocess has exited with reason {}", &err), source: None,
Some(NotificationType::Error( body: format!("Subprocess has exited with reason {err}").into(),
kind: Some(NotificationType::Error(
melib::error::ErrorKind::External, melib::error::ErrorKind::External,
)), )),
)); });
drop(embedded_pty_guard); drop(embedded_pty_guard);
self.embedded_pty = None; self.embedded_pty = None;
context context
@ -348,11 +353,12 @@ impl Component for EmbeddedContainer {
} }
_ => {} _ => {}
} }
context.replies.push_back(UIEvent::Notification( context.replies.push_back(UIEvent::Notification {
None, title: None,
"Subprocess was killed by SIGTERM signal".to_string(), source: None,
Some(NotificationType::Error(melib::error::ErrorKind::External)), body: "Subprocess was killed by SIGTERM signal".into(),
)); kind: Some(NotificationType::Error(melib::error::ErrorKind::External)),
});
context context
.replies .replies
.push_back(UIEvent::ChangeMode(UIMode::Normal)); .push_back(UIEvent::ChangeMode(UIMode::Normal));

@ -19,7 +19,6 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
use futures::executor::block_on; use futures::executor::block_on;
use meli::*; use meli::*;
use melib::{imap::managesieve::ManageSieveConnection, Result, *}; use melib::{imap::managesieve::ManageSieveConnection, Result, *};

Loading…
Cancel
Save