Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e2303490e9 | |||
| 9c1c6c2483 | |||
| c0c8f852d2 | |||
| ac6e2455a1 | |||
| 9374ff16ed | |||
| 17b92b25f4 | |||
| d2edbf16cc | |||
| b16627c2b6 | |||
| 4f7afb3bc9 | |||
| 5baf63e9ad | |||
| a0d9d1bc44 | |||
| f46f2bb5d3 | |||
| 46527fe761 |
@@ -6,7 +6,7 @@ on:
|
|||||||
- main
|
- main
|
||||||
|
|
||||||
env:
|
env:
|
||||||
VERSION: 1.10.0
|
VERSION: 1.12.0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# ЧАСТЬ 1: Собираем образы и кладем в Gitea (чтобы делиться с ребятами)
|
# ЧАСТЬ 1: Собираем образы и кладем в Gitea (чтобы делиться с ребятами)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>1.10.2</Version>
|
<Version>1.12.0</Version>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
|
|
||||||
bot:
|
bot:
|
||||||
image: git.codeanddice.ru/toutsu/gmrelay-bot:1.10.2
|
image: git.codeanddice.ru/toutsu/gmrelay-bot:1.12.0
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
@@ -30,7 +30,7 @@ services:
|
|||||||
- gmrelay
|
- gmrelay
|
||||||
|
|
||||||
web:
|
web:
|
||||||
image: git.codeanddice.ru/toutsu/gmrelay-web:1.10.2
|
image: git.codeanddice.ru/toutsu/gmrelay-web:1.12.0
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public sealed class CancelSessionHandler(
|
|||||||
|
|
||||||
// 3. Загружаем весь батч для перерисовки
|
// 3. Загружаем весь батч для перерисовки
|
||||||
var batchSessions = await connection.QueryAsync<SessionBatchDto>(
|
var batchSessions = await connection.QueryAsync<SessionBatchDto>(
|
||||||
@"SELECT id as SessionId, scheduled_at as ScheduledAt, status as Status, max_players as MaxPlayers
|
@"SELECT id as SessionId, scheduled_at as ScheduledAt, status as Status, max_players as MaxPlayers, join_link as JoinLink
|
||||||
FROM sessions
|
FROM sessions
|
||||||
WHERE batch_id = @BatchId
|
WHERE batch_id = @BatchId
|
||||||
ORDER BY scheduled_at",
|
ORDER BY scheduled_at",
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ public sealed class CreateSessionHandler(
|
|||||||
},
|
},
|
||||||
transaction);
|
transaction);
|
||||||
|
|
||||||
sessions.Add(new SessionBatchDto(sessionId, scheduledAt.UtcDateTime, SessionStatus.Planned, parseResult.MaxPlayers));
|
sessions.Add(new SessionBatchDto(sessionId, scheduledAt.UtcDateTime, SessionStatus.Planned, parseResult.MaxPlayers, link));
|
||||||
}
|
}
|
||||||
|
|
||||||
await transaction.CommitAsync(cancellationToken);
|
await transaction.CommitAsync(cancellationToken);
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ public sealed class JoinSessionHandler(
|
|||||||
|
|
||||||
// Загружаем весь батч для перерисовки
|
// Загружаем весь батч для перерисовки
|
||||||
var batchSessions = await connection.QueryAsync<SessionBatchDto>(
|
var batchSessions = await connection.QueryAsync<SessionBatchDto>(
|
||||||
@"SELECT id as SessionId, scheduled_at as ScheduledAt, status as Status, max_players as MaxPlayers
|
@"SELECT id as SessionId, scheduled_at as ScheduledAt, status as Status, max_players as MaxPlayers, join_link as JoinLink
|
||||||
FROM sessions
|
FROM sessions
|
||||||
WHERE batch_id = @BatchId
|
WHERE batch_id = @BatchId
|
||||||
ORDER BY scheduled_at",
|
ORDER BY scheduled_at",
|
||||||
|
|||||||
@@ -157,7 +157,8 @@ public sealed class LeaveSessionHandler(
|
|||||||
SELECT id AS SessionId,
|
SELECT id AS SessionId,
|
||||||
scheduled_at AS ScheduledAt,
|
scheduled_at AS ScheduledAt,
|
||||||
status AS Status,
|
status AS Status,
|
||||||
max_players AS MaxPlayers
|
max_players AS MaxPlayers,
|
||||||
|
join_link AS JoinLink
|
||||||
FROM sessions
|
FROM sessions
|
||||||
WHERE batch_id = @BatchId
|
WHERE batch_id = @BatchId
|
||||||
ORDER BY scheduled_at
|
ORDER BY scheduled_at
|
||||||
|
|||||||
@@ -137,7 +137,8 @@ public sealed class PromoteWaitlistedPlayerHandler(
|
|||||||
SELECT id AS SessionId,
|
SELECT id AS SessionId,
|
||||||
scheduled_at AS ScheduledAt,
|
scheduled_at AS ScheduledAt,
|
||||||
status AS Status,
|
status AS Status,
|
||||||
max_players AS MaxPlayers
|
max_players AS MaxPlayers,
|
||||||
|
join_link AS JoinLink
|
||||||
FROM sessions
|
FROM sessions
|
||||||
WHERE batch_id = @BatchId
|
WHERE batch_id = @BatchId
|
||||||
ORDER BY scheduled_at
|
ORDER BY scheduled_at
|
||||||
|
|||||||
+1
-1
@@ -357,7 +357,7 @@ public sealed class HandleRescheduleTimeInputHandler(
|
|||||||
await using var conn = await dataSource.OpenConnectionAsync(ct);
|
await using var conn = await dataSource.OpenConnectionAsync(ct);
|
||||||
|
|
||||||
var batchSessions = (await conn.QueryAsync<SessionBatchDto>(
|
var batchSessions = (await conn.QueryAsync<SessionBatchDto>(
|
||||||
"SELECT id AS SessionId, scheduled_at AS ScheduledAt, status AS Status, max_players AS MaxPlayers FROM sessions WHERE batch_id = @BatchId ORDER BY scheduled_at",
|
"SELECT id AS SessionId, scheduled_at AS ScheduledAt, status AS Status, max_players AS MaxPlayers, join_link AS JoinLink FROM sessions WHERE batch_id = @BatchId ORDER BY scheduled_at",
|
||||||
new { proposal.BatchId })).ToList();
|
new { proposal.BatchId })).ToList();
|
||||||
|
|
||||||
var batchParticipants = (await conn.QueryAsync<ParticipantBatchDto>(
|
var batchParticipants = (await conn.QueryAsync<ParticipantBatchDto>(
|
||||||
|
|||||||
+1
-1
@@ -286,7 +286,7 @@ public sealed class RescheduleVotingDeadlineService(
|
|||||||
await using var connection = await dataSource.OpenConnectionAsync(ct);
|
await using var connection = await dataSource.OpenConnectionAsync(ct);
|
||||||
|
|
||||||
var batchSessions = (await connection.QueryAsync<SessionBatchDto>(
|
var batchSessions = (await connection.QueryAsync<SessionBatchDto>(
|
||||||
"SELECT id AS SessionId, scheduled_at AS ScheduledAt, status AS Status, max_players AS MaxPlayers FROM sessions WHERE batch_id = @BatchId ORDER BY scheduled_at",
|
"SELECT id AS SessionId, scheduled_at AS ScheduledAt, status AS Status, max_players AS MaxPlayers, join_link AS JoinLink FROM sessions WHERE batch_id = @BatchId ORDER BY scheduled_at",
|
||||||
new { proposal.BatchId })).ToList();
|
new { proposal.BatchId })).ToList();
|
||||||
|
|
||||||
var batchParticipants = (await connection.QueryAsync<ParticipantBatchDto>(
|
var batchParticipants = (await connection.QueryAsync<ParticipantBatchDto>(
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ public static class TelegramSessionBatchRenderer
|
|||||||
? $"👥 Места: {session.ActivePlayerCount}/{session.MaxPlayers.Value}\n"
|
? $"👥 Места: {session.ActivePlayerCount}/{session.MaxPlayers.Value}\n"
|
||||||
: $"👥 Игроки ({session.ActivePlayerCount}):\n";
|
: $"👥 Игроки ({session.ActivePlayerCount}):\n";
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(session.JoinLink))
|
||||||
|
{
|
||||||
|
messageText += $"🔗 <a href=\"{System.Net.WebUtility.HtmlEncode(session.JoinLink)}\">Ссылка на игру</a>\n";
|
||||||
|
}
|
||||||
|
|
||||||
if (session.ActivePlayers.Count > 0)
|
if (session.ActivePlayers.Count > 0)
|
||||||
{
|
{
|
||||||
messageText += string.Join("\n", session.ActivePlayers.Select(p =>
|
messageText += string.Join("\n", session.ActivePlayers.Select(p =>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace GmRelay.Shared.Rendering;
|
namespace GmRelay.Shared.Rendering;
|
||||||
|
|
||||||
public sealed record SessionBatchDto(Guid SessionId, DateTime ScheduledAt, string Status, int? MaxPlayers);
|
public sealed record SessionBatchDto(Guid SessionId, DateTime ScheduledAt, string Status, int? MaxPlayers, string JoinLink);
|
||||||
public sealed record ParticipantBatchDto(Guid SessionId, string DisplayName, string? TelegramUsername, string RegistrationStatus);
|
public sealed record ParticipantBatchDto(Guid SessionId, string DisplayName, string? TelegramUsername, string RegistrationStatus);
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public static class SessionBatchViewBuilder
|
|||||||
session.ScheduledAt,
|
session.ScheduledAt,
|
||||||
session.Status,
|
session.Status,
|
||||||
session.MaxPlayers,
|
session.MaxPlayers,
|
||||||
|
session.JoinLink,
|
||||||
activePlayers.Count,
|
activePlayers.Count,
|
||||||
activePlayers,
|
activePlayers,
|
||||||
waitlistedPlayers,
|
waitlistedPlayers,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public sealed record SessionViewItem(
|
|||||||
DateTime ScheduledAt,
|
DateTime ScheduledAt,
|
||||||
string Status,
|
string Status,
|
||||||
int? MaxPlayers,
|
int? MaxPlayers,
|
||||||
|
string JoinLink,
|
||||||
int ActivePlayerCount,
|
int ActivePlayerCount,
|
||||||
IReadOnlyList<PlayerViewItem> ActivePlayers,
|
IReadOnlyList<PlayerViewItem> ActivePlayers,
|
||||||
IReadOnlyList<PlayerViewItem> WaitlistedPlayers,
|
IReadOnlyList<PlayerViewItem> WaitlistedPlayers,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<div class="nav-header">
|
<div class="nav-header">
|
||||||
<a class="nav-brand" href="">
|
<a class="nav-brand" href="">
|
||||||
<span class="nav-brand-icon">🐢</span>
|
<img src="logo.png" alt="GM-Relay" class="nav-brand-icon" />
|
||||||
<span class="nav-brand-text">GM-Relay</span>
|
<span class="nav-brand-text">GM-Relay</span>
|
||||||
</a>
|
</a>
|
||||||
<button class="nav-toggle" @onclick="ToggleMenu" aria-label="Переключить меню">
|
<button class="nav-toggle" @onclick="ToggleMenu" aria-label="Переключить меню">
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="nav-version">v1.10.2</div>
|
<div class="nav-version">v1.12.0</div>
|
||||||
</div>
|
</div>
|
||||||
</Authorized>
|
</Authorized>
|
||||||
<NotAuthorized>
|
<NotAuthorized>
|
||||||
@@ -79,4 +79,4 @@
|
|||||||
|
|
||||||
private void ToggleMenu() => isOpen = !isOpen;
|
private void ToggleMenu() => isOpen = !isOpen;
|
||||||
private void CloseMenu() => isOpen = false;
|
private void CloseMenu() => isOpen = false;
|
||||||
}
|
}
|
||||||
@@ -9,14 +9,17 @@
|
|||||||
|
|
||||||
.nav-brand {
|
.nav-brand {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: baseline;
|
||||||
gap: 0.625rem;
|
gap: 0.75rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-brand-icon {
|
.nav-brand-icon {
|
||||||
font-size: 1.5rem;
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
object-fit: contain;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-brand-text {
|
.nav-brand-text {
|
||||||
@@ -195,6 +198,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav-header {
|
.nav-header {
|
||||||
|
padding-left: 3.75rem;
|
||||||
padding-right: 0.75rem;
|
padding-right: 0.75rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<div class="login-page">
|
<div class="login-page">
|
||||||
<div class="login-card">
|
<div class="login-card">
|
||||||
<div class="login-logo">🎲</div>
|
<img src="logo.png" alt="GM-Relay" class="login-logo" />
|
||||||
<h1 class="login-title">GM-Relay</h1>
|
<h1 class="login-title">GM-Relay</h1>
|
||||||
<p class="login-subtitle">Войдите через Telegram для управления игровыми сессиями</p>
|
<p class="login-subtitle">Войдите через Telegram для управления игровыми сессиями</p>
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<div class="mini-app-page">
|
<div class="mini-app-page">
|
||||||
<div class="mini-app-auth-card" data-auth-status="@miniAppAuthStatus">
|
<div class="mini-app-auth-card" data-auth-status="@miniAppAuthStatus">
|
||||||
<div class="mini-app-logo">🎲</div>
|
<img src="logo.png" alt="GM-Relay" class="mini-app-logo" />
|
||||||
<h1>GM-Relay</h1>
|
<h1>GM-Relay</h1>
|
||||||
<p>@statusMessage</p>
|
<p>@statusMessage</p>
|
||||||
|
|
||||||
|
|||||||
@@ -938,7 +938,7 @@ public sealed class SessionService(
|
|||||||
},
|
},
|
||||||
transaction);
|
transaction);
|
||||||
|
|
||||||
renderedSessions.Add(new SessionBatchDto(sessionId, scheduledAt, SessionStatus.Planned, sourceSession.MaxPlayers));
|
renderedSessions.Add(new SessionBatchDto(sessionId, scheduledAt, SessionStatus.Planned, sourceSession.MaxPlayers, batchJoinLink));
|
||||||
}
|
}
|
||||||
|
|
||||||
await transaction.CommitAsync();
|
await transaction.CommitAsync();
|
||||||
@@ -1147,7 +1147,7 @@ public sealed class SessionService(
|
|||||||
},
|
},
|
||||||
transaction);
|
transaction);
|
||||||
|
|
||||||
renderedSessions.Add(new SessionBatchDto(sessionId, scheduledAt, SessionStatus.Planned, template.MaxPlayers));
|
renderedSessions.Add(new SessionBatchDto(sessionId, scheduledAt, SessionStatus.Planned, template.MaxPlayers, template.JoinLink));
|
||||||
}
|
}
|
||||||
|
|
||||||
await transaction.CommitAsync();
|
await transaction.CommitAsync();
|
||||||
@@ -1245,7 +1245,7 @@ public sealed class SessionService(
|
|||||||
await using var conn = await dataSource.OpenConnectionAsync();
|
await using var conn = await dataSource.OpenConnectionAsync();
|
||||||
|
|
||||||
var sessions = (await conn.QueryAsync<SessionBatchDto>(
|
var sessions = (await conn.QueryAsync<SessionBatchDto>(
|
||||||
"SELECT id AS SessionId, scheduled_at AS ScheduledAt, status AS Status, max_players AS MaxPlayers FROM sessions WHERE batch_id = @BatchId ORDER BY scheduled_at",
|
"SELECT id AS SessionId, scheduled_at AS ScheduledAt, status AS Status, max_players AS MaxPlayers, join_link AS JoinLink FROM sessions WHERE batch_id = @BatchId ORDER BY scheduled_at",
|
||||||
new { BatchId = batchId })).ToList();
|
new { BatchId = batchId })).ToList();
|
||||||
|
|
||||||
var participants = (await conn.QueryAsync<ParticipantBatchDto>(
|
var participants = (await conn.QueryAsync<ParticipantBatchDto>(
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ public static class TelegramSessionBatchRenderer
|
|||||||
? $"👥 Места: {session.ActivePlayerCount}/{session.MaxPlayers.Value}\n"
|
? $"👥 Места: {session.ActivePlayerCount}/{session.MaxPlayers.Value}\n"
|
||||||
: $"👥 Игроки ({session.ActivePlayerCount}):\n";
|
: $"👥 Игроки ({session.ActivePlayerCount}):\n";
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(session.JoinLink))
|
||||||
|
{
|
||||||
|
messageText += $"🔗 <a href=\"{System.Net.WebUtility.HtmlEncode(session.JoinLink)}\">Ссылка на игру</a>\n";
|
||||||
|
}
|
||||||
|
|
||||||
if (session.ActivePlayers.Count > 0)
|
if (session.ActivePlayers.Count > 0)
|
||||||
{
|
{
|
||||||
messageText += string.Join("\n", session.ActivePlayers.Select(p =>
|
messageText += string.Join("\n", session.ActivePlayers.Select(p =>
|
||||||
|
|||||||
@@ -877,8 +877,13 @@ select option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.login-logo {
|
.login-logo {
|
||||||
font-size: 2.5rem;
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
object-fit: contain;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-title {
|
.login-title {
|
||||||
@@ -919,8 +924,13 @@ select option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mini-app-logo {
|
.mini-app-logo {
|
||||||
font-size: 2.25rem;
|
width: 2.25rem;
|
||||||
|
height: 2.25rem;
|
||||||
|
object-fit: contain;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mini-app-auth-card h1 {
|
.mini-app-auth-card h1 {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 279 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 279 KiB |
@@ -147,6 +147,7 @@ public sealed class TelegramLandingPromisesSmokeTests
|
|||||||
string title,
|
string title,
|
||||||
IReadOnlyList<DateTimeOffset> scheduledTimes,
|
IReadOnlyList<DateTimeOffset> scheduledTimes,
|
||||||
int? maxPlayers,
|
int? maxPlayers,
|
||||||
|
string joinLink,
|
||||||
SessionNotificationMode notificationMode)
|
SessionNotificationMode notificationMode)
|
||||||
{
|
{
|
||||||
Title = title;
|
Title = title;
|
||||||
@@ -156,7 +157,8 @@ public sealed class TelegramLandingPromisesSmokeTests
|
|||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
scheduledAt.UtcDateTime,
|
scheduledAt.UtcDateTime,
|
||||||
SessionStatus.Planned,
|
SessionStatus.Planned,
|
||||||
maxPlayers))
|
maxPlayers,
|
||||||
|
joinLink))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,6 +175,7 @@ public sealed class TelegramLandingPromisesSmokeTests
|
|||||||
parseResult.Title!,
|
parseResult.Title!,
|
||||||
parseResult.ScheduledTimes,
|
parseResult.ScheduledTimes,
|
||||||
parseResult.MaxPlayers,
|
parseResult.MaxPlayers,
|
||||||
|
parseResult.Link!,
|
||||||
notificationMode);
|
notificationMode);
|
||||||
|
|
||||||
scenario.RenderBatch();
|
scenario.RenderBatch();
|
||||||
@@ -318,7 +321,8 @@ public sealed class TelegramLandingPromisesSmokeTests
|
|||||||
session.Id,
|
session.Id,
|
||||||
session.ScheduledAt,
|
session.ScheduledAt,
|
||||||
session.Status,
|
session.Status,
|
||||||
session.MaxPlayers))
|
session.MaxPlayers,
|
||||||
|
session.JoinLink))
|
||||||
.ToList(),
|
.ToList(),
|
||||||
participants
|
participants
|
||||||
.Select(participant => new ParticipantBatchDto(
|
.Select(participant => new ParticipantBatchDto(
|
||||||
@@ -371,7 +375,8 @@ public sealed class TelegramLandingPromisesSmokeTests
|
|||||||
Guid Id,
|
Guid Id,
|
||||||
DateTime ScheduledAt,
|
DateTime ScheduledAt,
|
||||||
string Status,
|
string Status,
|
||||||
int? MaxPlayers)
|
int? MaxPlayers,
|
||||||
|
string JoinLink)
|
||||||
{
|
{
|
||||||
public DateTime ScheduledAt { get; set; } = ScheduledAt;
|
public DateTime ScheduledAt { get; set; } = ScheduledAt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ public sealed class SessionBatchViewBuilderTests
|
|||||||
|
|
||||||
var sessions = new[]
|
var sessions = new[]
|
||||||
{
|
{
|
||||||
new SessionBatchDto(secondSessionId, new DateTime(2026, 4, 27, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Planned, 4),
|
new SessionBatchDto(secondSessionId, new DateTime(2026, 4, 27, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Planned, 4, "https://example.com/game"),
|
||||||
new SessionBatchDto(cancelledSessionId, new DateTime(2026, 4, 28, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Cancelled, null),
|
new SessionBatchDto(cancelledSessionId, new DateTime(2026, 4, 28, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Cancelled, null, ""),
|
||||||
new SessionBatchDto(firstSessionId, new DateTime(2026, 4, 26, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Planned, 2)
|
new SessionBatchDto(firstSessionId, new DateTime(2026, 4, 26, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Planned, 2, "https://example.com/game")
|
||||||
};
|
};
|
||||||
var participants = new[]
|
var participants = new[]
|
||||||
{
|
{
|
||||||
@@ -38,7 +38,7 @@ public sealed class SessionBatchViewBuilderTests
|
|||||||
public void Build_ShouldCalculatePlayerCounts()
|
public void Build_ShouldCalculatePlayerCounts()
|
||||||
{
|
{
|
||||||
var sessionId = Guid.NewGuid();
|
var sessionId = Guid.NewGuid();
|
||||||
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4) };
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "https://example.com/game") };
|
||||||
var participants = new[]
|
var participants = new[]
|
||||||
{
|
{
|
||||||
new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active),
|
new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active),
|
||||||
@@ -59,7 +59,7 @@ public sealed class SessionBatchViewBuilderTests
|
|||||||
public void Build_ShouldIncludeActionsForActiveSessions()
|
public void Build_ShouldIncludeActionsForActiveSessions()
|
||||||
{
|
{
|
||||||
var sessionId = Guid.NewGuid();
|
var sessionId = Guid.NewGuid();
|
||||||
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4) };
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "https://example.com/game") };
|
||||||
var participants = Array.Empty<ParticipantBatchDto>();
|
var participants = Array.Empty<ParticipantBatchDto>();
|
||||||
|
|
||||||
var result = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
var result = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
||||||
@@ -76,7 +76,7 @@ public sealed class SessionBatchViewBuilderTests
|
|||||||
public void Build_ShouldNotIncludeActionsForCancelledSessions()
|
public void Build_ShouldNotIncludeActionsForCancelledSessions()
|
||||||
{
|
{
|
||||||
var sessionId = Guid.NewGuid();
|
var sessionId = Guid.NewGuid();
|
||||||
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Cancelled, null) };
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Cancelled, null, "") };
|
||||||
var participants = Array.Empty<ParticipantBatchDto>();
|
var participants = Array.Empty<ParticipantBatchDto>();
|
||||||
|
|
||||||
var result = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
var result = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
||||||
@@ -88,7 +88,7 @@ public sealed class SessionBatchViewBuilderTests
|
|||||||
public void Build_ShouldMarkWaitlistActionWhenFull()
|
public void Build_ShouldMarkWaitlistActionWhenFull()
|
||||||
{
|
{
|
||||||
var sessionId = Guid.NewGuid();
|
var sessionId = Guid.NewGuid();
|
||||||
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 1) };
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 1, "https://example.com/game") };
|
||||||
var participants = new[] { new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active) };
|
var participants = new[] { new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active) };
|
||||||
|
|
||||||
var result = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
var result = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
||||||
@@ -101,7 +101,7 @@ public sealed class SessionBatchViewBuilderTests
|
|||||||
public void Build_ShouldIncludePlayerUsernames()
|
public void Build_ShouldIncludePlayerUsernames()
|
||||||
{
|
{
|
||||||
var sessionId = Guid.NewGuid();
|
var sessionId = Guid.NewGuid();
|
||||||
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, null) };
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, null, "https://example.com/game") };
|
||||||
var participants = new[] { new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active) };
|
var participants = new[] { new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active) };
|
||||||
|
|
||||||
var result = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
var result = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ public sealed class TelegramSessionBatchRendererTests
|
|||||||
|
|
||||||
var sessions = new[]
|
var sessions = new[]
|
||||||
{
|
{
|
||||||
new SessionBatchDto(secondSessionId, new DateTime(2026, 4, 27, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Planned, 4),
|
new SessionBatchDto(secondSessionId, new DateTime(2026, 4, 27, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Planned, 4, "https://example.com/game2"),
|
||||||
new SessionBatchDto(cancelledSessionId, new DateTime(2026, 4, 28, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Cancelled, null),
|
new SessionBatchDto(cancelledSessionId, new DateTime(2026, 4, 28, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Cancelled, null, ""),
|
||||||
new SessionBatchDto(firstSessionId, new DateTime(2026, 4, 26, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Planned, 2)
|
new SessionBatchDto(firstSessionId, new DateTime(2026, 4, 26, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Planned, 2, "https://example.com/game1")
|
||||||
};
|
};
|
||||||
var participants = new[]
|
var participants = new[]
|
||||||
{
|
{
|
||||||
@@ -35,6 +35,9 @@ public sealed class TelegramSessionBatchRendererTests
|
|||||||
Assert.Contains("Charlie", text);
|
Assert.Contains("Charlie", text);
|
||||||
Assert.Contains("Bob", text);
|
Assert.Contains("Bob", text);
|
||||||
Assert.Contains("Сессия отменена", text);
|
Assert.Contains("Сессия отменена", text);
|
||||||
|
Assert.Contains("Ссылка на игру", text);
|
||||||
|
Assert.Contains("https://example.com/game1", text);
|
||||||
|
Assert.Contains("https://example.com/game2", text);
|
||||||
|
|
||||||
var buttons = markup.InlineKeyboard.SelectMany(row => row).ToList();
|
var buttons = markup.InlineKeyboard.SelectMany(row => row).ToList();
|
||||||
Assert.Equal(4, buttons.Count); // 2 sessions x 2 buttons each
|
Assert.Equal(4, buttons.Count); // 2 sessions x 2 buttons each
|
||||||
@@ -51,7 +54,7 @@ public sealed class TelegramSessionBatchRendererTests
|
|||||||
public void Render_ShouldSkipButtonsForCancelledSessions()
|
public void Render_ShouldSkipButtonsForCancelledSessions()
|
||||||
{
|
{
|
||||||
var cancelledSessionId = Guid.NewGuid();
|
var cancelledSessionId = Guid.NewGuid();
|
||||||
var sessions = new[] { new SessionBatchDto(cancelledSessionId, DateTime.UtcNow, SessionStatus.Cancelled, null) };
|
var sessions = new[] { new SessionBatchDto(cancelledSessionId, DateTime.UtcNow, SessionStatus.Cancelled, null, "") };
|
||||||
var participants = Array.Empty<ParticipantBatchDto>();
|
var participants = Array.Empty<ParticipantBatchDto>();
|
||||||
|
|
||||||
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
||||||
@@ -64,7 +67,7 @@ public sealed class TelegramSessionBatchRendererTests
|
|||||||
public void Render_ShouldShowWaitlistButtonWhenFull()
|
public void Render_ShouldShowWaitlistButtonWhenFull()
|
||||||
{
|
{
|
||||||
var sessionId = Guid.NewGuid();
|
var sessionId = Guid.NewGuid();
|
||||||
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 1) };
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 1, "https://example.com/game") };
|
||||||
var participants = new[] { new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active) };
|
var participants = new[] { new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active) };
|
||||||
|
|
||||||
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
||||||
|
|||||||
@@ -244,6 +244,50 @@ public sealed class AuthorizedSessionServiceTests
|
|||||||
Assert.Empty(store.LogEntries);
|
Assert.Empty(store.LogEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetSessionHistoryForGmAsync_ReturnsHistory_WhenSessionBelongsToOwnedGroup()
|
||||||
|
{
|
||||||
|
var gmId = 1001L;
|
||||||
|
var groupId = Guid.NewGuid();
|
||||||
|
var sessionId = Guid.NewGuid();
|
||||||
|
var store = new FakeSessionStore(
|
||||||
|
groups:
|
||||||
|
[
|
||||||
|
new(groupId, 42, "Alpha", gmId)
|
||||||
|
],
|
||||||
|
sessions:
|
||||||
|
[
|
||||||
|
new(sessionId, groupId, "Session A", DateTime.UtcNow, "Planned", "https://example.test/a", Guid.NewGuid(), 10, 42, 4, 1, 0)
|
||||||
|
]);
|
||||||
|
var service = new AuthorizedSessionService(store);
|
||||||
|
|
||||||
|
var history = await service.GetSessionHistoryForGmAsync(sessionId, gmId);
|
||||||
|
|
||||||
|
Assert.NotNull(history);
|
||||||
|
Assert.Empty(history);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetSessionHistoryForGmAsync_ReturnsNull_WhenSessionBelongsToAnotherGm()
|
||||||
|
{
|
||||||
|
var groupId = Guid.NewGuid();
|
||||||
|
var sessionId = Guid.NewGuid();
|
||||||
|
var store = new FakeSessionStore(
|
||||||
|
groups:
|
||||||
|
[
|
||||||
|
new(groupId, 42, "Alpha", 2002L)
|
||||||
|
],
|
||||||
|
sessions:
|
||||||
|
[
|
||||||
|
new(sessionId, groupId, "Session A", DateTime.UtcNow, "Planned", "https://example.test/a", Guid.NewGuid(), 10, 42, 4, 1, 0)
|
||||||
|
]);
|
||||||
|
var service = new AuthorizedSessionService(store);
|
||||||
|
|
||||||
|
var history = await service.GetSessionHistoryForGmAsync(sessionId, 1001L);
|
||||||
|
|
||||||
|
Assert.Null(history);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task PromoteWaitlistedPlayerForGmAsync_PromotesOwnedSession()
|
public async Task PromoteWaitlistedPlayerForGmAsync_PromotesOwnedSession()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user