diff --git a/Directory.Packages.props b/Directory.Packages.props index db480e57..c97f7eb2 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -34,7 +34,7 @@ - + diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlForwardSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlForwardSpecs.cs index 823c900e..24929c89 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlForwardSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlForwardSpecs.cs @@ -1,9 +1,9 @@ using System.Threading.Tasks; using AngleSharp.Dom; using DiscordChatExporter.Cli.Tests.Infra; -using DiscordChatExporter.Cli.Tests.Utils.Extensions; using DiscordChatExporter.Core.Discord; using FluentAssertions; +using PowerKit.Extensions; using Xunit; namespace DiscordChatExporter.Cli.Tests.Specs; @@ -22,7 +22,7 @@ public class HtmlForwardSpecs // Assert message .Text() - .ReplaceWhiteSpace() + .ReplaceWhiteSpace(' ') .Should() .ContainAll("Forwarded", @"¯\_(ツ)_/¯", "12/29/2025 2:14 PM"); } diff --git a/DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownSpecs.cs index c9623efb..c8496ada 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/HtmlMarkdownSpecs.cs @@ -1,9 +1,9 @@ using System.Threading.Tasks; using AngleSharp.Dom; using DiscordChatExporter.Cli.Tests.Infra; -using DiscordChatExporter.Cli.Tests.Utils.Extensions; using DiscordChatExporter.Core.Discord; using FluentAssertions; +using PowerKit.Extensions; using Xunit; namespace DiscordChatExporter.Cli.Tests.Specs; @@ -22,11 +22,14 @@ public class HtmlMarkdownSpecs // Assert message .Text() - .ReplaceWhiteSpace() + .ReplaceWhiteSpace(' ') .Should() .Contain("Default timestamp: 2/12/2023 1:36 PM"); - message.InnerHtml.ReplaceWhiteSpace().Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -39,8 +42,11 @@ public class HtmlMarkdownSpecs ); // Assert - message.Text().ReplaceWhiteSpace().Should().Contain("Short time timestamp: 1:36 PM"); - message.InnerHtml.ReplaceWhiteSpace().Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message.Text().ReplaceWhiteSpace(' ').Should().Contain("Short time timestamp: 1:36 PM"); + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -53,8 +59,11 @@ public class HtmlMarkdownSpecs ); // Assert - message.Text().ReplaceWhiteSpace().Should().Contain("Long time timestamp: 1:36:12 PM"); - message.InnerHtml.ReplaceWhiteSpace().Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message.Text().ReplaceWhiteSpace(' ').Should().Contain("Long time timestamp: 1:36:12 PM"); + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -67,8 +76,11 @@ public class HtmlMarkdownSpecs ); // Assert - message.Text().ReplaceWhiteSpace().Should().Contain("Short date timestamp: 2/12/2023"); - message.InnerHtml.ReplaceWhiteSpace().Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message.Text().ReplaceWhiteSpace(' ').Should().Contain("Short date timestamp: 2/12/2023"); + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -83,11 +95,14 @@ public class HtmlMarkdownSpecs // Assert message .Text() - .ReplaceWhiteSpace() + .ReplaceWhiteSpace(' ') .Should() .Contain("Long date timestamp: Sunday, February 12, 2023"); - message.InnerHtml.ReplaceWhiteSpace().Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -102,11 +117,14 @@ public class HtmlMarkdownSpecs // Assert message .Text() - .ReplaceWhiteSpace() + .ReplaceWhiteSpace(' ') .Should() .Contain("Full timestamp: Sunday, February 12, 2023 1:36 PM"); - message.InnerHtml.ReplaceWhiteSpace().Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -121,11 +139,14 @@ public class HtmlMarkdownSpecs // Assert message .Text() - .ReplaceWhiteSpace() + .ReplaceWhiteSpace(' ') .Should() .Contain("Full long timestamp: Sunday, February 12, 2023 1:36:12 PM"); - message.InnerHtml.ReplaceWhiteSpace().Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] @@ -140,11 +161,14 @@ public class HtmlMarkdownSpecs // Assert message .Text() - .ReplaceWhiteSpace() + .ReplaceWhiteSpace(' ') .Should() .Contain("Relative timestamp: 2/12/2023 1:36 PM"); - message.InnerHtml.ReplaceWhiteSpace().Should().Contain("Sunday, February 12, 2023 1:36 PM"); + message + .InnerHtml.ReplaceWhiteSpace(' ') + .Should() + .Contain("Sunday, February 12, 2023 1:36 PM"); } [Fact] diff --git a/DiscordChatExporter.Cli.Tests/Specs/PlainTextForwardSpecs.cs b/DiscordChatExporter.Cli.Tests/Specs/PlainTextForwardSpecs.cs index 8d42a36e..2f8ef73c 100644 --- a/DiscordChatExporter.Cli.Tests/Specs/PlainTextForwardSpecs.cs +++ b/DiscordChatExporter.Cli.Tests/Specs/PlainTextForwardSpecs.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using DiscordChatExporter.Cli.Tests.Infra; -using DiscordChatExporter.Cli.Tests.Utils.Extensions; using FluentAssertions; +using PowerKit.Extensions; using Xunit; namespace DiscordChatExporter.Cli.Tests.Specs; @@ -16,7 +16,7 @@ public class PlainTextForwardSpecs // Assert document - .ReplaceWhiteSpace() + .ReplaceWhiteSpace(' ') .Should() .ContainAll("{Forwarded Message}", @"¯\_(ツ)_/¯", "12/28/2025 10:52 PM"); } diff --git a/DiscordChatExporter.Cli.Tests/Utils/Extensions/StringExtensions.cs b/DiscordChatExporter.Cli.Tests/Utils/Extensions/StringExtensions.cs deleted file mode 100644 index 4db07483..00000000 --- a/DiscordChatExporter.Cli.Tests/Utils/Extensions/StringExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Text; - -namespace DiscordChatExporter.Cli.Tests.Utils.Extensions; - -internal static class StringExtensions -{ - extension(string str) - { - public string ReplaceWhiteSpace(string replacement = " ") - { - var buffer = new StringBuilder(str.Length); - - foreach (var ch in str) - buffer.Append(char.IsWhiteSpace(ch) ? replacement : ch); - - return buffer.ToString(); - } - } -} diff --git a/DiscordChatExporter.Core/Discord/Data/Embeds/Embed.cs b/DiscordChatExporter.Core/Discord/Data/Embeds/Embed.cs index bb32cc54..8b6a6591 100644 --- a/DiscordChatExporter.Core/Discord/Data/Embeds/Embed.cs +++ b/DiscordChatExporter.Core/Discord/Data/Embeds/Embed.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text.Json; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; using PowerKit.Extensions; @@ -59,7 +58,7 @@ public partial record Embed var color = json.GetPropertyOrNull("color") ?.GetInt32OrNull() ?.Pipe(System.Drawing.Color.FromArgb) - .ResetAlpha(); + .WithFullAlpha(); var author = json.GetPropertyOrNull("author")?.Pipe(EmbedAuthor.Parse); var description = json.GetPropertyOrNull("description")?.GetStringOrNull(); diff --git a/DiscordChatExporter.Core/Discord/Data/Role.cs b/DiscordChatExporter.Core/Discord/Data/Role.cs index 10e2cf6d..999da66e 100644 --- a/DiscordChatExporter.Core/Discord/Data/Role.cs +++ b/DiscordChatExporter.Core/Discord/Data/Role.cs @@ -1,7 +1,6 @@ using System.Drawing; using System.Text.Json; using DiscordChatExporter.Core.Discord.Data.Common; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Reading; using PowerKit.Extensions; @@ -19,7 +18,7 @@ public record Role(Snowflake Id, string Name, int Position, Color? Color) : IHas var color = json.GetPropertyOrNull("color") ?.GetInt32OrNull() ?.Pipe(System.Drawing.Color.FromArgb) - .ResetAlpha() + .WithFullAlpha() .NullIf(c => c.ToRgb() <= 0); return new Role(id, name, position, color); diff --git a/DiscordChatExporter.Core/Discord/DiscordClient.cs b/DiscordChatExporter.Core/Discord/DiscordClient.cs index acda5f93..03d7cf6b 100644 --- a/DiscordChatExporter.Core/Discord/DiscordClient.cs +++ b/DiscordChatExporter.Core/Discord/DiscordClient.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Exceptions; using DiscordChatExporter.Core.Utils; -using DiscordChatExporter.Core.Utils.Extensions; using Gress; using JsonExtensions.Http; using JsonExtensions.Reading; @@ -60,10 +59,12 @@ public class DiscordClient( { var remainingRequestCount = response .Headers.TryGetValue("X-RateLimit-Remaining") + .NullIfWhiteSpace() ?.Pipe(s => int.Parse(s, CultureInfo.InvariantCulture)); var resetAfterDelay = response .Headers.TryGetValue("X-RateLimit-Reset-After") + .NullIfWhiteSpace() ?.Pipe(s => double.Parse(s, CultureInfo.InvariantCulture)) .Pipe(TimeSpan.FromSeconds); diff --git a/DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs b/DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs index 1ba5af37..43f820d3 100644 --- a/DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/JsonMessageWriter.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Discord.Data.Embeds; using DiscordChatExporter.Core.Markdown.Parsing; -using DiscordChatExporter.Core.Utils.Extensions; using JsonExtensions.Writing; using PowerKit.Extensions; @@ -56,7 +55,7 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) Context.TryGetMember(user.Id)?.DisplayName ?? user.DisplayName ); - _writer.WriteString("color", Context.TryGetUserColor(user.Id)?.ToHex()); + _writer.WriteString("color", Context.TryGetUserColor(user.Id)?.ToHexString()); _writer.WriteBoolean("isBot", user.IsBot); if (includeRoles) @@ -110,7 +109,7 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) _writer.WriteString("id", role.Id.ToString()); _writer.WriteString("name", role.Name); - _writer.WriteString("color", role.Color?.ToHex()); + _writer.WriteString("color", role.Color?.ToHexString()); _writer.WriteNumber("position", role.Position); _writer.WriteEndObject(); @@ -282,7 +281,7 @@ internal class JsonMessageWriter(Stream stream, ExportContext context) ); if (embed.Color is not null) - _writer.WriteString("color", embed.Color.Value.ToHex()); + _writer.WriteString("color", embed.Color.Value.ToHexString()); if (embed.Author is not null) { diff --git a/DiscordChatExporter.Core/Utils/Extensions/ColorExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/ColorExtensions.cs deleted file mode 100644 index a3ec6486..00000000 --- a/DiscordChatExporter.Core/Utils/Extensions/ColorExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Drawing; - -namespace DiscordChatExporter.Core.Utils.Extensions; - -public static class ColorExtensions -{ - extension(Color color) - { - public Color WithAlpha(int alpha) => Color.FromArgb(alpha, color); - - public Color ResetAlpha() => color.WithAlpha(255); - - public int ToRgb() => color.ToArgb() & 0xffffff; - - public string ToHex() => $"#{color.R:X2}{color.G:X2}{color.B:X2}"; - } -} diff --git a/DiscordChatExporter.Core/Utils/Extensions/HttpExtensions.cs b/DiscordChatExporter.Core/Utils/Extensions/HttpExtensions.cs deleted file mode 100644 index 19841afc..00000000 --- a/DiscordChatExporter.Core/Utils/Extensions/HttpExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Net.Http.Headers; - -namespace DiscordChatExporter.Core.Utils.Extensions; - -public static class HttpExtensions -{ - extension(HttpHeaders headers) - { - public string? TryGetValue(string name) => - headers.TryGetValues(name, out var values) ? string.Concat(values) : null; - } -} diff --git a/DiscordChatExporter.Gui/App.axaml.cs b/DiscordChatExporter.Gui/App.axaml.cs index 64e4e422..af71f607 100644 --- a/DiscordChatExporter.Gui/App.axaml.cs +++ b/DiscordChatExporter.Gui/App.axaml.cs @@ -7,13 +7,13 @@ using Avalonia.Platform; using DiscordChatExporter.Gui.Framework; using DiscordChatExporter.Gui.Localization; using DiscordChatExporter.Gui.Services; -using DiscordChatExporter.Gui.Utils.Extensions; using DiscordChatExporter.Gui.ViewModels; using DiscordChatExporter.Gui.ViewModels.Components; using DiscordChatExporter.Gui.ViewModels.Dialogs; using Material.Styles.Themes; using Microsoft.Extensions.DependencyInjection; using PowerKit; +using PowerKit.Extensions; namespace DiscordChatExporter.Gui; diff --git a/DiscordChatExporter.Gui/Localization/LocalizationManager.cs b/DiscordChatExporter.Gui/Localization/LocalizationManager.cs index 92734b8b..9b8fdfba 100644 --- a/DiscordChatExporter.Gui/Localization/LocalizationManager.cs +++ b/DiscordChatExporter.Gui/Localization/LocalizationManager.cs @@ -3,8 +3,8 @@ using System.Globalization; using System.Runtime.CompilerServices; using CommunityToolkit.Mvvm.ComponentModel; using DiscordChatExporter.Gui.Services; -using DiscordChatExporter.Gui.Utils.Extensions; using PowerKit; +using PowerKit.Extensions; namespace DiscordChatExporter.Gui.Localization; diff --git a/DiscordChatExporter.Gui/Utils/Extensions/CommandExtensions.cs b/DiscordChatExporter.Gui/Utils/Extensions/CommandExtensions.cs deleted file mode 100644 index fc0008cb..00000000 --- a/DiscordChatExporter.Gui/Utils/Extensions/CommandExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Windows.Input; - -namespace DiscordChatExporter.Gui.Utils.Extensions; - -internal static class CommandExtensions -{ - extension(ICommand command) - { - public void ExecuteIfCan(object? parameter = null) - { - if (command.CanExecute(parameter)) - command.Execute(parameter); - } - } -} diff --git a/DiscordChatExporter.Gui/Utils/Extensions/NotifyPropertyChangedExtensions.cs b/DiscordChatExporter.Gui/Utils/Extensions/NotifyPropertyChangedExtensions.cs deleted file mode 100644 index 2744d2ff..00000000 --- a/DiscordChatExporter.Gui/Utils/Extensions/NotifyPropertyChangedExtensions.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.ComponentModel; -using System.Linq.Expressions; -using System.Reflection; -using PowerKit; - -namespace DiscordChatExporter.Gui.Utils.Extensions; - -internal static class NotifyPropertyChangedExtensions -{ - extension(TOwner owner) - where TOwner : INotifyPropertyChanged - { - public IDisposable WatchProperty( - Expression> propertyExpression, - Action callback, - bool watchInitialValue = false - ) - { - var memberExpression = propertyExpression.Body as MemberExpression; - if (memberExpression?.Member is not PropertyInfo property) - throw new ArgumentException("Provided expression must reference a property."); - - var getValue = propertyExpression.Compile(); - - void OnPropertyChanged(object? sender, PropertyChangedEventArgs args) - { - if ( - string.IsNullOrWhiteSpace(args.PropertyName) - || string.Equals(args.PropertyName, property.Name, StringComparison.Ordinal) - ) - { - callback(getValue(owner)); - } - } - - owner.PropertyChanged += OnPropertyChanged; - - if (watchInitialValue) - callback(getValue(owner)); - - return Disposable.Create(() => owner.PropertyChanged -= OnPropertyChanged); - } - - public IDisposable WatchAllProperties(Action callback, bool watchInitialValues = false) - { - void OnPropertyChanged(object? sender, PropertyChangedEventArgs args) => callback(); - owner.PropertyChanged += OnPropertyChanged; - - if (watchInitialValues) - callback(); - - return Disposable.Create(() => owner.PropertyChanged -= OnPropertyChanged); - } - } -} diff --git a/DiscordChatExporter.Gui/Utils/Extensions/ProcessExtensions.cs b/DiscordChatExporter.Gui/Utils/Extensions/ProcessExtensions.cs deleted file mode 100644 index 07317d21..00000000 --- a/DiscordChatExporter.Gui/Utils/Extensions/ProcessExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Diagnostics; - -namespace DiscordChatExporter.Gui.Utils.Extensions; - -internal static class ProcessExtensions -{ - extension(Process) - { - public static void StartShellExecute(string path) - { - using var process = new Process(); - process.StartInfo = new ProcessStartInfo(path) { UseShellExecute = true }; - - process.Start(); - } - } -} diff --git a/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs b/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs index 365ef709..1d1866cd 100644 --- a/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs @@ -14,7 +14,6 @@ using DiscordChatExporter.Gui.Framework; using DiscordChatExporter.Gui.Localization; using DiscordChatExporter.Gui.Models; using DiscordChatExporter.Gui.Services; -using DiscordChatExporter.Gui.Utils.Extensions; using Gress; using Gress.Completable; using PowerKit; diff --git a/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs b/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs index 4fe63ee7..c1c19543 100644 --- a/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs @@ -5,7 +5,6 @@ using DiscordChatExporter.Gui.Framework; using DiscordChatExporter.Gui.Localization; using DiscordChatExporter.Gui.Models; using DiscordChatExporter.Gui.Services; -using DiscordChatExporter.Gui.Utils.Extensions; using PowerKit; using PowerKit.Extensions; diff --git a/DiscordChatExporter.Gui/ViewModels/MainViewModel.cs b/DiscordChatExporter.Gui/ViewModels/MainViewModel.cs index ca428c63..b4715d8e 100644 --- a/DiscordChatExporter.Gui/ViewModels/MainViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/MainViewModel.cs @@ -7,6 +7,7 @@ using DiscordChatExporter.Gui.Localization; using DiscordChatExporter.Gui.Services; using DiscordChatExporter.Gui.Utils.Extensions; using DiscordChatExporter.Gui.ViewModels.Components; +using PowerKit.Extensions; namespace DiscordChatExporter.Gui.ViewModels; diff --git a/DiscordChatExporter.Gui/Views/Components/DashboardView.axaml.cs b/DiscordChatExporter.Gui/Views/Components/DashboardView.axaml.cs index 6155a55d..4bd49841 100644 --- a/DiscordChatExporter.Gui/Views/Components/DashboardView.axaml.cs +++ b/DiscordChatExporter.Gui/Views/Components/DashboardView.axaml.cs @@ -4,8 +4,8 @@ using Avalonia.Input; using Avalonia.Interactivity; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Gui.Framework; -using DiscordChatExporter.Gui.Utils.Extensions; using DiscordChatExporter.Gui.ViewModels.Components; +using PowerKit.Extensions; namespace DiscordChatExporter.Gui.Views.Components; diff --git a/DiscordChatExporter.Gui/Views/Controls/HyperLink.axaml.cs b/DiscordChatExporter.Gui/Views/Controls/HyperLink.axaml.cs index 72ed47a8..d8d94dc6 100644 --- a/DiscordChatExporter.Gui/Views/Controls/HyperLink.axaml.cs +++ b/DiscordChatExporter.Gui/Views/Controls/HyperLink.axaml.cs @@ -3,7 +3,7 @@ using System.Windows.Input; using Avalonia; using Avalonia.Controls; using Avalonia.Input; -using DiscordChatExporter.Gui.Utils.Extensions; +using PowerKit.Extensions; namespace DiscordChatExporter.Gui.Views.Controls;