max / goingson
8 files changed,
+16 insertions,
-77 deletions
| @@ -2292,7 +2292,6 @@ | |||
| 2292 | 2292 | "synckit-config", | |
| 2293 | 2293 | "tauri", | |
| 2294 | 2294 | "tauri-build", | |
| 2295 | - | "tauri-plugin-biometric", | |
| 2296 | 2295 | "tauri-plugin-dialog", | |
| 2297 | 2296 | "tauri-plugin-haptics", | |
| 2298 | 2297 | "tauri-plugin-notification", | |
| @@ -6288,21 +6287,6 @@ | |||
| 6288 | 6287 | "walkdir", | |
| 6289 | 6288 | ] | |
| 6290 | 6289 | ||
| 6291 | - | [[package]] | |
| 6292 | - | name = "tauri-plugin-biometric" | |
| 6293 | - | version = "2.3.2" | |
| 6294 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 6295 | - | checksum = "306175b744890b5e4aeb8add6aae7debec1b1504c5087bd0310ed7c9c6feae38" | |
| 6296 | - | dependencies = [ | |
| 6297 | - | "log", | |
| 6298 | - | "serde", | |
| 6299 | - | "serde_json", | |
| 6300 | - | "serde_repr", | |
| 6301 | - | "tauri", | |
| 6302 | - | "tauri-plugin", | |
| 6303 | - | "thiserror 2.0.20", | |
| 6304 | - | ] | |
| 6305 | - | ||
| 6306 | 6290 | [[package]] | |
| 6307 | 6291 | name = "tauri-plugin-dialog" | |
| 6308 | 6292 | version = "2.7.2" |
| @@ -99,7 +99,6 @@ | |||
| 99 | 99 | # no-op, so they are gated to iOS/Android at the target level in src-tauri rather | |
| 100 | 100 | # than carried into the desktop binary and guarded at the call site. | |
| 101 | 101 | tauri-plugin-haptics = "2.3.2" | |
| 102 | - | tauri-plugin-biometric = "2.3.2" | |
| 103 | 102 | ||
| 104 | 103 | # Filesystem watching (db change notifications) | |
| 105 | 104 | notify = "8.2" |
| @@ -180,7 +180,6 @@ | |||
| 180 | 180 | # what keeps a call on desktop a compile error instead of a runtime one. | |
| 181 | 181 | [target.'cfg(any(target_os = "ios", target_os = "android"))'.dependencies] | |
| 182 | 182 | tauri-plugin-haptics = { workspace = true } | |
| 183 | - | tauri-plugin-biometric = { workspace = true } | |
| 184 | 183 | ||
| 185 | 184 | # SMTP trust source, everywhere lettre can express it: the OS-native store, the | |
| 186 | 185 | # same one reqwest and IMAP use. |
| @@ -1,14 +1,12 @@ | |||
| 1 | 1 | { | |
| 2 | 2 | "$schema": "https://schemas.tauri.app/capabilities/2", | |
| 3 | 3 | "identifier": "mobile", | |
| 4 | - | "description": "Mobile-only capabilities (haptic feedback, biometric app lock). Granted command by command rather than through the plugins' default sets: vibrate is not granted because nothing calls raw vibration, and a grant nothing uses is a grant nobody notices going wrong.", | |
| 4 | + | "description": "Mobile-only capabilities (haptic feedback). Granted command by command rather than through the plugins' default sets: vibrate is not granted because nothing calls raw vibration, and a grant nothing uses is a grant nobody notices going wrong.", | |
| 5 | 5 | "platforms": ["iOS", "android"], | |
| 6 | 6 | "windows": ["main"], | |
| 7 | 7 | "permissions": [ | |
| 8 | 8 | "haptics:allow-impact-feedback", | |
| 9 | 9 | "haptics:allow-notification-feedback", | |
| 10 | - | "haptics:allow-selection-feedback", | |
| 11 | - | "biometric:allow-status", | |
| 12 | - | "biometric:allow-authenticate" | |
| 10 | + | "haptics:allow-selection-feedback" | |
| 13 | 11 | ] | |
| 14 | 12 | } |
| @@ -353,7 +353,6 @@ | |||
| 353 | 353 | // Preferences | |
| 354 | 354 | $crate::commands::get_preferences, | |
| 355 | 355 | $crate::commands::set_update_check_on_launch, | |
| 356 | - | $crate::commands::set_require_biometric_unlock, | |
| 357 | 356 | ] | |
| 358 | 357 | }; | |
| 359 | 358 | } | |
| @@ -405,13 +404,10 @@ | |||
| 405 | 404 | } | |
| 406 | 405 | ||
| 407 | 406 | // Mobile-only plugins. Haptics carries gesture confirmation the desktop | |
| 408 | - | // build answers with hover and focus states instead; biometric backs the | |
| 409 | - | // app lock, which is offered only where the OS has an enrolled biometry. | |
| 407 | + | // build answers with hover and focus states instead. | |
| 410 | 408 | #[cfg(any(target_os = "ios", target_os = "android"))] | |
| 411 | 409 | { | |
| 412 | - | builder = builder | |
| 413 | - | .plugin(tauri_plugin_haptics::init()) | |
| 414 | - | .plugin(tauri_plugin_biometric::init()); | |
| 410 | + | builder = builder.plugin(tauri_plugin_haptics::init()); | |
| 415 | 411 | } | |
| 416 | 412 | ||
| 417 | 413 | builder |
| @@ -1,1 +1,1 @@ | |||
| 1 | - | {"default":{"identifier":"default","description":"Default capabilities for the main window","local":true,"windows":["main","compose-*"],"permissions":["core:default","core:window:allow-create","core:window:allow-close","core:window:allow-center","dialog:default"]},"desktop":{"identifier":"desktop","description":"Desktop-only capabilities (notifications, OTA updater). External URLs are opened via the validated open_external_url command, not a JS opener binding.","local":true,"windows":["main","compose-*"],"permissions":["notification:default","updater:default","process:allow-restart"],"platforms":["linux","macOS","windows"]},"mobile":{"identifier":"mobile","description":"Mobile-only capabilities (haptic feedback, biometric app lock). Granted command by command rather than through the plugins' default sets: vibrate is not granted because nothing calls raw vibration, and a grant nothing uses is a grant nobody notices going wrong.","local":true,"windows":["main"],"permissions":["haptics:allow-impact-feedback","haptics:allow-notification-feedback","haptics:allow-selection-feedback","biometric:allow-status","biometric:allow-authenticate"],"platforms":["iOS","android"]}} | |
| 1 | + | {"default":{"identifier":"default","description":"Default capabilities for the main window","local":true,"windows":["main","compose-*"],"permissions":["core:default","core:window:allow-create","core:window:allow-close","core:window:allow-center","dialog:default"]},"desktop":{"identifier":"desktop","description":"Desktop-only capabilities (notifications, OTA updater). External URLs are opened via the validated open_external_url command, not a JS opener binding.","local":true,"windows":["main","compose-*"],"permissions":["notification:default","updater:default","process:allow-restart"],"platforms":["linux","macOS","windows"]},"mobile":{"identifier":"mobile","description":"Mobile-only capabilities (haptic feedback). Granted command by command rather than through the plugins' default sets: vibrate is not granted because nothing calls raw vibration, and a grant nothing uses is a grant nobody notices going wrong.","local":true,"windows":["main"],"permissions":["haptics:allow-impact-feedback","haptics:allow-notification-feedback","haptics:allow-selection-feedback"],"platforms":["iOS","android"]}} |
| @@ -17,25 +17,12 @@ | |||
| 17 | 17 | #[serde(rename_all = "camelCase")] | |
| 18 | 18 | pub struct Preferences { | |
| 19 | 19 | pub update_check_on_launch: bool, | |
| 20 | - | /// Whether the app hides its contents behind a system biometric prompt. | |
| 21 | - | /// | |
| 22 | - | /// Here rather than in `user_config` for two reasons. It is read before the | |
| 23 | - | /// database is open, which is the whole point of this file: a lock that | |
| 24 | - | /// appears after the task list has painted has already failed. And it is a | |
| 25 | - | /// per-device choice, so it must not sync — enrolled biometry on a phone | |
| 26 | - | /// says nothing about the desktop the same account syncs to, and | |
| 27 | - | /// `user_config` would have to carry it as `Local` to say the same thing. | |
| 28 | - | /// | |
| 29 | - | /// Defaults off. Turning it on is the user asking for it. | |
| 30 | - | #[serde(default)] | |
| 31 | - | pub require_biometric_unlock: bool, | |
| 32 | 20 | } | |
| 33 | 21 | ||
| 34 | 22 | impl Default for Preferences { | |
| 35 | 23 | fn default() -> Self { | |
| 36 | 24 | Self { | |
| 37 | 25 | update_check_on_launch: true, | |
| 38 | - | require_biometric_unlock: false, | |
| 39 | 26 | } | |
| 40 | 27 | } | |
| 41 | 28 | } | |
| @@ -123,44 +110,20 @@ | |||
| 123 | 110 | save(&app, &prefs) | |
| 124 | 111 | } | |
| 125 | 112 | ||
| 126 | - | /// Turn the biometric app lock on or off. | |
| 127 | - | /// | |
| 128 | - | /// Registered on every platform, not only mobile: the preference is written by | |
| 129 | - | /// the same settings screen everywhere and a desktop build that rejected the | |
| 130 | - | /// command would make the row's absence a runtime error rather than a layout | |
| 131 | - | /// decision. Whether the lock is *offered* is decided in the frontend by asking | |
| 132 | - | /// the biometric plugin for its status, which only mobile answers. | |
| 133 | - | #[tauri::command] | |
| 134 | - | #[instrument(skip_all)] | |
| 135 | - | #[allow( | |
| 136 | - | clippy::needless_pass_by_value, | |
| 137 | - | reason = "Tauri command handler: AppHandle and payload args are supplied by value per the #[tauri::command] contract; command parameters cannot be borrowed" | |
| 138 | - | )] | |
| 139 | - | pub fn set_require_biometric_unlock(app: AppHandle, enabled: bool) -> Result<(), ApiError> { | |
| 140 | - | let mut prefs = load(&app); | |
| 141 | - | prefs.require_biometric_unlock = enabled; | |
| 142 | - | save(&app, &prefs) | |
| 143 | - | } | |
| 144 | - | ||
| 145 | 113 | #[cfg(test)] | |
| 146 | 114 | mod tests { | |
| 147 | 115 | use super::Preferences; | |
| 148 | 116 | ||
| 149 | 117 | #[test] | |
| 150 | - | fn a_file_written_before_the_app_lock_keeps_its_other_settings() { | |
| 151 | - | // `load` falls back to Default on any parse failure, so a missing | |
| 152 | - | // `#[serde(default)]` would not fail loudly -- it would silently turn | |
| 153 | - | // the update check back on for everyone who had switched it off. This | |
| 154 | - | // is the test that catches that, and the reason to add one per new | |
| 155 | - | // field rather than trusting the fallback. | |
| 156 | - | let old = r#"{"updateCheckOnLaunch": false}"#; | |
| 118 | + | fn a_file_written_before_a_setting_was_removed_keeps_its_others() { | |
| 119 | + | // `load` falls back to Default on any parse failure, so a struct that | |
| 120 | + | // rejected an unknown key would not fail loudly -- it would silently | |
| 121 | + | // turn the update check back on for everyone who had switched it off. | |
| 122 | + | // `requireBiometricUnlock` is the key an install written before the app | |
| 123 | + | // lock was removed still carries; there is no `deny_unknown_fields`, so | |
| 124 | + | // serde drops it. | |
| 125 | + | let old = r#"{"updateCheckOnLaunch": false, "requireBiometricUnlock": true}"#; | |
| 157 | 126 | let prefs: Preferences = serde_json::from_str(old).expect("old file still parses"); | |
| 158 | 127 | assert!(!prefs.update_check_on_launch); | |
| 159 | - | assert!(!prefs.require_biometric_unlock, "absent means off"); | |
| 160 | - | } | |
| 161 | - | ||
| 162 | - | #[test] | |
| 163 | - | fn the_app_lock_is_off_until_asked_for() { | |
| 164 | - | assert!(!Preferences::default().require_biometric_unlock); | |
| 165 | 128 | } | |
| 166 | 129 | } |
| @@ -52,9 +52,9 @@ | |||
| 52 | 52 | //! platform, not a fact a startup can resolve and hold: biometry can be | |
| 53 | 53 | //! enrolled and unenrolled while the app runs. Offering a toggle that cannot | |
| 54 | 54 | //! work is worse than not offering one, which is the reasoning the JS carried | |
| 55 | - | //! and it is unchanged. The preference itself still exists and still syncs | |
| 56 | - | //! nowhere; nothing in the described app writes it, and on desktop nothing ever | |
| 57 | - | //! did. | |
| 55 | + | //! and it is unchanged. It is also the work a rebuild owes, and it is why the | |
| 56 | + | //! row is recorded here rather than in the plugin, command and preference that | |
| 57 | + | //! were removed on 2026-08-28 (goingson `458e1753`) for reaching nothing. | |
| 58 | 58 | //! | |
| 59 | 59 | //! **The Keyboard Shortcuts button.** There is no described help screen to open | |
| 60 | 60 | //! and no chrome binding to open it with; see [`super::super::shell`], and |