diff --git a/app/Desktop/Dialogs/Progress/DelayedProgressDialog.cs b/app/Desktop/Dialogs/Progress/DelayedProgressDialog.cs new file mode 100644 index 0000000..d6c7c0c --- /dev/null +++ b/app/Desktop/Dialogs/Progress/DelayedProgressDialog.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Threading; + +namespace DHT.Desktop.Dialogs.Progress; + +static class DelayedProgressDialog { + public static async ValueTask Await(Func taskProvider, TimeSpan delay, Window window, string progressDialogTitle, string progressDialogDescription) { + Dispatcher.UIThread.VerifyAccess(); + + Task task = Task.Run(taskProvider); + if (task.IsCompleted) { + return; + } + + // Freeze the UI thread for a short while in case the task finishes quickly. + _ = Task.WhenAny(Task.Delay(delay), task).GetAwaiter().GetResult(); + + if (!task.IsCompleted) { + await ProgressDialog.ShowIndeterminate(window, progressDialogTitle, progressDialogDescription, _ => task); + } + } +} diff --git a/app/Desktop/Dialogs/Progress/ProgressDialog.axaml.cs b/app/Desktop/Dialogs/Progress/ProgressDialog.axaml.cs index 5d35882..59d0c90 100644 --- a/app/Desktop/Dialogs/Progress/ProgressDialog.axaml.cs +++ b/app/Desktop/Dialogs/Progress/ProgressDialog.axaml.cs @@ -2,13 +2,12 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Avalonia.Controls; -using DHT.Utils.Logging; namespace DHT.Desktop.Dialogs.Progress; [SuppressMessage("ReSharper", "MemberCanBeInternal")] public sealed partial class ProgressDialog : Window { - private static readonly Log Log = Log.ForType(); + private static readonly TimeSpan MinimumShowDuration = TimeSpan.FromMilliseconds(500); internal static async Task Show(Window owner, string title, Func action) { var dialog = new ProgressDialog(); @@ -43,6 +42,7 @@ public sealed partial class ProgressDialog : Window { } private bool isFinished = false; + private DateTime startTime = DateTime.Now; private Task progressTask = Task.CompletedTask; public ProgressDialog() { @@ -50,6 +50,8 @@ public sealed partial class ProgressDialog : Window { } public void OnOpened(object? sender, EventArgs e) { + startTime = DateTime.Now; + if (DataContext is ProgressDialogModel model) { progressTask = Task.Run(model.StartTask); progressTask.ContinueWith(OnFinished, TaskScheduler.FromCurrentSynchronizationContext()); @@ -60,8 +62,14 @@ public sealed partial class ProgressDialog : Window { e.Cancel = !isFinished; } - private void OnFinished(Task task) { + private async Task OnFinished(Task task) { isFinished = true; + + TimeSpan elapsedTime = DateTime.Now - startTime; + if (elapsedTime < MinimumShowDuration) { + await Task.Delay(MinimumShowDuration - elapsedTime); + } + Close(); } diff --git a/app/Desktop/Main/MainWindowModel.cs b/app/Desktop/Main/MainWindowModel.cs index 19c84e5..971dc15 100644 --- a/app/Desktop/Main/MainWindowModel.cs +++ b/app/Desktop/Main/MainWindowModel.cs @@ -4,6 +4,7 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; using Avalonia.Controls; using DHT.Desktop.Dialogs.Message; +using DHT.Desktop.Dialogs.Progress; using DHT.Desktop.Main.Screens; using DHT.Desktop.Server; using DHT.Server; @@ -104,32 +105,33 @@ sealed partial class MainWindowModel : IAsyncDisposable { } private async void MainContentScreenModelOnDatabaseClosed(object? sender, EventArgs e) { + await DisposeContent(); + + Title = DefaultTitle; + CurrentScreen = welcomeScreen; + + await DisposeState(); + + welcomeScreenModel.DatabaseSelected += OnDatabaseSelected; + } + + private async Task DisposeContent() { if (mainContentScreenModel != null) { mainContentScreenModel.DatabaseClosed -= MainContentScreenModelOnDatabaseClosed; await mainContentScreenModel.DisposeAsync(); mainContentScreenModel = null; } - - await DisposeState(); - - Title = DefaultTitle; - CurrentScreen = welcomeScreen; - - welcomeScreenModel.DatabaseSelected += OnDatabaseSelected; } private async Task DisposeState() { if (state != null) { - await state.DisposeAsync(); + await DelayedProgressDialog.Await(() => state.DisposeAsync().AsTask(), TimeSpan.FromMilliseconds(200), window, "Close Database", "Please wait for the database to close..."); state = null; } } public async ValueTask DisposeAsync() { - if (mainContentScreenModel != null) { - await mainContentScreenModel.DisposeAsync(); - } - + await DisposeContent(); await DisposeState(); } }