Skip to content

Commit c6c5a56

Browse files
committed
feat: Add plugin system for changelog viewers
Implements a flexible plugin system for displaying changelog content with multiple viewer options: - Add IChangelogViewer interface for viewer implementations - Add RichTextBox viewer for simple text display - Add WebBrowser viewer for legacy IE-based browser - Add WebView2 viewer for modern Edge WebView2 - Add ChangelogViewerFactory for viewer instantiation Users can now: - Choose their preferred viewer type via AutoUpdater.ChangelogViewerType - Load content directly as text or from URL (where supported) - Extend the system with custom viewer implementations The system automatically handles viewer initialization, cleanup, and fallback behavior when preferred viewers are unavailable.
1 parent f3f1bf2 commit c6c5a56

File tree

12 files changed

+1261
-1116
lines changed

12 files changed

+1261
-1116
lines changed

AutoUpdater.NET/AutoUpdater.cs

Lines changed: 763 additions & 757 deletions
Large diffs are not rendered by default.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
3+
namespace AutoUpdaterDotNET.ChangelogViewers;
4+
5+
public static class ChangelogViewerFactory
6+
{
7+
public static IChangelogViewer Create(ChangelogViewerType type)
8+
{
9+
return type switch
10+
{
11+
ChangelogViewerType.RichTextBox => new RichTextBoxViewer(),
12+
ChangelogViewerType.WebBrowser => new WebBrowserViewer(),
13+
ChangelogViewerType.WebView2 when WebView2Viewer.IsAvailable() => new WebView2Viewer(),
14+
ChangelogViewerType.WebView2 => throw new InvalidOperationException("WebView2 runtime is not available"),
15+
_ => throw new ArgumentException($"Unknown viewer type: {type}")
16+
};
17+
}
18+
19+
public static ChangelogViewerType GetDefaultViewerType()
20+
{
21+
if (WebView2Viewer.IsAvailable())
22+
return ChangelogViewerType.WebView2;
23+
24+
return ChangelogViewerType.WebBrowser;
25+
}
26+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace AutoUpdaterDotNET.ChangelogViewers;
2+
3+
public enum ChangelogViewerType
4+
{
5+
RichTextBox,
6+
WebBrowser,
7+
WebView2
8+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.Windows.Forms;
2+
3+
namespace AutoUpdaterDotNET.ChangelogViewers;
4+
5+
public interface IChangelogViewer
6+
{
7+
Control Control { get; }
8+
bool SupportsUrl { get; }
9+
void LoadContent(string content);
10+
void LoadUrl(string url);
11+
void Cleanup();
12+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System.Windows.Forms;
2+
3+
namespace AutoUpdaterDotNET.ChangelogViewers;
4+
5+
public class RichTextBoxViewer : IChangelogViewer
6+
{
7+
private readonly RichTextBox _richTextBox = new()
8+
{
9+
ReadOnly = true,
10+
Dock = DockStyle.Fill,
11+
BackColor = System.Drawing.SystemColors.Control,
12+
BorderStyle = BorderStyle.Fixed3D
13+
};
14+
public Control Control => _richTextBox;
15+
public bool SupportsUrl => false;
16+
17+
public void LoadContent(string content)
18+
{
19+
_richTextBox.Text = content;
20+
}
21+
22+
public void LoadUrl(string url)
23+
{
24+
throw new System.NotSupportedException("RichTextBox does not support loading from URL");
25+
}
26+
27+
public void Cleanup()
28+
{
29+
_richTextBox.Dispose();
30+
}
31+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using System.IO;
2+
using System.Diagnostics;
3+
using System.Windows.Forms;
4+
using Microsoft.Win32;
5+
6+
namespace AutoUpdaterDotNET.ChangelogViewers;
7+
8+
public class WebBrowserViewer : IChangelogViewer
9+
{
10+
private const string EmulationKey = @"SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION";
11+
private readonly int _emulationValue;
12+
private readonly string _executableName;
13+
private readonly WebBrowser _webBrowser;
14+
public Control Control => _webBrowser;
15+
public bool SupportsUrl => true;
16+
17+
public WebBrowserViewer()
18+
{
19+
_webBrowser = new WebBrowser
20+
{
21+
Dock = DockStyle.Fill,
22+
ScriptErrorsSuppressed = true,
23+
AllowWebBrowserDrop = false,
24+
WebBrowserShortcutsEnabled = false,
25+
IsWebBrowserContextMenuEnabled = false
26+
};
27+
28+
_executableName = Path.GetFileName(
29+
Process.GetCurrentProcess().MainModule?.FileName
30+
?? System.Reflection.Assembly.GetEntryAssembly()?.Location
31+
?? Application.ExecutablePath);
32+
33+
_emulationValue = _webBrowser.Version.Major switch
34+
{
35+
11 => 11001,
36+
10 => 10001,
37+
9 => 9999,
38+
8 => 8888,
39+
7 => 7000,
40+
_ => 0
41+
};
42+
43+
SetupEmulation();
44+
}
45+
46+
public void LoadContent(string content) => _webBrowser.DocumentText = content;
47+
48+
public void LoadUrl(string url)
49+
{
50+
if (AutoUpdater.BasicAuthChangeLog != null)
51+
_webBrowser.Navigate(url, "", null, $"Authorization: {AutoUpdater.BasicAuthChangeLog}");
52+
else
53+
_webBrowser.Navigate(url);
54+
}
55+
private void SetupEmulation()
56+
{
57+
if (_emulationValue == 0)
58+
return;
59+
60+
try
61+
{
62+
using var registryKey = Registry.CurrentUser.OpenSubKey(EmulationKey, true);
63+
registryKey?.SetValue(_executableName, _emulationValue, RegistryValueKind.DWord);
64+
}
65+
catch
66+
{
67+
// ignored
68+
}
69+
}
70+
private void RemoveEmulation()
71+
{
72+
if (_emulationValue == 0)
73+
return;
74+
75+
try
76+
{
77+
using var registryKey = Registry.CurrentUser.OpenSubKey(EmulationKey, true);
78+
registryKey?.DeleteValue(_executableName, false);
79+
}
80+
catch
81+
{
82+
// ignored
83+
}
84+
}
85+
86+
public void Cleanup()
87+
{
88+
_webBrowser.Dispose();
89+
RemoveEmulation();
90+
}
91+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using System;
2+
using System.IO;
3+
using System.Windows.Forms;
4+
using System.Threading.Tasks;
5+
using Microsoft.Web.WebView2.Core;
6+
using Microsoft.Web.WebView2.WinForms;
7+
8+
namespace AutoUpdaterDotNET.ChangelogViewers;
9+
10+
public class WebView2Viewer : IChangelogViewer
11+
{
12+
private bool _isInitialized;
13+
private readonly WebView2 _webView = new()
14+
{
15+
Dock = DockStyle.Fill,
16+
AllowExternalDrop = false
17+
};
18+
public Control Control => _webView;
19+
public bool SupportsUrl => true;
20+
21+
private async Task EnsureInitialized()
22+
{
23+
if (_isInitialized) return;
24+
25+
try
26+
{
27+
await _webView.EnsureCoreWebView2Async(await CoreWebView2Environment.CreateAsync(null, Path.GetTempPath()));
28+
_isInitialized = true;
29+
}
30+
catch (Exception)
31+
{
32+
throw new InvalidOperationException("WebView2 runtime is not available");
33+
}
34+
}
35+
36+
public async void LoadContent(string content)
37+
{
38+
await EnsureInitialized();
39+
_webView.CoreWebView2.SetVirtualHostNameToFolderMapping("local.files", Path.GetTempPath(), CoreWebView2HostResourceAccessKind.Allow);
40+
41+
// Write content to a temporary HTML file
42+
var tempFile = Path.Combine(Path.GetTempPath(), "changelog.html");
43+
File.WriteAllText(tempFile, content);
44+
45+
// Navigate to the local file
46+
_webView.CoreWebView2.Navigate("https://local.files/changelog.html");
47+
}
48+
49+
public async void LoadUrl(string url)
50+
{
51+
await EnsureInitialized();
52+
53+
if (AutoUpdater.BasicAuthChangeLog != null)
54+
{
55+
_webView.CoreWebView2.BasicAuthenticationRequested += delegate (object _, CoreWebView2BasicAuthenticationRequestedEventArgs args)
56+
{
57+
args.Response.UserName = ((BasicAuthentication)AutoUpdater.BasicAuthChangeLog).Username;
58+
args.Response.Password = ((BasicAuthentication)AutoUpdater.BasicAuthChangeLog).Password;
59+
};
60+
}
61+
62+
_webView.CoreWebView2.Navigate(url);
63+
}
64+
65+
public void Cleanup()
66+
{
67+
if (File.Exists(Path.Combine(Path.GetTempPath(), "changelog.html")))
68+
{
69+
try
70+
{
71+
File.Delete(Path.Combine(Path.GetTempPath(), "changelog.html"));
72+
}
73+
catch
74+
{
75+
// Ignore deletion errors
76+
}
77+
}
78+
_webView.Dispose();
79+
}
80+
81+
public static bool IsAvailable()
82+
{
83+
try
84+
{
85+
var availableBrowserVersion = CoreWebView2Environment.GetAvailableBrowserVersionString(null);
86+
const string requiredMinBrowserVersion = "86.0.616.0";
87+
return !string.IsNullOrEmpty(availableBrowserVersion) &&
88+
CoreWebView2Environment.CompareBrowserVersions(availableBrowserVersion,
89+
requiredMinBrowserVersion) >= 0;
90+
}
91+
catch (Exception)
92+
{
93+
return false;
94+
}
95+
}
96+
}

AutoUpdater.NET/UpdateForm.Designer.cs

Lines changed: 6 additions & 34 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)