patch notes setup, added some of the older patchnotes into a changelog.yaml
This commit is contained in:
332
LightlessSync/UI/UpdateNotesUi.cs
Normal file
332
LightlessSync/UI/UpdateNotesUi.cs
Normal file
@@ -0,0 +1,332 @@
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Utility;
|
||||
using LightlessSync.LightlessConfiguration;
|
||||
using LightlessSync.Services;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace LightlessSync.UI;
|
||||
|
||||
public class UpdateNotesUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
private readonly LightlessConfigService _configService;
|
||||
private readonly UiSharedService _uiShared;
|
||||
|
||||
private readonly List<string> _contributors = [];
|
||||
private readonly List<string> _credits = [];
|
||||
private readonly List<string> _supporters = [];
|
||||
|
||||
private ChangelogFile _changelog = new();
|
||||
private bool _scrollToTop;
|
||||
private int _selectedTab;
|
||||
|
||||
public UpdateNotesUi(ILogger<UpdateNotesUi> logger,
|
||||
LightlessMediator mediator,
|
||||
UiSharedService uiShared,
|
||||
LightlessConfigService configService,
|
||||
PerformanceCollectorService performanceCollectorService)
|
||||
: base(logger, mediator, "Lightless Sync — Update Notes", performanceCollectorService)
|
||||
{
|
||||
_configService = configService;
|
||||
_uiShared = uiShared;
|
||||
|
||||
AllowClickthrough = false;
|
||||
AllowPinning = false;
|
||||
RespectCloseHotkey = true;
|
||||
ShowCloseButton = true;
|
||||
|
||||
Flags = ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoCollapse;
|
||||
|
||||
SizeConstraints = new WindowSizeConstraints()
|
||||
{
|
||||
MinimumSize = new Vector2(600, 500),
|
||||
MaximumSize = new Vector2(900, 2000),
|
||||
};
|
||||
|
||||
LoadEmbeddedResources();
|
||||
}
|
||||
|
||||
public override void OnOpen()
|
||||
{
|
||||
_scrollToTop = true;
|
||||
}
|
||||
|
||||
protected override void DrawInternal()
|
||||
{
|
||||
if (_uiShared.IsInGpose)
|
||||
return;
|
||||
|
||||
DrawHeader();
|
||||
DrawLinkButtons();
|
||||
ImGuiHelpers.ScaledDummy(6);
|
||||
DrawTabs();
|
||||
DrawCloseButton();
|
||||
}
|
||||
|
||||
private void DrawHeader()
|
||||
{
|
||||
using (_uiShared.UidFont.Push())
|
||||
{
|
||||
ImGui.TextUnformatted("Lightless Sync");
|
||||
}
|
||||
|
||||
_uiShared.ColoredSeparator(UIColors.Get("LightlessBlue"), thickness: 2f);
|
||||
|
||||
if (!string.IsNullOrEmpty(_changelog.Tagline))
|
||||
{
|
||||
_uiShared.MediumText(_changelog.Tagline, UIColors.Get("LightlessBlue"));
|
||||
if (!string.IsNullOrEmpty(_changelog.Subline))
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGui.TextColored(new Vector4(.75f, .75f, .85f, 1f), $" – {_changelog.Subline}");
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
}
|
||||
|
||||
private void DrawLinkButtons()
|
||||
{
|
||||
var segmentSize = ImGui.GetWindowSize().X / 4.2f;
|
||||
var buttonSize = new Vector2(segmentSize, ImGui.GetTextLineHeight() * 1.6f);
|
||||
|
||||
if (ImGui.Button("Discord", buttonSize))
|
||||
Util.OpenLink("https://discord.gg/dsbjcXMnhA");
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("GitHub", buttonSize))
|
||||
Util.OpenLink("https://github.com/Light-Public-Syncshells/LightlessSync");
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Ko-fi", buttonSize))
|
||||
Util.OpenLink("https://ko-fi.com/lightlesssync");
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("More Links", buttonSize))
|
||||
Util.OpenLink("https://lightless.link");
|
||||
}
|
||||
|
||||
private void DrawCloseButton()
|
||||
{
|
||||
var closeWidth = 300f * ImGuiHelpers.GlobalScale;
|
||||
ImGui.SetCursorPosX((ImGui.GetWindowSize().X - closeWidth) / 2);
|
||||
if (ImGui.Button("Close", new Vector2(closeWidth, 0)))
|
||||
{
|
||||
IsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTabs()
|
||||
{
|
||||
using var tabBar = ImRaii.TabBar("lightless_update_tabs");
|
||||
if (!tabBar)
|
||||
return;
|
||||
|
||||
using (var changelogTab = ImRaii.TabItem(" Changelog "))
|
||||
{
|
||||
if (changelogTab)
|
||||
{
|
||||
_selectedTab = 0;
|
||||
DrawChangelog();
|
||||
}
|
||||
}
|
||||
|
||||
using (var creditsTab = ImRaii.TabItem(" Supporters & Credits "))
|
||||
{
|
||||
if (creditsTab)
|
||||
{
|
||||
_selectedTab = 1;
|
||||
DrawCredits();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawChangelog()
|
||||
{
|
||||
using var child = ImRaii.Child("###ll_changelog", new Vector2(0, ImGui.GetContentRegionAvail().Y - 44), false);
|
||||
if (!child)
|
||||
return;
|
||||
|
||||
if (_scrollToTop)
|
||||
{
|
||||
_scrollToTop = false;
|
||||
ImGui.SetScrollHereY(0);
|
||||
}
|
||||
|
||||
foreach (var entry in _changelog.Changelog)
|
||||
DrawChangelogEntry(entry);
|
||||
|
||||
ImGui.Spacing();
|
||||
}
|
||||
|
||||
private void DrawChangelogEntry(ChangelogEntry entry)
|
||||
{
|
||||
var currentColor = entry.IsCurrent == true
|
||||
? new Vector4(0.5f, 0.9f, 0.5f, 1.0f)
|
||||
: new Vector4(0.75f, 0.75f, 0.85f, 1.0f);
|
||||
|
||||
bool isOpen;
|
||||
using (ImRaii.PushColor(ImGuiCol.Text, currentColor))
|
||||
{
|
||||
isOpen = ImGui.CollapsingHeader($" {entry.Name} — {entry.Date} ");
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextColored(new Vector4(0.75f, 0.75f, 0.85f, 1.0f), $" — {entry.Tagline}");
|
||||
|
||||
if (!isOpen)
|
||||
return;
|
||||
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
|
||||
if (!string.IsNullOrEmpty(entry.Message))
|
||||
{
|
||||
ImGui.TextWrapped(entry.Message);
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry.Versions != null)
|
||||
{
|
||||
foreach (var version in entry.Versions)
|
||||
{
|
||||
DrawFeatureHeader(version.Number, new Vector4(0.5f, 0.9f, 0.5f, 1.0f));
|
||||
foreach (var item in version.Items)
|
||||
ImGui.BulletText(item);
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
}
|
||||
|
||||
private static void DrawFeatureHeader(string title, Vector4 accentColor)
|
||||
{
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
var startPos = ImGui.GetCursorScreenPos();
|
||||
|
||||
var backgroundMin = startPos + new Vector2(-10, -5);
|
||||
var backgroundMax = startPos + new Vector2(ImGui.GetContentRegionAvail().X + 10, 25);
|
||||
drawList.AddRectFilled(backgroundMin, backgroundMax, ImGui.GetColorU32(new Vector4(0.12f, 0.12f, 0.15f, 0.6f)), 4f);
|
||||
drawList.AddRectFilled(backgroundMin, backgroundMin + new Vector2(3, backgroundMax.Y - backgroundMin.Y), ImGui.GetColorU32(accentColor), 2f);
|
||||
|
||||
ImGui.Spacing();
|
||||
ImGui.TextColored(accentColor, title);
|
||||
ImGui.Spacing();
|
||||
}
|
||||
|
||||
private void DrawCredits()
|
||||
{
|
||||
ImGui.TextUnformatted("Maintained & Developed by the Lightless Sync team.");
|
||||
ImGui.TextUnformatted("Thank you to all supporters and contributors!");
|
||||
ImGuiHelpers.ScaledDummy(5);
|
||||
|
||||
var availableRegion = ImGui.GetContentRegionAvail();
|
||||
var halfWidth = new Vector2(
|
||||
availableRegion.X / 2f - ImGui.GetStyle().ItemSpacing.X / 2f,
|
||||
availableRegion.Y - 120 * ImGuiHelpers.GlobalScale);
|
||||
|
||||
using (var leftChild = ImRaii.Child("left_supporters", halfWidth))
|
||||
{
|
||||
if (leftChild)
|
||||
{
|
||||
ImGui.TextUnformatted("Supporters (Ko-fi / Patreon)");
|
||||
_uiShared.RoundedSeparator(UIColors.Get("LightlessBlue"), thickness: 2f);
|
||||
foreach (var supporter in _supporters)
|
||||
ImGui.BulletText(supporter);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
using (var rightChild = ImRaii.Child("right_contributors", halfWidth))
|
||||
{
|
||||
if (rightChild)
|
||||
{
|
||||
ImGui.TextUnformatted("Contributors");
|
||||
_uiShared.RoundedSeparator(UIColors.Get("LightlessBlue"), thickness: 2f);
|
||||
foreach (var contributor in _contributors)
|
||||
ImGui.BulletText(contributor);
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiHelpers.ScaledDummy(8);
|
||||
ImGui.TextUnformatted("Credits");
|
||||
_uiShared.RoundedSeparator(UIColors.Get("LightlessYellow"), thickness: 2f);
|
||||
foreach (var credit in _credits)
|
||||
ImGui.BulletText(credit);
|
||||
}
|
||||
|
||||
private void LoadEmbeddedResources()
|
||||
{
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
ReadLines(assembly, "LightlessSync.UI.Changelog.supporters.txt", _supporters);
|
||||
ReadLines(assembly, "LightlessSync.UI.Changelog.contributors.txt", _contributors);
|
||||
ReadLines(assembly, "LightlessSync.UI.Changelog.credits.txt", _credits);
|
||||
|
||||
using var changelogStream = assembly.GetManifestResourceStream("LightlessSync.UI.Changelog.changelog.yaml");
|
||||
if (changelogStream != null)
|
||||
{
|
||||
using var reader = new StreamReader(changelogStream, Encoding.UTF8, true, 128);
|
||||
var yaml = reader.ReadToEnd();
|
||||
var deserializer = new DeserializerBuilder()
|
||||
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
||||
.IgnoreUnmatchedProperties()
|
||||
.Build();
|
||||
_changelog = deserializer.Deserialize<ChangelogFile>(yaml) ?? new();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore - window will gracefully render with defaults
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReadLines(Assembly assembly, string resourceName, List<string> target)
|
||||
{
|
||||
using var stream = assembly.GetManifestResourceStream(resourceName);
|
||||
if (stream == null)
|
||||
return;
|
||||
|
||||
using var reader = new StreamReader(stream, Encoding.UTF8, true, 128);
|
||||
string? line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(line))
|
||||
target.Add(line.Trim());
|
||||
}
|
||||
}
|
||||
|
||||
private sealed record ChangelogFile
|
||||
{
|
||||
public string Tagline { get; init; } = string.Empty;
|
||||
public string Subline { get; init; } = string.Empty;
|
||||
public List<ChangelogEntry> Changelog { get; init; } = new();
|
||||
}
|
||||
|
||||
private sealed record ChangelogEntry
|
||||
{
|
||||
public string Name { get; init; } = string.Empty;
|
||||
public string Date { get; init; } = string.Empty;
|
||||
public string Tagline { get; init; } = string.Empty;
|
||||
public bool? IsCurrent { get; init; }
|
||||
public string? Message { get; init; }
|
||||
public List<ChangelogVersion>? Versions { get; init; }
|
||||
}
|
||||
|
||||
private sealed record ChangelogVersion
|
||||
{
|
||||
public string Number { get; init; } = string.Empty;
|
||||
public List<string> Items { get; init; } = new();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user