Add capability-first account and calendar setup flow
This commit is contained in:
@@ -45,6 +45,6 @@ public class GmailAuthenticator : BaseAuthenticator, IGmailAuthenticator
|
|||||||
return GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets()
|
return GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets()
|
||||||
{
|
{
|
||||||
ClientId = ClientId
|
ClientId = ClientId
|
||||||
}, AuthenticatorConfig.GmailScope, account.Id.ToString(), CancellationToken.None, new FileDataStore(AuthenticatorConfig.GmailTokenStoreIdentifier));
|
}, AuthenticatorConfig.GetGmailScope(account?.IsMailAccessGranted != false, account?.IsCalendarAccessGranted == true), account.Id.ToString(), CancellationToken.None, new FileDataStore(AuthenticatorConfig.GmailTokenStoreIdentifier));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,10 @@ public class OutlookAuthenticator : BaseAuthenticator, IOutlookAuthenticator
|
|||||||
_publicClientApplication = outlookAppBuilder.Build();
|
_publicClientApplication = outlookAppBuilder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] Scope => AuthenticatorConfig.OutlookScope;
|
private string[] GetScope(MailAccount account)
|
||||||
|
=> AuthenticatorConfig.GetOutlookScope(
|
||||||
|
account?.IsMailAccessGranted != false,
|
||||||
|
account?.IsCalendarAccessGranted == true);
|
||||||
|
|
||||||
private async Task EnsureTokenCacheAttachedAsync()
|
private async Task EnsureTokenCacheAttachedAsync()
|
||||||
{
|
{
|
||||||
@@ -91,7 +94,7 @@ public class OutlookAuthenticator : BaseAuthenticator, IOutlookAuthenticator
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var authResult = await _publicClientApplication.AcquireTokenSilent(Scope, storedAccount).ExecuteAsync();
|
var authResult = await _publicClientApplication.AcquireTokenSilent(GetScope(account), storedAccount).ExecuteAsync();
|
||||||
|
|
||||||
return new TokenInformationEx(authResult.AccessToken, authResult.Account.Username);
|
return new TokenInformationEx(authResult.AccessToken, authResult.Account.Username);
|
||||||
}
|
}
|
||||||
@@ -122,7 +125,7 @@ public class OutlookAuthenticator : BaseAuthenticator, IOutlookAuthenticator
|
|||||||
if (_nativeAppService.GetCoreWindowHwnd == null) throw new AuthenticationAttentionException(account);
|
if (_nativeAppService.GetCoreWindowHwnd == null) throw new AuthenticationAttentionException(account);
|
||||||
|
|
||||||
AuthenticationResult authResult = await _publicClientApplication
|
AuthenticationResult authResult = await _publicClientApplication
|
||||||
.AcquireTokenInteractive(Scope)
|
.AcquireTokenInteractive(GetScope(account))
|
||||||
.ExecuteAsync();
|
.ExecuteAsync();
|
||||||
|
|
||||||
// If the account is null, it means it's the initial creation of it.
|
// If the account is null, it means it's the initial creation of it.
|
||||||
|
|||||||
@@ -299,6 +299,9 @@ public partial class CalendarAppShellViewModel : CalendarBaseViewModel,
|
|||||||
|
|
||||||
foreach (var account in accounts)
|
foreach (var account in accounts)
|
||||||
{
|
{
|
||||||
|
if (!GroupedAccountCalendarViewModel.SupportsCalendar(account))
|
||||||
|
continue;
|
||||||
|
|
||||||
var accountCalendars = await _calendarService.GetAccountCalendarsAsync(account.Id).ConfigureAwait(false);
|
var accountCalendars = await _calendarService.GetAccountCalendarsAsync(account.Id).ConfigureAwait(false);
|
||||||
var calendarViewModels = accountCalendars.Select(calendar => new AccountCalendarViewModel(account, calendar)).ToList();
|
var calendarViewModels = accountCalendars.Select(calendar => new AccountCalendarViewModel(account, calendar)).ToList();
|
||||||
var groupedAccountCalendarViewModel = new GroupedAccountCalendarViewModel(account, calendarViewModels);
|
var groupedAccountCalendarViewModel = new GroupedAccountCalendarViewModel(account, calendarViewModels);
|
||||||
|
|||||||
@@ -408,6 +408,9 @@ public partial class CalendarEventComposePageViewModel : CalendarBaseViewModel
|
|||||||
|
|
||||||
foreach (var account in accounts)
|
foreach (var account in accounts)
|
||||||
{
|
{
|
||||||
|
if (!GroupedAccountCalendarViewModel.SupportsCalendar(account))
|
||||||
|
continue;
|
||||||
|
|
||||||
var calendars = await _calendarService.GetAccountCalendarsAsync(account.Id).ConfigureAwait(false);
|
var calendars = await _calendarService.GetAccountCalendarsAsync(account.Id).ConfigureAwait(false);
|
||||||
var viewModels = calendars
|
var viewModels = calendars
|
||||||
.Select(calendar => new AccountCalendarViewModel(account, calendar))
|
.Select(calendar => new AccountCalendarViewModel(account, calendar))
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ public partial class GroupedAccountCalendarViewModel : ObservableObject
|
|||||||
public MailAccount Account { get; }
|
public MailAccount Account { get; }
|
||||||
public ObservableCollection<AccountCalendarViewModel> AccountCalendars { get; }
|
public ObservableCollection<AccountCalendarViewModel> AccountCalendars { get; }
|
||||||
|
|
||||||
|
public static bool SupportsCalendar(MailAccount account)
|
||||||
|
=> account?.IsCalendarAccessGranted == true;
|
||||||
|
|
||||||
public GroupedAccountCalendarViewModel(MailAccount account, IEnumerable<AccountCalendarViewModel> calendarViewModels)
|
public GroupedAccountCalendarViewModel(MailAccount account, IEnumerable<AccountCalendarViewModel> calendarViewModels)
|
||||||
{
|
{
|
||||||
Account = account;
|
Account = account;
|
||||||
|
|||||||
@@ -78,6 +78,13 @@ public class MailAccount
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public SpecialImapProvider SpecialImapProvider { get; set; }
|
public SpecialImapProvider SpecialImapProvider { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether mail access is granted for this account.
|
||||||
|
/// When false, mail folders, aliases, compose flows, and mail synchronization are unavailable.
|
||||||
|
/// Default is true for legacy accounts to preserve existing behavior.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsMailAccessGranted { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets whether calendar access is granted for this account.
|
/// Gets or sets whether calendar access is granted for this account.
|
||||||
/// When false, synchronizers will not process EventMessages or calendar invitations.
|
/// When false, synchronizers will not process EventMessages or calendar invitations.
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
public interface IAuthenticatorConfig
|
public interface IAuthenticatorConfig
|
||||||
{
|
{
|
||||||
string OutlookAuthenticatorClientId { get; }
|
string OutlookAuthenticatorClientId { get; }
|
||||||
string[] OutlookScope { get; }
|
string[] GetOutlookScope(bool isMailAccessGranted, bool isCalendarAccessGranted);
|
||||||
string GmailAuthenticatorClientId { get; }
|
string GmailAuthenticatorClientId { get; }
|
||||||
string[] GmailScope { get; }
|
string[] GetGmailScope(bool isMailAccessGranted, bool isCalendarAccessGranted);
|
||||||
string GmailTokenStoreIdentifier { get; }
|
string GmailTokenStoreIdentifier { get; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,4 +7,6 @@ public record AccountCreationDialogResult(
|
|||||||
string AccountName,
|
string AccountName,
|
||||||
SpecialImapProviderDetails SpecialImapProviderDetails,
|
SpecialImapProviderDetails SpecialImapProviderDetails,
|
||||||
string AccountColorHex,
|
string AccountColorHex,
|
||||||
InitialSynchronizationRange InitialSynchronizationRange);
|
InitialSynchronizationRange InitialSynchronizationRange,
|
||||||
|
bool IsMailAccessGranted,
|
||||||
|
bool IsCalendarAccessGranted);
|
||||||
|
|||||||
@@ -47,6 +47,11 @@
|
|||||||
"AccountDetailsPage_CalendarListDescription": "Select a calendar to configure its settings",
|
"AccountDetailsPage_CalendarListDescription": "Select a calendar to configure its settings",
|
||||||
"AccountDetailsPage_InitialSynchronization_Title": "Initial synchronization",
|
"AccountDetailsPage_InitialSynchronization_Title": "Initial synchronization",
|
||||||
"AccountDetailsPage_InitialSynchronization_Description": "Wino synchronized your mails until {0} going back.",
|
"AccountDetailsPage_InitialSynchronization_Description": "Wino synchronized your mails until {0} going back.",
|
||||||
|
"AccountDetailsPage_CapabilityTitle": "Connected features",
|
||||||
|
"AccountDetailsPage_CapabilityDescription": "Choose whether this account is used for mail, calendar, or both. Enabling a new feature may ask you to sign in again.",
|
||||||
|
"AccountCapability_MailOnly": "Mail only",
|
||||||
|
"AccountCapability_CalendarOnly": "Calendar only",
|
||||||
|
"AccountCapability_MailAndCalendar": "Mail + Calendar",
|
||||||
"AddHyperlink": "Add",
|
"AddHyperlink": "Add",
|
||||||
"AppCloseBackgroundSynchronizationWarningTitle": "Background Synchronization",
|
"AppCloseBackgroundSynchronizationWarningTitle": "Background Synchronization",
|
||||||
"AppCloseStartupLaunchDisabledWarningMessageFirstLine": "Application has not been set to launch on Windows startup.",
|
"AppCloseStartupLaunchDisabledWarningMessageFirstLine": "Application has not been set to launch on Windows startup.",
|
||||||
@@ -76,6 +81,7 @@
|
|||||||
"Buttons_ApplyTheme": "Apply Theme",
|
"Buttons_ApplyTheme": "Apply Theme",
|
||||||
"Buttons_PopOut": "Pop out",
|
"Buttons_PopOut": "Pop out",
|
||||||
"Buttons_Browse": "Browse",
|
"Buttons_Browse": "Browse",
|
||||||
|
"Buttons_Back": "Back",
|
||||||
"Buttons_Cancel": "Cancel",
|
"Buttons_Cancel": "Cancel",
|
||||||
"Buttons_Close": "Close",
|
"Buttons_Close": "Close",
|
||||||
"Buttons_Copy": "Copy",
|
"Buttons_Copy": "Copy",
|
||||||
@@ -681,6 +687,10 @@
|
|||||||
"NoMailSelected": "No message selected",
|
"NoMailSelected": "No message selected",
|
||||||
"NoMessageCrieteria": "No messages match your search criteria",
|
"NoMessageCrieteria": "No messages match your search criteria",
|
||||||
"NoMessageEmptyFolder": "This folder is empty",
|
"NoMessageEmptyFolder": "This folder is empty",
|
||||||
|
"MailEmptyState_Title": "No mail-enabled accounts",
|
||||||
|
"MailEmptyState_Message": "You have accounts connected for calendar, but none of them are enabled for mail. Add a mail account or update an existing account to use mail.",
|
||||||
|
"MailEmptyState_AddAccount": "Add account",
|
||||||
|
"MailEmptyState_ManageAccounts": "Manage accounts",
|
||||||
"Notifications_MultipleNotificationsMessage": "You have {0} new messages.",
|
"Notifications_MultipleNotificationsMessage": "You have {0} new messages.",
|
||||||
"Notifications_MultipleNotificationsTitle": "New Mail",
|
"Notifications_MultipleNotificationsTitle": "New Mail",
|
||||||
"Notifications_WinoUpdatedMessage": "Checkout new version {0}",
|
"Notifications_WinoUpdatedMessage": "Checkout new version {0}",
|
||||||
@@ -702,8 +712,8 @@
|
|||||||
"ProviderDetail_Gmail_Description": "Google Account",
|
"ProviderDetail_Gmail_Description": "Google Account",
|
||||||
"ProviderDetail_iCloud_Description": "Apple iCloud Account",
|
"ProviderDetail_iCloud_Description": "Apple iCloud Account",
|
||||||
"ProviderDetail_iCloud_Title": "iCloud",
|
"ProviderDetail_iCloud_Title": "iCloud",
|
||||||
"ProviderDetail_IMAP_Description": "Custom IMAP/SMTP server",
|
"ProviderDetail_IMAP_Description": "IMAP/SMTP mail with CalDAV or local calendar",
|
||||||
"ProviderDetail_IMAP_Title": "IMAP Server",
|
"ProviderDetail_IMAP_Title": "Custom server",
|
||||||
"ProviderDetail_Yahoo_Description": "Yahoo Account",
|
"ProviderDetail_Yahoo_Description": "Yahoo Account",
|
||||||
"ProviderDetail_Yahoo_Title": "Yahoo Mail",
|
"ProviderDetail_Yahoo_Title": "Yahoo Mail",
|
||||||
"QuickEventDialog_EventName": "Event name",
|
"QuickEventDialog_EventName": "Event name",
|
||||||
@@ -1427,8 +1437,8 @@
|
|||||||
"WinoAccount_Management_ExportDialog_InProgress": "Exporting your selected Wino data...",
|
"WinoAccount_Management_ExportDialog_InProgress": "Exporting your selected Wino data...",
|
||||||
"WinoAccount_Management_LocalDataSectionTitle": "Transfer with a JSON file",
|
"WinoAccount_Management_LocalDataSectionTitle": "Transfer with a JSON file",
|
||||||
"WinoAccount_Management_LocalDataSectionDescription": "Import from or export to a local JSON file. Passwords, tokens, and other sensitive information are not included.",
|
"WinoAccount_Management_LocalDataSectionDescription": "Import from or export to a local JSON file. Passwords, tokens, and other sensitive information are not included.",
|
||||||
"WinoAccount_Management_LocalDataImportAction": "Import JSON",
|
"WinoAccount_Management_LocalDataImportAction": "Import",
|
||||||
"WinoAccount_Management_LocalDataExportAction": "Export JSON",
|
"WinoAccount_Management_LocalDataExportAction": "Export",
|
||||||
"WinoAccount_Management_LocalDataSaved": "Saved your exported Wino data to {0}.",
|
"WinoAccount_Management_LocalDataSaved": "Saved your exported Wino data to {0}.",
|
||||||
"WinoAccount_Management_LocalDataInvalidFile": "The selected JSON file doesn't contain a valid Wino export.",
|
"WinoAccount_Management_LocalDataInvalidFile": "The selected JSON file doesn't contain a valid Wino export.",
|
||||||
"WinoAccount_Management_LoadFailed": "Wino could not load the latest Wino Account information.",
|
"WinoAccount_Management_LoadFailed": "Wino could not load the latest Wino Account information.",
|
||||||
@@ -1527,8 +1537,22 @@
|
|||||||
"WelcomeWizard_Step3Title": "Finish Setup",
|
"WelcomeWizard_Step3Title": "Finish Setup",
|
||||||
"ProviderSelection_Title": "Choose your email provider",
|
"ProviderSelection_Title": "Choose your email provider",
|
||||||
"ProviderSelection_Subtitle": "Select a provider below to add your email account to Wino Mail.",
|
"ProviderSelection_Subtitle": "Select a provider below to add your email account to Wino Mail.",
|
||||||
|
"ProviderSelection_StepProgress": "Step {0} of 3",
|
||||||
|
"ProviderSelection_IdentityTitle": "Account identity",
|
||||||
|
"ProviderSelection_IdentityDescription": "Choose how this account appears inside Wino.",
|
||||||
|
"ProviderSelection_ProviderSectionTitle": "Provider",
|
||||||
|
"ProviderSelection_ProviderSectionDescription": "Select the service you want to connect.",
|
||||||
|
"ProviderSelection_CapabilitySectionTitle": "Use this account for",
|
||||||
|
"ProviderSelection_CapabilitySectionDescription": "Choose whether you want mail, calendar, or both.",
|
||||||
|
"ProviderSelection_CapabilityProviderDescription_OAuth": "On the next step, secure sign-in will connect your account. If you enable calendar, Wino will also connect Outlook Calendar or Google Calendar automatically.",
|
||||||
|
"ProviderSelection_CapabilityProviderDescription_SpecialImap": "On the next step, you'll enter your provider credentials. Mail uses IMAP/SMTP, and calendar can use CalDAV or stay local on this device.",
|
||||||
|
"ProviderSelection_CapabilityProviderDescription_CustomServer": "On the next step, you'll enter your server details. Mail uses IMAP/SMTP, and calendar can use CalDAV or stay local on this device.",
|
||||||
"ProviderSelection_AccountNameHeader": "Account Name",
|
"ProviderSelection_AccountNameHeader": "Account Name",
|
||||||
"ProviderSelection_AccountNamePlaceholder": "e.g. Personal, Work",
|
"ProviderSelection_AccountNamePlaceholder": "e.g. Personal, Work",
|
||||||
|
"ProviderSelection_UseForMail": "Mail",
|
||||||
|
"ProviderSelection_UseForCalendar": "Calendar",
|
||||||
|
"ProviderSelection_CapabilityValidationMessage": "Choose at least one capability before continuing.",
|
||||||
|
"ProviderSelection_CalendarOnlyServerHint": "If you continue with calendar only, the next page will not require an email address.",
|
||||||
"ProviderSelection_DisplayNameHeader": "Display Name",
|
"ProviderSelection_DisplayNameHeader": "Display Name",
|
||||||
"ProviderSelection_DisplayNamePlaceholder": "e.g. John Doe",
|
"ProviderSelection_DisplayNamePlaceholder": "e.g. John Doe",
|
||||||
"ProviderSelection_EmailHeader": "E-mail Address",
|
"ProviderSelection_EmailHeader": "E-mail Address",
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
using Xunit;
|
||||||
|
|
||||||
|
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
||||||
@@ -83,7 +83,7 @@ public class AccountServiceTests : IAsyncLifetime
|
|||||||
var secondAccountId = Guid.NewGuid();
|
var secondAccountId = Guid.NewGuid();
|
||||||
|
|
||||||
await _accountService.CreateAccountAsync(
|
await _accountService.CreateAccountAsync(
|
||||||
CreateImapAccount(firstAccountId),
|
CreateImapAccount(firstAccountId, "IMAP Test Account 1", "imap1@test.local"),
|
||||||
new CustomServerInformation
|
new CustomServerInformation
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
@@ -92,7 +92,7 @@ public class AccountServiceTests : IAsyncLifetime
|
|||||||
});
|
});
|
||||||
|
|
||||||
await _accountService.CreateAccountAsync(
|
await _accountService.CreateAccountAsync(
|
||||||
CreateImapAccount(secondAccountId),
|
CreateImapAccount(secondAccountId, "IMAP Test Account 2", "imap2@test.local"),
|
||||||
new CustomServerInformation
|
new CustomServerInformation
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
@@ -119,13 +119,13 @@ public class AccountServiceTests : IAsyncLifetime
|
|||||||
.BeGreaterThanOrEqualTo(50);
|
.BeGreaterThanOrEqualTo(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MailAccount CreateImapAccount(Guid accountId)
|
private static MailAccount CreateImapAccount(Guid accountId, string name = "IMAP Test Account", string address = "imap@test.local")
|
||||||
{
|
{
|
||||||
return new MailAccount
|
return new MailAccount
|
||||||
{
|
{
|
||||||
Id = accountId,
|
Id = accountId,
|
||||||
Name = "IMAP Test Account",
|
Name = name,
|
||||||
Address = "imap@test.local",
|
Address = address,
|
||||||
SenderName = "IMAP Test",
|
SenderName = "IMAP Test",
|
||||||
ProviderType = MailProviderType.IMAP4
|
ProviderType = MailProviderType.IMAP4
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ public sealed class MailRequestStateTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void MarkReadRequest_RevertUiChanges_RestoresOriginalReadState()
|
public void MarkReadRequest_RevertUiChanges_RestoresOriginalReadState()
|
||||||
{
|
{
|
||||||
|
WeakReferenceMessenger.Default.Reset();
|
||||||
|
|
||||||
var mailCopy = CreateMailCopy(isRead: false, isFlagged: false);
|
var mailCopy = CreateMailCopy(isRead: false, isFlagged: false);
|
||||||
var request = new MarkReadRequest(mailCopy, IsRead: true);
|
var request = new MarkReadRequest(mailCopy, IsRead: true);
|
||||||
var recipient = new MailRequestRecipient();
|
var recipient = new MailRequestRecipient();
|
||||||
@@ -35,12 +37,15 @@ public sealed class MailRequestStateTests
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
WeakReferenceMessenger.Default.UnregisterAll(recipient);
|
WeakReferenceMessenger.Default.UnregisterAll(recipient);
|
||||||
|
WeakReferenceMessenger.Default.Reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ChangeFlagRequest_RevertUiChanges_RestoresOriginalFlagState()
|
public void ChangeFlagRequest_RevertUiChanges_RestoresOriginalFlagState()
|
||||||
{
|
{
|
||||||
|
WeakReferenceMessenger.Default.Reset();
|
||||||
|
|
||||||
var mailCopy = CreateMailCopy(isRead: true, isFlagged: false);
|
var mailCopy = CreateMailCopy(isRead: true, isFlagged: false);
|
||||||
var request = new ChangeFlagRequest(mailCopy, IsFlagged: true);
|
var request = new ChangeFlagRequest(mailCopy, IsFlagged: true);
|
||||||
var recipient = new MailRequestRecipient();
|
var recipient = new MailRequestRecipient();
|
||||||
@@ -63,6 +68,7 @@ public sealed class MailRequestStateTests
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
WeakReferenceMessenger.Default.UnregisterAll(recipient);
|
WeakReferenceMessenger.Default.UnregisterAll(recipient);
|
||||||
|
WeakReferenceMessenger.Default.Reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ public sealed class BaseSynchronizerUiChangeTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void ApplyOptimisticUiChanges_UsesBundleUiChangeRequest_ForBatchBundle()
|
public void ApplyOptimisticUiChanges_UsesBundleUiChangeRequest_ForBatchBundle()
|
||||||
{
|
{
|
||||||
|
WeakReferenceMessenger.Default.Reset();
|
||||||
|
|
||||||
var folderId = Guid.NewGuid();
|
var folderId = Guid.NewGuid();
|
||||||
var account = new MailAccount { Id = Guid.NewGuid(), Name = "Test account" };
|
var account = new MailAccount { Id = Guid.NewGuid(), Name = "Test account" };
|
||||||
var synchronizer = new TestSynchronizer(account);
|
var synchronizer = new TestSynchronizer(account);
|
||||||
@@ -48,6 +50,7 @@ public sealed class BaseSynchronizerUiChangeTests
|
|||||||
{
|
{
|
||||||
WeakReferenceMessenger.Default.Unregister<MailStateUpdatedMessage>(recipient);
|
WeakReferenceMessenger.Default.Unregister<MailStateUpdatedMessage>(recipient);
|
||||||
WeakReferenceMessenger.Default.Unregister<BulkMailStateUpdatedMessage>(recipient);
|
WeakReferenceMessenger.Default.Unregister<BulkMailStateUpdatedMessage>(recipient);
|
||||||
|
WeakReferenceMessenger.Default.Reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Generic;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
@@ -9,6 +10,7 @@ using Wino.Core.Domain.Entities.Shared;
|
|||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Domain.Models.Navigation;
|
using Wino.Core.Domain.Models.Navigation;
|
||||||
|
using Wino.Core.ViewModels.Data;
|
||||||
using Wino.Mail.ViewModels.Data;
|
using Wino.Mail.ViewModels.Data;
|
||||||
using Wino.Messaging.Client.Navigation;
|
using Wino.Messaging.Client.Navigation;
|
||||||
|
|
||||||
@@ -17,6 +19,7 @@ namespace Wino.Core.ViewModels;
|
|||||||
public abstract partial class AccountManagementPageViewModelBase : CoreBaseViewModel
|
public abstract partial class AccountManagementPageViewModelBase : CoreBaseViewModel
|
||||||
{
|
{
|
||||||
public ObservableCollection<IAccountProviderDetailViewModel> Accounts { get; set; } = [];
|
public ObservableCollection<IAccountProviderDetailViewModel> Accounts { get; set; } = [];
|
||||||
|
public IEnumerable<IAccountProviderDetailViewModel> StartupAccounts => Accounts.Where(IsStartupEligible);
|
||||||
|
|
||||||
public bool IsPurchasePanelVisible => !HasUnlimitedAccountProduct;
|
public bool IsPurchasePanelVisible => !HasUnlimitedAccountProduct;
|
||||||
public bool IsAccountCreationAlmostOnLimit => Accounts != null && Accounts.Count == FREE_ACCOUNT_COUNT - 1;
|
public bool IsAccountCreationAlmostOnLimit => Accounts != null && Accounts.Count == FREE_ACCOUNT_COUNT - 1;
|
||||||
@@ -130,10 +133,21 @@ public abstract partial class AccountManagementPageViewModelBase : CoreBaseViewM
|
|||||||
private void AccountsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
private void AccountsChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
OnPropertyChanged(nameof(HasAccountsDefined));
|
OnPropertyChanged(nameof(HasAccountsDefined));
|
||||||
|
OnPropertyChanged(nameof(StartupAccounts));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetAccountDetailsTitle(MailAccount account)
|
private static string GetAccountDetailsTitle(MailAccount account)
|
||||||
=> !string.IsNullOrWhiteSpace(account?.Address)
|
=> !string.IsNullOrWhiteSpace(account?.Address)
|
||||||
? string.Format(Translator.SettingsAccountDetails_NavigationTitle, account.Address)
|
? string.Format(Translator.SettingsAccountDetails_NavigationTitle, account.Address)
|
||||||
: account?.Name ?? Translator.AccountDetailsPage_Title;
|
: account?.Name ?? Translator.AccountDetailsPage_Title;
|
||||||
|
|
||||||
|
private static bool IsStartupEligible(IAccountProviderDetailViewModel account)
|
||||||
|
{
|
||||||
|
return account switch
|
||||||
|
{
|
||||||
|
AccountProviderDetailViewModel accountViewModel => accountViewModel.Account.IsMailAccessGranted,
|
||||||
|
MergedAccountProviderDetailViewModel mergedAccountViewModel => mergedAccountViewModel.HoldingAccounts.Any(a => a.Account.IsMailAccessGranted),
|
||||||
|
_ => true
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using Wino.Core.Domain;
|
||||||
using Wino.Core.Domain.Entities.Shared;
|
using Wino.Core.Domain.Entities.Shared;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
|
||||||
@@ -9,6 +10,8 @@ public partial class AccountProviderDetailViewModel : ObservableObject, IAccount
|
|||||||
{
|
{
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
|
[NotifyPropertyChangedFor(nameof(CapabilitySummary))]
|
||||||
|
[NotifyPropertyChangedFor(nameof(DescriptionText))]
|
||||||
private MailAccount account;
|
private MailAccount account;
|
||||||
|
|
||||||
public IProviderDetail ProviderDetail { get; set; }
|
public IProviderDetail ProviderDetail { get; set; }
|
||||||
@@ -20,6 +23,10 @@ public partial class AccountProviderDetailViewModel : ObservableObject, IAccount
|
|||||||
public int Order => Account.Order;
|
public int Order => Account.Order;
|
||||||
|
|
||||||
public string StartupEntityAddresses => Account.Address;
|
public string StartupEntityAddresses => Account.Address;
|
||||||
|
public string CapabilitySummary => BuildCapabilitySummary(Account);
|
||||||
|
public string DescriptionText => string.IsNullOrWhiteSpace(Account.Address)
|
||||||
|
? CapabilitySummary
|
||||||
|
: $"{CapabilitySummary} | {Account.Address}";
|
||||||
|
|
||||||
public int HoldingAccountCount => 1;
|
public int HoldingAccountCount => 1;
|
||||||
|
|
||||||
@@ -30,4 +37,15 @@ public partial class AccountProviderDetailViewModel : ObservableObject, IAccount
|
|||||||
ProviderDetail = providerDetail;
|
ProviderDetail = providerDetail;
|
||||||
Account = account;
|
Account = account;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string BuildCapabilitySummary(MailAccount account)
|
||||||
|
{
|
||||||
|
if (account?.IsMailAccessGranted == true && account.IsCalendarAccessGranted)
|
||||||
|
return Translator.AccountCapability_MailAndCalendar;
|
||||||
|
|
||||||
|
if (account?.IsMailAccessGranted == true)
|
||||||
|
return Translator.AccountCapability_MailOnly;
|
||||||
|
|
||||||
|
return Translator.AccountCapability_CalendarOnly;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ using Wino.Core.Domain.Interfaces;
|
|||||||
using Wino.Core.Domain.Models.Accounts;
|
using Wino.Core.Domain.Models.Accounts;
|
||||||
using Wino.Core.Domain.Models.Folders;
|
using Wino.Core.Domain.Models.Folders;
|
||||||
using Wino.Core.Domain.Models.Navigation;
|
using Wino.Core.Domain.Models.Navigation;
|
||||||
|
using Wino.Core.Domain.Models.Synchronization;
|
||||||
using Wino.Core.Misc;
|
using Wino.Core.Misc;
|
||||||
using Wino.Core.Services;
|
using Wino.Core.Services;
|
||||||
using Wino.Core.ViewModels.Data;
|
using Wino.Core.ViewModels.Data;
|
||||||
@@ -96,8 +97,14 @@ public partial class AccountDetailsPageViewModel : MailBaseViewModel
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool isTaskbarBadgeEnabled;
|
private bool isTaskbarBadgeEnabled;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public partial AccountCapabilityOption SelectedCapabilityOption { get; set; }
|
||||||
|
|
||||||
public bool IsFocusedInboxSupportedForAccount => Account != null && Account.Preferences.IsFocusedInboxEnabled != null;
|
public bool IsFocusedInboxSupportedForAccount => Account != null && Account.Preferences.IsFocusedInboxEnabled != null;
|
||||||
public bool IsImapServer => ServerInformation != null;
|
public bool IsImapServer => ServerInformation != null;
|
||||||
|
public bool HasMailAccess => Account?.IsMailAccessGranted == true;
|
||||||
|
public bool HasCalendarAccess => Account?.IsCalendarAccessGranted == true;
|
||||||
|
public bool IsOAuthCapabilityEditable => Account?.ProviderType is MailProviderType.Outlook or MailProviderType.Gmail;
|
||||||
public string ProviderIconPath => Account?.SpecialImapProvider != SpecialImapProvider.None
|
public string ProviderIconPath => Account?.SpecialImapProvider != SpecialImapProvider.None
|
||||||
? $"ms-appx:///Assets/Providers/{Account.SpecialImapProvider}.png"
|
? $"ms-appx:///Assets/Providers/{Account.SpecialImapProvider}.png"
|
||||||
: $"ms-appx:///Assets/Providers/{Account?.ProviderType}.png";
|
: $"ms-appx:///Assets/Providers/{Account?.ProviderType}.png";
|
||||||
@@ -130,6 +137,13 @@ public partial class AccountDetailsPageViewModel : MailBaseViewModel
|
|||||||
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.None, Translator.ImapConnectionSecurity_None)
|
new ImapConnectionSecurityModel(Core.Domain.Enums.ImapConnectionSecurity.None, Translator.ImapConnectionSecurity_None)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public List<AccountCapabilityOption> CapabilityOptions { get; } =
|
||||||
|
[
|
||||||
|
new(true, false, Translator.AccountCapability_MailOnly),
|
||||||
|
new(false, true, Translator.AccountCapability_CalendarOnly),
|
||||||
|
new(true, true, Translator.AccountCapability_MailAndCalendar)
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
public AccountDetailsPageViewModel(IMailDialogService dialogService,
|
public AccountDetailsPageViewModel(IMailDialogService dialogService,
|
||||||
IAccountService accountService,
|
IAccountService accountService,
|
||||||
@@ -262,6 +276,7 @@ public partial class AccountDetailsPageViewModel : MailBaseViewModel
|
|||||||
AccountName = Account.Name;
|
AccountName = Account.Name;
|
||||||
SenderName = Account.SenderName;
|
SenderName = Account.SenderName;
|
||||||
ServerInformation = Account.ServerInformation;
|
ServerInformation = Account.ServerInformation;
|
||||||
|
SelectedCapabilityOption = ResolveCapabilityOption(Account.IsMailAccessGranted, Account.IsCalendarAccessGranted);
|
||||||
|
|
||||||
IsFocusedInboxEnabled = Account.Preferences.IsFocusedInboxEnabled.GetValueOrDefault();
|
IsFocusedInboxEnabled = Account.Preferences.IsFocusedInboxEnabled.GetValueOrDefault();
|
||||||
AreNotificationsEnabled = Account.Preferences.IsNotificationsEnabled;
|
AreNotificationsEnabled = Account.Preferences.IsNotificationsEnabled;
|
||||||
@@ -288,7 +303,11 @@ public partial class AccountDetailsPageViewModel : MailBaseViewModel
|
|||||||
SelectedOutgoingServerConnectionSecurityIndex = AvailableConnectionSecurities.FindIndex(a => a.ImapConnectionSecurity == ServerInformation.OutgoingServerSocketOption);
|
SelectedOutgoingServerConnectionSecurityIndex = AvailableConnectionSecurities.FindIndex(a => a.ImapConnectionSecurity == ServerInformation.OutgoingServerSocketOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectedTabIndex = _statePersistanceService.ApplicationMode == WinoApplicationMode.Calendar ? 2 : 1;
|
SelectedTabIndex = _statePersistanceService.ApplicationMode == WinoApplicationMode.Calendar && HasCalendarAccess
|
||||||
|
? 2
|
||||||
|
: HasMailAccess
|
||||||
|
? 1
|
||||||
|
: 0;
|
||||||
|
|
||||||
var folderStructures = (await _folderService.GetFolderStructureForAccountAsync(Account.Id, true)).Folders;
|
var folderStructures = (await _folderService.GetFolderStructureForAccountAsync(Account.Id, true)).Folders;
|
||||||
|
|
||||||
@@ -382,11 +401,15 @@ public partial class AccountDetailsPageViewModel : MailBaseViewModel
|
|||||||
|
|
||||||
partial void OnAccountChanged(MailAccount value)
|
partial void OnAccountChanged(MailAccount value)
|
||||||
{
|
{
|
||||||
|
SelectedCapabilityOption = ResolveCapabilityOption(value?.IsMailAccessGranted == true, value?.IsCalendarAccessGranted == true);
|
||||||
OnPropertyChanged(nameof(IsFocusedInboxSupportedForAccount));
|
OnPropertyChanged(nameof(IsFocusedInboxSupportedForAccount));
|
||||||
OnPropertyChanged(nameof(ProviderIconPath));
|
OnPropertyChanged(nameof(ProviderIconPath));
|
||||||
OnPropertyChanged(nameof(Address));
|
OnPropertyChanged(nameof(Address));
|
||||||
OnPropertyChanged(nameof(IsInitialSynchronizationSummaryVisible));
|
OnPropertyChanged(nameof(IsInitialSynchronizationSummaryVisible));
|
||||||
OnPropertyChanged(nameof(InitialSynchronizationSummary));
|
OnPropertyChanged(nameof(InitialSynchronizationSummary));
|
||||||
|
OnPropertyChanged(nameof(HasMailAccess));
|
||||||
|
OnPropertyChanged(nameof(HasCalendarAccess));
|
||||||
|
OnPropertyChanged(nameof(IsOAuthCapabilityEditable));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async void OnPropertyChanged(PropertyChangedEventArgs e)
|
protected override async void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||||
@@ -417,6 +440,21 @@ public partial class AccountDetailsPageViewModel : MailBaseViewModel
|
|||||||
Account.Preferences.IsTaskbarBadgeEnabled = IsTaskbarBadgeEnabled;
|
Account.Preferences.IsTaskbarBadgeEnabled = IsTaskbarBadgeEnabled;
|
||||||
await _accountService.UpdateAccountAsync(Account);
|
await _accountService.UpdateAccountAsync(Account);
|
||||||
break;
|
break;
|
||||||
|
case nameof(SelectedCapabilityOption) when IsOAuthCapabilityEditable && SelectedCapabilityOption != null:
|
||||||
|
if (Account.IsMailAccessGranted == SelectedCapabilityOption.IsMailAccessGranted &&
|
||||||
|
Account.IsCalendarAccessGranted == SelectedCapabilityOption.IsCalendarAccessGranted)
|
||||||
|
break;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await UpdateOAuthCapabilityAsync(SelectedCapabilityOption);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await ExecuteUIThread(() => SelectedCapabilityOption = ResolveCapabilityOption(Account.IsMailAccessGranted, Account.IsCalendarAccessGranted));
|
||||||
|
_dialogService.InfoBarMessage(Translator.GeneralTitle_Error, ex.Message, InfoBarMessageType.Error);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case nameof(SelectedPrimaryCalendar) when SelectedPrimaryCalendar != null:
|
case nameof(SelectedPrimaryCalendar) when SelectedPrimaryCalendar != null:
|
||||||
foreach (var calendar in AccountCalendars)
|
foreach (var calendar in AccountCalendars)
|
||||||
{
|
{
|
||||||
@@ -427,6 +465,111 @@ public partial class AccountDetailsPageViewModel : MailBaseViewModel
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AccountCapabilityOption ResolveCapabilityOption(bool isMailAccessGranted, bool isCalendarAccessGranted)
|
||||||
|
=> CapabilityOptions.First(option =>
|
||||||
|
option.IsMailAccessGranted == isMailAccessGranted &&
|
||||||
|
option.IsCalendarAccessGranted == isCalendarAccessGranted);
|
||||||
|
|
||||||
|
private async Task UpdateOAuthCapabilityAsync(AccountCapabilityOption selectedOption)
|
||||||
|
{
|
||||||
|
var previousMailAccess = Account.IsMailAccessGranted;
|
||||||
|
var previousCalendarAccess = Account.IsCalendarAccessGranted;
|
||||||
|
var requiresReauthorization = (selectedOption.IsMailAccessGranted && !previousMailAccess) ||
|
||||||
|
(selectedOption.IsCalendarAccessGranted && !previousCalendarAccess);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (requiresReauthorization)
|
||||||
|
{
|
||||||
|
Account.IsMailAccessGranted = selectedOption.IsMailAccessGranted;
|
||||||
|
Account.IsCalendarAccessGranted = selectedOption.IsCalendarAccessGranted;
|
||||||
|
|
||||||
|
await SynchronizationManager.Instance.HandleAuthorizationAsync(
|
||||||
|
Account.ProviderType,
|
||||||
|
Account,
|
||||||
|
Account.ProviderType == MailProviderType.Gmail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Account.IsMailAccessGranted = previousMailAccess;
|
||||||
|
Account.IsCalendarAccessGranted = previousCalendarAccess;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
Account.IsMailAccessGranted = selectedOption.IsMailAccessGranted;
|
||||||
|
Account.IsCalendarAccessGranted = selectedOption.IsCalendarAccessGranted;
|
||||||
|
|
||||||
|
await _accountService.UpdateAccountAsync(Account);
|
||||||
|
|
||||||
|
if (selectedOption.IsMailAccessGranted && !previousMailAccess)
|
||||||
|
{
|
||||||
|
await SynchronizationManager.Instance.SynchronizeProfileAsync(Account.Id);
|
||||||
|
await SynchronizationManager.Instance.SynchronizeMailAsync(new MailSynchronizationOptions
|
||||||
|
{
|
||||||
|
AccountId = Account.Id,
|
||||||
|
Type = MailSynchronizationType.FullFolders
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Account.ProviderType == MailProviderType.Outlook)
|
||||||
|
{
|
||||||
|
await SynchronizationManager.Instance.SynchronizeMailAsync(new MailSynchronizationOptions
|
||||||
|
{
|
||||||
|
AccountId = Account.Id,
|
||||||
|
Type = MailSynchronizationType.Categories
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(Account.Address))
|
||||||
|
{
|
||||||
|
var aliases = await _accountService.GetAccountAliasesAsync(Account.Id);
|
||||||
|
var hasRootAlias = aliases.Any(alias => alias.IsRootAlias);
|
||||||
|
|
||||||
|
if (!hasRootAlias)
|
||||||
|
{
|
||||||
|
await _accountService.CreateRootAliasAsync(Account.Id, Account.Address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await SynchronizationManager.Instance.SynchronizeMailAsync(new MailSynchronizationOptions
|
||||||
|
{
|
||||||
|
AccountId = Account.Id,
|
||||||
|
Type = MailSynchronizationType.Alias
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedOption.IsCalendarAccessGranted && !previousCalendarAccess)
|
||||||
|
{
|
||||||
|
await SynchronizationManager.Instance.SynchronizeCalendarAsync(new CalendarSynchronizationOptions
|
||||||
|
{
|
||||||
|
AccountId = Account.Id,
|
||||||
|
Type = CalendarSynchronizationType.CalendarMetadata
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var refreshedAccount = await _accountService.GetAccountAsync(Account.Id);
|
||||||
|
|
||||||
|
await ExecuteUIThread(() =>
|
||||||
|
{
|
||||||
|
Account = refreshedAccount;
|
||||||
|
AccountName = refreshedAccount.Name;
|
||||||
|
SenderName = refreshedAccount.SenderName;
|
||||||
|
EnsureSelectedTabForCapabilities();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureSelectedTabForCapabilities()
|
||||||
|
{
|
||||||
|
if (SelectedTabIndex == 1 && !HasMailAccess)
|
||||||
|
{
|
||||||
|
SelectedTabIndex = HasCalendarAccess ? 2 : 0;
|
||||||
|
}
|
||||||
|
else if (SelectedTabIndex == 2 && !HasCalendarAccess)
|
||||||
|
{
|
||||||
|
SelectedTabIndex = HasMailAccess ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class AccountCalendarShowAsOption
|
public sealed class AccountCalendarShowAsOption
|
||||||
@@ -441,6 +584,20 @@ public sealed class AccountCalendarShowAsOption
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class AccountCapabilityOption
|
||||||
|
{
|
||||||
|
public bool IsMailAccessGranted { get; }
|
||||||
|
public bool IsCalendarAccessGranted { get; }
|
||||||
|
public string DisplayText { get; }
|
||||||
|
|
||||||
|
public AccountCapabilityOption(bool isMailAccessGranted, bool isCalendarAccessGranted, string displayText)
|
||||||
|
{
|
||||||
|
IsMailAccessGranted = isMailAccessGranted;
|
||||||
|
IsCalendarAccessGranted = isCalendarAccessGranted;
|
||||||
|
DisplayText = displayText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public partial class AccountCalendarSettingsItemViewModel : ObservableObject
|
public partial class AccountCalendarSettingsItemViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
public AccountCalendar Calendar { get; }
|
public AccountCalendar Calendar { get; }
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ public partial class AccountSetupProgressPageViewModel : MailBaseViewModel
|
|||||||
private void BuildSteps()
|
private void BuildSteps()
|
||||||
{
|
{
|
||||||
Steps.Clear();
|
Steps.Clear();
|
||||||
|
var shouldSetupMail = WizardContext.IsMailAccessEnabled;
|
||||||
|
var shouldSetupCalendar = WizardContext.IsCalendarAccessEnabled;
|
||||||
|
|
||||||
if (WizardContext.IsOAuthProvider)
|
if (WizardContext.IsOAuthProvider)
|
||||||
{
|
{
|
||||||
@@ -78,31 +80,47 @@ public partial class AccountSetupProgressPageViewModel : MailBaseViewModel
|
|||||||
{
|
{
|
||||||
Title = string.Format(Translator.AccountSetup_Step_Authenticating, WizardContext.SelectedProvider.Name)
|
Title = string.Format(Translator.AccountSetup_Step_Authenticating, WizardContext.SelectedProvider.Name)
|
||||||
});
|
});
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_FetchingProfile });
|
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SavingAccount });
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SavingAccount });
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SyncingFolders });
|
if (shouldSetupMail)
|
||||||
if (WizardContext.SelectedProvider.Type == MailProviderType.Outlook)
|
|
||||||
{
|
{
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SyncingCategories });
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_FetchingProfile });
|
||||||
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SyncingFolders });
|
||||||
|
|
||||||
|
if (WizardContext.SelectedProvider.Type == MailProviderType.Outlook)
|
||||||
|
{
|
||||||
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SyncingCategories });
|
||||||
|
}
|
||||||
|
|
||||||
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SyncingAliases });
|
||||||
}
|
}
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_FetchingCalendarMetadata });
|
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SyncingAliases });
|
if (shouldSetupCalendar)
|
||||||
|
{
|
||||||
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_FetchingCalendarMetadata });
|
||||||
|
}
|
||||||
|
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_Finalizing });
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_Finalizing });
|
||||||
}
|
}
|
||||||
else if (WizardContext.IsSpecialImapProvider)
|
else if (WizardContext.IsSpecialImapProvider)
|
||||||
{
|
{
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_TestingMailAuth });
|
if (shouldSetupMail)
|
||||||
|
{
|
||||||
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_TestingMailAuth });
|
||||||
|
}
|
||||||
|
|
||||||
if (WizardContext.CalendarSupportMode == ImapCalendarSupportMode.CalDav)
|
if (shouldSetupCalendar && WizardContext.CalendarSupportMode == ImapCalendarSupportMode.CalDav)
|
||||||
{
|
{
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_DiscoveringCalDav });
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_DiscoveringCalDav });
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_TestingCalendarAuth });
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_TestingCalendarAuth });
|
||||||
}
|
}
|
||||||
|
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SavingAccount });
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SavingAccount });
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SyncingFolders });
|
if (shouldSetupMail)
|
||||||
|
{
|
||||||
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SyncingFolders });
|
||||||
|
}
|
||||||
|
|
||||||
if (WizardContext.CalendarSupportMode != ImapCalendarSupportMode.Disabled)
|
if (shouldSetupCalendar && WizardContext.CalendarSupportMode != ImapCalendarSupportMode.Disabled)
|
||||||
{
|
{
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_FetchingCalendarMetadata });
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_FetchingCalendarMetadata });
|
||||||
}
|
}
|
||||||
@@ -112,7 +130,10 @@ public partial class AccountSetupProgressPageViewModel : MailBaseViewModel
|
|||||||
else // Generic IMAP
|
else // Generic IMAP
|
||||||
{
|
{
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SavingAccount });
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SavingAccount });
|
||||||
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SyncingFolders });
|
if (shouldSetupMail)
|
||||||
|
{
|
||||||
|
Steps.Add(new AccountSetupStepModel { Title = Translator.AccountSetup_Step_SyncingFolders });
|
||||||
|
}
|
||||||
|
|
||||||
var setupResult = WizardContext.ImapCalDavSetupResult;
|
var setupResult = WizardContext.ImapCalDavSetupResult;
|
||||||
if (setupResult?.IsCalendarAccessGranted == true &&
|
if (setupResult?.IsCalendarAccessGranted == true &&
|
||||||
@@ -186,7 +207,8 @@ public partial class AccountSetupProgressPageViewModel : MailBaseViewModel
|
|||||||
AccountColorHex = WizardContext.AccountColorHex,
|
AccountColorHex = WizardContext.AccountColorHex,
|
||||||
CreatedAt = accountCreatedAt,
|
CreatedAt = accountCreatedAt,
|
||||||
InitialSynchronizationRange = WizardContext.SelectedInitialSynchronizationRange,
|
InitialSynchronizationRange = WizardContext.SelectedInitialSynchronizationRange,
|
||||||
IsCalendarAccessGranted = true
|
IsMailAccessGranted = WizardContext.IsMailAccessEnabled,
|
||||||
|
IsCalendarAccessGranted = WizardContext.IsCalendarAccessEnabled
|
||||||
};
|
};
|
||||||
|
|
||||||
if (WizardContext.IsOAuthProvider)
|
if (WizardContext.IsOAuthProvider)
|
||||||
@@ -208,50 +230,53 @@ public partial class AccountSetupProgressPageViewModel : MailBaseViewModel
|
|||||||
_dbWritten = true;
|
_dbWritten = true;
|
||||||
SetCurrentStepSucceeded();
|
SetCurrentStepSucceeded();
|
||||||
|
|
||||||
// Step: Profile
|
if (_createdAccount.IsMailAccessGranted)
|
||||||
SetStepInProgress(Translator.AccountSetup_Step_FetchingProfile);
|
|
||||||
var profileResult = await SynchronizationManager.Instance.SynchronizeProfileAsync(_createdAccount.Id);
|
|
||||||
if (profileResult.CompletedState != SynchronizationCompletedState.Success)
|
|
||||||
throw new Exception(Translator.Exception_FailedToSynchronizeProfileInformation);
|
|
||||||
|
|
||||||
if (profileResult.ProfileInformation != null)
|
|
||||||
{
|
{
|
||||||
_createdAccount.SenderName = profileResult.ProfileInformation.SenderName;
|
// Step: Profile
|
||||||
_createdAccount.Base64ProfilePictureData = profileResult.ProfileInformation.Base64ProfilePictureData;
|
SetStepInProgress(Translator.AccountSetup_Step_FetchingProfile);
|
||||||
|
var profileResult = await SynchronizationManager.Instance.SynchronizeProfileAsync(_createdAccount.Id);
|
||||||
|
if (profileResult.CompletedState != SynchronizationCompletedState.Success)
|
||||||
|
throw new Exception(Translator.Exception_FailedToSynchronizeProfileInformation);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(profileResult.ProfileInformation.AccountAddress))
|
if (profileResult.ProfileInformation != null)
|
||||||
{
|
{
|
||||||
if (await _accountService.AccountAddressExistsAsync(profileResult.ProfileInformation.AccountAddress, _createdAccount.Id))
|
_createdAccount.SenderName = profileResult.ProfileInformation.SenderName;
|
||||||
throw new InvalidOperationException(Translator.DialogMessage_AccountAddressExistsMessage);
|
_createdAccount.Base64ProfilePictureData = profileResult.ProfileInformation.Base64ProfilePictureData;
|
||||||
|
|
||||||
_createdAccount.Address = profileResult.ProfileInformation.AccountAddress;
|
if (!string.IsNullOrEmpty(profileResult.ProfileInformation.AccountAddress))
|
||||||
|
{
|
||||||
|
if (await _accountService.AccountAddressExistsAsync(profileResult.ProfileInformation.AccountAddress, _createdAccount.Id))
|
||||||
|
throw new InvalidOperationException(Translator.DialogMessage_AccountAddressExistsMessage);
|
||||||
|
|
||||||
|
_createdAccount.Address = profileResult.ProfileInformation.AccountAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _accountService.UpdateProfileInformationAsync(_createdAccount.Id, profileResult.ProfileInformation);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _accountService.UpdateProfileInformationAsync(_createdAccount.Id, profileResult.ProfileInformation);
|
|
||||||
}
|
|
||||||
SetCurrentStepSucceeded();
|
|
||||||
|
|
||||||
// Step: Folders
|
|
||||||
SetStepInProgress(Translator.AccountSetup_Step_SyncingFolders);
|
|
||||||
var folderResult = await SynchronizationManager.Instance.SynchronizeFoldersAsync(_createdAccount.Id);
|
|
||||||
if (folderResult == null || folderResult.CompletedState != SynchronizationCompletedState.Success)
|
|
||||||
throw new Exception(Translator.Exception_FailedToSynchronizeFolders);
|
|
||||||
SetCurrentStepSucceeded();
|
|
||||||
|
|
||||||
// Step: Categories
|
|
||||||
if (_createdAccount.IsCategorySyncSupported)
|
|
||||||
{
|
|
||||||
SetStepInProgress(Translator.AccountSetup_Step_SyncingCategories);
|
|
||||||
var categoryResult = await SynchronizationManager.Instance.SynchronizeCategoriesAsync(_createdAccount.Id);
|
|
||||||
if (categoryResult.CompletedState != SynchronizationCompletedState.Success)
|
|
||||||
throw new Exception(Translator.Exception_FailedToSynchronizeCategories);
|
|
||||||
SetCurrentStepSucceeded();
|
SetCurrentStepSucceeded();
|
||||||
|
|
||||||
|
// Step: Folders
|
||||||
|
SetStepInProgress(Translator.AccountSetup_Step_SyncingFolders);
|
||||||
|
var folderResult = await SynchronizationManager.Instance.SynchronizeFoldersAsync(_createdAccount.Id);
|
||||||
|
if (folderResult == null || folderResult.CompletedState != SynchronizationCompletedState.Success)
|
||||||
|
throw new Exception(Translator.Exception_FailedToSynchronizeFolders);
|
||||||
|
SetCurrentStepSucceeded();
|
||||||
|
|
||||||
|
// Step: Categories
|
||||||
|
if (_createdAccount.IsCategorySyncSupported)
|
||||||
|
{
|
||||||
|
SetStepInProgress(Translator.AccountSetup_Step_SyncingCategories);
|
||||||
|
var categoryResult = await SynchronizationManager.Instance.SynchronizeCategoriesAsync(_createdAccount.Id);
|
||||||
|
if (categoryResult.CompletedState != SynchronizationCompletedState.Success)
|
||||||
|
throw new Exception(Translator.Exception_FailedToSynchronizeCategories);
|
||||||
|
SetCurrentStepSucceeded();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step: Calendar metadata
|
// Step: Calendar metadata
|
||||||
SetStepInProgress(Translator.AccountSetup_Step_FetchingCalendarMetadata);
|
|
||||||
if (_createdAccount.IsCalendarAccessGranted)
|
if (_createdAccount.IsCalendarAccessGranted)
|
||||||
{
|
{
|
||||||
|
SetStepInProgress(Translator.AccountSetup_Step_FetchingCalendarMetadata);
|
||||||
var calResult = await SynchronizationManager.Instance.SynchronizeCalendarAsync(new CalendarSynchronizationOptions
|
var calResult = await SynchronizationManager.Instance.SynchronizeCalendarAsync(new CalendarSynchronizationOptions
|
||||||
{
|
{
|
||||||
AccountId = _createdAccount.Id,
|
AccountId = _createdAccount.Id,
|
||||||
@@ -259,22 +284,25 @@ public partial class AccountSetupProgressPageViewModel : MailBaseViewModel
|
|||||||
});
|
});
|
||||||
if (calResult == null || calResult.CompletedState != SynchronizationCompletedState.Success)
|
if (calResult == null || calResult.CompletedState != SynchronizationCompletedState.Success)
|
||||||
throw new Exception(Translator.Exception_FailedToSynchronizeCalendarMetadata);
|
throw new Exception(Translator.Exception_FailedToSynchronizeCalendarMetadata);
|
||||||
|
SetCurrentStepSucceeded();
|
||||||
}
|
}
|
||||||
SetCurrentStepSucceeded();
|
|
||||||
|
|
||||||
// Step: Aliases
|
// Step: Aliases
|
||||||
SetStepInProgress(Translator.AccountSetup_Step_SyncingAliases);
|
if (_createdAccount.IsMailAccessGranted)
|
||||||
if (_createdAccount.IsAliasSyncSupported)
|
|
||||||
{
|
{
|
||||||
var aliasResult = await SynchronizationManager.Instance.SynchronizeAliasesAsync(_createdAccount.Id);
|
SetStepInProgress(Translator.AccountSetup_Step_SyncingAliases);
|
||||||
if (aliasResult.CompletedState != SynchronizationCompletedState.Success)
|
if (_createdAccount.IsAliasSyncSupported)
|
||||||
throw new Exception(Translator.Exception_FailedToSynchronizeAliases);
|
{
|
||||||
|
var aliasResult = await SynchronizationManager.Instance.SynchronizeAliasesAsync(_createdAccount.Id);
|
||||||
|
if (aliasResult.CompletedState != SynchronizationCompletedState.Success)
|
||||||
|
throw new Exception(Translator.Exception_FailedToSynchronizeAliases);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _accountService.CreateRootAliasAsync(_createdAccount.Id, _createdAccount.Address);
|
||||||
|
}
|
||||||
|
SetCurrentStepSucceeded();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
await _accountService.CreateRootAliasAsync(_createdAccount.Id, _createdAccount.Address);
|
|
||||||
}
|
|
||||||
SetCurrentStepSucceeded();
|
|
||||||
}
|
}
|
||||||
else if (WizardContext.IsSpecialImapProvider)
|
else if (WizardContext.IsSpecialImapProvider)
|
||||||
{
|
{
|
||||||
@@ -288,13 +316,17 @@ public partial class AccountSetupProgressPageViewModel : MailBaseViewModel
|
|||||||
|
|
||||||
_createdAccount.Address = WizardContext.EmailAddress;
|
_createdAccount.Address = WizardContext.EmailAddress;
|
||||||
_createdAccount.SenderName = WizardContext.DisplayName;
|
_createdAccount.SenderName = WizardContext.DisplayName;
|
||||||
|
_createdAccount.IsMailAccessGranted = dialogResult.IsMailAccessGranted;
|
||||||
_createdAccount.IsCalendarAccessGranted = customServerInformation.CalendarSupportMode != ImapCalendarSupportMode.Disabled;
|
_createdAccount.IsCalendarAccessGranted = customServerInformation.CalendarSupportMode != ImapCalendarSupportMode.Disabled;
|
||||||
_createdAccount.ServerInformation = customServerInformation;
|
_createdAccount.ServerInformation = customServerInformation;
|
||||||
|
|
||||||
// Step: Test IMAP
|
if (_createdAccount.IsMailAccessGranted)
|
||||||
SetStepInProgress(Translator.AccountSetup_Step_TestingMailAuth);
|
{
|
||||||
await ValidateImapConnectivityAsync(customServerInformation);
|
// Step: Test IMAP
|
||||||
SetCurrentStepSucceeded();
|
SetStepInProgress(Translator.AccountSetup_Step_TestingMailAuth);
|
||||||
|
await ValidateImapConnectivityAsync(customServerInformation);
|
||||||
|
SetCurrentStepSucceeded();
|
||||||
|
}
|
||||||
|
|
||||||
// Step: CalDAV discovery and testing (if applicable)
|
// Step: CalDAV discovery and testing (if applicable)
|
||||||
if (customServerInformation.CalendarSupportMode == ImapCalendarSupportMode.CalDav)
|
if (customServerInformation.CalendarSupportMode == ImapCalendarSupportMode.CalDav)
|
||||||
@@ -313,12 +345,15 @@ public partial class AccountSetupProgressPageViewModel : MailBaseViewModel
|
|||||||
_dbWritten = true;
|
_dbWritten = true;
|
||||||
SetCurrentStepSucceeded();
|
SetCurrentStepSucceeded();
|
||||||
|
|
||||||
// Step: Folders
|
if (_createdAccount.IsMailAccessGranted)
|
||||||
SetStepInProgress(Translator.AccountSetup_Step_SyncingFolders);
|
{
|
||||||
var folderResult = await SynchronizationManager.Instance.SynchronizeFoldersAsync(_createdAccount.Id);
|
// Step: Folders
|
||||||
if (folderResult == null || folderResult.CompletedState != SynchronizationCompletedState.Success)
|
SetStepInProgress(Translator.AccountSetup_Step_SyncingFolders);
|
||||||
throw new Exception(Translator.Exception_FailedToSynchronizeFolders);
|
var folderResult = await SynchronizationManager.Instance.SynchronizeFoldersAsync(_createdAccount.Id);
|
||||||
SetCurrentStepSucceeded();
|
if (folderResult == null || folderResult.CompletedState != SynchronizationCompletedState.Success)
|
||||||
|
throw new Exception(Translator.Exception_FailedToSynchronizeFolders);
|
||||||
|
SetCurrentStepSucceeded();
|
||||||
|
}
|
||||||
|
|
||||||
// Step: Calendar metadata (if not disabled)
|
// Step: Calendar metadata (if not disabled)
|
||||||
if (_createdAccount.IsCalendarAccessGranted)
|
if (_createdAccount.IsCalendarAccessGranted)
|
||||||
@@ -334,8 +369,10 @@ public partial class AccountSetupProgressPageViewModel : MailBaseViewModel
|
|||||||
SetCurrentStepSucceeded();
|
SetCurrentStepSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aliases for IMAP
|
if (_createdAccount.IsMailAccessGranted)
|
||||||
await _accountService.CreateRootAliasAsync(_createdAccount.Id, _createdAccount.Address);
|
{
|
||||||
|
await _accountService.CreateRootAliasAsync(_createdAccount.Id, _createdAccount.Address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else // Generic IMAP
|
else // Generic IMAP
|
||||||
{
|
{
|
||||||
@@ -350,6 +387,7 @@ public partial class AccountSetupProgressPageViewModel : MailBaseViewModel
|
|||||||
|
|
||||||
_createdAccount.Address = setupResult.EmailAddress;
|
_createdAccount.Address = setupResult.EmailAddress;
|
||||||
_createdAccount.SenderName = setupResult.DisplayName;
|
_createdAccount.SenderName = setupResult.DisplayName;
|
||||||
|
_createdAccount.IsMailAccessGranted = setupResult.IsMailAccessGranted;
|
||||||
_createdAccount.IsCalendarAccessGranted = setupResult.IsCalendarAccessGranted;
|
_createdAccount.IsCalendarAccessGranted = setupResult.IsCalendarAccessGranted;
|
||||||
_createdAccount.ServerInformation = customServerInformation;
|
_createdAccount.ServerInformation = customServerInformation;
|
||||||
|
|
||||||
@@ -359,12 +397,15 @@ public partial class AccountSetupProgressPageViewModel : MailBaseViewModel
|
|||||||
_dbWritten = true;
|
_dbWritten = true;
|
||||||
SetCurrentStepSucceeded();
|
SetCurrentStepSucceeded();
|
||||||
|
|
||||||
// Step: Folders
|
if (_createdAccount.IsMailAccessGranted)
|
||||||
SetStepInProgress(Translator.AccountSetup_Step_SyncingFolders);
|
{
|
||||||
var folderResult = await SynchronizationManager.Instance.SynchronizeFoldersAsync(_createdAccount.Id);
|
// Step: Folders
|
||||||
if (folderResult == null || folderResult.CompletedState != SynchronizationCompletedState.Success)
|
SetStepInProgress(Translator.AccountSetup_Step_SyncingFolders);
|
||||||
throw new Exception(Translator.Exception_FailedToSynchronizeFolders);
|
var folderResult = await SynchronizationManager.Instance.SynchronizeFoldersAsync(_createdAccount.Id);
|
||||||
SetCurrentStepSucceeded();
|
if (folderResult == null || folderResult.CompletedState != SynchronizationCompletedState.Success)
|
||||||
|
throw new Exception(Translator.Exception_FailedToSynchronizeFolders);
|
||||||
|
SetCurrentStepSucceeded();
|
||||||
|
}
|
||||||
|
|
||||||
// Step: CalDAV (if applicable)
|
// Step: CalDAV (if applicable)
|
||||||
if (setupResult.IsCalendarAccessGranted &&
|
if (setupResult.IsCalendarAccessGranted &&
|
||||||
@@ -392,8 +433,10 @@ public partial class AccountSetupProgressPageViewModel : MailBaseViewModel
|
|||||||
SetCurrentStepSucceeded();
|
SetCurrentStepSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aliases for IMAP
|
if (_createdAccount.IsMailAccessGranted)
|
||||||
await _accountService.CreateRootAliasAsync(_createdAccount.Id, _createdAccount.Address);
|
{
|
||||||
|
await _accountService.CreateRootAliasAsync(_createdAccount.Id, _createdAccount.Address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step: Finalizing
|
// Step: Finalizing
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ public sealed class ImapCalDavSetupResult
|
|||||||
{
|
{
|
||||||
public string DisplayName { get; init; }
|
public string DisplayName { get; init; }
|
||||||
public string EmailAddress { get; init; }
|
public string EmailAddress { get; init; }
|
||||||
|
public bool IsMailAccessGranted { get; init; }
|
||||||
public bool IsCalendarAccessGranted { get; init; }
|
public bool IsCalendarAccessGranted { get; init; }
|
||||||
public CustomServerInformation ServerInformation { get; init; }
|
public CustomServerInformation ServerInformation { get; init; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,12 @@ public partial class WelcomeWizardContext : ObservableObject
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial InitialSynchronizationRange SelectedInitialSynchronizationRange { get; set; } = InitialSynchronizationRange.SixMonths;
|
public partial InitialSynchronizationRange SelectedInitialSynchronizationRange { get; set; } = InitialSynchronizationRange.SixMonths;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public partial bool IsMailAccessEnabled { get; set; } = true;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public partial bool IsCalendarAccessEnabled { get; set; } = true;
|
||||||
|
|
||||||
// Special IMAP fields (iCloud/Yahoo)
|
// Special IMAP fields (iCloud/Yahoo)
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial string DisplayName { get; set; }
|
public partial string DisplayName { get; set; }
|
||||||
@@ -66,7 +72,9 @@ public partial class WelcomeWizardContext : ObservableObject
|
|||||||
AccountName,
|
AccountName,
|
||||||
BuildSpecialImapProviderDetails(),
|
BuildSpecialImapProviderDetails(),
|
||||||
AccountColorHex,
|
AccountColorHex,
|
||||||
SelectedInitialSynchronizationRange);
|
SelectedInitialSynchronizationRange,
|
||||||
|
IsMailAccessEnabled,
|
||||||
|
IsCalendarAccessEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
@@ -75,6 +83,8 @@ public partial class WelcomeWizardContext : ObservableObject
|
|||||||
AccountName = null;
|
AccountName = null;
|
||||||
AccountColorHex = null;
|
AccountColorHex = null;
|
||||||
SelectedInitialSynchronizationRange = InitialSynchronizationRange.SixMonths;
|
SelectedInitialSynchronizationRange = InitialSynchronizationRange.SixMonths;
|
||||||
|
IsMailAccessEnabled = true;
|
||||||
|
IsCalendarAccessEnabled = true;
|
||||||
DisplayName = null;
|
DisplayName = null;
|
||||||
EmailAddress = null;
|
EmailAddress = null;
|
||||||
AppSpecificPassword = null;
|
AppSpecificPassword = null;
|
||||||
|
|||||||
@@ -1,9 +1,50 @@
|
|||||||
using Wino.Core.Domain.Interfaces;
|
using System;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Wino.Core.Domain;
|
||||||
|
using Wino.Core.Domain.Enums;
|
||||||
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
using Wino.Core.Domain.Models.Navigation;
|
||||||
using Wino.Core.ViewModels;
|
using Wino.Core.ViewModels;
|
||||||
|
|
||||||
namespace Wino.Mail.ViewModels;
|
namespace Wino.Mail.ViewModels;
|
||||||
|
|
||||||
public partial class IdlePageViewModel : CoreBaseViewModel
|
public partial class IdlePageViewModel : CoreBaseViewModel
|
||||||
{
|
{
|
||||||
public IdlePageViewModel(IMailDialogService dialogService) { }
|
public const string MailEmptyStateParameter = "mail-empty-state";
|
||||||
|
|
||||||
|
private readonly INavigationService _navigationService;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public partial bool IsMailEmptyStateVisible { get; set; }
|
||||||
|
|
||||||
|
public string MailEmptyStateTitle => Translator.MailEmptyState_Title;
|
||||||
|
public string MailEmptyStateMessage => Translator.MailEmptyState_Message;
|
||||||
|
public string AddAccountText => Translator.MailEmptyState_AddAccount;
|
||||||
|
public string ManageAccountsText => Translator.MailEmptyState_ManageAccounts;
|
||||||
|
|
||||||
|
public IdlePageViewModel(IMailDialogService dialogService, INavigationService navigationService)
|
||||||
|
{
|
||||||
|
_navigationService = navigationService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||||
|
{
|
||||||
|
base.OnNavigatedTo(mode, parameters);
|
||||||
|
IsMailEmptyStateVisible = string.Equals(parameters as string, MailEmptyStateParameter, StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void AddAccount()
|
||||||
|
=> _navigationService.Navigate(
|
||||||
|
WinoPage.SettingsPage,
|
||||||
|
ProviderSelectionNavigationContext.CreateForSettingsAddAccount(),
|
||||||
|
NavigationReferenceFrame.ShellFrame);
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void ManageAccounts()
|
||||||
|
=> _navigationService.Navigate(
|
||||||
|
WinoPage.SettingsPage,
|
||||||
|
WinoPage.ManageAccountsPage,
|
||||||
|
NavigationReferenceFrame.ShellFrame);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,12 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string password = string.Empty;
|
private string password = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
[NotifyPropertyChangedFor(nameof(IsMailSettingsVisible))]
|
||||||
|
[NotifyPropertyChangedFor(nameof(IsMailPasswordInputVisible))]
|
||||||
|
[NotifyPropertyChangedFor(nameof(IsMailActionsVisible))]
|
||||||
|
private bool isMailSupportEnabled = true;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
[NotifyPropertyChangedFor(nameof(IsCalendarModeSelectionVisible))]
|
[NotifyPropertyChangedFor(nameof(IsCalendarModeSelectionVisible))]
|
||||||
[NotifyPropertyChangedFor(nameof(IsCalDavSettingsVisible))]
|
[NotifyPropertyChangedFor(nameof(IsCalDavSettingsVisible))]
|
||||||
@@ -141,6 +147,9 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
public bool HasProviderHint => !string.IsNullOrWhiteSpace(ProviderHint);
|
public bool HasProviderHint => !string.IsNullOrWhiteSpace(ProviderHint);
|
||||||
public bool IsBasicSetupSelected => SelectedSetupTabIndex == 0;
|
public bool IsBasicSetupSelected => SelectedSetupTabIndex == 0;
|
||||||
public bool IsAdvancedSetupSelected => SelectedSetupTabIndex == 1;
|
public bool IsAdvancedSetupSelected => SelectedSetupTabIndex == 1;
|
||||||
|
public bool IsMailSettingsVisible => IsMailSupportEnabled;
|
||||||
|
public bool IsMailPasswordInputVisible => IsMailSupportEnabled;
|
||||||
|
public bool IsMailActionsVisible => IsMailSupportEnabled;
|
||||||
public bool IsCalendarModeSelectionVisible => IsCalendarSupportEnabled;
|
public bool IsCalendarModeSelectionVisible => IsCalendarSupportEnabled;
|
||||||
public bool IsCalDavSettingsVisible => IsCalendarSupportEnabled && SelectedCalendarSupportMode == ImapCalendarSupportMode.CalDav;
|
public bool IsCalDavSettingsVisible => IsCalendarSupportEnabled && SelectedCalendarSupportMode == ImapCalendarSupportMode.CalDav;
|
||||||
public bool IsLocalCalendarModeSelected => IsCalendarSupportEnabled && SelectedCalendarSupportMode == ImapCalendarSupportMode.LocalOnly;
|
public bool IsLocalCalendarModeSelected => IsCalendarSupportEnabled && SelectedCalendarSupportMode == ImapCalendarSupportMode.LocalOnly;
|
||||||
@@ -152,6 +161,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
public string EmailAddressHeaderText => Translator.IMAPSetupDialog_MailAddress;
|
public string EmailAddressHeaderText => Translator.IMAPSetupDialog_MailAddress;
|
||||||
public string EmailAddressPlaceholderText => Translator.IMAPSetupDialog_MailAddressPlaceholder;
|
public string EmailAddressPlaceholderText => Translator.IMAPSetupDialog_MailAddressPlaceholder;
|
||||||
public string PasswordHeaderText => Translator.IMAPSetupDialog_Password;
|
public string PasswordHeaderText => Translator.IMAPSetupDialog_Password;
|
||||||
|
public string EnableMailSupportText => Translator.ProviderSelection_UseForMail;
|
||||||
public string EnableCalendarSupportText => Translator.ImapCalDavSettingsPage_EnableCalendarSupport;
|
public string EnableCalendarSupportText => Translator.ImapCalDavSettingsPage_EnableCalendarSupport;
|
||||||
public string AutoDiscoverButtonText => Translator.ImapCalDavSettingsPage_AutoDiscoverButton;
|
public string AutoDiscoverButtonText => Translator.ImapCalDavSettingsPage_AutoDiscoverButton;
|
||||||
public string BasicTabText => Translator.ImapCalDavSettingsPage_BasicTab;
|
public string BasicTabText => Translator.ImapCalDavSettingsPage_BasicTab;
|
||||||
@@ -342,6 +352,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
ValidateCapabilitySelection();
|
||||||
await EnsureImapSettingsPreparedAsync().ConfigureAwait(false);
|
await EnsureImapSettingsPreparedAsync().ConfigureAwait(false);
|
||||||
var serverInformation = BuildServerInformation();
|
var serverInformation = BuildServerInformation();
|
||||||
|
|
||||||
@@ -401,6 +412,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
ValidateCapabilitySelection();
|
||||||
await EnsureImapSettingsPreparedAsync();
|
await EnsureImapSettingsPreparedAsync();
|
||||||
|
|
||||||
var serverInformation = BuildServerInformation();
|
var serverInformation = BuildServerInformation();
|
||||||
@@ -416,8 +428,15 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
if (!await ValidateAccountUniquenessAsync(excludedAccountId))
|
if (!await ValidateAccountUniquenessAsync(excludedAccountId))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await ValidateImapConnectivityAsync(serverInformation);
|
if (IsMailSupportEnabled)
|
||||||
IsImapValidationSucceeded = true;
|
{
|
||||||
|
await ValidateImapConnectivityAsync(serverInformation);
|
||||||
|
IsImapValidationSucceeded = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IsImapValidationSucceeded = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (serverInformation.CalendarSupportMode == ImapCalendarSupportMode.CalDav)
|
if (serverInformation.CalendarSupportMode == ImapCalendarSupportMode.CalDav)
|
||||||
{
|
{
|
||||||
@@ -485,6 +504,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
{
|
{
|
||||||
DisplayName = DisplayName.Trim(),
|
DisplayName = DisplayName.Trim(),
|
||||||
EmailAddress = EmailAddress.Trim(),
|
EmailAddress = EmailAddress.Trim(),
|
||||||
|
IsMailAccessGranted = IsMailSupportEnabled,
|
||||||
IsCalendarAccessGranted = serverInformation.CalendarSupportMode != ImapCalendarSupportMode.Disabled,
|
IsCalendarAccessGranted = serverInformation.CalendarSupportMode != ImapCalendarSupportMode.Disabled,
|
||||||
ServerInformation = serverInformation
|
ServerInformation = serverInformation
|
||||||
};
|
};
|
||||||
@@ -511,6 +531,14 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
partial void OnIsMailSupportEnabledChanged(bool value)
|
||||||
|
{
|
||||||
|
if (!value)
|
||||||
|
{
|
||||||
|
IsImapValidationSucceeded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
partial void OnSelectedCalendarSupportModeChanged(ImapCalendarSupportMode value)
|
partial void OnSelectedCalendarSupportModeChanged(ImapCalendarSupportMode value)
|
||||||
{
|
{
|
||||||
if (value == ImapCalendarSupportMode.LocalOnly && !_localOnlyInfoShown)
|
if (value == ImapCalendarSupportMode.LocalOnly && !_localOnlyInfoShown)
|
||||||
@@ -562,6 +590,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
ApplyProviderHint(_editingSpecialImapProvider);
|
ApplyProviderHint(_editingSpecialImapProvider);
|
||||||
|
|
||||||
ApplyServerInformation(account.ServerInformation);
|
ApplyServerInformation(account.ServerInformation);
|
||||||
|
IsMailSupportEnabled = account.IsMailAccessGranted;
|
||||||
|
|
||||||
if (account.ServerInformation != null)
|
if (account.ServerInformation != null)
|
||||||
{
|
{
|
||||||
@@ -588,8 +617,10 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
if (!string.IsNullOrWhiteSpace(accountCreationDialogResult?.SpecialImapProviderDetails?.SenderName))
|
if (!string.IsNullOrWhiteSpace(accountCreationDialogResult?.SpecialImapProviderDetails?.SenderName))
|
||||||
DisplayName = accountCreationDialogResult.SpecialImapProviderDetails.SenderName;
|
DisplayName = accountCreationDialogResult.SpecialImapProviderDetails.SenderName;
|
||||||
|
|
||||||
IsCalendarSupportEnabled = true;
|
IsMailSupportEnabled = accountCreationDialogResult?.IsMailAccessGranted != false;
|
||||||
SelectedCalendarSupportMode = ImapCalendarSupportMode.CalDav;
|
IsCalendarSupportEnabled = accountCreationDialogResult?.IsCalendarAccessGranted == true;
|
||||||
|
SelectedCalendarSupportMode = accountCreationDialogResult?.SpecialImapProviderDetails?.CalendarSupportMode
|
||||||
|
?? (IsCalendarSupportEnabled ? ImapCalendarSupportMode.CalDav : ImapCalendarSupportMode.Disabled);
|
||||||
|
|
||||||
var specialProvider = accountCreationDialogResult?.SpecialImapProviderDetails?.SpecialImapProvider ?? SpecialImapProvider.None;
|
var specialProvider = accountCreationDialogResult?.SpecialImapProviderDetails?.SpecialImapProvider ?? SpecialImapProvider.None;
|
||||||
_editingSpecialImapProvider = specialProvider;
|
_editingSpecialImapProvider = specialProvider;
|
||||||
@@ -737,6 +768,9 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
|
|
||||||
private async Task EnsureImapSettingsPreparedAsync()
|
private async Task EnsureImapSettingsPreparedAsync()
|
||||||
{
|
{
|
||||||
|
if (!IsMailSupportEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
if (HasCompleteImapSettings())
|
if (HasCompleteImapSettings())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -847,6 +881,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
{
|
{
|
||||||
DisplayName = DisplayName.Trim(),
|
DisplayName = DisplayName.Trim(),
|
||||||
EmailAddress = EmailAddress.Trim(),
|
EmailAddress = EmailAddress.Trim(),
|
||||||
|
IsMailAccessGranted = IsMailSupportEnabled,
|
||||||
IsCalendarAccessGranted = serverInformation.CalendarSupportMode != ImapCalendarSupportMode.Disabled,
|
IsCalendarAccessGranted = serverInformation.CalendarSupportMode != ImapCalendarSupportMode.Disabled,
|
||||||
ServerInformation = serverInformation
|
ServerInformation = serverInformation
|
||||||
});
|
});
|
||||||
@@ -863,7 +898,9 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
|
|
||||||
private async Task<bool> ValidateAccountUniquenessAsync(Guid? excludedAccountId)
|
private async Task<bool> ValidateAccountUniquenessAsync(Guid? excludedAccountId)
|
||||||
{
|
{
|
||||||
var accountName = (_pageMode == ImapCalDavSettingsPageMode.Create || _pageMode == ImapCalDavSettingsPageMode.Wizard)
|
var accountName = (_pageMode == ImapCalDavSettingsPageMode.Create
|
||||||
|
|| _pageMode == ImapCalDavSettingsPageMode.Wizard
|
||||||
|
|| _pageMode == ImapCalDavSettingsPageMode.AddAccount)
|
||||||
? _accountCreationContext?.AccountName
|
? _accountCreationContext?.AccountName
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@@ -889,6 +926,15 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ValidateCapabilitySelection(bool isMailEnabled, bool isCalendarEnabled)
|
||||||
|
{
|
||||||
|
if (!isMailEnabled && !isCalendarEnabled)
|
||||||
|
throw new InvalidOperationException(Translator.ProviderSelection_CapabilityValidationMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidateCapabilitySelection()
|
||||||
|
=> ValidateCapabilitySelection(IsMailSupportEnabled, IsCalendarSupportEnabled);
|
||||||
|
|
||||||
private async Task SaveEditFlowAsync(CustomServerInformation serverInformation)
|
private async Task SaveEditFlowAsync(CustomServerInformation serverInformation)
|
||||||
{
|
{
|
||||||
var account = await _accountService.GetAccountAsync(_editingAccountId);
|
var account = await _accountService.GetAccountAsync(_editingAccountId);
|
||||||
@@ -897,6 +943,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
|
|
||||||
account.SenderName = DisplayName.Trim();
|
account.SenderName = DisplayName.Trim();
|
||||||
account.Address = EmailAddress.Trim();
|
account.Address = EmailAddress.Trim();
|
||||||
|
account.IsMailAccessGranted = IsMailSupportEnabled;
|
||||||
account.IsCalendarAccessGranted = serverInformation.CalendarSupportMode != ImapCalendarSupportMode.Disabled;
|
account.IsCalendarAccessGranted = serverInformation.CalendarSupportMode != ImapCalendarSupportMode.Disabled;
|
||||||
|
|
||||||
serverInformation.Id = account.ServerInformation?.Id ?? Guid.NewGuid();
|
serverInformation.Id = account.ServerInformation?.Id ?? Guid.NewGuid();
|
||||||
@@ -908,11 +955,14 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
await _accountService.UpdateAccountCustomServerInformationAsync(serverInformation);
|
await _accountService.UpdateAccountCustomServerInformationAsync(serverInformation);
|
||||||
await _accountService.UpdateAccountAsync(account);
|
await _accountService.UpdateAccountAsync(account);
|
||||||
|
|
||||||
Messenger.Send(new NewMailSynchronizationRequested(new MailSynchronizationOptions
|
if (account.IsMailAccessGranted)
|
||||||
{
|
{
|
||||||
AccountId = account.Id,
|
Messenger.Send(new NewMailSynchronizationRequested(new MailSynchronizationOptions
|
||||||
Type = MailSynchronizationType.FullFolders
|
{
|
||||||
}));
|
AccountId = account.Id,
|
||||||
|
Type = MailSynchronizationType.FullFolders
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
if (account.IsCalendarAccessGranted)
|
if (account.IsCalendarAccessGranted)
|
||||||
{
|
{
|
||||||
@@ -1007,6 +1057,9 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
|
|
||||||
private void ValidateImapSettings(CustomServerInformation serverInformation)
|
private void ValidateImapSettings(CustomServerInformation serverInformation)
|
||||||
{
|
{
|
||||||
|
if (!IsMailSupportEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
ValidateIdentitySettings();
|
ValidateIdentitySettings();
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(serverInformation.IncomingServer))
|
if (string.IsNullOrWhiteSpace(serverInformation.IncomingServer))
|
||||||
@@ -1075,7 +1128,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
|
|
||||||
private bool TryApplyKnownProviderSettingsIfNeeded(bool requireCompleteImapSettings, bool requireCompleteCalDavSettings)
|
private bool TryApplyKnownProviderSettingsIfNeeded(bool requireCompleteImapSettings, bool requireCompleteCalDavSettings)
|
||||||
{
|
{
|
||||||
var needsImapSettings = requireCompleteImapSettings && !HasCompleteImapSettings();
|
var needsImapSettings = IsMailSupportEnabled && requireCompleteImapSettings && !HasCompleteImapSettings();
|
||||||
var needsCalDavSettings = requireCompleteCalDavSettings
|
var needsCalDavSettings = requireCompleteCalDavSettings
|
||||||
&& IsCalendarSupportEnabled
|
&& IsCalendarSupportEnabled
|
||||||
&& SelectedCalendarSupportMode == ImapCalendarSupportMode.CalDav
|
&& SelectedCalendarSupportMode == ImapCalendarSupportMode.CalDav
|
||||||
@@ -1114,6 +1167,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
SenderName = DisplayName.Trim(),
|
SenderName = DisplayName.Trim(),
|
||||||
ProviderType = MailProviderType.IMAP4,
|
ProviderType = MailProviderType.IMAP4,
|
||||||
SpecialImapProvider = _editingSpecialImapProvider,
|
SpecialImapProvider = _editingSpecialImapProvider,
|
||||||
|
IsMailAccessGranted = IsMailSupportEnabled,
|
||||||
IsCalendarAccessGranted = mode != ImapCalendarSupportMode.Disabled
|
IsCalendarAccessGranted = mode != ImapCalendarSupportMode.Disabled
|
||||||
},
|
},
|
||||||
new AccountCreationDialogResult(
|
new AccountCreationDialogResult(
|
||||||
@@ -1121,7 +1175,9 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
DisplayName.Trim(),
|
DisplayName.Trim(),
|
||||||
providerDetails,
|
providerDetails,
|
||||||
string.Empty,
|
string.Empty,
|
||||||
_wizardContext.SelectedInitialSynchronizationRange));
|
_wizardContext.SelectedInitialSynchronizationRange,
|
||||||
|
IsMailSupportEnabled,
|
||||||
|
mode != ImapCalendarSupportMode.Disabled));
|
||||||
|
|
||||||
if (serverInformation == null)
|
if (serverInformation == null)
|
||||||
return false;
|
return false;
|
||||||
@@ -1158,7 +1214,8 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
&& !string.IsNullOrWhiteSpace(CalDavPassword));
|
&& !string.IsNullOrWhiteSpace(CalDavPassword));
|
||||||
|
|
||||||
private bool HasCompleteImapSettings()
|
private bool HasCompleteImapSettings()
|
||||||
=> !string.IsNullOrWhiteSpace(IncomingServer)
|
=> !IsMailSupportEnabled
|
||||||
|
|| (!string.IsNullOrWhiteSpace(IncomingServer)
|
||||||
&& !string.IsNullOrWhiteSpace(IncomingServerPort)
|
&& !string.IsNullOrWhiteSpace(IncomingServerPort)
|
||||||
&& !string.IsNullOrWhiteSpace(IncomingServerUsername)
|
&& !string.IsNullOrWhiteSpace(IncomingServerUsername)
|
||||||
&& !string.IsNullOrWhiteSpace(IncomingServerPassword)
|
&& !string.IsNullOrWhiteSpace(IncomingServerPassword)
|
||||||
@@ -1167,7 +1224,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
|
|||||||
&& !string.IsNullOrWhiteSpace(OutgoingServerUsername)
|
&& !string.IsNullOrWhiteSpace(OutgoingServerUsername)
|
||||||
&& !string.IsNullOrWhiteSpace(OutgoingServerPassword)
|
&& !string.IsNullOrWhiteSpace(OutgoingServerPassword)
|
||||||
&& IsValidPort(IncomingServerPort)
|
&& IsValidPort(IncomingServerPort)
|
||||||
&& IsValidPort(OutgoingServerPort);
|
&& IsValidPort(OutgoingServerPort));
|
||||||
|
|
||||||
private int FindAuthenticationMethodIndex(ImapAuthenticationMethod method)
|
private int FindAuthenticationMethodIndex(ImapAuthenticationMethod method)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
IRecipient<AccountRemovedMessage>,
|
IRecipient<AccountRemovedMessage>,
|
||||||
IRecipient<AccountUpdatedMessage>
|
IRecipient<AccountUpdatedMessage>
|
||||||
{
|
{
|
||||||
|
private const string MailEmptyStateNavigationParameter = IdlePageViewModel.MailEmptyStateParameter;
|
||||||
|
|
||||||
#region Menu Items
|
#region Menu Items
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
@@ -188,7 +190,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
// First clear all account menu items.
|
// First clear all account menu items.
|
||||||
MenuItems.RemoveRange(MenuItems.Where(a => a is IAccountMenuItem));
|
MenuItems.RemoveRange(MenuItems.Where(a => a is IAccountMenuItem));
|
||||||
|
|
||||||
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
|
var accounts = await GetMailEnabledAccountsAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
List<Guid> initializedAccountIds = new();
|
List<Guid> initializedAccountIds = new();
|
||||||
|
|
||||||
@@ -373,7 +375,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
private async Task ForceAllAccountSynchronizationsAsync()
|
private async Task ForceAllAccountSynchronizationsAsync()
|
||||||
{
|
{
|
||||||
// Run Inbox synchronization for all accounts on startup.
|
// Run Inbox synchronization for all accounts on startup.
|
||||||
var accounts = await _accountService.GetAccountsAsync();
|
var accounts = await GetMailEnabledAccountsAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
foreach (var account in accounts)
|
foreach (var account in accounts)
|
||||||
{
|
{
|
||||||
@@ -442,6 +444,9 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
|
|
||||||
private async Task ProcessLaunchDefaultAsync()
|
private async Task ProcessLaunchDefaultAsync()
|
||||||
{
|
{
|
||||||
|
if (await NavigateToMailEmptyStateIfNeededAsync().ConfigureAwait(false))
|
||||||
|
return;
|
||||||
|
|
||||||
if (PreferencesService.StartupEntityId == null)
|
if (PreferencesService.StartupEntityId == null)
|
||||||
{
|
{
|
||||||
NavigateToWelcomeWizard();
|
NavigateToWelcomeWizard();
|
||||||
@@ -468,8 +473,16 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Fallback to the welcome wizard if startup entity is not found.
|
var firstMailAccountMenuItem = MenuItems.FirstOrDefault(a => a is IAccountMenuItem) as IAccountMenuItem;
|
||||||
NavigateToWelcomeWizard();
|
if (firstMailAccountMenuItem != null)
|
||||||
|
{
|
||||||
|
firstMailAccountMenuItem.Expand();
|
||||||
|
await ChangeLoadedAccountAsync(firstMailAccountMenuItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NavigateToWelcomeWizard();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -947,6 +960,11 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
operationAccount = selectedFolderMenuItem.ParentAccount;
|
operationAccount = selectedFolderMenuItem.ParentAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (operationAccount?.IsMailAccessGranted == false)
|
||||||
|
{
|
||||||
|
operationAccount = null;
|
||||||
|
}
|
||||||
|
|
||||||
// We couldn't find any account so far.
|
// We couldn't find any account so far.
|
||||||
// If there is only 1 account to use, use it. If not,
|
// If there is only 1 account to use, use it. If not,
|
||||||
// send a message for flyout so user can pick from it.
|
// send a message for flyout so user can pick from it.
|
||||||
@@ -956,7 +974,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
// No selected account.
|
// No selected account.
|
||||||
// List all accounts and let user pick one.
|
// List all accounts and let user pick one.
|
||||||
|
|
||||||
var accounts = await _accountService.GetAccountsAsync();
|
var accounts = await GetMailEnabledAccountsAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
if (!accounts.Any())
|
if (!accounts.Any())
|
||||||
{
|
{
|
||||||
@@ -1081,7 +1099,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
|
|
||||||
public async void Receive(MailtoProtocolMessageRequested message)
|
public async void Receive(MailtoProtocolMessageRequested message)
|
||||||
{
|
{
|
||||||
var accounts = await _accountService.GetAccountsAsync();
|
var accounts = await GetMailEnabledAccountsAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
MailAccount targetAccount = null;
|
MailAccount targetAccount = null;
|
||||||
|
|
||||||
@@ -1114,7 +1132,7 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
if (shareRequest?.Files == null || shareRequest.Files.Count == 0)
|
if (shareRequest?.Files == null || shareRequest.Files.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var accounts = await _accountService.GetAccountsAsync();
|
var accounts = await GetMailEnabledAccountsAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
if (!accounts.Any())
|
if (!accounts.Any())
|
||||||
return;
|
return;
|
||||||
@@ -1188,7 +1206,10 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
await ExecuteUIThread(() => SelectedMenuItem = null);
|
await ExecuteUIThread(() => SelectedMenuItem = null);
|
||||||
NavigateToWelcomeWizard();
|
if (!await NavigateToMailEmptyStateIfNeededAsync().ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
NavigateToWelcomeWizard();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1199,6 +1220,37 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
NavigationReferenceFrame.ShellFrame,
|
NavigationReferenceFrame.ShellFrame,
|
||||||
NavigationTransitionType.None);
|
NavigationTransitionType.None);
|
||||||
|
|
||||||
|
private Task<List<MailAccount>> GetMailEnabledAccountsAsync()
|
||||||
|
=> GetAccountsByCapabilityAsync(requireMail: true);
|
||||||
|
|
||||||
|
private async Task<List<MailAccount>> GetAccountsByCapabilityAsync(bool requireMail = false, bool requireCalendar = false)
|
||||||
|
{
|
||||||
|
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
return accounts
|
||||||
|
.Where(account => (!requireMail || account.IsMailAccessGranted) &&
|
||||||
|
(!requireCalendar || account.IsCalendarAccessGranted))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> NavigateToMailEmptyStateIfNeededAsync()
|
||||||
|
{
|
||||||
|
var accounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
|
||||||
|
if (!accounts.Any() || accounts.Any(account => account.IsMailAccessGranted))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
latestSelectedAccountMenuItem = null;
|
||||||
|
await ExecuteUIThread(() => SelectedMenuItem = null);
|
||||||
|
|
||||||
|
NavigationService.Navigate(
|
||||||
|
WinoPage.IdlePage,
|
||||||
|
MailEmptyStateNavigationParameter,
|
||||||
|
NavigationReferenceFrame.InnerShellFrame,
|
||||||
|
NavigationTransitionType.None);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private bool IsAccountCurrentlyLoaded(Guid accountId)
|
private bool IsAccountCurrentlyLoaded(Guid accountId)
|
||||||
{
|
{
|
||||||
return latestSelectedAccountMenuItem?.HoldingAccounts?.Any(a => a.Id == accountId) == true;
|
return latestSelectedAccountMenuItem?.HoldingAccounts?.Any(a => a.Id == accountId) == true;
|
||||||
@@ -1385,7 +1437,9 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
public async void Receive(AccountRemovedMessage message)
|
public async void Receive(AccountRemovedMessage message)
|
||||||
{
|
{
|
||||||
var remainingAccounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
|
var remainingAccounts = await _accountService.GetAccountsAsync().ConfigureAwait(false);
|
||||||
if (!remainingAccounts.Any())
|
var remainingMailAccounts = remainingAccounts.Where(account => account.IsMailAccessGranted).ToList();
|
||||||
|
|
||||||
|
if (!remainingMailAccounts.Any())
|
||||||
{
|
{
|
||||||
latestSelectedAccountMenuItem = null;
|
latestSelectedAccountMenuItem = null;
|
||||||
await ExecuteUIThread(() =>
|
await ExecuteUIThread(() =>
|
||||||
@@ -1394,6 +1448,12 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
MenuItems?.Clear();
|
MenuItems?.Clear();
|
||||||
MenuItems?.Add(CreateMailMenuItem);
|
MenuItems?.Add(CreateMailMenuItem);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (remainingAccounts.Any())
|
||||||
|
{
|
||||||
|
await NavigateToMailEmptyStateIfNeededAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1413,22 +1473,34 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
|
|
||||||
await RecreateMenuItemsAsync();
|
await RecreateMenuItemsAsync();
|
||||||
|
|
||||||
|
if (!createdAccount.IsMailAccessGranted)
|
||||||
|
{
|
||||||
|
await NavigateToMailEmptyStateIfNeededAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (!MenuItems.TryGetAccountMenuItem(createdAccount.Id, out IAccountMenuItem createdMenuItem))
|
if (!MenuItems.TryGetAccountMenuItem(createdAccount.Id, out IAccountMenuItem createdMenuItem))
|
||||||
{
|
{
|
||||||
Log.Warning("Created account {AccountId} could not be found in menu items after refresh.", createdAccount.Id);
|
Log.Warning("Created account {AccountId} could not be found in menu items after refresh.", createdAccount.Id);
|
||||||
|
|
||||||
|
if (!createdAccount.IsMailAccessGranted)
|
||||||
|
return;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await ChangeLoadedAccountAsync(createdMenuItem);
|
await ChangeLoadedAccountAsync(createdMenuItem);
|
||||||
|
|
||||||
// Each created account should start a new synchronization automatically.
|
if (createdAccount.IsMailAccessGranted)
|
||||||
var options = new MailSynchronizationOptions()
|
|
||||||
{
|
{
|
||||||
AccountId = createdAccount.Id,
|
// Each created account should start a new synchronization automatically.
|
||||||
Type = MailSynchronizationType.FullFolders,
|
var options = new MailSynchronizationOptions()
|
||||||
};
|
{
|
||||||
|
AccountId = createdAccount.Id,
|
||||||
|
Type = MailSynchronizationType.FullFolders,
|
||||||
|
};
|
||||||
|
|
||||||
Messenger.Send(new NewMailSynchronizationRequested(options));
|
Messenger.Send(new NewMailSynchronizationRequested(options));
|
||||||
|
}
|
||||||
|
|
||||||
if (createdAccount.IsCalendarAccessGranted)
|
if (createdAccount.IsCalendarAccessGranted)
|
||||||
{
|
{
|
||||||
@@ -1455,6 +1527,13 @@ public partial class MailAppShellViewModel : MailBaseViewModel,
|
|||||||
{
|
{
|
||||||
var updatedAccount = message.Account;
|
var updatedAccount = message.Account;
|
||||||
|
|
||||||
|
if (!updatedAccount.IsMailAccessGranted || !MenuItems.TryGetAccountMenuItem(updatedAccount.Id, out _))
|
||||||
|
{
|
||||||
|
await RecreateMenuItemsAsync();
|
||||||
|
await RestoreSelectedAccountAfterMenuRefreshAsync(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await ExecuteUIThread(() =>
|
await ExecuteUIThread(() =>
|
||||||
{
|
{
|
||||||
if (MenuItems.TryGetAccountMenuItem(updatedAccount.Id, out IAccountMenuItem foundAccountMenuItem))
|
if (MenuItems.TryGetAccountMenuItem(updatedAccount.Id, out IAccountMenuItem foundAccountMenuItem))
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ using Wino.Core.Domain.Entities.Mail;
|
|||||||
using Wino.Core.Domain.Entities.Shared;
|
using Wino.Core.Domain.Entities.Shared;
|
||||||
using Wino.Core.Domain.Enums;
|
using Wino.Core.Domain.Enums;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
using Wino.Core.Domain.Models.MailItem;
|
|
||||||
using Wino.Core.Domain.Models.Navigation;
|
using Wino.Core.Domain.Models.Navigation;
|
||||||
using Wino.Core.Domain.Models.Synchronization;
|
|
||||||
using Wino.Core.Requests.Category;
|
using Wino.Core.Requests.Category;
|
||||||
using Wino.Core.Services;
|
using Wino.Core.Services;
|
||||||
|
|
||||||
@@ -53,11 +51,11 @@ public partial class MailCategoryManagementPageViewModel : MailBaseViewModel
|
|||||||
if (parameters is not Guid accountId)
|
if (parameters is not Guid accountId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Account = await _accountService.GetAccountAsync(accountId).ConfigureAwait(false);
|
Account = await _accountService.GetAccountAsync(accountId);
|
||||||
|
|
||||||
if (Account != null)
|
if (Account != null)
|
||||||
{
|
{
|
||||||
await LoadCategoriesAsync().ConfigureAwait(false);
|
await LoadCategoriesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ using Wino.Messaging.Client.Navigation;
|
|||||||
|
|
||||||
namespace Wino.Mail.ViewModels;
|
namespace Wino.Mail.ViewModels;
|
||||||
|
|
||||||
|
public enum ProviderSelectionWizardStep
|
||||||
|
{
|
||||||
|
Provider = 0,
|
||||||
|
Identity = 1,
|
||||||
|
Capabilities = 2
|
||||||
|
}
|
||||||
|
|
||||||
public partial class ProviderSelectionPageViewModel : MailBaseViewModel
|
public partial class ProviderSelectionPageViewModel : MailBaseViewModel
|
||||||
{
|
{
|
||||||
private readonly IAccountService _accountService;
|
private readonly IAccountService _accountService;
|
||||||
@@ -45,16 +52,50 @@ public partial class ProviderSelectionPageViewModel : MailBaseViewModel
|
|||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
[NotifyPropertyChangedFor(nameof(IsInitialSynchronizationWarningVisible))]
|
[NotifyPropertyChangedFor(nameof(IsInitialSynchronizationWarningVisible))]
|
||||||
|
[NotifyPropertyChangedFor(nameof(IsMailSynchronizationRangeVisible))]
|
||||||
public partial InitialSynchronizationRangeOption SelectedInitialSynchronizationRange { get; set; }
|
public partial InitialSynchronizationRangeOption SelectedInitialSynchronizationRange { get; set; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial string AccountName { get; set; }
|
public partial string AccountName { get; set; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial bool CanProceed { get; set; }
|
[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;
|
||||||
|
|
||||||
public bool IsColorSelected => SelectedColor != null;
|
public bool IsColorSelected => SelectedColor != null;
|
||||||
public bool IsInitialSynchronizationWarningVisible => SelectedInitialSynchronizationRange?.IsEverything == true;
|
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;
|
||||||
|
|
||||||
public ProviderSelectionPageViewModel(
|
public ProviderSelectionPageViewModel(
|
||||||
IAccountService accountService,
|
IAccountService accountService,
|
||||||
@@ -101,48 +142,110 @@ public partial class ProviderSelectionPageViewModel : MailBaseViewModel
|
|||||||
p.Type == WizardContext.SelectedProvider.Type &&
|
p.Type == WizardContext.SelectedProvider.Type &&
|
||||||
p.SpecialImapProvider == WizardContext.SelectedProvider.SpecialImapProvider);
|
p.SpecialImapProvider == WizardContext.SelectedProvider.SpecialImapProvider);
|
||||||
AccountName = WizardContext.AccountName;
|
AccountName = WizardContext.AccountName;
|
||||||
|
IsMailAccessEnabled = WizardContext.IsMailAccessEnabled;
|
||||||
|
IsCalendarAccessEnabled = WizardContext.IsCalendarAccessEnabled;
|
||||||
|
|
||||||
if (WizardContext.AccountColorHex != null)
|
if (WizardContext.AccountColorHex != null)
|
||||||
SelectedColor = AvailableColors.FirstOrDefault(c => c.Hex == WizardContext.AccountColorHex);
|
SelectedColor = AvailableColors.FirstOrDefault(c => c.Hex == WizardContext.AccountColorHex);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IsMailAccessEnabled = true;
|
||||||
|
IsCalendarAccessEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
Validate();
|
CurrentStep = mode == NavigationMode.Back && SelectedProvider != null
|
||||||
|
? ProviderSelectionWizardStep.Capabilities
|
||||||
|
: ProviderSelectionWizardStep.Provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnSelectedProviderChanged(IProviderDetail value)
|
partial void OnSelectedProviderChanged(IProviderDetail value)
|
||||||
{
|
{
|
||||||
Validate();
|
OnPropertyChanged(nameof(SelectedProviderName));
|
||||||
|
OnPropertyChanged(nameof(SelectedProviderDescription));
|
||||||
|
OnPropertyChanged(nameof(SelectedProviderImage));
|
||||||
|
OnPropertyChanged(nameof(SelectedProviderCapabilityDescription));
|
||||||
|
OnPropertyChanged(nameof(IsCapabilitySelectionMissing));
|
||||||
|
OnPropertyChanged(nameof(IsCalendarOnlyServerHintVisible));
|
||||||
|
ContinueCommand.NotifyCanExecuteChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnAccountNameChanged(string value) => Validate();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void ClearColor() => SelectedColor = null;
|
private void ClearColor() => SelectedColor = null;
|
||||||
|
|
||||||
private void Validate()
|
private bool CanContinue()
|
||||||
{
|
{
|
||||||
CanProceed = SelectedProvider != null && !string.IsNullOrWhiteSpace(AccountName);
|
return CurrentStep switch
|
||||||
|
{
|
||||||
|
ProviderSelectionWizardStep.Provider => SelectedProvider != null,
|
||||||
|
ProviderSelectionWizardStep.Identity => !string.IsNullOrWhiteSpace(AccountName),
|
||||||
|
ProviderSelectionWizardStep.Capabilities => IsMailAccessEnabled || IsCalendarAccessEnabled,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand(CanExecute = nameof(CanGoBack))]
|
||||||
private async Task ProceedAsync()
|
private void GoBack()
|
||||||
{
|
{
|
||||||
if (!CanProceed) return;
|
if (!CanGoBack)
|
||||||
|
return;
|
||||||
if (await _accountService.AccountNameExistsAsync(AccountName))
|
|
||||||
{
|
CurrentStep--;
|
||||||
await _dialogService.ShowMessageAsync(
|
}
|
||||||
Translator.DialogMessage_AccountNameExistsMessage,
|
|
||||||
Translator.DialogMessage_AccountExistsTitle,
|
[RelayCommand(CanExecute = nameof(CanContinue))]
|
||||||
WinoCustomMessageDialogIcon.Warning);
|
private async Task ContinueAsync()
|
||||||
|
{
|
||||||
|
switch (CurrentStep)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CompleteWizardAsync()
|
||||||
|
{
|
||||||
|
if (!CanContinue())
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Persist to wizard context
|
|
||||||
WizardContext.SelectedProvider = SelectedProvider;
|
WizardContext.SelectedProvider = SelectedProvider;
|
||||||
WizardContext.AccountName = AccountName?.Trim();
|
WizardContext.AccountName = AccountName?.Trim();
|
||||||
WizardContext.AccountColorHex = SelectedColor?.Hex ?? string.Empty;
|
WizardContext.AccountColorHex = SelectedColor?.Hex ?? string.Empty;
|
||||||
WizardContext.SelectedInitialSynchronizationRange = SelectedInitialSynchronizationRange?.Range ?? InitialSynchronizationRange.SixMonths;
|
WizardContext.SelectedInitialSynchronizationRange = SelectedInitialSynchronizationRange?.Range ?? InitialSynchronizationRange.SixMonths;
|
||||||
|
WizardContext.IsMailAccessEnabled = IsMailAccessEnabled;
|
||||||
|
WizardContext.IsCalendarAccessEnabled = IsCalendarAccessEnabled;
|
||||||
|
|
||||||
if (WizardContext.IsGenericImap)
|
if (WizardContext.IsGenericImap)
|
||||||
{
|
{
|
||||||
@@ -172,4 +275,23 @@ public partial class ProviderSelectionPageViewModel : MailBaseViewModel
|
|||||||
WinoPage.AccountSetupProgressPage));
|
WinoPage.AccountSetupProgressPage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,11 +38,15 @@ public partial class SpecialImapCredentialsPageViewModel : MailBaseViewModel
|
|||||||
public partial string AppSpecificPassword { get; set; }
|
public partial string AppSpecificPassword { get; set; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
|
[NotifyPropertyChangedFor(nameof(RequiresAppSpecificPassword))]
|
||||||
public partial int SelectedCalendarModeIndex { get; set; }
|
public partial int SelectedCalendarModeIndex { get; set; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial bool CanProceed { get; set; }
|
public partial bool CanProceed { get; set; }
|
||||||
|
|
||||||
|
public bool IsCalendarModeSelectionVisible => WizardContext.IsCalendarAccessEnabled;
|
||||||
|
public bool RequiresAppSpecificPassword => WizardContext.IsMailAccessEnabled || SelectedCalendarModeIndex == 1;
|
||||||
|
|
||||||
public string AppPasswordHelpUrl
|
public string AppPasswordHelpUrl
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -86,8 +90,15 @@ public partial class SpecialImapCredentialsPageViewModel : MailBaseViewModel
|
|||||||
_ => 0
|
_ => 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!WizardContext.IsCalendarAccessEnabled)
|
||||||
|
{
|
||||||
|
SelectedCalendarModeIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
OnPropertyChanged(nameof(AppPasswordHelpUrl));
|
OnPropertyChanged(nameof(AppPasswordHelpUrl));
|
||||||
OnPropertyChanged(nameof(CalendarModeCalDavDescription));
|
OnPropertyChanged(nameof(CalendarModeCalDavDescription));
|
||||||
|
OnPropertyChanged(nameof(IsCalendarModeSelectionVisible));
|
||||||
|
OnPropertyChanged(nameof(RequiresAppSpecificPassword));
|
||||||
|
|
||||||
Validate();
|
Validate();
|
||||||
}
|
}
|
||||||
@@ -95,13 +106,18 @@ public partial class SpecialImapCredentialsPageViewModel : MailBaseViewModel
|
|||||||
partial void OnDisplayNameChanged(string value) => Validate();
|
partial void OnDisplayNameChanged(string value) => Validate();
|
||||||
partial void OnEmailAddressChanged(string value) => Validate();
|
partial void OnEmailAddressChanged(string value) => Validate();
|
||||||
partial void OnAppSpecificPasswordChanged(string value) => Validate();
|
partial void OnAppSpecificPasswordChanged(string value) => Validate();
|
||||||
|
partial void OnSelectedCalendarModeIndexChanged(int value)
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(RequiresAppSpecificPassword));
|
||||||
|
Validate();
|
||||||
|
}
|
||||||
|
|
||||||
private void Validate()
|
private void Validate()
|
||||||
{
|
{
|
||||||
CanProceed = !string.IsNullOrWhiteSpace(DisplayName)
|
CanProceed = !string.IsNullOrWhiteSpace(DisplayName)
|
||||||
&& !string.IsNullOrWhiteSpace(EmailAddress)
|
&& !string.IsNullOrWhiteSpace(EmailAddress)
|
||||||
&& MailAccountAddressValidator.IsValid(EmailAddress)
|
&& MailAccountAddressValidator.IsValid(EmailAddress)
|
||||||
&& !string.IsNullOrWhiteSpace(AppSpecificPassword);
|
&& (!RequiresAppSpecificPassword || !string.IsNullOrWhiteSpace(AppSpecificPassword));
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
@@ -121,12 +137,14 @@ public partial class SpecialImapCredentialsPageViewModel : MailBaseViewModel
|
|||||||
WizardContext.DisplayName = DisplayName?.Trim();
|
WizardContext.DisplayName = DisplayName?.Trim();
|
||||||
WizardContext.EmailAddress = EmailAddress?.Trim();
|
WizardContext.EmailAddress = EmailAddress?.Trim();
|
||||||
WizardContext.AppSpecificPassword = AppSpecificPassword?.Trim();
|
WizardContext.AppSpecificPassword = AppSpecificPassword?.Trim();
|
||||||
WizardContext.CalendarSupportMode = SelectedCalendarModeIndex switch
|
WizardContext.CalendarSupportMode = WizardContext.IsCalendarAccessEnabled
|
||||||
{
|
? SelectedCalendarModeIndex switch
|
||||||
1 => ImapCalendarSupportMode.CalDav,
|
{
|
||||||
2 => ImapCalendarSupportMode.LocalOnly,
|
1 => ImapCalendarSupportMode.CalDav,
|
||||||
_ => ImapCalendarSupportMode.Disabled
|
2 => ImapCalendarSupportMode.LocalOnly,
|
||||||
};
|
_ => ImapCalendarSupportMode.Disabled
|
||||||
|
}
|
||||||
|
: ImapCalendarSupportMode.Disabled;
|
||||||
|
|
||||||
Messenger.Send(new BreadcrumbNavigationRequested(
|
Messenger.Send(new BreadcrumbNavigationRequested(
|
||||||
Translator.WelcomeWizard_Step3Title,
|
Translator.WelcomeWizard_Step3Title,
|
||||||
|
|||||||
@@ -158,7 +158,9 @@ public sealed partial class NewAccountDialog : ContentDialog
|
|||||||
AccountNameTextbox.Text.Trim(),
|
AccountNameTextbox.Text.Trim(),
|
||||||
details,
|
details,
|
||||||
SelectedColor?.Hex ?? string.Empty,
|
SelectedColor?.Hex ?? string.Empty,
|
||||||
initialSynchronizationRange);
|
initialSynchronizationRange,
|
||||||
|
true,
|
||||||
|
calendarSupportMode != ImapCalendarSupportMode.Disabled);
|
||||||
Hide();
|
Hide();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -185,7 +187,9 @@ public sealed partial class NewAccountDialog : ContentDialog
|
|||||||
AccountNameTextbox.Text.Trim(),
|
AccountNameTextbox.Text.Trim(),
|
||||||
null,
|
null,
|
||||||
SelectedColor?.Hex ?? string.Empty,
|
SelectedColor?.Hex ?? string.Empty,
|
||||||
initialSynchronizationRange);
|
initialSynchronizationRange,
|
||||||
|
true,
|
||||||
|
true);
|
||||||
Hide();
|
Hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,26 @@ public static class XamlHelpers
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Microsoft.UI.Xaml.Media.Imaging.BitmapImage? StringToBitmapImage(string? imagePath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(imagePath))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var uri = imagePath.StartsWith("/")
|
||||||
|
? new Uri($"ms-appx://{imagePath}")
|
||||||
|
: new Uri(imagePath, UriKind.Absolute);
|
||||||
|
|
||||||
|
return new Microsoft.UI.Xaml.Media.Imaging.BitmapImage(uri);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static InfoBarSeverity InfoBarSeverityConverter(InfoBarMessageType messageType)
|
public static InfoBarSeverity InfoBarSeverityConverter(InfoBarMessageType messageType)
|
||||||
{
|
{
|
||||||
return messageType switch
|
return messageType switch
|
||||||
|
|||||||
@@ -105,6 +105,9 @@ public partial class AccountCalendarStateService : ObservableRecipient,
|
|||||||
{
|
{
|
||||||
lock (_calendarStateLock)
|
lock (_calendarStateLock)
|
||||||
{
|
{
|
||||||
|
if (!GroupedAccountCalendarViewModel.SupportsCalendar(groupedAccountCalendar.Account))
|
||||||
|
return;
|
||||||
|
|
||||||
groupedAccountCalendar.CalendarSelectionStateChanged += SingleCalendarSelectionStateChanged;
|
groupedAccountCalendar.CalendarSelectionStateChanged += SingleCalendarSelectionStateChanged;
|
||||||
groupedAccountCalendar.CollectiveSelectionStateChanged += SingleGroupCalendarCollectiveStateChanged;
|
groupedAccountCalendar.CollectiveSelectionStateChanged += SingleGroupCalendarCollectiveStateChanged;
|
||||||
try
|
try
|
||||||
@@ -180,6 +183,9 @@ public partial class AccountCalendarStateService : ObservableRecipient,
|
|||||||
{
|
{
|
||||||
lock (_calendarStateLock)
|
lock (_calendarStateLock)
|
||||||
{
|
{
|
||||||
|
if (!GroupedAccountCalendarViewModel.SupportsCalendar(accountCalendar.Account))
|
||||||
|
return;
|
||||||
|
|
||||||
// Find the group that this calendar belongs to.
|
// Find the group that this calendar belongs to.
|
||||||
var group = _internalGroupedAccountCalendars.FirstOrDefault(g => g.Account.Id == accountCalendar.Account.Id);
|
var group = _internalGroupedAccountCalendars.FirstOrDefault(g => g.Account.Id == accountCalendar.Account.Id);
|
||||||
|
|
||||||
@@ -396,6 +402,16 @@ public partial class AccountCalendarStateService : ObservableRecipient,
|
|||||||
lock (_calendarStateLock)
|
lock (_calendarStateLock)
|
||||||
{
|
{
|
||||||
groupedAccount = _internalGroupedAccountCalendars.FirstOrDefault(a => a.Account.Id == updatedAccount.Id);
|
groupedAccount = _internalGroupedAccountCalendars.FirstOrDefault(a => a.Account.Id == updatedAccount.Id);
|
||||||
|
|
||||||
|
if (!GroupedAccountCalendarViewModel.SupportsCalendar(updatedAccount))
|
||||||
|
{
|
||||||
|
if (groupedAccount != null)
|
||||||
|
{
|
||||||
|
RemoveGroupedAccountCalendar(groupedAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
groupedAccount?.UpdateAccount(updatedAccount);
|
groupedAccount?.UpdateAccount(updatedAccount);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
using Wino.Core.Domain.Interfaces;
|
using Wino.Core.Domain.Interfaces;
|
||||||
|
|
||||||
namespace Wino.Services;
|
namespace Wino.Services;
|
||||||
@@ -6,35 +7,73 @@ public class MailAuthenticatorConfiguration : IAuthenticatorConfig
|
|||||||
{
|
{
|
||||||
public string OutlookAuthenticatorClientId => "b19c2035-d740-49ff-b297-de6ec561b208";
|
public string OutlookAuthenticatorClientId => "b19c2035-d740-49ff-b297-de6ec561b208";
|
||||||
|
|
||||||
public string[] OutlookScope =>
|
|
||||||
[
|
|
||||||
"email",
|
|
||||||
"mail.readwrite",
|
|
||||||
"offline_access",
|
|
||||||
"mail.send",
|
|
||||||
"Mail.Send.Shared",
|
|
||||||
"Mail.ReadWrite.Shared",
|
|
||||||
"User.Read",
|
|
||||||
"Calendars.ReadBasic",
|
|
||||||
"Calendars.ReadWrite",
|
|
||||||
"Calendars.ReadWrite.Shared",
|
|
||||||
"Calendars.Read",
|
|
||||||
"Calendars.Read.Shared",
|
|
||||||
];
|
|
||||||
|
|
||||||
public string GmailAuthenticatorClientId => "973025879644-s7b4ur9p3rlgop6a22u7iuptdc0brnrn.apps.googleusercontent.com";
|
public string GmailAuthenticatorClientId => "973025879644-s7b4ur9p3rlgop6a22u7iuptdc0brnrn.apps.googleusercontent.com";
|
||||||
|
|
||||||
public string[] GmailScope =>
|
|
||||||
[
|
|
||||||
"https://mail.google.com/",
|
|
||||||
"https://www.googleapis.com/auth/userinfo.profile",
|
|
||||||
"https://www.googleapis.com/auth/gmail.labels",
|
|
||||||
"https://www.googleapis.com/auth/userinfo.email",
|
|
||||||
"https://www.googleapis.com/auth/calendar",
|
|
||||||
"https://www.googleapis.com/auth/calendar.events",
|
|
||||||
"https://www.googleapis.com/auth/calendar.settings.readonly",
|
|
||||||
"https://www.googleapis.com/auth/drive.file",
|
|
||||||
];
|
|
||||||
|
|
||||||
public string GmailTokenStoreIdentifier => "WinoMailGmailTokenStore";
|
public string GmailTokenStoreIdentifier => "WinoMailGmailTokenStore";
|
||||||
|
|
||||||
|
public string[] GetOutlookScope(bool isMailAccessGranted, bool isCalendarAccessGranted)
|
||||||
|
{
|
||||||
|
var scopes = new List<string>
|
||||||
|
{
|
||||||
|
"email",
|
||||||
|
"offline_access",
|
||||||
|
"User.Read"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isMailAccessGranted)
|
||||||
|
{
|
||||||
|
scopes.AddRange(
|
||||||
|
[
|
||||||
|
"mail.readwrite",
|
||||||
|
"mail.send",
|
||||||
|
"Mail.Send.Shared",
|
||||||
|
"Mail.ReadWrite.Shared"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCalendarAccessGranted)
|
||||||
|
{
|
||||||
|
scopes.AddRange(
|
||||||
|
[
|
||||||
|
"Calendars.ReadBasic",
|
||||||
|
"Calendars.ReadWrite",
|
||||||
|
"Calendars.ReadWrite.Shared",
|
||||||
|
"Calendars.Read",
|
||||||
|
"Calendars.Read.Shared"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [.. scopes];
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] GetGmailScope(bool isMailAccessGranted, bool isCalendarAccessGranted)
|
||||||
|
{
|
||||||
|
var scopes = new List<string>
|
||||||
|
{
|
||||||
|
"https://www.googleapis.com/auth/userinfo.profile",
|
||||||
|
"https://www.googleapis.com/auth/userinfo.email"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isMailAccessGranted)
|
||||||
|
{
|
||||||
|
scopes.AddRange(
|
||||||
|
[
|
||||||
|
"https://mail.google.com/",
|
||||||
|
"https://www.googleapis.com/auth/gmail.labels"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCalendarAccessGranted)
|
||||||
|
{
|
||||||
|
scopes.AddRange(
|
||||||
|
[
|
||||||
|
"https://www.googleapis.com/auth/calendar",
|
||||||
|
"https://www.googleapis.com/auth/calendar.events",
|
||||||
|
"https://www.googleapis.com/auth/calendar.settings.readonly",
|
||||||
|
"https://www.googleapis.com/auth/drive.file"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [.. scopes];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Wino.Mail.ViewModels;
|
using Wino.Mail.ViewModels;
|
||||||
using Wino.Mail.WinUI;
|
|
||||||
|
|
||||||
namespace Wino.Mail.WinUI.Views.Abstract;
|
namespace Wino.Mail.WinUI.Views.Abstract;
|
||||||
|
|
||||||
|
|||||||
@@ -90,12 +90,12 @@
|
|||||||
<SymbolIcon Symbol="ContactInfo" />
|
<SymbolIcon Symbol="ContactInfo" />
|
||||||
</controls:SegmentedItem.Icon>
|
</controls:SegmentedItem.Icon>
|
||||||
</controls:SegmentedItem>
|
</controls:SegmentedItem>
|
||||||
<controls:SegmentedItem Content="{x:Bind domain:Translator.AccountDetailsPage_TabMail, Mode=OneTime}">
|
<controls:SegmentedItem Content="{x:Bind domain:Translator.AccountDetailsPage_TabMail, Mode=OneTime}" Visibility="{x:Bind ViewModel.HasMailAccess, Mode=OneWay}">
|
||||||
<controls:SegmentedItem.Icon>
|
<controls:SegmentedItem.Icon>
|
||||||
<SymbolIcon Symbol="Mail" />
|
<SymbolIcon Symbol="Mail" />
|
||||||
</controls:SegmentedItem.Icon>
|
</controls:SegmentedItem.Icon>
|
||||||
</controls:SegmentedItem>
|
</controls:SegmentedItem>
|
||||||
<controls:SegmentedItem Content="{x:Bind domain:Translator.AccountDetailsPage_TabCalendar, Mode=OneTime}">
|
<controls:SegmentedItem Content="{x:Bind domain:Translator.AccountDetailsPage_TabCalendar, Mode=OneTime}" Visibility="{x:Bind ViewModel.HasCalendarAccess, Mode=OneWay}">
|
||||||
<controls:SegmentedItem.Icon>
|
<controls:SegmentedItem.Icon>
|
||||||
<FontIcon Glyph="" />
|
<FontIcon Glyph="" />
|
||||||
</controls:SegmentedItem.Icon>
|
</controls:SegmentedItem.Icon>
|
||||||
@@ -117,7 +117,10 @@
|
|||||||
Text="{x:Bind ViewModel.AccountName, Mode=TwoWay}" />
|
Text="{x:Bind ViewModel.AccountName, Mode=TwoWay}" />
|
||||||
</controls:SettingsCard>
|
</controls:SettingsCard>
|
||||||
|
|
||||||
<controls:SettingsCard Description="{x:Bind domain:Translator.SettingsEditAccountDetails_Description}" Header="{x:Bind domain:Translator.AccountSettingsDialog_AccountName}">
|
<controls:SettingsCard
|
||||||
|
Description="{x:Bind domain:Translator.SettingsEditAccountDetails_Description}"
|
||||||
|
Header="{x:Bind domain:Translator.AccountSettingsDialog_AccountName}"
|
||||||
|
Visibility="{x:Bind ViewModel.HasMailAccess, Mode=OneWay}">
|
||||||
<controls:SettingsCard.HeaderIcon>
|
<controls:SettingsCard.HeaderIcon>
|
||||||
<SymbolIcon Symbol="Mail" />
|
<SymbolIcon Symbol="Mail" />
|
||||||
</controls:SettingsCard.HeaderIcon>
|
</controls:SettingsCard.HeaderIcon>
|
||||||
@@ -127,7 +130,9 @@
|
|||||||
Text="{x:Bind ViewModel.SenderName, Mode=TwoWay}" />
|
Text="{x:Bind ViewModel.SenderName, Mode=TwoWay}" />
|
||||||
</controls:SettingsCard>
|
</controls:SettingsCard>
|
||||||
|
|
||||||
<controls:SettingsCard Header="{x:Bind domain:Translator.IMAPSetupDialog_MailAddress}">
|
<controls:SettingsCard
|
||||||
|
Header="{x:Bind domain:Translator.IMAPSetupDialog_MailAddress}"
|
||||||
|
Visibility="{x:Bind ViewModel.HasMailAccess, Mode=OneWay}">
|
||||||
<controls:SettingsCard.HeaderIcon>
|
<controls:SettingsCard.HeaderIcon>
|
||||||
<SymbolIcon Symbol="Mail" />
|
<SymbolIcon Symbol="Mail" />
|
||||||
</controls:SettingsCard.HeaderIcon>
|
</controls:SettingsCard.HeaderIcon>
|
||||||
@@ -137,6 +142,25 @@
|
|||||||
Text="{x:Bind ViewModel.Address, Mode=OneWay}" />
|
Text="{x:Bind ViewModel.Address, Mode=OneWay}" />
|
||||||
</controls:SettingsCard>
|
</controls:SettingsCard>
|
||||||
|
|
||||||
|
<controls:SettingsCard
|
||||||
|
Description="{x:Bind domain:Translator.AccountDetailsPage_CapabilityDescription, Mode=OneTime}"
|
||||||
|
Header="{x:Bind domain:Translator.AccountDetailsPage_CapabilityTitle, Mode=OneTime}"
|
||||||
|
Visibility="{x:Bind ViewModel.IsOAuthCapabilityEditable, Mode=OneWay}">
|
||||||
|
<controls:SettingsCard.HeaderIcon>
|
||||||
|
<FontIcon Glyph="" />
|
||||||
|
</controls:SettingsCard.HeaderIcon>
|
||||||
|
<ComboBox
|
||||||
|
MinWidth="220"
|
||||||
|
ItemsSource="{x:Bind ViewModel.CapabilityOptions, Mode=OneWay}"
|
||||||
|
SelectedItem="{x:Bind ViewModel.SelectedCapabilityOption, Mode=TwoWay}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="mailViewModels:AccountCapabilityOption">
|
||||||
|
<TextBlock Text="{x:Bind DisplayText}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</controls:SettingsCard>
|
||||||
|
|
||||||
<controls:SettingsCard
|
<controls:SettingsCard
|
||||||
HorizontalContentAlignment="Stretch"
|
HorizontalContentAlignment="Stretch"
|
||||||
VerticalContentAlignment="Stretch"
|
VerticalContentAlignment="Stretch"
|
||||||
@@ -212,6 +236,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="MailSettingsPanel"
|
x:Name="MailSettingsPanel"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
|
x:Load="{x:Bind ViewModel.HasMailAccess, Mode=OneWay}"
|
||||||
Visibility="Collapsed">
|
Visibility="Collapsed">
|
||||||
<StackPanel Spacing="4">
|
<StackPanel Spacing="4">
|
||||||
<StackPanel.ChildrenTransitions>
|
<StackPanel.ChildrenTransitions>
|
||||||
@@ -403,6 +428,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="CalendarSettingsPanel"
|
x:Name="CalendarSettingsPanel"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
|
x:Load="{x:Bind ViewModel.HasCalendarAccess, Mode=OneWay}"
|
||||||
Visibility="Collapsed">
|
Visibility="Collapsed">
|
||||||
<StackPanel MaxWidth="900" Spacing="12">
|
<StackPanel MaxWidth="900" Spacing="12">
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
Margin="0,2,0,0"
|
Margin="0,2,0,0"
|
||||||
Click="RootAccountTemplate_Click"
|
Click="RootAccountTemplate_Click"
|
||||||
CommandParameter="{x:Bind}"
|
CommandParameter="{x:Bind}"
|
||||||
Description="{x:Bind Account.Address}"
|
Description="{x:Bind DescriptionText}"
|
||||||
Header="{x:Bind Account.Name}"
|
Header="{x:Bind Account.Name}"
|
||||||
IsClickEnabled="True">
|
IsClickEnabled="True">
|
||||||
<winuiControls:SettingsCard.HeaderIcon>
|
<winuiControls:SettingsCard.HeaderIcon>
|
||||||
@@ -199,6 +199,7 @@
|
|||||||
x:Name="AccountsListView"
|
x:Name="AccountsListView"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
x:Load="{x:Bind ViewModel.HasAccountsDefined, Mode=OneWay}"
|
x:Load="{x:Bind ViewModel.HasAccountsDefined, Mode=OneWay}"
|
||||||
|
CanReorderItems="True"
|
||||||
ItemContainerStyle="{StaticResource StretchedItemContainerStyle}"
|
ItemContainerStyle="{StaticResource StretchedItemContainerStyle}"
|
||||||
ItemTemplateSelector="{StaticResource AccountProviderViewModelTemplateSelector}"
|
ItemTemplateSelector="{StaticResource AccountProviderViewModelTemplateSelector}"
|
||||||
ItemsSource="{x:Bind ViewModel.Accounts, Mode=OneWay}"
|
ItemsSource="{x:Bind ViewModel.Accounts, Mode=OneWay}"
|
||||||
@@ -208,7 +209,7 @@
|
|||||||
<winuiControls:SettingsCard Description="{x:Bind domain:Translator.SettingsStartupItem_Description}" Header="{x:Bind domain:Translator.SettingsStartupItem_Title}">
|
<winuiControls:SettingsCard Description="{x:Bind domain:Translator.SettingsStartupItem_Description}" Header="{x:Bind domain:Translator.SettingsStartupItem_Title}">
|
||||||
<ComboBox
|
<ComboBox
|
||||||
MinWidth="150"
|
MinWidth="150"
|
||||||
ItemsSource="{x:Bind ViewModel.Accounts, Mode=OneTime}"
|
ItemsSource="{x:Bind ViewModel.StartupAccounts, Mode=OneWay}"
|
||||||
SelectedItem="{x:Bind ViewModel.StartupAccount, Mode=TwoWay}">
|
SelectedItem="{x:Bind ViewModel.StartupAccount, Mode=TwoWay}">
|
||||||
<ComboBox.ItemTemplate>
|
<ComboBox.ItemTemplate>
|
||||||
<DataTemplate x:DataType="interfaces:IAccountProviderDetailViewModel">
|
<DataTemplate x:DataType="interfaces:IAccountProviderDetailViewModel">
|
||||||
@@ -220,16 +221,10 @@
|
|||||||
<SymbolIcon Symbol="Account" />
|
<SymbolIcon Symbol="Account" />
|
||||||
</winuiControls:SettingsCard.HeaderIcon>
|
</winuiControls:SettingsCard.HeaderIcon>
|
||||||
</winuiControls:SettingsCard>
|
</winuiControls:SettingsCard>
|
||||||
<winuiControls:SettingsCard
|
<winuiControls:SettingsCard Description="{x:Bind domain:Translator.WinoAccount_Management_LocalDataSectionDescription}" Header="{x:Bind domain:Translator.WinoAccount_Management_LocalDataSectionTitle}">
|
||||||
Description="{x:Bind domain:Translator.WinoAccount_Management_LocalDataSectionDescription}"
|
|
||||||
Header="{x:Bind domain:Translator.WinoAccount_Management_LocalDataSectionTitle}">
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||||
<Button
|
<Button Command="{x:Bind ViewModel.ImportLocalDataCommand}" Content="{x:Bind domain:Translator.WinoAccount_Management_LocalDataImportAction}" />
|
||||||
Command="{x:Bind ViewModel.ImportLocalDataCommand}"
|
<Button Command="{x:Bind ViewModel.ExportLocalDataCommand}" Content="{x:Bind domain:Translator.WinoAccount_Management_LocalDataExportAction}" />
|
||||||
Content="{x:Bind domain:Translator.WinoAccount_Management_LocalDataImportAction}" />
|
|
||||||
<Button
|
|
||||||
Command="{x:Bind ViewModel.ExportLocalDataCommand}"
|
|
||||||
Content="{x:Bind domain:Translator.WinoAccount_Management_LocalDataExportAction}" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<winuiControls:SettingsCard.HeaderIcon>
|
<winuiControls:SettingsCard.HeaderIcon>
|
||||||
<SymbolIcon Symbol="Sync" />
|
<SymbolIcon Symbol="Sync" />
|
||||||
|
|||||||
@@ -76,12 +76,18 @@
|
|||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.ColumnSpan="2"
|
Grid.ColumnSpan="2"
|
||||||
Header="{x:Bind ViewModel.PasswordHeaderText, Mode=OneWay}"
|
Header="{x:Bind ViewModel.PasswordHeaderText, Mode=OneWay}"
|
||||||
Password="{x:Bind ViewModel.Password, Mode=TwoWay}" />
|
Password="{x:Bind ViewModel.Password, Mode=TwoWay}"
|
||||||
|
Visibility="{x:Bind ViewModel.IsMailPasswordInputVisible, Mode=OneWay}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<CheckBox
|
<StackPanel Spacing="10">
|
||||||
Content="{x:Bind ViewModel.EnableCalendarSupportText, Mode=OneWay}"
|
<CheckBox
|
||||||
IsChecked="{x:Bind ViewModel.IsCalendarSupportEnabled, Mode=TwoWay}" />
|
Content="{x:Bind ViewModel.EnableMailSupportText, Mode=OneWay}"
|
||||||
|
IsChecked="{x:Bind ViewModel.IsMailSupportEnabled, Mode=TwoWay}" />
|
||||||
|
<CheckBox
|
||||||
|
Content="{x:Bind ViewModel.EnableCalendarSupportText, Mode=OneWay}"
|
||||||
|
IsChecked="{x:Bind ViewModel.IsCalendarSupportEnabled, Mode=TwoWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel Spacing="8">
|
<StackPanel Spacing="8">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -93,7 +99,8 @@
|
|||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
Command="{x:Bind ViewModel.AutoDiscoverSettingsCommand}"
|
Command="{x:Bind ViewModel.AutoDiscoverSettingsCommand}"
|
||||||
Content="{x:Bind ViewModel.AutoDiscoverButtonText, Mode=OneWay}"
|
Content="{x:Bind ViewModel.AutoDiscoverButtonText, Mode=OneWay}"
|
||||||
Style="{ThemeResource AccentButtonStyle}" />
|
Style="{ThemeResource AccentButtonStyle}"
|
||||||
|
Visibility="{x:Bind ViewModel.IsMailActionsVisible, Mode=OneWay}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
@@ -103,7 +110,8 @@
|
|||||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
CornerRadius="8">
|
CornerRadius="8"
|
||||||
|
Visibility="{x:Bind ViewModel.IsMailSettingsVisible, Mode=OneWay}">
|
||||||
<StackPanel Spacing="16">
|
<StackPanel Spacing="16">
|
||||||
<StackPanel Spacing="2">
|
<StackPanel Spacing="2">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -176,7 +184,8 @@
|
|||||||
<Button
|
<Button
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Command="{x:Bind ViewModel.TestImapConnectionCommand}"
|
Command="{x:Bind ViewModel.TestImapConnectionCommand}"
|
||||||
Content="{x:Bind ViewModel.TestImapButtonText, Mode=OneWay}" />
|
Content="{x:Bind ViewModel.TestImapButtonText, Mode=OneWay}"
|
||||||
|
Visibility="{x:Bind ViewModel.IsMailActionsVisible, Mode=OneWay}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,39 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<!-- Empty page for disposing composer or renderer page. -->
|
<Grid>
|
||||||
<Grid />
|
<StackPanel
|
||||||
|
MaxWidth="440"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Spacing="12"
|
||||||
|
Visibility="{x:Bind ViewModel.IsMailEmptyStateVisible, Mode=OneWay}">
|
||||||
|
<TextBlock
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Style="{StaticResource TitleTextBlockStyle}"
|
||||||
|
Text="{x:Bind ViewModel.MailEmptyStateTitle, Mode=OneWay}"
|
||||||
|
TextAlignment="Center"
|
||||||
|
TextWrapping="WrapWholeWords" />
|
||||||
|
<TextBlock
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||||
|
Style="{StaticResource BodyTextBlockStyle}"
|
||||||
|
Text="{x:Bind ViewModel.MailEmptyStateMessage, Mode=OneWay}"
|
||||||
|
TextAlignment="Center"
|
||||||
|
TextWrapping="WrapWholeWords" />
|
||||||
|
<StackPanel
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="12">
|
||||||
|
<Button
|
||||||
|
Command="{x:Bind ViewModel.AddAccountCommand}"
|
||||||
|
Content="{x:Bind ViewModel.AddAccountText, Mode=OneWay}"
|
||||||
|
Style="{ThemeResource AccentButtonStyle}" />
|
||||||
|
<Button
|
||||||
|
Command="{x:Bind ViewModel.ManageAccountsCommand}"
|
||||||
|
Content="{x:Bind ViewModel.ManageAccountsText, Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
</abstract:IdlePageAbstract>
|
</abstract:IdlePageAbstract>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -12,6 +12,16 @@ public sealed partial class ProviderSelectionPage : ProviderSelectionPageAbstrac
|
|||||||
|
|
||||||
private void ProviderSelectionChanged(ItemsView sender, ItemsViewSelectionChangedEventArgs args)
|
private void ProviderSelectionChanged(ItemsView sender, ItemsViewSelectionChangedEventArgs args)
|
||||||
{
|
{
|
||||||
|
if (sender.SelectedItem == null) return;
|
||||||
|
|
||||||
ViewModel.SelectedProvider = sender.SelectedItem as Wino.Core.Domain.Interfaces.IProviderDetail;
|
ViewModel.SelectedProvider = sender.SelectedItem as Wino.Core.Domain.Interfaces.IProviderDetail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AccountColorGridView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.AddedItems.Count > 0)
|
||||||
|
{
|
||||||
|
AccountColorFlyout.Hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,24 +50,33 @@
|
|||||||
<PasswordBox
|
<PasswordBox
|
||||||
x:Name="AppPasswordBox"
|
x:Name="AppPasswordBox"
|
||||||
Header="{x:Bind domain:Translator.ProviderSelection_AppPasswordHeader}"
|
Header="{x:Bind domain:Translator.ProviderSelection_AppPasswordHeader}"
|
||||||
PasswordChanged="AppPasswordChanged" />
|
PasswordChanged="AppPasswordChanged"
|
||||||
|
Visibility="{x:Bind ViewModel.RequiresAppSpecificPassword, Mode=OneWay}" />
|
||||||
|
|
||||||
<HyperlinkButton
|
<HyperlinkButton
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Command="{x:Bind ViewModel.OpenAppPasswordHelpCommand}"
|
Command="{x:Bind ViewModel.OpenAppPasswordHelpCommand}"
|
||||||
Content="{x:Bind domain:Translator.ProviderSelection_AppPasswordHelp}" />
|
Content="{x:Bind domain:Translator.ProviderSelection_AppPasswordHelp}"
|
||||||
|
Visibility="{x:Bind ViewModel.RequiresAppSpecificPassword, Mode=OneWay}" />
|
||||||
|
|
||||||
<!-- Divider -->
|
<!-- Divider -->
|
||||||
<Rectangle Height="1" Fill="{ThemeResource CardStrokeColorDefaultBrush}" />
|
<Rectangle
|
||||||
|
Height="1"
|
||||||
|
Fill="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||||
|
Visibility="{x:Bind ViewModel.IsCalendarModeSelectionVisible, Mode=OneWay}" />
|
||||||
|
|
||||||
<!-- Calendar Mode -->
|
<!-- Calendar Mode -->
|
||||||
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="{x:Bind domain:Translator.ProviderSelection_CalendarModeHeader}" />
|
<TextBlock
|
||||||
|
Style="{StaticResource BodyStrongTextBlockStyle}"
|
||||||
|
Text="{x:Bind domain:Translator.ProviderSelection_CalendarModeHeader}"
|
||||||
|
Visibility="{x:Bind ViewModel.IsCalendarModeSelectionVisible, Mode=OneWay}" />
|
||||||
|
|
||||||
<ListView
|
<ListView
|
||||||
x:Name="CalendarModeListView"
|
x:Name="CalendarModeListView"
|
||||||
IsItemClickEnabled="False"
|
IsItemClickEnabled="False"
|
||||||
SelectionChanged="CalendarModeSelectionChanged"
|
SelectionChanged="CalendarModeSelectionChanged"
|
||||||
SelectionMode="Single">
|
SelectionMode="Single"
|
||||||
|
Visibility="{x:Bind ViewModel.IsCalendarModeSelectionVisible, Mode=OneWay}">
|
||||||
<!-- Disabled -->
|
<!-- Disabled -->
|
||||||
<ListViewItem>
|
<ListViewItem>
|
||||||
<Grid Padding="12" ColumnSpacing="10">
|
<Grid Padding="12" ColumnSpacing="10">
|
||||||
|
|||||||
@@ -371,8 +371,13 @@ public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
|
|||||||
{
|
{
|
||||||
_ = DispatcherQueue.EnqueueAsync(async () =>
|
_ = DispatcherQueue.EnqueueAsync(async () =>
|
||||||
{
|
{
|
||||||
ViewModel.NavigationService.ChangeApplicationMode(WinoApplicationMode.Mail);
|
|
||||||
await ViewModel.MailClient.HandleAccountCreatedAsync(message.Account);
|
await ViewModel.MailClient.HandleAccountCreatedAsync(message.Account);
|
||||||
|
|
||||||
|
var targetMode = !message.Account.IsMailAccessGranted && message.Account.IsCalendarAccessGranted
|
||||||
|
? WinoApplicationMode.Calendar
|
||||||
|
: WinoApplicationMode.Mail;
|
||||||
|
|
||||||
|
ViewModel.NavigationService.ChangeApplicationMode(targetMode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -211,9 +211,6 @@ public class AccountService : BaseDatabaseService, IAccountService
|
|||||||
|
|
||||||
Guard.IsNotNull(token);
|
Guard.IsNotNull(token);
|
||||||
|
|
||||||
// Enable calendar access since new token includes calendar scopes
|
|
||||||
account.IsCalendarAccessGranted = true;
|
|
||||||
|
|
||||||
await UpdateAccountAsync(account);
|
await UpdateAccountAsync(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,6 +267,9 @@ public class AccountService : BaseDatabaseService, IAccountService
|
|||||||
|
|
||||||
public async Task CreateRootAliasAsync(Guid accountId, string address)
|
public async Task CreateRootAliasAsync(Guid accountId, string address)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(address))
|
||||||
|
return;
|
||||||
|
|
||||||
var rootAlias = new MailAccountAlias()
|
var rootAlias = new MailAccountAlias()
|
||||||
{
|
{
|
||||||
AccountId = accountId,
|
AccountId = accountId,
|
||||||
|
|||||||
@@ -97,6 +97,13 @@ public class DatabaseService : IDatabaseService
|
|||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!accountColumns.Any(c => c.Name == nameof(MailAccount.IsMailAccessGranted)))
|
||||||
|
{
|
||||||
|
await Connection
|
||||||
|
.ExecuteAsync($"ALTER TABLE {nameof(MailAccount)} ADD COLUMN {nameof(MailAccount.IsMailAccessGranted)} INTEGER NOT NULL DEFAULT 1")
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
var folderColumns = await Connection.GetTableInfoAsync(nameof(MailItemFolder)).ConfigureAwait(false);
|
var folderColumns = await Connection.GetTableInfoAsync(nameof(MailItemFolder)).ConfigureAwait(false);
|
||||||
|
|
||||||
if (!folderColumns.Any(c => c.Name == nameof(MailItemFolder.HighestKnownUid)))
|
if (!folderColumns.Any(c => c.Name == nameof(MailItemFolder.HighestKnownUid)))
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -245,7 +246,11 @@ public sealed class WinoAccountDataSyncService : IWinoAccountDataSyncService
|
|||||||
var serverInformation = CreateImportedServerInformation(mailbox, account.Id);
|
var serverInformation = CreateImportedServerInformation(mailbox, account.Id);
|
||||||
|
|
||||||
await _accountService.CreateAccountAsync(account, serverInformation).ConfigureAwait(false);
|
await _accountService.CreateAccountAsync(account, serverInformation).ConfigureAwait(false);
|
||||||
await _accountService.CreateRootAliasAsync(account.Id, account.Address).ConfigureAwait(false);
|
|
||||||
|
if (account.IsMailAccessGranted)
|
||||||
|
{
|
||||||
|
await _accountService.CreateRootAliasAsync(account.Id, account.Address).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (account.ProviderType == MailProviderType.IMAP4)
|
if (account.ProviderType == MailProviderType.IMAP4)
|
||||||
{
|
{
|
||||||
@@ -289,7 +294,7 @@ public sealed class WinoAccountDataSyncService : IWinoAccountDataSyncService
|
|||||||
? account.ServerInformation
|
? account.ServerInformation
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return new UserMailboxSyncItemDto
|
var mailbox = new UserMailboxSyncItemDto
|
||||||
{
|
{
|
||||||
Address = account.Address ?? string.Empty,
|
Address = account.Address ?? string.Empty,
|
||||||
ProviderType = (int)account.ProviderType,
|
ProviderType = (int)account.ProviderType,
|
||||||
@@ -316,6 +321,10 @@ public sealed class WinoAccountDataSyncService : IWinoAccountDataSyncService
|
|||||||
ProxyServerPort = serverInformation?.ProxyServerPort,
|
ProxyServerPort = serverInformation?.ProxyServerPort,
|
||||||
MaxConcurrentClients = serverInformation?.MaxConcurrentClients
|
MaxConcurrentClients = serverInformation?.MaxConcurrentClients
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SetOptionalBooleanProperty(mailbox, "IsMailAccessGranted", account.IsMailAccessGranted);
|
||||||
|
|
||||||
|
return mailbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MailAccount CreateImportedAccount(UserMailboxSyncItemDto mailbox)
|
private static MailAccount CreateImportedAccount(UserMailboxSyncItemDto mailbox)
|
||||||
@@ -334,6 +343,7 @@ public sealed class WinoAccountDataSyncService : IWinoAccountDataSyncService
|
|||||||
Base64ProfilePictureData = string.Empty,
|
Base64ProfilePictureData = string.Empty,
|
||||||
CreatedAt = DateTime.UtcNow,
|
CreatedAt = DateTime.UtcNow,
|
||||||
InitialSynchronizationRange = InitialSynchronizationRange.SixMonths,
|
InitialSynchronizationRange = InitialSynchronizationRange.SixMonths,
|
||||||
|
IsMailAccessGranted = GetOptionalBooleanProperty(mailbox, "IsMailAccessGranted", defaultValue: true),
|
||||||
IsCalendarAccessGranted = mailbox.IsCalendarAccessGranted,
|
IsCalendarAccessGranted = mailbox.IsCalendarAccessGranted,
|
||||||
SynchronizationDeltaIdentifier = string.Empty,
|
SynchronizationDeltaIdentifier = string.Empty,
|
||||||
CalendarSynchronizationDeltaIdentifier = string.Empty,
|
CalendarSynchronizationDeltaIdentifier = string.Empty,
|
||||||
@@ -410,6 +420,38 @@ public sealed class WinoAccountDataSyncService : IWinoAccountDataSyncService
|
|||||||
private static string CreateMailboxKey(string? address, int providerType)
|
private static string CreateMailboxKey(string? address, int providerType)
|
||||||
=> $"{address?.Trim().ToLowerInvariant()}|{providerType}";
|
=> $"{address?.Trim().ToLowerInvariant()}|{providerType}";
|
||||||
|
|
||||||
|
private static bool GetOptionalBooleanProperty<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(
|
||||||
|
T instance,
|
||||||
|
string propertyName,
|
||||||
|
bool defaultValue)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
var property = typeof(T).GetProperty(propertyName);
|
||||||
|
if (property?.PropertyType != typeof(bool) || !property.CanRead)
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
return property.GetValue(instance) is bool boolValue
|
||||||
|
? boolValue
|
||||||
|
: defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetOptionalBooleanProperty<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(
|
||||||
|
T instance,
|
||||||
|
string propertyName,
|
||||||
|
bool value)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var property = typeof(T).GetProperty(propertyName);
|
||||||
|
if (property?.PropertyType == typeof(bool) && property.CanWrite)
|
||||||
|
{
|
||||||
|
property.SetValue(instance, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static string TrimUtf8Bom(string jsonContent)
|
private static string TrimUtf8Bom(string jsonContent)
|
||||||
=> !string.IsNullOrEmpty(jsonContent) && jsonContent[0] == '\uFEFF'
|
=> !string.IsNullOrEmpty(jsonContent) && jsonContent[0] == '\uFEFF'
|
||||||
? jsonContent[1..]
|
? jsonContent[1..]
|
||||||
|
|||||||
Reference in New Issue
Block a user