Implement iPhone and Apple Watch preferences (#53)

This commit is contained in:
2026-08-15 22:37:31 +02:00
parent 81886756a2
commit affdb55519
15 changed files with 1409 additions and 60 deletions

View File

@@ -40,6 +40,7 @@ pub struct Config {
clipboard_timeout: ClipboardTimeout,
authentication_timeout: AuthenticationTimeout,
biometric_unlock_enabled: bool,
mobile_appearance: MobileAppearance,
mobile_tab: MobileTab,
mobile_home_refreshed_at: Option<i64>,
watch_shared_totp_entries: BTreeSet<EntryPath>,
@@ -60,6 +61,35 @@ pub struct ConfigSettings {
authentication_timeout: Duration,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum MobileAppearance {
#[default]
System,
Light,
Dark,
}
impl MobileAppearance {
fn from_config(value: &str) -> Result<Self, ConfigError> {
match value {
"system" => Ok(Self::System),
"light" => Ok(Self::Light),
"dark" => Ok(Self::Dark),
_ => Err(ConfigError::InvalidField {
field: "ui.mobile_appearance",
}),
}
}
const fn config_value(self) -> &'static str {
match self {
Self::System => "system",
Self::Light => "light",
Self::Dark => "dark",
}
}
}
impl ConfigSettings {
pub fn vault(&self) -> &Path {
&self.vault
@@ -133,6 +163,10 @@ impl Config {
self.biometric_unlock_enabled
}
pub fn mobile_appearance(&self) -> MobileAppearance {
self.mobile_appearance
}
pub fn mobile_tab(&self) -> MobileTab {
self.mobile_tab
}
@@ -280,6 +314,66 @@ impl Config {
validate_config(self.source.clone(), document, raw)?.persist()
}
pub fn update_authentication_timeout(
&self,
timeout: AuthenticationTimeout,
) -> Result<(), ConfigError> {
let mut document = self.current_document()?;
let root = document
.as_table_mut()
.ok_or_else(|| ConfigError::Malformed {
path: self.source.clone(),
})?;
let security = root
.entry("security")
.or_insert_with(|| toml::Value::Table(toml::Table::new()))
.as_table_mut()
.ok_or(ConfigError::InvalidField { field: "security" })?;
let seconds =
i64::try_from(timeout.duration().as_secs()).map_err(|_| ConfigError::InvalidField {
field: "security.inactivity_timeout_seconds",
})?;
security.insert(
"inactivity_timeout_seconds".to_owned(),
toml::Value::Integer(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 fn update_mobile_appearance(
&self,
appearance: MobileAppearance,
) -> Result<(), ConfigError> {
let mut document = self.current_document()?;
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(
"mobile_appearance".to_owned(),
toml::Value::String(appearance.config_value().to_owned()),
);
let raw = document
.clone()
.try_into::<RawConfig>()
.map_err(|_| ConfigError::Malformed {
path: self.source.clone(),
})?;
validate_config(self.source.clone(), document, raw)?.persist()
}
pub fn update_git_identity(&self, identity: &GitIdentity) -> Result<(), ConfigError> {
let mut document = self.current_document()?;
let root = document
@@ -952,6 +1046,7 @@ struct RawSecurity {
#[serde(deny_unknown_fields)]
struct RawUi {
selected_mobile_tab: Option<String>,
mobile_appearance: Option<String>,
home_remote_refreshed_at_unix_seconds: Option<i64>,
#[serde(default)]
watch_shared_totp_entries: Vec<String>,
@@ -1032,6 +1127,13 @@ fn validate_config(
field: "security.inactivity_timeout_seconds",
})?;
let biometric_unlock_enabled = raw.security.biometric_unlock_enabled.unwrap_or(false);
let mobile_appearance = raw
.ui
.mobile_appearance
.as_deref()
.map(MobileAppearance::from_config)
.transpose()?
.unwrap_or_default();
let mobile_tab = raw
.ui
.selected_mobile_tab
@@ -1083,6 +1185,7 @@ fn validate_config(
clipboard_timeout,
authentication_timeout,
biometric_unlock_enabled,
mobile_appearance,
mobile_tab,
mobile_home_refreshed_at,
watch_shared_totp_entries,
@@ -1270,6 +1373,7 @@ fn validate_known_fields(value: &toml::Value, source: &Path) -> Result<(), Confi
"ui",
&[
"selected_mobile_tab",
"mobile_appearance",
"home_remote_refreshed_at_unix_seconds",
"watch_shared_totp_entries",
],