temp: broken push to laptop

This commit is contained in:
alphafetus committed 2026-06-19 12:31:12 -04:00
1 parent 0a0ea86d25
commit 3a819ce180
32 files changed
+503 -573

No files matched your search

@@ -1,40 +1,34 @@
using System; using System.Net.Http.Headers;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using PcgsInvUi.Models;
using System.Text.Json; using System.Text.Json;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.SourceGenerators;
namespace PcgsInvUi.Services; namespace PcgsApi;
public class PcgsClient : ReactiveObject { public partial class PcgsClient : ReactiveObject {
public enum ErrorType { public enum ErrorType {
None, None,
NoCoinFound, NoCoinFound,
InvalidRequestFormat, InvalidRequestFormat,
ApiKeyInvalid ApiKeyInvalid
} }
public int NumApiRequests { protected readonly string _apiKey;
get => _numApiRequests;
set => this.RaiseAndSetIfChanged(ref _numApiRequests, value); protected readonly string _apiUri = "https://api.pcgs.com/publicapi";
}
private string _apiUri = "https://api.pcgs.com/publicapi";
private readonly string _apiKey;
private int _numApiRequests;
/// <summary> /// <summary>
/// Creates a new instance of the PcgsClient class. /// Creates a new instance of the PcgsClient class.
/// Although this is a constructor, it's meant to be called only from the CreateClient method. /// Although this is a constructor, it's meant to be called only from the CreateClient method.
/// </summary> /// </summary>
private PcgsClient(string apiKey) { private PcgsClient(string apiKey) {
_apiKey = apiKey; _apiKey = apiKey;
} }
[Reactive] private int _numApiRequests;
/// <summary> /// <summary>
/// Creates a new instance of the PcgsClient class. /// Creates a new instance of the PcgsClient class.
/// </summary> /// </summary>
/// <param name="apiKey">The api key to use for requests.</param> /// <param name="apiKey">The api key to use for requests.</param>
/// <returns>A new instance of the PcgsClient class, or null if the key is invalid.</returns> /// <returns>A new instance of the PcgsClient class, or null if the key is invalid.</returns>
@@ -47,83 +41,87 @@ public class PcgsClient : ReactiveObject {
} }
/// <summary> /// <summary>
/// Sends a request to the API to verify the credentials. /// Sends a request to the API to verify the credentials.
/// </summary> /// </summary>
/// <returns>A boolean indicating whether or not the credentials are valid.</returns> /// <returns>A boolean indicating whether or not the credentials are valid.</returns>
public async Task<bool> VerifyCredentials() { public async Task<bool> VerifyCredentials() {
// Create a way to check if the api key given is valid // Create a way to check if the api key given is valid
// I think the best way to do this is to make a request to the api and see if it returns the expected response. // I think the best way to do this is to make a request to the api and see if it returns the expected response.
var result = await this.GetCoinFactsByGrade(1, "AG-03"); // This is a known invalid coin id - this is because the api is very fast with invalid requests. var result =
await GetCoinFactsByGrade(1,
"AG-03"); // This is a known invalid coin id - this is because the api is very fast with invalid requests.
if (result.Item1 == ErrorType.ApiKeyInvalid) return false; if (result.Item1 == ErrorType.ApiKeyInvalid) return false;
return true; return true;
} }
/// <summary> /// <summary>
/// Gets the coin information for a given PCGS number and grade. /// Gets the coin information for a given PCGS number and grade.
/// </summary> /// </summary>
/// <param name="pcgsNumber">The PCGS number of the coin.</param> /// <param name="pcgsNumber">The PCGS number of the coin.</param>
/// <param name="grade">The grade of the coin.</param> /// <param name="grade">The grade of the coin.</param>
/// <param name="plusGrade">Whether or not the coin is a plus grade.</param> /// <param name="plusGrade">Whether or not the coin is a plus grade.</param>
/// <returns>A tuple containing the error type and the coin information.</returns> /// <returns>A tuple containing the error type and the coin information.</returns>
public async Task<(ErrorType, Coin?)> GetCoinFactsByGrade(int pcgsNumber, string grade, bool plusGrade = false) { public async Task<(ErrorType, PcgsCoin?)>
using (HttpClient client = new HttpClient()) { GetCoinFactsByGrade(int pcgsNumber, string grade, bool plusGrade = false) {
using (var client = new HttpClient()) {
var gradeParam = GetGradeFromString(grade); var gradeParam = GetGradeFromString(grade);
string requestUri = var requestUri =
$"{_apiUri}/coindetail/GetCoinFactsByGrade/?PCGSNo={pcgsNumber}&GradeNo={gradeParam}&PlusGrade={plusGrade}"; $"{_apiUri}/coindetail/GetCoinFactsByGrade/?PCGSNo={pcgsNumber}&GradeNo={gradeParam}&PlusGrade={plusGrade}";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", _apiKey); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", _apiKey);
Console.WriteLine($"Sending request to {requestUri}"); Console.WriteLine($"Sending request to {requestUri}");
HttpResponseMessage response = await client.GetAsync(requestUri); var response = await client.GetAsync(requestUri);
Console.WriteLine("Got response."); Console.WriteLine("Got response.");
if (response.IsSuccessStatusCode) { if (response.IsSuccessStatusCode) {
// Check response for errors. // Check response for errors.
string jsonResponse = await response.Content.ReadAsStringAsync(); var jsonResponse = await response.Content.ReadAsStringAsync();
var parsedResponse = ParseJsonResponse(jsonResponse); var parsedResponse = ParseJsonResponse(jsonResponse);
return parsedResponse; return parsedResponse;
} }
} }
return (ErrorType.ApiKeyInvalid, null); return (ErrorType.ApiKeyInvalid, null);
} }
/// <summary> /// <summary>
/// Utility function to parse the grade number from the grade string passed from the front end. /// Utility function to parse the grade number from the grade string passed from the front end.
/// </summary> /// </summary>
/// <param name="grade">The grade string.</param> /// <param name="grade">The grade string.</param>
/// <returns>The grade number.</returns> /// <returns>The grade number.</returns>
private int GetGradeFromString(string grade) { private int GetGradeFromString(string grade) {
var actualGrade = int.Parse(grade.Substring(grade.Length - 2)); // Throws an exception if the grade doesn't exist. var actualGrade =
int.Parse(grade.Substring(grade.Length - 2)); // Throws an exception if the grade doesn't exist.
Console.WriteLine($"Grade is {actualGrade}"); Console.WriteLine($"Grade is {actualGrade}");
return actualGrade; return actualGrade;
} }
/// <summary> /// <summary>
/// Parses the json response from the api. /// Parses the json response from the api.
/// </summary> /// </summary>
/// <param name="json">The json response.</param> /// <param name="json">The json response.</param>
/// <returns>A tuple containing an error type and the coin information.</returns> /// <returns>A tuple containing an error type and the coin information.</returns>
private (ErrorType, Coin?) ParseJsonResponse(string json) { private (ErrorType, PcgsCoin?) ParseJsonResponse(string json) {
Console.WriteLine(json); Console.WriteLine(json);
// Check for errors in the response // Check for errors in the response
if (json.Contains("\"ServerMessage\":\"No data found\"")) return (ErrorType.NoCoinFound, null); if (json.Contains("\"ServerMessage\":\"No data found\"")) return (ErrorType.NoCoinFound, null);
if (json.Contains("\"IsValidRequest\":false")) return (ErrorType.InvalidRequestFormat, null); if (json.Contains("\"IsValidRequest\":false")) return (ErrorType.InvalidRequestFormat, null);
var newCoin = JsonSerializer.Deserialize<Coin>(json); var newCoin = JsonSerializer.Deserialize<PcgsCoin>(json);
return (ErrorType.None, newCoin); return (ErrorType.None, newCoin);
} }
} }
// Exception for invalid request format // Exception for invalid request format
class InvalidRequestFormatException : Exception { internal class InvalidRequestFormatException : Exception {
public InvalidRequestFormatException() { } public InvalidRequestFormatException() { }
public InvalidRequestFormatException(string message) : base(message) { } public InvalidRequestFormatException(string message) : base(message) { }
public InvalidRequestFormatException(string message, Exception inner) : base(message, inner) { } public InvalidRequestFormatException(string message, Exception inner) : base(message, inner) { }
} }
// Exception for no coin found // Exception for no coin found
class CoinNotFoundException : Exception { internal class CoinNotFoundException : Exception {
public CoinNotFoundException() { } public CoinNotFoundException() { }
public CoinNotFoundException(string message) : base(message) { } public CoinNotFoundException(string message) : base(message) { }
public CoinNotFoundException(string message, Exception inner) : base(message, inner) { } public CoinNotFoundException(string message, Exception inner) : base(message, inner) { }
} }
+38
View File
@@ -0,0 +1,38 @@
using System.Diagnostics;
using System.Reactive.Linq;
using ReactiveUI;
using ReactiveUI.SourceGenerators;
namespace PcgsApi;
public partial class PcgsCoin : ReactiveObject {
[Reactive] private string _pcgsNumber;
[Reactive] private string _name;
[Reactive] private int _year;
[Reactive] private string _denomination;
[Reactive] private string _mintMark;
[Reactive] private string _grade;
[Reactive] private string _coinFactsLink;
[Reactive] private string _majorVariety;
[Reactive] private string _minorVariety;
[Reactive] private string _dieVariety;
[Reactive] private string _seriesName;
[Reactive] private string _category;
[Reactive] private string _designation;
[Reactive] private int _certificateNumber;
[Reactive] private double _paidFor;
[Reactive] private string _notes;
[Reactive] private int _quantity;
[Reactive] private double _priceGuideValue;
[ObservableAsProperty] private double _totalPrice;
public PcgsCoin() {
_totalPriceHelper = this.WhenAnyValue(
x => x.Quantity,
x => x.PriceGuideValue,
(quantity, price) => quantity * price
)
.Do(_ => Debug.WriteLine("PcgsCoin total price calculated"))
.ToProperty(this, nameof(TotalPrice));
}
}
+47
View File
@@ -0,0 +1,47 @@
using ReactiveUI;
using ReactiveUI.SourceGenerators;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Linq;
using DynamicData;
using DynamicData.Binding;
using PcgsApi;
namespace PcgsInv.Models;
/// <summary>
/// Contains all information related to a collection of coins.
/// </summary>
public partial class CoinCollection : ReactiveObject {
/// <summary>
/// The name of the collection.
/// </summary>
[Reactive] private string _name;
/// <summary>
/// The collection of <see cref="PcgsCoin"/> objects.
/// </summary>
[Reactive] private ObservableCollection<PcgsCoin> _coins;
/// <summary>
/// The total value of the collection.
/// </summary>
[ObservableAsProperty] private double _totalValue;
/// <summary>
/// Creates a new CoinCollection instance.
/// </summary>
public CoinCollection() {
Name = string.Empty;
Coins = [];
Coins.ToObservableChangeSet()
.AutoRefresh(x => x.TotalPrice)
.ToCollection()
.Select(coins => coins.Sum(c => c.TotalPrice))
.ToProperty(this, nameof(TotalValue), out _totalValueHelper);
}
}
-1
View File
@@ -1,7 +1,6 @@
<Application xmlns="https://github.com/avaloniaui" <Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="PcgsInvUi.App" x:Class="PcgsInvUi.App"
xmlns:local="using:PcgsInvUi"
xmlns:pcgsInvUi="clr-namespace:PcgsInvUi" xmlns:pcgsInvUi="clr-namespace:PcgsInvUi"
RequestedThemeVariant="Light"> RequestedThemeVariant="Light">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. --> <!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
+6 -7
View File
@@ -7,7 +7,7 @@ using PcgsInvUi.Views;
namespace PcgsInvUi; namespace PcgsInvUi;
public partial class App : Application { public class App : Application {
public override void Initialize() { public override void Initialize() {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
@@ -17,14 +17,13 @@ public partial class App : Application {
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) {
var coins = new CoinDatabase(); var coins = new CoinDatabase();
desktop.MainWindow = new MainWindow { desktop.MainWindow = new MainWindow {
DataContext = new MainWindowViewModel(coins), DataContext = new MainWindowViewModel(coins)
}; };
// Allow the application to save the database when it exits. // Allow the application to save the database when it exits.
desktop.Exit += (sender, args) => { desktop.Exit += (sender, args) => { coins.UpdateCollection("CollectionTable"); };
coins.UpdateCollection("CollectionTable");
};
} }
base.OnFrameworkInitializationCompleted(); base.OnFrameworkInitializationCompleted();
} }
} }
+28 -28
View File
@@ -5,33 +5,33 @@ project(PcgsInvUi LANGUAGES CSharp)
include(CSharpUtilities) include(CSharpUtilities)
add_executable(PcgsInvUi add_executable(PcgsInvUi
App.axaml.cs App.axaml.cs
Program.cs Program.cs
ViewLocator.cs ViewLocator.cs
App.axaml App.axaml
app.manifest app.manifest
Models/Coin.cs Models/Coin.cs
Services/CoinCollection.cs Services/CoinCollection.cs
Services/CoinDatabase.cs Services/CoinDatabase.cs
Services/PcgsClient.cs Services/PcgsClient.cs
ViewModels/CoinListViewModel.cs ViewModels/CoinListViewModel.cs
ViewModels/DeleteWindowViewModel.cs ViewModels/DeleteWindowViewModel.cs
ViewModels/EditViewModel.cs ViewModels/EditViewModel.cs
ViewModels/ErrorWindowViewModel.cs ViewModels/ErrorWindowViewModel.cs
ViewModels/FindWindowViewModel.cs ViewModels/FindWindowViewModel.cs
ViewModels/MainWindowViewModel.cs ViewModels/MainWindowViewModel.cs
ViewModels/NewViewModel.cs ViewModels/NewViewModel.cs
ViewModels/ViewModelBase.cs ViewModels/ViewModelBase.cs
Views/DeleteWindow.axaml Views/DeleteWindow.axaml
Views/DeleteWindow.axaml.cs Views/DeleteWindow.axaml.cs
Views/ErrorWindow.axaml Views/ErrorWindow.axaml
Views/ErrorWindow.axaml.cs Views/ErrorWindow.axaml.cs
Views/FindWindow.axaml Views/FindWindow.axaml
Views/FindWindow.axaml.cs Views/FindWindow.axaml.cs
Views/MainWindow.axaml Views/MainWindow.axaml
Views/MainWindow.axaml.cs Views/MainWindow.axaml.cs
Views/NewView.axaml Views/NewView.axaml
Views/NewView.axaml.cs Views/NewView.axaml.cs
) )
# Set the .NET Framework version for the executable. # Set the .NET Framework version for the executable.
@@ -43,5 +43,5 @@ set(CMAKE_CSharp_FLAGS "/langversion:latest")
# Add the .NET reference libraries as dependencies. # Add the .NET reference libraries as dependencies.
set_property(TARGET PcgsInvUi PROPERTY VS_DOTNET_REFERENCES set_property(TARGET PcgsInvUi PROPERTY VS_DOTNET_REFERENCES
"Microsoft.CSharp" "Microsoft.CSharp"
) )
-165
View File
@@ -1,165 +0,0 @@
using ReactiveUI;
namespace PcgsInvUi.Models;
public class Coin : ReactiveObject {
public string PCGSNo {
get => _pcgsNo;
set => this.RaiseAndSetIfChanged(ref _pcgsNo, value);
}
private string _pcgsNo;
public string Name {
get => _name;
set => this.RaiseAndSetIfChanged(ref _name, value);
}
private string _name;
public int Year {
get => _year;
set => this.RaiseAndSetIfChanged(ref _year, value);
}
private int _year;
public string Denomination {
get => _denomination;
set => this.RaiseAndSetIfChanged(ref _denomination, value);
}
private string _denomination;
public string MintMark {
get => _mintMark;
set => this.RaiseAndSetIfChanged(ref _mintMark, value);
}
private string _mintMark;
public string Grade {
get => _grade;
set => this.RaiseAndSetIfChanged(ref _grade, value);
}
private string _grade;
public double PriceGuideValue {
get => _priceGuideValue;
set {
TotalPrice = value * Quantity;
this.RaiseAndSetIfChanged(ref _priceGuideValue, value);
}
}
private double _priceGuideValue;
public string CoinFactsLink {
get => _coinFactsLink;
set => this.RaiseAndSetIfChanged(ref _coinFactsLink, value);
}
private string _coinFactsLink;
public string MajorVariety {
get => _majorVariety;
set => this.RaiseAndSetIfChanged(ref _majorVariety, value);
}
private string _majorVariety;
public string MinorVariety {
get => _minorVariety;
set => this.RaiseAndSetIfChanged(ref _minorVariety, value);
}
private string _minorVariety;
public string DieVariety {
get => _dieVariety;
set => this.RaiseAndSetIfChanged(ref _dieVariety, value);
}
private string _dieVariety;
public string SeriesName {
get => _seriesName;
set => this.RaiseAndSetIfChanged(ref _seriesName, value);
}
private string _seriesName;
public string Category {
get => _category;
set => this.RaiseAndSetIfChanged(ref _category, value);
}
private string _category;
public string Designation {
get => _designation;
set => this.RaiseAndSetIfChanged(ref _designation, value);
}
private string _designation;
public int CertificateNumber {
get => _certificateNumber;
set => this.RaiseAndSetIfChanged(ref _certificateNumber, value);
}
private int _certificateNumber;
public int Quantity {
get => _quantity;
set {
TotalPrice = value * PriceGuideValue;
this.RaiseAndSetIfChanged(ref _quantity, value);
}
}
private int _quantity;
public double TotalPrice {
get => _totalPrice;
set => this.RaiseAndSetIfChanged(ref _totalPrice, value);
}
private double _totalPrice;
public double PaidFor {
get => _paidFor;
set => this.RaiseAndSetIfChanged(ref _paidFor, value);
}
private double _paidFor;
public string Notes {
get => _notes;
set => this.RaiseAndSetIfChanged(ref _notes, value);
}
private string _notes;
public Coin() {
PCGSNo = "";
Name = "";
Year = 0;
Denomination = "";
MintMark = "";
Grade = "";
PriceGuideValue = 0;
CoinFactsLink = "";
MajorVariety = "";
MinorVariety = "";
DieVariety = "";
SeriesName = "";
Category = "";
Designation = "";
CertificateNumber = 0;
Quantity = 0;
PaidFor = 0;
Notes = "";
}
}
+18 -11
View File
@@ -1,13 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport> <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault> <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<Company>BobdaFett</Company> <Company>BobdaFett</Company>
<AssemblyVersion>0.0.5</AssemblyVersion> <AssemblyVersion>0.0.5</AssemblyVersion>
<LangVersion>latestmajor</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -17,16 +18,17 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.5" /> <PackageReference Include="Avalonia" Version="12.0.4"/>
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.5" /> <PackageReference Include="Avalonia.Controls.DataGrid" Version="12.0.0"/>
<PackageReference Include="Avalonia.Desktop" Version="11.0.5" /> <PackageReference Include="Avalonia.Desktop" Version="12.0.4"/>
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.5" /> <PackageReference Include="Avalonia.Themes.Fluent" Version="12.0.4"/>
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.5" /> <PackageReference Include="Avalonia.Fonts.Inter" Version="12.0.4"/>
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.5" /> <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.3.17"/>
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.5" /> <PackageReference Include="Avalonia.ReactiveUI" Version="11.3.8"/>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/> <PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
<PackageReference Include="System.Data.SQLite" Version="1.0.118" /> <PackageReference Include="System.Data.SQLite" Version="2.0.3"/>
</ItemGroup> </ItemGroup>
@@ -65,7 +67,7 @@
<Compile Include="obj\Debug\net6.0\.NETCoreApp,Version=v6.0.AssemblyAttributes.cs"/> <Compile Include="obj\Debug\net6.0\.NETCoreApp,Version=v6.0.AssemblyAttributes.cs"/>
<Compile Include="obj\Debug\net6.0\PcgsInvUi.AssemblyInfo.cs"/> <Compile Include="obj\Debug\net6.0\PcgsInvUi.AssemblyInfo.cs"/>
<Compile Remove="obj\**"/> <Compile Remove="obj\**"/>
<Compile Remove="Services\CoinCollection.cs" /> <Compile Remove="Services\CoinCollection.cs"/>
</ItemGroup> </ItemGroup>
@@ -90,6 +92,11 @@
<ItemGroup> <ItemGroup>
<Reference Include="System.Data" /> <Reference Include="System.Data"/>
</ItemGroup>
<ItemGroup>
<Folder Include="Models\"/>
</ItemGroup> </ItemGroup>
</Project> </Project>
+9 -7
View File
@@ -1,22 +1,24 @@
using Avalonia; using System;
using Avalonia;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using System;
using PcgsInvUi.ViewModels;
namespace PcgsInvUi; namespace PcgsInvUi;
class Program { internal class Program {
// Initialization code. Don't use any Avalonia, third-party APIs or any // Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break. // yet and stuff might break.
[STAThread] [STAThread]
public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); public static void Main(string[] args) {
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
// Avalonia configuration, don't remove; also used by visual designer. // Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp() public static AppBuilder BuildAvaloniaApp() {
=> AppBuilder.Configure<App>() return AppBuilder.Configure<App>()
.UsePlatformDetect() .UsePlatformDetect()
.WithInterFont() .WithInterFont()
.LogToTrace() .LogToTrace()
.UseReactiveUI(); .UseReactiveUI();
}
} }
+128 -130
View File
@@ -1,22 +1,17 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Data.SQLite; using System.Data.SQLite;
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using PcgsInvUi.Models;
namespace PcgsInvUi.Services; namespace PcgsInvUi.Services;
public class CoinDatabase { public class CoinDatabase {
public SQLiteConnection Connection { get; set; }
private Boolean IsConnected { get; set; }
public ObservableCollection<Coin> Collection { get; set; }
public Boolean IsClientConnected { get; set; }
private PcgsClient? _pcgsClient; private PcgsClient? _pcgsClient;
/// <summary> /// <summary>
/// Creates and initilizes a new instance of the CoinDatabase. /// Creates and initilizes a new instance of the CoinDatabase.
/// Must call TryInitApiClient() after creating a new instance. /// Must call TryInitApiClient() after creating a new instance.
/// </summary> /// </summary>
public CoinDatabase() { public CoinDatabase() {
// Create a connection to SQLite // Create a connection to SQLite
@@ -25,13 +20,18 @@ public class CoinDatabase {
CreateCollection("CollectionTable"); CreateCollection("CollectionTable");
CreateApiTable(); CreateApiTable();
Console.WriteLine("Connected to database."); Console.WriteLine("Connected to database.");
Collection = new ObservableCollection<Coin>(); Collection = new ObservableCollection<PcgsCoin>();
GetCollection("CollectionTable"); GetCollection("CollectionTable");
Console.WriteLine("Got collection."); Console.WriteLine("Got collection.");
} }
public SQLiteConnection Connection { get; set; }
private bool IsConnected { get; set; }
public ObservableCollection<PcgsCoin> Collection { get; set; }
public bool IsClientConnected { get; set; }
/// <summary> /// <summary>
/// Destructor. Currently not used, due to the strange nature of desktop applications. /// Destructor. Currently not used, due to the strange nature of desktop applications.
/// </summary> /// </summary>
~CoinDatabase() { ~CoinDatabase() {
Console.WriteLine("Destructor called, saving collection."); Console.WriteLine("Destructor called, saving collection.");
@@ -40,14 +40,14 @@ public class CoinDatabase {
// Close the connection. // Close the connection.
Connection.Close(); Connection.Close();
} }
/// <summary> /// <summary>
/// Attempts to intitialize the API client by selecting the API key from the database. /// Attempts to intitialize the API client by selecting the API key from the database.
/// </summary> /// </summary>
/// <returns>True if the API key was found, false otherwise.</returns> /// <returns>True if the API key was found, false otherwise.</returns>
public async Task<bool> TryInitApiClient() { public async Task<bool> TryInitApiClient() {
Console.Write("Attempting to initialize API client... "); Console.Write("Attempting to initialize API client... ");
SQLiteCommand cmd = Connection.CreateCommand(); var cmd = Connection.CreateCommand();
cmd.CommandText = "SELECT API_KEY FROM ApiTable"; cmd.CommandText = "SELECT API_KEY FROM ApiTable";
using (var reader = cmd.ExecuteReader()) { using (var reader = cmd.ExecuteReader()) {
@@ -70,13 +70,14 @@ public class CoinDatabase {
Console.WriteLine("Failed. API key not found."); Console.WriteLine("Failed. API key not found.");
} }
} }
return IsClientConnected; return IsClientConnected;
} }
/// <summary> /// <summary>
/// Atttempts to initialize the PCGS API client. /// Atttempts to initialize the PCGS API client.
/// </summary> /// </summary>
/// <param name=apiKey>The API key to verify.</param> /// <param name= apiKey>The API key to verify.</param>
/// <returns>A Task with a return value that returns true if the client intializes properly.</returns> /// <returns>A Task with a return value that returns true if the client intializes properly.</returns>
public async Task<bool> TryInitApiClient(string apiKey) { public async Task<bool> TryInitApiClient(string apiKey) {
Console.Write("Attempting to initialize API client with key param... "); Console.Write("Attempting to initialize API client with key param... ");
@@ -98,23 +99,23 @@ public class CoinDatabase {
} }
/// <summary> /// <summary>
/// Gets the collection from the database and stores it in the Collection property. /// Gets the collection from the database and stores it in the Collection property.
/// Creates the collection table if it does not exist already. /// Creates the collection table if it does not exist already.
/// </summary> /// </summary>
/// <returns>The SQLiteConnection that corresponds to the connected database.</returns> /// <returns>The SQLiteConnection that corresponds to the connected database.</returns>
private SQLiteConnection CreateConnection() { private SQLiteConnection CreateConnection() {
// Check if database exists. // Check if database exists.
if (!System.IO.File.Exists("coin_collection")) { if (!File.Exists("coin_collection")) {
// If not, create the database. // If not, create the database.
Console.Write("Creating database... "); Console.Write("Creating database... ");
SQLiteConnection.CreateFile("coin_collection.sqlite"); SQLiteConnection.CreateFile("coin_collection.sqlite");
Console.WriteLine("Done."); Console.WriteLine("Done.");
} }
// Create a connection to the database. // Create a connection to the database.
SQLiteConnection connection = var connection =
new SQLiteConnection("DataSource=coin_collection"); new SQLiteConnection("DataSource=coin_collection");
// Open the connection. // Open the connection.
try { try {
connection.Open(); connection.Open();
@@ -128,7 +129,7 @@ public class CoinDatabase {
} }
/// <summary> /// <summary>
/// Creates the table that stores the API key and the number of requests remaining. /// Creates the table that stores the API key and the number of requests remaining.
/// </summary> /// </summary>
public void CreateApiTable() { public void CreateApiTable() {
// Create a table to store the API key and the number of requests remaining. // Create a table to store the API key and the number of requests remaining.
@@ -139,12 +140,12 @@ public class CoinDatabase {
} }
// Create a SQL command. // Create a SQL command.
SQLiteCommand cmd = Connection.CreateCommand(); var cmd = Connection.CreateCommand();
cmd.CommandText = cmd.CommandText =
"CREATE TABLE IF NOT EXISTS ApiTable (" + "CREATE TABLE IF NOT EXISTS ApiTable (" +
"API_KEY TEXT, " + "API_KEY TEXT, " +
"REQUESTS_REMAINING INTEGER, " + "REQUESTS_REMAINING INTEGER, " +
"PRIMARY KEY(API_KEY))"; "PRIMARY KEY(API_KEY))";
// Execute the command // Execute the command
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
@@ -152,7 +153,7 @@ public class CoinDatabase {
} }
/// <summary> /// <summary>
/// Creates a table to store the collection. /// Creates a table to store the collection.
/// </summary> /// </summary>
public void CreateCollection(string collectionName) { public void CreateCollection(string collectionName) {
// Create a table to store the collection. // Create a table to store the collection.
@@ -161,9 +162,9 @@ public class CoinDatabase {
Console.WriteLine("Not connected to database."); Console.WriteLine("Not connected to database.");
return; return;
} }
// Create a SQL command. // Create a SQL command.
SQLiteCommand cmd = Connection.CreateCommand(); var cmd = Connection.CreateCommand();
cmd.CommandText = cmd.CommandText =
$"CREATE TABLE IF NOT EXISTS {collectionName} (" + $"CREATE TABLE IF NOT EXISTS {collectionName} (" +
"PCGS_NUMBER TEXT," + "PCGS_NUMBER TEXT," +
@@ -184,16 +185,16 @@ public class CoinDatabase {
"QUANTITY INTEGER," + "QUANTITY INTEGER," +
"NOTES TEXT," + "NOTES TEXT," +
"PRIMARY KEY(PCGS_NUMBER, GRADE))"; "PRIMARY KEY(PCGS_NUMBER, GRADE))";
// Execute the command. // Execute the command.
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
Console.WriteLine("Collection table initialized."); Console.WriteLine("Collection table initialized.");
} }
/// <summary> /// <summary>
/// Inserts a Coin object into the database. /// Inserts a Coin object into the database.
/// </summary> /// </summary>
public void InsertCoin(Coin coin) { public void InsertCoin(PcgsCoin pcgsCoin) {
Console.WriteLine("Inserting coin..."); Console.WriteLine("Inserting coin...");
// Use our existing connection to insert a coin to the database. // Use our existing connection to insert a coin to the database.
if (!IsConnected) { if (!IsConnected) {
@@ -201,26 +202,26 @@ public class CoinDatabase {
Console.WriteLine("Not connected to database."); Console.WriteLine("Not connected to database.");
return; return;
} }
// Create a SQL command. // Create a SQL command.
SQLiteCommand cmd = Connection.CreateCommand(); var cmd = Connection.CreateCommand();
cmd.CommandText = cmd.CommandText =
"INSERT INTO CollectionTable (PCGS_NUMBER, GRADE, YEAR," + "INSERT INTO CollectionTable (PCGS_NUMBER, GRADE, YEAR," +
"DENOMINATION, MINT_MARK, PRICE_GUIDE_VALUE, COIN_FACTS_LINK," + "DENOMINATION, MINT_MARK, PRICE_GUIDE_VALUE, COIN_FACTS_LINK," +
"MAJOR_VARIETY, MINOR_VARIETY, DIE_VARIETY, SERIES_NAME, CATEGORY, DESIGNATION," + "MAJOR_VARIETY, MINOR_VARIETY, DIE_VARIETY, SERIES_NAME, CATEGORY, DESIGNATION," +
"CERT_NUM, QUANTITY, NOTES) " + "CERT_NUM, QUANTITY, NOTES) " +
$"VALUES ('{coin.PCGSNo}', '{coin.Grade}', {coin.Year}, '{coin.Denomination}', '{coin.MintMark}', {coin.PriceGuideValue}," + $"VALUES ('{pcgsCoin.PcgsNumber}', '{pcgsCoin.Grade}', {pcgsCoin.Year}, '{pcgsCoin.Denomination}', '{pcgsCoin.MintMark}', {pcgsCoin.PriceGuideValue}," +
$"'{coin.CoinFactsLink}', '{coin.MajorVariety}', '{coin.MinorVariety}', '{coin.DieVariety}', '{coin.SeriesName}', " + $"'{pcgsCoin.CoinFactsLink}', '{pcgsCoin.MajorVariety}', '{pcgsCoin.MinorVariety}', '{pcgsCoin.DieVariety}', '{pcgsCoin.SeriesName}', " +
$"'{coin.Category}', '{coin.Designation}', {coin.CertificateNumber}, {coin.Quantity}, @Notes)"; $"'{pcgsCoin.Category}', '{pcgsCoin.Designation}', {pcgsCoin.CertificateNumber}, {pcgsCoin.Quantity}, @Notes)";
cmd.Parameters.AddWithValue("@Notes", coin.Notes); cmd.Parameters.AddWithValue("@Notes", pcgsCoin.Notes);
// Execute the command. // Execute the command.
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
Console.WriteLine("Coin inserted."); Console.WriteLine("Coin inserted.");
} }
/// <summary> /// <summary>
/// Creates a Coin object and saves it to the database. /// Creates a Coin object and saves it to the database.
/// </summary> /// </summary>
/// <param name="pcgsNumber">The PCGS number of the coin.</param> /// <param name="pcgsNumber">The PCGS number of the coin.</param>
/// <param name="grade">The grade of the coin.</param> /// <param name="grade">The grade of the coin.</param>
@@ -236,7 +237,7 @@ public class CoinDatabase {
SaveCoin("CollectionTable", coin); SaveCoin("CollectionTable", coin);
return PcgsClient.ErrorType.None; return PcgsClient.ErrorType.None;
} }
// Otherwise, build a new coin with the API and insert it. // Otherwise, build a new coin with the API and insert it.
var apiResult = await _pcgsClient.GetCoinFactsByGrade(pcgsNumber, grade); var apiResult = await _pcgsClient.GetCoinFactsByGrade(pcgsNumber, grade);
if (apiResult.Item1 == PcgsClient.ErrorType.None) { if (apiResult.Item1 == PcgsClient.ErrorType.None) {
@@ -246,68 +247,68 @@ public class CoinDatabase {
Collection.Add(newCoin); Collection.Add(newCoin);
return PcgsClient.ErrorType.None; return PcgsClient.ErrorType.None;
} }
Console.WriteLine("Error getting coin from API - {0}", apiResult.Item1); Console.WriteLine("Error getting coin from API - {0}", apiResult.Item1);
return apiResult.Item1; return apiResult.Item1;
} }
/// <summary> /// <summary>
/// Gets a coin from the database. /// Gets a coin from the database.
/// </summary> /// </summary>
/// <param name="pcgsNumber">The PCGS number of the coin.</param> /// <param name="pcgsNumber">The PCGS number of the coin.</param>
/// <param name="grade">The grade of the coin.</param> /// <param name="grade">The grade of the coin.</param>
/// <returns>The coin if it exists, null otherwise.</returns> /// <returns>The coin if it exists, null otherwise.</returns>
public Coin? GetCoin(int pcgsNumber, string grade) { public PcgsCoin? GetCoin(int pcgsNumber, string grade) {
Console.WriteLine($"Getting coin {pcgsNumber}, {grade}..."); Console.WriteLine($"Getting coin {pcgsNumber}, {grade}...");
int gradeInt = GetGradeFromString(grade); var gradeInt = GetGradeFromString(grade);
// Check the collection to see if the coin exists. // Check the collection to see if the coin exists.
// We can't make a more efficient algorithm since there's not really an ordering to the collection. // We can't make a more efficient algorithm since there's not really an ordering to the collection.
foreach (var coin in Collection) { foreach (var coin in Collection)
if (coin.PCGSNo == pcgsNumber.ToString() && coin.Grade.Contains(gradeInt.ToString())) { // This has the possibility of a false positive for single digit grades. if (coin.PcgsNumber == pcgsNumber.ToString() && coin.Grade.Contains(gradeInt.ToString())) {
// This has the possibility of a false positive for single digit grades.
if (gradeInt < 10) if (gradeInt < 10)
if (char.IsDigit(coin.Grade[3])) continue; // TODO For right now, we're going to assume that the first 2 characters are ciruclation strings, and the next 2 are grade numbers. if (char.IsDigit(coin.Grade[3]))
continue; // TODO For right now, we're going to assume that the first 2 characters are ciruclation strings, and the next 2 are grade numbers.
Console.WriteLine("Found coin."); Console.WriteLine("Found coin.");
return coin; return coin;
} }
}
Console.WriteLine("Couldn't find coin."); Console.WriteLine("Couldn't find coin.");
return null; return null;
} }
/// <summary> /// <summary>
/// Used to get all Coin objects within the database. /// Used to get all Coin objects within the database.
/// </summary> /// </summary>
/// <param name="collectionName">The name of the collection table.</param> /// <param name="collectionName">The name of the collection table.</param>
public void GetCollection(string collectionName) { public void GetCollection(string collectionName) {
Console.WriteLine("Getting collection..."); Console.WriteLine("Getting collection...");
SQLiteCommand cmd = Connection.CreateCommand(); var cmd = Connection.CreateCommand();
cmd.CommandText = $"SELECT * FROM {collectionName}"; cmd.CommandText = $"SELECT * FROM {collectionName}";
using (var reader = cmd.ExecuteReader()) { using (var reader = cmd.ExecuteReader()) {
while (reader.Read()) { while (reader.Read())
// Create coin from reader then add to list. // Create coin from reader then add to list.
Collection.Add(CoinFromReader(reader)); Collection.Add(CoinFromReader(reader));
}
} }
} }
/// <summary> /// <summary>
/// Creates a CSV formatted string from a collection. /// Creates a CSV formatted string from a collection.
/// </summary> /// </summary>
/// <param name="collectionName">The name of the collection table.</param> /// <param name="collectionName">The name of the collection table.</param>
/// <returns>A CSV formatted string.</returns> /// <returns>A CSV formatted string.</returns>
public String CollectionToCSV(string collectionName) { public string CollectionToCSV(string collectionName) {
var cmd = Connection.CreateCommand(); var cmd = Connection.CreateCommand();
cmd.CommandText = $"SELECT * FROM {collectionName}"; cmd.CommandText = $"SELECT * FROM {collectionName}";
var headersCommand = Connection.CreateCommand(); var headersCommand = Connection.CreateCommand();
headersCommand.CommandText = $"PRAGMA table_info({collectionName})"; headersCommand.CommandText = $"PRAGMA table_info({collectionName})";
var resultString = ""; var resultString = "";
var numHeaders = 0; var numHeaders = 0;
// Get the headers. // Get the headers.
using (var reader = headersCommand.ExecuteReader()) { using (var reader = headersCommand.ExecuteReader()) {
while (reader.Read()) { while (reader.Read()) {
@@ -322,23 +323,20 @@ public class CoinDatabase {
using (var reader = cmd.ExecuteReader()) { using (var reader = cmd.ExecuteReader()) {
while (reader.Read()) { while (reader.Read()) {
// Read information from the reader, then add to CSV string. // Read information from the reader, then add to CSV string.
for (int i = 0; i < numHeaders; i++) { for (var i = 0; i < numHeaders; i++) {
// Get the type of each column. // Get the type of each column.
var type = reader.GetFieldType(i); var type = reader.GetFieldType(i);
Console.WriteLine("Type: " + type); Console.WriteLine("Type: " + type);
if (type == typeof(Int64)) { if (type == typeof(long))
resultString += reader.GetInt32(i) + ","; resultString += reader.GetInt32(i) + ",";
} else if (type == typeof(double))
else if (type == typeof(Double)) {
resultString += reader.GetDouble(i) + ","; resultString += reader.GetDouble(i) + ",";
} else if (type == typeof(string))
else if (type == typeof(String)) {
resultString += reader.GetString(i) + ","; resultString += reader.GetString(i) + ",";
} else
else {
resultString += "NULL" + ","; resultString += "NULL" + ",";
}
} }
resultString += "\n"; resultString += "\n";
} }
} }
@@ -347,91 +345,91 @@ public class CoinDatabase {
} }
/// <summary> /// <summary>
/// Saves a Coin object to the database. /// Saves a Coin object to the database.
/// </summary> /// </summary>
/// <param name="collectionName">The name of the collection table.</param> /// <param name="collectionName">The name of the collection table.</param>
/// <param name="coin">The coin to save.</param> /// <param name="pcgsCoin">The coin to save.</param>
public void SaveCoin(string collectionName, Coin coin) { public void SaveCoin(string collectionName, PcgsCoin pcgsCoin) {
// Get and replace the coin from the database. // Get and replace the coin from the database.
Console.WriteLine("Saving coin..."); Console.WriteLine("Saving coin...");
SQLiteCommand cmd = Connection.CreateCommand(); var cmd = Connection.CreateCommand();
cmd.CommandText = $"UPDATE {collectionName} " + cmd.CommandText = $"UPDATE {collectionName} " +
$"SET YEAR = {coin.Year}, " + $"SET YEAR = {pcgsCoin.Year}, " +
$"DENOMINATION = \'{coin.Denomination}\', " + $"DENOMINATION = \'{pcgsCoin.Denomination}\', " +
$"MINT_MARK = \'{coin.MintMark}\', " + $"MINT_MARK = \'{pcgsCoin.MintMark}\', " +
$"PRICE_GUIDE_VALUE = \'{coin.PriceGuideValue}\', " + $"PRICE_GUIDE_VALUE = \'{pcgsCoin.PriceGuideValue}\', " +
$"COIN_FACTS_LINK = \'{coin.CoinFactsLink}\', " + $"COIN_FACTS_LINK = \'{pcgsCoin.CoinFactsLink}\', " +
$"MAJOR_VARIETY = \'{coin.MajorVariety}\', " + $"MAJOR_VARIETY = \'{pcgsCoin.MajorVariety}\', " +
$"MINOR_VARIETY = \'{coin.MinorVariety}\', " + $"MINOR_VARIETY = \'{pcgsCoin.MinorVariety}\', " +
$"DIE_VARIETY = \'{coin.DieVariety}\', " + $"DIE_VARIETY = \'{pcgsCoin.DieVariety}\', " +
$"SERIES_NAME = \'{coin.SeriesName}\', " + $"SERIES_NAME = \'{pcgsCoin.SeriesName}\', " +
$"CATEGORY = \'{coin.Category}\', " + $"CATEGORY = \'{pcgsCoin.Category}\', " +
$"DESIGNATION = \'{coin.Designation}\', " + $"DESIGNATION = \'{pcgsCoin.Designation}\', " +
$"CERT_NUM = \'{coin.CertificateNumber}\', " + $"CERT_NUM = \'{pcgsCoin.CertificateNumber}\', " +
$"QUANTITY = {coin.Quantity}, " + $"QUANTITY = {pcgsCoin.Quantity}, " +
$"NOTES = @Notes " + $"NOTES = @Notes " +
$"WHERE PCGS_NUMBER = {coin.PCGSNo} AND GRADE = \'{coin.Grade}\'"; $"WHERE PCGS_NUMBER = {pcgsCoin.PcgsNumber} AND GRADE = \'{pcgsCoin.Grade}\'";
cmd.Parameters.AddWithValue("@Notes", coin.Notes); cmd.Parameters.AddWithValue("@Notes", pcgsCoin.Notes);
Console.WriteLine(cmd.CommandText); Console.WriteLine(cmd.CommandText);
// Execute the command. // Execute the command.
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
} }
/// <summary> /// <summary>
/// Synchronizes the database with the current contents of the specified collection. /// Synchronizes the database with the current contents of the specified collection.
/// </summary> /// </summary>
/// <param name="collectionName">The name of the collection table.</param> /// <param name="collectionName">The name of the collection table.</param>
public void UpdateCollection(string collectionName) { public void UpdateCollection(string collectionName) {
// Update the database with the contents of the collection. // Update the database with the contents of the collection.
// TODO This is a very inefficient way of doing this. I should be able to do this in one command. // TODO This is a very inefficient way of doing this. I should be able to do this in one command.
foreach (var coin in Collection) { foreach (var coin in
SaveCoin(collectionName, coin); // CAREFUL! This will not create a coin if it doesn't currently exist. Collection)
} SaveCoin(collectionName, coin); // CAREFUL! This will not create a coin if it doesn't currently exist.
} }
/// <summary> /// <summary>
/// Creates a Coin from the information in a reader specifically from this database. /// Creates a Coin from the information in a reader specifically from this database.
/// </summary> /// </summary>
/// <param name="reader">The reader to get the information from.</param> /// <param name="reader">The reader to get the information from.</param>
private Coin CoinFromReader(SQLiteDataReader reader) { private PcgsCoin CoinFromReader(SQLiteDataReader reader) {
// Create a coin while filtering out any possible null values - they throw exceptions. // Create a coin while filtering out any possible null values - they throw exceptions.
Coin coin = new Coin(); PcgsCoin pcgsCoin = new PcgsCoin();
if (!reader.IsDBNull(0)) coin.PCGSNo = reader.GetString(0); if (!reader.IsDBNull(0)) pcgsCoin.PcgsNumber = reader.GetString(0);
if (!reader.IsDBNull(1)) coin.Grade = reader.GetString(1); if (!reader.IsDBNull(1)) pcgsCoin.Grade = reader.GetString(1);
if (!reader.IsDBNull(2)) coin.Year = reader.GetInt32(2); if (!reader.IsDBNull(2)) pcgsCoin.Year = reader.GetInt32(2);
if (!reader.IsDBNull(3)) coin.Denomination = reader.GetString(3); if (!reader.IsDBNull(3)) pcgsCoin.Denomination = reader.GetString(3);
if (!reader.IsDBNull(4)) coin.MintMark = reader.GetString(4); if (!reader.IsDBNull(4)) pcgsCoin.MintMark = reader.GetString(4);
if (!reader.IsDBNull(5)) coin.PriceGuideValue = reader.GetDouble(5); if (!reader.IsDBNull(5)) pcgsCoin.PriceGuideValue = reader.GetDouble(5);
if (!reader.IsDBNull(6)) coin.CoinFactsLink = reader.GetString(6); if (!reader.IsDBNull(6)) pcgsCoin.CoinFactsLink = reader.GetString(6);
if (!reader.IsDBNull(7)) coin.MajorVariety = reader.GetString(7); if (!reader.IsDBNull(7)) pcgsCoin.MajorVariety = reader.GetString(7);
if (!reader.IsDBNull(8)) coin.MinorVariety = reader.GetString(8); if (!reader.IsDBNull(8)) pcgsCoin.MinorVariety = reader.GetString(8);
if (!reader.IsDBNull(9)) coin.DieVariety = reader.GetString(9); if (!reader.IsDBNull(9)) pcgsCoin.DieVariety = reader.GetString(9);
if (!reader.IsDBNull(10)) coin.SeriesName = reader.GetString(10); if (!reader.IsDBNull(10)) pcgsCoin.SeriesName = reader.GetString(10);
if (!reader.IsDBNull(11)) coin.Category = reader.GetString(11); if (!reader.IsDBNull(11)) pcgsCoin.Category = reader.GetString(11);
if (!reader.IsDBNull(12)) coin.Designation = reader.GetString(12); if (!reader.IsDBNull(12)) pcgsCoin.Designation = reader.GetString(12);
if (!reader.IsDBNull(13)) coin.CertificateNumber = reader.GetInt32(13); if (!reader.IsDBNull(13)) pcgsCoin.CertificateNumber = reader.GetInt32(13);
if (!reader.IsDBNull(14)) coin.Quantity = reader.GetInt32(14); if (!reader.IsDBNull(14)) pcgsCoin.Quantity = reader.GetInt32(14);
if (!reader.IsDBNull(15)) coin.Notes = reader.GetString(15); if (!reader.IsDBNull(15)) pcgsCoin.Notes = reader.GetString(15);
coin.TotalPrice = coin.Quantity * coin.PriceGuideValue; // pcgsCoin.TotalPrice = pcgsCoin.Quantity * pcgsCoin.PriceGuideValue;
return coin; return pcgsCoin;
} }
/// <summary> /// <summary>
/// Deletes a Coin object from the database. /// Deletes a Coin object from the database.
/// </summary> /// </summary>
/// <param name="delCoin">The coin to delete.</param> /// <param name="delPcgsCoin">The coin to delete.</param>
public void DeleteCoin(Coin delCoin) { public void DeleteCoin(PcgsCoin delPcgsCoin) {
SQLiteCommand cmd = Connection.CreateCommand(); var cmd = Connection.CreateCommand();
cmd.CommandText = "DELETE FROM CollectionTable WHERE PCGS_NUMBER = @PcgsNumber AND GRADE = @Grade"; cmd.CommandText = "DELETE FROM CollectionTable WHERE PCGS_NUMBER = @PcgsNumber AND GRADE = @Grade";
cmd.Parameters.AddWithValue("@PcgsNumber", delCoin.PCGSNo); cmd.Parameters.AddWithValue("@PcgsNumber", delPcgsCoin.PcgsNumber);
cmd.Parameters.AddWithValue("@Grade", delCoin.Grade); cmd.Parameters.AddWithValue("@Grade", delPcgsCoin.Grade);
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
} }
/// <summary> /// <summary>
/// Gets the grade from a string returned from the main window. /// Gets the grade from a string returned from the main window.
/// These are always in a format similar to "MS-65", so we can just get the last two characters. /// These are always in a format similar to "MS-65", so we can just get the last two characters.
/// </summary> /// </summary>
/// <param name="grade">The grade string.</param> /// <param name="grade">The grade string.</param>
private int GetGradeFromString(string grade) { private int GetGradeFromString(string grade) {
@@ -443,4 +441,4 @@ public class CoinDatabase {
public class NotConnectedToDatabaseException : Exception { public class NotConnectedToDatabaseException : Exception {
public NotConnectedToDatabaseException() : base("Not connected to database.") { } public NotConnectedToDatabaseException() : base("Not connected to database.") { }
} }
+1 -3
View File
@@ -10,9 +10,7 @@ public class ViewLocator : IDataTemplate {
var name = data.GetType().FullName!.Replace("ViewModel", "View"); var name = data.GetType().FullName!.Replace("ViewModel", "View");
var type = Type.GetType(name); var type = Type.GetType(name);
if (type != null) { if (type != null) return (Control)Activator.CreateInstance(type)!;
return (Control)Activator.CreateInstance(type)!;
}
return new TextBlock { Text = "Not Found: " + name }; return new TextBlock { Text = "Not Found: " + name };
} }
+14 -26
View File
@@ -1,30 +1,18 @@
using ReactiveUI; using System.Diagnostics;
using System.Reactive; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace PcgsInvUi.ViewModels; namespace PcgsInvUi.ViewModels;
public class ApiKeyViewModel : ViewModelBase { public partial class ApiKeyViewModel : ViewModelBase {
public string ApiKeyInput { [ObservableProperty]
get => _apiKeyInput; public partial string ApiKeyInput { get; set; }
set => this.RaiseAndSetIfChanged(ref _apiKeyInput, value);
[ObservableProperty]
public partial bool OkEnabled { get; set; }
[RelayCommand]
public void OkCommand() {
Debug.WriteLine("Running OkCommand from ApiKeyViewModel");
} }
private string _apiKeyInput = ""; }
public bool OkEnabled {
get => _okEnabled;
set => this.RaiseAndSetIfChanged(ref _okEnabled, value);
}
private bool _okEnabled;
public ReactiveCommand<Unit, string> OkCommand { get; }
public ApiKeyViewModel() {
var okEnabled = this.WhenAnyValue(
x => x.ApiKeyInput,
x => !string.IsNullOrWhiteSpace(x));
// Get the API key from the user.
OkCommand = ReactiveCommand.Create(() => ApiKeyInput, okEnabled);
}
}
+12 -15
View File
@@ -1,23 +1,20 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using PcgsInvUi.Models; using CommunityToolkit.Mvvm.ComponentModel;
using ReactiveUI;
namespace PcgsInvUi.ViewModels; namespace PcgsInvUi.ViewModels;
public class CoinListViewModel : ViewModelBase { public partial class CoinListViewModel : ViewModelBase {
public ObservableCollection<Coin> CoinCollection { get; set; } public CoinListViewModel(IEnumerable<PcgsCoin> items) {
private float _totalValue; CoinCollection = new ObservableCollection<PcgsCoin>(items);
public float TotalValue {
get => _totalValue;
set => this.RaiseAndSetIfChanged(ref _totalValue, value);
}
public Coin? SelectedCoin { get; set; }
public CoinListViewModel(IEnumerable<Coin> items) {
CoinCollection = new ObservableCollection<Coin>(items);
TotalValue = 0; TotalValue = 0;
} }
public ObservableCollection<PcgsCoin> CoinCollection { get; set; }
[ObservableProperty]
public partial float TotalValue { get; set; }
[ObservableProperty]
public partial PcgsCoin? SelectedCoin { get; set; }
} }
@@ -4,11 +4,11 @@ using ReactiveUI;
namespace PcgsInvUi.ViewModels; namespace PcgsInvUi.ViewModels;
public class DeleteWindowViewModel : ViewModelBase { public class DeleteWindowViewModel : ViewModelBase {
public ReactiveCommand<Unit, object> OkCommand { get; }
public ReactiveCommand<Unit, object> CancelCommand { get; }
public DeleteWindowViewModel() { public DeleteWindowViewModel() {
OkCommand = ReactiveCommand.Create(() => (object)true); OkCommand = ReactiveCommand.Create(() => (object)true);
CancelCommand = ReactiveCommand.Create(() => (object)false); CancelCommand = ReactiveCommand.Create(() => (object)false);
} }
}
public ReactiveCommand<Unit, object> OkCommand { get; }
public ReactiveCommand<Unit, object> CancelCommand { get; }
}
+11 -12
View File
@@ -1,27 +1,26 @@
using System.Reactive; using System.Reactive;
using PcgsInvUi.Models;
using ReactiveUI; using ReactiveUI;
namespace PcgsInvUi.ViewModels; namespace PcgsInvUi.ViewModels;
public class EditViewModel : ViewModelBase { public class EditViewModel : ViewModelBase {
public Coin CurrentCoin { get; set; } public EditViewModel(PcgsCoin pcgsCoin) {
CurrentPcgsCoin = pcgsCoin;
public ReactiveCommand<Unit, Unit> OkCommand { get; }
public EditViewModel(Coin coin) {
CurrentCoin = coin;
var okEnabled = this.WhenAnyValue( var okEnabled = this.WhenAnyValue(
x => x.CurrentCoin.PCGSNo, x => x.CurrentPcgsCoin.PcgsNumber,
x => x.CurrentCoin.Grade, x => x.CurrentPcgsCoin.Grade,
x => x.CurrentCoin.Quantity, x => x.CurrentPcgsCoin.Quantity,
(id, grade, quantity) => !string.IsNullOrWhiteSpace(id) && (id, grade, quantity) => !string.IsNullOrWhiteSpace(id) &&
!string.IsNullOrWhiteSpace(grade) && !string.IsNullOrWhiteSpace(grade) &&
(quantity != 0) quantity != 0
); );
// Setup OkCommand - all Coin-related properties must be non-whitespace. // Setup OkCommand - all Coin-related properties must be non-whitespace.
OkCommand = ReactiveCommand.Create(() => { }); OkCommand = ReactiveCommand.Create(() => { });
} }
}
public PcgsCoin CurrentPcgsCoin { get; set; }
public ReactiveCommand<Unit, Unit> OkCommand { get; }
}
+17 -18
View File
@@ -1,23 +1,22 @@
using System.Reactive; using Avalonia.Controls;
using ReactiveUI; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace PcgsInvUi.ViewModels; namespace PcgsInvUi.ViewModels;
public class ErrorWindowViewModel : ViewModelBase { public partial class ErrorWindowViewModel : ViewModelBase {
private readonly Window _dialog;
public string DisplayedMessage { public ErrorWindowViewModel(Window dialog, string message) {
get => _message; _dialog = dialog;
set => this.RaiseAndSetIfChanged(ref _message, value); Message = message;
}
private string _message;
public ReactiveCommand<Unit, object> CloseCommand { get; }
public ErrorWindowViewModel(string message)
{
_message = message;
CloseCommand = ReactiveCommand.Create(() => (object)false);
} }
} [ObservableProperty]
public partial string Message { get; set; }
[RelayCommand]
public void Close() {
_dialog.Close();
}
}
+44 -45
View File
@@ -1,45 +1,19 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.Linq;
using System.Reactive; using System.Reactive;
using System.Reactive.Linq; using System.Reactive.Linq;
using PcgsInvUi.Models; using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using PcgsInvUi.Services; using PcgsInvUi.Services;
using ReactiveUI; using ReactiveUI;
using System.Threading.Tasks;
namespace PcgsInvUi.ViewModels; namespace PcgsInvUi.ViewModels;
public class MainWindowViewModel : ViewModelBase { public partial class MainWindowViewModel : ViewModelBase {
public ViewModelBase SidebarContent {
get => _sideContent;
private set => this.RaiseAndSetIfChanged(ref _sideContent, value);
}
public ObservableCollection<Coin> DisplayedList { get; set; }
public CoinDatabase ConnectedCoinDatabase { get; set; }
public Coin? SelectedCoin {
get => _selectedCoin;
set => this.RaiseAndSetIfChanged(ref _selectedCoin, value);
}
public double TotalValue {
get => _totalValue;
set => this.RaiseAndSetIfChanged(ref _totalValue, value);
}
public ReactiveCommand<Unit, Unit> DeleteCommand { get; }
public Interaction<DeleteWindowViewModel, Boolean> ShowDeleteWindow { get; }
public ReactiveCommand<Unit, Unit> ExportCommand { get; }
public Interaction<Unit, Uri> ShowExportWindow { get; }
public Interaction<ErrorWindowViewModel, bool> ShowErrorWindow { get; }
private ViewModelBase _sideContent;
private Coin? _selectedCoin;
private double _totalValue;
public MainWindowViewModel(CoinDatabase coins) { public MainWindowViewModel(CoinDatabase coins) {
ConnectedCoinDatabase = coins; ConnectedCoinDatabase = coins;
var keyFound = Task.Run(() => coins.TryInitApiClient()).Result; var keyFound = Task.Run(coins.TryInitApiClient).Result;
Console.WriteLine("Function completed."); Console.WriteLine("Function completed.");
var newViewModel = new NewViewModel(); var newViewModel = new NewViewModel();
var apiKeyViewModel = new ApiKeyViewModel(); var apiKeyViewModel = new ApiKeyViewModel();
@@ -48,21 +22,25 @@ public class MainWindowViewModel : ViewModelBase {
TotalValue = DisplayedList.Sum(x => x.TotalPrice); TotalValue = DisplayedList.Sum(x => x.TotalPrice);
// Setup interaction to show ApiKeyWindow // Setup interaction to show ApiKeyWindow
apiKeyViewModel.OkCommand.Subscribe(async apiKey => { apiKeyViewModel.OkCommandCommand.Subscribe(async apiKey => {
if (await ConnectedCoinDatabase.TryInitApiClient(apiKey)) if (await ConnectedCoinDatabase.TryInitApiClient(apiKey))
SidebarContent = newViewModel; // Must assign this to the observable property rather than private variable. SidebarContent =
}); newViewModel; // Must assign this to the observable property rather than private variable.
});
// Check if API key must be entered. // Check if API key must be entered.
if (!keyFound) { if (!keyFound) {
Console.WriteLine("API key must be entered by user."); Console.WriteLine("API key must be entered by user.");
// Show the API key input view. // Show the API key input view.
_sideContent = apiKeyViewModel; SidebarContent = apiKeyViewModel;
} else _sideContent = newViewModel; }
else {
SidebarContent = newViewModel;
}
// Set up the ability to show the error window. // Set up the ability to show the error window.
ShowErrorWindow = new Interaction<ErrorWindowViewModel, bool>(); ShowErrorWindow = new Interaction<ErrorWindowViewModel, bool>();
// Set up the ability to open the delete window. // Set up the ability to open the delete window.
ShowDeleteWindow = new Interaction<DeleteWindowViewModel, bool>(); ShowDeleteWindow = new Interaction<DeleteWindowViewModel, bool>();
var deleteEnabled = this.WhenAnyValue(x => x.SelectedCoin) var deleteEnabled = this.WhenAnyValue(x => x.SelectedCoin)
@@ -89,23 +67,25 @@ public class MainWindowViewModel : ViewModelBase {
// Something is wrong with the file after it's been created. // Something is wrong with the file after it's been created.
Console.WriteLine(e.Message); Console.WriteLine(e.Message);
await ShowErrorWindow.Handle(new ErrorWindowViewModel(e.Message)); await ShowErrorWindow.Handle(new ErrorWindowViewModel(e.Message));
} catch (Exception e) { }
catch (Exception e) {
Console.WriteLine(e.Message); Console.WriteLine(e.Message);
await ShowErrorWindow.Handle(new ErrorWindowViewModel(e.Message)); await ShowErrorWindow.Handle(new ErrorWindowViewModel(e.Message));
} }
}); });
// Subscribe to SidebarContent.OkCommand, which is an IObservable<(int, string, int)> // Subscribe to SidebarContent.OkCommand, which is an IObservable<(int, string, int)>
// This is the same as the above, but with a different syntax. // This is the same as the above, but with a different syntax.
newViewModel.OkCommand newViewModel.OkCommand
.Subscribe(async requestStructure => { .Subscribe(async requestStructure => {
var result = await ConnectedCoinDatabase.CreateCoin(requestStructure.Item1, requestStructure.Item2, var result = await ConnectedCoinDatabase.CreateCoin(requestStructure.Item1, requestStructure.Item2,
requestStructure.Item3); requestStructure.Item3);
switch (result) { switch (result) {
case PcgsClient.ErrorType.ApiKeyInvalid: case PcgsClient.ErrorType.ApiKeyInvalid:
Console.WriteLine("API key is invalid - showing window."); Console.WriteLine("API key is invalid - showing window.");
await ShowErrorWindow.Handle(new ErrorWindowViewModel("API key is invalid - authorization failed.")); await ShowErrorWindow.Handle(
new ErrorWindowViewModel("API key is invalid - authorization failed."));
break; break;
case PcgsClient.ErrorType.InvalidRequestFormat: case PcgsClient.ErrorType.InvalidRequestFormat:
Console.WriteLine("Request format is invalid - showing window."); Console.WriteLine("Request format is invalid - showing window.");
@@ -113,14 +93,15 @@ public class MainWindowViewModel : ViewModelBase {
break; break;
case PcgsClient.ErrorType.NoCoinFound: case PcgsClient.ErrorType.NoCoinFound:
Console.WriteLine("No coin was found with the given parameters."); Console.WriteLine("No coin was found with the given parameters.");
await ShowErrorWindow.Handle(new ErrorWindowViewModel("No coin was found with the given parameters.")); await ShowErrorWindow.Handle(
new ErrorWindowViewModel("No coin was found with the given parameters."));
break; break;
} }
// Clear the text boxes. // Clear the text boxes.
newViewModel.ClearText(); newViewModel.ClearText();
}); });
// Update TotalValue anytime a coin's quantity changes. // Update TotalValue anytime a coin's quantity changes.
// Possible null reference is currently ignored - SelectedCoin will never be null. // Possible null reference is currently ignored - SelectedCoin will never be null.
this.WhenAnyValue(x => x.SelectedCoin.TotalPrice) this.WhenAnyValue(x => x.SelectedCoin.TotalPrice)
@@ -132,10 +113,28 @@ public class MainWindowViewModel : ViewModelBase {
}; };
} }
[ObservableProperty]
public partial ViewModelBase SidebarContent { get; private set; }
public ObservableCollection<PcgsCoin> DisplayedList { get; set; }
public CoinDatabase ConnectedCoinDatabase { get; set; }
[ObservableProperty]
public partial PcgsCoin? SelectedCoin { get; set; }
[ObservableProperty]
public partial double TotalValue { get; set; }
public ReactiveCommand<Unit, Unit> DeleteCommand { get; }
public Interaction<DeleteWindowViewModel, bool> ShowDeleteWindow { get; }
public ReactiveCommand<Unit, Unit> ExportCommand { get; }
public Interaction<Unit, Uri> ShowExportWindow { get; }
public Interaction<ErrorWindowViewModel, bool> ShowErrorWindow { get; }
// Create destructor to save the database when the application exits. // Create destructor to save the database when the application exits.
// This is necessary because the database is initialized in this class. // This is necessary because the database is initialized in this class.
~MainWindowViewModel() { ~MainWindowViewModel() {
Console.WriteLine("Running MainWindowViewModel destructor."); Console.WriteLine("Running MainWindowViewModel destructor.");
ConnectedCoinDatabase.UpdateCollection("CollectionTable"); ConnectedCoinDatabase.UpdateCollection("CollectionTable");
} }
} }
+23 -25
View File
@@ -1,31 +1,14 @@
using System.Reactive; using System.Collections.Generic;
using System.Collections.Generic; using System.Reactive;
using ReactiveUI; using ReactiveUI;
namespace PcgsInvUi.ViewModels; namespace PcgsInvUi.ViewModels;
public class NewViewModel : ViewModelBase { public class NewViewModel : ViewModelBase {
private string _pcgsNumber = "";
private string _grade = ""; private string _grade = "";
private string _pcgsNumber = "";
private string _quantity = ""; private string _quantity = "";
public string PcgsNumber {
get => _pcgsNumber;
set => this.RaiseAndSetIfChanged(ref _pcgsNumber, value);
}
public string Grade {
get => _grade;
set => this.RaiseAndSetIfChanged(ref _grade, value);
}
public string Quantity {
get => _quantity;
set => this.RaiseAndSetIfChanged(ref _quantity, value);
}
public List<string> GradesList { get; }
public NewViewModel() { public NewViewModel() {
var okEnabled = this.WhenAnyValue( var okEnabled = this.WhenAnyValue(
x => x.PcgsNumber, x => x.PcgsNumber,
@@ -39,9 +22,7 @@ public class NewViewModel : ViewModelBase {
() => (int.Parse(PcgsNumber), Grade, int.Parse(Quantity)), () => (int.Parse(PcgsNumber), Grade, int.Parse(Quantity)),
okEnabled); okEnabled);
ClearCommand = ReactiveCommand.Create(() => { ClearCommand = ReactiveCommand.Create(() => { ClearText(); });
ClearText();
});
// Initialize grade values for use in the view. // Initialize grade values for use in the view.
// This must be typed out manually, as there is no way to get a list of grades from the API. // This must be typed out manually, as there is no way to get a list of grades from the API.
@@ -79,15 +60,32 @@ public class NewViewModel : ViewModelBase {
}; };
} }
public string PcgsNumber {
get => _pcgsNumber;
set => this.RaiseAndSetIfChanged(ref _pcgsNumber, value);
}
public string Grade {
get => _grade;
set => this.RaiseAndSetIfChanged(ref _grade, value);
}
public string Quantity {
get => _quantity;
set => this.RaiseAndSetIfChanged(ref _quantity, value);
}
public List<string> GradesList { get; }
public ReactiveCommand<Unit, (int, string, int)> OkCommand { get; } public ReactiveCommand<Unit, (int, string, int)> OkCommand { get; }
public ReactiveCommand<Unit, Unit> ClearCommand { get; } public ReactiveCommand<Unit, Unit> ClearCommand { get; }
/// <summary> /// <summary>
/// Clears the text fields. /// Clears the text fields.
/// </summary> /// </summary>
public void ClearText() { public void ClearText() {
PcgsNumber = ""; PcgsNumber = "";
Grade = ""; Grade = "";
Quantity = ""; Quantity = "";
} }
} }
+2 -2
View File
@@ -1,5 +1,5 @@
using ReactiveUI; using CommunityToolkit.Mvvm.ComponentModel;
namespace PcgsInvUi.ViewModels; namespace PcgsInvUi.ViewModels;
public class ViewModelBase : ReactiveObject { } public class ViewModelBase : ObservableObject { }
+8 -8
View File
@@ -1,14 +1,14 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:PcgsInvUi.ViewModels" xmlns:viewModels="clr-namespace:PcgsInvUi.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="PcgsInvUi.Views.ApiKeyView" x:DataType="viewModels:ApiKeyViewModel" > x:Class="PcgsInvUi.Views.ApiKeyView" x:DataType="viewModels:ApiKeyViewModel">
<StackPanel> <StackPanel>
<Label Content="API key was not found or did not authenticate." /> <Label Content="API key was not found or did not authenticate." />
<Label Content="Please enter your API key below:" /> <Label Content="Please enter your API key below:" />
<TextBox Margin="10" Text="{Binding ApiKeyInput}" /> <TextBox Margin="10" Text="{Binding ApiKeyInput}" />
<Button HorizontalAlignment="Center" Content="Submit" Command="{Binding OkCommand}" /> <Button HorizontalAlignment="Center" Content="Submit" Command="{Binding OkCommand}" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>
+2 -2
View File
@@ -1,9 +1,9 @@
using Avalonia.Controls; using Avalonia.Controls;
namespace PcgsInvUi.Views; namespace PcgsInvUi.Views;
public partial class ApiKeyView : UserControl { public partial class ApiKeyView : UserControl {
public ApiKeyView() { public ApiKeyView() {
InitializeComponent(); InitializeComponent();
} }
} }
+1 -1
View File
@@ -21,4 +21,4 @@
HorizontalContentAlignment="Center" IsEnabled="True" HorizontalContentAlignment="Center" IsEnabled="True"
Content="No" Command="{Binding CancelCommand}" /> Content="No" Command="{Binding CancelCommand}" />
</Grid> </Grid>
</Window> </Window>
+3 -3
View File
@@ -1,7 +1,7 @@
using PcgsInvUi.ViewModels; using System;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using PcgsInvUi.ViewModels;
using ReactiveUI; using ReactiveUI;
using System;
namespace PcgsInvUi.Views; namespace PcgsInvUi.Views;
@@ -11,4 +11,4 @@ public partial class DeleteWindow : ReactiveWindow<DeleteWindowViewModel> {
this.WhenActivated(d => d(ViewModel!.OkCommand.Subscribe(Close))); this.WhenActivated(d => d(ViewModel!.OkCommand.Subscribe(Close)));
this.WhenActivated(d => d(ViewModel!.CancelCommand.Subscribe(Close))); this.WhenActivated(d => d(ViewModel!.CancelCommand.Subscribe(Close)));
} }
} }
+4 -4
View File
@@ -5,14 +5,14 @@
xmlns:viewModels="clr-namespace:PcgsInvUi.ViewModels" xmlns:viewModels="clr-namespace:PcgsInvUi.ViewModels"
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="150" mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="150"
x:Class="PcgsInvUi.Views.ErrorWindow" x:DataType="viewModels:ErrorWindowViewModel" x:Class="PcgsInvUi.Views.ErrorWindow" x:DataType="viewModels:ErrorWindowViewModel"
Title="Error!" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterOwner" Title="Error!" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterOwner"
x:Name="NameErrorWindow" CanResize="False" > x:Name="NameErrorWindow" CanResize="False">
<Grid RowDefinitions="*,*"> <Grid RowDefinitions="*,*">
<Label Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Stretch" <Label Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Stretch"
Margin="15" Margin="15"
Content="{Binding DisplayedMessage}" /> Content="{Binding Message}" />
<Button Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Bottom" <Button Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Bottom"
Margin="10" Name="CloseButton" IsEnabled="True" Margin="10" Name="CloseButton" IsEnabled="True"
Content="OK" Command="{Binding CloseCommand, Mode=OneWay}" /> Content="OK" Command="{Binding CloseCommand, Mode=OneWay}" />
</Grid> </Grid>
</Window> </Window>
+3 -6
View File
@@ -1,14 +1,11 @@
using System;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using PcgsInvUi.ViewModels; using PcgsInvUi.ViewModels;
using ReactiveUI;
namespace PcgsInvUi.Views; namespace PcgsInvUi.Views;
public partial class ErrorWindow : ReactiveWindow<ErrorWindowViewModel> { public partial class ErrorWindow : ReactiveWindow<ErrorWindowViewModel> {
public ErrorWindow() public ErrorWindow() {
{
InitializeComponent(); InitializeComponent();
this.WhenActivated(d => d(ViewModel!.CloseCommand.Subscribe(Close))); this.WhenActivated(d => d(ViewModel!.CloseCommand.Subscribe(Close)));
} }
} }
+2 -2
View File
@@ -1,7 +1,7 @@
using Avalonia.ReactiveUI; using System;
using Avalonia.ReactiveUI;
using PcgsInvUi.ViewModels; using PcgsInvUi.ViewModels;
using ReactiveUI; using ReactiveUI;
using System;
namespace PcgsInvUi.Views; namespace PcgsInvUi.Views;
+4 -4
View File
@@ -5,7 +5,7 @@
xmlns:viewModels="clr-namespace:PcgsInvUi.ViewModels" xmlns:viewModels="clr-namespace:PcgsInvUi.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="PcgsInvUi.Views.MainWindow" x:DataType="viewModels:MainWindowViewModel" x:Class="PcgsInvUi.Views.MainWindow" x:DataType="viewModels:MainWindowViewModel"
Title="Main Window" WindowStartupLocation="CenterScreen" WindowState="Maximized" > Title="Main Window" WindowStartupLocation="CenterScreen" WindowState="Maximized">
<SplitView IsPaneOpen="True" <SplitView IsPaneOpen="True"
DisplayMode="Inline" DisplayMode="Inline"
PanePlacement="Right"> PanePlacement="Right">
@@ -15,7 +15,7 @@
Content="{Binding SidebarContent}" /> Content="{Binding SidebarContent}" />
<TextBox Grid.Row="1" Margin="5" Watermark="Notes..." <TextBox Grid.Row="1" Margin="5" Watermark="Notes..."
Text="{Binding SelectedCoin.Notes}" Text="{Binding SelectedCoin.Notes}"
IsReadOnly="False" TextWrapping="Wrap"/> IsReadOnly="False" TextWrapping="Wrap" />
</Grid> </Grid>
</SplitView.Pane> </SplitView.Pane>
<Grid RowDefinitions="*, Auto" ShowGridLines="False"> <Grid RowDefinitions="*, Auto" ShowGridLines="False">
@@ -51,7 +51,7 @@
Binding="{Binding PriceGuideValue, StringFormat='${0:0.00}'}" Binding="{Binding PriceGuideValue, StringFormat='${0:0.00}'}"
IsReadOnly="True" /> IsReadOnly="True" />
<DataGridTextColumn Header="PCGS #" Width="*" <DataGridTextColumn Header="PCGS #" Width="*"
Binding="{Binding PCGSNo}" Binding="{Binding PcgsNumber}"
IsReadOnly="True" /> IsReadOnly="True" />
<DataGridTextColumn Header="Quantity" Width="*" <DataGridTextColumn Header="Quantity" Width="*"
Binding="{Binding Quantity}" Binding="{Binding Quantity}"
@@ -77,4 +77,4 @@
</DockPanel> </DockPanel>
</Grid> </Grid>
</SplitView> </SplitView>
</Window> </Window>
+4 -6
View File
@@ -44,15 +44,13 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel> {
SuggestedStartLocation = await StorageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Documents) SuggestedStartLocation = await StorageProvider.TryGetWellKnownFolderAsync(WellKnownFolder.Documents)
}); });
if (file is not null) { if (file is not null)
interationContext.SetOutput(file.Path); interationContext.SetOutput(file.Path);
} else
else {
// TODO Handle user closing the dialog. // TODO Handle user closing the dialog.
throw new Exception("User cancelled the file picker."); throw new Exception("User cancelled the file picker.");
}
} }
private async Task ShowErrorWindowAsync(InteractionContext<ErrorWindowViewModel, bool> interaction) { private async Task ShowErrorWindowAsync(InteractionContext<ErrorWindowViewModel, bool> interaction) {
var window = new ErrorWindow(); var window = new ErrorWindow();
window.DataContext = interaction.Input; window.DataContext = interaction.Input;
@@ -60,4 +58,4 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel> {
var result = await window.ShowDialog<bool>(this); var result = await window.ShowDialog<bool>(this);
interaction.SetOutput(result); interaction.SetOutput(result);
} }
} }
+13
View File
@@ -0,0 +1,13 @@
<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"
x:Class="PcgsInvUi.Views.MainWindowRewrite"
Title="MainWindowRewrite">
<Grid>
<TextBlock Text="This is a testing block!" />
</Grid>
</Window>
@@ -0,0 +1,9 @@
using Avalonia.Controls;
namespace PcgsInvUi.Views;
public partial class MainWindowRewrite : Window {
public MainWindowRewrite() {
InitializeComponent();
}
}
+1 -1
View File
@@ -6,4 +6,4 @@ public partial class NewView : UserControl {
public NewView() { public NewView() {
InitializeComponent(); InitializeComponent();
} }
} }
+12
View File
@@ -2,6 +2,10 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PcgsInvUi", "PcgsInvUi\PcgsInvUi.csproj", "{887D7BD0-3E71-435D-82CD-3DAF3187D056}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PcgsInvUi", "PcgsInvUi\PcgsInvUi.csproj", "{887D7BD0-3E71-435D-82CD-3DAF3187D056}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PcgsInv", "PcgsInv\PcgsInv.csproj", "{8B62E70B-9F3C-4E1B-9E7D-FBADC680E5D0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PcgsApi", "PcgsApi\PcgsApi.csproj", "{08B02A6D-EEA5-4335-AC5B-B1347CEBBE88}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -13,5 +17,13 @@ Global
{887D7BD0-3E71-435D-82CD-3DAF3187D056}.Release|Any CPU.ActiveCfg = Release|Any CPU {887D7BD0-3E71-435D-82CD-3DAF3187D056}.Release|Any CPU.ActiveCfg = Release|Any CPU
{887D7BD0-3E71-435D-82CD-3DAF3187D056}.Release|Any CPU.Build.0 = Release|Any CPU {887D7BD0-3E71-435D-82CD-3DAF3187D056}.Release|Any CPU.Build.0 = Release|Any CPU
{887D7BD0-3E71-435D-82CD-3DAF3187D056}.Release|Any CPU.Deploy.0 = Release|Any CPU {887D7BD0-3E71-435D-82CD-3DAF3187D056}.Release|Any CPU.Deploy.0 = Release|Any CPU
{8B62E70B-9F3C-4E1B-9E7D-FBADC680E5D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8B62E70B-9F3C-4E1B-9E7D-FBADC680E5D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B62E70B-9F3C-4E1B-9E7D-FBADC680E5D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8B62E70B-9F3C-4E1B-9E7D-FBADC680E5D0}.Release|Any CPU.Build.0 = Release|Any CPU
{08B02A6D-EEA5-4335-AC5B-B1347CEBBE88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{08B02A6D-EEA5-4335-AC5B-B1347CEBBE88}.Debug|Any CPU.Build.0 = Debug|Any CPU
{08B02A6D-EEA5-4335-AC5B-B1347CEBBE88}.Release|Any CPU.ActiveCfg = Release|Any CPU
{08B02A6D-EEA5-4335-AC5B-B1347CEBBE88}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal