Implement ChangelogWindow function
This commit is contained in:
parent
01b1f171fc
commit
05de6aeadf
1 changed files with 150 additions and 0 deletions
150
src/Ryujinx/UI/Windows/ChangelogWindow.axaml.cs
Normal file
150
src/Ryujinx/UI/Windows/ChangelogWindow.axaml.cs
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Layout;
|
||||||
|
using Avalonia.Styling;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
using HtmlAgilityPack;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
|
using Ryujinx.UI.Common.Helper;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Button = Avalonia.Controls.Button;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.Windows
|
||||||
|
{
|
||||||
|
public partial class ChangelogWindow : Window
|
||||||
|
{
|
||||||
|
public ChangelogWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
InitializeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void InitializeAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LoadingTextBlock.IsVisible = true; // Show the loading text
|
||||||
|
ChangelogTextBlock.IsVisible = false; // Hide the changelog text initially
|
||||||
|
|
||||||
|
string changelogHtml = await FetchChangelogHtml();
|
||||||
|
string changelog = ParseChangelogForRecentVersions(changelogHtml, 10);
|
||||||
|
LoadChangelog(changelog);
|
||||||
|
|
||||||
|
LoadingTextBlock.IsVisible = false; // Hide the loading text
|
||||||
|
ChangelogTextBlock.IsVisible = true; // Show the changelog text
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoadingTextBlock.Text = "Failed to load changelog: " + ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadChangelog(string changelog)
|
||||||
|
{
|
||||||
|
ChangelogTextBlock.Text = changelog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<string> FetchChangelogHtml()
|
||||||
|
{
|
||||||
|
using (var client = new HttpClient())
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.UserAgent.ParseAdd("Ryujinx-Updater/1.0.0");
|
||||||
|
return await client.GetStringAsync("https://github.com/Ryujinx/Ryujinx/wiki/Changelog");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ParseChangelogForRecentVersions(string html, int count)
|
||||||
|
{
|
||||||
|
var doc = new HtmlDocument();
|
||||||
|
doc.LoadHtml(html);
|
||||||
|
|
||||||
|
var headers = doc.DocumentNode.SelectNodes("//div[contains(@class, 'markdown-heading')]//h2");
|
||||||
|
if (headers != null)
|
||||||
|
{
|
||||||
|
var content = new StringBuilder();
|
||||||
|
int versionsFound = 0;
|
||||||
|
|
||||||
|
foreach (var header in headers)
|
||||||
|
{
|
||||||
|
if (versionsFound >= count) break; // Stop after finding the desired number of versions
|
||||||
|
content.Append(header.OuterHtml);
|
||||||
|
var currentNode = header.ParentNode.NextSibling;
|
||||||
|
|
||||||
|
while (currentNode != null && versionsFound < count)
|
||||||
|
{
|
||||||
|
if (currentNode.Name == "div" && currentNode.SelectSingleNode("h2") != null)
|
||||||
|
{
|
||||||
|
versionsFound++; // Increment for each version header found
|
||||||
|
if (versionsFound >= count) break;
|
||||||
|
}
|
||||||
|
content.Append(currentNode.OuterHtml);
|
||||||
|
currentNode = currentNode.NextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConvertHtmlToPlainText(content.ToString());
|
||||||
|
}
|
||||||
|
return "No changelog found.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ConvertHtmlToPlainText(string html)
|
||||||
|
{
|
||||||
|
var doc = new HtmlDocument();
|
||||||
|
doc.LoadHtml(html);
|
||||||
|
|
||||||
|
// Recursively format nodes
|
||||||
|
string formattedText = FormatNode(doc.DocumentNode);
|
||||||
|
|
||||||
|
return HtmlEntity.DeEntitize(formattedText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatNode(HtmlNode node, int depth = 0)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
foreach (var child in node.ChildNodes)
|
||||||
|
{
|
||||||
|
switch (child.Name)
|
||||||
|
{
|
||||||
|
case "ul":
|
||||||
|
// Recursively format the list items
|
||||||
|
sb.Append(FormatNode(child, depth));
|
||||||
|
sb.AppendLine();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "li":
|
||||||
|
// Format list item based on depth: "-" for top-level, "+" for nested items
|
||||||
|
string prefix = (depth == 0 ? "- " : new string(' ', depth * 4) + "+ ");
|
||||||
|
sb.AppendLine($"{prefix}{FormatNode(child, depth + 1).Trim()}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "p":
|
||||||
|
case "#text": // Handling direct text nodes
|
||||||
|
if (!string.IsNullOrWhiteSpace(child.InnerText))
|
||||||
|
{
|
||||||
|
// Trim the text
|
||||||
|
string text = HtmlEntity.DeEntitize(child.InnerText).Trim();
|
||||||
|
sb.Append($"{text}\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Recursively process other types of nodes
|
||||||
|
if (child.HasChildNodes)
|
||||||
|
{
|
||||||
|
sb.Append(FormatNode(child, depth)); // Keep current depth for other types
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue