GUI: double-clicking a child-less server auto-opens export setup

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-02-27 16:15:04 +00:00
parent 6879fca6fd
commit c2ee6f047d
9 changed files with 78 additions and 25 deletions

View file

@ -9,7 +9,8 @@ public record MessageReference(
MessageReferenceKind Kind, MessageReferenceKind Kind,
Snowflake? MessageId, Snowflake? MessageId,
Snowflake? ChannelId, Snowflake? ChannelId,
Snowflake? GuildId) Snowflake? GuildId
)
{ {
public static MessageReference Parse(JsonElement json) public static MessageReference Parse(JsonElement json)
{ {

View file

@ -15,7 +15,8 @@ public record MessageSnapshot(
string Content, string Content,
IReadOnlyList<Attachment> Attachments, IReadOnlyList<Attachment> Attachments,
IReadOnlyList<Embed> Embeds, IReadOnlyList<Embed> Embeds,
IReadOnlyList<Sticker> Stickers) IReadOnlyList<Sticker> Stickers
)
{ {
public static MessageSnapshot Parse(JsonElement json) public static MessageSnapshot Parse(JsonElement json)
{ {
@ -23,37 +24,35 @@ public record MessageSnapshot(
json.GetPropertyOrNull("timestamp")?.GetDateTimeOffsetOrNull() json.GetPropertyOrNull("timestamp")?.GetDateTimeOffsetOrNull()
?? DateTimeOffset.MinValue; ?? DateTimeOffset.MinValue;
var editedTimestamp = json var editedTimestamp = json.GetPropertyOrNull("edited_timestamp")?.GetDateTimeOffsetOrNull();
.GetPropertyOrNull("edited_timestamp")
?.GetDateTimeOffsetOrNull();
var content = json.GetPropertyOrNull("content")?.GetStringOrNull() ?? ""; var content = json.GetPropertyOrNull("content")?.GetStringOrNull() ?? "";
var attachments = var attachments =
json json.GetPropertyOrNull("attachments")
.GetPropertyOrNull("attachments")
?.EnumerateArrayOrNull() ?.EnumerateArrayOrNull()
?.Select(Attachment.Parse) ?.Select(Attachment.Parse)
.ToArray() .ToArray()
?? []; ?? [];
var embeds = var embeds =
json json.GetPropertyOrNull("embeds")?.EnumerateArrayOrNull()?.Select(Embed.Parse).ToArray()
.GetPropertyOrNull("embeds")
?.EnumerateArrayOrNull()
?.Select(Embed.Parse)
.ToArray()
?? []; ?? [];
var stickers = var stickers =
json json.GetPropertyOrNull("sticker_items")
.GetPropertyOrNull("sticker_items")
?.EnumerateArrayOrNull() ?.EnumerateArrayOrNull()
?.Select(Sticker.Parse) ?.Select(Sticker.Parse)
.ToArray() .ToArray()
?? []; ?? [];
return new MessageSnapshot(timestamp, return new MessageSnapshot(
editedTimestamp, content, attachments, embeds, stickers); timestamp,
editedTimestamp,
content,
attachments,
embeds,
stickers
);
} }
} }

View file

@ -553,7 +553,7 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
_writer.WriteString( _writer.WriteString(
"timestamp", "timestamp",
Context.NormalizeDate(message.ForwardedMessage.Timestamp) Context.NormalizeDate(message.ForwardedMessage.Timestamp)
); );
_writer.WriteString( _writer.WriteString(

View file

@ -239,9 +239,9 @@ internal class PlainTextMessageWriter(Stream stream, ExportContext context)
); );
} }
await _writer.WriteLineAsync( await _writer.WriteLineAsync(
$"Originally sent: {Context.FormatDate(forwardedMessage.Timestamp)}" $"Originally sent: {Context.FormatDate(forwardedMessage.Timestamp)}"
); );
await WriteAttachmentsAsync(forwardedMessage.Attachments, cancellationToken); await WriteAttachmentsAsync(forwardedMessage.Attachments, cancellationToken);
await WriteEmbedsAsync(forwardedMessage.Embeds, cancellationToken); await WriteEmbedsAsync(forwardedMessage.Embeds, cancellationToken);

View file

@ -223,6 +223,54 @@ public partial class DashboardViewModel : ViewModelBase
} }
} }
[RelayCommand]
private async Task ShowSetupIfSingleChannelAsync()
{
// Wait for any in-progress channel loading to complete
if (IsBusy)
{
var tcs = new TaskCompletionSource();
void Handler(object? _, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(IsBusy) && !IsBusy)
tcs.TrySetResult();
}
PropertyChanged += Handler;
// Re-check after subscribing to avoid missing the transition
if (!IsBusy)
{
PropertyChanged -= Handler;
}
else
{
try
{
await tcs.Task;
}
finally
{
PropertyChanged -= Handler;
}
}
}
// Only auto-export when there is exactly one child-less (non-category) channel
if (
AvailableChannels is not { Count: 1 }
|| AvailableChannels[0].Children.Count != 0
|| AvailableChannels[0].Channel.IsCategory
)
return;
SelectedChannels.Clear();
SelectedChannels.Add(AvailableChannels[0]);
await ExportAsync();
}
private bool CanExport() => private bool CanExport() =>
!IsBusy && _discord is not null && SelectedGuild is not null && SelectedChannels.Any(); !IsBusy && _discord is not null && SelectedGuild is not null && SelectedChannels.Any();

View file

@ -99,6 +99,7 @@
BorderThickness="0,0,1,0" BorderThickness="0,0,1,0"
Grid.Column="0"> Grid.Column="0">
<ListBox <ListBox
DoubleTapped="AvailableGuildsListBox_OnDoubleTapped"
ItemsSource="{Binding AvailableGuilds}" ItemsSource="{Binding AvailableGuilds}"
ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden"
SelectedItem="{Binding SelectedGuild}" SelectedItem="{Binding SelectedGuild}"

View file

@ -1,5 +1,6 @@
using System.Linq; using System.Linq;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Discord.Data;
using DiscordChatExporter.Gui.Framework; using DiscordChatExporter.Gui.Framework;
@ -22,6 +23,9 @@ public partial class DashboardView : UserControl<DashboardViewModel>
SelectionChangedEventArgs args SelectionChangedEventArgs args
) => DataContext.PullChannelsCommand.Execute(null); ) => DataContext.PullChannelsCommand.Execute(null);
private void AvailableGuildsListBox_OnDoubleTapped(object? sender, TappedEventArgs args) =>
DataContext.ShowSetupIfSingleChannelCommand.Execute(null);
private void AvailableChannelsTreeView_OnSelectionChanged( private void AvailableChannelsTreeView_OnSelectionChanged(
object? sender, object? sender,
SelectionChangedEventArgs args SelectionChangedEventArgs args