patch notes setup, added some of the older patchnotes into a changelog.yaml

This commit is contained in:
choco
2025-10-15 13:29:32 +02:00
parent 77ff8ae372
commit a66a43dda8
9 changed files with 542 additions and 1 deletions

View 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();
}
}