File scoped namespaces

This commit is contained in:
Aleh Khantsevich
2025-02-16 11:35:43 +01:00
committed by GitHub
parent c1336428dc
commit d31d8f574e
617 changed files with 32118 additions and 32737 deletions

View File

@@ -149,7 +149,7 @@ csharp_preferred_modifier_order = public,private,protected,internal,static,exter
# Code-block preferences # Code-block preferences
csharp_prefer_braces = true:silent csharp_prefer_braces = true:silent
csharp_prefer_simple_using_statement = true:suggestion csharp_prefer_simple_using_statement = true:suggestion
csharp_style_namespace_declarations = block_scoped:silent csharp_style_namespace_declarations = file_scoped:error
# Expression-level preferences # Expression-level preferences
csharp_prefer_simple_default_expression = true:suggestion csharp_prefer_simple_default_expression = true:suggestion
@@ -288,3 +288,5 @@ csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_prefer_readonly_struct = true:suggestion csharp_style_prefer_readonly_struct = true:suggestion
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
csharp_style_prefer_primary_constructors = true:silent
csharp_prefer_system_threading_lock = true:suggestion

View File

@@ -1,17 +1,16 @@
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
namespace Wino.Authentication namespace Wino.Authentication;
public abstract class BaseAuthenticator
{ {
public abstract class BaseAuthenticator public abstract MailProviderType ProviderType { get; }
protected IAuthenticatorConfig AuthenticatorConfig { get; }
protected BaseAuthenticator(IAuthenticatorConfig authenticatorConfig)
{ {
public abstract MailProviderType ProviderType { get; }
protected IAuthenticatorConfig AuthenticatorConfig { get; }
protected BaseAuthenticator(IAuthenticatorConfig authenticatorConfig) AuthenticatorConfig = authenticatorConfig;
{
AuthenticatorConfig = authenticatorConfig;
}
} }
} }

View File

@@ -7,45 +7,44 @@ using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Authentication; using Wino.Core.Domain.Models.Authentication;
namespace Wino.Authentication namespace Wino.Authentication;
public class GmailAuthenticator : BaseAuthenticator, IGmailAuthenticator
{ {
public class GmailAuthenticator : BaseAuthenticator, IGmailAuthenticator public GmailAuthenticator(IAuthenticatorConfig authConfig) : base(authConfig)
{ {
public GmailAuthenticator(IAuthenticatorConfig authConfig) : base(authConfig) }
public string ClientId => AuthenticatorConfig.GmailAuthenticatorClientId;
public bool ProposeCopyAuthURL { get; set; }
public override MailProviderType ProviderType => MailProviderType.Gmail;
/// <summary>
/// Generates the token information for the given account.
/// For gmail, interactivity is automatically handled when you get the token.
/// </summary>
/// <param name="account">Account to get token for.</param>
public Task<TokenInformationEx> GenerateTokenInformationAsync(MailAccount account)
=> GetTokenInformationAsync(account);
public async Task<TokenInformationEx> GetTokenInformationAsync(MailAccount account)
{
var userCredential = await GetGoogleUserCredentialAsync(account);
if (userCredential.Token.IsStale)
{ {
await userCredential.RefreshTokenAsync(CancellationToken.None);
} }
public string ClientId => AuthenticatorConfig.GmailAuthenticatorClientId; return new TokenInformationEx(userCredential.Token.AccessToken, account.Address);
public bool ProposeCopyAuthURL { get; set; } }
public override MailProviderType ProviderType => MailProviderType.Gmail; private Task<UserCredential> GetGoogleUserCredentialAsync(MailAccount account)
{
/// <summary> return GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets()
/// Generates the token information for the given account.
/// For gmail, interactivity is automatically handled when you get the token.
/// </summary>
/// <param name="account">Account to get token for.</param>
public Task<TokenInformationEx> GenerateTokenInformationAsync(MailAccount account)
=> GetTokenInformationAsync(account);
public async Task<TokenInformationEx> GetTokenInformationAsync(MailAccount account)
{ {
var userCredential = await GetGoogleUserCredentialAsync(account); ClientId = ClientId
}, AuthenticatorConfig.GmailScope, account.Id.ToString(), CancellationToken.None, new FileDataStore(AuthenticatorConfig.GmailTokenStoreIdentifier));
if (userCredential.Token.IsStale)
{
await userCredential.RefreshTokenAsync(CancellationToken.None);
}
return new TokenInformationEx(userCredential.Token.AccessToken, account.Address);
}
private Task<UserCredential> GetGoogleUserCredentialAsync(MailAccount account)
{
return GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets()
{
ClientId = ClientId
}, AuthenticatorConfig.GmailScope, account.Id.ToString(), CancellationToken.None, new FileDataStore(AuthenticatorConfig.GmailTokenStoreIdentifier));
}
} }
} }

View File

@@ -11,116 +11,115 @@ using Wino.Core.Domain.Exceptions;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Authentication; using Wino.Core.Domain.Models.Authentication;
namespace Wino.Authentication namespace Wino.Authentication;
public class OutlookAuthenticator : BaseAuthenticator, IOutlookAuthenticator
{ {
public class OutlookAuthenticator : BaseAuthenticator, IOutlookAuthenticator private const string TokenCacheFileName = "OutlookCache.bin";
private bool isTokenCacheAttached = false;
// Outlook
private const string Authority = "https://login.microsoftonline.com/common";
public override MailProviderType ProviderType => MailProviderType.Outlook;
private readonly IPublicClientApplication _publicClientApplication;
private readonly IApplicationConfiguration _applicationConfiguration;
public OutlookAuthenticator(INativeAppService nativeAppService,
IApplicationConfiguration applicationConfiguration,
IAuthenticatorConfig authenticatorConfig) : base(authenticatorConfig)
{ {
private const string TokenCacheFileName = "OutlookCache.bin"; _applicationConfiguration = applicationConfiguration;
private bool isTokenCacheAttached = false;
// Outlook var authenticationRedirectUri = nativeAppService.GetWebAuthenticationBrokerUri();
private const string Authority = "https://login.microsoftonline.com/common";
public override MailProviderType ProviderType => MailProviderType.Outlook; var options = new BrokerOptions(BrokerOptions.OperatingSystems.Windows)
private readonly IPublicClientApplication _publicClientApplication;
private readonly IApplicationConfiguration _applicationConfiguration;
public OutlookAuthenticator(INativeAppService nativeAppService,
IApplicationConfiguration applicationConfiguration,
IAuthenticatorConfig authenticatorConfig) : base(authenticatorConfig)
{ {
_applicationConfiguration = applicationConfiguration; Title = "Wino Mail",
ListOperatingSystemAccounts = true,
};
var authenticationRedirectUri = nativeAppService.GetWebAuthenticationBrokerUri(); var outlookAppBuilder = PublicClientApplicationBuilder.Create(AuthenticatorConfig.OutlookAuthenticatorClientId)
.WithParentActivityOrWindow(nativeAppService.GetCoreWindowHwnd)
.WithBroker(options)
.WithDefaultRedirectUri()
.WithAuthority(Authority);
var options = new BrokerOptions(BrokerOptions.OperatingSystems.Windows) _publicClientApplication = outlookAppBuilder.Build();
{ }
Title = "Wino Mail",
ListOperatingSystemAccounts = true,
};
var outlookAppBuilder = PublicClientApplicationBuilder.Create(AuthenticatorConfig.OutlookAuthenticatorClientId) public string[] Scope => AuthenticatorConfig.OutlookScope;
.WithParentActivityOrWindow(nativeAppService.GetCoreWindowHwnd)
.WithBroker(options)
.WithDefaultRedirectUri()
.WithAuthority(Authority);
_publicClientApplication = outlookAppBuilder.Build(); private async Task EnsureTokenCacheAttachedAsync()
} {
if (!isTokenCacheAttached)
public string[] Scope => AuthenticatorConfig.OutlookScope;
private async Task EnsureTokenCacheAttachedAsync()
{ {
if (!isTokenCacheAttached) var storageProperties = new StorageCreationPropertiesBuilder(TokenCacheFileName, _applicationConfiguration.PublisherSharedFolderPath).Build();
{ var msalcachehelper = await MsalCacheHelper.CreateAsync(storageProperties);
var storageProperties = new StorageCreationPropertiesBuilder(TokenCacheFileName, _applicationConfiguration.PublisherSharedFolderPath).Build(); msalcachehelper.RegisterCache(_publicClientApplication.UserTokenCache);
var msalcachehelper = await MsalCacheHelper.CreateAsync(storageProperties);
msalcachehelper.RegisterCache(_publicClientApplication.UserTokenCache);
isTokenCacheAttached = true; isTokenCacheAttached = true;
}
} }
}
public async Task<TokenInformationEx> GetTokenInformationAsync(MailAccount account) public async Task<TokenInformationEx> GetTokenInformationAsync(MailAccount account)
{
await EnsureTokenCacheAttachedAsync();
var storedAccount = (await _publicClientApplication.GetAccountsAsync()).FirstOrDefault(a => a.Username == account.Address);
if (storedAccount == null)
return await GenerateTokenInformationAsync(account);
try
{
var authResult = await _publicClientApplication.AcquireTokenSilent(Scope, storedAccount).ExecuteAsync();
return new TokenInformationEx(authResult.AccessToken, authResult.Account.Username);
}
catch (MsalUiRequiredException)
{
// Somehow MSAL is not able to refresh the token silently.
// Force interactive login.
return await GenerateTokenInformationAsync(account);
}
catch (Exception)
{
throw;
}
}
public async Task<TokenInformationEx> GenerateTokenInformationAsync(MailAccount account)
{
try
{ {
await EnsureTokenCacheAttachedAsync(); await EnsureTokenCacheAttachedAsync();
var storedAccount = (await _publicClientApplication.GetAccountsAsync()).FirstOrDefault(a => a.Username == account.Address); var authResult = await _publicClientApplication
.AcquireTokenInteractive(Scope)
.ExecuteAsync();
if (storedAccount == null) // If the account is null, it means it's the initial creation of it.
return await GenerateTokenInformationAsync(account); // If not, make sure the authenticated user address matches the username.
// When people refresh their token, accounts must match.
try if (account?.Address != null && !account.Address.Equals(authResult.Account.Username, StringComparison.OrdinalIgnoreCase))
{ {
var authResult = await _publicClientApplication.AcquireTokenSilent(Scope, storedAccount).ExecuteAsync(); throw new AuthenticationException("Authenticated address does not match with your account address.");
}
return new TokenInformationEx(authResult.AccessToken, authResult.Account.Username); return new TokenInformationEx(authResult.AccessToken, authResult.Account.Username);
}
catch (MsalUiRequiredException)
{
// Somehow MSAL is not able to refresh the token silently.
// Force interactive login.
return await GenerateTokenInformationAsync(account);
}
catch (Exception)
{
throw;
}
} }
catch (MsalClientException msalClientException)
public async Task<TokenInformationEx> GenerateTokenInformationAsync(MailAccount account)
{ {
try if (msalClientException.ErrorCode == "authentication_canceled" || msalClientException.ErrorCode == "access_denied")
{ throw new AccountSetupCanceledException();
await EnsureTokenCacheAttachedAsync();
var authResult = await _publicClientApplication throw;
.AcquireTokenInteractive(Scope)
.ExecuteAsync();
// If the account is null, it means it's the initial creation of it.
// If not, make sure the authenticated user address matches the username.
// When people refresh their token, accounts must match.
if (account?.Address != null && !account.Address.Equals(authResult.Account.Username, StringComparison.OrdinalIgnoreCase))
{
throw new AuthenticationException("Authenticated address does not match with your account address.");
}
return new TokenInformationEx(authResult.AccessToken, authResult.Account.Username);
}
catch (MsalClientException msalClientException)
{
if (msalClientException.ErrorCode == "authentication_canceled" || msalClientException.ErrorCode == "access_denied")
throw new AccountSetupCanceledException();
throw;
}
throw new AuthenticationException(Translator.Exception_UnknowErrorDuringAuthentication, new Exception(Translator.Exception_TokenGenerationFailed));
} }
throw new AuthenticationException(Translator.Exception_UnknowErrorDuringAuthentication, new Exception(Translator.Exception_TokenGenerationFailed));
} }
} }

View File

@@ -1,33 +1,32 @@
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
namespace Wino.Calendar.Services namespace Wino.Calendar.Services;
public class CalendarAuthenticatorConfig : IAuthenticatorConfig
{ {
public class CalendarAuthenticatorConfig : IAuthenticatorConfig public string OutlookAuthenticatorClientId => "b19c2035-d740-49ff-b297-de6ec561b208";
public string[] OutlookScope => new string[]
{ {
public string OutlookAuthenticatorClientId => "b19c2035-d740-49ff-b297-de6ec561b208"; "Calendars.Read",
"Calendars.Read.Shared",
"offline_access",
"Calendars.ReadBasic",
"Calendars.ReadWrite",
"Calendars.ReadWrite.Shared",
"User.Read"
};
public string[] OutlookScope => new string[] public string GmailAuthenticatorClientId => "973025879644-s7b4ur9p3rlgop6a22u7iuptdc0brnrn.apps.googleusercontent.com";
{
"Calendars.Read",
"Calendars.Read.Shared",
"offline_access",
"Calendars.ReadBasic",
"Calendars.ReadWrite",
"Calendars.ReadWrite.Shared",
"User.Read"
};
public string GmailAuthenticatorClientId => "973025879644-s7b4ur9p3rlgop6a22u7iuptdc0brnrn.apps.googleusercontent.com"; public string[] GmailScope => new string[]
{
"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/userinfo.profile",
"https://www.googleapis.com/auth/userinfo.email"
};
public string[] GmailScope => new string[] public string GmailTokenStoreIdentifier => "WinoCalendarGmailTokenStore";
{
"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/userinfo.profile",
"https://www.googleapis.com/auth/userinfo.email"
};
public string GmailTokenStoreIdentifier => "WinoCalendarGmailTokenStore";
}
} }

View File

@@ -7,150 +7,149 @@ using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Calendar; using Wino.Core.Domain.Models.Calendar;
namespace Wino.Core.Domain.Collections namespace Wino.Core.Domain.Collections;
public class CalendarEventCollection
{ {
public class CalendarEventCollection public event EventHandler<ICalendarItem> CalendarItemAdded;
public event EventHandler<ICalendarItem> CalendarItemRemoved;
public event EventHandler CalendarItemsCleared;
private ObservableRangeCollection<ICalendarItem> _internalRegularEvents = [];
private ObservableRangeCollection<ICalendarItem> _internalAllDayEvents = [];
public ReadOnlyObservableCollection<ICalendarItem> RegularEvents { get; }
public ReadOnlyObservableCollection<ICalendarItem> AllDayEvents { get; } // TODO: Rename this to include multi-day events.
public ITimePeriod Period { get; }
public CalendarSettings Settings { get; }
private readonly List<ICalendarItem> _allItems = new List<ICalendarItem>();
public CalendarEventCollection(ITimePeriod period, CalendarSettings settings)
{ {
public event EventHandler<ICalendarItem> CalendarItemAdded; Period = period;
public event EventHandler<ICalendarItem> CalendarItemRemoved; Settings = settings;
public event EventHandler CalendarItemsCleared; RegularEvents = new ReadOnlyObservableCollection<ICalendarItem>(_internalRegularEvents);
AllDayEvents = new ReadOnlyObservableCollection<ICalendarItem>(_internalAllDayEvents);
}
private ObservableRangeCollection<ICalendarItem> _internalRegularEvents = []; public bool HasCalendarEvent(AccountCalendar accountCalendar)
private ObservableRangeCollection<ICalendarItem> _internalAllDayEvents = []; => _allItems.Any(x => x.AssignedCalendar.Id == accountCalendar.Id);
public ReadOnlyObservableCollection<ICalendarItem> RegularEvents { get; } public ICalendarItem GetCalendarItem(Guid calendarItemId)
public ReadOnlyObservableCollection<ICalendarItem> AllDayEvents { get; } // TODO: Rename this to include multi-day events. {
public ITimePeriod Period { get; } return _allItems.FirstOrDefault(x => x.Id == calendarItemId);
public CalendarSettings Settings { get; } }
private readonly List<ICalendarItem> _allItems = new List<ICalendarItem>(); public void ClearSelectionStates()
{
public CalendarEventCollection(ITimePeriod period, CalendarSettings settings) foreach (var item in _allItems)
{ {
Period = period; if (item is ICalendarItemViewModel calendarItemViewModel)
Settings = settings;
RegularEvents = new ReadOnlyObservableCollection<ICalendarItem>(_internalRegularEvents);
AllDayEvents = new ReadOnlyObservableCollection<ICalendarItem>(_internalAllDayEvents);
}
public bool HasCalendarEvent(AccountCalendar accountCalendar)
=> _allItems.Any(x => x.AssignedCalendar.Id == accountCalendar.Id);
public ICalendarItem GetCalendarItem(Guid calendarItemId)
{
return _allItems.FirstOrDefault(x => x.Id == calendarItemId);
}
public void ClearSelectionStates()
{
foreach (var item in _allItems)
{ {
if (item is ICalendarItemViewModel calendarItemViewModel) calendarItemViewModel.IsSelected = false;
{
calendarItemViewModel.IsSelected = false;
}
} }
} }
}
public void FilterByCalendars(IEnumerable<Guid> visibleCalendarIds)
{ public void FilterByCalendars(IEnumerable<Guid> visibleCalendarIds)
foreach (var item in _allItems) {
{ foreach (var item in _allItems)
var collections = GetProperCollectionsForCalendarItem(item); {
var collections = GetProperCollectionsForCalendarItem(item);
foreach (var collection in collections)
{ foreach (var collection in collections)
if (!visibleCalendarIds.Contains(item.AssignedCalendar.Id) && collection.Contains(item)) {
{ if (!visibleCalendarIds.Contains(item.AssignedCalendar.Id) && collection.Contains(item))
RemoveCalendarItemInternal(collection, item, false); {
} RemoveCalendarItemInternal(collection, item, false);
else if (visibleCalendarIds.Contains(item.AssignedCalendar.Id) && !collection.Contains(item)) }
{ else if (visibleCalendarIds.Contains(item.AssignedCalendar.Id) && !collection.Contains(item))
AddCalendarItemInternal(collection, item, false); {
} AddCalendarItemInternal(collection, item, false);
} }
} }
} }
}
private IEnumerable<ObservableRangeCollection<ICalendarItem>> GetProperCollectionsForCalendarItem(ICalendarItem calendarItem)
{ private IEnumerable<ObservableRangeCollection<ICalendarItem>> GetProperCollectionsForCalendarItem(ICalendarItem calendarItem)
// All-day events go to all days. {
// Multi-day events go to both. // All-day events go to all days.
// Anything else goes to regular. // Multi-day events go to both.
// Anything else goes to regular.
if (calendarItem.IsAllDayEvent)
{ if (calendarItem.IsAllDayEvent)
return [_internalAllDayEvents]; {
} return [_internalAllDayEvents];
else if (calendarItem.IsMultiDayEvent) }
{ else if (calendarItem.IsMultiDayEvent)
return [_internalRegularEvents, _internalAllDayEvents]; {
} return [_internalRegularEvents, _internalAllDayEvents];
else }
{ else
return [_internalRegularEvents]; {
} return [_internalRegularEvents];
} }
}
public void AddCalendarItem(ICalendarItem calendarItem)
{ public void AddCalendarItem(ICalendarItem calendarItem)
var collections = GetProperCollectionsForCalendarItem(calendarItem); {
var collections = GetProperCollectionsForCalendarItem(calendarItem);
foreach (var collection in collections)
{ foreach (var collection in collections)
AddCalendarItemInternal(collection, calendarItem); {
} AddCalendarItemInternal(collection, calendarItem);
} }
}
public void RemoveCalendarItem(ICalendarItem calendarItem)
{ public void RemoveCalendarItem(ICalendarItem calendarItem)
var collections = GetProperCollectionsForCalendarItem(calendarItem); {
var collections = GetProperCollectionsForCalendarItem(calendarItem);
foreach (var collection in collections)
{ foreach (var collection in collections)
RemoveCalendarItemInternal(collection, calendarItem); {
} RemoveCalendarItemInternal(collection, calendarItem);
} }
}
private void AddCalendarItemInternal(ObservableRangeCollection<ICalendarItem> collection, ICalendarItem calendarItem, bool create = true)
{ private void AddCalendarItemInternal(ObservableRangeCollection<ICalendarItem> collection, ICalendarItem calendarItem, bool create = true)
if (calendarItem is not ICalendarItemViewModel) {
throw new ArgumentException("CalendarItem must be of type ICalendarItemViewModel", nameof(calendarItem)); if (calendarItem is not ICalendarItemViewModel)
throw new ArgumentException("CalendarItem must be of type ICalendarItemViewModel", nameof(calendarItem));
collection.Add(calendarItem);
collection.Add(calendarItem);
if (create)
{ if (create)
_allItems.Add(calendarItem); {
} _allItems.Add(calendarItem);
}
CalendarItemAdded?.Invoke(this, calendarItem);
} CalendarItemAdded?.Invoke(this, calendarItem);
}
private void RemoveCalendarItemInternal(ObservableRangeCollection<ICalendarItem> collection, ICalendarItem calendarItem, bool destroy = true)
{ private void RemoveCalendarItemInternal(ObservableRangeCollection<ICalendarItem> collection, ICalendarItem calendarItem, bool destroy = true)
if (calendarItem is not ICalendarItemViewModel) {
throw new ArgumentException("CalendarItem must be of type ICalendarItemViewModel", nameof(calendarItem)); if (calendarItem is not ICalendarItemViewModel)
throw new ArgumentException("CalendarItem must be of type ICalendarItemViewModel", nameof(calendarItem));
collection.Remove(calendarItem);
collection.Remove(calendarItem);
if (destroy)
{ if (destroy)
_allItems.Remove(calendarItem); {
} _allItems.Remove(calendarItem);
}
CalendarItemRemoved?.Invoke(this, calendarItem);
} CalendarItemRemoved?.Invoke(this, calendarItem);
}
public void Clear()
{ public void Clear()
_internalAllDayEvents.Clear(); {
_internalRegularEvents.Clear(); _internalAllDayEvents.Clear();
_allItems.Clear(); _internalRegularEvents.Clear();
_allItems.Clear();
CalendarItemsCleared?.Invoke(this, EventArgs.Empty);
} CalendarItemsCleared?.Invoke(this, EventArgs.Empty);
} }
} }

View File

@@ -2,41 +2,40 @@
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Calendar; using Wino.Core.Domain.Models.Calendar;
namespace Wino.Core.Domain.Collections namespace Wino.Core.Domain.Collections;
public class DayRangeCollection : ObservableRangeCollection<DayRangeRenderModel>
{ {
public class DayRangeCollection : ObservableRangeCollection<DayRangeRenderModel> /// <summary>
/// Gets the range of dates that are currently displayed in the collection.
/// </summary>
public DateRange DisplayRange
{ {
/// <summary> get
/// Gets the range of dates that are currently displayed in the collection.
/// </summary>
public DateRange DisplayRange
{ {
get if (Count == 0) return null;
{
if (Count == 0) return null;
var minimumLoadedDate = this[0].CalendarRenderOptions.DateRange.StartDate; var minimumLoadedDate = this[0].CalendarRenderOptions.DateRange.StartDate;
var maximumLoadedDate = this[Count - 1].CalendarRenderOptions.DateRange.EndDate; var maximumLoadedDate = this[Count - 1].CalendarRenderOptions.DateRange.EndDate;
return new DateRange(minimumLoadedDate, maximumLoadedDate); return new DateRange(minimumLoadedDate, maximumLoadedDate);
}
} }
}
public void RemoveCalendarItem(ICalendarItem calendarItem) public void RemoveCalendarItem(ICalendarItem calendarItem)
{
foreach (var dayRange in this)
{ {
foreach (var dayRange in this)
{
}
} }
}
public void AddCalendarItem(ICalendarItem calendarItem) public void AddCalendarItem(ICalendarItem calendarItem)
{
foreach (var dayRange in this)
{ {
foreach (var dayRange in this) var calendarDayModel = dayRange.CalendarDays.FirstOrDefault(x => x.Period.HasInside(calendarItem.Period.Start));
{ calendarDayModel?.EventsCollection.AddCalendarItem(calendarItem);
var calendarDayModel = dayRange.CalendarDays.FirstOrDefault(x => x.Period.HasInside(calendarItem.Period.Start));
calendarDayModel?.EventsCollection.AddCalendarItem(calendarItem);
}
} }
} }
} }

View File

@@ -4,171 +4,170 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
namespace Wino.Core.Domain.Collections namespace Wino.Core.Domain.Collections;
/// <summary>
/// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObservableRangeCollection<T> : ObservableCollection<T>
{ {
/// <summary> /// <summary>
/// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed. /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class.
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> public ObservableRangeCollection()
public class ObservableRangeCollection<T> : ObservableCollection<T> : base()
{ {
}
/// <summary> /// <summary>
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class. /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection.
/// </summary> /// </summary>
public ObservableRangeCollection() /// <param name="collection">collection: The collection from which the elements are copied.</param>
: base() /// <exception cref="ArgumentNullException">The collection parameter cannot be null.</exception>
public ObservableRangeCollection(IEnumerable<T> collection)
: base(collection)
{
}
/// <summary>
/// Adds the elements of the specified collection to the end of the ObservableCollection(Of T).
/// </summary>
public void AddRange(IEnumerable<T> collection, NotifyCollectionChangedAction notificationMode = NotifyCollectionChangedAction.Add)
{
if (notificationMode != NotifyCollectionChangedAction.Add && notificationMode != NotifyCollectionChangedAction.Reset)
throw new ArgumentException("Mode must be either Add or Reset for AddRange.", nameof(notificationMode));
if (collection == null)
throw new ArgumentNullException(nameof(collection));
CheckReentrancy();
var startIndex = Count;
var itemsAdded = AddArrangeCore(collection);
if (!itemsAdded)
return;
if (notificationMode == NotifyCollectionChangedAction.Reset)
{ {
}
/// <summary>
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection.
/// </summary>
/// <param name="collection">collection: The collection from which the elements are copied.</param>
/// <exception cref="ArgumentNullException">The collection parameter cannot be null.</exception>
public ObservableRangeCollection(IEnumerable<T> collection)
: base(collection)
{
}
/// <summary>
/// Adds the elements of the specified collection to the end of the ObservableCollection(Of T).
/// </summary>
public void AddRange(IEnumerable<T> collection, NotifyCollectionChangedAction notificationMode = NotifyCollectionChangedAction.Add)
{
if (notificationMode != NotifyCollectionChangedAction.Add && notificationMode != NotifyCollectionChangedAction.Reset)
throw new ArgumentException("Mode must be either Add or Reset for AddRange.", nameof(notificationMode));
if (collection == null)
throw new ArgumentNullException(nameof(collection));
CheckReentrancy();
var startIndex = Count;
var itemsAdded = AddArrangeCore(collection);
if (!itemsAdded)
return;
if (notificationMode == NotifyCollectionChangedAction.Reset)
{
RaiseChangeNotificationEvents(action: NotifyCollectionChangedAction.Reset);
return;
}
var changedItems = collection is List<T> ? (List<T>)collection : new List<T>(collection);
RaiseChangeNotificationEvents(
action: NotifyCollectionChangedAction.Add,
changedItems: changedItems,
startingIndex: startIndex);
}
/// <summary>
/// Removes the first occurence of each item in the specified collection from ObservableCollection(Of T). NOTE: with notificationMode = Remove, removed items starting index is not set because items are not guaranteed to be consecutive.
/// </summary>
public void RemoveRange(IEnumerable<T> collection, NotifyCollectionChangedAction notificationMode = NotifyCollectionChangedAction.Reset)
{
if (notificationMode != NotifyCollectionChangedAction.Remove && notificationMode != NotifyCollectionChangedAction.Reset)
throw new ArgumentException("Mode must be either Remove or Reset for RemoveRange.", nameof(notificationMode));
if (collection == null)
throw new ArgumentNullException(nameof(collection));
CheckReentrancy();
if (notificationMode == NotifyCollectionChangedAction.Reset)
{
var raiseEvents = false;
foreach (var item in collection)
{
Items.Remove(item);
raiseEvents = true;
}
if (raiseEvents)
RaiseChangeNotificationEvents(action: NotifyCollectionChangedAction.Reset);
return;
}
var changedItems = new List<T>(collection);
for (var i = 0; i < changedItems.Count; i++)
{
if (!Items.Remove(changedItems[i]))
{
changedItems.RemoveAt(i); //Can't use a foreach because changedItems is intended to be (carefully) modified
i--;
}
}
if (changedItems.Count == 0)
return;
RaiseChangeNotificationEvents(
action: NotifyCollectionChangedAction.Remove,
changedItems: changedItems);
}
/// <summary>
/// Clears the current collection and replaces it with the specified item.
/// </summary>
public void Replace(T item) => ReplaceRange(new T[] { item });
/// <summary>
/// Clears the current collection and replaces it with the specified collection.
/// </summary>
public void ReplaceRange(IEnumerable<T> collection)
{
if (collection == null)
throw new ArgumentNullException(nameof(collection));
CheckReentrancy();
var previouslyEmpty = Items.Count == 0;
Items.Clear();
AddArrangeCore(collection);
var currentlyEmpty = Items.Count == 0;
if (previouslyEmpty && currentlyEmpty)
return;
RaiseChangeNotificationEvents(action: NotifyCollectionChangedAction.Reset); RaiseChangeNotificationEvents(action: NotifyCollectionChangedAction.Reset);
return;
} }
public void InsertRange(IEnumerable<T> items) var changedItems = collection is List<T> ? (List<T>)collection : new List<T>(collection);
RaiseChangeNotificationEvents(
action: NotifyCollectionChangedAction.Add,
changedItems: changedItems,
startingIndex: startIndex);
}
/// <summary>
/// Removes the first occurence of each item in the specified collection from ObservableCollection(Of T). NOTE: with notificationMode = Remove, removed items starting index is not set because items are not guaranteed to be consecutive.
/// </summary>
public void RemoveRange(IEnumerable<T> collection, NotifyCollectionChangedAction notificationMode = NotifyCollectionChangedAction.Reset)
{
if (notificationMode != NotifyCollectionChangedAction.Remove && notificationMode != NotifyCollectionChangedAction.Reset)
throw new ArgumentException("Mode must be either Remove or Reset for RemoveRange.", nameof(notificationMode));
if (collection == null)
throw new ArgumentNullException(nameof(collection));
CheckReentrancy();
if (notificationMode == NotifyCollectionChangedAction.Reset)
{ {
CheckReentrancy(); var raiseEvents = false;
foreach (var item in items)
Items.Insert(0, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
private bool AddArrangeCore(IEnumerable<T> collection)
{
var itemAdded = false;
foreach (var item in collection) foreach (var item in collection)
{ {
Items.Add(item); Items.Remove(item);
itemAdded = true; raiseEvents = true;
} }
return itemAdded;
if (raiseEvents)
RaiseChangeNotificationEvents(action: NotifyCollectionChangedAction.Reset);
return;
} }
private void RaiseChangeNotificationEvents(NotifyCollectionChangedAction action, List<T> changedItems = null, int startingIndex = -1) var changedItems = new List<T>(collection);
for (var i = 0; i < changedItems.Count; i++)
{ {
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Count))); if (!Items.Remove(changedItems[i]))
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); {
changedItems.RemoveAt(i); //Can't use a foreach because changedItems is intended to be (carefully) modified
if (changedItems is null) i--;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action)); }
else
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, changedItems: changedItems, startingIndex: startingIndex));
} }
if (changedItems.Count == 0)
return;
RaiseChangeNotificationEvents(
action: NotifyCollectionChangedAction.Remove,
changedItems: changedItems);
}
/// <summary>
/// Clears the current collection and replaces it with the specified item.
/// </summary>
public void Replace(T item) => ReplaceRange(new T[] { item });
/// <summary>
/// Clears the current collection and replaces it with the specified collection.
/// </summary>
public void ReplaceRange(IEnumerable<T> collection)
{
if (collection == null)
throw new ArgumentNullException(nameof(collection));
CheckReentrancy();
var previouslyEmpty = Items.Count == 0;
Items.Clear();
AddArrangeCore(collection);
var currentlyEmpty = Items.Count == 0;
if (previouslyEmpty && currentlyEmpty)
return;
RaiseChangeNotificationEvents(action: NotifyCollectionChangedAction.Reset);
}
public void InsertRange(IEnumerable<T> items)
{
CheckReentrancy();
foreach (var item in items)
Items.Insert(0, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
private bool AddArrangeCore(IEnumerable<T> collection)
{
var itemAdded = false;
foreach (var item in collection)
{
Items.Add(item);
itemAdded = true;
}
return itemAdded;
}
private void RaiseChangeNotificationEvents(NotifyCollectionChangedAction action, List<T> changedItems = null, int startingIndex = -1)
{
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Count)));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
if (changedItems is null)
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action));
else
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, changedItems: changedItems, startingIndex: startingIndex));
} }
} }

View File

@@ -1,23 +1,22 @@
namespace Wino.Core.Domain namespace Wino.Core.Domain;
public static class Constants
{ {
public static class Constants /// <summary>
{ /// MIME header that exists in all the drafts created from Wino.
/// <summary> /// </summary>
/// MIME header that exists in all the drafts created from Wino. public const string WinoLocalDraftHeader = "X-Wino-Draft-Id";
/// </summary> public const string LocalDraftStartPrefix = "localDraft_";
public const string WinoLocalDraftHeader = "X-Wino-Draft-Id";
public const string LocalDraftStartPrefix = "localDraft_";
public const string CalendarEventRecurrenceRuleSeperator = "___"; public const string CalendarEventRecurrenceRuleSeperator = "___";
public const string ToastMailUniqueIdKey = nameof(ToastMailUniqueIdKey); public const string ToastMailUniqueIdKey = nameof(ToastMailUniqueIdKey);
public const string ToastActionKey = nameof(ToastActionKey); public const string ToastActionKey = nameof(ToastActionKey);
public const string ClientLogFile = "Client_.log"; public const string ClientLogFile = "Client_.log";
public const string ServerLogFile = "Server_.log"; public const string ServerLogFile = "Server_.log";
public const string LogArchiveFileName = "WinoLogs.zip"; public const string LogArchiveFileName = "WinoLogs.zip";
public const string WinoMailIdentiifer = nameof(WinoMailIdentiifer); public const string WinoMailIdentiifer = nameof(WinoMailIdentiifer);
public const string WinoCalendarIdentifier = nameof(WinoCalendarIdentifier); public const string WinoCalendarIdentifier = nameof(WinoCalendarIdentifier);
}
} }

View File

@@ -2,24 +2,23 @@
using SQLite; using SQLite;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
namespace Wino.Core.Domain.Entities.Calendar namespace Wino.Core.Domain.Entities.Calendar;
{
public class AccountCalendar : IAccountCalendar
{
[PrimaryKey]
public Guid Id { get; set; }
public Guid AccountId { get; set; }
public string RemoteCalendarId { get; set; }
public string SynchronizationDeltaToken { get; set; }
public string Name { get; set; }
public bool IsPrimary { get; set; }
public bool IsExtended { get; set; } = true;
/// <summary> public class AccountCalendar : IAccountCalendar
/// Unused for now. {
/// </summary> [PrimaryKey]
public string TextColorHex { get; set; } public Guid Id { get; set; }
public string BackgroundColorHex { get; set; } public Guid AccountId { get; set; }
public string TimeZone { get; set; } public string RemoteCalendarId { get; set; }
} public string SynchronizationDeltaToken { get; set; }
public string Name { get; set; }
public bool IsPrimary { get; set; }
public bool IsExtended { get; set; } = true;
/// <summary>
/// Unused for now.
/// </summary>
public string TextColorHex { get; set; }
public string BackgroundColorHex { get; set; }
public string TimeZone { get; set; }
} }

View File

@@ -2,19 +2,18 @@
using SQLite; using SQLite;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Entities.Calendar namespace Wino.Core.Domain.Entities.Calendar;
// TODO: Connect to Contact store with Wino People.
public class CalendarEventAttendee
{ {
// TODO: Connect to Contact store with Wino People. [PrimaryKey]
public class CalendarEventAttendee public Guid Id { get; set; }
{ public Guid CalendarItemId { get; set; }
[PrimaryKey] public string Name { get; set; }
public Guid Id { get; set; } public string Email { get; set; }
public Guid CalendarItemId { get; set; } public AttendeeStatus AttendenceStatus { get; set; }
public string Name { get; set; } public bool IsOrganizer { get; set; }
public string Email { get; set; } public bool IsOptionalAttendee { get; set; }
public AttendeeStatus AttendenceStatus { get; set; } public string Comment { get; set; }
public bool IsOrganizer { get; set; }
public bool IsOptionalAttendee { get; set; }
public string Comment { get; set; }
}
} }

View File

@@ -5,176 +5,175 @@ using SQLite;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Interfaces;
namespace Wino.Core.Domain.Entities.Calendar namespace Wino.Core.Domain.Entities.Calendar;
[DebuggerDisplay("{Title} ({StartDate} - {EndDate})")]
public class CalendarItem : ICalendarItem
{ {
[DebuggerDisplay("{Title} ({StartDate} - {EndDate})")] [PrimaryKey]
public class CalendarItem : ICalendarItem public Guid Id { get; set; }
public string RemoteEventId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate
{ {
[PrimaryKey] get
public Guid Id { get; set; }
public string RemoteEventId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate
{ {
get return StartDate.AddSeconds(DurationInSeconds);
{
return StartDate.AddSeconds(DurationInSeconds);
}
}
public TimeSpan StartDateOffset { get; set; }
public TimeSpan EndDateOffset { get; set; }
private ITimePeriod _period;
public ITimePeriod Period
{
get
{
_period ??= new TimeRange(StartDate, EndDate);
return _period;
}
}
/// <summary>
/// Events that starts at midnight and ends at midnight are considered all-day events.
/// </summary>
public bool IsAllDayEvent
{
get
{
return
StartDate.TimeOfDay == TimeSpan.Zero &&
EndDate.TimeOfDay == TimeSpan.Zero;
}
}
/// <summary>
/// Events that are either an exceptional instance of a recurring event or occurrences.
/// IsOccurrence is used to display occurrence instances of parent recurring events.
/// IsOccurrence == false && IsRecurringChild == true => exceptional single instance.
/// </summary>
public bool IsRecurringChild
{
get
{
return RecurringCalendarItemId != null;
}
}
/// <summary>
/// Events that are either an exceptional instance of a recurring event or occurrences.
/// </summary>
public bool IsRecurringEvent => IsRecurringChild || IsRecurringParent;
/// <summary>
/// Events that are the master event definition of recurrence events.
/// </summary>
public bool IsRecurringParent
{
get
{
return !string.IsNullOrEmpty(Recurrence) && RecurringCalendarItemId == null;
}
}
/// <summary>
/// Events that are not all-day events and last more than one day are considered multi-day events.
/// </summary>
public bool IsMultiDayEvent
{
get
{
return Period.Duration.TotalDays >= 1 && !IsAllDayEvent;
}
}
public double DurationInSeconds { get; set; }
public string Recurrence { get; set; }
public string OrganizerDisplayName { get; set; }
public string OrganizerEmail { get; set; }
/// <summary>
/// The id of the parent calendar item of the recurring event.
/// Exceptional instances are stored as a separate calendar item.
/// This makes the calendar item a child of the recurring event.
/// </summary>
public Guid? RecurringCalendarItemId { get; set; }
/// <summary>
/// Indicates read-only events. Default is false.
/// </summary>
public bool IsLocked { get; set; }
/// <summary>
/// Hidden events must not be displayed to the user.
/// This usually happens when a child instance of recurring parent is cancelled after creation.
/// </summary>
public bool IsHidden { get; set; }
// TODO
public string CustomEventColorHex { get; set; }
public string HtmlLink { get; set; }
public CalendarItemStatus Status { get; set; }
public CalendarItemVisibility Visibility { get; set; }
public DateTimeOffset CreatedAt { get; set; }
public DateTimeOffset UpdatedAt { get; set; }
public Guid CalendarId { get; set; }
[Ignore]
public IAccountCalendar AssignedCalendar { get; set; }
/// <summary>
/// Whether this item does not really exist in the database or not.
/// These are used to display occurrence instances of parent recurring events.
/// </summary>
[Ignore]
public bool IsOccurrence { get; set; }
/// <summary>
/// Id to load information related to this event.
/// Occurrences tracked by the parent recurring event if they are not exceptional instances.
/// Recurring children here are exceptional instances. They have their own info in the database including Id.
/// </summary>
public Guid EventTrackingId => IsOccurrence ? RecurringCalendarItemId.Value : Id;
public CalendarItem CreateRecurrence(DateTime startDate, double durationInSeconds)
{
// Create a copy with the new start date and duration
return new CalendarItem
{
Id = Guid.NewGuid(),
Title = Title,
Description = Description,
Location = Location,
StartDate = startDate,
DurationInSeconds = durationInSeconds,
Recurrence = Recurrence,
OrganizerDisplayName = OrganizerDisplayName,
OrganizerEmail = OrganizerEmail,
RecurringCalendarItemId = Id,
AssignedCalendar = AssignedCalendar,
CalendarId = CalendarId,
CreatedAt = CreatedAt,
UpdatedAt = UpdatedAt,
Visibility = Visibility,
Status = Status,
CustomEventColorHex = CustomEventColorHex,
HtmlLink = HtmlLink,
StartDateOffset = StartDateOffset,
EndDateOffset = EndDateOffset,
RemoteEventId = RemoteEventId,
IsHidden = IsHidden,
IsLocked = IsLocked,
IsOccurrence = true
};
} }
} }
public TimeSpan StartDateOffset { get; set; }
public TimeSpan EndDateOffset { get; set; }
private ITimePeriod _period;
public ITimePeriod Period
{
get
{
_period ??= new TimeRange(StartDate, EndDate);
return _period;
}
}
/// <summary>
/// Events that starts at midnight and ends at midnight are considered all-day events.
/// </summary>
public bool IsAllDayEvent
{
get
{
return
StartDate.TimeOfDay == TimeSpan.Zero &&
EndDate.TimeOfDay == TimeSpan.Zero;
}
}
/// <summary>
/// Events that are either an exceptional instance of a recurring event or occurrences.
/// IsOccurrence is used to display occurrence instances of parent recurring events.
/// IsOccurrence == false && IsRecurringChild == true => exceptional single instance.
/// </summary>
public bool IsRecurringChild
{
get
{
return RecurringCalendarItemId != null;
}
}
/// <summary>
/// Events that are either an exceptional instance of a recurring event or occurrences.
/// </summary>
public bool IsRecurringEvent => IsRecurringChild || IsRecurringParent;
/// <summary>
/// Events that are the master event definition of recurrence events.
/// </summary>
public bool IsRecurringParent
{
get
{
return !string.IsNullOrEmpty(Recurrence) && RecurringCalendarItemId == null;
}
}
/// <summary>
/// Events that are not all-day events and last more than one day are considered multi-day events.
/// </summary>
public bool IsMultiDayEvent
{
get
{
return Period.Duration.TotalDays >= 1 && !IsAllDayEvent;
}
}
public double DurationInSeconds { get; set; }
public string Recurrence { get; set; }
public string OrganizerDisplayName { get; set; }
public string OrganizerEmail { get; set; }
/// <summary>
/// The id of the parent calendar item of the recurring event.
/// Exceptional instances are stored as a separate calendar item.
/// This makes the calendar item a child of the recurring event.
/// </summary>
public Guid? RecurringCalendarItemId { get; set; }
/// <summary>
/// Indicates read-only events. Default is false.
/// </summary>
public bool IsLocked { get; set; }
/// <summary>
/// Hidden events must not be displayed to the user.
/// This usually happens when a child instance of recurring parent is cancelled after creation.
/// </summary>
public bool IsHidden { get; set; }
// TODO
public string CustomEventColorHex { get; set; }
public string HtmlLink { get; set; }
public CalendarItemStatus Status { get; set; }
public CalendarItemVisibility Visibility { get; set; }
public DateTimeOffset CreatedAt { get; set; }
public DateTimeOffset UpdatedAt { get; set; }
public Guid CalendarId { get; set; }
[Ignore]
public IAccountCalendar AssignedCalendar { get; set; }
/// <summary>
/// Whether this item does not really exist in the database or not.
/// These are used to display occurrence instances of parent recurring events.
/// </summary>
[Ignore]
public bool IsOccurrence { get; set; }
/// <summary>
/// Id to load information related to this event.
/// Occurrences tracked by the parent recurring event if they are not exceptional instances.
/// Recurring children here are exceptional instances. They have their own info in the database including Id.
/// </summary>
public Guid EventTrackingId => IsOccurrence ? RecurringCalendarItemId.Value : Id;
public CalendarItem CreateRecurrence(DateTime startDate, double durationInSeconds)
{
// Create a copy with the new start date and duration
return new CalendarItem
{
Id = Guid.NewGuid(),
Title = Title,
Description = Description,
Location = Location,
StartDate = startDate,
DurationInSeconds = durationInSeconds,
Recurrence = Recurrence,
OrganizerDisplayName = OrganizerDisplayName,
OrganizerEmail = OrganizerEmail,
RecurringCalendarItemId = Id,
AssignedCalendar = AssignedCalendar,
CalendarId = CalendarId,
CreatedAt = CreatedAt,
UpdatedAt = UpdatedAt,
Visibility = Visibility,
Status = Status,
CustomEventColorHex = CustomEventColorHex,
HtmlLink = HtmlLink,
StartDateOffset = StartDateOffset,
EndDateOffset = EndDateOffset,
RemoteEventId = RemoteEventId,
IsHidden = IsHidden,
IsLocked = IsLocked,
IsOccurrence = true
};
}
} }

View File

@@ -2,15 +2,14 @@
using SQLite; using SQLite;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Entities.Calendar namespace Wino.Core.Domain.Entities.Calendar;
{
public class Reminder
{
[PrimaryKey]
public Guid Id { get; set; }
public Guid CalendarItemId { get; set; }
public DateTimeOffset ReminderTime { get; set; } public class Reminder
public CalendarItemReminderType ReminderType { get; set; } {
} [PrimaryKey]
public Guid Id { get; set; }
public Guid CalendarItemId { get; set; }
public DateTimeOffset ReminderTime { get; set; }
public CalendarItemReminderType ReminderType { get; set; }
} }

View File

@@ -1,17 +1,16 @@
using System; using System;
using SQLite; using SQLite;
namespace Wino.Core.Domain.Entities.Mail namespace Wino.Core.Domain.Entities.Mail;
public class AccountSignature
{ {
public class AccountSignature [PrimaryKey]
{ public Guid Id { get; set; }
[PrimaryKey]
public Guid Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string HtmlBody { get; set; } public string HtmlBody { get; set; }
public Guid MailAccountId { get; set; } public Guid MailAccountId { get; set; }
}
} }

View File

@@ -1,63 +1,62 @@
using System; using System;
using SQLite; using SQLite;
namespace Wino.Core.Domain.Entities.Mail namespace Wino.Core.Domain.Entities.Mail;
public class RemoteAccountAlias
{ {
public class RemoteAccountAlias /// <summary>
{ /// Display address of the alias.
/// <summary> /// </summary>
/// Display address of the alias. public string AliasAddress { get; set; }
/// </summary>
public string AliasAddress { get; set; }
/// <summary> /// <summary>
/// Address to be included in Reply-To header when alias is used for sending messages. /// Address to be included in Reply-To header when alias is used for sending messages.
/// </summary> /// </summary>
public string ReplyToAddress { get; set; } public string ReplyToAddress { get; set; }
/// <summary> /// <summary>
/// Whether this alias is the primary alias for the account. /// Whether this alias is the primary alias for the account.
/// </summary> /// </summary>
public bool IsPrimary { get; set; } public bool IsPrimary { get; set; }
/// <summary> /// <summary>
/// Whether the alias is verified by the server. /// Whether the alias is verified by the server.
/// Only Gmail aliases are verified for now. /// Only Gmail aliases are verified for now.
/// Non-verified alias messages might be rejected by SMTP server. /// Non-verified alias messages might be rejected by SMTP server.
/// </summary> /// </summary>
public bool IsVerified { get; set; } public bool IsVerified { get; set; }
/// <summary> /// <summary>
/// Whether this alias is the root alias for the account. /// Whether this alias is the root alias for the account.
/// Root alias means the first alias that was created for the account. /// Root alias means the first alias that was created for the account.
/// It can't be deleted or changed. /// It can't be deleted or changed.
/// </summary> /// </summary>
public bool IsRootAlias { get; set; } public bool IsRootAlias { get; set; }
/// <summary> /// <summary>
/// Optional sender name for the alias. /// Optional sender name for the alias.
/// Falls back to account's sender name if not set when preparing messages. /// Falls back to account's sender name if not set when preparing messages.
/// Used for Gmail only. /// Used for Gmail only.
/// </summary> /// </summary>
public string AliasSenderName { get; set; } public string AliasSenderName { get; set; }
} }
public class MailAccountAlias : RemoteAccountAlias public class MailAccountAlias : RemoteAccountAlias
{ {
/// <summary> /// <summary>
/// Unique Id for the alias. /// Unique Id for the alias.
/// </summary> /// </summary>
[PrimaryKey] [PrimaryKey]
public Guid Id { get; set; } public Guid Id { get; set; }
/// <summary> /// <summary>
/// Account id that this alias is attached to. /// Account id that this alias is attached to.
/// </summary> /// </summary>
public Guid AccountId { get; set; } public Guid AccountId { get; set; }
/// <summary> /// <summary>
/// Root aliases can't be deleted. /// Root aliases can't be deleted.
/// </summary> /// </summary>
public bool CanDelete => !IsRootAlias; public bool CanDelete => !IsRootAlias;
}
} }

View File

@@ -5,153 +5,152 @@ using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.MailItem; using Wino.Core.Domain.Models.MailItem;
namespace Wino.Core.Domain.Entities.Mail namespace Wino.Core.Domain.Entities.Mail;
/// <summary>
/// Summary of the parsed MIME messages.
/// Wino will do non-network operations on this table and others from the original MIME.
/// </summary>
public class MailCopy : IMailItem
{ {
/// <summary> /// <summary>
/// Summary of the parsed MIME messages. /// Unique Id of the mail.
/// Wino will do non-network operations on this table and others from the original MIME.
/// </summary> /// </summary>
public class MailCopy : IMailItem [PrimaryKey]
{ public Guid UniqueId { get; set; }
/// <summary>
/// Unique Id of the mail.
/// </summary>
[PrimaryKey]
public Guid UniqueId { get; set; }
/// <summary> /// <summary>
/// Not unique id of the item. Some operations held on this Id, some on the UniqueId. /// Not unique id of the item. Some operations held on this Id, some on the UniqueId.
/// Same message can be in different folder. In that case UniqueId is used. /// Same message can be in different folder. In that case UniqueId is used.
/// </summary> /// </summary>
public string Id { get; set; } public string Id { get; set; }
/// <summary> /// <summary>
/// Folder that this mail belongs to. /// Folder that this mail belongs to.
/// </summary> /// </summary>
public Guid FolderId { get; set; } public Guid FolderId { get; set; }
/// <summary> /// <summary>
/// Conversation id for the mail. /// Conversation id for the mail.
/// </summary> /// </summary>
public string ThreadId { get; set; } public string ThreadId { get; set; }
/// <summary> /// <summary>
/// MIME MessageId if exists. /// MIME MessageId if exists.
/// </summary> /// </summary>
public string MessageId { get; set; } public string MessageId { get; set; }
/// <summary> /// <summary>
/// References header from MIME /// References header from MIME
/// </summary> /// </summary>
public string References { get; set; } public string References { get; set; }
/// <summary> /// <summary>
/// In-Reply-To header from MIME /// In-Reply-To header from MIME
/// </summary> /// </summary>
public string InReplyTo { get; set; } public string InReplyTo { get; set; }
/// <summary> /// <summary>
/// Name for the sender. /// Name for the sender.
/// </summary> /// </summary>
public string FromName { get; set; } public string FromName { get; set; }
/// <summary> /// <summary>
/// Address of the sender. /// Address of the sender.
/// </summary> /// </summary>
public string FromAddress { get; set; } public string FromAddress { get; set; }
/// <summary> /// <summary>
/// Subject of the mail. /// Subject of the mail.
/// </summary> /// </summary>
public string Subject { get; set; } public string Subject { get; set; }
/// <summary> /// <summary>
/// Short preview of the content. /// Short preview of the content.
/// </summary> /// </summary>
public string PreviewText { get; set; } public string PreviewText { get; set; }
/// <summary> /// <summary>
/// Date that represents this mail has been created in provider servers. /// Date that represents this mail has been created in provider servers.
/// Stored always in UTC. /// Stored always in UTC.
/// </summary> /// </summary>
public DateTime CreationDate { get; set; } public DateTime CreationDate { get; set; }
/// <summary> /// <summary>
/// Importance of the mail. /// Importance of the mail.
/// </summary> /// </summary>
public MailImportance Importance { get; set; } public MailImportance Importance { get; set; }
/// <summary> /// <summary>
/// Read status for the mail. /// Read status for the mail.
/// </summary> /// </summary>
public bool IsRead { get; set; } public bool IsRead { get; set; }
/// <summary> /// <summary>
/// Flag status. /// Flag status.
/// Flagged for Outlook. /// Flagged for Outlook.
/// Important for Gmail. /// Important for Gmail.
/// </summary> /// </summary>
public bool IsFlagged { get; set; } public bool IsFlagged { get; set; }
/// <summary> /// <summary>
/// To support Outlook. /// To support Outlook.
/// Gmail doesn't use it. /// Gmail doesn't use it.
/// </summary> /// </summary>
public bool IsFocused { get; set; } public bool IsFocused { get; set; }
/// <summary> /// <summary>
/// Whether mail has attachments included or not. /// Whether mail has attachments included or not.
/// </summary> /// </summary>
public bool HasAttachments { get; set; } public bool HasAttachments { get; set; }
/// <summary> /// <summary>
/// Assigned draft id. /// Assigned draft id.
/// </summary> /// </summary>
public string DraftId { get; set; } public string DraftId { get; set; }
/// <summary> /// <summary>
/// Whether this mail is only created locally. /// Whether this mail is only created locally.
/// </summary> /// </summary>
[Ignore] [Ignore]
public bool IsLocalDraft => !string.IsNullOrEmpty(DraftId) && DraftId.StartsWith(Constants.LocalDraftStartPrefix); public bool IsLocalDraft => !string.IsNullOrEmpty(DraftId) && DraftId.StartsWith(Constants.LocalDraftStartPrefix);
/// <summary> /// <summary>
/// Whether this copy is draft or not. /// Whether this copy is draft or not.
/// </summary> /// </summary>
public bool IsDraft { get; set; } public bool IsDraft { get; set; }
/// <summary> /// <summary>
/// File id that this mail is assigned to. /// File id that this mail is assigned to.
/// This Id is immutable. It's used to find the file in the file system. /// This Id is immutable. It's used to find the file in the file system.
/// Even after mapping local draft to remote draft, it will not change. /// Even after mapping local draft to remote draft, it will not change.
/// </summary> /// </summary>
public Guid FileId { get; set; } public Guid FileId { get; set; }
/// <summary> /// <summary>
/// Folder that this mail is assigned to. /// Folder that this mail is assigned to.
/// Warning: This field is not populated by queries. /// Warning: This field is not populated by queries.
/// Services or View Models are responsible for populating this field. /// Services or View Models are responsible for populating this field.
/// </summary> /// </summary>
[Ignore] [Ignore]
public MailItemFolder AssignedFolder { get; set; } public MailItemFolder AssignedFolder { get; set; }
/// <summary> /// <summary>
/// Account that this mail is assigned to. /// Account that this mail is assigned to.
/// Warning: This field is not populated by queries. /// Warning: This field is not populated by queries.
/// Services or View Models are responsible for populating this field. /// Services or View Models are responsible for populating this field.
/// </summary> /// </summary>
[Ignore] [Ignore]
public MailAccount AssignedAccount { get; set; } public MailAccount AssignedAccount { get; set; }
/// <summary> /// <summary>
/// Contact information of the sender if exists. /// Contact information of the sender if exists.
/// Warning: This field is not populated by queries. /// Warning: This field is not populated by queries.
/// Services or View Models are responsible for populating this field. /// Services or View Models are responsible for populating this field.
/// </summary> /// </summary>
[Ignore] [Ignore]
public AccountContact SenderContact { get; set; } public AccountContact SenderContact { get; set; }
public IEnumerable<Guid> GetContainingIds() => [UniqueId]; public IEnumerable<Guid> GetContainingIds() => [UniqueId];
public override string ToString() => $"{Subject} <-> {Id}"; public override string ToString() => $"{Subject} <-> {Id}";
}
} }

View File

@@ -5,71 +5,70 @@ using SQLite;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Models.Folders; using Wino.Core.Domain.Models.Folders;
namespace Wino.Core.Domain.Entities.Mail namespace Wino.Core.Domain.Entities.Mail;
[DebuggerDisplay("{FolderName} - {SpecialFolderType}")]
public class MailItemFolder : IMailItemFolder
{ {
[DebuggerDisplay("{FolderName} - {SpecialFolderType}")] [PrimaryKey]
public class MailItemFolder : IMailItemFolder public Guid Id { get; set; }
public string RemoteFolderId { get; set; }
public string ParentRemoteFolderId { get; set; }
public Guid MailAccountId { get; set; }
public string FolderName { get; set; }
public SpecialFolderType SpecialFolderType { get; set; }
public bool IsSystemFolder { get; set; }
public bool IsSticky { get; set; }
public bool IsSynchronizationEnabled { get; set; }
public bool IsHidden { get; set; }
public bool ShowUnreadCount { get; set; }
public DateTime? LastSynchronizedDate { get; set; }
// For IMAP
public uint UidValidity { get; set; }
public long HighestModeSeq { get; set; }
/// <summary>
/// Outlook shares delta changes per-folder. Gmail is for per-account.
/// This is only used for Outlook provider.
/// </summary>
public string DeltaToken { get; set; }
// For GMail Labels
public string TextColorHex { get; set; }
public string BackgroundColorHex { get; set; }
[Ignore]
public List<IMailItemFolder> ChildFolders { get; set; } = [];
// Category and Move type folders are not valid move targets.
// These folders are virtual. They don't exist on the server.
public bool IsMoveTarget => !(SpecialFolderType == SpecialFolderType.More || SpecialFolderType == SpecialFolderType.Category);
public bool ContainsSpecialFolderType(SpecialFolderType type)
{ {
[PrimaryKey] if (SpecialFolderType == type)
public Guid Id { get; set; } return true;
public string RemoteFolderId { get; set; } foreach (var child in ChildFolders)
public string ParentRemoteFolderId { get; set; }
public Guid MailAccountId { get; set; }
public string FolderName { get; set; }
public SpecialFolderType SpecialFolderType { get; set; }
public bool IsSystemFolder { get; set; }
public bool IsSticky { get; set; }
public bool IsSynchronizationEnabled { get; set; }
public bool IsHidden { get; set; }
public bool ShowUnreadCount { get; set; }
public DateTime? LastSynchronizedDate { get; set; }
// For IMAP
public uint UidValidity { get; set; }
public long HighestModeSeq { get; set; }
/// <summary>
/// Outlook shares delta changes per-folder. Gmail is for per-account.
/// This is only used for Outlook provider.
/// </summary>
public string DeltaToken { get; set; }
// For GMail Labels
public string TextColorHex { get; set; }
public string BackgroundColorHex { get; set; }
[Ignore]
public List<IMailItemFolder> ChildFolders { get; set; } = [];
// Category and Move type folders are not valid move targets.
// These folders are virtual. They don't exist on the server.
public bool IsMoveTarget => !(SpecialFolderType == SpecialFolderType.More || SpecialFolderType == SpecialFolderType.Category);
public bool ContainsSpecialFolderType(SpecialFolderType type)
{ {
if (SpecialFolderType == type) if (child.SpecialFolderType == type)
return true;
foreach (var child in ChildFolders)
{ {
if (child.SpecialFolderType == type) return true;
{ }
return true; else
} {
else return child.ContainsSpecialFolderType(type);
{
return child.ContainsSpecialFolderType(type);
}
} }
return false;
} }
public static MailItemFolder CreateMoreFolder() => new MailItemFolder() { IsSticky = true, SpecialFolderType = SpecialFolderType.More, FolderName = Translator.MoreFolderNameOverride }; return false;
public static MailItemFolder CreateCategoriesFolder() => new MailItemFolder() { IsSticky = true, SpecialFolderType = SpecialFolderType.Category, FolderName = Translator.CategoriesFolderNameOverride };
public override string ToString() => FolderName;
} }
public static MailItemFolder CreateMoreFolder() => new MailItemFolder() { IsSticky = true, SpecialFolderType = SpecialFolderType.More, FolderName = Translator.MoreFolderNameOverride };
public static MailItemFolder CreateCategoriesFolder() => new MailItemFolder() { IsSticky = true, SpecialFolderType = SpecialFolderType.Category, FolderName = Translator.CategoriesFolderNameOverride };
public override string ToString() => FolderName;
} }

View File

@@ -1,13 +1,12 @@
using System; using System;
using SQLite; using SQLite;
namespace Wino.Core.Domain.Entities.Mail namespace Wino.Core.Domain.Entities.Mail;
{
public class MergedInbox
{
[PrimaryKey]
public Guid Id { get; set; }
public string Name { get; set; } public class MergedInbox
} {
[PrimaryKey]
public Guid Id { get; set; }
public string Name { get; set; }
} }

View File

@@ -2,76 +2,75 @@
using System.Collections.Generic; using System.Collections.Generic;
using SQLite; using SQLite;
namespace Wino.Core.Domain.Entities.Shared namespace Wino.Core.Domain.Entities.Shared;
/// <summary>
/// Back storage for simple name-address book.
/// These values will be inserted during MIME fetch.
/// </summary>
// TODO: This can easily evolve to Contact store, just like People app in Windows 10/11.
// Do it.
public class AccountContact : IEquatable<AccountContact>
{ {
/// <summary> /// <summary>
/// Back storage for simple name-address book. /// E-mail address of the contact.
/// These values will be inserted during MIME fetch.
/// </summary> /// </summary>
[PrimaryKey]
public string Address { get; set; }
// TODO: This can easily evolve to Contact store, just like People app in Windows 10/11. /// <summary>
// Do it. /// Display name of the contact.
public class AccountContact : IEquatable<AccountContact> /// </summary>
public string Name { get; set; }
/// <summary>
/// Base64 encoded profile image of the contact.
/// </summary>
public string Base64ContactPicture { get; set; }
/// <summary>
/// All registered accounts have their contacts registered as root.
/// Root contacts must not be overridden by any configuration.
/// They are created on account creation.
/// </summary>
public bool IsRootContact { get; set; }
/// <summary>
/// Short display name of the contact.
/// Eather Name or Address.
/// </summary>
public string ShortDisplayName => Address == Name || string.IsNullOrWhiteSpace(Name) ? $"{Address.ToLowerInvariant()};" : $"{Name};";
public string DisplayName => Address == Name || string.IsNullOrWhiteSpace(Name) ? Address.ToLowerInvariant() : $"{Name} <{Address.ToLowerInvariant()}>";
public override bool Equals(object obj)
{ {
/// <summary> return Equals(obj as AccountContact);
/// E-mail address of the contact. }
/// </summary>
[PrimaryKey]
public string Address { get; set; }
/// <summary> public bool Equals(AccountContact other)
/// Display name of the contact. {
/// </summary> return other is not null &&
public string Name { get; set; } Address == other.Address &&
Name == other.Name;
}
/// <summary> public override int GetHashCode()
/// Base64 encoded profile image of the contact. {
/// </summary> int hashCode = -1717786383;
public string Base64ContactPicture { get; set; } hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Address);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Name);
return hashCode;
}
/// <summary> public static bool operator ==(AccountContact left, AccountContact right)
/// All registered accounts have their contacts registered as root. {
/// Root contacts must not be overridden by any configuration. return EqualityComparer<AccountContact>.Default.Equals(left, right);
/// They are created on account creation. }
/// </summary>
public bool IsRootContact { get; set; }
/// <summary> public static bool operator !=(AccountContact left, AccountContact right)
/// Short display name of the contact. {
/// Eather Name or Address. return !(left == right);
/// </summary>
public string ShortDisplayName => Address == Name || string.IsNullOrWhiteSpace(Name) ? $"{Address.ToLowerInvariant()};" : $"{Name};";
public string DisplayName => Address == Name || string.IsNullOrWhiteSpace(Name) ? Address.ToLowerInvariant() : $"{Name} <{Address.ToLowerInvariant()}>";
public override bool Equals(object obj)
{
return Equals(obj as AccountContact);
}
public bool Equals(AccountContact other)
{
return other is not null &&
Address == other.Address &&
Name == other.Name;
}
public override int GetHashCode()
{
int hashCode = -1717786383;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Address);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Name);
return hashCode;
}
public static bool operator ==(AccountContact left, AccountContact right)
{
return EqualityComparer<AccountContact>.Default.Equals(left, right);
}
public static bool operator !=(AccountContact left, AccountContact right)
{
return !(left == right);
}
} }
} }

View File

@@ -2,52 +2,51 @@
using SQLite; using SQLite;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Entities.Shared namespace Wino.Core.Domain.Entities.Shared;
public class CustomServerInformation
{ {
public class CustomServerInformation [PrimaryKey]
{ public Guid Id { get; set; }
[PrimaryKey]
public Guid Id { get; set; }
public Guid AccountId { get; set; } public Guid AccountId { get; set; }
/// <summary> /// <summary>
/// This field is ignored. DisplayName is stored in MailAccount as SenderName from now. /// This field is ignored. DisplayName is stored in MailAccount as SenderName from now.
/// </summary> /// </summary>
[Ignore] [Ignore]
public string DisplayName { get; set; } public string DisplayName { get; set; }
public string Address { get; set; } public string Address { get; set; }
public string IncomingServer { get; set; } public string IncomingServer { get; set; }
public string IncomingServerUsername { get; set; } public string IncomingServerUsername { get; set; }
public string IncomingServerPassword { get; set; } public string IncomingServerPassword { get; set; }
public string IncomingServerPort { get; set; } public string IncomingServerPort { get; set; }
public CustomIncomingServerType IncomingServerType { get; set; } public CustomIncomingServerType IncomingServerType { get; set; }
public string OutgoingServer { get; set; } public string OutgoingServer { get; set; }
public string OutgoingServerPort { get; set; } public string OutgoingServerPort { get; set; }
public string OutgoingServerUsername { get; set; } public string OutgoingServerUsername { get; set; }
public string OutgoingServerPassword { get; set; } public string OutgoingServerPassword { get; set; }
/// <summary> /// <summary>
/// useSSL True: SslOnConnect /// useSSL True: SslOnConnect
/// useSSL False: StartTlsWhenAvailable /// useSSL False: StartTlsWhenAvailable
/// </summary> /// </summary>
public ImapConnectionSecurity IncomingServerSocketOption { get; set; } public ImapConnectionSecurity IncomingServerSocketOption { get; set; }
public ImapAuthenticationMethod IncomingAuthenticationMethod { get; set; } public ImapAuthenticationMethod IncomingAuthenticationMethod { get; set; }
public ImapConnectionSecurity OutgoingServerSocketOption { get; set; } public ImapConnectionSecurity OutgoingServerSocketOption { get; set; }
public ImapAuthenticationMethod OutgoingAuthenticationMethod { get; set; } public ImapAuthenticationMethod OutgoingAuthenticationMethod { get; set; }
public string ProxyServer { get; set; } public string ProxyServer { get; set; }
public string ProxyServerPort { get; set; } public string ProxyServerPort { get; set; }
/// <summary> /// <summary>
/// Number of concurrent clients that can connect to the server. /// Number of concurrent clients that can connect to the server.
/// Default is 5. /// Default is 5.
/// </summary> /// </summary>
public int MaxConcurrentClients { get; set; } public int MaxConcurrentClients { get; set; }
}
} }

View File

@@ -3,109 +3,108 @@ using SQLite;
using Wino.Core.Domain.Entities.Mail; using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Entities.Shared namespace Wino.Core.Domain.Entities.Shared;
public class MailAccount
{ {
public class MailAccount [PrimaryKey]
{ public Guid Id { get; set; }
[PrimaryKey]
public Guid Id { get; set; }
/// <summary> /// <summary>
/// Given name of the account in Wino. /// Given name of the account in Wino.
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// TODO: Display name of the authenticated user/account. /// TODO: Display name of the authenticated user/account.
/// API integrations will query this value from the API. /// API integrations will query this value from the API.
/// IMAP is populated by user on setup dialog. /// IMAP is populated by user on setup dialog.
/// </summary> /// </summary>
public string SenderName { get; set; } public string SenderName { get; set; }
/// <summary> /// <summary>
/// Account e-mail address. /// Account e-mail address.
/// </summary> /// </summary>
public string Address { get; set; } public string Address { get; set; }
/// <summary> /// <summary>
/// Provider type of the account. Outlook,Gmail etc... /// Provider type of the account. Outlook,Gmail etc...
/// </summary> /// </summary>
public MailProviderType ProviderType { get; set; } public MailProviderType ProviderType { get; set; }
/// <summary> /// <summary>
/// For tracking mail change delta. /// For tracking mail change delta.
/// Gmail : historyId /// Gmail : historyId
/// Outlook: deltaToken /// Outlook: deltaToken
/// </summary> /// </summary>
public string SynchronizationDeltaIdentifier { get; set; } public string SynchronizationDeltaIdentifier { get; set; }
/// <summary> /// <summary>
/// For tracking calendar change delta. /// For tracking calendar change delta.
/// Gmail: It's per-calendar, so unused. /// Gmail: It's per-calendar, so unused.
/// Outlook: deltaLink /// Outlook: deltaLink
/// </summary> /// </summary>
public string CalendarSynchronizationDeltaIdentifier { get; set; } public string CalendarSynchronizationDeltaIdentifier { get; set; }
/// <summary> /// <summary>
/// TODO: Gets or sets the custom account identifier color in hex. /// TODO: Gets or sets the custom account identifier color in hex.
/// </summary> /// </summary>
public string AccountColorHex { get; set; } public string AccountColorHex { get; set; }
/// <summary> /// <summary>
/// Base64 encoded profile picture of the account. /// Base64 encoded profile picture of the account.
/// </summary> /// </summary>
public string Base64ProfilePictureData { get; set; } public string Base64ProfilePictureData { get; set; }
/// <summary> /// <summary>
/// Gets or sets the listing order of the account in the accounts list. /// Gets or sets the listing order of the account in the accounts list.
/// </summary> /// </summary>
public int Order { get; set; } public int Order { get; set; }
/// <summary> /// <summary>
/// Gets or sets whether the account has any reason for an interactive user action to fix continue operating. /// Gets or sets whether the account has any reason for an interactive user action to fix continue operating.
/// </summary> /// </summary>
public AccountAttentionReason AttentionReason { get; set; } public AccountAttentionReason AttentionReason { get; set; }
/// <summary> /// <summary>
/// Gets or sets the id of the merged inbox this account belongs to. /// Gets or sets the id of the merged inbox this account belongs to.
/// </summary> /// </summary>
public Guid? MergedInboxId { get; set; } public Guid? MergedInboxId { get; set; }
/// <summary> /// <summary>
/// Gets or sets the additional IMAP provider assignment for the account. /// Gets or sets the additional IMAP provider assignment for the account.
/// Providers that use IMAP as a synchronizer but have special requirements. /// Providers that use IMAP as a synchronizer but have special requirements.
/// </summary> /// </summary>
public SpecialImapProvider SpecialImapProvider { get; set; } public SpecialImapProvider SpecialImapProvider { get; set; }
/// <summary> /// <summary>
/// Contains the merged inbox this account belongs to. /// Contains the merged inbox this account belongs to.
/// Ignored for all SQLite operations. /// Ignored for all SQLite operations.
/// </summary> /// </summary>
[Ignore] [Ignore]
public MergedInbox MergedInbox { get; set; } public MergedInbox MergedInbox { get; set; }
/// <summary> /// <summary>
/// Populated only when account has custom server information. /// Populated only when account has custom server information.
/// </summary> /// </summary>
[Ignore] [Ignore]
public CustomServerInformation ServerInformation { get; set; } public CustomServerInformation ServerInformation { get; set; }
/// <summary> /// <summary>
/// Account preferences. /// Account preferences.
/// </summary> /// </summary>
[Ignore] [Ignore]
public MailAccountPreferences Preferences { get; set; } public MailAccountPreferences Preferences { get; set; }
/// <summary> /// <summary>
/// Gets whether the account can perform ProfileInformation sync type. /// Gets whether the account can perform ProfileInformation sync type.
/// </summary> /// </summary>
public bool IsProfileInfoSyncSupported => ProviderType == MailProviderType.Outlook || ProviderType == MailProviderType.Gmail; public bool IsProfileInfoSyncSupported => ProviderType == MailProviderType.Outlook || ProviderType == MailProviderType.Gmail;
/// <summary> /// <summary>
/// Gets whether the account can perform AliasInformation sync type. /// Gets whether the account can perform AliasInformation sync type.
/// </summary> /// </summary>
public bool IsAliasSyncSupported => ProviderType == MailProviderType.Gmail; public bool IsAliasSyncSupported => ProviderType == MailProviderType.Gmail;
}
} }

View File

@@ -1,54 +1,53 @@
using System; using System;
using SQLite; using SQLite;
namespace Wino.Core.Domain.Entities.Shared namespace Wino.Core.Domain.Entities.Shared;
public class MailAccountPreferences
{ {
public class MailAccountPreferences [PrimaryKey]
{ public Guid Id { get; set; }
[PrimaryKey]
public Guid Id { get; set; }
/// <summary> /// <summary>
/// Id of the account in MailAccount table. /// Id of the account in MailAccount table.
/// </summary> /// </summary>
public Guid AccountId { get; set; } public Guid AccountId { get; set; }
/// <summary> /// <summary>
/// Gets or sets whether sent draft messages should be appended to the sent folder. /// Gets or sets whether sent draft messages should be appended to the sent folder.
/// Some IMAP servers do this automatically, some don't. /// Some IMAP servers do this automatically, some don't.
/// It's disabled by default. /// It's disabled by default.
/// </summary> /// </summary>
public bool ShouldAppendMessagesToSentFolder { get; set; } public bool ShouldAppendMessagesToSentFolder { get; set; }
/// <summary> /// <summary>
/// Gets or sets whether the notifications are enabled for the account. /// Gets or sets whether the notifications are enabled for the account.
/// </summary> /// </summary>
public bool IsNotificationsEnabled { get; set; } public bool IsNotificationsEnabled { get; set; }
/// <summary> /// <summary>
/// Gets or sets whether the account has Focused inbox support. /// Gets or sets whether the account has Focused inbox support.
/// Null if the account provider type doesn't support Focused inbox. /// Null if the account provider type doesn't support Focused inbox.
/// </summary> /// </summary>
public bool? IsFocusedInboxEnabled { get; set; } public bool? IsFocusedInboxEnabled { get; set; }
/// <summary> /// <summary>
/// Gets or sets whether signature should be appended automatically. /// Gets or sets whether signature should be appended automatically.
/// </summary> /// </summary>
public bool IsSignatureEnabled { get; set; } public bool IsSignatureEnabled { get; set; }
/// <summary> /// <summary>
/// Gets or sets whether this account's unread items should be included in taskbar badge. /// Gets or sets whether this account's unread items should be included in taskbar badge.
/// </summary> /// </summary>
public bool IsTaskbarBadgeEnabled { get; set; } = true; public bool IsTaskbarBadgeEnabled { get; set; } = true;
/// <summary> /// <summary>
/// Gets or sets signature for new messages. Null if signature is not needed. /// Gets or sets signature for new messages. Null if signature is not needed.
/// </summary> /// </summary>
public Guid? SignatureIdForNewMessages { get; set; } public Guid? SignatureIdForNewMessages { get; set; }
/// <summary> /// <summary>
/// Gets or sets signature for following messages. Null if signature is not needed. /// Gets or sets signature for following messages. Null if signature is not needed.
/// </summary> /// </summary>
public Guid? SignatureIdForFollowingMessages { get; set; } public Guid? SignatureIdForFollowingMessages { get; set; }
}
} }

View File

@@ -1,9 +1,8 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum AccountAttentionReason
{ {
public enum AccountAttentionReason None,
{ InvalidCredentials,
None, MissingSystemFolderConfiguration
InvalidCredentials,
MissingSystemFolderConfiguration
}
} }

View File

@@ -1,17 +1,16 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum AccountCreationDialogState
{ {
public enum AccountCreationDialogState Idle,
{ SigningIn,
Idle, PreparingFolders,
SigningIn, Completed,
PreparingFolders, ManuelSetupWaiting,
Completed, TestingConnection,
ManuelSetupWaiting, AutoDiscoverySetup,
TestingConnection, AutoDiscoveryInProgress,
AutoDiscoverySetup, FetchingProfileInformation,
AutoDiscoveryInProgress, Canceled,
FetchingProfileInformation, FetchingEvents
Canceled,
FetchingEvents
}
} }

View File

@@ -1,12 +1,11 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
/// <summary>
/// Indicates the state of synchronizer.
/// </summary>
public enum AccountSynchronizerState
{ {
/// <summary> Idle,
/// Indicates the state of synchronizer. ExecutingRequests,
/// </summary> Synchronizing
public enum AccountSynchronizerState
{
Idle,
ExecutingRequests,
Synchronizing
}
} }

View File

@@ -1,21 +1,20 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum AppLanguage
{ {
public enum AppLanguage None,
{ English,
None, Deutsch,
English, Russian,
Deutsch, Turkish,
Russian, Polish,
Turkish, Czech,
Polish, Chinese,
Czech, Spanish,
Chinese, French,
Spanish, Indonesian,
French, Greek,
Indonesian, PortugeseBrazil,
Greek, Italian,
PortugeseBrazil, Romanian
Italian,
Romanian
}
} }

View File

@@ -1,9 +1,8 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum AppThemeType
{ {
public enum AppThemeType System,
{ PreDefined,
System, Custom,
PreDefined,
Custom,
}
} }

View File

@@ -1,9 +1,8 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum ApplicationElementTheme
{ {
public enum ApplicationElementTheme Default,
{ Light,
Default, Dark
Light,
Dark
}
} }

View File

@@ -1,10 +1,9 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum AttendeeStatus
{ {
public enum AttendeeStatus NeedsAction,
{ Accepted,
NeedsAction, Tentative,
Accepted, Declined
Tentative,
Declined
}
} }

View File

@@ -1,8 +1,7 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum BackgroundSynchronizationReason
{ {
public enum BackgroundSynchronizationReason SessionConnected,
{ Timer
SessionConnected,
Timer
}
} }

View File

@@ -1,11 +1,10 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum CalendarDisplayType
{ {
public enum CalendarDisplayType Day,
{ Week,
Day, WorkWeek,
Week, Month,
WorkWeek, Year
Month,
Year
}
} }

View File

@@ -1,8 +1,7 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum CalendarEventTargetType
{ {
public enum CalendarEventTargetType Single, // Show details for a single event.
{ Series // Show the series event. Parent of all recurring events.
Single, // Show details for a single event.
Series // Show the series event. Parent of all recurring events.
}
} }

View File

@@ -1,11 +1,10 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
/// <summary>
/// Trigger to load more data.
/// </summary>
public enum CalendarInitInitiative
{ {
/// <summary> User,
/// Trigger to load more data. App
/// </summary>
public enum CalendarInitInitiative
{
User,
App
}
} }

View File

@@ -1,10 +1,9 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum CalendarItemRecurrenceFrequency
{ {
public enum CalendarItemRecurrenceFrequency Daily,
{ Weekly,
Daily, Monthly,
Weekly, Yearly
Monthly,
Yearly
}
} }

View File

@@ -1,8 +1,7 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum CalendarItemReminderType
{ {
public enum CalendarItemReminderType Popup,
{ Email
Popup,
Email
}
} }

View File

@@ -1,10 +1,9 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum CalendarItemStatus
{ {
public enum CalendarItemStatus NotResponded,
{ Confirmed,
NotResponded, Tentative,
Confirmed, Cancelled,
Tentative,
Cancelled,
}
} }

View File

@@ -1,10 +1,9 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum CalendarItemVisibility
{ {
public enum CalendarItemVisibility Default,
{ Public,
Default, Private,
Public, Confidential
Private,
Confidential
}
} }

View File

@@ -1,12 +1,11 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
/// <summary>
/// Which way in time to load more data for calendar.
/// </summary>
public enum CalendarLoadDirection
{ {
/// <summary> Replace,
/// Which way in time to load more data for calendar. Previous,
/// </summary> Next
public enum CalendarLoadDirection
{
Replace,
Previous,
Next
}
} }

View File

@@ -1,8 +1,7 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum CalendarOrientation
{ {
public enum CalendarOrientation Horizontal,
{ Vertical
Horizontal,
Vertical
}
} }

View File

@@ -1,11 +1,10 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum CalendarSynchronizationType
{ {
public enum CalendarSynchronizationType ExecuteRequests, // Execute all requests in the queue.
{ CalendarMetadata, // Sync calendar metadata.
ExecuteRequests, // Execute all requests in the queue. CalendarEvents, // Sync all events for all calendars.
CalendarMetadata, // Sync calendar metadata. SingleCalendar, // Sync events for only specified calendars.
CalendarEvents, // Sync all events for all calendars. UpdateProfile // Update profile information only.
SingleCalendar, // Sync events for only specified calendars.
UpdateProfile // Update profile information only.
}
} }

View File

@@ -1,24 +1,23 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum ChangeRequestType
{ {
public enum ChangeRequestType MailMarkAs,
{ MailChangeFlag,
MailMarkAs, MailHardDelete,
MailChangeFlag, MailMove,
MailHardDelete, MailAlwaysMoveTo,
MailMove, MailChangeFocused,
MailAlwaysMoveTo, MailArchive,
MailChangeFocused, MailUnarchive,
MailArchive, FolderMarkAsRead,
MailUnarchive, FolderDelete,
FolderMarkAsRead, FolderEmpty,
FolderDelete, FolderRename,
FolderEmpty, CreateNewDraft,
FolderRename, CreateReplyDraft,
CreateNewDraft, CreateForwardDraft,
CreateReplyDraft, DiscardDraft,
CreateForwardDraft, SendDraft,
DiscardDraft, FetchSingleItem
SendDraft,
FetchSingleItem
}
} }

View File

@@ -1,8 +1,7 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum CustomIncomingServerType
{ {
public enum CustomIncomingServerType POP3,
{ IMAP4
POP3,
IMAP4
}
} }

View File

@@ -1,8 +1,7 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum DayHeaderDisplayType
{ {
public enum DayHeaderDisplayType TwelveHour,
{ TwentyFourHour,
TwelveHour,
TwentyFourHour,
}
} }

View File

@@ -1,10 +1,9 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum DraftCreationReason
{ {
public enum DraftCreationReason Empty,
{ Reply,
Empty, ReplyAll,
Reply, Forward
ReplyAll,
Forward
}
} }

View File

@@ -1,11 +1,10 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum FilterOptionType
{ {
public enum FilterOptionType All,
{ Unread,
All, Flagged,
Unread, Mentions,
Flagged, Files
Mentions,
Files
}
} }

View File

@@ -1,23 +1,22 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
/// <summary>
/// Defines all possible folder operations that can be done.
/// Available values for each folder is returned by IContextMenuProvider
/// that integrators hold.
/// </summary>
public enum FolderOperation
{ {
/// <summary> None,
/// Defines all possible folder operations that can be done. Pin,
/// Available values for each folder is returned by IContextMenuProvider Unpin,
/// that integrators hold. MarkAllAsRead,
/// </summary> DontSync,
public enum FolderOperation Empty,
{ Rename,
None, Delete,
Pin, Move,
Unpin, TurnOffNotifications,
MarkAllAsRead, CreateSubFolder,
DontSync, Seperator
Empty,
Rename,
Delete,
Move,
TurnOffNotifications,
CreateSubFolder,
Seperator
}
} }

View File

@@ -1,13 +1,12 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum ImapAuthenticationMethod
{ {
public enum ImapAuthenticationMethod Auto,
{ None,
Auto, NormalPassword,
None, EncryptedPassword,
NormalPassword, Ntlm,
EncryptedPassword, CramMd5,
Ntlm, DigestMd5
CramMd5,
DigestMd5
}
} }

View File

@@ -1,10 +1,9 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum ImapConnectionSecurity
{ {
public enum ImapConnectionSecurity Auto,
{ None,
Auto, StartTls,
None, SslTls
StartTls,
SslTls
}
} }

View File

@@ -1,8 +1,7 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum InfoBarAnimationType
{ {
public enum InfoBarAnimationType SlideFromRightToLeft,
{ SlideFromBottomToTop
SlideFromRightToLeft,
SlideFromBottomToTop
}
} }

View File

@@ -1,10 +1,9 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum InfoBarMessageType
{ {
public enum InfoBarMessageType Information,
{ Success,
Information, Warning,
Success, Error
Warning,
Error
}
} }

View File

@@ -1,16 +1,15 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum MailAttachmentType
{ {
public enum MailAttachmentType None,
{ Executable,
None, Image,
Executable, Audio,
Image, Video,
Audio, PDF,
Video, HTML,
PDF, RarArchive,
HTML, Archive,
RarArchive, Other
Archive,
Other
}
} }

View File

@@ -1,9 +1,8 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum MailImportance
{ {
public enum MailImportance Low,
{ Normal,
Low, High
Normal,
High
}
} }

View File

@@ -1,9 +1,8 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum MailListDisplayMode
{ {
public enum MailListDisplayMode Spacious,
{ Medium,
Spacious, Compact,
Medium,
Compact,
}
} }

View File

@@ -1,9 +1,8 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum MailMarkAsOption
{ {
public enum MailMarkAsOption WhenSelected,
{ DontMark,
WhenSelected, AfterDelay
DontMark,
AfterDelay
}
} }

View File

@@ -1,58 +1,57 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
// Synchronizer requests.
public enum MailSynchronizerOperation
{ {
// Synchronizer requests. MarkRead,
public enum MailSynchronizerOperation Move,
{ Delete, // Hard delete.
MarkRead, CreateDraft,
Move, Send,
Delete, // Hard delete. ChangeFlag,
CreateDraft, AlwaysMoveTo,
Send, MoveToFocused,
ChangeFlag, Archive,
AlwaysMoveTo, }
MoveToFocused,
Archive, public enum FolderSynchronizerOperation
} {
RenameFolder,
public enum FolderSynchronizerOperation EmptyFolder,
{ MarkFolderRead,
RenameFolder, }
EmptyFolder,
MarkFolderRead, // UI requests
} public enum MailOperation
{
// UI requests None,
public enum MailOperation Archive,
{ UnArchive,
None, SoftDelete,
Archive, HardDelete,
UnArchive, Move,
SoftDelete, MoveToJunk,
HardDelete, MoveToFocused,
Move, MoveToOther,
MoveToJunk, AlwaysMoveToOther,
MoveToFocused, AlwaysMoveToFocused,
MoveToOther, SetFlag,
AlwaysMoveToOther, ClearFlag,
AlwaysMoveToFocused, MarkAsRead,
SetFlag, MarkAsUnread,
ClearFlag, MarkAsNotJunk,
MarkAsRead, Seperator,
MarkAsUnread, Ignore,
MarkAsNotJunk, Reply,
Seperator, ReplyAll,
Ignore, Zoom,
Reply, SaveAs,
ReplyAll, Find,
Zoom, Forward,
SaveAs, DarkEditor,
Find, LightEditor,
Forward, Print,
DarkEditor, ViewMessageSource,
LightEditor, DiscardLocalDraft,
Print, Navigate // For toast activation
ViewMessageSource,
DiscardLocalDraft,
Navigate // For toast activation
}
} }

View File

@@ -1,9 +1,8 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum MailProviderType
{ {
public enum MailProviderType Outlook,
{ Gmail,
Outlook, IMAP4 = 4 // 2-3 were removed after release. Don't change for backward compatibility.
Gmail,
IMAP4 = 4 // 2-3 were removed after release. Don't change for backward compatibility.
}
} }

View File

@@ -1,14 +1,13 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum MailSynchronizationType
{ {
public enum MailSynchronizationType UpdateProfile, // Only update profile information
{ ExecuteRequests, // Run the queued requests, and then synchronize if needed.
UpdateProfile, // Only update profile information FoldersOnly, // Only synchronize folder metadata.
ExecuteRequests, // Run the queued requests, and then synchronize if needed. InboxOnly, // Only Inbox, Sent, Draft and Deleted folders.
FoldersOnly, // Only synchronize folder metadata. CustomFolders, // Only sync folders that are specified in the options.
InboxOnly, // Only Inbox, Sent, Draft and Deleted folders. FullFolders, // Synchronize all folders. This won't update profile or alias information.
CustomFolders, // Only sync folders that are specified in the options. Alias, // Only update alias information
FullFolders, // Synchronize all folders. This won't update profile or alias information. IMAPIdle // Idle client triggered synchronization.
Alias, // Only update alias information
IMAPIdle // Idle client triggered synchronization.
}
} }

View File

@@ -1,8 +1,7 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum NavigationReferenceFrame
{ {
public enum NavigationReferenceFrame ShellFrame,
{ RenderingFrame
ShellFrame,
RenderingFrame
}
} }

View File

@@ -1,12 +1,11 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
/// <summary>
/// Defines the potential reasons for picking folder in the folder picking dialog.
/// </summary>
public enum PickFolderReason
{ {
/// <summary> Move,
/// Defines the potential reasons for picking folder in the folder picking dialog. SpecialFolder,
/// </summary> Any
public enum PickFolderReason
{
Move,
SpecialFolder,
Any
}
} }

View File

@@ -1,10 +1,9 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum PrintingResult
{ {
public enum PrintingResult Abandoned,
{ Canceled,
Abandoned, Failed,
Canceled, Submitted
Failed,
Submitted
}
} }

View File

@@ -1,12 +1,11 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
/// <summary>
/// What should happen to server app when the client is terminated.
/// </summary>
public enum ServerBackgroundMode
{ {
/// <summary> MinimizedTray, // Still runs, tray icon is visible.
/// What should happen to server app when the client is terminated. Invisible, // Still runs, tray icon is invisible.
/// </summary> Terminate // Server is terminated as Wino terminates.
public enum ServerBackgroundMode
{
MinimizedTray, // Still runs, tray icon is visible.
Invisible, // Still runs, tray icon is invisible.
Terminate // Server is terminated as Wino terminates.
}
} }

View File

@@ -1,8 +1,7 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum SortingOptionType
{ {
public enum SortingOptionType ReceiveDate,
{ Sender
ReceiveDate,
Sender
}
} }

View File

@@ -1,24 +1,23 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum SpecialFolderType
{ {
public enum SpecialFolderType Inbox,
{ Starred,
Inbox, Important,
Starred, Sent,
Important, Draft,
Sent, Archive,
Draft, Deleted,
Archive, Junk,
Deleted, Chat,
Junk, Category,
Chat, Unread,
Category, Forums,
Unread, Updates,
Forums, Personal,
Updates, Promotions,
Personal, Social,
Promotions, Other,
Social, More
Other,
More
}
} }

View File

@@ -1,9 +1,8 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum SpecialImapProvider
{ {
public enum SpecialImapProvider None,
{ iCloud,
None, Yahoo
iCloud,
Yahoo
}
} }

View File

@@ -1,11 +1,10 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum StartupBehaviorResult
{ {
public enum StartupBehaviorResult Enabled,
{ Disabled,
Enabled, DisabledByUser,
Disabled, DisabledByPolicy,
DisabledByUser, Fatal
DisabledByPolicy,
Fatal
}
} }

View File

@@ -1,19 +1,18 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
// From the SDK.
public enum StorePurchaseResult
{ {
// From the SDK. //
public enum StorePurchaseResult // Summary:
{ // The purchase request succeeded.
// Succeeded,
// Summary: //
// The purchase request succeeded. // Summary:
Succeeded, // The current user has already purchased the specified app or add-on.
// AlreadyPurchased,
// Summary: //
// The current user has already purchased the specified app or add-on. // Summary:
AlreadyPurchased, // The purchase request did not succeed.
// NotPurchased,
// Summary:
// The purchase request did not succeed.
NotPurchased,
}
} }

View File

@@ -1,9 +1,8 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum SynchronizationCompletedState
{ {
public enum SynchronizationCompletedState Success, // All succeeded.
{ Canceled, // Canceled by user or HTTP call.
Success, // All succeeded. Failed // Exception.
Canceled, // Canceled by user or HTTP call.
Failed // Exception.
}
} }

View File

@@ -1,12 +1,11 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
/// <summary>
/// Enumeration for the source of synchronization.
/// Right now it can either be from the client or the server.
/// </summary>
public enum SynchronizationSource
{ {
/// <summary> Client,
/// Enumeration for the source of synchronization. Server
/// Right now it can either be from the client or the server.
/// </summary>
public enum SynchronizationSource
{
Client,
Server
}
} }

View File

@@ -1,9 +1,8 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum WinoAppType
{ {
public enum WinoAppType Unknown,
{ Mail,
Unknown, Calendar
Mail,
Calendar
}
} }

View File

@@ -1,10 +1,9 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum WinoCustomMessageDialogIcon
{ {
public enum WinoCustomMessageDialogIcon Information,
{ Warning,
Information, Error,
Warning, Question
Error,
Question
}
} }

View File

@@ -1,34 +1,33 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
{
/// <summary>
/// All registered views.
/// </summary>
public enum WinoPage
{
None,
IdlePage,
ComposePage,
SettingsPage,
MailRenderingPage,
WelcomePage,
AccountDetailsPage,
MergedAccountDetailsPage,
ManageAccountsPage,
AccountManagementPage,
SignatureManagementPage,
AboutPage,
PersonalizationPage,
MessageListPage,
MailListPage,
ReadComposePanePage,
LanguageTimePage,
AppPreferencesPage,
SettingOptionsPage,
AliasManagementPage,
// Calendar /// <summary>
CalendarPage, /// All registered views.
CalendarSettingsPage, /// </summary>
EventDetailsPage public enum WinoPage
} {
None,
IdlePage,
ComposePage,
SettingsPage,
MailRenderingPage,
WelcomePage,
AccountDetailsPage,
MergedAccountDetailsPage,
ManageAccountsPage,
AccountManagementPage,
SignatureManagementPage,
AboutPage,
PersonalizationPage,
MessageListPage,
MailListPage,
ReadComposePanePage,
LanguageTimePage,
AppPreferencesPage,
SettingOptionsPage,
AliasManagementPage,
// Calendar
CalendarPage,
CalendarSettingsPage,
EventDetailsPage
} }

View File

@@ -1,11 +1,10 @@
namespace Wino.Core.Domain.Enums namespace Wino.Core.Domain.Enums;
public enum WinoServerConnectionStatus
{ {
public enum WinoServerConnectionStatus None,
{ Connecting,
None, Connected,
Connecting, Disconnected,
Connected, Failed
Disconnected,
Failed
}
} }

View File

@@ -1,7 +1,6 @@
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
{
public class AccountSetupCanceledException : System.Exception public class AccountSetupCanceledException : System.Exception
{ {
}
} }

View File

@@ -1,19 +1,18 @@
using System; using System;
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
{
/// <summary>
/// Thrown when IAuthenticator requires user interaction to fix authentication issues.
/// It can be expired and can't restorable token, or some stuff that requires re-authentication.
/// </summary>
public class AuthenticationAttentionException : Exception
{
public AuthenticationAttentionException(MailAccount account)
{
Account = account;
}
public MailAccount Account { get; } /// <summary>
/// Thrown when IAuthenticator requires user interaction to fix authentication issues.
/// It can be expired and can't restorable token, or some stuff that requires re-authentication.
/// </summary>
public class AuthenticationAttentionException : Exception
{
public AuthenticationAttentionException(MailAccount account)
{
Account = account;
} }
public MailAccount Account { get; }
} }

View File

@@ -1,18 +1,17 @@
using System; using System;
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
{
/// <summary>
/// All exceptions related to authentication.
/// </summary>
public class AuthenticationException : Exception
{
public AuthenticationException(string message) : base(message)
{
}
public AuthenticationException(string message, Exception innerException) : base(message, innerException) /// <summary>
{ /// All exceptions related to authentication.
} /// </summary>
public class AuthenticationException : Exception
{
public AuthenticationException(string message) : base(message)
{
}
public AuthenticationException(string message, Exception innerException) : base(message, innerException)
{
} }
} }

View File

@@ -1,9 +1,8 @@
using System; using System;
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
{
/// <summary> /// <summary>
/// An exception thrown when the background task registration is failed. /// An exception thrown when the background task registration is failed.
/// </summary> /// </summary>
public class BackgroundTaskRegistrationFailedException : Exception { } public class BackgroundTaskRegistrationFailedException : Exception { }
}

View File

@@ -1,11 +1,10 @@
using System; using System;
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
/// <summary>
/// Thrown when composer cant find the mime to load.
/// </summary>
public class ComposerMimeNotFoundException : Exception
{ {
/// <summary>
/// Thrown when composer cant find the mime to load.
/// </summary>
public class ComposerMimeNotFoundException : Exception
{
}
} }

View File

@@ -1,11 +1,10 @@
using System; using System;
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
public class CustomThemeCreationFailedException : Exception
{ {
public class CustomThemeCreationFailedException : Exception public CustomThemeCreationFailedException(string message) : base(message)
{ {
public CustomThemeCreationFailedException(string message) : base(message)
{
}
} }
} }

View File

@@ -1,7 +1,6 @@
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
public class GoogleAuthenticationException : System.Exception
{ {
public class GoogleAuthenticationException : System.Exception public GoogleAuthenticationException(string message) : base(message) { }
{
public GoogleAuthenticationException(string message) : base(message) { }
}
} }

View File

@@ -1,14 +1,13 @@
using System; using System;
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
{
public class ImapClientPoolException : Exception
{
public ImapClientPoolException(Exception innerException, string protocolLog) : base(Translator.Exception_ImapClientPoolFailed, innerException)
{
ProtocolLog = protocolLog;
}
public string ProtocolLog { get; } public class ImapClientPoolException : Exception
{
public ImapClientPoolException(Exception innerException, string protocolLog) : base(Translator.Exception_ImapClientPoolFailed, innerException)
{
ProtocolLog = protocolLog;
} }
public string ProtocolLog { get; }
} }

View File

@@ -1,18 +1,17 @@
using Wino.Core.Domain.Models.AutoDiscovery; using Wino.Core.Domain.Models.AutoDiscovery;
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
{
public class ImapConnectionFailedPackage
{
public ImapConnectionFailedPackage(string errorMessage, string protocolLog, AutoDiscoverySettings settings)
{
ErrorMessage = errorMessage;
ProtocolLog = protocolLog;
Settings = settings;
}
public AutoDiscoverySettings Settings { get; } public class ImapConnectionFailedPackage
public string ErrorMessage { get; set; } {
public string ProtocolLog { get; } public ImapConnectionFailedPackage(string errorMessage, string protocolLog, AutoDiscoverySettings settings)
{
ErrorMessage = errorMessage;
ProtocolLog = protocolLog;
Settings = settings;
} }
public AutoDiscoverySettings Settings { get; }
public string ErrorMessage { get; set; }
public string ProtocolLog { get; }
} }

View File

@@ -1,10 +1,9 @@
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
{
public class ImapSynchronizerStrategyException : System.Exception public class ImapSynchronizerStrategyException : System.Exception
{ {
public ImapSynchronizerStrategyException(string message) : base(message) public ImapSynchronizerStrategyException(string message) : base(message)
{ {
}
} }
} }

View File

@@ -1,17 +1,16 @@
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
public class ImapTestSSLCertificateException : System.Exception
{ {
public class ImapTestSSLCertificateException : System.Exception public ImapTestSSLCertificateException(string issuer, string expirationDateString, string validFromDateString)
{ {
public ImapTestSSLCertificateException(string issuer, string expirationDateString, string validFromDateString) Issuer = issuer;
{ ExpirationDateString = expirationDateString;
Issuer = issuer; ValidFromDateString = validFromDateString;
ExpirationDateString = expirationDateString;
ValidFromDateString = validFromDateString;
}
public string Issuer { get; set; }
public string ExpirationDateString { get; set; }
public string ValidFromDateString { get; set; }
} }
public string Issuer { get; set; }
public string ExpirationDateString { get; set; }
public string ValidFromDateString { get; set; }
} }

View File

@@ -1,6 +1,5 @@
using System; using System;
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
{
public class InvalidMoveTargetException : Exception { } public class InvalidMoveTargetException : Exception { }
}

View File

@@ -1,7 +1,6 @@
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
public class MissingAliasException : System.Exception
{ {
public class MissingAliasException : System.Exception public MissingAliasException() : base(Translator.Exception_MissingAlias) { }
{
public MissingAliasException() : base(Translator.Exception_MissingAlias) { }
}
} }

View File

@@ -1,11 +1,10 @@
using System; using System;
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
public class SynchronizerEntityNotFoundException : Exception
{ {
public class SynchronizerEntityNotFoundException : Exception public SynchronizerEntityNotFoundException(string message) : base(message)
{ {
public SynchronizerEntityNotFoundException(string message) : base(message)
{
}
} }
} }

View File

@@ -1,15 +1,14 @@
using System; using System;
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
{
public class SynchronizerException : Exception
{
public SynchronizerException(string message) : base(message)
{
}
public SynchronizerException(string message, Exception innerException) : base(message, innerException) public class SynchronizerException : Exception
{ {
} public SynchronizerException(string message) : base(message)
{
}
public SynchronizerException(string message, Exception innerException) : base(message, innerException)
{
} }
} }

View File

@@ -1,7 +1,6 @@
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
{
/// <summary> /// <summary>
/// When IMAP account's system folder configuration setup is not done yet. /// When IMAP account's system folder configuration setup is not done yet.
/// </summary> /// </summary>
public class SystemFolderConfigurationMissingException : System.Exception { } public class SystemFolderConfigurationMissingException : System.Exception { }
}

View File

@@ -1,20 +1,19 @@
using System; using System;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
{
/// <summary>
/// Emitted when special folder is needed for an operation but it couldn't be found.
/// </summary>
public class UnavailableSpecialFolderException : Exception
{
public UnavailableSpecialFolderException(SpecialFolderType specialFolderType, Guid accountId)
{
SpecialFolderType = specialFolderType;
AccountId = accountId;
}
public SpecialFolderType SpecialFolderType { get; } /// <summary>
public Guid AccountId { get; set; } /// Emitted when special folder is needed for an operation but it couldn't be found.
/// </summary>
public class UnavailableSpecialFolderException : Exception
{
public UnavailableSpecialFolderException(SpecialFolderType specialFolderType, Guid accountId)
{
SpecialFolderType = specialFolderType;
AccountId = accountId;
} }
public SpecialFolderType SpecialFolderType { get; }
public Guid AccountId { get; set; }
} }

View File

@@ -1,12 +1,11 @@
using System; using System;
namespace Wino.Core.Domain.Exceptions namespace Wino.Core.Domain.Exceptions;
/// <summary>
/// All server crash types. Wino Server ideally should not throw anything else than this Exception type.
/// </summary>
public class WinoServerException : Exception
{ {
/// <summary> public WinoServerException(string message) : base(message) { }
/// All server crash types. Wino Server ideally should not throw anything else than this Exception type.
/// </summary>
public class WinoServerException : Exception
{
public WinoServerException(string message) : base(message) { }
}
} }

View File

@@ -1,33 +1,32 @@
using System; using System;
using Wino.Core.Domain.Models.Calendar; using Wino.Core.Domain.Models.Calendar;
namespace Wino.Core.Domain.Extensions namespace Wino.Core.Domain.Extensions;
public static class DateTimeExtensions
{ {
public static class DateTimeExtensions /// <summary>
/// Returns a date range for the month of the given date.
/// </summary>
/// <param name="date">Date to get range for.</param>
public static DateRange GetMonthDateRangeStartingWeekday(this DateTime date, DayOfWeek WeekStartDay)
{ {
/// <summary> DateTime firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
/// Returns a date range for the month of the given date.
/// </summary>
/// <param name="date">Date to get range for.</param>
public static DateRange GetMonthDateRangeStartingWeekday(this DateTime date, DayOfWeek WeekStartDay)
{
DateTime firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
int daysToSubtract = (7 + (firstDayOfMonth.DayOfWeek - WeekStartDay)) % 7; int daysToSubtract = (7 + (firstDayOfMonth.DayOfWeek - WeekStartDay)) % 7;
DateTime rangeStart = firstDayOfMonth.AddDays(-daysToSubtract); DateTime rangeStart = firstDayOfMonth.AddDays(-daysToSubtract);
DateTime rangeEnd = rangeStart.AddDays(34); DateTime rangeEnd = rangeStart.AddDays(34);
return new DateRange(rangeStart, rangeEnd); return new DateRange(rangeStart, rangeEnd);
} }
public static DateTime GetWeekStartDateForDate(this DateTime date, DayOfWeek firstDayOfWeek) public static DateTime GetWeekStartDateForDate(this DateTime date, DayOfWeek firstDayOfWeek)
{ {
// Detect the first day of the week that contains the selected date. // Detect the first day of the week that contains the selected date.
int diff = (7 + (date.DayOfWeek - firstDayOfWeek)) % 7; int diff = (7 + (date.DayOfWeek - firstDayOfWeek)) % 7;
// Start loading from this date instead of visible date. // Start loading from this date instead of visible date.
return date.AddDays(-diff).Date; return date.AddDays(-diff).Date;
}
} }
} }

View File

@@ -1,24 +1,23 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Wino.Core.Domain.Extensions namespace Wino.Core.Domain.Extensions;
{
public static class ExceptionExtensions
{
public static IEnumerable<Exception> GetInnerExceptions(this Exception ex)
{
if (ex == null)
{
throw new ArgumentNullException("ex");
}
var innerException = ex; public static class ExceptionExtensions
do {
{ public static IEnumerable<Exception> GetInnerExceptions(this Exception ex)
yield return innerException; {
innerException = innerException.InnerException; if (ex == null)
} {
while (innerException != null); throw new ArgumentNullException("ex");
} }
var innerException = ex;
do
{
yield return innerException;
innerException = innerException.InnerException;
}
while (innerException != null);
} }
} }

View File

@@ -1,20 +1,19 @@
using System; using System;
using System.IO; using System.IO;
namespace Wino.Core.Domain.Extensions namespace Wino.Core.Domain.Extensions;
public static class MimeExtensions
{ {
public static class MimeExtensions public static string GetBase64MimeMessage(this MimeKit.MimeMessage message)
{ {
public static string GetBase64MimeMessage(this MimeKit.MimeMessage message) using MemoryStream memoryStream = new();
{
using MemoryStream memoryStream = new();
message.WriteTo(memoryStream); message.WriteTo(memoryStream);
return Convert.ToBase64String(memoryStream.ToArray()); return Convert.ToBase64String(memoryStream.ToArray());
}
public static MimeKit.MimeMessage GetMimeMessageFromBase64(this string base64)
=> MimeKit.MimeMessage.Load(new System.IO.MemoryStream(Convert.FromBase64String(base64)));
} }
public static MimeKit.MimeMessage GetMimeMessageFromBase64(this string base64)
=> MimeKit.MimeMessage.Load(new System.IO.MemoryStream(Convert.FromBase64String(base64)));
} }

View File

@@ -1,16 +1,15 @@
using System; using System;
namespace Wino.Core.Domain.Interfaces namespace Wino.Core.Domain.Interfaces;
public interface IAccountCalendar
{ {
public interface IAccountCalendar string Name { get; set; }
{ string TextColorHex { get; set; }
string Name { get; set; } string BackgroundColorHex { get; set; }
string TextColorHex { get; set; } bool IsPrimary { get; set; }
string BackgroundColorHex { get; set; } Guid AccountId { get; set; }
bool IsPrimary { get; set; } string RemoteCalendarId { get; set; }
Guid AccountId { get; set; } bool IsExtended { get; set; }
string RemoteCalendarId { get; set; } Guid Id { get; set; }
bool IsExtended { get; set; }
Guid Id { get; set; }
}
} }

View File

@@ -2,12 +2,11 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Wino.Core.Domain.Enums; using Wino.Core.Domain.Enums;
namespace Wino.Core.Domain.Interfaces namespace Wino.Core.Domain.Interfaces;
public interface IAccountCreationDialog
{ {
public interface IAccountCreationDialog Task ShowDialogAsync(CancellationTokenSource cancellationTokenSource);
{ void Complete(bool cancel);
Task ShowDialogAsync(CancellationTokenSource cancellationTokenSource); AccountCreationDialogState State { get; set; }
void Complete(bool cancel);
AccountCreationDialogState State { get; set; }
}
} }

View File

@@ -2,21 +2,20 @@
using Wino.Core.Domain.Entities.Mail; using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
namespace Wino.Core.Domain.Interfaces namespace Wino.Core.Domain.Interfaces;
public interface IAccountMenuItem : IMenuItem
{ {
public interface IAccountMenuItem : IMenuItem bool IsEnabled { get; set; }
{ double SynchronizationProgress { get; set; }
bool IsEnabled { get; set; } int UnreadItemCount { get; set; }
double SynchronizationProgress { get; set; } IEnumerable<MailAccount> HoldingAccounts { get; }
int UnreadItemCount { get; set; } void UpdateAccount(MailAccount account);
IEnumerable<MailAccount> HoldingAccounts { get; } }
void UpdateAccount(MailAccount account);
} public interface IMergedAccountMenuItem : IAccountMenuItem
{
public interface IMergedAccountMenuItem : IAccountMenuItem int MergedAccountCount { get; }
{
int MergedAccountCount { get; } MergedInbox Parameter { get; }
MergedInbox Parameter { get; }
}
} }

View File

@@ -1,6 +1,5 @@
namespace Wino.Core.Domain.Interfaces namespace Wino.Core.Domain.Interfaces;
public interface IAccountPickerDialog
{ {
public interface IAccountPickerDialog
{
}
} }

View File

@@ -1,38 +1,37 @@
using System; using System;
namespace Wino.Core.Domain.Interfaces namespace Wino.Core.Domain.Interfaces;
public interface IAccountProviderDetailViewModel
{ {
public interface IAccountProviderDetailViewModel /// <summary>
{ /// Entity id that will help to identify the startup entity on launch.
/// <summary> /// </summary>
/// Entity id that will help to identify the startup entity on launch. Guid StartupEntityId { get; }
/// </summary>
Guid StartupEntityId { get; }
/// <summary> /// <summary>
/// Name representation of the view model that will be used to identify the startup entity on launch. /// Name representation of the view model that will be used to identify the startup entity on launch.
/// </summary> /// </summary>
string StartupEntityTitle { get; } string StartupEntityTitle { get; }
/// <summary> /// <summary>
/// E-mail addresses that this account holds. /// E-mail addresses that this account holds.
/// </summary> /// </summary>
string StartupEntityAddresses { get; } string StartupEntityAddresses { get; }
/// <summary> /// <summary>
/// Represents the account order in the accounts list. /// Represents the account order in the accounts list.
/// </summary> /// </summary>
int Order { get; } int Order { get; }
/// <summary> /// <summary>
/// Provider details of the account. /// Provider details of the account.
/// </summary> /// </summary>
IProviderDetail ProviderDetail { get; set; } IProviderDetail ProviderDetail { get; set; }
/// <summary> /// <summary>
/// How many accounts this provider has. /// How many accounts this provider has.
/// </summary> /// </summary>
int HoldingAccountCount { get; } int HoldingAccountCount { get; }
}
} }

View File

@@ -1,11 +1,10 @@
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
namespace Wino.Core.Domain.Interfaces namespace Wino.Core.Domain.Interfaces;
public interface IAccountProviderDetails
{ {
public interface IAccountProviderDetails MailAccount Account { get; set; }
{ bool AutoExtend { get; set; }
MailAccount Account { get; set; } IProviderDetail ProviderDetail { get; set; }
bool AutoExtend { get; set; }
IProviderDetail ProviderDetail { get; set; }
}
} }

View File

@@ -5,156 +5,155 @@ using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Entities.Shared; using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Models.Accounts; using Wino.Core.Domain.Models.Accounts;
namespace Wino.Core.Domain.Interfaces namespace Wino.Core.Domain.Interfaces;
public interface IAccountService
{ {
public interface IAccountService /// <summary>
{ /// Current IAuthenticator that should receive external authentication process to continue with.
/// <summary> /// For example: Google auth will launch a browser authentication. After it completes, this is the IAuthenticator
/// Current IAuthenticator that should receive external authentication process to continue with. /// to continue process for token exchange.
/// For example: Google auth will launch a browser authentication. After it completes, this is the IAuthenticator /// </summary>
/// to continue process for token exchange. IAuthenticator ExternalAuthenticationAuthenticator { get; set; }
/// </summary>
IAuthenticator ExternalAuthenticationAuthenticator { get; set; }
/// <summary> /// <summary>
/// Returns all local accounts. /// Returns all local accounts.
/// </summary> /// </summary>
/// <returns>All local accounts</returns> /// <returns>All local accounts</returns>
Task<List<MailAccount>> GetAccountsAsync(); Task<List<MailAccount>> GetAccountsAsync();
/// <summary> /// <summary>
/// Returns single MailAccount /// Returns single MailAccount
/// </summary> /// </summary>
/// <param name="accountId">AccountId.</param> /// <param name="accountId">AccountId.</param>
Task<MailAccount> GetAccountAsync(Guid accountId); Task<MailAccount> GetAccountAsync(Guid accountId);
/// <summary> /// <summary>
/// Deletes all information about the account, including token information. /// Deletes all information about the account, including token information.
/// </summary> /// </summary>
/// <param name="account">MailAccount to be removed</param> /// <param name="account">MailAccount to be removed</param>
Task DeleteAccountAsync(MailAccount account); Task DeleteAccountAsync(MailAccount account);
/// <summary> /// <summary>
/// Returns the custom server information for the given account id. /// Returns the custom server information for the given account id.
/// </summary> /// </summary>
Task<CustomServerInformation> GetAccountCustomServerInformationAsync(Guid accountId); Task<CustomServerInformation> GetAccountCustomServerInformationAsync(Guid accountId);
/// <summary> /// <summary>
/// Updates the given account properties. /// Updates the given account properties.
/// </summary> /// </summary>
Task UpdateAccountAsync(MailAccount account); Task UpdateAccountAsync(MailAccount account);
/// <summary> /// <summary>
/// Creates new account with the given server information if any. /// Creates new account with the given server information if any.
/// Also sets the account as Startup account if there are no accounts. /// Also sets the account as Startup account if there are no accounts.
/// </summary> /// </summary>
Task CreateAccountAsync(MailAccount account, CustomServerInformation customServerInformation); Task CreateAccountAsync(MailAccount account, CustomServerInformation customServerInformation);
/// <summary> /// <summary>
/// Fixed authentication errors for account by forcing interactive login. /// Fixed authentication errors for account by forcing interactive login.
/// </summary> /// </summary>
Task FixTokenIssuesAsync(Guid accountId); Task FixTokenIssuesAsync(Guid accountId);
/// <summary> /// <summary>
/// Removed the attention from an account. /// Removed the attention from an account.
/// </summary> /// </summary>
/// <param name="accountId">Account id to remove from</param> /// <param name="accountId">Account id to remove from</param>
Task ClearAccountAttentionAsync(Guid accountId); Task ClearAccountAttentionAsync(Guid accountId);
/// <summary> /// <summary>
/// Updates the account synchronization identifier. /// Updates the account synchronization identifier.
/// For example: Gmail uses this identifier to keep track of the last synchronization. /// For example: Gmail uses this identifier to keep track of the last synchronization.
/// Update is ignored for Gmail if the new identifier is older than the current one. /// Update is ignored for Gmail if the new identifier is older than the current one.
/// </summary> /// </summary>
/// <param name="newIdentifier">Identifier to update</param> /// <param name="newIdentifier">Identifier to update</param>
/// <returns>Current account synchronization modifier.</returns> /// <returns>Current account synchronization modifier.</returns>
Task<string> UpdateSynchronizationIdentifierAsync(Guid accountId, string newIdentifier); Task<string> UpdateSynchronizationIdentifierAsync(Guid accountId, string newIdentifier);
/// <summary> /// <summary>
/// Renames the merged inbox with the given id. /// Renames the merged inbox with the given id.
/// </summary> /// </summary>
/// <param name="mergedInboxId">Merged Inbox id</param> /// <param name="mergedInboxId">Merged Inbox id</param>
/// <param name="newName">New name for the merged/linked inbox.</param> /// <param name="newName">New name for the merged/linked inbox.</param>
Task RenameMergedAccountAsync(Guid mergedInboxId, string newName); Task RenameMergedAccountAsync(Guid mergedInboxId, string newName);
/// <summary> /// <summary>
/// Creates a new merged inbox with the given accounts. /// Creates a new merged inbox with the given accounts.
/// </summary> /// </summary>
/// <param name="mergedInbox">Merged inbox properties.</param> /// <param name="mergedInbox">Merged inbox properties.</param>
/// <param name="accountsToMerge">List of accounts to merge together.</param> /// <param name="accountsToMerge">List of accounts to merge together.</param>
Task CreateMergeAccountsAsync(MergedInbox mergedInbox, IEnumerable<MailAccount> accountsToMerge); Task CreateMergeAccountsAsync(MergedInbox mergedInbox, IEnumerable<MailAccount> accountsToMerge);
/// <summary> /// <summary>
/// Updates the merged inbox with the given id with the new linked accounts. /// Updates the merged inbox with the given id with the new linked accounts.
/// </summary> /// </summary>
/// <param name="mergedInboxId">Updating merged inbox id.</param> /// <param name="mergedInboxId">Updating merged inbox id.</param>
/// <param name="linkedAccountIds">List of linked account ids.</param> /// <param name="linkedAccountIds">List of linked account ids.</param>
Task UpdateMergedInboxAsync(Guid mergedInboxId, IEnumerable<Guid> linkedAccountIds); Task UpdateMergedInboxAsync(Guid mergedInboxId, IEnumerable<Guid> linkedAccountIds);
/// <summary> /// <summary>
/// Destroys the merged inbox with the given id. /// Destroys the merged inbox with the given id.
/// </summary> /// </summary>
/// <param name="mergedInboxId">Merged inbox id to destroy.</param> /// <param name="mergedInboxId">Merged inbox id to destroy.</param>
Task UnlinkMergedInboxAsync(Guid mergedInboxId); Task UnlinkMergedInboxAsync(Guid mergedInboxId);
/// <summary> /// <summary>
/// Updates the account listing orders. /// Updates the account listing orders.
/// </summary> /// </summary>
/// <param name="accountIdOrderPair">AccountId-OrderNumber pair for all accounts.</param> /// <param name="accountIdOrderPair">AccountId-OrderNumber pair for all accounts.</param>
Task UpdateAccountOrdersAsync(Dictionary<Guid, int> accountIdOrderPair); Task UpdateAccountOrdersAsync(Dictionary<Guid, int> accountIdOrderPair);
/// <summary> /// <summary>
/// Returns the account aliases. /// Returns the account aliases.
/// </summary> /// </summary>
/// <param name="accountId">Account id.</param> /// <param name="accountId">Account id.</param>
/// <returns>A list of MailAccountAlias that has e-mail aliases.</returns> /// <returns>A list of MailAccountAlias that has e-mail aliases.</returns>
Task<List<MailAccountAlias>> GetAccountAliasesAsync(Guid accountId); Task<List<MailAccountAlias>> GetAccountAliasesAsync(Guid accountId);
/// <summary> /// <summary>
/// Updated account's aliases. /// Updated account's aliases.
/// </summary> /// </summary>
/// <param name="accountId">Account id to update aliases for.</param> /// <param name="accountId">Account id to update aliases for.</param>
/// <param name="aliases">Full list of updated aliases.</param> /// <param name="aliases">Full list of updated aliases.</param>
/// <returns></returns> /// <returns></returns>
Task UpdateAccountAliasesAsync(Guid accountId, List<MailAccountAlias> aliases); Task UpdateAccountAliasesAsync(Guid accountId, List<MailAccountAlias> aliases);
/// <summary> /// <summary>
/// Delete account alias. /// Delete account alias.
/// </summary> /// </summary>
/// <param name="aliasId">Alias to remove.</param> /// <param name="aliasId">Alias to remove.</param>
Task DeleteAccountAliasAsync(Guid aliasId); Task DeleteAccountAliasAsync(Guid aliasId);
/// <summary> /// <summary>
/// Updated profile information of the account. /// Updated profile information of the account.
/// </summary> /// </summary>
/// <param name="accountId">Account id to update info for.</param> /// <param name="accountId">Account id to update info for.</param>
/// <param name="profileInformation">Info data.</param> /// <param name="profileInformation">Info data.</param>
/// <returns></returns> /// <returns></returns>
Task UpdateProfileInformationAsync(Guid accountId, ProfileInformation profileInformation); Task UpdateProfileInformationAsync(Guid accountId, ProfileInformation profileInformation);
/// <summary> /// <summary>
/// Creates a root + primary alias for the account. /// Creates a root + primary alias for the account.
/// This is only called when the account is created. /// This is only called when the account is created.
/// </summary> /// </summary>
/// <param name="accountId">Account id.</param> /// <param name="accountId">Account id.</param>
/// <param name="address">Address to create root primary alias from.</param> /// <param name="address">Address to create root primary alias from.</param>
Task CreateRootAliasAsync(Guid accountId, string address); Task CreateRootAliasAsync(Guid accountId, string address);
/// <summary> /// <summary>
/// Will compare local-remote aliases and update the local ones or add/delete new ones. /// Will compare local-remote aliases and update the local ones or add/delete new ones.
/// </summary> /// </summary>
/// <param name="remoteAccountAliases">Remotely fetched basic alias info from synchronizer.</param> /// <param name="remoteAccountAliases">Remotely fetched basic alias info from synchronizer.</param>
/// <param name="account">Account to update remote aliases for..</param> /// <param name="account">Account to update remote aliases for..</param>
Task UpdateRemoteAliasInformationAsync(MailAccount account, List<RemoteAccountAlias> remoteAccountAliases); Task UpdateRemoteAliasInformationAsync(MailAccount account, List<RemoteAccountAlias> remoteAccountAliases);
/// <summary> /// <summary>
/// Gets the primary account alias for the given account id. /// Gets the primary account alias for the given account id.
/// Used when creating draft messages. /// Used when creating draft messages.
/// </summary> /// </summary>
/// <param name="accountId">Account id.</param> /// <param name="accountId">Account id.</param>
/// <returns>Primary alias for the account.</returns> /// <returns>Primary alias for the account.</returns>
Task<MailAccountAlias> GetPrimaryAccountAliasAsync(Guid accountId); Task<MailAccountAlias> GetPrimaryAccountAliasAsync(Guid accountId);
Task<bool> IsAccountFocusedEnabledAsync(Guid accountId); Task<bool> IsAccountFocusedEnabledAsync(Guid accountId);
}
} }

Some files were not shown because too many files have changed in this diff Show More