diff --git a/.idea/.idea.pcgs-inv/.idea/.gitignore b/.idea/.idea.pcgs-inv/.idea/.gitignore
deleted file mode 100644
index 37a38b9..0000000
--- a/.idea/.idea.pcgs-inv/.idea/.gitignore
+++ /dev/null
@@ -1,13 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Rider ignored files
-/.idea.pcgs-inv.iml
-/contentModel.xml
-/modules.xml
-/projectSettingsUpdater.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/.idea.pcgs-inv/.idea/avalonia.xml b/.idea/.idea.pcgs-inv/.idea/avalonia.xml
deleted file mode 100644
index 091e888..0000000
--- a/.idea/.idea.pcgs-inv/.idea/avalonia.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/.idea.pcgs-inv/.idea/discord.xml b/.idea/.idea.pcgs-inv/.idea/discord.xml
deleted file mode 100644
index d8e9561..0000000
--- a/.idea/.idea.pcgs-inv/.idea/discord.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/.idea.pcgs-inv/.idea/encodings.xml b/.idea/.idea.pcgs-inv/.idea/encodings.xml
deleted file mode 100644
index df87cf9..0000000
--- a/.idea/.idea.pcgs-inv/.idea/encodings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/.idea/.idea.pcgs-inv/.idea/indexLayout.xml b/.idea/.idea.pcgs-inv/.idea/indexLayout.xml
deleted file mode 100644
index 7b08163..0000000
--- a/.idea/.idea.pcgs-inv/.idea/indexLayout.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/PcgsInvUi/App.axaml.cs b/PcgsInvUi/App.axaml.cs
index e71a99c..8501681 100644
--- a/PcgsInvUi/App.axaml.cs
+++ b/PcgsInvUi/App.axaml.cs
@@ -1,12 +1,9 @@
-using System;
-using System.Data.Entity.Migrations.Model;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using PcgsInvUi.Services;
using PcgsInvUi.ViewModels;
using PcgsInvUi.Views;
-using PcgsInvUi.Models;
namespace PcgsInvUi;
@@ -23,14 +20,11 @@ public partial class App : Application {
DataContext = new MainWindowViewModel(coins),
};
+ // Allow the application to save the database when it exits.
desktop.Exit += (sender, args) => {
coins.UpdateCollection("CollectionTable");
};
}
base.OnFrameworkInitializationCompleted();
}
-
- public void OnExit(object sender, ControlledApplicationLifetimeExitEventArgs e) {
- // Save the database?
- }
-}
\ No newline at end of file
+}
diff --git a/PcgsInvUi/Services/CoinCollection.cs b/PcgsInvUi/Services/CoinCollection.cs
deleted file mode 100644
index dc89845..0000000
--- a/PcgsInvUi/Services/CoinCollection.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Collections.Generic;
-using PcgsInvUi.Models;
-
-namespace PcgsInvUi.Services;
-
-public class CoinCollection {
- public IEnumerable GetItems() => new[] {
- new Coin(),
- new Coin(),
- new Coin(),
- };
-}
\ No newline at end of file
diff --git a/PcgsInvUi/Services/CoinDatabase.cs b/PcgsInvUi/Services/CoinDatabase.cs
index 221d845..03e6cc1 100644
--- a/PcgsInvUi/Services/CoinDatabase.cs
+++ b/PcgsInvUi/Services/CoinDatabase.cs
@@ -10,23 +10,29 @@ 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;
-
+ private PcgsClient? _pcgsClient;
+
+ ///
+ /// Creates and initilizes a new instance of the CoinDatabase.
+ /// Must call TryInitApiClient() after creating a new instance.
+ ///
public CoinDatabase() {
// Create a connection to SQLite
Connection = CreateConnection();
- // Create a client to access the API.
- _pcgsClient = new PcgsClient(
- "eAb8gS0I2XAvT_5gJeiGJaglMia1Tk-oB4kJUK6kuafyrny_S61vIJY-Ikl4nCQM67wrdxzUqLVWTV2kBSxD3d5XNBHxHnYBhcSS6dOPug0hZaF3qAv56df3gYSzOGh9Tif5y0eP3Iw0LrqKDr1Hj-dk6SV6GKog2IIqCPQhhHH8FMTWBTYO-_O8cx7qLdM5GM8KlTsic6g3VRUhM8EA_4OO04dCfmNLGhqINRl3jGZ0Q4ziI8fng2bVWsIyteqiPzUn10rIQ3-OPpqVZG_DxeOmOejj4GzbUNyqUOajy-nr5rYY");
// Create a base table.
CreateCollection("CollectionTable");
+ CreateApiTable();
Console.WriteLine("Connected to database.");
Collection = new ObservableCollection();
GetCollection("CollectionTable");
Console.WriteLine("Got collection.");
}
+ ///
+ /// Destructor. Currently not used, due to the strange nature of desktop applications.
+ ///
~CoinDatabase() {
Console.WriteLine("Destructor called, saving collection.");
// Save collection.
@@ -34,13 +40,74 @@ public class CoinDatabase {
// Close the connection.
Connection.Close();
}
+
+ ///
+ /// 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();
+ cmd.CommandText = "SELECT API_KEY FROM ApiTable";
+ using (var reader = cmd.ExecuteReader()) {
+ if (reader.Read()) {
+ var apiKey = reader.GetString(0);
+ _pcgsClient = await PcgsClient.CreateClient(apiKey);
+
+ // Check that the client is valid.
+ if (_pcgsClient is null) {
+ IsClientConnected = false;
+ Console.WriteLine("Failed. API key was invalid.");
+ return IsClientConnected;
+ }
+
+ IsClientConnected = true;
+ Console.WriteLine("Done! API key was found from database.");
+ }
+ else {
+ IsClientConnected = false;
+ Console.WriteLine("Failed. API key not found.");
+ }
+ }
+ return IsClientConnected;
+ }
+
+ ///
+ /// Atttempts to initialize the PCGS API client.
+ ///
+ /// 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... ");
+ _pcgsClient = await PcgsClient.CreateClient(apiKey);
+ // Check if the API key is valid and if it is, then add to the database.
+ if (_pcgsClient is null) {
+ IsClientConnected = false;
+ Console.WriteLine("Failed. API key was invalid.");
+ return IsClientConnected;
+ }
+
+ var cmd = Connection.CreateCommand();
+ cmd.CommandText = $"INSERT INTO ApiTable (API_KEY, REQUESTS_REMAINING) VALUES (\"{apiKey}\", 1000)";
+ await cmd.ExecuteNonQueryAsync();
+
+ IsClientConnected = true;
+ Console.WriteLine("Done! API key was added to database.");
+ return IsClientConnected;
+ }
+
+ ///
+ /// 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")) {
- // Create the database.
+ // If not, create the database.
Console.Write("Creating database... ");
- SQLiteConnection.CreateFile("coin_collection");
+ SQLiteConnection.CreateFile("coin_collection.sqlite");
Console.WriteLine("Done.");
}
@@ -60,6 +127,33 @@ public class CoinDatabase {
return connection;
}
+ ///
+ /// 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.
+ if (!IsConnected) {
+ // TODO Create a NotConnectedToDatabaseException
+ Console.WriteLine("Not connected to database.");
+ return;
+ }
+
+ // Create a SQL command.
+ SQLiteCommand cmd = Connection.CreateCommand();
+ cmd.CommandText =
+ "CREATE TABLE IF NOT EXISTS ApiTable (" +
+ "API_KEY TEXT, " +
+ "REQUESTS_REMAINING INTEGER, " +
+ "PRIMARY KEY(API_KEY))";
+
+ // Execute the command
+ cmd.ExecuteNonQuery();
+ Console.WriteLine("API table initialized.");
+ }
+
+ ///
+ /// Creates a table to store the collection.
+ ///
public void CreateCollection(string collectionName) {
// Create a table to store the collection.
if (!IsConnected) {
@@ -93,8 +187,12 @@ public class CoinDatabase {
// Execute the command.
cmd.ExecuteNonQuery();
+ Console.WriteLine("Collection table initialized.");
}
+ ///
+ /// Inserts a Coin object into the database.
+ ///
public void InsertCoin(Coin coin) {
Console.WriteLine("Inserting coin...");
// Use our existing connection to insert a coin to the database.
@@ -121,6 +219,13 @@ public class CoinDatabase {
Console.WriteLine("Coin inserted.");
}
+ ///
+ /// Creates a Coin object and saves it to the database.
+ ///
+ /// The PCGS number of the coin.
+ /// The grade of the coin.
+ /// The quantity of the coin.
+ /// The error type returned by the API.
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.
@@ -146,6 +251,12 @@ public class CoinDatabase {
return apiResult.Item1;
}
+ ///
+ /// 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) {
Console.WriteLine($"Getting coin {pcgsNumber}, {grade}...");
int gradeInt = GetGradeFromString(grade);
@@ -165,6 +276,10 @@ public class CoinDatabase {
return null;
}
+ ///
+ /// 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();
@@ -178,6 +293,11 @@ public class CoinDatabase {
}
}
+ ///
+ /// Creates a CSV formatted string from a collection.
+ ///
+ /// The name of the collection table.
+ /// A CSV formatted string.
public String CollectionToCSV(string collectionName) {
var cmd = Connection.CreateCommand();
cmd.CommandText = $"SELECT * FROM {collectionName}";
@@ -226,6 +346,11 @@ public class CoinDatabase {
return resultString;
}
+ ///
+ /// Saves a Coin object to the database.
+ ///
+ /// The name of the collection table.
+ /// The coin to save.
public void SaveCoin(string collectionName, Coin coin) {
// Get and replace the coin from the database.
Console.WriteLine("Saving coin...");
@@ -252,6 +377,10 @@ public class CoinDatabase {
cmd.ExecuteNonQuery();
}
+ ///
+ /// 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.
@@ -260,6 +389,10 @@ public class CoinDatabase {
}
}
+ ///
+ /// 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) {
// Create a coin while filtering out any possible null values - they throw exceptions.
Coin coin = new Coin();
@@ -283,6 +416,10 @@ public class CoinDatabase {
return coin;
}
+ ///
+ /// Deletes a Coin object from the database.
+ ///
+ /// The coin to delete.
public void DeleteCoin(Coin delCoin) {
SQLiteCommand cmd = Connection.CreateCommand();
cmd.CommandText = "DELETE FROM CollectionTable WHERE PCGS_NUMBER = @PcgsNumber AND GRADE = @Grade";
@@ -292,9 +429,18 @@ public class CoinDatabase {
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.
+ ///
+ /// The grade string.
private int GetGradeFromString(string grade) {
var actualGrade = int.Parse(grade.Substring(grade.Length - 2));
Console.WriteLine($"Grade is {actualGrade}");
return actualGrade;
}
-}
\ No newline at end of file
+}
+
+public class NotConnectedToDatabaseException : Exception {
+ public NotConnectedToDatabaseException() : base("Not connected to database.") { }
+}
diff --git a/PcgsInvUi/Services/PcgsClient.cs b/PcgsInvUi/Services/PcgsClient.cs
index e4329d2..0f70915 100644
--- a/PcgsInvUi/Services/PcgsClient.cs
+++ b/PcgsInvUi/Services/PcgsClient.cs
@@ -1,11 +1,9 @@
using System;
-using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using PcgsInvUi.Models;
using System.Text.Json;
-using DynamicData.Kernel;
using ReactiveUI;
namespace PcgsInvUi.Services;
@@ -27,14 +25,47 @@ public class PcgsClient : ReactiveObject {
private readonly string _apiKey;
private int _numApiRequests;
- public PcgsClient(string apiKey) {
- // _apiKey = apiKey;
- _apiKey =
- "E8xivTVN_k4Q8ubk8Xv_uU11U91WX0wDht4S4m4BAf2HPmKnGrZ0J2f9nccM7Ns1VkGoT1SXYR74I7aNaMKqXlJGEy3oobseKV0nGiqu1IKvFkOTVjhV37SWUVExSDFMC6FLzs7xJQIHwkcxr5JFOQNmc4ZytEh_-hmCN3NL-z0fEk98tzBmPJ_F7bq_xNru86QMD2A9yalu_dWe-lCysfyE2bCA2EDqwvWenRYf-0XnNtp6qlxxCMlCd-TDqqRKLC3qefCwpXSemwBiNSP5gbC9pwcKrZugkruuJ0nyxxThZymm";
+ ///
+ /// 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;
+ }
+
+ ///
+ /// 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.
+ public static async Task CreateClient(string apiKey) {
+ // Check if the api key is valid.
+ var testClient = new PcgsClient(apiKey);
+ var validCreds = await testClient.VerifyCredentials();
+ if (!validCreds) return null;
+ return testClient;
}
+ ///
+ /// 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.
+ if (result.Item1 == ErrorType.ApiKeyInvalid) return false;
+ return true;
+ }
+
+ ///
+ /// 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) {
- Coin newCoin = null;
using (HttpClient client = new HttpClient()) {
var gradeParam = GetGradeFromString(grade);
@@ -46,6 +77,7 @@ public class PcgsClient : ReactiveObject {
Console.WriteLine("Got response.");
if (response.IsSuccessStatusCode) {
+ // Check response for errors.
string jsonResponse = await response.Content.ReadAsStringAsync();
var parsedResponse = ParseJsonResponse(jsonResponse);
return parsedResponse;
@@ -55,17 +87,25 @@ public class PcgsClient : ReactiveObject {
return (ErrorType.ApiKeyInvalid, null);
}
+ ///
+ /// 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));
+ 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;
}
- // TODO Will eventually create the other methods that are included in the Public 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) {
Console.WriteLine(json);
- // TODO 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("\"IsValidRequest\":false")) return (ErrorType.InvalidRequestFormat, null);
@@ -86,4 +126,4 @@ 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/PcgsInvUi/ViewModels/ApiKeyViewModel.cs b/PcgsInvUi/ViewModels/ApiKeyViewModel.cs
new file mode 100644
index 0000000..468b2dd
--- /dev/null
+++ b/PcgsInvUi/ViewModels/ApiKeyViewModel.cs
@@ -0,0 +1,30 @@
+using ReactiveUI;
+using System.Reactive;
+
+namespace PcgsInvUi.ViewModels;
+
+public class ApiKeyViewModel : ViewModelBase {
+ public string ApiKeyInput {
+ get => _apiKeyInput;
+ set => this.RaiseAndSetIfChanged(ref _apiKeyInput, value);
+ }
+ 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);
+ }
+}
diff --git a/PcgsInvUi/ViewModels/MainWindowViewModel.cs b/PcgsInvUi/ViewModels/MainWindowViewModel.cs
index af680ba..2291d54 100644
--- a/PcgsInvUi/ViewModels/MainWindowViewModel.cs
+++ b/PcgsInvUi/ViewModels/MainWindowViewModel.cs
@@ -7,21 +7,10 @@ using System.Reactive.Linq;
using PcgsInvUi.Models;
using PcgsInvUi.Services;
using ReactiveUI;
+using System.Threading.Tasks;
namespace PcgsInvUi.ViewModels;
-// TODO Create proper sums for total value.
-// TODO Error handling for API calls.
-// TODO Manual handling of API key.
-// TODO Use CoinFacts link.
-// TODO Splash screen during startup.
-// TODO Take giant sheet of coins to integrate into the application.
-// TODO Update all coins. Choose those that are older first.
-// TODO API request tracker (1000 per day)
-// TODO Ok button greys out while request is happening.
-// TODO Track when coins were last updated.
-// TODO Image for coin in notes view.
-
public class MainWindowViewModel : ViewModelBase {
public ViewModelBase SidebarContent {
get => _sideContent;
@@ -47,14 +36,29 @@ public class MainWindowViewModel : ViewModelBase {
private ViewModelBase _sideContent;
private Coin? _selectedCoin;
private double _totalValue;
-
+
public MainWindowViewModel(CoinDatabase coins) {
ConnectedCoinDatabase = coins;
+ var keyFound = Task.Run(() => coins.TryInitApiClient()).Result;
+ Console.WriteLine("Function completed.");
var newViewModel = new NewViewModel();
- _sideContent = newViewModel;
+ var apiKeyViewModel = new ApiKeyViewModel();
// DisplayedList = CoinCollection = new ObservableCollection(coins.GetItems());
DisplayedList = ConnectedCoinDatabase.Collection;
TotalValue = DisplayedList.Sum(x => x.TotalPrice);
+
+ // Setup interaction to show ApiKeyWindow
+ apiKeyViewModel.OkCommand.Subscribe(async apiKey => {
+ if (await ConnectedCoinDatabase.TryInitApiClient(apiKey))
+ 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;
// Set up the ability to show the error window.
ShowErrorWindow = new Interaction();
@@ -113,7 +117,8 @@ public class MainWindowViewModel : ViewModelBase {
break;
}
- // TODO Clear the text boxes.
+ // Clear the text boxes.
+ newViewModel.ClearText();
});
// Update TotalValue anytime a coin's quantity changes.
@@ -126,4 +131,11 @@ public class MainWindowViewModel : ViewModelBase {
TotalValue = ConnectedCoinDatabase.Collection.Sum(x => x.TotalPrice);
};
}
+
+ // 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");
+ }
}
diff --git a/PcgsInvUi/ViewModels/NewViewModel.cs b/PcgsInvUi/ViewModels/NewViewModel.cs
index dca166d..3909324 100644
--- a/PcgsInvUi/ViewModels/NewViewModel.cs
+++ b/PcgsInvUi/ViewModels/NewViewModel.cs
@@ -40,9 +40,7 @@ public class NewViewModel : ViewModelBase {
okEnabled);
ClearCommand = ReactiveCommand.Create(() => {
- PcgsNumber = "";
- Grade = "";
- Quantity = "";
+ ClearText();
});
// Initialize grade values for use in the view.
@@ -83,4 +81,13 @@ public class NewViewModel : ViewModelBase {
public ReactiveCommand OkCommand { get; }
public ReactiveCommand ClearCommand { get; }
-}
\ No newline at end of file
+
+ ///
+ /// Clears the text fields.
+ ///
+ public void ClearText() {
+ PcgsNumber = "";
+ Grade = "";
+ Quantity = "";
+ }
+}
diff --git a/PcgsInvUi/Views/ApiKeyView.axaml b/PcgsInvUi/Views/ApiKeyView.axaml
new file mode 100644
index 0000000..e125862
--- /dev/null
+++ b/PcgsInvUi/Views/ApiKeyView.axaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/PcgsInvUi/Views/ApiKeyView.axaml.cs b/PcgsInvUi/Views/ApiKeyView.axaml.cs
new file mode 100644
index 0000000..74d877c
--- /dev/null
+++ b/PcgsInvUi/Views/ApiKeyView.axaml.cs
@@ -0,0 +1,9 @@
+using Avalonia.Controls;
+
+namespace PcgsInvUi.Views;
+
+public partial class ApiKeyView : UserControl {
+ public ApiKeyView() {
+ InitializeComponent();
+ }
+}
diff --git a/PcgsInvUi/Views/MainWindow.axaml.cs b/PcgsInvUi/Views/MainWindow.axaml.cs
index 6cd8203..be6ccd3 100644
--- a/PcgsInvUi/Views/MainWindow.axaml.cs
+++ b/PcgsInvUi/Views/MainWindow.axaml.cs
@@ -1,5 +1,4 @@
using System;
-using System.IO;
using System.Reactive;
using System.Threading.Tasks;
using Avalonia.Platform.Storage;
@@ -36,6 +35,7 @@ public partial class MainWindow : ReactiveWindow {
private async Task ShowFilePickerAsync(InteractionContext interationContext) {
var topLevel = GetTopLevel(this);
+ if (topLevel is null) throw new Exception("Could not find top level of the file path.");
var file = await topLevel.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions {
Title = "Save CSV...",
DefaultExtension = ".csv",
diff --git a/PcgsInvUi/Views/NewView.axaml.cs b/PcgsInvUi/Views/NewView.axaml.cs
index 5a4abb6..905418a 100644
--- a/PcgsInvUi/Views/NewView.axaml.cs
+++ b/PcgsInvUi/Views/NewView.axaml.cs
@@ -1,6 +1,4 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
+using Avalonia.Controls;
namespace PcgsInvUi.Views;
@@ -8,4 +6,4 @@ public partial class NewView : UserControl {
public NewView() {
InitializeComponent();
}
-}
\ No newline at end of file
+}
diff --git a/README.md b/README.md
index f44cc5d..c75ca23 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
A coin collection inventory app, written in C# with the Avalonia UI framework utilizing the PCGS Public API.
# Requirements
-[.NET Core >= 6](https://dotnet.microsoft.com/en-us/download) - you will be prompted for install before running the program if it is not installed.
+[.NET Runtime >= 6](https://dotnet.microsoft.com/en-us/download) - you will be prompted for install before running the program if it is not installed.
# Install
Extract contents of the downloaded file to an accessible folder (ie. Documents/PCGSApp)
@@ -31,3 +31,12 @@ in the lower right corner) will update automatically.
To export into a CSV file, click the Export to CSV button in the lower right corner. This will allow you to choose a
location and name for the file. These files are usable with any application that can open a .csv file, like Excel.
+
+# Contributions
+
+If you would like to help development, and don't mind looking through my spaghetti code (I'm still learning how to structure this)
+then feel free to submit a pull request! I'm happy to take any help that I can get.
+
+I've just started utilizing makefiles, however, since I'm currently using Windows, I'm not completely sure if there are formatting
+changes that should be made to that. I will leave makefile creation for other platforms up to someone that uses these other
+operating systems for now.
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..7d8b7e6
--- /dev/null
+++ b/makefile
@@ -0,0 +1,5 @@
+build:
+ dotnet build .\PcgsInvUi\PcgsInvUi.csproj
+
+run:
+ dotnet run --project .\PcgsInvUi\PcgsInvUi.csproj
diff --git a/pcgs-inv.sln.DotSettings.user b/pcgs-inv.sln.DotSettings.user
new file mode 100644
index 0000000..73f7e49
--- /dev/null
+++ b/pcgs-inv.sln.DotSettings.user
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file