2026-03-06 03:42:08 +01:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2026-04-16 13:45:11 +02:00
|
|
|
using System.Threading.Tasks;
|
2026-03-06 03:42:08 +01:00
|
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
|
using CommunityToolkit.Mvvm.Input;
|
|
|
|
|
using CommunityToolkit.Mvvm.Messaging;
|
|
|
|
|
using Wino.Core.Domain;
|
|
|
|
|
using Wino.Core.Domain.Enums;
|
|
|
|
|
using Wino.Core.Domain.Interfaces;
|
2026-04-14 00:03:48 +02:00
|
|
|
using Wino.Core.Domain.Models.Accounts;
|
2026-03-06 03:42:08 +01:00
|
|
|
using Wino.Core.Domain.Models.Navigation;
|
|
|
|
|
using Wino.Core.ViewModels.Data;
|
|
|
|
|
using Wino.Mail.ViewModels.Data;
|
|
|
|
|
using Wino.Messaging.Client.Navigation;
|
|
|
|
|
|
|
|
|
|
namespace Wino.Mail.ViewModels;
|
|
|
|
|
|
2026-04-20 19:38:30 +02:00
|
|
|
public enum ProviderSelectionWizardStep
|
|
|
|
|
{
|
|
|
|
|
Provider = 0,
|
|
|
|
|
Identity = 1,
|
|
|
|
|
Capabilities = 2
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-06 03:42:08 +01:00
|
|
|
public partial class ProviderSelectionPageViewModel : MailBaseViewModel
|
|
|
|
|
{
|
2026-04-16 13:45:11 +02:00
|
|
|
private readonly IAccountService _accountService;
|
|
|
|
|
private readonly IDialogServiceBase _dialogService;
|
2026-03-06 03:42:08 +01:00
|
|
|
private readonly IProviderService _providerService;
|
|
|
|
|
private readonly INewThemeService _themeService;
|
2026-04-19 20:13:09 +02:00
|
|
|
private ProviderSelectionHostMode _hostMode = ProviderSelectionHostMode.Wizard;
|
2026-03-06 03:42:08 +01:00
|
|
|
|
|
|
|
|
public WelcomeWizardContext WizardContext { get; }
|
|
|
|
|
|
|
|
|
|
public List<IProviderDetail> Providers { get; private set; } = [];
|
|
|
|
|
public List<AppColorViewModel> AvailableColors { get; private set; } = [];
|
2026-04-14 00:03:48 +02:00
|
|
|
public List<InitialSynchronizationRangeOption> InitialSynchronizationRanges { get; } =
|
|
|
|
|
[
|
|
|
|
|
new(InitialSynchronizationRange.ThreeMonths, Translator.AccountCreation_InitialSynchronization_3Months),
|
|
|
|
|
new(InitialSynchronizationRange.SixMonths, Translator.AccountCreation_InitialSynchronization_6Months),
|
|
|
|
|
new(InitialSynchronizationRange.NineMonths, Translator.AccountCreation_InitialSynchronization_9Months),
|
|
|
|
|
new(InitialSynchronizationRange.OneYear, Translator.AccountCreation_InitialSynchronization_Year),
|
|
|
|
|
new(InitialSynchronizationRange.Everything, Translator.AccountCreation_InitialSynchronization_Everything)
|
|
|
|
|
];
|
2026-03-06 03:42:08 +01:00
|
|
|
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
public partial IProviderDetail SelectedProvider { get; set; }
|
|
|
|
|
|
|
|
|
|
[ObservableProperty]
|
2026-04-14 00:03:48 +02:00
|
|
|
[NotifyPropertyChangedFor(nameof(IsColorSelected))]
|
2026-03-06 03:42:08 +01:00
|
|
|
public partial AppColorViewModel SelectedColor { get; set; }
|
|
|
|
|
|
2026-04-14 00:03:48 +02:00
|
|
|
[ObservableProperty]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(IsInitialSynchronizationWarningVisible))]
|
2026-04-20 19:38:30 +02:00
|
|
|
[NotifyPropertyChangedFor(nameof(IsMailSynchronizationRangeVisible))]
|
2026-04-14 00:03:48 +02:00
|
|
|
public partial InitialSynchronizationRangeOption SelectedInitialSynchronizationRange { get; set; }
|
|
|
|
|
|
2026-03-06 03:42:08 +01:00
|
|
|
[ObservableProperty]
|
|
|
|
|
public partial string AccountName { get; set; }
|
|
|
|
|
|
|
|
|
|
[ObservableProperty]
|
2026-04-20 19:38:30 +02:00
|
|
|
[NotifyPropertyChangedFor(nameof(IsMailSynchronizationRangeVisible))]
|
|
|
|
|
public partial bool IsMailAccessEnabled { get; set; } = true;
|
|
|
|
|
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
public partial bool IsCalendarAccessEnabled { get; set; } = true;
|
|
|
|
|
|
|
|
|
|
[ObservableProperty]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(CurrentStepNumber))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(StepProgressValue))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(StepProgressText))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(IsProviderStepVisible))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(IsIdentityStepVisible))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(IsCapabilityStepVisible))]
|
|
|
|
|
[NotifyPropertyChangedFor(nameof(CanGoBack))]
|
|
|
|
|
[NotifyCanExecuteChangedFor(nameof(ContinueCommand))]
|
|
|
|
|
[NotifyCanExecuteChangedFor(nameof(GoBackCommand))]
|
|
|
|
|
public partial ProviderSelectionWizardStep CurrentStep { get; set; } = ProviderSelectionWizardStep.Provider;
|
2026-03-06 03:42:08 +01:00
|
|
|
|
|
|
|
|
public bool IsColorSelected => SelectedColor != null;
|
2026-04-20 19:38:30 +02:00
|
|
|
public bool IsInitialSynchronizationWarningVisible => IsMailSynchronizationRangeVisible && SelectedInitialSynchronizationRange?.IsEverything == true;
|
|
|
|
|
public bool IsMailSynchronizationRangeVisible => IsMailAccessEnabled;
|
|
|
|
|
public int CurrentStepNumber => (int)CurrentStep + 1;
|
|
|
|
|
public double StepProgressValue => CurrentStepNumber;
|
|
|
|
|
public string StepProgressText => string.Format(Translator.ProviderSelection_StepProgress, CurrentStepNumber);
|
|
|
|
|
public bool IsProviderStepVisible => CurrentStep == ProviderSelectionWizardStep.Provider;
|
|
|
|
|
public bool IsIdentityStepVisible => CurrentStep == ProviderSelectionWizardStep.Identity;
|
|
|
|
|
public bool IsCapabilityStepVisible => CurrentStep == ProviderSelectionWizardStep.Capabilities;
|
|
|
|
|
public bool CanGoBack => CurrentStep != ProviderSelectionWizardStep.Provider;
|
|
|
|
|
public string SelectedProviderName => SelectedProvider?.Name ?? string.Empty;
|
|
|
|
|
public string SelectedProviderDescription => SelectedProvider?.Description ?? string.Empty;
|
|
|
|
|
public string SelectedProviderImage => SelectedProvider?.ProviderImage ?? string.Empty;
|
|
|
|
|
public string SelectedProviderCapabilityDescription => GetSelectedProviderCapabilityDescription();
|
|
|
|
|
public bool IsCapabilitySelectionMissing => !IsMailAccessEnabled && !IsCalendarAccessEnabled;
|
|
|
|
|
public bool IsCalendarOnlyServerHintVisible =>
|
|
|
|
|
SelectedProvider?.Type == MailProviderType.IMAP4 &&
|
|
|
|
|
!IsMailAccessEnabled &&
|
|
|
|
|
IsCalendarAccessEnabled;
|
2026-03-06 03:42:08 +01:00
|
|
|
|
|
|
|
|
public ProviderSelectionPageViewModel(
|
2026-04-16 13:45:11 +02:00
|
|
|
IAccountService accountService,
|
|
|
|
|
IDialogServiceBase dialogService,
|
2026-03-06 03:42:08 +01:00
|
|
|
IProviderService providerService,
|
|
|
|
|
INewThemeService themeService,
|
|
|
|
|
WelcomeWizardContext wizardContext)
|
|
|
|
|
{
|
2026-04-16 13:45:11 +02:00
|
|
|
_accountService = accountService;
|
|
|
|
|
_dialogService = dialogService;
|
2026-03-06 03:42:08 +01:00
|
|
|
_providerService = providerService;
|
|
|
|
|
_themeService = themeService;
|
|
|
|
|
WizardContext = wizardContext;
|
2026-04-14 00:03:48 +02:00
|
|
|
SelectedInitialSynchronizationRange = InitialSynchronizationRanges.First(option => option.Range == InitialSynchronizationRange.SixMonths);
|
2026-03-06 03:42:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnNavigatedTo(NavigationMode mode, object parameters)
|
|
|
|
|
{
|
|
|
|
|
base.OnNavigatedTo(mode, parameters);
|
|
|
|
|
|
2026-04-19 20:13:09 +02:00
|
|
|
var navigationContext = parameters as ProviderSelectionNavigationContext
|
|
|
|
|
?? ProviderSelectionNavigationContext.CreateForWizard();
|
|
|
|
|
|
|
|
|
|
_hostMode = navigationContext.HostMode;
|
|
|
|
|
|
|
|
|
|
if (mode != NavigationMode.Back)
|
|
|
|
|
{
|
|
|
|
|
WizardContext.Reset();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-06 03:42:08 +01:00
|
|
|
Providers = _providerService.GetAvailableProviders();
|
|
|
|
|
AvailableColors = _themeService.GetAvailableAccountColors()
|
|
|
|
|
.Select(hex => new AppColorViewModel(hex))
|
|
|
|
|
.ToList();
|
|
|
|
|
|
2026-04-14 00:03:48 +02:00
|
|
|
SelectedInitialSynchronizationRange = InitialSynchronizationRanges
|
|
|
|
|
.FirstOrDefault(option => option.Range == WizardContext.SelectedInitialSynchronizationRange)
|
|
|
|
|
?? InitialSynchronizationRanges.First(option => option.Range == InitialSynchronizationRange.SixMonths);
|
|
|
|
|
|
2026-03-06 03:42:08 +01:00
|
|
|
// Restore from wizard context if navigating back
|
|
|
|
|
if (WizardContext.SelectedProvider != null)
|
|
|
|
|
{
|
|
|
|
|
SelectedProvider = Providers.FirstOrDefault(p =>
|
|
|
|
|
p.Type == WizardContext.SelectedProvider.Type &&
|
|
|
|
|
p.SpecialImapProvider == WizardContext.SelectedProvider.SpecialImapProvider);
|
|
|
|
|
AccountName = WizardContext.AccountName;
|
2026-04-20 19:38:30 +02:00
|
|
|
IsMailAccessEnabled = WizardContext.IsMailAccessEnabled;
|
|
|
|
|
IsCalendarAccessEnabled = WizardContext.IsCalendarAccessEnabled;
|
2026-03-06 03:42:08 +01:00
|
|
|
|
|
|
|
|
if (WizardContext.AccountColorHex != null)
|
|
|
|
|
SelectedColor = AvailableColors.FirstOrDefault(c => c.Hex == WizardContext.AccountColorHex);
|
|
|
|
|
}
|
2026-04-20 19:38:30 +02:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
IsMailAccessEnabled = true;
|
|
|
|
|
IsCalendarAccessEnabled = true;
|
|
|
|
|
}
|
2026-03-06 03:42:08 +01:00
|
|
|
|
2026-04-20 19:38:30 +02:00
|
|
|
CurrentStep = mode == NavigationMode.Back && SelectedProvider != null
|
|
|
|
|
? ProviderSelectionWizardStep.Capabilities
|
|
|
|
|
: ProviderSelectionWizardStep.Provider;
|
2026-03-06 03:42:08 +01:00
|
|
|
}
|
|
|
|
|
|
2026-04-14 00:03:48 +02:00
|
|
|
partial void OnSelectedProviderChanged(IProviderDetail value)
|
|
|
|
|
{
|
2026-04-20 19:38:30 +02:00
|
|
|
OnPropertyChanged(nameof(SelectedProviderName));
|
|
|
|
|
OnPropertyChanged(nameof(SelectedProviderDescription));
|
|
|
|
|
OnPropertyChanged(nameof(SelectedProviderImage));
|
|
|
|
|
OnPropertyChanged(nameof(SelectedProviderCapabilityDescription));
|
|
|
|
|
OnPropertyChanged(nameof(IsCapabilitySelectionMissing));
|
|
|
|
|
OnPropertyChanged(nameof(IsCalendarOnlyServerHintVisible));
|
|
|
|
|
ContinueCommand.NotifyCanExecuteChanged();
|
2026-04-14 00:03:48 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-20 19:38:30 +02:00
|
|
|
partial void OnAccountNameChanged(string value) => ContinueCommand.NotifyCanExecuteChanged();
|
|
|
|
|
|
|
|
|
|
partial void OnIsMailAccessEnabledChanged(bool value)
|
|
|
|
|
{
|
|
|
|
|
OnPropertyChanged(nameof(IsCapabilitySelectionMissing));
|
|
|
|
|
OnPropertyChanged(nameof(IsCalendarOnlyServerHintVisible));
|
|
|
|
|
ContinueCommand.NotifyCanExecuteChanged();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
partial void OnIsCalendarAccessEnabledChanged(bool value)
|
|
|
|
|
{
|
|
|
|
|
OnPropertyChanged(nameof(IsCapabilitySelectionMissing));
|
|
|
|
|
OnPropertyChanged(nameof(IsCalendarOnlyServerHintVisible));
|
|
|
|
|
ContinueCommand.NotifyCanExecuteChanged();
|
|
|
|
|
}
|
2026-03-06 03:42:08 +01:00
|
|
|
|
|
|
|
|
[RelayCommand]
|
|
|
|
|
private void ClearColor() => SelectedColor = null;
|
|
|
|
|
|
2026-04-20 19:38:30 +02:00
|
|
|
private bool CanContinue()
|
2026-03-06 03:42:08 +01:00
|
|
|
{
|
2026-04-20 19:38:30 +02:00
|
|
|
return CurrentStep switch
|
|
|
|
|
{
|
|
|
|
|
ProviderSelectionWizardStep.Provider => SelectedProvider != null,
|
|
|
|
|
ProviderSelectionWizardStep.Identity => !string.IsNullOrWhiteSpace(AccountName),
|
|
|
|
|
ProviderSelectionWizardStep.Capabilities => IsMailAccessEnabled || IsCalendarAccessEnabled,
|
|
|
|
|
_ => false
|
|
|
|
|
};
|
2026-03-06 03:42:08 +01:00
|
|
|
}
|
|
|
|
|
|
2026-04-20 19:38:30 +02:00
|
|
|
[RelayCommand(CanExecute = nameof(CanGoBack))]
|
|
|
|
|
private void GoBack()
|
2026-03-06 03:42:08 +01:00
|
|
|
{
|
2026-04-20 19:38:30 +02:00
|
|
|
if (!CanGoBack)
|
|
|
|
|
return;
|
2026-03-06 03:42:08 +01:00
|
|
|
|
2026-04-20 19:38:30 +02:00
|
|
|
CurrentStep--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[RelayCommand(CanExecute = nameof(CanContinue))]
|
|
|
|
|
private async Task ContinueAsync()
|
|
|
|
|
{
|
|
|
|
|
switch (CurrentStep)
|
2026-04-16 13:45:11 +02:00
|
|
|
{
|
2026-04-20 19:38:30 +02:00
|
|
|
case ProviderSelectionWizardStep.Provider:
|
|
|
|
|
CurrentStep = ProviderSelectionWizardStep.Identity;
|
|
|
|
|
return;
|
|
|
|
|
case ProviderSelectionWizardStep.Identity:
|
|
|
|
|
if (await _accountService.AccountNameExistsAsync(AccountName?.Trim()))
|
|
|
|
|
{
|
|
|
|
|
await _dialogService.ShowMessageAsync(
|
|
|
|
|
Translator.DialogMessage_AccountNameExistsMessage,
|
|
|
|
|
Translator.DialogMessage_AccountExistsTitle,
|
|
|
|
|
WinoCustomMessageDialogIcon.Warning);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CurrentStep = ProviderSelectionWizardStep.Capabilities;
|
|
|
|
|
return;
|
|
|
|
|
case ProviderSelectionWizardStep.Capabilities:
|
|
|
|
|
await CompleteWizardAsync();
|
|
|
|
|
return;
|
2026-04-16 13:45:11 +02:00
|
|
|
}
|
2026-04-20 19:38:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task CompleteWizardAsync()
|
|
|
|
|
{
|
|
|
|
|
if (!CanContinue())
|
|
|
|
|
return;
|
2026-04-16 13:45:11 +02:00
|
|
|
|
2026-03-06 03:42:08 +01:00
|
|
|
WizardContext.SelectedProvider = SelectedProvider;
|
|
|
|
|
WizardContext.AccountName = AccountName?.Trim();
|
|
|
|
|
WizardContext.AccountColorHex = SelectedColor?.Hex ?? string.Empty;
|
2026-04-14 00:03:48 +02:00
|
|
|
WizardContext.SelectedInitialSynchronizationRange = SelectedInitialSynchronizationRange?.Range ?? InitialSynchronizationRange.SixMonths;
|
2026-04-20 19:38:30 +02:00
|
|
|
WizardContext.IsMailAccessEnabled = IsMailAccessEnabled;
|
|
|
|
|
WizardContext.IsCalendarAccessEnabled = IsCalendarAccessEnabled;
|
2026-03-06 03:42:08 +01:00
|
|
|
|
|
|
|
|
if (WizardContext.IsGenericImap)
|
|
|
|
|
{
|
2026-04-19 20:13:09 +02:00
|
|
|
var context = _hostMode == ProviderSelectionHostMode.SettingsAddAccount
|
|
|
|
|
? ImapCalDavSettingsNavigationContext.CreateForAddAccountMode(
|
|
|
|
|
WizardContext.BuildAccountCreationDialogResult())
|
|
|
|
|
: ImapCalDavSettingsNavigationContext.CreateForWizardMode(
|
|
|
|
|
WizardContext.BuildAccountCreationDialogResult());
|
2026-03-06 03:42:08 +01:00
|
|
|
|
|
|
|
|
Messenger.Send(new BreadcrumbNavigationRequested(
|
|
|
|
|
Translator.ImapCalDavSettingsPage_TitleCreate,
|
|
|
|
|
WinoPage.ImapCalDavSettingsPage,
|
|
|
|
|
context));
|
|
|
|
|
}
|
|
|
|
|
else if (SelectedProvider.SpecialImapProvider is SpecialImapProvider.iCloud or SpecialImapProvider.Yahoo)
|
|
|
|
|
{
|
|
|
|
|
// Navigate to credentials page for special IMAP providers
|
|
|
|
|
Messenger.Send(new BreadcrumbNavigationRequested(
|
|
|
|
|
SelectedProvider.Name,
|
|
|
|
|
WinoPage.SpecialImapCredentialsPage));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// OAuth — go directly to progress page
|
|
|
|
|
Messenger.Send(new BreadcrumbNavigationRequested(
|
|
|
|
|
Translator.WelcomeWizard_Step3Title,
|
|
|
|
|
WinoPage.AccountSetupProgressPage));
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-20 19:38:30 +02:00
|
|
|
|
|
|
|
|
partial void OnSelectedProviderChanging(IProviderDetail value)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string GetSelectedProviderCapabilityDescription()
|
|
|
|
|
{
|
|
|
|
|
if (SelectedProvider == null)
|
|
|
|
|
return string.Empty;
|
|
|
|
|
|
|
|
|
|
if (SelectedProvider.Type is MailProviderType.Outlook or MailProviderType.Gmail)
|
|
|
|
|
return Translator.ProviderSelection_CapabilityProviderDescription_OAuth;
|
|
|
|
|
|
|
|
|
|
if (SelectedProvider.SpecialImapProvider is SpecialImapProvider.iCloud or SpecialImapProvider.Yahoo)
|
|
|
|
|
return Translator.ProviderSelection_CapabilityProviderDescription_SpecialImap;
|
|
|
|
|
|
|
|
|
|
return Translator.ProviderSelection_CapabilityProviderDescription_CustomServer;
|
|
|
|
|
}
|
2026-03-06 03:42:08 +01:00
|
|
|
}
|