using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using FieldLogger.Models; using FieldLogger.Services; using FieldLogger.Services.Sync; namespace FieldLogger.ViewModels; public sealed partial class SettingsViewModel : ObservableObject { private readonly DeviceConnectionManager _manager; private readonly SettingsService _settings; private readonly IMqttSyncService _sync; public DeviceConnectionManager Manager => _manager; [ObservableProperty] private string _mapsApiKey = ""; [ObservableProperty] private bool _mqttEnabled; [ObservableProperty] private string _mqttHost = ""; [ObservableProperty] private string _mqttPort = "8884"; [ObservableProperty] private string _mqttOrgId = ""; [ObservableProperty] private string _mqttPassword = ""; [ObservableProperty] private string _syncStatus = ""; [ObservableProperty] private bool _isSavingSync; public string MqttClientId => _settings.MqttClientId; public string AppVersion => AppInfo.Current.VersionString; public SettingsViewModel(DeviceConnectionManager manager, SettingsService settings, IMqttSyncService sync) { _manager = manager; _settings = settings; _sync = sync; MapsApiKey = settings.GoogleMapsApiKey; MqttEnabled = settings.MqttEnabled; MqttHost = settings.MqttHost; MqttPort = settings.MqttPort.ToString(); MqttOrgId = settings.MqttOrgId; SyncStatus = sync.Status; _sync.StatusChanged += OnSyncStatusChanged; } partial void OnMapsApiKeyChanged(string value) => _settings.GoogleMapsApiKey = value.Trim(); [RelayCommand] private async Task SaveSyncAsync() { if (!int.TryParse(MqttPort, out var port) || port is < 1 or > 65535) { await Shell.Current.DisplayAlert("Invalid MQTT Port", "Enter a port between 1 and 65535.", "OK"); return; } if (MqttEnabled && (string.IsNullOrWhiteSpace(MqttHost) || string.IsNullOrWhiteSpace(MqttOrgId))) { await Shell.Current.DisplayAlert("Incomplete Sync Settings", "Host and organization id are required.", "OK"); return; } IsSavingSync = true; try { _settings.MqttHost = MqttHost; _settings.MqttPort = port; _settings.MqttOrgId = MqttOrgId; _settings.MqttEnabled = MqttEnabled; if (!string.IsNullOrWhiteSpace(MqttPassword)) { await _settings.SetMqttPasswordAsync(MqttPassword); MqttPassword = ""; } if (MqttEnabled) { await _sync.ConnectAsync(); await Shell.Current.DisplayAlert("Sync Connected", "The durable MQTT queue is connected.", "OK"); } else { await _sync.DisconnectAsync(); } } catch (Exception ex) { await Shell.Current.DisplayAlert("Sync Connection Failed", ex.Message, "OK"); } finally { IsSavingSync = false; } } private void OnSyncStatusChanged(object? sender, string status) => MainThread.BeginInvokeOnMainThread(() => SyncStatus = status); [RelayCommand] private Task ChangeLocatorAsync() => Shell.Current.GoToAsync($"devicescan?kind={DeviceKind.Locator}"); [RelayCommand] private Task ChangeGpsAsync() => Shell.Current.GoToAsync($"devicescan?kind={DeviceKind.RtkGps}"); [RelayCommand] private async Task ForgetLocatorAsync() { if (await ConfirmForgetAsync(_manager.LocatorName ?? "locating receiver")) await _manager.ForgetDeviceAsync(DeviceKind.Locator); } [RelayCommand] private async Task ForgetGpsAsync() { if (await ConfirmForgetAsync(_manager.GpsName ?? "RTK GPS receiver")) await _manager.ForgetDeviceAsync(DeviceKind.RtkGps); } private static Task ConfirmForgetAsync(string name) => Shell.Current.DisplayAlert("Forget Device", $"Forget \"{name}\"? The app will no longer auto-connect to it.", "Forget", "Cancel"); }