diff --git a/EstusShots.Client/EstusShots.Client.csproj b/EstusShots.Client/EstusShots.Client.csproj new file mode 100644 index 0000000..548a249 --- /dev/null +++ b/EstusShots.Client/EstusShots.Client.csproj @@ -0,0 +1,11 @@ + + + + netcoreapp3.1 + + + + + + + diff --git a/EstusShots.Client/EstusShotsClient.cs b/EstusShots.Client/EstusShotsClient.cs new file mode 100644 index 0000000..dba29d2 --- /dev/null +++ b/EstusShots.Client/EstusShotsClient.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using EstusShots.Shared.Models; + +namespace EstusShots.Client +{ + public class EstusShotsClient + { + private readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + }; + + private string ApiUrl { get; } + private HttpClient HttpClient { get; } + + public EstusShotsClient(string apiUrl) + { + ApiUrl = apiUrl; + HttpClient = new HttpClient {Timeout = TimeSpan.FromSeconds(10)}; + } + + public async Task> GetSeasons() + { + var response = HttpClient.GetAsync(ApiUrl + "seasons").Result; + var jsonData = await response.Content.ReadAsStringAsync(); + var data = JsonSerializer.Deserialize>(jsonData, _serializerOptions); + return data; + } + } +} \ No newline at end of file diff --git a/EstusShots.Gtk/DataStores/SeasonsDataStore.cs b/EstusShots.Gtk/DataStores/SeasonsDataStore.cs new file mode 100644 index 0000000..5961da9 --- /dev/null +++ b/EstusShots.Gtk/DataStores/SeasonsDataStore.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using EstusShots.Shared.Models; +using GLib; +using Gtk; + +namespace EstusShots.Gtk.DataStores +{ + public class SeasonsDataStore + { + public ListStore ListStore { get; private set; } + + public TreeView View { get; set; } + + public List Data { get; set; } + + public SeasonsDataStore(TreeView view) + { + ListStore = new ListStore(GType.Int, GType.String, GType.String); + Data = new List(); + View = view; + var columns = BuildColumns(); + columns.ForEach(column => View.AppendColumn(column)); + View.Model = ListStore; + } + + public void DataBind() + { + ListStore.Clear(); + foreach (var datum in Data) + { + var row = new object[] {datum.Number, datum.Game, datum.Start.ToString(CultureInfo.InvariantCulture)}; + try + { + ListStore.AppendValues(row); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + } + private List BuildColumns() + { + var columns = new List + { + new TreeViewColumn("Number", new CellRendererText(), "text", 0), + new TreeViewColumn("Game", new CellRendererText(), "text", 1), + new TreeViewColumn("Start", new CellRendererText(), "text", 2) + }; + return columns; + } + } +} \ No newline at end of file diff --git a/EstusShots.Gtk/EstusShots.Gtk.csproj b/EstusShots.Gtk/EstusShots.Gtk.csproj index 82d6bad..4ddfaa2 100644 --- a/EstusShots.Gtk/EstusShots.Gtk.csproj +++ b/EstusShots.Gtk/EstusShots.Gtk.csproj @@ -17,6 +17,7 @@ + diff --git a/EstusShots.Gtk/MainWindow.cs b/EstusShots.Gtk/MainWindow.cs index 78df622..4a7c5da 100644 --- a/EstusShots.Gtk/MainWindow.cs +++ b/EstusShots.Gtk/MainWindow.cs @@ -1,9 +1,14 @@ using System; +using System.Net; using System.Net.Http; using System.Text; using System.Text.Json; +using EstusShots.Client; +using EstusShots.Gtk.DataStores; using EstusShots.Shared.Models; using Gtk; +using Application = Gtk.Application; +using DateTime = System.DateTime; using UI = Gtk.Builder.ObjectAttribute; namespace EstusShots.Gtk @@ -11,60 +16,86 @@ namespace EstusShots.Gtk class MainWindow : Window { private const string ApiUrl = "http://localhost:5000/api/"; + + private EstusShotsClient Client { get; } - [UI] private TreeView _seasonsView = null; - [UI] private Button _loadButton = null; - [UI] private Label _infoLabel = null; + [UI] private readonly TreeView _seasonsView = null; + [UI] private readonly Button _loadButton = null; + [UI] private readonly Button _newSeasonButton = null; + [UI] private readonly Label _infoLabel = null; + + private SeasonsDataStore SeasonsView { get; set; } public MainWindow() : this(new Builder("MainWindow.glade")) { } private MainWindow(Builder builder) : base(builder.GetObject("MainWindow").Handle) { builder.Autoconnect(this); + Client = new EstusShotsClient("http://localhost:5000/api/"); DeleteEvent += Window_DeleteEvent; - _loadButton.Clicked += _loadButton_Clicked; + _loadButton.Clicked += LoadButtonClicked; + _newSeasonButton.Clicked += NewSeasonButtonOnClicked; + + SeasonsView = new SeasonsDataStore(_seasonsView); + + Info("Application Started"); } - private void Window_DeleteEvent(object sender, DeleteEventArgs a) + private void NewSeasonButtonOnClicked(object sender, EventArgs e) { - Application.Quit(); - } - - private void _loadButton_Clicked(object sender, EventArgs a) - { - var season = new Season() + var season = new Season { Game = "Test Game", + Number = 1, Start = DateTime.Now - }; - + }; var content = new StringContent(JsonSerializer.Serialize(season), Encoding.UTF8, "application/json"); var client = new HttpClient(); try { - var response = client.PostAsync(ApiUrl + "seasons", content).Result; + var response = client.PostAsync(ApiUrl + "season", content).Result; - if (response.Headers.Location == null) + if (!response.IsSuccessStatusCode) { _infoLabel.Text = $"Error while creating Season: {response.ReasonPhrase}"; return; } - var data = client.GetAsync(response.Headers.Location).Result; - var jsonData = data.Content.ReadAsStringAsync().Result; - var options = new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true, - }; - var s = JsonSerializer.Deserialize(jsonData, options); - _infoLabel.Text = $"Created new Season: {s.Game} ({s.SeasonId})"; + Info($"Created new Season"); } - catch (Exception e) + catch (Exception ex) { - _infoLabel.Text = $"Exception Occured: {e.Message}"; - Console.WriteLine(e.Message); + _infoLabel.Text = $"Exception Occured: {ex.Message}"; + Console.WriteLine(ex.Message); } } + + private void SeasonsViewOnShown(object sender, EventArgs e) + { + Info("Loading Data"); + // var seasons = Client.GetSeasons().Result; + // SeasonsView.Data = seasons; + // SeasonsView.DataBind(); + // Info("Data Loaded"); + } + + private void LoadButtonClicked(object sender, EventArgs a) + { + var seasons = Client.GetSeasons().Result; + SeasonsView.Data = seasons; + SeasonsView.DataBind(); + Info("List Refreshed"); + } + + private void Window_DeleteEvent(object sender, DeleteEventArgs a) + { + Application.Quit(); + } + + private void Info(string message) + { + _infoLabel.Text = message; + } } } \ No newline at end of file diff --git a/EstusShots.Gtk/MainWindow.glade b/EstusShots.Gtk/MainWindow.glade index b7ec637..a3fec1e 100644 --- a/EstusShots.Gtk/MainWindow.glade +++ b/EstusShots.Gtk/MainWindow.glade @@ -4,9 +4,9 @@ False - Example Window - 480 - 240 + Estus Shots + 800 + 600 @@ -14,38 +14,106 @@ True False + 5 + 5 + 5 + 2 vertical - - True - False - - - False - True - 0 - - - - + True True - - + in + + + True + True + horizontal + + + + + True True + 0 + + + + + True + False + end + + + Reload + True + True + True + + + False + True + 0 + + + + + New Season + True + True + True + + + True + True + 1 + + + + + + + + False + True + 2 1 - - Load Seasons + True - True - True + False + 2 + 5 + + + True + False + gtk-connect + 2 + + + False + True + 0 + + + + + True + False + + + False + True + 1 + + False diff --git a/EstusShots.Server/Controllers/SeasonController.cs b/EstusShots.Server/Controllers/SeasonController.cs new file mode 100644 index 0000000..c848499 --- /dev/null +++ b/EstusShots.Server/Controllers/SeasonController.cs @@ -0,0 +1,43 @@ +using System; +using System.Threading.Tasks; +using AutoMapper; +using EstusShots.Server.Models; +using EstusShots.Server.Services; +using Microsoft.AspNetCore.Mvc; +using Dto = EstusShots.Shared.Models; + +namespace EstusShots.Server.Controllers +{ + [ApiController] + [Route("/api/[controller]")] + public class SeasonController : ControllerBase + { + private readonly EstusShotsContext _context; + private readonly IMapper _mapper; + + public SeasonController(EstusShotsContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + [HttpGet("{id}")] + public async Task> GetSeason(Guid id) + { + var season = await _context.Seasons.FindAsync(id); + if (season == null) return NotFound(); + + var seasonDto = _mapper.Map(season); + return seasonDto; + } + + [HttpPost] + public async Task> CreateSeason(Dto.Season season) + { + var dbSeason = _mapper.Map(season); + _context.Seasons.Add(dbSeason); + await _context.SaveChangesAsync(); + return CreatedAtAction(nameof(GetSeason), new {id = dbSeason.SeasonId}, dbSeason); + } + } +} \ No newline at end of file diff --git a/EstusShots.Server/Controllers/SeasonsController.cs b/EstusShots.Server/Controllers/SeasonsController.cs index 9786ec3..3ce9ad3 100644 --- a/EstusShots.Server/Controllers/SeasonsController.cs +++ b/EstusShots.Server/Controllers/SeasonsController.cs @@ -1,8 +1,10 @@ -using System; +using System.Collections.Generic; using System.Threading.Tasks; +using AutoMapper; using EstusShots.Server.Services; -using EstusShots.Shared.Models; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Dto = EstusShots.Shared.Models; namespace EstusShots.Server.Controllers { @@ -11,26 +13,21 @@ namespace EstusShots.Server.Controllers public class SeasonsController : ControllerBase { private readonly EstusShotsContext _context; + private readonly IMapper _mapper; - public SeasonsController(EstusShotsContext context) + + public SeasonsController(EstusShotsContext context, IMapper mapper) { _context = context; - } - - [HttpGet("{id}")] - public async Task> GetSeason(Guid id) - { - var season = await _context.Seasons.FindAsync(id); - if (season == null) return NotFound(); - return season; + _mapper = mapper; } - [HttpPost] - public async Task> CreateSeason(Season season) + [HttpGet] + public async Task>> GetSeasons() { - _context.Seasons.Add(season); - await _context.SaveChangesAsync(); - return CreatedAtAction(nameof(GetSeason), new {id = season.SeasonId}, season); + var seasons = await _context.Seasons.ToListAsync(); + var dtos = _mapper.Map>(seasons); + return dtos; } } } \ No newline at end of file diff --git a/EstusShots.Server/EstusShots.Server.csproj b/EstusShots.Server/EstusShots.Server.csproj index 96f98dd..58e143c 100644 --- a/EstusShots.Server/EstusShots.Server.csproj +++ b/EstusShots.Server/EstusShots.Server.csproj @@ -14,6 +14,8 @@ + + diff --git a/EstusShots.Server/Mapping/Profiles.cs b/EstusShots.Server/Mapping/Profiles.cs new file mode 100644 index 0000000..8ccb991 --- /dev/null +++ b/EstusShots.Server/Mapping/Profiles.cs @@ -0,0 +1,14 @@ +using AutoMapper; +using EstusShots.Server.Models; + +namespace EstusShots.Server.Mapping +{ + public class Profiles : Profile + { + public Profiles() + { + CreateMap(); + CreateMap(); + } + } +} \ No newline at end of file diff --git a/EstusShots.Server/Models/Season.cs b/EstusShots.Server/Models/Season.cs new file mode 100644 index 0000000..08b449e --- /dev/null +++ b/EstusShots.Server/Models/Season.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace EstusShots.Server.Models +{ + public class Season + { + public Guid SeasonId { get; set; } + + public int Number { get; set; } + + [MaxLength(50)] public string Game { get; set; } = default!; + + public string? Description { get; set; } + + public DateTime Start { get; set; } + + public DateTime? End { get; set; } + } +} \ No newline at end of file diff --git a/EstusShots.Server/Services/EstusShotsContext.cs b/EstusShots.Server/Services/EstusShotsContext.cs index 636094f..7a4b617 100644 --- a/EstusShots.Server/Services/EstusShotsContext.cs +++ b/EstusShots.Server/Services/EstusShotsContext.cs @@ -1,4 +1,4 @@ -using EstusShots.Shared.Models; +using EstusShots.Server.Models; using Microsoft.EntityFrameworkCore; namespace EstusShots.Server.Services diff --git a/EstusShots.Server/Startup.cs b/EstusShots.Server/Startup.cs index 4d7c0f1..da66e13 100644 --- a/EstusShots.Server/Startup.cs +++ b/EstusShots.Server/Startup.cs @@ -1,3 +1,4 @@ +using AutoMapper; using EstusShots.Server.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -22,6 +23,7 @@ namespace EstusShots.Server { services.AddDbContext( opt => opt.UseInMemoryDatabase("debug.db")); + services.AddAutoMapper(typeof(Startup)); services.AddControllers(); } diff --git a/EstusShots.Shared/Models/Season.cs b/EstusShots.Shared/Models/Season.cs index 3a3dda7..a7dbcc0 100644 --- a/EstusShots.Shared/Models/Season.cs +++ b/EstusShots.Shared/Models/Season.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel.DataAnnotations; namespace EstusShots.Shared.Models { @@ -7,12 +6,16 @@ namespace EstusShots.Shared.Models { public Guid SeasonId { get; set; } - [MaxLength(50)] public string Game { get; set; } = default!; + public int Number { get; set; } + + public string Game { get; set; } = default!; public string? Description { get; set; } public DateTime Start { get; set; } public DateTime? End { get; set; } + + public string DisplayName => $"S{Number:D2} {Game}"; } } \ No newline at end of file diff --git a/EstusShots.sln b/EstusShots.sln index fd2ab21..dc753bb 100644 --- a/EstusShots.sln +++ b/EstusShots.sln @@ -6,6 +6,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EstusShots.Shared", "EstusS EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EstusShots.Gtk", "EstusShots.Gtk\EstusShots.Gtk.csproj", "{165F3B5C-9802-46C5-BCE2-F40C42819045}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EstusShots.Client", "EstusShots.Client\EstusShots.Client.csproj", "{9D2CCB23-6232-4587-AA95-24ADA96569E8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -24,5 +26,9 @@ Global {165F3B5C-9802-46C5-BCE2-F40C42819045}.Debug|Any CPU.Build.0 = Debug|Any CPU {165F3B5C-9802-46C5-BCE2-F40C42819045}.Release|Any CPU.ActiveCfg = Release|Any CPU {165F3B5C-9802-46C5-BCE2-F40C42819045}.Release|Any CPU.Build.0 = Release|Any CPU + {9D2CCB23-6232-4587-AA95-24ADA96569E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D2CCB23-6232-4587-AA95-24ADA96569E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D2CCB23-6232-4587-AA95-24ADA96569E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D2CCB23-6232-4587-AA95-24ADA96569E8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal