Nice Exception Dialog
This commit is contained in:
@@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="EstusShotsClient"/>
|
||||
@@ -34,6 +36,7 @@ namespace EstusShots.Client
|
||||
|
||||
Seasons = new Seasons(this);
|
||||
Episodes = new Episodes(this);
|
||||
Players = new Players(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
40
EstusShots.Gtk/Dialogs/ErrorDialog.cs
Normal file
40
EstusShots.Gtk/Dialogs/ErrorDialog.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -19,6 +25,8 @@ namespace EstusShots.Gtk
|
||||
[UI] public readonly Box LoadingSpinner = null;
|
||||
[UI] public readonly Notebook Navigation = null;
|
||||
|
||||
|
||||
|
||||
private EstusShotsClient Client { get; }
|
||||
private BindableListControl<Episode> 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();
|
||||
|
||||
@@ -439,4 +439,158 @@
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkDialog" id="ShowErrorDialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">An Error Occured</property>
|
||||
<property name="resizable">False</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="window_position">center-on-parent</property>
|
||||
<property name="default_width">500</property>
|
||||
<property name="default_height">350</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="ErrorDialogCloseButton">
|
||||
<property name="label">gtk-close</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-no</property>
|
||||
<property name="icon_size">6</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Error:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="ErrorShortTextLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Unknown Error</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="wrap_mode">char</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="ErrorDetailTextLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="wrap_mode">char</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="StackTraceTextView">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="cursor_visible">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
|
||||
@@ -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;
|
||||
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
|
||||
// Client.Players.SavePlayer();
|
||||
// SeasonsControl.Items = res.Data.Seasons;
|
||||
// SeasonsControl.DataBind();
|
||||
// Info("Player list refreshed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0" />
|
||||
<PackageReference Include="Z.ExtensionMethods" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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<PlayersService> logger)
|
||||
public PlayersService(ILogger<PlayersService> logger, EstusShotsContext context, IMapper mapper)
|
||||
{
|
||||
_logger = logger;
|
||||
_context = context;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public Task<ApiResponse<GetPlayersResponse>> GetPlayers(GetPlayersParameter parameter)
|
||||
public async Task<ApiResponse<GetPlayersResponse>> GetPlayers(GetPlayersParameter parameter)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<ApiResponse<GetPlayerDetailsResponse>> GetPlayerDetails(GetPlayerDetailsParameter parameter)
|
||||
public async Task<ApiResponse<GetPlayerDetailsResponse>> GetPlayerDetails(GetPlayerDetailsParameter parameter)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<ApiResponse<SavePlayerResponse>> SavePlayer(SavePlayerParameter parameter)
|
||||
public async Task<ApiResponse<SavePlayerResponse>> SavePlayer(SavePlayerParameter parameter)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
var player = _mapper.Map<Player>(parameter.Player);
|
||||
if (player.PlayerId.IsEmpty())
|
||||
{
|
||||
_context.Players.Add(player);
|
||||
var count = await _context.SaveChangesAsync();
|
||||
_logger.LogInformation($"Created {count} rows");
|
||||
return new ApiResponse<SavePlayerResponse>(new SavePlayerResponse(player.PlayerId));
|
||||
}
|
||||
// TODO Update Player
|
||||
return new ApiResponse<SavePlayerResponse>(new OperationResult(false, "NotImplemented"));
|
||||
}
|
||||
|
||||
public Task<ApiResponse<DeletePlayerResponse>> DeletePlayers(DeletePlayerParameter parameter)
|
||||
public async Task<ApiResponse<DeletePlayerResponse>> DeletePlayers(DeletePlayerParameter parameter)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace EstusShots.Server
|
||||
// Register business logic services
|
||||
services.AddScoped<SeasonsService>();
|
||||
services.AddScoped<EpisodesService>();
|
||||
services.AddScoped<PlayersService>();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
||||
Reference in New Issue
Block a user