diff --git a/app/.idea/.idea.DiscordHistoryTracker/.idea/avalonia.xml b/app/.idea/.idea.DiscordHistoryTracker/.idea/avalonia.xml
index 0642693..39aca8e 100644
--- a/app/.idea/.idea.DiscordHistoryTracker/.idea/avalonia.xml
+++ b/app/.idea/.idea.DiscordHistoryTracker/.idea/avalonia.xml
@@ -9,9 +9,11 @@
+
+
diff --git a/app/Desktop/Main/Controls/ServerConfigurationPanel.axaml b/app/Desktop/Main/Controls/ServerConfigurationPanel.axaml
new file mode 100644
index 0000000..f1a504b
--- /dev/null
+++ b/app/Desktop/Main/Controls/ServerConfigurationPanel.axaml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The following settings determine how the tracking script communicates with this application. If you change them, you will have to copy and apply the tracking script again.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/Desktop/Main/Controls/ServerConfigurationPanel.axaml.cs b/app/Desktop/Main/Controls/ServerConfigurationPanel.axaml.cs
new file mode 100644
index 0000000..cac5153
--- /dev/null
+++ b/app/Desktop/Main/Controls/ServerConfigurationPanel.axaml.cs
@@ -0,0 +1,16 @@
+using System.Diagnostics.CodeAnalysis;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace DHT.Desktop.Main.Controls {
+ [SuppressMessage("ReSharper", "MemberCanBeInternal")]
+ public sealed class ServerConfigurationPanel : UserControl {
+ public ServerConfigurationPanel() {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent() {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/app/Desktop/Main/Controls/ServerConfigurationPanelModel.cs b/app/Desktop/Main/Controls/ServerConfigurationPanelModel.cs
new file mode 100644
index 0000000..b96767e
--- /dev/null
+++ b/app/Desktop/Main/Controls/ServerConfigurationPanelModel.cs
@@ -0,0 +1,116 @@
+using System;
+using Avalonia.Controls;
+using DHT.Desktop.Dialogs.Message;
+using DHT.Desktop.Server;
+using DHT.Server.Database;
+using DHT.Server.Service;
+using DHT.Utils.Models;
+
+namespace DHT.Desktop.Main.Controls {
+ sealed class ServerConfigurationPanelModel : BaseModel, IDisposable {
+ private string inputPort;
+
+ public string InputPort {
+ get => inputPort;
+ set {
+ Change(ref inputPort, value);
+ OnPropertyChanged(nameof(HasMadeChanges));
+ }
+ }
+
+ private string inputToken;
+
+ public string InputToken {
+ get => inputToken;
+ set {
+ Change(ref inputToken, value);
+ OnPropertyChanged(nameof(HasMadeChanges));
+ }
+ }
+
+ public bool HasMadeChanges => ServerManager.Port.ToString() != InputPort || ServerManager.Token != InputToken;
+
+ private bool isToggleServerButtonEnabled = true;
+
+ public bool IsToggleServerButtonEnabled {
+ get => isToggleServerButtonEnabled;
+ set => Change(ref isToggleServerButtonEnabled, value);
+ }
+
+ public string ToggleServerButtonText => serverManager.IsRunning ? "Stop Server" : "Start Server";
+
+ public event EventHandler? ServerStatusChanged;
+
+ private readonly Window window;
+ private readonly ServerManager serverManager;
+
+ [Obsolete("Designer")]
+ public ServerConfigurationPanelModel() : this(null!, new ServerManager(DummyDatabaseFile.Instance)) {}
+
+ public ServerConfigurationPanelModel(Window window, ServerManager serverManager) {
+ this.window = window;
+ this.serverManager = serverManager;
+ this.inputPort = ServerManager.Port.ToString();
+ this.inputToken = ServerManager.Token;
+ }
+
+ public void Initialize() {
+ ServerLauncher.ServerStatusChanged += ServerLauncherOnServerStatusChanged;
+ }
+
+ public void Dispose() {
+ ServerLauncher.ServerStatusChanged -= ServerLauncherOnServerStatusChanged;
+ }
+
+ private void ServerLauncherOnServerStatusChanged(object? sender, EventArgs e) {
+ ServerStatusChanged?.Invoke(this, serverManager.IsRunning ? StatusBarModel.Status.Ready : StatusBarModel.Status.Stopped);
+ OnPropertyChanged(nameof(ToggleServerButtonText));
+ IsToggleServerButtonEnabled = true;
+ }
+
+ private void BeforeServerStart() {
+ IsToggleServerButtonEnabled = false;
+ ServerStatusChanged?.Invoke(this, StatusBarModel.Status.Starting);
+ }
+
+ private void StartServer() {
+ BeforeServerStart();
+ serverManager.Launch();
+ }
+
+ private void StopServer() {
+ IsToggleServerButtonEnabled = false;
+ ServerStatusChanged?.Invoke(this, StatusBarModel.Status.Stopping);
+ serverManager.Stop();
+ }
+
+ public void OnClickToggleServerButton() {
+ if (serverManager.IsRunning) {
+ StopServer();
+ }
+ else {
+ StartServer();
+ }
+ }
+
+ public void OnClickRandomizeToken() {
+ InputToken = ServerUtils.GenerateRandomToken(20);
+ }
+
+ public async void OnClickApplyChanges() {
+ if (!ushort.TryParse(InputPort, out ushort port)) {
+ await Dialog.ShowOk(window, "Invalid Port", "Port must be a number between 0 and 65535.");
+ return;
+ }
+
+ BeforeServerStart();
+ serverManager.Relaunch(port, InputToken);
+ OnPropertyChanged(nameof(HasMadeChanges));
+ }
+
+ public void OnClickCancelChanges() {
+ InputPort = ServerManager.Port.ToString();
+ InputToken = ServerManager.Token;
+ }
+ }
+}
diff --git a/app/Desktop/Main/MainContentScreen.axaml b/app/Desktop/Main/MainContentScreen.axaml
index 64b3154..32cf416 100644
--- a/app/Desktop/Main/MainContentScreen.axaml
+++ b/app/Desktop/Main/MainContentScreen.axaml
@@ -12,7 +12,7 @@
-
-
-
-
-
To start tracking messages, copy the tracking script and paste it into the console of either the Discord app, or your browser. The console is usually opened by pressing Ctrl+Shift+I.
-
-
-
-
- The following settings determine how the tracking script communicates with this application. If you change them, you will have to copy and apply the tracking script again.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
By default, the Discord app does not allow opening the console. The button below will change a hidden setting in the Discord app that controls whether the Ctrl+Shift+I shortcut is enabled.
diff --git a/app/Desktop/Main/Pages/TrackingPageModel.cs b/app/Desktop/Main/Pages/TrackingPageModel.cs
index 6ef7611..0b8fce5 100644
--- a/app/Desktop/Main/Pages/TrackingPageModel.cs
+++ b/app/Desktop/Main/Pages/TrackingPageModel.cs
@@ -5,51 +5,12 @@ using Avalonia;
using Avalonia.Controls;
using DHT.Desktop.Dialogs.Message;
using DHT.Desktop.Discord;
-using DHT.Desktop.Main.Controls;
-using DHT.Server.Database;
-using DHT.Server.Service;
-using DHT.Utils.Logging;
+using DHT.Desktop.Server;
using DHT.Utils.Models;
using static DHT.Desktop.Program;
namespace DHT.Desktop.Main.Pages {
- sealed class TrackingPageModel : BaseModel, IDisposable {
- private static readonly Log Log = Log.ForType();
-
- internal static string ServerPort { get; set; } = ServerUtils.FindAvailablePort(50000, 60000).ToString();
- internal static string ServerToken { get; set; } = ServerUtils.GenerateRandomToken(20);
-
- private string inputPort = ServerPort;
-
- public string InputPort {
- get => inputPort;
- set {
- Change(ref inputPort, value);
- OnPropertyChanged(nameof(HasMadeChanges));
- }
- }
-
- private string inputToken = ServerToken;
-
- public string InputToken {
- get => inputToken;
- set {
- Change(ref inputToken, value);
- OnPropertyChanged(nameof(HasMadeChanges));
- }
- }
-
- public bool HasMadeChanges => ServerPort != InputPort || ServerToken != InputToken;
-
- private bool isToggleTrackingButtonEnabled = true;
-
- public bool IsToggleButtonEnabled {
- get => isToggleTrackingButtonEnabled;
- set => Change(ref isToggleTrackingButtonEnabled, value);
- }
-
- public string ToggleTrackingButtonText => ServerLauncher.IsRunning ? "Pause Tracking" : "Resume Tracking";
-
+ sealed class TrackingPageModel : BaseModel {
private bool areDevToolsEnabled;
private bool AreDevToolsEnabled {
@@ -72,28 +33,16 @@ namespace DHT.Desktop.Main.Pages {
}
}
- public event EventHandler? ServerStatusChanged;
-
private readonly Window window;
- private readonly IDatabaseFile db;
[Obsolete("Designer")]
- public TrackingPageModel() : this(null!, DummyDatabaseFile.Instance) {}
+ public TrackingPageModel() : this(null!) {}
- public TrackingPageModel(Window window, IDatabaseFile db) {
+ public TrackingPageModel(Window window) {
this.window = window;
- this.db = db;
}
public async Task Initialize() {
- ServerLauncher.ServerStatusChanged += ServerLauncherOnServerStatusChanged;
- ServerLauncher.ServerManagementExceptionCaught += ServerLauncherOnServerManagementExceptionCaught;
-
- if (int.TryParse(ServerPort, out int port)) {
- string token = ServerToken;
- ServerLauncher.Relaunch(port, token, db);
- }
-
bool? devToolsEnabled = await DiscordAppSettings.AreDevToolsEnabled();
if (devToolsEnabled.HasValue) {
AreDevToolsEnabled = devToolsEnabled.Value;
@@ -104,55 +53,10 @@ namespace DHT.Desktop.Main.Pages {
}
}
- public void Dispose() {
- ServerLauncher.ServerManagementExceptionCaught -= ServerLauncherOnServerManagementExceptionCaught;
- ServerLauncher.ServerStatusChanged -= ServerLauncherOnServerStatusChanged;
- ServerLauncher.Stop();
- }
-
- private void ServerLauncherOnServerStatusChanged(object? sender, EventArgs e) {
- ServerStatusChanged?.Invoke(this, ServerLauncher.IsRunning ? StatusBarModel.Status.Ready : StatusBarModel.Status.Stopped);
- OnPropertyChanged(nameof(ToggleTrackingButtonText));
- IsToggleButtonEnabled = true;
- }
-
- private async void ServerLauncherOnServerManagementExceptionCaught(object? sender, Exception ex) {
- Log.Error(ex);
- await Dialog.ShowOk(window, "Server Error", ex.Message);
- }
-
- private async Task StartServer() {
- if (!int.TryParse(InputPort, out int port) || port is < 0 or > 65535) {
- await Dialog.ShowOk(window, "Invalid Port", "Port must be a number between 0 and 65535.");
- return false;
- }
-
- IsToggleButtonEnabled = false;
- ServerStatusChanged?.Invoke(this, StatusBarModel.Status.Starting);
- ServerLauncher.Relaunch(port, InputToken, db);
- return true;
- }
-
- private void StopServer() {
- IsToggleButtonEnabled = false;
- ServerStatusChanged?.Invoke(this, StatusBarModel.Status.Stopping);
- ServerLauncher.Stop();
- }
-
- public async Task OnClickToggleTrackingButton() {
- if (ServerLauncher.IsRunning) {
- StopServer();
- return true;
- }
- else {
- return await StartServer();
- }
- }
-
public async Task OnClickCopyTrackingScript() {
string bootstrap = await Resources.ReadTextAsync("Tracker/bootstrap.js");
- string script = bootstrap.Replace("= 0; /*[PORT]*/", "= " + ServerPort + ";")
- .Replace("/*[TOKEN]*/", HttpUtility.JavaScriptStringEncode(ServerToken))
+ string script = bootstrap.Replace("= 0; /*[PORT]*/", "= " + ServerManager.Port + ";")
+ .Replace("/*[TOKEN]*/", HttpUtility.JavaScriptStringEncode(ServerManager.Token))
.Replace("/*[IMPORTS]*/", await Resources.ReadJoinedAsync("Tracker/scripts/", '\n'))
.Replace("/*[CSS-CONTROLLER]*/", await Resources.ReadTextAsync("Tracker/styles/controller.css"))
.Replace("/*[CSS-SETTINGS]*/", await Resources.ReadTextAsync("Tracker/styles/settings.css"));
@@ -172,23 +76,6 @@ namespace DHT.Desktop.Main.Pages {
}
}
- public void OnClickRandomizeToken() {
- InputToken = ServerUtils.GenerateRandomToken(20);
- }
-
- public async void OnClickApplyChanges() {
- if (await StartServer()) {
- ServerPort = InputPort;
- ServerToken = InputToken;
- OnPropertyChanged(nameof(HasMadeChanges));
- }
- }
-
- public void OnClickCancelChanges() {
- InputPort = ServerPort;
- InputToken = ServerToken;
- }
-
public async void OnClickToggleAppDevTools() {
const string DialogTitle = "Discord App Settings File";
diff --git a/app/Desktop/Server/ServerManager.cs b/app/Desktop/Server/ServerManager.cs
new file mode 100644
index 0000000..3089abf
--- /dev/null
+++ b/app/Desktop/Server/ServerManager.cs
@@ -0,0 +1,50 @@
+using System;
+using DHT.Server.Database;
+using DHT.Server.Service;
+
+namespace DHT.Desktop.Server {
+ sealed class ServerManager : IDisposable {
+ public static ushort Port { get; set; } = ServerUtils.FindAvailablePort(50000, 60000);
+ public static string Token { get; set; } = ServerUtils.GenerateRandomToken(20);
+
+ private static ServerManager? instance;
+
+ public bool IsRunning => ServerLauncher.IsRunning;
+
+ private readonly IDatabaseFile db;
+
+ public ServerManager(IDatabaseFile db) {
+ if (db != DummyDatabaseFile.Instance) {
+ if (instance != null) {
+ throw new InvalidOperationException("Only one instance of ServerManager can exist at the same time!");
+ }
+
+ instance = this;
+ }
+
+ this.db = db;
+ }
+
+ public void Launch() {
+ ServerLauncher.Relaunch(Port, Token, db);
+ }
+
+ public void Relaunch(ushort port, string token) {
+ Port = port;
+ Token = token;
+ Launch();
+ }
+
+ public void Stop() {
+ ServerLauncher.Stop();
+ }
+
+ public void Dispose() {
+ Stop();
+
+ if (instance == this) {
+ instance = null;
+ }
+ }
+ }
+}
diff --git a/app/Server/Service/ServerUtils.cs b/app/Server/Service/ServerUtils.cs
index 9ae8025..ba978e6 100644
--- a/app/Server/Service/ServerUtils.cs
+++ b/app/Server/Service/ServerUtils.cs
@@ -7,7 +7,7 @@ using System.Text.RegularExpressions;
namespace DHT.Server.Service {
public static class ServerUtils {
- public static int FindAvailablePort(int min, int max) {
+ public static ushort FindAvailablePort(ushort min, ushort max) {
var properties = IPGlobalProperties.GetIPGlobalProperties();
var occupied = new HashSet();
occupied.UnionWith(properties.GetActiveTcpListeners().Select(static tcp => tcp.Port));
@@ -15,7 +15,7 @@ namespace DHT.Server.Service {
for (int port = min; port < max; port++) {
if (!occupied.Contains(port)) {
- return port;
+ return (ushort) port;
}
}