Show remote activity on iPhone Home

This commit is contained in:
2026-08-11 17:12:33 +02:00
parent daf122aff3
commit 8e883f5ccf
9 changed files with 3377 additions and 35 deletions

View File

@@ -36,6 +36,7 @@ pub struct Config {
clipboard_timeout: ClipboardTimeout,
authentication_timeout: AuthenticationTimeout,
mobile_tab: MobileTab,
mobile_home_refreshed_at: Option<i64>,
git_remotes: Vec<GitRemote>,
}
@@ -125,6 +126,10 @@ impl Config {
self.mobile_tab
}
pub fn mobile_home_refreshed_at(&self) -> Option<i64> {
self.mobile_home_refreshed_at
}
pub fn git_remotes(&self) -> &[GitRemote] {
&self.git_remotes
}
@@ -168,6 +173,36 @@ impl Config {
validate_config(self.source.clone(), document, raw)?.persist()
}
pub(crate) fn update_mobile_home_refresh(&self, unix_seconds: i64) -> Result<(), ConfigError> {
if unix_seconds <= 0 {
return Err(ConfigError::InvalidField {
field: "ui.home_remote_refreshed_at_unix_seconds",
});
}
let mut document = self.document.clone();
let root = document
.as_table_mut()
.ok_or_else(|| ConfigError::Malformed {
path: self.source.clone(),
})?;
let ui = root
.entry("ui")
.or_insert_with(|| toml::Value::Table(toml::Table::new()))
.as_table_mut()
.ok_or(ConfigError::InvalidField { field: "ui" })?;
ui.insert(
"home_remote_refreshed_at_unix_seconds".to_owned(),
toml::Value::Integer(unix_seconds),
);
let raw = document
.clone()
.try_into::<RawConfig>()
.map_err(|_| ConfigError::Malformed {
path: self.source.clone(),
})?;
validate_config(self.source.clone(), document, raw)?.persist()
}
pub(crate) fn create_mobile_clone(
source: PathBuf,
vault: &Path,
@@ -806,6 +841,7 @@ struct RawSecurity {
#[serde(deny_unknown_fields)]
struct RawUi {
selected_mobile_tab: Option<String>,
home_remote_refreshed_at_unix_seconds: Option<i64>,
}
#[derive(Deserialize)]
@@ -887,6 +923,15 @@ fn validate_config(
.map(MobileTab::from_config)
.transpose()?
.unwrap_or_default();
let mobile_home_refreshed_at = match raw.ui.home_remote_refreshed_at_unix_seconds {
Some(value) if value > 0 => Some(value),
Some(_) => {
return Err(ConfigError::InvalidField {
field: "ui.home_remote_refreshed_at_unix_seconds",
});
}
None => None,
};
let git_remotes = validate_remotes(raw.git.remotes)?;
Ok(Config {
@@ -899,6 +944,7 @@ fn validate_config(
clipboard_timeout,
authentication_timeout,
mobile_tab,
mobile_home_refreshed_at,
git_remotes,
})
}
@@ -1073,7 +1119,14 @@ fn validate_known_fields(value: &toml::Value, source: &Path) -> Result<(), Confi
let ui = ui.as_table().ok_or_else(|| ConfigError::Malformed {
path: source.to_owned(),
})?;
validate_table(ui, "ui", &["selected_mobile_tab"])?;
validate_table(
ui,
"ui",
&[
"selected_mobile_tab",
"home_remote_refreshed_at_unix_seconds",
],
)?;
}
let Some(git) = root.get("git") else {
return Ok(());
@@ -1215,6 +1268,8 @@ fn native_config_directory() -> Option<PathBuf> {
mod tests {
use std::fs;
use crate::mobile::MobileTab;
use super::{Config, ConfigError, GitRemote};
#[test]
@@ -1234,8 +1289,16 @@ mod tests {
"repository-example",
)
.expect("remote");
Config::create_mobile_clone(source.clone(), &vault, &keys, "ALICE", &remote)
let config = Config::create_mobile_clone(source.clone(), &vault, &keys, "ALICE", &remote)
.expect("create config");
assert_eq!(config.mobile_home_refreshed_at(), None);
config
.update_mobile_home_refresh(1_789_000_000)
.expect("persist refresh time");
Config::load(Some(&source))
.expect("reload timestamped config")
.update_mobile_tab(MobileTab::Preferences)
.expect("persist tab after refresh time");
let contents = fs::read_to_string(&source).expect("read config");
assert!(!contents.contains("token ="));
assert!(!contents.contains("password ="));
@@ -1251,12 +1314,15 @@ mod tests {
}
);
fs::rename(&original, &relocated).expect("relocate app container");
let relocated_config =
Config::load(Some(&relocated.join("config.toml"))).expect("reload config");
assert_eq!(
Config::load(Some(&relocated.join("config.toml")))
.expect("reload config")
.default_key()
.as_str(),
"ALICE"
(
relocated_config.default_key().as_str(),
relocated_config.mobile_home_refreshed_at(),
relocated_config.mobile_tab(),
),
("ALICE", Some(1_789_000_000), MobileTab::Preferences)
);
}
}