feat(about): restore the integrations section with honorific status
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
namespace HellionChat.Integrations;
|
||||
|
||||
internal enum HonorificStatusKind
|
||||
{
|
||||
NotInstalled,
|
||||
Incompatible,
|
||||
Detected,
|
||||
}
|
||||
|
||||
internal static class HonorificStatus
|
||||
{
|
||||
// Mirrors the 1.5.6 three-state discriminator (1d3b429:About.cs:171/183/196):
|
||||
// it keys on IsAvailable + the *nullability* of DetectedApiVersion, never a
|
||||
// recomputed major check. IsAvailable already encodes the compatibility
|
||||
// result HonorificService set during the initial pull. Null-safe: an
|
||||
// (isAvailable=true, detectedApiVersion=null) state a test seam can produce
|
||||
// resolves to NotInstalled rather than dereferencing null.
|
||||
internal static HonorificStatusKind Resolve(
|
||||
bool isAvailable,
|
||||
(uint Major, uint Minor)? detectedApiVersion
|
||||
)
|
||||
{
|
||||
if (isAvailable && detectedApiVersion is not null)
|
||||
return HonorificStatusKind.Detected;
|
||||
if (detectedApiVersion is not null)
|
||||
return HonorificStatusKind.Incompatible;
|
||||
return HonorificStatusKind.NotInstalled;
|
||||
}
|
||||
}
|
||||
@@ -196,7 +196,10 @@ internal static class PluginHostFactory
|
||||
));
|
||||
services.AddSingleton(sp => new Ui.Components.Settings.Tabs.AboutTab(
|
||||
sp.GetRequiredService<FontManager>(),
|
||||
sp.GetRequiredService<ILogger<Ui.Components.Settings.Tabs.AboutTab>>()
|
||||
sp.GetRequiredService<Plugin>(),
|
||||
sp.GetRequiredService<Integrations.HonorificService>(),
|
||||
sp.GetRequiredService<ThemeRegistry>(),
|
||||
sp.GetRequiredService<IPlatformUtil>()
|
||||
));
|
||||
services.AddSingleton(sp => new Ui.Components.StatusBar(
|
||||
sp.GetRequiredService<ThemeRegistry>(),
|
||||
|
||||
@@ -1,30 +1,52 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface;
|
||||
using HellionChat.Branding;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using HellionChat.Integrations;
|
||||
using HellionChat.Resources;
|
||||
using HellionChat.Themes;
|
||||
using HellionChat.Util;
|
||||
|
||||
namespace HellionChat.Ui.Components.Settings.Tabs;
|
||||
|
||||
internal sealed class AboutTab
|
||||
{
|
||||
private readonly FontManager _fonts;
|
||||
private readonly ILogger<AboutTab> _logger;
|
||||
private readonly Plugin _plugin;
|
||||
private readonly HonorificService _honorific;
|
||||
private readonly ThemeRegistry _themes;
|
||||
private readonly IPlatformUtil _platformUtil;
|
||||
|
||||
public AboutTab(FontManager fonts, ILogger<AboutTab> logger)
|
||||
// SelfTest observable — the status key the real render path resolved.
|
||||
internal string? LastHonorificStatusKey { get; private set; }
|
||||
|
||||
public AboutTab(
|
||||
FontManager fonts,
|
||||
Plugin plugin,
|
||||
HonorificService honorific,
|
||||
ThemeRegistry themes,
|
||||
IPlatformUtil platformUtil
|
||||
)
|
||||
{
|
||||
_fonts = fonts;
|
||||
_logger = logger;
|
||||
_plugin = plugin;
|
||||
_honorific = honorific;
|
||||
_themes = themes;
|
||||
_platformUtil = platformUtil;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
// Reset the SelfTest observable each frame so a stale value from a prior
|
||||
// real render can never let the integrations-status SelfTest pass falsely.
|
||||
LastHonorificStatusKey = null;
|
||||
DrawPluginInfo();
|
||||
DrawSectionHeader("Brand");
|
||||
DrawBrand();
|
||||
DrawSectionHeader("Links");
|
||||
DrawLinks();
|
||||
DrawSectionHeader("Integrations");
|
||||
DrawIntegrations();
|
||||
DrawSectionHeader("Credits");
|
||||
DrawCredits();
|
||||
DrawSectionHeader("License");
|
||||
@@ -74,24 +96,152 @@ internal sealed class AboutTab
|
||||
DrawLinkButton("Custom repo manifest", BrandingLinks.HellionChatCustomRepoManifest);
|
||||
}
|
||||
|
||||
// URLs in v1.7.0 are exclusively hardcoded BrandingLinks.* constants —
|
||||
// Process.Start with UseShellExecute=true is safe under that constraint.
|
||||
// If a future cycle ever feeds user-supplied URLs here, add an https/http
|
||||
// allow-list filter via Uri.TryCreate before Process.Start; without it
|
||||
// UseShellExecute would happily launch file:// or shell-protocol handlers.
|
||||
private void DrawIntegrations()
|
||||
{
|
||||
ImGui.TextWrapped(HellionStrings.Settings_Integrations_Intro);
|
||||
ImGui.Spacing();
|
||||
|
||||
ImGui.TextUnformatted(HellionStrings.Settings_Integrations_Honorific_SectionHeader);
|
||||
DrawHonorificStatus();
|
||||
DrawToggle(
|
||||
HellionStrings.Settings_Integrations_Honorific_Toggle,
|
||||
() => Plugin.Config.ShowHonorificTitleInHeader,
|
||||
v => Plugin.Config.ShowHonorificTitleInHeader = v
|
||||
);
|
||||
ImGui.TextDisabled(HellionStrings.Settings_Integrations_Honorific_ToggleHint);
|
||||
DrawLinkButton(
|
||||
HellionStrings.Settings_Integrations_Honorific_LinkRepo,
|
||||
IntegrationLinks.HonorificRepo
|
||||
);
|
||||
DrawLinkButton(
|
||||
HellionStrings.Settings_Integrations_Honorific_LinkAuthor,
|
||||
IntegrationLinks.HonorificAuthor
|
||||
);
|
||||
|
||||
DrawComingSoon();
|
||||
DrawGotAnIdea();
|
||||
}
|
||||
|
||||
private void DrawHonorificStatus()
|
||||
{
|
||||
var kind = HonorificStatus.Resolve(_honorific.IsAvailable, _honorific.DetectedApiVersion);
|
||||
LastHonorificStatusKey = kind.ToString();
|
||||
var colors = _themes.Active.Colors;
|
||||
|
||||
// Null-safety via the `is { } v` pattern, never `.Value` raw (spec SEC-2):
|
||||
// the version is bound only on the arms that have it; the impossible
|
||||
// Detected/Incompatible-without-version state falls through to default.
|
||||
switch (kind)
|
||||
{
|
||||
case HonorificStatusKind.Detected when _honorific.DetectedApiVersion is { } v:
|
||||
DrawStatusGlyph('●', colors.StatusSuccess);
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(
|
||||
string.Format(
|
||||
HellionStrings.Settings_Integrations_Honorific_Status_Detected,
|
||||
v.Major,
|
||||
v.Minor
|
||||
)
|
||||
);
|
||||
break;
|
||||
case HonorificStatusKind.Incompatible when _honorific.DetectedApiVersion is { } iv:
|
||||
DrawStatusGlyph('⚠', colors.StatusWarning);
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(
|
||||
string.Format(
|
||||
HellionStrings.Settings_Integrations_Honorific_Status_Incompatible,
|
||||
HonorificService.ExpectedApiMajor,
|
||||
iv.Major,
|
||||
iv.Minor
|
||||
)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
DrawStatusGlyph('○', colors.TextMuted);
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(
|
||||
HellionStrings.Settings_Integrations_Honorific_Status_NotInstalled
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawStatusGlyph(char glyph, uint rgba)
|
||||
{
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, ColourUtil.RgbaToAbgr(rgba));
|
||||
ImGui.TextUnformatted(glyph.ToString());
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
private void DrawComingSoon()
|
||||
{
|
||||
ImGui.Spacing();
|
||||
ImGui.TextUnformatted(HellionStrings.Settings_Integrations_ComingSoon_SectionHeader);
|
||||
ImGui.TextDisabled(HellionStrings.Settings_Integrations_ComingSoon_Intro);
|
||||
DrawComingSoonItem(
|
||||
HellionStrings.Settings_Integrations_ComingSoon_ContextMenu_Title,
|
||||
HellionStrings.Settings_Integrations_ComingSoon_ContextMenu_Description
|
||||
);
|
||||
DrawComingSoonItem(
|
||||
HellionStrings.Settings_Integrations_ComingSoon_Notifications_Title,
|
||||
HellionStrings.Settings_Integrations_ComingSoon_Notifications_Description
|
||||
);
|
||||
DrawComingSoonItem(
|
||||
HellionStrings.Settings_Integrations_ComingSoon_RPStatus_Title,
|
||||
HellionStrings.Settings_Integrations_ComingSoon_RPStatus_Description
|
||||
);
|
||||
DrawComingSoonItem(
|
||||
HellionStrings.Settings_Integrations_ComingSoon_ExtraChat_Title,
|
||||
HellionStrings.Settings_Integrations_ComingSoon_ExtraChat_Description
|
||||
);
|
||||
DrawComingSoonItem(
|
||||
HellionStrings.Settings_Integrations_ComingSoon_QuickDM_Title,
|
||||
HellionStrings.Settings_Integrations_ComingSoon_QuickDM_Description
|
||||
);
|
||||
}
|
||||
|
||||
private void DrawComingSoonItem(string title, string description)
|
||||
{
|
||||
using (_fonts.FontAwesome.Push())
|
||||
{
|
||||
ImGui.TextDisabled(FontAwesomeIcon.Hourglass.ToIconString());
|
||||
}
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(title);
|
||||
ImGui.TextDisabled(description);
|
||||
}
|
||||
|
||||
private void DrawGotAnIdea()
|
||||
{
|
||||
ImGui.Spacing();
|
||||
ImGui.TextUnformatted(HellionStrings.Settings_Integrations_GotAnIdea_SectionHeader);
|
||||
ImGui.TextWrapped(HellionStrings.Settings_Integrations_GotAnIdea_Body);
|
||||
if (ImGui.Button(HellionStrings.Settings_Integrations_GotAnIdea_LinkLabel))
|
||||
{
|
||||
_platformUtil.OpenLink(BrandingLinks.HellionForgeDiscordInvite);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawToggle(string label, Func<bool> get, Action<bool> set)
|
||||
{
|
||||
var current = get();
|
||||
if (ImGui.Checkbox(label, ref current))
|
||||
{
|
||||
set(current);
|
||||
_plugin.SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
// URLs are exclusively hardcoded BrandingLinks/IntegrationLinks constants,
|
||||
// validated to http/https at module-init. OpenLink centralises the browser
|
||||
// open on an off-draw thread (it internally uses the same ShellExecute, so
|
||||
// this is a consistency cleanup, not a security change). The standalone Copy
|
||||
// button stays as the clipboard path.
|
||||
private void DrawLinkButton(string label, string url)
|
||||
{
|
||||
if (ImGui.Button(label))
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Could not open {Url}, copying to clipboard instead", url);
|
||||
ImGui.SetClipboardText(url);
|
||||
}
|
||||
_platformUtil.OpenLink(url);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
if (ImGui.SmallButton($"Copy##{url}"))
|
||||
|
||||
Reference in New Issue
Block a user