From 3a819ce1806257f23c54baedccf4a007a33ed819 Mon Sep 17 00:00:00 2001 From: alphafetus Date: Fri, 19 Jun 2026 12:31:12 -0400 Subject: [PATCH] temp: broken push to laptop --- {PcgsInvUi/Services => PcgsApi}/PcgsClient.cs | 72 +++-- PcgsApi/PcgsCoin.cs | 38 +++ PcgsInv/Models/CoinCollection.cs | 47 ++++ PcgsInvUi/App.axaml | 1 - PcgsInvUi/App.axaml.cs | 13 +- PcgsInvUi/CMakeLists.txt | 56 ++-- PcgsInvUi/Models/Coin.cs | 165 ----------- PcgsInvUi/PcgsInvUi.csproj | 29 +- PcgsInvUi/Program.cs | 16 +- PcgsInvUi/Services/CoinDatabase.cs | 258 +++++++++--------- PcgsInvUi/ViewLocator.cs | 4 +- PcgsInvUi/ViewModels/ApiKeyViewModel.cs | 40 +-- PcgsInvUi/ViewModels/CoinListViewModel.cs | 27 +- PcgsInvUi/ViewModels/DeleteWindowViewModel.cs | 8 +- PcgsInvUi/ViewModels/EditViewModel.cs | 23 +- PcgsInvUi/ViewModels/ErrorWindowViewModel.cs | 35 ++- PcgsInvUi/ViewModels/MainWindowViewModel.cs | 89 +++--- PcgsInvUi/ViewModels/NewViewModel.cs | 48 ++-- PcgsInvUi/ViewModels/ViewModelBase.cs | 4 +- PcgsInvUi/Views/ApiKeyView.axaml | 16 +- PcgsInvUi/Views/ApiKeyView.axaml.cs | 4 +- PcgsInvUi/Views/DeleteWindow.axaml | 2 +- PcgsInvUi/Views/DeleteWindow.axaml.cs | 6 +- PcgsInvUi/Views/ErrorWindow.axaml | 8 +- PcgsInvUi/Views/ErrorWindow.axaml.cs | 9 +- PcgsInvUi/Views/FindWindow.axaml.cs | 4 +- PcgsInvUi/Views/MainWindow.axaml | 8 +- PcgsInvUi/Views/MainWindow.axaml.cs | 10 +- PcgsInvUi/Views/MainWindowRewrite.axaml | 13 + PcgsInvUi/Views/MainWindowRewrite.axaml.cs | 9 + PcgsInvUi/Views/NewView.axaml.cs | 2 +- pcgs-inv.sln | 12 + 32 files changed, 503 insertions(+), 573 deletions(-) rename {PcgsInvUi/Services => PcgsApi}/PcgsClient.cs (65%) create mode 100644 PcgsApi/PcgsCoin.cs create mode 100644 PcgsInv/Models/CoinCollection.cs delete mode 100644 PcgsInvUi/Models/Coin.cs create mode 100644 PcgsInvUi/Views/MainWindowRewrite.axaml create mode 100644 PcgsInvUi/Views/MainWindowRewrite.axaml.cs diff --git a/PcgsInvUi/Services/PcgsClient.cs b/PcgsApi/PcgsClient.cs similarity index 65% rename from PcgsInvUi/Services/PcgsClient.cs rename to PcgsApi/PcgsClient.cs index 0f70915..d16e001 100644 --- a/PcgsInvUi/Services/PcgsClient.cs +++ b/PcgsApi/PcgsClient.cs @@ -1,40 +1,34 @@ -using System; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using PcgsInvUi.Models; +using System.Net.Http.Headers; using System.Text.Json; using ReactiveUI; +using ReactiveUI.SourceGenerators; -namespace PcgsInvUi.Services; +namespace PcgsApi; -public class PcgsClient : ReactiveObject { +public partial class PcgsClient : ReactiveObject { public enum ErrorType { None, NoCoinFound, InvalidRequestFormat, ApiKeyInvalid } - - public int NumApiRequests { - get => _numApiRequests; - set => this.RaiseAndSetIfChanged(ref _numApiRequests, value); - } - - private string _apiUri = "https://api.pcgs.com/publicapi"; - private readonly string _apiKey; - private int _numApiRequests; + + protected readonly string _apiKey; + + protected readonly string _apiUri = "https://api.pcgs.com/publicapi"; /// - /// Creates a new instance of the PcgsClient class. - /// Although this is a constructor, it's meant to be called only from the CreateClient method. + /// Creates a new instance of the PcgsClient class. + /// Although this is a constructor, it's meant to be called only from the CreateClient method. /// private PcgsClient(string apiKey) { _apiKey = apiKey; } - + + [Reactive] private int _numApiRequests; + /// - /// Creates a new instance of the PcgsClient class. + /// Creates a new instance of the PcgsClient class. /// /// The api key to use for requests. /// A new instance of the PcgsClient class, or null if the key is invalid. @@ -47,83 +41,87 @@ public class PcgsClient : ReactiveObject { } /// - /// Sends a request to the API to verify the credentials. + /// Sends a request to the API to verify the credentials. /// /// A boolean indicating whether or not the credentials are valid. public async Task VerifyCredentials() { // 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. - 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; return true; } /// - /// Gets the coin information for a given PCGS number and grade. + /// Gets the coin information for a given PCGS number and grade. /// /// The PCGS number of the coin. /// The grade of the coin. /// Whether or not the coin is a plus grade. /// A tuple containing the error type and the coin information. - public async Task<(ErrorType, Coin?)> GetCoinFactsByGrade(int pcgsNumber, string grade, bool plusGrade = false) { - using (HttpClient client = new HttpClient()) { + public async Task<(ErrorType, PcgsCoin?)> + GetCoinFactsByGrade(int pcgsNumber, string grade, bool plusGrade = false) { + using (var client = new HttpClient()) { var gradeParam = GetGradeFromString(grade); - string requestUri = + var requestUri = $"{_apiUri}/coindetail/GetCoinFactsByGrade/?PCGSNo={pcgsNumber}&GradeNo={gradeParam}&PlusGrade={plusGrade}"; client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", _apiKey); Console.WriteLine($"Sending request to {requestUri}"); - HttpResponseMessage response = await client.GetAsync(requestUri); + var response = await client.GetAsync(requestUri); Console.WriteLine("Got response."); if (response.IsSuccessStatusCode) { // Check response for errors. - string jsonResponse = await response.Content.ReadAsStringAsync(); + var jsonResponse = await response.Content.ReadAsStringAsync(); var parsedResponse = ParseJsonResponse(jsonResponse); return parsedResponse; } } - + return (ErrorType.ApiKeyInvalid, null); } /// - /// 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. /// /// The grade string. /// The grade number. 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}"); return actualGrade; } /// - /// Parses the json response from the api. + /// Parses the json response from the api. /// /// The json response. /// A tuple containing an error type and the coin information. - private (ErrorType, Coin?) ParseJsonResponse(string json) { + private (ErrorType, PcgsCoin?) ParseJsonResponse(string json) { Console.WriteLine(json); // Check for errors in the response if (json.Contains("\"ServerMessage\":\"No data found\"")) return (ErrorType.NoCoinFound, null); if (json.Contains("\"IsValidRequest\":false")) return (ErrorType.InvalidRequestFormat, null); - var newCoin = JsonSerializer.Deserialize(json); + var newCoin = JsonSerializer.Deserialize(json); return (ErrorType.None, newCoin); } } // Exception for invalid request format -class InvalidRequestFormatException : Exception { +internal class InvalidRequestFormatException : Exception { public InvalidRequestFormatException() { } public InvalidRequestFormatException(string message) : base(message) { } public InvalidRequestFormatException(string message, Exception inner) : base(message, inner) { } } // Exception for no coin found -class CoinNotFoundException : Exception { +internal class CoinNotFoundException : Exception { public CoinNotFoundException() { } public CoinNotFoundException(string message) : base(message) { } public CoinNotFoundException(string message, Exception inner) : base(message, inner) { } -} +} \ No newline at end of file diff --git a/PcgsApi/PcgsCoin.cs b/PcgsApi/PcgsCoin.cs new file mode 100644 index 0000000..95c7683 --- /dev/null +++ b/PcgsApi/PcgsCoin.cs @@ -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)); + } +} \ No newline at end of file diff --git a/PcgsInv/Models/CoinCollection.cs b/PcgsInv/Models/CoinCollection.cs new file mode 100644 index 0000000..92b926c --- /dev/null +++ b/PcgsInv/Models/CoinCollection.cs @@ -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; + +/// +/// Contains all information related to a collection of coins. +/// +public partial class CoinCollection : ReactiveObject { + /// + /// The name of the collection. + /// + [Reactive] private string _name; + + /// + /// The collection of objects. + /// + [Reactive] private ObservableCollection _coins; + + /// + /// The total value of the collection. + /// + [ObservableAsProperty] private double _totalValue; + + /// + /// Creates a new CoinCollection instance. + /// + 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); + } +} \ No newline at end of file diff --git a/PcgsInvUi/App.axaml b/PcgsInvUi/App.axaml index 0855646..636641b 100644 --- a/PcgsInvUi/App.axaml +++ b/PcgsInvUi/App.axaml @@ -1,7 +1,6 @@ diff --git a/PcgsInvUi/App.axaml.cs b/PcgsInvUi/App.axaml.cs index 8501681..84910bc 100644 --- a/PcgsInvUi/App.axaml.cs +++ b/PcgsInvUi/App.axaml.cs @@ -7,7 +7,7 @@ using PcgsInvUi.Views; namespace PcgsInvUi; -public partial class App : Application { +public class App : Application { public override void Initialize() { AvaloniaXamlLoader.Load(this); } @@ -17,14 +17,13 @@ public partial class App : Application { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { var coins = new CoinDatabase(); desktop.MainWindow = new MainWindow { - DataContext = new MainWindowViewModel(coins), + DataContext = new MainWindowViewModel(coins) }; - + // Allow the application to save the database when it exits. - desktop.Exit += (sender, args) => { - coins.UpdateCollection("CollectionTable"); - }; + desktop.Exit += (sender, args) => { coins.UpdateCollection("CollectionTable"); }; } + base.OnFrameworkInitializationCompleted(); } -} +} \ No newline at end of file diff --git a/PcgsInvUi/CMakeLists.txt b/PcgsInvUi/CMakeLists.txt index 7c894f7..bc5220c 100644 --- a/PcgsInvUi/CMakeLists.txt +++ b/PcgsInvUi/CMakeLists.txt @@ -5,33 +5,33 @@ project(PcgsInvUi LANGUAGES CSharp) include(CSharpUtilities) add_executable(PcgsInvUi - App.axaml.cs - Program.cs - ViewLocator.cs - App.axaml - app.manifest - Models/Coin.cs - Services/CoinCollection.cs - Services/CoinDatabase.cs - Services/PcgsClient.cs - ViewModels/CoinListViewModel.cs - ViewModels/DeleteWindowViewModel.cs - ViewModels/EditViewModel.cs - ViewModels/ErrorWindowViewModel.cs - ViewModels/FindWindowViewModel.cs - ViewModels/MainWindowViewModel.cs - ViewModels/NewViewModel.cs - ViewModels/ViewModelBase.cs - Views/DeleteWindow.axaml - Views/DeleteWindow.axaml.cs - Views/ErrorWindow.axaml - Views/ErrorWindow.axaml.cs - Views/FindWindow.axaml - Views/FindWindow.axaml.cs - Views/MainWindow.axaml - Views/MainWindow.axaml.cs - Views/NewView.axaml - Views/NewView.axaml.cs + App.axaml.cs + Program.cs + ViewLocator.cs + App.axaml + app.manifest + Models/Coin.cs + Services/CoinCollection.cs + Services/CoinDatabase.cs + Services/PcgsClient.cs + ViewModels/CoinListViewModel.cs + ViewModels/DeleteWindowViewModel.cs + ViewModels/EditViewModel.cs + ViewModels/ErrorWindowViewModel.cs + ViewModels/FindWindowViewModel.cs + ViewModels/MainWindowViewModel.cs + ViewModels/NewViewModel.cs + ViewModels/ViewModelBase.cs + Views/DeleteWindow.axaml + Views/DeleteWindow.axaml.cs + Views/ErrorWindow.axaml + Views/ErrorWindow.axaml.cs + Views/FindWindow.axaml + Views/FindWindow.axaml.cs + Views/MainWindow.axaml + Views/MainWindow.axaml.cs + Views/NewView.axaml + Views/NewView.axaml.cs ) # 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. set_property(TARGET PcgsInvUi PROPERTY VS_DOTNET_REFERENCES - "Microsoft.CSharp" + "Microsoft.CSharp" ) diff --git a/PcgsInvUi/Models/Coin.cs b/PcgsInvUi/Models/Coin.cs deleted file mode 100644 index 2d9e8bf..0000000 --- a/PcgsInvUi/Models/Coin.cs +++ /dev/null @@ -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 = ""; - } -} \ No newline at end of file diff --git a/PcgsInvUi/PcgsInvUi.csproj b/PcgsInvUi/PcgsInvUi.csproj index 11fa9a3..07462f8 100644 --- a/PcgsInvUi/PcgsInvUi.csproj +++ b/PcgsInvUi/PcgsInvUi.csproj @@ -1,13 +1,14 @@  WinExe - net6.0 + net10.0 enable true app.manifest true BobdaFett 0.0.5 + latestmajor @@ -17,16 +18,17 @@ - - - - - + + + + + - - + + + - + @@ -65,7 +67,7 @@ - + @@ -90,6 +92,11 @@ - + + + + + + diff --git a/PcgsInvUi/Program.cs b/PcgsInvUi/Program.cs index cc1d9e7..d9d4aeb 100644 --- a/PcgsInvUi/Program.cs +++ b/PcgsInvUi/Program.cs @@ -1,22 +1,24 @@ -using Avalonia; +using System; +using Avalonia; using Avalonia.ReactiveUI; -using System; -using PcgsInvUi.ViewModels; namespace PcgsInvUi; -class Program { +internal class Program { // Initialization code. Don't use any Avalonia, third-party APIs or any // SynchronizationContext-reliant code before AppMain is called: things aren't initialized // yet and stuff might break. [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. - public static AppBuilder BuildAvaloniaApp() - => AppBuilder.Configure() + public static AppBuilder BuildAvaloniaApp() { + return AppBuilder.Configure() .UsePlatformDetect() .WithInterFont() .LogToTrace() .UseReactiveUI(); + } } \ No newline at end of file diff --git a/PcgsInvUi/Services/CoinDatabase.cs b/PcgsInvUi/Services/CoinDatabase.cs index 03e6cc1..cd175fb 100644 --- a/PcgsInvUi/Services/CoinDatabase.cs +++ b/PcgsInvUi/Services/CoinDatabase.cs @@ -1,22 +1,17 @@ using System; using System.Collections.ObjectModel; using System.Data.SQLite; +using System.IO; using System.Threading.Tasks; -using PcgsInvUi.Models; namespace PcgsInvUi.Services; public class CoinDatabase { - public SQLiteConnection Connection { get; set; } - private Boolean IsConnected { get; set; } - public ObservableCollection Collection { get; set; } - public Boolean IsClientConnected { get; set; } - private PcgsClient? _pcgsClient; - + /// - /// Creates and initilizes a new instance of the CoinDatabase. - /// Must call TryInitApiClient() after creating a new instance. + /// Creates and initilizes a new instance of the CoinDatabase. + /// Must call TryInitApiClient() after creating a new instance. /// public CoinDatabase() { // Create a connection to SQLite @@ -25,13 +20,18 @@ public class CoinDatabase { CreateCollection("CollectionTable"); CreateApiTable(); Console.WriteLine("Connected to database."); - Collection = new ObservableCollection(); + Collection = new ObservableCollection(); GetCollection("CollectionTable"); Console.WriteLine("Got collection."); } + public SQLiteConnection Connection { get; set; } + private bool IsConnected { get; set; } + public ObservableCollection Collection { get; set; } + public bool IsClientConnected { get; set; } + /// - /// Destructor. Currently not used, due to the strange nature of desktop applications. + /// Destructor. Currently not used, due to the strange nature of desktop applications. /// ~CoinDatabase() { Console.WriteLine("Destructor called, saving collection."); @@ -40,14 +40,14 @@ public class CoinDatabase { // Close the connection. Connection.Close(); } - + /// - /// 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. /// /// True if the API key was found, false otherwise. public async Task TryInitApiClient() { Console.Write("Attempting to initialize API client... "); - SQLiteCommand cmd = Connection.CreateCommand(); + var cmd = Connection.CreateCommand(); cmd.CommandText = "SELECT API_KEY FROM ApiTable"; using (var reader = cmd.ExecuteReader()) { @@ -70,13 +70,14 @@ public class CoinDatabase { Console.WriteLine("Failed. API key not found."); } } + return IsClientConnected; } /// - /// Atttempts to initialize the PCGS API client. + /// Atttempts to initialize the PCGS API client. /// - /// The API key to verify. + /// The API key to verify. /// A Task with a return value that returns true if the client intializes properly. public async Task TryInitApiClient(string apiKey) { Console.Write("Attempting to initialize API client with key param... "); @@ -98,23 +99,23 @@ public class CoinDatabase { } /// - /// Gets the collection from the database and stores it in the Collection property. - /// Creates the collection table if it does not exist already. + /// Gets the collection from the database and stores it in the Collection property. + /// Creates the collection table if it does not exist already. /// /// The SQLiteConnection that corresponds to the connected database. private SQLiteConnection CreateConnection() { // Check if database exists. - if (!System.IO.File.Exists("coin_collection")) { + if (!File.Exists("coin_collection")) { // If not, create the database. Console.Write("Creating database... "); SQLiteConnection.CreateFile("coin_collection.sqlite"); Console.WriteLine("Done."); } - + // Create a connection to the database. - SQLiteConnection connection = + var connection = new SQLiteConnection("DataSource=coin_collection"); - + // Open the connection. try { connection.Open(); @@ -128,7 +129,7 @@ public class CoinDatabase { } /// - /// 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. /// public void CreateApiTable() { // 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. - SQLiteCommand cmd = Connection.CreateCommand(); + var cmd = Connection.CreateCommand(); cmd.CommandText = - "CREATE TABLE IF NOT EXISTS ApiTable (" + - "API_KEY TEXT, " + - "REQUESTS_REMAINING INTEGER, " + - "PRIMARY KEY(API_KEY))"; + "CREATE TABLE IF NOT EXISTS ApiTable (" + + "API_KEY TEXT, " + + "REQUESTS_REMAINING INTEGER, " + + "PRIMARY KEY(API_KEY))"; // Execute the command cmd.ExecuteNonQuery(); @@ -152,7 +153,7 @@ public class CoinDatabase { } /// - /// Creates a table to store the collection. + /// Creates a table to store the collection. /// public void CreateCollection(string collectionName) { // Create a table to store the collection. @@ -161,9 +162,9 @@ public class CoinDatabase { Console.WriteLine("Not connected to database."); return; } - + // Create a SQL command. - SQLiteCommand cmd = Connection.CreateCommand(); + var cmd = Connection.CreateCommand(); cmd.CommandText = $"CREATE TABLE IF NOT EXISTS {collectionName} (" + "PCGS_NUMBER TEXT," + @@ -184,16 +185,16 @@ public class CoinDatabase { "QUANTITY INTEGER," + "NOTES TEXT," + "PRIMARY KEY(PCGS_NUMBER, GRADE))"; - + // Execute the command. cmd.ExecuteNonQuery(); Console.WriteLine("Collection table initialized."); } /// - /// Inserts a Coin object into the database. + /// Inserts a Coin object into the database. /// - public void InsertCoin(Coin coin) { + public void InsertCoin(PcgsCoin pcgsCoin) { Console.WriteLine("Inserting coin..."); // Use our existing connection to insert a coin to the database. if (!IsConnected) { @@ -201,26 +202,26 @@ public class CoinDatabase { Console.WriteLine("Not connected to database."); return; } - + // Create a SQL command. - SQLiteCommand cmd = Connection.CreateCommand(); + var cmd = Connection.CreateCommand(); cmd.CommandText = "INSERT INTO CollectionTable (PCGS_NUMBER, GRADE, YEAR," + "DENOMINATION, MINT_MARK, PRICE_GUIDE_VALUE, COIN_FACTS_LINK," + "MAJOR_VARIETY, MINOR_VARIETY, DIE_VARIETY, SERIES_NAME, CATEGORY, DESIGNATION," + "CERT_NUM, QUANTITY, NOTES) " + - $"VALUES ('{coin.PCGSNo}', '{coin.Grade}', {coin.Year}, '{coin.Denomination}', '{coin.MintMark}', {coin.PriceGuideValue}," + - $"'{coin.CoinFactsLink}', '{coin.MajorVariety}', '{coin.MinorVariety}', '{coin.DieVariety}', '{coin.SeriesName}', " + - $"'{coin.Category}', '{coin.Designation}', {coin.CertificateNumber}, {coin.Quantity}, @Notes)"; - cmd.Parameters.AddWithValue("@Notes", coin.Notes); - + $"VALUES ('{pcgsCoin.PcgsNumber}', '{pcgsCoin.Grade}', {pcgsCoin.Year}, '{pcgsCoin.Denomination}', '{pcgsCoin.MintMark}', {pcgsCoin.PriceGuideValue}," + + $"'{pcgsCoin.CoinFactsLink}', '{pcgsCoin.MajorVariety}', '{pcgsCoin.MinorVariety}', '{pcgsCoin.DieVariety}', '{pcgsCoin.SeriesName}', " + + $"'{pcgsCoin.Category}', '{pcgsCoin.Designation}', {pcgsCoin.CertificateNumber}, {pcgsCoin.Quantity}, @Notes)"; + cmd.Parameters.AddWithValue("@Notes", pcgsCoin.Notes); + // Execute the command. cmd.ExecuteNonQuery(); Console.WriteLine("Coin inserted."); } /// - /// Creates a Coin object and saves it to the database. + /// Creates a Coin object and saves it to the database. /// /// The PCGS number of the coin. /// The grade of the coin. @@ -236,7 +237,7 @@ public class CoinDatabase { SaveCoin("CollectionTable", coin); return PcgsClient.ErrorType.None; } - + // Otherwise, build a new coin with the API and insert it. var apiResult = await _pcgsClient.GetCoinFactsByGrade(pcgsNumber, grade); if (apiResult.Item1 == PcgsClient.ErrorType.None) { @@ -246,68 +247,68 @@ public class CoinDatabase { Collection.Add(newCoin); return PcgsClient.ErrorType.None; } - + Console.WriteLine("Error getting coin from API - {0}", apiResult.Item1); return apiResult.Item1; } - + /// - /// Gets a coin from the database. + /// Gets a coin from the database. /// /// The PCGS number of the coin. /// The grade of the coin. /// The coin if it exists, null otherwise. - public Coin? GetCoin(int pcgsNumber, string grade) { + public PcgsCoin? GetCoin(int pcgsNumber, string grade) { Console.WriteLine($"Getting coin {pcgsNumber}, {grade}..."); - int gradeInt = GetGradeFromString(grade); - + var gradeInt = GetGradeFromString(grade); + // 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. - 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. + foreach (var coin in Collection) + 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 (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."); return coin; } - } - + Console.WriteLine("Couldn't find coin."); return null; } /// - /// Used to get all Coin objects within the database. + /// Used to get all Coin objects within the database. /// /// The name of the collection table. public void GetCollection(string collectionName) { Console.WriteLine("Getting collection..."); - SQLiteCommand cmd = Connection.CreateCommand(); + var cmd = Connection.CreateCommand(); cmd.CommandText = $"SELECT * FROM {collectionName}"; - + using (var reader = cmd.ExecuteReader()) { - while (reader.Read()) { + while (reader.Read()) // Create coin from reader then add to list. Collection.Add(CoinFromReader(reader)); - } } } /// - /// Creates a CSV formatted string from a collection. + /// Creates a CSV formatted string from a collection. /// /// The name of the collection table. /// A CSV formatted string. - public String CollectionToCSV(string collectionName) { + public string CollectionToCSV(string collectionName) { var cmd = Connection.CreateCommand(); cmd.CommandText = $"SELECT * FROM {collectionName}"; var headersCommand = Connection.CreateCommand(); headersCommand.CommandText = $"PRAGMA table_info({collectionName})"; - + var resultString = ""; var numHeaders = 0; - + // Get the headers. using (var reader = headersCommand.ExecuteReader()) { while (reader.Read()) { @@ -322,23 +323,20 @@ public class CoinDatabase { using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { // 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. var type = reader.GetFieldType(i); Console.WriteLine("Type: " + type); - if (type == typeof(Int64)) { + if (type == typeof(long)) resultString += reader.GetInt32(i) + ","; - } - else if (type == typeof(Double)) { + else if (type == typeof(double)) resultString += reader.GetDouble(i) + ","; - } - else if (type == typeof(String)) { + else if (type == typeof(string)) resultString += reader.GetString(i) + ","; - } - else { + else resultString += "NULL" + ","; - } } + resultString += "\n"; } } @@ -347,91 +345,91 @@ public class CoinDatabase { } /// - /// Saves a Coin object to the database. + /// Saves a Coin object to the database. /// /// The name of the collection table. - /// The coin to save. - public void SaveCoin(string collectionName, Coin coin) { + /// The coin to save. + public void SaveCoin(string collectionName, PcgsCoin pcgsCoin) { // Get and replace the coin from the database. Console.WriteLine("Saving coin..."); - SQLiteCommand cmd = Connection.CreateCommand(); + var cmd = Connection.CreateCommand(); cmd.CommandText = $"UPDATE {collectionName} " + - $"SET YEAR = {coin.Year}, " + - $"DENOMINATION = \'{coin.Denomination}\', " + - $"MINT_MARK = \'{coin.MintMark}\', " + - $"PRICE_GUIDE_VALUE = \'{coin.PriceGuideValue}\', " + - $"COIN_FACTS_LINK = \'{coin.CoinFactsLink}\', " + - $"MAJOR_VARIETY = \'{coin.MajorVariety}\', " + - $"MINOR_VARIETY = \'{coin.MinorVariety}\', " + - $"DIE_VARIETY = \'{coin.DieVariety}\', " + - $"SERIES_NAME = \'{coin.SeriesName}\', " + - $"CATEGORY = \'{coin.Category}\', " + - $"DESIGNATION = \'{coin.Designation}\', " + - $"CERT_NUM = \'{coin.CertificateNumber}\', " + - $"QUANTITY = {coin.Quantity}, " + - $"NOTES = @Notes " + - $"WHERE PCGS_NUMBER = {coin.PCGSNo} AND GRADE = \'{coin.Grade}\'"; - cmd.Parameters.AddWithValue("@Notes", coin.Notes); + $"SET YEAR = {pcgsCoin.Year}, " + + $"DENOMINATION = \'{pcgsCoin.Denomination}\', " + + $"MINT_MARK = \'{pcgsCoin.MintMark}\', " + + $"PRICE_GUIDE_VALUE = \'{pcgsCoin.PriceGuideValue}\', " + + $"COIN_FACTS_LINK = \'{pcgsCoin.CoinFactsLink}\', " + + $"MAJOR_VARIETY = \'{pcgsCoin.MajorVariety}\', " + + $"MINOR_VARIETY = \'{pcgsCoin.MinorVariety}\', " + + $"DIE_VARIETY = \'{pcgsCoin.DieVariety}\', " + + $"SERIES_NAME = \'{pcgsCoin.SeriesName}\', " + + $"CATEGORY = \'{pcgsCoin.Category}\', " + + $"DESIGNATION = \'{pcgsCoin.Designation}\', " + + $"CERT_NUM = \'{pcgsCoin.CertificateNumber}\', " + + $"QUANTITY = {pcgsCoin.Quantity}, " + + $"NOTES = @Notes " + + $"WHERE PCGS_NUMBER = {pcgsCoin.PcgsNumber} AND GRADE = \'{pcgsCoin.Grade}\'"; + cmd.Parameters.AddWithValue("@Notes", pcgsCoin.Notes); Console.WriteLine(cmd.CommandText); // Execute the command. cmd.ExecuteNonQuery(); } /// - /// Synchronizes the database with the current contents of the specified collection. + /// Synchronizes the database with the current contents of the specified collection. /// /// The name of the collection table. public void UpdateCollection(string collectionName) { // 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. - foreach (var coin in Collection) { - SaveCoin(collectionName, coin); // CAREFUL! This will not create a coin if it doesn't currently exist. - } + foreach (var coin in + Collection) + SaveCoin(collectionName, coin); // CAREFUL! This will not create a coin if it doesn't currently exist. } - + /// - /// 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. /// /// The reader to get the information from. - private Coin CoinFromReader(SQLiteDataReader reader) { + private PcgsCoin CoinFromReader(SQLiteDataReader reader) { // Create a coin while filtering out any possible null values - they throw exceptions. - Coin coin = new Coin(); - if (!reader.IsDBNull(0)) coin.PCGSNo = reader.GetString(0); - if (!reader.IsDBNull(1)) coin.Grade = reader.GetString(1); - if (!reader.IsDBNull(2)) coin.Year = reader.GetInt32(2); - if (!reader.IsDBNull(3)) coin.Denomination = reader.GetString(3); - if (!reader.IsDBNull(4)) coin.MintMark = reader.GetString(4); - if (!reader.IsDBNull(5)) coin.PriceGuideValue = reader.GetDouble(5); - if (!reader.IsDBNull(6)) coin.CoinFactsLink = reader.GetString(6); - if (!reader.IsDBNull(7)) coin.MajorVariety = reader.GetString(7); - if (!reader.IsDBNull(8)) coin.MinorVariety = reader.GetString(8); - if (!reader.IsDBNull(9)) coin.DieVariety = reader.GetString(9); - if (!reader.IsDBNull(10)) coin.SeriesName = reader.GetString(10); - if (!reader.IsDBNull(11)) coin.Category = reader.GetString(11); - if (!reader.IsDBNull(12)) coin.Designation = reader.GetString(12); - if (!reader.IsDBNull(13)) coin.CertificateNumber = reader.GetInt32(13); - if (!reader.IsDBNull(14)) coin.Quantity = reader.GetInt32(14); - if (!reader.IsDBNull(15)) coin.Notes = reader.GetString(15); - coin.TotalPrice = coin.Quantity * coin.PriceGuideValue; - return coin; + PcgsCoin pcgsCoin = new PcgsCoin(); + if (!reader.IsDBNull(0)) pcgsCoin.PcgsNumber = reader.GetString(0); + if (!reader.IsDBNull(1)) pcgsCoin.Grade = reader.GetString(1); + if (!reader.IsDBNull(2)) pcgsCoin.Year = reader.GetInt32(2); + if (!reader.IsDBNull(3)) pcgsCoin.Denomination = reader.GetString(3); + if (!reader.IsDBNull(4)) pcgsCoin.MintMark = reader.GetString(4); + if (!reader.IsDBNull(5)) pcgsCoin.PriceGuideValue = reader.GetDouble(5); + if (!reader.IsDBNull(6)) pcgsCoin.CoinFactsLink = reader.GetString(6); + if (!reader.IsDBNull(7)) pcgsCoin.MajorVariety = reader.GetString(7); + if (!reader.IsDBNull(8)) pcgsCoin.MinorVariety = reader.GetString(8); + if (!reader.IsDBNull(9)) pcgsCoin.DieVariety = reader.GetString(9); + if (!reader.IsDBNull(10)) pcgsCoin.SeriesName = reader.GetString(10); + if (!reader.IsDBNull(11)) pcgsCoin.Category = reader.GetString(11); + if (!reader.IsDBNull(12)) pcgsCoin.Designation = reader.GetString(12); + if (!reader.IsDBNull(13)) pcgsCoin.CertificateNumber = reader.GetInt32(13); + if (!reader.IsDBNull(14)) pcgsCoin.Quantity = reader.GetInt32(14); + if (!reader.IsDBNull(15)) pcgsCoin.Notes = reader.GetString(15); + // pcgsCoin.TotalPrice = pcgsCoin.Quantity * pcgsCoin.PriceGuideValue; + return pcgsCoin; } - + /// - /// Deletes a Coin object from the database. + /// Deletes a Coin object from the database. /// - /// The coin to delete. - public void DeleteCoin(Coin delCoin) { - SQLiteCommand cmd = Connection.CreateCommand(); + /// The coin to delete. + public void DeleteCoin(PcgsCoin delPcgsCoin) { + var cmd = Connection.CreateCommand(); cmd.CommandText = "DELETE FROM CollectionTable WHERE PCGS_NUMBER = @PcgsNumber AND GRADE = @Grade"; - cmd.Parameters.AddWithValue("@PcgsNumber", delCoin.PCGSNo); - cmd.Parameters.AddWithValue("@Grade", delCoin.Grade); - + cmd.Parameters.AddWithValue("@PcgsNumber", delPcgsCoin.PcgsNumber); + cmd.Parameters.AddWithValue("@Grade", delPcgsCoin.Grade); + cmd.ExecuteNonQuery(); } - + /// - /// 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. + /// 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. /// /// The grade string. private int GetGradeFromString(string grade) { @@ -443,4 +441,4 @@ public class CoinDatabase { public class NotConnectedToDatabaseException : Exception { public NotConnectedToDatabaseException() : base("Not connected to database.") { } -} +} \ No newline at end of file diff --git a/PcgsInvUi/ViewLocator.cs b/PcgsInvUi/ViewLocator.cs index b80701a..2e041d8 100644 --- a/PcgsInvUi/ViewLocator.cs +++ b/PcgsInvUi/ViewLocator.cs @@ -10,9 +10,7 @@ public class ViewLocator : IDataTemplate { var name = data.GetType().FullName!.Replace("ViewModel", "View"); var type = Type.GetType(name); - if (type != null) { - return (Control)Activator.CreateInstance(type)!; - } + if (type != null) return (Control)Activator.CreateInstance(type)!; return new TextBlock { Text = "Not Found: " + name }; } diff --git a/PcgsInvUi/ViewModels/ApiKeyViewModel.cs b/PcgsInvUi/ViewModels/ApiKeyViewModel.cs index 468b2dd..50048c8 100644 --- a/PcgsInvUi/ViewModels/ApiKeyViewModel.cs +++ b/PcgsInvUi/ViewModels/ApiKeyViewModel.cs @@ -1,30 +1,18 @@ -using ReactiveUI; -using System.Reactive; +using System.Diagnostics; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; namespace PcgsInvUi.ViewModels; -public class ApiKeyViewModel : ViewModelBase { - public string ApiKeyInput { - get => _apiKeyInput; - set => this.RaiseAndSetIfChanged(ref _apiKeyInput, value); +public partial class ApiKeyViewModel : ViewModelBase { + [ObservableProperty] + public partial string ApiKeyInput { get; set; } + + [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 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); - } -} +} \ No newline at end of file diff --git a/PcgsInvUi/ViewModels/CoinListViewModel.cs b/PcgsInvUi/ViewModels/CoinListViewModel.cs index a8285fa..030a772 100644 --- a/PcgsInvUi/ViewModels/CoinListViewModel.cs +++ b/PcgsInvUi/ViewModels/CoinListViewModel.cs @@ -1,23 +1,20 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -using PcgsInvUi.Models; -using ReactiveUI; +using CommunityToolkit.Mvvm.ComponentModel; namespace PcgsInvUi.ViewModels; -public class CoinListViewModel : ViewModelBase { - public ObservableCollection CoinCollection { get; set; } - private float _totalValue; - - public float TotalValue { - get => _totalValue; - set => this.RaiseAndSetIfChanged(ref _totalValue, value); - } - - public Coin? SelectedCoin { get; set; } - - public CoinListViewModel(IEnumerable items) { - CoinCollection = new ObservableCollection(items); +public partial class CoinListViewModel : ViewModelBase { + public CoinListViewModel(IEnumerable items) { + CoinCollection = new ObservableCollection(items); TotalValue = 0; } + + public ObservableCollection CoinCollection { get; set; } + + [ObservableProperty] + public partial float TotalValue { get; set; } + + [ObservableProperty] + public partial PcgsCoin? SelectedCoin { get; set; } } \ No newline at end of file diff --git a/PcgsInvUi/ViewModels/DeleteWindowViewModel.cs b/PcgsInvUi/ViewModels/DeleteWindowViewModel.cs index db4b132..f924e79 100644 --- a/PcgsInvUi/ViewModels/DeleteWindowViewModel.cs +++ b/PcgsInvUi/ViewModels/DeleteWindowViewModel.cs @@ -4,11 +4,11 @@ using ReactiveUI; namespace PcgsInvUi.ViewModels; public class DeleteWindowViewModel : ViewModelBase { - public ReactiveCommand OkCommand { get; } - public ReactiveCommand CancelCommand { get; } - public DeleteWindowViewModel() { OkCommand = ReactiveCommand.Create(() => (object)true); CancelCommand = ReactiveCommand.Create(() => (object)false); } -} + + public ReactiveCommand OkCommand { get; } + public ReactiveCommand CancelCommand { get; } +} \ No newline at end of file diff --git a/PcgsInvUi/ViewModels/EditViewModel.cs b/PcgsInvUi/ViewModels/EditViewModel.cs index cf64977..8df81f2 100644 --- a/PcgsInvUi/ViewModels/EditViewModel.cs +++ b/PcgsInvUi/ViewModels/EditViewModel.cs @@ -1,27 +1,26 @@ using System.Reactive; -using PcgsInvUi.Models; using ReactiveUI; namespace PcgsInvUi.ViewModels; public class EditViewModel : ViewModelBase { - public Coin CurrentCoin { get; set; } - - public ReactiveCommand OkCommand { get; } - - public EditViewModel(Coin coin) { - CurrentCoin = coin; + public EditViewModel(PcgsCoin pcgsCoin) { + CurrentPcgsCoin = pcgsCoin; var okEnabled = this.WhenAnyValue( - x => x.CurrentCoin.PCGSNo, - x => x.CurrentCoin.Grade, - x => x.CurrentCoin.Quantity, + x => x.CurrentPcgsCoin.PcgsNumber, + x => x.CurrentPcgsCoin.Grade, + x => x.CurrentPcgsCoin.Quantity, (id, grade, quantity) => !string.IsNullOrWhiteSpace(id) && !string.IsNullOrWhiteSpace(grade) && - (quantity != 0) + quantity != 0 ); // Setup OkCommand - all Coin-related properties must be non-whitespace. OkCommand = ReactiveCommand.Create(() => { }); } -} + + public PcgsCoin CurrentPcgsCoin { get; set; } + + public ReactiveCommand OkCommand { get; } +} \ No newline at end of file diff --git a/PcgsInvUi/ViewModels/ErrorWindowViewModel.cs b/PcgsInvUi/ViewModels/ErrorWindowViewModel.cs index 68ed391..885c42c 100644 --- a/PcgsInvUi/ViewModels/ErrorWindowViewModel.cs +++ b/PcgsInvUi/ViewModels/ErrorWindowViewModel.cs @@ -1,23 +1,22 @@ -using System.Reactive; -using ReactiveUI; +using Avalonia.Controls; +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 { - get => _message; - set => this.RaiseAndSetIfChanged(ref _message, value); - } - private string _message; - - public ReactiveCommand CloseCommand { get; } - - public ErrorWindowViewModel(string message) - { - _message = message; - - CloseCommand = ReactiveCommand.Create(() => (object)false); + public ErrorWindowViewModel(Window dialog, string message) { + _dialog = dialog; + Message = message; } -} + [ObservableProperty] + public partial string Message { get; set; } + + [RelayCommand] + public void Close() { + _dialog.Close(); + } +} \ No newline at end of file diff --git a/PcgsInvUi/ViewModels/MainWindowViewModel.cs b/PcgsInvUi/ViewModels/MainWindowViewModel.cs index 2291d54..74341ed 100644 --- a/PcgsInvUi/ViewModels/MainWindowViewModel.cs +++ b/PcgsInvUi/ViewModels/MainWindowViewModel.cs @@ -1,45 +1,19 @@ using System; using System.Collections.ObjectModel; using System.IO; -using System.Linq; using System.Reactive; using System.Reactive.Linq; -using PcgsInvUi.Models; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; using PcgsInvUi.Services; using ReactiveUI; -using System.Threading.Tasks; namespace PcgsInvUi.ViewModels; -public class MainWindowViewModel : ViewModelBase { - public ViewModelBase SidebarContent { - get => _sideContent; - private set => this.RaiseAndSetIfChanged(ref _sideContent, value); - } - public ObservableCollection 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 DeleteCommand { get; } - public Interaction ShowDeleteWindow { get; } - public ReactiveCommand ExportCommand { get; } - public Interaction ShowExportWindow { get; } - public Interaction ShowErrorWindow { get; } - - private ViewModelBase _sideContent; - private Coin? _selectedCoin; - private double _totalValue; - +public partial class MainWindowViewModel : ViewModelBase { public MainWindowViewModel(CoinDatabase coins) { ConnectedCoinDatabase = coins; - var keyFound = Task.Run(() => coins.TryInitApiClient()).Result; + var keyFound = Task.Run(coins.TryInitApiClient).Result; Console.WriteLine("Function completed."); var newViewModel = new NewViewModel(); var apiKeyViewModel = new ApiKeyViewModel(); @@ -48,21 +22,25 @@ public class MainWindowViewModel : ViewModelBase { TotalValue = DisplayedList.Sum(x => x.TotalPrice); // Setup interaction to show ApiKeyWindow - apiKeyViewModel.OkCommand.Subscribe(async apiKey => { + apiKeyViewModel.OkCommandCommand.Subscribe(async 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. if (!keyFound) { Console.WriteLine("API key must be entered by user."); // Show the API key input view. - _sideContent = apiKeyViewModel; - } else _sideContent = newViewModel; - + SidebarContent = apiKeyViewModel; + } + else { + SidebarContent = newViewModel; + } + // Set up the ability to show the error window. ShowErrorWindow = new Interaction(); - + // Set up the ability to open the delete window. ShowDeleteWindow = new Interaction(); 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. Console.WriteLine(e.Message); await ShowErrorWindow.Handle(new ErrorWindowViewModel(e.Message)); - } catch (Exception e) { + } + catch (Exception e) { Console.WriteLine(e.Message); await ShowErrorWindow.Handle(new ErrorWindowViewModel(e.Message)); } }); - + // Subscribe to SidebarContent.OkCommand, which is an IObservable<(int, string, int)> // This is the same as the above, but with a different syntax. newViewModel.OkCommand .Subscribe(async requestStructure => { var result = await ConnectedCoinDatabase.CreateCoin(requestStructure.Item1, requestStructure.Item2, requestStructure.Item3); - + switch (result) { case PcgsClient.ErrorType.ApiKeyInvalid: 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; case PcgsClient.ErrorType.InvalidRequestFormat: Console.WriteLine("Request format is invalid - showing window."); @@ -113,14 +93,15 @@ public class MainWindowViewModel : ViewModelBase { break; case PcgsClient.ErrorType.NoCoinFound: 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; } - + // Clear the text boxes. newViewModel.ClearText(); }); - + // Update TotalValue anytime a coin's quantity changes. // Possible null reference is currently ignored - SelectedCoin will never be null. this.WhenAnyValue(x => x.SelectedCoin.TotalPrice) @@ -132,10 +113,28 @@ public class MainWindowViewModel : ViewModelBase { }; } + [ObservableProperty] + public partial ViewModelBase SidebarContent { get; private set; } + + public ObservableCollection DisplayedList { get; set; } + public CoinDatabase ConnectedCoinDatabase { get; set; } + + [ObservableProperty] + public partial PcgsCoin? SelectedCoin { get; set; } + + [ObservableProperty] + public partial double TotalValue { get; set; } + + public ReactiveCommand DeleteCommand { get; } + public Interaction ShowDeleteWindow { get; } + public ReactiveCommand ExportCommand { get; } + public Interaction ShowExportWindow { get; } + public Interaction ShowErrorWindow { get; } + // Create destructor to save the database when the application exits. // This is necessary because the database is initialized in this class. ~MainWindowViewModel() { Console.WriteLine("Running MainWindowViewModel destructor."); ConnectedCoinDatabase.UpdateCollection("CollectionTable"); } -} +} \ No newline at end of file diff --git a/PcgsInvUi/ViewModels/NewViewModel.cs b/PcgsInvUi/ViewModels/NewViewModel.cs index 3909324..5467e7f 100644 --- a/PcgsInvUi/ViewModels/NewViewModel.cs +++ b/PcgsInvUi/ViewModels/NewViewModel.cs @@ -1,31 +1,14 @@ -using System.Reactive; -using System.Collections.Generic; +using System.Collections.Generic; +using System.Reactive; using ReactiveUI; namespace PcgsInvUi.ViewModels; public class NewViewModel : ViewModelBase { - private string _pcgsNumber = ""; private string _grade = ""; + private string _pcgsNumber = ""; 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 GradesList { get; } - public NewViewModel() { var okEnabled = this.WhenAnyValue( x => x.PcgsNumber, @@ -39,9 +22,7 @@ public class NewViewModel : ViewModelBase { () => (int.Parse(PcgsNumber), Grade, int.Parse(Quantity)), okEnabled); - ClearCommand = ReactiveCommand.Create(() => { - ClearText(); - }); + ClearCommand = ReactiveCommand.Create(() => { ClearText(); }); // 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. @@ -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 GradesList { get; } + public ReactiveCommand OkCommand { get; } public ReactiveCommand ClearCommand { get; } /// - /// Clears the text fields. + /// Clears the text fields. /// public void ClearText() { PcgsNumber = ""; Grade = ""; Quantity = ""; } -} +} \ No newline at end of file diff --git a/PcgsInvUi/ViewModels/ViewModelBase.cs b/PcgsInvUi/ViewModels/ViewModelBase.cs index 03a9024..8df3a4e 100644 --- a/PcgsInvUi/ViewModels/ViewModelBase.cs +++ b/PcgsInvUi/ViewModels/ViewModelBase.cs @@ -1,5 +1,5 @@ -using ReactiveUI; +using CommunityToolkit.Mvvm.ComponentModel; namespace PcgsInvUi.ViewModels; -public class ViewModelBase : ReactiveObject { } \ No newline at end of file +public class ViewModelBase : ObservableObject { } \ No newline at end of file diff --git a/PcgsInvUi/Views/ApiKeyView.axaml b/PcgsInvUi/Views/ApiKeyView.axaml index e125862..6c2bf46 100644 --- a/PcgsInvUi/Views/ApiKeyView.axaml +++ b/PcgsInvUi/Views/ApiKeyView.axaml @@ -1,14 +1,14 @@  + 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" + xmlns:viewModels="clr-namespace:PcgsInvUi.ViewModels" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + x:Class="PcgsInvUi.Views.ApiKeyView" x:DataType="viewModels:ApiKeyViewModel"> -