diff --git a/DiscordChatExporter.Cli/Commands/ExportCommandBase.cs b/DiscordChatExporter.Cli/Commands/ExportCommandBase.cs index ddc5648..33b2941 100644 --- a/DiscordChatExporter.Cli/Commands/ExportCommandBase.cs +++ b/DiscordChatExporter.Cli/Commands/ExportCommandBase.cs @@ -7,7 +7,6 @@ using CliFx.Utilities; using DiscordChatExporter.Core.Models; using DiscordChatExporter.Core.Services; using DiscordChatExporter.Core.Services.Helpers; -using Tyrrrz.Extensions; namespace DiscordChatExporter.Cli.Commands { @@ -22,7 +21,7 @@ namespace DiscordChatExporter.Cli.Commands public ExportFormat ExportFormat { get; set; } = ExportFormat.HtmlDark; [CommandOption("output", 'o', Description = "Output file or directory path.")] - public string OutputPath { get; set; } + public string? OutputPath { get; set; } [CommandOption("after",Description = "Limit to messages sent after this date.")] public DateTimeOffset? After { get; set; } @@ -34,7 +33,7 @@ namespace DiscordChatExporter.Cli.Commands public int? PartitionLimit { get; set; } [CommandOption("dateformat", Description = "Date format used in output.")] - public string DateFormat { get; set; } + public string? DateFormat { get; set; } protected ExportCommandBase(SettingsService settingsService, DataService dataService, ExportService exportService) : base(dataService) @@ -46,8 +45,8 @@ namespace DiscordChatExporter.Cli.Commands protected async Task ExportChannelAsync(IConsole console, Channel channel) { // Configure settings - if (!DateFormat.IsNullOrWhiteSpace()) - SettingsService.DateFormat = DateFormat; + if (!string.IsNullOrWhiteSpace(DateFormat)) + SettingsService.DateFormat = DateFormat!; console.Output.Write($"Exporting channel [{channel.Name}]... "); var progress = console.CreateProgressTicker(); @@ -57,7 +56,7 @@ namespace DiscordChatExporter.Cli.Commands // Generate file path if not set or is a directory var filePath = OutputPath; - if (filePath.IsNullOrWhiteSpace() || ExportHelper.IsDirectoryPath(filePath)) + if (string.IsNullOrWhiteSpace(filePath) || ExportHelper.IsDirectoryPath(filePath)) { // Generate default file name var fileName = ExportHelper.GetDefaultExportFileName(ExportFormat, chatLog.Guild, diff --git a/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj b/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj index d4a63d9..b7f7b22 100644 --- a/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj +++ b/DiscordChatExporter.Cli/DiscordChatExporter.Cli.csproj @@ -6,13 +6,14 @@ 2.15 Tyrrrz Copyright (c) Alexey Golub + enable ..\favicon.ico - + diff --git a/DiscordChatExporter.Core.Markdown/DiscordChatExporter.Core.Markdown.csproj b/DiscordChatExporter.Core.Markdown/DiscordChatExporter.Core.Markdown.csproj index 5f4a3f5..70a6a23 100644 --- a/DiscordChatExporter.Core.Markdown/DiscordChatExporter.Core.Markdown.csproj +++ b/DiscordChatExporter.Core.Markdown/DiscordChatExporter.Core.Markdown.csproj @@ -2,10 +2,11 @@ netcoreapp3.0 + enable - + \ No newline at end of file diff --git a/DiscordChatExporter.Core.Markdown/Internal/AggregateMatcher.cs b/DiscordChatExporter.Core.Markdown/Internal/AggregateMatcher.cs index ee54096..fe02d9f 100644 --- a/DiscordChatExporter.Core.Markdown/Internal/AggregateMatcher.cs +++ b/DiscordChatExporter.Core.Markdown/Internal/AggregateMatcher.cs @@ -16,9 +16,9 @@ namespace DiscordChatExporter.Core.Markdown.Internal { } - public ParsedMatch Match(StringPart stringPart) + public ParsedMatch? Match(StringPart stringPart) { - ParsedMatch earliestMatch = null; + ParsedMatch? earliestMatch = null; // Try to match the input with each matcher and get the match with the lowest start index foreach (var matcher in _matchers) diff --git a/DiscordChatExporter.Core.Markdown/Internal/IMatcher.cs b/DiscordChatExporter.Core.Markdown/Internal/IMatcher.cs index b6c8905..e61e6af 100644 --- a/DiscordChatExporter.Core.Markdown/Internal/IMatcher.cs +++ b/DiscordChatExporter.Core.Markdown/Internal/IMatcher.cs @@ -2,6 +2,6 @@ { internal interface IMatcher { - ParsedMatch Match(StringPart stringPart); + ParsedMatch? Match(StringPart stringPart); } } \ No newline at end of file diff --git a/DiscordChatExporter.Core.Markdown/Internal/RegexMatcher.cs b/DiscordChatExporter.Core.Markdown/Internal/RegexMatcher.cs index ee7cf32..a35d92f 100644 --- a/DiscordChatExporter.Core.Markdown/Internal/RegexMatcher.cs +++ b/DiscordChatExporter.Core.Markdown/Internal/RegexMatcher.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; using System.Text.RegularExpressions; namespace DiscordChatExporter.Core.Markdown.Internal @@ -23,7 +19,7 @@ namespace DiscordChatExporter.Core.Markdown.Internal { } - public ParsedMatch Match(StringPart stringPart) + public ParsedMatch? Match(StringPart stringPart) { var match = _regex.Match(stringPart.Target, stringPart.StartIndex, stringPart.Length); if (!match.Success) diff --git a/DiscordChatExporter.Core.Markdown/Internal/StringMatcher.cs b/DiscordChatExporter.Core.Markdown/Internal/StringMatcher.cs index fbf4290..2722ce5 100644 --- a/DiscordChatExporter.Core.Markdown/Internal/StringMatcher.cs +++ b/DiscordChatExporter.Core.Markdown/Internal/StringMatcher.cs @@ -20,7 +20,7 @@ namespace DiscordChatExporter.Core.Markdown.Internal { } - public ParsedMatch Match(StringPart stringPart) + public ParsedMatch? Match(StringPart stringPart) { var index = stringPart.Target.IndexOf(_needle, stringPart.StartIndex, stringPart.Length, _comparison); diff --git a/DiscordChatExporter.Core.Markdown/MarkdownParser.cs b/DiscordChatExporter.Core.Markdown/MarkdownParser.cs index 31de461..45a0dfc 100644 --- a/DiscordChatExporter.Core.Markdown/MarkdownParser.cs +++ b/DiscordChatExporter.Core.Markdown/MarkdownParser.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Text.RegularExpressions; using DiscordChatExporter.Core.Markdown.Internal; using DiscordChatExporter.Core.Markdown.Nodes; -using Tyrrrz.Extensions; namespace DiscordChatExporter.Core.Markdown { @@ -125,7 +124,7 @@ namespace DiscordChatExporter.Core.Markdown // Capture <:lul:123456> or private static readonly IMatcher CustomEmojiNodeMatcher = new RegexMatcher( new Regex("<(a)?:(.+?):(\\d+?)>", DefaultRegexOptions), - m => new EmojiNode(m.Groups[3].Value, m.Groups[2].Value, !m.Groups[1].Value.IsNullOrWhiteSpace())); + m => new EmojiNode(m.Groups[3].Value, m.Groups[2].Value, !string.IsNullOrWhiteSpace(m.Groups[1].Value))); /* Links */ diff --git a/DiscordChatExporter.Core.Markdown/Nodes/EmojiNode.cs b/DiscordChatExporter.Core.Markdown/Nodes/EmojiNode.cs index 798411b..0aa2bd1 100644 --- a/DiscordChatExporter.Core.Markdown/Nodes/EmojiNode.cs +++ b/DiscordChatExporter.Core.Markdown/Nodes/EmojiNode.cs @@ -1,18 +1,16 @@ -using Tyrrrz.Extensions; - -namespace DiscordChatExporter.Core.Markdown.Nodes +namespace DiscordChatExporter.Core.Markdown.Nodes { public class EmojiNode : Node { - public string Id { get; } + public string? Id { get; } public string Name { get; } public bool IsAnimated { get; } - public bool IsCustomEmoji => !Id.IsNullOrWhiteSpace(); + public bool IsCustomEmoji => !string.IsNullOrWhiteSpace(Id); - public EmojiNode(string id, string name, bool isAnimated) + public EmojiNode(string? id, string name, bool isAnimated) { Id = id; Name = name; diff --git a/DiscordChatExporter.Core.Models/Channel.cs b/DiscordChatExporter.Core.Models/Channel.cs index 513970d..34b8d91 100644 --- a/DiscordChatExporter.Core.Models/Channel.cs +++ b/DiscordChatExporter.Core.Models/Channel.cs @@ -6,17 +6,17 @@ { public string Id { get; } - public string ParentId { get; } + public string? ParentId { get; } - public string GuildId { get; } + public string? GuildId { get; } public string Name { get; } - public string Topic { get; } + public string? Topic { get; } public ChannelType Type { get; } - public Channel(string id, string parentId, string guildId, string name, string topic, ChannelType type) + public Channel(string id, string? parentId, string? guildId, string name, string? topic, ChannelType type) { Id = id; ParentId = parentId; diff --git a/DiscordChatExporter.Core.Models/DiscordChatExporter.Core.Models.csproj b/DiscordChatExporter.Core.Models/DiscordChatExporter.Core.Models.csproj index 5f4a3f5..70a6a23 100644 --- a/DiscordChatExporter.Core.Models/DiscordChatExporter.Core.Models.csproj +++ b/DiscordChatExporter.Core.Models/DiscordChatExporter.Core.Models.csproj @@ -2,10 +2,11 @@ netcoreapp3.0 + enable - + \ No newline at end of file diff --git a/DiscordChatExporter.Core.Models/Embed.cs b/DiscordChatExporter.Core.Models/Embed.cs index 5246ccd..8cf4434 100644 --- a/DiscordChatExporter.Core.Models/Embed.cs +++ b/DiscordChatExporter.Core.Models/Embed.cs @@ -8,28 +8,29 @@ namespace DiscordChatExporter.Core.Models public class Embed { - public string Title { get; } + public string? Title { get; } - public string Url { get; } + public string? Url { get; } public DateTimeOffset? Timestamp { get; } + // TODO: this should be nullable and default color should be set in CSS public Color Color { get; } - public EmbedAuthor Author { get; } + public EmbedAuthor? Author { get; } - public string Description { get; } + public string? Description { get; } public IReadOnlyList Fields { get; } - public EmbedImage Thumbnail { get; } + public EmbedImage? Thumbnail { get; } - public EmbedImage Image { get; } + public EmbedImage? Image { get; } - public EmbedFooter Footer { get; } + public EmbedFooter? Footer { get; } - public Embed(string title, string url, DateTimeOffset? timestamp, Color color, EmbedAuthor author, string description, - IReadOnlyList fields, EmbedImage thumbnail, EmbedImage image, EmbedFooter footer) + public Embed(string? title, string? url, DateTimeOffset? timestamp, Color color, EmbedAuthor? author, string? description, + IReadOnlyList fields, EmbedImage? thumbnail, EmbedImage? image, EmbedFooter? footer) { Title = title; Url = url; @@ -43,6 +44,6 @@ namespace DiscordChatExporter.Core.Models Footer = footer; } - public override string ToString() => Title; + public override string ToString() => Title ?? ""; } } \ No newline at end of file diff --git a/DiscordChatExporter.Core.Models/EmbedAuthor.cs b/DiscordChatExporter.Core.Models/EmbedAuthor.cs index b4496a3..659eb28 100644 --- a/DiscordChatExporter.Core.Models/EmbedAuthor.cs +++ b/DiscordChatExporter.Core.Models/EmbedAuthor.cs @@ -4,19 +4,19 @@ namespace DiscordChatExporter.Core.Models public class EmbedAuthor { - public string Name { get; } + public string? Name { get; } - public string Url { get; } + public string? Url { get; } - public string IconUrl { get; } + public string? IconUrl { get; } - public EmbedAuthor(string name, string url, string iconUrl) + public EmbedAuthor(string? name, string? url, string? iconUrl) { Name = name; Url = url; IconUrl = iconUrl; } - public override string ToString() => Name; + public override string ToString() => Name ?? ""; } } \ No newline at end of file diff --git a/DiscordChatExporter.Core.Models/EmbedFooter.cs b/DiscordChatExporter.Core.Models/EmbedFooter.cs index 38229d1..4c10025 100644 --- a/DiscordChatExporter.Core.Models/EmbedFooter.cs +++ b/DiscordChatExporter.Core.Models/EmbedFooter.cs @@ -6,9 +6,9 @@ namespace DiscordChatExporter.Core.Models { public string Text { get; } - public string IconUrl { get; } + public string? IconUrl { get; } - public EmbedFooter(string text, string iconUrl) + public EmbedFooter(string text, string? iconUrl) { Text = text; IconUrl = iconUrl; diff --git a/DiscordChatExporter.Core.Models/EmbedImage.cs b/DiscordChatExporter.Core.Models/EmbedImage.cs index 3382788..c438f90 100644 --- a/DiscordChatExporter.Core.Models/EmbedImage.cs +++ b/DiscordChatExporter.Core.Models/EmbedImage.cs @@ -4,13 +4,13 @@ namespace DiscordChatExporter.Core.Models public class EmbedImage { - public string Url { get; } + public string? Url { get; } public int? Width { get; } public int? Height { get; } - public EmbedImage(string url, int? width, int? height) + public EmbedImage(string? url, int? width, int? height) { Url = url; Height = height; diff --git a/DiscordChatExporter.Core.Models/Emoji.cs b/DiscordChatExporter.Core.Models/Emoji.cs index 9de011f..19af4e4 100644 --- a/DiscordChatExporter.Core.Models/Emoji.cs +++ b/DiscordChatExporter.Core.Models/Emoji.cs @@ -8,7 +8,7 @@ namespace DiscordChatExporter.Core.Models public partial class Emoji { - public string Id { get; } + public string? Id { get; } public string Name { get; } @@ -16,7 +16,7 @@ namespace DiscordChatExporter.Core.Models public string ImageUrl { get; } - public Emoji(string id, string name, bool isAnimated) + public Emoji(string? id, string name, bool isAnimated) { Id = id; Name = name; @@ -37,10 +37,10 @@ namespace DiscordChatExporter.Core.Models private static string GetTwemojiName(string emoji) => GetCodePoints(emoji).Select(i => i.ToString("x")).JoinToString("-"); - public static string GetImageUrl(string id, string name, bool isAnimated) + public static string GetImageUrl(string? id, string name, bool isAnimated) { // Custom emoji - if (!id.IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(id)) { // Animated if (isAnimated) diff --git a/DiscordChatExporter.Core.Models/Guild.cs b/DiscordChatExporter.Core.Models/Guild.cs index 2fb93c1..97c7b9f 100644 --- a/DiscordChatExporter.Core.Models/Guild.cs +++ b/DiscordChatExporter.Core.Models/Guild.cs @@ -1,8 +1,6 @@ -using Tyrrrz.Extensions; - -namespace DiscordChatExporter.Core.Models +namespace DiscordChatExporter.Core.Models { - // https://discordapp.com/developers/docs/resources/guild#guild-object + // https://discordapp.string.IsNullOrWhiteSpace(com/developers/docs/resources/guild#guild-object public partial class Guild { @@ -10,11 +8,11 @@ namespace DiscordChatExporter.Core.Models public string Name { get; } - public string IconHash { get; } + public string? IconHash { get; } public string IconUrl { get; } - public Guild(string id, string name, string iconHash) + public Guild(string id, string name, string? iconHash) { Id = id; Name = name; @@ -28,9 +26,9 @@ namespace DiscordChatExporter.Core.Models public partial class Guild { - public static string GetIconUrl(string id, string iconHash) + public static string GetIconUrl(string id, string? iconHash) { - return !iconHash.IsNullOrWhiteSpace() + return !string.IsNullOrWhiteSpace(iconHash) ? $"https://cdn.discordapp.com/icons/{id}/{iconHash}.png" : "https://cdn.discordapp.com/embed/avatars/0.png"; } diff --git a/DiscordChatExporter.Core.Models/Message.cs b/DiscordChatExporter.Core.Models/Message.cs index cb64974..7fe2135 100644 --- a/DiscordChatExporter.Core.Models/Message.cs +++ b/DiscordChatExporter.Core.Models/Message.cs @@ -19,7 +19,7 @@ namespace DiscordChatExporter.Core.Models public DateTimeOffset? EditedTimestamp { get; } - public string Content { get; } + public string? Content { get; } public IReadOnlyList Attachments { get; } @@ -32,7 +32,7 @@ namespace DiscordChatExporter.Core.Models public bool IsPinned { get; } public Message(string id, string channelId, MessageType type, User author, DateTimeOffset timestamp, - DateTimeOffset? editedTimestamp, string content, IReadOnlyList attachments, + DateTimeOffset? editedTimestamp, string? content, IReadOnlyList attachments, IReadOnlyList embeds, IReadOnlyList reactions, IReadOnlyList mentionedUsers, bool isPinned) { @@ -50,6 +50,6 @@ namespace DiscordChatExporter.Core.Models IsPinned = isPinned; } - public override string ToString() => Content; + public override string ToString() => Content ?? ""; } } \ No newline at end of file diff --git a/DiscordChatExporter.Core.Models/User.cs b/DiscordChatExporter.Core.Models/User.cs index bd529c6..ae33ba2 100644 --- a/DiscordChatExporter.Core.Models/User.cs +++ b/DiscordChatExporter.Core.Models/User.cs @@ -1,5 +1,4 @@ using System; -using Tyrrrz.Extensions; namespace DiscordChatExporter.Core.Models { @@ -15,13 +14,13 @@ namespace DiscordChatExporter.Core.Models public string FullName { get; } - public string AvatarHash { get; } + public string? AvatarHash { get; } public string AvatarUrl { get; } public bool IsBot { get; } - public User(string id, int discriminator, string name, string avatarHash, bool isBot) + public User(string id, int discriminator, string name, string? avatarHash, bool isBot) { Id = id; Discriminator = discriminator; @@ -40,10 +39,10 @@ namespace DiscordChatExporter.Core.Models { public static string GetFullName(string name, int discriminator) => $"{name}#{discriminator:0000}"; - public static string GetAvatarUrl(string id, int discriminator, string avatarHash) + public static string GetAvatarUrl(string id, int discriminator, string? avatarHash) { // Custom avatar - if (!avatarHash.IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(avatarHash)) { // Animated if (avatarHash.StartsWith("a_", StringComparison.Ordinal)) diff --git a/DiscordChatExporter.Core.Rendering/CsvChatLogRenderer.cs b/DiscordChatExporter.Core.Rendering/CsvChatLogRenderer.cs index b79f7f7..37d317a 100644 --- a/DiscordChatExporter.Core.Rendering/CsvChatLogRenderer.cs +++ b/DiscordChatExporter.Core.Rendering/CsvChatLogRenderer.cs @@ -96,7 +96,7 @@ namespace DiscordChatExporter.Core.Rendering await RenderFieldAsync(writer, FormatDate(message.Timestamp)); // Content - await RenderFieldAsync(writer, FormatMarkdown(message.Content)); + await RenderFieldAsync(writer, FormatMarkdown(message.Content ?? "")); // Attachments var formattedAttachments = message.Attachments.Select(a => a.Url).JoinToString(","); diff --git a/DiscordChatExporter.Core.Rendering/DiscordChatExporter.Core.Rendering.csproj b/DiscordChatExporter.Core.Rendering/DiscordChatExporter.Core.Rendering.csproj index a0074ce..3c74320 100644 --- a/DiscordChatExporter.Core.Rendering/DiscordChatExporter.Core.Rendering.csproj +++ b/DiscordChatExporter.Core.Rendering/DiscordChatExporter.Core.Rendering.csproj @@ -2,6 +2,7 @@ netcoreapp3.0 + enable diff --git a/DiscordChatExporter.Core.Rendering/HtmlChatLogRenderer.cs b/DiscordChatExporter.Core.Rendering/HtmlChatLogRenderer.cs index fa994b9..9dc60dd 100644 --- a/DiscordChatExporter.Core.Rendering/HtmlChatLogRenderer.cs +++ b/DiscordChatExporter.Core.Rendering/HtmlChatLogRenderer.cs @@ -97,7 +97,7 @@ namespace DiscordChatExporter.Core.Rendering if (node is MultiLineCodeBlockNode multilineCodeBlockNode) { // Set CSS class for syntax highlighting - var highlightCssClass = !multilineCodeBlockNode.Language.IsNullOrWhiteSpace() + var highlightCssClass = !string.IsNullOrWhiteSpace(multilineCodeBlockNode.Language) ? $"language-{multilineCodeBlockNode.Language}" : "nohighlight"; @@ -153,7 +153,7 @@ namespace DiscordChatExporter.Core.Rendering // Extract message ID if the link points to a Discord message var linkedMessageId = Regex.Match(linkNode.Url, "^https?://discordapp.com/channels/.*?/(\\d+)/?$").Groups[1].Value; - return linkedMessageId.IsNullOrWhiteSpace() + return string.IsNullOrWhiteSpace(linkedMessageId) ? $"{HtmlEncode(linkNode.Title)}" : $"{HtmlEncode(linkNode.Title)}"; } @@ -165,7 +165,7 @@ namespace DiscordChatExporter.Core.Rendering private string FormatMarkdown(IReadOnlyList nodes, bool isTopLevel) { // Emojis are jumbo if all top-level nodes are emoji nodes or whitespace text nodes - var isJumbo = isTopLevel && nodes.All(n => n is EmojiNode || n is TextNode textNode && textNode.Text.IsNullOrWhiteSpace()); + var isJumbo = isTopLevel && nodes.All(n => n is EmojiNode || n is TextNode textNode && string.IsNullOrWhiteSpace(textNode.Text)); return nodes.Select(n => FormatMarkdown(n, isJumbo)).JoinToString(""); } diff --git a/DiscordChatExporter.Core.Rendering/PlainTextChatLogRenderer.cs b/DiscordChatExporter.Core.Rendering/PlainTextChatLogRenderer.cs index bf9f503..ee1b357 100644 --- a/DiscordChatExporter.Core.Rendering/PlainTextChatLogRenderer.cs +++ b/DiscordChatExporter.Core.Rendering/PlainTextChatLogRenderer.cs @@ -40,7 +40,7 @@ namespace DiscordChatExporter.Core.Rendering return $"before {FormatDate(before.Value)}"; // Neither - return null; + return ""; } private string FormatMarkdown(Node node) @@ -131,43 +131,43 @@ namespace DiscordChatExporter.Core.Rendering await writer.WriteLineAsync("{Embed}"); // Author name - if (!(embed.Author?.Name).IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(embed.Author?.Name)) await writer.WriteLineAsync(embed.Author?.Name); // URL - if (!embed.Url.IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(embed.Url)) await writer.WriteLineAsync(embed.Url); // Title - if (!embed.Title.IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(embed.Title)) await writer.WriteLineAsync(FormatMarkdown(embed.Title)); // Description - if (!embed.Description.IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(embed.Description)) await writer.WriteLineAsync(FormatMarkdown(embed.Description)); // Fields foreach (var field in embed.Fields) { // Name - if (!field.Name.IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(field.Name)) await writer.WriteLineAsync(field.Name); // Value - if (!field.Value.IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(field.Value)) await writer.WriteLineAsync(field.Value); } // Thumbnail URL - if (!(embed.Thumbnail?.Url).IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(embed.Thumbnail?.Url)) await writer.WriteLineAsync(embed.Thumbnail?.Url); // Image URL - if (!(embed.Image?.Url).IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(embed.Image?.Url)) await writer.WriteLineAsync(embed.Image?.Url); // Footer text - if (!(embed.Footer?.Text).IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(embed.Footer?.Text)) await writer.WriteLineAsync(embed.Footer?.Text); await writer.WriteLineAsync(); @@ -201,7 +201,8 @@ namespace DiscordChatExporter.Core.Rendering await RenderMessageHeaderAsync(writer, message); // Content - await writer.WriteLineAsync(FormatMarkdown(message.Content)); + if (!string.IsNullOrWhiteSpace(message.Content)) + await writer.WriteLineAsync(FormatMarkdown(message.Content)); // Separator await writer.WriteLineAsync(); diff --git a/DiscordChatExporter.Core.Services/DataService.Parsers.cs b/DiscordChatExporter.Core.Services/DataService.Parsers.cs index f7f77e6..c4ed7ef 100644 --- a/DiscordChatExporter.Core.Services/DataService.Parsers.cs +++ b/DiscordChatExporter.Core.Services/DataService.Parsers.cs @@ -12,10 +12,10 @@ namespace DiscordChatExporter.Core.Services { private User ParseUser(JToken json) { - var id = json["id"].Value(); - var discriminator = json["discriminator"].Value(); - var name = json["username"].Value(); - var avatarHash = json["avatar"].Value(); + var id = json["id"]!.Value(); + var discriminator = json["discriminator"]!.Value(); + var name = json["username"]!.Value(); + var avatarHash = json["avatar"]!.Value(); var isBot = json["bot"]?.Value() ?? false; return new User(id, discriminator, name, avatarHash, isBot); @@ -23,9 +23,9 @@ namespace DiscordChatExporter.Core.Services private Guild ParseGuild(JToken json) { - var id = json["id"].Value(); - var name = json["name"].Value(); - var iconHash = json["icon"].Value(); + var id = json["id"]!.Value(); + var name = json["name"]!.Value(); + var iconHash = json["icon"]!.Value(); return new Guild(id, name, iconHash); } @@ -33,23 +33,23 @@ namespace DiscordChatExporter.Core.Services private Channel ParseChannel(JToken json) { // Get basic data - var id = json["id"].Value(); + var id = json["id"]!.Value(); var parentId = json["parent_id"]?.Value(); - var type = (ChannelType) json["type"].Value(); + var type = (ChannelType) json["type"]!.Value(); var topic = json["topic"]?.Value(); // Try to extract guild ID var guildId = json["guild_id"]?.Value(); // If the guild ID is blank, it's direct messages - if (guildId.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(guildId)) guildId = Guild.DirectMessages.Id; // Try to extract name var name = json["name"]?.Value(); // If the name is blank, it's direct messages - if (name.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(name)) name = json["recipients"].Select(ParseUser).Select(u => u.Name).JoinToString(", "); return new Channel(id, parentId, guildId, name, topic, type); @@ -57,20 +57,20 @@ namespace DiscordChatExporter.Core.Services private Role ParseRole(JToken json) { - var id = json["id"].Value(); - var name = json["name"].Value(); + var id = json["id"]!.Value(); + var name = json["name"]!.Value(); return new Role(id, name); } private Attachment ParseAttachment(JToken json) { - var id = json["id"].Value(); - var url = json["url"].Value(); + var id = json["id"]!.Value(); + var url = json["url"]!.Value(); var width = json["width"]?.Value(); var height = json["height"]?.Value(); - var fileName = json["filename"].Value(); - var fileSizeBytes = json["size"].Value(); + var fileName = json["filename"]!.Value(); + var fileSizeBytes = json["size"]!.Value(); var fileSize = new FileSize(fileSizeBytes); @@ -88,8 +88,8 @@ namespace DiscordChatExporter.Core.Services private EmbedField ParseEmbedField(JToken json) { - var name = json["name"].Value(); - var value = json["value"].Value(); + var name = json["name"]!.Value(); + var value = json["value"]!.Value(); var isInline = json["inline"]?.Value() ?? false; return new EmbedField(name, value, isInline); @@ -106,7 +106,7 @@ namespace DiscordChatExporter.Core.Services private EmbedFooter ParseEmbedFooter(JToken json) { - var text = json["text"].Value(); + var text = json["text"]!.Value(); var iconUrl = json["icon_url"]?.Value(); return new EmbedFooter(text, iconUrl); @@ -122,23 +122,23 @@ namespace DiscordChatExporter.Core.Services // Get color var color = json["color"] != null - ? Color.FromArgb(json["color"].Value()).ResetAlpha() + ? Color.FromArgb(json["color"]!.Value()).ResetAlpha() : Color.FromArgb(79, 84, 92); // default color // Get author - var author = json["author"] != null ? ParseEmbedAuthor(json["author"]) : null; + var author = json["author"] != null ? ParseEmbedAuthor(json["author"]!) : null; // Get fields - var fields = json["fields"].EmptyIfNull().Select(ParseEmbedField).ToArray(); + var fields = (json["fields"] ?? Enumerable.Empty()).Select(ParseEmbedField).ToArray(); // Get thumbnail - var thumbnail = json["thumbnail"] != null ? ParseEmbedImage(json["thumbnail"]) : null; + var thumbnail = json["thumbnail"] != null ? ParseEmbedImage(json["thumbnail"]!) : null; // Get image - var image = json["image"] != null ? ParseEmbedImage(json["image"]) : null; + var image = json["image"] != null ? ParseEmbedImage(json["image"]!) : null; // Get footer - var footer = json["footer"] != null ? ParseEmbedFooter(json["footer"]) : null; + var footer = json["footer"] != null ? ParseEmbedFooter(json["footer"]!) : null; return new Embed(title, url, timestamp, color, author, description, fields, thumbnail, image, footer); } @@ -146,7 +146,7 @@ namespace DiscordChatExporter.Core.Services private Emoji ParseEmoji(JToken json) { var id = json["id"]?.Value(); - var name = json["name"]?.Value(); + var name = json["name"]!.Value(); var isAnimated = json["animated"]?.Value() ?? false; return new Emoji(id, name, isAnimated); @@ -154,8 +154,8 @@ namespace DiscordChatExporter.Core.Services private Reaction ParseReaction(JToken json) { - var count = json["count"].Value(); - var emoji = ParseEmoji(json["emoji"]); + var count = json["count"]!.Value(); + var emoji = ParseEmoji(json["emoji"]!); return new Reaction(count, emoji); } @@ -163,12 +163,12 @@ namespace DiscordChatExporter.Core.Services private Message ParseMessage(JToken json) { // Get basic data - var id = json["id"].Value(); - var channelId = json["channel_id"].Value(); - var timestamp = json["timestamp"].Value().ToDateTimeOffset(); + var id = json["id"]!.Value(); + var channelId = json["channel_id"]!.Value(); + var timestamp = json["timestamp"]!.Value().ToDateTimeOffset(); var editedTimestamp = json["edited_timestamp"]?.Value()?.ToDateTimeOffset(); - var content = json["content"].Value(); - var type = (MessageType) json["type"].Value(); + var content = json["content"]!.Value(); + var type = (MessageType) json["type"]!.Value(); // Workarounds for non-default types if (type == MessageType.RecipientAdd) @@ -187,22 +187,22 @@ namespace DiscordChatExporter.Core.Services content = "Joined the server."; // Get author - var author = ParseUser(json["author"]); + var author = ParseUser(json["author"]!); // Get attachments - var attachments = json["attachments"].EmptyIfNull().Select(ParseAttachment).ToArray(); + var attachments = (json["attachments"] ?? Enumerable.Empty()).Select(ParseAttachment).ToArray(); // Get embeds - var embeds = json["embeds"].EmptyIfNull().Select(ParseEmbed).ToArray(); + var embeds = (json["embeds"] ?? Enumerable.Empty()).Select(ParseEmbed).ToArray(); // Get reactions - var reactions = json["reactions"].EmptyIfNull().Select(ParseReaction).ToArray(); + var reactions = (json["reactions"] ?? Enumerable.Empty()).Select(ParseReaction).ToArray(); // Get mentioned users - var mentionedUsers = json["mentions"].EmptyIfNull().Select(ParseUser).ToArray(); + var mentionedUsers = (json["mentions"] ?? Enumerable.Empty()).Select(ParseUser).ToArray(); // Get whether this message is pinned - var isPinned = json["pinned"].Value(); + var isPinned = json["pinned"]!.Value(); return new Message(id, channelId, type, author, timestamp, editedTimestamp, content, attachments, embeds, reactions, mentionedUsers, isPinned); diff --git a/DiscordChatExporter.Core.Services/DataService.cs b/DiscordChatExporter.Core.Services/DataService.cs index 3615e38..774d1b9 100644 --- a/DiscordChatExporter.Core.Services/DataService.cs +++ b/DiscordChatExporter.Core.Services/DataService.cs @@ -45,7 +45,7 @@ namespace DiscordChatExporter.Core.Services var value = parameter.SubstringAfter("="); // Skip empty values - if (value.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(value)) continue; request.RequestUri = request.RequestUri.SetQueryParameter(key, value); @@ -53,6 +53,7 @@ namespace DiscordChatExporter.Core.Services // Get response using var response = await _httpClient.SendAsync(request); + // Check status code // We throw our own exception here because default one doesn't have status code if (!response.IsSuccessStatusCode) @@ -119,7 +120,7 @@ namespace DiscordChatExporter.Core.Services } public async Task> GetChannelMessagesAsync(AuthToken token, string channelId, - DateTimeOffset? after = null, DateTimeOffset? before = null, IProgress progress = null) + DateTimeOffset? after = null, DateTimeOffset? before = null, IProgress? progress = null) { var result = new List(); @@ -211,7 +212,7 @@ namespace DiscordChatExporter.Core.Services } public async Task GetChatLogAsync(AuthToken token, Guild guild, Channel channel, - DateTimeOffset? after = null, DateTimeOffset? before = null, IProgress progress = null) + DateTimeOffset? after = null, DateTimeOffset? before = null, IProgress? progress = null) { // Get messages var messages = await GetChannelMessagesAsync(token, channel.Id, after, before, progress); @@ -223,19 +224,19 @@ namespace DiscordChatExporter.Core.Services } public async Task GetChatLogAsync(AuthToken token, Channel channel, - DateTimeOffset? after = null, DateTimeOffset? before = null, IProgress progress = null) + DateTimeOffset? after = null, DateTimeOffset? before = null, IProgress? progress = null) { // Get guild - var guild = channel.GuildId == Guild.DirectMessages.Id - ? Guild.DirectMessages - : await GetGuildAsync(token, channel.GuildId); + var guild = !string.IsNullOrWhiteSpace(channel.GuildId) + ? await GetGuildAsync(token, channel.GuildId) + : Guild.DirectMessages; // Get the chat log return await GetChatLogAsync(token, guild, channel, after, before, progress); } public async Task GetChatLogAsync(AuthToken token, string channelId, - DateTimeOffset? after = null, DateTimeOffset? before = null, IProgress progress = null) + DateTimeOffset? after = null, DateTimeOffset? before = null, IProgress? progress = null) { // Get channel var channel = await GetChannelAsync(token, channelId); diff --git a/DiscordChatExporter.Core.Services/DiscordChatExporter.Core.Services.csproj b/DiscordChatExporter.Core.Services/DiscordChatExporter.Core.Services.csproj index a4e171f..4b7ef06 100644 --- a/DiscordChatExporter.Core.Services/DiscordChatExporter.Core.Services.csproj +++ b/DiscordChatExporter.Core.Services/DiscordChatExporter.Core.Services.csproj @@ -2,13 +2,14 @@ netcoreapp3.0 + enable - - - + + + diff --git a/DiscordChatExporter.Core.Services/ExportService.cs b/DiscordChatExporter.Core.Services/ExportService.cs index 395aa77..58cf7bb 100644 --- a/DiscordChatExporter.Core.Services/ExportService.cs +++ b/DiscordChatExporter.Core.Services/ExportService.cs @@ -38,7 +38,7 @@ namespace DiscordChatExporter.Core.Services { // Create output directory var dirPath = Path.GetDirectoryName(filePath); - if (!dirPath.IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(dirPath)) Directory.CreateDirectory(dirPath); // Render chat log to output file @@ -74,7 +74,7 @@ namespace DiscordChatExporter.Core.Services var partitionFilePath = $"{fileNameWithoutExt} [{partitionNumber} of {partitions.Length}]{fileExt}"; // Compose full file path - if (!dirPath.IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(dirPath)) partitionFilePath = Path.Combine(dirPath, partitionFilePath); // Export diff --git a/DiscordChatExporter.Core.Services/Helpers/ExportHelper.cs b/DiscordChatExporter.Core.Services/Helpers/ExportHelper.cs index cd82782..84aca81 100644 --- a/DiscordChatExporter.Core.Services/Helpers/ExportHelper.cs +++ b/DiscordChatExporter.Core.Services/Helpers/ExportHelper.cs @@ -3,7 +3,6 @@ using System.IO; using System.Linq; using System.Text; using DiscordChatExporter.Core.Models; -using Tyrrrz.Extensions; namespace DiscordChatExporter.Core.Services.Helpers { @@ -12,7 +11,7 @@ namespace DiscordChatExporter.Core.Services.Helpers public static bool IsDirectoryPath(string path) => path.Last() == Path.DirectorySeparatorChar || path.Last() == Path.AltDirectorySeparatorChar || - Path.GetExtension(path).IsNullOrWhiteSpace() && !File.Exists(path); + string.IsNullOrWhiteSpace(Path.GetExtension(path)) && !File.Exists(path); public static string GetDefaultExportFileName(ExportFormat format, Guild guild, Channel channel, DateTimeOffset? after = null, DateTimeOffset? before = null) diff --git a/DiscordChatExporter.Core.Services/SettingsService.cs b/DiscordChatExporter.Core.Services/SettingsService.cs index b36445b..e8bd53c 100644 --- a/DiscordChatExporter.Core.Services/SettingsService.cs +++ b/DiscordChatExporter.Core.Services/SettingsService.cs @@ -9,8 +9,10 @@ namespace DiscordChatExporter.Core.Services public string DateFormat { get; set; } = "dd-MMM-yy hh:mm tt"; - public AuthToken LastToken { get; set; } + public AuthToken? LastToken { get; set; } + public ExportFormat LastExportFormat { get; set; } = ExportFormat.HtmlDark; + public int? LastPartitionLimit { get; set; } public SettingsService() diff --git a/DiscordChatExporter.Gui/App.xaml.cs b/DiscordChatExporter.Gui/App.xaml.cs index 57df507..a0ba95a 100644 --- a/DiscordChatExporter.Gui/App.xaml.cs +++ b/DiscordChatExporter.Gui/App.xaml.cs @@ -1,6 +1,16 @@ -namespace DiscordChatExporter.Gui +using System; +using System.Reflection; + +namespace DiscordChatExporter.Gui { public partial class App { + private static readonly Assembly Assembly = typeof(App).Assembly; + + public static string Name => Assembly.GetName().Name!; + + public static Version Version => Assembly.GetName().Version!; + + public static string VersionString => Version.ToString(3); } } \ No newline at end of file diff --git a/DiscordChatExporter.Gui/Bootstrapper.cs b/DiscordChatExporter.Gui/Bootstrapper.cs index b442a28..e40e971 100644 --- a/DiscordChatExporter.Gui/Bootstrapper.cs +++ b/DiscordChatExporter.Gui/Bootstrapper.cs @@ -1,11 +1,14 @@ -using System.Windows; -using System.Windows.Threading; -using DiscordChatExporter.Core.Services; +using DiscordChatExporter.Core.Services; using DiscordChatExporter.Gui.ViewModels; using DiscordChatExporter.Gui.ViewModels.Framework; using Stylet; using StyletIoC; +#if !DEBUG +using System.Windows; +using System.Windows.Threading; +#endif + namespace DiscordChatExporter.Gui { public class Bootstrapper : Bootstrapper diff --git a/DiscordChatExporter.Gui/Converters/DateTimeOffsetToDateTimeConverter.cs b/DiscordChatExporter.Gui/Converters/DateTimeOffsetToDateTimeConverter.cs index 6ac4c84..617f487 100644 --- a/DiscordChatExporter.Gui/Converters/DateTimeOffsetToDateTimeConverter.cs +++ b/DiscordChatExporter.Gui/Converters/DateTimeOffsetToDateTimeConverter.cs @@ -14,7 +14,7 @@ namespace DiscordChatExporter.Gui.Converters if (value is DateTimeOffset dateTimeOffsetValue) return dateTimeOffsetValue.DateTime; - return default; + return default(DateTime); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) @@ -22,7 +22,7 @@ namespace DiscordChatExporter.Gui.Converters if (value is DateTime dateTimeValue) return new DateTimeOffset(dateTimeValue); - return default; + return default(DateTimeOffset); } } } \ No newline at end of file diff --git a/DiscordChatExporter.Gui/Converters/ExportFormatToStringConverter.cs b/DiscordChatExporter.Gui/Converters/ExportFormatToStringConverter.cs index 33410e3..5e1332a 100644 --- a/DiscordChatExporter.Gui/Converters/ExportFormatToStringConverter.cs +++ b/DiscordChatExporter.Gui/Converters/ExportFormatToStringConverter.cs @@ -10,12 +10,12 @@ namespace DiscordChatExporter.Gui.Converters { public static ExportFormatToStringConverter Instance { get; } = new ExportFormatToStringConverter(); - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + public object? Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is ExportFormat exportFormatValue) return exportFormatValue.GetDisplayName(); - return default; + return default(string); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) diff --git a/DiscordChatExporter.Gui/Converters/InverseBoolConverter.cs b/DiscordChatExporter.Gui/Converters/InverseBoolConverter.cs index 30e2638..6c6c067 100644 --- a/DiscordChatExporter.Gui/Converters/InverseBoolConverter.cs +++ b/DiscordChatExporter.Gui/Converters/InverseBoolConverter.cs @@ -14,7 +14,7 @@ namespace DiscordChatExporter.Gui.Converters if (value is bool boolValue) return !boolValue; - return default; + return default(bool); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) @@ -22,7 +22,7 @@ namespace DiscordChatExporter.Gui.Converters if (value is bool boolValue) return !boolValue; - return default; + return default(bool); } } } \ No newline at end of file diff --git a/DiscordChatExporter.Gui/DiscordChatExporter.Gui.csproj b/DiscordChatExporter.Gui/DiscordChatExporter.Gui.csproj index fa97d6a..51f0447 100644 --- a/DiscordChatExporter.Gui/DiscordChatExporter.Gui.csproj +++ b/DiscordChatExporter.Gui/DiscordChatExporter.Gui.csproj @@ -7,6 +7,7 @@ 2.15 Tyrrrz Copyright (c) Alexey Golub + enable true ../favicon.ico @@ -19,11 +20,11 @@ - + - + diff --git a/DiscordChatExporter.Gui/Services/UpdateService.cs b/DiscordChatExporter.Gui/Services/UpdateService.cs index 1057a65..b66bb13 100644 --- a/DiscordChatExporter.Gui/Services/UpdateService.cs +++ b/DiscordChatExporter.Gui/Services/UpdateService.cs @@ -1,6 +1,5 @@ using System; using System.Threading.Tasks; -using DiscordChatExporter.Core.Services; using Onova; using Onova.Exceptions; using Onova.Services; @@ -13,10 +12,10 @@ namespace DiscordChatExporter.Gui.Services new GithubPackageResolver("Tyrrrz", "DiscordChatExporter", "DiscordChatExporter.zip"), new ZipPackageExtractor()); - private Version _updateVersion; + private Version? _updateVersion; private bool _updaterLaunched; - public async Task CheckForUpdatesAsync() + public async Task CheckForUpdatesAsync() { var check = await _updateManager.CheckForUpdatesAsync(); return check.CanUpdate ? check.LastVersion : null; diff --git a/DiscordChatExporter.Gui/ViewModels/Components/ChannelViewModel.cs b/DiscordChatExporter.Gui/ViewModels/Components/ChannelViewModel.cs index f92786b..b6d2f81 100644 --- a/DiscordChatExporter.Gui/ViewModels/Components/ChannelViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/Components/ChannelViewModel.cs @@ -7,7 +7,7 @@ namespace DiscordChatExporter.Gui.ViewModels.Components { public Channel Model { get; set; } - public string Category { get; set; } + public string? Category { get; set; } } public partial class ChannelViewModel diff --git a/DiscordChatExporter.Gui/ViewModels/Dialogs/ExportSetupViewModel.cs b/DiscordChatExporter.Gui/ViewModels/Dialogs/ExportSetupViewModel.cs index bd0df60..a4c43b2 100644 --- a/DiscordChatExporter.Gui/ViewModels/Dialogs/ExportSetupViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/Dialogs/ExportSetupViewModel.cs @@ -6,7 +6,6 @@ using DiscordChatExporter.Core.Services; using DiscordChatExporter.Core.Services.Helpers; using DiscordChatExporter.Gui.ViewModels.Components; using DiscordChatExporter.Gui.ViewModels.Framework; -using Tyrrrz.Extensions; namespace DiscordChatExporter.Gui.ViewModels.Dialogs { @@ -21,12 +20,12 @@ namespace DiscordChatExporter.Gui.ViewModels.Dialogs public bool IsSingleChannel => Channels.Count == 1; - public string OutputPath { get; set; } + public string? OutputPath { get; set; } public IReadOnlyList AvailableFormats => Enum.GetValues(typeof(ExportFormat)).Cast().ToArray(); - public ExportFormat SelectedFormat { get; set; } = ExportFormat.HtmlDark; + public ExportFormat SelectedFormat { get; set; } public DateTimeOffset? After { get; set; } @@ -38,11 +37,6 @@ namespace DiscordChatExporter.Gui.ViewModels.Dialogs { _dialogManager = dialogManager; _settingsService = settingsService; - } - - protected override void OnViewLoaded() - { - base.OnViewLoaded(); // Persist preferences SelectedFormat = _settingsService.LastExportFormat; @@ -85,7 +79,7 @@ namespace DiscordChatExporter.Gui.ViewModels.Dialogs } // If canceled - return - if (OutputPath.IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(OutputPath)) return; // Close dialog diff --git a/DiscordChatExporter.Gui/ViewModels/Framework/DialogManager.cs b/DiscordChatExporter.Gui/ViewModels/Framework/DialogManager.cs index 09d378f..fbb3aeb 100644 --- a/DiscordChatExporter.Gui/ViewModels/Framework/DialogManager.cs +++ b/DiscordChatExporter.Gui/ViewModels/Framework/DialogManager.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Threading.Tasks; using MaterialDesignThemes.Wpf; using Microsoft.Win32; @@ -22,10 +23,10 @@ namespace DiscordChatExporter.Gui.ViewModels.Framework var view = _viewManager.CreateAndBindViewForModelIfNecessary(dialogScreen); // Set up event routing that will close the view when called from viewmodel - void OnDialogOpened(object sender, DialogOpenedEventArgs openArgs) + void OnDialogOpened(object? sender, DialogOpenedEventArgs openArgs) { // Delegate to close the dialog and unregister event handler - void OnScreenClosed(object o, CloseEventArgs closeArgs) + void OnScreenClosed(object? o, EventArgs closeArgs) { openArgs.Session.Close(); dialogScreen.Closed -= OnScreenClosed; @@ -41,7 +42,7 @@ namespace DiscordChatExporter.Gui.ViewModels.Framework return dialogScreen.DialogResult; } - public string PromptSaveFilePath(string filter = "All files|*.*", string defaultFilePath = "") + public string? PromptSaveFilePath(string filter = "All files|*.*", string defaultFilePath = "") { // Create dialog var dialog = new SaveFileDialog @@ -56,7 +57,7 @@ namespace DiscordChatExporter.Gui.ViewModels.Framework return dialog.ShowDialog() == true ? dialog.FileName : null; } - public string PromptDirectoryPath(string defaultDirPath = "") + public string? PromptDirectoryPath(string defaultDirPath = "") { // Create dialog var dialog = new VistaFolderBrowserDialog diff --git a/DiscordChatExporter.Gui/ViewModels/Framework/DialogScreen.cs b/DiscordChatExporter.Gui/ViewModels/Framework/DialogScreen.cs index 8cd090f..e5a535c 100644 --- a/DiscordChatExporter.Gui/ViewModels/Framework/DialogScreen.cs +++ b/DiscordChatExporter.Gui/ViewModels/Framework/DialogScreen.cs @@ -1,22 +1,18 @@ -using Stylet; +using System; +using Stylet; namespace DiscordChatExporter.Gui.ViewModels.Framework { - public abstract class DialogScreen : Screen + public abstract class DialogScreen : PropertyChangedBase { public T DialogResult { get; private set; } + public event EventHandler? Closed; + public void Close(T dialogResult = default) { - // Set the result DialogResult = dialogResult; - - // If there is a parent - ask them to close this dialog - if (Parent != null) - RequestClose(Equals(dialogResult, default(T))); - // Otherwise close ourselves - else - ((IScreenState) this).Close(); + Closed?.Invoke(this, EventArgs.Empty); } } diff --git a/DiscordChatExporter.Gui/ViewModels/Framework/Extensions.cs b/DiscordChatExporter.Gui/ViewModels/Framework/Extensions.cs index edac0c8..551ac89 100644 --- a/DiscordChatExporter.Gui/ViewModels/Framework/Extensions.cs +++ b/DiscordChatExporter.Gui/ViewModels/Framework/Extensions.cs @@ -7,8 +7,7 @@ namespace DiscordChatExporter.Gui.ViewModels.Framework { public static class Extensions { - public static ChannelViewModel CreateChannelViewModel(this IViewModelFactory factory, Channel model, - string category = null) + public static ChannelViewModel CreateChannelViewModel(this IViewModelFactory factory, Channel model, string? category = null) { var viewModel = factory.CreateChannelViewModel(); viewModel.Model = model; diff --git a/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs b/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs index f147c86..5db61c3 100644 --- a/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/RootViewModel.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; -using System.Reflection; using System.Threading.Tasks; using DiscordChatExporter.Core.Models; using DiscordChatExporter.Core.Services; @@ -38,13 +37,13 @@ namespace DiscordChatExporter.Gui.ViewModels public bool IsBotToken { get; set; } - public string TokenValue { get; set; } + public string? TokenValue { get; set; } - public IReadOnlyList AvailableGuilds { get; private set; } + public IReadOnlyList? AvailableGuilds { get; private set; } - public GuildViewModel SelectedGuild { get; set; } + public GuildViewModel? SelectedGuild { get; set; } - public IReadOnlyList SelectedChannels { get; set; } + public IReadOnlyList? SelectedChannels { get; set; } public RootViewModel(IViewModelFactory viewModelFactory, DialogManager dialogManager, SettingsService settingsService, UpdateService updateService, DataService dataService, @@ -58,8 +57,7 @@ namespace DiscordChatExporter.Gui.ViewModels _exportService = exportService; // Set title - var version = Assembly.GetExecutingAssembly().GetName().Version.ToString(3); - DisplayName = $"DiscordChatExporter v{version}"; + DisplayName = $"{App.Name} v{App.VersionString}"; // Update busy state when progress manager changes ProgressManager.Bind(o => o.IsActive, (sender, args) => IsBusy = ProgressManager.IsActive); @@ -83,7 +81,7 @@ namespace DiscordChatExporter.Gui.ViewModels return; // Notify user of an update and prepare it - Notifications.Enqueue($"Downloading update to DiscordChatExporter v{updateVersion}..."); + Notifications.Enqueue($"Downloading update to {App.Name} v{updateVersion}..."); await _updateService.PrepareUpdateAsync(updateVersion); // Prompt user to install update (otherwise install it when application exits) @@ -140,7 +138,7 @@ namespace DiscordChatExporter.Gui.ViewModels await _dialogManager.ShowDialogAsync(dialog); } - public bool CanPopulateGuildsAndChannels => !IsBusy && !TokenValue.IsNullOrWhiteSpace(); + public bool CanPopulateGuildsAndChannels => !IsBusy && !string.IsNullOrWhiteSpace(TokenValue); public async void PopulateGuildsAndChannels() { @@ -150,7 +148,7 @@ namespace DiscordChatExporter.Gui.ViewModels try { // Sanitize token - TokenValue = TokenValue.Trim('"'); + TokenValue = TokenValue!.Trim('"'); // Create token var token = new AuthToken( @@ -253,15 +251,15 @@ namespace DiscordChatExporter.Gui.ViewModels } } - public bool CanExportChannels => !IsBusy && !SelectedChannels.IsNullOrEmpty(); + public bool CanExportChannels => !IsBusy && SelectedGuild != null && SelectedChannels != null && SelectedChannels.Any(); public async void ExportChannels() { // Get last used token - var token = _settingsService.LastToken; + var token = _settingsService.LastToken!; // Create dialog - var dialog = _viewModelFactory.CreateExportSetupViewModel(SelectedGuild, SelectedChannels); + var dialog = _viewModelFactory.CreateExportSetupViewModel(SelectedGuild!, SelectedChannels!); // Show dialog, if canceled - return if (await _dialogManager.ShowDialogAsync(dialog) != true) @@ -281,7 +279,7 @@ namespace DiscordChatExporter.Gui.ViewModels try { // Generate file path if necessary - var filePath = dialog.OutputPath; + var filePath = dialog.OutputPath!; if (ExportHelper.IsDirectoryPath(filePath)) { // Generate default file name