diff --git a/app/.idea/.idea.DiscordHistoryTracker/.idea/avalonia.xml b/app/.idea/.idea.DiscordHistoryTracker/.idea/avalonia.xml
index b063395..6cc5127 100644
--- a/app/.idea/.idea.DiscordHistoryTracker/.idea/avalonia.xml
+++ b/app/.idea/.idea.DiscordHistoryTracker/.idea/avalonia.xml
@@ -14,6 +14,7 @@
+
diff --git a/app/Desktop/Main/Pages/DebugPage.axaml b/app/Desktop/Main/Pages/DebugPage.axaml
new file mode 100644
index 0000000..a6701f6
--- /dev/null
+++ b/app/Desktop/Main/Pages/DebugPage.axaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/Desktop/Main/Pages/DebugPage.axaml.cs b/app/Desktop/Main/Pages/DebugPage.axaml.cs
new file mode 100644
index 0000000..ae3e253
--- /dev/null
+++ b/app/Desktop/Main/Pages/DebugPage.axaml.cs
@@ -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 DebugPage : UserControl {
+ public DebugPage() {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent() {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/app/Desktop/Main/Pages/DebugPageModel.cs b/app/Desktop/Main/Pages/DebugPageModel.cs
new file mode 100644
index 0000000..3704be3
--- /dev/null
+++ b/app/Desktop/Main/Pages/DebugPageModel.cs
@@ -0,0 +1,185 @@
+#if DEBUG
+using System;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using DHT.Desktop.Dialogs.Message;
+using DHT.Desktop.Dialogs.Progress;
+using DHT.Server.Data;
+using DHT.Server.Database;
+using DHT.Server.Service;
+using DHT.Utils.Models;
+
+namespace DHT.Desktop.Main.Pages {
+ sealed class DebugPageModel : BaseModel {
+ public string GenerateChannels { get; set; } = "0";
+ public string GenerateUsers { get; set; } = "0";
+ public string GenerateMessages { get; set; } = "0";
+
+ private readonly Window window;
+ private readonly IDatabaseFile db;
+
+ [Obsolete("Designer")]
+ public DebugPageModel() : this(null!, DummyDatabaseFile.Instance) {}
+
+ public DebugPageModel(Window window, IDatabaseFile db) {
+ this.window = window;
+ this.db = db;
+ }
+
+ public async void OnClickAddRandomDataToDatabase() {
+ if (!int.TryParse(GenerateChannels, out int channels) || channels < 1) {
+ await Dialog.ShowOk(window, "Generate Random Data", "Amount of channels must be at least 1!");
+ return;
+ }
+
+ if (!int.TryParse(GenerateUsers, out int users) || users < 1) {
+ await Dialog.ShowOk(window, "Generate Random Data", "Amount of users must be at least 1!");
+ return;
+ }
+
+ if (!int.TryParse(GenerateMessages, out int messages) || messages < 1) {
+ await Dialog.ShowOk(window, "Generate Random Data", "Amount of messages must be at least 1!");
+ return;
+ }
+
+ ProgressDialog progressDialog = new ProgressDialog {
+ DataContext = new ProgressDialogModel(async callback => await GenerateRandomData(channels, users, messages, callback)) {
+ Title = "Generating Random Data"
+ }
+ };
+
+ await progressDialog.ShowDialog(window);
+ }
+
+ private const int BatchSize = 100;
+
+ private async Task GenerateRandomData(int channelCount, int userCount, int messageCount, IProgressCallback callback) {
+ var rand = new Random();
+ var server = new DHT.Server.Data.Server {
+ Id = RandomId(rand),
+ Name = RandomName("s"),
+ Type = ServerType.Server
+ };
+
+ var channels = Enumerable.Range(0, channelCount).Select(i => new Channel {
+ Id = RandomId(rand),
+ Server = server.Id,
+ Name = RandomName("c"),
+ ParentId = null,
+ Position = i,
+ Topic = RandomText(rand, 10),
+ Nsfw = rand.Next(4) == 0
+ }).ToArray();
+
+ var users = Enumerable.Range(0, userCount).Select(_ => new User {
+ Id = RandomId(rand),
+ Name = RandomName("u"),
+ AvatarUrl = null,
+ Discriminator = rand.Next(0, 9999).ToString()
+ }).ToArray();
+
+ db.AddServer(server);
+ db.AddUsers(users);
+
+ foreach (var channel in channels) {
+ db.AddChannel(channel);
+ }
+
+ int batchCount = (messageCount + BatchSize - 1) / BatchSize;
+ await callback.Update("Adding messages in batches of 100", 0, batchCount);
+
+ var now = DateTimeOffset.Now;
+ int batchIndex = 0;
+
+ while (messageCount > 0) {
+ int hourOffset = batchIndex;
+
+ var messages = Enumerable.Range(0, Math.Min(messageCount, BatchSize)).Select(i => {
+ DateTimeOffset time = now.AddHours(hourOffset).AddMinutes((i * 60.0) / BatchSize);
+ DateTimeOffset? edit = rand.Next(100) == 0 ? time.AddSeconds(rand.Next(1, 60)) : null;
+
+ var timeMillis = time.ToUnixTimeMilliseconds();
+ var editMillis = edit?.ToUnixTimeMilliseconds();
+
+ return new Message {
+ Id = (ulong) timeMillis,
+ Sender = RandomBiasedIndex(rand, users).Id,
+ Channel = RandomBiasedIndex(rand, channels).Id,
+ Text = RandomText(rand, 100),
+ Timestamp = timeMillis,
+ EditTimestamp = editMillis,
+ RepliedToId = null,
+ Attachments = ImmutableArray.Empty,
+ Embeds = ImmutableArray