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.Empty, + Reactions = ImmutableArray.Empty + }; + }).ToArray(); + + db.AddMessages(messages); + + messageCount -= 100; + await callback.Update("Adding messages in batches of 100", ++batchIndex, batchCount); + } + } + + private static ulong RandomId(Random rand) { + ulong h = unchecked((ulong) rand.Next()); + ulong l = unchecked((ulong) rand.Next()); + return (h << 32) | l; + } + + private static string RandomName(string prefix) { + return prefix + "-" + ServerUtils.GenerateRandomToken(5); + } + + private static T RandomBiasedIndex(Random rand, T[] options) { + return options[(int) Math.Floor(options.Length * rand.NextDouble() * rand.NextDouble())]; + } + + private static readonly string[] RandomWords = { + "apple", "apricot", "artichoke", "arugula", "asparagus", "avocado", + "banana", "bean", "beechnut", "beet", "blackberry", "blackcurrant", "blueberry", "boysenberry", "bramble", "broccoli", + "cabbage", "cacao", "cantaloupe", "caper", "carambola", "carrot", "cauliflower", "celery", "chard", "cherry", "chokeberry", "citron", "clementine", "coconut", "corn", "crabapple", "cranberry", "cucumber", "currant", + "daikon", "date", "dewberry", "durian", + "edamame", "eggplant", "elderberry", "endive", + "fig", + "garlic", "ginger", "gooseberry", "grape", "grapefruit", "guava", + "honeysuckle", "horseradish", "huckleberry", + "jackfruit", "jicama", + "kale", "kiwi", "kohlrabi", "kumquat", + "leek", "lemon", "lentil", "lettuce", "lime", + "mandarin", "mango", "mushroom", "myrtle", + "nectarine", "nut", + "olive", "okra", "onion", "orange", + "papaya", "parsnip", "pawpaw", "peach", "pear", "pea", "pepper", "persimmon", "pineapple", "plum", "plantain", "pomegranate", "pomelo", "potato", "prune", "pumpkin", + "quandong", "quinoa", + "radicchio", "radish", "raisin", "raspberry", "redcurrant", "rhubarb", "rutabaga", + "spinach", "strawberry", "squash", + "tamarind", "tangerine", "tomatillo", "tomato", "turnip", + "vanilla", + "watercress", "watermelon", + "yam", + "zucchini" + }; + + private static string RandomText(Random rand, int maxWords) { + int wordCount = 1 + (int) Math.Floor(maxWords * Math.Pow(rand.NextDouble(), 3)); + return string.Join(' ', Enumerable.Range(0, wordCount).Select(_ => RandomWords[rand.Next(RandomWords.Length)])); + } + } +} +#else +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"; + + public void OnClickAddRandomDataToDatabase() {} + } +} +#endif diff --git a/app/Desktop/Main/Screens/MainContentScreen.axaml b/app/Desktop/Main/Screens/MainContentScreen.axaml index 6f2b334..4bc5c6c 100644 --- a/app/Desktop/Main/Screens/MainContentScreen.axaml +++ b/app/Desktop/Main/Screens/MainContentScreen.axaml @@ -80,7 +80,7 @@ - + @@ -103,6 +103,11 @@ + + + + + diff --git a/app/Desktop/Main/Screens/MainContentScreenModel.cs b/app/Desktop/Main/Screens/MainContentScreenModel.cs index 47a702f..89e0065 100644 --- a/app/Desktop/Main/Screens/MainContentScreenModel.cs +++ b/app/Desktop/Main/Screens/MainContentScreenModel.cs @@ -25,6 +25,15 @@ namespace DHT.Desktop.Main.Screens { public AdvancedPage AdvancedPage { get; } private AdvancedPageModel AdvancedPageModel { get; } + public DebugPage? DebugPage { get; } + + #if DEBUG + public bool HasDebugPage => true; + private DebugPageModel DebugPageModel { get; } + #else + public bool HasDebugPage => false; + #endif + public StatusBarModel StatusBarModel { get; } public event EventHandler? DatabaseClosed { @@ -35,7 +44,7 @@ namespace DHT.Desktop.Main.Screens { DatabasePageModel.DatabaseClosed -= value; } } - + private readonly Window window; private readonly ServerManager serverManager; @@ -45,9 +54,9 @@ namespace DHT.Desktop.Main.Screens { 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 }; @@ -56,15 +65,22 @@ namespace DHT.Desktop.Main.Screens { 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); + #if DEBUG + DebugPageModel = new DebugPageModel(window, db); + DebugPage = new DebugPage { DataContext = DebugPageModel }; + #else + DebugPage = null; + #endif + StatusBarModel = new StatusBarModel(db.Statistics); + AdvancedPageModel.ServerConfigurationModel.ServerStatusChanged += OnServerStatusChanged; DatabaseClosed += OnDatabaseClosed; - + StatusBarModel.CurrentStatus = serverManager.IsRunning ? StatusBarModel.Status.Ready : StatusBarModel.Status.Stopped; } diff --git a/app/Server/Data/Channel.cs b/app/Server/Data/Channel.cs index 7af3882..b31893c 100644 --- a/app/Server/Data/Channel.cs +++ b/app/Server/Data/Channel.cs @@ -1,11 +1,11 @@ namespace DHT.Server.Data { public readonly struct Channel { - public ulong Id { get; internal init; } - public ulong Server { get; internal init; } - public string Name { get; internal init; } - public ulong? ParentId { get; internal init; } - public int? Position { get; internal init; } - public string? Topic { get; internal init; } - public bool? Nsfw { get; internal init; } + public ulong Id { get; init; } + public ulong Server { get; init; } + public string Name { get; init; } + public ulong? ParentId { get; init; } + public int? Position { get; init; } + public string? Topic { get; init; } + public bool? Nsfw { get; init; } } } diff --git a/app/Server/Data/Message.cs b/app/Server/Data/Message.cs index 156b162..d276418 100644 --- a/app/Server/Data/Message.cs +++ b/app/Server/Data/Message.cs @@ -2,15 +2,15 @@ using System.Collections.Immutable; namespace DHT.Server.Data { public readonly struct Message { - public ulong Id { get; internal init; } - public ulong Sender { get; internal init; } - public ulong Channel { get; internal init; } - public string Text { get; internal init; } - public long Timestamp { get; internal init; } - public long? EditTimestamp { get; internal init; } - public ulong? RepliedToId { get; internal init; } - public ImmutableArray Attachments { get; internal init; } - public ImmutableArray Embeds { get; internal init; } - public ImmutableArray Reactions { get; internal init; } + public ulong Id { get; init; } + public ulong Sender { get; init; } + public ulong Channel { get; init; } + public string Text { get; init; } + public long Timestamp { get; init; } + public long? EditTimestamp { get; init; } + public ulong? RepliedToId { get; init; } + public ImmutableArray Attachments { get; init; } + public ImmutableArray Embeds { get; init; } + public ImmutableArray Reactions { get; init; } } } diff --git a/app/Server/Data/Server.cs b/app/Server/Data/Server.cs index 0461219..2cf5bde 100644 --- a/app/Server/Data/Server.cs +++ b/app/Server/Data/Server.cs @@ -1,7 +1,7 @@ namespace DHT.Server.Data { public readonly struct Server { - public ulong Id { get; internal init; } - public string Name { get; internal init; } - public ServerType? Type { get; internal init; } + public ulong Id { get; init; } + public string Name { get; init; } + public ServerType? Type { get; init; } } } diff --git a/app/Server/Data/User.cs b/app/Server/Data/User.cs index 2dfd6ee..4801e65 100644 --- a/app/Server/Data/User.cs +++ b/app/Server/Data/User.cs @@ -1,8 +1,8 @@ namespace DHT.Server.Data { public readonly struct User { - public ulong Id { get; internal init; } - public string Name { get; internal init; } - public string? AvatarUrl { get; internal init; } - public string? Discriminator { get; internal init; } + public ulong Id { get; init; } + public string Name { get; init; } + public string? AvatarUrl { get; init; } + public string? Discriminator { get; init; } } }