Improved client handling and episode models
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using EstusShots.Shared.Dto;
|
||||||
using EstusShots.Shared.Models;
|
using EstusShots.Shared.Models;
|
||||||
|
|
||||||
namespace EstusShots.Client
|
namespace EstusShots.Client
|
||||||
@@ -23,12 +25,39 @@ namespace EstusShots.Client
|
|||||||
HttpClient = new HttpClient {Timeout = TimeSpan.FromSeconds(10)};
|
HttpClient = new HttpClient {Timeout = TimeSpan.FromSeconds(10)};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<Season>> GetSeasons()
|
public async Task<(OperationResult, List<Season>)> GetSeasons()
|
||||||
{
|
{
|
||||||
var response = HttpClient.GetAsync(ApiUrl + "seasons").Result;
|
try
|
||||||
|
{
|
||||||
|
var response = await HttpClient.GetAsync(ApiUrl + "seasons");
|
||||||
var jsonData = await response.Content.ReadAsStringAsync();
|
var jsonData = await response.Content.ReadAsStringAsync();
|
||||||
var data = JsonSerializer.Deserialize<List<Season>>(jsonData, _serializerOptions);
|
var data = JsonSerializer.Deserialize<List<Season>>(jsonData, _serializerOptions);
|
||||||
return data;
|
return (new OperationResult(), data);
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return (new OperationResult(e), new List<Season>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(OperationResult, Guid)> CreateSeason(Season season)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var content = new StringContent(JsonSerializer.Serialize(season), Encoding.UTF8, "application/json");
|
||||||
|
var response = await HttpClient.PostAsync(ApiUrl + "season", content);
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return (new OperationResult(false, response.ReasonPhrase), Guid.Empty);
|
||||||
|
}
|
||||||
|
// TODO should give the created id
|
||||||
|
return (new OperationResult(), Guid.Empty);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return (new OperationResult(e), Guid.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
168
EstusShots.Gtk/Controls/BindableListControl.cs
Normal file
168
EstusShots.Gtk/Controls/BindableListControl.cs
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using GLib;
|
||||||
|
using Gtk;
|
||||||
|
using DateTime = GLib.DateTime;
|
||||||
|
|
||||||
|
namespace EstusShots.Gtk.Controls
|
||||||
|
{
|
||||||
|
public class SelectionChangedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public SelectionChangedEventArgs(object selection)
|
||||||
|
{
|
||||||
|
Selection = selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Selection { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void SelectionChangedEventHandler(object o, SelectionChangedEventArgs args);
|
||||||
|
|
||||||
|
public class BindableListControl<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize a new BindableListView with an existing TreeView Widget
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="columns">The columns of the grid</param>
|
||||||
|
/// <param name="keyField">Unique key field in the item sources type</param>
|
||||||
|
/// <param name="treeView">An instance of an existing TreeView Widget</param>
|
||||||
|
public BindableListControl(List<DataColumn> columns, string keyField, TreeView treeView = null)
|
||||||
|
{
|
||||||
|
TreeView = treeView ?? new TreeView();
|
||||||
|
Columns = columns;
|
||||||
|
KeyField = keyField;
|
||||||
|
InitTreeViewColumns();
|
||||||
|
InitListStore();
|
||||||
|
TreeView.Model = ListStore;
|
||||||
|
Items = new List<T>();
|
||||||
|
|
||||||
|
TreeView.Selection.Changed += TreeView_SelectionChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> The GTK ListStore that is managed by this <see cref="BindableListControl{T}" />. </summary>
|
||||||
|
public ListStore ListStore { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary> The GTK TreeView control that is managed by this <see cref="BindableListControl{T}" />. </summary>
|
||||||
|
public TreeView TreeView { get; }
|
||||||
|
|
||||||
|
/// <summary> Property of the element type that is used as a unique identifier for accessing elements. </summary>
|
||||||
|
public string KeyField { get; }
|
||||||
|
|
||||||
|
/// <summary> The collection of all elements, that should be shown in the list view. </summary>
|
||||||
|
public List<T> Items { get; set; }
|
||||||
|
|
||||||
|
/// <summary> The currently selected item in the view. </summary>
|
||||||
|
public T SelectedItem { get; set; }
|
||||||
|
|
||||||
|
/// <summary> All columns that are displayed in the list. </summary>
|
||||||
|
public List<DataColumn> Columns { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event will be invoked when the selected item in the <see cref="TreeView" /> has changed.
|
||||||
|
/// </summary>
|
||||||
|
public event SelectionChangedEventHandler OnSelectionChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set elements from the <see cref="Items" /> property in the <see cref="ListStore" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="TypeLoadException"></exception>
|
||||||
|
public void DataBind()
|
||||||
|
{
|
||||||
|
ListStore.Clear();
|
||||||
|
Items.ForEach(BindItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BindItem(T item)
|
||||||
|
{
|
||||||
|
var row = new List<object>();
|
||||||
|
foreach (var column in Columns)
|
||||||
|
{
|
||||||
|
var prop = item.GetType().GetProperty(column.PropertyName);
|
||||||
|
if (prop == null)
|
||||||
|
throw new TypeLoadException(
|
||||||
|
$"Property '{column.PropertyName}' does not exist on Type '{item.GetType()}'");
|
||||||
|
var val = prop.GetValue(item);
|
||||||
|
if (column.Format != null) val = column.Format(val);
|
||||||
|
row.Add(val.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The key value must be the first value in the row
|
||||||
|
var key = item.GetType().GetProperty(KeyField)?.GetValue(item);
|
||||||
|
row.Insert(0, key);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ListStore.AppendValues(row.ToArray());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TreeView_SelectionChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (!(sender is TreeSelection selection)) return;
|
||||||
|
selection.GetSelected(out var model, out var iter);
|
||||||
|
var key = model.GetValue(iter, 0);
|
||||||
|
var item = Items.FirstOrDefault(x =>
|
||||||
|
{
|
||||||
|
var prop = x.GetType().GetProperty(KeyField);
|
||||||
|
var value = prop?.GetValue(x);
|
||||||
|
return value != null && value.Equals(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"No item for key '{key}' found in data store");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedItem = item;
|
||||||
|
OnSelectionChanged?.Invoke(this, new SelectionChangedEventArgs(SelectedItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitTreeViewColumns()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitListStore()
|
||||||
|
{
|
||||||
|
var types = Columns
|
||||||
|
.Select(x =>
|
||||||
|
{
|
||||||
|
var propType = typeof(T).GetProperty(x.PropertyName)?.PropertyType;
|
||||||
|
var gType = (GType) propType;
|
||||||
|
if (gType.ToString() == "GtkSharpValue") gType = MapType(propType);
|
||||||
|
return gType;
|
||||||
|
});
|
||||||
|
var data = new DateTime();
|
||||||
|
// The first column in the data store is always the key field.
|
||||||
|
var columns = new List<GType> {(GType) typeof(T).GetProperty(KeyField)?.PropertyType};
|
||||||
|
columns.AddRange(types);
|
||||||
|
ListStore = new ListStore(columns.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GType MapType(Type type)
|
||||||
|
{
|
||||||
|
return type.Name switch
|
||||||
|
{
|
||||||
|
_ => GType.String
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Gtk;
|
|
||||||
|
|
||||||
namespace EstusShots.Gtk.Controls
|
|
||||||
{
|
|
||||||
public class SelectionChangedEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public object Selection { get; }
|
|
||||||
|
|
||||||
public SelectionChangedEventArgs(object selection)
|
|
||||||
{
|
|
||||||
Selection = selection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public delegate void SelectionChangedEventHandler(object o, SelectionChangedEventArgs args);
|
|
||||||
|
|
||||||
public class BindableListView<T>
|
|
||||||
{
|
|
||||||
public ListStore ListStore { get; internal set; }
|
|
||||||
|
|
||||||
public TreeView TreeView { get; }
|
|
||||||
|
|
||||||
public string KeyField { get; }
|
|
||||||
|
|
||||||
public IEnumerable<T> Items { get; set; }
|
|
||||||
|
|
||||||
public T SelectedItem { get; set; }
|
|
||||||
|
|
||||||
public List<DataColumn> Columns { get; }
|
|
||||||
|
|
||||||
public event SelectionChangedEventHandler OnSelectionChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize a new BindableListView with an existing TreeView Widget
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="columns">The columns of the grid</param>
|
|
||||||
/// <param name="keyField">Unique key field in the item sources type</param>
|
|
||||||
/// <param name="treeView">An instance of an existing TreeView Widget</param>
|
|
||||||
public BindableListView(List<DataColumn> columns, string keyField, TreeView treeView = null)
|
|
||||||
{
|
|
||||||
TreeView = treeView ?? new TreeView();
|
|
||||||
Columns = columns;
|
|
||||||
KeyField = keyField;
|
|
||||||
InitTreeViewColumns();
|
|
||||||
InitListStore();
|
|
||||||
TreeView.Model = ListStore;
|
|
||||||
Items = new List<T>();
|
|
||||||
|
|
||||||
TreeView.Selection.Changed += TreeView_SelectionChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TreeView_SelectionChanged(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
if (!(sender is TreeSelection selection)) return;
|
|
||||||
selection.GetSelected(out var model, out var iter);
|
|
||||||
var key = model.GetValue(iter, 0);
|
|
||||||
var item = Items.FirstOrDefault(x =>
|
|
||||||
{
|
|
||||||
var prop = x.GetType().GetProperty(KeyField);
|
|
||||||
var value = prop?.GetValue(x);
|
|
||||||
return value != null && value.Equals(key);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (item == null)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"No item for key '{key}' found in data store");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectedItem = item;
|
|
||||||
OnSelectionChanged?.Invoke(this, new SelectionChangedEventArgs(SelectedItem));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DataBind()
|
|
||||||
{
|
|
||||||
ListStore.Clear();
|
|
||||||
foreach (var item in Items)
|
|
||||||
{
|
|
||||||
var row = new List<object>();
|
|
||||||
foreach (var column in Columns)
|
|
||||||
{
|
|
||||||
var prop = item.GetType().GetProperty(column.PropertyName);
|
|
||||||
|
|
||||||
if (prop == null)
|
|
||||||
throw new TypeLoadException(
|
|
||||||
$"Property '{column.PropertyName}' does not exist on Type '{item.GetType()}'");
|
|
||||||
|
|
||||||
row.Add(prop.GetValue(item));
|
|
||||||
}
|
|
||||||
|
|
||||||
// The key value must be the first value in the row
|
|
||||||
var key = item.GetType().GetProperty(KeyField)?.GetValue(item);
|
|
||||||
row.Insert(0, key);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ListStore.AppendValues(row.ToArray());
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Console.WriteLine(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitTreeViewColumns()
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
TreeView.AppendColumn(column);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitListStore()
|
|
||||||
{
|
|
||||||
var types = Columns
|
|
||||||
.Select(x => typeof(T).GetProperty(x.PropertyName)?.PropertyType);
|
|
||||||
|
|
||||||
// The first column in the data store is always the key field.
|
|
||||||
var columns = new List<Type> {typeof(T).GetProperty(KeyField)?.PropertyType};
|
|
||||||
columns.AddRange(types);
|
|
||||||
|
|
||||||
ListStore = new ListStore(columns.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
namespace EstusShots.Gtk.Controls
|
namespace EstusShots.Gtk.Controls
|
||||||
{
|
{
|
||||||
public class DataColumn
|
public class DataColumn
|
||||||
{
|
{
|
||||||
public string PropertyName { get; set; }
|
public DataColumn()
|
||||||
|
{
|
||||||
public string Title { get; set; }
|
}
|
||||||
|
|
||||||
public DataColumn() { }
|
|
||||||
|
|
||||||
public DataColumn(string propertyName)
|
public DataColumn(string propertyName)
|
||||||
{
|
{
|
||||||
PropertyName = propertyName;
|
PropertyName = propertyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the property in the data source, that should be show nin the view
|
||||||
|
/// </summary>
|
||||||
|
public string PropertyName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The column header.
|
||||||
|
/// </summary>
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies the given transformation on each item in the column.
|
||||||
|
/// This changes only the display of the value.
|
||||||
|
/// </summary>
|
||||||
|
public Func<object, string> Format { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
26
EstusShots.Gtk/Controls/LoadingMode.cs
Normal file
26
EstusShots.Gtk/Controls/LoadingMode.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace EstusShots.Gtk.Controls
|
||||||
|
{
|
||||||
|
internal class LoadingMode : IDisposable
|
||||||
|
{
|
||||||
|
private MainWindow Window { get; set; }
|
||||||
|
|
||||||
|
public LoadingMode(MainWindow window)
|
||||||
|
{
|
||||||
|
Window = window;
|
||||||
|
Window.LoadButton.Sensitive = false;
|
||||||
|
Window.NewSeasonButton.Sensitive = false;
|
||||||
|
Window.SeasonsView.Sensitive = false;
|
||||||
|
Window.SeasonsOverlay.AddOverlay(Window.LoadingSpinner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Window.LoadButton.Sensitive = true;
|
||||||
|
Window.NewSeasonButton.Sensitive = true;
|
||||||
|
Window.SeasonsView.Sensitive = true;
|
||||||
|
Window.SeasonsOverlay.Remove(Window.LoadingSpinner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,8 +21,4 @@
|
|||||||
<ProjectReference Include="..\EstusShots.Shared\EstusShots.Shared.csproj" />
|
<ProjectReference Include="..\EstusShots.Shared\EstusShots.Shared.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="DataStores" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,35 +1,28 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Threading.Tasks;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
|
||||||
using EstusShots.Client;
|
using EstusShots.Client;
|
||||||
using EstusShots.Gtk.Controls;
|
using EstusShots.Gtk.Controls;
|
||||||
using EstusShots.Shared.Models;
|
using EstusShots.Shared.Dto;
|
||||||
using Gtk;
|
using Gtk;
|
||||||
using Application = Gtk.Application;
|
|
||||||
using DateTime = System.DateTime;
|
|
||||||
using Task = System.Threading.Tasks.Task;
|
|
||||||
using UI = Gtk.Builder.ObjectAttribute;
|
using UI = Gtk.Builder.ObjectAttribute;
|
||||||
|
|
||||||
namespace EstusShots.Gtk
|
namespace EstusShots.Gtk
|
||||||
{
|
{
|
||||||
class MainWindow : Window
|
internal class MainWindow : Window
|
||||||
{
|
{
|
||||||
private const string ApiUrl = "http://localhost:5000/api/";
|
private const string ApiUrl = "http://localhost:5000/api/";
|
||||||
|
|
||||||
private EstusShotsClient Client { get; }
|
|
||||||
private BindableListView<Season> SeasonsView { get; }
|
|
||||||
|
|
||||||
[UI] private readonly TreeView _seasonsView = null;
|
|
||||||
[UI] private readonly Button _loadButton = null;
|
|
||||||
[UI] private readonly Button _newSeasonButton = null;
|
|
||||||
[UI] private readonly Label _infoLabel = null;
|
[UI] private readonly Label _infoLabel = null;
|
||||||
[UI] private readonly Overlay _seasonsOverlay = null;
|
[UI] public readonly Button LoadButton = null;
|
||||||
[UI] private readonly Box _loadingSpinner = null;
|
[UI] public readonly Box LoadingSpinner = null;
|
||||||
|
[UI] public readonly Button NewSeasonButton = null;
|
||||||
|
[UI] public readonly Overlay SeasonsOverlay = null;
|
||||||
|
[UI] public readonly TreeView SeasonsView = null;
|
||||||
|
|
||||||
public MainWindow() : this(new Builder("MainWindow.glade")) { }
|
public MainWindow() : this(new Builder("MainWindow.glade"))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private MainWindow(Builder builder) : base(builder.GetObject("MainWindow").Handle)
|
private MainWindow(Builder builder) : base(builder.GetObject("MainWindow").Handle)
|
||||||
{
|
{
|
||||||
@@ -37,17 +30,29 @@ namespace EstusShots.Gtk
|
|||||||
Client = new EstusShotsClient("http://localhost:5000/api/");
|
Client = new EstusShotsClient("http://localhost:5000/api/");
|
||||||
|
|
||||||
DeleteEvent += Window_DeleteEvent;
|
DeleteEvent += Window_DeleteEvent;
|
||||||
_loadButton.Clicked += LoadButtonClicked;
|
LoadButton.Clicked += LoadButtonClicked;
|
||||||
_newSeasonButton.Clicked += NewSeasonButtonOnClicked;
|
NewSeasonButton.Clicked += NewSeasonButtonOnClicked;
|
||||||
|
|
||||||
var seasonsColumns = new List<DataColumn>
|
var seasonsColumns = new List<DataColumn>
|
||||||
{
|
{
|
||||||
new DataColumn(nameof(Season.DisplayName)){Title = "Name"}
|
new DataColumn(nameof(Season.DisplayName)) {Title = "Name"},
|
||||||
};
|
new DataColumn(nameof(Season.Description)) {Title = "Description"},
|
||||||
SeasonsView = new BindableListView<Season>(seasonsColumns, nameof(Season.SeasonId) ,_seasonsView);
|
new DataColumn(nameof(Season.Start))
|
||||||
SeasonsView.OnSelectionChanged += SeasonsViewOnOnSelectionChanged;
|
{
|
||||||
Info("Application Started");
|
Title = "Start",
|
||||||
|
Format = date => (date as DateTime?)?.ToString("dd.MM.yyyy hh:mm")
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
SeasonsControl = new BindableListControl<Season>(seasonsColumns, nameof(Season.SeasonId), SeasonsView);
|
||||||
|
SeasonsControl.OnSelectionChanged += SeasonsViewOnOnSelectionChanged;
|
||||||
|
Info("Application Started");
|
||||||
|
|
||||||
|
// No need to wait for the loading to finnish
|
||||||
|
var _ = ReloadSeasons();
|
||||||
|
}
|
||||||
|
|
||||||
|
private EstusShotsClient Client { get; }
|
||||||
|
private BindableListControl<Season> SeasonsControl { get; }
|
||||||
|
|
||||||
private void SeasonsViewOnOnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void SeasonsViewOnOnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -57,49 +62,45 @@ namespace EstusShots.Gtk
|
|||||||
|
|
||||||
private async void NewSeasonButtonOnClicked(object sender, EventArgs e)
|
private async void NewSeasonButtonOnClicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var nextNum = SeasonsView.Items.Any() ? SeasonsView.Items.Max(x => x.Number) + 1 : 1 ;
|
using var _ = new LoadingMode(this);
|
||||||
var season = new Season
|
var season = new Season
|
||||||
{
|
{
|
||||||
Game = "Test Game",
|
Game = "Test Game",
|
||||||
Number = nextNum,
|
Number = SeasonsControl.Items.Any() ? SeasonsControl.Items.Max(x => x.Number) + 1 : 1,
|
||||||
Start = DateTime.Now
|
Start = DateTime.Now,
|
||||||
|
Description = "This is a demo description!"
|
||||||
};
|
};
|
||||||
var content = new StringContent(JsonSerializer.Serialize(season), Encoding.UTF8, "application/json");
|
|
||||||
var client = new HttpClient();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var response = await client.PostAsync(ApiUrl + "season", content);
|
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
var (res, _) = await Client.CreateSeason(season);
|
||||||
|
if (!res.Success)
|
||||||
{
|
{
|
||||||
_infoLabel.Text = $"Error while creating Season: {response.ReasonPhrase}";
|
_infoLabel.Text = $"Error while creating Season: {res.ShortMessage}";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await ReloadSeasons();
|
await ReloadSeasons();
|
||||||
Info("Created new Season");
|
Info("Created new Season");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_infoLabel.Text = $"Exception Occured: {ex.Message}";
|
|
||||||
Console.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void LoadButtonClicked(object sender, EventArgs a)
|
private async void LoadButtonClicked(object sender, EventArgs a)
|
||||||
{
|
{
|
||||||
|
using var _ = new LoadingMode(this);
|
||||||
Info("Loading Seasons...");
|
Info("Loading Seasons...");
|
||||||
await ReloadSeasons();
|
await ReloadSeasons();
|
||||||
Info("List Refreshed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ReloadSeasons()
|
private async Task ReloadSeasons()
|
||||||
{
|
{
|
||||||
LoadingMode(true);
|
var (res, seasons) = await Task.Factory.StartNew(() => Client.GetSeasons().Result);
|
||||||
var seasons = await Task.Factory.StartNew(() => Client.GetSeasons().Result);
|
if (!res.Success)
|
||||||
SeasonsView.Items = seasons;
|
{
|
||||||
SeasonsView.DataBind();
|
_infoLabel.Text = $"Refresh Failed: {res.ShortMessage}";
|
||||||
LoadingMode(false);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SeasonsControl.Items = seasons;
|
||||||
|
SeasonsControl.DataBind();
|
||||||
|
Info("List Refreshed");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Window_DeleteEvent(object sender, DeleteEventArgs a)
|
private void Window_DeleteEvent(object sender, DeleteEventArgs a)
|
||||||
@@ -107,20 +108,10 @@ namespace EstusShots.Gtk
|
|||||||
Application.Quit();
|
Application.Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadingMode(bool active)
|
|
||||||
{
|
|
||||||
_loadButton.Sensitive = !active;
|
|
||||||
_newSeasonButton.Sensitive = !active;
|
|
||||||
_seasonsView.Sensitive = !active;
|
|
||||||
if (active)
|
|
||||||
_seasonsOverlay.AddOverlay(_loadingSpinner);
|
|
||||||
else
|
|
||||||
_seasonsOverlay.Remove(_loadingSpinner);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Info(string message)
|
private void Info(string message)
|
||||||
{
|
{
|
||||||
_infoLabel.Text = message;
|
_infoLabel.Text = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,37 @@
|
|||||||
<!-- Generated with glade 3.22.1 -->
|
<!-- Generated with glade 3.22.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.18"/>
|
<requires lib="gtk+" version="3.18"/>
|
||||||
|
<object class="GtkBox" id="LoadingSpinner">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="spacing">5</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSpinner">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="active">True</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="label" translatable="yes">Loading....</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
<object class="GtkWindow" id="MainWindow">
|
<object class="GtkWindow" id="MainWindow">
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="title" translatable="yes">Estus Shots</property>
|
<property name="title" translatable="yes">Estus Shots</property>
|
||||||
@@ -20,7 +51,7 @@
|
|||||||
<property name="margin_bottom">2</property>
|
<property name="margin_bottom">2</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkOverlay" id="_seasonsOverlay">
|
<object class="GtkOverlay" id="SeasonsOverlay">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<child>
|
<child>
|
||||||
@@ -29,7 +60,7 @@
|
|||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="shadow_type">in</property>
|
<property name="shadow_type">in</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkTreeView" id="_seasonsView">
|
<object class="GtkTreeView" id="SeasonsView">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="enable_grid_lines">horizontal</property>
|
<property name="enable_grid_lines">horizontal</property>
|
||||||
@@ -56,7 +87,7 @@
|
|||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="layout_style">end</property>
|
<property name="layout_style">end</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="_loadButton">
|
<object class="GtkButton" id="LoadButton">
|
||||||
<property name="label" translatable="yes">Reload</property>
|
<property name="label" translatable="yes">Reload</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
@@ -69,7 +100,7 @@
|
|||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="_newSeasonButton">
|
<object class="GtkButton" id="NewSeasonButton">
|
||||||
<property name="label" translatable="yes">New Season</property>
|
<property name="label" translatable="yes">New Season</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
@@ -132,35 +163,4 @@
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkBox" id="_loadingSpinner">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">center</property>
|
|
||||||
<property name="valign">center</property>
|
|
||||||
<property name="spacing">5</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkSpinner">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="active">True</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="label" translatable="yes">Loading....</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</interface>
|
</interface>
|
||||||
|
|||||||
52
EstusShots.Server/Controllers/EpisodeController.cs
Normal file
52
EstusShots.Server/Controllers/EpisodeController.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AutoMapper;
|
||||||
|
using EstusShots.Server.Models;
|
||||||
|
using EstusShots.Server.Services;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace EstusShots.Server.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("/api/[controller]")]
|
||||||
|
public class EpisodeController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly EstusShotsContext _context;
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public EpisodeController(EstusShotsContext context, IMapper mapper, ILogger<EpisodeController> logger)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_mapper = mapper;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<ActionResult<Shared.Dto.Episode>> GetEpisode(Guid id)
|
||||||
|
{
|
||||||
|
var episode = await _context.Seasons.FindAsync(id);
|
||||||
|
if (episode == null) {return NotFound();}
|
||||||
|
|
||||||
|
var episodeDto = _mapper.Map<Shared.Dto.Episode>(episode);
|
||||||
|
return episodeDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<Shared.Dto.Episode>> CreateSeason(Shared.Dto.Episode episodeDto)
|
||||||
|
{
|
||||||
|
var episode = _mapper.Map<Episode>(episodeDto);
|
||||||
|
_context.Episodes.Add(episode);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, "Error while saving object");
|
||||||
|
}
|
||||||
|
return CreatedAtAction(nameof(GetEpisode), new {id = episode.EpisodeId}, episode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
EstusShots.Server/Controllers/EpisodesController.cs
Normal file
38
EstusShots.Server/Controllers/EpisodesController.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AutoMapper;
|
||||||
|
using EstusShots.Server.Services;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Dto = EstusShots.Shared.Dto;
|
||||||
|
|
||||||
|
namespace EstusShots.Server.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("/api/[controller]")]
|
||||||
|
public class EpisodesController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly EstusShotsContext _context;
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public EpisodesController(ILogger<EpisodesController> logger, IMapper mapper, EstusShotsContext context)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_mapper = mapper;
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("seasonId")]
|
||||||
|
public async Task<ActionResult<List<Dto.Episode>>> GetEpisodes(Guid seasonId)
|
||||||
|
{
|
||||||
|
_logger.LogDebug($"All");
|
||||||
|
var episodes = await _context.Episodes.Where(x => x.SeasonId == seasonId).ToListAsync();
|
||||||
|
var dtos = _mapper.Map<List<Dto.Episode>>(episodes);
|
||||||
|
return dtos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@ using AutoMapper;
|
|||||||
using EstusShots.Server.Models;
|
using EstusShots.Server.Models;
|
||||||
using EstusShots.Server.Services;
|
using EstusShots.Server.Services;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Dto = EstusShots.Shared.Models;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Dto = EstusShots.Shared.Dto;
|
||||||
|
|
||||||
namespace EstusShots.Server.Controllers
|
namespace EstusShots.Server.Controllers
|
||||||
{
|
{
|
||||||
@@ -14,18 +15,20 @@ namespace EstusShots.Server.Controllers
|
|||||||
{
|
{
|
||||||
private readonly EstusShotsContext _context;
|
private readonly EstusShotsContext _context;
|
||||||
private readonly IMapper _mapper;
|
private readonly IMapper _mapper;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public SeasonController(EstusShotsContext context, IMapper mapper)
|
public SeasonController(EstusShotsContext context, IMapper mapper, ILogger<SeasonController> logger)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
public async Task<ActionResult<Dto.Season>> GetSeason(Guid id)
|
public async Task<ActionResult<Dto.Season>> GetSeason(Guid id)
|
||||||
{
|
{
|
||||||
var season = await _context.Seasons.FindAsync(id);
|
var season = await _context.Seasons.FindAsync(id);
|
||||||
if (season == null) return NotFound();
|
if (season == null) {return NotFound();}
|
||||||
|
|
||||||
var seasonDto = _mapper.Map<Dto.Season>(season);
|
var seasonDto = _mapper.Map<Dto.Season>(season);
|
||||||
return seasonDto;
|
return seasonDto;
|
||||||
@@ -36,7 +39,15 @@ namespace EstusShots.Server.Controllers
|
|||||||
{
|
{
|
||||||
var dbSeason = _mapper.Map<Season>(season);
|
var dbSeason = _mapper.Map<Season>(season);
|
||||||
_context.Seasons.Add(dbSeason);
|
_context.Seasons.Add(dbSeason);
|
||||||
|
try
|
||||||
|
{
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
|
_logger.LogInformation("New season created");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, "Error while saving Season");
|
||||||
|
}
|
||||||
return CreatedAtAction(nameof(GetSeason), new {id = dbSeason.SeasonId}, dbSeason);
|
return CreatedAtAction(nameof(GetSeason), new {id = dbSeason.SeasonId}, dbSeason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using AutoMapper;
|
|||||||
using EstusShots.Server.Services;
|
using EstusShots.Server.Services;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Dto = EstusShots.Shared.Models;
|
using Dto = EstusShots.Shared.Dto;
|
||||||
|
|
||||||
namespace EstusShots.Server.Controllers
|
namespace EstusShots.Server.Controllers
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,8 +7,11 @@ namespace EstusShots.Server.Mapping
|
|||||||
{
|
{
|
||||||
public Profiles()
|
public Profiles()
|
||||||
{
|
{
|
||||||
CreateMap<Season, Shared.Models.Season>();
|
CreateMap<Season, Shared.Dto.Season>();
|
||||||
CreateMap<Shared.Models.Season, Season>();
|
CreateMap<Shared.Dto.Season, Season>();
|
||||||
|
|
||||||
|
CreateMap<Episode, Shared.Dto.Episode>();
|
||||||
|
CreateMap<Shared.Dto.Episode, Episode>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
24
EstusShots.Server/Models/Episode.cs
Normal file
24
EstusShots.Server/Models/Episode.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace EstusShots.Server.Models
|
||||||
|
{
|
||||||
|
public class Episode
|
||||||
|
{
|
||||||
|
public Guid EpisodeId { get; set; }
|
||||||
|
|
||||||
|
public int Number { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(50)] public string Title { get; set; } = default!;
|
||||||
|
|
||||||
|
public DateTime DateTime { get; set; }
|
||||||
|
|
||||||
|
public DateTime Start { get; set; }
|
||||||
|
|
||||||
|
public DateTime? End { get; set; }
|
||||||
|
|
||||||
|
public Guid SeasonId { get; set; }
|
||||||
|
|
||||||
|
public Season Season { get; set; } = default!;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace EstusShots.Server.Models
|
namespace EstusShots.Server.Models
|
||||||
@@ -16,5 +17,7 @@ namespace EstusShots.Server.Models
|
|||||||
public DateTime Start { get; set; }
|
public DateTime Start { get; set; }
|
||||||
|
|
||||||
public DateTime? End { get; set; }
|
public DateTime? End { get; set; }
|
||||||
|
|
||||||
|
public List<Episode> Episodes { get; set; } = default!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,7 @@ namespace EstusShots.Server
|
|||||||
|
|
||||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
Host.CreateDefaultBuilder(args)
|
Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureLogging(builder => { builder.AddConsole(); })
|
||||||
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
|
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,8 +7,10 @@ namespace EstusShots.Server.Services
|
|||||||
{
|
{
|
||||||
public EstusShotsContext(DbContextOptions options) : base(options)
|
public EstusShotsContext(DbContextOptions options) : base(options)
|
||||||
{
|
{
|
||||||
|
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DbSet<Season> Seasons { get; set; } = default!;
|
public DbSet<Season> Seasons { get; set; } = default!;
|
||||||
|
public DbSet<Episode> Episodes { get; set; } = default!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace EstusShots.Server
|
namespace EstusShots.Server
|
||||||
{
|
{
|
||||||
|
|||||||
25
EstusShots.Shared/Dto/Episode.cs
Normal file
25
EstusShots.Shared/Dto/Episode.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace EstusShots.Shared.Dto
|
||||||
|
{
|
||||||
|
public class Episode
|
||||||
|
{
|
||||||
|
public Guid EpisodeId { get; set; }
|
||||||
|
|
||||||
|
public int Number { get; set; }
|
||||||
|
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
public DateTime DateTime { get; set; }
|
||||||
|
|
||||||
|
public DateTime Start { get; set; }
|
||||||
|
|
||||||
|
public DateTime? End { get; set; }
|
||||||
|
|
||||||
|
public Guid SeasonId { get; set; }
|
||||||
|
|
||||||
|
public Season Season { get; set; }
|
||||||
|
|
||||||
|
public string Displayname => $"S{Season.Number:D2}E{Number:D2}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace EstusShots.Shared.Models
|
namespace EstusShots.Shared.Dto
|
||||||
{
|
{
|
||||||
public class Season
|
public class Season
|
||||||
{
|
{
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>disable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
|||||||
32
EstusShots.Shared/Models/OperationResult.cs
Normal file
32
EstusShots.Shared/Models/OperationResult.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace EstusShots.Shared.Models
|
||||||
|
{
|
||||||
|
public class OperationResult
|
||||||
|
{
|
||||||
|
public bool Success { get; }
|
||||||
|
public string ShortMessage { get; set; }
|
||||||
|
public string DetailedMessage { get; set; }
|
||||||
|
public string StackTrace { get; set; }
|
||||||
|
|
||||||
|
public OperationResult()
|
||||||
|
{
|
||||||
|
Success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OperationResult(bool success, string shortMessage, string detailedMessage = null)
|
||||||
|
{
|
||||||
|
Success = success;
|
||||||
|
ShortMessage = shortMessage;
|
||||||
|
DetailedMessage = detailedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OperationResult(Exception e)
|
||||||
|
{
|
||||||
|
Success = false;
|
||||||
|
ShortMessage = e.Message;
|
||||||
|
DetailedMessage = e.InnerException?.Message;
|
||||||
|
StackTrace = e.StackTrace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user