fix: финальные правки ревью для issue #39
PR Checks / test-and-build (pull_request) Successful in 13m16s
PR Checks / test-and-build (pull_request) Successful in 13m16s
- PublicSession.razor: добавлена обработка ?register=1, AuthStateProvider, TryGetPlatformIdentity, кнопки записи для авторизованных/неавторизованных пользователей, отображение результата регистрации - CreateSessionHandler: добавлен cover_image_url в INSERT SQL - DiscordProjectStructureTests: версия 3.4.0 во всех проверках - README.md: актуальная версия v3.4.0 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
Проект разработан с упором на производительность, архитектуру Vertical Slice, Native AOT (для бота) и удобство развертывания с использованием .NET Aspire.
|
||||
|
||||
**Текущая версия:** `v3.3.0`.
|
||||
**Текущая версия:** `v3.4.0`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -118,8 +118,8 @@ public sealed class CreateSessionHandler(
|
||||
{
|
||||
var sessionId = await connection.ExecuteScalarAsync<Guid>(
|
||||
"""
|
||||
INSERT INTO sessions (batch_id, group_id, title, join_link, scheduled_at, status, max_players, system, description, format, duration_minutes, is_one_shot)
|
||||
VALUES (@BatchId, @GroupId, @Title, @Link, @ScheduledAt, @Status, @MaxPlayers, @System, @Description, @Format, @DurationMinutes, @IsOneShot)
|
||||
INSERT INTO sessions (batch_id, group_id, title, join_link, scheduled_at, status, max_players, system, description, format, duration_minutes, is_one_shot, cover_image_url)
|
||||
VALUES (@BatchId, @GroupId, @Title, @Link, @ScheduledAt, @Status, @MaxPlayers, @System, @Description, @Format, @DurationMinutes, @IsOneShot, @CoverImageUrl)
|
||||
RETURNING id;
|
||||
""",
|
||||
new
|
||||
@@ -135,7 +135,8 @@ public sealed class CreateSessionHandler(
|
||||
command.Description,
|
||||
command.Format,
|
||||
DurationMinutes = command.DurationMinutes,
|
||||
IsOneShot = command.IsOneShot
|
||||
IsOneShot = command.IsOneShot,
|
||||
CoverImageUrl = command.ImageReference
|
||||
},
|
||||
transaction);
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
@layout PublicLayout
|
||||
@inject ISessionStore SessionStore
|
||||
@inject NavigationManager Navigation
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
@using GmRelay.Shared.Features.Showcase
|
||||
@using GmRelay.Web.Services
|
||||
|
||||
<PageTitle>@PageTitleText</PageTitle>
|
||||
|
||||
@@ -87,6 +89,13 @@ else if (session is not null)
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (registrationResult is not null)
|
||||
{
|
||||
<div class="glass-card @GetRegistrationResultClass()">
|
||||
<p>@registrationResult</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="public-settings-actions">
|
||||
@if (!string.IsNullOrWhiteSpace(session.GroupSlug))
|
||||
{
|
||||
@@ -95,7 +104,14 @@ else if (session is not null)
|
||||
<a class="btn-gm btn-gm-outline" href="@PublicSessionUrl" target="_blank" rel="noopener noreferrer">Ссылка на сессию</a>
|
||||
@if (session.AllowDirectRegistration)
|
||||
{
|
||||
<a class="btn-gm btn-gm-primary" href="@($"/s/{SessionId}?register=1")">Записаться</a>
|
||||
@if (isAuthenticated)
|
||||
{
|
||||
<button class="btn-gm btn-gm-primary" @onclick="RegisterAsync">Записаться</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a class="btn-gm btn-gm-primary" href="@GetLoginUrl()">Войти, чтобы записаться</a>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</article>
|
||||
@@ -106,6 +122,8 @@ else if (session is not null)
|
||||
|
||||
private ShowcaseSessionDto? session;
|
||||
private bool loaded;
|
||||
private bool isAuthenticated;
|
||||
private string? registrationResult;
|
||||
|
||||
private string PageTitleText => session is null ? "Публичная сессия — GM-Relay" : $"{session.Title} — GM-Relay";
|
||||
|
||||
@@ -114,10 +132,47 @@ else if (session is not null)
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
loaded = false;
|
||||
registrationResult = null;
|
||||
session = await SessionStore.GetShowcaseSessionAsync(SessionId);
|
||||
|
||||
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||
isAuthenticated = authState.User.Identity?.IsAuthenticated ?? false;
|
||||
|
||||
if (session is not null && Navigation.Uri.Contains("register=1") && session.AllowDirectRegistration)
|
||||
{
|
||||
if (isAuthenticated && authState.User.TryGetPlatformIdentity(out var platform, out var externalUserId))
|
||||
{
|
||||
var success = await SessionStore.RegisterFromShowcaseAsync(SessionId, platform, externalUserId, authState.User.Identity?.Name ?? "Игрок");
|
||||
registrationResult = success
|
||||
? "Вы успешно записались на игру!"
|
||||
: "Не удалось записаться. Возможно, места закончились или вы уже зарегистрированы.";
|
||||
}
|
||||
else if (!isAuthenticated)
|
||||
{
|
||||
Navigation.NavigateTo($"/login?returnUrl={Uri.EscapeDataString($"/s/{SessionId}")}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
private async Task RegisterAsync()
|
||||
{
|
||||
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||
if (authState.User.TryGetPlatformIdentity(out var platform, out var externalUserId))
|
||||
{
|
||||
var success = await SessionStore.RegisterFromShowcaseAsync(SessionId, platform, externalUserId, authState.User.Identity?.Name ?? "Игрок");
|
||||
registrationResult = success
|
||||
? "Вы успешно записались на игру!"
|
||||
: "Не удалось записаться. Возможно, места закончились или вы уже зарегистрированы.";
|
||||
}
|
||||
}
|
||||
|
||||
private string GetLoginUrl() => $"/login?returnUrl={Uri.EscapeDataString($"/s/{SessionId}?register=1")}";
|
||||
|
||||
private string GetRegistrationResultClass() => registrationResult?.StartsWith("Вы успешно") == true ? "status-success-bg" : "status-warning-bg";
|
||||
|
||||
private static string FormatSeats(ShowcaseSessionDto session)
|
||||
{
|
||||
var seats = session.MaxPlayers.HasValue
|
||||
|
||||
@@ -62,7 +62,7 @@ public sealed class DiscordProjectStructureTests
|
||||
var prChecks = File.ReadAllText(Path.Combine(repoRoot, ".gitea", "workflows", "pr-checks.yml"));
|
||||
var deploy = File.ReadAllText(Path.Combine(repoRoot, ".gitea", "workflows", "deploy.yml"));
|
||||
|
||||
Assert.Contains("gmrelay-discord-bot:3.3.0", compose);
|
||||
Assert.Contains("gmrelay-discord-bot:3.4.0", compose);
|
||||
Assert.Contains("Discord__Token=${DISCORD_BOT_TOKEN:?Set DISCORD_BOT_TOKEN in .env}", compose);
|
||||
Assert.Contains("src/GmRelay.DiscordBot/Dockerfile", deploy);
|
||||
Assert.Contains("DISCORD_BOT_TOKEN", deploy);
|
||||
@@ -76,13 +76,13 @@ public sealed class DiscordProjectStructureTests
|
||||
{
|
||||
var repoRoot = GetRepoRoot();
|
||||
|
||||
Assert.Contains("<Version>3.3.0</Version>", File.ReadAllText(Path.Combine(repoRoot, "Directory.Build.props")));
|
||||
Assert.Contains("VERSION: 3.3.0", File.ReadAllText(Path.Combine(repoRoot, ".gitea", "workflows", "deploy.yml")));
|
||||
Assert.Contains("gmrelay-bot:3.3.0", File.ReadAllText(Path.Combine(repoRoot, "compose.yaml")));
|
||||
Assert.Contains("gmrelay-web:3.3.0", File.ReadAllText(Path.Combine(repoRoot, "compose.yaml")));
|
||||
Assert.Contains("gmrelay-discord-bot:3.3.0", File.ReadAllText(Path.Combine(repoRoot, "compose.yaml")));
|
||||
Assert.Contains("<Version>3.4.0</Version>", File.ReadAllText(Path.Combine(repoRoot, "Directory.Build.props")));
|
||||
Assert.Contains("VERSION: 3.4.0", File.ReadAllText(Path.Combine(repoRoot, ".gitea", "workflows", "deploy.yml")));
|
||||
Assert.Contains("gmrelay-bot:3.4.0", File.ReadAllText(Path.Combine(repoRoot, "compose.yaml")));
|
||||
Assert.Contains("gmrelay-web:3.4.0", File.ReadAllText(Path.Combine(repoRoot, "compose.yaml")));
|
||||
Assert.Contains("gmrelay-discord-bot:3.4.0", File.ReadAllText(Path.Combine(repoRoot, "compose.yaml")));
|
||||
Assert.Contains(
|
||||
"v3.3.0",
|
||||
"v3.4.0",
|
||||
File.ReadAllText(Path.Combine(repoRoot, "src", "GmRelay.Web", "Components", "Layout", "NavMenu.razor")));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user