in the middle of relearning msal

This commit is contained in:
2022-11-17 16:18:15 -06:00
parent 716470f24d
commit e3cf2f1059
14 changed files with 324 additions and 298 deletions

View File

@@ -9,85 +9,89 @@ namespace Shogi.Api.Repositories;
public class SessionRepository : ISessionRepository public class SessionRepository : ISessionRepository
{ {
private readonly string connectionString; private readonly string connectionString;
public SessionRepository(IConfiguration configuration) public SessionRepository(IConfiguration configuration)
{ {
connectionString = configuration.GetConnectionString("ShogiDatabase"); connectionString = configuration.GetConnectionString("ShogiDatabase");
} }
public async Task CreateSession(Session session) public async Task CreateSession(Session session)
{ {
using var connection = new SqlConnection(connectionString); using var connection = new SqlConnection(connectionString);
await connection.ExecuteAsync( await connection.ExecuteAsync(
"session.CreateSession", "session.CreateSession",
new new
{ {
session.Name, session.Name,
Player1Name = session.Player1, Player1Name = session.Player1,
}, },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
} }
public async Task DeleteSession(string name) public async Task DeleteSession(string name)
{ {
using var connection = new SqlConnection(connectionString); using var connection = new SqlConnection(connectionString);
await connection.ExecuteAsync( await connection.ExecuteAsync(
"session.DeleteSession", "session.DeleteSession",
new { Name = name }, new { Name = name },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
} }
public async Task<Session?> ReadSession(string name) public async Task<Session?> ReadSession(string name)
{ {
using var connection = new SqlConnection(connectionString); using var connection = new SqlConnection(connectionString);
var results = await connection.QueryMultipleAsync( var results = await connection.QueryMultipleAsync(
"session.ReadSession", "session.ReadSession",
new { Name = name }, new { Name = name },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
var sessionDtos = await results.ReadAsync<SessionDto>(); var sessionDtos = await results.ReadAsync<SessionDto>();
if (!sessionDtos.Any()) return null; if (!sessionDtos.Any()) return null;
var dto = sessionDtos.First(); var dto = sessionDtos.First();
var session = new Session(dto.Name, dto.Player1); var session = new Session(dto.Name, dto.Player1);
if (!string.IsNullOrWhiteSpace(dto.Player2)) session.AddPlayer2(dto.Player2); if (!string.IsNullOrWhiteSpace(dto.Player2)) session.AddPlayer2(dto.Player2);
var moveDtos = await results.ReadAsync<MoveDto>(); var moveDtos = await results.ReadAsync<MoveDto>();
foreach (var move in moveDtos) foreach (var move in moveDtos)
{ {
if (move.PieceFromHand.HasValue) if (move.PieceFromHand.HasValue)
{ {
session.Board.Move(move.PieceFromHand.Value, move.To); session.Board.Move(move.PieceFromHand.Value, move.To);
} }
else else if (move.From != null)
{ {
session.Board.Move(move.From, move.To, false); session.Board.Move(move.From, move.To, false);
} }
} else
return session; {
} throw new InvalidOperationException($"Corrupt data during {nameof(ReadSession)}");
}
}
return session;
}
public async Task CreateMove(string sessionName, Contracts.Api.MovePieceCommand command) public async Task CreateMove(string sessionName, Contracts.Api.MovePieceCommand command)
{ {
using var connection = new SqlConnection(connectionString); using var connection = new SqlConnection(connectionString);
await connection.ExecuteAsync( await connection.ExecuteAsync(
"session.CreateMove", "session.CreateMove",
new new
{ {
command.To, command.To,
command.From, command.From,
command.IsPromotion, command.IsPromotion,
command.PieceFromHand, command.PieceFromHand,
SessionName = sessionName SessionName = sessionName
}, },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
} }
} }
public interface ISessionRepository public interface ISessionRepository
{ {
Task CreateMove(string sessionName, MovePieceCommand command); Task CreateMove(string sessionName, MovePieceCommand command);
Task CreateSession(Session session); Task CreateSession(Session session);
Task DeleteSession(string name); Task DeleteSession(string name);
Task<Session?> ReadSession(string name); Task<Session?> ReadSession(string name);
} }

View File

@@ -47,7 +47,7 @@ public class ShogiUserClaimsTransformer : IShogiUserClaimsTransformer
private async Task<ClaimsPrincipal> CreateClaimsFromMicrosoftPrincipal(ClaimsPrincipal principal) private async Task<ClaimsPrincipal> CreateClaimsFromMicrosoftPrincipal(ClaimsPrincipal principal)
{ {
var id = principal.GetMsalAccountId(); var id = principal.GetMicrosoftUserId();
if (string.IsNullOrWhiteSpace(id)) if (string.IsNullOrWhiteSpace(id))
{ {
throw new UnauthorizedAccessException("Found MSAL claims but no preferred_username."); throw new UnauthorizedAccessException("Found MSAL claims but no preferred_username.");

View File

@@ -26,7 +26,7 @@ BEGIN
piece.[Name] as PieceFromHand piece.[Name] as PieceFromHand
FROM [session].[Move] mv FROM [session].[Move] mv
INNER JOIN [session].[Session] sess ON sess.Id = mv.SessionId INNER JOIN [session].[Session] sess ON sess.Id = mv.SessionId
RIGHT JOIN [session].Piece piece on piece.Id = mv.PieceIdFromHand LEFT JOIN [session].Piece piece on piece.Id = mv.PieceIdFromHand
WHERE sess.[Name] = @Name; WHERE sess.[Name] = @Name;
COMMIT COMMIT

View File

@@ -1,9 +1,9 @@
@*<CascadingAuthenticationState>*@ <CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly"> <Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData"> <Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
@*<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"> <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<Authorizing> <Authorizing>
Authorizing!! Authorizing!!
</Authorizing> </Authorizing>
@@ -17,7 +17,7 @@
<p role="alert">You are not authorized to access this resource.</p> <p role="alert">You are not authorized to access this resource.</p>
} }
</NotAuthorized> </NotAuthorized>
</AuthorizeRouteView>*@ </AuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found> </Found>
<NotFound> <NotFound>
@@ -28,4 +28,4 @@
</NotFound> </NotFound>
</Router> </Router>
@*</CascadingAuthenticationState>*@ </CascadingAuthenticationState>

View File

@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Shogi.UI.Pages.Home.Api; using Shogi.UI.Pages.Home.Api;
using Shogi.UI.Shared; using Shogi.UI.Shared;
@@ -6,133 +7,132 @@ namespace Shogi.UI.Pages.Home.Account;
public class AccountManager public class AccountManager
{ {
private readonly AccountState accountState; private readonly AccountState accountState;
private readonly IShogiApi shogiApi; private readonly IShogiApi shogiApi;
private readonly IConfiguration configuration; private readonly IConfiguration configuration;
private readonly ILocalStorage localStorage; private readonly ILocalStorage localStorage;
//private readonly AuthenticationStateProvider authState; private readonly AuthenticationStateProvider authState;
private readonly NavigationManager navigation; private readonly NavigationManager navigation;
private readonly ShogiSocket shogiSocket; private readonly ShogiSocket shogiSocket;
public AccountManager( public AccountManager(
AccountState accountState, AccountState accountState,
IShogiApi unauthenticatedClient, IShogiApi unauthenticatedClient,
IConfiguration configuration, IConfiguration configuration,
//AuthenticationStateProvider authState, AuthenticationStateProvider authState,
ILocalStorage localStorage, ILocalStorage localStorage,
NavigationManager navigation, NavigationManager navigation,
ShogiSocket shogiSocket) ShogiSocket shogiSocket)
{ {
this.accountState = accountState; this.accountState = accountState;
this.shogiApi = unauthenticatedClient; this.shogiApi = unauthenticatedClient;
this.configuration = configuration; this.configuration = configuration;
//this.authState = authState; this.authState = authState;
this.localStorage = localStorage; this.localStorage = localStorage;
this.navigation = navigation; this.navigation = navigation;
this.shogiSocket = shogiSocket; this.shogiSocket = shogiSocket;
} }
private User? User { get => accountState.User; set => accountState.User = value; } private User? User { get => accountState.User; set => accountState.User = value; }
public async Task LoginWithGuestAccount() public async Task LoginWithGuestAccount()
{ {
var response = await shogiApi.GetToken(); var response = await shogiApi.GetToken();
if (response != null) if (response != null)
{ {
User = new User User = new User
{ {
DisplayName = response.DisplayName, DisplayName = response.DisplayName,
Id = response.UserId, Id = response.UserId,
OneTimeSocketToken = response.OneTimeToken, OneTimeSocketToken = response.OneTimeToken,
WhichAccountPlatform = WhichAccountPlatform.Guest WhichAccountPlatform = WhichAccountPlatform.Guest
}; };
await shogiSocket.OpenAsync(User.OneTimeSocketToken.ToString()); await shogiSocket.OpenAsync(User.OneTimeSocketToken.ToString());
await localStorage.SetAccountPlatform(WhichAccountPlatform.Guest); await localStorage.SetAccountPlatform(WhichAccountPlatform.Guest);
} }
} }
public async Task LoginWithMicrosoftAccount() public async Task LoginWithMicrosoftAccount()
{ {
throw new NotImplementedException(); var state = await authState.GetAuthenticationStateAsync();
//var state = await authState.GetAuthenticationStateAsync();
//if (state.User?.Identity?.Name == null || state.User?.Identity?.IsAuthenticated != true) if (state.User?.Identity?.Name == null || state.User?.Identity?.IsAuthenticated != true)
//{ {
// navigation.NavigateTo("authentication/login"); navigation.NavigateTo("authentication/login");
// return; return;
//} }
//var id = state.User.Identity.Name; var response = await shogiApi.GetToken();
//var socketToken = await shogiApi.GetToken(); if (response != null)
//if (socketToken.HasValue) {
//{ User = new User
// User = new User {
// { DisplayName = response.DisplayName,
// DisplayName = id, Id = response.UserId,
// Id = id, OneTimeSocketToken = response.OneTimeToken,
// OneTimeSocketToken = socketToken.Value WhichAccountPlatform = WhichAccountPlatform.Microsoft,
// }; };
// await ConnectToSocketAsync(); await shogiSocket.OpenAsync(User.OneTimeSocketToken.ToString());
// await localStorage.SetAccountPlatform(WhichAccountPlatform.Microsoft); await localStorage.SetAccountPlatform(WhichAccountPlatform.Microsoft);
// } }
} }
/// <summary> /// <summary>
/// Try to log in with the account used from the previous browser session. /// Try to log in with the account used from the previous browser session.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public async Task<bool> TryLoginSilentAsync() public async Task<bool> TryLoginSilentAsync()
{ {
var platform = await localStorage.GetAccountPlatform(); var platform = await localStorage.GetAccountPlatform();
if (platform == WhichAccountPlatform.Guest) if (platform == WhichAccountPlatform.Guest)
{ {
var response = await shogiApi.GetToken(); var response = await shogiApi.GetToken();
if (response != null) if (response != null)
{ {
User = new User User = new User
{ {
DisplayName = response.DisplayName, DisplayName = response.DisplayName,
Id = response.UserId, Id = response.UserId,
OneTimeSocketToken = response.OneTimeToken, OneTimeSocketToken = response.OneTimeToken,
WhichAccountPlatform = WhichAccountPlatform.Guest WhichAccountPlatform = WhichAccountPlatform.Guest
}; };
} }
} }
else if (platform == WhichAccountPlatform.Microsoft) else if (platform == WhichAccountPlatform.Microsoft)
{ {
Console.WriteLine("Login Microsoft"); Console.WriteLine("Login Microsoft");
throw new NotImplementedException(); throw new NotImplementedException();
//var state = await authState.GetAuthenticationStateAsync(); //var state = await authState.GetAuthenticationStateAsync();
//if (state.User?.Identity?.Name != null) //if (state.User?.Identity?.Name != null)
//{ //{
// var id = state.User.Identity; // var id = state.User.Identity;
// User = new User // User = new User
// { // {
// DisplayName = id.Name, // DisplayName = id.Name,
// Id = id.Name // Id = id.Name
// }; // };
// var token = await shogiApi.GetToken(); // var token = await shogiApi.GetToken();
// if (token.HasValue) // if (token.HasValue)
// { // {
// User.OneTimeSocketToken = token.Value; // User.OneTimeSocketToken = token.Value;
// } // }
//} //}
// TODO: If this fails then platform saved to localStorage should get cleared // TODO: If this fails then platform saved to localStorage should get cleared
} }
if (User != null) if (User != null)
{ {
await shogiSocket.OpenAsync(User.OneTimeSocketToken.ToString()); await shogiSocket.OpenAsync(User.OneTimeSocketToken.ToString());
return true; return true;
} }
return false; return false;
} }
public async Task LogoutAsync() public async Task LogoutAsync()
{ {
await Task.WhenAll(shogiApi.GuestLogout(), localStorage.DeleteAccountPlatform()); await Task.WhenAll(shogiApi.GuestLogout(), localStorage.DeleteAccountPlatform());
User = null; User = null;
} }
} }

View File

@@ -1,24 +1,23 @@
using Shogi.UI.Shared; using Shogi.UI.Shared;
namespace Shogi.UI.Pages.Home.Account namespace Shogi.UI.Pages.Home.Account;
public static class LocalStorageExtensions
{ {
public static class LocalStorageExtensions private const string AccountPlatform = "AccountPlatform";
public static Task<WhichAccountPlatform?> GetAccountPlatform(this ILocalStorage self)
{ {
private const string AccountPlatform = "AccountPlatform"; return self.Get<WhichAccountPlatform>(AccountPlatform).AsTask();
}
public static Task<WhichAccountPlatform?> GetAccountPlatform(this ILocalStorage self) public static Task SetAccountPlatform(this ILocalStorage self, WhichAccountPlatform platform)
{ {
return self.Get<WhichAccountPlatform>(AccountPlatform).AsTask(); return self.Set(AccountPlatform, platform.ToString()).AsTask();
} }
public static Task SetAccountPlatform(this ILocalStorage self, WhichAccountPlatform platform) public static Task DeleteAccountPlatform(this ILocalStorage self)
{ {
return self.Set(AccountPlatform, platform.ToString()).AsTask(); return self.Delete(AccountPlatform).AsTask();
}
public static Task DeleteAccountPlatform(this ILocalStorage self)
{
return self.Delete(AccountPlatform).AsTask();
}
} }
} }

View File

@@ -1,7 +1,6 @@
namespace Shogi.UI.Pages.Home.Account namespace Shogi.UI.Pages.Home.Account;
public class LoginEventArgs : EventArgs
{ {
public class LoginEventArgs : EventArgs public User? User { get; set; }
{
public User? User { get; set; }
}
} }

View File

@@ -10,6 +10,6 @@ public interface IShogiApi
Task<ReadSessionsPlayerCountResponse?> GetSessionsPlayerCount(); Task<ReadSessionsPlayerCountResponse?> GetSessionsPlayerCount();
Task<CreateTokenResponse?> GetToken(); Task<CreateTokenResponse?> GetToken();
Task GuestLogout(); Task GuestLogout();
Task PostMove(string sessionName, MovePieceCommand move); Task Move(string sessionName, MovePieceCommand move);
Task<HttpStatusCode> PostSession(string name, bool isPrivate); Task<HttpStatusCode> PostSession(string name, bool isPrivate);
} }

View File

@@ -7,74 +7,74 @@ using System.Text.Json;
namespace Shogi.UI.Pages.Home.Api namespace Shogi.UI.Pages.Home.Api
{ {
public class ShogiApi : IShogiApi public class ShogiApi : IShogiApi
{ {
public const string GuestClientName = "Guest"; public const string GuestClientName = "Guest";
public const string MsalClientName = "Msal"; public const string MsalClientName = "Msal";
public const string AnonymouseClientName = "Anonymous"; public const string AnonymouseClientName = "Anonymous";
private readonly JsonSerializerOptions serializerOptions; private readonly JsonSerializerOptions serializerOptions;
private readonly IHttpClientFactory clientFactory; private readonly IHttpClientFactory clientFactory;
private readonly AccountState accountState; private readonly AccountState accountState;
public ShogiApi(IHttpClientFactory clientFactory, AccountState accountState) public ShogiApi(IHttpClientFactory clientFactory, AccountState accountState)
{ {
serializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web); serializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web);
this.clientFactory = clientFactory; this.clientFactory = clientFactory;
this.accountState = accountState; this.accountState = accountState;
} }
private HttpClient HttpClient => accountState.User?.WhichAccountPlatform switch private HttpClient HttpClient => accountState.User?.WhichAccountPlatform switch
{ {
WhichAccountPlatform.Guest => clientFactory.CreateClient(GuestClientName), WhichAccountPlatform.Guest => clientFactory.CreateClient(GuestClientName),
WhichAccountPlatform.Microsoft => clientFactory.CreateClient(MsalClientName), WhichAccountPlatform.Microsoft => clientFactory.CreateClient(MsalClientName),
_ => clientFactory.CreateClient(AnonymouseClientName) _ => clientFactory.CreateClient(AnonymouseClientName)
}; };
public async Task GuestLogout() public async Task GuestLogout()
{ {
var response = await HttpClient.PutAsync(new Uri("User/GuestLogout", UriKind.Relative), null); var response = await HttpClient.PutAsync(new Uri("User/GuestLogout", UriKind.Relative), null);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
} }
public async Task<Session?> GetSession(string name) public async Task<Session?> GetSession(string name)
{ {
var response = await HttpClient.GetAsync(new Uri($"Sessions/{name}", UriKind.Relative)); var response = await HttpClient.GetAsync(new Uri($"Sessions/{name}", UriKind.Relative));
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {
return (await response.Content.ReadFromJsonAsync<ReadSessionResponse>(serializerOptions))?.Session; return (await response.Content.ReadFromJsonAsync<ReadSessionResponse>(serializerOptions))?.Session;
} }
return null; return null;
} }
public async Task<ReadSessionsPlayerCountResponse?> GetSessionsPlayerCount() public async Task<ReadSessionsPlayerCountResponse?> GetSessionsPlayerCount()
{ {
var response = await HttpClient.GetAsync(new Uri("Sessions/PlayerCount", UriKind.Relative)); var response = await HttpClient.GetAsync(new Uri("Sessions/PlayerCount", UriKind.Relative));
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {
return await response.Content.ReadFromJsonAsync<ReadSessionsPlayerCountResponse>(serializerOptions); return await response.Content.ReadFromJsonAsync<ReadSessionsPlayerCountResponse>(serializerOptions);
} }
return null; return null;
} }
public async Task<CreateTokenResponse?> GetToken() public async Task<CreateTokenResponse?> GetToken()
{ {
var response = await HttpClient.GetFromJsonAsync<CreateTokenResponse>(new Uri("User/Token", UriKind.Relative), serializerOptions); var response = await HttpClient.GetFromJsonAsync<CreateTokenResponse>(new Uri("User/Token", UriKind.Relative), serializerOptions);
return response; return response;
} }
public async Task PostMove(string sessionName, MovePieceCommand command) public async Task Move(string sessionName, MovePieceCommand command)
{ {
await this.HttpClient.PostAsJsonAsync($"Sessions{sessionName}/Move", command); await this.HttpClient.PatchAsync($"Sessions/{sessionName}/Move", JsonContent.Create(command));
} }
public async Task<HttpStatusCode> PostSession(string name, bool isPrivate) public async Task<HttpStatusCode> PostSession(string name, bool isPrivate)
{ {
var response = await HttpClient.PostAsJsonAsync(new Uri("Sessions", UriKind.Relative), new CreateSessionCommand var response = await HttpClient.PostAsJsonAsync(new Uri("Sessions", UriKind.Relative), new CreateSessionCommand
{ {
Name = name, Name = name,
}); });
return response.StatusCode; return response.StatusCode;
} }
} }
} }

View File

@@ -15,10 +15,10 @@
var position = $"{file}{rank}"; var position = $"{file}{rank}";
var piece = session?.BoardState.Board[position]; var piece = session?.BoardState.Board[position];
<div class="tile" <div class="tile"
data-position="@(position)" data-position="@(position)"
data-selected="@(piece != null && selectedPosition == position)" data-selected="@(piece != null && selectedPosition == position)"
style="grid-area: @(position)" style="grid-area: @(position)"
@onclick="() => OnClickTile(piece, position)"> @onclick="() => OnClickTile(piece, position)">
<GamePiece Piece="piece" Perspective="Perspective" /> <GamePiece Piece="piece" Perspective="Perspective" />
</div> </div>
} }
@@ -46,7 +46,7 @@
<span>I</span> <span>I</span>
</div> </div>
<!-- Promote prompt --> <!-- Promote prompt -->
<div class="promote-prompt"> <div class="promote-prompt" data-visible="@PromotePrompt.IsVisible">
<p>Do you wish to promote?</p> <p>Do you wish to promote?</p>
<div> <div>
<button type="button">Yes</button> <button type="button">Yes</button>
@@ -114,6 +114,7 @@
: this.session.BoardState.Player2Hand; : this.session.BoardState.Player2Hand;
} }
} }
bool IsMyTurn => session?.BoardState.WhoseTurn == Perspective;
string? selectedPosition; string? selectedPosition;
WhichPiece? selectedPiece; WhichPiece? selectedPiece;
@@ -138,21 +139,24 @@
} }
return false; return false;
} }
async void OnClickTile(Piece? piece, string position) async void OnClickTile(Piece? piece, string position)
{ {
if (SessionName == null) return; if (SessionName == null || !IsMyTurn) return;
if (selectedPosition == null) if (selectedPosition == null || piece?.Owner == Perspective)
{ {
// Select a position.
selectedPosition = position; selectedPosition = position;
return; return;
} }
else if (selectedPosition == position) if (selectedPosition == position)
{ {
// Deselect the selected position.
selectedPosition = null; selectedPosition = null;
return; return;
} }
else if (piece != null) if (piece == null)
{ {
if (ShouldPromptForPromotion(position) || ShouldPromptForPromotion(selectedPosition)) if (ShouldPromptForPromotion(position) || ShouldPromptForPromotion(selectedPosition))
{ {
@@ -164,7 +168,7 @@
} }
else else
{ {
await ShogiApi.PostMove(SessionName, new MovePieceCommand await ShogiApi.Move(SessionName, new MovePieceCommand
{ {
From = selectedPosition, From = selectedPosition,
IsPromotion = false, IsPromotion = false,
@@ -173,6 +177,7 @@
} }
} }
} }
void OnClickHand(Piece piece) void OnClickHand(Piece piece)
{ {
selectedPiece = piece.WhichPiece; selectedPiece = piece.WhichPiece;

View File

@@ -15,6 +15,7 @@
} }
.board { .board {
position: relative;
display: grid; display: grid;
grid-template-areas: grid-template-areas:
"rank A9 B9 C9 D9 E9 F9 G9 H9 I9" "rank A9 B9 C9 D9 E9 F9 G9 H9 I9"
@@ -59,9 +60,10 @@
overflow: hidden; /* Because SVGs are shaped weird */ overflow: hidden; /* Because SVGs are shaped weird */
transition: filter linear 0.25s; transition: filter linear 0.25s;
} }
.tile[data-selected] {
filter: invert(0.8); .tile[data-selected] {
} filter: invert(0.8);
}
.ruler { .ruler {
color: beige; color: beige;
@@ -91,3 +93,20 @@
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
.promote-prompt {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 2px solid #444;
background-color: #eaeaea;
padding: 1rem;
box-shadow: 1px 1px 1px #444;
text-align: center;
}
.promote-prompt[data-visible="true"] {
display: block;
}

View File

@@ -42,7 +42,7 @@ public class PromotePrompt
if (command != null && sessionName != null) if (command != null && sessionName != null)
{ {
command.IsPromotion = false; command.IsPromotion = false;
return shogiApi.PostMove(sessionName, command); return shogiApi.Move(sessionName, command);
} }
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -51,7 +51,7 @@ public class PromotePrompt
if (command != null && sessionName != null) if (command != null && sessionName != null)
{ {
command.IsPromotion = true; command.IsPromotion = true;
return shogiApi.PostMove(sessionName, command); return shogiApi.Move(sessionName, command);
} }
return Task.CompletedTask; return Task.CompletedTask;
} }

View File

@@ -9,6 +9,6 @@
protected override void OnInitialized() protected override void OnInitialized()
{ {
//ModalService.ShowLoginModal(); //ModalService.ShowLoginModal();
//Navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}"); Navigation.NavigateTo($"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}");
} }
} }

View File

@@ -277,11 +277,11 @@ public class AcceptanceTests : IClassFixture<GuestTestFixture>
// Assert // Assert
var session = (await ReadTestSession()).Session; var session = (await ReadTestSession()).Session;
session.BoardState.Board["A2"].Should().BeNull(); session.BoardState.Board["A3"].Should().BeNull();
session.BoardState.Board["A3"].Should().NotBeNull(); session.BoardState.Board["A4"].Should().NotBeNull();
session.BoardState.Board["A3"]!.IsPromoted.Should().BeFalse(); session.BoardState.Board["A4"]!.IsPromoted.Should().BeFalse();
session.BoardState.Board["A3"]!.Owner.Should().Be(WhichPlayer.Player1); session.BoardState.Board["A4"]!.Owner.Should().Be(WhichPlayer.Player1);
session.BoardState.Board["A3"]!.WhichPiece.Should().Be(WhichPiece.Pawn); session.BoardState.Board["A4"]!.WhichPiece.Should().Be(WhichPiece.Pawn);
} }
finally finally
{ {