From 4973526bf8266d19fd28947ff957fba1eef87e4b Mon Sep 17 00:00:00 2001 From: Paul De Smul Date: Wed, 18 Sep 2024 20:51:55 +0200 Subject: [PATCH] Capture all create directory exceptions to avoid crashes --- src/App.xaml.cs | 17 +++++- src/Models/CodexCollection.cs | 53 +++++++++++++++---- src/Models/Folder.cs | 19 ++++--- src/Services/IOService.cs | 28 ++++++++++ src/ViewModels/CollectionViewModel.cs | 47 +++++++++++++--- .../Import/ImportFolderViewModel.cs | 25 ++++++++- src/ViewModels/SettingsViewModel.cs | 25 ++++++--- src/Windows/NotificationWindow.xaml | 6 +-- 8 files changed, 185 insertions(+), 35 deletions(-) diff --git a/src/App.xaml.cs b/src/App.xaml.cs index c56b414e..c3badbf7 100644 --- a/src/App.xaml.cs +++ b/src/App.xaml.cs @@ -2,7 +2,9 @@ using COMPASS.Interfaces; using COMPASS.Models.Enums; using COMPASS.Services; +using COMPASS.Tools; using COMPASS.ViewModels; +using System; using System.IO; using System.Windows; @@ -15,7 +17,20 @@ public partial class App : Application { public App() { - Directory.CreateDirectory(SettingsViewModel.CompassDataPath); + try + { + if (!Directory.Exists(SettingsViewModel.CompassDataPath)) + { + Directory.CreateDirectory(SettingsViewModel.CompassDataPath); + } + } + catch (Exception ex) + { + Logger.Error($"Failed to create folder at compass data path, so data cannot be saved", ex); + string msg = $"Failed to create a folder to store user data at {SettingsViewModel.CompassDataPath}, " + + $"please pick a new location to save your data. Creation failed with the following error {ex.Message}"; + IOService.AskNewCodexFilePath(msg); + } } private static IContainer? _container; diff --git a/src/Models/CodexCollection.cs b/src/Models/CodexCollection.cs index b1f70ca3..b226971f 100644 --- a/src/Models/CodexCollection.cs +++ b/src/Models/CodexCollection.cs @@ -221,18 +221,39 @@ public void InitAsNew() private void CreateDirectories() { - //top folder - Directory.CreateDirectory(FullDataPath); - //subfolders - Directory.CreateDirectory(CoverArtPath); - Directory.CreateDirectory(ThumbnailsPath); - Directory.CreateDirectory(UserFilesPath); + try + { + //top folder + Directory.CreateDirectory(FullDataPath); + //subfolders + Directory.CreateDirectory(CoverArtPath); + Directory.CreateDirectory(ThumbnailsPath); + Directory.CreateDirectory(UserFilesPath); + } + catch (Exception ex) + { + Logger.Error("Failed to create folders to store user data for this collection.", ex); + var windowedNotificationService = App.Container.ResolveKeyed(NotificationDisplayType.Windowed); + + string msg = $"Failed to create the necessary folders to store data about this collection. The following error occured: {ex.Message}"; + Notification failedFolderCreation = new("Failed to save collection", msg, Severity.Error); + windowedNotificationService.Show(failedFolderCreation); + } } public void Save() { OnPropertyChanged(nameof(AllCodices)); - Directory.CreateDirectory(UserFilesPath); + + try + { + Directory.CreateDirectory(UserFilesPath); + } + catch (Exception ex) + { + Logger.Error("Failed to create the folder to save the data for this collection", ex); + } + bool savedTags = SaveTags(); bool savedCodices = SaveCodices(); bool savedInfo = SaveInfo(); @@ -456,7 +477,14 @@ public void ImportCodicesFrom(CodexCollection source) //if import includes files, make sure directory exists to copy files into if (Path.Exists(source.UserFilesPath)) { - Directory.CreateDirectory(UserFilesPath); + try + { + Directory.CreateDirectory(UserFilesPath); + } + catch (Exception ex) + { + Logger.Error("Failed to create a folder to store the imported files", ex); + } } foreach (var codex in source.AllCodices) @@ -508,7 +536,14 @@ public void ImportCodicesFrom(CodexCollection source) string? newDir = Path.GetDirectoryName(newPath); if (newDir != null) { - Directory.CreateDirectory(newDir); + try + { + Directory.CreateDirectory(newDir); + } + catch (Exception ex) + { + Logger.Error("Failed to create a folder to store the imported files", ex); + } } File.Copy(codex.Sources.Path, newPath, true); codex.Sources.Path = newPath; diff --git a/src/Models/Folder.cs b/src/Models/Folder.cs index 74d82e9e..bbffd0ed 100644 --- a/src/Models/Folder.cs +++ b/src/Models/Folder.cs @@ -1,4 +1,6 @@ using CommunityToolkit.Mvvm.ComponentModel; +using COMPASS.Tools; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; @@ -55,13 +57,18 @@ private IEnumerable FindSubFolders() { if (Directory.Exists(FullPath)) { - var directories = Directory.GetDirectories(FullPath); - return directories.Select(dir => new Folder(dir)); - } - else - { - return Enumerable.Empty(); + try + { + var directories = Directory.GetDirectories(FullPath); + return directories.Select(dir => new Folder(dir)); + } + catch (Exception ex) + { + Logger.Error($"Failed to get subfolders of {FullPath}", ex); + } } + + return Enumerable.Empty(); } /// diff --git a/src/Services/IOService.cs b/src/Services/IOService.cs index 9c398f1f..f6200687 100644 --- a/src/Services/IOService.cs +++ b/src/Services/IOService.cs @@ -338,6 +338,34 @@ public static bool TryPickFolders(out string[] selectedPaths) return null; } } + + /// + /// If the exsting codex path is inaccessable for any reason, prompt the user to pick another one + /// + /// + public static void AskNewCodexFilePath(string msg) + { + var windowedNotificationService = App.Container.ResolveKeyed(NotificationDisplayType.Windowed); + + Notification pickNewPath = new("Pick a location to save your data", msg, Severity.Warning); + pickNewPath.ConfirmText = "Continue"; + windowedNotificationService.Show(pickNewPath); + + bool success = false; + while (!success) + { + string? newPath = PickFolder(); + if (!string.IsNullOrWhiteSpace(newPath) && Path.Exists(newPath)) + { + success = SettingsViewModel.GetInstance().SetNewDataPath(newPath); + } + else + { + Notification notValid = new("Invalid path", $"{newPath} is not a valid path, please try again", Severity.Warning); + windowedNotificationService.Show(notValid); + } + } + } #endregion /// diff --git a/src/ViewModels/CollectionViewModel.cs b/src/ViewModels/CollectionViewModel.cs index c80bf72b..593655ef 100644 --- a/src/ViewModels/CollectionViewModel.cs +++ b/src/ViewModels/CollectionViewModel.cs @@ -32,14 +32,32 @@ public CollectionViewModel(MainViewModel? mainViewModel) _tagsVM = new(new("__tmp"), _filterVM); //Create Collections Directory - Directory.CreateDirectory(CodexCollection.CollectionsPath); + try + { + Directory.CreateDirectory(CodexCollection.CollectionsPath); + } + catch (Exception ex) + { + Logger.Error($"Failed to create folder to store user data, so data cannot be saved", ex); + string msg = $"Failed to create a folder to store user data at {SettingsViewModel.CompassDataPath}, " + + $"please pick a new location to save your data. Creation failed with the following error {ex.Message}"; + IOService.AskNewCodexFilePath(msg); + } - //Get all collections by folder name - AllCodexCollections = new(Directory - .GetDirectories(CodexCollection.CollectionsPath) - .Select(dir => Path.GetFileName(dir)) - .Where(IsLegalCollectionName) - .Select(dir => new CodexCollection(dir))); + try + { + //Get all collections by folder name + AllCodexCollections = new(Directory + .GetDirectories(CodexCollection.CollectionsPath) + .Select(dir => Path.GetFileName(dir)) + .Where(IsLegalCollectionName) + .Select(dir => new CodexCollection(dir))); + } + catch (Exception ex) + { + Logger.Error($"Failed to find existing collections in {CodexCollection.CollectionsPath}", ex); + AllCodexCollections = new(); + } LoadInitialCollection(); @@ -112,7 +130,20 @@ public bool EditCollectionVisibility #region Methods and Commands private void LoadInitialCollection() { - Directory.CreateDirectory(CodexCollection.CollectionsPath); + while (!Directory.Exists(CodexCollection.CollectionsPath)) + { + try + { + Directory.CreateDirectory(CodexCollection.CollectionsPath); + } + catch (Exception ex) + { + Logger.Error($"Failed to create folder to store user data, so data cannot be saved", ex); + string msg = $"Failed to create a folder to store user data at {SettingsViewModel.CompassDataPath}, " + + $"please pick a new location to save your data. Creation failed with the following error {ex.Message}"; + IOService.AskNewCodexFilePath(msg); + } + } bool initSuccess = false; while (!initSuccess) diff --git a/src/ViewModels/Import/ImportFolderViewModel.cs b/src/ViewModels/Import/ImportFolderViewModel.cs index dd442cd3..8944553c 100644 --- a/src/ViewModels/Import/ImportFolderViewModel.cs +++ b/src/ViewModels/Import/ImportFolderViewModel.cs @@ -5,6 +5,7 @@ using COMPASS.Services; using COMPASS.Tools; using COMPASS.Windows; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -94,7 +95,17 @@ private List GetPathsToImport() if (Directory.Exists(currentFolder)) { NonRecursiveDirectories.Add(currentFolder); - foreach (string dir in Directory.GetDirectories(currentFolder)) + IEnumerable subfolders; + try + { + subfolders = Directory.GetDirectories(currentFolder); + } + catch (Exception ex) + { + Logger.Error($"Failed to get subfolders of {currentFolder}", ex); + subfolders = Enumerable.Empty(); + } + foreach (string dir in subfolders) { toSearch.Enqueue(dir); } @@ -107,7 +118,17 @@ private List GetPathsToImport() { if (Directory.Exists(folder)) { - toImport.AddRange(Directory.GetFiles(folder)); + IEnumerable files; + try + { + files = Directory.GetFiles(folder); + } + catch (Exception ex) + { + Logger.Error($"Failed to get files of folder {folder}", ex); + files = Enumerable.Empty(); + } + toImport.AddRange(files); } } diff --git a/src/ViewModels/SettingsViewModel.cs b/src/ViewModels/SettingsViewModel.cs index 89fe14bb..8b02e9d7 100644 --- a/src/ViewModels/SettingsViewModel.cs +++ b/src/ViewModels/SettingsViewModel.cs @@ -461,25 +461,38 @@ private void ChooseNewDataPath() private RelayCommand? _resetDataPathCommand; public RelayCommand ResetDataPathCommand => _resetDataPathCommand ??= new(() => SetNewDataPath(_defaultDataPath)); - private void SetNewDataPath(string newPath) + public bool SetNewDataPath(string newPath) { - if (String.IsNullOrWhiteSpace(newPath) || !Path.Exists(newPath)) { return; } + if (String.IsNullOrWhiteSpace(newPath) || !Path.Exists(newPath)) { return false; } //make sure new folder ends on /COMPASS string foldername = new DirectoryInfo(newPath).Name; if (foldername != "COMPASS") { newPath = Path.Combine(newPath, "COMPASS"); - Directory.CreateDirectory(newPath); + try + { + Directory.CreateDirectory(newPath); + } + catch (Exception ex) + { + Logger.Error($"Failed to create the COMPASS folder at new data path location {newPath}", ex); + return false; + } } - if (newPath == CompassDataPath) { return; } + if (newPath == CompassDataPath) { return false; } NewDataPath = newPath; //Give users choice between moving or copying - ChangeDataLocationWindow window = new(this); - window.ShowDialog(); + if (Path.Exists(CompassDataPath)) + { + ChangeDataLocationWindow window = new(this); + window.ShowDialog(); + } + + return true; } private AsyncRelayCommand? _moveToNewDataPathCommand; diff --git a/src/Windows/NotificationWindow.xaml b/src/Windows/NotificationWindow.xaml index e7d3c79c..398367ab 100644 --- a/src/Windows/NotificationWindow.xaml +++ b/src/Windows/NotificationWindow.xaml @@ -92,13 +92,13 @@ -