Better move error visibility
This commit is contained in:
@@ -87,28 +87,21 @@ public class ShogiApplication(
|
|||||||
return new ForbidResult();
|
return new ForbidResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
var moveResult = command.PieceFromHand.HasValue
|
||||||
|
? session.Board.Move(command.PieceFromHand.Value.ToDomain(), command.To)
|
||||||
|
: session.Board.Move(command.From!, command.To, command.IsPromotion ?? false);
|
||||||
|
|
||||||
|
if (moveResult.IsSuccess)
|
||||||
{
|
{
|
||||||
if (command.PieceFromHand.HasValue)
|
await sessionRepository.CreateMove(sessionId, command);
|
||||||
{
|
await gameHubContext.Emit_PieceMoved(sessionId);
|
||||||
session.Board.Move(command.PieceFromHand.Value.ToDomain(), command.To);
|
return new NoContentResult();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var isPromotion = command.IsPromotion ?? false;
|
return new ConflictObjectResult(moveResult.Reason);
|
||||||
session.Board.Move(command.From!, command.To, isPromotion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (InvalidOperationException e)
|
|
||||||
{
|
|
||||||
return new ConflictObjectResult(e.Message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await sessionRepository.CreateMove(sessionId, command);
|
|
||||||
|
|
||||||
await gameHubContext.Emit_PieceMoved(sessionId);
|
|
||||||
|
|
||||||
return new NoContentResult();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> JoinSession(string sessionId, string player2Id)
|
public async Task<IActionResult> JoinSession(string sessionId, string player2Id)
|
||||||
|
|||||||
@@ -31,13 +31,11 @@
|
|||||||
<h3>Capturing and the Hand</h3>
|
<h3>Capturing and the Hand</h3>
|
||||||
<h3>The King and "Check"</h3>
|
<h3>The King and "Check"</h3>
|
||||||
<h3>Victory</h3>
|
<h3>Victory</h3>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
private bool show = true;
|
||||||
private string activeSessionName = string.Empty;
|
private string activeSessionName = string.Empty;
|
||||||
|
|
||||||
private Task OnLoginChanged()
|
private Task OnLoginChanged()
|
||||||
{
|
{
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
@@ -48,4 +46,10 @@
|
|||||||
activeSessionName = s.SessionId.ToString();
|
activeSessionName = s.SessionId.ToString();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnClickClose()
|
||||||
|
{
|
||||||
|
show = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,6 @@
|
|||||||
|
|
||||||
@if (showPromotePrompt)
|
@if (showPromotePrompt)
|
||||||
{
|
{
|
||||||
<!-- Promote prompt -->
|
|
||||||
<!-- TODO: Add a background div which prevents mouse inputs to the board while this decision is being made. -->
|
|
||||||
<section class="promote-prompt">
|
<section class="promote-prompt">
|
||||||
<p>Do you wish to promote?</p>
|
<p>Do you wish to promote?</p>
|
||||||
<div>
|
<div>
|
||||||
@@ -25,7 +23,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
@if (showError)
|
||||||
|
{
|
||||||
|
<div class="errorModal">
|
||||||
|
<TemporaryModal OnClickClose="HideError">
|
||||||
|
<p>That is not a valid move.</p>
|
||||||
|
</TemporaryModal>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</Stretch>
|
</Stretch>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
@@ -37,6 +42,7 @@
|
|||||||
private WhichPiece? selectedPieceFromHand;
|
private WhichPiece? selectedPieceFromHand;
|
||||||
private bool showPromotePrompt;
|
private bool showPromotePrompt;
|
||||||
private string? moveTo;
|
private string? moveTo;
|
||||||
|
private bool showError = false;
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
@@ -96,6 +102,7 @@
|
|||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
selectedPieceFromHand = null;
|
selectedPieceFromHand = null;
|
||||||
|
showError = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
@@ -119,9 +126,12 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var success = await ShogiApi.Move(Session.SessionId, new MovePieceCommand(selectedBoardPosition, position, false));
|
var success = await ShogiApi.Move(Session.SessionId, new MovePieceCommand(selectedBoardPosition, position, false));
|
||||||
|
Console.WriteLine("Success? {0}", success);
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
selectedBoardPosition = null;
|
selectedBoardPosition = null;
|
||||||
|
showError = true;
|
||||||
|
Console.WriteLine("Show error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
@@ -149,11 +159,16 @@
|
|||||||
{
|
{
|
||||||
if (selectedBoardPosition != null && moveTo != null)
|
if (selectedBoardPosition != null && moveTo != null)
|
||||||
{
|
{
|
||||||
await ShogiApi.Move(Session.SessionId, new MovePieceCommand(selectedBoardPosition, moveTo, shouldPromote));
|
showError = await ShogiApi.Move(Session.SessionId, new MovePieceCommand(selectedBoardPosition, moveTo, shouldPromote));
|
||||||
showPromotePrompt = false;
|
showPromotePrompt = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException("Unexpected scenario during OnClickPromotionChoice.");
|
throw new InvalidOperationException("Unexpected scenario during OnClickPromotionChoice.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HideError()
|
||||||
|
{
|
||||||
|
showError = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,12 @@
|
|||||||
box-shadow: 1px 1px 1px #444;
|
box-shadow: 1px 1px 1px #444;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
z-index: 101;
|
z-index: 101;
|
||||||
|
|
||||||
background-color: #444;
|
background-color: #444;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.errorModal {
|
||||||
|
position: absolute;
|
||||||
|
top: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
.SearchPage {
|
.SearchPage {
|
||||||
background-color: var(--contrast-color);
|
|
||||||
padding: 0 0.5rem;
|
padding: 0 0.5rem;
|
||||||
}
|
}
|
||||||
|
|||||||
39
Shogi.UI/Shared/TemporaryModal.razor
Normal file
39
Shogi.UI/Shared/TemporaryModal.razor
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
@using System.Timers
|
||||||
|
<div class="TemporaryModal PrimaryTheme ThemeVariant--Contrast">
|
||||||
|
<div class="content">
|
||||||
|
@ChildContent
|
||||||
|
</div>
|
||||||
|
<button class="close" @onclick="OnClickClose">X</button>
|
||||||
|
<div class="countdown" style="--timeToClose: @TimeToClose">
|
||||||
|
<div class="time-background">
|
||||||
|
<div class="time"></div>
|
||||||
|
</div>
|
||||||
|
<div class="time-helper-text">This message will close soon.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter][EditorRequired] public RenderFragment? ChildContent { get; set; }
|
||||||
|
[Parameter][EditorRequired] public EventCallback OnClickClose { get; set; }
|
||||||
|
|
||||||
|
[Parameter] public int SecondsUntilClose { get; set; } = 3;
|
||||||
|
|
||||||
|
private string TimeToClose => $"{SecondsUntilClose}s";
|
||||||
|
private System.Timers.Timer closingTimer = new System.Timers.Timer();
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
closingTimer = new System.Timers.Timer(TimeSpan.FromSeconds(SecondsUntilClose).TotalMilliseconds)
|
||||||
|
{
|
||||||
|
AutoReset = false
|
||||||
|
};
|
||||||
|
closingTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnTimerElapsed);
|
||||||
|
|
||||||
|
closingTimer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTimerElapsed(object? source, ElapsedEventArgs elapsedEventArgs)
|
||||||
|
{
|
||||||
|
OnClickClose.InvokeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
64
Shogi.UI/Shared/TemporaryModal.razor.css
Normal file
64
Shogi.UI/Shared/TemporaryModal.razor.css
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/* Grid layout */
|
||||||
|
.TemporaryModal {
|
||||||
|
--timeToClose: 3s;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
grid-template-rows: repeat(1fr, 3) auto;
|
||||||
|
grid-template-areas:
|
||||||
|
"content close"
|
||||||
|
"content ."
|
||||||
|
"countdown countdown"
|
||||||
|
"helper-text helper-text";
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
grid-area: content;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.close {
|
||||||
|
grid-area: close;
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown {
|
||||||
|
grid-area: countdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.time-helper-text {
|
||||||
|
grid-area: helper-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apperance */
|
||||||
|
|
||||||
|
.TemporaryModal {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown .time-background {
|
||||||
|
background-color: beige;
|
||||||
|
height: 1ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown .time {
|
||||||
|
background-color: red;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
animation: countdown var(--timeToClose) ease-in-out;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown .time-helper-text {
|
||||||
|
font-size: 70%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes countdown {
|
||||||
|
0% {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
width: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user