diff --git a/ZuneModCore/ModManager.cs b/ZuneModCore/ModManager.cs
index d9780ec..55da1e3 100644
--- a/ZuneModCore/ModManager.cs
+++ b/ZuneModCore/ModManager.cs
@@ -11,14 +11,14 @@ public class ModManager
///
/// Factories for all available mods.
///
- public static readonly IReadOnlyList> ModFactories = new List>
- {
+ public static readonly IReadOnlyList> ModFactories =
+ [
new FeaturesOverrideModFactory(),
new VideoSyncModFactory(),
new WebservicesModFactory(),
new BackgroundImageModFactory(),
new MbidLocatorModFactory(),
- }.AsReadOnly();
+ ];
///
/// Creates instances of each mod using the available factories
diff --git a/ZuneModdingHelper/App.xaml.cs b/ZuneModdingHelper/App.xaml.cs
index 08288cc..4f36159 100644
--- a/ZuneModdingHelper/App.xaml.cs
+++ b/ZuneModdingHelper/App.xaml.cs
@@ -1,4 +1,6 @@
-using System.Windows;
+using System;
+using System.Text;
+using System.Windows;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using ZuneModCore;
@@ -16,10 +18,11 @@ namespace ZuneModdingHelper
public static readonly ReleaseVersion Version = new(2025, 8, 20, 0, Phase.Alpha);
public static readonly string VersionStr = Version.ToString();
- public static readonly System.Uri VersionUri = new($"https://github.com/ZuneDev/ZuneModdingHelper/releases/tag/{VersionStr}");
+ public static readonly Uri RepoUri = new($"https://github.com/ZuneDev/ZuneModdingHelper");
+ public static readonly Uri VersionUri = new($"{RepoUri}/releases/tag/{VersionStr}");
public const string DonateLink = "http://josh.askharoun.com/donate";
- public static readonly System.Uri DonateUri = new(DonateLink);
+ public static readonly Uri DonateUri = new(DonateLink);
protected override void OnStartup(StartupEventArgs e)
{
@@ -37,6 +40,21 @@ namespace ZuneModdingHelper
public static void OpenDonationLink() => OpenInBrowser(DonateLink);
+ public static void OpenIssueReport(ModMetadata modMetadata, string errorMessage)
+ {
+ StringBuilder issueBodyBuilder = new();
+ issueBodyBuilder.AppendLine("# Description");
+ issueBodyBuilder.AppendLine("");
+ issueBodyBuilder.AppendLine();
+ issueBodyBuilder.AppendLine("# Details");
+ issueBodyBuilder.AppendLine($"**Mod:** {modMetadata.Id} v{modMetadata.Version}");
+ issueBodyBuilder.AppendLine(errorMessage);
+
+ var escapedIssueBody = Uri.EscapeDataString(issueBodyBuilder.ToString());
+ var issueUrl = $"{RepoUri}/issues/new?title=&body={escapedIssueBody}";
+ OpenInBrowser(issueUrl);
+ }
+
private static void ConfigureServices()
{
ServiceCollection services = new();
diff --git a/ZuneModdingHelper/Pages/ModsPage.xaml.cs b/ZuneModdingHelper/Pages/ModsPage.xaml.cs
index 068d20b..5dc3777 100644
--- a/ZuneModdingHelper/Pages/ModsPage.xaml.cs
+++ b/ZuneModdingHelper/Pages/ModsPage.xaml.cs
@@ -8,7 +8,6 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using ZuneModCore;
-using ZuneModCore.Services;
using ZuneModdingHelper.Messages;
using ZuneModdingHelper.Services;
@@ -73,14 +72,7 @@ namespace ZuneModdingHelper.Pages
if (applyResult != null)
{
WeakReferenceMessenger.Default.Send();
-
- DialogViewModel errorDialog = new()
- {
- Title = MOD_MANAGER_TITLE,
- Description = $"Failed to apply '{modTitle}'.\r\n{applyResult}",
- };
- WeakReferenceMessenger.Default.Send(new ShowDialogMessage(errorDialog));
-
+ ShowErrorDialog(mod, "apply", applyResult);
return;
}
++progDialog.Progress;
@@ -126,14 +118,7 @@ namespace ZuneModdingHelper.Pages
if (resetResult != null)
{
WeakReferenceMessenger.Default.Send();
-
- DialogViewModel errorDialog = new()
- {
- Title = MOD_MANAGER_TITLE,
- Description = $"Failed to reset '{modTitle}'.\r\n{resetResult}",
- };
- WeakReferenceMessenger.Default.Send(new ShowDialogMessage(errorDialog));
-
+ ShowErrorDialog(mod, "reset", resetResult);
return;
}
@@ -163,6 +148,26 @@ namespace ZuneModdingHelper.Pages
return mod;
}
+ private static void ShowErrorDialog(Mod mod, string action, string errorMessage)
+ {
+ DialogViewModel errorDialog = new()
+ {
+ Title = MOD_MANAGER_TITLE,
+ Description = $"Failed to {action} '{mod.Metadata.Title}'.\r\n{errorMessage}",
+ ShowNegativeButton = true,
+ NegativeText = "REPORT ISSUE",
+ OnAction = new AsyncRelayCommand(ignoreIssue =>
+ {
+ if (!ignoreIssue)
+ App.OpenIssueReport(mod.Metadata, errorMessage);
+
+ WeakReferenceMessenger.Default.Send();
+ return Task.CompletedTask;
+ })
+ };
+ WeakReferenceMessenger.Default.Send(new ShowDialogMessage(errorDialog));
+ }
+
private async Task ShowDonationRequestDialog(bool _)
{
// Close success dialog