From 4203d270cc9e8a35c64f2235b1b125803487c523 Mon Sep 17 00:00:00 2001 From: Rupert Benbrook Date: Tue, 12 Sep 2023 06:41:01 +0100 Subject: [PATCH] Adding Legacy, UTM Basic and UTM Ready display (#3166) * How to spot each utm status * Initial badges in header * CdnUrl for environment specific CDN content * Map info panel now matching * Switch to Markdig * Move MarkdownPipeline to service locator --------- Co-authored-by: rupertbenbrook --- .../Resources/MapInfoDockPanel.html | 40 ++++- .../AltitudeAngelWings.csproj | 2 +- .../Api/Model/DisplayInfoExtensions.cs | 168 +++++++++++------- .../Api/Model/FeaturePropertiesExtensions.cs | 46 +++++ ExtLibs/AltitudeAngelWings/ISettings.cs | 1 + .../ServiceLocatorConfiguration.cs | 16 ++ ExtLibs/AltitudeAngelWings/Settings.cs | 8 +- 7 files changed, 208 insertions(+), 73 deletions(-) diff --git a/ExtLibs/AltitudeAngelWings.Plugin/Resources/MapInfoDockPanel.html b/ExtLibs/AltitudeAngelWings.Plugin/Resources/MapInfoDockPanel.html index 56a30eaaf1..14dd03e3b6 100644 --- a/ExtLibs/AltitudeAngelWings.Plugin/Resources/MapInfoDockPanel.html +++ b/ExtLibs/AltitudeAngelWings.Plugin/Resources/MapInfoDockPanel.html @@ -72,17 +72,34 @@ text-transform: capitalize; } - div.utmStatus { + div.utmReadyStatus { border: 1px solid #3dbb4e; margin-top: 10px; padding: 0 10px; } - div.utmStatus div.section div.title { - display: block; - text-align: center; - font-size: 18px; - padding: 5px; + div.utmBasicStatus { + border: 1px solid darkorange; + margin-top: 10px; + padding: 0 10px; + } + + div.utmReadyTitle { + background-color: #edf8ed; + margin: 8px 0; + padding: 8px; + } + + div.utmBasicTitle { + background-color: #fcebb4; + margin: 8px 0; + padding: 8px; + } + + div.utmLegacyTitle { + background-color: #fde5ee; + margin: 8px 0; + padding: 8px; } div.noUtm { @@ -91,7 +108,7 @@ padding: 0 10px; } - div.utmStatus div.button { + div.utmReadyStatus div.button { background-color: #3dbb4e; text-align: center; margin: 0 50px 10px; @@ -100,6 +117,15 @@ font-size: 14px; } + div.utmBasicStatus div.button { + background-color: #fb0; + text-align: center; + margin: 0 50px 10px; + padding: 10px 10px; + font-weight: bold; + font-size: 14px; + } + div.phoneNumber { margin: 5px 0; padding-top: 10px; diff --git a/ExtLibs/AltitudeAngelWings/AltitudeAngelWings.csproj b/ExtLibs/AltitudeAngelWings/AltitudeAngelWings.csproj index 449baab0fd..bb570dd036 100644 --- a/ExtLibs/AltitudeAngelWings/AltitudeAngelWings.csproj +++ b/ExtLibs/AltitudeAngelWings/AltitudeAngelWings.csproj @@ -15,7 +15,7 @@ True - + diff --git a/ExtLibs/AltitudeAngelWings/Clients/Api/Model/DisplayInfoExtensions.cs b/ExtLibs/AltitudeAngelWings/Clients/Api/Model/DisplayInfoExtensions.cs index ac89782aa3..5db773639b 100644 --- a/ExtLibs/AltitudeAngelWings/Clients/Api/Model/DisplayInfoExtensions.cs +++ b/ExtLibs/AltitudeAngelWings/Clients/Api/Model/DisplayInfoExtensions.cs @@ -3,7 +3,7 @@ using System.Globalization; using System.Linq; using System.Text; -using MarkdownSharp; +using Markdig; namespace AltitudeAngelWings.Clients.Api.Model { @@ -29,92 +29,132 @@ public static class DisplayInfoExtensions public static string FormatAsHtml(this FeatureProperties featureProperties, IDictionary rateCardDetails) { + var settings = ServiceLocator.GetService(); + var pipeline = ServiceLocator.GetService(); var builder = new StringBuilder(); - var markdown = new Markdown(new MarkdownOptions - { - AutoNewlines = false, - AutoHyperlink = false, - LinkEmails = false, - EmptyElementSuffix = "/>" - }); builder.Append("
"); builder.Append($"
"); - builder.Append($"
"); + builder.Append("
"); builder.Append($"
{featureProperties.DisplayInfo.Category}
"); builder.Append($"
: {featureProperties.DisplayInfo.DetailedCategory}
"); builder.Append($"
{featureProperties.DisplayInfo.Title.ToUpper()}
"); + if (featureProperties.HasUtmStatus()) + { + builder.Append($"
"); + } builder.Append("
"); - if (featureProperties.UtmStatus?.UtmDetails != null && featureProperties.UtmStatus.Enabled) + + if (featureProperties.HasUtmStatus()) { - builder.Append("
"); builder.Append("
"); + builder.Append("
APPROVAL REQUIRED
"); - if (!string.IsNullOrWhiteSpace(featureProperties.UtmStatus.Title)) + if (featureProperties.IsUtmReady()) { - builder.Append($"
{markdown.Transform(featureProperties.UtmStatus.Title)}
"); - } - - builder.Append("
FACILITY IS UTM READY
"); + builder.Append("
Online Approval
"); - if (!string.IsNullOrWhiteSpace(featureProperties.UtmStatus.Description)) + } + else if (featureProperties.IsUtmBasic()) + { + builder.Append("
Notification Available
"); + } + else { - builder.Append($"
{markdown.Transform(featureProperties.UtmStatus.Description)}
"); + builder.Append("
Not Connected
"); } + builder.Append("
"); - foreach (var rateType in featureProperties.UtmStatus.RateTypes.Keys) + if (featureProperties.IsUtmEnabled()) { - var rateCard = featureProperties.UtmStatus.RateTypes[rateType] - .Where(c => - { - if (c.AppliesFrom == null) return false; - var now = DateTimeOffset.UtcNow; - if (c.AppliesFrom > now) return false; - if (c.AppliesTo == null) return true; - return c.AppliesTo >= now; - }) - .OrderBy(c => c.AppliesFrom) - .Select(c => rateCardDetails[c.Id]) - .FirstOrDefault(); - if (rateCard == null) continue; - builder.Append("
"); + var isReady = featureProperties.IsUtmReady(); + builder.Append($"
"); builder.Append("
"); - builder.Append($"
{MapRateTypeToText(rateType)}
"); - builder.Append("
"); - builder.Append($"

{featureProperties.DisplayInfo.Title.ToUpper()}

"); - builder.Append($"

{rateCard.ExplanatoryText}

"); - builder.Append("
    "); - foreach (var rate in rateCard.Rates.OrderBy(r => r.Ordinal)) + + if (!string.IsNullOrWhiteSpace(featureProperties.UtmStatus.Title)) { - builder.Append("
  • "); - builder.Append(rate.Name); - builder.Append(" ("); - var total = rate.Rate + rateCard.StandingCharge; - total += rateCard.TaxRate / 100 * total; - builder.Append(CurrencyLookup.ContainsKey(rateCard.Currency) - ? total.ToString("C", CurrencyLookup[rateCard.Currency]) - : $"{total} {rateCard.Currency}"); - - builder.Append(")
  • "); + builder.Append($"
    {Markdown.ToHtml(featureProperties.UtmStatus.Title, pipeline)}
    "); } - builder.Append("
"); - builder.Append("

If you wish to operate here, depending on your flight plan, you may require their prior approval.

"); - builder.Append($"

Terms and conditions

"); - builder.Append("
"); + + builder.Append($"
{(isReady ? "FACILITY IS UTM CONNECTED" : "NOTIFY FACILITY")}
"); + + if (!string.IsNullOrWhiteSpace(featureProperties.UtmStatus.Description)) + { + builder.Append($"
{Markdown.ToHtml(featureProperties.UtmStatus.Description, pipeline)}
"); + } + else if (!isReady) + { + builder.Append("

This facility supports prior notification. Submitting your flight plan will begin the approval process with the facility.

  • Prior Notification

"); + } + + foreach (var rateType in featureProperties.UtmStatus.RateTypes.Keys) + { + var rateCard = featureProperties.UtmStatus.RateTypes[rateType] + .Where(c => + { + if (c.AppliesFrom == null) return false; + var now = DateTimeOffset.UtcNow; + if (c.AppliesFrom > now) return false; + if (c.AppliesTo == null) return true; + return c.AppliesTo >= now; + }) + .OrderBy(c => c.AppliesFrom) + .Select(c => rateCardDetails[c.Id]) + .FirstOrDefault(); + if (rateCard == null) continue; + builder.Append("
"); + builder.Append("
"); + builder.Append($"
{MapRateTypeToText(rateType)}
"); + builder.Append("
"); + builder.Append($"

{featureProperties.DisplayInfo.Title.ToUpper()}

"); + builder.Append($"

{Markdown.ToHtml(rateCard.ExplanatoryText, pipeline)}

"); + builder.Append("
    "); + foreach (var rate in rateCard.Rates.OrderBy(r => r.Ordinal)) + { + builder.Append("
  • "); + builder.Append(rate.Name); + builder.Append(" ("); + var total = rate.Rate + rateCard.StandingCharge; + total += rateCard.TaxRate / 100 * total; + builder.Append(CurrencyLookup.TryGetValue(rateCard.Currency, out var value) + ? total.ToString("C", value) + : $"{total} {rateCard.Currency}"); + + builder.Append(")
  • "); + } + builder.Append("
"); + builder.Append("

If you wish to operate here, depending on your flight plan, you may require their prior approval.

"); + builder.Append($"

Terms and conditions

"); + builder.Append("
"); + builder.Append("
"); + builder.Append("
"); + } + + if (!string.IsNullOrWhiteSpace(featureProperties.UtmStatus.UtmDetails.ExternalUrl)) + { + builder.Append($""); + } + builder.Append("
"); builder.Append("
"); } - - if (!string.IsNullOrWhiteSpace(featureProperties.UtmStatus.UtmDetails.ExternalUrl)) - { - builder.Append($""); - } - - builder.Append("
"); - builder.Append("
"); } + if (featureProperties.Contact?.PhoneNumbers != null && featureProperties.Contact.PhoneNumbers.Count > 0) { - if (featureProperties.UtmStatus?.UtmDetails != null && featureProperties.UtmStatus.Enabled) + if (featureProperties.IsUtmEnabled()) { builder.Append("
"); builder.Append("
"); @@ -153,12 +193,12 @@ public static string FormatAsHtml(this FeatureProperties featureProperties, if (!string.IsNullOrWhiteSpace(section.Text)) { - builder.Append($"
{markdown.Transform(section.Text)}
"); + builder.Append($"
{Markdown.ToHtml(section.Text, pipeline)}
"); } if (!string.IsNullOrWhiteSpace(section.Disclaimer)) { - builder.Append($"
{markdown.Transform(section.Disclaimer)}
"); + builder.Append($"
{Markdown.ToHtml(section.Disclaimer, pipeline)}
"); } builder.Append("
"); } diff --git a/ExtLibs/AltitudeAngelWings/Clients/Api/Model/FeaturePropertiesExtensions.cs b/ExtLibs/AltitudeAngelWings/Clients/Api/Model/FeaturePropertiesExtensions.cs index 36ae85caeb..c5448842dc 100644 --- a/ExtLibs/AltitudeAngelWings/Clients/Api/Model/FeaturePropertiesExtensions.cs +++ b/ExtLibs/AltitudeAngelWings/Clients/Api/Model/FeaturePropertiesExtensions.cs @@ -5,6 +5,52 @@ namespace AltitudeAngelWings.Clients.Api.Model { public static class FeaturePropertiesExtensions { + public static bool HasUtmStatus(this FeatureProperties properties) + => properties?.UtmStatus != null; + + public static bool IsUtmEnabled(this FeatureProperties properties) + => properties?.UtmStatus != null && properties.UtmStatus.Enabled; + + public static bool IsUtmLegacy(this FeatureProperties properties) + { + if (!properties.HasUtmStatus()) + { + return false; + } + + return !properties.IsUtmEnabled(); + } + + public static bool IsUtmBasic(this FeatureProperties properties) + { + if (!properties.IsUtmEnabled()) + { + return false; + } + + if (string.IsNullOrEmpty(properties.UtmStatus.UtmDetails?.Id)) + { + return false; + } + + return properties.UtmStatus.UtmDetails.Id == "AA_UTM_READY"; + } + + public static bool IsUtmReady(this FeatureProperties properties) + { + if (!properties.IsUtmEnabled()) + { + return false; + } + + if (string.IsNullOrEmpty(properties.UtmStatus.UtmDetails?.Id)) + { + return false; + } + + return properties.UtmStatus.UtmDetails.Id == "AA_GUARDIAN_UTM"; + } + public static ColorInfo ToColorInfo(this FeatureProperties properties, float opacityAdjust = 1f) { var fillColor = ToARGB(properties.FillColor, properties.FillOpacity, opacityAdjust); diff --git a/ExtLibs/AltitudeAngelWings/ISettings.cs b/ExtLibs/AltitudeAngelWings/ISettings.cs index 69b698db12..bcce4dd593 100644 --- a/ExtLibs/AltitudeAngelWings/ISettings.cs +++ b/ExtLibs/AltitudeAngelWings/ISettings.cs @@ -56,5 +56,6 @@ public interface ISettings string FlightIdentifierSerialNumber { get; set; } int AltitudeFilter { get; set; } string SurveillanceUrl { get; } + string CdnUrl { get; } } } \ No newline at end of file diff --git a/ExtLibs/AltitudeAngelWings/ServiceLocatorConfiguration.cs b/ExtLibs/AltitudeAngelWings/ServiceLocatorConfiguration.cs index 585297867a..1a16290646 100644 --- a/ExtLibs/AltitudeAngelWings/ServiceLocatorConfiguration.cs +++ b/ExtLibs/AltitudeAngelWings/ServiceLocatorConfiguration.cs @@ -20,6 +20,7 @@ using AltitudeAngelWings.Service.OutboundNotifications; using Flurl.Http; using Flurl.Http.Configuration; +using Markdig; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using NodaTime; @@ -144,6 +145,21 @@ public void Configure() l.Resolve(), l.Resolve(), l.Resolve())); + ServiceLocator.Register(l => new MarkdownPipelineBuilder() + .UseAbbreviations() + .UseAutoIdentifiers() + .UseCitations() + .UseDefinitionLists() + .UseEmphasisExtras() + .UseFooters() + .UseFootnotes() + .UseGridTables() + .UsePipeTables() + .UseListExtras() + .UseTaskLists() + .DisableHtml() + .UseSmartyPants() + .Build()); } private static void ResetAccessToken(ISettings settings) diff --git a/ExtLibs/AltitudeAngelWings/Settings.cs b/ExtLibs/AltitudeAngelWings/Settings.cs index 282a964961..b41f29a96a 100644 --- a/ExtLibs/AltitudeAngelWings/Settings.cs +++ b/ExtLibs/AltitudeAngelWings/Settings.cs @@ -69,7 +69,13 @@ public bool UseFlights public string SurveillanceUrl => $"https://surveillance-api.{UrlDomainSuffix}"; - public string UrlDomainSuffix => OverrideClientUrlSettings ? OverrideUrlDomainSuffix : "altitudeangel.com"; + public string CdnUrl => UrlDomainSuffix == DefaultDomainSuffix + ? "https://dronesafetymap.com" + : $"https://map.{UrlDomainSuffix}"; + + private const string DefaultDomainSuffix = "altitudeangel.com"; + + public string UrlDomainSuffix => OverrideClientUrlSettings ? OverrideUrlDomainSuffix : DefaultDomainSuffix; public bool OverrideClientUrlSettings {