commit 2d6c259ef6c15d16e849445dfab6504c79b2951f
parent 140c6e99217b0f681450df0a5f53a1294f895559
Author: triesap <tyson@radroots.org>
Date: Sun, 22 Mar 2026 02:12:02 +0000
core: add home location tools module
- add a dedicated core module for future home location lookup state instead of growing the root app file further
- move home location tool lifecycle ownership into the app state so it resets cleanly across identity transitions
- keep the extraction slice behavior-neutral while establishing the first real geocoder consumer boundary
- add core tests covering the location tool state reset contract
Diffstat:
2 files changed, 63 insertions(+), 0 deletions(-)
diff --git a/crates/core/src/home_location_tools.rs b/crates/core/src/home_location_tools.rs
@@ -0,0 +1,55 @@
+use crate::{RadrootsLocationPoint, RadrootsResolvedLocation};
+
+#[derive(Debug, Clone, PartialEq)]
+pub(crate) struct HomeLocationLookupResult {
+ pub queried_point: RadrootsLocationPoint,
+ pub matches: Vec<RadrootsResolvedLocation>,
+}
+
+#[derive(Debug, Default, Clone, PartialEq)]
+pub(crate) struct HomeLocationTools {
+ latitude_input: String,
+ longitude_input: String,
+ status_message: Option<String>,
+ lookup_result: Option<HomeLocationLookupResult>,
+}
+
+impl HomeLocationTools {
+ pub(crate) fn new() -> Self {
+ Self::default()
+ }
+
+ pub(crate) fn clear(&mut self) {
+ self.latitude_input.clear();
+ self.longitude_input.clear();
+ self.status_message = None;
+ self.lookup_result = None;
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn clear_resets_inputs_and_feedback() {
+ let mut tools = HomeLocationTools::new();
+ tools.latitude_input = "10.5".to_owned();
+ tools.longitude_input = "20.5".to_owned();
+ tools.status_message = Some("lookup failed".to_owned());
+ tools.lookup_result = Some(HomeLocationLookupResult {
+ queried_point: RadrootsLocationPoint {
+ lat: 10.5,
+ lng: 20.5,
+ },
+ matches: Vec::new(),
+ });
+
+ tools.clear();
+
+ assert_eq!(tools.latitude_input, "");
+ assert_eq!(tools.longitude_input, "");
+ assert_eq!(tools.status_message, None);
+ assert_eq!(tools.lookup_result, None);
+ }
+}
diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs
@@ -4,6 +4,7 @@ use eframe::egui;
use std::time::Duration;
use zeroize::Zeroizing;
+mod home_location_tools;
mod location_resolver;
mod offline_geocoder;
@@ -18,6 +19,8 @@ pub use offline_geocoder::{
RadrootsOfflineGeocoderState, RadrootsOfflineGeocoderUnavailableKind,
};
+use home_location_tools::HomeLocationTools;
+
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SetupActionState {
pub label: String,
@@ -137,6 +140,7 @@ pub struct RadrootsApp {
screen: AppScreen,
offline_geocoder_state: Option<RadrootsOfflineGeocoderState>,
status_message: Option<String>,
+ home_location_tools: HomeLocationTools,
pending_home_confirmation: Option<HomeActionKind>,
pending_import_entry: bool,
secret_key_input: Zeroizing<String>,
@@ -150,6 +154,7 @@ impl RadrootsApp {
screen: AppScreen::Setup,
offline_geocoder_state: None,
status_message: None,
+ home_location_tools: HomeLocationTools::new(),
pending_home_confirmation: None,
pending_import_entry: false,
secret_key_input: Zeroizing::new(String::new()),
@@ -171,6 +176,7 @@ impl RadrootsApp {
IdentityGateState::Missing => {
self.screen = AppScreen::Setup;
self.status_message = None;
+ self.home_location_tools.clear();
self.pending_home_confirmation = None;
self.pending_import_entry = false;
self.secret_key_input.clear();
@@ -179,6 +185,7 @@ impl RadrootsApp {
IdentityGateState::Ready { account_id, npub } => {
self.screen = AppScreen::Home { account_id, npub };
self.status_message = None;
+ self.home_location_tools.clear();
self.pending_home_confirmation = None;
self.pending_import_entry = false;
self.secret_key_input.clear();
@@ -187,6 +194,7 @@ impl RadrootsApp {
IdentityGateState::Unsupported { reason } => {
self.screen = AppScreen::Setup;
self.status_message = Some(reason);
+ self.home_location_tools.clear();
self.pending_home_confirmation = None;
self.pending_import_entry = false;
self.secret_key_input.clear();