using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; using Wino.Core.Domain; using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Enums; using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Models.Navigation; using Wino.Core.Domain.Models.Personalization; using Wino.Core.Domain.Models.Settings; using Wino.Core.Domain.Models.Translations; using Wino.Core.Extensions; using Wino.Core.ViewModels.Data; using Wino.Mail.ViewModels.Data; using Wino.Messaging.Client.Navigation; namespace Wino.Core.ViewModels; public partial class SettingOptionsPageViewModel : CoreBaseViewModel { private readonly INativeAppService _nativeAppService; private readonly IAccountService _accountService; private readonly IMimeStorageService _mimeStorageService; private readonly ITranslationService _translationService; private readonly INewThemeService _newThemeService; private readonly IPreferencesService _preferencesService; private readonly IProviderService _providerService; private bool _isInitializingSettings; public string WebsiteUrl => AppUrls.Website; public string PrivacyPolicyUrl => AppUrls.PrivacyPolicy; public ObservableCollection SearchSuggestions { get; } = []; public ObservableCollection Accounts { get; } = []; public ObservableCollection Colors { get; } = []; public List ElementThemes { get; } = [ new(ApplicationElementTheme.Default, Translator.ElementTheme_Default), new(ApplicationElementTheme.Light, Translator.ElementTheme_Light), new(ApplicationElementTheme.Dark, Translator.ElementTheme_Dark), ]; public bool HasAccounts => Accounts.Count > 0; [ObservableProperty] public partial string VersionText { get; set; } = string.Empty; [ObservableProperty] public partial string AccountSummaryText { get; set; } = string.Empty; [ObservableProperty] public partial int AccountCount { get; set; } [ObservableProperty] public partial string StorageSummaryText { get; set; } = string.Empty; [ObservableProperty] public partial string SearchQuery { get; set; } = string.Empty; [ObservableProperty] public partial List AvailableLanguages { get; set; } = []; [ObservableProperty] public partial AppLanguageModel SelectedLanguage { get; set; } [ObservableProperty] public partial ElementThemeContainer SelectedElementTheme { get; set; } [ObservableProperty] public partial AppColorViewModel SelectedAppColor { get; set; } [ObservableProperty] public partial bool UseAccentColor { get; set; } public SettingOptionsPageViewModel(INativeAppService nativeAppService, IAccountService accountService, IMimeStorageService mimeStorageService, ITranslationService translationService, INewThemeService newThemeService, IPreferencesService preferencesService, IProviderService providerService) { _nativeAppService = nativeAppService; _accountService = accountService; _mimeStorageService = mimeStorageService; _translationService = translationService; _newThemeService = newThemeService; _preferencesService = preferencesService; _providerService = providerService; } public override void OnNavigatedTo(NavigationMode mode, object parameters) { base.OnNavigatedTo(mode, parameters); VersionText = string.Format("{0}{1}", Translator.SettingsAboutVersion, _nativeAppService.GetFullAppVersion()); SearchQuery = string.Empty; SearchSuggestions.Clear(); StorageSummaryText = Translator.SettingsHome_StorageLoading; InitializeQuickSettings(); _ = LoadDashboardAsync(); } public void UpdateSearchSuggestions(string query) { SearchQuery = query; SearchSuggestions.Clear(); foreach (var result in SettingsNavigationInfoProvider.Search(query, AccountSummaryText).Take(6)) { SearchSuggestions.Add(result); } } public SettingsNavigationItemInfo GetBestSearchSuggestion(string query) => SettingsNavigationInfoProvider.Search(query, AccountSummaryText).FirstOrDefault(); public void NavigateToSetting(SettingsNavigationItemInfo item) { if (item?.PageType is not WinoPage pageType) { return; } Messenger.Send(new BreadcrumbNavigationRequested( SettingsNavigationInfoProvider.GetPageTitle(pageType), pageType)); } public void NavigateToAccount(IAccountProviderDetailViewModel account) { if (account == null) { return; } Messenger.Send(new BreadcrumbNavigationRequested(Translator.SettingsManageAccountSettings_Title, WinoPage.ManageAccountsPage)); switch (account) { case AccountProviderDetailViewModel accountDetails: Messenger.Send(new BreadcrumbNavigationRequested(GetAccountDetailsTitle(accountDetails.Account), WinoPage.AccountDetailsPage, accountDetails.Account.Id)); break; case MergedAccountProviderDetailViewModel mergedAccount: Messenger.Send(new BreadcrumbNavigationRequested(mergedAccount.MergedInbox.Name, WinoPage.MergedAccountDetailsPage, mergedAccount)); break; } } public void NavigateToAddAccount() { Messenger.Send(new BreadcrumbNavigationRequested(Translator.SettingsManageAccountSettings_Title, WinoPage.ManageAccountsPage)); Messenger.Send(new BreadcrumbNavigationRequested( Translator.WelcomeWizard_Step2Title, WinoPage.ProviderSelectionPage, ProviderSelectionNavigationContext.CreateForSettingsAddAccount())); } public void NavigateToManageAccounts() { Messenger.Send(new BreadcrumbNavigationRequested(Translator.SettingsManageAccountSettings_Title, WinoPage.ManageAccountsPage)); } private async Task LoadDashboardAsync() { var accounts = (await _accountService.GetAccountsAsync().ConfigureAwait(false) ?? []).ToList(); var count = accounts.Count; Dictionary storageSizeMap = count == 0 ? [] : await _mimeStorageService.GetAccountsMimeStorageSizesAsync(accounts.Select(account => account.Id)).ConfigureAwait(false); var totalStorageBytes = storageSizeMap.Values.Sum(); var groupedAccountItems = CreateAccountItems(accounts); await ExecuteUIThread(() => { AccountCount = count; AccountSummaryText = string.Format(Translator.SettingsOptions_AccountsSummary, count); Accounts.Clear(); foreach (var account in groupedAccountItems) { Accounts.Add(account); } OnPropertyChanged(nameof(HasAccounts)); StorageSummaryText = totalStorageBytes == 0 ? Translator.SettingsHome_StorageEmptySummary : string.Format(Translator.SettingsStorage_TotalUsage, totalStorageBytes.GetBytesReadable()); if (!string.IsNullOrWhiteSpace(SearchQuery)) { UpdateSearchSuggestions(SearchQuery); } }); } private static string GetAccountDetailsTitle(MailAccount account) => !string.IsNullOrWhiteSpace(account?.Address) ? string.Format(Translator.SettingsAccountDetails_NavigationTitle, account.Address) : account?.Name ?? Translator.AccountDetailsPage_Title; private void InitializeQuickSettings() { _isInitializingSettings = true; InitializeColors(); InitializeLanguageOptions(); InitializeAppearanceOptions(); _isInitializingSettings = false; } private void InitializeColors() { Colors.Clear(); var fixedColors = new[] { "#0078d7", "#00838c", "#e3008c", "#ca4f07", "#e81123", "#00819e", "#10893e", "#881798", "#c239b3", "#767676", "#e1b12c", "#16a085", "#0984e3", "#4a69bd", "#05c46b" }; foreach (var color in fixedColors) { Colors.Add(new AppColorViewModel(color)); } Colors.Add(new AppColorViewModel(_newThemeService.GetSystemAccentColorHex(), true)); var currentAccentColor = _newThemeService.AccentColor; var useSystemAccent = string.IsNullOrEmpty(currentAccentColor); UseAccentColor = useSystemAccent; SelectedAppColor = useSystemAccent ? Colors.LastOrDefault() : Colors.FirstOrDefault(color => color.Hex == currentAccentColor) ?? Colors.FirstOrDefault(); } private void InitializeLanguageOptions() { AvailableLanguages = _translationService.GetAvailableLanguages(); SelectedLanguage = AvailableLanguages.FirstOrDefault(language => language.Language == _preferencesService.CurrentLanguage) ?? AvailableLanguages.FirstOrDefault(); } private void InitializeAppearanceOptions() { SelectedElementTheme = ElementThemes.FirstOrDefault(theme => theme.NativeTheme == _newThemeService.RootTheme) ?? ElementThemes[0]; } partial void OnSelectedLanguageChanged(AppLanguageModel value) { if (_isInitializingSettings || value == null) return; _ = _translationService.InitializeLanguageAsync(value.Language); } partial void OnSelectedElementThemeChanged(ElementThemeContainer value) { if (_isInitializingSettings || value == null) return; _newThemeService.RootTheme = value.NativeTheme; } partial void OnSelectedAppColorChanged(AppColorViewModel value) { if (_isInitializingSettings || value == null) return; _newThemeService.AccentColor = UseAccentColor ? string.Empty : value.Hex; } partial void OnUseAccentColorChanged(bool value) { if (_isInitializingSettings) return; if (value) { SelectedAppColor = Colors.LastOrDefault(); _newThemeService.AccentColor = string.Empty; } else if (SelectedAppColor?.IsAccentColor == true) { SelectedAppColor = Colors.FirstOrDefault(); _newThemeService.AccentColor = SelectedAppColor?.Hex ?? string.Empty; } } [RelayCommand] private Task NavigateExternalAsync(string url) { if (string.IsNullOrWhiteSpace(url)) return Task.CompletedTask; return _nativeAppService.LaunchUriAsync(new Uri(url)); } private List CreateAccountItems(List accounts) { return accounts .GroupBy(account => account.MergedInboxId) .SelectMany(group => { if (group.Key == null) { return group.Select(account => (IAccountProviderDetailViewModel)new AccountProviderDetailViewModel( _providerService.GetProviderDetail(account.ProviderType), account)); } var mergedInbox = group.First().MergedInbox; var holdingAccounts = group .Select(account => new AccountProviderDetailViewModel(_providerService.GetProviderDetail(account.ProviderType), account)) .ToList(); return [ (IAccountProviderDetailViewModel)new MergedAccountProviderDetailViewModel(mergedInbox, holdingAccounts) ]; }) .ToList(); } }