Files
HellionChat/HellionChat/Ui/Components/Settings/Tabs/AboutTab.cs
T

268 lines
9.6 KiB
C#

using System.Reflection;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface;
using HellionChat.Branding;
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 Plugin _plugin;
private readonly HonorificService _honorific;
private readonly ThemeRegistry _themes;
private readonly IPlatformUtil _platformUtil;
// 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;
_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");
DrawLicense();
}
// Dalamud.Bindings.ImGui does not expose ImGui.SeparatorText, so we use
// the Separator + TextUnformatted idiom the rest of the codebase uses.
private static void DrawSectionHeader(string title)
{
ImGui.Spacing();
ImGui.Separator();
ImGui.Spacing();
ImGui.TextUnformatted(title);
}
private static void DrawPluginInfo()
{
var version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown";
ImGui.TextUnformatted("HellionChat");
// Schema version pulled from Configuration.LatestVersion (single source of
// truth) so future schema bumps don't have to touch this string.
ImGui.TextDisabled($"Version {version} · Schema v{Configuration.LatestVersion}");
}
private void DrawBrand()
{
using (_fonts.FontAwesome.Push())
{
// ImGui's PushStyleColor uint API is ABGR-native. 0xFF0C41C2u packs
// as A=FF B=0C G=41 R=C2 → RGB #C2410C (Forge-Bronze). No swap needed
// because the literal is already ABGR; theme-sourced uints from
// ThemeColors.* (RGBA) would need ColourUtil.RgbaToAbgr first.
ImGui.PushStyleColor(ImGuiCol.Text, 0xFF0C41C2u);
ImGui.TextUnformatted(FontAwesomeIcon.Hammer.ToIconString());
ImGui.PopStyleColor();
}
ImGui.SameLine();
ImGui.TextUnformatted("by Hellion Online Media");
ImGui.TextDisabled("Hellion Forge — Modding Division");
}
private void DrawLinks()
{
DrawLinkButton("Discord (Hellion Forge)", BrandingLinks.HellionForgeDiscordInvite);
DrawLinkButton("Gitea repository", BrandingLinks.HellionChatRepo);
DrawLinkButton("Custom repo manifest", BrandingLinks.HellionChatCustomRepoManifest);
}
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))
{
_platformUtil.OpenLink(url);
}
ImGui.SameLine();
if (ImGui.SmallButton($"Copy##{url}"))
{
ImGui.SetClipboardText(url);
}
}
private static void DrawCredits()
{
ImGui.BulletText("ChatTwo — original maintainer Anna Clemens, GPL-3.0");
ImGui.BulletText("Dalamud — goatcorp, AGPL-3.0");
ImGui.BulletText("ImGui — Omar Cornut, MIT");
ImGui.BulletText("FontAwesome — Fonticons, OFL-1.1");
ImGui.BulletText("Inter — rsms, OFL-1.1");
ImGui.BulletText("NotoSansCJK — Google, OFL-1.1");
}
private static void DrawLicense()
{
ImGui.TextUnformatted("GPL-3.0-or-later");
}
}