commit 8c7e1b917f543ffaa2f83e88b9842938efeb83f4 Author: BobdaFett Date: Mon Oct 9 11:11:48 2023 -0400 Initial commit diff --git a/.idea/.idea.pcgs-inv/.idea/.gitignore b/.idea/.idea.pcgs-inv/.idea/.gitignore new file mode 100644 index 0000000..37a38b9 --- /dev/null +++ b/.idea/.idea.pcgs-inv/.idea/.gitignore @@ -0,0 +1,13 @@ +# 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 new file mode 100644 index 0000000..2e6f308 --- /dev/null +++ b/.idea/.idea.pcgs-inv/.idea/avalonia.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/.idea/.idea.pcgs-inv/.idea/discord.xml b/.idea/.idea.pcgs-inv/.idea/discord.xml new file mode 100644 index 0000000..d8e9561 --- /dev/null +++ b/.idea/.idea.pcgs-inv/.idea/discord.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/.idea.pcgs-inv/.idea/encodings.xml b/.idea/.idea.pcgs-inv/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.pcgs-inv/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.pcgs-inv/.idea/indexLayout.xml b/.idea/.idea.pcgs-inv/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.pcgs-inv/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/PcgsInvUi/App.axaml b/PcgsInvUi/App.axaml new file mode 100644 index 0000000..a175947 --- /dev/null +++ b/PcgsInvUi/App.axaml @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/PcgsInvUi/App.axaml.cs b/PcgsInvUi/App.axaml.cs new file mode 100644 index 0000000..42647b1 --- /dev/null +++ b/PcgsInvUi/App.axaml.cs @@ -0,0 +1,32 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using PcgsInvUi.ViewModels; +using PcgsInvUi.Views; + +namespace PcgsInvUi; + +public partial class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + base.OnFrameworkInitializationCompleted(); + + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + var coins = new Services.CoinCollection(); + + desktop.MainWindow = new MainWindow + { + DataContext = new MainWindowViewModel(coins), + }; + } + + base.OnFrameworkInitializationCompleted(); + } +} \ No newline at end of file diff --git a/PcgsInvUi/Assets/avalonia-logo.ico b/PcgsInvUi/Assets/avalonia-logo.ico new file mode 100644 index 0000000..da8d49f Binary files /dev/null and b/PcgsInvUi/Assets/avalonia-logo.ico differ diff --git a/PcgsInvUi/Models/Coin.cs b/PcgsInvUi/Models/Coin.cs new file mode 100644 index 0000000..bb9e9ad --- /dev/null +++ b/PcgsInvUi/Models/Coin.cs @@ -0,0 +1,26 @@ +using System; + +namespace PcgsInvUi.Models; + +public class Coin +{ + public int PcgsNumber { get; set; } + public string Name { get; set; } + public int Year { get; set; } + public string Denomination { get; set; } + public string MintMark { get; set; } + public string Grade { get; set; } + public double PriceGuideValue { get; set; } + public string CoinFactsLink { get; set; } + public string MajorVariety { get; set; } + public string MinorVariety { get; set; } + public string DieVariety { get; set; } + public string Series { get; set; } + public string Category { get; set; } + public string Designation { get; set; } + public int CertificateNumber { get; set; } + public int Quantity { get; set; } + public double TotalPrice => Quantity * PriceGuideValue; + public double PaidFor { get; set; } + public String Notes { get; set; } +} \ No newline at end of file diff --git a/PcgsInvUi/PcgsInvUi.csproj b/PcgsInvUi/PcgsInvUi.csproj new file mode 100644 index 0000000..2736751 --- /dev/null +++ b/PcgsInvUi/PcgsInvUi.csproj @@ -0,0 +1,26 @@ + + + WinExe + net6.0 + enable + true + app.manifest + true + + + + + + + + + + + + + + + + + + diff --git a/PcgsInvUi/Program.cs b/PcgsInvUi/Program.cs new file mode 100644 index 0000000..177ca67 --- /dev/null +++ b/PcgsInvUi/Program.cs @@ -0,0 +1,23 @@ +using Avalonia; +using Avalonia.ReactiveUI; +using System; + +namespace PcgsInvUi; + +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); + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .WithInterFont() + .LogToTrace() + .UseReactiveUI(); +} diff --git a/PcgsInvUi/Services/CoinCollection.cs b/PcgsInvUi/Services/CoinCollection.cs new file mode 100644 index 0000000..7852adf --- /dev/null +++ b/PcgsInvUi/Services/CoinCollection.cs @@ -0,0 +1,14 @@ +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/Database.cs b/PcgsInvUi/Services/Database.cs new file mode 100644 index 0000000..9dc4a04 --- /dev/null +++ b/PcgsInvUi/Services/Database.cs @@ -0,0 +1,6 @@ +namespace PcgsInvUi.Services; + +public class Database +{ + // TODO Create a database using SQLite. +} \ No newline at end of file diff --git a/PcgsInvUi/Services/PcgsClient.cs b/PcgsInvUi/Services/PcgsClient.cs new file mode 100644 index 0000000..b6bf2a7 --- /dev/null +++ b/PcgsInvUi/Services/PcgsClient.cs @@ -0,0 +1,51 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Newtonsoft.Json; +using PcgsInvUi.Models; + +namespace PcgsInvUi.Services; + +public class PcgsClient +{ + private string _apiUri = "https://api.pcgs.com/publicapi"; + private readonly string _apiKey; + + public PcgsClient(string apiKey) + { + _apiKey = apiKey; + } + + public async Task GetCoinFactsByGrade(int pcgsNumber, int grade, bool plusGrade) + { + Coin newCoin = null; + using (HttpClient client = new HttpClient()) + { + string requestUri = + $"{_apiUri}/coindetail/GetCoinFactsByGrade/?PCGSNo={pcgsNumber}&GradeNo={grade}&PlusGrade={plusGrade}"; + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", _apiKey); + HttpResponseMessage response = await client.GetAsync(requestUri); + + if (response.IsSuccessStatusCode) + { + string jsonResponse = await response.Content.ReadAsStringAsync(); + newCoin = ParseJsonResponse(jsonResponse); + } + else + { + Console.WriteLine($"Error: {response.StatusCode} - {response.ReasonPhrase}"); + } + } + + return newCoin; + } + + // TODO Will eventually create the other methods that are included in the Public API. + + private Coin ParseJsonResponse(string json) + { + Coin newCoin = JsonConvert.DeserializeObject(json); + return newCoin; + } +} \ No newline at end of file diff --git a/PcgsInvUi/ViewLocator.cs b/PcgsInvUi/ViewLocator.cs new file mode 100644 index 0000000..8ee75d9 --- /dev/null +++ b/PcgsInvUi/ViewLocator.cs @@ -0,0 +1,27 @@ +using System; +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using PcgsInvUi.ViewModels; + +namespace PcgsInvUi; + +public class ViewLocator : IDataTemplate +{ + public Control Build(object data) + { + var name = data.GetType().FullName!.Replace("ViewModel", "View"); + var type = Type.GetType(name); + + if (type != null) + { + return (Control)Activator.CreateInstance(type)!; + } + + return new TextBlock { Text = "Not Found: " + name }; + } + + public bool Match(object data) + { + return data is ViewModelBase; + } +} \ No newline at end of file diff --git a/PcgsInvUi/ViewModels/CoinListViewModel.cs b/PcgsInvUi/ViewModels/CoinListViewModel.cs new file mode 100644 index 0000000..19fb367 --- /dev/null +++ b/PcgsInvUi/ViewModels/CoinListViewModel.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using PcgsInvUi.Models; +using ReactiveUI; + +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); + TotalValue = 0; + } +} \ No newline at end of file diff --git a/PcgsInvUi/ViewModels/MainWindowViewModel.cs b/PcgsInvUi/ViewModels/MainWindowViewModel.cs new file mode 100644 index 0000000..1611313 --- /dev/null +++ b/PcgsInvUi/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,45 @@ +using System; +using System.Reactive.Linq; +using PcgsInvUi.Models; +using PcgsInvUi.Services; +using ReactiveUI; + +namespace PcgsInvUi.ViewModels; + +public class MainWindowViewModel : ViewModelBase +{ + public CoinListViewModel List { get; } + private ViewModelBase _content; + + public MainWindowViewModel(CoinCollection coins) + { + List = new CoinListViewModel(coins.GetItems()); + Content = List; + } + + public ViewModelBase Content + { + get => _content; + private set => this.RaiseAndSetIfChanged(ref _content, value); + } + + public void AddItem() + { + Console.WriteLine("You hit the button!"); + var newViewModel = new NewViewModel(); + + Observable.Merge( + newViewModel.OkCommand, + newViewModel.CancelCommand.Select(_ => (Coin?)null)) + .Take(1) + .Subscribe(model => + { + if (model != null) + List.CoinCollection.Add(model); + + Content = List; + }); + + Content = newViewModel; + } +} diff --git a/PcgsInvUi/ViewModels/NewViewModel.cs b/PcgsInvUi/ViewModels/NewViewModel.cs new file mode 100644 index 0000000..95c3f29 --- /dev/null +++ b/PcgsInvUi/ViewModels/NewViewModel.cs @@ -0,0 +1,45 @@ +using System; +using System.Reactive; +using PcgsInvUi.Models; +using ReactiveUI; + +namespace PcgsInvUi.ViewModels; + +public class NewViewModel : ViewModelBase +{ + private string _pcgsNumber; + private string _grade; + 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 NewViewModel() + { + var okEnabled = this.WhenAnyValue( + x => x.PcgsNumber, + x => !string.IsNullOrWhiteSpace(x)); + + OkCommand = ReactiveCommand.Create( + () => new Coin { PcgsNumber = Int32.Parse(PcgsNumber) }, + okEnabled); + CancelCommand = ReactiveCommand.Create(() => { }); + } + + public ReactiveCommand OkCommand { get; } + public ReactiveCommand CancelCommand { get; } + +} \ No newline at end of file diff --git a/PcgsInvUi/ViewModels/ViewModelBase.cs b/PcgsInvUi/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000..6ea13e7 --- /dev/null +++ b/PcgsInvUi/ViewModels/ViewModelBase.cs @@ -0,0 +1,7 @@ +using ReactiveUI; + +namespace PcgsInvUi.ViewModels; + +public class ViewModelBase : ReactiveObject +{ +} diff --git a/PcgsInvUi/Views/CoinListView.axaml b/PcgsInvUi/Views/CoinListView.axaml new file mode 100644 index 0000000..6315898 --- /dev/null +++ b/PcgsInvUi/Views/CoinListView.axaml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + +