summaryrefslogtreecommitdiff
path: root/src/components/mail/compose.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/mail/compose.rs')
-rw-r--r--src/components/mail/compose.rs609
1 files changed, 514 insertions, 95 deletions
diff --git a/src/components/mail/compose.rs b/src/components/mail/compose.rs
index c5f3e53f..31cbf5b5 100644
--- a/src/components/mail/compose.rs
+++ b/src/components/mail/compose.rs
@@ -84,8 +84,8 @@ pub struct Composer {
embed_area: Area,
embed: Option<EmbedStatus>,
- sign_mail: ToggleFlag,
- encrypt_mail: ToggleFlag,
+ #[cfg(feature = "gpgme")]
+ gpg_state: gpg::GpgComposeState,
dirty: bool,
has_changes: bool,
initialized: bool,
@@ -107,8 +107,8 @@ impl Default for Composer {
form: FormWidget::default(),
mode: ViewMode::Edit,
- sign_mail: ToggleFlag::Unset,
- encrypt_mail: ToggleFlag::Unset,
+ #[cfg(feature = "gpgme")]
+ gpg_state: gpg::GpgComposeState::new(),
dirty: true,
has_changes: false,
embed_area: ((0, 0), (0, 0)),
@@ -125,6 +125,8 @@ enum ViewMode {
Edit,
Embed,
SelectRecipients(UIDialog<Address>),
+ #[cfg(feature = "gpgme")]
+ SelectEncryptKey(bool, gpg::KeySelection),
Send(UIConfirmationDialog),
WaitingForSendResult(UIDialog<char>, JoinHandle<Result<()>>),
}
@@ -430,14 +432,23 @@ impl Composer {
let attachments_no = self.draft.attachments().len();
let theme_default = crate::conf::value(context, "theme_default");
clear_area(grid, area, theme_default);
- if self.sign_mail.is_true() {
+ #[cfg(feature = "gpgme")]
+ if self.gpg_state.sign_mail.is_true() {
+ let key_list = self
+ .gpg_state
+ .sign_keys
+ .iter()
+ .map(|k| k.fingerprint())
+ .collect::<Vec<_>>()
+ .join(", ");
write_string_to_grid(
&format!(
"☑ sign with {}",
- account_settings!(context[self.account_hash].pgp.sign_key)
- .as_ref()
- .map(|s| s.as_str())
- .unwrap_or("default key")
+ if self.gpg_state.sign_keys.is_empty() {
+ "default key"
+ } else {
+ key_list.as_str()
+ }
),
grid,
theme_default.fg,
@@ -465,14 +476,29 @@ impl Composer {
None,
);
}
- if self.encrypt_mail.is_true() {
+ #[cfg(feature = "gpgme")]
+ if self.gpg_state.encrypt_mail.is_true() {
+ let key_list = self
+ .gpg_state
+ .encrypt_keys
+ .iter()
+ .map(|k| k.fingerprint())
+ .collect::<Vec<_>>()
+ .join(", ");
+
write_string_to_grid(
&format!(
- "☑ encrypt with {}",
- account_settings!(context[self.account_hash].pgp.encrypt_key)
- .as_ref()
- .map(|s| s.as_str())
- .unwrap_or("default key")
+ "{}{}",
+ if self.gpg_state.encrypt_keys.is_empty() {
+ "☐ no keys to encrypt with!"
+ } else {
+ "☑ encrypt with "
+ },
+ if self.gpg_state.encrypt_keys.is_empty() {
+ ""
+ } else {
+ key_list.as_str()
+ }
),
grid,
theme_default.fg,
@@ -575,8 +601,9 @@ impl Component for Composer {
let width = width!(area);
if !self.initialized {
- if self.sign_mail.is_unset() {
- self.sign_mail = ToggleFlag::InternalVal(*account_settings!(
+ #[cfg(feature = "gpgme")]
+ if self.gpg_state.sign_mail.is_unset() {
+ self.gpg_state.sign_mail = ToggleFlag::InternalVal(*account_settings!(
context[self.account_hash].pgp.auto_sign
));
}
@@ -752,6 +779,18 @@ impl Component for Composer {
ViewMode::Send(ref mut s) => {
s.draw(grid, center_area(area, s.content.size()), context);
}
+ #[cfg(feature = "gpgme")]
+ ViewMode::SelectEncryptKey(
+ _,
+ gpg::KeySelection::Loaded {
+ ref mut widget,
+ keys: _,
+ },
+ ) => {
+ widget.draw(grid, center_area(area, widget.content.size()), context);
+ }
+ #[cfg(feature = "gpgme")]
+ ViewMode::SelectEncryptKey(_, _) => {}
ViewMode::SelectRecipients(ref mut s) => {
s.draw(grid, center_area(area, s.content.size()), context);
}
@@ -783,8 +822,8 @@ impl Component for Composer {
if let Some(true) = result.downcast_ref::<bool>() {
self.update_draft();
match send_draft_async(
- self.sign_mail,
- self.encrypt_mail,
+ #[cfg(feature = "gpgme")]
+ self.gpg_state.clone(),
context,
self.account_hash,
self.draft.clone(),
@@ -846,6 +885,14 @@ impl Component for Composer {
self.mode = ViewMode::Edit;
self.set_dirty(true);
}
+ #[cfg(feature = "gpgme")]
+ (ViewMode::SelectEncryptKey(_, ref mut selector), UIEvent::ComponentKill(ref id))
+ if *id == selector.id() =>
+ {
+ self.mode = ViewMode::Edit;
+ self.set_dirty(true);
+ return true;
+ }
(ViewMode::Send(ref mut selector), _) => {
if selector.process_event(event, context) {
return true;
@@ -954,6 +1001,34 @@ impl Component for Composer {
return true;
}
}
+ #[cfg(feature = "gpgme")]
+ (
+ ViewMode::SelectEncryptKey(is_encrypt, ref mut selector),
+ UIEvent::FinishedUIDialog(id, result),
+ ) if *id == selector.id() => {
+ debug!(&result);
+ if let Some(key) = result.downcast_mut::<Option<melib::gpgme::Key>>() {
+ debug!("got key {:?}", key);
+ if let Some(key) = key {
+ if *is_encrypt {
+ self.gpg_state.encrypt_keys.clear();
+ self.gpg_state.encrypt_keys.push(key.clone());
+ } else {
+ self.gpg_state.sign_keys.clear();
+ self.gpg_state.sign_keys.push(key.clone());
+ }
+ }
+ }
+ self.mode = ViewMode::Edit;
+ self.set_dirty(true);
+ return true;
+ }
+ #[cfg(feature = "gpgme")]
+ (ViewMode::SelectEncryptKey(_, ref mut selector), _) => {
+ if selector.process_event(event, context) {
+ return true;
+ }
+ }
_ => {}
}
if self.cursor == Cursor::Headers
@@ -1025,14 +1100,15 @@ impl Component for Composer {
if self.mode.is_edit()
&& (self.cursor == Cursor::Sign || self.cursor == Cursor::Encrypt) =>
{
+ #[cfg(feature = "gpgme")]
match self.cursor {
Cursor::Sign => {
- let is_true = self.sign_mail.is_true();
- self.sign_mail = ToggleFlag::from(!is_true);
+ let is_true = self.gpg_state.sign_mail.is_true();
+ self.gpg_state.sign_mail = ToggleFlag::from(!is_true);
}
Cursor::Encrypt => {
- let is_true = self.encrypt_mail.is_true();
- self.encrypt_mail = ToggleFlag::from(!is_true);
+ let is_true = self.gpg_state.encrypt_mail.is_true();
+ self.gpg_state.encrypt_mail = ToggleFlag::from(!is_true);
}
_ => {}
};
@@ -1195,6 +1271,86 @@ impl Component for Composer {
return true;
}
UIEvent::Input(ref key)
+ if self.mode.is_edit()
+ && self.cursor == Cursor::Sign
+ && shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) =>
+ {
+ #[cfg(feature = "gpgme")]
+ match melib::email::parser::address::rfc2822address_list(
+ self.form.values()["From"].as_str().as_bytes(),
+ )
+ .map_err(|_err| -> MeliError { "No valid sender address in `From:`".into() })
+ .and_then(|(_, list)| {
+ list.get(0)
+ .cloned()
+ .ok_or_else(|| "No valid sender address in `From:`".into())
+ })
+ .and_then(|addr| {
+ gpg::KeySelection::new(
+ false,
+ account_settings!(context[self.account_hash].pgp.allow_remote_lookup)
+ .is_true(),
+ addr.get_email(),
+ *account_settings!(context[self.account_hash].pgp.allow_remote_lookup),
+ context,
+ )
+ }) {
+ Ok(widget) => {
+ self.gpg_state.sign_mail = ToggleFlag::from(true);
+ self.mode = ViewMode::SelectEncryptKey(false, widget);
+ }
+ Err(err) => {
+ context.replies.push_back(UIEvent::Notification(
+ Some("Could not list keys.".to_string()),
+ format!("libgpgme error: {}", &err),
+ Some(NotificationType::Error(melib::error::ErrorKind::External)),
+ ));
+ }
+ }
+ self.set_dirty(true);
+ return true;
+ }
+ UIEvent::Input(ref key)
+ if self.mode.is_edit()
+ && self.cursor == Cursor::Encrypt
+ && shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) =>
+ {
+ #[cfg(feature = "gpgme")]
+ match melib::email::parser::address::rfc2822address_list(
+ self.form.values()["To"].as_str().as_bytes(),
+ )
+ .map_err(|_err| -> MeliError { "No valid recipient addresses in `To:`".into() })
+ .and_then(|(_, list)| {
+ list.get(0)
+ .cloned()
+ .ok_or_else(|| "No valid recipient addresses in `To:`".into())
+ })
+ .and_then(|addr| {
+ gpg::KeySelection::new(
+ false,
+ account_settings!(context[self.account_hash].pgp.allow_remote_lookup)
+ .is_true(),
+ addr.get_email(),
+ *account_settings!(context[self.account_hash].pgp.allow_remote_lookup),
+ context,
+ )
+ }) {
+ Ok(widget) => {
+ self.gpg_state.encrypt_mail = ToggleFlag::from(true);
+ self.mode = ViewMode::SelectEncryptKey(true, widget);
+ }
+ Err(err) => {
+ context.replies.push_back(UIEvent::Notification(
+ Some("Could not list keys.".to_string()),
+ format!("libgpgme error: {}", &err),
+ Some(NotificationType::Error(melib::error::ErrorKind::External)),
+ ));
+ }
+ }
+ self.set_dirty(true);
+ return true;
+ }
+ UIEvent::Input(ref key)
if self.embed.is_some()
&& shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) =>
{
@@ -1498,15 +1654,17 @@ impl Component for Composer {
);
return true;
}
+ #[cfg(feature = "gpgme")]
Action::Compose(ComposeAction::ToggleSign) => {
- let is_true = self.sign_mail.is_true();
- self.sign_mail = ToggleFlag::from(!is_true);
+ let is_true = self.gpg_state.sign_mail.is_true();
+ self.gpg_state.sign_mail = ToggleFlag::from(!is_true);
self.dirty = true;
return true;
}
+ #[cfg(feature = "gpgme")]
Action::Compose(ComposeAction::ToggleEncrypt) => {
- let is_true = self.encrypt_mail.is_true();
- self.encrypt_mail = ToggleFlag::from(!is_true);
+ let is_true = self.gpg_state.encrypt_mail.is_true();
+ self.gpg_state.encrypt_mail = ToggleFlag::from(!is_true);
self.dirty = true;
return true;
}
@@ -1527,6 +1685,10 @@ impl Component for Composer {
ViewMode::SelectRecipients(ref widget) => {
widget.is_dirty() || self.pager.is_dirty() || self.form.is_dirty()
}
+ #[cfg(feature = "gpgme")]
+ ViewMode::SelectEncryptKey(_, ref widget) => {
+ widget.is_dirty() || self.pager.is_dirty() || self.form.is_dirty()
+ }
ViewMode::Send(ref widget) => {
widget.is_dirty() || self.pager.is_dirty() || self.form.is_dirty()
}
@@ -1626,7 +1788,7 @@ impl Component for Composer {
}
pub fn send_draft(
- sign_mail: ToggleFlag,
+ _sign_mail: ToggleFlag,
context: &mut Context,
account_hash: AccountHash,
mut draft: Draft,
@@ -1635,7 +1797,7 @@ pub fn send_draft(
complete_in_background: bool,
) -> Result<Option<JoinHandle<Result<()>>>> {
let format_flowed = *account_settings!(context[account_hash].composing.format_flowed);
- if sign_mail.is_true() {
+ /* if sign_mail.is_true() {
let mut content_type = ContentType::default();
if format_flowed {
if let ContentType::Text {
@@ -1667,41 +1829,44 @@ pub fn send_draft(
)
.into();
}
- let output = crate::components::mail::pgp::sign(
- body.into(),
- account_settings!(context[account_hash].pgp.gpg_binary)
- .as_ref()
- .map(|s| s.as_str()),
- account_settings!(context[account_hash].pgp.sign_key)
- .as_ref()
- .map(|s| s.as_str()),
- );
- match output {
- Err(err) => {
- debug!("{:?} could not sign draft msg", err);
- log(
- format!(
- "Could not sign draft in account `{}`: {}.",
- context.accounts[&account_hash].name(),
- err.to_string()
- ),
- ERROR,
- );
- context.replies.push_back(UIEvent::Notification(
- Some(format!(
- "Could not sign draft in account `{}`.",
- context.accounts[&account_hash].name()
- )),
- err.to_string(),
- Some(NotificationType::Error(err.kind)),
- ));
- return Err(err);
- }
- Ok(output) => {
- draft.attachments.push(output);
- }
+ let output = todo!();
+ crate::components::mail::pgp::sign(
+ body.into(),
+ account_settings!(context[account_hash].pgp.gpg_binary)
+ .as_ref()
+ .map(|s| s.as_str()),
+ account_settings!(context[account_hash].pgp.sign_key)
+ .as_ref()
+ .map(|s| s.as_str()),
+ );
+ match output {
+ Err(err) => {
+ debug!("{:?} could not sign draft msg", err);
+ log(
+ format!(
+ "Could not sign draft in account `{}`: {}.",
+ context.accounts[&account_hash].name(),
+ err.to_string()
+ ),
+ ERROR,
+ );
+ context.replies.push_back(UIEvent::Notification(
+ Some(format!(
+ "Could not sign draft in account `{}`.",
+ context.accounts[&account_hash].name()
+ )),
+ err.to_string(),
+ Some(NotificationType::Error(err.kind)),
+ ));
+ return Err(err);
+ }
+ Ok(output) => {
+ draft.attachments.push(output);
}
+ }
} else {
+ */
+ {
let mut content_type = ContentType::default();
if format_flowed {
if let ContentType::Text {
@@ -1762,8 +1927,7 @@ pub fn save_draft(
}
pub fn send_draft_async(
- sign_mail: ToggleFlag,
- encrypt_mail: ToggleFlag,
+ #[cfg(feature = "gpgme")] gpg_state: gpg::GpgComposeState,
context: &mut Context,
account_hash: AccountHash,
mut draft: Draft,
@@ -1772,6 +1936,7 @@ pub fn send_draft_async(
) -> Result<Pin<Box<dyn Future<Output = Result<()>> + Send>>> {
let format_flowed = *account_settings!(context[account_hash].composing.format_flowed);
let event_sender = context.sender.clone();
+ #[cfg(feature = "gpgme")]
let mut filters_stack: Vec<
Box<
dyn FnOnce(
@@ -1781,40 +1946,19 @@ pub fn send_draft_async(
+ Send,
>,
> = vec![];
- if sign_mail.is_true() {
+ #[cfg(feature = "gpgme")]
+ if gpg_state.sign_mail.is_true() && !gpg_state.encrypt_mail.is_true() {
filters_stack.push(Box::new(crate::components::mail::pgp::sign_filter(
- account_settings!(context[account_hash].pgp.gpg_binary)
- .as_ref()
- .map(|s| s.to_string()),
- account_settings!(context[account_hash].pgp.sign_key)
- .as_ref()
- .map(|s| s.to_string()),
+ gpg_state.sign_keys.clone(),
)?));
- }
- if encrypt_mail.is_true() {
- let mut recipients = vec![];
- if let Ok((_, v)) =
- melib::email::parser::address::rfc2822address_list(draft.headers()["To"].as_bytes())
- {
- for addr in v {
- recipients.push(addr.get_email());
- }
- }
- if let Ok((_, v)) =
- melib::email::parser::address::rfc2822address_list(draft.headers()["Cc"].as_bytes())
- {
- for addr in v {
- recipients.push(addr.get_email());
- }
- }
+ } else if gpg_state.encrypt_mail.is_true() {
filters_stack.push(Box::new(crate::components::mail::pgp::encrypt_filter(
- account_settings!(context[account_hash].pgp.gpg_binary)
- .as_ref()
- .map(|s| s.to_string()),
- account_settings!(context[account_hash].pgp.encrypt_key)
- .as_ref()
- .map(|s| s.to_string()),
- recipients,
+ if gpg_state.sign_mail.is_true() {
+ Some(gpg_state.sign_keys.clone())
+ } else {
+ None
+ },
+ gpg_state.encrypt_keys.clone(),
)?));
}
let send_mail = account_settings!(context[account_hash].composing.send_mail).clone();
@@ -1850,6 +1994,7 @@ pub fn send_draft_async(
.into();
}
Ok(Box::pin(async move {
+ #[cfg(feature = "gpgme")]
for f in filters_stack {
body = f(body).await?;
}
@@ -1882,3 +2027,277 @@ pub fn send_draft_async(
ret
}))
}
+
+#[cfg(feature = "gpgme")]
+mod gpg {
+ use super::*;
+
+ #[derive(Debug)]
+ pub enum KeySelection {
+ LoadingKeys {
+ handle: JoinHandle<Result<Vec<melib::gpgme::Key>>>,
+ progress_spinner: ProgressSpinner,
+ secret: bool,
+ local: bool,
+ pattern: String,
+ allow_remote_lookup: ToggleFlag,
+ },
+ Error {
+ id: ComponentId,
+ err: MeliError,
+ },
+ Loaded {
+ widget: UIDialog<melib::gpgme::Key>,
+ keys: Vec<melib::gpgme::Key>,
+ },
+ }
+
+ impl std::fmt::Display for KeySelection {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "select pgp keys")
+ }
+ }
+
+ impl KeySelection {
+ pub fn new(
+ secret: bool,
+ local: bool,
+ pattern: String,
+ allow_remote_lookup: ToggleFlag,
+ context: &mut Context,
+ ) -> Result<Self> {
+ use melib::gpgme::*;
+ debug!("KeySelection::new");
+ debug!(&secret);
+ debug!(&local);
+ debug!(&pattern);
+ debug!(&allow_remote_lookup);
+ let mut ctx = Context::new()?;
+ if local {
+ ctx.set_auto_key_locate(LocateKey::LOCAL)?;
+ } else {
+ ctx.set_auto_key_locate(LocateKey::WKD | LocateKey::LOCAL)?;
+ }
+ let job = ctx.keylist(secret, Some(pattern.clone()))?;
+ let handle = context.job_executor.spawn_specialized(job);
+ let mut progress_spinner = ProgressSpinner::new(8);
+ progress_spinner.start();
+ Ok(KeySelection::LoadingKeys {
+ handle,
+ secret,
+ local,
+ pattern,
+ allow_remote_lookup,
+ progress_spinner,
+ })
+ }
+ }
+
+ impl Component for KeySelection {
+ fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
+ match self {
+ KeySelection::LoadingKeys {
+ ref mut progress_spinner,
+ ..
+ } => progress_spinner.draw(grid, center_area(area, (2, 2)), context),
+ KeySelection::Error { ref err, .. } => {
+ let theme_default = crate::conf::value(context, "theme_default");
+ write_string_to_grid(
+ &err.to_string(),
+ grid,
+ theme_default.fg,
+ theme_default.bg,
+ theme_default.attrs,
+ center_area(area, (15, 2)),
+ Some(0),
+ );
+ }
+ KeySelection::Loaded { ref mut widget, .. } => {
+ widget.draw(grid, center_area(area, widget.content.size()), context)
+ }
+ }
+ }
+
+ fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
+ debug!(&self);
+ debug!(&event);
+ match self {
+ KeySelection::LoadingKeys {
+ ref mut progress_spinner,
+ ref mut handle,
+ secret,
+ local,
+ ref mut pattern,
+ allow_remote_lookup,
+ ..
+ } => match event {
+ UIEvent::StatusEvent(StatusEvent::JobFinished(ref id))
+ if *id == handle.job_id =>
+ {
+ match handle.chan.try_recv().unwrap().unwrap() {
+ Ok(keys) => {
+ if keys.is_empty() {
+ let id = progress_spinner.id();
+ if allow_remote_lookup.is_true() {
+ match Self::new(
+ *secret,
+ *local,
+ std::mem::replace(pattern, String::new()),
+ *allow_remote_lookup,
+ context,
+ ) {
+ Ok(w) => {
+ *self = w;
+ }
+ Err(err) => *self = KeySelection::Error { err, id },
+ }
+ } else if !*local && allow_remote_lookup.is_ask() {
+ *self = KeySelection::Error {
+ err: MeliError::new(format!(
+ "No keys found for {}, perform remote lookup?",
+ pattern
+ )),
+ id,
+ }
+ } else {
+ *self = KeySelection::Error {
+ err: MeliError::new(format!(
+ "No keys found for {}.",
+ pattern
+ )),
+ id,
+ }
+ }
+ if let KeySelection::Error { ref err, .. } = self {
+ context.replies.push_back(UIEvent::StatusEvent(
+ StatusEvent::DisplayMessage(err.to_string()),
+ ));
+ let res: Option<melib::gpgme::Key> = None;
+ context.replies.push_back(UIEvent::FinishedUIDialog(
+ id,
+ Box::new(res),
+ ));
+ }
+ return true;
+ }
+ let mut widget = UIDialog::new(
+ "select key",
+ keys.iter()
+ .map(|k| {
+ (
+ k.clone(),
+ if let Some(primary_uid) = k.primary_uid() {
+ format!("{} {}", k.fingerprint(), primary_uid)
+ } else {
+ k.fingerprint().to_string()
+ },
+ )
+ })
+ .collect::<Vec<(melib::gpgme::Key, String)>>(),
+ true,
+ Some(Box::new(
+ move |id: ComponentId, results: &[melib::gpgme::Key]| {
+ Some(UIEvent::FinishedUIDialog(
+ id,
+ Box::new(results.get(0).map(|k| k.clone())),
+ ))
+ },
+ )),
+ context,
+ );
+ widget.set_dirty(true);
+ *self = KeySelection::Loaded { widget, keys };
+ }
+ Err(err) => {
+ *self = KeySelection::Error {
+ err,
+ id: ComponentId::new_v4(),
+ };
+ }
+ }
+ true
+ }
+ _ => progress_spinner.process_event(event, context),
+ },
+ KeySelection::Error { .. } => false,
+ KeySelection::Loaded { ref mut widget, .. } => widget.process_event(event, context),
+ }
+ }
+
+ fn is_dirty(&self) -> bool {
+ match self {
+ KeySelection::LoadingKeys {
+ ref progress_spinner,
+ ..
+ } => progress_spinner.is_dirty(),
+ KeySelection::Error { .. } => true,
+ KeySelection::Loaded { ref widget, .. } => widget.is_dirty(),
+ }
+ }
+
+ fn set_dirty(&mut self, value: bool) {
+ match self {
+ KeySelection::LoadingKeys {
+ ref mut progress_spinner,
+ ..
+ } => progress_spinner.set_dirty(value),
+ KeySelection::Error { .. } => {}
+ KeySelection::Loaded { ref mut widget, .. } => widget.set_dirty(value),
+ }
+ }
+
+ fn kill(&mut self, _uuid: Uuid, _context: &mut Context) {}
+
+ fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
+ match self {
+ KeySelection::LoadingKeys { .. } | KeySelection::Error { .. } => {
+ ShortcutMaps::default()
+ }
+ KeySelection::Loaded { ref widget, .. } => widget.get_shortcuts(context),
+ }
+ }
+
+ fn id(&self) -> ComponentId {
+ match self {
+ KeySelection::LoadingKeys {
+ ref progress_spinner,
+ ..
+ } => progress_spinner.id(),
+ KeySelection::Error { ref id, .. } => *id,
+ KeySelection::Loaded { ref widget, .. } => widget.id(),
+ }
+ }
+
+ fn set_id(&mut self, new_id: ComponentId) {
+ match self {
+ KeySelection::LoadingKeys {
+ ref mut progress_spinner,
+ ..
+ } => progress_spinner.set_id(new_id),
+ KeySelection::Error { ref mut id, .. } => *id = new_id,
+ KeySelection::Loaded { ref mut widget, .. } => widget.set_id(new_id),
+ }
+ }
+ }
+
+ #[derive(Debug, Clone)]
+ pub struct GpgComposeState {
+ pub sign_mail: ToggleFlag,
+ pub encrypt_mail: ToggleFlag,
+ pub encrypt_keys: Vec<melib::gpgme::Key>,
+ pub encrypt_for_self: bool,
+ pub sign_keys: Vec<melib::gpgme::Key>,
+ }
+
+ impl GpgComposeState {
+ pub fn new() -> Self {
+ GpgComposeState {
+ sign_mail: ToggleFlag::Unset,
+ encrypt_mail: ToggleFlag::Unset,
+ encrypt_keys: vec![],
+ encrypt_for_self: true,
+ sign_keys: vec![],
+ }
+ }
+ }
+}