diff --git a/Source/LibationAvalonia/AvaloniaUtils.cs b/Source/LibationAvalonia/AvaloniaUtils.cs index 66b4935d..8d9fd607 100644 --- a/Source/LibationAvalonia/AvaloniaUtils.cs +++ b/Source/LibationAvalonia/AvaloniaUtils.cs @@ -18,22 +18,6 @@ namespace LibationAvalonia return defaultBrush; } - public static Window GetParentWindow(this IControl control) - { - Window window = null; - - var p = control.Parent; - while (p != null) - { - if (p is Window) - { - window = (Window)p; - break; - } - p = p.Parent; - } - - return window; - } + public static Window GetParentWindow(this IControl control) => control.VisualRoot as Window; } } diff --git a/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs b/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs index 02b85fe6..658e0e0b 100644 --- a/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs +++ b/Source/LibationAvalonia/Controls/DirectoryOrCustomSelectControl.axaml.cs @@ -5,6 +5,7 @@ using Dinah.Core; using LibationFileManager; using System.Collections.Generic; using ReactiveUI; +using System.Linq; namespace LibationAvalonia.Controls { @@ -16,7 +17,6 @@ namespace LibationAvalonia.Controls public static readonly StyledProperty SubDirectoryProperty = AvaloniaProperty.Register(nameof(SubDirectory)); - public static readonly StyledProperty DirectoryProperty = AvaloniaProperty.Register(nameof(Directory)); @@ -90,8 +90,19 @@ namespace LibationAvalonia.Controls private async void CustomDirBrowseBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) { - OpenFolderDialog ofd = new(); - customStates.CustomDir = await ofd.ShowAsync(VisualRoot as Window); + var options = new Avalonia.Platform.Storage.FolderPickerOpenOptions + { + AllowMultiple = false + }; + + var selectedFolders = await (VisualRoot as Window).StorageProvider.OpenFolderPickerAsync(options); + + customStates.CustomDir = + selectedFolders + .SingleOrDefault()?. + TryGetUri(out var uri) is true + ? uri.LocalPath + : customStates.CustomDir; } private void CheckStates_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) @@ -125,7 +136,6 @@ namespace LibationAvalonia.Controls Directory = customStates.CustomChecked ? selectedDir : System.IO.Path.Combine(selectedDir, SubDirectory); } - private void DirectoryOrCustomSelectControl_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) { if (e.Property.Name == nameof(Directory) && e.OldValue is null) diff --git a/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs index 8020436c..eab179ea 100644 --- a/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/AccountsDialog.axaml.cs @@ -9,6 +9,9 @@ using System.Linq; using System.Threading.Tasks; using ReactiveUI; using AudibleApi; +using Avalonia.Platform.Storage; +using LibationFileManager; +using Avalonia.Platform.Storage.FileIO; namespace LibationAvalonia.Dialogs { @@ -110,24 +113,29 @@ namespace LibationAvalonia.Dialogs public async void ImportButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e) { - - OpenFileDialog ofd = new(); - ofd.Filters.Add(new() { Name = "JSON File", Extensions = new() { "json" } }); - ofd.Directory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - ofd.AllowMultiple = false; + var openFileDialogOptions = new FilePickerOpenOptions + { + Title = $"Select the audible-cli [account].json file", + AllowMultiple = false, + SuggestedStartLocation = new BclStorageFolder(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)), + FileTypeFilter = new FilePickerFileType[] + { + new("JSON files (*.json)") { Patterns = new[] { "json" } }, + } + }; string audibleAppDataDir = GetAudibleCliAppDataPath(); - if (Directory.Exists(audibleAppDataDir)) - ofd.Directory = audibleAppDataDir; + openFileDialogOptions.SuggestedStartLocation = new BclStorageFolder(audibleAppDataDir); - var filePath = await ofd.ShowAsync(this); + var selectedFiles = await StorageProvider.OpenFilePickerAsync(openFileDialogOptions); + var selectedFile = selectedFiles.SingleOrDefault(); - if (filePath is null || filePath.Length == 0) return; + if (!selectedFile.TryGetUri(out var uri)) return; try { - var jsonText = File.ReadAllText(filePath[0]); + var jsonText = File.ReadAllText(uri.LocalPath); var mkbAuth = Mkb79Auth.FromJson(jsonText); var account = await mkbAuth.ToAccountAsync(); @@ -148,7 +156,7 @@ namespace LibationAvalonia.Dialogs { await MessageBox.ShowAdminAlert( this, - $"An error occurred while importing an account from:\r\n{filePath[0]}\r\n\r\nIs the file encrypted?", + $"An error occurred while importing an account from:\r\n{uri.LocalPath}\r\n\r\nIs the file encrypted?", "Error Importing Account", ex); } @@ -263,26 +271,36 @@ namespace LibationAvalonia.Dialogs return; } - SaveFileDialog sfd = new(); - sfd.Filters.Add(new() { Name = "JSON File", Extensions = new() { "json" } }); + var options = new FilePickerSaveOptions + { + Title = $"Save Sover Image", + SuggestedStartLocation = new BclStorageFolder(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)), + SuggestedFileName = $"{acc.AccountId}.json", + DefaultExtension = "json", + ShowOverwritePrompt = true, + FileTypeChoices = new FilePickerFileType[] + { + new("JSON files (*.json)") { Patterns = new[] { "json" } }, + } + }; string audibleAppDataDir = GetAudibleCliAppDataPath(); if (Directory.Exists(audibleAppDataDir)) - sfd.Directory = audibleAppDataDir; + options.SuggestedStartLocation = new BclStorageFolder(audibleAppDataDir); - string fileName = await sfd.ShowAsync(this); - if (fileName is null) - return; + var selectedFile = await StorageProvider.SaveFilePickerAsync(options); + + if (!selectedFile.TryGetUri(out var uri)) return; try { var mkbAuth = Mkb79Auth.FromAccount(account); var jsonText = mkbAuth.ToJson(); - File.WriteAllText(fileName, jsonText); + File.WriteAllText(uri.LocalPath, jsonText); - await MessageBox.Show(this, $"Successfully exported {account.AccountName} to\r\n\r\n{fileName}", "Success!"); + await MessageBox.Show(this, $"Successfully exported {account.AccountName} to\r\n\r\n{uri.LocalPath}", "Success!"); } catch (Exception ex) { diff --git a/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs index 992ae7a3..5d6f444e 100644 --- a/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/ImageDisplayDialog.axaml.cs @@ -7,6 +7,7 @@ using System; using System.ComponentModel; using System.IO; using ReactiveUI; +using Avalonia.Platform.Storage; namespace LibationAvalonia.Dialogs { @@ -46,27 +47,30 @@ namespace LibationAvalonia.Dialogs public async void SaveImage_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e) { + var options = new FilePickerSaveOptions + { + Title = $"Save Sover Image", + SuggestedStartLocation = new Avalonia.Platform.Storage.FileIO.BclStorageFolder(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)), + SuggestedFileName = $"{PictureFileName}.jpg", + DefaultExtension = "jpg", + ShowOverwritePrompt = true, + FileTypeChoices = new FilePickerFileType[] + { + new("Jpeg (*.jpg)") { Patterns = new[] { "jpg" } } + } + }; - SaveFileDialog saveFileDialog = new(); - saveFileDialog.Filters.Add(new FileDialogFilter { Name = "Jpeg", Extensions = new System.Collections.Generic.List() { "jpg" } }); - saveFileDialog.InitialFileName = PictureFileName; - saveFileDialog.Directory - = !LibationFileManager.Configuration.IsWindows ? null - : Directory.Exists(BookSaveDirectory) ? BookSaveDirectory - : Path.GetDirectoryName(BookSaveDirectory); + var selectedFile = await StorageProvider.SaveFilePickerAsync(options); - var fileName = await saveFileDialog.ShowAsync(this); - - if (fileName is null) - return; + if (!selectedFile.TryGetUri(out var uri)) return; try { - File.WriteAllBytes(fileName, CoverBytes); + File.WriteAllBytes(uri.LocalPath, CoverBytes); } catch (Exception ex) { - Serilog.Log.Logger.Error(ex, $"Failed to save picture to {fileName}"); + Serilog.Log.Logger.Error(ex, $"Failed to save picture to {uri.LocalPath}"); await MessageBox.Show(this, $"An error was encountered while trying to save the picture\r\n\r\n{ex.Message}", "Failed to save picture", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); } } diff --git a/Source/LibationAvalonia/Views/MainWindow.Export.cs b/Source/LibationAvalonia/Views/MainWindow.Export.cs index fa39ebc3..f5d903be 100644 --- a/Source/LibationAvalonia/Views/MainWindow.Export.cs +++ b/Source/LibationAvalonia/Views/MainWindow.Export.cs @@ -1,5 +1,7 @@ using ApplicationServices; using Avalonia.Controls; +using Avalonia.Platform.Storage; +using LibationFileManager; using System; using System.Linq; @@ -14,34 +16,42 @@ namespace LibationAvalonia.Views { try { - var saveFileDialog = new SaveFileDialog + var options = new FilePickerSaveOptions { Title = "Where to export Library", + SuggestedStartLocation = new Avalonia.Platform.Storage.FileIO.BclStorageFolder(Configuration.Instance.Books), + SuggestedFileName = $"Libation Library Export {DateTime.Now:yyyy-MM-dd}.xlsx", + DefaultExtension = "xlsx", + ShowOverwritePrompt = true, + FileTypeChoices = new FilePickerFileType[] + { + new("Excel Workbook (*.xlsx)") { Patterns = new[] { "xlsx" } }, + new("CSV files (*.csv)") { Patterns = new[] { "csv" } }, + new("JSON files (*.json)") { Patterns = new[] { "json" } }, + new("All files (*.*)") { Patterns = new[] { "*" } }, + } }; - saveFileDialog.Filters.Add(new FileDialogFilter { Name = "Excel Workbook (*.xlsx)", Extensions = new() { "xlsx" } }); - saveFileDialog.Filters.Add(new FileDialogFilter { Name = "CSV files (*.csv)", Extensions = new() { "csv" } }); - saveFileDialog.Filters.Add(new FileDialogFilter { Name = "JSON files (*.json)", Extensions = new() { "json" } }); - saveFileDialog.Filters.Add(new FileDialogFilter { Name = "All files (*.*)", Extensions = new() { "*" } }); - var fileName = await saveFileDialog.ShowAsync(this); - if (fileName is null) return; + var selectedFile = await StorageProvider.SaveFilePickerAsync(options); - var ext = System.IO.Path.GetExtension(fileName); + if (!selectedFile.TryGetUri(out var uri)) return; + + var ext = System.IO.Path.GetExtension(uri.LocalPath); switch (ext) { case "xlsx": // xlsx default: - LibraryExporter.ToXlsx(fileName); + LibraryExporter.ToXlsx(uri.LocalPath); break; case "csv": // csv - LibraryExporter.ToCsv(fileName); + LibraryExporter.ToCsv(uri.LocalPath); break; case "json": // json - LibraryExporter.ToJson(fileName); + LibraryExporter.ToJson(uri.LocalPath); break; } - await MessageBox.Show("Library exported to:\r\n" + fileName, "Library Exported"); + await MessageBox.Show("Library exported to:\r\n" + uri.LocalPath, "Library Exported"); } catch (Exception ex) { diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs index 30cff7a5..8c479f94 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs @@ -6,6 +6,7 @@ using Avalonia; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using Avalonia.Platform.Storage; using DataLayer; using FileLiberator; using LibationAvalonia.Controls; @@ -42,7 +43,7 @@ namespace LibationAvalonia.Views }; var pdvm = new ProductsDisplayViewModel(); - pdvm.DisplayBooksAsync(sampleEntries); + _ = pdvm.DisplayBooksAsync(sampleEntries); DataContext = pdvm; return; @@ -106,17 +107,22 @@ namespace LibationAvalonia.Views { try { - var openFileDialog = new OpenFileDialog + var openFileDialogOptions = new FilePickerOpenOptions { Title = $"Locate the audio file for '{entry.Book.Title}'", - Filters = new() { new() { Name = "All files (*.*)", Extensions = new() { "*" } } }, - AllowMultiple = false + AllowMultiple = false, + SuggestedStartLocation = new Avalonia.Platform.Storage.FileIO.BclStorageFolder(Configuration.Instance.Books), + FileTypeFilter = new FilePickerFileType[] + { + new("All files (*.*)") { Patterns = new[] { "*" } }, + } }; - var filePaths = await openFileDialog.ShowAsync(this.GetParentWindow()); - var filePath = filePaths.SingleOrDefault(); - if (!string.IsNullOrWhiteSpace(filePath)) - FilePathCache.Insert(entry.AudibleProductId, filePath); + var selectedFiles = await this.GetParentWindow().StorageProvider.OpenFilePickerAsync(openFileDialogOptions); + var selectedFile = selectedFiles.SingleOrDefault(); + + if (selectedFile.TryGetUri(out var uri)) + FilePathCache.Insert(entry.AudibleProductId, uri.LocalPath); } catch (Exception ex) {