mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-03-31 17:43:04 -06:00
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:
parent
6879fca6fd
commit
c2ee6f047d
|
|
@ -180,7 +180,7 @@ public partial record Message
|
||||||
// Currently Discord only supports 1 snapshot per forward
|
// Currently Discord only supports 1 snapshot per forward
|
||||||
var forwardedMessage = json.GetPropertyOrNull("message_snapshots")
|
var forwardedMessage = json.GetPropertyOrNull("message_snapshots")
|
||||||
?.EnumerateArrayOrNull()
|
?.EnumerateArrayOrNull()
|
||||||
?.Select(j => j.GetPropertyOrNull("message"))
|
?.Select(j => j.GetPropertyOrNull("message"))
|
||||||
.WhereNotNull()
|
.WhereNotNull()
|
||||||
.Select(MessageSnapshot.Parse)
|
.Select(MessageSnapshot.Parse)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,15 @@ 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)
|
||||||
{
|
{
|
||||||
var kind =
|
var kind =
|
||||||
json.GetPropertyOrNull("type")?.GetInt32OrNull()?.Pipe(t => (MessageReferenceKind)t)
|
json.GetPropertyOrNull("type")?.GetInt32OrNull()?.Pipe(t => (MessageReferenceKind)t)
|
||||||
?? MessageReferenceKind.Default;
|
?? MessageReferenceKind.Default;
|
||||||
|
|
||||||
var messageId = json.GetPropertyOrNull("message_id")
|
var messageId = json.GetPropertyOrNull("message_id")
|
||||||
?.GetNonWhiteSpaceStringOrNull()
|
?.GetNonWhiteSpaceStringOrNull()
|
||||||
?.Pipe(Snowflake.Parse);
|
?.Pipe(Snowflake.Parse);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -352,7 +352,7 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
|
||||||
_writer.WriteEndObject();
|
_writer.WriteEndObject();
|
||||||
await _writer.FlushAsync(cancellationToken);
|
await _writer.FlushAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ValueTask WriteStickerAsync(
|
private async ValueTask WriteStickerAsync(
|
||||||
Sticker sticker,
|
Sticker sticker,
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
|
|
@ -553,14 +553,14 @@ 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(
|
||||||
"timestampEdited",
|
"timestampEdited",
|
||||||
message.ForwardedMessage.EditedTimestamp?.Pipe(Context.NormalizeDate)
|
message.ForwardedMessage.EditedTimestamp?.Pipe(Context.NormalizeDate)
|
||||||
);
|
);
|
||||||
|
|
||||||
_writer.WriteString(
|
_writer.WriteString(
|
||||||
"content",
|
"content",
|
||||||
await FormatMarkdownAsync(message.ForwardedMessage.Content, cancellationToken)
|
await FormatMarkdownAsync(message.ForwardedMessage.Content, cancellationToken)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ public static class CollectionExtensions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension<T>(IEnumerable<T?> source)
|
extension<T>(IEnumerable<T?> source)
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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}"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue