mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-04-23 06:24:48 +00:00
Refactor portions of RootViewModel into DashboardViewModel
This commit is contained in:
@@ -1,59 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DiscordChatExporter.Core.Discord;
|
||||
using DiscordChatExporter.Core.Discord.Data;
|
||||
using DiscordChatExporter.Core.Exceptions;
|
||||
using DiscordChatExporter.Core.Exporting;
|
||||
using DiscordChatExporter.Core.Utils.Extensions;
|
||||
using DiscordChatExporter.Gui.Services;
|
||||
using DiscordChatExporter.Gui.Utils;
|
||||
using DiscordChatExporter.Gui.ViewModels.Components;
|
||||
using DiscordChatExporter.Gui.ViewModels.Dialogs;
|
||||
using DiscordChatExporter.Gui.ViewModels.Messages;
|
||||
using DiscordChatExporter.Gui.ViewModels.Framework;
|
||||
using Gress;
|
||||
using Gress.Completable;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using Stylet;
|
||||
|
||||
namespace DiscordChatExporter.Gui.ViewModels;
|
||||
|
||||
public class RootViewModel : Screen
|
||||
public class RootViewModel : Screen, IHandle<NotificationMessage>, IDisposable
|
||||
{
|
||||
private readonly IViewModelFactory _viewModelFactory;
|
||||
private readonly DialogManager _dialogManager;
|
||||
private readonly SettingsService _settingsService;
|
||||
private readonly UpdateService _updateService;
|
||||
|
||||
private readonly AutoResetProgressMuxer _progressMuxer;
|
||||
|
||||
private DiscordClient? _discord;
|
||||
|
||||
public bool IsBusy { get; private set; }
|
||||
|
||||
public ProgressContainer<Percentage> Progress { get; } = new();
|
||||
|
||||
public bool IsProgressIndeterminate => IsBusy && Progress.Current.Fraction is <= 0 or >= 1;
|
||||
|
||||
|
||||
public SnackbarMessageQueue Notifications { get; } = new(TimeSpan.FromSeconds(5));
|
||||
|
||||
public string? Token { get; set; }
|
||||
|
||||
private IReadOnlyDictionary<Guild, IReadOnlyList<Channel>>? GuildChannelMap { get; set; }
|
||||
|
||||
public IReadOnlyList<Guild>? AvailableGuilds => GuildChannelMap?.Keys.ToArray();
|
||||
|
||||
public Guild? SelectedGuild { get; set; }
|
||||
|
||||
public IReadOnlyList<Channel>? AvailableChannels => SelectedGuild is not null
|
||||
? GuildChannelMap?[SelectedGuild]
|
||||
: null;
|
||||
|
||||
public IReadOnlyList<Channel>? SelectedChannels { get; set; }
|
||||
|
||||
public DashboardViewModel Dashboard { get; }
|
||||
|
||||
public RootViewModel(
|
||||
IViewModelFactory viewModelFactory,
|
||||
IEventAggregator eventAggregator,
|
||||
DialogManager dialogManager,
|
||||
SettingsService settingsService,
|
||||
UpdateService updateService)
|
||||
@@ -62,13 +33,12 @@ public class RootViewModel : Screen
|
||||
_dialogManager = dialogManager;
|
||||
_settingsService = settingsService;
|
||||
_updateService = updateService;
|
||||
|
||||
_progressMuxer = Progress.CreateMuxer().WithAutoReset();
|
||||
|
||||
eventAggregator.Subscribe(this);
|
||||
|
||||
Dashboard = _viewModelFactory.CreateDashboardViewModel();
|
||||
|
||||
DisplayName = $"{App.Name} v{App.VersionString}";
|
||||
|
||||
this.Bind(o => o.IsBusy, (_, _) => NotifyOfPropertyChange(() => IsProgressIndeterminate));
|
||||
Progress.Bind(o => o.Current, (_, _) => NotifyOfPropertyChange(() => IsProgressIndeterminate));
|
||||
}
|
||||
|
||||
private async Task ShowWarInUkraineMessageAsync()
|
||||
@@ -115,23 +85,18 @@ Press LEARN MORE to find ways that you can help.".Trim(),
|
||||
}
|
||||
}
|
||||
|
||||
// This is a custom event that fires when the dialog host is loaded
|
||||
public async void OnViewFullyLoaded()
|
||||
{
|
||||
await ShowWarInUkraineMessageAsync();
|
||||
await CheckForUpdatesAsync();
|
||||
}
|
||||
|
||||
protected override async void OnViewLoaded()
|
||||
protected override void OnViewLoaded()
|
||||
{
|
||||
base.OnViewLoaded();
|
||||
|
||||
_settingsService.Load();
|
||||
|
||||
if (_settingsService.LastToken is not null)
|
||||
{
|
||||
Token = _settingsService.LastToken;
|
||||
}
|
||||
|
||||
if (_settingsService.IsDarkModeEnabled)
|
||||
{
|
||||
App.SetDarkTheme();
|
||||
@@ -140,8 +105,6 @@ Press LEARN MORE to find ways that you can help.".Trim(),
|
||||
{
|
||||
App.SetLightTheme();
|
||||
}
|
||||
|
||||
await CheckForUpdatesAsync();
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
@@ -152,149 +115,8 @@ Press LEARN MORE to find ways that you can help.".Trim(),
|
||||
_updateService.FinalizeUpdate(false);
|
||||
}
|
||||
|
||||
public async void ShowSettings()
|
||||
{
|
||||
var dialog = _viewModelFactory.CreateSettingsViewModel();
|
||||
await _dialogManager.ShowDialogAsync(dialog);
|
||||
}
|
||||
public void Handle(NotificationMessage message) =>
|
||||
Notifications.Enqueue(message.Text);
|
||||
|
||||
public void ShowHelp() => ProcessEx.StartShellExecute(App.GitHubProjectWikiUrl);
|
||||
|
||||
public bool CanPopulateGuildsAndChannels =>
|
||||
!IsBusy && !string.IsNullOrWhiteSpace(Token);
|
||||
|
||||
public async void PopulateGuildsAndChannels()
|
||||
{
|
||||
IsBusy = true;
|
||||
var progress = _progressMuxer.CreateInput();
|
||||
|
||||
try
|
||||
{
|
||||
var token = Token?.Trim('"', ' ');
|
||||
if (string.IsNullOrWhiteSpace(token))
|
||||
return;
|
||||
|
||||
_settingsService.LastToken = token;
|
||||
|
||||
var discord = new DiscordClient(token);
|
||||
|
||||
var guildChannelMap = new Dictionary<Guild, IReadOnlyList<Channel>>();
|
||||
await foreach (var guild in discord.GetUserGuildsAsync())
|
||||
{
|
||||
var channels = await discord.GetGuildChannelsAsync(guild.Id);
|
||||
guildChannelMap[guild] = channels.Where(c => c.IsTextChannel).ToArray();
|
||||
}
|
||||
|
||||
_discord = discord;
|
||||
GuildChannelMap = guildChannelMap;
|
||||
SelectedGuild = guildChannelMap.Keys.FirstOrDefault();
|
||||
}
|
||||
catch (DiscordChatExporterException ex) when (!ex.IsFatal)
|
||||
{
|
||||
Notifications.Enqueue(ex.Message.TrimEnd('.'));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var dialog = _viewModelFactory.CreateMessageBoxViewModel(
|
||||
"Error pulling guilds and channels",
|
||||
ex.ToString()
|
||||
);
|
||||
|
||||
await _dialogManager.ShowDialogAsync(dialog);
|
||||
}
|
||||
finally
|
||||
{
|
||||
progress.ReportCompletion();
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanExportChannels =>
|
||||
!IsBusy &&
|
||||
_discord is not null &&
|
||||
SelectedGuild is not null &&
|
||||
SelectedChannels is not null &&
|
||||
SelectedChannels.Any();
|
||||
|
||||
public async void ExportChannels()
|
||||
{
|
||||
IsBusy = true;
|
||||
|
||||
try
|
||||
{
|
||||
if (_discord is null || SelectedGuild is null || SelectedChannels is null || !SelectedChannels.Any())
|
||||
return;
|
||||
|
||||
var dialog = _viewModelFactory.CreateExportSetupViewModel(SelectedGuild, SelectedChannels);
|
||||
if (await _dialogManager.ShowDialogAsync(dialog) != true)
|
||||
return;
|
||||
|
||||
var exporter = new ChannelExporter(_discord);
|
||||
|
||||
var progresses = Enumerable
|
||||
.Range(0, dialog.Channels!.Count)
|
||||
.Select(_ => _progressMuxer.CreateInput())
|
||||
.ToArray();
|
||||
|
||||
var successfulExportCount = 0;
|
||||
|
||||
await Parallel.ForEachAsync(
|
||||
dialog.Channels.Zip(progresses),
|
||||
new ParallelOptions
|
||||
{
|
||||
MaxDegreeOfParallelism = Math.Max(1, _settingsService.ParallelLimit)
|
||||
},
|
||||
async (tuple, cancellationToken) =>
|
||||
{
|
||||
var (channel, progress) = tuple;
|
||||
|
||||
try
|
||||
{
|
||||
var request = new ExportRequest(
|
||||
dialog.Guild!,
|
||||
channel,
|
||||
dialog.OutputPath!,
|
||||
dialog.SelectedFormat,
|
||||
dialog.After?.Pipe(Snowflake.FromDate),
|
||||
dialog.Before?.Pipe(Snowflake.FromDate),
|
||||
dialog.PartitionLimit,
|
||||
dialog.MessageFilter,
|
||||
dialog.ShouldDownloadMedia,
|
||||
_settingsService.ShouldReuseMedia,
|
||||
_settingsService.DateFormat
|
||||
);
|
||||
|
||||
await exporter.ExportChannelAsync(request, progress, cancellationToken);
|
||||
|
||||
Interlocked.Increment(ref successfulExportCount);
|
||||
}
|
||||
catch (DiscordChatExporterException ex) when (!ex.IsFatal)
|
||||
{
|
||||
Notifications.Enqueue(ex.Message.TrimEnd('.'));
|
||||
}
|
||||
finally
|
||||
{
|
||||
progress.ReportCompletion();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Notify of overall completion
|
||||
if (successfulExportCount > 0)
|
||||
Notifications.Enqueue($"Successfully exported {successfulExportCount} channel(s)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var dialog = _viewModelFactory.CreateMessageBoxViewModel(
|
||||
"Error exporting channel(s)",
|
||||
ex.ToString()
|
||||
);
|
||||
|
||||
await _dialogManager.ShowDialogAsync(dialog);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
public void Dispose() => Notifications.Dispose();
|
||||
}
|
||||
Reference in New Issue
Block a user