dragon-iso/src/TeamsISO.App/AboutWindow.xaml.cs
Zac Gaetano 1d1ce6a2a0
Some checks failed
CI / build-and-test (push) Failing after 29s
feat(wpf): rollback to WPF host, axe recording, fix settings pane
2026-05-14 06:02:40 -04:00

214 lines
7.4 KiB
C#

using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Versioning;
using System.Windows;
using System.Windows.Navigation;
using TeamsISO.App.Services;
using TeamsISO.Engine.NdiInterop;
namespace TeamsISO.App;
/// <summary>
/// Modal "About" dialog. Surfaces enough info that a user filing a support ticket
/// can paste version + NDI runtime + OS in a single screenshot.
/// </summary>
public partial class AboutWindow : Window
{
public AboutWindow()
{
InitializeComponent();
PopulateText();
}
private void PopulateText()
{
var asm = typeof(App).Assembly;
var info = asm.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion
?? asm.GetName().Version?.ToString()
?? "unknown";
VersionText.Text = info;
RuntimeText.Text = $".NET {Environment.Version}";
OsText.Text = Environment.OSVersion.ToString();
NdiText.Text = TryGetNdiVersion();
}
[SupportedOSPlatform("windows")]
private static string TryGetNdiVersion()
{
try
{
using var interop = new NdiInteropPInvoke(
Microsoft.Extensions.Logging.Abstractions.NullLogger<NdiInteropPInvoke>.Instance);
return interop.GetRuntimeVersion();
}
catch (Exception ex)
{
return $"not initialized ({ex.Message})";
}
}
private void OnClose(object sender, RoutedEventArgs e) => Close();
/// <summary>
/// Re-open the first-launch welcome dialog from About so users can revisit
/// the setup checklist without having to delete the suppression flag file
/// by hand. The "Don't show again" checkbox in the welcome dialog defaults
/// to checked so a re-shown welcome won't unset the suppression on close.
/// </summary>
private void OnShowOnboarding(object sender, RoutedEventArgs e)
{
var onboarding = new OnboardingWindow { Owner = this };
onboarding.ShowDialog();
}
/// <summary>
/// Quick-jump: open a path in Explorer. Creates the directory if missing
/// (operator might click "Recordings" before any have been made). Best-
/// effort — Explorer launch failures don't surface a dialog.
/// </summary>
private static void OpenInExplorer(string path)
{
try
{
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
Process.Start(new ProcessStartInfo
{
FileName = path,
UseShellExecute = true,
});
}
catch
{
// No-op: shell launch failed (path inaccessible / Explorer crashed)
}
}
private void OnOpenLogs(object sender, RoutedEventArgs e) =>
OpenInExplorer(Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"TeamsISO", "Logs"));
// OnOpenRecordings removed — recording feature axed.
private void OnOpenNotes(object sender, RoutedEventArgs e) =>
OpenInExplorer(Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"TeamsISO", "Notes"));
/// <summary>
/// Build the diagnostic bundle and tell the operator where it landed. The
/// bundle is just zipped logs / config / presets — no screenshots, no
/// memory dumps. Intended to be attached to a bug report.
/// </summary>
private void OnExportDiagnostics(object sender, RoutedEventArgs e)
{
try
{
var path = DiagnosticsBundle.Export();
var open = MessageBox.Show(
this,
$"Diagnostic bundle written to:\n\n{path}\n\nOpen the containing folder?",
"TeamsISO — Diagnostics exported",
MessageBoxButton.YesNo,
MessageBoxImage.Information);
if (open == MessageBoxResult.Yes)
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = "explorer.exe",
Arguments = $"/select,\"{path}\"",
UseShellExecute = true,
});
}
catch { /* shell launch failure is best-effort */ }
}
}
catch (Exception ex)
{
MessageBox.Show(
this,
$"Diagnostic export failed.\n\n{ex.Message}",
"TeamsISO — Diagnostic export",
MessageBoxButton.OK,
MessageBoxImage.Warning);
}
}
/// <summary>
/// Click handler for "Check for updates". Disables the button while the
/// HTTP call is in flight (so a second click doesn't spawn parallel
/// requests), then surfaces the result via MessageBox. On
/// <see cref="UpdateChecker.UpdateStatus.UpdateAvailable"/> we offer
/// to open the releases page so the operator can grab the new MSI.
/// </summary>
private async void OnCheckUpdate(object sender, RoutedEventArgs e)
{
UpdateButton.IsEnabled = false;
try
{
var result = await UpdateChecker.CheckAsync();
switch (result.Status)
{
case UpdateChecker.UpdateStatus.UpdateAvailable:
var open = MessageBox.Show(
this,
$"{result.Message}\n\n" +
$"You're on {result.CurrentVersion}; latest is {result.LatestTag}.\n\n" +
"Open the releases page to download the new MSI?",
"TeamsISO — Update available",
MessageBoxButton.YesNo,
MessageBoxImage.Information);
if (open == MessageBoxResult.Yes)
UpdateChecker.OpenReleasesPage();
break;
case UpdateChecker.UpdateStatus.UpToDate:
MessageBox.Show(
this,
result.Message ?? "You're on the latest release.",
"TeamsISO — Up to date",
MessageBoxButton.OK,
MessageBoxImage.Information);
break;
case UpdateChecker.UpdateStatus.Error:
default:
MessageBox.Show(
this,
$"Couldn't check for updates.\n\n{result.Message}",
"TeamsISO — Update check failed",
MessageBoxButton.OK,
MessageBoxImage.Warning);
break;
}
}
finally
{
UpdateButton.IsEnabled = true;
}
}
/// <summary>
/// Open the company site in the default browser. We intentionally use the
/// shell's URL handler rather than a tab inside the app — this is a
/// "tell me more" link, not a workflow.
/// </summary>
private void OnWebsiteClick(object sender, RoutedEventArgs e)
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = "https://wilddragon.net",
UseShellExecute = true,
});
}
catch
{
// best-effort; if shell launch fails the click is a no-op
}
}
}