From 4c15c54de7df0601729b6ad6ab8fda5e271d9375 Mon Sep 17 00:00:00 2001 From: Nick Randolph Date: Thu, 26 May 2022 16:03:39 +1000 Subject: [PATCH] feat: Enabling light/dark switching (with persistence) --- samples/Commerce/Commerce.UI/App.xaml.cs | 9 ++++--- samples/Commerce/Commerce.UI/App.xaml.host.cs | 25 +++++++++++------- samples/Commerce/Commerce.UI/AppTheme.cs | 23 ++++++++++++++++ .../Commerce.UI/Commerce.UI.projitems | 9 ++++--- .../Commerce/Commerce.UI/ReactiveClasses.cs | 1 + .../Commerce.UI/Views/ProfilePage.xaml | 2 +- .../Commerce.UI/Views/ProfilePage.xaml.cs | 16 ------------ samples/Commerce/Commerce.UI/appsettings.json | 5 +++- .../Commerce/{ => Configuration}/AppInfo.cs | 1 + samples/Commerce/Commerce/IAppTheme.cs | 10 +++++++ .../Commerce/Commerce/Settings/CommerceApp.cs | 6 +++++ .../{ViewModels => Settings}/Credentials.cs | 0 .../Commerce/ViewModels/ProfileViewModel.cs | 26 ++++++++++++++++--- 13 files changed, 94 insertions(+), 39 deletions(-) create mode 100644 samples/Commerce/Commerce.UI/AppTheme.cs rename samples/Commerce/Commerce/{ => Configuration}/AppInfo.cs (98%) create mode 100644 samples/Commerce/Commerce/IAppTheme.cs create mode 100644 samples/Commerce/Commerce/Settings/CommerceApp.cs rename samples/Commerce/Commerce/{ViewModels => Settings}/Credentials.cs (100%) diff --git a/samples/Commerce/Commerce.UI/App.xaml.cs b/samples/Commerce/Commerce.UI/App.xaml.cs index bbab478a6f..5159eb5d46 100644 --- a/samples/Commerce/Commerce.UI/App.xaml.cs +++ b/samples/Commerce/Commerce.UI/App.xaml.cs @@ -39,10 +39,11 @@ protected async override void OnLaunched(LaunchActivatedEventArgs args) _window.AttachNavigation(_host.Services); _window.Activate(); - await Task.Run(async () => - { - await _host.StartAsync(); - }); + await Task.Run(() => _host.StartAsync()); + + var appSettings = _host.Services.GetRequiredService>(); + var isDark = appSettings.Value?.IsDark ?? false; + SystemThemeHelper.SetRootTheme(_window.Content.XamlRoot, isDark); } diff --git a/samples/Commerce/Commerce.UI/App.xaml.host.cs b/samples/Commerce/Commerce.UI/App.xaml.host.cs index 2176d89a00..bdb4278725 100644 --- a/samples/Commerce/Commerce.UI/App.xaml.host.cs +++ b/samples/Commerce/Commerce.UI/App.xaml.host.cs @@ -16,21 +16,22 @@ private static IHost BuildAppHost() // Add platform specific log providers - .UseLogging(configure: logBuilder => - { - // Configure log levels for different categories of logging - logBuilder - .SetMinimumLevel(LogLevel.Information) - .XamlLogLevel(LogLevel.Information) - .XamlLayoutLogLevel(LogLevel.Information) - .AddFilter("Uno.Extensions.Navigation", LogLevel.Trace); - }) + .UseLogging(configure: (context, logBuilder) => + // Configure log levels for different categories of logging + logBuilder + .SetMinimumLevel( + context.HostingEnvironment.IsDevelopment() ? + LogLevel.Information : + LogLevel.Warning)) .UseConfiguration(configure: configBuilder=> configBuilder - .ContentSource() + .EmbeddedSource() + .Section() + .Section() + .Section() ) @@ -41,6 +42,8 @@ private static IHost BuildAppHost() .ConfigureServices(services => { services + .AddScoped() + .AddSingleton() .AddSingleton() @@ -64,6 +67,8 @@ private static IHost BuildAppHost() // Add navigation support for toolkit controls such as TabBar and NavigationView .UseToolkitNavigation() + // Add localization support + .UseLocalization() .Build(enableUnoLogging: true); diff --git a/samples/Commerce/Commerce.UI/AppTheme.cs b/samples/Commerce/Commerce.UI/AppTheme.cs new file mode 100644 index 0000000000..4c940c26e0 --- /dev/null +++ b/samples/Commerce/Commerce.UI/AppTheme.cs @@ -0,0 +1,23 @@ +namespace Commerce; + +// TODO: Extract these to uno extensions +// See https://github.com/unoplatform/uno.extensions/discussions/420 +public class AppTheme : IAppTheme +{ + private readonly Window _window; + private readonly IDispatcher _dispatcher; + public AppTheme(Window window, IDispatcher dispatcher) + { + _window = window; + _dispatcher = dispatcher; + } + public bool IsDark => SystemThemeHelper.IsRootInDarkMode(_window.Content.XamlRoot); + + public async Task SetThemeAsync(bool darkMode) + { + await _dispatcher.ExecuteAsync(() => + { + SystemThemeHelper.SetRootTheme(_window.Content.XamlRoot, darkMode); + }); + } +} diff --git a/samples/Commerce/Commerce.UI/Commerce.UI.projitems b/samples/Commerce/Commerce.UI/Commerce.UI.projitems index f939b761fc..b3bef85417 100644 --- a/samples/Commerce/Commerce.UI/Commerce.UI.projitems +++ b/samples/Commerce/Commerce.UI/Commerce.UI.projitems @@ -21,6 +21,7 @@ App.xaml + @@ -121,9 +122,9 @@ - + Always - + @@ -166,9 +167,9 @@ - + Always - + Always diff --git a/samples/Commerce/Commerce.UI/ReactiveClasses.cs b/samples/Commerce/Commerce.UI/ReactiveClasses.cs index af3a3dd76c..de60c9f343 100644 --- a/samples/Commerce/Commerce.UI/ReactiveClasses.cs +++ b/samples/Commerce/Commerce.UI/ReactiveClasses.cs @@ -13,6 +13,7 @@ public static partial class ReactiveViewModelMappings public static IDictionary ViewModelMappings = new Dictionary() { { typeof(LoginViewModel), typeof(LoginViewModel.BindableLoginViewModel)}, + { typeof(ProfileViewModel),typeof(ProfileViewModel.BindableProfileViewModel)}, { typeof(ProductsViewModel),typeof(ProductsViewModel.BindableProductsViewModel)}, { typeof(ProductDetailsViewModel),typeof(ProductDetailsViewModel.BindableProductDetailsViewModel)}, { typeof(FiltersViewModel),typeof(FiltersViewModel.BindableFiltersViewModel)}, diff --git a/samples/Commerce/Commerce.UI/Views/ProfilePage.xaml b/samples/Commerce/Commerce.UI/Views/ProfilePage.xaml index ac32e65859..748233656a 100644 --- a/samples/Commerce/Commerce.UI/Views/ProfilePage.xaml +++ b/samples/Commerce/Commerce.UI/Views/ProfilePage.xaml @@ -210,7 +210,7 @@ diff --git a/samples/Commerce/Commerce.UI/Views/ProfilePage.xaml.cs b/samples/Commerce/Commerce.UI/Views/ProfilePage.xaml.cs index 0f6fc2ee5c..675e35b89e 100644 --- a/samples/Commerce/Commerce.UI/Views/ProfilePage.xaml.cs +++ b/samples/Commerce/Commerce.UI/Views/ProfilePage.xaml.cs @@ -9,14 +9,6 @@ public ProfilePage() { this.InitializeComponent(); - this.Loaded += (s, e) => - { - // Initialize the toggle to the current theme. - darkModeToggle.IsEnabled = false; - darkModeToggle.IsOn = SystemThemeHelper.IsRootInDarkMode(XamlRoot); - darkModeToggle.IsEnabled = true; - }; - DataContextChanged += ProfilePage_DataContextChanged; } @@ -24,12 +16,4 @@ private void ProfilePage_DataContextChanged(FrameworkElement sender, DataContext { ViewModel = args.NewValue as ProfileViewModel; } - - private void ToggleDarkMode() - { - if (darkModeToggle.IsEnabled) - { - SystemThemeHelper.SetRootTheme(XamlRoot, darkModeToggle.IsOn); - } - } } diff --git a/samples/Commerce/Commerce.UI/appsettings.json b/samples/Commerce/Commerce.UI/appsettings.json index 49fa1ce2d3..61408a7129 100644 --- a/samples/Commerce/Commerce.UI/appsettings.json +++ b/samples/Commerce/Commerce.UI/appsettings.json @@ -1,5 +1,8 @@ -{ +{ "AppInfo": { "Title": "Uno Commerce App" + }, + "LocalizationConfiguration": { + "Cultures": [ "en" ] } } diff --git a/samples/Commerce/Commerce/AppInfo.cs b/samples/Commerce/Commerce/Configuration/AppInfo.cs similarity index 98% rename from samples/Commerce/Commerce/AppInfo.cs rename to samples/Commerce/Commerce/Configuration/AppInfo.cs index 566541441d..b138544228 100644 --- a/samples/Commerce/Commerce/AppInfo.cs +++ b/samples/Commerce/Commerce/Configuration/AppInfo.cs @@ -3,4 +3,5 @@ public record AppInfo { public string? Title { get; init; } + } diff --git a/samples/Commerce/Commerce/IAppTheme.cs b/samples/Commerce/Commerce/IAppTheme.cs new file mode 100644 index 0000000000..a5f6afa264 --- /dev/null +++ b/samples/Commerce/Commerce/IAppTheme.cs @@ -0,0 +1,10 @@ +namespace Commerce; + +// TODO: Extract these to uno extensions +// See https://github.com/unoplatform/uno.extensions/discussions/420 +public interface IAppTheme +{ + bool IsDark { get; } + + Task SetThemeAsync(bool darkMode); +} diff --git a/samples/Commerce/Commerce/Settings/CommerceApp.cs b/samples/Commerce/Commerce/Settings/CommerceApp.cs new file mode 100644 index 0000000000..7369a6be4d --- /dev/null +++ b/samples/Commerce/Commerce/Settings/CommerceApp.cs @@ -0,0 +1,6 @@ +namespace Commerce; + +public record CommerceApp +{ + public bool? IsDark { get; init; } +} diff --git a/samples/Commerce/Commerce/ViewModels/Credentials.cs b/samples/Commerce/Commerce/Settings/Credentials.cs similarity index 100% rename from samples/Commerce/Commerce/ViewModels/Credentials.cs rename to samples/Commerce/Commerce/Settings/Credentials.cs diff --git a/samples/Commerce/Commerce/ViewModels/ProfileViewModel.cs b/samples/Commerce/Commerce/ViewModels/ProfileViewModel.cs index 1e18f92478..c6a34cf1d5 100644 --- a/samples/Commerce/Commerce/ViewModels/ProfileViewModel.cs +++ b/samples/Commerce/Commerce/ViewModels/ProfileViewModel.cs @@ -1,27 +1,47 @@ namespace Commerce.ViewModels; -public class ProfileViewModel +public partial class ProfileViewModel { private readonly INavigator _navigator; private readonly IWritableOptions _credentials; private readonly IProfileService _profileService; + + private readonly IAppTheme _appTheme; + private readonly IWritableOptions _appSettings; + public ProfileViewModel( INavigator navigator, IWritableOptions credentials, - IProfileService profileService) + IProfileService profileService, + IAppTheme appTheme, + IWritableOptions appSettings) { _navigator = navigator; _credentials = credentials; _profileService = profileService; + _appTheme = appTheme; + _appSettings = appSettings; + + IsDarkTheme = State.Value(this, () => appTheme.IsDark); + IsDarkTheme.Execute(ChangeAppTheme); } + [Value] + public IState IsDarkTheme { get; } + public IFeed Profile => Feed.Async(_profileService.GetProfile); - public async void Logout() + public async ValueTask Logout() { await _credentials.UpdateAsync(c => new Credentials()); await _navigator.NavigateRouteAsync(this, "/"); } + + private async ValueTask ChangeAppTheme(bool isDark, CancellationToken ct) + { + await _appTheme.SetThemeAsync(isDark); + await _appSettings.UpdateAsync(s => s with { IsDark = isDark }); + } }