From da3bcefbb9a736d9cf66edac53b486ff3303890e Mon Sep 17 00:00:00 2001 From: Kornelius Rohrschneider Date: Thu, 30 Oct 2025 01:40:59 +0100 Subject: [PATCH] Added UI options to enable the existing export search The setting on whether the existing export search is enabled or not has been made configurable in both the CLI and the GUI (instead of being hardcoded): - A search-existing-exports option has been added to all CLI export commands, which enables the existing export search for the respective command. - A Search for existing exports option has been added to the GUI settings, which enables the existing export search for exports. Additionally, there have been several minor improvements: - The ConsoleProgressLogger has been fixed to interpolate given messages (previously, the square brackets in the default export file names could cause an exception). - The names of existing channel exports in log messages have been enclosed by quotation marks. - The setting on how to handle existing export files has been renamed from prev-export / Previous Export to export-exists / Export Exists. --- .../Commands/Base/ExportCommandBase.cs | 12 ++++++++++-- .../Utils/Extensions/ConsoleExtensions.cs | 12 ++++++------ .../Exporting/ChannelExporter.cs | 8 +++----- DiscordChatExporter.Core/Exporting/ExportRequest.cs | 4 ++++ DiscordChatExporter.Gui/Services/SettingsService.cs | 3 +++ .../ViewModels/Components/DashboardViewModel.cs | 1 + .../ViewModels/Dialogs/SettingsViewModel.cs | 6 ++++++ .../Views/Dialogs/SettingsView.axaml | 13 +++++++++++-- 8 files changed, 44 insertions(+), 15 deletions(-) diff --git a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs index 354d9e0e..95acf168 100644 --- a/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs +++ b/DiscordChatExporter.Cli/Commands/Base/ExportCommandBase.cs @@ -115,11 +115,18 @@ public abstract class ExportCommandBase : DiscordCommandBase } [CommandOption( - "prev-export", - Description = "What the exporter should do if the channel had already been exported." + "export-exists", + Description = "What the exporter should do if a channel had already been exported." )] public ExportExistsHandling ExportExistsHandling { get; init; } = ExportExistsHandling.Abort; + [CommandOption( + "search-existing-exports", + Description = "Whether the target directory should be searched for existing export(s) of the channel(s). " + + "This is required to find an existing export if the channel, channel parent or guild name has changed." + )] + public bool SearchForExistingExports { get; init; } + [Obsolete("This option doesn't do anything. Kept for backwards compatibility.")] [CommandOption( "dateformat", @@ -272,6 +279,7 @@ public abstract class ExportCommandBase : DiscordCommandBase After, Before, ExportExistsHandling, + SearchForExistingExports, PartitionLimit, MessageFilter, ShouldFormatMarkdown, diff --git a/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs b/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs index dd132e8a..f311dc87 100644 --- a/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs +++ b/DiscordChatExporter.Cli/Utils/Extensions/ConsoleExtensions.cs @@ -78,21 +78,21 @@ public class ConsoleProgressLogger(IAnsiConsole console) : ProgressLogger /// The ConsoleProgressLogger logs the success message to the console. public override void LogSuccess(ExportRequest request, string message) { - LogMessage("SUCCESS", "[green]", request, message); + LogMessage("SUCCESS", "green", request, message); } /// /// The ConsoleProgressLogger logs the informational message to the console. public override void LogInfo(ExportRequest request, string message) { - LogMessage("INFO", "[default]", request, message); + LogMessage("INFO", "default", request, message); } /// /// The ConsoleProgressLogger logs the warning message to the console. public override void LogWarning(ExportRequest request, string message) { - LogMessage("WARNING", "[yellow]", request, message); + LogMessage("WARNING", "yellow", request, message); } /// @@ -100,7 +100,7 @@ public class ConsoleProgressLogger(IAnsiConsole console) : ProgressLogger public override void LogError(ExportRequest? request, string message) { IncrementCounter(ExportResult.ExportError); - LogMessage("ERROR", "[red]", request, message); + LogMessage("ERROR", "red", request, message); } /// @@ -120,8 +120,8 @@ public class ConsoleProgressLogger(IAnsiConsole console) : ProgressLogger channelInfo = request.Guild.Name + " / " + request.Channel.GetHierarchicalName() + " | "; - var logMessage = $"{color}{paddedCategory}{channelInfo}{message}[/]"; - console.MarkupLine(logMessage); + FormattableString logMessage = $"[{color}]{paddedCategory}{channelInfo}{message}[/]"; + console.MarkupLineInterpolated(logMessage); } /// diff --git a/DiscordChatExporter.Core/Exporting/ChannelExporter.cs b/DiscordChatExporter.Core/Exporting/ChannelExporter.cs index 2348b5c7..ae3a30db 100644 --- a/DiscordChatExporter.Core/Exporting/ChannelExporter.cs +++ b/DiscordChatExporter.Core/Exporting/ChannelExporter.cs @@ -35,13 +35,11 @@ public class ChannelExporter(DiscordClient discord) return; } - // TODO: Add a way for the user to choose the setting - var searchForExistingExport = true; if ( !DetectExistingExport( request, logger, - searchForExistingExport, + request.SearchForExistingExports, outputDirFilesDict, out var existingExportFile ) @@ -215,7 +213,7 @@ public class ChannelExporter(DiscordClient discord) logger.LogError( request, "Found multiple existing channel exports under different file names: " - + string.Join(", ", regexFiles) + + string.Join(", ", regexFiles.Select(fileName => $"\"{fileName}\"")) + "." ); return false; @@ -223,7 +221,7 @@ public class ChannelExporter(DiscordClient discord) logger.LogInfo( request, - "Found existing channel export under file name " + regexFiles[0] + "." + $"Found existing channel export under file name \"{regexFiles[0]}\"." ); existingExportFile = Path.Combine(request.OutputDirPath, regexFiles[0]); return true; diff --git a/DiscordChatExporter.Core/Exporting/ExportRequest.cs b/DiscordChatExporter.Core/Exporting/ExportRequest.cs index 5abe25fb..248e6754 100644 --- a/DiscordChatExporter.Core/Exporting/ExportRequest.cs +++ b/DiscordChatExporter.Core/Exporting/ExportRequest.cs @@ -32,6 +32,8 @@ public partial class ExportRequest public ExportExistsHandling ExportExistsHandling { get; } + public bool SearchForExistingExports { get; } + public Snowflake? LastPriorMessage { get; set; } public PartitionLimit PartitionLimit { get; } @@ -59,6 +61,7 @@ public partial class ExportRequest Snowflake? after, Snowflake? before, ExportExistsHandling exportExistsHandling, + bool searchForExistingExports, PartitionLimit partitionLimit, MessageFilter messageFilter, bool shouldFormatMarkdown, @@ -74,6 +77,7 @@ public partial class ExportRequest After = after; Before = before; ExportExistsHandling = exportExistsHandling; + SearchForExistingExports = searchForExistingExports; PartitionLimit = partitionLimit; MessageFilter = messageFilter; ShouldFormatMarkdown = shouldFormatMarkdown; diff --git a/DiscordChatExporter.Gui/Services/SettingsService.cs b/DiscordChatExporter.Gui/Services/SettingsService.cs index a56da7e1..bb2ed219 100644 --- a/DiscordChatExporter.Gui/Services/SettingsService.cs +++ b/DiscordChatExporter.Gui/Services/SettingsService.cs @@ -39,6 +39,9 @@ public partial class SettingsService() [ObservableProperty] public partial ExportExistsHandling ExportExistsHandling { get; set; } + [ObservableProperty] + public partial bool SearchForExistingExports { get; set; } + [ObservableProperty] public partial string? Locale { get; set; } diff --git a/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs b/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs index 2f9761b0..be843c6b 100644 --- a/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/Components/DashboardViewModel.cs @@ -272,6 +272,7 @@ public partial class DashboardViewModel : ViewModelBase dialog.After?.Pipe(timestamp => Snowflake.FromDate(timestamp, true)), dialog.Before?.Pipe(timestamp => Snowflake.FromDate(timestamp)), _settingsService.ExportExistsHandling, + _settingsService.SearchForExistingExports, dialog.PartitionLimit, dialog.MessageFilter, dialog.ShouldFormatMarkdown, diff --git a/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs b/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs index 8d96f0d1..8283703e 100644 --- a/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs +++ b/DiscordChatExporter.Gui/ViewModels/Dialogs/SettingsViewModel.cs @@ -71,6 +71,12 @@ public class SettingsViewModel : DialogViewModelBase set => _settingsService.ExportExistsHandling = value; } + public bool SearchForExistingExports + { + get => _settingsService.SearchForExistingExports; + set => _settingsService.SearchForExistingExports = value; + } + // These items have to be non-nullable because Avalonia ComboBox doesn't allow a null value to be selected public IReadOnlyList AvailableLocales { get; } = [ diff --git a/DiscordChatExporter.Gui/Views/Dialogs/SettingsView.axaml b/DiscordChatExporter.Gui/Views/Dialogs/SettingsView.axaml index b1cc5abf..29f99a82 100644 --- a/DiscordChatExporter.Gui/Views/Dialogs/SettingsView.axaml +++ b/DiscordChatExporter.Gui/Views/Dialogs/SettingsView.axaml @@ -136,14 +136,23 @@ - + ToolTip.Tip="What the exporter should do if a channel had already been exported"> + + + + + + +