diff --git a/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml b/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml index 142ea69a..890d8004 100644 --- a/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml +++ b/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml @@ -3,6 +3,9 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="450" + xmlns:uibase="clr-namespace:LibationUiBase;assembly=LibationUiBase" + x:DataType="uibase:LocatedAudiobooksViewModel" + x:CompileBindings="True" Width="600" Height="450" x:Class="LibationAvalonia.Dialogs.LocateAudiobooksDialog" Title="Locate Audiobooks" @@ -13,17 +16,34 @@ - + - - - - - - - - - - + + + + + + + + + diff --git a/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs index f265d404..385f0bef 100644 --- a/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/LocateAudiobooksDialog.axaml.cs @@ -1,41 +1,39 @@ -using ApplicationServices; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Platform.Storage; using DataLayer; -using LibationAvalonia.ViewModels; +using Dinah.Core; using LibationFileManager; -using ReactiveUI; +using LibationUiBase; using System; -using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reactive.Linq; using System.Threading; namespace LibationAvalonia.Dialogs { public partial class LocateAudiobooksDialog : DialogWindow { - private event EventHandler? FileFound; private readonly CancellationTokenSource tokenSource = new(); - private readonly List foundAsins = new(); private readonly LocatedAudiobooksViewModel _viewModel; public LocateAudiobooksDialog() { InitializeComponent(); - DataContext = _viewModel = new(); + var list = new AvaloniaList(); + DataContext = _viewModel = new(list); + list.CollectionChanged += (_, _) => foundFilesDataGrid.ScrollIntoView(list[^1], foundFilesDataGrid.Columns[0]); this.RestoreSizeAndLocation(Configuration.Instance); if (Design.IsDesignMode) { - _viewModel.FoundFiles.Add(new("[0000001]", "Filename 1.m4b")); - _viewModel.FoundFiles.Add(new("[0000002]", "Filename 2.m4b")); + _viewModel.AddFoundFile(new("0000000001", FileType.Audio, "Filename 1.m4b")); + _viewModel.AddFoundFile(new("0000000002", FileType.Audio, "Filename 2.m4b")); } else { Opened += LocateAudiobooksDialog_Opened; - FileFound += LocateAudiobooks_FileFound; Closing += LocateAudiobooksDialog_Closing; } } @@ -49,19 +47,6 @@ namespace LibationAvalonia.Dialogs this.SaveSizeAndLocation(Configuration.Instance); } - private void LocateAudiobooks_FileFound(object? sender, FilePathCache.CacheEntry e) - { - var newItem = new Tuple($"[{e.Id}]", Path.GetFileName(e.Path)); - _viewModel.FoundFiles.Add(newItem); - foundAudiobooksLB.SelectedItem = newItem; - - if (!foundAsins.Any(asin => asin == e.Id)) - { - foundAsins.Add(e.Id); - _viewModel.FoundAsins = foundAsins.Count; - } - } - private async void LocateAudiobooksDialog_Opened(object? sender, EventArgs e) { var folderPicker = new FolderPickerOpenOptions @@ -76,37 +61,18 @@ namespace LibationAvalonia.Dialogs if (selectedFolder is null || !Directory.Exists(selectedFolder)) { await CancelAndCloseAsync(); - return; } - - await foreach (var book in AudioFileStorage.FindAudiobooksAsync(selectedFolder, tokenSource.Token)) + else { - try - { - FilePathCache.Insert(book); - - var lb = DbContexts.GetLibraryBook_Flat_NoTracking(book.Id); - if (lb is not null && lb.Book?.UserDefinedItem.BookStatus is not LiberatedStatus.Liberated) - await lb.UpdateBookStatusAsync(LiberatedStatus.Liberated); - - tokenSource.Token.ThrowIfCancellationRequested(); - FileFound?.Invoke(this, book); - } - catch (OperationCanceledException) { } - catch (Exception ex) - { - Serilog.Log.Error(ex, "Error adding found audiobook file to Libation. {@audioFile}", book); - } + await _viewModel.FindAndAddBooksAsync(selectedFolder, tokenSource.Token); + await MessageBox.Show(this, $"Libation has found {_viewModel.FoundAsinCount} unique audiobooks and added them to its database. ", $"Found {_viewModel.FoundAsinCount} Audiobooks"); } + } - await MessageBox.Show(this, $"Libation has found {foundAsins.Count} unique audiobooks and added them to its database. ", $"Found {foundAsins.Count} Audiobooks"); - await SaveAndCloseAsync(); + private void foundFilesDataGrid_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e) + { + if (sender is DataGrid dg && dg.SelectedItem is FoundAudiobook foundAudiobook) + Go.To.File(foundAudiobook.Entry.Path); } } - - public class LocatedAudiobooksViewModel : ViewModelBase - { - public AvaloniaList> FoundFiles { get; } = new(); - public int FoundAsins { get => field; set => this.RaiseAndSetIfChanged(ref field, value); } - } } diff --git a/Source/LibationUiBase/LocatedAudiobooksViewModel.cs b/Source/LibationUiBase/LocatedAudiobooksViewModel.cs new file mode 100644 index 00000000..6859b600 --- /dev/null +++ b/Source/LibationUiBase/LocatedAudiobooksViewModel.cs @@ -0,0 +1,71 @@ +using ApplicationServices; +using DataLayer; +using LibationFileManager; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace LibationUiBase; + +public class FoundAudiobook +{ + public string ID => Entry.Id; + public string FileName { get; } + public FilePathCache.CacheEntry Entry { get; } + public FoundAudiobook(FilePathCache.CacheEntry entry) + { + Entry = entry; + FileName = Path.GetFileName(entry.Path); + } +} +public class LocatedAudiobooksViewModel : ReactiveObject +{ + public IList FoundFiles { get; } + public int FoundAsinCount { get => field; private set => RaiseAndSetIfChanged(ref field, value); } + private readonly HashSet foundAsinsSet = []; + public LocatedAudiobooksViewModel(IList fileList) + { + FoundFiles = fileList; + } + + public void AddFoundFile(FilePathCache.CacheEntry entry) + { + FoundAudiobook foundFile = new(entry); + Invoke(() => FoundFiles?.Add(foundFile)); + if (!foundAsinsSet.Contains(entry.Id)) + { + foundAsinsSet.Add(entry.Id); + FoundAsinCount = foundAsinsSet.Count; + } + } + + public async Task FindAndAddBooksAsync(string searchdir, CancellationToken cancellation) + { + await Task.Run(() => FindAndAddBooksInternal(searchdir, cancellation), cancellation).ConfigureAwait(false); + } + + private async Task FindAndAddBooksInternal(string searchdir, CancellationToken cancellation) + { + await foreach (var book in AudioFileStorage.FindAudiobooksAsync(searchdir, cancellation)) + { + try + { + FilePathCache.Insert(book); + + var lb = DbContexts.GetLibraryBook_Flat_NoTracking(book.Id); + if (lb is not null && lb.Book?.UserDefinedItem.BookStatus is not LiberatedStatus.Liberated) + await lb.UpdateBookStatusAsync(LiberatedStatus.Liberated); + + cancellation.ThrowIfCancellationRequested(); + AddFoundFile(book); + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + Serilog.Log.Error(ex, "Error adding found audiobook file to Libation. {@audioFile}", book); + } + } + } +} diff --git a/Source/LibationWinForms/Dialogs/LocateAudiobooksDialog.Designer.cs b/Source/LibationWinForms/Dialogs/LocateAudiobooksDialog.Designer.cs index 58e436b4..7c71c329 100644 --- a/Source/LibationWinForms/Dialogs/LocateAudiobooksDialog.Designer.cs +++ b/Source/LibationWinForms/Dialogs/LocateAudiobooksDialog.Designer.cs @@ -28,80 +28,111 @@ /// private void InitializeComponent() { - this.label1 = new System.Windows.Forms.Label(); - this.foundAudiobooksLV = new System.Windows.Forms.ListView(); - this.columnHeader1 = new System.Windows.Forms.ColumnHeader(); - this.columnHeader2 = new System.Windows.Forms.ColumnHeader(); - this.booksFoundLbl = new System.Windows.Forms.Label(); - this.SuspendLayout(); + components = new System.ComponentModel.Container(); + label1 = new System.Windows.Forms.Label(); + booksFoundLbl = new System.Windows.Forms.Label(); + dataGridView1 = new System.Windows.Forms.DataGridView(); + foundAudiobookBindingSource = new System.Windows.Forms.BindingSource(components); + iDDataGridViewTextBoxColumn = new AccessibleDataGridViewColumn(); + fileNameDataGridViewTextBoxColumn = new AccessibleDataGridViewColumn(); + ((System.ComponentModel.ISupportInitialize)dataGridView1).BeginInit(); + ((System.ComponentModel.ISupportInitialize)foundAudiobookBindingSource).BeginInit(); + SuspendLayout(); // // label1 // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(12, 9); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(108, 15); - this.label1.TabIndex = 1; - this.label1.Text = "Found Audiobooks"; - // - // foundAudiobooksLV - // - this.foundAudiobooksLV.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.foundAudiobooksLV.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { - this.columnHeader1, - this.columnHeader2}); - this.foundAudiobooksLV.FullRowSelect = true; - this.foundAudiobooksLV.Location = new System.Drawing.Point(12, 33); - this.foundAudiobooksLV.Name = "foundAudiobooksLV"; - this.foundAudiobooksLV.Size = new System.Drawing.Size(321, 261); - this.foundAudiobooksLV.TabIndex = 2; - this.foundAudiobooksLV.UseCompatibleStateImageBehavior = false; - this.foundAudiobooksLV.View = System.Windows.Forms.View.Details; - // - // columnHeader1 - // - this.columnHeader1.Text = "Book ID"; - this.columnHeader1.Width = 85; - // - // columnHeader2 - // - this.columnHeader2.Text = "Title"; + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(12, 9); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(108, 15); + label1.TabIndex = 1; + label1.Text = "Found Audiobooks"; // // booksFoundLbl // - this.booksFoundLbl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.booksFoundLbl.AutoSize = true; - this.booksFoundLbl.Location = new System.Drawing.Point(253, 9); - this.booksFoundLbl.Name = "booksFoundLbl"; - this.booksFoundLbl.Size = new System.Drawing.Size(80, 15); - this.booksFoundLbl.TabIndex = 3; - this.booksFoundLbl.Text = "IDs Found: {0}"; - this.booksFoundLbl.TextAlign = System.Drawing.ContentAlignment.TopRight; + booksFoundLbl.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right; + booksFoundLbl.AutoSize = true; + booksFoundLbl.Location = new System.Drawing.Point(253, 9); + booksFoundLbl.Name = "booksFoundLbl"; + booksFoundLbl.Size = new System.Drawing.Size(72, 15); + booksFoundLbl.TabIndex = 3; + booksFoundLbl.Text = "IDs Found: 0"; + booksFoundLbl.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // dataGridView1 + // + dataGridView1.AllowUserToAddRows = false; + dataGridView1.AllowUserToDeleteRows = false; + dataGridView1.AllowUserToResizeRows = false; + dataGridView1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + dataGridView1.AutoGenerateColumns = false; + dataGridView1.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.AllCells; + dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { iDDataGridViewTextBoxColumn, fileNameDataGridViewTextBoxColumn }); + dataGridView1.DataSource = foundAudiobookBindingSource; + dataGridView1.Location = new System.Drawing.Point(12, 27); + dataGridView1.Name = "dataGridView1"; + dataGridView1.RowHeadersVisible = false; + dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect; + dataGridView1.Size = new System.Drawing.Size(321, 267); + dataGridView1.TabIndex = 4; + dataGridView1.CellDoubleClick += dataGridView1_CellDoubleClick; + // + // foundAudiobookBindingSource + // + foundAudiobookBindingSource.DataSource = typeof(LibationUiBase.FoundAudiobook); + // + // iDDataGridViewTextBoxColumn + // + iDDataGridViewTextBoxColumn.AccessibilityDescription = "Audiobook's Audible product ID forund by the scan."; + iDDataGridViewTextBoxColumn.AccessibilityName = "Found ASIN"; + iDDataGridViewTextBoxColumn.DataPropertyName = "ID"; + iDDataGridViewTextBoxColumn.HeaderText = "Found ID"; + iDDataGridViewTextBoxColumn.Name = "iDDataGridViewTextBoxColumn"; + iDDataGridViewTextBoxColumn.ReadOnly = true; + iDDataGridViewTextBoxColumn.Resizable = System.Windows.Forms.DataGridViewTriState.True; + iDDataGridViewTextBoxColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + iDDataGridViewTextBoxColumn.Width = 80; + // + // fileNameDataGridViewTextBoxColumn + // + fileNameDataGridViewTextBoxColumn.AccessibilityDescription = "Audiobook file found. Double-click to open containing folder."; + fileNameDataGridViewTextBoxColumn.AccessibilityName = "Found File"; + fileNameDataGridViewTextBoxColumn.DataPropertyName = "FileName"; + fileNameDataGridViewTextBoxColumn.HeaderText = "Found File"; + fileNameDataGridViewTextBoxColumn.Name = "fileNameDataGridViewTextBoxColumn"; + fileNameDataGridViewTextBoxColumn.ReadOnly = true; + fileNameDataGridViewTextBoxColumn.Resizable = System.Windows.Forms.DataGridViewTriState.True; + fileNameDataGridViewTextBoxColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + fileNameDataGridViewTextBoxColumn.Width = 87; // // LocateAudiobooksDialog // - this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.ClientSize = new System.Drawing.Size(345, 306); - this.Controls.Add(this.booksFoundLbl); - this.Controls.Add(this.foundAudiobooksLV); - this.Controls.Add(this.label1); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow; - this.Name = "LocateAudiobooksDialog"; - this.Text = "Locate Audiobooks"; - this.ResumeLayout(false); - this.PerformLayout(); + AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + ClientSize = new System.Drawing.Size(345, 306); + Controls.Add(dataGridView1); + Controls.Add(booksFoundLbl); + Controls.Add(label1); + FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow; + Name = "LocateAudiobooksDialog"; + Text = "Locate Audiobooks"; + FormClosing += LocateAudiobooks_FormClosing; + Shown += LocateAudiobooks_Shown; + ((System.ComponentModel.ISupportInitialize)dataGridView1).EndInit(); + ((System.ComponentModel.ISupportInitialize)foundAudiobookBindingSource).EndInit(); + ResumeLayout(false); + PerformLayout(); } #endregion private System.Windows.Forms.Label label1; - private System.Windows.Forms.ListView foundAudiobooksLV; - private System.Windows.Forms.ColumnHeader columnHeader1; - private System.Windows.Forms.ColumnHeader columnHeader2; private System.Windows.Forms.Label booksFoundLbl; + private System.Windows.Forms.DataGridView dataGridView1; + private System.Windows.Forms.BindingSource foundAudiobookBindingSource; + private AccessibleDataGridViewColumn iDDataGridViewTextBoxColumn; + private AccessibleDataGridViewColumn fileNameDataGridViewTextBoxColumn; } } \ No newline at end of file diff --git a/Source/LibationWinForms/Dialogs/LocateAudiobooksDialog.cs b/Source/LibationWinForms/Dialogs/LocateAudiobooksDialog.cs index c05d0e34..70994eb7 100644 --- a/Source/LibationWinForms/Dialogs/LocateAudiobooksDialog.cs +++ b/Source/LibationWinForms/Dialogs/LocateAudiobooksDialog.cs @@ -1,53 +1,35 @@ -using ApplicationServices; -using DataLayer; +using DataLayer; +using Dinah.Core; using LibationFileManager; +using LibationUiBase; using System; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading; -using System.Threading.Tasks; using System.Windows.Forms; namespace LibationWinForms.Dialogs { public partial class LocateAudiobooksDialog : Form { - private event EventHandler FileFound; private readonly CancellationTokenSource tokenSource = new(); - private readonly List foundAsins = new(); - private readonly string labelFormatText; + private readonly LocatedAudiobooksViewModel _viewModel; public LocateAudiobooksDialog() { InitializeComponent(); - labelFormatText = booksFoundLbl.Text; - setFoundBookCount(0); - this.SetLibationIcon(); this.RestoreSizeAndLocation(Configuration.Instance); - Shown += LocateAudiobooks_Shown; - FileFound += LocateAudiobooks_FileFound; - FormClosing += LocateAudiobooks_FormClosing; + _viewModel = new LocatedAudiobooksViewModel(new SortBindingList()); + dataGridView1.EnableHeadersVisualStyles = !Application.IsDarkModeEnabled; + dataGridView1.RowsAdded += DataGridView1_RowsAdded; + foundAudiobookBindingSource.DataSource = _viewModel.FoundFiles; + booksFoundLbl.DataBindings.Add(new Binding(nameof(booksFoundLbl.Text), _viewModel, nameof(_viewModel.FoundAsinCount), true, DataSourceUpdateMode.OnPropertyChanged, 0, booksFoundLbl.Text)); } - private void setFoundBookCount(int count) - => booksFoundLbl.Text = string.Format(labelFormatText, count); - - private void LocateAudiobooks_FileFound(object sender, FilePathCache.CacheEntry e) + private void DataGridView1_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e) { - foundAudiobooksLV.Items - .Add(new ListViewItem(new string[] { $"[{e.Id}]", Path.GetFileName(e.Path) })) - .EnsureVisible(); - - foundAudiobooksLV.AutoResizeColumn(1, ColumnHeaderAutoResizeStyle.ColumnContent); - - if (!foundAsins.Any(asin => asin == e.Id)) - { - foundAsins.Add(e.Id); - setFoundBookCount(foundAsins.Count); - } + dataGridView1.FirstDisplayedScrollingRowIndex = e.RowIndex; } private void LocateAudiobooks_FormClosing(object sender, FormClosingEventArgs e) @@ -65,34 +47,22 @@ namespace LibationWinForms.Dialogs InitialDirectory = Configuration.Instance.Books }; - if (fbd.ShowDialog() != DialogResult.OK || !Directory.Exists(fbd.SelectedPath)) + var result = fbd.ShowDialog(this); + if (result != DialogResult.OK || !Directory.Exists(fbd.SelectedPath)) { - Close(); - return; + DialogResult = result; } - - await foreach (var book in AudioFileStorage.FindAudiobooksAsync(fbd.SelectedPath, tokenSource.Token)) + else { - try - { - FilePathCache.Insert(book); - - var lb = DbContexts.GetLibraryBook_Flat_NoTracking(book.Id); - if (lb.Book.UserDefinedItem.BookStatus is not LiberatedStatus.Liberated) - await lb.UpdateBookStatusAsync(LiberatedStatus.Liberated); - - tokenSource.Token.ThrowIfCancellationRequested(); - this.Invoke(FileFound, this, book); - } - catch (OperationCanceledException) { } - catch (Exception ex) - { - Serilog.Log.Error(ex, "Error adding found audiobook file to Libation. {@audioFile}", book); - } + await _viewModel.FindAndAddBooksAsync(fbd.SelectedPath, tokenSource.Token); + MessageBox.Show(this, $"Libation has found {_viewModel.FoundAsinCount} unique audiobooks and added them to its database. ", $"Found {_viewModel.FoundAsinCount} Audiobooks"); } + } - MessageBox.Show(this, $"Libation has found {foundAsins.Count} unique audiobooks and added them to its database. ", $"Found {foundAsins.Count} Audiobooks"); - Close(); + private void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e) + { + if (e.RowIndex >= 0 && e.RowIndex < _viewModel.FoundFiles.Count) + Go.To.File(_viewModel.FoundFiles[e.RowIndex].Entry.Path); } } } diff --git a/Source/LibationWinForms/Dialogs/UpgradeNotificationDialog.cs b/Source/LibationWinForms/Dialogs/UpgradeNotificationDialog.cs index 6138d3ee..ae8c18e4 100644 --- a/Source/LibationWinForms/Dialogs/UpgradeNotificationDialog.cs +++ b/Source/LibationWinForms/Dialogs/UpgradeNotificationDialog.cs @@ -24,21 +24,6 @@ namespace LibationWinForms.Dialogs releaseNotesTbox.Text = upgradeProperties.Notes; Shown += (_, _) => yesBtn.Focus(); - Load += UpgradeNotificationDialog_Load; - } - - private void UpgradeNotificationDialog_Load(object sender, EventArgs e) - { - //This dialog starts before Form1, soposition it at the center of where Form1 will be. - var savedState = Configuration.Instance.GetNonString(defaultValue: null, nameof(Form1)); - - if (savedState is null) return; - - int x = savedState.X + (savedState.Width - Width) / 2; - int y = savedState.Y + (savedState.Height - Height) / 2; - - Location = new(x, y); - TopMost = true; } private void PackageDlLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) diff --git a/Source/LibationWinForms/Form1.Upgrade.cs b/Source/LibationWinForms/Form1.Upgrade.cs index 23b5ba6f..ec43b38e 100644 --- a/Source/LibationWinForms/Form1.Upgrade.cs +++ b/Source/LibationWinForms/Form1.Upgrade.cs @@ -11,14 +11,12 @@ namespace LibationWinForms { setProgressVisible(false); #pragma warning disable CS8321 // Local function is declared but never used - Task upgradeAvailable(UpgradeEventArgs e) + async Task upgradeAvailable(UpgradeEventArgs e) { - var notificationResult = new UpgradeNotificationDialog(e.UpgradeProperties).ShowDialog(this); + var notificationResult = await new UpgradeNotificationDialog(e.UpgradeProperties).ShowDialogAsync(this); e.Ignore = notificationResult == DialogResult.Ignore; e.InstallUpgrade = notificationResult == DialogResult.Yes; - - return Task.CompletedTask; } #pragma warning restore CS8321 // Local function is declared but never used diff --git a/docs/installation/linux.md b/docs/installation/linux.md index 4933f0eb..ee8f0a70 100644 --- a/docs/installation/linux.md +++ b/docs/installation/linux.md @@ -69,6 +69,16 @@ Thanks to [mhdi](https://aur.archlinux.org/account/mhdi) for taking care of AUR Thanks to [TomaSajt](https://github.com/tomasajt) for taking care of Nix package maintenance. +### Pacstall + +Pacstall is the AUR Ubuntu wishes it had. It takes the concept of the AUR and puts a spin on it, making it easier to install programs without scouring github repos and the likes. See the [Pacstall](https://pacstall.dev/) project for more information. + +```bash +pacstall -I libation-deb +``` + +--- + If your desktop uses gtk, you should now see Libation among your applications. Additionally, you may launch Libation, LibationCli, and Hangover (the Libation recovery app) via the command line using 'libation, libationcli', and 'hangover' aliases respectively.