From 8953ac18082a9d3794a9dc42f731072e7c3bb08d Mon Sep 17 00:00:00 2001 From: luxick Date: Sun, 1 Mar 2020 23:37:43 +0100 Subject: [PATCH] Nice Exception Dialog --- EstusShots.Client/EstusShotsClient.cs | 3 + EstusShots.Gtk/Dialogs/ErrorDialog.cs | 40 +++++ EstusShots.Gtk/Dialogs/PlayerEditor.cs | 3 +- EstusShots.Gtk/MainWindow.cs | 16 ++ EstusShots.Gtk/MainWindow.glade | 154 +++++++++++++++++++ EstusShots.Gtk/Pages/PlayersPage.cs | 35 ++++- EstusShots.Gtk/Pages/SeasonsPage.cs | 3 + EstusShots.Server/EstusShots.Server.csproj | 1 + EstusShots.Server/Services/PlayersService.cs | 27 +++- EstusShots.Server/Startup.cs | 1 + 10 files changed, 273 insertions(+), 10 deletions(-) create mode 100644 EstusShots.Gtk/Dialogs/ErrorDialog.cs diff --git a/EstusShots.Client/EstusShotsClient.cs b/EstusShots.Client/EstusShotsClient.cs index 4a126fe..c19dfa2 100644 --- a/EstusShots.Client/EstusShotsClient.cs +++ b/EstusShots.Client/EstusShotsClient.cs @@ -4,6 +4,7 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; using EstusShots.Client.Routes; +using EstusShots.Shared.Dto; using EstusShots.Shared.Interfaces; using EstusShots.Shared.Models; @@ -22,6 +23,7 @@ namespace EstusShots.Client // API Routes public Seasons Seasons { get; } public Episodes Episodes { get; } + public Players Players { get; } /// /// Creates a new instance of @@ -34,6 +36,7 @@ namespace EstusShots.Client Seasons = new Seasons(this); Episodes = new Episodes(this); + Players = new Players(this); } /// diff --git a/EstusShots.Gtk/Dialogs/ErrorDialog.cs b/EstusShots.Gtk/Dialogs/ErrorDialog.cs new file mode 100644 index 0000000..f11e456 --- /dev/null +++ b/EstusShots.Gtk/Dialogs/ErrorDialog.cs @@ -0,0 +1,40 @@ +using EstusShots.Shared.Models; +using Gtk; +using UI = Gtk.Builder.ObjectAttribute; + +namespace EstusShots.Gtk.Dialogs +{ + public class ErrorHandler + { + [UI] private readonly Dialog ShowErrorDialog = null; + [UI] private readonly Label ErrorShortTextLabel = null; + [UI] private readonly Label ErrorDetailTextLabel = null; + [UI] private readonly TextView StackTraceTextView = null; + [UI] private readonly Button ErrorDialogCloseButton = null; + + public void ShowFor(Window window, OperationResult error) + { + ShowErrorDialog.TransientFor = window; + ErrorDialogCloseButton.Clicked += (sender, args) => { ShowErrorDialog.Dispose();}; + ErrorShortTextLabel.Text = error.ShortMessage; + ErrorDetailTextLabel.Visible = error.DetailedMessage != null; + ErrorDetailTextLabel.Text = error.DetailedMessage; + StackTraceTextView.Visible = error.StackTrace != null; + var buff = new TextBuffer(new TextTagTable()) {Text = error.StackTrace}; + StackTraceTextView.Buffer = buff; + ShowErrorDialog.Show(); + } + } + + public static class ErrorDialog + { + public static Window MainWindow; + public static void Show(OperationResult error) + { + var handler = new ErrorHandler(); + var builder = new Builder("MainWindow.glade"); + builder.Autoconnect(handler); + handler.ShowFor(MainWindow, error); + } + } +} \ No newline at end of file diff --git a/EstusShots.Gtk/Dialogs/PlayerEditor.cs b/EstusShots.Gtk/Dialogs/PlayerEditor.cs index 8eada13..f85c3a1 100644 --- a/EstusShots.Gtk/Dialogs/PlayerEditor.cs +++ b/EstusShots.Gtk/Dialogs/PlayerEditor.cs @@ -43,8 +43,8 @@ namespace EstusShots.Gtk.Dialogs SavePlayerButton.Clicked += SavePlayerButtonOnClicked; - PlayerEditorDialog.Parent = parent; PlayerEditorDialog.TransientFor = parent; + PlayerEditorDialog.Show(); ReadFromModel(); } @@ -55,6 +55,7 @@ namespace EstusShots.Gtk.Dialogs { ReadToModel(); OnDialogClosed?.Invoke(this, new DialogClosedEventArgs(true, _player)); + PlayerEditorDialog.Dispose(); } // Private Methods diff --git a/EstusShots.Gtk/MainWindow.cs b/EstusShots.Gtk/MainWindow.cs index ccc0bd7..32f73f3 100644 --- a/EstusShots.Gtk/MainWindow.cs +++ b/EstusShots.Gtk/MainWindow.cs @@ -1,8 +1,14 @@ +using System; +using System.Threading.Tasks; using EstusShots.Client; using EstusShots.Gtk.Controls; +using EstusShots.Gtk.Dialogs; using EstusShots.Shared.Dto; +using EstusShots.Shared.Models; using Gdk; +using GLib; using Gtk; +using Application = Gtk.Application; using UI = Gtk.Builder.ObjectAttribute; using Window = Gtk.Window; @@ -18,6 +24,8 @@ namespace EstusShots.Gtk [UI] public readonly Label InfoLabel = null; [UI] public readonly Box LoadingSpinner = null; [UI] public readonly Notebook Navigation = null; + + private EstusShotsClient Client { get; } private BindableListControl EpisodesControl { get; set; } @@ -31,6 +39,8 @@ namespace EstusShots.Gtk builder.Autoconnect(this); Client = new EstusShotsClient(ApiUrl); + ErrorDialog.MainWindow = this; + ExceptionManager.UnhandledException += ExceptionManagerOnUnhandledException; DeleteEvent += Window_DeleteEvent; Icon = Pixbuf.LoadFromResource("icon.png"); @@ -49,6 +59,12 @@ namespace EstusShots.Gtk UpdateTitle(); } + private void ExceptionManagerOnUnhandledException(UnhandledExceptionArgs args) + { + Console.WriteLine(args.ExceptionObject); + args.ExitApplication = false; + } + private void Window_DeleteEvent(object sender, DeleteEventArgs a) { Application.Quit(); diff --git a/EstusShots.Gtk/MainWindow.glade b/EstusShots.Gtk/MainWindow.glade index 11f9c94..0cd542b 100644 --- a/EstusShots.Gtk/MainWindow.glade +++ b/EstusShots.Gtk/MainWindow.glade @@ -439,4 +439,158 @@ + + False + An Error Occured + False + True + center-on-parent + 500 + 350 + True + dialog + + + + + + False + 5 + 5 + 5 + 5 + vertical + 2 + + + False + end + + + + + + gtk-close + True + True + True + True + + + True + True + 1 + + + + + False + False + 0 + + + + + True + False + vertical + 5 + + + True + False + 5 + + + True + False + gtk-no + 6 + + + False + True + 0 + + + + + True + False + end + Error: + + + False + True + 1 + + + + + True + False + Unknown Error + True + char + + + False + True + 2 + + + + + False + True + 0 + + + + + True + False + start + 10 + 10 + True + char + + + False + True + 1 + + + + + True + True + in + + + True + True + False + False + + + + + True + True + 2 + + + + + True + True + 1 + + + + + diff --git a/EstusShots.Gtk/Pages/PlayersPage.cs b/EstusShots.Gtk/Pages/PlayersPage.cs index 6e9334b..eff3b3a 100644 --- a/EstusShots.Gtk/Pages/PlayersPage.cs +++ b/EstusShots.Gtk/Pages/PlayersPage.cs @@ -1,6 +1,8 @@ using System; +using System.Threading.Tasks; using EstusShots.Gtk.Dialogs; using EstusShots.Shared.Dto; +using EstusShots.Shared.Models.Parameters; using Gtk; using UI = Gtk.Builder.ObjectAttribute; @@ -28,11 +30,38 @@ namespace EstusShots.Gtk dialog.OnDialogClosed += PlayerEditorClosed; } - private void PlayerEditorClosed(object o, DialogClosedEventArgs args) + private async void PlayerEditorClosed(object o, DialogClosedEventArgs args) { if (!args.Ok || !(args.Model is Player player)) return; - // TODO - // Client.Players.SavePlayer(); + var res = await Task.Factory.StartNew(() + => Client.Players.SavePlayer(new SavePlayerParameter(player)).Result); + if (!res.OperationResult.Success) + { + Info($"Unable to save: {res.OperationResult.ShortMessage}"); + ErrorDialog.Show(res.OperationResult); + return; + } + + // ReloadPlayers(); + } + + // Private Methods + + private async void ReloadPlayers() + { + var res = await Task.Factory.StartNew(() + => Client.Players.GetPlayers(new GetPlayersParameter()).Result); + if (!res.OperationResult.Success) + { + InfoLabel.Text = $"Refresh failed: {res.OperationResult.ShortMessage}"; + ErrorDialog.Show(res.OperationResult); + return; + } + + // TODO + // SeasonsControl.Items = res.Data.Seasons; + // SeasonsControl.DataBind(); + // Info("Player list refreshed"); } } } \ No newline at end of file diff --git a/EstusShots.Gtk/Pages/SeasonsPage.cs b/EstusShots.Gtk/Pages/SeasonsPage.cs index 9549381..82d5e4d 100644 --- a/EstusShots.Gtk/Pages/SeasonsPage.cs +++ b/EstusShots.Gtk/Pages/SeasonsPage.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using EstusShots.Gtk.Controls; +using EstusShots.Gtk.Dialogs; using EstusShots.Shared.Dto; using EstusShots.Shared.Models.Parameters; using Gtk; @@ -55,6 +56,7 @@ namespace EstusShots.Gtk if (!res.OperationResult.Success) { InfoLabel.Text = $"Error while creating Season: {res.OperationResult.ShortMessage}"; + ErrorDialog.Show(res.OperationResult); return; } @@ -88,6 +90,7 @@ namespace EstusShots.Gtk if (!res.OperationResult.Success) { InfoLabel.Text = $"Refresh Failed: {res.OperationResult.ShortMessage}"; + ErrorDialog.Show(res.OperationResult); return; } diff --git a/EstusShots.Server/EstusShots.Server.csproj b/EstusShots.Server/EstusShots.Server.csproj index f793bf3..93597be 100644 --- a/EstusShots.Server/EstusShots.Server.csproj +++ b/EstusShots.Server/EstusShots.Server.csproj @@ -20,6 +20,7 @@ + diff --git a/EstusShots.Server/Services/PlayersService.cs b/EstusShots.Server/Services/PlayersService.cs index 8f813f0..2fbf684 100644 --- a/EstusShots.Server/Services/PlayersService.cs +++ b/EstusShots.Server/Services/PlayersService.cs @@ -1,4 +1,6 @@ using System.Threading.Tasks; +using AutoMapper; +using EstusShots.Server.Models; using EstusShots.Shared.Interfaces; using EstusShots.Shared.Models; using EstusShots.Shared.Models.Parameters; @@ -9,28 +11,41 @@ namespace EstusShots.Server.Services public class PlayersService : IPlayersController { private readonly ILogger _logger; + private readonly EstusShotsContext _context; + private readonly IMapper _mapper; - public PlayersService(ILogger logger) + public PlayersService(ILogger logger, EstusShotsContext context, IMapper mapper) { _logger = logger; + _context = context; + _mapper = mapper; } - public Task> GetPlayers(GetPlayersParameter parameter) + public async Task> GetPlayers(GetPlayersParameter parameter) { throw new System.NotImplementedException(); } - public Task> GetPlayerDetails(GetPlayerDetailsParameter parameter) + public async Task> GetPlayerDetails(GetPlayerDetailsParameter parameter) { throw new System.NotImplementedException(); } - public Task> SavePlayer(SavePlayerParameter parameter) + public async Task> SavePlayer(SavePlayerParameter parameter) { - throw new System.NotImplementedException(); + var player = _mapper.Map(parameter.Player); + if (player.PlayerId.IsEmpty()) + { + _context.Players.Add(player); + var count = await _context.SaveChangesAsync(); + _logger.LogInformation($"Created {count} rows"); + return new ApiResponse(new SavePlayerResponse(player.PlayerId)); + } + // TODO Update Player + return new ApiResponse(new OperationResult(false, "NotImplemented")); } - public Task> DeletePlayers(DeletePlayerParameter parameter) + public async Task> DeletePlayers(DeletePlayerParameter parameter) { throw new System.NotImplementedException(); } diff --git a/EstusShots.Server/Startup.cs b/EstusShots.Server/Startup.cs index 4c7bb55..a15fd2f 100644 --- a/EstusShots.Server/Startup.cs +++ b/EstusShots.Server/Startup.cs @@ -47,6 +47,7 @@ namespace EstusShots.Server // Register business logic services services.AddScoped(); services.AddScoped(); + services.AddScoped(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.