Couple fixes for beta.

This commit is contained in:
Burak Kaan Köse
2026-04-16 13:45:11 +02:00
parent 65f7e0236a
commit 784144cd13
12 changed files with 217 additions and 33 deletions
@@ -23,6 +23,20 @@ public interface IAccountService
/// <returns>All local accounts</returns> /// <returns>All local accounts</returns>
Task<List<MailAccount>> GetAccountsAsync(); Task<List<MailAccount>> GetAccountsAsync();
/// <summary>
/// Checks whether an account with the same display name already exists.
/// </summary>
/// <param name="name">Account display name.</param>
/// <param name="excludedAccountId">Optional account id to exclude from the check.</param>
Task<bool> AccountNameExistsAsync(string name, Guid? excludedAccountId = null);
/// <summary>
/// Checks whether an account with the same primary address already exists.
/// </summary>
/// <param name="address">Primary e-mail address.</param>
/// <param name="excludedAccountId">Optional account id to exclude from the check.</param>
Task<bool> AccountAddressExistsAsync(string address, Guid? excludedAccountId = null);
/// <summary> /// <summary>
/// Returns single MailAccount /// Returns single MailAccount
/// </summary> /// </summary>
@@ -274,10 +274,13 @@
"Dialog_DontAskAgain": "Don't ask again", "Dialog_DontAskAgain": "Don't ask again",
"DialogMessage_AccountLimitMessage": "You have reached the account creation limit.\nWould you like to purchase 'Unlimited Account' add-on to continue?", "DialogMessage_AccountLimitMessage": "You have reached the account creation limit.\nWould you like to purchase 'Unlimited Account' add-on to continue?",
"DialogMessage_AccountLimitTitle": "Account Limit Reached", "DialogMessage_AccountLimitTitle": "Account Limit Reached",
"DialogMessage_AccountAddressExistsMessage": "An account with the same e-mail address already exists.",
"DialogMessage_AccountExistsTitle": "Existing Account",
"DialogMessage_AliasCreatedMessage": "New alias is succesfully created.", "DialogMessage_AliasCreatedMessage": "New alias is succesfully created.",
"DialogMessage_AliasCreatedTitle": "Created New Alias", "DialogMessage_AliasCreatedTitle": "Created New Alias",
"DialogMessage_AliasExistsMessage": "This alias is already in use.", "DialogMessage_AliasExistsMessage": "This alias is already in use.",
"DialogMessage_AliasExistsTitle": "Existing Alias", "DialogMessage_AliasExistsTitle": "Existing Alias",
"DialogMessage_AccountNameExistsMessage": "An account with the same name already exists.",
"DialogMessage_AliasNotSelectedMessage": "You must select an alias before sending a message.", "DialogMessage_AliasNotSelectedMessage": "You must select an alias before sending a message.",
"DialogMessage_AliasNotSelectedTitle": "Missing Alias", "DialogMessage_AliasNotSelectedTitle": "Missing Alias",
"DialogMessage_CantDeleteRootAliasMessage": "Root alias can't be deleted. This is your main identity associated with your account setup.", "DialogMessage_CantDeleteRootAliasMessage": "Root alias can't be deleted. This is your main identity associated with your account setup.",
@@ -883,6 +886,7 @@
"SettingsMailCategories_Title": "Categories", "SettingsMailCategories_Title": "Categories",
"SettingsEditAccountDetails_Title": "Edit Account Details", "SettingsEditAccountDetails_Title": "Edit Account Details",
"SettingsEditAccountDetails_Description": "Change account name, sender name and assign a new color if you like.", "SettingsEditAccountDetails_Description": "Change account name, sender name and assign a new color if you like.",
"SettingsAccountDetails_NavigationTitle": "{0} details",
"EditAccountDetailsPage_SaveSuccess_Title": "Changes Saved", "EditAccountDetailsPage_SaveSuccess_Title": "Changes Saved",
"EditAccountDetailsPage_SaveSuccess_Message": "Your account details have been updated successfully.", "EditAccountDetailsPage_SaveSuccess_Message": "Your account details have been updated successfully.",
"MailCategoryManagementPage_Title": "Categories", "MailCategoryManagementPage_Title": "Categories",
@@ -69,7 +69,7 @@ public abstract partial class AccountManagementPageViewModelBase : CoreBaseViewM
[RelayCommand] [RelayCommand]
private void NavigateAccountDetails(AccountProviderDetailViewModel accountDetails) private void NavigateAccountDetails(AccountProviderDetailViewModel accountDetails)
{ {
Messenger.Send(new BreadcrumbNavigationRequested(accountDetails.Account.Name, Messenger.Send(new BreadcrumbNavigationRequested(GetAccountDetailsTitle(accountDetails.Account),
WinoPage.AccountDetailsPage, WinoPage.AccountDetailsPage,
accountDetails.Account.Id)); accountDetails.Account.Id));
} }
@@ -131,4 +131,9 @@ public abstract partial class AccountManagementPageViewModelBase : CoreBaseViewM
{ {
OnPropertyChanged(nameof(HasAccountsDefined)); OnPropertyChanged(nameof(HasAccountsDefined));
} }
private static string GetAccountDetailsTitle(MailAccount account)
=> !string.IsNullOrWhiteSpace(account?.Address)
? string.Format(Translator.SettingsAccountDetails_NavigationTitle, account.Address)
: account?.Name ?? Translator.AccountDetailsPage_Title;
} }
@@ -149,7 +149,7 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel
switch (account) switch (account)
{ {
case AccountProviderDetailViewModel accountDetails: case AccountProviderDetailViewModel accountDetails:
Messenger.Send(new BreadcrumbNavigationRequested(accountDetails.Account.Name, WinoPage.AccountDetailsPage, accountDetails.Account.Id)); Messenger.Send(new BreadcrumbNavigationRequested(GetAccountDetailsTitle(accountDetails.Account), WinoPage.AccountDetailsPage, accountDetails.Account.Id));
break; break;
case MergedAccountProviderDetailViewModel mergedAccount: case MergedAccountProviderDetailViewModel mergedAccount:
Messenger.Send(new BreadcrumbNavigationRequested(mergedAccount.MergedInbox.Name, WinoPage.MergedAccountDetailsPage, mergedAccount)); Messenger.Send(new BreadcrumbNavigationRequested(mergedAccount.MergedInbox.Name, WinoPage.MergedAccountDetailsPage, mergedAccount));
@@ -201,6 +201,11 @@ public partial class SettingOptionsPageViewModel : CoreBaseViewModel
}); });
} }
private static string GetAccountDetailsTitle(MailAccount account)
=> !string.IsNullOrWhiteSpace(account?.Address)
? string.Format(Translator.SettingsAccountDetails_NavigationTitle, account.Address)
: account?.Name ?? Translator.AccountDetailsPage_Title;
private void InitializeQuickSettings() private void InitializeQuickSettings()
{ {
_isInitializingSettings = true; _isInitializingSettings = true;
@@ -220,7 +220,12 @@ public partial class AccountSetupProgressPageViewModel : MailBaseViewModel
_createdAccount.Base64ProfilePictureData = profileResult.ProfileInformation.Base64ProfilePictureData; _createdAccount.Base64ProfilePictureData = profileResult.ProfileInformation.Base64ProfilePictureData;
if (!string.IsNullOrEmpty(profileResult.ProfileInformation.AccountAddress)) if (!string.IsNullOrEmpty(profileResult.ProfileInformation.AccountAddress))
{
if (await _accountService.AccountAddressExistsAsync(profileResult.ProfileInformation.AccountAddress, _createdAccount.Id).ConfigureAwait(false))
throw new InvalidOperationException(Translator.DialogMessage_AccountAddressExistsMessage);
_createdAccount.Address = profileResult.ProfileInformation.AccountAddress; _createdAccount.Address = profileResult.ProfileInformation.AccountAddress;
}
await _accountService.UpdateProfileInformationAsync(_createdAccount.Id, profileResult.ProfileInformation); await _accountService.UpdateProfileInformationAsync(_createdAccount.Id, profileResult.ProfileInformation);
} }
@@ -34,6 +34,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
private Guid _editingAccountId; private Guid _editingAccountId;
private SpecialImapProvider _editingSpecialImapProvider; private SpecialImapProvider _editingSpecialImapProvider;
private TaskCompletionSource<ImapCalDavSetupResult> _completionSource; private TaskCompletionSource<ImapCalDavSetupResult> _completionSource;
private AccountCreationDialogResult _accountCreationContext;
private bool _isCompletionFinalized; private bool _isCompletionFinalized;
private bool _localOnlyInfoShown; private bool _localOnlyInfoShown;
@@ -283,6 +284,7 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
_pageMode = context.Mode; _pageMode = context.Mode;
_editingAccountId = context.AccountId; _editingAccountId = context.AccountId;
_completionSource = context.CompletionSource; _completionSource = context.CompletionSource;
_accountCreationContext = context.AccountCreationDialogResult;
_isCompletionFinalized = false; _isCompletionFinalized = false;
_localOnlyInfoShown = false; _localOnlyInfoShown = false;
SelectedSetupTabIndex = 0; SelectedSetupTabIndex = 0;
@@ -406,6 +408,13 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
ValidateImapSettings(serverInformation); ValidateImapSettings(serverInformation);
ValidateCalendarModeSpecificSettings(serverInformation); ValidateCalendarModeSpecificSettings(serverInformation);
var excludedAccountId = _pageMode == ImapCalDavSettingsPageMode.Edit
? _editingAccountId
: (Guid?)null;
if (!await ValidateAccountUniquenessAsync(excludedAccountId).ConfigureAwait(false))
return;
await ValidateImapConnectivityAsync(serverInformation); await ValidateImapConnectivityAsync(serverInformation);
IsImapValidationSucceeded = true; IsImapValidationSucceeded = true;
@@ -770,6 +779,34 @@ public partial class ImapCalDavSettingsPageViewModel : MailBaseViewModel
Messenger.Send(new BackBreadcrumNavigationRequested()); Messenger.Send(new BackBreadcrumNavigationRequested());
} }
private async Task<bool> ValidateAccountUniquenessAsync(Guid? excludedAccountId)
{
var accountName = (_pageMode == ImapCalDavSettingsPageMode.Create || _pageMode == ImapCalDavSettingsPageMode.Wizard)
? _accountCreationContext?.AccountName
: null;
if (!string.IsNullOrWhiteSpace(accountName) &&
await _accountService.AccountNameExistsAsync(accountName, excludedAccountId).ConfigureAwait(false))
{
_mailDialogService.InfoBarMessage(
Translator.DialogMessage_AccountExistsTitle,
Translator.DialogMessage_AccountNameExistsMessage,
InfoBarMessageType.Error);
return false;
}
if (await _accountService.AccountAddressExistsAsync(EmailAddress, excludedAccountId).ConfigureAwait(false))
{
_mailDialogService.InfoBarMessage(
Translator.DialogMessage_AccountExistsTitle,
Translator.DialogMessage_AccountAddressExistsMessage,
InfoBarMessageType.Error);
return false;
}
return true;
}
private async Task SaveEditFlowAsync(CustomServerInformation serverInformation) private async Task SaveEditFlowAsync(CustomServerInformation serverInformation)
{ {
var account = await _accountService.GetAccountAsync(_editingAccountId).ConfigureAwait(false); var account = await _accountService.GetAccountAsync(_editingAccountId).ConfigureAwait(false);
@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
@@ -16,6 +17,8 @@ namespace Wino.Mail.ViewModels;
public partial class ProviderSelectionPageViewModel : MailBaseViewModel public partial class ProviderSelectionPageViewModel : MailBaseViewModel
{ {
private readonly IAccountService _accountService;
private readonly IDialogServiceBase _dialogService;
private readonly IProviderService _providerService; private readonly IProviderService _providerService;
private readonly INewThemeService _themeService; private readonly INewThemeService _themeService;
@@ -53,10 +56,14 @@ public partial class ProviderSelectionPageViewModel : MailBaseViewModel
public bool IsInitialSynchronizationWarningVisible => SelectedInitialSynchronizationRange?.IsEverything == true; public bool IsInitialSynchronizationWarningVisible => SelectedInitialSynchronizationRange?.IsEverything == true;
public ProviderSelectionPageViewModel( public ProviderSelectionPageViewModel(
IAccountService accountService,
IDialogServiceBase dialogService,
IProviderService providerService, IProviderService providerService,
INewThemeService themeService, INewThemeService themeService,
WelcomeWizardContext wizardContext) WelcomeWizardContext wizardContext)
{ {
_accountService = accountService;
_dialogService = dialogService;
_providerService = providerService; _providerService = providerService;
_themeService = themeService; _themeService = themeService;
WizardContext = wizardContext; WizardContext = wizardContext;
@@ -107,10 +114,19 @@ public partial class ProviderSelectionPageViewModel : MailBaseViewModel
} }
[RelayCommand] [RelayCommand]
private void Proceed() private async Task ProceedAsync()
{ {
if (!CanProceed) return; if (!CanProceed) return;
if (await _accountService.AccountNameExistsAsync(AccountName).ConfigureAwait(false))
{
await _dialogService.ShowMessageAsync(
Translator.DialogMessage_AccountNameExistsMessage,
Translator.DialogMessage_AccountExistsTitle,
WinoCustomMessageDialogIcon.Warning);
return;
}
// Persist to wizard context // Persist to wizard context
WizardContext.SelectedProvider = SelectedProvider; WizardContext.SelectedProvider = SelectedProvider;
WizardContext.AccountName = AccountName?.Trim(); WizardContext.AccountName = AccountName?.Trim();
@@ -15,6 +15,8 @@ namespace Wino.Mail.ViewModels;
public partial class SpecialImapCredentialsPageViewModel : MailBaseViewModel public partial class SpecialImapCredentialsPageViewModel : MailBaseViewModel
{ {
private readonly IAccountService _accountService;
private readonly IDialogServiceBase _dialogService;
private static readonly Dictionary<SpecialImapProvider, string> AppPasswordHelpLinks = new() private static readonly Dictionary<SpecialImapProvider, string> AppPasswordHelpLinks = new()
{ {
{ SpecialImapProvider.iCloud, "https://support.apple.com/en-us/102654" }, { SpecialImapProvider.iCloud, "https://support.apple.com/en-us/102654" },
@@ -56,9 +58,13 @@ public partial class SpecialImapCredentialsPageViewModel : MailBaseViewModel
: Translator.ProviderSelection_CalendarMode_CalDavDescription_Yahoo; : Translator.ProviderSelection_CalendarMode_CalDavDescription_Yahoo;
public SpecialImapCredentialsPageViewModel( public SpecialImapCredentialsPageViewModel(
IAccountService accountService,
IDialogServiceBase dialogService,
INativeAppService nativeAppService, INativeAppService nativeAppService,
WelcomeWizardContext wizardContext) WelcomeWizardContext wizardContext)
{ {
_accountService = accountService;
_dialogService = dialogService;
_nativeAppService = nativeAppService; _nativeAppService = nativeAppService;
WizardContext = wizardContext; WizardContext = wizardContext;
} }
@@ -98,10 +104,19 @@ public partial class SpecialImapCredentialsPageViewModel : MailBaseViewModel
} }
[RelayCommand] [RelayCommand]
private void Proceed() private async Task ProceedAsync()
{ {
if (!CanProceed) return; if (!CanProceed) return;
if (await _accountService.AccountAddressExistsAsync(EmailAddress).ConfigureAwait(false))
{
await _dialogService.ShowMessageAsync(
Translator.DialogMessage_AccountAddressExistsMessage,
Translator.DialogMessage_AccountExistsTitle,
WinoCustomMessageDialogIcon.Warning);
return;
}
WizardContext.DisplayName = DisplayName?.Trim(); WizardContext.DisplayName = DisplayName?.Trim();
WizardContext.EmailAddress = EmailAddress?.Trim(); WizardContext.EmailAddress = EmailAddress?.Trim();
WizardContext.AppSpecificPassword = AppSpecificPassword?.Trim(); WizardContext.AppSpecificPassword = AppSpecificPassword?.Trim();
@@ -127,6 +127,16 @@
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.HeaderIcon>
<SymbolIcon Symbol="Mail" />
</controls:SettingsCard.HeaderIcon>
<TextBox
Width="250"
IsReadOnly="True"
Text="{x:Bind ViewModel.Address, Mode=OneWay}" />
</controls:SettingsCard>
<controls:SettingsCard <controls:SettingsCard
HorizontalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
+61 -28
View File
@@ -22,12 +22,10 @@ using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Models.Reader; using Wino.Core.Domain.Models.Reader;
using Wino.Mail.ViewModels.Data; using Wino.Mail.ViewModels.Data;
using Wino.Mail.ViewModels.Messages;
using Wino.Mail.WinUI.Controls; using Wino.Mail.WinUI.Controls;
using Wino.Mail.WinUI.Extensions; using Wino.Mail.WinUI.Extensions;
using Wino.Mail.WinUI.Interfaces; using Wino.Mail.WinUI.Interfaces;
using Wino.Mail.WinUI.Models; using Wino.Mail.WinUI.Models;
using Wino.Messaging.Client.Mails;
using Wino.Messaging.Client.Shell; using Wino.Messaging.Client.Shell;
using Wino.Views.Abstract; using Wino.Views.Abstract;
@@ -42,6 +40,7 @@ public sealed partial class ComposePage : ComposePageAbstract,
private bool _isPoppedOut; private bool _isPoppedOut;
private bool _isInitialFocusHandled; private bool _isInitialFocusHandled;
private readonly Dictionary<TokenizingTextBox, List<AccountContact>> _recipientSuggestions = [];
public bool SupportsPopOut => !_isPoppedOut; public bool SupportsPopOut => !_isPoppedOut;
public event EventHandler<PopOutRequestedEventArgs>? PopOutRequested; public event EventHandler<PopOutRequestedEventArgs>? PopOutRequested;
@@ -132,12 +131,17 @@ public sealed partial class ComposePage : ComposePageAbstract,
{ {
_ = ViewModel.ExecuteUIThread(() => _ = ViewModel.ExecuteUIThread(() =>
{ {
var addresses = x.Result; var addresses = x.Result ?? [];
_recipientSuggestions[box] = addresses;
senderBox.ItemsSource = addresses; senderBox.ItemsSource = addresses;
}); });
}); });
} }
else
{
_recipientSuggestions[box] = [];
}
} }
}); });
} }
@@ -293,7 +297,7 @@ public sealed partial class ComposePage : ComposePageAbstract,
{ {
base.OnNavigatedTo(e); base.OnNavigatedTo(e);
FocusManager.GotFocus += GlobalFocusManagerGotFocus; // FocusManager.GotFocus += GlobalFocusManagerGotFocus;
var webView = GetWebView(); var webView = GetWebView();
@@ -335,36 +339,60 @@ public sealed partial class ComposePage : ComposePageAbstract,
private async void TokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args) private async void TokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
{ {
// Check is valid email.
if (!EmailValidator.Validate(args.TokenText))
{
args.Cancel = true;
ViewModel.NotifyInvalidEmail(args.TokenText);
return;
}
var deferral = args.GetDeferral(); var deferral = args.GetDeferral();
try
var addedItem = (sender.Tag?.ToString()) switch
{ {
"ToBox" => await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.ToItems), var suggestedContact = GetFirstSuggestedContact(sender);
"CCBox" => await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.CCItems), var tokenText = suggestedContact?.Address ?? args.TokenText;
"BCCBox" => await ViewModel.GetAddressInformationAsync(args.TokenText, ViewModel.BCCItems), var addressCollection = sender.Tag?.ToString() switch
_ => null {
}; "ToBox" => ViewModel.ToItems,
"CCBox" => ViewModel.CCItems,
"BCCBox" => ViewModel.BCCItems,
_ => null
};
if (addedItem == null) if (suggestedContact == null && !EmailValidator.Validate(tokenText))
{ {
args.Cancel = true; args.Cancel = true;
ViewModel.NotifyAddressExists(); ViewModel.NotifyInvalidEmail(args.TokenText);
return;
}
AccountContact? addedItem = null;
if (suggestedContact != null)
{
addedItem = addressCollection?.Any(a => string.Equals(a.Address, suggestedContact.Address, StringComparison.OrdinalIgnoreCase)) == true
? null
: suggestedContact;
}
else
{
addedItem = sender.Tag?.ToString() switch
{
"ToBox" => await ViewModel.GetAddressInformationAsync(tokenText, ViewModel.ToItems),
"CCBox" => await ViewModel.GetAddressInformationAsync(tokenText, ViewModel.CCItems),
"BCCBox" => await ViewModel.GetAddressInformationAsync(tokenText, ViewModel.BCCItems),
_ => null
};
}
if (addedItem == null)
{
args.Cancel = true;
ViewModel.NotifyAddressExists();
}
else
{
args.Item = addedItem;
}
} }
else finally
{ {
args.Item = addedItem; _recipientSuggestions[sender] = [];
deferral.Complete();
} }
deferral.Complete();
} }
void IRecipient<ApplicationThemeChanged>.Receive(ApplicationThemeChanged message) void IRecipient<ApplicationThemeChanged>.Receive(ApplicationThemeChanged message)
@@ -438,6 +466,11 @@ public sealed partial class ComposePage : ComposePageAbstract,
} }
} }
private AccountContact? GetFirstSuggestedContact(TokenizingTextBox box)
=> _recipientSuggestions.TryGetValue(box, out var suggestions)
? suggestions.FirstOrDefault()
: null;
private void ComposerLoaded(object sender, RoutedEventArgs e) private void ComposerLoaded(object sender, RoutedEventArgs e)
{ {
if (ShouldFocusRecipients()) if (ShouldFocusRecipients())
+7 -1
View File
@@ -5,6 +5,7 @@ using CommunityToolkit.Mvvm.Messaging;
using Microsoft.UI.Xaml.Media.Animation; using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.UI.Xaml.Navigation; using Microsoft.UI.Xaml.Navigation;
using Wino.Core.Domain; using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Settings; using Wino.Core.Domain.Models.Settings;
using Wino.Helpers; using Wino.Helpers;
@@ -162,7 +163,7 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
DispatcherQueue.TryEnqueue(() => DispatcherQueue.TryEnqueue(() =>
{ {
activePage.Title = message.Account.Name; activePage.Title = GetAccountDetailsTitle(message.Account);
_ = RefreshCurrentPageStateAsync(); _ = RefreshCurrentPageStateAsync();
UpdateWindowTitle(); UpdateWindowTitle();
}); });
@@ -249,6 +250,11 @@ public sealed partial class SettingsPage : SettingsPageAbstract,
: activeTitle; : activeTitle;
} }
private static string GetAccountDetailsTitle(MailAccount account)
=> !string.IsNullOrWhiteSpace(account?.Address)
? string.Format(Translator.SettingsAccountDetails_NavigationTitle, account.Address)
: account?.Name ?? Translator.AccountDetailsPage_Title;
public Task OnTitleBarSearchTextChangedAsync() public Task OnTitleBarSearchTextChangedAsync()
{ {
SearchSuggestions.Clear(); SearchSuggestions.Clear();
+34
View File
@@ -240,6 +240,34 @@ public class AccountService : BaseDatabaseService, IAccountService
return accounts; return accounts;
} }
public async Task<bool> AccountNameExistsAsync(string name, Guid? excludedAccountId = null)
{
var normalizedName = name?.Trim();
if (string.IsNullOrWhiteSpace(normalizedName))
return false;
var accounts = await Connection.Table<MailAccount>().ToListAsync().ConfigureAwait(false);
return accounts.Any(account =>
account.Id != excludedAccountId &&
string.Equals(account.Name?.Trim(), normalizedName, StringComparison.OrdinalIgnoreCase));
}
public async Task<bool> AccountAddressExistsAsync(string address, Guid? excludedAccountId = null)
{
var normalizedAddress = address?.Trim();
if (string.IsNullOrWhiteSpace(normalizedAddress))
return false;
var accounts = await Connection.Table<MailAccount>().ToListAsync().ConfigureAwait(false);
return accounts.Any(account =>
account.Id != excludedAccountId &&
string.Equals(account.Address?.Trim(), normalizedAddress, StringComparison.OrdinalIgnoreCase));
}
public async Task CreateRootAliasAsync(Guid accountId, string address) public async Task CreateRootAliasAsync(Guid accountId, string address)
{ {
var rootAlias = new MailAccountAlias() var rootAlias = new MailAccountAlias()
@@ -610,6 +638,12 @@ public class AccountService : BaseDatabaseService, IAccountService
{ {
Guard.IsNotNull(account); Guard.IsNotNull(account);
if (await AccountNameExistsAsync(account.Name).ConfigureAwait(false))
throw new InvalidOperationException(Translator.DialogMessage_AccountNameExistsMessage);
if (await AccountAddressExistsAsync(account.Address).ConfigureAwait(false))
throw new InvalidOperationException(Translator.DialogMessage_AccountAddressExistsMessage);
if (!account.CreatedAt.HasValue) if (!account.CreatedAt.HasValue)
{ {
account.CreatedAt = DateTime.UtcNow; account.CreatedAt = DateTime.UtcNow;