@page "/portfolio/{Slug}"
@layout PublicLayout
@inject IPortfolioStore PortfolioStore
@inject AuthorizedPortfolioService AuthorizedPortfolio
@inject NavigationManager Navigation
@inject AuthenticationStateProvider AuthStateProvider
@using GmRelay.Shared.Domain
@using GmRelay.Web.Services.Portfolio
@PageTitleText
@if (loaded && game is null)
{
Недоступно
Портфолио не найдено
Эта игра скрыта, ещё не опубликована или короткий адрес больше не используется.
}
else if (!loaded)
{
}
else if (game is not null)
{
@if (!string.IsNullOrWhiteSpace(game.CoverPath))
{
}
Завершено
@game.Title
Завершено @game.CompletedAt.ToLocalTime().FormatMoscow()
@if (!string.IsNullOrWhiteSpace(game.System))
{
@GetSystemDisplayName(game.System)
}
@if (!string.IsNullOrWhiteSpace(game.Format))
{
@TranslateFormat(game.Format)
}
@if (!string.IsNullOrWhiteSpace(game.Description))
{
Описание
@game.Description
}
@if (game.Masters.Count > 0)
{
}
@if (!string.IsNullOrWhiteSpace(game.ClubSlug) && !string.IsNullOrWhiteSpace(game.ClubName))
{
}
Отзывы игроков
@if (game.Reviews.Count == 0)
{
Пока нет одобренных отзывов.
}
else
{
}
Оставить отзыв
@switch (submissionState)
{
case PortfolioReviewSubmissionState.RequiresAuthentication:
Войдите, чтобы оставить отзыв об этом приключении.
break;
case PortfolioReviewSubmissionState.Ineligible:
Отзыв могут оставить только игроки, участвовавшие в этом приключении.
break;
case PortfolioReviewSubmissionState.AlreadySubmitted:
Отзыв отправлен на модерацию.
break;
case PortfolioReviewSubmissionState.Eligible:
@if (!string.IsNullOrWhiteSpace(submissionError))
{
@submissionError
}
break;
}
}
@code {
[Parameter] public string? Slug { get; set; }
private PublicPortfolioGame? game;
private PortfolioReviewSubmissionState submissionState = PortfolioReviewSubmissionState.RequiresAuthentication;
private ReviewFormModel reviewModel = new();
private string? submissionError;
private bool isSubmitting;
private bool loaded;
private string PageTitleText => game is null ? "Портфолио — GM-Relay" : $"{game.Title} — GM-Relay";
private string PublicPortfolioUrl => Navigation.ToAbsoluteUri($"/portfolio/{Slug}").ToString();
private string GetLoginUrl() => $"/login?returnUrl={Uri.EscapeDataString($"/portfolio/{Slug}")}";
protected override async Task OnParametersSetAsync()
{
loaded = false;
var trimmedSlug = string.IsNullOrWhiteSpace(Slug) ? null : Slug.Trim();
game = trimmedSlug is null
? null
: await PortfolioStore.GetPublicPortfolioGameBySlugAsync(trimmedSlug);
if (game is not null)
{
submissionState = await AuthorizedPortfolio.GetReviewSubmissionStateForCurrentUserAsync(game.Slug);
}
reviewModel = new ReviewFormModel();
submissionError = null;
isSubmitting = false;
loaded = true;
}
private async Task SubmitReviewAsync()
{
if (game is null)
{
return;
}
if (!reviewModel.PublicationConsent)
{
submissionError = "Нужно подтвердить согласие на публикацию.";
return;
}
if (string.IsNullOrWhiteSpace(reviewModel.Body) || reviewModel.Body.Trim().Length < 10)
{
submissionError = "Отзыв должен содержать не меньше 10 символов.";
return;
}
isSubmitting = true;
submissionError = null;
try
{
await AuthorizedPortfolio.SubmitReviewForCurrentUserAsync(
game.Slug,
reviewModel.Body,
reviewModel.PublicationConsent);
submissionState = PortfolioReviewSubmissionState.AlreadySubmitted;
reviewModel = new ReviewFormModel();
}
catch (Exception ex)
{
submissionError = ex.Message;
}
finally
{
isSubmitting = false;
}
}
private static string GetSystemDisplayName(string? system)
{
if (string.IsNullOrWhiteSpace(system))
return system ?? string.Empty;
if (Enum.TryParse(system, out var gs))
return gs.ToDisplayName();
return system;
}
private static string TranslateFormat(string format) => format switch
{
"Online" => "Онлайн",
"Offline" => "Офлайн",
"Hybrid" => "Гибрид",
_ => format
};
private sealed class ReviewFormModel
{
public string Body { get; set; } = string.Empty;
public bool PublicationConsent { get; set; }
}
}