ui: add manual_refresh, refresh_command settings

manual_refresh Ar boolean
  (optional) if true, do not monitor account for changes (shortcut listing.refresh)
  refresh_command Ar String
  (optional) command to execute when manually refreshing (shortcut listing.refresh)
memfd
Manos Pitsidianakis 2020-01-08 21:41:57 +02:00
parent 5e912db461
commit 86d8419ce7
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
6 changed files with 111 additions and 10 deletions

View File

@ -53,7 +53,7 @@ is not a comment:
The accepted regular expression is The accepted regular expression is
.Li ^\es*include\es*\&\\&\e"(\e\e.|[^\e"])+\e"\es*$ .Li ^\es*include\es*\&\\&\e"(\e\e.|[^\e"])+\e"\es*$
.Sh SECTIONS .Sh SECTIONS
The top level sections of the config are accounts, shortcuts, notifications, pager, composing, pgp, terminal. The top level sections of the config are accounts, shortcuts, notifications, pager, listing, composing, pgp, terminal.
.Pp .Pp
.Sy example configuration .Sy example configuration
@ -138,6 +138,12 @@ plain:shows one row per mail, regardless of threading
.It Ic read_only Ar boolean .It Ic read_only Ar boolean
attempt to not make any changes to this account. attempt to not make any changes to this account.
.Pq Em false .Pq Em false
.It Ic manual_refresh Ar boolean
(optional) if true, do not monitor account for changes (shortcut listing.refresh)
.Pq Em false
.It Ic refresh_command Ar String
(optional) command to execute when manually refreshing (shortcut listing.refresh)
.Pq Em None
.It Ic cache_type Ar String .It Ic cache_type Ar String
(optional) choose which cache backend to use. Available options are 'none' and 'sqlite3' (optional) choose which cache backend to use. Available options are 'none' and 'sqlite3'
.Pq Em "sqlite3" .Pq Em "sqlite3"
@ -351,6 +357,14 @@ Go to next account.
Start new mail draft in new tab Start new mail draft in new tab
.\" default value .\" default value
.Pq Em m .Pq Em m
.It Ic set_seen
Set thread as seen.
.\" default value
.Pq Em n
.It Ic refresh
Manually request a folder refresh.
.\" default value
.Pq Em F5
.It Ic search .It Ic search
Search within list of e-mails. Search within list of e-mails.
.\" default value .\" default value
@ -563,6 +577,21 @@ enable freedesktop-spec notifications. this is usually what you want
.\" default value .\" default value
.Pq Em 80 .Pq Em 80
.El .El
.Sh LISTING
.Bl -tag -width 36n
.It Ic context_lines Ar num
(optional) number of context lines when going to next page. (Unimplemented)
.\" default value
.Pq Em 0
.It Ic datetime_fmt Ar String
(optional) datetime formatting passed verbatim to strftime(3).
.\" default value
.Pq Em \&%Y-\&%m-\&%d \&%T
.It Ic recent_dates Ar Boolean
(optional) Show recent dates as `X {minutes,hours,days} ago`, up to 7 days.
.\" default value
.Pq Em true
.El
.Sh TAGS .Sh TAGS
.Bl -tag -width 36n .Bl -tag -width 36n
.It Ic colours Ar hash table String[Color] .It Ic colours Ar hash table String[Color]

View File

@ -591,7 +591,7 @@ impl CompactListing {
subject.truncate_at_boundary(150); subject.truncate_at_boundary(150);
if thread_node.len() > 0 { if thread_node.len() > 0 {
EntryStrings { EntryStrings {
date: DateString(ConversationsListing::format_date(thread_node)), date: DateString(ConversationsListing::format_date(context, thread_node)),
subject: SubjectString(format!("{} ({})", subject, thread_node.len(),)), subject: SubjectString(format!("{} ({})", subject, thread_node.len(),)),
flag: FlagString(format!( flag: FlagString(format!(
"{}{}", "{}{}",
@ -603,7 +603,7 @@ impl CompactListing {
} }
} else { } else {
EntryStrings { EntryStrings {
date: DateString(ConversationsListing::format_date(thread_node)), date: DateString(ConversationsListing::format_date(context, thread_node)),
subject: SubjectString(subject), subject: SubjectString(subject),
flag: FlagString(format!( flag: FlagString(format!(
"{}{}", "{}{}",

View File

@ -562,7 +562,7 @@ impl ConversationsListing {
subject.truncate_at_boundary(150); subject.truncate_at_boundary(150);
if thread_node.len() > 0 { if thread_node.len() > 0 {
EntryStrings { EntryStrings {
date: DateString(ConversationsListing::format_date(thread_node)), date: DateString(ConversationsListing::format_date(context, thread_node)),
subject: SubjectString(format!("{} ({})", subject, thread_node.len(),)), subject: SubjectString(format!("{} ({})", subject, thread_node.len(),)),
flag: FlagString(format!( flag: FlagString(format!(
"{}{}", "{}{}",
@ -574,7 +574,7 @@ impl ConversationsListing {
} }
} else { } else {
EntryStrings { EntryStrings {
date: DateString(ConversationsListing::format_date(thread_node)), date: DateString(ConversationsListing::format_date(context, thread_node)),
subject: SubjectString(subject), subject: SubjectString(subject),
flag: FlagString(format!( flag: FlagString(format!(
"{}{}", "{}{}",
@ -890,28 +890,37 @@ impl ConversationsListing {
} }
} }
pub(super) fn format_date(thread_node: &ThreadNode) -> String { pub(super) fn format_date(context: &Context, thread_node: &ThreadNode) -> String {
let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(thread_node.date()); let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(thread_node.date());
let now: std::time::Duration = std::time::SystemTime::now() let now: std::time::Duration = std::time::SystemTime::now()
.duration_since(d) .duration_since(d)
.unwrap_or_else(|_| std::time::Duration::new(std::u64::MAX, 0)); .unwrap_or_else(|_| std::time::Duration::new(std::u64::MAX, 0));
match now.as_secs() { match now.as_secs() {
n if n < 60 * 60 => format!( n if context.settings.listing.recent_dates && n < 60 * 60 => format!(
"{} minute{} ago", "{} minute{} ago",
n / (60), n / (60),
if n / 60 == 1 { "" } else { "s" } if n / 60 == 1 { "" } else { "s" }
), ),
n if n < 24 * 60 * 60 => format!( n if context.settings.listing.recent_dates && n < 24 * 60 * 60 => format!(
"{} hour{} ago", "{} hour{} ago",
n / (60 * 60), n / (60 * 60),
if n / (60 * 60) == 1 { "" } else { "s" } if n / (60 * 60) == 1 { "" } else { "s" }
), ),
n if n < 7 * 24 * 60 * 60 => format!( n if context.settings.listing.recent_dates && n < 7 * 24 * 60 * 60 => format!(
"{} day{} ago", "{} day{} ago",
n / (24 * 60 * 60), n / (24 * 60 * 60),
if n / (24 * 60 * 60) == 1 { "" } else { "s" } if n / (24 * 60 * 60) == 1 { "" } else { "s" }
), ),
_ => melib::datetime::timestamp_to_string(thread_node.date(), Some("%Y-%m-%d %T")), _ => melib::datetime::timestamp_to_string(
thread_node.date(),
context
.settings
.listing
.datetime_fmt
.as_ref()
.map(String::as_str)
.or(Some("%Y-%m-%d %T")),
),
} }
} }

View File

@ -31,6 +31,7 @@ pub mod pgp;
pub mod tags; pub mod tags;
#[macro_use] #[macro_use]
pub mod shortcuts; pub mod shortcuts;
mod listing;
pub mod terminal; pub mod terminal;
pub mod accounts; pub mod accounts;
@ -41,6 +42,7 @@ pub use self::shortcuts::*;
pub use self::tags::*; pub use self::tags::*;
use self::default_vals::*; use self::default_vals::*;
use self::listing::ListingSettings;
use self::notifications::NotificationsSettings; use self::notifications::NotificationsSettings;
use self::terminal::TerminalSettings; use self::terminal::TerminalSettings;
use crate::pager::PagerSettings; use crate::pager::PagerSettings;
@ -68,6 +70,7 @@ macro_rules! split_command {
#[derive(Default, Debug, Clone, Serialize, Deserialize)] #[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct MailUIConf { pub struct MailUIConf {
pub pager: Option<PagerSettings>, pub pager: Option<PagerSettings>,
pub listing: Option<ListingSettings>,
pub notifications: Option<NotificationsSettings>, pub notifications: Option<NotificationsSettings>,
pub shortcuts: Option<Shortcuts>, pub shortcuts: Option<Shortcuts>,
pub composing: Option<ComposingSettings>, pub composing: Option<ComposingSettings>,
@ -114,6 +117,8 @@ pub struct FileAccount {
cache_type: CacheType, cache_type: CacheType,
#[serde(default)] #[serde(default)]
pub manual_refresh: bool, pub manual_refresh: bool,
#[serde(default = "none")]
pub refresh_command: Option<String>,
#[serde(flatten)] #[serde(flatten)]
#[serde(deserialize_with = "extra_settings")] #[serde(deserialize_with = "extra_settings")]
pub extra: HashMap<String, String>, /* use custom deserializer to convert any given value (eg bool, number, etc) to string */ pub extra: HashMap<String, String>, /* use custom deserializer to convert any given value (eg bool, number, etc) to string */
@ -230,6 +235,8 @@ pub struct FileSettings {
#[serde(default)] #[serde(default)]
pager: PagerSettings, pager: PagerSettings,
#[serde(default)] #[serde(default)]
listing: ListingSettings,
#[serde(default)]
notifications: NotificationsSettings, notifications: NotificationsSettings,
#[serde(default)] #[serde(default)]
shortcuts: Shortcuts, shortcuts: Shortcuts,
@ -267,6 +274,7 @@ impl AccountConf {
pub struct Settings { pub struct Settings {
pub accounts: HashMap<String, AccountConf>, pub accounts: HashMap<String, AccountConf>,
pub pager: PagerSettings, pub pager: PagerSettings,
pub listing: ListingSettings,
pub notifications: NotificationsSettings, pub notifications: NotificationsSettings,
pub shortcuts: Shortcuts, pub shortcuts: Shortcuts,
pub tags: TagsSettings, pub tags: TagsSettings,
@ -359,6 +367,7 @@ impl FileSettings {
folders, folders,
extra, extra,
manual_refresh, manual_refresh,
refresh_command: _,
index_style: _, index_style: _,
cache_type: _, cache_type: _,
} = acc; } = acc;
@ -401,6 +410,7 @@ impl Settings {
Ok(Settings { Ok(Settings {
accounts: s, accounts: s,
pager: fs.pager, pager: fs.pager,
listing: fs.listing,
notifications: fs.notifications, notifications: fs.notifications,
shortcuts: fs.shortcuts, shortcuts: fs.shortcuts,
tags: fs.tags, tags: fs.tags,

View File

@ -723,6 +723,18 @@ impl Account {
None None
} }
pub fn refresh(&mut self, folder_hash: FolderHash) -> Result<()> { pub fn refresh(&mut self, folder_hash: FolderHash) -> Result<()> {
if let Some(ref refresh_command) = self.settings.conf().refresh_command {
let parts = crate::split_command!(refresh_command);
let (cmd, args) = (parts[0], &parts[1..]);
std::process::Command::new(cmd)
.args(args)
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::piped())
.spawn()?;
return Ok(());
}
let sender_ = self.sender.clone(); let sender_ = self.sender.clone();
let r = RefreshEventConsumer::new(Box::new(move |r| { let r = RefreshEventConsumer::new(Box::new(move |r| {
sender_.send(ThreadEvent::from(r)).unwrap(); sender_.send(ThreadEvent::from(r)).unwrap();

View File

@ -0,0 +1,41 @@
/*
* meli - listing conf module
*
* Copyright 2020 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use super::default_vals::*;
/// Settings for mail listings
#[derive(Debug, Deserialize, Clone, Default, Serialize)]
pub struct ListingSettings {
/// Number of context lines when going to next page.
/// Default: 0
#[serde(default = "zero_val")]
pub context_lines: usize,
/// Datetime formatting passed verbatim to strftime(3).
/// Default: %Y-%m-%d %T
#[serde(default = "none")]
pub datetime_fmt: Option<String>,
/// Show recent dates as `X {minutes,hours,days} ago`, up to 7 days.
/// Default: true
#[serde(default = "true_val")]
pub recent_dates: bool,
}