diff --git a/EstusShots.Gtk/Controls/BindableListControl.cs b/EstusShots.Gtk/Controls/BindableListControl.cs index 19e26a8..3c7004b 100644 --- a/EstusShots.Gtk/Controls/BindableListControl.cs +++ b/EstusShots.Gtk/Controls/BindableListControl.cs @@ -100,7 +100,7 @@ namespace EstusShots.Gtk.Controls } } - private void TreeViewOnRowActivated(object o, RowActivatedArgs args) + private void TreeViewOnRowActivated(object o, RowActivatedArgs args) { if (!(o is TreeView tree)) return; var selection = tree.Selection; @@ -128,19 +128,35 @@ namespace EstusShots.Gtk.Controls foreach (var dataColumn in Columns) { // Offset by one, because the first column in the data store is fixed to the key value of the row - var index = Columns.IndexOf(dataColumn) + 1; - var column = new TreeViewColumn( - dataColumn.Title, - new CellRendererText(), - "text", index) - { - Resizable = true, - Reorderable = true - }; - TreeView.AppendColumn(column); + var valueIndex = Columns.IndexOf(dataColumn) + 1; + var cell = GetRenderer(dataColumn); + var attr = GetAttribute(dataColumn); + dataColumn.PackStart(cell, true); + dataColumn.AddAttribute(cell, attr, valueIndex); + TreeView.AppendColumn(dataColumn); } } + private CellRenderer GetRenderer(DataColumn column) + { + var property = typeof(T).GetProperty(column.PropertyName); + return property?.PropertyType.Name switch + { + nameof(Boolean) => new CellRendererToggle(), + _ => new CellRendererText() + }; + } + + private string GetAttribute(DataColumn column) + { + var property = typeof(T).GetProperty(column.PropertyName); + return property?.PropertyType.Name switch + { + nameof(Boolean) => "active", + _ => "text" + }; + } + private void InitListStore() { var types = Columns diff --git a/EstusShots.Gtk/Controls/DataColumn.cs b/EstusShots.Gtk/Controls/DataColumn.cs index df2f6a7..135a8cf 100644 --- a/EstusShots.Gtk/Controls/DataColumn.cs +++ b/EstusShots.Gtk/Controls/DataColumn.cs @@ -1,16 +1,20 @@ using System; +using Gtk; namespace EstusShots.Gtk.Controls { - public class DataColumn + public class DataColumn : TreeViewColumn { public DataColumn() { + Resizable = true; + Reorderable = true; } public DataColumn(string propertyName) { PropertyName = propertyName; + Title = propertyName; } /// @@ -18,11 +22,6 @@ namespace EstusShots.Gtk.Controls /// public string PropertyName { get; } - /// - /// The column header. - /// - public string Title { get; set; } - /// /// Applies the given transformation on each item in the column. /// This changes only the display of the value. diff --git a/EstusShots.Gtk/Dialogs/PlayerEditor.cs b/EstusShots.Gtk/Dialogs/PlayerEditor.cs index f85c3a1..de69939 100644 --- a/EstusShots.Gtk/Dialogs/PlayerEditor.cs +++ b/EstusShots.Gtk/Dialogs/PlayerEditor.cs @@ -42,6 +42,11 @@ namespace EstusShots.Gtk.Dialogs _builder.Autoconnect(this); SavePlayerButton.Clicked += SavePlayerButtonOnClicked; + CancelPlayerEditorButton.Clicked += (sender, args) => + { + OnDialogClosed?.Invoke(this, new DialogClosedEventArgs(false, null)); + PlayerEditorDialog.Dispose(); + }; PlayerEditorDialog.TransientFor = parent; PlayerEditorDialog.Show(); diff --git a/EstusShots.Gtk/Pages/PlayersPage.cs b/EstusShots.Gtk/Pages/PlayersPage.cs index eff3b3a..2b2128d 100644 --- a/EstusShots.Gtk/Pages/PlayersPage.cs +++ b/EstusShots.Gtk/Pages/PlayersPage.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; +using EstusShots.Gtk.Controls; using EstusShots.Gtk.Dialogs; using EstusShots.Shared.Dto; using EstusShots.Shared.Models.Parameters; @@ -15,14 +17,34 @@ namespace EstusShots.Gtk [UI] public readonly TreeView PlayersTreeView = null; [UI] public readonly Button NewPlayerButton = null; [UI] public readonly Box PlayerEditorContainer = null; - + + private BindableListControl PlayersControl; private void InitPlayersPage() { NewPlayerButton.Clicked += NewPlayerButtonOnClicked; + + var columns = new List + { + new DataColumn(nameof(Player.Name)), + new DataColumn(nameof(Player.Alias)), + new DataColumn(nameof(Player.HexId)) {Title = "Hex ID"}, + new DataColumn(nameof(Player.Anonymous)) {Title = "Is Anonymous?", FixedWidth = 30} + }; + PlayersControl = new BindableListControl(columns, nameof(Player.PlayerId), PlayersTreeView); + PlayersControl.OnSelectionChanged += PlayersControlOnOnSelectionChanged; + + Task.Factory.StartNew(ReloadPlayers); } // Events + + private void PlayersControlOnOnSelectionChanged(object o, SelectionChangedEventArgs args) + { + if (!(args.Selection is Player player)) return; + var dialog = new PlayerEditor(this, player); + dialog.OnDialogClosed += PlayerEditorClosed; + } private void NewPlayerButtonOnClicked(object sender, EventArgs e) { @@ -33,7 +55,7 @@ namespace EstusShots.Gtk private async void PlayerEditorClosed(object o, DialogClosedEventArgs args) { if (!args.Ok || !(args.Model is Player player)) return; - var res = await Task.Factory.StartNew(() + var res = await Task.Factory.StartNew(() => Client.Players.SavePlayer(new SavePlayerParameter(player)).Result); if (!res.OperationResult.Success) { @@ -42,12 +64,12 @@ namespace EstusShots.Gtk return; } - // ReloadPlayers(); + await ReloadPlayers(); } - + // Private Methods - private async void ReloadPlayers() + private async Task ReloadPlayers() { var res = await Task.Factory.StartNew(() => Client.Players.GetPlayers(new GetPlayersParameter()).Result); @@ -58,10 +80,9 @@ namespace EstusShots.Gtk return; } - // TODO - // SeasonsControl.Items = res.Data.Seasons; - // SeasonsControl.DataBind(); - // Info("Player list refreshed"); + PlayersControl.Items = res.Data.Players; + PlayersControl.DataBind(); + Info("Player list refreshed"); } } } \ No newline at end of file diff --git a/EstusShots.Server/Mapping/Profiles.cs b/EstusShots.Server/Mapping/Profiles.cs index 0b9efc2..a9bf2bc 100644 --- a/EstusShots.Server/Mapping/Profiles.cs +++ b/EstusShots.Server/Mapping/Profiles.cs @@ -12,6 +12,9 @@ namespace EstusShots.Server.Mapping CreateMap(); CreateMap(); + + CreateMap(); + CreateMap(); } } } \ No newline at end of file diff --git a/EstusShots.Server/Services/PlayersService.cs b/EstusShots.Server/Services/PlayersService.cs index 2fbf684..aeed627 100644 --- a/EstusShots.Server/Services/PlayersService.cs +++ b/EstusShots.Server/Services/PlayersService.cs @@ -1,10 +1,13 @@ +using System.Collections.Generic; using System.Threading.Tasks; using AutoMapper; using EstusShots.Server.Models; using EstusShots.Shared.Interfaces; using EstusShots.Shared.Models; using EstusShots.Shared.Models.Parameters; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using Dto = EstusShots.Shared.Dto; namespace EstusShots.Server.Services { @@ -23,26 +26,42 @@ namespace EstusShots.Server.Services public async Task> GetPlayers(GetPlayersParameter parameter) { - throw new System.NotImplementedException(); + var players = await _context.Players.ToListAsync(); + var dtos = _mapper.Map>(players); + return new ApiResponse(new GetPlayersResponse(dtos)); } public async Task> GetPlayerDetails(GetPlayerDetailsParameter parameter) { - throw new System.NotImplementedException(); + var player = await _context.Players.FindAsync(parameter.PlayerId); + if (player == null) + { + _logger.LogWarning($"Player '{parameter.PlayerId}' not found in database"); + return new ApiResponse(new OperationResult(false, "Player not found")); + } + + var dto = _mapper.Map(player); + return new ApiResponse(new GetPlayerDetailsResponse(dto)); } public async Task> SavePlayer(SavePlayerParameter parameter) { - var player = _mapper.Map(parameter.Player); - if (player.PlayerId.IsEmpty()) + if (parameter.Player.PlayerId.IsEmpty()) { - _context.Players.Add(player); + _context.Players.Add(_mapper.Map(parameter.Player)); var count = await _context.SaveChangesAsync(); _logger.LogInformation($"Created {count} rows"); + return new ApiResponse(new SavePlayerResponse(parameter.Player.PlayerId)); + } + else + { + var player = await _context.Players.FindAsync(parameter.Player.PlayerId); + _context.Players.Update(player); + _mapper.Map(parameter.Player, player); + var count = await _context.SaveChangesAsync(); + _logger.LogInformation($"Updated player '{player.PlayerId}'"); return new ApiResponse(new SavePlayerResponse(player.PlayerId)); } - // TODO Update Player - return new ApiResponse(new OperationResult(false, "NotImplemented")); } public async Task> DeletePlayers(DeletePlayerParameter parameter)