file scoped namespaces (#565)
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Accounts
|
||||
{
|
||||
public record AccountCreationDialogResult(MailProviderType ProviderType, string AccountName, SpecialImapProviderDetails SpecialImapProviderDetails);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Accounts;
|
||||
|
||||
public record AccountCreationDialogResult(MailProviderType ProviderType, string AccountName, SpecialImapProviderDetails SpecialImapProviderDetails);
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Accounts
|
||||
namespace Wino.Core.Domain.Models.Accounts;
|
||||
|
||||
public class ImapAuthenticationMethodModel(ImapAuthenticationMethod imapAuthenticationMethod, string displayName)
|
||||
{
|
||||
public class ImapAuthenticationMethodModel(ImapAuthenticationMethod imapAuthenticationMethod, string displayName)
|
||||
{
|
||||
public ImapAuthenticationMethod ImapAuthenticationMethod { get; } = imapAuthenticationMethod;
|
||||
public string DisplayName { get; } = displayName;
|
||||
}
|
||||
public ImapAuthenticationMethod ImapAuthenticationMethod { get; } = imapAuthenticationMethod;
|
||||
public string DisplayName { get; } = displayName;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Accounts
|
||||
namespace Wino.Core.Domain.Models.Accounts;
|
||||
|
||||
public class ImapConnectionSecurityModel(ImapConnectionSecurity imapConnectionSecurity, string displayName)
|
||||
{
|
||||
public class ImapConnectionSecurityModel(ImapConnectionSecurity imapConnectionSecurity, string displayName)
|
||||
{
|
||||
public ImapConnectionSecurity ImapConnectionSecurity { get; } = imapConnectionSecurity;
|
||||
public string DisplayName { get; } = displayName;
|
||||
}
|
||||
public ImapConnectionSecurity ImapConnectionSecurity { get; } = imapConnectionSecurity;
|
||||
public string DisplayName { get; } = displayName;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
namespace Wino.Core.Domain.Models.Accounts
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates the profile information of an account.
|
||||
/// </summary>
|
||||
/// <param name="SenderName">Display sender name for the account.</param>
|
||||
/// <param name="Base64ProfilePictureData">Base 64 encoded profile picture data of the account. Thumbnail size.</param>
|
||||
/// <param name="AccountAddress">Address of the profile.</param>
|
||||
public record ProfileInformation(string SenderName, string Base64ProfilePictureData, string AccountAddress);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Accounts;
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates the profile information of an account.
|
||||
/// </summary>
|
||||
/// <param name="SenderName">Display sender name for the account.</param>
|
||||
/// <param name="Base64ProfilePictureData">Base 64 encoded profile picture data of the account. Thumbnail size.</param>
|
||||
/// <param name="AccountAddress">Address of the profile.</param>
|
||||
public record ProfileInformation(string SenderName, string Base64ProfilePictureData, string AccountAddress);
|
||||
|
||||
@@ -1,69 +1,68 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Accounts
|
||||
namespace Wino.Core.Domain.Models.Accounts;
|
||||
|
||||
public class ProviderDetail : IProviderDetail
|
||||
{
|
||||
public class ProviderDetail : IProviderDetail
|
||||
public MailProviderType Type { get; }
|
||||
public SpecialImapProvider SpecialImapProvider { get; }
|
||||
public string Name { get; }
|
||||
|
||||
public string Description { get; }
|
||||
|
||||
public string ProviderImage
|
||||
{
|
||||
public MailProviderType Type { get; }
|
||||
public SpecialImapProvider SpecialImapProvider { get; }
|
||||
public string Name { get; }
|
||||
|
||||
public string Description { get; }
|
||||
|
||||
public string ProviderImage
|
||||
get
|
||||
{
|
||||
get
|
||||
if (SpecialImapProvider == SpecialImapProvider.None)
|
||||
{
|
||||
if (SpecialImapProvider == SpecialImapProvider.None)
|
||||
{
|
||||
return $"/Wino.Core.UWP/Assets/Providers/{Type}.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"/Wino.Core.UWP/Assets/Providers/{SpecialImapProvider}.png";
|
||||
}
|
||||
return $"/Wino.Core.UWP/Assets/Providers/{Type}.png";
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSupported => Type == MailProviderType.Outlook || Type == MailProviderType.Gmail || Type == MailProviderType.IMAP4;
|
||||
|
||||
public ProviderDetail(MailProviderType type, SpecialImapProvider specialImapProvider)
|
||||
{
|
||||
Type = type;
|
||||
SpecialImapProvider = specialImapProvider;
|
||||
|
||||
switch (Type)
|
||||
else
|
||||
{
|
||||
case MailProviderType.Outlook:
|
||||
Name = "Outlook";
|
||||
Description = "Outlook.com, Live.com, Hotmail, MSN";
|
||||
break;
|
||||
case MailProviderType.Gmail:
|
||||
Name = "Gmail";
|
||||
Description = Translator.ProviderDetail_Gmail_Description;
|
||||
break;
|
||||
case MailProviderType.IMAP4:
|
||||
switch (specialImapProvider)
|
||||
{
|
||||
case SpecialImapProvider.None:
|
||||
Name = Translator.ProviderDetail_IMAP_Title;
|
||||
Description = Translator.ProviderDetail_IMAP_Description;
|
||||
break;
|
||||
case SpecialImapProvider.iCloud:
|
||||
Name = Translator.ProviderDetail_iCloud_Title;
|
||||
Description = Translator.ProviderDetail_iCloud_Description;
|
||||
break;
|
||||
case SpecialImapProvider.Yahoo:
|
||||
Name = Translator.ProviderDetail_Yahoo_Title;
|
||||
Description = Translator.ProviderDetail_Yahoo_Description;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
return $"/Wino.Core.UWP/Assets/Providers/{SpecialImapProvider}.png";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSupported => Type == MailProviderType.Outlook || Type == MailProviderType.Gmail || Type == MailProviderType.IMAP4;
|
||||
|
||||
public ProviderDetail(MailProviderType type, SpecialImapProvider specialImapProvider)
|
||||
{
|
||||
Type = type;
|
||||
SpecialImapProvider = specialImapProvider;
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case MailProviderType.Outlook:
|
||||
Name = "Outlook";
|
||||
Description = "Outlook.com, Live.com, Hotmail, MSN";
|
||||
break;
|
||||
case MailProviderType.Gmail:
|
||||
Name = "Gmail";
|
||||
Description = Translator.ProviderDetail_Gmail_Description;
|
||||
break;
|
||||
case MailProviderType.IMAP4:
|
||||
switch (specialImapProvider)
|
||||
{
|
||||
case SpecialImapProvider.None:
|
||||
Name = Translator.ProviderDetail_IMAP_Title;
|
||||
Description = Translator.ProviderDetail_IMAP_Description;
|
||||
break;
|
||||
case SpecialImapProvider.iCloud:
|
||||
Name = Translator.ProviderDetail_iCloud_Title;
|
||||
Description = Translator.ProviderDetail_iCloud_Description;
|
||||
break;
|
||||
case SpecialImapProvider.Yahoo:
|
||||
Name = Translator.ProviderDetail_Yahoo_Title;
|
||||
Description = Translator.ProviderDetail_Yahoo_Description;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Accounts
|
||||
{
|
||||
public record SpecialImapProviderDetails(string Address, string Password, string SenderName, SpecialImapProvider SpecialImapProvider);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Accounts;
|
||||
|
||||
public record SpecialImapProviderDetails(string Address, string Password, string SenderName, SpecialImapProvider SpecialImapProvider);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using System;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Accounts
|
||||
namespace Wino.Core.Domain.Models.Accounts;
|
||||
|
||||
public class UnreadItemCountResult
|
||||
{
|
||||
public class UnreadItemCountResult
|
||||
{
|
||||
public Guid FolderId { get; set; }
|
||||
public Guid AccountId { get; set; }
|
||||
public SpecialFolderType SpecialFolderType { get; set; }
|
||||
public int UnreadItemCount { get; set; }
|
||||
}
|
||||
public Guid FolderId { get; set; }
|
||||
public Guid AccountId { get; set; }
|
||||
public SpecialFolderType SpecialFolderType { get; set; }
|
||||
public int UnreadItemCount { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
namespace Wino.Core.Domain.Models.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Previously known as TokenInformation.
|
||||
/// We used to store this model in the database.
|
||||
/// Now we store it in the memory.
|
||||
/// </summary>
|
||||
/// <param name="AccessToken">Access token/</param>
|
||||
/// <param name="AccountAddress">Address of the authenticated user.</param>
|
||||
public record TokenInformationEx(string AccessToken, string AccountAddress);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Authentication;
|
||||
|
||||
/// <summary>
|
||||
/// Previously known as TokenInformation.
|
||||
/// We used to store this model in the database.
|
||||
/// Now we store it in the memory.
|
||||
/// </summary>
|
||||
/// <param name="AccessToken">Access token/</param>
|
||||
/// <param name="AccountAddress">Address of the authenticated user.</param>
|
||||
public record TokenInformationEx(string AccessToken, string AccountAddress);
|
||||
|
||||
@@ -3,70 +3,69 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Wino.Core.Domain.Exceptions;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Authorization
|
||||
namespace Wino.Core.Domain.Models.Authorization;
|
||||
|
||||
public class GoogleAuthorizationRequest
|
||||
{
|
||||
public class GoogleAuthorizationRequest
|
||||
public const string RedirectUri = "google.pw.oauth2:/oauth2redirect";
|
||||
|
||||
const string authorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth";
|
||||
const string CodeChallangeMethod = "S256";
|
||||
|
||||
public GoogleAuthorizationRequest(string state, string codeVerifier, string codeChallange)
|
||||
{
|
||||
public const string RedirectUri = "google.pw.oauth2:/oauth2redirect";
|
||||
State = state;
|
||||
CodeVerifier = codeVerifier;
|
||||
CodeChallange = codeChallange;
|
||||
}
|
||||
|
||||
const string authorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth";
|
||||
const string CodeChallangeMethod = "S256";
|
||||
// Pre
|
||||
public string State { get; set; }
|
||||
public string CodeVerifier { get; set; }
|
||||
public string CodeChallange { get; set; }
|
||||
public string ClientId { get; set; }
|
||||
|
||||
public GoogleAuthorizationRequest(string state, string codeVerifier, string codeChallange)
|
||||
{
|
||||
State = state;
|
||||
CodeVerifier = codeVerifier;
|
||||
CodeChallange = codeChallange;
|
||||
}
|
||||
// Post
|
||||
public string AuthorizationCode { get; set; }
|
||||
|
||||
// Pre
|
||||
public string State { get; set; }
|
||||
public string CodeVerifier { get; set; }
|
||||
public string CodeChallange { get; set; }
|
||||
public string ClientId { get; set; }
|
||||
public string BuildRequest(string clientId)
|
||||
{
|
||||
ClientId = clientId;
|
||||
|
||||
// Post
|
||||
public string AuthorizationCode { get; set; }
|
||||
// Creates the OAuth 2.0 authorization request.
|
||||
return string.Format("{0}?response_type=code&scope=https://mail.google.com/ https://www.googleapis.com/auth/gmail.labels https://www.googleapis.com/auth/userinfo.profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
|
||||
authorizationEndpoint,
|
||||
Uri.EscapeDataString(RedirectUri),
|
||||
ClientId,
|
||||
State,
|
||||
CodeChallange,
|
||||
CodeChallangeMethod);
|
||||
}
|
||||
|
||||
public string BuildRequest(string clientId)
|
||||
{
|
||||
ClientId = clientId;
|
||||
public void ValidateAuthorizationCode(Uri callbackUri)
|
||||
{
|
||||
if (callbackUri == null)
|
||||
throw new GoogleAuthenticationException(Translator.Exception_GoogleAuthCallbackNull);
|
||||
|
||||
// Creates the OAuth 2.0 authorization request.
|
||||
return string.Format("{0}?response_type=code&scope=https://mail.google.com/ https://www.googleapis.com/auth/gmail.labels https://www.googleapis.com/auth/userinfo.profile&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
|
||||
authorizationEndpoint,
|
||||
Uri.EscapeDataString(RedirectUri),
|
||||
ClientId,
|
||||
State,
|
||||
CodeChallange,
|
||||
CodeChallangeMethod);
|
||||
}
|
||||
string queryString = callbackUri.Query;
|
||||
|
||||
public void ValidateAuthorizationCode(Uri callbackUri)
|
||||
{
|
||||
if (callbackUri == null)
|
||||
throw new GoogleAuthenticationException(Translator.Exception_GoogleAuthCallbackNull);
|
||||
Dictionary<string, string> queryStringParams = queryString.Substring(1).Split('&').ToDictionary(c => c.Split('=')[0], c => Uri.UnescapeDataString(c.Split('=')[1]));
|
||||
|
||||
string queryString = callbackUri.Query;
|
||||
if (queryStringParams.ContainsKey("error"))
|
||||
throw new GoogleAuthenticationException(string.Format(Translator.Exception_GoogleAuthError, queryStringParams["error"]));
|
||||
|
||||
Dictionary<string, string> queryStringParams = queryString.Substring(1).Split('&').ToDictionary(c => c.Split('=')[0], c => Uri.UnescapeDataString(c.Split('=')[1]));
|
||||
if (!queryStringParams.ContainsKey("code") || !queryStringParams.ContainsKey("state"))
|
||||
throw new GoogleAuthenticationException(Translator.Exception_GoogleAuthCorruptedCode + queryString);
|
||||
|
||||
if (queryStringParams.ContainsKey("error"))
|
||||
throw new GoogleAuthenticationException(string.Format(Translator.Exception_GoogleAuthError, queryStringParams["error"]));
|
||||
// Gets the Authorization code & state
|
||||
string code = queryStringParams["code"];
|
||||
string incomingState = queryStringParams["state"];
|
||||
|
||||
if (!queryStringParams.ContainsKey("code") || !queryStringParams.ContainsKey("state"))
|
||||
throw new GoogleAuthenticationException(Translator.Exception_GoogleAuthCorruptedCode + queryString);
|
||||
// Compares the receieved state to the expected value, to ensure that
|
||||
// this app made the request which resulted in authorization
|
||||
if (incomingState != State)
|
||||
throw new GoogleAuthenticationException(string.Format(Translator.Exception_GoogleAuthInvalidResponse, incomingState));
|
||||
|
||||
// Gets the Authorization code & state
|
||||
string code = queryStringParams["code"];
|
||||
string incomingState = queryStringParams["state"];
|
||||
|
||||
// Compares the receieved state to the expected value, to ensure that
|
||||
// this app made the request which resulted in authorization
|
||||
if (incomingState != State)
|
||||
throw new GoogleAuthenticationException(string.Format(Translator.Exception_GoogleAuthInvalidResponse, incomingState));
|
||||
|
||||
AuthorizationCode = code;
|
||||
}
|
||||
AuthorizationCode = code;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
using System;
|
||||
using Wino.Core.Domain.Exceptions;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Authorization
|
||||
namespace Wino.Core.Domain.Models.Authorization;
|
||||
|
||||
public class GoogleTokenizationRequest
|
||||
{
|
||||
public class GoogleTokenizationRequest
|
||||
public GoogleTokenizationRequest(GoogleAuthorizationRequest authorizationRequest)
|
||||
{
|
||||
public GoogleTokenizationRequest(GoogleAuthorizationRequest authorizationRequest)
|
||||
{
|
||||
if (authorizationRequest == null)
|
||||
throw new GoogleAuthenticationException("Authorization request is empty.");
|
||||
if (authorizationRequest == null)
|
||||
throw new GoogleAuthenticationException("Authorization request is empty.");
|
||||
|
||||
AuthorizationRequest = authorizationRequest;
|
||||
AuthorizationRequest = authorizationRequest;
|
||||
|
||||
if (string.IsNullOrEmpty(AuthorizationRequest.AuthorizationCode))
|
||||
throw new GoogleAuthenticationException("Authorization request has empty code.");
|
||||
}
|
||||
if (string.IsNullOrEmpty(AuthorizationRequest.AuthorizationCode))
|
||||
throw new GoogleAuthenticationException("Authorization request has empty code.");
|
||||
}
|
||||
|
||||
public GoogleAuthorizationRequest AuthorizationRequest { get; set; }
|
||||
public GoogleAuthorizationRequest AuthorizationRequest { get; set; }
|
||||
|
||||
public string BuildRequest()
|
||||
{
|
||||
return string.Format("code={0}&redirect_uri={1}&client_id={2}&code_verifier={3}&scope=&grant_type=authorization_code",
|
||||
AuthorizationRequest.AuthorizationCode, Uri.EscapeDataString(GoogleAuthorizationRequest.RedirectUri), AuthorizationRequest.ClientId, AuthorizationRequest.CodeVerifier);
|
||||
}
|
||||
public string BuildRequest()
|
||||
{
|
||||
return string.Format("code={0}&redirect_uri={1}&client_id={2}&code_verifier={3}&scope=&grant_type=authorization_code",
|
||||
AuthorizationRequest.AuthorizationCode, Uri.EscapeDataString(GoogleAuthorizationRequest.RedirectUri), AuthorizationRequest.ClientId, AuthorizationRequest.CodeVerifier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
namespace Wino.Core.Domain.Models.AutoDiscovery
|
||||
namespace Wino.Core.Domain.Models.AutoDiscovery;
|
||||
|
||||
public class AutoDiscoveryMinimalSettings
|
||||
{
|
||||
public class AutoDiscoveryMinimalSettings
|
||||
{
|
||||
public string DisplayName { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
public string DisplayName { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Wino.Core.Domain.Models.AutoDiscovery
|
||||
namespace Wino.Core.Domain.Models.AutoDiscovery;
|
||||
|
||||
public class AutoDiscoveryProviderSetting
|
||||
{
|
||||
public class AutoDiscoveryProviderSetting
|
||||
{
|
||||
[JsonPropertyName("protocol")]
|
||||
public string Protocol { get; set; }
|
||||
[JsonPropertyName("protocol")]
|
||||
public string Protocol { get; set; }
|
||||
|
||||
[JsonPropertyName("address")]
|
||||
public string Address { get; set; }
|
||||
[JsonPropertyName("address")]
|
||||
public string Address { get; set; }
|
||||
|
||||
[JsonPropertyName("port")]
|
||||
public int Port { get; set; }
|
||||
[JsonPropertyName("port")]
|
||||
public int Port { get; set; }
|
||||
|
||||
[JsonPropertyName("secure")]
|
||||
public string Secure { get; set; }
|
||||
[JsonPropertyName("secure")]
|
||||
public string Secure { get; set; }
|
||||
|
||||
[JsonPropertyName("username")]
|
||||
public string Username { get; set; }
|
||||
}
|
||||
[JsonPropertyName("username")]
|
||||
public string Username { get; set; }
|
||||
}
|
||||
|
||||
@@ -3,70 +3,69 @@ using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
namespace Wino.Core.Domain.Models.AutoDiscovery
|
||||
namespace Wino.Core.Domain.Models.AutoDiscovery;
|
||||
|
||||
public class AutoDiscoverySettings
|
||||
{
|
||||
public class AutoDiscoverySettings
|
||||
[JsonPropertyName("domain")]
|
||||
public string Domain { get; set; }
|
||||
|
||||
[JsonPropertyName("password")]
|
||||
public string Password { get; set; }
|
||||
|
||||
[JsonPropertyName("settings")]
|
||||
public List<AutoDiscoveryProviderSetting> Settings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this domain requires additional steps for password like app-specific password or sth.
|
||||
/// </summary>
|
||||
public bool IsPasswordSupportLinkAvailable => !string.IsNullOrEmpty(Password) && Uri.TryCreate(Password, UriKind.Absolute, out _);
|
||||
|
||||
public AutoDiscoveryMinimalSettings UserMinimalSettings { get; set; }
|
||||
|
||||
public CustomServerInformation ToServerInformation()
|
||||
{
|
||||
[JsonPropertyName("domain")]
|
||||
public string Domain { get; set; }
|
||||
var imapSettings = GetImapSettings();
|
||||
var smtpSettings = GetSmptpSettings();
|
||||
|
||||
[JsonPropertyName("password")]
|
||||
public string Password { get; set; }
|
||||
if (imapSettings == null || smtpSettings == null) return null;
|
||||
|
||||
[JsonPropertyName("settings")]
|
||||
public List<AutoDiscoveryProviderSetting> Settings { get; set; }
|
||||
string imapUrl = imapSettings.Address;
|
||||
string smtpUrl = smtpSettings.Address;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this domain requires additional steps for password like app-specific password or sth.
|
||||
/// </summary>
|
||||
public bool IsPasswordSupportLinkAvailable => !string.IsNullOrEmpty(Password) && Uri.TryCreate(Password, UriKind.Absolute, out _);
|
||||
string imapUsername = imapSettings.Username;
|
||||
string smtpUsername = smtpSettings.Username;
|
||||
|
||||
public AutoDiscoveryMinimalSettings UserMinimalSettings { get; set; }
|
||||
int imapPort = imapSettings.Port;
|
||||
int smtpPort = smtpSettings.Port;
|
||||
|
||||
public CustomServerInformation ToServerInformation()
|
||||
var serverInfo = new CustomServerInformation
|
||||
{
|
||||
var imapSettings = GetImapSettings();
|
||||
var smtpSettings = GetSmptpSettings();
|
||||
Id = Guid.NewGuid(),
|
||||
DisplayName = UserMinimalSettings.DisplayName,
|
||||
Address = UserMinimalSettings.Email,
|
||||
IncomingServerPassword = UserMinimalSettings.Password,
|
||||
OutgoingServerPassword = UserMinimalSettings.Password,
|
||||
IncomingAuthenticationMethod = Enums.ImapAuthenticationMethod.Auto,
|
||||
OutgoingAuthenticationMethod = Enums.ImapAuthenticationMethod.Auto,
|
||||
OutgoingServerSocketOption = Enums.ImapConnectionSecurity.Auto,
|
||||
IncomingServerSocketOption = Enums.ImapConnectionSecurity.Auto,
|
||||
IncomingServer = imapUrl,
|
||||
OutgoingServer = smtpUrl,
|
||||
IncomingServerPort = imapPort.ToString(),
|
||||
OutgoingServerPort = smtpPort.ToString(),
|
||||
IncomingServerType = Enums.CustomIncomingServerType.IMAP4,
|
||||
IncomingServerUsername = imapUsername,
|
||||
OutgoingServerUsername = smtpUsername,
|
||||
MaxConcurrentClients = 5
|
||||
};
|
||||
|
||||
if (imapSettings == null || smtpSettings == null) return null;
|
||||
|
||||
string imapUrl = imapSettings.Address;
|
||||
string smtpUrl = smtpSettings.Address;
|
||||
|
||||
string imapUsername = imapSettings.Username;
|
||||
string smtpUsername = smtpSettings.Username;
|
||||
|
||||
int imapPort = imapSettings.Port;
|
||||
int smtpPort = smtpSettings.Port;
|
||||
|
||||
var serverInfo = new CustomServerInformation
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
DisplayName = UserMinimalSettings.DisplayName,
|
||||
Address = UserMinimalSettings.Email,
|
||||
IncomingServerPassword = UserMinimalSettings.Password,
|
||||
OutgoingServerPassword = UserMinimalSettings.Password,
|
||||
IncomingAuthenticationMethod = Enums.ImapAuthenticationMethod.Auto,
|
||||
OutgoingAuthenticationMethod = Enums.ImapAuthenticationMethod.Auto,
|
||||
OutgoingServerSocketOption = Enums.ImapConnectionSecurity.Auto,
|
||||
IncomingServerSocketOption = Enums.ImapConnectionSecurity.Auto,
|
||||
IncomingServer = imapUrl,
|
||||
OutgoingServer = smtpUrl,
|
||||
IncomingServerPort = imapPort.ToString(),
|
||||
OutgoingServerPort = smtpPort.ToString(),
|
||||
IncomingServerType = Enums.CustomIncomingServerType.IMAP4,
|
||||
IncomingServerUsername = imapUsername,
|
||||
OutgoingServerUsername = smtpUsername,
|
||||
MaxConcurrentClients = 5
|
||||
};
|
||||
|
||||
return serverInfo;
|
||||
}
|
||||
|
||||
public AutoDiscoveryProviderSetting GetImapSettings()
|
||||
=> Settings?.Find(a => a.Protocol == "IMAP");
|
||||
|
||||
public AutoDiscoveryProviderSetting GetSmptpSettings()
|
||||
=> Settings?.Find(a => a.Protocol == "SMTP");
|
||||
return serverInfo;
|
||||
}
|
||||
|
||||
public AutoDiscoveryProviderSetting GetImapSettings()
|
||||
=> Settings?.Find(a => a.Protocol == "IMAP");
|
||||
|
||||
public AutoDiscoveryProviderSetting GetSmptpSettings()
|
||||
=> Settings?.Find(a => a.Protocol == "SMTP");
|
||||
}
|
||||
|
||||
@@ -2,26 +2,25 @@
|
||||
using Itenso.TimePeriod;
|
||||
using Wino.Core.Domain.Collections;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a day in the calendar.
|
||||
/// Can hold events, appointments, wheather status etc.
|
||||
/// </summary>
|
||||
public class CalendarDayModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a day in the calendar.
|
||||
/// Can hold events, appointments, wheather status etc.
|
||||
/// </summary>
|
||||
public class CalendarDayModel
|
||||
public ITimePeriod Period { get; }
|
||||
public CalendarEventCollection EventsCollection { get; }
|
||||
|
||||
public CalendarDayModel(DateTime representingDate, CalendarRenderOptions calendarRenderOptions)
|
||||
{
|
||||
public ITimePeriod Period { get; }
|
||||
public CalendarEventCollection EventsCollection { get; }
|
||||
|
||||
public CalendarDayModel(DateTime representingDate, CalendarRenderOptions calendarRenderOptions)
|
||||
{
|
||||
RepresentingDate = representingDate;
|
||||
Period = new TimeRange(representingDate, representingDate.AddDays(1));
|
||||
CalendarRenderOptions = calendarRenderOptions;
|
||||
EventsCollection = new CalendarEventCollection(Period, calendarRenderOptions.CalendarSettings);
|
||||
}
|
||||
|
||||
public DateTime RepresentingDate { get; }
|
||||
public CalendarRenderOptions CalendarRenderOptions { get; }
|
||||
RepresentingDate = representingDate;
|
||||
Period = new TimeRange(representingDate, representingDate.AddDays(1));
|
||||
CalendarRenderOptions = calendarRenderOptions;
|
||||
EventsCollection = new CalendarEventCollection(Period, calendarRenderOptions.CalendarSettings);
|
||||
}
|
||||
|
||||
public DateTime RepresentingDate { get; }
|
||||
public CalendarRenderOptions CalendarRenderOptions { get; }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Wino.Core.Domain.Entities.Calendar;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar
|
||||
{
|
||||
public record CalendarItemTarget(CalendarItem Item, CalendarEventTargetType TargetType);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
public record CalendarItemTarget(CalendarItem Item, CalendarEventTargetType TargetType);
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar
|
||||
{
|
||||
public class CalendarPageNavigationArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// When the app launches, automatically request the default calendar navigation options.
|
||||
/// </summary>
|
||||
public bool RequestDefaultNavigation { get; set; }
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
/// <summary>
|
||||
/// Display the calendar view for the specified date.
|
||||
/// </summary>
|
||||
public DateTime NavigationDate { get; set; }
|
||||
}
|
||||
public class CalendarPageNavigationArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// When the app launches, automatically request the default calendar navigation options.
|
||||
/// </summary>
|
||||
public bool RequestDefaultNavigation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Display the calendar view for the specified date.
|
||||
/// </summary>
|
||||
public DateTime NavigationDate { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
namespace Wino.Core.Domain.Models.Calendar
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
public class CalendarRenderOptions
|
||||
{
|
||||
public class CalendarRenderOptions
|
||||
public CalendarRenderOptions(DateRange dateRange, CalendarSettings calendarSettings)
|
||||
{
|
||||
public CalendarRenderOptions(DateRange dateRange, CalendarSettings calendarSettings)
|
||||
{
|
||||
DateRange = dateRange;
|
||||
CalendarSettings = calendarSettings;
|
||||
}
|
||||
public int TotalDayCount => DateRange.TotalDays;
|
||||
public DateRange DateRange { get; }
|
||||
public CalendarSettings CalendarSettings { get; }
|
||||
DateRange = dateRange;
|
||||
CalendarSettings = calendarSettings;
|
||||
}
|
||||
public int TotalDayCount => DateRange.TotalDays;
|
||||
public DateRange DateRange { get; }
|
||||
public CalendarSettings CalendarSettings { get; }
|
||||
}
|
||||
|
||||
@@ -3,46 +3,45 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
public record CalendarSettings(DayOfWeek FirstDayOfWeek,
|
||||
List<DayOfWeek> WorkingDays,
|
||||
TimeSpan WorkingHourStart,
|
||||
TimeSpan WorkingHourEnd,
|
||||
double HourHeight,
|
||||
DayHeaderDisplayType DayHeaderDisplayType,
|
||||
CultureInfo CultureInfo)
|
||||
{
|
||||
public record CalendarSettings(DayOfWeek FirstDayOfWeek,
|
||||
List<DayOfWeek> WorkingDays,
|
||||
TimeSpan WorkingHourStart,
|
||||
TimeSpan WorkingHourEnd,
|
||||
double HourHeight,
|
||||
DayHeaderDisplayType DayHeaderDisplayType,
|
||||
CultureInfo CultureInfo)
|
||||
public TimeSpan? GetTimeSpan(string selectedTime)
|
||||
{
|
||||
public TimeSpan? GetTimeSpan(string selectedTime)
|
||||
{
|
||||
// Regardless of the format, we need to parse the time to a TimeSpan.
|
||||
// User may list as 14:00 but enters 2:00 PM by input.
|
||||
// Be flexible, not annoying.
|
||||
// Regardless of the format, we need to parse the time to a TimeSpan.
|
||||
// User may list as 14:00 but enters 2:00 PM by input.
|
||||
// Be flexible, not annoying.
|
||||
|
||||
if (DateTime.TryParse(selectedTime, out DateTime parsedTime))
|
||||
{
|
||||
return parsedTime.TimeOfDay;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (DateTime.TryParse(selectedTime, out DateTime parsedTime))
|
||||
{
|
||||
return parsedTime.TimeOfDay;
|
||||
}
|
||||
|
||||
public string GetTimeString(TimeSpan timeSpan)
|
||||
else
|
||||
{
|
||||
// Here we don't need to be flexible cuz we're saving back the value to the combos.
|
||||
// They are populated based on the format and must be returned with the format.
|
||||
|
||||
var format = DayHeaderDisplayType switch
|
||||
{
|
||||
DayHeaderDisplayType.TwelveHour => "h:mm tt",
|
||||
DayHeaderDisplayType.TwentyFourHour => "HH:mm",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(DayHeaderDisplayType))
|
||||
};
|
||||
|
||||
var dateTime = DateTime.Today.Add(timeSpan);
|
||||
return dateTime.ToString(format, CultureInfo.InvariantCulture);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetTimeString(TimeSpan timeSpan)
|
||||
{
|
||||
// Here we don't need to be flexible cuz we're saving back the value to the combos.
|
||||
// They are populated based on the format and must be returned with the format.
|
||||
|
||||
var format = DayHeaderDisplayType switch
|
||||
{
|
||||
DayHeaderDisplayType.TwelveHour => "h:mm tt",
|
||||
DayHeaderDisplayType.TwentyFourHour => "HH:mm",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(DayHeaderDisplayType))
|
||||
};
|
||||
|
||||
var dateTime = DateTime.Today.Add(timeSpan);
|
||||
return dateTime.ToString(format, CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,37 +2,36 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies
|
||||
namespace Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies;
|
||||
|
||||
public abstract class BaseCalendarTypeDrawingStrategy
|
||||
{
|
||||
public abstract class BaseCalendarTypeDrawingStrategy
|
||||
public CalendarSettings Settings { get; }
|
||||
public CalendarDisplayType HandlingType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Day range of the pre-rendered items.
|
||||
/// </summary>
|
||||
public abstract DateRange GetRenderDateRange(DateTime DisplayDate, int DayDisplayCount);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the previous date range for rendering.
|
||||
/// For example, 1 week view with 7 days will return -7 <> 0 days.
|
||||
/// </summary>
|
||||
/// <param name="CurrentDateRange">Current displayed date range.</param>
|
||||
/// <param name="DayDisplayCount">Day display count/</param>
|
||||
public abstract DateRange GetPreviousDateRange(DateRange CurrentDateRange, int DayDisplayCount);
|
||||
|
||||
public abstract DateRange GetNextDateRange(DateRange CurrentDateRange, int DayDisplayCount);
|
||||
|
||||
/// <summary>
|
||||
/// How many items should be placed in 1 FlipViewItem.
|
||||
/// </summary>
|
||||
public abstract int GetRenderDayCount(DateTime DisplayDate, int DayDisplayCount);
|
||||
|
||||
protected BaseCalendarTypeDrawingStrategy(CalendarSettings settings, CalendarDisplayType handlingType)
|
||||
{
|
||||
public CalendarSettings Settings { get; }
|
||||
public CalendarDisplayType HandlingType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Day range of the pre-rendered items.
|
||||
/// </summary>
|
||||
public abstract DateRange GetRenderDateRange(DateTime DisplayDate, int DayDisplayCount);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the previous date range for rendering.
|
||||
/// For example, 1 week view with 7 days will return -7 <> 0 days.
|
||||
/// </summary>
|
||||
/// <param name="CurrentDateRange">Current displayed date range.</param>
|
||||
/// <param name="DayDisplayCount">Day display count/</param>
|
||||
public abstract DateRange GetPreviousDateRange(DateRange CurrentDateRange, int DayDisplayCount);
|
||||
|
||||
public abstract DateRange GetNextDateRange(DateRange CurrentDateRange, int DayDisplayCount);
|
||||
|
||||
/// <summary>
|
||||
/// How many items should be placed in 1 FlipViewItem.
|
||||
/// </summary>
|
||||
public abstract int GetRenderDayCount(DateTime DisplayDate, int DayDisplayCount);
|
||||
|
||||
protected BaseCalendarTypeDrawingStrategy(CalendarSettings settings, CalendarDisplayType handlingType)
|
||||
{
|
||||
Settings = settings;
|
||||
HandlingType = handlingType;
|
||||
}
|
||||
Settings = settings;
|
||||
HandlingType = handlingType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,35 +2,34 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies
|
||||
namespace Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies;
|
||||
|
||||
public class DayCalendarDrawingStrategy : BaseCalendarTypeDrawingStrategy
|
||||
{
|
||||
public class DayCalendarDrawingStrategy : BaseCalendarTypeDrawingStrategy
|
||||
public DayCalendarDrawingStrategy(CalendarSettings settings) : base(settings, CalendarDisplayType.Day)
|
||||
{
|
||||
public DayCalendarDrawingStrategy(CalendarSettings settings) : base(settings, CalendarDisplayType.Day)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override DateRange GetNextDateRange(DateRange CurrentDateRange, int DayDisplayCount)
|
||||
{
|
||||
return new DateRange(CurrentDateRange.EndDate, CurrentDateRange.EndDate.AddDays(DayDisplayCount * 5));
|
||||
}
|
||||
|
||||
public override DateRange GetPreviousDateRange(DateRange CurrentDateRange, int DayDisplayCount)
|
||||
{
|
||||
return new DateRange(CurrentDateRange.StartDate.AddDays(-DayDisplayCount * 5), CurrentDateRange.StartDate);
|
||||
}
|
||||
|
||||
public override DateRange GetRenderDateRange(DateTime DisplayDate, int DayDisplayCount)
|
||||
{
|
||||
// Add good amount of days to the left and right of the DisplayDate.
|
||||
|
||||
var start = DisplayDate.AddDays(-4 * DayDisplayCount);
|
||||
var end = DisplayDate.AddDays(4 * DayDisplayCount);
|
||||
|
||||
return new DateRange(start, end);
|
||||
}
|
||||
|
||||
public override int GetRenderDayCount(DateTime DisplayDate, int DayDisplayCount) => DayDisplayCount;
|
||||
}
|
||||
|
||||
public override DateRange GetNextDateRange(DateRange CurrentDateRange, int DayDisplayCount)
|
||||
{
|
||||
return new DateRange(CurrentDateRange.EndDate, CurrentDateRange.EndDate.AddDays(DayDisplayCount * 5));
|
||||
}
|
||||
|
||||
public override DateRange GetPreviousDateRange(DateRange CurrentDateRange, int DayDisplayCount)
|
||||
{
|
||||
return new DateRange(CurrentDateRange.StartDate.AddDays(-DayDisplayCount * 5), CurrentDateRange.StartDate);
|
||||
}
|
||||
|
||||
public override DateRange GetRenderDateRange(DateTime DisplayDate, int DayDisplayCount)
|
||||
{
|
||||
// Add good amount of days to the left and right of the DisplayDate.
|
||||
|
||||
var start = DisplayDate.AddDays(-4 * DayDisplayCount);
|
||||
var end = DisplayDate.AddDays(4 * DayDisplayCount);
|
||||
|
||||
return new DateRange(start, end);
|
||||
}
|
||||
|
||||
public override int GetRenderDayCount(DateTime DisplayDate, int DayDisplayCount) => DayDisplayCount;
|
||||
}
|
||||
|
||||
@@ -2,32 +2,31 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Extensions;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies
|
||||
namespace Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies;
|
||||
|
||||
public class MonthCalendarDrawingStrategy : BaseCalendarTypeDrawingStrategy
|
||||
{
|
||||
public class MonthCalendarDrawingStrategy : BaseCalendarTypeDrawingStrategy
|
||||
public MonthCalendarDrawingStrategy(CalendarSettings settings)
|
||||
: base(settings, CalendarDisplayType.Month)
|
||||
{
|
||||
public MonthCalendarDrawingStrategy(CalendarSettings settings)
|
||||
: base(settings, CalendarDisplayType.Month)
|
||||
{
|
||||
}
|
||||
|
||||
public override DateRange GetNextDateRange(DateRange CurrentDateRange, int DayDisplayCount)
|
||||
{
|
||||
return new DateRange(CurrentDateRange.EndDate, CurrentDateRange.EndDate.AddDays(35));
|
||||
}
|
||||
|
||||
public override DateRange GetPreviousDateRange(DateRange CurrentDateRange, int DayDisplayCount)
|
||||
{
|
||||
return new DateRange(CurrentDateRange.StartDate.AddDays(-35), CurrentDateRange.StartDate);
|
||||
}
|
||||
|
||||
public override DateRange GetRenderDateRange(DateTime DisplayDate, int DayDisplayCount)
|
||||
{
|
||||
// Get the first day of the month.
|
||||
var firstDayOfMonth = new DateTime(DisplayDate.Year, DisplayDate.Month, 1);
|
||||
return DateTimeExtensions.GetMonthDateRangeStartingWeekday(firstDayOfMonth, Settings.FirstDayOfWeek);
|
||||
}
|
||||
|
||||
public override int GetRenderDayCount(DateTime DisplayDate, int DayDisplayCount) => 35;
|
||||
}
|
||||
|
||||
public override DateRange GetNextDateRange(DateRange CurrentDateRange, int DayDisplayCount)
|
||||
{
|
||||
return new DateRange(CurrentDateRange.EndDate, CurrentDateRange.EndDate.AddDays(35));
|
||||
}
|
||||
|
||||
public override DateRange GetPreviousDateRange(DateRange CurrentDateRange, int DayDisplayCount)
|
||||
{
|
||||
return new DateRange(CurrentDateRange.StartDate.AddDays(-35), CurrentDateRange.StartDate);
|
||||
}
|
||||
|
||||
public override DateRange GetRenderDateRange(DateTime DisplayDate, int DayDisplayCount)
|
||||
{
|
||||
// Get the first day of the month.
|
||||
var firstDayOfMonth = new DateTime(DisplayDate.Year, DisplayDate.Month, 1);
|
||||
return DateTimeExtensions.GetMonthDateRangeStartingWeekday(firstDayOfMonth, Settings.FirstDayOfWeek);
|
||||
}
|
||||
|
||||
public override int GetRenderDayCount(DateTime DisplayDate, int DayDisplayCount) => 35;
|
||||
}
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
using System;
|
||||
using Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies
|
||||
namespace Wino.Core.Domain.Models.Calendar.CalendarTypeStrategies;
|
||||
|
||||
public class WeekCalendarDrawingStrategy : BaseCalendarTypeDrawingStrategy
|
||||
{
|
||||
public class WeekCalendarDrawingStrategy : BaseCalendarTypeDrawingStrategy
|
||||
public WeekCalendarDrawingStrategy(CalendarSettings settings) : base(settings, Enums.CalendarDisplayType.Week) { }
|
||||
|
||||
public override DateRange GetNextDateRange(DateRange CurrentDateRange, int DayDisplayCount)
|
||||
=> new DateRange(CurrentDateRange.EndDate, CurrentDateRange.EndDate.AddDays(7 * 2));
|
||||
|
||||
public override DateRange GetPreviousDateRange(DateRange CurrentDateRange, int DayDisplayCount)
|
||||
=> new DateRange(CurrentDateRange.StartDate.AddDays(-7 * 2), CurrentDateRange.StartDate);
|
||||
|
||||
public override DateRange GetRenderDateRange(DateTime DisplayDate, int DayDisplayCount)
|
||||
{
|
||||
public WeekCalendarDrawingStrategy(CalendarSettings settings) : base(settings, Enums.CalendarDisplayType.Week) { }
|
||||
// Detect the first day of the week that contains the selected date.
|
||||
DayOfWeek firstDayOfWeek = Settings.FirstDayOfWeek;
|
||||
|
||||
public override DateRange GetNextDateRange(DateRange CurrentDateRange, int DayDisplayCount)
|
||||
=> new DateRange(CurrentDateRange.EndDate, CurrentDateRange.EndDate.AddDays(7 * 2));
|
||||
int diff = (7 + (DisplayDate.DayOfWeek - Settings.FirstDayOfWeek)) % 7;
|
||||
|
||||
public override DateRange GetPreviousDateRange(DateRange CurrentDateRange, int DayDisplayCount)
|
||||
=> new DateRange(CurrentDateRange.StartDate.AddDays(-7 * 2), CurrentDateRange.StartDate);
|
||||
// Start loading from this date instead of visible date.
|
||||
var weekStartDate = DisplayDate.AddDays(-diff).Date;
|
||||
|
||||
public override DateRange GetRenderDateRange(DateTime DisplayDate, int DayDisplayCount)
|
||||
{
|
||||
// Detect the first day of the week that contains the selected date.
|
||||
DayOfWeek firstDayOfWeek = Settings.FirstDayOfWeek;
|
||||
// Load -+ 14 days
|
||||
var startDate = weekStartDate.AddDays(-14);
|
||||
var endDte = weekStartDate.AddDays(14);
|
||||
|
||||
int diff = (7 + (DisplayDate.DayOfWeek - Settings.FirstDayOfWeek)) % 7;
|
||||
|
||||
// Start loading from this date instead of visible date.
|
||||
var weekStartDate = DisplayDate.AddDays(-diff).Date;
|
||||
|
||||
// Load -+ 14 days
|
||||
var startDate = weekStartDate.AddDays(-14);
|
||||
var endDte = weekStartDate.AddDays(14);
|
||||
|
||||
return new DateRange(startDate, endDte);
|
||||
}
|
||||
|
||||
public override int GetRenderDayCount(DateTime DisplayDate, int DayDisplayCount) => 7;
|
||||
return new DateRange(startDate, endDte);
|
||||
}
|
||||
|
||||
public override int GetRenderDayCount(DateTime DisplayDate, int DayDisplayCount) => 7;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the clicked date on the calendar view.
|
||||
/// </summary>
|
||||
/// <param name="ClickedDate">Requested date.</param>
|
||||
public record CalendarViewDayClickedEventArgs(DateTime ClickedDate);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the clicked date on the calendar view.
|
||||
/// </summary>
|
||||
/// <param name="ClickedDate">Requested date.</param>
|
||||
public record CalendarViewDayClickedEventArgs(DateTime ClickedDate);
|
||||
|
||||
@@ -1,42 +1,41 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
public class DateRange
|
||||
{
|
||||
public class DateRange
|
||||
public DateRange(DateTime startDate, DateTime endDate)
|
||||
{
|
||||
public DateRange(DateTime startDate, DateTime endDate)
|
||||
{
|
||||
StartDate = startDate;
|
||||
EndDate = endDate;
|
||||
}
|
||||
StartDate = startDate;
|
||||
EndDate = endDate;
|
||||
}
|
||||
|
||||
public DateTime StartDate { get; }
|
||||
public DateTime EndDate { get; }
|
||||
public DateTime StartDate { get; }
|
||||
public DateTime EndDate { get; }
|
||||
|
||||
public int TotalDays => (EndDate - StartDate).Days;
|
||||
public int TotalDays => (EndDate - StartDate).Days;
|
||||
|
||||
public override string ToString() => $"{StartDate.ToString("dd MMMM")} - {EndDate.ToString("dd MMMM")}";
|
||||
public override string ToString() => $"{StartDate.ToString("dd MMMM")} - {EndDate.ToString("dd MMMM")}";
|
||||
|
||||
public bool IsInRange(DateTime date)
|
||||
{
|
||||
return date >= StartDate && date <= EndDate;
|
||||
}
|
||||
public bool IsInRange(DateTime date)
|
||||
{
|
||||
return date >= StartDate && date <= EndDate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the most visible month index in the visible date range.
|
||||
/// </summary>
|
||||
public int GetMostVisibleMonthIndex()
|
||||
{
|
||||
var dateRange = Enumerable.Range(0, (EndDate - StartDate).Days + 1).Select(offset => StartDate.AddDays(offset));
|
||||
/// <summary>
|
||||
/// Gets the most visible month index in the visible date range.
|
||||
/// </summary>
|
||||
public int GetMostVisibleMonthIndex()
|
||||
{
|
||||
var dateRange = Enumerable.Range(0, (EndDate - StartDate).Days + 1).Select(offset => StartDate.AddDays(offset));
|
||||
|
||||
var groupedByMonth = dateRange.GroupBy(date => date.Month)
|
||||
.Select(g => new { Month = g.Key, DayCount = g.Count() });
|
||||
var groupedByMonth = dateRange.GroupBy(date => date.Month)
|
||||
.Select(g => new { Month = g.Key, DayCount = g.Count() });
|
||||
|
||||
// Find the month with the maximum count of days
|
||||
var mostVisibleMonth = groupedByMonth.OrderByDescending(g => g.DayCount).FirstOrDefault();
|
||||
// Find the month with the maximum count of days
|
||||
var mostVisibleMonth = groupedByMonth.OrderByDescending(g => g.DayCount).FirstOrDefault();
|
||||
|
||||
return mostVisibleMonth?.Month ?? -1;
|
||||
}
|
||||
return mostVisibleMonth?.Month ?? -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
namespace Wino.Core.Domain.Models.Calendar
|
||||
{
|
||||
public class DayHeaderRenderModel
|
||||
{
|
||||
public DayHeaderRenderModel(string dayHeader, double hourHeight)
|
||||
{
|
||||
DayHeader = dayHeader;
|
||||
HourHeight = hourHeight;
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
public string DayHeader { get; }
|
||||
public double HourHeight { get; }
|
||||
public class DayHeaderRenderModel
|
||||
{
|
||||
public DayHeaderRenderModel(string dayHeader, double hourHeight)
|
||||
{
|
||||
DayHeader = dayHeader;
|
||||
HourHeight = hourHeight;
|
||||
}
|
||||
|
||||
public string DayHeader { get; }
|
||||
public double HourHeight { get; }
|
||||
}
|
||||
|
||||
@@ -3,50 +3,49 @@ using System.Linq;
|
||||
using Itenso.TimePeriod;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Calendar
|
||||
namespace Wino.Core.Domain.Models.Calendar;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a range of days in the calendar.
|
||||
/// Corresponds to 1 view of the FlipView in CalendarPage.
|
||||
/// </summary>
|
||||
public class DayRangeRenderModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a range of days in the calendar.
|
||||
/// Corresponds to 1 view of the FlipView in CalendarPage.
|
||||
/// </summary>
|
||||
public class DayRangeRenderModel
|
||||
public ITimePeriod Period { get; }
|
||||
public List<CalendarDayModel> CalendarDays { get; } = [];
|
||||
|
||||
// TODO: Get rid of this at some point.
|
||||
public List<DayHeaderRenderModel> DayHeaders { get; } = [];
|
||||
public CalendarRenderOptions CalendarRenderOptions { get; }
|
||||
|
||||
public DayRangeRenderModel(CalendarRenderOptions calendarRenderOptions)
|
||||
{
|
||||
public ITimePeriod Period { get; }
|
||||
public List<CalendarDayModel> CalendarDays { get; } = [];
|
||||
CalendarRenderOptions = calendarRenderOptions;
|
||||
|
||||
// TODO: Get rid of this at some point.
|
||||
public List<DayHeaderRenderModel> DayHeaders { get; } = [];
|
||||
public CalendarRenderOptions CalendarRenderOptions { get; }
|
||||
|
||||
public DayRangeRenderModel(CalendarRenderOptions calendarRenderOptions)
|
||||
for (var i = 0; i < CalendarRenderOptions.TotalDayCount; i++)
|
||||
{
|
||||
CalendarRenderOptions = calendarRenderOptions;
|
||||
var representingDate = calendarRenderOptions.DateRange.StartDate.AddDays(i);
|
||||
var calendarDayModel = new CalendarDayModel(representingDate, calendarRenderOptions);
|
||||
|
||||
for (var i = 0; i < CalendarRenderOptions.TotalDayCount; i++)
|
||||
CalendarDays.Add(calendarDayModel);
|
||||
}
|
||||
|
||||
Period = new TimeRange(CalendarDays.First().RepresentingDate, CalendarDays.Last().RepresentingDate.AddDays(1));
|
||||
|
||||
// Create day headers based on culture info.
|
||||
|
||||
for (var i = 0; i < 24; i++)
|
||||
{
|
||||
var representingDate = calendarRenderOptions.DateRange.StartDate.Date.AddHours(i);
|
||||
|
||||
string dayHeader = calendarRenderOptions.CalendarSettings.DayHeaderDisplayType switch
|
||||
{
|
||||
var representingDate = calendarRenderOptions.DateRange.StartDate.AddDays(i);
|
||||
var calendarDayModel = new CalendarDayModel(representingDate, calendarRenderOptions);
|
||||
DayHeaderDisplayType.TwelveHour => representingDate.ToString("h tt", calendarRenderOptions.CalendarSettings.CultureInfo),
|
||||
DayHeaderDisplayType.TwentyFourHour => representingDate.ToString("HH", calendarRenderOptions.CalendarSettings.CultureInfo),
|
||||
_ => "N/A"
|
||||
};
|
||||
|
||||
CalendarDays.Add(calendarDayModel);
|
||||
}
|
||||
|
||||
Period = new TimeRange(CalendarDays.First().RepresentingDate, CalendarDays.Last().RepresentingDate.AddDays(1));
|
||||
|
||||
// Create day headers based on culture info.
|
||||
|
||||
for (var i = 0; i < 24; i++)
|
||||
{
|
||||
var representingDate = calendarRenderOptions.DateRange.StartDate.Date.AddHours(i);
|
||||
|
||||
string dayHeader = calendarRenderOptions.CalendarSettings.DayHeaderDisplayType switch
|
||||
{
|
||||
DayHeaderDisplayType.TwelveHour => representingDate.ToString("h tt", calendarRenderOptions.CalendarSettings.CultureInfo),
|
||||
DayHeaderDisplayType.TwentyFourHour => representingDate.ToString("HH", calendarRenderOptions.CalendarSettings.CultureInfo),
|
||||
_ => "N/A"
|
||||
};
|
||||
|
||||
DayHeaders.Add(new DayHeaderRenderModel(dayHeader, calendarRenderOptions.CalendarSettings.HourHeight));
|
||||
}
|
||||
DayHeaders.Add(new DayHeaderRenderModel(dayHeader, calendarRenderOptions.CalendarSettings.HourHeight));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Common
|
||||
namespace Wino.Core.Domain.Models.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Abstraction for StorageFile
|
||||
/// </summary>
|
||||
/// <param name="FullFilePath">Full path of the file.</param>
|
||||
/// <param name="Data">Content</param>
|
||||
public record SharedFile(string FullFilePath, byte[] Data)
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstraction for StorageFile
|
||||
/// </summary>
|
||||
/// <param name="FullFilePath">Full path of the file.</param>
|
||||
/// <param name="Data">Content</param>
|
||||
public record SharedFile(string FullFilePath, byte[] Data)
|
||||
{
|
||||
public string FileName => Path.GetFileName(FullFilePath);
|
||||
}
|
||||
public string FileName => Path.GetFileName(FullFilePath);
|
||||
}
|
||||
|
||||
@@ -3,30 +3,29 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Comparers
|
||||
namespace Wino.Core.Domain.Models.Comparers;
|
||||
|
||||
public class DateComparer : IComparer<IMailItem>, IEqualityComparer
|
||||
{
|
||||
public class DateComparer : IComparer<IMailItem>, IEqualityComparer
|
||||
public int Compare(IMailItem x, IMailItem y)
|
||||
{
|
||||
public int Compare(IMailItem x, IMailItem y)
|
||||
return DateTime.Compare(y.CreationDate, x.CreationDate);
|
||||
}
|
||||
|
||||
public new bool Equals(object x, object y)
|
||||
{
|
||||
if (x is IMailItem firstItem && y is IMailItem secondItem)
|
||||
{
|
||||
return DateTime.Compare(y.CreationDate, x.CreationDate);
|
||||
return firstItem.Equals(secondItem);
|
||||
}
|
||||
|
||||
public new bool Equals(object x, object y)
|
||||
{
|
||||
if (x is IMailItem firstItem && y is IMailItem secondItem)
|
||||
{
|
||||
return firstItem.Equals(secondItem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public int GetHashCode(object obj) => (obj as IMailItem).GetHashCode();
|
||||
|
||||
public int GetHashCode(object obj) => (obj as IMailItem).GetHashCode();
|
||||
public DateComparer()
|
||||
{
|
||||
|
||||
public DateComparer()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Comparers
|
||||
namespace Wino.Core.Domain.Models.Comparers;
|
||||
|
||||
/// <summary>
|
||||
/// Used to insert date grouping into proper place in Reader page.
|
||||
/// </summary>
|
||||
public class DateTimeComparer : IComparer<DateTime>
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to insert date grouping into proper place in Reader page.
|
||||
/// </summary>
|
||||
public class DateTimeComparer : IComparer<DateTime>
|
||||
public int Compare(DateTime x, DateTime y)
|
||||
{
|
||||
public int Compare(DateTime x, DateTime y)
|
||||
{
|
||||
return DateTime.Compare(y, x);
|
||||
}
|
||||
return DateTime.Compare(y, x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Comparers
|
||||
namespace Wino.Core.Domain.Models.Comparers;
|
||||
|
||||
public class FolderNameComparer : IComparer<MailItemFolder>
|
||||
{
|
||||
public class FolderNameComparer : IComparer<MailItemFolder>
|
||||
public int Compare(MailItemFolder x, MailItemFolder y)
|
||||
{
|
||||
public int Compare(MailItemFolder x, MailItemFolder y)
|
||||
{
|
||||
return x.FolderName.CompareTo(y.FolderName);
|
||||
}
|
||||
return x.FolderName.CompareTo(y.FolderName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,37 +2,36 @@
|
||||
using System.Collections.Generic;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Comparers
|
||||
namespace Wino.Core.Domain.Models.Comparers;
|
||||
|
||||
public class ListItemComparer : IComparer<object>
|
||||
{
|
||||
public class ListItemComparer : IComparer<object>
|
||||
public bool SortByName { get; set; }
|
||||
|
||||
public DateComparer DateComparer = new DateComparer();
|
||||
public readonly NameComparer NameComparer = new NameComparer();
|
||||
|
||||
public int Compare(object x, object y)
|
||||
{
|
||||
public bool SortByName { get; set; }
|
||||
|
||||
public DateComparer DateComparer = new DateComparer();
|
||||
public readonly NameComparer NameComparer = new NameComparer();
|
||||
|
||||
public int Compare(object x, object y)
|
||||
if (x is IMailItem xMail && y is IMailItem yMail)
|
||||
{
|
||||
if (x is IMailItem xMail && y is IMailItem yMail)
|
||||
{
|
||||
var itemComparer = GetItemComparer();
|
||||
var itemComparer = GetItemComparer();
|
||||
|
||||
return itemComparer.Compare(xMail, yMail);
|
||||
}
|
||||
else if (x is DateTime dateX && y is DateTime dateY)
|
||||
return DateTime.Compare(dateY, dateX);
|
||||
else if (x is string stringX && y is string stringY)
|
||||
return stringY.CompareTo(stringX);
|
||||
|
||||
return 0;
|
||||
return itemComparer.Compare(xMail, yMail);
|
||||
}
|
||||
else if (x is DateTime dateX && y is DateTime dateY)
|
||||
return DateTime.Compare(dateY, dateX);
|
||||
else if (x is string stringX && y is string stringY)
|
||||
return stringY.CompareTo(stringX);
|
||||
|
||||
public IComparer<IMailItem> GetItemComparer()
|
||||
{
|
||||
if (SortByName)
|
||||
return NameComparer;
|
||||
else
|
||||
return DateComparer;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public IComparer<IMailItem> GetItemComparer()
|
||||
{
|
||||
if (SortByName)
|
||||
return NameComparer;
|
||||
else
|
||||
return DateComparer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Comparers
|
||||
namespace Wino.Core.Domain.Models.Comparers;
|
||||
|
||||
public class NameComparer : IComparer<IMailItem>
|
||||
{
|
||||
public class NameComparer : IComparer<IMailItem>
|
||||
public int Compare(IMailItem x, IMailItem y)
|
||||
{
|
||||
public int Compare(IMailItem x, IMailItem y)
|
||||
{
|
||||
return string.Compare(x.FromName, y.FromName);
|
||||
}
|
||||
return string.Compare(x.FromName, y.FromName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
using System.IO;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Connectivity
|
||||
namespace Wino.Core.Domain.Models.Connectivity;
|
||||
|
||||
public class ImapClientPoolOptions
|
||||
{
|
||||
public class ImapClientPoolOptions
|
||||
public Stream ProtocolLog { get; }
|
||||
public CustomServerInformation ServerInformation { get; }
|
||||
public bool IsTestPool { get; }
|
||||
|
||||
protected ImapClientPoolOptions(CustomServerInformation serverInformation, Stream protocolLog, bool isTestPool)
|
||||
{
|
||||
public Stream ProtocolLog { get; }
|
||||
public CustomServerInformation ServerInformation { get; }
|
||||
public bool IsTestPool { get; }
|
||||
|
||||
protected ImapClientPoolOptions(CustomServerInformation serverInformation, Stream protocolLog, bool isTestPool)
|
||||
{
|
||||
ServerInformation = serverInformation;
|
||||
ProtocolLog = protocolLog;
|
||||
IsTestPool = isTestPool;
|
||||
}
|
||||
|
||||
public static ImapClientPoolOptions CreateDefault(CustomServerInformation serverInformation, Stream protocolLog)
|
||||
=> new(serverInformation, protocolLog, false);
|
||||
|
||||
public static ImapClientPoolOptions CreateTestPool(CustomServerInformation serverInformation, Stream protocolLog)
|
||||
=> new(serverInformation, protocolLog, true);
|
||||
ServerInformation = serverInformation;
|
||||
ProtocolLog = protocolLog;
|
||||
IsTestPool = isTestPool;
|
||||
}
|
||||
|
||||
public static ImapClientPoolOptions CreateDefault(CustomServerInformation serverInformation, Stream protocolLog)
|
||||
=> new(serverInformation, protocolLog, false);
|
||||
|
||||
public static ImapClientPoolOptions CreateTestPool(CustomServerInformation serverInformation, Stream protocolLog)
|
||||
=> new(serverInformation, protocolLog, true);
|
||||
}
|
||||
|
||||
@@ -3,46 +3,45 @@ using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using Wino.Core.Domain.Extensions;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Connectivity
|
||||
namespace Wino.Core.Domain.Models.Connectivity;
|
||||
|
||||
/// <summary>
|
||||
/// Contains validation of the IMAP server connectivity during account setup.
|
||||
/// </summary>
|
||||
public class ImapConnectivityTestResults
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains validation of the IMAP server connectivity during account setup.
|
||||
/// </summary>
|
||||
public class ImapConnectivityTestResults
|
||||
[JsonConstructor]
|
||||
protected ImapConnectivityTestResults() { }
|
||||
|
||||
public bool IsSuccess { get; set; }
|
||||
|
||||
public bool IsCertificateUIRequired { get; set; }
|
||||
|
||||
public string FailedReason { get; set; }
|
||||
public string FailureProtocolLog { get; set; }
|
||||
|
||||
public static ImapConnectivityTestResults Success() => new ImapConnectivityTestResults() { IsSuccess = true };
|
||||
public static ImapConnectivityTestResults Failure(Exception ex, string failureProtocolLog) => new ImapConnectivityTestResults()
|
||||
{
|
||||
[JsonConstructor]
|
||||
protected ImapConnectivityTestResults() { }
|
||||
FailedReason = string.Join(Environment.NewLine, ex.GetInnerExceptions().Select(e => e.Message)),
|
||||
FailureProtocolLog = failureProtocolLog
|
||||
};
|
||||
|
||||
public bool IsSuccess { get; set; }
|
||||
|
||||
public bool IsCertificateUIRequired { get; set; }
|
||||
|
||||
public string FailedReason { get; set; }
|
||||
public string FailureProtocolLog { get; set; }
|
||||
|
||||
public static ImapConnectivityTestResults Success() => new ImapConnectivityTestResults() { IsSuccess = true };
|
||||
public static ImapConnectivityTestResults Failure(Exception ex, string failureProtocolLog) => new ImapConnectivityTestResults()
|
||||
public static ImapConnectivityTestResults CertificateUIRequired(string issuer,
|
||||
string expirationString,
|
||||
string validFromString)
|
||||
{
|
||||
return new ImapConnectivityTestResults()
|
||||
{
|
||||
FailedReason = string.Join(Environment.NewLine, ex.GetInnerExceptions().Select(e => e.Message)),
|
||||
FailureProtocolLog = failureProtocolLog
|
||||
IsSuccess = false,
|
||||
IsCertificateUIRequired = true,
|
||||
CertificateIssuer = issuer,
|
||||
CertificateExpirationDateString = expirationString,
|
||||
CertificateValidFromDateString = validFromString
|
||||
};
|
||||
|
||||
public static ImapConnectivityTestResults CertificateUIRequired(string issuer,
|
||||
string expirationString,
|
||||
string validFromString)
|
||||
{
|
||||
return new ImapConnectivityTestResults()
|
||||
{
|
||||
IsSuccess = false,
|
||||
IsCertificateUIRequired = true,
|
||||
CertificateIssuer = issuer,
|
||||
CertificateExpirationDateString = expirationString,
|
||||
CertificateValidFromDateString = validFromString
|
||||
};
|
||||
}
|
||||
|
||||
public string CertificateIssuer { get; set; }
|
||||
public string CertificateValidFromDateString { get; set; }
|
||||
public string CertificateExpirationDateString { get; set; }
|
||||
}
|
||||
|
||||
public string CertificateIssuer { get; set; }
|
||||
public string CertificateValidFromDateString { get; set; }
|
||||
public string CertificateExpirationDateString { get; set; }
|
||||
}
|
||||
|
||||
@@ -2,32 +2,31 @@
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Folders
|
||||
namespace Wino.Core.Domain.Models.Folders;
|
||||
|
||||
/// <summary>
|
||||
/// Grouped folder information for the menu for given account.
|
||||
/// </summary>
|
||||
public class AccountFolderTree
|
||||
{
|
||||
/// <summary>
|
||||
/// Grouped folder information for the menu for given account.
|
||||
/// </summary>
|
||||
public class AccountFolderTree
|
||||
public MailAccount Account { get; }
|
||||
public List<IMailItemFolder> Folders { get; set; } = new List<IMailItemFolder>();
|
||||
|
||||
public AccountFolderTree(MailAccount account)
|
||||
{
|
||||
public MailAccount Account { get; }
|
||||
public List<IMailItemFolder> Folders { get; set; } = new List<IMailItemFolder>();
|
||||
Account = account;
|
||||
}
|
||||
|
||||
public AccountFolderTree(MailAccount account)
|
||||
public bool HasSpecialTypeFolder(SpecialFolderType type)
|
||||
{
|
||||
foreach (var folderStructure in Folders)
|
||||
{
|
||||
Account = account;
|
||||
bool hasSpecialFolder = folderStructure.ContainsSpecialFolderType(type);
|
||||
|
||||
if (hasSpecialFolder)
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool HasSpecialTypeFolder(SpecialFolderType type)
|
||||
{
|
||||
foreach (var folderStructure in Folders)
|
||||
{
|
||||
bool hasSpecialFolder = folderStructure.ContainsSpecialFolderType(type);
|
||||
|
||||
if (hasSpecialFolder)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Menus;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Folders
|
||||
{
|
||||
public class FolderOperationMenuItem : MenuOperationItemBase<FolderOperation>, IMenuOperation
|
||||
{
|
||||
protected FolderOperationMenuItem(FolderOperation operation, bool isEnabled) : base(operation, isEnabled) { }
|
||||
namespace Wino.Core.Domain.Models.Folders;
|
||||
|
||||
public static FolderOperationMenuItem Create(FolderOperation operation, bool isEnabled = true)
|
||||
=> new FolderOperationMenuItem(operation, isEnabled);
|
||||
}
|
||||
public class FolderOperationMenuItem : MenuOperationItemBase<FolderOperation>, IMenuOperation
|
||||
{
|
||||
protected FolderOperationMenuItem(FolderOperation operation, bool isEnabled) : base(operation, isEnabled) { }
|
||||
|
||||
public static FolderOperationMenuItem Create(FolderOperation operation, bool isEnabled = true)
|
||||
=> new FolderOperationMenuItem(operation, isEnabled);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Folders
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates a request to prepare a folder operation like Rename, Delete, etc.
|
||||
/// </summary>
|
||||
/// <param name="Action">Folder operation.</param>
|
||||
/// <param name="Folder">Target folder.</param>
|
||||
public record FolderOperationPreperationRequest(FolderOperation Action, MailItemFolder Folder) { }
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Folders;
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates a request to prepare a folder operation like Rename, Delete, etc.
|
||||
/// </summary>
|
||||
/// <param name="Action">Folder operation.</param>
|
||||
/// <param name="Folder">Target folder.</param>
|
||||
public record FolderOperationPreperationRequest(FolderOperation Action, MailItemFolder Folder) { }
|
||||
|
||||
@@ -2,30 +2,29 @@
|
||||
using System.Collections.Generic;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Folders
|
||||
{
|
||||
public interface IMailItemFolder
|
||||
{
|
||||
string BackgroundColorHex { get; set; }
|
||||
string DeltaToken { get; set; }
|
||||
string FolderName { get; set; }
|
||||
long HighestModeSeq { get; set; }
|
||||
Guid Id { get; set; }
|
||||
bool IsHidden { get; set; }
|
||||
bool IsSticky { get; set; }
|
||||
bool IsSynchronizationEnabled { get; set; }
|
||||
bool IsSystemFolder { get; set; }
|
||||
DateTime? LastSynchronizedDate { get; set; }
|
||||
Guid MailAccountId { get; set; }
|
||||
string ParentRemoteFolderId { get; set; }
|
||||
string RemoteFolderId { get; set; }
|
||||
SpecialFolderType SpecialFolderType { get; set; }
|
||||
string TextColorHex { get; set; }
|
||||
uint UidValidity { get; set; }
|
||||
List<IMailItemFolder> ChildFolders { get; set; }
|
||||
bool IsMoveTarget { get; }
|
||||
bool ShowUnreadCount { get; set; }
|
||||
namespace Wino.Core.Domain.Models.Folders;
|
||||
|
||||
bool ContainsSpecialFolderType(SpecialFolderType type);
|
||||
}
|
||||
public interface IMailItemFolder
|
||||
{
|
||||
string BackgroundColorHex { get; set; }
|
||||
string DeltaToken { get; set; }
|
||||
string FolderName { get; set; }
|
||||
long HighestModeSeq { get; set; }
|
||||
Guid Id { get; set; }
|
||||
bool IsHidden { get; set; }
|
||||
bool IsSticky { get; set; }
|
||||
bool IsSynchronizationEnabled { get; set; }
|
||||
bool IsSystemFolder { get; set; }
|
||||
DateTime? LastSynchronizedDate { get; set; }
|
||||
Guid MailAccountId { get; set; }
|
||||
string ParentRemoteFolderId { get; set; }
|
||||
string RemoteFolderId { get; set; }
|
||||
SpecialFolderType SpecialFolderType { get; set; }
|
||||
string TextColorHex { get; set; }
|
||||
uint UidValidity { get; set; }
|
||||
List<IMailItemFolder> ChildFolders { get; set; }
|
||||
bool IsMoveTarget { get; }
|
||||
bool ShowUnreadCount { get; set; }
|
||||
|
||||
bool ContainsSpecialFolderType(SpecialFolderType type);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Folders
|
||||
{
|
||||
public record SystemFolderConfiguration(MailItemFolder SentFolder,
|
||||
MailItemFolder DraftFolder,
|
||||
MailItemFolder ArchiveFolder,
|
||||
MailItemFolder TrashFolder,
|
||||
MailItemFolder JunkFolder);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Folders;
|
||||
|
||||
public record SystemFolderConfiguration(MailItemFolder SentFolder,
|
||||
MailItemFolder DraftFolder,
|
||||
MailItemFolder ArchiveFolder,
|
||||
MailItemFolder TrashFolder,
|
||||
MailItemFolder JunkFolder);
|
||||
|
||||
@@ -5,251 +5,250 @@ using MimeKit;
|
||||
using MimeKit.Text;
|
||||
using MimeKit.Tnef;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// Visits a MimeMessage and generates HTML suitable to be rendered by a browser control.
|
||||
/// </summary>
|
||||
public class HtmlPreviewVisitor : MimeVisitor
|
||||
{
|
||||
List<MultipartRelated> stack = new List<MultipartRelated>();
|
||||
List<MimeEntity> attachments = new List<MimeEntity>();
|
||||
|
||||
readonly string tempDir;
|
||||
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Visits a MimeMessage and generates HTML suitable to be rendered by a browser control.
|
||||
/// Creates a new HtmlPreviewVisitor.
|
||||
/// </summary>
|
||||
public class HtmlPreviewVisitor : MimeVisitor
|
||||
/// <param name="tempDirectory">A temporary directory used for storing image files.</param>
|
||||
public HtmlPreviewVisitor(string tempDirectory)
|
||||
{
|
||||
List<MultipartRelated> stack = new List<MultipartRelated>();
|
||||
List<MimeEntity> attachments = new List<MimeEntity>();
|
||||
tempDir = tempDirectory;
|
||||
}
|
||||
|
||||
readonly string tempDir;
|
||||
/// <summary>
|
||||
/// The list of attachments that were in the MimeMessage.
|
||||
/// </summary>
|
||||
public IList<MimeEntity> Attachments
|
||||
{
|
||||
get { return attachments; }
|
||||
}
|
||||
|
||||
public string Body { get; set; }
|
||||
/// <summary>
|
||||
/// The HTML string that can be set on the BrowserControl.
|
||||
/// </summary>
|
||||
public string HtmlBody
|
||||
{
|
||||
get { return Body ?? string.Empty; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new HtmlPreviewVisitor.
|
||||
/// </summary>
|
||||
/// <param name="tempDirectory">A temporary directory used for storing image files.</param>
|
||||
public HtmlPreviewVisitor(string tempDirectory)
|
||||
protected override void VisitMultipartAlternative(MultipartAlternative alternative)
|
||||
{
|
||||
// walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful
|
||||
for (int i = alternative.Count - 1; i >= 0 && Body == null; i--)
|
||||
alternative[i].Accept(this);
|
||||
}
|
||||
|
||||
protected override void VisitMultipartRelated(MultipartRelated related)
|
||||
{
|
||||
var root = related.Root;
|
||||
|
||||
// push this multipart/related onto our stack
|
||||
stack.Add(related);
|
||||
|
||||
// visit the root document
|
||||
root.Accept(this);
|
||||
|
||||
// pop this multipart/related off our stack
|
||||
stack.RemoveAt(stack.Count - 1);
|
||||
}
|
||||
|
||||
// look up the image based on the img src url within our multipart/related stack
|
||||
bool TryGetImage(string url, out MimePart image)
|
||||
{
|
||||
UriKind kind;
|
||||
int index;
|
||||
Uri uri;
|
||||
|
||||
if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
||||
kind = UriKind.Absolute;
|
||||
else if (Uri.IsWellFormedUriString(url, UriKind.Relative))
|
||||
kind = UriKind.Relative;
|
||||
else
|
||||
kind = UriKind.RelativeOrAbsolute;
|
||||
|
||||
try
|
||||
{
|
||||
tempDir = tempDirectory;
|
||||
uri = new Uri(url, kind);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The list of attachments that were in the MimeMessage.
|
||||
/// </summary>
|
||||
public IList<MimeEntity> Attachments
|
||||
catch
|
||||
{
|
||||
get { return attachments; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The HTML string that can be set on the BrowserControl.
|
||||
/// </summary>
|
||||
public string HtmlBody
|
||||
{
|
||||
get { return Body ?? string.Empty; }
|
||||
}
|
||||
|
||||
protected override void VisitMultipartAlternative(MultipartAlternative alternative)
|
||||
{
|
||||
// walk the multipart/alternative children backwards from greatest level of faithfulness to the least faithful
|
||||
for (int i = alternative.Count - 1; i >= 0 && Body == null; i--)
|
||||
alternative[i].Accept(this);
|
||||
}
|
||||
|
||||
protected override void VisitMultipartRelated(MultipartRelated related)
|
||||
{
|
||||
var root = related.Root;
|
||||
|
||||
// push this multipart/related onto our stack
|
||||
stack.Add(related);
|
||||
|
||||
// visit the root document
|
||||
root.Accept(this);
|
||||
|
||||
// pop this multipart/related off our stack
|
||||
stack.RemoveAt(stack.Count - 1);
|
||||
}
|
||||
|
||||
// look up the image based on the img src url within our multipart/related stack
|
||||
bool TryGetImage(string url, out MimePart image)
|
||||
{
|
||||
UriKind kind;
|
||||
int index;
|
||||
Uri uri;
|
||||
|
||||
if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
|
||||
kind = UriKind.Absolute;
|
||||
else if (Uri.IsWellFormedUriString(url, UriKind.Relative))
|
||||
kind = UriKind.Relative;
|
||||
else
|
||||
kind = UriKind.RelativeOrAbsolute;
|
||||
|
||||
try
|
||||
{
|
||||
uri = new Uri(url, kind);
|
||||
}
|
||||
catch
|
||||
{
|
||||
image = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = stack.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if ((index = stack[i].IndexOf(uri)) == -1)
|
||||
continue;
|
||||
|
||||
image = stack[i][index] as MimePart;
|
||||
return image != null;
|
||||
}
|
||||
|
||||
image = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the image to our temp directory and return a "file://" url suitable for
|
||||
// the browser control to load.
|
||||
// Note: if you'd rather embed the image data into the HTML, you can construct a
|
||||
// "data:" url instead.
|
||||
string SaveImage(MimePart image)
|
||||
for (int i = stack.Count - 1; i >= 0; i--)
|
||||
{
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
image.Content.DecodeTo(memory);
|
||||
var buffer = memory.GetBuffer();
|
||||
var length = (int)memory.Length;
|
||||
var base64 = Convert.ToBase64String(buffer, 0, length);
|
||||
if ((index = stack[i].IndexOf(uri)) == -1)
|
||||
continue;
|
||||
|
||||
return string.Format("data:{0};base64,{1}", image.ContentType.MimeType, base64);
|
||||
}
|
||||
|
||||
//string fileName = url
|
||||
// .Replace(':', '_')
|
||||
// .Replace('\\', '_')
|
||||
// .Replace('/', '_');
|
||||
|
||||
//string path = Path.Combine(tempDir, fileName);
|
||||
|
||||
//if (!File.Exists(path))
|
||||
//{
|
||||
// using (var output = File.Create(path))
|
||||
// image.Content.DecodeTo(output);
|
||||
//}
|
||||
|
||||
//return "file://" + path.Replace('\\', '/');
|
||||
image = stack[i][index] as MimePart;
|
||||
return image != null;
|
||||
}
|
||||
|
||||
// Replaces <img src=...> urls that refer to images embedded within the message with
|
||||
// "file://" urls that the browser control will actually be able to load.
|
||||
void HtmlTagCallback(HtmlTagContext ctx, HtmlWriter htmlWriter)
|
||||
image = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the image to our temp directory and return a "file://" url suitable for
|
||||
// the browser control to load.
|
||||
// Note: if you'd rather embed the image data into the HTML, you can construct a
|
||||
// "data:" url instead.
|
||||
string SaveImage(MimePart image)
|
||||
{
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
if (ctx.TagId == HtmlTagId.Image && !ctx.IsEndTag && stack.Count > 0)
|
||||
image.Content.DecodeTo(memory);
|
||||
var buffer = memory.GetBuffer();
|
||||
var length = (int)memory.Length;
|
||||
var base64 = Convert.ToBase64String(buffer, 0, length);
|
||||
|
||||
return string.Format("data:{0};base64,{1}", image.ContentType.MimeType, base64);
|
||||
}
|
||||
|
||||
//string fileName = url
|
||||
// .Replace(':', '_')
|
||||
// .Replace('\\', '_')
|
||||
// .Replace('/', '_');
|
||||
|
||||
//string path = Path.Combine(tempDir, fileName);
|
||||
|
||||
//if (!File.Exists(path))
|
||||
//{
|
||||
// using (var output = File.Create(path))
|
||||
// image.Content.DecodeTo(output);
|
||||
//}
|
||||
|
||||
//return "file://" + path.Replace('\\', '/');
|
||||
}
|
||||
|
||||
// Replaces <img src=...> urls that refer to images embedded within the message with
|
||||
// "file://" urls that the browser control will actually be able to load.
|
||||
void HtmlTagCallback(HtmlTagContext ctx, HtmlWriter htmlWriter)
|
||||
{
|
||||
if (ctx.TagId == HtmlTagId.Image && !ctx.IsEndTag && stack.Count > 0)
|
||||
{
|
||||
ctx.WriteTag(htmlWriter, false);
|
||||
|
||||
// replace the src attribute with a file:// URL
|
||||
foreach (var attribute in ctx.Attributes)
|
||||
{
|
||||
ctx.WriteTag(htmlWriter, false);
|
||||
|
||||
// replace the src attribute with a file:// URL
|
||||
foreach (var attribute in ctx.Attributes)
|
||||
if (attribute.Id == HtmlAttributeId.Src)
|
||||
{
|
||||
if (attribute.Id == HtmlAttributeId.Src)
|
||||
{
|
||||
MimePart image;
|
||||
string url;
|
||||
MimePart image;
|
||||
string url;
|
||||
|
||||
if (!TryGetImage(attribute.Value, out image))
|
||||
{
|
||||
htmlWriter.WriteAttribute(attribute);
|
||||
continue;
|
||||
}
|
||||
|
||||
url = SaveImage(image);
|
||||
|
||||
htmlWriter.WriteAttributeName(attribute.Name);
|
||||
htmlWriter.WriteAttributeValue(url);
|
||||
}
|
||||
else
|
||||
if (!TryGetImage(attribute.Value, out image))
|
||||
{
|
||||
htmlWriter.WriteAttribute(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ctx.TagId == HtmlTagId.Body && !ctx.IsEndTag)
|
||||
{
|
||||
ctx.WriteTag(htmlWriter, false);
|
||||
|
||||
// add and/or replace oncontextmenu="return false;"
|
||||
foreach (var attribute in ctx.Attributes)
|
||||
{
|
||||
if (attribute.Name.ToLowerInvariant() == "oncontextmenu")
|
||||
continue;
|
||||
}
|
||||
|
||||
htmlWriter.WriteAttribute(attribute);
|
||||
}
|
||||
url = SaveImage(image);
|
||||
|
||||
htmlWriter.WriteAttribute("oncontextmenu", "return false;");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctx.TagId == HtmlTagId.Unknown)
|
||||
{
|
||||
ctx.DeleteTag = true;
|
||||
ctx.DeleteEndTag = true;
|
||||
htmlWriter.WriteAttributeName(attribute.Name);
|
||||
htmlWriter.WriteAttributeValue(url);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.WriteTag(htmlWriter, true);
|
||||
htmlWriter.WriteAttribute(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void VisitTextPart(TextPart entity)
|
||||
else if (ctx.TagId == HtmlTagId.Body && !ctx.IsEndTag)
|
||||
{
|
||||
TextConverter converter;
|
||||
ctx.WriteTag(htmlWriter, false);
|
||||
|
||||
if (Body != null)
|
||||
// add and/or replace oncontextmenu="return false;"
|
||||
foreach (var attribute in ctx.Attributes)
|
||||
{
|
||||
// since we've already found the body, treat this as an attachment
|
||||
attachments.Add(entity);
|
||||
return;
|
||||
if (attribute.Name.ToLowerInvariant() == "oncontextmenu")
|
||||
continue;
|
||||
|
||||
htmlWriter.WriteAttribute(attribute);
|
||||
}
|
||||
|
||||
if (entity.IsHtml)
|
||||
htmlWriter.WriteAttribute("oncontextmenu", "return false;");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctx.TagId == HtmlTagId.Unknown)
|
||||
{
|
||||
converter = new HtmlToHtml
|
||||
{
|
||||
HtmlTagCallback = HtmlTagCallback
|
||||
};
|
||||
}
|
||||
else if (entity.IsFlowed)
|
||||
{
|
||||
var flowed = new FlowedToHtml();
|
||||
string delsp;
|
||||
|
||||
if (entity.ContentType.Parameters.TryGetValue("delsp", out delsp))
|
||||
flowed.DeleteSpace = delsp.ToLowerInvariant() == "yes";
|
||||
|
||||
converter = flowed;
|
||||
ctx.DeleteTag = true;
|
||||
ctx.DeleteEndTag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
converter = new TextToHtml();
|
||||
ctx.WriteTag(htmlWriter, true);
|
||||
}
|
||||
|
||||
Body = converter.Convert(entity.Text);
|
||||
}
|
||||
|
||||
protected override void VisitTnefPart(TnefPart entity)
|
||||
{
|
||||
// extract any attachments in the MS-TNEF part
|
||||
attachments.AddRange(entity.ExtractAttachments());
|
||||
}
|
||||
|
||||
protected override void VisitMessagePart(MessagePart entity)
|
||||
{
|
||||
// treat message/rfc822 parts as attachments
|
||||
attachments.Add(entity);
|
||||
}
|
||||
|
||||
protected override void VisitMimePart(MimePart entity)
|
||||
{
|
||||
// realistically, if we've gotten this far, then we can treat this as an attachment
|
||||
// even if the IsAttachment property is false.
|
||||
attachments.Add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void VisitTextPart(TextPart entity)
|
||||
{
|
||||
TextConverter converter;
|
||||
|
||||
if (Body != null)
|
||||
{
|
||||
// since we've already found the body, treat this as an attachment
|
||||
attachments.Add(entity);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity.IsHtml)
|
||||
{
|
||||
converter = new HtmlToHtml
|
||||
{
|
||||
HtmlTagCallback = HtmlTagCallback
|
||||
};
|
||||
}
|
||||
else if (entity.IsFlowed)
|
||||
{
|
||||
var flowed = new FlowedToHtml();
|
||||
string delsp;
|
||||
|
||||
if (entity.ContentType.Parameters.TryGetValue("delsp", out delsp))
|
||||
flowed.DeleteSpace = delsp.ToLowerInvariant() == "yes";
|
||||
|
||||
converter = flowed;
|
||||
}
|
||||
else
|
||||
{
|
||||
converter = new TextToHtml();
|
||||
}
|
||||
|
||||
Body = converter.Convert(entity.Text);
|
||||
}
|
||||
|
||||
protected override void VisitTnefPart(TnefPart entity)
|
||||
{
|
||||
// extract any attachments in the MS-TNEF part
|
||||
attachments.AddRange(entity.ExtractAttachments());
|
||||
}
|
||||
|
||||
protected override void VisitMessagePart(MessagePart entity)
|
||||
{
|
||||
// treat message/rfc822 parts as attachments
|
||||
attachments.Add(entity);
|
||||
}
|
||||
|
||||
protected override void VisitMimePart(MimePart entity)
|
||||
{
|
||||
// realistically, if we've gotten this far, then we can treat this as an attachment
|
||||
// even if the IsAttachment property is false.
|
||||
attachments.Add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// An interface that returns the UniqueId store for IMailItem.
|
||||
/// For threads, it may be multiple items.
|
||||
/// For single mails, it'll always be one item.
|
||||
/// </summary>
|
||||
public interface IMailHashContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface that returns the UniqueId store for IMailItem.
|
||||
/// For threads, it may be multiple items.
|
||||
/// For single mails, it'll always be one item.
|
||||
/// </summary>
|
||||
public interface IMailHashContainer
|
||||
{
|
||||
IEnumerable<Guid> GetContainingIds();
|
||||
}
|
||||
IEnumerable<Guid> GetContainingIds();
|
||||
}
|
||||
|
||||
@@ -2,34 +2,33 @@
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface of simplest representation of a MailCopy.
|
||||
/// </summary>
|
||||
public interface IMailItem : IMailHashContainer
|
||||
{
|
||||
Guid UniqueId { get; }
|
||||
string Id { get; }
|
||||
string Subject { get; }
|
||||
string ThreadId { get; }
|
||||
string MessageId { get; }
|
||||
string References { get; }
|
||||
string InReplyTo { get; }
|
||||
string PreviewText { get; }
|
||||
string FromName { get; }
|
||||
DateTime CreationDate { get; }
|
||||
string FromAddress { get; }
|
||||
bool HasAttachments { get; }
|
||||
bool IsFlagged { get; }
|
||||
bool IsFocused { get; }
|
||||
bool IsRead { get; }
|
||||
string DraftId { get; }
|
||||
bool IsDraft { get; }
|
||||
Guid FileId { get; }
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
MailItemFolder AssignedFolder { get; }
|
||||
MailAccount AssignedAccount { get; }
|
||||
AccountContact SenderContact { get; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Interface of simplest representation of a MailCopy.
|
||||
/// </summary>
|
||||
public interface IMailItem : IMailHashContainer
|
||||
{
|
||||
Guid UniqueId { get; }
|
||||
string Id { get; }
|
||||
string Subject { get; }
|
||||
string ThreadId { get; }
|
||||
string MessageId { get; }
|
||||
string References { get; }
|
||||
string InReplyTo { get; }
|
||||
string PreviewText { get; }
|
||||
string FromName { get; }
|
||||
DateTime CreationDate { get; }
|
||||
string FromAddress { get; }
|
||||
bool HasAttachments { get; }
|
||||
bool IsFlagged { get; }
|
||||
bool IsFocused { get; }
|
||||
bool IsRead { get; }
|
||||
string DraftId { get; }
|
||||
bool IsDraft { get; }
|
||||
Guid FileId { get; }
|
||||
|
||||
MailItemFolder AssignedFolder { get; }
|
||||
MailAccount AssignedAccount { get; }
|
||||
AccountContact SenderContact { get; }
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// Interface that represents conversation threads.
|
||||
/// Even though this type has 1 single UI representation most of the time,
|
||||
/// it can contain multiple IMailItem.
|
||||
/// </summary>
|
||||
public interface IMailItemThread : IMailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface that represents conversation threads.
|
||||
/// Even though this type has 1 single UI representation most of the time,
|
||||
/// it can contain multiple IMailItem.
|
||||
/// </summary>
|
||||
public interface IMailItemThread : IMailItem
|
||||
{
|
||||
ObservableCollection<IMailItem> ThreadItems { get; }
|
||||
IMailItem LatestMailItem { get; }
|
||||
IMailItem FirstMailItem { get; }
|
||||
}
|
||||
ObservableCollection<IMailItem> ThreadItems { get; }
|
||||
IMailItem LatestMailItem { get; }
|
||||
IMailItem FirstMailItem { get; }
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
using MailKit;
|
||||
using MimeKit;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates all required information to create a MimeMessage for IMAP synchronizer.
|
||||
/// </summary>
|
||||
public class ImapMessageCreationPackage
|
||||
{
|
||||
public IMessageSummary MessageSummary { get; }
|
||||
public MimeMessage MimeMessage { get; }
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public ImapMessageCreationPackage(IMessageSummary messageSummary, MimeMessage mimeMessage)
|
||||
{
|
||||
MessageSummary = messageSummary;
|
||||
MimeMessage = mimeMessage;
|
||||
}
|
||||
/// <summary>
|
||||
/// Encapsulates all required information to create a MimeMessage for IMAP synchronizer.
|
||||
/// </summary>
|
||||
public class ImapMessageCreationPackage
|
||||
{
|
||||
public IMessageSummary MessageSummary { get; }
|
||||
public MimeMessage MimeMessage { get; }
|
||||
|
||||
public ImapMessageCreationPackage(IMessageSummary messageSummary, MimeMessage mimeMessage)
|
||||
{
|
||||
MessageSummary = messageSummary;
|
||||
MimeMessage = mimeMessage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
using System;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
public class MailDetailInformation
|
||||
{
|
||||
public string Id { get; set; }
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public Guid AccountId { get; set; }
|
||||
public Guid FolderId { get; set; }
|
||||
public string RemoteFolderId { get; set; }
|
||||
public SpecialFolderType SpecialFolderType { get; set; }
|
||||
public bool IsRead { get; set; }
|
||||
public bool IsFlagged { get; set; }
|
||||
public bool IsDraft { get; set; }
|
||||
}
|
||||
public class MailDetailInformation
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public Guid AccountId { get; set; }
|
||||
public Guid FolderId { get; set; }
|
||||
public string RemoteFolderId { get; set; }
|
||||
public SpecialFolderType SpecialFolderType { get; set; }
|
||||
public bool IsRead { get; set; }
|
||||
public bool IsFlagged { get; set; }
|
||||
public bool IsDraft { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// Class that holds information when the drag/drop of mails are performed.
|
||||
/// </summary>
|
||||
public class MailDragPackage
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that holds information when the drag/drop of mails are performed.
|
||||
/// </summary>
|
||||
public class MailDragPackage
|
||||
public MailDragPackage(IEnumerable<IMailItem> draggingMails)
|
||||
{
|
||||
public MailDragPackage(IEnumerable<IMailItem> draggingMails)
|
||||
{
|
||||
DraggingMails = draggingMails;
|
||||
}
|
||||
|
||||
public MailDragPackage(IMailItem draggingMail)
|
||||
{
|
||||
DraggingMails =
|
||||
[
|
||||
draggingMail
|
||||
];
|
||||
}
|
||||
|
||||
public IEnumerable<IMailItem> DraggingMails { get; set; }
|
||||
DraggingMails = draggingMails;
|
||||
}
|
||||
|
||||
public MailDragPackage(IMailItem draggingMail)
|
||||
{
|
||||
DraggingMails =
|
||||
[
|
||||
draggingMail
|
||||
];
|
||||
}
|
||||
|
||||
public IEnumerable<IMailItem> DraggingMails { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public class MailFolderPairMetadata
|
||||
{
|
||||
public class MailFolderPairMetadata
|
||||
{
|
||||
public Guid FolderId { get; set; }
|
||||
public string RemoteFolderId { get; set; }
|
||||
public string MailCopyId { get; set; }
|
||||
}
|
||||
public Guid FolderId { get; set; }
|
||||
public string RemoteFolderId { get; set; }
|
||||
public string MailCopyId { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using MimeKit;
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
public record NewMailItemPackage(MailCopy Copy, MimeMessage Mime, string AssignedRemoteFolderId);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public record NewMailItemPackage(MailCopy Copy, MimeMessage Mime, string AssignedRemoteFolderId);
|
||||
|
||||
@@ -3,13 +3,12 @@ using System.Collections.Generic;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Folders;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
public record MailListInitializationOptions(IEnumerable<IMailItemFolder> Folders,
|
||||
FilterOptionType FilterType,
|
||||
SortingOptionType SortingOptionType,
|
||||
bool CreateThreads,
|
||||
bool? IsFocusedOnly,
|
||||
string SearchQuery,
|
||||
IEnumerable<Guid> ExistingUniqueIds);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public record MailListInitializationOptions(IEnumerable<IMailItemFolder> Folders,
|
||||
FilterOptionType FilterType,
|
||||
SortingOptionType SortingOptionType,
|
||||
bool CreateThreads,
|
||||
bool? IsFocusedOnly,
|
||||
string SearchQuery,
|
||||
IEnumerable<Guid> ExistingUniqueIds);
|
||||
|
||||
@@ -4,36 +4,35 @@ using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Folders;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates the options for preparing requests to execute mail operations for mail items like Move, Delete, MarkAsRead, etc.
|
||||
/// </summary>
|
||||
/// <param name="Action"> Action to execute. </param>
|
||||
/// <param name="MailItems"> Mail copies execute the action on. </param>
|
||||
/// <param name="ToggleExecution"> Whether the operation can be reverted if needed.
|
||||
/// eg. MarkAsRead on already read item will set the action to MarkAsUnread.
|
||||
/// This is used in hover actions for example. </param>
|
||||
/// <param name="IgnoreHardDeleteProtection"> Whether hard delete protection should be ignored.
|
||||
/// Discard draft requests for example should ignore hard delete protection. </param>
|
||||
/// <param name="MoveTargetFolder"> Moving folder for the Move operation.
|
||||
/// If null and the action is Move, the user will be prompted to select a folder. </param>
|
||||
public record MailOperationPreperationRequest(MailOperation Action, IEnumerable<MailCopy> MailItems, bool ToggleExecution, bool IgnoreHardDeleteProtection, IMailItemFolder MoveTargetFolder)
|
||||
{
|
||||
public MailOperationPreperationRequest(MailOperation action,
|
||||
IEnumerable<MailCopy> mailItems,
|
||||
bool toggleExecution = false,
|
||||
IMailItemFolder moveTargetFolder = null,
|
||||
bool ignoreHardDeleteProtection = false) : this(action, mailItems ?? throw new ArgumentNullException(nameof(mailItems)), toggleExecution, ignoreHardDeleteProtection, moveTargetFolder)
|
||||
{
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public MailOperationPreperationRequest(MailOperation action,
|
||||
MailCopy singleMailItem,
|
||||
bool toggleExecution = false,
|
||||
IMailItemFolder moveTargetFolder = null,
|
||||
bool ignoreHardDeleteProtection = false) : this(action, new List<MailCopy>() { singleMailItem }, toggleExecution, ignoreHardDeleteProtection, moveTargetFolder)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Encapsulates the options for preparing requests to execute mail operations for mail items like Move, Delete, MarkAsRead, etc.
|
||||
/// </summary>
|
||||
/// <param name="Action"> Action to execute. </param>
|
||||
/// <param name="MailItems"> Mail copies execute the action on. </param>
|
||||
/// <param name="ToggleExecution"> Whether the operation can be reverted if needed.
|
||||
/// eg. MarkAsRead on already read item will set the action to MarkAsUnread.
|
||||
/// This is used in hover actions for example. </param>
|
||||
/// <param name="IgnoreHardDeleteProtection"> Whether hard delete protection should be ignored.
|
||||
/// Discard draft requests for example should ignore hard delete protection. </param>
|
||||
/// <param name="MoveTargetFolder"> Moving folder for the Move operation.
|
||||
/// If null and the action is Move, the user will be prompted to select a folder. </param>
|
||||
public record MailOperationPreperationRequest(MailOperation Action, IEnumerable<MailCopy> MailItems, bool ToggleExecution, bool IgnoreHardDeleteProtection, IMailItemFolder MoveTargetFolder)
|
||||
{
|
||||
public MailOperationPreperationRequest(MailOperation action,
|
||||
IEnumerable<MailCopy> mailItems,
|
||||
bool toggleExecution = false,
|
||||
IMailItemFolder moveTargetFolder = null,
|
||||
bool ignoreHardDeleteProtection = false) : this(action, mailItems ?? throw new ArgumentNullException(nameof(mailItems)), toggleExecution, ignoreHardDeleteProtection, moveTargetFolder)
|
||||
{
|
||||
}
|
||||
|
||||
public MailOperationPreperationRequest(MailOperation action,
|
||||
MailCopy singleMailItem,
|
||||
bool toggleExecution = false,
|
||||
IMailItemFolder moveTargetFolder = null,
|
||||
bool ignoreHardDeleteProtection = false) : this(action, new List<MailCopy>() { singleMailItem }, toggleExecution, ignoreHardDeleteProtection, moveTargetFolder)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using MimeKit;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates MimeMessage and the path to the file.
|
||||
/// </summary>
|
||||
public record MimeMessageInformation(MimeMessage MimeMessage, string Path);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates MimeMessage and the path to the file.
|
||||
/// </summary>
|
||||
public record MimeMessageInformation(MimeMessage MimeMessage, string Path);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Class that holds immutable information about special folders in Outlook.
|
||||
/// </summary>
|
||||
/// <param name="InboxId"></param>
|
||||
/// <param name="TrashId"></param>
|
||||
/// <param name="JunkId"></param>
|
||||
/// <param name="DraftId"></param>
|
||||
/// <param name="SentId"></param>
|
||||
/// <param name="ArchiveId"></param>
|
||||
public record OutlookSpecialFolderIdInformation(string InboxId, string TrashId, string JunkId, string DraftId, string SentId, string ArchiveId);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// Class that holds immutable information about special folders in Outlook.
|
||||
/// </summary>
|
||||
/// <param name="InboxId"></param>
|
||||
/// <param name="TrashId"></param>
|
||||
/// <param name="JunkId"></param>
|
||||
/// <param name="DraftId"></param>
|
||||
/// <param name="SentId"></param>
|
||||
/// <param name="ArchiveId"></param>
|
||||
public record OutlookSpecialFolderIdInformation(string InboxId, string TrashId, string JunkId, string DraftId, string SentId, string ArchiveId);
|
||||
|
||||
@@ -4,19 +4,18 @@ using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
using Wino.Core.Domain.Extensions;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
public record SendDraftPreparationRequest(MailCopy MailItem,
|
||||
MailAccountAlias SendingAlias,
|
||||
MailItemFolder SentFolder,
|
||||
MailItemFolder DraftFolder,
|
||||
MailAccountPreferences AccountPreferences,
|
||||
string Base64MimeMessage)
|
||||
{
|
||||
[JsonIgnore]
|
||||
private MimeMessage mime;
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
[JsonIgnore]
|
||||
public MimeMessage Mime => mime ??= Base64MimeMessage.GetMimeMessageFromBase64();
|
||||
}
|
||||
public record SendDraftPreparationRequest(MailCopy MailItem,
|
||||
MailAccountAlias SendingAlias,
|
||||
MailItemFolder SentFolder,
|
||||
MailItemFolder DraftFolder,
|
||||
MailAccountPreferences AccountPreferences,
|
||||
string Base64MimeMessage)
|
||||
{
|
||||
[JsonIgnore]
|
||||
private MimeMessage mime;
|
||||
|
||||
[JsonIgnore]
|
||||
public MimeMessage Mime => mime ??= Base64MimeMessage.GetMimeMessageFromBase64();
|
||||
}
|
||||
|
||||
@@ -5,91 +5,90 @@ using System.Linq;
|
||||
using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Entities.Shared;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
public class ThreadMailItem : IMailItemThread
|
||||
{
|
||||
public class ThreadMailItem : IMailItemThread
|
||||
// TODO: Ideally this should be SortedList.
|
||||
public ObservableCollection<IMailItem> ThreadItems { get; } = new ObservableCollection<IMailItem>();
|
||||
|
||||
public IMailItem LatestMailItem => ThreadItems.LastOrDefault();
|
||||
public IMailItem FirstMailItem => ThreadItems.FirstOrDefault();
|
||||
|
||||
public bool AddThreadItem(IMailItem item)
|
||||
{
|
||||
// TODO: Ideally this should be SortedList.
|
||||
public ObservableCollection<IMailItem> ThreadItems { get; } = new ObservableCollection<IMailItem>();
|
||||
if (item == null) return false;
|
||||
|
||||
public IMailItem LatestMailItem => ThreadItems.LastOrDefault();
|
||||
public IMailItem FirstMailItem => ThreadItems.FirstOrDefault();
|
||||
|
||||
public bool AddThreadItem(IMailItem item)
|
||||
if (ThreadItems.Any(a => a.Id == item.Id))
|
||||
{
|
||||
if (item == null) return false;
|
||||
|
||||
if (ThreadItems.Any(a => a.Id == item.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item != null && item.IsDraft)
|
||||
{
|
||||
ThreadItems.Insert(0, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
var insertItem = ThreadItems.FirstOrDefault(a => !a.IsDraft && a.CreationDate < item.CreationDate);
|
||||
|
||||
if (insertItem == null)
|
||||
ThreadItems.Insert(ThreadItems.Count, item);
|
||||
else
|
||||
{
|
||||
var index = ThreadItems.IndexOf(insertItem);
|
||||
|
||||
ThreadItems.Insert(index, item);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item != null && item.IsDraft)
|
||||
{
|
||||
ThreadItems.Insert(0, item);
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerable<Guid> GetContainingIds() => ThreadItems?.Select(a => a.UniqueId) ?? default;
|
||||
var insertItem = ThreadItems.FirstOrDefault(a => !a.IsDraft && a.CreationDate < item.CreationDate);
|
||||
|
||||
#region IMailItem
|
||||
if (insertItem == null)
|
||||
ThreadItems.Insert(ThreadItems.Count, item);
|
||||
else
|
||||
{
|
||||
var index = ThreadItems.IndexOf(insertItem);
|
||||
|
||||
public Guid UniqueId => LatestMailItem?.UniqueId ?? Guid.Empty;
|
||||
public string Id => LatestMailItem?.Id ?? string.Empty;
|
||||
ThreadItems.Insert(index, item);
|
||||
}
|
||||
|
||||
// Show subject from last item.
|
||||
public string Subject => LatestMailItem?.Subject ?? string.Empty;
|
||||
|
||||
public string ThreadId => LatestMailItem?.ThreadId ?? string.Empty;
|
||||
|
||||
public string PreviewText => FirstMailItem?.PreviewText ?? string.Empty;
|
||||
|
||||
public string FromName => LatestMailItem?.FromName ?? string.Empty;
|
||||
|
||||
public string FromAddress => LatestMailItem?.FromAddress ?? string.Empty;
|
||||
|
||||
public bool HasAttachments => ThreadItems.Any(a => a.HasAttachments);
|
||||
|
||||
public bool IsFlagged => ThreadItems.Any(a => a.IsFlagged);
|
||||
|
||||
public bool IsFocused => LatestMailItem?.IsFocused ?? false;
|
||||
|
||||
public bool IsRead => ThreadItems.All(a => a.IsRead);
|
||||
|
||||
public DateTime CreationDate => FirstMailItem?.CreationDate ?? DateTime.MinValue;
|
||||
|
||||
public bool IsDraft => ThreadItems.Any(a => a.IsDraft);
|
||||
|
||||
public string DraftId => string.Empty;
|
||||
|
||||
public string MessageId => LatestMailItem?.MessageId;
|
||||
|
||||
public string References => LatestMailItem?.References ?? string.Empty;
|
||||
|
||||
public string InReplyTo => LatestMailItem?.InReplyTo ?? string.Empty;
|
||||
|
||||
public MailItemFolder AssignedFolder => LatestMailItem?.AssignedFolder;
|
||||
|
||||
public MailAccount AssignedAccount => LatestMailItem?.AssignedAccount;
|
||||
|
||||
public Guid FileId => LatestMailItem?.FileId ?? Guid.Empty;
|
||||
|
||||
public AccountContact SenderContact => LatestMailItem?.SenderContact;
|
||||
|
||||
#endregion
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerable<Guid> GetContainingIds() => ThreadItems?.Select(a => a.UniqueId) ?? default;
|
||||
|
||||
#region IMailItem
|
||||
|
||||
public Guid UniqueId => LatestMailItem?.UniqueId ?? Guid.Empty;
|
||||
public string Id => LatestMailItem?.Id ?? string.Empty;
|
||||
|
||||
// Show subject from last item.
|
||||
public string Subject => LatestMailItem?.Subject ?? string.Empty;
|
||||
|
||||
public string ThreadId => LatestMailItem?.ThreadId ?? string.Empty;
|
||||
|
||||
public string PreviewText => FirstMailItem?.PreviewText ?? string.Empty;
|
||||
|
||||
public string FromName => LatestMailItem?.FromName ?? string.Empty;
|
||||
|
||||
public string FromAddress => LatestMailItem?.FromAddress ?? string.Empty;
|
||||
|
||||
public bool HasAttachments => ThreadItems.Any(a => a.HasAttachments);
|
||||
|
||||
public bool IsFlagged => ThreadItems.Any(a => a.IsFlagged);
|
||||
|
||||
public bool IsFocused => LatestMailItem?.IsFocused ?? false;
|
||||
|
||||
public bool IsRead => ThreadItems.All(a => a.IsRead);
|
||||
|
||||
public DateTime CreationDate => FirstMailItem?.CreationDate ?? DateTime.MinValue;
|
||||
|
||||
public bool IsDraft => ThreadItems.Any(a => a.IsDraft);
|
||||
|
||||
public string DraftId => string.Empty;
|
||||
|
||||
public string MessageId => LatestMailItem?.MessageId;
|
||||
|
||||
public string References => LatestMailItem?.References ?? string.Empty;
|
||||
|
||||
public string InReplyTo => LatestMailItem?.InReplyTo ?? string.Empty;
|
||||
|
||||
public MailItemFolder AssignedFolder => LatestMailItem?.AssignedFolder;
|
||||
|
||||
public MailAccount AssignedAccount => LatestMailItem?.AssignedAccount;
|
||||
|
||||
public Guid FileId => LatestMailItem?.FileId ?? Guid.Empty;
|
||||
|
||||
public AccountContact SenderContact => LatestMailItem?.SenderContact;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using System;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.MailItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a single rule for toggling user actions if needed.
|
||||
/// For example: If user wants to mark a mail as read, but it's already read, then it should be marked as unread.
|
||||
/// </summary>
|
||||
/// <param name="SourceAction"></param>
|
||||
/// <param name="TargetAction"></param>
|
||||
/// <param name="Condition"></param>
|
||||
public record ToggleRequestRule(MailOperation SourceAction, MailOperation TargetAction, Func<IMailItem, bool> Condition);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
/// <summary>
|
||||
/// Defines a single rule for toggling user actions if needed.
|
||||
/// For example: If user wants to mark a mail as read, but it's already read, then it should be marked as unread.
|
||||
/// </summary>
|
||||
/// <param name="SourceAction"></param>
|
||||
/// <param name="TargetAction"></param>
|
||||
/// <param name="Condition"></param>
|
||||
public record ToggleRequestRule(MailOperation SourceAction, MailOperation TargetAction, Func<IMailItem, bool> Condition);
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Menus
|
||||
namespace Wino.Core.Domain.Models.Menus;
|
||||
|
||||
public class MailOperationMenuItem : MenuOperationItemBase<MailOperation>, IMenuOperation
|
||||
{
|
||||
public class MailOperationMenuItem : MenuOperationItemBase<MailOperation>, IMenuOperation
|
||||
/// <summary>
|
||||
/// Gets or sets whether this menu item should be placed in SecondaryCommands if used in CommandBar.
|
||||
/// </summary>
|
||||
public bool IsSecondaryMenuPreferred { get; set; }
|
||||
|
||||
protected MailOperationMenuItem(MailOperation operation, bool isEnabled, bool isSecondaryMenuItem = false) : base(operation, isEnabled)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets whether this menu item should be placed in SecondaryCommands if used in CommandBar.
|
||||
/// </summary>
|
||||
public bool IsSecondaryMenuPreferred { get; set; }
|
||||
|
||||
protected MailOperationMenuItem(MailOperation operation, bool isEnabled, bool isSecondaryMenuItem = false) : base(operation, isEnabled)
|
||||
{
|
||||
IsSecondaryMenuPreferred = isSecondaryMenuItem;
|
||||
}
|
||||
|
||||
public static MailOperationMenuItem Create(MailOperation operation, bool isEnabled = true, bool isSecondaryMenuItem = false)
|
||||
=> new MailOperationMenuItem(operation, isEnabled, isSecondaryMenuItem);
|
||||
IsSecondaryMenuPreferred = isSecondaryMenuItem;
|
||||
}
|
||||
|
||||
public static MailOperationMenuItem Create(MailOperation operation, bool isEnabled = true, bool isSecondaryMenuItem = false)
|
||||
=> new MailOperationMenuItem(operation, isEnabled, isSecondaryMenuItem);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Menus
|
||||
{
|
||||
public class MenuOperationItemBase<TOperation> where TOperation : Enum
|
||||
{
|
||||
public MenuOperationItemBase(TOperation operation, bool isEnabled)
|
||||
{
|
||||
Operation = operation;
|
||||
IsEnabled = isEnabled;
|
||||
Identifier = operation.ToString();
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Menus;
|
||||
|
||||
public TOperation Operation { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public bool IsEnabled { get; set; }
|
||||
public class MenuOperationItemBase<TOperation> where TOperation : Enum
|
||||
{
|
||||
public MenuOperationItemBase(TOperation operation, bool isEnabled)
|
||||
{
|
||||
Operation = operation;
|
||||
IsEnabled = isEnabled;
|
||||
Identifier = operation.ToString();
|
||||
}
|
||||
|
||||
public TOperation Operation { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public bool IsEnabled { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
using System.Threading.Tasks;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Navigation
|
||||
namespace Wino.Core.Domain.Models.Navigation;
|
||||
|
||||
public class NavigateMailFolderEventArgs
|
||||
{
|
||||
public class NavigateMailFolderEventArgs
|
||||
public NavigateMailFolderEventArgs(IBaseFolderMenuItem baseFolderMenuItem, TaskCompletionSource<bool> folderInitLoadAwaitTask = null)
|
||||
{
|
||||
public NavigateMailFolderEventArgs(IBaseFolderMenuItem baseFolderMenuItem, TaskCompletionSource<bool> folderInitLoadAwaitTask = null)
|
||||
{
|
||||
BaseFolderMenuItem = baseFolderMenuItem;
|
||||
FolderInitLoadAwaitTask = folderInitLoadAwaitTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base folder menu item.
|
||||
/// </summary>
|
||||
public IBaseFolderMenuItem BaseFolderMenuItem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Completion source for waiting folder's mail initialization.
|
||||
/// </summary>
|
||||
public TaskCompletionSource<bool> FolderInitLoadAwaitTask { get; }
|
||||
BaseFolderMenuItem = baseFolderMenuItem;
|
||||
FolderInitLoadAwaitTask = folderInitLoadAwaitTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base folder menu item.
|
||||
/// </summary>
|
||||
public IBaseFolderMenuItem BaseFolderMenuItem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Completion source for waiting folder's mail initialization.
|
||||
/// </summary>
|
||||
public TaskCompletionSource<bool> FolderInitLoadAwaitTask { get; }
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
namespace Wino.Core.Domain.Models.Navigation
|
||||
namespace Wino.Core.Domain.Models.Navigation;
|
||||
|
||||
public enum NavigationMode
|
||||
{
|
||||
public enum NavigationMode
|
||||
{
|
||||
New,
|
||||
Back,
|
||||
Forward,
|
||||
Refresh
|
||||
}
|
||||
New,
|
||||
Back,
|
||||
Forward,
|
||||
Refresh
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
namespace Wino.Core.Domain.Models.Navigation
|
||||
namespace Wino.Core.Domain.Models.Navigation;
|
||||
|
||||
public enum NavigationTransitionType
|
||||
{
|
||||
public enum NavigationTransitionType
|
||||
{
|
||||
None, // Supress
|
||||
DrillIn,
|
||||
Entrance,
|
||||
}
|
||||
None, // Supress
|
||||
DrillIn,
|
||||
Entrance,
|
||||
}
|
||||
|
||||
@@ -2,28 +2,27 @@
|
||||
using System.Threading.Tasks;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Personalization
|
||||
namespace Wino.Core.Domain.Models.Personalization;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all app themes.
|
||||
/// </summary>
|
||||
public abstract class AppThemeBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all app themes.
|
||||
/// </summary>
|
||||
public abstract class AppThemeBase
|
||||
public Guid Id { get; set; }
|
||||
public string ThemeName { get; set; }
|
||||
public ApplicationElementTheme ForceElementTheme { get; set; }
|
||||
public string AccentColor { get; set; }
|
||||
public bool IsAccentColorAssigned => !string.IsNullOrEmpty(AccentColor);
|
||||
public string BackgroundPreviewImage => GetBackgroundPreviewImagePath();
|
||||
public abstract AppThemeType AppThemeType { get; }
|
||||
|
||||
protected AppThemeBase(string themeName, Guid id)
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string ThemeName { get; set; }
|
||||
public ApplicationElementTheme ForceElementTheme { get; set; }
|
||||
public string AccentColor { get; set; }
|
||||
public bool IsAccentColorAssigned => !string.IsNullOrEmpty(AccentColor);
|
||||
public string BackgroundPreviewImage => GetBackgroundPreviewImagePath();
|
||||
public abstract AppThemeType AppThemeType { get; }
|
||||
|
||||
protected AppThemeBase(string themeName, Guid id)
|
||||
{
|
||||
ThemeName = themeName;
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public abstract Task<string> GetThemeResourceDictionaryContentAsync();
|
||||
public abstract string GetBackgroundPreviewImagePath();
|
||||
ThemeName = themeName;
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public abstract Task<string> GetThemeResourceDictionaryContentAsync();
|
||||
public abstract string GetBackgroundPreviewImagePath();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Personalization
|
||||
namespace Wino.Core.Domain.Models.Personalization;
|
||||
|
||||
public class CustomThemeMetadata
|
||||
{
|
||||
public class CustomThemeMetadata
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string AccentColorHex { get; set; }
|
||||
public bool HasCustomAccentColor => !string.IsNullOrEmpty(AccentColorHex);
|
||||
}
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string AccentColorHex { get; set; }
|
||||
public bool HasCustomAccentColor => !string.IsNullOrEmpty(AccentColorHex);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Personalization
|
||||
{
|
||||
public class ElementThemeContainer
|
||||
{
|
||||
public ElementThemeContainer(ApplicationElementTheme nativeTheme, string title)
|
||||
{
|
||||
NativeTheme = nativeTheme;
|
||||
Title = title;
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Personalization;
|
||||
|
||||
public ApplicationElementTheme NativeTheme { get; set; }
|
||||
public string Title { get; set; }
|
||||
public class ElementThemeContainer
|
||||
{
|
||||
public ElementThemeContainer(ApplicationElementTheme nativeTheme, string title)
|
||||
{
|
||||
NativeTheme = nativeTheme;
|
||||
Title = title;
|
||||
}
|
||||
|
||||
public ApplicationElementTheme NativeTheme { get; set; }
|
||||
public string Title { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Printing
|
||||
{
|
||||
public class PrintInformation
|
||||
{
|
||||
public PrintInformation(string pDFFilePath, string pDFTitle)
|
||||
{
|
||||
PDFFilePath = pDFFilePath ?? throw new ArgumentNullException(nameof(pDFFilePath));
|
||||
PDFTitle = pDFTitle ?? throw new ArgumentNullException(nameof(pDFTitle));
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Printing;
|
||||
|
||||
public string PDFFilePath { get; }
|
||||
public string PDFTitle { get; }
|
||||
public class PrintInformation
|
||||
{
|
||||
public PrintInformation(string pDFFilePath, string pDFTitle)
|
||||
{
|
||||
PDFFilePath = pDFFilePath ?? throw new ArgumentNullException(nameof(pDFFilePath));
|
||||
PDFTitle = pDFTitle ?? throw new ArgumentNullException(nameof(pDFTitle));
|
||||
}
|
||||
|
||||
public string PDFFilePath { get; }
|
||||
public string PDFTitle { get; }
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Reader
|
||||
{
|
||||
public class FilterOption
|
||||
{
|
||||
public FilterOptionType Type { get; set; }
|
||||
public string Title { get; set; }
|
||||
namespace Wino.Core.Domain.Models.Reader;
|
||||
|
||||
public FilterOption(string title, FilterOptionType type)
|
||||
{
|
||||
Title = title;
|
||||
Type = type;
|
||||
}
|
||||
public class FilterOption
|
||||
{
|
||||
public FilterOptionType Type { get; set; }
|
||||
public string Title { get; set; }
|
||||
|
||||
public FilterOption(string title, FilterOptionType type)
|
||||
{
|
||||
Title = title;
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using MimeKit;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Reader
|
||||
namespace Wino.Core.Domain.Models.Reader;
|
||||
|
||||
/// <summary>
|
||||
/// Final model to be passed to renderer page.
|
||||
/// Data here are created based on rendering settings.
|
||||
/// </summary>
|
||||
public class MailRenderModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Final model to be passed to renderer page.
|
||||
/// Data here are created based on rendering settings.
|
||||
/// </summary>
|
||||
public class MailRenderModel
|
||||
public string RenderHtml { get; }
|
||||
public MailRenderingOptions MailRenderingOptions { get; }
|
||||
public List<MimePart> Attachments { get; set; } = [];
|
||||
|
||||
public UnsubscribeInfo UnsubscribeInfo { get; set; }
|
||||
|
||||
public MailRenderModel(string renderHtml, MailRenderingOptions mailRenderingOptions = null)
|
||||
{
|
||||
public string RenderHtml { get; }
|
||||
public MailRenderingOptions MailRenderingOptions { get; }
|
||||
public List<MimePart> Attachments { get; set; } = [];
|
||||
|
||||
public UnsubscribeInfo UnsubscribeInfo { get; set; }
|
||||
|
||||
public MailRenderModel(string renderHtml, MailRenderingOptions mailRenderingOptions = null)
|
||||
{
|
||||
RenderHtml = renderHtml;
|
||||
MailRenderingOptions = mailRenderingOptions;
|
||||
}
|
||||
}
|
||||
|
||||
public class UnsubscribeInfo
|
||||
{
|
||||
public string HttpLink { get; set; }
|
||||
public string MailToLink { get; set; }
|
||||
public bool IsOneClick { get; set; }
|
||||
public bool CanUnsubscribe => HttpLink != null || MailToLink != null;
|
||||
RenderHtml = renderHtml;
|
||||
MailRenderingOptions = mailRenderingOptions;
|
||||
}
|
||||
}
|
||||
|
||||
public class UnsubscribeInfo
|
||||
{
|
||||
public string HttpLink { get; set; }
|
||||
public string MailToLink { get; set; }
|
||||
public bool IsOneClick { get; set; }
|
||||
public bool CanUnsubscribe => HttpLink != null || MailToLink != null;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
namespace Wino.Core.Domain.Models.Reader
|
||||
namespace Wino.Core.Domain.Models.Reader;
|
||||
|
||||
/// <summary>
|
||||
/// Rendering options for mail.
|
||||
/// </summary>
|
||||
public class MailRenderingOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Rendering options for mail.
|
||||
/// </summary>
|
||||
public class MailRenderingOptions
|
||||
{
|
||||
private const bool DefaultLoadImageValue = true;
|
||||
private const bool DefaultLoadStylesValue = true;
|
||||
private const bool DefaultRenderPlaintextLinksValue = true;
|
||||
private const bool DefaultLoadImageValue = true;
|
||||
private const bool DefaultLoadStylesValue = true;
|
||||
private const bool DefaultRenderPlaintextLinksValue = true;
|
||||
|
||||
public bool LoadImages { get; set; } = DefaultLoadImageValue;
|
||||
public bool LoadStyles { get; set; } = DefaultLoadStylesValue;
|
||||
public bool RenderPlaintextLinks { get; set; } = DefaultRenderPlaintextLinksValue;
|
||||
public bool LoadImages { get; set; } = DefaultLoadImageValue;
|
||||
public bool LoadStyles { get; set; } = DefaultLoadStylesValue;
|
||||
public bool RenderPlaintextLinks { get; set; } = DefaultRenderPlaintextLinksValue;
|
||||
|
||||
// HtmlDocument.Load call is redundant if all the settings are in default values.
|
||||
// Therefore we will purify the HTML only if needed.
|
||||
// HtmlDocument.Load call is redundant if all the settings are in default values.
|
||||
// Therefore we will purify the HTML only if needed.
|
||||
|
||||
public bool IsPurifyingNeeded() => LoadImages != DefaultLoadImageValue || LoadStyles != DefaultLoadStylesValue;
|
||||
}
|
||||
public bool IsPurifyingNeeded() => LoadImages != DefaultLoadImageValue || LoadStyles != DefaultLoadStylesValue;
|
||||
}
|
||||
|
||||
@@ -3,27 +3,26 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Comparers;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Reader
|
||||
{
|
||||
public class SortingOption
|
||||
{
|
||||
public SortingOptionType Type { get; set; }
|
||||
public string Title { get; set; }
|
||||
public IComparer<IMailItem> Comparer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Type == SortingOptionType.ReceiveDate)
|
||||
return new DateComparer();
|
||||
else
|
||||
return new NameComparer();
|
||||
}
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Reader;
|
||||
|
||||
public SortingOption(string title, SortingOptionType type)
|
||||
public class SortingOption
|
||||
{
|
||||
public SortingOptionType Type { get; set; }
|
||||
public string Title { get; set; }
|
||||
public IComparer<IMailItem> Comparer
|
||||
{
|
||||
get
|
||||
{
|
||||
Title = title;
|
||||
Type = type;
|
||||
if (Type == SortingOptionType.ReceiveDate)
|
||||
return new DateComparer();
|
||||
else
|
||||
return new NameComparer();
|
||||
}
|
||||
}
|
||||
|
||||
public SortingOption(string title, SortingOptionType type)
|
||||
{
|
||||
Title = title;
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Reader
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to pass messages from the webview to the app.
|
||||
/// </summary>
|
||||
public class WebViewMessage
|
||||
{
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; }
|
||||
namespace Wino.Core.Domain.Models.Reader;
|
||||
|
||||
[JsonPropertyName("value")]
|
||||
public string Value { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Used to pass messages from the webview to the app.
|
||||
/// </summary>
|
||||
public class WebViewMessage
|
||||
{
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonPropertyName("value")]
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
@@ -4,37 +4,36 @@ using Wino.Core.Domain.Entities.Mail;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Requests
|
||||
namespace Wino.Core.Domain.Models.Requests;
|
||||
|
||||
public abstract record RequestBase<TOperation> where TOperation : Enum
|
||||
{
|
||||
public abstract record RequestBase<TOperation> where TOperation : Enum
|
||||
{
|
||||
public virtual void ApplyUIChanges() { }
|
||||
public virtual void RevertUIChanges() { }
|
||||
public virtual int ResynchronizationDelay => 0;
|
||||
public abstract TOperation Operation { get; }
|
||||
public virtual object GroupingKey() { return Operation; }
|
||||
}
|
||||
|
||||
public abstract record MailRequestBase(MailCopy Item) : RequestBase<MailSynchronizerOperation>, IMailActionRequest
|
||||
{
|
||||
}
|
||||
|
||||
public abstract record FolderRequestBase(MailItemFolder Folder, FolderSynchronizerOperation Operation) : IFolderActionRequest
|
||||
{
|
||||
public abstract void ApplyUIChanges();
|
||||
public abstract void RevertUIChanges();
|
||||
|
||||
public virtual int ResynchronizationDelay => 0;
|
||||
|
||||
public virtual object GroupingKey() { return Operation; }
|
||||
}
|
||||
|
||||
public class BatchCollection<TRequestType> : List<TRequestType>, IUIChangeRequest where TRequestType : IUIChangeRequest
|
||||
{
|
||||
public BatchCollection(IEnumerable<TRequestType> collection) : base(collection)
|
||||
{
|
||||
}
|
||||
public void ApplyUIChanges() => ForEach(x => x.ApplyUIChanges());
|
||||
public void RevertUIChanges() => ForEach(x => x.RevertUIChanges());
|
||||
}
|
||||
public virtual void ApplyUIChanges() { }
|
||||
public virtual void RevertUIChanges() { }
|
||||
public virtual int ResynchronizationDelay => 0;
|
||||
public abstract TOperation Operation { get; }
|
||||
public virtual object GroupingKey() { return Operation; }
|
||||
}
|
||||
|
||||
public abstract record MailRequestBase(MailCopy Item) : RequestBase<MailSynchronizerOperation>, IMailActionRequest
|
||||
{
|
||||
}
|
||||
|
||||
public abstract record FolderRequestBase(MailItemFolder Folder, FolderSynchronizerOperation Operation) : IFolderActionRequest
|
||||
{
|
||||
public abstract void ApplyUIChanges();
|
||||
public abstract void RevertUIChanges();
|
||||
|
||||
public virtual int ResynchronizationDelay => 0;
|
||||
|
||||
public virtual object GroupingKey() { return Operation; }
|
||||
}
|
||||
|
||||
public class BatchCollection<TRequestType> : List<TRequestType>, IUIChangeRequest where TRequestType : IUIChangeRequest
|
||||
{
|
||||
public BatchCollection(IEnumerable<TRequestType> collection) : base(collection)
|
||||
{
|
||||
}
|
||||
public void ApplyUIChanges() => ForEach(x => x.ApplyUIChanges());
|
||||
public void RevertUIChanges() => ForEach(x => x.RevertUIChanges());
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
using System;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Requests
|
||||
namespace Wino.Core.Domain.Models.Requests;
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates request to queue and account for synchronizer.
|
||||
/// </summary>
|
||||
/// <param name="AccountId">Which account to execute this request for.</param>
|
||||
/// <param name="Request">Prepared request for the server.</param>
|
||||
public record ServerRequestPackage(Guid AccountId, IRequestBase Request) : IClientMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates request to queue and account for synchronizer.
|
||||
/// </summary>
|
||||
/// <param name="AccountId">Which account to execute this request for.</param>
|
||||
/// <param name="Request">Prepared request for the server.</param>
|
||||
public record ServerRequestPackage(Guid AccountId, IRequestBase Request) : IClientMessage
|
||||
{
|
||||
public override string ToString() => $"Server Package: {Request.GetType().Name}";
|
||||
}
|
||||
public override string ToString() => $"Server Package: {Request.GetType().Name}";
|
||||
}
|
||||
|
||||
@@ -1,40 +1,39 @@
|
||||
using Wino.Core.Domain.Exceptions;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Server
|
||||
namespace Wino.Core.Domain.Models.Server;
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates responses from the Wino server.
|
||||
/// Exceptions are stored separately in the Message and StackTrace properties due to serialization issues.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the expected response.</typeparam>
|
||||
public class WinoServerResponse<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates responses from the Wino server.
|
||||
/// Exceptions are stored separately in the Message and StackTrace properties due to serialization issues.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the expected response.</typeparam>
|
||||
public class WinoServerResponse<T>
|
||||
public bool IsSuccess { get; set; }
|
||||
public string Message { get; set; }
|
||||
public T Data { get; set; }
|
||||
|
||||
public static WinoServerResponse<T> CreateSuccessResponse(T data)
|
||||
{
|
||||
public bool IsSuccess { get; set; }
|
||||
public string Message { get; set; }
|
||||
public T Data { get; set; }
|
||||
|
||||
public static WinoServerResponse<T> CreateSuccessResponse(T data)
|
||||
return new WinoServerResponse<T>
|
||||
{
|
||||
return new WinoServerResponse<T>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = data
|
||||
};
|
||||
}
|
||||
IsSuccess = true,
|
||||
Data = data
|
||||
};
|
||||
}
|
||||
|
||||
public static WinoServerResponse<T> CreateErrorResponse(string message)
|
||||
public static WinoServerResponse<T> CreateErrorResponse(string message)
|
||||
{
|
||||
return new WinoServerResponse<T>
|
||||
{
|
||||
return new WinoServerResponse<T>
|
||||
{
|
||||
IsSuccess = false,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
IsSuccess = false,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
|
||||
public void ThrowIfFailed()
|
||||
{
|
||||
if (!IsSuccess)
|
||||
throw new WinoServerException(Message);
|
||||
}
|
||||
public void ThrowIfFailed()
|
||||
{
|
||||
if (!IsSuccess)
|
||||
throw new WinoServerException(Message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Settings
|
||||
{
|
||||
public record SettingOption(string Title, string Description, WinoPage NavigationPage, string PathIcon);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Settings;
|
||||
|
||||
public record SettingOption(string Title, string Description, WinoPage NavigationPage, string PathIcon);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace Wino.Core.Domain.Models.Store
|
||||
namespace Wino.Core.Domain.Models.Store;
|
||||
|
||||
public enum StoreProductType
|
||||
{
|
||||
public enum StoreProductType
|
||||
{
|
||||
UnlimitedAccounts
|
||||
}
|
||||
UnlimitedAccounts
|
||||
}
|
||||
|
||||
@@ -2,30 +2,29 @@
|
||||
using System.Collections.Generic;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Synchronization
|
||||
namespace Wino.Core.Domain.Models.Synchronization;
|
||||
|
||||
public class CalendarSynchronizationOptions
|
||||
{
|
||||
public class CalendarSynchronizationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique id of synchronization.
|
||||
/// </summary>
|
||||
public Guid Id { get; } = Guid.NewGuid();
|
||||
/// <summary>
|
||||
/// Unique id of synchronization.
|
||||
/// </summary>
|
||||
public Guid Id { get; } = Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// Account to execute synchronization for.
|
||||
/// </summary>
|
||||
public Guid AccountId { get; set; }
|
||||
/// <summary>
|
||||
/// Account to execute synchronization for.
|
||||
/// </summary>
|
||||
public Guid AccountId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of the synchronization to be performed.
|
||||
/// </summary>
|
||||
public CalendarSynchronizationType Type { get; set; }
|
||||
/// <summary>
|
||||
/// Type of the synchronization to be performed.
|
||||
/// </summary>
|
||||
public CalendarSynchronizationType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Calendar ids to synchronize.
|
||||
/// </summary>
|
||||
public List<Guid> SynchronizationCalendarIds { get; set; }
|
||||
/// <summary>
|
||||
/// Calendar ids to synchronize.
|
||||
/// </summary>
|
||||
public List<Guid> SynchronizationCalendarIds { get; set; }
|
||||
|
||||
public override string ToString() => $"Type: {Type}, Calendars: {(SynchronizationCalendarIds == null ? "All" : string.Join(",", SynchronizationCalendarIds))}";
|
||||
}
|
||||
public override string ToString() => $"Type: {Type}, Calendars: {(SynchronizationCalendarIds == null ? "All" : string.Join(",", SynchronizationCalendarIds))}";
|
||||
}
|
||||
|
||||
@@ -4,43 +4,42 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Accounts;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Synchronization
|
||||
namespace Wino.Core.Domain.Models.Synchronization;
|
||||
|
||||
public class CalendarSynchronizationResult
|
||||
{
|
||||
public class CalendarSynchronizationResult
|
||||
{
|
||||
public CalendarSynchronizationResult() { }
|
||||
public CalendarSynchronizationResult() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new downloaded events from synchronization.
|
||||
/// Server will create notifications for these event.
|
||||
/// It's ignored in serialization. Client should not react to this.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<ICalendarItem> DownloadedEvents { get; set; } = [];
|
||||
/// <summary>
|
||||
/// Gets the new downloaded events from synchronization.
|
||||
/// Server will create notifications for these event.
|
||||
/// It's ignored in serialization. Client should not react to this.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<ICalendarItem> DownloadedEvents { get; set; } = [];
|
||||
|
||||
public ProfileInformation ProfileInformation { get; set; }
|
||||
public ProfileInformation ProfileInformation { get; set; }
|
||||
|
||||
public SynchronizationCompletedState CompletedState { get; set; }
|
||||
public SynchronizationCompletedState CompletedState { get; set; }
|
||||
|
||||
public static CalendarSynchronizationResult Empty => new() { CompletedState = SynchronizationCompletedState.Success };
|
||||
public static CalendarSynchronizationResult Empty => new() { CompletedState = SynchronizationCompletedState.Success };
|
||||
|
||||
// Mail synchronization
|
||||
public static CalendarSynchronizationResult Completed(IEnumerable<ICalendarItem> downloadedEvent)
|
||||
=> new()
|
||||
{
|
||||
DownloadedEvents = downloadedEvent,
|
||||
CompletedState = SynchronizationCompletedState.Success
|
||||
};
|
||||
// Mail synchronization
|
||||
public static CalendarSynchronizationResult Completed(IEnumerable<ICalendarItem> downloadedEvent)
|
||||
=> new()
|
||||
{
|
||||
DownloadedEvents = downloadedEvent,
|
||||
CompletedState = SynchronizationCompletedState.Success
|
||||
};
|
||||
|
||||
// Profile synchronization
|
||||
public static CalendarSynchronizationResult Completed(ProfileInformation profileInformation)
|
||||
=> new()
|
||||
{
|
||||
ProfileInformation = profileInformation,
|
||||
CompletedState = SynchronizationCompletedState.Success
|
||||
};
|
||||
// Profile synchronization
|
||||
public static CalendarSynchronizationResult Completed(ProfileInformation profileInformation)
|
||||
=> new()
|
||||
{
|
||||
ProfileInformation = profileInformation,
|
||||
CompletedState = SynchronizationCompletedState.Success
|
||||
};
|
||||
|
||||
public static CalendarSynchronizationResult Canceled => new() { CompletedState = SynchronizationCompletedState.Canceled };
|
||||
public static CalendarSynchronizationResult Failed => new() { CompletedState = SynchronizationCompletedState.Failed };
|
||||
}
|
||||
public static CalendarSynchronizationResult Canceled => new() { CompletedState = SynchronizationCompletedState.Canceled };
|
||||
public static CalendarSynchronizationResult Failed => new() { CompletedState = SynchronizationCompletedState.Failed };
|
||||
}
|
||||
|
||||
@@ -2,43 +2,42 @@
|
||||
using System.Collections.Generic;
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Synchronization
|
||||
namespace Wino.Core.Domain.Models.Synchronization;
|
||||
|
||||
public class MailSynchronizationOptions
|
||||
{
|
||||
public class MailSynchronizationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique id of synchronization.
|
||||
/// </summary>
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
/// <summary>
|
||||
/// Unique id of synchronization.
|
||||
/// </summary>
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// Account to execute synchronization for.
|
||||
/// </summary>
|
||||
public Guid AccountId { get; set; }
|
||||
/// <summary>
|
||||
/// Account to execute synchronization for.
|
||||
/// </summary>
|
||||
public Guid AccountId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of the synchronization to be performed.
|
||||
/// </summary>
|
||||
public MailSynchronizationType Type { get; set; }
|
||||
/// <summary>
|
||||
/// Type of the synchronization to be performed.
|
||||
/// </summary>
|
||||
public MailSynchronizationType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Collection of FolderId to perform SynchronizationType.Custom type sync.
|
||||
/// </summary>
|
||||
public List<Guid> SynchronizationFolderIds { get; set; }
|
||||
/// <summary>
|
||||
/// Collection of FolderId to perform SynchronizationType.Custom type sync.
|
||||
/// </summary>
|
||||
public List<Guid> SynchronizationFolderIds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, additional folders like Sent,Drafts and Deleted will not be synchronized
|
||||
/// with InboxOnly and CustomFolders sync type.
|
||||
/// </summary>
|
||||
public bool ExcludeMustHaveFolders { get; set; }
|
||||
/// <summary>
|
||||
/// If true, additional folders like Sent,Drafts and Deleted will not be synchronized
|
||||
/// with InboxOnly and CustomFolders sync type.
|
||||
/// </summary>
|
||||
public bool ExcludeMustHaveFolders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When doing a linked inbox synchronization, we must ignore reporting completion to the caller for each folder.
|
||||
/// This Id will help tracking that. Id is unique, but this one can be the same for all sync requests
|
||||
/// inside the same linked inbox sync.
|
||||
/// </summary>
|
||||
public Guid? GroupedSynchronizationTrackingId { get; set; }
|
||||
/// <summary>
|
||||
/// When doing a linked inbox synchronization, we must ignore reporting completion to the caller for each folder.
|
||||
/// This Id will help tracking that. Id is unique, but this one can be the same for all sync requests
|
||||
/// inside the same linked inbox sync.
|
||||
/// </summary>
|
||||
public Guid? GroupedSynchronizationTrackingId { get; set; }
|
||||
|
||||
public override string ToString() => $"Type: {Type}";
|
||||
}
|
||||
public override string ToString() => $"Type: {Type}";
|
||||
}
|
||||
|
||||
@@ -4,43 +4,42 @@ using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.Accounts;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Synchronization
|
||||
namespace Wino.Core.Domain.Models.Synchronization;
|
||||
|
||||
public class MailSynchronizationResult
|
||||
{
|
||||
public class MailSynchronizationResult
|
||||
{
|
||||
public MailSynchronizationResult() { }
|
||||
public MailSynchronizationResult() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the new downloaded messages from synchronization.
|
||||
/// Server will create notifications for these messages.
|
||||
/// It's ignored in serialization. Client should not react to this.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<IMailItem> DownloadedMessages { get; set; } = [];
|
||||
/// <summary>
|
||||
/// Gets the new downloaded messages from synchronization.
|
||||
/// Server will create notifications for these messages.
|
||||
/// It's ignored in serialization. Client should not react to this.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public IEnumerable<IMailItem> DownloadedMessages { get; set; } = [];
|
||||
|
||||
public ProfileInformation ProfileInformation { get; set; }
|
||||
public ProfileInformation ProfileInformation { get; set; }
|
||||
|
||||
public SynchronizationCompletedState CompletedState { get; set; }
|
||||
public SynchronizationCompletedState CompletedState { get; set; }
|
||||
|
||||
public static MailSynchronizationResult Empty => new() { CompletedState = SynchronizationCompletedState.Success };
|
||||
public static MailSynchronizationResult Empty => new() { CompletedState = SynchronizationCompletedState.Success };
|
||||
|
||||
// Mail synchronization
|
||||
public static MailSynchronizationResult Completed(IEnumerable<IMailItem> downloadedMessages)
|
||||
=> new()
|
||||
{
|
||||
DownloadedMessages = downloadedMessages,
|
||||
CompletedState = SynchronizationCompletedState.Success
|
||||
};
|
||||
// Mail synchronization
|
||||
public static MailSynchronizationResult Completed(IEnumerable<IMailItem> downloadedMessages)
|
||||
=> new()
|
||||
{
|
||||
DownloadedMessages = downloadedMessages,
|
||||
CompletedState = SynchronizationCompletedState.Success
|
||||
};
|
||||
|
||||
// Profile synchronization
|
||||
public static MailSynchronizationResult Completed(ProfileInformation profileInformation)
|
||||
=> new()
|
||||
{
|
||||
ProfileInformation = profileInformation,
|
||||
CompletedState = SynchronizationCompletedState.Success
|
||||
};
|
||||
// Profile synchronization
|
||||
public static MailSynchronizationResult Completed(ProfileInformation profileInformation)
|
||||
=> new()
|
||||
{
|
||||
ProfileInformation = profileInformation,
|
||||
CompletedState = SynchronizationCompletedState.Success
|
||||
};
|
||||
|
||||
public static MailSynchronizationResult Canceled => new() { CompletedState = SynchronizationCompletedState.Canceled };
|
||||
public static MailSynchronizationResult Failed => new() { CompletedState = SynchronizationCompletedState.Failed };
|
||||
}
|
||||
public static MailSynchronizationResult Canceled => new() { CompletedState = SynchronizationCompletedState.Canceled };
|
||||
public static MailSynchronizationResult Failed => new() { CompletedState = SynchronizationCompletedState.Failed };
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Wino.Core.Domain.Enums;
|
||||
|
||||
namespace Wino.Core.Domain.Models.Translations
|
||||
{
|
||||
public record AppLanguageModel(AppLanguage Language, string DisplayName);
|
||||
}
|
||||
namespace Wino.Core.Domain.Models.Translations;
|
||||
|
||||
public record AppLanguageModel(AppLanguage Language, string DisplayName);
|
||||
|
||||
Reference in New Issue
Block a user