Add publication settings for clubs and sessions, read-only public club/session pages, dashboard controls, privacy-focused public queries, docs, and tests. Bump version to 3.3.0
This commit is contained in:
@@ -73,7 +73,7 @@
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="nav-version">v3.2.0</div>
|
||||
<div class="nav-version">v3.3.0</div>
|
||||
</div>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<div class="public-shell">
|
||||
<header class="public-topbar">
|
||||
<a class="public-brand" href="/">
|
||||
<img src="/logo.png" alt="GM-Relay" />
|
||||
<span>GM-Relay</span>
|
||||
</a>
|
||||
<a class="btn-gm btn-gm-outline" href="/login">Войти</a>
|
||||
</header>
|
||||
|
||||
<main class="public-content">
|
||||
@Body
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui" data-nosnippet>
|
||||
Произошла непредвиденная ошибка.
|
||||
<a href="." class="reload">Перезагрузить</a>
|
||||
<span class="dismiss">×</span>
|
||||
</div>
|
||||
@@ -72,6 +72,58 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (publicSettings is not null)
|
||||
{
|
||||
<div class="glass-card animate-slide-up public-settings-panel" style="margin-bottom: 1rem;">
|
||||
<div class="batch-bulk-header">
|
||||
<div>
|
||||
<h3>Публичная страница клуба</h3>
|
||||
<p>@publicSettings.PublicSessionCount опубликованных игр без состава игроков и приватных ссылок</p>
|
||||
</div>
|
||||
<span class="status-badge @(publicSettings.PublicScheduleEnabled ? "status-success" : "status-neutral")">
|
||||
@(publicSettings.PublicScheduleEnabled ? "Включена" : "Выключена")
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<EditForm Model="@publicSettingsModel" OnValidSubmit="SavePublicSettings">
|
||||
<div class="batch-bulk-fields">
|
||||
<div class="gm-form-group public-toggle-field">
|
||||
<label class="gm-checkbox-label">
|
||||
<InputCheckbox @bind-Value="publicSettingsModel.PublicScheduleEnabled" />
|
||||
<span>Включить публичное расписание</span>
|
||||
</label>
|
||||
<div class="gm-form-hint">Если выключено, публичная страница и ссылки на сессии недоступны.</div>
|
||||
</div>
|
||||
<div class="gm-form-group">
|
||||
<label class="gm-form-label">Короткий адрес</label>
|
||||
<InputText @bind-Value="publicSettingsModel.PublicSlug" class="gm-form-control" />
|
||||
<div class="gm-form-hint">Латиница, цифры и дефисы, например `night-city-club`.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="public-settings-actions">
|
||||
<button type="submit" class="btn-gm btn-gm-primary" disabled="@savingPublicSettings">
|
||||
@(savingPublicSettings ? "Сохраняем..." : "Сохранить публикацию")
|
||||
</button>
|
||||
@if (PublicClubUrl is not null && publicSettings.PublicScheduleEnabled)
|
||||
{
|
||||
<a href="@PublicClubUrl" target="_blank" rel="noopener noreferrer" class="btn-gm btn-gm-outline">
|
||||
Открыть публичную страницу
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</EditForm>
|
||||
|
||||
@if (PublicClubUrl is not null && publicSettings.PublicScheduleEnabled)
|
||||
{
|
||||
<div class="public-link-row">
|
||||
<span>Ссылка клуба</span>
|
||||
<a href="@PublicClubUrl" target="_blank" rel="noopener noreferrer">@PublicClubUrl</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
<div class="gm-alert gm-alert-danger" style="margin-bottom: 1rem;">
|
||||
@@ -201,6 +253,17 @@
|
||||
</button>
|
||||
</EditForm>
|
||||
|
||||
<div class="batch-publish-row">
|
||||
<span class="status-badge @(batch.AllSessionsPublic ? "status-success" : batch.PublicSessionCount > 0 ? "status-warning" : "status-neutral")">
|
||||
@FormatBatchPublication(batch)
|
||||
</span>
|
||||
<button type="button" class="btn-gm btn-gm-outline" disabled="@IsBatchPublishBusy(batch)" @onclick="() => SetBatchPublic(batch, !batch.AllSessionsPublic)">
|
||||
@(IsBatchPublishBusy(batch)
|
||||
? "Обновляем..."
|
||||
: batch.AllSessionsPublic ? "Скрыть batch" : "Опубликовать batch")
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="batch-clone-row">
|
||||
<select @bind="batch.CloneInterval" class="gm-form-control">
|
||||
<option value="week">Следующая неделя</option>
|
||||
@@ -249,6 +312,16 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="session-table-actions">
|
||||
<span class="status-badge @GetPublicationStatusClass(session)">@FormatPublicationStatus(session)</span>
|
||||
<button type="button" class="btn-gm btn-gm-outline" disabled="@(publishingSessionId == session.Id)" @onclick="() => SetSessionPublic(session.Id, !session.IsPublic)">
|
||||
@(publishingSessionId == session.Id
|
||||
? "Обновляем..."
|
||||
: session.IsPublic ? "Скрыть" : "Опубликовать")
|
||||
</button>
|
||||
@if (session.IsPublic && publicSettings?.PublicScheduleEnabled == true)
|
||||
{
|
||||
<a href="@PublicSessionUrl(session.Id)" target="_blank" rel="noopener noreferrer" class="btn-gm btn-gm-outline">Публичная ссылка</a>
|
||||
}
|
||||
<a href="/session/edit/@session.Id" class="btn-gm btn-gm-outline">
|
||||
✏️ Изменить
|
||||
</a>
|
||||
@@ -337,6 +410,15 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="session-card-actions">
|
||||
<button type="button" class="btn-gm btn-gm-outline" style="flex: 1; justify-content: center; font-size: 0.8125rem; padding: 0.5rem;" disabled="@(publishingSessionId == session.Id)" @onclick="() => SetSessionPublic(session.Id, !session.IsPublic)">
|
||||
@(publishingSessionId == session.Id
|
||||
? "Обновляем..."
|
||||
: session.IsPublic ? "Скрыть" : "Опубликовать")
|
||||
</button>
|
||||
@if (session.IsPublic && publicSettings?.PublicScheduleEnabled == true)
|
||||
{
|
||||
<a href="@PublicSessionUrl(session.Id)" target="_blank" rel="noopener noreferrer" class="btn-gm btn-gm-outline" style="flex: 1; justify-content: center; font-size: 0.8125rem; padding: 0.5rem;">Публичная ссылка</a>
|
||||
}
|
||||
<a href="/session/edit/@session.Id" class="btn-gm btn-gm-outline" style="flex: 1; justify-content: center; font-size: 0.8125rem; padding: 0.5rem;">
|
||||
✏️ Изменить
|
||||
</a>
|
||||
@@ -398,18 +480,23 @@
|
||||
private List<WebSession>? sessions;
|
||||
private List<WebCampaignTemplate>? campaignTemplates;
|
||||
private WebGroupManagement? groupManagement;
|
||||
private WebPublicGroupSettings? publicSettings;
|
||||
private List<BatchBulkEditModel> batchModels = [];
|
||||
private List<CampaignTemplateUsageModel> campaignTemplateModels = [];
|
||||
private Guid? promotingSessionId;
|
||||
private Guid? processingBatchId;
|
||||
private Guid? processingTemplateId;
|
||||
private Guid? publishingBatchId;
|
||||
private Guid? publishingSessionId;
|
||||
private string? removingCoGmId;
|
||||
private bool isAddingCoGm;
|
||||
private bool savingPublicSettings;
|
||||
private string? currentPlatform;
|
||||
private string? externalUserId;
|
||||
private string? errorMessage;
|
||||
private string? successMessage;
|
||||
private CoGmEditModel coGmModel = new();
|
||||
private PublicSettingsEditModel publicSettingsModel = new();
|
||||
private Dictionary<Guid, List<WebParticipant>> participantsCache = new();
|
||||
private HashSet<Guid> expandedSessions = new();
|
||||
private Guid? kickingParticipantId;
|
||||
@@ -444,6 +531,13 @@
|
||||
return;
|
||||
}
|
||||
|
||||
publicSettings = await SessionService.GetPublicGroupSettingsForCurrentUserAsync(GroupId);
|
||||
if (publicSettings is null)
|
||||
{
|
||||
Navigation.NavigateTo("/access-denied");
|
||||
return;
|
||||
}
|
||||
|
||||
campaignTemplates = await SessionService.GetCampaignTemplatesForCurrentUserAsync(GroupId);
|
||||
if (campaignTemplates is null)
|
||||
{
|
||||
@@ -453,6 +547,92 @@
|
||||
|
||||
RebuildBatchModels();
|
||||
RebuildCampaignTemplateModels();
|
||||
RebuildPublicSettingsModel();
|
||||
}
|
||||
|
||||
private async Task SavePublicSettings()
|
||||
{
|
||||
errorMessage = null;
|
||||
successMessage = null;
|
||||
savingPublicSettings = true;
|
||||
|
||||
try
|
||||
{
|
||||
await SessionService.UpdatePublicGroupSettingsForCurrentUserAsync(
|
||||
GroupId,
|
||||
publicSettingsModel.PublicSlug,
|
||||
publicSettingsModel.PublicScheduleEnabled);
|
||||
successMessage = "Настройки публичной страницы обновлены.";
|
||||
await LoadSessions();
|
||||
}
|
||||
catch (SessionAccessDeniedException)
|
||||
{
|
||||
Navigation.NavigateTo("/access-denied");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorMessage = "Не удалось обновить публичную страницу: " + ex.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
savingPublicSettings = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SetBatchPublic(BatchBulkEditModel batch, bool isPublic)
|
||||
{
|
||||
errorMessage = null;
|
||||
successMessage = null;
|
||||
publishingBatchId = batch.BatchId;
|
||||
|
||||
try
|
||||
{
|
||||
await SessionService.SetBatchPublicForCurrentUserAsync(batch.BatchId, isPublic);
|
||||
successMessage = isPublic
|
||||
? "Batch опубликован в публичном расписании."
|
||||
: "Batch скрыт из публичного расписания.";
|
||||
await LoadSessions();
|
||||
}
|
||||
catch (SessionAccessDeniedException)
|
||||
{
|
||||
Navigation.NavigateTo("/access-denied");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorMessage = "Не удалось обновить публичность batch: " + ex.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
publishingBatchId = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SetSessionPublic(Guid sessionId, bool isPublic)
|
||||
{
|
||||
errorMessage = null;
|
||||
successMessage = null;
|
||||
publishingSessionId = sessionId;
|
||||
|
||||
try
|
||||
{
|
||||
await SessionService.SetSessionPublicForCurrentUserAsync(sessionId, isPublic);
|
||||
successMessage = isPublic
|
||||
? "Сессия опубликована в публичном расписании."
|
||||
: "Сессия скрыта из публичного расписания.";
|
||||
await LoadSessions();
|
||||
}
|
||||
catch (SessionAccessDeniedException)
|
||||
{
|
||||
Navigation.NavigateTo("/access-denied");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorMessage = "Не удалось обновить публичность сессии: " + ex.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
publishingSessionId = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AddCoGm()
|
||||
@@ -806,7 +986,9 @@
|
||||
FirstScheduledAtLocal = firstSession.ScheduledAt.ToMoscow(),
|
||||
LastScheduledAtLocal = lastSession.ScheduledAt.ToMoscow(),
|
||||
IntervalDays = InferIntervalDays(orderedSessions),
|
||||
SessionCount = orderedSessions.Count
|
||||
SessionCount = orderedSessions.Count,
|
||||
PublicSessionCount = orderedSessions.Count(session => session.IsPublic),
|
||||
AllSessionsPublic = orderedSessions.All(session => session.IsPublic)
|
||||
};
|
||||
})
|
||||
.OrderBy(batch => batch.FirstScheduledAtLocal)
|
||||
@@ -834,6 +1016,20 @@
|
||||
.ToList() ?? [];
|
||||
}
|
||||
|
||||
private void RebuildPublicSettingsModel()
|
||||
{
|
||||
if (publicSettings is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
publicSettingsModel = new PublicSettingsEditModel
|
||||
{
|
||||
PublicScheduleEnabled = publicSettings.PublicScheduleEnabled,
|
||||
PublicSlug = publicSettings.PublicSlug ?? ""
|
||||
};
|
||||
}
|
||||
|
||||
private static bool ValidateBatchDetails(BatchBulkEditModel batch)
|
||||
{
|
||||
batch.Title = batch.Title.Trim();
|
||||
@@ -843,8 +1039,31 @@
|
||||
|
||||
private bool IsBatchBusy(BatchBulkEditModel batch) => processingBatchId == batch.BatchId;
|
||||
|
||||
private bool IsBatchPublishBusy(BatchBulkEditModel batch) => publishingBatchId == batch.BatchId;
|
||||
|
||||
private bool IsTemplateBusy(CampaignTemplateUsageModel template) => processingTemplateId == template.Id;
|
||||
|
||||
private string? PublicClubUrl =>
|
||||
string.IsNullOrWhiteSpace(publicSettings?.PublicSlug)
|
||||
? null
|
||||
: Navigation.ToAbsoluteUri($"/club/{publicSettings.PublicSlug}").ToString();
|
||||
|
||||
private string PublicSessionUrl(Guid sessionId) =>
|
||||
Navigation.ToAbsoluteUri($"/s/{sessionId}").ToString();
|
||||
|
||||
private static string FormatPublicationStatus(WebSession session) =>
|
||||
session.IsPublic ? "Опубликована" : "Скрыта";
|
||||
|
||||
private static string GetPublicationStatusClass(WebSession session) =>
|
||||
session.IsPublic ? "status-success" : "status-neutral";
|
||||
|
||||
private static string FormatBatchPublication(BatchBulkEditModel batch) =>
|
||||
batch.PublicSessionCount == 0
|
||||
? "Все игры скрыты"
|
||||
: batch.PublicSessionCount == batch.SessionCount
|
||||
? "Все игры опубликованы"
|
||||
: $"{batch.PublicSessionCount}/{batch.SessionCount} опубликовано";
|
||||
|
||||
private string CoGmPlatform =>
|
||||
string.IsNullOrWhiteSpace(groupManagement?.Group.Platform)
|
||||
? "Telegram"
|
||||
@@ -966,6 +1185,8 @@
|
||||
public DateTime LastScheduledAtLocal { get; init; } = DateTime.Now;
|
||||
public int IntervalDays { get; set; } = 7;
|
||||
public int SessionCount { get; init; }
|
||||
public int PublicSessionCount { get; init; }
|
||||
public bool AllSessionsPublic { get; init; }
|
||||
public string CloneInterval { get; set; } = "week";
|
||||
}
|
||||
|
||||
@@ -988,4 +1209,10 @@
|
||||
public string DisplayName { get; set; } = "";
|
||||
public string? ExternalUsername { get; set; }
|
||||
}
|
||||
|
||||
private sealed class PublicSettingsEditModel
|
||||
{
|
||||
public bool PublicScheduleEnabled { get; set; }
|
||||
public string? PublicSlug { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
@page "/club/{Slug}"
|
||||
@layout PublicLayout
|
||||
@inject ISessionStore SessionStore
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
<PageTitle>@PageTitleText</PageTitle>
|
||||
|
||||
@if (loaded && club is null)
|
||||
{
|
||||
<HeadContent>
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
</HeadContent>
|
||||
|
||||
<section class="public-hero public-hero-compact">
|
||||
<span class="status-badge status-neutral">Недоступно</span>
|
||||
<h1>Публичная страница не найдена</h1>
|
||||
<p>Расписание клуба выключено или адрес больше не используется.</p>
|
||||
</section>
|
||||
}
|
||||
else if (!loaded)
|
||||
{
|
||||
<section class="public-hero public-hero-compact">
|
||||
<div class="skeleton skeleton-text" style="width: 55%; height: 2rem;"></div>
|
||||
<div class="skeleton skeleton-text" style="width: 75%;"></div>
|
||||
</section>
|
||||
}
|
||||
else if (club is not null)
|
||||
{
|
||||
<HeadContent>
|
||||
<meta name="description" content="@($"Публичное расписание клуба {club.Name} в GM-Relay.")" />
|
||||
</HeadContent>
|
||||
|
||||
<section class="public-hero">
|
||||
<span class="status-badge status-success">Публичное расписание</span>
|
||||
<h1>@club.Name</h1>
|
||||
<p>Открытые игры клуба без состава игроков, личных данных и приватных ссылок.</p>
|
||||
<div class="public-share-row">
|
||||
<span>Ссылка клуба</span>
|
||||
<a href="@PublicClubUrl" target="_blank" rel="noopener noreferrer">@PublicClubUrl</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@if (club.Sessions.Count == 0)
|
||||
{
|
||||
<div class="glass-card public-empty-state">
|
||||
<h2>Опубликованных игр пока нет</h2>
|
||||
<p>Когда owner или co-GM откроет сессии для публичного расписания, они появятся здесь.</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="public-session-list">
|
||||
@foreach (var session in club.Sessions)
|
||||
{
|
||||
<article class="public-session-card">
|
||||
<div class="public-session-main">
|
||||
<span class="status-badge @GetStatusClass(session.Status)">@TranslateStatus(session.Status)</span>
|
||||
<h2>@session.Title</h2>
|
||||
<div class="public-session-meta">
|
||||
<span>@session.ScheduledAt.FormatMoscow()</span>
|
||||
<span>@FormatSeats(session)</span>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn-gm btn-gm-outline" href="@PublicSessionPath(session.Id)">Открыть</a>
|
||||
</article>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public string? Slug { get; set; }
|
||||
|
||||
private WebPublicClub? club;
|
||||
private bool loaded;
|
||||
|
||||
private string PageTitleText => club is null ? "Публичный клуб — GM-Relay" : $"{club.Name} — GM-Relay";
|
||||
|
||||
private string PublicClubUrl =>
|
||||
club is null
|
||||
? Navigation.ToAbsoluteUri($"/club/{Slug}").ToString()
|
||||
: Navigation.ToAbsoluteUri($"/club/{club.Slug}").ToString();
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
loaded = false;
|
||||
club = string.IsNullOrWhiteSpace(Slug)
|
||||
? null
|
||||
: await SessionStore.GetPublicClubBySlugAsync(Slug.Trim());
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
private string PublicSessionPath(Guid sessionId) => $"/s/{sessionId}";
|
||||
|
||||
private static string FormatSeats(WebPublicSession session)
|
||||
{
|
||||
var seats = session.MaxPlayers.HasValue
|
||||
? $"{session.ActivePlayerCount}/{session.MaxPlayers.Value}"
|
||||
: $"{session.ActivePlayerCount} игроков";
|
||||
|
||||
return session.WaitlistedPlayerCount > 0
|
||||
? $"{seats}, ожидание {session.WaitlistedPlayerCount}"
|
||||
: seats;
|
||||
}
|
||||
|
||||
private static string GetStatusClass(string status) => status switch
|
||||
{
|
||||
SessionStatus.Confirmed => "status-success",
|
||||
SessionStatus.ConfirmationSent => "status-warning",
|
||||
SessionStatus.Planned => "status-info",
|
||||
_ => "status-neutral"
|
||||
};
|
||||
|
||||
private static string TranslateStatus(string status) => status switch
|
||||
{
|
||||
SessionStatus.Planned => "Запланировано",
|
||||
SessionStatus.ConfirmationSent => "Ждем подтверждения",
|
||||
SessionStatus.Confirmed => "Подтверждено",
|
||||
_ => status
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
@page "/s/{SessionId:guid}"
|
||||
@layout PublicLayout
|
||||
@inject ISessionStore SessionStore
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
<PageTitle>@PageTitleText</PageTitle>
|
||||
|
||||
@if (loaded && session is null)
|
||||
{
|
||||
<HeadContent>
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
</HeadContent>
|
||||
|
||||
<section class="public-hero public-hero-compact">
|
||||
<span class="status-badge status-neutral">Недоступно</span>
|
||||
<h1>Сессия не опубликована</h1>
|
||||
<p>Эта игра скрыта, отменена, уже прошла или клуб выключил публичное расписание.</p>
|
||||
</section>
|
||||
}
|
||||
else if (!loaded)
|
||||
{
|
||||
<section class="public-hero public-hero-compact">
|
||||
<div class="skeleton skeleton-text" style="width: 55%; height: 2rem;"></div>
|
||||
<div class="skeleton skeleton-text" style="width: 75%;"></div>
|
||||
</section>
|
||||
}
|
||||
else if (session is not null)
|
||||
{
|
||||
<HeadContent>
|
||||
<meta name="description" content="@($"Публичная сессия {session.Title} клуба {session.GroupName} в GM-Relay.")" />
|
||||
</HeadContent>
|
||||
|
||||
<section class="public-hero public-hero-compact">
|
||||
<span class="status-badge @GetStatusClass(session.Status)">@TranslateStatus(session.Status)</span>
|
||||
<h1>@session.Title</h1>
|
||||
<p>@session.GroupName</p>
|
||||
</section>
|
||||
|
||||
<article class="glass-card public-session-detail">
|
||||
<div class="public-detail-grid">
|
||||
<div>
|
||||
<span>Время</span>
|
||||
<strong>@session.ScheduledAt.FormatMoscow()</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Места</span>
|
||||
<strong>@FormatSeats(session)</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Статус</span>
|
||||
<strong>@TranslateStatus(session.Status)</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="public-settings-actions">
|
||||
@if (!string.IsNullOrWhiteSpace(session.GroupSlug))
|
||||
{
|
||||
<a class="btn-gm btn-gm-primary" href="@($"/club/{session.GroupSlug}")">Расписание клуба</a>
|
||||
}
|
||||
<a class="btn-gm btn-gm-outline" href="@PublicSessionUrl" target="_blank" rel="noopener noreferrer">Ссылка на сессию</a>
|
||||
</div>
|
||||
</article>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public Guid SessionId { get; set; }
|
||||
|
||||
private WebPublicSession? session;
|
||||
private bool loaded;
|
||||
|
||||
private string PageTitleText => session is null ? "Публичная сессия — GM-Relay" : $"{session.Title} — GM-Relay";
|
||||
|
||||
private string PublicSessionUrl => Navigation.ToAbsoluteUri($"/s/{SessionId}").ToString();
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
loaded = false;
|
||||
session = await SessionStore.GetPublicSessionAsync(SessionId);
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
private static string FormatSeats(WebPublicSession session)
|
||||
{
|
||||
var seats = session.MaxPlayers.HasValue
|
||||
? $"{session.ActivePlayerCount}/{session.MaxPlayers.Value}"
|
||||
: $"{session.ActivePlayerCount} игроков";
|
||||
|
||||
return session.WaitlistedPlayerCount > 0
|
||||
? $"{seats}, ожидание {session.WaitlistedPlayerCount}"
|
||||
: seats;
|
||||
}
|
||||
|
||||
private static string GetStatusClass(string status) => status switch
|
||||
{
|
||||
SessionStatus.Confirmed => "status-success",
|
||||
SessionStatus.ConfirmationSent => "status-warning",
|
||||
SessionStatus.Planned => "status-info",
|
||||
_ => "status-neutral"
|
||||
};
|
||||
|
||||
private static string TranslateStatus(string status) => status switch
|
||||
{
|
||||
SessionStatus.Planned => "Запланировано",
|
||||
SessionStatus.ConfirmationSent => "Ждем подтверждения",
|
||||
SessionStatus.Confirmed => "Подтверждено",
|
||||
_ => status
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user