using System.Net;
using System.Net.Http.Json;
using System.Net.Mail;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using decePubClient.Models;
using decePubClient.Services;
using PrivaPub.ClientModels;
namespace decePubClient.Helpers
{
public static class SUtility
{
public const string dateTimeFormat = "dd/MM/yyyy";
public const string timeFormat = "HH:mm";
public const string dateTimeFormatExt = "dd/MM/yyyy HH:mm";
/// Marks a response HttpService made up itself (a classified failure), not one the server sent.
public const string FailureHeader = "X-DecePub-Failure";
/// The JSON options for every HTTP payload: web defaults, ClientJsonContext first, reflection after.
public static JsonSerializerOptions DefaultSerializer { get; } = new(JsonSerializerDefaults.Web)
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
TypeInfoResolver = JsonTypeInfoResolver.Combine(ClientJsonContext.Default, new DefaultJsonTypeInfoResolver())
};
/// What a cancelled request turns into: HTTP 410, which AddResponseError ignores.
public static WebResult DefaultTaskCancelledResult =>
new WebResult().Invalidate("Request cancelled, not an error.", (int)HttpStatusCode.Gone, FailureCodes.Cancelled);
public static HttpResponseMessage DefaultTaskCancelled() =>
ClassifiedFailure(FailureCodes.Cancelled, DefaultTaskCancelledResult.ErrorMessage, HttpStatusCode.Gone);
public static HttpResponseMessage DefaultUnauthorized(string message) =>
ClassifiedFailure(FailureCodes.Unauthorized, message, HttpStatusCode.Unauthorized);
/// A response carrying an invalid WebResult, tagged with FailureHeader so ReadWebResult reads it as is.
public static HttpResponseMessage ClassifiedFailure(string failureCode, string message, HttpStatusCode statusCode)
{
var content = JsonContent.Create(new WebResult().Invalidate(message, (int)statusCode, failureCode), options: DefaultSerializer);
content.Headers.Add(FailureHeader, failureCode);
return new(statusCode) { Content = content };
}
/// The value a cancelled read returns: default for value types, an empty list for list interfaces.
public static T CancellationDefault()
{
var type = typeof(T);
if (type.IsValueType)
return default;
if (type.IsInterface && type.IsGenericType &&
type.GetGenericTypeDefinition() is var definition &&
(definition == typeof(IEnumerable<>) || definition == typeof(ICollection<>) || definition == typeof(IList<>) ||
definition == typeof(IReadOnlyCollection<>) || definition == typeof(IReadOnlyList<>)))
return (T)Activator.CreateInstance(typeof(List<>).MakeGenericType(type.GetGenericArguments()[0]));
if (type.IsInterface || type.IsAbstract || type.GetConstructor(Type.EmptyTypes) is null)
return default;
return Activator.CreateInstance();
}
#region Toasts
/// Shows a failed result: connectivity failures as warnings, the rest as errors; cancellations not at all.
public static void AddResponseError(this ToastService toast, WebResult result)
{
if (result.StatusCode is (int)HttpStatusCode.Gone)
return;
if (FailureCodes.IsConnectivity(result.ErrorCode))
{
toast.Add(ToastSeverity.Warning, result.ErrorMessage);
return;
}
toast.Add(ToastSeverity.Error, $"{result.StatusCode} - {result.ErrorMessage}");
}
public static void AddResponseError(this ToastService toast, string message) =>
toast.Add(ToastSeverity.Error, message);
public static void AddResponseSuccess(this ToastService toast, string message) =>
toast.Add(ToastSeverity.Success, message, TimeSpan.FromSeconds(5));
public static void AddWarning(this ToastService toast, string message) =>
toast.Add(ToastSeverity.Warning, message);
public static void AddException(this ToastService toast, Exception exception) =>
toast.Add(ToastSeverity.Error, exception.Message);
#endregion
public static T IfTrueThen(bool conditionIsTrue, T ifTrue, T ifFalse = default) =>
conditionIsTrue ? ifTrue : ifFalse;
public static bool TimeBetween(this DateTime datetime, TimeSpan start, TimeSpan end)
{
// convert datetime to a TimeSpan
var now = datetime.TimeOfDay;
// see if start comes before end
if (start < end)
return start <= now && now <= end;
// start is after end, so do the inverse comparison
return !(end < now && now < start);
}
public static string GetFileIcon(string fileName)
{
switch (Path.GetExtension(fileName))
{
case ".odp":
case ".pptx":
return "file-powerpoint";
case ".ods":
case ".xlsx":
return "file-excel";
case ".odt":
case ".docx":
return "file-word";
case ".pdf":
return "file-pdf";
case ".jpg":
case ".jpeg":
case ".png":
return "file-image";
default:
return "file";
}
}
public static bool IsValidEmail(string email)
{
try
{
var addr = new MailAddress(email);
return addr.Address == email;
}
catch
{
return false;
}
}
}
}