Revert "File scoped namespaces"

This reverts commit d31d8f574e.
This commit is contained in:
Burak Kaan Köse
2025-02-16 11:43:30 +01:00
parent d31d8f574e
commit cf9869b71e
617 changed files with 32097 additions and 31478 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 = file_scoped:error csharp_style_namespace_declarations = block_scoped:silent
# Expression-level preferences # Expression-level preferences
csharp_prefer_simple_default_expression = true:suggestion csharp_prefer_simple_default_expression = true:suggestion
@@ -287,6 +287,4 @@ csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_utf8_string_literals = true:suggestion 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,16 +1,17 @@
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 MailProviderType ProviderType { get; } public abstract class BaseAuthenticator
protected IAuthenticatorConfig AuthenticatorConfig { get; }
protected BaseAuthenticator(IAuthenticatorConfig authenticatorConfig)
{ {
public abstract MailProviderType ProviderType { get; }
protected IAuthenticatorConfig AuthenticatorConfig { get; }
AuthenticatorConfig = authenticatorConfig; protected BaseAuthenticator(IAuthenticatorConfig authenticatorConfig)
{
AuthenticatorConfig = authenticatorConfig;
}
} }
} }

View File

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

View File

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

View File

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

View File

@@ -2,40 +2,41 @@
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>
{ {
/// <summary> public class DayRangeCollection : ObservableRangeCollection<DayRangeRenderModel>
/// Gets the range of dates that are currently displayed in the collection.
/// </summary>
public DateRange DisplayRange
{ {
get /// <summary>
/// Gets the range of dates that are currently displayed in the collection.
/// </summary>
public DateRange DisplayRange
{ {
if (Count == 0) return null; get
{
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)
{ {
var calendarDayModel = dayRange.CalendarDays.FirstOrDefault(x => x.Period.HasInside(calendarItem.Period.Start)); foreach (var dayRange in this)
calendarDayModel?.EventsCollection.AddCalendarItem(calendarItem); {
var calendarDayModel = dayRange.CalendarDays.FirstOrDefault(x => x.Period.HasInside(calendarItem.Period.Start));
calendarDayModel?.EventsCollection.AddCalendarItem(calendarItem);
}
} }
} }
} }

View File

@@ -4,170 +4,171 @@ 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>
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class. /// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
/// </summary> /// </summary>
public ObservableRangeCollection() /// <typeparam name="T"></typeparam>
: base() public class ObservableRangeCollection<T> : ObservableCollection<T>
{ {
}
/// <summary> /// <summary>
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection. /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class.
/// </summary> /// </summary>
/// <param name="collection">collection: The collection from which the elements are copied.</param> public ObservableRangeCollection()
/// <exception cref="ArgumentNullException">The collection parameter cannot be null.</exception> : base()
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); /// <summary>
/// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection.
RaiseChangeNotificationEvents( /// </summary>
action: NotifyCollectionChangedAction.Add, /// <param name="collection">collection: The collection from which the elements are copied.</param>
changedItems: changedItems, /// <exception cref="ArgumentNullException">The collection parameter cannot be null.</exception>
startingIndex: startIndex); public ObservableRangeCollection(IEnumerable<T> collection)
} : base(collection)
/// <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) /// <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); RaiseChangeNotificationEvents(action: NotifyCollectionChangedAction.Reset);
return; 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--;
} }
var changedItems = collection is List<T> ? (List<T>)collection : new List<T>(collection);
RaiseChangeNotificationEvents(
action: NotifyCollectionChangedAction.Add,
changedItems: changedItems,
startingIndex: startIndex);
} }
if (changedItems.Count == 0) /// <summary>
return; /// 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>
RaiseChangeNotificationEvents( public void RemoveRange(IEnumerable<T> collection, NotifyCollectionChangedAction notificationMode = NotifyCollectionChangedAction.Reset)
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); if (notificationMode != NotifyCollectionChangedAction.Remove && notificationMode != NotifyCollectionChangedAction.Reset)
itemAdded = true; 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);
} }
return itemAdded;
}
private void RaiseChangeNotificationEvents(NotifyCollectionChangedAction action, List<T> changedItems = null, int startingIndex = -1) /// <summary>
{ /// Clears the current collection and replaces it with the specified item.
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Count))); /// </summary>
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); public void Replace(T item) => ReplaceRange(new T[] { item });
if (changedItems is null) /// <summary>
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action)); /// Clears the current collection and replaces it with the specified collection.
else /// </summary>
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, changedItems: changedItems, startingIndex: startingIndex)); 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,22 +1,23 @@
namespace Wino.Core.Domain; namespace Wino.Core.Domain
public static class Constants
{ {
/// <summary> public static class Constants
/// MIME header that exists in all the drafts created from Wino. {
/// </summary> /// <summary>
public const string WinoLocalDraftHeader = "X-Wino-Draft-Id"; /// MIME header that exists in all the drafts created from Wino.
public const string LocalDraftStartPrefix = "localDraft_"; /// </summary>
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,23 +2,24 @@
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 class AccountCalendar : IAccountCalendar
public Guid Id { get; set; } {
public Guid AccountId { get; set; } [PrimaryKey]
public string RemoteCalendarId { get; set; } public Guid Id { get; set; }
public string SynchronizationDeltaToken { get; set; } public Guid AccountId { get; set; }
public string Name { get; set; } public string RemoteCalendarId { get; set; }
public bool IsPrimary { get; set; } public string SynchronizationDeltaToken { get; set; }
public bool IsExtended { get; set; } = true; public string Name { get; set; }
public bool IsPrimary { get; set; }
public bool IsExtended { get; set; } = true;
/// <summary> /// <summary>
/// Unused for now. /// Unused for now.
/// </summary> /// </summary>
public string TextColorHex { get; set; } public string TextColorHex { get; set; }
public string BackgroundColorHex { get; set; } public string BackgroundColorHex { get; set; }
public string TimeZone { get; set; } public string TimeZone { get; set; }
}
} }

View File

@@ -2,18 +2,19 @@
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
{ {
[PrimaryKey] // TODO: Connect to Contact store with Wino People.
public Guid Id { get; set; } public class CalendarEventAttendee
public Guid CalendarItemId { get; set; } {
public string Name { get; set; } [PrimaryKey]
public string Email { get; set; } public Guid Id { get; set; }
public AttendeeStatus AttendenceStatus { get; set; } public Guid CalendarItemId { get; set; }
public bool IsOrganizer { get; set; } public string Name { get; set; }
public bool IsOptionalAttendee { get; set; } public string Email { get; set; }
public string Comment { get; set; } public AttendeeStatus AttendenceStatus { get; set; }
public bool IsOrganizer { get; set; }
public bool IsOptionalAttendee { get; set; }
public string Comment { get; set; }
}
} }

View File

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

View File

@@ -2,14 +2,15 @@
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 class Reminder
public Guid Id { get; set; } {
public Guid CalendarItemId { get; set; } [PrimaryKey]
public Guid Id { get; set; }
public Guid CalendarItemId { get; set; }
public DateTimeOffset ReminderTime { get; set; } public DateTimeOffset ReminderTime { get; set; }
public CalendarItemReminderType ReminderType { get; set; } public CalendarItemReminderType ReminderType { get; set; }
}
} }

View File

@@ -1,16 +1,17 @@
using System; using System;
using SQLite; using SQLite;
namespace Wino.Core.Domain.Entities.Mail; namespace Wino.Core.Domain.Entities.Mail
public class AccountSignature
{ {
[PrimaryKey] public class AccountSignature
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,62 +1,63 @@
using System; using System;
using SQLite; using SQLite;
namespace Wino.Core.Domain.Entities.Mail; namespace Wino.Core.Domain.Entities.Mail
public class RemoteAccountAlias
{ {
/// <summary> public class RemoteAccountAlias
/// Display address of the alias. {
/// </summary> /// <summary>
public string AliasAddress { get; set; } /// Display address of the alias.
/// </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,152 +5,153 @@ 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>
/// Unique Id of the mail. /// Summary of the parsed MIME messages.
/// Wino will do non-network operations on this table and others from the original MIME.
/// </summary> /// </summary>
[PrimaryKey] public class MailCopy : IMailItem
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,70 +5,71 @@ 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
{ {
[PrimaryKey] [DebuggerDisplay("{FolderName} - {SpecialFolderType}")]
public Guid Id { get; set; } public class MailItemFolder : IMailItemFolder
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)
{ {
if (SpecialFolderType == type) [PrimaryKey]
return true; public Guid Id { get; set; }
foreach (var child in ChildFolders) 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)
{ {
if (child.SpecialFolderType == type) if (SpecialFolderType == type)
{
return true; return true;
}
else foreach (var child in ChildFolders)
{ {
return child.ContainsSpecialFolderType(type); if (child.SpecialFolderType == type)
{
return true;
}
else
{
return child.ContainsSpecialFolderType(type);
}
} }
return false;
} }
return false; 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;
} }
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,12 +1,13 @@
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 class MergedInbox
public Guid Id { get; set; } {
[PrimaryKey]
public Guid Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
}
} }

View File

@@ -2,75 +2,76 @@
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>
/// E-mail address of the contact. /// Back storage for simple name-address book.
/// These values will be inserted during MIME fetch.
/// </summary> /// </summary>
[PrimaryKey]
public string Address { get; set; }
/// <summary> // TODO: This can easily evolve to Contact store, just like People app in Windows 10/11.
/// Display name of the contact. // Do it.
/// </summary> public class AccountContact : IEquatable<AccountContact>
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)
{ {
return Equals(obj as AccountContact); /// <summary>
} /// E-mail address of the contact.
/// </summary>
[PrimaryKey]
public string Address { get; set; }
public bool Equals(AccountContact other) /// <summary>
{ /// Display name of the contact.
return other is not null && /// </summary>
Address == other.Address && public string Name { get; set; }
Name == other.Name;
}
public override int GetHashCode() /// <summary>
{ /// Base64 encoded profile image of the contact.
int hashCode = -1717786383; /// </summary>
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Address); public string Base64ContactPicture { get; set; }
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Name);
return hashCode;
}
public static bool operator ==(AccountContact left, AccountContact right) /// <summary>
{ /// All registered accounts have their contacts registered as root.
return EqualityComparer<AccountContact>.Default.Equals(left, right); /// Root contacts must not be overridden by any configuration.
} /// They are created on account creation.
/// </summary>
public bool IsRootContact { get; set; }
public static bool operator !=(AccountContact left, AccountContact right) /// <summary>
{ /// Short display name of the contact.
return !(left == right); /// 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)
{
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,51 +2,52 @@
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
{ {
[PrimaryKey] public class CustomServerInformation
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,108 +3,109 @@ 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
{ {
[PrimaryKey] public class MailAccount
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,53 +1,54 @@
using System; using System;
using SQLite; using SQLite;
namespace Wino.Core.Domain.Entities.Shared; namespace Wino.Core.Domain.Entities.Shared
public class MailAccountPreferences
{ {
[PrimaryKey] public class MailAccountPreferences
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,8 +1,9 @@
namespace Wino.Core.Domain.Enums; namespace Wino.Core.Domain.Enums
public enum AccountAttentionReason
{ {
None, public enum AccountAttentionReason
InvalidCredentials, {
MissingSystemFolderConfiguration None,
InvalidCredentials,
MissingSystemFolderConfiguration
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,8 @@
namespace Wino.Core.Domain.Enums; namespace Wino.Core.Domain.Enums
public enum CalendarEventTargetType
{ {
Single, // Show details for a single event. public enum CalendarEventTargetType
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,10 +1,11 @@
namespace Wino.Core.Domain.Enums; namespace Wino.Core.Domain.Enums
/// <summary>
/// Trigger to load more data.
/// </summary>
public enum CalendarInitInitiative
{ {
User, /// <summary>
App /// Trigger to load more data.
/// </summary>
public enum CalendarInitInitiative
{
User,
App
}
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,12 @@
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
{ {
Replace, /// <summary>
Previous, /// Which way in time to load more data for calendar.
Next /// </summary>
public enum CalendarLoadDirection
{
Replace,
Previous,
Next
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,12 @@
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
{ {
Move, /// <summary>
SpecialFolder, /// Defines the potential reasons for picking folder in the folder picking dialog.
Any /// </summary>
public enum PickFolderReason
{
Move,
SpecialFolder,
Any
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,12 @@
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
{ {
Client, /// <summary>
Server /// Enumeration for the source of synchronization.
/// Right now it can either be from the client or the server.
/// </summary>
public enum SynchronizationSource
{
Client,
Server
}
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,18 +1,19 @@
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) /// <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
{ {
Account = account; public AuthenticationAttentionException(MailAccount account)
} {
Account = account;
}
public MailAccount Account { get; } public MailAccount Account { get; }
}
} }

View File

@@ -1,17 +1,18 @@
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) /// <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) public AuthenticationException(string message, Exception innerException) : base(message, innerException)
{ {
}
} }
} }

View File

@@ -1,8 +1,9 @@
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,10 +1,11 @@
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,10 +1,11 @@
using System; using System;
namespace Wino.Core.Domain.Exceptions; namespace Wino.Core.Domain.Exceptions
public class CustomThemeCreationFailedException : Exception
{ {
public CustomThemeCreationFailedException(string message) : base(message) public class CustomThemeCreationFailedException : Exception
{ {
public CustomThemeCreationFailedException(string message) : base(message)
{
}
} }
} }

View File

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

View File

@@ -1,13 +1,14 @@
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) public class ImapClientPoolException : Exception
{ {
ProtocolLog = protocolLog; public ImapClientPoolException(Exception innerException, string protocolLog) : base(Translator.Exception_ImapClientPoolFailed, innerException)
} {
ProtocolLog = protocolLog;
}
public string ProtocolLog { get; } public string ProtocolLog { get; }
}
} }

View File

@@ -1,17 +1,18 @@
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) public class ImapConnectionFailedPackage
{ {
ErrorMessage = errorMessage; public ImapConnectionFailedPackage(string errorMessage, string protocolLog, AutoDiscoverySettings settings)
ProtocolLog = protocolLog; {
Settings = settings; ErrorMessage = errorMessage;
} ProtocolLog = protocolLog;
Settings = settings;
}
public AutoDiscoverySettings Settings { get; } public AutoDiscoverySettings Settings { get; }
public string ErrorMessage { get; set; } public string ErrorMessage { get; set; }
public string ProtocolLog { get; } public string ProtocolLog { get; }
}
} }

View File

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

View File

@@ -1,16 +1,17 @@
namespace Wino.Core.Domain.Exceptions; namespace Wino.Core.Domain.Exceptions
public class ImapTestSSLCertificateException : System.Exception
{ {
public ImapTestSSLCertificateException(string issuer, string expirationDateString, string validFromDateString) public class ImapTestSSLCertificateException : System.Exception
{ {
Issuer = issuer; public ImapTestSSLCertificateException(string issuer, string expirationDateString, string validFromDateString)
ExpirationDateString = expirationDateString; {
ValidFromDateString = validFromDateString; Issuer = issuer;
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,5 +1,6 @@
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,6 +1,7 @@
namespace Wino.Core.Domain.Exceptions; namespace Wino.Core.Domain.Exceptions
public class MissingAliasException : System.Exception
{ {
public MissingAliasException() : base(Translator.Exception_MissingAlias) { } public class MissingAliasException : System.Exception
{
public MissingAliasException() : base(Translator.Exception_MissingAlias) { }
}
} }

View File

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

View File

@@ -1,14 +1,15 @@
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 class SynchronizerException : Exception
{ {
} public SynchronizerException(string message) : base(message)
{
}
public SynchronizerException(string message, Exception innerException) : base(message, innerException) public SynchronizerException(string message, Exception innerException) : base(message, innerException)
{ {
}
} }
} }

View File

@@ -1,6 +1,7 @@
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,19 +1,20 @@
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) /// <summary>
/// Emitted when special folder is needed for an operation but it couldn't be found.
/// </summary>
public class UnavailableSpecialFolderException : Exception
{ {
SpecialFolderType = specialFolderType; public UnavailableSpecialFolderException(SpecialFolderType specialFolderType, Guid accountId)
AccountId = accountId; {
} SpecialFolderType = specialFolderType;
AccountId = accountId;
}
public SpecialFolderType SpecialFolderType { get; } public SpecialFolderType SpecialFolderType { get; }
public Guid AccountId { get; set; } public Guid AccountId { get; set; }
}
} }

View File

@@ -1,11 +1,12 @@
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
{ {
public WinoServerException(string message) : base(message) { } /// <summary>
/// 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,32 +1,33 @@
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
{ {
/// <summary> public static class DateTimeExtensions
/// 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); /// <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)
{
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,23 +1,24 @@
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) public static class ExceptionExtensions
{ {
if (ex == null) public static IEnumerable<Exception> GetInnerExceptions(this Exception ex)
{ {
throw new ArgumentNullException("ex"); if (ex == null)
} {
throw new ArgumentNullException("ex");
}
var innerException = ex; var innerException = ex;
do do
{ {
yield return innerException; yield return innerException;
innerException = innerException.InnerException; innerException = innerException.InnerException;
}
while (innerException != null);
} }
while (innerException != null);
} }
} }

View File

@@ -1,19 +1,20 @@
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 string GetBase64MimeMessage(this MimeKit.MimeMessage message) public static class MimeExtensions
{ {
using MemoryStream memoryStream = new(); public static string GetBase64MimeMessage(this MimeKit.MimeMessage message)
{
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,15 +1,16 @@
using System; using System;
namespace Wino.Core.Domain.Interfaces; namespace Wino.Core.Domain.Interfaces
public interface IAccountCalendar
{ {
string Name { get; set; } public interface IAccountCalendar
string TextColorHex { get; set; } {
string BackgroundColorHex { get; set; } string Name { get; set; }
bool IsPrimary { get; set; } string TextColorHex { get; set; }
Guid AccountId { get; set; } string BackgroundColorHex { get; set; }
string RemoteCalendarId { get; set; } bool IsPrimary { get; set; }
bool IsExtended { get; set; } Guid AccountId { get; set; }
Guid Id { get; set; } string RemoteCalendarId { get; set; }
bool IsExtended { get; set; }
Guid Id { get; set; }
}
} }

View File

@@ -2,11 +2,12 @@
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
{ {
Task ShowDialogAsync(CancellationTokenSource cancellationTokenSource); public interface IAccountCreationDialog
void Complete(bool cancel); {
AccountCreationDialogState State { get; set; } Task ShowDialogAsync(CancellationTokenSource cancellationTokenSource);
void Complete(bool cancel);
AccountCreationDialogState State { get; set; }
}
} }

View File

@@ -2,20 +2,21 @@
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
{ {
bool IsEnabled { get; set; } public interface IAccountMenuItem : IMenuItem
double SynchronizationProgress { get; set; } {
int UnreadItemCount { get; set; } bool IsEnabled { get; set; }
IEnumerable<MailAccount> HoldingAccounts { get; } double SynchronizationProgress { get; set; }
void UpdateAccount(MailAccount account); int UnreadItemCount { get; set; }
} 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,5 +1,6 @@
namespace Wino.Core.Domain.Interfaces; namespace Wino.Core.Domain.Interfaces
public interface IAccountPickerDialog
{ {
public interface IAccountPickerDialog
{
}
} }

View File

@@ -1,37 +1,38 @@
using System; using System;
namespace Wino.Core.Domain.Interfaces; namespace Wino.Core.Domain.Interfaces
public interface IAccountProviderDetailViewModel
{ {
/// <summary> public interface IAccountProviderDetailViewModel
/// Entity id that will help to identify the startup entity on launch. {
/// </summary> /// <summary>
Guid StartupEntityId { get; } /// Entity id that will help to identify the startup entity on launch.
/// </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,10 +1,11 @@
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
{ {
MailAccount Account { get; set; } public interface IAccountProviderDetails
bool AutoExtend { get; set; } {
IProviderDetail ProviderDetail { get; set; } MailAccount Account { get; set; }
bool AutoExtend { get; set; }
IProviderDetail ProviderDetail { get; set; }
}
} }

View File

@@ -5,155 +5,156 @@ 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
{ {
/// <summary> public interface IAccountService
/// Current IAuthenticator that should receive external authentication process to continue with. {
/// For example: Google auth will launch a browser authentication. After it completes, this is the IAuthenticator /// <summary>
/// to continue process for token exchange. /// 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
IAuthenticator ExternalAuthenticationAuthenticator { get; set; } /// to continue process for token exchange.
/// </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