Skip to content
Open

Dev #14

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d65930e
Updates
Dippys Feb 8, 2026
04631fc
Merge branch 'nitrodevco:main' into feature/messenger
Dippys Feb 8, 2026
8aee13b
Merge branch 'main' into feature/messenger
Dippys Feb 9, 2026
40a002b
feat(catalog): implement production-grade LTD raffle system
Diddyy Feb 8, 2026
c1d7cf8
refactor(LtdRaffleGrain): simplify dependency usage
Diddyy Feb 9, 2026
4a7f71f
feat(LtdRaffleGrain): enhance deactivation process and update loser h…
Diddyy Feb 9, 2026
82b37da
feat(messenger): align protocol with client and add session message c…
Diddyy Feb 9, 2026
0544c72
Review fixes: Orleans best practices, performance, and correctness
Diddyy Feb 9, 2026
0035e94
Remove unused _deliveredFlushTimer field
Diddyy Feb 9, 2026
0a6a3c7
Add Orleans grain development rules to AI context
Diddyy Feb 9, 2026
ee1891d
Add grain architecture principles to AI context
Diddyy Feb 9, 2026
345198b
feat(LtdRaffleGrain): add max entries per batch limit and optimize lo…
Diddyy Feb 9, 2026
0282c90
Merge pull request #8 from Dippys/feature/messenger
Diddyy Feb 10, 2026
cc932d6
Merge branch 'dev' into feature/ltd-raffle-system
Diddyy Feb 10, 2026
9390b99
Merge pull request #9 from Diddyy/feature/ltd-raffle-system
Diddyy Feb 10, 2026
f089ed1
fix(appsettings): add missing closing brace in Catalog configuration
Diddyy Feb 10, 2026
0ffa888
chore: run CSharpier on TurboDbContext and GrainFactoryExtensions
Diddyy Feb 10, 2026
c1a97b6
fixed messenger timestamp issues
Dippys Feb 10, 2026
06eab7a
format
Dippys Feb 10, 2026
03f29ef
Merge pull request #10 from Dippys/fix/messenger-timestamp
Diddyy Feb 10, 2026
f54ae88
fix .md files
Dippys Feb 10, 2026
ef53bd0
Merge pull request #11 from Dippys/dev
Diddyy Feb 10, 2026
1be1cc2
Fix Ignore to actually work
Dippys Feb 10, 2026
9789aa0
Merge branch 'nitrodevco:dev' into dev
Dippys Feb 10, 2026
cfda135
Merge pull request #12 from Dippys/dev
Diddyy Feb 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
"csharpier"
],
"rollForward": false
},
"dotnet-ef": {
"version": "9.0.8",
"commands": [
"dotnet-ef"
],
"rollForward": false
}
}
}
}
15 changes: 15 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ Include in every request:
- keep active-room indexing and keepalive behavior in `RoomDirectoryGrain`
- treat `[KeepAlive]` as explicit infrastructure-only usage

## Orleans grain constraints
- Never use bare `catch { }` — always `catch (Exception ex)` with `ILogger<T>`.
- Parallelize independent grain calls with `Task.WhenAll`, not sequential `await` in loops.
- Hoist repeated grain calls out of loops.
- Batch DB deletes with `WHERE ... IN (...)`, not per-entity `ExecuteDeleteAsync`.
- Use timer-flush for housekeeping writes (see `RoomPersistenceGrain`).
- No hardcoded limits in grains — pass from handlers via `IConfiguration`.
- Use tracked EF deletes when atomicity with inserts is required.
- Replace `.Ignore()` with a `LogAndForget` helper that logs faulted tasks.
- Cap in-memory per-event collections (message history, queues).
- One grain per responsibility — isolate heavy I/O into secondary grains (e.g. `RoomPersistenceGrain`).
- Use grain single-threading for concurrency safety (per-player `PurchaseGrain`, per-item `LimitedItemGrain`). No manual locks.
- Grains orchestrate their own outbound communication via `PlayerPresenceGrain.SendComposerAsync`. Callers do not send composers.
- All mutations to grain-owned data go through grain methods. No direct DB updates for grain-owned state.

## Task routing hints
- Handler task: use neighboring handler + `Turbo.Primitives/Orleans/GrainFactoryExtensions.cs`.
- Grain task: use grain interface + snapshot/state types as primary references.
Expand Down
76 changes: 70 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ Activate the relevant skill checklist before editing code in that domain:
- `grain-development`
- Trigger: editing files under `Turbo.*\\Grains\\**` or `Turbo.Primitives/**/Grains/*.cs`.
- Enforce: keep ownership boundaries, lifecycle rules, and snapshot/state coherence.
- Enforce: all rules in the **Orleans grain development rules** section below.
- `session-presence-routing`
- Trigger: touching session gateway, presence flow, room routing, outbound composer fan-out.
- Enforce: player outbound via `PlayerPresenceGrain.SendComposerAsync`; no direct handler socket sends.
- `message-contracts`
- Trigger: editing `Turbo.Primitives/Messages/Incoming/**` or outgoing composer payload mappings.
- Enforce: explicit mandatory fields, no placeholder payloads when source data exists.
- `revision-protocol` (cross-repo)
- `revision-protocol`
- Trigger: changes referencing `Revision<id>` packet mappings.
- Enforce: edit plugin revision tree in `../turbo-sample-plugin/TurboSamplePlugin/Revision/**`.
- Enforce: edit `Turbo.Revisions/Revision<id>/**` in `turbo-cloud`.

## Priority order
1. Build and quality checks in repo files (`Directory.Build.props`, `Directory.Build.targets`, `.editorconfig`)
Expand Down Expand Up @@ -64,12 +65,75 @@ Default output format:
- Prefer deterministic handlers/services with clear guard clauses.
- Preserve cancellation and async flow where it already exists.
- Handle failure paths explicitly; do not ship happy-path-only changes.
- Avoid dead code, unused allocations, and broad catch blocks that hide errors.
- Avoid dead code, unused allocations, and broad catch blocks that hide errors (see **Orleans grain development rules** for specifics).
- For revision compatibility work, prefer restoring/adding missing incoming message contracts in `Turbo.Primitives/Messages/Incoming/**` before mutating serializer/composer payload behavior.
- Do not alter serializer/composer behavior by replacing real payload writes with placeholder constants (for example, unconditional `WriteInteger(0)`) unless explicitly requested.
- If work references `Revision<id>` parsers/serializers, edit the plugin repo path:
- `../turbo-sample-plugin/TurboSamplePlugin/Revision/**`
- Do not hallucinate those trees into `turbo-cloud`.
- If work references `Revision<id>` parsers/serializers, edit `Turbo.Revisions/Revision<id>/**` in `turbo-cloud`.

## Orleans grain development rules
These rules exist because every one of these mistakes has shipped and caused real issues.

### Never swallow exceptions silently
Every bare `catch { }` hides a real bug path. Always use `catch (Exception ex)` and log it.
If a cross-grain notification fails silently, state goes asymmetric and nobody knows why.
- **Required**: inject `ILogger<T>` into every grain that does cross-grain calls or DB work.
- **Forbidden**: bare `catch { }`, `catch (Exception) { }` without logging.

### Parallelize independent grain calls
When checking status on N grains (e.g. online status for a friend list), do not `await` each one in a `foreach`.
Grain calls to different grains can run concurrently with `Task.WhenAll`.
- Sequential = O(n) round-trips. Parallel = O(1) wall time.
- Apply everywhere: activation hydration, search results, batch accept/deny.

### Do not repeat identical grain calls in loops
If a grain method calls its own player's `GetSummaryAsync` inside a loop, hoist the call before the loop.
Same result every iteration = wasted round-trips.

### Batch DB operations
Do not loop `ExecuteDeleteAsync` per entity. Use a single `WHERE ... IN (...)` query.
Same for composer fan-out: collect all updates, send once.

### Use timer-based flush for housekeeping writes
Follow the `RoomPersistenceGrain` pattern: queue dirty state, flush with `RegisterGrainTimer` on interval, and flush on `OnDeactivateAsync`.
Do not issue per-event DB writes that block the grain turn.

### Do not hardcode limits in grains
Handlers already read configuration values (e.g. `Turbo:FriendList:UserFriendLimit`) from `IConfiguration` and pass them to grains.
Magic numbers like `Take(50)`, `Take(20)`, or `maxIgnoreCapacity = 100` must come from configuration parameters on the grain interface method.
A 10,000 user hotel needs different tuning than a 10 player dev server.

### Use tracked deletes for atomicity
`ExecuteDeleteAsync` commits immediately and bypasses the EF change tracker.
If a delete + insert must succeed or fail together, use `FirstOrDefaultAsync` + `Remove` so both go through one `SaveChangesAsync`.

### Replace .Ignore() with a LogAndForget helper
Orleans `.Ignore()` makes cross-grain failures invisible. Use a `LogAndForget` extension that calls `ContinueWith(OnlyOnFaulted)` to log the exception.
Still fire-and-forget, but failures are visible in production logs.

### Bound session/history collections
Any in-memory collection that grows per-message (e.g. conversation history) must have a configurable cap.
Without a cap, long-running sessions leak memory.

### One grain per responsibility — isolate heavy I/O
Each major domain component should operate in its own grain. When a grain needs heavy I/O (DB writes, persistence flushes), delegate that work to a dedicated secondary grain so it does not block the primary grain's turn.
- Example: `RoomGrain` delegates furniture saves to `RoomPersistenceGrain`. The room grain stays responsive while persistence queues and flushes.
- Do not combine domain logic and persistence flushing in the same grain.

### Use grain boundaries for thread safety
Orleans grains are single-threaded by design. Use this for concurrency-sensitive operations by giving each user their own grain for the operation.
- Example: each player gets a `PurchaseGrain` so catalog purchases are serialized per-player with no locks needed.
- Example: limited-edition items should use a dedicated grain (e.g. `LimitedItemGrain`) so concurrent buyers are safely serialized.
- Do not add manual locking (`lock`, `SemaphoreSlim`) inside grains — that fights the actor model.

### Grains orchestrate their own outbound communication
When grain state changes (e.g. wallet balance updates), the grain itself sends the snapshot to `PlayerPresenceGrain.SendComposerAsync`. The caller that triggered the change does not pass or send the composer — the grain owns that responsibility.
- **Correct**: handler calls `grain.UpdateWalletAsync(...)` → grain updates state → grain calls `PlayerPresenceGrain.SendComposerAsync(...)`.
- **Wrong**: handler calls `grain.UpdateWalletAsync(...)` → handler builds composer → handler sends composer to player.

### Do not mutate the database directly for grain-owned state
Grains may hold cached or in-memory state that will not reflect direct DB changes. All mutations to grain-owned data must go through the grain's methods, even when the player is offline.
- If a grain uses `[PersistentState]`, state is hydrated from the configured store (not DB) on activation. Direct DB edits will be overwritten by stale store data.
- Admin tools and external systems must call grain methods, not issue raw SQL/DB updates, for data that grains own.

## Profile and grain flow constraints
- Keep packet handlers orchestration-only:
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This adapter points Claude to the canonical AI contract for this repository.
- Keep packet handlers orchestration-only.
- Do not query database contexts/repositories from packet handlers.
- Do not send composers directly to sockets/sessions from handlers; route via `PlayerPresenceGrain.SendComposerAsync`.
- For `Revision<id>` parser/serializer work, edit `../turbo-sample-plugin/TurboSamplePlugin/Revision/**`, not `turbo-cloud`.
- For `Revision<id>` parser/serializer work, edit `./turbo-cloud/Turbo.Revisions/Revision**`.

## Validation commands
```bash
Expand Down
2 changes: 1 addition & 1 deletion CODEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This adapter points Codex to the canonical AI contract for this repository.
- Keep packet handlers orchestration-only.
- Do not query database contexts/repositories from packet handlers.
- Do not send composers directly to sockets/sessions from handlers; route via `PlayerPresenceGrain.SendComposerAsync`.
- For `Revision<id>` parser/serializer work, edit `../turbo-sample-plugin/TurboSamplePlugin/Revision/**`, not `turbo-cloud`.
- For `Revision<id>` parser/serializer work, edit `./turbo-cloud/Turbo.Revisions/Revision**`.

## Validation commands
```bash
Expand Down
15 changes: 15 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,21 @@
- Lifecycle:
- inactive grains are Orleans-managed and can deactivate automatically unless explicitly marked `[KeepAlive]`.

## Grain runtime patterns
- Every grain that does cross-grain calls or DB work must inject `ILogger<T>` and log caught exceptions. No bare `catch { }`.
- Independent grain calls (e.g. checking online status for N friends) must use `Task.WhenAll`, not sequential `await` in a loop.
- Identical grain calls must not repeat inside loops — hoist before the loop.
- DB batch operations use single `WHERE ... IN (...)` queries, not per-entity `ExecuteDeleteAsync` loops.
- Housekeeping writes (e.g. delivered flags) follow the timer-flush pattern: queue dirty state, flush with `RegisterGrainTimer`, flush on `OnDeactivateAsync`. See `RoomPersistenceGrain` for reference.
- Do not hardcode limits (`Take(N)`, capacity constants) in grains. Pass them from handlers via `IConfiguration`.
- When a delete + insert must be atomic, use EF tracked operations (`Remove` + `SaveChangesAsync`), not `ExecuteDeleteAsync`.
- Replace `.Ignore()` on grain tasks with a `LogAndForget` helper that logs faulted continuations.
- In-memory collections that grow per-event (message history, queues) must have a configurable cap.
- One grain per responsibility: isolate heavy I/O (DB writes, persistence) into secondary grains so the primary domain grain stays responsive (e.g. `RoomGrain` → `RoomPersistenceGrain`).
- Use grain single-threading for concurrency safety: per-player `PurchaseGrain` for catalog buys, dedicated grains for limited-edition items. Do not add manual locks inside grains.
- Grains orchestrate their own outbound: when state changes, the grain sends the composer via `PlayerPresenceGrain.SendComposerAsync`. Callers do not build or send composers after calling a grain method.
- All mutations to grain-owned data must go through grain methods. Do not update the database directly — grains may hold cached state that will not reflect raw DB changes.

## Placement rules
- New host startup/wiring behavior:
- `Turbo.Main/` (usually `Program.cs`, `Extensions/`, or `Console/`)
Expand Down
45 changes: 44 additions & 1 deletion Turbo.Catalog/CatalogService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Turbo.Database.Context;
using Turbo.Primitives.Catalog;
using Turbo.Primitives.Catalog.Enums;
using Turbo.Primitives.Catalog.Providers;
Expand All @@ -10,12 +15,14 @@

public sealed class CatalogService(
ILogger<ICatalogService> logger,
ICatalogSnapshotProvider<NormalCatalog> normalCatalogProvider
ICatalogSnapshotProvider<NormalCatalog> normalCatalogProvider,
IDbContextFactory<TurboDbContext> dbCtxFactory
) : ICatalogService
{
private readonly ILogger<ICatalogService> _logger = logger;
private readonly ICatalogSnapshotProvider<NormalCatalog> _normalCatalogProvider =
normalCatalogProvider;
private readonly IDbContextFactory<TurboDbContext> _dbCtxFactory = dbCtxFactory;

public CatalogSnapshot GetCatalogSnapshot(CatalogType catalogType)
{
Expand All @@ -28,4 +35,40 @@
_ => throw new NotSupportedException($"Catalog type {catalogType} is not supported."),
};
}

public async Task<UpcomingLtdSnapshot?> GetUpcomingLtdAsync(CancellationToken ct)
{
await using var dbCtx = await _dbCtxFactory.CreateDbContextAsync(ct);

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 41 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

// Find the nearest upcoming active LTD drop
var now = DateTime.UtcNow;
var nextSeries = await dbCtx

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-ubuntu-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-macos-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)

Check warning on line 45 in Turbo.Catalog/CatalogService.cs

View workflow job for this annotation

GitHub Actions / quality-windows-latest

Consider calling ConfigureAwait on the awaited task (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007)
.LtdSeries.AsNoTracking()
.Where(s => s.IsActive && s.StartsAt > now)
.OrderBy(s => s.StartsAt)
.FirstOrDefaultAsync(ct);

if (nextSeries == null)
return null;

var catalogSnap = GetCatalogSnapshot(CatalogType.Normal);
var product = catalogSnap.ProductsById.Values.FirstOrDefault(p =>
p.LtdSeriesId == nextSeries.Id
);

if (product == null)
return null;

// Resolve PageId from Offer
if (!catalogSnap.OffersById.TryGetValue(product.OfferId, out var offer))
return null;

return new UpcomingLtdSnapshot
{
SecondsUntil = (int)(nextSeries.StartsAt!.Value - now).TotalSeconds,
PageId = offer.PageId,
OfferId = offer.Id,
ClassName = product.ClassName,
};
}
}
Loading