diff --git a/.idea/.idea.pcgs-inv/.idea/avalonia.xml b/.idea/.idea.pcgs-inv/.idea/avalonia.xml
index 94e266f..091e888 100644
--- a/.idea/.idea.pcgs-inv/.idea/avalonia.xml
+++ b/.idea/.idea.pcgs-inv/.idea/avalonia.xml
@@ -6,6 +6,7 @@
+
diff --git a/PcgsInvUi/PcgsInvUi.csproj b/PcgsInvUi/PcgsInvUi.csproj
index 1ac7009..0975dd1 100644
--- a/PcgsInvUi/PcgsInvUi.csproj
+++ b/PcgsInvUi/PcgsInvUi.csproj
@@ -62,6 +62,7 @@
+
diff --git a/PcgsInvUi/Program.cs b/PcgsInvUi/Program.cs
index e1f58b0..cc1d9e7 100644
--- a/PcgsInvUi/Program.cs
+++ b/PcgsInvUi/Program.cs
@@ -1,6 +1,7 @@
using Avalonia;
using Avalonia.ReactiveUI;
using System;
+using PcgsInvUi.ViewModels;
namespace PcgsInvUi;
@@ -9,8 +10,7 @@ class Program {
// 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()
diff --git a/PcgsInvUi/Services/CoinDatabase.cs b/PcgsInvUi/Services/CoinDatabase.cs
index a5ec1ac..0856db4 100644
--- a/PcgsInvUi/Services/CoinDatabase.cs
+++ b/PcgsInvUi/Services/CoinDatabase.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.ObjectModel;
using System.Data.SQLite;
+using System.Threading.Tasks;
using PcgsInvUi.Models;
namespace PcgsInvUi.Services;
@@ -109,9 +110,10 @@ public class CoinDatabase {
// Execute the command.
cmd.ExecuteNonQuery();
+ Console.WriteLine("Coin inserted.");
}
- public async void CreateCoin(int pcgsNumber, string grade, int quantity) {
+ public async Task CreateCoin(int pcgsNumber, string grade, int quantity) {
Console.WriteLine("Creating coin...");
// Create a coin by calling the API, then insert it into the database.
// First, check if the coin already exists - if it does, just update the quantity by adding the amount entered.
@@ -119,13 +121,21 @@ public class CoinDatabase {
if (coin is not null) {
coin.Quantity += quantity;
SaveCoin("CollectionTable", coin);
+ return PcgsClient.ErrorType.None;
}
- else {
- // Build a new coin with the API and insert it.
- var newCoin = await _pcgsClient.GetCoinFactsByGrade(pcgsNumber, grade);
+
+ // Otherwise, build a new coin with the API and insert it.
+ var apiResult = await _pcgsClient.GetCoinFactsByGrade(pcgsNumber, grade);
+ if (apiResult.Item1 == PcgsClient.ErrorType.None) {
+ var newCoin = apiResult.Item2;
newCoin.Quantity = quantity;
InsertCoin(newCoin);
+ Collection.Add(newCoin);
+ return PcgsClient.ErrorType.None;
}
+
+ Console.WriteLine("Error getting coin from API - {0}", apiResult.Item1);
+ return apiResult.Item1;
}
public Coin? GetCoin(int pcgsNumber, string grade) {
diff --git a/PcgsInvUi/Services/PcgsClient.cs b/PcgsInvUi/Services/PcgsClient.cs
index 3e2af44..e4329d2 100644
--- a/PcgsInvUi/Services/PcgsClient.cs
+++ b/PcgsInvUi/Services/PcgsClient.cs
@@ -11,6 +11,13 @@ using ReactiveUI;
namespace PcgsInvUi.Services;
public class PcgsClient : ReactiveObject {
+ public enum ErrorType {
+ None,
+ NoCoinFound,
+ InvalidRequestFormat,
+ ApiKeyInvalid
+ }
+
public int NumApiRequests {
get => _numApiRequests;
set => this.RaiseAndSetIfChanged(ref _numApiRequests, value);
@@ -26,7 +33,7 @@ public class PcgsClient : ReactiveObject {
"E8xivTVN_k4Q8ubk8Xv_uU11U91WX0wDht4S4m4BAf2HPmKnGrZ0J2f9nccM7Ns1VkGoT1SXYR74I7aNaMKqXlJGEy3oobseKV0nGiqu1IKvFkOTVjhV37SWUVExSDFMC6FLzs7xJQIHwkcxr5JFOQNmc4ZytEh_-hmCN3NL-z0fEk98tzBmPJ_F7bq_xNru86QMD2A9yalu_dWe-lCysfyE2bCA2EDqwvWenRYf-0XnNtp6qlxxCMlCd-TDqqRKLC3qefCwpXSemwBiNSP5gbC9pwcKrZugkruuJ0nyxxThZymm";
}
- public async Task GetCoinFactsByGrade(int pcgsNumber, string grade, bool plusGrade = false) {
+ public async Task<(ErrorType, Coin?)> GetCoinFactsByGrade(int pcgsNumber, string grade, bool plusGrade = false) {
Coin newCoin = null;
using (HttpClient client = new HttpClient()) {
var gradeParam = GetGradeFromString(grade);
@@ -40,15 +47,12 @@ public class PcgsClient : ReactiveObject {
if (response.IsSuccessStatusCode) {
string jsonResponse = await response.Content.ReadAsStringAsync();
- newCoin = ParseJsonResponse(jsonResponse);
- Console.WriteLine("Coin created.");
- }
- else {
- Console.WriteLine($"Error: {response.StatusCode} - {response.ReasonPhrase}");
+ var parsedResponse = ParseJsonResponse(jsonResponse);
+ return parsedResponse;
}
}
-
- return newCoin;
+
+ return (ErrorType.ApiKeyInvalid, null);
}
private int GetGradeFromString(string grade) {
@@ -59,14 +63,14 @@ public class PcgsClient : ReactiveObject {
// TODO Will eventually create the other methods that are included in the Public API.
- private Coin? ParseJsonResponse(string json) {
+ private (ErrorType, Coin?) ParseJsonResponse(string json) {
Console.WriteLine(json);
// TODO Check for errors in the response
- if (json.Contains("\"ServerMessage\":\"No data found\"")) throw new CoinNotFoundException("No coin found with that PCGS number.");
- if (json.Contains("\"IsValidRequest\":true")) throw new InvalidRequestFormatException("Invalid request format.");
+ 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);
- return newCoin;
+ return (ErrorType.None, newCoin);
}
}
diff --git a/PcgsInvUi/ViewModels/ErrorWindowViewModel.cs b/PcgsInvUi/ViewModels/ErrorWindowViewModel.cs
new file mode 100644
index 0000000..116b3d2
--- /dev/null
+++ b/PcgsInvUi/ViewModels/ErrorWindowViewModel.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Reactive;
+using ReactiveUI;
+using PcgsInvUi.Views;
+
+namespace PcgsInvUi.ViewModels;
+
+public class ErrorWindowViewModel : ViewModelBase {
+
+ public Exception DisplayedException {
+ get => _message;
+ set => this.RaiseAndSetIfChanged(ref _message, value);
+ }
+
+ private Exception _message;
+
+ public ReactiveCommand CloseCommand { get; }
+
+ public ErrorWindowViewModel(string message) {
+ CloseCommand = ReactiveCommand.Create(window => { window.Close(); });
+ }
+}
\ No newline at end of file
diff --git a/PcgsInvUi/ViewModels/MainWindowViewModel.cs b/PcgsInvUi/ViewModels/MainWindowViewModel.cs
index c4256e8..05d3433 100644
--- a/PcgsInvUi/ViewModels/MainWindowViewModel.cs
+++ b/PcgsInvUi/ViewModels/MainWindowViewModel.cs
@@ -41,12 +41,11 @@ public class MainWindowViewModel : ViewModelBase {
public Interaction ShowDeleteWindow { get; }
public ReactiveCommand ExportCommand { get; }
public Interaction ShowExportWindow { get; }
- public Interaction ShowErrorWindow { get; }
+ public Interaction ShowErrorWindow { get; }
private ViewModelBase _sideContent;
private Coin? _selectedCoin;
private double _totalValue;
- private PcgsClient _pcgsClient;
public MainWindowViewModel(CoinDatabase coins) {
ConnectedCoinDatabase = coins;
@@ -57,7 +56,7 @@ public class MainWindowViewModel : ViewModelBase {
TotalValue = DisplayedList.Sum(x => x.TotalPrice);
// Set up the ability to show the error window.
- ShowErrorWindow = new Interaction();
+ ShowErrorWindow = new Interaction();
// Set up the ability to open the delete window.
ShowDeleteWindow = new Interaction();
@@ -84,8 +83,10 @@ public class MainWindowViewModel : ViewModelBase {
catch (IOException e) {
// Something is wrong with the file after it's been created.
Console.WriteLine(e.Message);
+ ShowErrorWindow.Handle(new ErrorWindowViewModel(e.Message));
} catch (Exception e) {
Console.WriteLine(e.Message);
+ ShowErrorWindow.Handle(new ErrorWindowViewModel(e.Message));
}
});
@@ -93,19 +94,29 @@ public class MainWindowViewModel : ViewModelBase {
// This is the same as the above, but with a different syntax.
newViewModel.OkCommand
.Subscribe(async requestStructure => {
- try {
- ConnectedCoinDatabase.CreateCoin(requestStructure.Item1, requestStructure.Item2,
- requestStructure.Item3);
- }
- catch (Exception e) {
- // TODO Error handling - create a popup window.
- Console.WriteLine(e.Message);
+ 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."));
+ break;
+ case PcgsClient.ErrorType.InvalidRequestFormat:
+ Console.WriteLine("Request format is invalid - showing window.");
+ await ShowErrorWindow.Handle(new ErrorWindowViewModel("Request format is invalid."));
+ 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."));
+ break;
}
+
// TODO Clear the text boxes.
});
// Change TotalValue anytime SelectedCoin.TotalPrice changes.
- // Possible null deref - ignored due to null values summing to 0.
+ // Possible null deref - how do you remove this?
this.WhenAnyValue(x => x.SelectedCoin.TotalPrice)
.Subscribe(_ => TotalValue = ConnectedCoinDatabase.Collection.Sum(x => x.TotalPrice));
}
diff --git a/PcgsInvUi/Views/ErrorWindow.axaml b/PcgsInvUi/Views/ErrorWindow.axaml
new file mode 100644
index 0000000..3aa990f
--- /dev/null
+++ b/PcgsInvUi/Views/ErrorWindow.axaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/PcgsInvUi/Views/ErrorWindow.axaml.cs b/PcgsInvUi/Views/ErrorWindow.axaml.cs
new file mode 100644
index 0000000..68da6e4
--- /dev/null
+++ b/PcgsInvUi/Views/ErrorWindow.axaml.cs
@@ -0,0 +1,11 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace PcgsInvUi.Views;
+
+public partial class ErrorWindow : Window {
+ public ErrorWindow() {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/PcgsInvUi/Views/MainWindow.axaml.cs b/PcgsInvUi/Views/MainWindow.axaml.cs
index 445fa4e..b9d1c2c 100644
--- a/PcgsInvUi/Views/MainWindow.axaml.cs
+++ b/PcgsInvUi/Views/MainWindow.axaml.cs
@@ -14,6 +14,7 @@ public partial class MainWindow : ReactiveWindow {
InitializeComponent();
this.WhenActivated(d => d(ViewModel!.ShowDeleteWindow.RegisterHandler(ShowDeleteWindowAsync)));
this.WhenActivated(d => d(ViewModel!.ShowExportWindow.RegisterHandler(ShowFilePickerAsync)));
+ this.WhenActivated(d => d(ViewModel!.ShowErrorWindow.RegisterHandler(ShowErrorWindowAsync)));
// this.WhenActivated(d => d(ViewModel!.ShowFindWindow.RegisterHandler(ShowFindWindowAsync)));
}
@@ -54,7 +55,7 @@ public partial class MainWindow : ReactiveWindow {
}
}
- public async Task ShowErrorWindowAsync(InteractionContext interaction) {
+ public async Task ShowErrorWindowAsync(InteractionContext interaction) {
var window = new ErrorWindow();
window.DataContext = interaction.Input;