diff --git a/src/DesktopMagic/App.xaml b/src/DesktopMagic/App.xaml
index 83dca8a..85b7129 100644
--- a/src/DesktopMagic/App.xaml
+++ b/src/DesktopMagic/App.xaml
@@ -1,5 +1,6 @@
-
+
+
@@ -22,4 +25,4 @@
-
\ No newline at end of file
+
diff --git a/src/DesktopMagic/DesktopMagic.csproj b/src/DesktopMagic/DesktopMagic.csproj
index 60c67c3..bc1d2c3 100644
--- a/src/DesktopMagic/DesktopMagic.csproj
+++ b/src/DesktopMagic/DesktopMagic.csproj
@@ -52,6 +52,7 @@
+
@@ -75,4 +76,4 @@
PreserveNewest
-
\ No newline at end of file
+
diff --git a/src/DesktopMagic/Dialogs/ReleaseInfoDialog.xaml b/src/DesktopMagic/Dialogs/ReleaseInfoDialog.xaml
new file mode 100644
index 0000000..0ac8655
--- /dev/null
+++ b/src/DesktopMagic/Dialogs/ReleaseInfoDialog.xaml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/DesktopMagic/Dialogs/ReleaseInfoDialog.xaml.cs b/src/DesktopMagic/Dialogs/ReleaseInfoDialog.xaml.cs
new file mode 100644
index 0000000..7eb7bfa
--- /dev/null
+++ b/src/DesktopMagic/Dialogs/ReleaseInfoDialog.xaml.cs
@@ -0,0 +1,40 @@
+using System.Windows;
+
+namespace DesktopMagic.Dialogs;
+
+public partial class ReleaseInfoDialog : Wpf.Ui.Controls.FluentWindow
+{
+ public bool OpenReleaseRequested { get; private set; }
+
+ public ReleaseInfoDialog(
+ string title,
+ string releaseName,
+ string publishedAt,
+ string markdown,
+ string openButtonText,
+ string okButtonText)
+ {
+ InitializeComponent();
+
+ Resources.MergedDictionaries.Add(App.LanguageDictionary);
+
+ titleBar.Title = title;
+ Title = title;
+ releaseNameTextBlock.Text = releaseName;
+ publishedAtTextBlock.Text = publishedAt;
+ releaseMarkdownViewer.Markdown = string.IsNullOrWhiteSpace(markdown) ? "-" : markdown;
+ openButton.Content = openButtonText;
+ okButton.Content = okButtonText;
+ }
+
+ private void OpenButton_Click(object sender, RoutedEventArgs e)
+ {
+ OpenReleaseRequested = true;
+ DialogResult = true;
+ }
+
+ private void OkButton_Click(object sender, RoutedEventArgs e)
+ {
+ DialogResult = false;
+ }
+}
diff --git a/src/DesktopMagic/Helpers/GitHubReleaseService.cs b/src/DesktopMagic/Helpers/GitHubReleaseService.cs
new file mode 100644
index 0000000..6e74863
--- /dev/null
+++ b/src/DesktopMagic/Helpers/GitHubReleaseService.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Net.Http;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+
+namespace DesktopMagic.Helpers;
+
+public sealed class GitHubLatestRelease
+{
+ [JsonPropertyName("tag_name")]
+ public string TagName { get; init; } = "Unknown";
+
+ [JsonPropertyName("name")]
+ public string Name { get; init; } = "Unknown";
+
+ [JsonPropertyName("body")]
+ public string Body { get; init; } = string.Empty;
+
+ [JsonPropertyName("published_at")]
+ public DateTimeOffset? PublishedAt { get; init; }
+
+ [JsonPropertyName("html_url")]
+ public string HtmlUrl { get; init; } = "https://github.com/Stone-Red-Code/DesktopMagic/releases/latest";
+}
+
+public static class GitHubReleaseService
+{
+ private const string LatestReleaseApiUrl = "https://api.github.com/repos/Stone-Red-Code/DesktopMagic/releases/latest";
+ private static readonly HttpClient _httpClient = CreateReleaseHttpClient();
+
+ private static HttpClient CreateReleaseHttpClient()
+ {
+ HttpClient client = new()
+ {
+ Timeout = TimeSpan.FromSeconds(10)
+ };
+
+ client.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.github+json");
+ client.DefaultRequestHeaders.UserAgent.ParseAdd("DesktopMagic");
+ return client;
+ }
+
+ public static async Task GetLatestReleaseInfoAsync()
+ {
+ using HttpResponseMessage response = await _httpClient.GetAsync(LatestReleaseApiUrl);
+ response.EnsureSuccessStatusCode();
+ await using var responseStream = await response.Content.ReadAsStreamAsync();
+ return await JsonSerializer.DeserializeAsync(responseStream);
+ }
+}
diff --git a/src/DesktopMagic/MainWindow.xaml.cs b/src/DesktopMagic/MainWindow.xaml.cs
index ea07060..4672d65 100644
--- a/src/DesktopMagic/MainWindow.xaml.cs
+++ b/src/DesktopMagic/MainWindow.xaml.cs
@@ -1,7 +1,13 @@
-using DesktopMagic.DataContexts;
+using DesktopMagic.DataContexts;
+using DesktopMagic.Dialogs;
+using DesktopMagic.Helpers;
using System;
using System.Diagnostics;
+using System.Net.Http;
+using System.Reflection;
+using System.Text.Json;
+using System.Threading.Tasks;
using System.Windows;
using Wpf.Ui.Appearance;
@@ -43,6 +49,7 @@ public partial class MainWindow : FluentWindow
_manager.IsLoaded = true;
_mainWindowDataContext.IsLoading = false;
+ await ShowLatestReleaseInfoAfterUpdateAsync();
App.Logger.LogInfo("Application loaded", source: "MainWindow");
}
@@ -190,4 +197,75 @@ public partial class MainWindow : FluentWindow
{
Quit();
}
-}
\ No newline at end of file
+
+ private async Task ShowLatestReleaseInfoAfterUpdateAsync()
+ {
+ string currentVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0.0.0.0";
+ string? lastShownVersion = _manager.Settings.ReleaseInfoLastAppVersion;
+
+ if (string.Equals(lastShownVersion, currentVersion, StringComparison.Ordinal))
+ {
+ return;
+ }
+
+ GitHubLatestRelease? latestRelease = await GetLatestReleaseInfoAsync();
+ if (latestRelease is null)
+ {
+ return;
+ }
+
+ string releaseName = !string.IsNullOrWhiteSpace(latestRelease.Name) ? latestRelease.Name : latestRelease.TagName;
+ string publishedAt = latestRelease.PublishedAt?.ToLocalTime().ToString("g") ?? (string)FindResource("unknown");
+ string publishedAtText = string.Format((string)FindResource("releaseInfoPublishedFormat"), publishedAt);
+ string releaseNotesMarkdown = string.IsNullOrWhiteSpace(latestRelease.Body) ? "-" : latestRelease.Body.Trim();
+
+ ReleaseInfoDialog releaseInfoDialog = new(
+ (string)FindResource("releaseInfoTitle"),
+ releaseName,
+ publishedAtText,
+ releaseNotesMarkdown,
+ (string)FindResource("openReleasePage"),
+ (string)FindResource("ok"))
+ {
+ Owner = this,
+ Topmost = true
+ };
+
+ _ = releaseInfoDialog.ShowDialog();
+
+ if (releaseInfoDialog.OpenReleaseRequested)
+ {
+ ProcessStartInfo processStartInfo = new()
+ {
+ UseShellExecute = true,
+ FileName = latestRelease.HtmlUrl
+ };
+ _ = Process.Start(processStartInfo);
+ }
+
+ _manager.Settings.ReleaseInfoLastAppVersion = currentVersion;
+ _manager.SaveSettings();
+ }
+
+ private static async Task GetLatestReleaseInfoAsync()
+ {
+ try
+ {
+ return await GitHubReleaseService.GetLatestReleaseInfoAsync();
+ }
+ catch (HttpRequestException ex)
+ {
+ App.Logger.LogError($"Failed to retrieve latest release information: {ex.Message}", source: "MainWindow");
+ }
+ catch (TaskCanceledException ex)
+ {
+ App.Logger.LogWarn($"Timed out while retrieving latest release information: {ex.Message}", source: "MainWindow");
+ }
+ catch (JsonException ex)
+ {
+ App.Logger.LogError($"Failed to parse latest release information: {ex.Message}", source: "MainWindow");
+ }
+
+ return null;
+ }
+}
diff --git a/src/DesktopMagic/Resources/Strings/StringResources.de.xaml b/src/DesktopMagic/Resources/Strings/StringResources.de.xaml
index 06be6cd..1fa8a43 100644
--- a/src/DesktopMagic/Resources/Strings/StringResources.de.xaml
+++ b/src/DesktopMagic/Resources/Strings/StringResources.de.xaml
@@ -60,9 +60,14 @@
Erstellt: {0}
Aktualisiert: {0}
Version: {0}
+ DesktopMagic wurde aktualisiert!
+ Neueste Version: {0}
+ Veröffentlicht: {0}
+ Release Notes:
+ Release-Seite öffnen
Unten
Mitte
Oben
-
\ No newline at end of file
+
diff --git a/src/DesktopMagic/Resources/Strings/StringResources.en.xaml b/src/DesktopMagic/Resources/Strings/StringResources.en.xaml
index f1e0508..d003a1c 100644
--- a/src/DesktopMagic/Resources/Strings/StringResources.en.xaml
+++ b/src/DesktopMagic/Resources/Strings/StringResources.en.xaml
@@ -62,9 +62,14 @@
Added: {0}
Updated: {0}
Version: {0}
+ DesktopMagic has been updated!
+ Latest release: {0}
+ Published: {0}
+ Release notes:
+ Open release page
Bottom
Middle
Top
-
\ No newline at end of file
+
diff --git a/src/DesktopMagic/Settings/DesktopMagicSettings.cs b/src/DesktopMagic/Settings/DesktopMagicSettings.cs
index 9cbb10d..1cb61f6 100644
--- a/src/DesktopMagic/Settings/DesktopMagicSettings.cs
+++ b/src/DesktopMagic/Settings/DesktopMagicSettings.cs
@@ -64,6 +64,8 @@ public class DesktopMagicSettings : INotifyPropertyChanged
public string? ModIoAccessToken { get; set; }
+ public string? ReleaseInfoLastAppVersion { get; set; }
+
public DesktopMagicSettings()
{
themes.CollectionChanged += (s, e) => CurrentLayout.UpdateTheme();
@@ -76,4 +78,4 @@ public class DesktopMagicSettings : INotifyPropertyChanged
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
-}
\ No newline at end of file
+}