squash a bunch of commits

This commit is contained in:
2022-10-30 12:03:16 -05:00
parent 09b72c1858
commit 93027e8c57
222 changed files with 6157 additions and 3201 deletions

View File

@@ -0,0 +1,51 @@
using Microsoft.JSInterop;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Shogi.UI.Shared
{
public class LocalStorage : ILocalStorage
{
private readonly JsonSerializerOptions jsonOptions;
private readonly IJSRuntime jSRuntime;
public LocalStorage(IJSRuntime jSRuntime)
{
jsonOptions = new JsonSerializerOptions();
jsonOptions.Converters.Add(new JsonStringEnumConverter());
this.jSRuntime = jSRuntime;
}
public ValueTask Set<T>(string key, T value)
{
var serialized = JsonSerializer.Serialize(value);
return jSRuntime.InvokeVoidAsync("localStorage.setItem", key, serialized);
}
public async ValueTask<T?> Get<T>(string key) where T : struct
{
var value = await jSRuntime.InvokeAsync<string>("localStorage.getItem", key);
try
{
return JsonSerializer.Deserialize<T>(value, jsonOptions);
}
catch (ArgumentNullException)
{
return default;
}
}
public ValueTask Delete(string key)
{
return jSRuntime.InvokeVoidAsync("localStorage.removeItem", key);
}
}
public interface ILocalStorage
{
ValueTask Delete(string key);
ValueTask<T?> Get<T>(string key) where T : struct;
ValueTask Set<T>(string key, T value);
}
}

View File

@@ -0,0 +1,24 @@
@*@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication*@
@*@inject NavigationManager Navigation
@inject SignOutSessionStateManager SignOutManager*@
@*<AuthorizeView>
<Authorized>
Hello, @context.User.Identity?.Name!
<button class="nav-link btn btn-link" @onclick="BeginSignOut">Log out</button>
</Authorized>
<NotAuthorized>
<a href="authentication/login">Log in</a>
</NotAuthorized>
</AuthorizeView>*@
@code{
// https://docs.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/additional-scenarios?view=aspnetcore-6.0#customize-the-authentication-user-interface
//private async Task BeginSignOut(MouseEventArgs args)
//{
// await SignOutManager.SetSignOutState();
// Navigation.NavigateTo("authentication/logout");
//}
}

View File

@@ -0,0 +1,4 @@
@inherits LayoutComponentBase
@Body

View File

@@ -0,0 +1,3 @@
html, body, #app {
height: 100%;
}

View File

@@ -0,0 +1,54 @@
namespace Shogi.UI.Shared.Modal
{
/// <summary>
/// An injectible service which can be invoked to display preset modals via the <Modals /> razor component.
/// Only one modal me be visible at a time.
/// </summary>
public class ModalService
{
public event EventHandler<ModalVisibilityChangedEventArgs>? ModalVisibilityChangedEvent;
public ModalService()
{
}
public bool LoginModalIsVisible { get; private set; }
public bool GuestAccountDescriptionIsVisible { get; private set; }
public void ShowLoginModal()
{
SetAllVisibilitiesToFalse();
LoginModalIsVisible = true;
EmitCurrentState();
}
public void ShowGuestAccountDescriptionModal()
{
SetAllVisibilitiesToFalse();
GuestAccountDescriptionIsVisible = true;
EmitCurrentState();
}
public void HideAllModals()
{
SetAllVisibilitiesToFalse();
EmitCurrentState();
}
private void EmitCurrentState()
{
ModalVisibilityChangedEvent?.Invoke(this, new()
{
LoginModalIsVisible = LoginModalIsVisible,
GuestAccountDescriptionIsVisible = GuestAccountDescriptionIsVisible
});
}
private void SetAllVisibilitiesToFalse()
{
LoginModalIsVisible = false;
GuestAccountDescriptionIsVisible = false;
}
}
}

View File

@@ -0,0 +1,9 @@
namespace Shogi.UI.Shared.Modal
{
public class ModalVisibilityChangedEventArgs : EventArgs
{
public bool LoginModalIsVisible { get; set; }
public bool GuestAccountDescriptionIsVisible { get; set; }
}
}

View File

@@ -0,0 +1,39 @@
@inject ModalService modalService
@inject AccountManager Account
@inject NavigationManager NavManager
@inject ILocalStorage localStorage
@if (shouldShow)
{
<div class="my-modal-background">
<div class="my-modal">
@if (modalService.LoginModalIsVisible)
{
}
else if (modalService.GuestAccountDescriptionIsVisible)
{
}
</div>
</div>
}
@code {
bool shouldShow = false;
protected override void OnInitialized()
{
modalService.ModalVisibilityChangedEvent += OnModalChange;
}
void OnModalChange(object? sender, ModalVisibilityChangedEventArgs args)
{
Console.WriteLine("Modal Change");
if (args != null)
{
shouldShow = args.LoginModalIsVisible || args.GuestAccountDescriptionIsVisible;
StateHasChanged();
}
}
}

View File

@@ -0,0 +1,21 @@
.my-modal-background {
display: grid;
place-items: center;
position: fixed;
background-color: rgba(0,0,0,0.4);
inset: 0;
z-index: 900;
}
.my-modal {
text-align: center;
background-color: var(--contrast-color);
padding: 1rem;
max-width: 40rem;
}
.account-description {
display: grid;
grid-template-columns: 1fr max-content max-content;
column-gap: 1.5rem;
}

View File

@@ -0,0 +1,5 @@
<h3>MyNotAuthorized</h3>
@code {
}

View File

@@ -0,0 +1,14 @@
@inject NavigationManager Navigation
@inject ModalService ModalService
@inject AccountManager ShogiService
@*<button @onclick="() => ShogiService.ConnectAsync(asGuest: true)">Guest Login</button>*@
<div>Not implemented!</div>
@code {
protected override void OnInitialized()
{
//ModalService.ShowLoginModal();
//Navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}");
}
}

View File

@@ -0,0 +1,28 @@
<div className="rotating-cogs">
<svg className="cog" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<path fill="#444" d="M94.1 58.8c.6-2.8.9-5.8.9-8.8s-.3-6-.9-8.8l-11.7-.4c-.7-2.6-1.7-5-3-7.3l8-8.5c-3.3-4.9-7.5-9.2-12.5-12.5l-8.5 8c-2.3-1.3-4.7-2.3-7.3-3l-.3-11.6C56 5.3 53 5 50 5s-6 .3-8.8.9l-.4 11.7c-2.6.7-5 1.7-7.3 3l-8.5-8c-4.9 3.3-9.2 7.5-12.5 12.5l8 8.5c-1.3 2.3-2.3 4.7-3 7.3l-11.6.3C5.3 44 5 47 5 50s.3 6 .9 8.8l11.7.4c.7 2.6 1.7 5 3 7.3l-8 8.5c3.3 4.9 7.5 9.2 12.5 12.5l8.5-8c2.3 1.3 4.7 2.3 7.3 3l.4 11.7c2.7.5 5.7.8 8.7.8s6-.3 8.8-.9l.4-11.7c2.6-.7 5-1.7 7.3-3l8.5 8c4.9-3.3 9.2-7.5 12.5-12.5l-8-8.5c1.3-2.3 2.3-4.7 3-7.3l11.6-.3zM50 66.9c-9.3 0-16.9-7.6-16.9-16.9S40.7 33.1 50 33.1 66.9 40.7 66.9 50 59.3 66.9 50 66.9z" />
<animateTransform attributeName="transform"
attributeType="XML"
type="rotate"
from="20 0 0"
to="380 0 0"
dur="12s"
repeatCount="indefinite" />
</svg>
<svg className="cog" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<path fill="#444" d="M94.1 58.8c.6-2.8.9-5.8.9-8.8s-.3-6-.9-8.8l-11.7-.4c-.7-2.6-1.7-5-3-7.3l8-8.5c-3.3-4.9-7.5-9.2-12.5-12.5l-8.5 8c-2.3-1.3-4.7-2.3-7.3-3l-.3-11.6C56 5.3 53 5 50 5s-6 .3-8.8.9l-.4 11.7c-2.6.7-5 1.7-7.3 3l-8.5-8c-4.9 3.3-9.2 7.5-12.5 12.5l8 8.5c-1.3 2.3-2.3 4.7-3 7.3l-11.6.3C5.3 44 5 47 5 50s.3 6 .9 8.8l11.7.4c.7 2.6 1.7 5 3 7.3l-8 8.5c3.3 4.9 7.5 9.2 12.5 12.5l8.5-8c2.3 1.3 4.7 2.3 7.3 3l.4 11.7c2.7.5 5.7.8 8.7.8s6-.3 8.8-.9l.4-11.7c2.6-.7 5-1.7 7.3-3l8.5 8c4.9-3.3 9.2-7.5 12.5-12.5l-8-8.5c1.3-2.3 2.3-4.7 3-7.3l11.6-.3zM50 66.9c-9.3 0-16.9-7.6-16.9-16.9S40.7 33.1 50 33.1 66.9 40.7 66.9 50 59.3 66.9 50 66.9z" />
<animateTransform attributeName="transform"
attributeType="XML"
type="rotate"
from="360 0 0"
to="0 0 0"
dur="12s"
repeatCount="indefinite" />
</svg>
</div>
@code {
}

View File

@@ -0,0 +1,15 @@
.rotating-cogs {
text-align: center;
}
.left, right {
position: relative;
width: 32px;
}
.left {
left: 2px;
}
.right {
left: -2px;
}

View File

@@ -0,0 +1,89 @@
using Microsoft.AspNetCore.Http.Extensions;
using Shogi.Contracts.Socket;
using Shogi.Contracts.Types;
using System.Buffers;
using System.Net.WebSockets;
using System.Text.Json;
namespace Shogi.UI.Shared;
public class ShogiSocket : IDisposable
{
public event EventHandler<SessionCreatedSocketMessage>? OnCreateGameMessage;
private readonly ClientWebSocket socket;
private readonly JsonSerializerOptions serializerOptions;
private readonly UriBuilder uriBuilder;
private readonly CancellationTokenSource cancelToken;
private readonly IMemoryOwner<byte> memoryOwner;
private bool disposedValue;
public ShogiSocket(IConfiguration configuration, ClientWebSocket socket, JsonSerializerOptions serializerOptions)
{
this.socket = socket;
this.serializerOptions = serializerOptions;
this.uriBuilder = new UriBuilder(configuration["SocketUrl"]);
this.cancelToken = new CancellationTokenSource();
this.memoryOwner = MemoryPool<byte>.Shared.Rent(1024 * 2);
}
public async Task OpenAsync(string token)
{
uriBuilder.Query = new QueryBuilder
{
{ "token", token }
}.ToQueryString().Value;
await socket.ConnectAsync(this.uriBuilder.Uri, cancelToken.Token);
Console.WriteLine("CONNECTED");
Listen();
}
private async void Listen()
{
while (socket.State == WebSocketState.Open && !cancelToken.IsCancellationRequested)
{
var result = await socket.ReceiveAsync(this.memoryOwner.Memory, cancelToken.Token);
var memory = this.memoryOwner.Memory[..result.Count].ToArray();
var action = JsonDocument
.Parse(memory[..result.Count])
.RootElement
.GetProperty(nameof(ISocketResponse.Action))
.Deserialize<SocketAction>();
switch (action)
{
case SocketAction.SessionCreated:
this.OnCreateGameMessage?.Invoke(this, JsonSerializer.Deserialize<SessionCreatedSocketMessage>(memory, this.serializerOptions)!);
break;
default:
break;
}
}
if (!cancelToken.IsCancellationRequested)
{
throw new InvalidOperationException("Stopped socket listening without cancelling.");
}
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
cancelToken.Cancel();
socket.Dispose();
memoryOwner.Dispose();
}
disposedValue = true;
}
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}