Merge pull request #337 from Mbucari/master

Fixes and Improvements
This commit is contained in:
rmcrackan 2022-07-30 22:24:37 -04:00 committed by GitHub
commit f4749d703f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 406 additions and 339 deletions

View File

@ -11,6 +11,13 @@
<ProjectReference Include="..\ApplicationServices\ApplicationServices.csproj" /> <ProjectReference Include="..\ApplicationServices\ApplicationServices.csproj" />
<ProjectReference Include="..\AudibleUtilities\AudibleUtilities.csproj" /> <ProjectReference Include="..\AudibleUtilities\AudibleUtilities.csproj" />
</ItemGroup> </ItemGroup>
<PropertyGroup>
<DefineConstants Condition=" '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' ">$(DefineConstants);WINDOWS</DefineConstants>
<DefineConstants Condition=" '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' ">$(DefineConstants);LINUX</DefineConstants>
<DefineConstants Condition=" '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' ">$(DefineConstants);MACOS</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>

View File

@ -14,8 +14,23 @@ using Serilog;
namespace AppScaffolding namespace AppScaffolding
{ {
public enum ReleaseIdentifier
{
None,
WindowsClassic,
WindowsAvalonia,
LinuxAvalonia,
MacOSAvalonia
}
public static class LibationScaffolding public static class LibationScaffolding
{ {
public static ReleaseIdentifier ReleaseIdentifier { get; private set; }
public static void SetReleaseIdentifier(ReleaseIdentifier releaseID)
=> ReleaseIdentifier = releaseID;
// AppScaffolding // AppScaffolding
private static Assembly _executingAssembly; private static Assembly _executingAssembly;
private static Assembly ExecutingAssembly private static Assembly ExecutingAssembly
@ -275,12 +290,23 @@ namespace AppScaffolding
if (System.Diagnostics.Debugger.IsAttached) if (System.Diagnostics.Debugger.IsAttached)
mode += " (Debugger attached)"; mode += " (Debugger attached)";
#if MACOS
var os = "MacOS";
#elif LINUX
var os = "Linux";
#else
var os = "Windows";
#endif
// begin logging session with a form feed // begin logging session with a form feed
Log.Logger.Information("\r\n\f"); Log.Logger.Information("\r\n\f");
Log.Logger.Information("Begin. {@DebugInfo}", new Log.Logger.Information("Begin. {@DebugInfo}", new
{ {
AppName = EntryAssembly.GetName().Name, AppName = EntryAssembly.GetName().Name,
Version = BuildVersion.ToString(), Version = BuildVersion.ToString(),
ReleaseIdentifier = ReleaseIdentifier,
OS = os,
Mode = mode, Mode = mode,
LogLevel_Verbose_Enabled = Log.Logger.IsVerboseEnabled(), LogLevel_Verbose_Enabled = Log.Logger.IsVerboseEnabled(),
LogLevel_Debug_Enabled = Log.Logger.IsDebugEnabled(), LogLevel_Debug_Enabled = Log.Logger.IsDebugEnabled(),
@ -309,18 +335,10 @@ namespace AppScaffolding
LibraryCommands.BookUserDefinedItemCommitted += (_, books) => SearchEngineCommands.UpdateBooks(books); LibraryCommands.BookUserDefinedItemCommitted += (_, books) => SearchEngineCommands.UpdateBooks(books);
} }
public enum ReleaseIdentifier public static UpgradeProperties GetLatestRelease()
{
WindowsClassic,
WindowsAvalonia,
LinuxAvalonia,
MacOSAvalonia
}
public static UpgradeProperties GetLatestRelease(ReleaseIdentifier releaseID = ReleaseIdentifier.WindowsClassic)
{ {
// timed out // timed out
(var latest, var zip) = getLatestRelease(TimeSpan.FromSeconds(10), releaseID); (var latest, var zip) = getLatestRelease(TimeSpan.FromSeconds(10));
if (latest is null || zip is null) if (latest is null || zip is null)
return null; return null;
@ -346,11 +364,11 @@ namespace AppScaffolding
return new(zipUrl, latest.HtmlUrl, zip.Name, latestRelease); return new(zipUrl, latest.HtmlUrl, zip.Name, latestRelease);
} }
private static (Octokit.Release, Octokit.ReleaseAsset) getLatestRelease(TimeSpan timeout, ReleaseIdentifier releaseID) private static (Octokit.Release, Octokit.ReleaseAsset) getLatestRelease(TimeSpan timeout)
{ {
try try
{ {
var task = getLatestRelease(releaseID); var task = getLatestRelease();
if (task.Wait(timeout)) if (task.Wait(timeout))
return task.Result; return task.Result;
@ -362,7 +380,7 @@ namespace AppScaffolding
} }
return (null, null); return (null, null);
} }
private static async System.Threading.Tasks.Task<(Octokit.Release, Octokit.ReleaseAsset)> getLatestRelease(ReleaseIdentifier releaseID) private static async System.Threading.Tasks.Task<(Octokit.Release, Octokit.ReleaseAsset)> getLatestRelease()
{ {
var ownerAccount = "rmcrackan"; var ownerAccount = "rmcrackan";
var repoName = "Libation"; var repoName = "Libation";
@ -372,7 +390,7 @@ namespace AppScaffolding
//Download the release index //Download the release index
var bts = await gitHubClient.Repository.Content.GetRawContent(ownerAccount, repoName, ".releaseindex.json"); var bts = await gitHubClient.Repository.Content.GetRawContent(ownerAccount, repoName, ".releaseindex.json");
var releaseIndex = JObject.Parse(System.Text.Encoding.ASCII.GetString(bts)); var releaseIndex = JObject.Parse(System.Text.Encoding.ASCII.GetString(bts));
var regexPattern = releaseIndex.Value<string>(releaseID.ToString()); var regexPattern = releaseIndex.Value<string>(ReleaseIdentifier.ToString());
// https://octokitnet.readthedocs.io/en/latest/releases/ // https://octokitnet.readthedocs.io/en/latest/releases/
var releases = await gitHubClient.Repository.Release.GetAll(ownerAccount, repoName); var releases = await gitHubClient.Repository.Release.GetAll(ownerAccount, repoName);

View File

@ -6,6 +6,7 @@
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>hangover.ico</ApplicationIcon> <ApplicationIcon>hangover.ico</ApplicationIcon>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<IsPublishable>true</IsPublishable>
<PublishReadyToRun>true</PublishReadyToRun> <PublishReadyToRun>true</PublishReadyToRun>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
@ -54,4 +55,11 @@
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<Target Name="SpicNSpan" AfterTargets="Clean">
<!-- Remove obj folder -->
<RemoveDir Directories="$(BaseIntermediateOutputPath)" />
<!-- Remove bin folder -->
<RemoveDir Directories="$(BaseOutputPath)" />
</Target>
</Project> </Project>

View File

@ -12,5 +12,6 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained> <SelfContained>true</SelfContained>
<PublishSingleFile>false</PublishSingleFile> <PublishSingleFile>false</PublishSingleFile>
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -17,10 +17,16 @@ namespace LibationAvalonia
{ {
public class App : Application public class App : Application
{ {
public static bool IsWindows => PlatformID is PlatformID.Win32NT; public static readonly bool IsWindows;
public static bool IsUnix => PlatformID is PlatformID.Unix; public static readonly bool IsLinux;
public static readonly bool IsMacOs;
static App()
{
IsWindows = OperatingSystem.IsWindows();
IsLinux = OperatingSystem.IsLinux();
IsMacOs = OperatingSystem.IsMacOS();
}
public static readonly PlatformID PlatformID = Environment.OSVersion.Platform;
public static IBrush ProcessQueueBookFailedBrush { get; private set; } public static IBrush ProcessQueueBookFailedBrush { get; private set; }
public static IBrush ProcessQueueBookCompletedBrush { get; private set; } public static IBrush ProcessQueueBookCompletedBrush { get; private set; }
public static IBrush ProcessQueueBookCancelledBrush { get; private set; } public static IBrush ProcessQueueBookCancelledBrush { get; private set; }
@ -29,20 +35,20 @@ namespace LibationAvalonia
public static IAssetLoader AssetLoader { get; private set; } public static IAssetLoader AssetLoader { get; private set; }
public static readonly Uri AssetUriBase = new Uri("avares://Libation/Assets/"); public static readonly Uri AssetUriBase = new("avares://Libation/Assets/");
public static Stream OpenAsset(string assetRelativePath) public static Stream OpenAsset(string assetRelativePath)
=> AssetLoader.Open(new Uri(AssetUriBase, assetRelativePath)); => AssetLoader.Open(new Uri(AssetUriBase, assetRelativePath));
public static bool GoToFile(string path) public static bool GoToFile(string path)
=> PlatformID is PlatformID.Win32NT ? Go.To.File(path) => IsWindows ? Go.To.File(path)
: GoToFolder(path is null ? string.Empty : Path.GetDirectoryName(path)); : GoToFolder(path is null ? string.Empty : Path.GetDirectoryName(path));
public static bool GoToFolder(string path) public static bool GoToFolder(string path)
{ {
if (PlatformID is PlatformID.Win32NT) if (IsWindows)
return Go.To.Folder(path); return Go.To.Folder(path);
else else if (IsLinux)
{ {
var startInfo = new System.Diagnostics.ProcessStartInfo() var startInfo = new System.Diagnostics.ProcessStartInfo()
{ {
@ -56,6 +62,8 @@ namespace LibationAvalonia
System.Diagnostics.Process.Start(startInfo); System.Diagnostics.Process.Start(startInfo);
return true; return true;
} }
//Don't know how to do this for mac yet
else return true;
} }
public override void Initialize() public override void Initialize()
@ -107,7 +115,7 @@ namespace LibationAvalonia
base.OnFrameworkInitializationCompleted(); base.OnFrameworkInitializationCompleted();
} }
private void Setup_Closing(object sender, System.ComponentModel.CancelEventArgs e) private async void Setup_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{ {
var setupDialog = sender as SetupDialog; var setupDialog = sender as SetupDialog;
var desktop = ApplicationLifetime as IClassicDesktopStyleApplicationLifetime; var desktop = ApplicationLifetime as IClassicDesktopStyleApplicationLifetime;
@ -120,9 +128,9 @@ namespace LibationAvalonia
if ((!setupDialog.IsNewUser if ((!setupDialog.IsNewUser
&& !setupDialog.IsReturningUser) || && !setupDialog.IsReturningUser) ||
!RunInstall(setupDialog)) !await RunInstall(setupDialog))
{ {
CancelInstallation(); await CancelInstallation();
return; return;
} }
@ -130,7 +138,7 @@ namespace LibationAvalonia
// most migrations go in here // most migrations go in here
AppScaffolding.LibationScaffolding.RunPostConfigMigrations(setupDialog.Config); AppScaffolding.LibationScaffolding.RunPostConfigMigrations(setupDialog.Config);
MessageBox.VerboseLoggingWarning_ShowIfTrue(); await MessageBox.VerboseLoggingWarning_ShowIfTrue();
#if !DEBUG #if !DEBUG
//AutoUpdater.NET only works for WinForms or WPF application projects. //AutoUpdater.NET only works for WinForms or WPF application projects.
@ -146,11 +154,11 @@ namespace LibationAvalonia
var body = "An unrecoverable error occurred. Since this error happened before logging could be initialized, this error can not be written to the log file."; var body = "An unrecoverable error occurred. Since this error happened before logging could be initialized, this error can not be written to the log file.";
try try
{ {
MessageBox.ShowAdminAlert(null, body, title, ex); await MessageBox.ShowAdminAlert(null, body, title, ex);
} }
catch catch
{ {
MessageBox.Show($"{body}\r\n\r\n{ex.Message}\r\n\r\n{ex.StackTrace}", title, MessageBoxButtons.OK, MessageBoxIcon.Error); await MessageBox.Show($"{body}\r\n\r\n{ex.Message}\r\n\r\n{ex.StackTrace}", title, MessageBoxButtons.OK, MessageBoxIcon.Error);
} }
return; return;
} }
@ -160,7 +168,7 @@ namespace LibationAvalonia
ShowMainWindow(desktop); ShowMainWindow(desktop);
} }
private static bool RunInstall(SetupDialog setupDialog) private static async Task<bool> RunInstall(SetupDialog setupDialog)
{ {
var config = setupDialog.Config; var config = setupDialog.Config;
@ -173,7 +181,7 @@ namespace LibationAvalonia
var libationFilesDialog = new LibationFilesDialog(); var libationFilesDialog = new LibationFilesDialog();
if (libationFilesDialog.ShowDialogSynchronously<DialogResult>(setupDialog) != DialogResult.OK) if (await libationFilesDialog.ShowDialog<DialogResult>(setupDialog) != DialogResult.OK)
return false; return false;
config.SetLibationFiles(libationFilesDialog.SelectedDirectory); config.SetLibationFiles(libationFilesDialog.SelectedDirectory);
@ -181,7 +189,7 @@ namespace LibationAvalonia
return true; return true;
// path did not result in valid settings // path did not result in valid settings
var continueResult = MessageBox.Show( var continueResult = await MessageBox.Show(
$"No valid settings were found at this location.\r\nWould you like to create a new install settings in this folder?\r\n\r\n{libationFilesDialog.SelectedDirectory}", $"No valid settings were found at this location.\r\nWould you like to create a new install settings in this folder?\r\n\r\n{libationFilesDialog.SelectedDirectory}",
"New install?", "New install?",
MessageBoxButtons.YesNo, MessageBoxButtons.YesNo,
@ -196,13 +204,13 @@ namespace LibationAvalonia
config.Books ??= Path.Combine(Configuration.UserProfile, "Books"); config.Books ??= Path.Combine(Configuration.UserProfile, "Books");
AppScaffolding.LibationScaffolding.PopulateMissingConfigValues(config); AppScaffolding.LibationScaffolding.PopulateMissingConfigValues(config);
return new SettingsDialog().ShowDialogSynchronously<DialogResult>(setupDialog) == DialogResult.OK return await new SettingsDialog().ShowDialog<DialogResult>(setupDialog) == DialogResult.OK
&& config.LibationSettingsAreValid; && config.LibationSettingsAreValid;
} }
static void CancelInstallation() static async Task CancelInstallation()
{ {
MessageBox.Show("Initial set up cancelled.", "Cancelled", MessageBoxButtons.OK, MessageBoxIcon.Warning); await MessageBox.Show("Initial set up cancelled.", "Cancelled", MessageBoxButtons.OK, MessageBoxIcon.Warning);
Environment.Exit(0); Environment.Exit(0);
} }
@ -225,4 +233,4 @@ namespace LibationAvalonia
SeriesEntryGridBackgroundBrush = AvaloniaUtils.GetBrushFromResources("SeriesEntryGridBackgroundBrush"); SeriesEntryGridBackgroundBrush = AvaloniaUtils.GetBrushFromResources("SeriesEntryGridBackgroundBrush");
} }
} }
} }

View File

@ -1,30 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Avalonia.Threading
{
internal static class AvaloniaThreadUtils
{
public static TResult Invoke<TResult>(this Dispatcher dispatcher, Func<TResult> function, DispatcherPriority dispatcherPriority = DispatcherPriority.Normal)
=> WaitOnDispatcherAndGetResult(dispatcher.InvokeAsync(function, dispatcherPriority), dispatcher);
public static void Invoke(this Dispatcher dispatcher, Action action, DispatcherPriority dispatcherPriority = DispatcherPriority.Normal)
=> WaitOnDispatcher(dispatcher.InvokeAsync(action, dispatcherPriority), dispatcher);
public static TResult WaitOnDispatcherAndGetResult<TResult>(this Task<TResult> task, Dispatcher dispatcher)
{
using var source = new CancellationTokenSource();
task.ContinueWith(t => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext());
dispatcher.MainLoop(source.Token);
return task.Result;
}
public static void WaitOnDispatcher(this Task task, Dispatcher dispatcher)
{
using var source = new CancellationTokenSource();
task.ContinueWith(t => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext());
dispatcher.MainLoop(source.Token);
}
}
}

View File

@ -16,10 +16,5 @@ namespace LibationAvalonia
return brush; return brush;
return defaultBrush; return defaultBrush;
} }
public static T ShowDialogSynchronously<T>(this Avalonia.Controls.Window window, Avalonia.Controls.Window owner)
{
return window.ShowDialog<T>(owner).WaitOnDispatcherAndGetResult(Dispatcher.UIThread);
}
} }
} }

View File

@ -38,6 +38,7 @@ namespace LibationAvalonia.Controls
set => SetValue(SubDirectoryProperty, value); set => SetValue(SubDirectoryProperty, value);
} }
CustomState customStates = new(); CustomState customStates = new();
public DirectoryOrCustomSelectControl() public DirectoryOrCustomSelectControl()
{ {
InitializeComponent(); InitializeComponent();
@ -53,9 +54,8 @@ namespace LibationAvalonia.Controls
customDirBrowseBtn.Click += CustomDirBrowseBtn_Click; customDirBrowseBtn.Click += CustomDirBrowseBtn_Click;
PropertyChanged += DirectoryOrCustomSelectControl_PropertyChanged; PropertyChanged += DirectoryOrCustomSelectControl_PropertyChanged;
directorySelectControl.PropertyChanged += DirectorySelectControl_PropertyChanged; directorySelectControl.PropertyChanged += DirectorySelectControl_PropertyChanged;
} }
private class CustomState: ViewModels.ViewModelBase private class CustomState: ViewModels.ViewModelBase
{ {
private string _customDir; private string _customDir;
@ -116,12 +116,13 @@ namespace LibationAvalonia.Controls
private void setDirectory() private void setDirectory()
{ {
var path1 var selectedDir
= customStates.CustomChecked ? customStates.CustomDir = customStates.CustomChecked ? customStates.CustomDir
: directorySelectControl.SelectedDirectory is Configuration.KnownDirectories.AppDir ? Configuration.AppDir_Absolute : directorySelectControl.SelectedDirectory is Configuration.KnownDirectories.AppDir ? Configuration.AppDir_Absolute
: Configuration.GetKnownDirectoryPath(directorySelectControl.SelectedDirectory); : Configuration.GetKnownDirectoryPath(directorySelectControl.SelectedDirectory);
Directory selectedDir ??= string.Empty;
= System.IO.Path.Combine(path1 ?? string.Empty, SubDirectory);
Directory = customStates.CustomChecked ? selectedDir : System.IO.Path.Combine(selectedDir, SubDirectory);
} }
@ -140,7 +141,7 @@ namespace LibationAvalonia.Controls
if (known is Configuration.KnownDirectories.None) if (known is Configuration.KnownDirectories.None)
{ {
customStates.CustomChecked = true; customStates.CustomChecked = true;
customStates.CustomDir = noSubDir; customStates.CustomDir = directory;
} }
else else
{ {

View File

@ -136,7 +136,7 @@ namespace LibationAvalonia.Dialogs
if (persister.AccountsSettings.Accounts.Any(a => a.AccountId == account.AccountId && a.IdentityTokens.Locale.Name == account.Locale.Name)) if (persister.AccountsSettings.Accounts.Any(a => a.AccountId == account.AccountId && a.IdentityTokens.Locale.Name == account.Locale.Name))
{ {
MessageBox.Show(this, $"An account with that account id and country already exists.\r\n\r\nAccount ID: {account.AccountId}\r\nCountry: {account.Locale.Name}", "Cannot Add Duplicate Account"); await MessageBox.Show(this, $"An account with that account id and country already exists.\r\n\r\nAccount ID: {account.AccountId}\r\nCountry: {account.Locale.Name}", "Cannot Add Duplicate Account");
return; return;
} }
@ -146,7 +146,7 @@ namespace LibationAvalonia.Dialogs
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.ShowAdminAlert( await MessageBox.ShowAdminAlert(
this, 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{filePath[0]}\r\n\r\nIs the file encrypted?",
"Error Importing Account", "Error Importing Account",
@ -160,11 +160,11 @@ namespace LibationAvalonia.Dialogs
Export(acc); Export(acc);
} }
protected override void SaveAndClose() protected override async Task SaveAndCloseAsync()
{ {
try try
{ {
if (!inputIsValid()) if (!await inputIsValid())
return; return;
// without transaction, accounts persister will write ANY EDIT immediately to file // without transaction, accounts persister will write ANY EDIT immediately to file
@ -178,7 +178,7 @@ namespace LibationAvalonia.Dialogs
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.ShowAdminAlert(this, "Error attempting to save accounts", "Error saving accounts", ex); await MessageBox.ShowAdminAlert(this, "Error attempting to save accounts", "Error saving accounts", ex);
} }
} }
@ -221,7 +221,7 @@ namespace LibationAvalonia.Dialogs
: dto.AccountName.Trim(); : dto.AccountName.Trim();
} }
} }
private bool inputIsValid() private async Task<bool> inputIsValid()
{ {
foreach (var dto in Accounts.ToList()) foreach (var dto in Accounts.ToList())
{ {
@ -233,13 +233,13 @@ namespace LibationAvalonia.Dialogs
if (string.IsNullOrWhiteSpace(dto.AccountId)) if (string.IsNullOrWhiteSpace(dto.AccountId))
{ {
MessageBox.Show(this, "Account id cannot be blank. Please enter an account id for all accounts.", "Blank account", MessageBoxButtons.OK, MessageBoxIcon.Error); await MessageBox.Show(this, "Account id cannot be blank. Please enter an account id for all accounts.", "Blank account", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false; return false;
} }
if (string.IsNullOrWhiteSpace(dto.SelectedLocale?.Name)) if (string.IsNullOrWhiteSpace(dto.SelectedLocale?.Name))
{ {
MessageBox.Show(this, "Please select a locale (i.e.: country or region) for all accounts.", "Blank region", MessageBoxButtons.OK, MessageBoxIcon.Error); await MessageBox.Show(this, "Please select a locale (i.e.: country or region) for all accounts.", "Blank region", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false; return false;
} }
} }
@ -259,7 +259,7 @@ namespace LibationAvalonia.Dialogs
if (account.IdentityTokens?.IsValid != true) if (account.IdentityTokens?.IsValid != true)
{ {
MessageBox.Show(this, "This account hasn't been authenticated yet. First scan your library to log into your account, then try exporting again.", "Account Not Authenticated"); await MessageBox.Show(this, "This account hasn't been authenticated yet. First scan your library to log into your account, then try exporting again.", "Account Not Authenticated");
return; return;
} }
@ -282,11 +282,11 @@ namespace LibationAvalonia.Dialogs
File.WriteAllText(fileName, jsonText); File.WriteAllText(fileName, jsonText);
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{fileName}", "Success!");
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.ShowAdminAlert( await MessageBox.ShowAdminAlert(
this, this,
$"An error occurred while exporting account:\r\n{account.AccountName}", $"An error occurred while exporting account:\r\n{account.AccountName}",
"Error Exporting Account", "Error Exporting Account",

View File

@ -64,7 +64,7 @@ namespace LibationAvalonia.Dialogs
} }
protected override async Task SaveAndCloseAsync() protected override async Task SaveAndCloseAsync()
{ {
if (!_viewModel.Validate()) if (!await _viewModel.Validate())
return; return;
TemplateText = _viewModel.workingTemplateText; TemplateText = _viewModel.workingTemplateText;
@ -115,7 +115,7 @@ namespace LibationAvalonia.Dialogs
public void resetTextBox(string value) => workingTemplateText = value; public void resetTextBox(string value) => workingTemplateText = value;
public bool Validate() public async Task<bool> Validate()
{ {
if (template.IsValid(workingTemplateText)) if (template.IsValid(workingTemplateText))
return true; return true;
@ -123,7 +123,7 @@ namespace LibationAvalonia.Dialogs
.GetErrors(workingTemplateText) .GetErrors(workingTemplateText)
.Select(err => $"- {err}") .Select(err => $"- {err}")
.Aggregate((a, b) => $"{a}\r\n{b}"); .Aggregate((a, b) => $"{a}\r\n{b}");
MessageBox.Show($"This template text is not valid. Errors:\r\n{errors}", "Invalid", MessageBoxButtons.OK, MessageBoxIcon.Error); await MessageBox.Show($"This template text is not valid. Errors:\r\n{errors}", "Invalid", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false; return false;
} }

View File

@ -51,7 +51,7 @@ namespace LibationAvalonia.Dialogs
saveFileDialog.Filters.Add(new FileDialogFilter { Name = "Jpeg", Extensions = new System.Collections.Generic.List<string>() { "jpg" } }); saveFileDialog.Filters.Add(new FileDialogFilter { Name = "Jpeg", Extensions = new System.Collections.Generic.List<string>() { "jpg" } });
saveFileDialog.InitialFileName = PictureFileName; saveFileDialog.InitialFileName = PictureFileName;
saveFileDialog.Directory saveFileDialog.Directory
= App.IsUnix ? null = !App.IsWindows ? null
: Directory.Exists(BookSaveDirectory) ? BookSaveDirectory : Directory.Exists(BookSaveDirectory) ? BookSaveDirectory
: Path.GetDirectoryName(BookSaveDirectory); : Path.GetDirectoryName(BookSaveDirectory);
@ -67,7 +67,7 @@ namespace LibationAvalonia.Dialogs
catch (Exception ex) catch (Exception ex)
{ {
Serilog.Log.Logger.Error(ex, $"Failed to save picture to {fileName}"); Serilog.Log.Logger.Error(ex, $"Failed to save picture to {fileName}");
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); 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);
} }
} }

View File

@ -33,14 +33,14 @@ namespace LibationAvalonia.Dialogs
DataContext = dirSelectOptions = new(); DataContext = dirSelectOptions = new();
} }
public void SaveButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) public async void SaveButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{ {
var libationDir = dirSelectOptions.Directory; var libationDir = dirSelectOptions.Directory;
if (!System.IO.Directory.Exists(libationDir)) if (!System.IO.Directory.Exists(libationDir))
{ {
MessageBox.Show("Not saving change to Libation Files location. This folder does not exist:\r\n" + libationDir, "Folder does not exist", MessageBoxButtons.OK, MessageBoxIcon.Error, saveAndRestorePosition: false); await MessageBox.Show("Not saving change to Libation Files location. This folder does not exist:\r\n" + libationDir, "Folder does not exist", MessageBoxButtons.OK, MessageBoxIcon.Error, saveAndRestorePosition: false);
return; return;
} }

View File

@ -9,12 +9,12 @@ namespace LibationAvalonia.Dialogs.Login
{ {
/// <returns>True if ShowDialog's DialogResult == OK</returns> /// <returns>True if ShowDialog's DialogResult == OK</returns>
protected static bool ShowDialog(DialogWindow dialog) protected static async Task<bool> ShowDialog(DialogWindow dialog)
{ {
if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
return false; return false;
var result = dialog.ShowDialogSynchronously<DialogResult>(desktop.MainWindow); var result = await dialog.ShowDialog<DialogResult>(desktop.MainWindow);
Serilog.Log.Logger.Debug("{@DebugInfo}", new { DialogResult = result }); Serilog.Log.Logger.Debug("{@DebugInfo}", new { DialogResult = result });
return result == DialogResult.OK; return result == DialogResult.OK;
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Threading.Tasks;
using AudibleApi; using AudibleApi;
using AudibleUtilities; using AudibleUtilities;
@ -13,43 +14,43 @@ namespace LibationAvalonia.Dialogs.Login
_account = Dinah.Core.ArgumentValidator.EnsureNotNull(account, nameof(account)); _account = Dinah.Core.ArgumentValidator.EnsureNotNull(account, nameof(account));
} }
public string Get2faCode() public async Task<string> Get2faCodeAsync()
{ {
var dialog = new _2faCodeDialog(); var dialog = new _2faCodeDialog();
if (ShowDialog(dialog)) if (await ShowDialog(dialog))
return dialog.Code; return dialog.Code;
return null; return null;
} }
public string GetCaptchaAnswer(byte[] captchaImage) public async Task<string> GetCaptchaAnswerAsync(byte[] captchaImage)
{ {
var dialog = new CaptchaDialog(captchaImage); var dialog = new CaptchaDialog(captchaImage);
if (ShowDialog(dialog)) if (await ShowDialog(dialog))
return dialog.Answer; return dialog.Answer;
return null; return null;
} }
public (string name, string value) GetMfaChoice(MfaConfig mfaConfig) public async Task<(string name, string value)> GetMfaChoiceAsync(MfaConfig mfaConfig)
{ {
var dialog = new MfaDialog(mfaConfig); var dialog = new MfaDialog(mfaConfig);
if (ShowDialog(dialog)) if (await ShowDialog(dialog))
return (dialog.SelectedName, dialog.SelectedValue); return (dialog.SelectedName, dialog.SelectedValue);
return (null, null); return (null, null);
} }
public (string email, string password) GetLogin() public async Task<(string email, string password)> GetLoginAsync()
{ {
var dialog = new LoginCallbackDialog(_account); var dialog = new LoginCallbackDialog(_account);
if (ShowDialog(dialog)) if (await ShowDialog(dialog))
return (_account.AccountId, dialog.Password); return (_account.AccountId, dialog.Password);
return (null, null); return (null, null);
} }
public void ShowApprovalNeeded() public async Task ShowApprovalNeededAsync()
{ {
var dialog = new ApprovalNeededDialog(); var dialog = new ApprovalNeededDialog();
ShowDialog(dialog); await ShowDialog(dialog);
} }
} }
} }

View File

@ -20,11 +20,11 @@ namespace LibationAvalonia.Dialogs.Login
LoginCallback = new AvaloniaLoginCallback(_account); LoginCallback = new AvaloniaLoginCallback(_account);
} }
public ChoiceOut Start(ChoiceIn choiceIn) public async Task<ChoiceOut> StartAsync(ChoiceIn choiceIn)
{ {
var dialog = new LoginChoiceEagerDialog(_account); var dialog = new LoginChoiceEagerDialog(_account);
if (!ShowDialog(dialog)) if (!await ShowDialog(dialog))
return null; return null;
@ -33,15 +33,16 @@ namespace LibationAvalonia.Dialogs.Login
case LoginMethod.Api: case LoginMethod.Api:
return ChoiceOut.WithApi(dialog.Account.AccountId, dialog.Password); return ChoiceOut.WithApi(dialog.Account.AccountId, dialog.Password);
case LoginMethod.External: case LoginMethod.External:
{ {
var externalDialog = new LoginExternalDialog(_account, choiceIn.LoginUrl); var externalDialog = new LoginExternalDialog(_account, choiceIn.LoginUrl);
return ShowDialog(externalDialog) return await ShowDialog(externalDialog)
? ChoiceOut.External(externalDialog.ResponseUrl) ? ChoiceOut.External(externalDialog.ResponseUrl)
: null; : null;
} }
default: default:
throw new Exception($"Unknown {nameof(LoginMethod)} value"); throw new Exception($"Unknown {nameof(LoginMethod)} value");
} }
} }
} }
} }

View File

@ -51,7 +51,7 @@ namespace LibationAvalonia.Dialogs.Login
Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { ResponseUrl }); Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { ResponseUrl });
if (!Uri.TryCreate(ResponseUrl, UriKind.Absolute, out var result)) if (!Uri.TryCreate(ResponseUrl, UriKind.Absolute, out var result))
{ {
MessageBox.Show("Invalid response URL"); await MessageBox.Show("Invalid response URL");
return; return;
} }
await base.SaveAndCloseAsync(); await base.SaveAndCloseAsync();

View File

@ -82,7 +82,7 @@ namespace LibationAvalonia.Dialogs.Login
}); });
if (selected is null) if (selected is null)
{ {
MessageBox.Show("No MFA option selected", "None selected", MessageBoxButtons.OK, MessageBoxIcon.Error); await MessageBox.Show("No MFA option selected", "None selected", MessageBoxButtons.OK, MessageBoxIcon.Error);
return; return;
} }

View File

@ -28,7 +28,7 @@ namespace LibationAvalonia.Dialogs
DataContext = this; DataContext = this;
} }
private void GoToGithub_Tapped(object sender, Avalonia.Interactivity.RoutedEventArgs e) private async void GoToGithub_Tapped(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{ {
var url = "https://github.com/rmcrackan/Libation/issues"; var url = "https://github.com/rmcrackan/Libation/issues";
try try
@ -37,11 +37,11 @@ namespace LibationAvalonia.Dialogs
} }
catch catch
{ {
MessageBox.Show($"Error opening url\r\n{url}", "Error opening url", MessageBoxButtons.OK, MessageBoxIcon.Error); await MessageBox.Show($"Error opening url\r\n{url}", "Error opening url", MessageBoxButtons.OK, MessageBoxIcon.Error);
} }
} }
private void GoToLogs_Tapped(object sender, Avalonia.Interactivity.RoutedEventArgs e) private async void GoToLogs_Tapped(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{ {
LongPath dir = ""; LongPath dir = "";
try try
@ -56,7 +56,7 @@ namespace LibationAvalonia.Dialogs
} }
catch catch
{ {
MessageBox.Show($"Error opening folder\r\n{dir}", "Error opening folder", MessageBoxButtons.OK, MessageBoxIcon.Error); await MessageBox.Show($"Error opening folder\r\n{dir}", "Error opening folder", MessageBoxButtons.OK, MessageBoxIcon.Error);
} }
} }

View File

@ -33,10 +33,10 @@ namespace LibationAvalonia.Dialogs
protected override async Task SaveAndCloseAsync() protected override async Task SaveAndCloseAsync()
{ {
if (!settingsDisp.SaveSettings(config)) if (!await settingsDisp.SaveSettingsAsync(config))
return; return;
MessageBox.VerboseLoggingWarning_ShowIfTrue(); await MessageBox.VerboseLoggingWarning_ShowIfTrue();
await base.SaveAndCloseAsync(); await base.SaveAndCloseAsync();
} }
@ -97,7 +97,7 @@ namespace LibationAvalonia.Dialogs
internal interface ISettingsDisplay internal interface ISettingsDisplay
{ {
void LoadSettings(Configuration config); void LoadSettings(Configuration config);
bool SaveSettings(Configuration config); Task<bool> SaveSettingsAsync(Configuration config);
} }
public class SettingsPages : ISettingsDisplay public class SettingsPages : ISettingsDisplay
@ -120,12 +120,12 @@ namespace LibationAvalonia.Dialogs
AudioSettings = new(config); AudioSettings = new(config);
} }
public bool SaveSettings(Configuration config) public async Task<bool> SaveSettingsAsync(Configuration config)
{ {
var result = ImportantSettings.SaveSettings(config); var result = await ImportantSettings.SaveSettingsAsync(config);
result &= ImportSettings.SaveSettings(config); result &= await ImportSettings.SaveSettingsAsync(config);
result &= DownloadDecryptSettings.SaveSettings(config); result &= await DownloadDecryptSettings.SaveSettingsAsync(config);
result &= AudioSettings.SaveSettings(config); result &= await AudioSettings.SaveSettingsAsync(config);
return result; return result;
} }
@ -146,13 +146,13 @@ namespace LibationAvalonia.Dialogs
BetaOptIn = config.BetaOptIn; BetaOptIn = config.BetaOptIn;
} }
public bool SaveSettings(Configuration config) public async Task<bool> SaveSettingsAsync(Configuration config)
{ {
#region validation #region validation
if (string.IsNullOrWhiteSpace(BooksDirectory)) if (string.IsNullOrWhiteSpace(BooksDirectory))
{ {
MessageBox.Show("Cannot set Books Location to blank", "Location is blank", MessageBoxButtons.OK, MessageBoxIcon.Error); await MessageBox.Show("Cannot set Books Location to blank", "Location is blank", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false; return false;
} }
@ -204,14 +204,14 @@ namespace LibationAvalonia.Dialogs
AutoDownloadEpisodes = config.AutoDownloadEpisodes; AutoDownloadEpisodes = config.AutoDownloadEpisodes;
} }
public bool SaveSettings(Configuration config) public Task<bool> SaveSettingsAsync(Configuration config)
{ {
config.AutoScan = AutoScan; config.AutoScan = AutoScan;
config.ShowImportedStats = ShowImportedStats; config.ShowImportedStats = ShowImportedStats;
config.ImportEpisodes = ImportEpisodes; config.ImportEpisodes = ImportEpisodes;
config.DownloadEpisodes = DownloadEpisodes; config.DownloadEpisodes = DownloadEpisodes;
config.AutoDownloadEpisodes = AutoDownloadEpisodes; config.AutoDownloadEpisodes = AutoDownloadEpisodes;
return true; return Task.FromResult(true);
} }
public string AutoScanText { get; } = Configuration.GetDescription(nameof(Configuration.AutoScan)); public string AutoScanText { get; } = Configuration.GetDescription(nameof(Configuration.AutoScan));
@ -259,25 +259,25 @@ namespace LibationAvalonia.Dialogs
: Configuration.GetKnownDirectory(config.InProgress); : Configuration.GetKnownDirectory(config.InProgress);
} }
public bool SaveSettings(Configuration config) public async Task<bool> SaveSettingsAsync(Configuration config)
{ {
static void validationError(string text, string caption) static Task validationError(string text, string caption)
=> MessageBox.Show(text, caption, MessageBoxButtons.OK, MessageBoxIcon.Error); => MessageBox.Show(text, caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
// these 3 should do nothing. Configuration will only init these with a valid value. EditTemplateDialog ensures valid before returning // these 3 should do nothing. Configuration will only init these with a valid value. EditTemplateDialog ensures valid before returning
if (!Templates.Folder.IsValid(FolderTemplate)) if (!Templates.Folder.IsValid(FolderTemplate))
{ {
validationError($"Not saving change to folder naming template. Invalid format.", "Invalid folder template"); await validationError($"Not saving change to folder naming template. Invalid format.", "Invalid folder template");
return false; return false;
} }
if (!Templates.File.IsValid(FileTemplate)) if (!Templates.File.IsValid(FileTemplate))
{ {
validationError($"Not saving change to file naming template. Invalid format.", "Invalid file template"); await validationError($"Not saving change to file naming template. Invalid format.", "Invalid file template");
return false; return false;
} }
if (!Templates.ChapterFile.IsValid(ChapterFileTemplate)) if (!Templates.ChapterFile.IsValid(ChapterFileTemplate))
{ {
validationError($"Not saving change to chapter file naming template. Invalid format.", "Invalid chapter file template"); await validationError($"Not saving change to chapter file naming template. Invalid format.", "Invalid chapter file template");
return false; return false;
} }
@ -405,7 +405,7 @@ namespace LibationAvalonia.Dialogs
LameVBRQuality = config.LameVBRQuality; LameVBRQuality = config.LameVBRQuality;
} }
public bool SaveSettings(Configuration config) public Task<bool> SaveSettingsAsync(Configuration config)
{ {
config.CreateCueSheet = CreateCueSheet; config.CreateCueSheet = CreateCueSheet;
config.AllowLibationFixup = AllowLibationFixup; config.AllowLibationFixup = AllowLibationFixup;
@ -424,7 +424,7 @@ namespace LibationAvalonia.Dialogs
config.LameBitrate = LameBitrate; config.LameBitrate = LameBitrate;
config.LameVBRQuality = LameVBRQuality; config.LameVBRQuality = LameVBRQuality;
return true; return Task.FromResult(true);
} }
public string CreateCueSheetText { get; } = Configuration.GetDescription(nameof(Configuration.CreateCueSheet)); public string CreateCueSheetText { get; } = Configuration.GetDescription(nameof(Configuration.CreateCueSheet));

View File

@ -113,7 +113,7 @@ namespace LibationAvalonia
public static void HideMinMaxBtns(this Window form) public static void HideMinMaxBtns(this Window form)
{ {
if (Design.IsDesignMode || App.PlatformID is not PlatformID.Win32NT) if (Design.IsDesignMode || !App.IsWindows)
return; return;
var handle = form.PlatformImpl.Handle.Handle; var handle = form.PlatformImpl.Handle.Handle;
var currentStyle = GetWindowLong(handle, GWL_STYLE); var currentStyle = GetWindowLong(handle, GWL_STYLE);

View File

@ -10,6 +10,8 @@
<ApplicationIcon>libation.ico</ApplicationIcon> <ApplicationIcon>libation.ico</ApplicationIcon>
<AssemblyName>Libation</AssemblyName> <AssemblyName>Libation</AssemblyName>
<IsPublishable>true</IsPublishable>
<PublishReadyToRun>true</PublishReadyToRun> <PublishReadyToRun>true</PublishReadyToRun>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath> <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
@ -81,8 +83,6 @@
<None Remove="Assets\SEGOEUI.TTF" /> <None Remove="Assets\SEGOEUI.TTF" />
<None Remove="Assets\up.png" /> <None Remove="Assets\up.png" />
<None Remove="Assets\WINGDING.TTF" /> <None Remove="Assets\WINGDING.TTF" />
<None Remove="MessageBox.cs~RF105afb8d.TMP" />
<None Remove="Views\MainWindow\MainWindow.Export.axaml.cs~RF10732d95.TMP" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -144,4 +144,12 @@
</None> </None>
</ItemGroup> </ItemGroup>
<Target Name="SpicNSpan" AfterTargets="Clean">
<!-- Remove obj folder -->
<RemoveDir Directories="$(BaseIntermediateOutputPath)" />
<!-- Remove bin folder -->
<RemoveDir Directories="$(BaseOutputPath)" />
</Target>
</Project> </Project>

View File

@ -62,34 +62,34 @@ namespace LibationAvalonia
public class MessageBox public class MessageBox
{ {
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton) public static Task<DialogResult> Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
=> ShowCoreAsync(null, text, caption, buttons, icon, defaultButton); => ShowCoreAsync(null, text, caption, buttons, icon, defaultButton);
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, bool saveAndRestorePosition = true) public static Task<DialogResult> Show(string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, bool saveAndRestorePosition = true)
=> ShowCoreAsync(null, text, caption, buttons, icon, MessageBoxDefaultButton.Button1, saveAndRestorePosition); => ShowCoreAsync(null, text, caption, buttons, icon, MessageBoxDefaultButton.Button1, saveAndRestorePosition);
public static DialogResult Show(string text, string caption, MessageBoxButtons buttons) public static Task<DialogResult> Show(string text, string caption, MessageBoxButtons buttons)
=> ShowCoreAsync(null, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); => ShowCoreAsync(null, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
public static DialogResult Show(string text, string caption) public static Task<DialogResult> Show(string text, string caption)
=> ShowCoreAsync(null, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); => ShowCoreAsync(null, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
public static DialogResult Show(string text) public static Task<DialogResult> Show(string text)
=> ShowCoreAsync(null, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); => ShowCoreAsync(null, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
public static DialogResult Show(Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton) public static Task<DialogResult> Show(Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
=> ShowCoreAsync(owner, text, caption, buttons, icon, defaultButton); => ShowCoreAsync(owner, text, caption, buttons, icon, defaultButton);
public static DialogResult Show(Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon) public static Task<DialogResult> Show(Window owner, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon)
=> ShowCoreAsync(owner, text, caption, buttons, icon, MessageBoxDefaultButton.Button1); => ShowCoreAsync(owner, text, caption, buttons, icon, MessageBoxDefaultButton.Button1);
public static DialogResult Show(Window owner, string text, string caption, MessageBoxButtons buttons) public static Task<DialogResult> Show(Window owner, string text, string caption, MessageBoxButtons buttons)
=> ShowCoreAsync(owner, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); => ShowCoreAsync(owner, text, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
public static DialogResult Show(Window owner, string text, string caption) public static Task<DialogResult> Show(Window owner, string text, string caption)
=> ShowCoreAsync(owner, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); => ShowCoreAsync(owner, text, caption, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
public static DialogResult Show(Window owner, string text) public static Task<DialogResult> Show(Window owner, string text)
=> ShowCoreAsync(owner, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); => ShowCoreAsync(owner, text, string.Empty, MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
public static void VerboseLoggingWarning_ShowIfTrue() public static async Task VerboseLoggingWarning_ShowIfTrue()
{ {
// when turning on debug (and especially Verbose) to share logs, some privacy settings may not be obscured // when turning on debug (and especially Verbose) to share logs, some privacy settings may not be obscured
if (Serilog.Log.Logger.IsVerboseEnabled()) if (Serilog.Log.Logger.IsVerboseEnabled())
Show(@" await Show(@"
Warning: verbose logging is enabled. Warning: verbose logging is enabled.
This should be used for debugging only. It creates many This should be used for debugging only. It creates many
@ -102,7 +102,7 @@ Libation.
".Trim(), "Verbose logging enabled", MessageBoxButtons.OK, MessageBoxIcon.Warning); ".Trim(), "Verbose logging enabled", MessageBoxButtons.OK, MessageBoxIcon.Warning);
} }
public static DialogResult ShowConfirmationDialog(Window owner, IEnumerable<LibraryBook> libraryBooks, string format, string title, MessageBoxDefaultButton defaultButton = MessageBoxDefaultButton.Button1) public static async Task<DialogResult> ShowConfirmationDialog(Window owner, IEnumerable<LibraryBook> libraryBooks, string format, string title, MessageBoxDefaultButton defaultButton = MessageBoxDefaultButton.Button1)
{ {
if (libraryBooks is null || !libraryBooks.Any()) if (libraryBooks is null || !libraryBooks.Any())
return DialogResult.Cancel; return DialogResult.Cancel;
@ -117,7 +117,7 @@ Libation.
= string.Format(format, $"{thisThese} {count} {bookBooks}") = string.Format(format, $"{thisThese} {count} {bookBooks}")
+ $"\r\n\r\n{titlesAgg}"; + $"\r\n\r\n{titlesAgg}";
return ShowCoreAsync(owner, return await ShowCoreAsync(owner,
message, message,
title, title,
MessageBoxButtons.YesNo, MessageBoxButtons.YesNo,
@ -132,7 +132,7 @@ Libation.
/// <param name="text">The text to display in the message box.</param> /// <param name="text">The text to display in the message box.</param>
/// <param name="caption">The text to display in the title bar of the message box.</param> /// <param name="caption">The text to display in the title bar of the message box.</param>
/// <param name="exception">Exception to log.</param> /// <param name="exception">Exception to log.</param>
public static void ShowAdminAlert(Window owner, string text, string caption, Exception exception) public static async Task ShowAdminAlert(Window owner, string text, string caption, Exception exception)
{ {
// for development and debugging, show me what broke! // for development and debugging, show me what broke!
if (System.Diagnostics.Debugger.IsAttached) if (System.Diagnostics.Debugger.IsAttached)
@ -146,14 +146,14 @@ Libation.
var form = new MessageBoxAlertAdminDialog(text, caption, exception); var form = new MessageBoxAlertAdminDialog(text, caption, exception);
DisplayWindow(form, owner); await DisplayWindow(form, owner);
} }
private static DialogResult ShowCoreAsync(Window owner, string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, bool saveAndRestorePosition = true) private static async Task<DialogResult> ShowCoreAsync(Window owner, string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, bool saveAndRestorePosition = true)
{ {
var dialog = Dispatcher.UIThread.Invoke(() => CreateMessageBox(owner, message, caption, buttons, icon, defaultButton, saveAndRestorePosition)); var dialog = await Dispatcher.UIThread.InvokeAsync(() => CreateMessageBox(owner, message, caption, buttons, icon, defaultButton, saveAndRestorePosition));
return DisplayWindow(dialog, owner); return await DisplayWindow(dialog, owner);
} }
private static MessageBoxWindow CreateMessageBox(Window owner, string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, bool saveAndRestorePosition = true) private static MessageBoxWindow CreateMessageBox(Window owner, string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, bool saveAndRestorePosition = true)
@ -192,13 +192,13 @@ Libation.
dialog.Width = dialog.MinWidth; dialog.Width = dialog.MinWidth;
return dialog; return dialog;
} }
private static DialogResult DisplayWindow(Window toDisplay, Window owner) private static async Task<DialogResult> DisplayWindow(Window toDisplay, Window owner)
{ {
if (owner is null) if (owner is null)
{ {
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
return toDisplay.ShowDialogSynchronously<DialogResult>(desktop.MainWindow); return await toDisplay.ShowDialog<DialogResult>(desktop.MainWindow);
} }
else else
{ {
@ -212,7 +212,7 @@ Libation.
}; };
window.Show(); window.Show();
var result = toDisplay.ShowDialogSynchronously<DialogResult>(window); var result = await toDisplay.ShowDialog<DialogResult>(window);
window.Close(); window.Close();
return result; return result;
} }
@ -220,7 +220,7 @@ Libation.
} }
else else
{ {
return toDisplay.ShowDialogSynchronously<DialogResult>(owner); return await toDisplay.ShowDialog<DialogResult>(owner);
} }
} }

View File

@ -14,7 +14,7 @@ namespace LibationAvalonia
{ {
private static string EXE_DIR = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); private static string EXE_DIR = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
static async Task Main() static void Main()
{ {
//***********************************************// //***********************************************//
// // // //
@ -30,6 +30,14 @@ namespace LibationAvalonia
var classicLifetimeTask = Task.Run(() => new ClassicDesktopStyleApplicationLifetime()); var classicLifetimeTask = Task.Run(() => new ClassicDesktopStyleApplicationLifetime());
var appBuilderTask = Task.Run(BuildAvaloniaApp); var appBuilderTask = Task.Run(BuildAvaloniaApp);
if (App.IsWindows)
AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.WindowsAvalonia);
else if (App.IsLinux)
AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.LinuxAvalonia);
else if (App.IsMacOs)
AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.MacOSAvalonia);
else return;
if (!App.SetupRequired) if (!App.SetupRequired)
{ {
@ -39,9 +47,7 @@ namespace LibationAvalonia
App.LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true)); App.LibraryTask = Task.Run(() => DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
} }
(appBuilderTask.GetAwaiter().GetResult()).SetupWithLifetime(classicLifetimeTask.GetAwaiter().GetResult());
(await appBuilderTask).SetupWithLifetime(await classicLifetimeTask);
classicLifetimeTask.Result.Start(null); classicLifetimeTask.Result.Start(null);
} }

View File

@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup> <PropertyGroup>
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>Any CPU</Platform> <Platform>Any CPU</Platform>
<PublishDir>..\bin-Avalonia\publish\linux-x64\</PublishDir> <PublishDir>..\bin\Release\linux-chardonnay</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol> <PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier> <RuntimeIdentifier>linux-x64</RuntimeIdentifier>

View File

@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup> <PropertyGroup>
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>Any CPU</Platform> <Platform>Any CPU</Platform>
<PublishDir>..\bin-Avalonia\publish\osx-x64\</PublishDir> <PublishDir>..\bin\Release\macos-chardonnay</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol> <PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifier>osx-x64</RuntimeIdentifier> <RuntimeIdentifier>osx-x64</RuntimeIdentifier>

View File

@ -6,8 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup> <PropertyGroup>
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>Any CPU</Platform> <Platform>Any CPU</Platform>
<!-- since Windows Winforms and Avalonia both compile to "Libation.exe", they can't both default to "bin\publish\" or else they'll overwrite each other --> <PublishDir>..\bin\Release\win-chardonnay</PublishDir>
<PublishDir>..\bin\publish\win-avalonia-x64\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol> <PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>

View File

@ -152,7 +152,7 @@ namespace LibationAvalonia.ViewModels
finally finally
{ {
if (Result == ProcessBookResult.None) if (Result == ProcessBookResult.None)
Result = showRetry(LibraryBook); Result = await showRetry(LibraryBook);
Status = Result switch Status = Result switch
{ {
@ -313,7 +313,7 @@ namespace LibationAvalonia.ViewModels
#region Failure Handler #region Failure Handler
private ProcessBookResult showRetry(LibraryBook libraryBook) private async Task<ProcessBookResult> showRetry(LibraryBook libraryBook)
{ {
Logger.Error("ERROR. All books have not been processed. Most recent book: processing failed"); Logger.Error("ERROR. All books have not been processed. Most recent book: processing failed");
@ -346,7 +346,7 @@ $@" Title: {libraryBook.Book.Title}
} }
// if null then ask user // if null then ask user
dialogResult ??= MessageBox.Show(string.Format(SkipDialogText + "\r\n\r\nSee Settings to avoid this box in the future.", details), "Skip importing this book?", SkipDialogButtons, MessageBoxIcon.Question, SkipDialogDefaultButton); dialogResult ??= await MessageBox.Show(string.Format(SkipDialogText + "\r\n\r\nSee Settings to avoid this box in the future.", details), "Skip importing this book?", SkipDialogButtons, MessageBoxIcon.Question, SkipDialogDefaultButton);
if (dialogResult == DialogResult.Abort) if (dialogResult == DialogResult.Abort)
return ProcessBookResult.FailedAbort; return ProcessBookResult.FailedAbort;

View File

@ -244,7 +244,7 @@ namespace LibationAvalonia.ViewModels
return; return;
var libraryBooks = selectedBooks.Select(rge => rge.LibraryBook).ToList(); var libraryBooks = selectedBooks.Select(rge => rge.LibraryBook).ToList();
var result = MessageBox.ShowConfirmationDialog( var result = await MessageBox.ShowConfirmationDialog(
null, null,
libraryBooks, libraryBooks,
$"Are you sure you want to remove {selectedBooks.Count} books from Libation's library?", $"Are you sure you want to remove {selectedBooks.Count} books from Libation's library?",
@ -317,7 +317,7 @@ namespace LibationAvalonia.ViewModels
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.ShowAdminAlert( await MessageBox.ShowAdminAlert(
null, null,
"Error scanning library. You may still manually select books to remove from Libation's library.", "Error scanning library. You may still manually select books to remove from Libation's library.",
"Error scanning library", "Error scanning library",

View File

@ -41,11 +41,11 @@ namespace LibationAvalonia.Views
break; break;
} }
MessageBox.Show("Library exported to:\r\n" + fileName, "Library Exported"); await MessageBox.Show("Library exported to:\r\n" + fileName, "Library Exported");
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.ShowAdminAlert(this, "Error attempting to export your library.", "Error exporting", ex); await MessageBox.ShowAdminAlert(this, "Error attempting to export your library.", "Error exporting", ex);
} }
} }
} }

View File

@ -39,7 +39,7 @@ namespace LibationAvalonia.Views
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.Show($"Bad filter string:\r\n\r\n{ex.Message}", "Bad filter string", MessageBoxButtons.OK, MessageBoxIcon.Error); await MessageBox.Show($"Bad filter string:\r\n\r\n{ex.Message}", "Bad filter string", MessageBoxButtons.OK, MessageBoxIcon.Error);
// re-apply last good filter // re-apply last good filter
await performFilter(lastGoodFilter); await performFilter(lastGoodFilter);

View File

@ -40,7 +40,7 @@ namespace LibationAvalonia.Views
public async void convertAllM4bToMp3ToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args) public async void convertAllM4bToMp3ToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
{ {
var result = MessageBox.Show( var result = await MessageBox.Show(
"This converts all m4b titles in your library to mp3 files. Original files are not deleted." "This converts all m4b titles in your library to mp3 files. Original files are not deleted."
+ "\r\nFor large libraries this will take a long time and will take up more disk space." + "\r\nFor large libraries this will take a long time and will take up more disk space."
+ "\r\n\r\nContinue?" + "\r\n\r\nContinue?"

View File

@ -15,7 +15,7 @@ namespace LibationAvalonia.Views
SetQueueCollapseState(collapseState); SetQueueCollapseState(collapseState);
} }
public void ProductsDisplay_LiberateClicked(object sender, LibraryBook libraryBook) public async void ProductsDisplay_LiberateClicked(object sender, LibraryBook libraryBook)
{ {
try try
{ {
@ -39,7 +39,7 @@ namespace LibationAvalonia.Views
if (!App.GoToFile(filePath?.ShortPathName)) if (!App.GoToFile(filePath?.ShortPathName))
{ {
var suffix = string.IsNullOrWhiteSpace(filePath) ? "" : $":\r\n{filePath}"; var suffix = string.IsNullOrWhiteSpace(filePath) ? "" : $":\r\n{filePath}";
MessageBox.Show($"File not found" + suffix); await MessageBox.Show($"File not found" + suffix);
} }
} }
} }

View File

@ -26,7 +26,7 @@ namespace LibationAvalonia.Views
public async void noAccountsYetAddAccountToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) public async void noAccountsYetAddAccountToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{ {
MessageBox.Show("To load your Audible library, come back here to the Import menu after adding your account"); await MessageBox.Show("To load your Audible library, come back here to the Import menu after adding your account");
await new Dialogs.AccountsDialog().ShowDialog(this); await new Dialogs.AccountsDialog().ShowDialog(this);
} }
@ -66,11 +66,11 @@ namespace LibationAvalonia.Views
// this is here instead of ScanEnd so that the following is only possible when it's user-initiated, not automatic loop // this is here instead of ScanEnd so that the following is only possible when it's user-initiated, not automatic loop
if (Configuration.Instance.ShowImportedStats && newAdded > 0) if (Configuration.Instance.ShowImportedStats && newAdded > 0)
MessageBox.Show($"Total processed: {totalProcessed}\r\nNew: {newAdded}"); await MessageBox.Show($"Total processed: {totalProcessed}\r\nNew: {newAdded}");
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.ShowAdminAlert( await MessageBox.ShowAdminAlert(
this, this,
"Error importing library. Please try again. If this still happens after 2 or 3 tries, stop and contact administrator", "Error importing library. Please try again. If this still happens after 2 or 3 tries, stop and contact administrator",
"Error importing library", "Error importing library",

View File

@ -8,11 +8,13 @@ namespace LibationAvalonia.Views
{ {
private void Configure_Settings() { } private void Configure_Settings() { }
public async void accountsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) => await new Dialogs.AccountsDialog().ShowDialog(this); public async void accountsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
=> await new Dialogs.AccountsDialog().ShowDialog(this);
public async void basicSettingsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) => await new Dialogs.SettingsDialog().ShowDialog(this); public async void basicSettingsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
=> await new Dialogs.SettingsDialog().ShowDialog(this);
public void aboutToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) public async void aboutToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
=> MessageBox.Show($"Running Libation version {AppScaffolding.LibationScaffolding.BuildVersion}", $"Libation v{AppScaffolding.LibationScaffolding.BuildVersion}"); => await MessageBox.Show($"Running Libation version {AppScaffolding.LibationScaffolding.BuildVersion}", $"Libation v{AppScaffolding.LibationScaffolding.BuildVersion}");
} }
} }

View File

@ -47,7 +47,7 @@ namespace LibationAvalonia.Views
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries(); var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
var confirmationResult = MessageBox.ShowConfirmationDialog( var confirmationResult = await MessageBox.ShowConfirmationDialog(
this, this,
visibleLibraryBooks, visibleLibraryBooks,
"Are you sure you want to replace tags in {0}?", "Are you sure you want to replace tags in {0}?",
@ -70,7 +70,7 @@ namespace LibationAvalonia.Views
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries(); var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
var confirmationResult = MessageBox.ShowConfirmationDialog( var confirmationResult = await MessageBox.ShowConfirmationDialog(
this, this,
visibleLibraryBooks, visibleLibraryBooks,
"Are you sure you want to replace downloaded status in {0}?", "Are you sure you want to replace downloaded status in {0}?",
@ -88,7 +88,7 @@ namespace LibationAvalonia.Views
{ {
var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries(); var visibleLibraryBooks = _viewModel.ProductsDisplay.GetVisibleBookEntries();
var confirmationResult = MessageBox.ShowConfirmationDialog( var confirmationResult = await MessageBox.ShowConfirmationDialog(
this, this,
visibleLibraryBooks, visibleLibraryBooks,
"Are you sure you want to remove {0} from Libation's library?", "Are you sure you want to remove {0} from Libation's library?",

View File

@ -9,6 +9,7 @@ using LibationFileManager;
using DataLayer; using DataLayer;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using AppScaffolding;
namespace LibationAvalonia.Views namespace LibationAvalonia.Views
{ {
@ -53,7 +54,7 @@ namespace LibationAvalonia.Views
this.LibraryLoaded += MainWindow_LibraryLoaded; this.LibraryLoaded += MainWindow_LibraryLoaded;
LibraryCommands.LibrarySizeChanged += async (_, _) => await _viewModel.ProductsDisplay.DisplayBooks(DbContexts.GetLibrary_Flat_NoTracking(includeParents: true)); LibraryCommands.LibrarySizeChanged += async (_, _) => await _viewModel.ProductsDisplay.DisplayBooks(DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
Closing += (_,_) => this.SaveSizeAndLocation(Configuration.Instance); Closing += (_, _) => this.SaveSizeAndLocation(Configuration.Instance);
} }
Opened += MainWindow_Opened; Opened += MainWindow_Opened;
Closing += MainWindow_Closing; Closing += MainWindow_Closing;
@ -67,50 +68,72 @@ namespace LibationAvalonia.Views
private async void MainWindow_Opened(object sender, EventArgs e) private async void MainWindow_Opened(object sender, EventArgs e)
{ {
#if !DEBUG #if !DEBUG
if (App.IsWindows) //This is temporaty until we have a solution for linux/mac so that
//Libation doesn't download a zip every time it runs.
if (!App.IsWindows)
return;
try
{ {
try (string zipFile, UpgradeProperties upgradeProperties) = await Task.Run(() => downloadUpdate());
{
await Task.Run(checkForAndDownloadUpdate); if (string.IsNullOrEmpty(zipFile) || !System.IO.File.Exists(zipFile))
}
catch(Exception ex)
{
Serilog.Log.Logger.Error(ex, "An error occured while checking for app updates.");
return; return;
var result = await MessageBox.Show($"{upgradeProperties.HtmlUrl}\r\n\r\nWould you like to upgrade now?", "New version available", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);
if (result != DialogResult.Yes)
return;
if (App.IsWindows)
{
runWindowsUpgrader(zipFile);
} }
else if (App.IsLinux)
{
}
else if (App.IsMacOs)
{
}
}
catch (Exception ex)
{
Serilog.Log.Logger.Error(ex, "An error occured while checking for app updates.");
return;
} }
#endif #endif
} }
private async Task checkForAndDownloadUpdate() private async Task<(string zipFile, UpgradeProperties release)> downloadUpdate()
{ {
AppScaffolding.UpgradeProperties upgradeProperties; UpgradeProperties upgradeProperties;
try try
{ {
upgradeProperties = AppScaffolding.LibationScaffolding.GetLatestRelease(AppScaffolding.LibationScaffolding.ReleaseIdentifier.WindowsAvalonia); upgradeProperties = LibationScaffolding.GetLatestRelease();
if (upgradeProperties is null) if (upgradeProperties is null)
return; return (null,null);
} }
catch (Exception ex) catch (Exception ex)
{ {
Serilog.Log.Logger.Error(ex, "Failed to check for update"); Serilog.Log.Logger.Error(ex, "Failed to check for update");
return; return (null, null);
} }
if (upgradeProperties.ZipUrl is null) if (upgradeProperties.ZipUrl is null)
{ {
Serilog.Log.Logger.Information("Download link for new version not found"); Serilog.Log.Logger.Information("Download link for new version not found");
return; return (null, null);
} }
//Silently download the update in the background, save it to a temp file. //Silently download the update in the background, save it to a temp file.
var zipPath = System.IO.Path.GetTempFileName(); var zipFile = System.IO.Path.GetTempFileName();
try try
{ {
System.Net.Http.HttpClient cli = new(); System.Net.Http.HttpClient cli = new();
using (var fs = System.IO.File.OpenWrite(zipPath)) using (var fs = System.IO.File.OpenWrite(zipFile))
{ {
using (var dlStream = await cli.GetStreamAsync(new Uri(upgradeProperties.ZipUrl))) using (var dlStream = await cli.GetStreamAsync(new Uri(upgradeProperties.ZipUrl)))
await dlStream.CopyToAsync(fs); await dlStream.CopyToAsync(fs);
@ -119,18 +142,18 @@ namespace LibationAvalonia.Views
catch (Exception ex) catch (Exception ex)
{ {
Serilog.Log.Logger.Error(ex, "Failed to download the update: {pdate}", upgradeProperties.ZipUrl); Serilog.Log.Logger.Error(ex, "Failed to download the update: {pdate}", upgradeProperties.ZipUrl);
return; return (null, null);
} }
return (zipFile, upgradeProperties);
}
var result = MessageBox.Show($"{upgradeProperties.HtmlUrl}\r\n\r\nWould you like to upgrade now?", "New version available", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); private void runWindowsUpgrader(string zipFile)
{
if (result != DialogResult.Yes)
return;
var thisExe = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; var thisExe = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
var thisDir = System.IO.Path.GetDirectoryName(thisExe); var thisDir = System.IO.Path.GetDirectoryName(thisExe);
var args = $"--input {zipPath} --output {thisDir} --executable {thisExe}"; var args = $"--input {zipFile} --output {thisDir} --executable {thisExe}";
var zipExtractor = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "ZipExtractor.exe"); var zipExtractor = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "ZipExtractor.exe");
@ -147,7 +170,7 @@ namespace LibationAvalonia.Views
}; };
System.Diagnostics.Process.Start(psi); System.Diagnostics.Process.Start(psi);
Environment.Exit(0); Environment.Exit(0);
} }
public void ProductsDisplay_Initialized1(object sender, EventArgs e) public void ProductsDisplay_Initialized1(object sender, EventArgs e)

View File

@ -1,27 +1,26 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<PublishReadyToRun>true</PublishReadyToRun> <PublishReadyToRun>true</PublishReadyToRun>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath> <IsPublishable>True</IsPublishable>
<IsPublishable>True</IsPublishable> </PropertyGroup>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<!-- <!--
HACK FOR COMPILER BUG 2021-09-14. Hopefully will be fixed in future versions HACK FOR COMPILER BUG 2021-09-14. Hopefully will be fixed in future versions
- Not using SatelliteResourceLanguages will load all language packs: works - Not using SatelliteResourceLanguages will load all language packs: works
- Specifying 'en' semicolon 1 more should load 1 language pack: works - Specifying 'en' semicolon 1 more should load 1 language pack: works
- Specifying only 'en' should load no language packs: broken, still loads all - Specifying only 'en' should load no language packs: broken, still loads all
--> -->
<SatelliteResourceLanguages>en;es</SatelliteResourceLanguages> <SatelliteResourceLanguages>en;es</SatelliteResourceLanguages>
</PropertyGroup> </PropertyGroup>
<!-- <!--
When LibationWinForms and LibationCli output to the same dir, LibationCli must build before LibationWinForms When LibationWinForms and LibationCli output to the same dir, LibationCli must build before LibationWinForms
VS > rt-clk solution > Properties VS > rt-clk solution > Properties
@ -31,24 +30,31 @@
edit debug and release output paths edit debug and release output paths
--> -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>..\bin\Debug</OutputPath> <OutputPath>..\bin\Debug</OutputPath>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<OutputPath>..\bin\Release</OutputPath> <OutputPath>..\bin\Release</OutputPath>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" /> <PackageReference Include="CommandLineParser" Version="2.9.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <Target Name="SpicNSpan" AfterTargets="Clean">
<ProjectReference Include="..\ApplicationServices\ApplicationServices.csproj" /> <!-- Remove obj folder -->
<ProjectReference Include="..\AppScaffolding\AppScaffolding.csproj" /> <RemoveDir Directories="$(BaseIntermediateOutputPath)" />
<ProjectReference Include="..\FileLiberator\FileLiberator.csproj" /> <!-- Remove bin folder -->
</ItemGroup> <RemoveDir Directories="$(BaseOutputPath)" />
</Target>
<ItemGroup>
<ProjectReference Include="..\ApplicationServices\ApplicationServices.csproj" />
<ProjectReference Include="..\AppScaffolding\AppScaffolding.csproj" />
<ProjectReference Include="..\FileLiberator\FileLiberator.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup> <PropertyGroup>
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>Any CPU</Platform> <Platform>Any CPU</Platform>
<PublishDir>..\bin-Avalonia\publish\linux-x64\</PublishDir> <PublishDir>..\bin\Release\linux-chardonnay</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol> <PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier> <RuntimeIdentifier>linux-x64</RuntimeIdentifier>

View File

@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup> <PropertyGroup>
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>Any CPU</Platform> <Platform>Any CPU</Platform>
<PublishDir>..\bin-Avalonia\publish\osx-x64\</PublishDir> <PublishDir>..\bin\Release\macos-chardonnay</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol> <PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifier>osx-x64</RuntimeIdentifier> <RuntimeIdentifier>osx-x64</RuntimeIdentifier>

View File

@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup> <PropertyGroup>
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>Any CPU</Platform> <Platform>Any CPU</Platform>
<PublishDir>..\bin\publish\</PublishDir> <PublishDir>..\bin\Release\win-chardonnay</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol> <PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Threading.Tasks;
using AudibleApi; using AudibleApi;
using AudibleUtilities; using AudibleUtilities;
using LibationWinForms.Dialogs.Login; using LibationWinForms.Dialogs.Login;
@ -14,42 +15,43 @@ namespace LibationWinForms.Login
_account = Dinah.Core.ArgumentValidator.EnsureNotNull(account, nameof(account)); _account = Dinah.Core.ArgumentValidator.EnsureNotNull(account, nameof(account));
} }
public string Get2faCode() public Task<string> Get2faCodeAsync()
{ {
using var dialog = new _2faCodeDialog(); using var dialog = new _2faCodeDialog();
if (ShowDialog(dialog)) if (ShowDialog(dialog))
return dialog.Code; return Task.FromResult(dialog.Code);
return null; return Task.FromResult<string>(null);
} }
public string GetCaptchaAnswer(byte[] captchaImage) public Task<string> GetCaptchaAnswerAsync(byte[] captchaImage)
{ {
using var dialog = new CaptchaDialog(captchaImage); using var dialog = new CaptchaDialog(captchaImage);
if (ShowDialog(dialog)) if (ShowDialog(dialog))
return dialog.Answer; return Task.FromResult(dialog.Answer);
return null; return Task.FromResult<string>(null);
} }
public (string name, string value) GetMfaChoice(MfaConfig mfaConfig) public Task<(string name, string value)> GetMfaChoiceAsync(MfaConfig mfaConfig)
{ {
using var dialog = new MfaDialog(mfaConfig); using var dialog = new MfaDialog(mfaConfig);
if (ShowDialog(dialog)) if (ShowDialog(dialog))
return (dialog.SelectedName, dialog.SelectedValue); return Task.FromResult((dialog.SelectedName, dialog.SelectedValue));
return (null, null); return Task.FromResult<(string, string)>((null, null));
} }
public (string email, string password) GetLogin() public Task<(string email, string password)> GetLoginAsync()
{ {
using var dialog = new LoginCallbackDialog(_account); using var dialog = new LoginCallbackDialog(_account);
if (ShowDialog(dialog)) if (ShowDialog(dialog))
return (dialog.Email, dialog.Password); return Task.FromResult((dialog.Email, dialog.Password));
return (null, null); return Task.FromResult<(string, string)>((null, null));
} }
public void ShowApprovalNeeded() public Task ShowApprovalNeededAsync()
{ {
using var dialog = new ApprovalNeededDialog(); using var dialog = new ApprovalNeededDialog();
ShowDialog(dialog); ShowDialog(dialog);
return Task.CompletedTask;
} }
} }
} }

View File

@ -21,7 +21,7 @@ namespace LibationWinForms.Login
LoginCallback = new WinformLoginCallback(_account); LoginCallback = new WinformLoginCallback(_account);
} }
public ChoiceOut Start(ChoiceIn choiceIn) public Task<ChoiceOut> StartAsync(ChoiceIn choiceIn)
{ {
using var dialog = new LoginChoiceEagerDialog(_account); using var dialog = new LoginChoiceEagerDialog(_account);
@ -31,13 +31,14 @@ namespace LibationWinForms.Login
switch (dialog.LoginMethod) switch (dialog.LoginMethod)
{ {
case LoginMethod.Api: case LoginMethod.Api:
return ChoiceOut.WithApi(dialog.Email, dialog.Password); return Task.FromResult(ChoiceOut.WithApi(dialog.Email, dialog.Password));
case LoginMethod.External: case LoginMethod.External:
{ {
using var externalDialog = new LoginExternalDialog(_account, choiceIn.LoginUrl); using var externalDialog = new LoginExternalDialog(_account, choiceIn.LoginUrl);
return ShowDialog(externalDialog) return Task.FromResult(
? ChoiceOut.External(externalDialog.ResponseUrl) ShowDialog(externalDialog)
: null; ? ChoiceOut.External(externalDialog.ResponseUrl)
: null);
} }
default: default:
throw new Exception($"Unknown {nameof(LoginMethod)} value"); throw new Exception($"Unknown {nameof(LoginMethod)} value");

View File

@ -1,91 +1,99 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework> <TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>libation.ico</ApplicationIcon> <ApplicationIcon>libation.ico</ApplicationIcon>
<AssemblyName>Libation</AssemblyName> <AssemblyName>Libation</AssemblyName>
<PublishReadyToRun>true</PublishReadyToRun>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<StartupObject />
<!-- Version is now in AppScaffolding.csproj --> <PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<StartupObject />
<IsPublishable>true</IsPublishable>
<!-- Version is now in AppScaffolding.csproj -->
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<!-- <!--
HACK FOR COMPILER BUG 2021-09-14. Hopefully will be fixed in future versions HACK FOR COMPILER BUG 2021-09-14. Hopefully will be fixed in future versions
- Not using SatelliteResourceLanguages will load all language packs: works - Not using SatelliteResourceLanguages will load all language packs: works
- Specifying 'en' semicolon 1 more should load 1 language pack: works - Specifying 'en' semicolon 1 more should load 1 language pack: works
- Specifying only 'en' should load no language packs: broken, still loads all - Specifying only 'en' should load no language packs: broken, still loads all
--> -->
<SatelliteResourceLanguages>en;es</SatelliteResourceLanguages> <SatelliteResourceLanguages>en;es</SatelliteResourceLanguages>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>..\bin\Debug</OutputPath> <OutputPath>..\bin\Debug</OutputPath>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<OutputPath>..\bin\Release</OutputPath> <OutputPath>..\bin\Release</OutputPath>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove=".gitignore" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Autoupdater.NET.Official" Version="1.7.4" /> <None Remove=".gitignore" />
<PackageReference Include="Dinah.Core.WindowsDesktop" Version="4.2.3.1" /> </ItemGroup>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ApplicationServices\ApplicationServices.csproj" /> <ItemGroup>
<ProjectReference Include="..\AppScaffolding\AppScaffolding.csproj" /> <PackageReference Include="Autoupdater.NET.Official" Version="1.7.4" />
<ProjectReference Include="..\FileLiberator\FileLiberator.csproj" /> <PackageReference Include="Dinah.Core.WindowsDesktop" Version="4.2.3.1" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Update="Form1.*.cs"> <ItemGroup>
<DependentUpon>Form1.cs</DependentUpon> <ProjectReference Include="..\ApplicationServices\ApplicationServices.csproj" />
</Compile> <ProjectReference Include="..\AppScaffolding\AppScaffolding.csproj" />
</ItemGroup> <ProjectReference Include="..\FileLiberator\FileLiberator.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Dialogs\SettingsDialog.*.cs">
<DependentUpon>SettingsDialog.cs</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Update="Form1.*.cs">
<DependentUpon>Form1.cs</DependentUpon>
</Compile>
</ItemGroup> <ItemGroup>
<Compile Update="Form1.*.cs"> <Compile Update="Form1.*.cs">
<DependentUpon>Form1.cs</DependentUpon> <DependentUpon>Form1.cs</DependentUpon>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Properties\Resources.Designer.cs"> <Compile Update="Dialogs\SettingsDialog.*.cs">
<DesignTime>True</DesignTime> <DependentUpon>SettingsDialog.cs</DependentUpon>
<AutoGen>True</AutoGen> </Compile>
<DependentUpon>Resources.resx</DependentUpon> </ItemGroup>
</Compile> <ItemGroup>
</ItemGroup> <Compile Update="Form1.*.cs">
<DependentUpon>Form1.cs</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Update="Form1.*.cs">
<DependentUpon>Form1.cs</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx"> <Compile Update="Properties\Resources.Designer.cs">
<Generator>ResXFileCodeGenerator</Generator> <DesignTime>True</DesignTime>
<LastGenOutput>Resources.Designer.cs</LastGenOutput> <AutoGen>True</AutoGen>
</EmbeddedResource> <DependentUpon>Resources.resx</DependentUpon>
</ItemGroup> </Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<Target Name="SpicNSpan" AfterTargets="Clean">
<!-- Remove obj folder -->
<RemoveDir Directories="$(BaseIntermediateOutputPath)" />
<!-- Remove bin folder -->
<RemoveDir Directories="$(BaseOutputPath)" />
</Target>
</Project> </Project>

View File

@ -30,6 +30,8 @@ namespace LibationWinForms
ApplicationConfiguration.Initialize(); ApplicationConfiguration.Initialize();
AppScaffolding.LibationScaffolding.SetReleaseIdentifier(AppScaffolding.ReleaseIdentifier.WindowsClassic);
//***********************************************// //***********************************************//
// // // //
// do not use Configuration before this line // // do not use Configuration before this line //
@ -170,7 +172,7 @@ namespace LibationWinForms
try try
{ {
upgradeProperties = AppScaffolding.LibationScaffolding.GetLatestRelease(AppScaffolding.LibationScaffolding.ReleaseIdentifier.WindowsClassic); upgradeProperties = AppScaffolding.LibationScaffolding.GetLatestRelease();
if (upgradeProperties is null) if (upgradeProperties is null)
return; return;
} }