This commit is contained in:
yell0wsuit 2024-05-18 09:06:44 +02:00 committed by GitHub
commit b9a2a84df4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 216 additions and 6 deletions

View File

@ -17,6 +17,7 @@
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
<PackageVersion Include="HtmlAgilityPack" Version="1.11.60" />
<PackageVersion Include="LibHac" Version="0.19.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
@ -49,4 +50,4 @@
<PackageVersion Include="System.Management" Version="8.0.0" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
</ItemGroup>
</Project>
</Project>

View File

@ -771,8 +771,10 @@
"NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.",
"NetworkInterfaceDefault": "Default",
"PackagingShaders": "Packaging Shaders",
"AboutChangelogButton": "View Changelog on GitHub",
"AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser.",
"AboutChangelogButton": "View Changelog",
"AboutChangelogButtonTooltipMessage": "Click to view the changelog.",
"ChangelogWindowTitle": "Changelog",
"ChangelogDescription": "Showing the 10 most recent versions. For more changelog version history please visit Ryujinx GitHub page.",
"SettingsTabNetworkMultiplayer": "Multiplayer",
"MultiplayerMode": "Mode:",
"MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.",

View File

@ -44,6 +44,7 @@
<PackageReference Include="Avalonia.Svg.Skia" />
<PackageReference Include="DynamicData" />
<PackageReference Include="FluentAvaloniaUI" />
<PackageReference Include="HtmlAgilityPack" />
<PackageReference Include="OpenTK.Core" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />

View File

@ -87,8 +87,7 @@
Padding="5"
HorizontalAlignment="Center"
Background="Transparent"
Click="Button_OnClick"
Tag="https://github.com/Ryujinx/Ryujinx/wiki/Changelog#ryujinx-changelog">
Click="OpenChangelogWindow">
<TextBlock
FontSize="10"
Text="{locale:Locale AboutChangelogButton}"
@ -145,7 +144,6 @@
Background="Transparent"
Click="Button_OnClick"
CornerRadius="15"
Tag="https://github.com/Ryujinx/Ryujinx"
ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
<Image Source="{Binding GithubLogo}" />
</Button>

View File

@ -3,6 +3,7 @@ using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Styling;
using Avalonia.VisualTree;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
@ -52,6 +53,18 @@ namespace Ryujinx.Ava.UI.Windows
}
}
private void OpenChangelogWindow(object sender, RoutedEventArgs e)
{
ChangelogWindow changelogWindow = new ChangelogWindow();
// Find the parent window to use as the owner for the modal dialog
var parentWindow = this.FindAncestorOfType<Window>();
if (parentWindow != null)
{
changelogWindow.ShowDialog(parentWindow); // Pass the parent window as the owner
}
}
private void AmiiboLabel_OnPointerPressed(object sender, PointerPressedEventArgs e)
{
if (sender is TextBlock)

View File

@ -0,0 +1,42 @@
<Window
x:Class="Ryujinx.Ava.UI.Windows.ChangelogWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
Width="800"
Height="600"
Margin="0,-12,0,0"
d:DesignHeight="600"
d:DesignWidth="800"
CanResize="False"
WindowStartupLocation="CenterOwner"
Focusable="True"
mc:Ignorable="d"
Icon="resm:Ryujinx.UI.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.UI.Common">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical" Margin="20">
<!-- Localized text block -->
<TextBlock Text="{locale:Locale ChangelogDescription}"
TextWrapping="Wrap"
Margin="0,0,0,20"/>
<!-- Changelog -->
<TextBlock x:Name="LoadingTextBlock"
Text="Loading..."
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsVisible="True"/>
<ScrollViewer HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto"
Height="500">
<TextBlock x:Name="ChangelogTextBlock"
TextWrapping="Wrap"
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsVisible="True"/>
</ScrollViewer>
</StackPanel>
</Grid>
</Window>

View File

@ -0,0 +1,153 @@
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.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Button = Avalonia.Controls.Button;
namespace Ryujinx.Ava.UI.Windows
{
public partial class ChangelogWindow : StyleableWindow
{
public ChangelogWindow()
{
DataContext = this;
InitializeComponent();
InitializeAsync();
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.ChangelogWindowTitle];
}
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();
}
}
}