Move advanced tracking settings into a separate tab

This commit is contained in:
chylex 2022-03-14 16:47:18 +01:00
parent 8f5f6065d8
commit d26e16eadf
No known key found for this signature in database
GPG Key ID: 4DE42C8F19A80548
16 changed files with 368 additions and 181 deletions

View File

@ -9,9 +9,11 @@
<entry key="Desktop/Dialogs/Progress/ProgressDialog.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Main/AboutWindow.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Main/Controls/FilterPanel.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Main/Controls/ServerConfigurationPanel.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Main/Controls/StatusBar.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Main/MainContentScreen.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Main/MainWindow.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Main/Pages/AdvancedPage.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Main/Pages/DatabasePage.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Main/Pages/TrackingPage.axaml" value="Desktop/Desktop.csproj" />
<entry key="Desktop/Main/Pages/ViewerPage.axaml" value="Desktop/Desktop.csproj" />

View File

@ -0,0 +1,50 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:DHT.Desktop.Main.Controls"
mc:Ignorable="d"
x:Class="DHT.Desktop.Main.Controls.ServerConfigurationPanel">
<Design.DataContext>
<controls:ServerConfigurationPanelModel />
</Design.DataContext>
<UserControl.Styles>
<Style Selector="TextBox">
<Setter Property="FontFamily" Value="Consolas,Courier" />
<Setter Property="FontSize" Value="15" />
</Style>
<Style Selector="WrapPanel > StackPanel">
<Setter Property="Orientation" Value="Vertical" />
<Setter Property="Margin" Value="0 0 10 10" />
</Style>
</UserControl.Styles>
<StackPanel>
<Button Command="{Binding OnClickToggleServerButton}" Content="{Binding ToggleServerButtonText}" IsEnabled="{Binding IsToggleServerButtonEnabled}" />
<TextBlock TextWrapping="Wrap" Margin="0 15">
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.
</TextBlock>
<WrapPanel>
<StackPanel>
<Label Target="Port">Port</Label>
<TextBox x:Name="Port" Width="70" Text="{Binding InputPort}" />
</StackPanel>
<StackPanel>
<Label Target="Token">Token</Label>
<StackPanel Orientation="Horizontal">
<TextBox x:Name="Token" Width="200" Text="{Binding InputToken}" />
</StackPanel>
</StackPanel>
<StackPanel VerticalAlignment="Bottom">
<Button Command="{Binding OnClickRandomizeToken}">Randomize Token</Button>
</StackPanel>
</WrapPanel>
<StackPanel Orientation="Horizontal" Spacing="10">
<Button IsEnabled="{Binding HasMadeChanges}" Command="{Binding OnClickApplyChanges}">Apply &amp; Restart</Button>
<Button IsEnabled="{Binding HasMadeChanges}" Command="{Binding OnClickCancelChanges}">Cancel</Button>
</StackPanel>
</StackPanel>
</UserControl>

View File

@ -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);
}
}
}

View File

@ -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<StatusBarModel.Status>? 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;
}
}
}

View File

@ -12,7 +12,7 @@
</Design.DataContext>
<UserControl.Styles>
<Style Selector="TabControl ItemsPresenter > WrapPanel">
<Style Selector="TabControl ItemsPresenter > Grid">
<Setter Property="Background" Value="#546A9F" />
</Style>
<Style Selector="TabItem">
@ -61,7 +61,12 @@
<DockPanel>
<TabControl x:Name="TabControl" TabStripPlacement="Left">
<TabItem x:Name="TabDatabase" Header="Database" Classes="first">
<TabControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid ColumnDefinitions="Auto" RowDefinitions="Auto,Auto,Auto,*,Auto,43" />
</ItemsPanelTemplate>
</TabControl.ItemsPanel>
<TabItem x:Name="TabDatabase" Header="Database" Classes="first" Grid.Row="0">
<DockPanel>
<controls:StatusBar DataContext="{Binding StatusBarModel}" DockPanel.Dock="Bottom" />
<ScrollViewer>
@ -69,7 +74,7 @@
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem x:Name="TabTracking" Header="Tracking">
<TabItem x:Name="TabTracking" Header="Tracking" DockPanel.Dock="Top" Grid.Row="1">
<DockPanel>
<controls:StatusBar DataContext="{Binding StatusBarModel}" DockPanel.Dock="Bottom" />
<ScrollViewer>
@ -77,7 +82,7 @@
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem x:Name="TabViewer" Header="Viewer">
<TabItem x:Name="TabViewer" Header="Viewer" DockPanel.Dock="Top" Grid.Row="2">
<DockPanel>
<controls:StatusBar DataContext="{Binding StatusBarModel}" DockPanel.Dock="Bottom" />
<ScrollViewer>
@ -85,6 +90,14 @@
</ScrollViewer>
</DockPanel>
</TabItem>
<TabItem x:Name="TabAdvanced" Header="Advanced" DockPanel.Dock="Bottom" Grid.Row="4">
<DockPanel>
<controls:StatusBar DataContext="{Binding StatusBarModel}" DockPanel.Dock="Bottom" />
<ScrollViewer>
<ContentPresenter Content="{Binding AdvancedPage}" Classes="page" />
</ScrollViewer>
</DockPanel>
</TabItem>
</TabControl>
</DockPanel>

View File

@ -1,13 +1,18 @@
using System;
using System.Threading.Tasks;
using Avalonia.Controls;
using DHT.Desktop.Dialogs.Message;
using DHT.Desktop.Main.Controls;
using DHT.Desktop.Main.Pages;
using DHT.Desktop.Server;
using DHT.Server.Database;
using DHT.Server.Service;
using DHT.Utils.Logging;
namespace DHT.Desktop.Main {
sealed class MainContentScreenModel : IDisposable {
private static readonly Log Log = Log.ForType<MainContentScreenModel>();
public DatabasePage DatabasePage { get; }
private DatabasePageModel DatabasePageModel { get; }
@ -17,6 +22,9 @@ namespace DHT.Desktop.Main {
public ViewerPage ViewerPage { get; }
private ViewerPageModel ViewerPageModel { get; }
public AdvancedPage AdvancedPage { get; }
private AdvancedPageModel AdvancedPageModel { get; }
public StatusBarModel StatusBarModel { get; }
public event EventHandler? DatabaseClosed {
@ -27,36 +35,62 @@ namespace DHT.Desktop.Main {
DatabasePageModel.DatabaseClosed -= value;
}
}
private readonly Window window;
private readonly ServerManager serverManager;
[Obsolete("Designer")]
public MainContentScreenModel() : this(null!, DummyDatabaseFile.Instance) {}
public MainContentScreenModel(Window window, IDatabaseFile db) {
this.window = window;
this.serverManager = new ServerManager(db);
ServerLauncher.ServerManagementExceptionCaught += ServerLauncherOnServerManagementExceptionCaught;
DatabasePageModel = new DatabasePageModel(window, db);
DatabasePage = new DatabasePage { DataContext = DatabasePageModel };
TrackingPageModel = new TrackingPageModel(window, db);
TrackingPageModel = new TrackingPageModel(window);
TrackingPage = new TrackingPage { DataContext = TrackingPageModel };
ViewerPageModel = new ViewerPageModel(window, db);
ViewerPage = new ViewerPage { DataContext = ViewerPageModel };
AdvancedPageModel = new AdvancedPageModel(window, serverManager);
AdvancedPage = new AdvancedPage { DataContext = AdvancedPageModel };
StatusBarModel = new StatusBarModel(db.Statistics);
TrackingPageModel.ServerStatusChanged += TrackingPageModelOnServerStatusChanged;
StatusBarModel.CurrentStatus = ServerLauncher.IsRunning ? StatusBarModel.Status.Ready : StatusBarModel.Status.Stopped;
AdvancedPageModel.ServerConfigurationModel.ServerStatusChanged += OnServerStatusChanged;
DatabaseClosed += OnDatabaseClosed;
StatusBarModel.CurrentStatus = serverManager.IsRunning ? StatusBarModel.Status.Ready : StatusBarModel.Status.Stopped;
}
public async Task Initialize() {
await TrackingPageModel.Initialize();
}
private void TrackingPageModelOnServerStatusChanged(object? sender, StatusBarModel.Status e) {
StatusBarModel.CurrentStatus = e;
AdvancedPageModel.Initialize();
serverManager.Launch();
}
public void Dispose() {
TrackingPageModel.Dispose();
ServerLauncher.ServerManagementExceptionCaught -= ServerLauncherOnServerManagementExceptionCaught;
ViewerPageModel.Dispose();
serverManager.Dispose();
}
private void OnServerStatusChanged(object? sender, StatusBarModel.Status e) {
StatusBarModel.CurrentStatus = e;
}
private void OnDatabaseClosed(object? sender, EventArgs e) {
serverManager.Stop();
}
private async void ServerLauncherOnServerManagementExceptionCaught(object? sender, Exception ex) {
Log.Error(ex);
await Dialog.ShowOk(window, "Internal Server Error", ex.Message);
}
}
}

View File

@ -8,7 +8,7 @@
Title="{Binding Title}"
Icon="avares://DiscordHistoryTracker/Resources/icon.ico"
Width="800" Height="500"
MinWidth="480" MinHeight="240"
MinWidth="480" MinHeight="260"
WindowStartupLocation="CenterScreen"
Closed="OnClosed">

View File

@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Avalonia.Controls;
using DHT.Desktop.Dialogs.Message;
using DHT.Desktop.Main.Pages;
using DHT.Desktop.Server;
using DHT.Server.Database;
using DHT.Utils.Models;
@ -61,11 +61,11 @@ namespace DHT.Desktop.Main {
}
if (args.ServerPort != null) {
TrackingPageModel.ServerPort = args.ServerPort.ToString()!;
ServerManager.Port = args.ServerPort.Value;
}
if (args.ServerToken != null) {
TrackingPageModel.ServerToken = args.ServerToken;
ServerManager.Token = args.ServerToken;
}
}

View File

@ -0,0 +1,19 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pages="clr-namespace:DHT.Desktop.Main.Pages"
xmlns:controls="clr-namespace:DHT.Desktop.Main.Controls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="DHT.Desktop.Main.Pages.AdvancedPage">
<Design.DataContext>
<pages:AdvancedPageModel />
</Design.DataContext>
<StackPanel Orientation="Vertical" Spacing="10">
<Expander Header="Internal Server Configuration" IsExpanded="True">
<controls:ServerConfigurationPanel DataContext="{Binding ServerConfigurationModel}" />
</Expander>
</StackPanel>
</UserControl>

View File

@ -0,0 +1,16 @@
using System.Diagnostics.CodeAnalysis;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace DHT.Desktop.Main.Pages {
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
public sealed class AdvancedPage : UserControl {
public AdvancedPage() {
InitializeComponent();
}
private void InitializeComponent() {
AvaloniaXamlLoader.Load(this);
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using Avalonia.Controls;
using DHT.Desktop.Main.Controls;
using DHT.Desktop.Server;
using DHT.Server.Database;
using DHT.Utils.Models;
namespace DHT.Desktop.Main.Pages {
sealed class AdvancedPageModel : BaseModel, IDisposable {
public ServerConfigurationPanelModel ServerConfigurationModel { get; }
[Obsolete("Designer")]
public AdvancedPageModel() : this(null!, new ServerManager(DummyDatabaseFile.Instance)) {}
public AdvancedPageModel(Window window, ServerManager serverManager) {
ServerConfigurationModel = new ServerConfigurationPanelModel(window, serverManager);
}
public void Initialize() {
ServerConfigurationModel.Initialize();
}
public void Dispose() {
ServerConfigurationModel.Dispose();
}
}
}

View File

@ -8,7 +8,6 @@ using DHT.Desktop.Common;
using DHT.Desktop.Dialogs.Message;
using DHT.Desktop.Dialogs.Progress;
using DHT.Server.Database;
using DHT.Server.Service;
using DHT.Utils.Logging;
using DHT.Utils.Models;
@ -76,7 +75,6 @@ namespace DHT.Desktop.Main.Pages {
}
public void CloseDatabase() {
ServerLauncher.Stop();
DatabaseClosed?.Invoke(this, EventArgs.Empty);
}

View File

@ -10,55 +10,14 @@
<pages:TrackingPageModel />
</Design.DataContext>
<UserControl.Styles>
<Style Selector="TextBox">
<Setter Property="FontFamily" Value="Consolas,Courier" />
<Setter Property="FontSize" Value="15" />
</Style>
<Style Selector="WrapPanel > StackPanel">
<Setter Property="Orientation" Value="Vertical" />
<Setter Property="Margin" Value="0 0 10 10" />
</Style>
<Style Selector="Expander">
<Setter Property="Margin" Value="0 5 0 0" />
</Style>
</UserControl.Styles>
<StackPanel Spacing="10">
<TextBlock TextWrapping="Wrap">
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.
</TextBlock>
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal" Spacing="10">
<Button x:Name="CopyTrackingScript" Click="CopyTrackingScriptButton_OnClick">Copy Tracking Script</Button>
<Button Command="{Binding OnClickToggleTrackingButton}" Content="{Binding ToggleTrackingButtonText}" IsEnabled="{Binding IsToggleButtonEnabled}" />
</StackPanel>
<Expander Header="Advanced Tracking Settings">
<StackPanel Spacing="10">
<TextBlock TextWrapping="Wrap">
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.
</TextBlock>
<WrapPanel>
<StackPanel>
<Label Target="Port">Port</Label>
<TextBox x:Name="Port" Width="70" Text="{Binding InputPort}" />
</StackPanel>
<StackPanel>
<Label Target="Token">Token</Label>
<StackPanel Orientation="Horizontal">
<TextBox x:Name="Token" Width="200" Text="{Binding InputToken}" />
</StackPanel>
</StackPanel>
<StackPanel VerticalAlignment="Bottom">
<Button Command="{Binding OnClickRandomizeToken}">Randomize Token</Button>
</StackPanel>
</WrapPanel>
<StackPanel Orientation="Horizontal" Spacing="10">
<Button IsEnabled="{Binding HasMadeChanges}" Command="{Binding OnClickApplyChanges}">Apply &amp; Restart</Button>
<Button IsEnabled="{Binding HasMadeChanges}" Command="{Binding OnClickCancelChanges}">Cancel</Button>
</StackPanel>
</StackPanel>
</Expander>
<TextBlock TextWrapping="Wrap" Margin="0 10 0 0">
<TextBlock TextWrapping="Wrap" Margin="0 5 0 0">
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.
</TextBlock>
<Button DockPanel.Dock="Right" Command="{Binding OnClickToggleAppDevTools}" Content="{Binding ToggleAppDevToolsButtonText}" IsEnabled="{Binding IsToggleAppDevToolsButtonEnabled}" />

View File

@ -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<TrackingPageModel>();
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<StatusBarModel.Status>? 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<bool> 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<bool> OnClickToggleTrackingButton() {
if (ServerLauncher.IsRunning) {
StopServer();
return true;
}
else {
return await StartServer();
}
}
public async Task<bool> 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";

View File

@ -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;
}
}
}
}

View File

@ -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<int>();
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;
}
}