Added AccountsDialog
This commit is contained in:
parent
ccdd1dc9f3
commit
eff9c2b35d
@ -87,13 +87,6 @@
|
|||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
<Rectangle Name="VerticalSeparator"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="1"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="{DynamicResource DataGridGridLinesBrush}" />
|
|
||||||
|
|
||||||
<ContentPresenter Margin="{TemplateBinding Padding}"
|
<ContentPresenter Margin="{TemplateBinding Padding}"
|
||||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
@ -263,8 +256,8 @@
|
|||||||
<ControlTemplate>
|
<ControlTemplate>
|
||||||
<Border x:Name="RowBorder"
|
<Border x:Name="RowBorder"
|
||||||
Background="{TemplateBinding Background}"
|
Background="{TemplateBinding Background}"
|
||||||
BorderBrush="{DynamicResource DataGridGridLinesBrush}"
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
BorderThickness="0.5"
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
CornerRadius="{TemplateBinding CornerRadius}">
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
<DataGridFrozenGrid Name="PART_Root"
|
<DataGridFrozenGrid Name="PART_Root"
|
||||||
ColumnDefinitions="Auto,*"
|
ColumnDefinitions="Auto,*"
|
||||||
|
|||||||
@ -25,6 +25,8 @@ namespace LibationWinForms.AvaloniaUI
|
|||||||
|
|
||||||
public static void RestoreSizeAndLocation(this Window form, Configuration config)
|
public static void RestoreSizeAndLocation(this Window form, Configuration config)
|
||||||
{
|
{
|
||||||
|
if (Design.IsDesignMode) return;
|
||||||
|
|
||||||
FormSizeAndPosition savedState = config.GetNonString<FormSizeAndPosition>(form.GetType().Name);
|
FormSizeAndPosition savedState = config.GetNonString<FormSizeAndPosition>(form.GetType().Name);
|
||||||
|
|
||||||
if (savedState is null)
|
if (savedState is null)
|
||||||
@ -64,6 +66,8 @@ namespace LibationWinForms.AvaloniaUI
|
|||||||
}
|
}
|
||||||
public static void SaveSizeAndLocation(this Window form, Configuration config)
|
public static void SaveSizeAndLocation(this Window form, Configuration config)
|
||||||
{
|
{
|
||||||
|
if (Design.IsDesignMode) return;
|
||||||
|
|
||||||
var saveState = new FormSizeAndPosition();
|
var saveState = new FormSizeAndPosition();
|
||||||
|
|
||||||
saveState.IsMaximized = form.WindowState == WindowState.Maximized;
|
saveState.IsMaximized = form.WindowState == WindowState.Maximized;
|
||||||
|
|||||||
@ -0,0 +1,132 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
Width="800" Height="450"
|
||||||
|
x:Class="LibationWinForms.AvaloniaUI.Views.Dialogs.AccountsDialog"
|
||||||
|
Title="Audible Accounts"
|
||||||
|
Icon="/AvaloniaUI/Assets/libation.ico">
|
||||||
|
<Grid RowDefinitions="*,Auto">
|
||||||
|
|
||||||
|
<Grid.Styles>
|
||||||
|
<Style Selector="Button:focus">
|
||||||
|
<Setter Property="BorderBrush" Value="{DynamicResource SystemAccentColor}" />
|
||||||
|
<Setter Property="BorderThickness" Value="2" />
|
||||||
|
</Style>
|
||||||
|
</Grid.Styles>
|
||||||
|
|
||||||
|
<DataGrid
|
||||||
|
Grid.Row="0"
|
||||||
|
CanUserReorderColumns="False"
|
||||||
|
CanUserResizeColumns="False"
|
||||||
|
CanUserSortColumns="False"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
IsReadOnly="False"
|
||||||
|
Items="{Binding Accounts}"
|
||||||
|
GridLinesVisibility="All">
|
||||||
|
|
||||||
|
<DataGrid.Columns>
|
||||||
|
|
||||||
|
<DataGridTemplateColumn Header="Delete">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Width="60"
|
||||||
|
Height="30"
|
||||||
|
Content="X"
|
||||||
|
Click="DeleteButton_Clicked" />
|
||||||
|
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
|
||||||
|
<DataGridTemplateColumn Header="Export">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Width="60"
|
||||||
|
Height="30"
|
||||||
|
Content="Export"
|
||||||
|
ToolTip.Tip="Export account authorization to audible-cli"
|
||||||
|
Click="ExportButton_Clicked" />
|
||||||
|
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
|
||||||
|
<DataGridCheckBoxColumn
|
||||||
|
Binding="{Binding LibraryScan, Mode=TwoWay}"
|
||||||
|
Header="Include in
library scan?"/>
|
||||||
|
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="2*"
|
||||||
|
Binding="{Binding AccountId, Mode=TwoWay}"
|
||||||
|
Header="Autible
email/login"/>
|
||||||
|
|
||||||
|
<DataGridTemplateColumn Width="Auto" Header="Locale">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<ComboBox
|
||||||
|
MinHeight="30"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
SelectedItem="{Binding SelectedLocale, Mode=TwoWay}"
|
||||||
|
Items="{Binding Locales}">
|
||||||
|
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
|
||||||
|
<TextBlock ZIndex="2"
|
||||||
|
FontSize="12"
|
||||||
|
Text="{Binding Name}" />
|
||||||
|
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
|
||||||
|
<DataGridTextColumn
|
||||||
|
Width="3*"
|
||||||
|
Binding="{Binding AccountName, Mode=TwoWay}"
|
||||||
|
Header="Account Nickname
(optional)"/>
|
||||||
|
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
<Grid
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="10"
|
||||||
|
ColumnDefinitions="*,Auto" >
|
||||||
|
|
||||||
|
<StackPanel
|
||||||
|
Grid.Column="0"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Column="0"
|
||||||
|
Height="30"
|
||||||
|
Content="Add an Account"
|
||||||
|
Name="AddAccountButton"
|
||||||
|
Click="AddAccountButton_Clicked" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Column="0"
|
||||||
|
Height="30"
|
||||||
|
Margin="20,0,0,0"
|
||||||
|
Content="Import from audible-cli"
|
||||||
|
Click="ImportButton_Clicked" />
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Grid.Column="1"
|
||||||
|
Height="30"
|
||||||
|
Padding="30,3,30,3"
|
||||||
|
Content="Save"
|
||||||
|
Click="SaveButton_Clicked" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
@ -0,0 +1,270 @@
|
|||||||
|
using AudibleUtilities;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
||||||
|
{
|
||||||
|
public partial class AccountsDialog : DialogWindow
|
||||||
|
{
|
||||||
|
public ObservableCollection<AccountDto> Accounts { get; } = new();
|
||||||
|
public class AccountDto
|
||||||
|
{
|
||||||
|
public IList<AudibleApi.Locale> Locales { get; init; }
|
||||||
|
public bool LibraryScan { get; set; } = true;
|
||||||
|
public string AccountId { get; set; }
|
||||||
|
public AudibleApi.Locale SelectedLocale { get; set; }
|
||||||
|
public string AccountName { get; set; }
|
||||||
|
public bool IsDefault => AccountId is null && SelectedLocale is null && AccountName is null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetAudibleCliAppDataPath()
|
||||||
|
=> Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Audible");
|
||||||
|
|
||||||
|
private List<AudibleApi.Locale> Locales => AudibleApi.Localization.Locales.OrderBy(l => l.Name).ToList();
|
||||||
|
public AccountsDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
// WARNING: accounts persister will write ANY EDIT to object immediately to file
|
||||||
|
// here: copy strings and dispose of persister
|
||||||
|
// only persist in 'save' step
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
var accounts = persister.AccountsSettings.Accounts;
|
||||||
|
if (!accounts.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
ControlToFocusOnShow = this.FindControl<Button>(nameof(AddAccountButton));
|
||||||
|
|
||||||
|
DataContext = this;
|
||||||
|
|
||||||
|
foreach (var account in accounts)
|
||||||
|
AddAccountToGrid(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddAccountToGrid(Account account)
|
||||||
|
{
|
||||||
|
//ObservableCollection doesn't fire CollectionChanged on Add, so use Insert instead
|
||||||
|
Accounts.Insert(Accounts.Count, new()
|
||||||
|
{
|
||||||
|
LibraryScan = account.LibraryScan,
|
||||||
|
AccountId = account.AccountId,
|
||||||
|
SelectedLocale = Locales.Single(l => l.Name == account.Locale.Name),
|
||||||
|
AccountName = account.AccountName,
|
||||||
|
Locales = Locales
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void ImportButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
OpenFileDialog ofd = new();
|
||||||
|
ofd.Filters.Add(new() { Name = "JSON File", Extensions = new() { "json" } });
|
||||||
|
ofd.Directory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||||
|
ofd.AllowMultiple = false;
|
||||||
|
|
||||||
|
string audibleAppDataDir = GetAudibleCliAppDataPath();
|
||||||
|
|
||||||
|
if (Directory.Exists(audibleAppDataDir))
|
||||||
|
ofd.Directory = audibleAppDataDir;
|
||||||
|
|
||||||
|
var filePath = await ofd.ShowAsync(this);
|
||||||
|
|
||||||
|
if (filePath is null || filePath.Length == 0) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jsonText = File.ReadAllText(filePath[0]);
|
||||||
|
var mkbAuth = Mkb79Auth.FromJson(jsonText);
|
||||||
|
var account = await mkbAuth.ToAccountAsync();
|
||||||
|
|
||||||
|
// without transaction, accounts persister will write ANY EDIT immediately to file
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
|
||||||
|
if (persister.AccountsSettings.Accounts.Any(a => a.AccountId == account.AccountId && a.IdentityTokens.Locale.Name == account.Locale.Name))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
persister.AccountsSettings.Add(account);
|
||||||
|
|
||||||
|
AddAccountToGrid(account);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBoxLib.ShowAdminAlert(
|
||||||
|
null,
|
||||||
|
$"An error occurred while importing an account from:\r\n{filePath[0]}\r\n\r\nIs the file encrypted?",
|
||||||
|
"Error Importing Account",
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddAccountButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (Accounts.Any(a => a.IsDefault))
|
||||||
|
return;
|
||||||
|
|
||||||
|
//ObservableCollection doesn't fire CollectionChanged on Add, so use Insert instead
|
||||||
|
Accounts.Insert(Accounts.Count, new() { Locales = Locales });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Source is Button expBtn && expBtn.DataContext is AccountDto acc)
|
||||||
|
Accounts.Remove(acc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExportButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Source is Button expBtn && expBtn.DataContext is AccountDto acc)
|
||||||
|
Export(acc);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task SaveAndCloseAsync()
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!await inputIsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// without transaction, accounts persister will write ANY EDIT immediately to file
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
|
||||||
|
persister.BeginTransation();
|
||||||
|
persist(persister.AccountsSettings);
|
||||||
|
persister.CommitTransation();
|
||||||
|
|
||||||
|
base.SaveAndClose();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBoxLib.ShowAdminAlert(null, "Error attempting to save accounts", "Error saving accounts", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
=> await SaveAndCloseAsync();
|
||||||
|
|
||||||
|
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void persist(AccountsSettings accountsSettings)
|
||||||
|
{
|
||||||
|
var existingAccounts = accountsSettings.Accounts;
|
||||||
|
|
||||||
|
// editing account id is a special case. an account is defined by its account id, therefore this is really a different account. the user won't care about this distinction though.
|
||||||
|
// these will be caught below by normal means and re-created minus the convenience of persisting identity tokens
|
||||||
|
|
||||||
|
// delete
|
||||||
|
for (var i = existingAccounts.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var existing = existingAccounts[i];
|
||||||
|
if (!Accounts.Any(dto =>
|
||||||
|
dto.AccountId?.ToLower().Trim() == existing.AccountId.ToLower()
|
||||||
|
&& dto.SelectedLocale.Name == existing.Locale?.Name))
|
||||||
|
{
|
||||||
|
accountsSettings.Delete(existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// upsert each. validation occurs through Account and AccountsSettings
|
||||||
|
foreach (var dto in Accounts)
|
||||||
|
{
|
||||||
|
var acct = accountsSettings.Upsert(dto.AccountId, dto.SelectedLocale.Name);
|
||||||
|
acct.LibraryScan = dto.LibraryScan;
|
||||||
|
acct.AccountName
|
||||||
|
= string.IsNullOrWhiteSpace(dto.AccountName)
|
||||||
|
? $"{dto.AccountId} - {dto.SelectedLocale.Name}"
|
||||||
|
: dto.AccountName.Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private async Task<bool> inputIsValid()
|
||||||
|
{
|
||||||
|
foreach (var dto in Accounts.ToList())
|
||||||
|
{
|
||||||
|
if (dto.IsDefault)
|
||||||
|
{
|
||||||
|
Accounts.Remove(dto);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(dto.AccountId))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(dto.SelectedLocale.Name))
|
||||||
|
{
|
||||||
|
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 true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Export(AccountDto acc)
|
||||||
|
{
|
||||||
|
// without transaction, accounts persister will write ANY EDIT immediately to file
|
||||||
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
||||||
|
|
||||||
|
var account = persister.AccountsSettings.Accounts.FirstOrDefault(a => a.AccountId == acc.AccountId && a.Locale.Name == acc.SelectedLocale.Name);
|
||||||
|
|
||||||
|
if (account is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (account.IdentityTokens?.IsValid != true)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveFileDialog sfd = new();
|
||||||
|
sfd.Filters.Add(new() { Name = "JSON File", Extensions = new() { "json" } });
|
||||||
|
|
||||||
|
string audibleAppDataDir = GetAudibleCliAppDataPath();
|
||||||
|
|
||||||
|
if (Directory.Exists(audibleAppDataDir))
|
||||||
|
sfd.Directory = audibleAppDataDir;
|
||||||
|
|
||||||
|
string fileName = await sfd.ShowAsync(this);
|
||||||
|
if (fileName is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var mkbAuth = Mkb79Auth.FromAccount(account);
|
||||||
|
var jsonText = mkbAuth.ToJson();
|
||||||
|
|
||||||
|
File.WriteAllText(fileName, jsonText);
|
||||||
|
|
||||||
|
await MessageBox.Show(this, $"Successfully exported {account.AccountName} to\r\n\r\n{fileName}", "Success!");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBoxLib.ShowAdminAlert(
|
||||||
|
null,
|
||||||
|
$"An error occurred while exporting account:\r\n{account.AccountName}",
|
||||||
|
"Error Exporting Account",
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,7 +5,7 @@
|
|||||||
mc:Ignorable="d" d:DesignWidth="550" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="550" d:DesignHeight="450"
|
||||||
MinWidth="550" MinHeight="450"
|
MinWidth="550" MinHeight="450"
|
||||||
Width="650" Height="500"
|
Width="650" Height="500"
|
||||||
x:Class="LibationWinForms.AvaloniaUI.Views.Dialogs.BookDetailsDialog2"
|
x:Class="LibationWinForms.AvaloniaUI.Views.Dialogs.BookDetailsDialog"
|
||||||
xmlns:controls="clr-namespace:LibationWinForms.AvaloniaUI.Controls"
|
xmlns:controls="clr-namespace:LibationWinForms.AvaloniaUI.Controls"
|
||||||
Title="Book Details" Name="BookDetails"
|
Title="Book Details" Name="BookDetails"
|
||||||
Icon="/AvaloniaUI/Assets/libation.ico">
|
Icon="/AvaloniaUI/Assets/libation.ico">
|
||||||
@ -12,7 +12,7 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
||||||
{
|
{
|
||||||
public partial class BookDetailsDialog2 : DialogWindow
|
public partial class BookDetailsDialog : DialogWindow
|
||||||
{
|
{
|
||||||
private LibraryBook _libraryBook;
|
private LibraryBook _libraryBook;
|
||||||
private BookDetailsDialogViewModel _viewModel;
|
private BookDetailsDialogViewModel _viewModel;
|
||||||
@ -31,7 +31,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
public LiberatedStatus BookLiberatedStatus => _viewModel.BookLiberatedSelectedItem.Status;
|
public LiberatedStatus BookLiberatedStatus => _viewModel.BookLiberatedSelectedItem.Status;
|
||||||
public LiberatedStatus? PdfLiberatedStatus => _viewModel.PdfLiberatedSelectedItem?.Status;
|
public LiberatedStatus? PdfLiberatedStatus => _viewModel.PdfLiberatedSelectedItem?.Status;
|
||||||
|
|
||||||
public BookDetailsDialog2()
|
public BookDetailsDialog()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
LibraryBook = context.GetLibraryBook_Flat_NoTracking("B017V4IM1G");
|
LibraryBook = context.GetLibraryBook_Flat_NoTracking("B017V4IM1G");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public BookDetailsDialog2(LibraryBook libraryBook) :this()
|
public BookDetailsDialog(LibraryBook libraryBook) :this()
|
||||||
{
|
{
|
||||||
LibraryBook = libraryBook;
|
LibraryBook = libraryBook;
|
||||||
}
|
}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using LibationFileManager;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
||||||
{
|
{
|
||||||
@ -11,27 +13,42 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
{
|
{
|
||||||
this.HideMinMaxBtns();
|
this.HideMinMaxBtns();
|
||||||
this.KeyDown += DialogWindow_KeyDown;
|
this.KeyDown += DialogWindow_KeyDown;
|
||||||
|
this.Initialized += DialogWindow_Initialized;
|
||||||
this.Opened += DialogWindow_Opened;
|
this.Opened += DialogWindow_Opened;
|
||||||
|
this.Closing += DialogWindow_Closing;
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
this.AttachDevTools();
|
this.AttachDevTools();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DialogWindow_Initialized(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
this.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||||
|
this.RestoreSizeAndLocation(Configuration.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DialogWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||||
|
{
|
||||||
|
this.SaveSizeAndLocation(Configuration.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
private void DialogWindow_Opened(object sender, EventArgs e)
|
private void DialogWindow_Opened(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
ControlToFocusOnShow?.Focus();
|
ControlToFocusOnShow?.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void SaveAndClose() => Close(DialogResult.OK);
|
protected virtual void SaveAndClose() => Close(DialogResult.OK);
|
||||||
|
protected virtual Task SaveAndCloseAsync() => Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(SaveAndClose);
|
||||||
protected virtual void CancelAndClose() => Close(DialogResult.Cancel);
|
protected virtual void CancelAndClose() => Close(DialogResult.Cancel);
|
||||||
|
protected virtual Task CancelAndCloseAsync() => Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(CancelAndClose);
|
||||||
|
|
||||||
private void DialogWindow_KeyDown(object sender, Avalonia.Input.KeyEventArgs e)
|
private async void DialogWindow_KeyDown(object sender, Avalonia.Input.KeyEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Key == Avalonia.Input.Key.Escape)
|
if (e.Key == Avalonia.Input.Key.Escape)
|
||||||
CancelAndClose();
|
await CancelAndCloseAsync();
|
||||||
else if (e.Key == Avalonia.Input.Key.Return)
|
else if (e.Key == Avalonia.Input.Key.Return)
|
||||||
SaveAndClose();
|
await SaveAndCloseAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ using Avalonia.Markup.Xaml;
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
||||||
{
|
{
|
||||||
@ -56,9 +57,9 @@ namespace LibationWinForms.AvaloniaUI.Views.Dialogs
|
|||||||
this.FindControl<Button>(nameof(ImportButton)).Focus();
|
this.FindControl<Button>(nameof(ImportButton)).Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EditAccountsButton_Clicked(object sender, RoutedEventArgs e)
|
public async void EditAccountsButton_Clicked(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (new LibationWinForms.Dialogs.AccountsDialog().ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
if (await new AccountsDialog().ShowDialog<DialogResult>(this) == DialogResult.OK)
|
||||||
{
|
{
|
||||||
// reload grid and default checkboxes
|
// reload grid and default checkboxes
|
||||||
LoadAccounts();
|
LoadAccounts();
|
||||||
|
|||||||
@ -28,7 +28,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
public async void noAccountsYetAddAccountToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void noAccountsYetAddAccountToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
await 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");
|
||||||
new AccountsDialog().ShowDialog();
|
await new Dialogs.AccountsDialog().ShowDialog(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void scanLibraryToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void scanLibraryToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
|||||||
@ -9,7 +9,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
{
|
{
|
||||||
private void Configure_Settings() { }
|
private void Configure_Settings() { }
|
||||||
|
|
||||||
public void accountsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) => new AccountsDialog().ShowDialog();
|
public async void accountsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) => await new Dialogs.AccountsDialog().ShowDialog(this);
|
||||||
|
|
||||||
public void basicSettingsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) => new SettingsDialog().ShowDialog();
|
public void basicSettingsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e) => new SettingsDialog().ShowDialog();
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
<DataGrid
|
<DataGrid
|
||||||
CopyingRowClipboardContent="DataGrid_CopyToClipboard"
|
CopyingRowClipboardContent="DataGrid_CopyToClipboard"
|
||||||
Name="productsGrid"
|
Name="productsGrid"
|
||||||
|
GridLinesVisibility="All"
|
||||||
AutoGenerateColumns="False"
|
AutoGenerateColumns="False"
|
||||||
Items="{Binding GridEntries}"
|
Items="{Binding GridEntries}"
|
||||||
Sorting="ProductsGrid_Sorting"
|
Sorting="ProductsGrid_Sorting"
|
||||||
|
|||||||
@ -270,7 +270,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BookDetailsDialog2 bookDetailsForm;
|
BookDetailsDialog bookDetailsForm;
|
||||||
|
|
||||||
public void OnTagsButtonClick(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
public void OnTagsButtonClick(object sender, Avalonia.Interactivity.RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
@ -280,9 +280,7 @@ namespace LibationWinForms.AvaloniaUI.Views
|
|||||||
{
|
{
|
||||||
if (bookDetailsForm is null || !bookDetailsForm.IsVisible)
|
if (bookDetailsForm is null || !bookDetailsForm.IsVisible)
|
||||||
{
|
{
|
||||||
bookDetailsForm = new BookDetailsDialog2(lbEntry.LibraryBook);
|
bookDetailsForm = new BookDetailsDialog(lbEntry.LibraryBook);
|
||||||
bookDetailsForm.RestoreSizeAndLocation(Configuration.Instance);
|
|
||||||
bookDetailsForm.Closing += (_,_) => bookDetailsForm.SaveSizeAndLocation(Configuration.Instance);
|
|
||||||
bookDetailsForm.Show(window);
|
bookDetailsForm.Show(window);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@ -31,13 +31,13 @@
|
|||||||
this.cancelBtn = new System.Windows.Forms.Button();
|
this.cancelBtn = new System.Windows.Forms.Button();
|
||||||
this.saveBtn = new System.Windows.Forms.Button();
|
this.saveBtn = new System.Windows.Forms.Button();
|
||||||
this.dataGridView1 = new System.Windows.Forms.DataGridView();
|
this.dataGridView1 = new System.Windows.Forms.DataGridView();
|
||||||
this.importBtn = new System.Windows.Forms.Button();
|
|
||||||
this.DeleteAccount = new System.Windows.Forms.DataGridViewButtonColumn();
|
this.DeleteAccount = new System.Windows.Forms.DataGridViewButtonColumn();
|
||||||
this.ExportAccount = new System.Windows.Forms.DataGridViewButtonColumn();
|
this.ExportAccount = new System.Windows.Forms.DataGridViewButtonColumn();
|
||||||
this.LibraryScan = new System.Windows.Forms.DataGridViewCheckBoxColumn();
|
this.LibraryScan = new System.Windows.Forms.DataGridViewCheckBoxColumn();
|
||||||
this.AccountId = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
this.AccountId = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
this.Locale = new System.Windows.Forms.DataGridViewComboBoxColumn();
|
this.Locale = new System.Windows.Forms.DataGridViewComboBoxColumn();
|
||||||
this.AccountName = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
this.AccountName = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
|
this.importBtn = new System.Windows.Forms.Button();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
@ -62,7 +62,7 @@
|
|||||||
this.saveBtn.Name = "saveBtn";
|
this.saveBtn.Name = "saveBtn";
|
||||||
this.saveBtn.Size = new System.Drawing.Size(88, 27);
|
this.saveBtn.Size = new System.Drawing.Size(88, 27);
|
||||||
this.saveBtn.TabIndex = 1;
|
this.saveBtn.TabIndex = 1;
|
||||||
this.saveBtn.Text = "Save";
|
this.saveBtn.Text = "pub ";
|
||||||
this.saveBtn.UseVisualStyleBackColor = true;
|
this.saveBtn.UseVisualStyleBackColor = true;
|
||||||
this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click);
|
this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click);
|
||||||
//
|
//
|
||||||
@ -89,18 +89,6 @@
|
|||||||
this.dataGridView1.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.DataGridView1_CellContentClick);
|
this.dataGridView1.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.DataGridView1_CellContentClick);
|
||||||
this.dataGridView1.DefaultValuesNeeded += new System.Windows.Forms.DataGridViewRowEventHandler(this.dataGridView1_DefaultValuesNeeded);
|
this.dataGridView1.DefaultValuesNeeded += new System.Windows.Forms.DataGridViewRowEventHandler(this.dataGridView1_DefaultValuesNeeded);
|
||||||
//
|
//
|
||||||
// importBtn
|
|
||||||
//
|
|
||||||
this.importBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
|
||||||
this.importBtn.Location = new System.Drawing.Point(14, 480);
|
|
||||||
this.importBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
|
||||||
this.importBtn.Name = "importBtn";
|
|
||||||
this.importBtn.Size = new System.Drawing.Size(156, 27);
|
|
||||||
this.importBtn.TabIndex = 1;
|
|
||||||
this.importBtn.Text = "Import from audible-cli";
|
|
||||||
this.importBtn.UseVisualStyleBackColor = true;
|
|
||||||
this.importBtn.Click += new System.EventHandler(this.importBtn_Click);
|
|
||||||
//
|
|
||||||
// DeleteAccount
|
// DeleteAccount
|
||||||
//
|
//
|
||||||
this.DeleteAccount.HeaderText = "Delete";
|
this.DeleteAccount.HeaderText = "Delete";
|
||||||
@ -140,6 +128,18 @@
|
|||||||
this.AccountName.Name = "AccountName";
|
this.AccountName.Name = "AccountName";
|
||||||
this.AccountName.Width = 170;
|
this.AccountName.Width = 170;
|
||||||
//
|
//
|
||||||
|
// importBtn
|
||||||
|
//
|
||||||
|
this.importBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.importBtn.Location = new System.Drawing.Point(14, 480);
|
||||||
|
this.importBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
|
this.importBtn.Name = "importBtn";
|
||||||
|
this.importBtn.Size = new System.Drawing.Size(156, 27);
|
||||||
|
this.importBtn.TabIndex = 1;
|
||||||
|
this.importBtn.Text = "Import from audible-cli";
|
||||||
|
this.importBtn.UseVisualStyleBackColor = true;
|
||||||
|
this.importBtn.Click += new System.EventHandler(this.importBtn_Click);
|
||||||
|
//
|
||||||
// AccountsDialog
|
// AccountsDialog
|
||||||
//
|
//
|
||||||
this.AcceptButton = this.saveBtn;
|
this.AcceptButton = this.saveBtn;
|
||||||
|
|||||||
@ -109,8 +109,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="AvaloniaUI\Views\Dialogs\BookDetailsDialog2.axaml.cs">
|
<Compile Update="AvaloniaUI\Views\Dialogs\BookDetailsDialog.axaml.cs">
|
||||||
<DependentUpon>BookDetailsDialog2.axaml</DependentUpon>
|
<DependentUpon>BookDetailsDialog.axaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Update="AvaloniaUI\Views\Dialogs\SearchSyntaxDialog.axaml.cs">
|
<Compile Update="AvaloniaUI\Views\Dialogs\SearchSyntaxDialog.axaml.cs">
|
||||||
<DependentUpon>SearchSyntaxDialog.axaml</DependentUpon>
|
<DependentUpon>SearchSyntaxDialog.axaml</DependentUpon>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user