From 53cb6df8aff20fda448012e2f6379145a61cbf2a Mon Sep 17 00:00:00 2001 From: Ismo Vuorinen Date: Tue, 18 Nov 2025 12:52:20 +0200 Subject: [PATCH] feat: Complete Milestone 4 - Views/UI Layer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented complete MVVM UI layer with data binding: **Dependency Injection (MauiProgram.cs):** - Registered all services as Singleton (shared state) - Registered all ViewModels and Pages as Transient - Configured SQLite database path **Pages:** - MainPage: Mode selector, estimate display, shake status - HistoryPage: CollectionView with auto-load on appearing - SettingsPage: Mode picker, MaxHistorySize stepper **Value Converters:** - IsNullConverter / IsNotNullConverter (visibility) - BoolToShakingConverter (status text) - BoolToColorConverter (status colors) - InvertedBoolConverter (boolean inversion) **Navigation:** - AppShell with TabBar (Estimate, History, Settings) - ContentTemplate for lazy loading **Technical details:** - All XAML uses x:DataType for compile-time checking - All code-behind uses constructor injection - Zero warnings, zero errors - 165 tests passing (no UI tests yet) Build verified across all platforms (net8.0, iOS, macOS Catalyst) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CLAUDE.md | 65 ++++++++-- src/HihaArvio/App.xaml | 8 ++ src/HihaArvio/AppShell.xaml | 26 +++- .../Converters/BoolToColorConverter.cs | 20 +++ .../Converters/BoolToShakingConverter.cs | 20 +++ .../Converters/InvertedBoolConverter.cs | 16 +++ src/HihaArvio/Converters/IsNullConverter.cs | 29 +++++ src/HihaArvio/HistoryPage.xaml | 96 ++++++++++++++ src/HihaArvio/HistoryPage.xaml.cs | 23 ++++ src/HihaArvio/MainPage.xaml | 122 ++++++++++++++---- src/HihaArvio/MainPage.xaml.cs | 28 ++-- src/HihaArvio/MauiProgram.cs | 23 +++- src/HihaArvio/SettingsPage.xaml | 113 ++++++++++++++++ src/HihaArvio/SettingsPage.xaml.cs | 12 ++ 14 files changed, 538 insertions(+), 63 deletions(-) create mode 100644 src/HihaArvio/Converters/BoolToColorConverter.cs create mode 100644 src/HihaArvio/Converters/BoolToShakingConverter.cs create mode 100644 src/HihaArvio/Converters/InvertedBoolConverter.cs create mode 100644 src/HihaArvio/Converters/IsNullConverter.cs create mode 100644 src/HihaArvio/HistoryPage.xaml create mode 100644 src/HihaArvio/HistoryPage.xaml.cs create mode 100644 src/HihaArvio/SettingsPage.xaml create mode 100644 src/HihaArvio/SettingsPage.xaml.cs diff --git a/CLAUDE.md b/CLAUDE.md index 310153f..90d99b2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -228,19 +228,62 @@ HihaArvio.sln - **Coverage:** 51.28% line (low due to MAUI template), 87.5% branch - **Build:** 0 warnings, 0 errors across all platforms +**Milestone 3: ViewModels Layer (✅ Complete)** +- MainViewModel (18 tests) + - Coordinates shake detection and estimate generation + - Subscribes to ShakeDetectionService.ShakeDataChanged event + - Detects shake stop (transition from shaking → not shaking) + - Mode selection and current estimate display + - Implements IDisposable for cleanup +- HistoryViewModel (15 tests) + - Manages estimate history display with ObservableCollection + - LoadHistoryCommand for async history retrieval + - ClearHistoryCommand for pruning + - IsEmpty property for empty state handling +- SettingsViewModel (13 tests) + - Settings management (SelectedMode, MaxHistorySize) + - SaveSettingsCommand for persistence + - Auto-loads settings on initialization +- All ViewModels use CommunityToolkit.Mvvm source generators + - [ObservableProperty] for property change notifications + - [RelayCommand] for commands +- **Total: 165 tests, all passing (119 services + 46 ViewModels)** +- **Build:** 0 warnings, 0 errors across all platforms + +**Milestone 4: Views/UI Layer (✅ Complete)** +- Dependency Injection configuration in MauiProgram.cs + - All services registered as Singleton + - All ViewModels and Pages registered as Transient + - SQLite database path configured with FileSystem.AppDataDirectory +- MainPage.xaml with data binding + - Mode selector (Work/Generic) + - Current estimate display with conditional visibility + - Shake status indicator + - Uses x:DataType for compile-time binding verification +- HistoryPage.xaml with data binding + - CollectionView with ItemTemplate + - Empty state when no history + - Refresh and Clear All buttons + - Auto-loads history on OnAppearing() +- SettingsPage.xaml with data binding + - Picker for mode selection (Work/Generic only - Humorous is easter egg) + - Stepper for MaxHistorySize (5-100, increment by 5) + - Save Settings button + - About section with easter egg hint +- Value Converters for UI logic + - IsNullConverter / IsNotNullConverter (conditional visibility) + - BoolToShakingConverter (status text) + - BoolToColorConverter (status colors) + - InvertedBoolConverter (boolean inversion) + - All registered in App.xaml resources +- AppShell.xaml navigation + - TabBar with 3 tabs: Estimate, History, Settings + - Each tab uses ContentTemplate for lazy loading +- **Total: 165 tests still passing (no UI tests yet)** +- **Build:** 0 warnings, 0 errors across all platforms (net8.0, iOS, macOS Catalyst) + ### Remaining Work -**Milestone 3: ViewModels Layer** (Not Started) -- MainViewModel (estimate display, settings) -- HistoryViewModel (estimate history) -- SettingsViewModel (mode selection, history management) - -**Milestone 4: Views/UI Layer** (Not Started) -- MainPage (shake screen, estimate display) -- HistoryPage (estimate list) -- SettingsPage (mode toggle, history clear) -- Navigation infrastructure - **Milestone 5: Platform-Specific Implementations** (Not Started) - IAccelerometerService interface - iOS implementation (real accelerometer) diff --git a/src/HihaArvio/App.xaml b/src/HihaArvio/App.xaml index 22179c7..1404f7f 100644 --- a/src/HihaArvio/App.xaml +++ b/src/HihaArvio/App.xaml @@ -2,6 +2,7 @@ @@ -9,6 +10,13 @@ + + + + + + + diff --git a/src/HihaArvio/AppShell.xaml b/src/HihaArvio/AppShell.xaml index 574c217..a8de905 100644 --- a/src/HihaArvio/AppShell.xaml +++ b/src/HihaArvio/AppShell.xaml @@ -4,12 +4,26 @@ xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:HihaArvio" - Shell.FlyoutBehavior="Disabled" - Title="HihaArvio"> + Title="Hiha-Arvio"> - + + + + + + + diff --git a/src/HihaArvio/Converters/BoolToColorConverter.cs b/src/HihaArvio/Converters/BoolToColorConverter.cs new file mode 100644 index 0000000..6478110 --- /dev/null +++ b/src/HihaArvio/Converters/BoolToColorConverter.cs @@ -0,0 +1,20 @@ +using System.Globalization; + +namespace HihaArvio.Converters; + +public class BoolToColorConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is bool isShaking) + { + return isShaking ? Colors.Green : Colors.Gray; + } + return Colors.Gray; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/src/HihaArvio/Converters/BoolToShakingConverter.cs b/src/HihaArvio/Converters/BoolToShakingConverter.cs new file mode 100644 index 0000000..a5eea65 --- /dev/null +++ b/src/HihaArvio/Converters/BoolToShakingConverter.cs @@ -0,0 +1,20 @@ +using System.Globalization; + +namespace HihaArvio.Converters; + +public class BoolToShakingConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is bool isShaking) + { + return isShaking ? "Shaking" : "Idle"; + } + return "Unknown"; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/src/HihaArvio/Converters/InvertedBoolConverter.cs b/src/HihaArvio/Converters/InvertedBoolConverter.cs new file mode 100644 index 0000000..1eada0b --- /dev/null +++ b/src/HihaArvio/Converters/InvertedBoolConverter.cs @@ -0,0 +1,16 @@ +using System.Globalization; + +namespace HihaArvio.Converters; + +public class InvertedBoolConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + return value is bool boolValue && !boolValue; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + return value is bool boolValue && !boolValue; + } +} diff --git a/src/HihaArvio/Converters/IsNullConverter.cs b/src/HihaArvio/Converters/IsNullConverter.cs new file mode 100644 index 0000000..0d69e39 --- /dev/null +++ b/src/HihaArvio/Converters/IsNullConverter.cs @@ -0,0 +1,29 @@ +using System.Globalization; + +namespace HihaArvio.Converters; + +public class IsNullConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + return value is null; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} + +public class IsNotNullConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + return value is not null; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/src/HihaArvio/HistoryPage.xaml b/src/HihaArvio/HistoryPage.xaml new file mode 100644 index 0000000..ea2407f --- /dev/null +++ b/src/HihaArvio/HistoryPage.xaml @@ -0,0 +1,96 @@ + + + + + + + +