Ajoute le preremplissage du joueur connecte

This commit is contained in:
2026-04-15 22:40:12 +02:00
parent 0db95ee6ec
commit b2cbab7891
2 changed files with 161 additions and 4 deletions

View File

@@ -1,8 +1,15 @@
@page "/application" @page "/application"
@page "/application.html" @page "/application.html"
@using System.Net
@using System.Net.Http.Json
@using System.Security.Claims
@using ChessCubing.App.Models.Users
@implements IDisposable
@inject BrowserBridge Browser @inject BrowserBridge Browser
@inject MatchStore Store @inject MatchStore Store
@inject NavigationManager Navigation @inject NavigationManager Navigation
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject HttpClient Http
<PageTitle>ChessCubing Arena | Application</PageTitle> <PageTitle>ChessCubing Arena | Application</PageTitle>
<PageBody Page="setup" BodyClass="@SetupBodyClass" /> <PageBody Page="setup" BodyClass="@SetupBodyClass" />
@@ -137,13 +144,39 @@
</div> </div>
</fieldset> </fieldset>
<label class="field"> <label class="field player-name-field">
<span>Joueur blanc</span> <div class="field-heading">
<span class="field-label-text">Joueur blanc</span>
@if (CanUseConnectedPlayerName)
{
<button class="button ghost small icon-button player-fill-button"
type="button"
title="@BuildPrefillTitle("blanc")"
aria-label="@BuildPrefillTitle("blanc")"
@onclick="FillWhiteWithConnectedPlayer">
<span class="material-icons action-icon" aria-hidden="true">account_circle</span>
</button>
}
</div>
<input @bind="Form.WhiteName" @bind:event="oninput" name="whiteName" type="text" maxlength="40" placeholder="Blanc" /> <input @bind="Form.WhiteName" @bind:event="oninput" name="whiteName" type="text" maxlength="40" placeholder="Blanc" />
</label> </label>
<label class="field"> <label class="field player-name-field">
<span>Joueur noir</span> <div class="field-heading">
<span class="field-label-text">Joueur noir</span>
@if (CanUseConnectedPlayerName)
{
<button class="button ghost small icon-button player-fill-button"
type="button"
title="@BuildPrefillTitle("noir")"
aria-label="@BuildPrefillTitle("noir")"
@onclick="FillBlackWithConnectedPlayer">
<span class="material-icons action-icon" aria-hidden="true">account_circle</span>
</button>
}
</div>
<input @bind="Form.BlackName" @bind:event="oninput" name="blackName" type="text" maxlength="40" placeholder="Noir" /> <input @bind="Form.BlackName" @bind:event="oninput" name="blackName" type="text" maxlength="40" placeholder="Noir" />
</label> </label>
@@ -254,10 +287,12 @@
@code { @code {
private SetupFormModel Form { get; set; } = new(); private SetupFormModel Form { get; set; } = new();
private bool _ready; private bool _ready;
private string? ConnectedPlayerName;
private MatchState? CurrentMatch => Store.Current; private MatchState? CurrentMatch => Store.Current;
private string SetupBodyClass => UsesMoveLimit ? string.Empty : "time-setup-mode"; private string SetupBodyClass => UsesMoveLimit ? string.Empty : "time-setup-mode";
private bool CanUseConnectedPlayerName => !string.IsNullOrWhiteSpace(ConnectedPlayerName);
private bool UsesMoveLimit => MatchEngine.UsesMoveLimit(Form.Mode); private bool UsesMoveLimit => MatchEngine.UsesMoveLimit(Form.Mode);
@@ -286,6 +321,12 @@
? $"Quota actif : {CurrentPreset.Quota} coups par joueur." ? $"Quota actif : {CurrentPreset.Quota} coups par joueur."
: $"Quota actif : {CurrentPreset.Quota} coups par joueur et par Block."; : $"Quota actif : {CurrentPreset.Quota} coups par joueur et par Block.";
protected override async Task OnInitializedAsync()
{
AuthenticationStateProvider.AuthenticationStateChanged += HandleAuthenticationStateChanged;
await LoadConnectedPlayerAsync();
}
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
if (!firstRender) if (!firstRender)
@@ -298,6 +339,51 @@
StateHasChanged(); StateHasChanged();
} }
private void HandleAuthenticationStateChanged(Task<AuthenticationState> authenticationStateTask)
=> _ = InvokeAsync(LoadConnectedPlayerAsync);
private async Task LoadConnectedPlayerAsync()
{
string? fallbackName = null;
try
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
if (user.Identity?.IsAuthenticated != true)
{
ConnectedPlayerName = null;
await InvokeAsync(StateHasChanged);
return;
}
fallbackName = BuildConnectedPlayerFallback(user);
var response = await Http.GetAsync("api/users/me");
if (!response.IsSuccessStatusCode)
{
ConnectedPlayerName = response.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden
? null
: fallbackName;
await InvokeAsync(StateHasChanged);
return;
}
var profile = await response.Content.ReadFromJsonAsync<UserProfileResponse>();
ConnectedPlayerName = !string.IsNullOrWhiteSpace(profile?.DisplayName)
? profile.DisplayName
: fallbackName;
}
catch
{
ConnectedPlayerName = fallbackName;
}
await InvokeAsync(StateHasChanged);
}
private async Task HandleSubmit() private async Task HandleSubmit()
{ {
await Store.EnsureLoadedAsync(); await Store.EnsureLoadedAsync();
@@ -320,6 +406,26 @@
private void LoadDemo() private void LoadDemo()
=> Form = SetupFormModel.CreateDemo(); => Form = SetupFormModel.CreateDemo();
private void FillWhiteWithConnectedPlayer()
{
if (!CanUseConnectedPlayerName)
{
return;
}
Form.WhiteName = ConnectedPlayerName!;
}
private void FillBlackWithConnectedPlayer()
{
if (!CanUseConnectedPlayerName)
{
return;
}
Form.BlackName = ConnectedPlayerName!;
}
private void SetMode(string mode) private void SetMode(string mode)
=> Form.Mode = mode; => Form.Mode = mode;
@@ -360,4 +466,20 @@
return match.Phase == MatchEngine.PhaseCube ? "Page cube prete" : "Page chrono prete"; return match.Phase == MatchEngine.PhaseCube ? "Page cube prete" : "Page chrono prete";
} }
private string BuildPrefillTitle(string color)
=> $"Utiliser mon nom cote {color}";
private static string? BuildConnectedPlayerFallback(ClaimsPrincipal user)
=> FirstNonEmpty(
user.FindFirst("name")?.Value,
user.FindFirst(ClaimTypes.Name)?.Value,
user.FindFirst("preferred_username")?.Value,
user.FindFirst(ClaimTypes.Email)?.Value);
private static string? FirstNonEmpty(params string?[] candidates)
=> candidates.FirstOrDefault(candidate => !string.IsNullOrWhiteSpace(candidate));
public void Dispose()
=> AuthenticationStateProvider.AuthenticationStateChanged -= HandleAuthenticationStateChanged;
} }

View File

@@ -414,6 +414,17 @@ p {
gap: 0.45rem; gap: 0.45rem;
} }
.field-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.65rem;
}
.field-label-text {
font-weight: 700;
}
[hidden] { [hidden] {
display: none !important; display: none !important;
} }
@@ -567,6 +578,20 @@ textarea:focus {
gap: 0.75rem; gap: 0.75rem;
} }
.player-name-field .field-heading {
margin-bottom: -0.1rem;
}
.player-fill-button {
flex: 0 0 auto;
padding: 0.42rem 0.48rem;
border-radius: 12px;
}
.player-fill-button .action-icon {
font-size: 1.05rem;
}
.side-panel { .side-panel {
display: grid; display: grid;
gap: 1rem; gap: 1rem;
@@ -2380,11 +2405,16 @@ body.site-menu-hidden .site-menu-shell {
gap: 0.25rem; gap: 0.25rem;
} }
body[data-page="setup"] .field-label-text,
body[data-page="setup"] .field > span, body[data-page="setup"] .field > span,
body[data-page="setup"] legend { body[data-page="setup"] legend {
font-size: 0.9rem; font-size: 0.9rem;
} }
body[data-page="setup"] .player-fill-button {
padding: 0.36rem 0.42rem;
}
body[data-page="setup"] input, body[data-page="setup"] input,
body[data-page="setup"] textarea { body[data-page="setup"] textarea {
padding: 0.75rem 0.85rem; padding: 0.75rem 0.85rem;
@@ -2751,11 +2781,16 @@ body.site-menu-hidden .site-menu-shell {
gap: 0.45rem; gap: 0.45rem;
} }
body[data-page="setup"] .field-label-text,
body[data-page="setup"] .field > span, body[data-page="setup"] .field > span,
body[data-page="setup"] legend { body[data-page="setup"] legend {
font-size: 0.84rem; font-size: 0.84rem;
} }
body[data-page="setup"] .player-fill-button {
padding: 0.34rem 0.4rem;
}
body[data-page="setup"] input, body[data-page="setup"] input,
body[data-page="setup"] textarea { body[data-page="setup"] textarea {
padding: 0.68rem 0.75rem; padding: 0.68rem 0.75rem;