From 46d1a1ce608aab3bb7ffe049a0964e3c19d7f62a Mon Sep 17 00:00:00 2001 From: Ionite Date: Wed, 31 May 2023 20:23:20 -0400 Subject: [PATCH] Add DeleteDirectoryAsync and error dialog --- .../ViewModels/PackageManagerViewModel.cs | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/StabilityMatrix/ViewModels/PackageManagerViewModel.cs b/StabilityMatrix/ViewModels/PackageManagerViewModel.cs index a92c01cc..c9db7766 100644 --- a/StabilityMatrix/ViewModels/PackageManagerViewModel.cs +++ b/StabilityMatrix/ViewModels/PackageManagerViewModel.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; using System.IO; using System.Threading.Tasks; using StabilityMatrix.Helper; @@ -9,6 +11,7 @@ using System.Windows; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Extensions.Logging; +using Polly; using Wpf.Ui.Contracts; using Wpf.Ui.Controls.ContentDialogControl; using EventManager = StabilityMatrix.Helper.EventManager; @@ -22,6 +25,7 @@ public partial class PackageManagerViewModel : ObservableObject private readonly IPackageFactory packageFactory; private readonly IDialogFactory dialogFactory; private readonly IContentDialogService contentDialogService; + private readonly IDialogErrorHandler dialogErrorHandler; private const int MinutesToWaitForUpdateCheck = 60; [ObservableProperty] @@ -51,13 +55,14 @@ public partial class PackageManagerViewModel : ObservableObject private bool updateAvailable; public PackageManagerViewModel(ILogger logger, ISettingsManager settingsManager, - IPackageFactory packageFactory, IDialogFactory dialogFactory, IContentDialogService contentDialogService) + IPackageFactory packageFactory, IDialogFactory dialogFactory, IContentDialogService contentDialogService, IDialogErrorHandler dialogErrorHandler) { this.logger = logger; this.settingsManager = settingsManager; this.packageFactory = packageFactory; this.dialogFactory = dialogFactory; this.contentDialogService = contentDialogService; + this.dialogErrorHandler = dialogErrorHandler; ProgressText = "shrug"; InstallButtonText = "Install"; @@ -159,12 +164,43 @@ public partial class PackageManagerViewModel : ObservableObject if (result == ContentDialogResult.Primary) { - DeleteDirectory(SelectedPackage.Path); - settingsManager.RemoveInstalledPackage(SelectedPackage); + var deleteTask = DeleteDirectoryAsync(SelectedPackage.Path); + var taskResult = await dialogErrorHandler.TryAsync(deleteTask, + "Some files could not be deleted. Please close any open files in the package directory and try again."); + if (taskResult.IsSuccessful) + { + settingsManager.RemoveInstalledPackage(SelectedPackage); + } await OnLoaded(); } } + /// + /// Deletes a directory and all of its contents recursively. + /// Uses Polly to retry the deletion if it fails, up to 5 times with an exponential backoff. + /// + /// + private Task DeleteDirectoryAsync(string targetDirectory) + { + var policy = Policy.Handle() + .WaitAndRetryAsync(3, attempt => TimeSpan.FromMilliseconds(50 * Math.Pow(2, attempt)), + onRetry: (exception, calculatedWaitDuration) => + { + logger.LogWarning( + exception, + "Deletion of {TargetDirectory} failed. Retrying in {CalculatedWaitDuration}", + targetDirectory, calculatedWaitDuration); + }); + + return policy.ExecuteAsync(async () => + { + await Task.Run(() => + { + DeleteDirectory(targetDirectory); + }); + }); + } + private void DeleteDirectory(string targetDirectory) { // Delete all files in the directory