refactor: hoist Moscow offset + sport labels into shared helpers (HIGH)
* New Marathon.Domain.ValueObjects.MoscowTime with Offset, Now, and EndOfMoscowDay(DateOnly) — replaces ~15 inline TimeSpan.FromHours(3) literals across Domain/Application/Infrastructure/UI. * New Marathon.UI.Services.SportLabels.Resolve(IStringLocalizer, int) — replaces 6 near-identical SportLabel switch bodies in EventListShell, Events/Detail, Anomalies/AnomalyFeed, Results/ResultsList, Results/ResultsLoader, and AnomalyCard. Single source of truth for the 6/11/22723/43658 sport-code mapping. Pages keep a one-liner wrapper so the call sites stay terse.
This commit is contained in:
@@ -215,14 +215,7 @@
|
||||
_ => kind.ToString(),
|
||||
};
|
||||
|
||||
private string SportLabel(int code) => code switch
|
||||
{
|
||||
6 => L["Sport.Basketball"],
|
||||
11 => L["Sport.Football"],
|
||||
22723 => L["Sport.Tennis"],
|
||||
43658 => L["Sport.Hockey"],
|
||||
_ => string.Format(System.Globalization.CultureInfo.InvariantCulture, "Sport {0}", code),
|
||||
};
|
||||
private string SportLabel(int code) => SportLabels.Resolve(L, code);
|
||||
|
||||
private static string FormatRate(decimal? r) => r is { } v
|
||||
? v.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture)
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
var moscow = DateTimeOffset.UtcNow.ToOffset(TimeSpan.FromHours(3));
|
||||
var moscow = MoscowTime.Now;
|
||||
_from = InitialFrom ?? moscow.AddDays(-1).Date;
|
||||
_to = InitialTo ?? moscow.AddDays(1).Date;
|
||||
_kind = InitialKind;
|
||||
@@ -140,10 +140,9 @@
|
||||
try
|
||||
{
|
||||
// Use Moscow offset to match domain ScheduledAt invariant.
|
||||
var moscow = TimeSpan.FromHours(3);
|
||||
var range = new AppDateRange(
|
||||
new DateTimeOffset(_from.Value.Date, moscow),
|
||||
new DateTimeOffset(_to.Value.Date.AddDays(1).AddSeconds(-1), moscow));
|
||||
new DateTimeOffset(_from.Value.Date, MoscowTime.Offset),
|
||||
MoscowTime.EndOfMoscowDay(DateOnly.FromDateTime(_to.Value.Date)));
|
||||
|
||||
var path = await ExportUseCase.ExecuteAsync(range, _kind, CancellationToken.None);
|
||||
Dialog.Close(DialogResult.Ok(path));
|
||||
|
||||
@@ -252,8 +252,7 @@
|
||||
{
|
||||
if (DateTimeOffset.TryParse(e.Value?.ToString(), out var v))
|
||||
{
|
||||
var moscow = TimeSpan.FromHours(3);
|
||||
await UpdateFilter(_filter with { From = new DateTimeOffset(v.Date, moscow) });
|
||||
await UpdateFilter(_filter with { From = new DateTimeOffset(v.Date, MoscowTime.Offset) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,8 +260,7 @@
|
||||
{
|
||||
if (DateTimeOffset.TryParse(e.Value?.ToString(), out var v))
|
||||
{
|
||||
var moscow = TimeSpan.FromHours(3);
|
||||
await UpdateFilter(_filter with { To = new DateTimeOffset(v.Date, moscow).AddDays(1).AddSeconds(-1) });
|
||||
await UpdateFilter(_filter with { To = MoscowTime.EndOfMoscowDay(DateOnly.FromDateTime(v.Date)) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,14 +281,7 @@
|
||||
_ => L["Anomaly.Severity.Low"],
|
||||
};
|
||||
|
||||
private string SportLabel(int code) => code switch
|
||||
{
|
||||
6 => L["Sport.Basketball"],
|
||||
11 => L["Sport.Football"],
|
||||
22723 => L["Sport.Tennis"],
|
||||
43658 => L["Sport.Hockey"],
|
||||
_ => string.Format(System.Globalization.CultureInfo.InvariantCulture, "Sport {0}", code),
|
||||
};
|
||||
private string SportLabel(int code) => SportLabels.Resolve(L, code);
|
||||
|
||||
private static string FormatDate(DateTimeOffset? value)
|
||||
=> value?.ToString("yyyy-MM-dd") ?? string.Empty;
|
||||
|
||||
@@ -306,14 +306,7 @@
|
||||
_ => "—",
|
||||
};
|
||||
|
||||
private string SportLabel(int code) => code switch
|
||||
{
|
||||
6 => L["Sport.Basketball"],
|
||||
11 => L["Sport.Football"],
|
||||
22723 => L["Sport.Tennis"],
|
||||
43658 => L["Sport.Hockey"],
|
||||
_ => $"Sport {code}",
|
||||
};
|
||||
private string SportLabel(int code) => SportLabels.Resolve(L, code);
|
||||
|
||||
private string BetTypeLabel(BetType t) => t switch
|
||||
{
|
||||
|
||||
@@ -164,8 +164,6 @@
|
||||
</style>
|
||||
|
||||
@code {
|
||||
private static readonly TimeSpan MoscowOffset = TimeSpan.FromHours(3);
|
||||
|
||||
private DateTimeOffset _from;
|
||||
private DateTimeOffset _to;
|
||||
private string _searchInput = string.Empty;
|
||||
@@ -181,7 +179,7 @@
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var todayMoscow = new DateTimeOffset(DateTime.UtcNow.Date, TimeSpan.Zero).ToOffset(MoscowOffset);
|
||||
var todayMoscow = new DateTimeOffset(DateTime.UtcNow.Date, TimeSpan.Zero).ToOffset(MoscowTime.Offset);
|
||||
_from = todayMoscow.AddDays(-30);
|
||||
_to = todayMoscow.AddDays(1).AddSeconds(-1);
|
||||
|
||||
@@ -246,7 +244,7 @@
|
||||
{
|
||||
if (DateTimeOffset.TryParse(e.Value?.ToString(), out var v))
|
||||
{
|
||||
_from = new DateTimeOffset(v.Date, MoscowOffset);
|
||||
_from = new DateTimeOffset(v.Date, MoscowTime.Offset);
|
||||
await LoadAsync();
|
||||
}
|
||||
}
|
||||
@@ -255,7 +253,7 @@
|
||||
{
|
||||
if (DateTimeOffset.TryParse(e.Value?.ToString(), out var v))
|
||||
{
|
||||
_to = new DateTimeOffset(v.Date, MoscowOffset).AddDays(1).AddSeconds(-1);
|
||||
_to = new DateTimeOffset(v.Date, MoscowTime.Offset).AddDays(1).AddSeconds(-1);
|
||||
await LoadAsync();
|
||||
}
|
||||
}
|
||||
@@ -311,14 +309,7 @@
|
||||
_ => "draw",
|
||||
};
|
||||
|
||||
private string SportLabel(int code) => code switch
|
||||
{
|
||||
6 => L["Sport.Basketball"],
|
||||
11 => L["Sport.Football"],
|
||||
22723 => L["Sport.Tennis"],
|
||||
43658 => L["Sport.Hockey"],
|
||||
_ => $"Sport {code}",
|
||||
};
|
||||
private string SportLabel(int code) => SportLabels.Resolve(L, code);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
@@ -204,8 +204,6 @@
|
||||
</style>
|
||||
|
||||
@code {
|
||||
private static readonly TimeSpan MoscowOffset = TimeSpan.FromHours(3);
|
||||
|
||||
private DateTimeOffset _from;
|
||||
private DateTimeOffset _to;
|
||||
private LoaderMode _mode = LoaderMode.AllInRange;
|
||||
@@ -234,7 +232,7 @@
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var todayMoscow = new DateTimeOffset(DateTime.UtcNow.Date, TimeSpan.Zero).ToOffset(MoscowOffset);
|
||||
var todayMoscow = new DateTimeOffset(DateTime.UtcNow.Date, TimeSpan.Zero).ToOffset(MoscowTime.Offset);
|
||||
_from = todayMoscow.AddDays(-7);
|
||||
_to = todayMoscow.AddDays(1).AddSeconds(-1);
|
||||
await ReloadCandidatesAsync();
|
||||
@@ -347,7 +345,7 @@
|
||||
{
|
||||
if (DateTimeOffset.TryParse(e.Value?.ToString(), out var v))
|
||||
{
|
||||
_from = new DateTimeOffset(v.Date, MoscowOffset);
|
||||
_from = new DateTimeOffset(v.Date, MoscowTime.Offset);
|
||||
await ReloadCandidatesAsync();
|
||||
}
|
||||
}
|
||||
@@ -356,7 +354,7 @@
|
||||
{
|
||||
if (DateTimeOffset.TryParse(e.Value?.ToString(), out var v))
|
||||
{
|
||||
_to = new DateTimeOffset(v.Date, MoscowOffset).AddDays(1).AddSeconds(-1);
|
||||
_to = new DateTimeOffset(v.Date, MoscowTime.Offset).AddDays(1).AddSeconds(-1);
|
||||
await ReloadCandidatesAsync();
|
||||
}
|
||||
}
|
||||
@@ -400,14 +398,7 @@
|
||||
_ => o.ToString(),
|
||||
};
|
||||
|
||||
private string SportLabel(int code) => code switch
|
||||
{
|
||||
6 => L["Sport.Basketball"],
|
||||
11 => L["Sport.Football"],
|
||||
22723 => L["Sport.Tennis"],
|
||||
43658 => L["Sport.Hockey"],
|
||||
_ => $"Sport {code}",
|
||||
};
|
||||
private string SportLabel(int code) => SportLabels.Resolve(L, code);
|
||||
|
||||
private static string FormatDate(DateTimeOffset value) => value.ToString("yyyy-MM-dd");
|
||||
|
||||
|
||||
@@ -437,8 +437,7 @@
|
||||
{
|
||||
if (DateTimeOffset.TryParse(e.Value?.ToString(), out var v))
|
||||
{
|
||||
var moscow = TimeSpan.FromHours(3);
|
||||
await UpdateFilter(_filter with { From = new DateTimeOffset(v.Date, moscow) });
|
||||
await UpdateFilter(_filter with { From = new DateTimeOffset(v.Date, MoscowTime.Offset) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,8 +445,7 @@
|
||||
{
|
||||
if (DateTimeOffset.TryParse(e.Value?.ToString(), out var v))
|
||||
{
|
||||
var moscow = TimeSpan.FromHours(3);
|
||||
await UpdateFilter(_filter with { To = new DateTimeOffset(v.Date, moscow).AddDays(1).AddSeconds(-1) });
|
||||
await UpdateFilter(_filter with { To = MoscowTime.EndOfMoscowDay(DateOnly.FromDateTime(v.Date)) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,14 +517,7 @@
|
||||
private static string FormatDate(DateTimeOffset value)
|
||||
=> value.ToString("yyyy-MM-dd");
|
||||
|
||||
private string SportLabel(int code) => code switch
|
||||
{
|
||||
6 => L["Sport.Basketball"],
|
||||
11 => L["Sport.Football"],
|
||||
22723 => L["Sport.Tennis"],
|
||||
43658 => L["Sport.Hockey"],
|
||||
_ => $"Sport {code}",
|
||||
};
|
||||
private string SportLabel(int code) => SportLabels.Resolve(L, code);
|
||||
|
||||
private readonly record struct RatesSnap(decimal? Win1, decimal? Draw, decimal? Win2);
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Marathon.Domain.ValueObjects;
|
||||
|
||||
namespace Marathon.UI.Services;
|
||||
|
||||
/// <summary>
|
||||
@@ -54,8 +56,7 @@ public sealed class EventBrowsingState
|
||||
public static PageFilter Default(DateTime nowUtc)
|
||||
{
|
||||
// Default window: -1d .. +7d in Moscow time, the same TZ events use.
|
||||
var moscow = TimeSpan.FromHours(3);
|
||||
var midnight = new DateTimeOffset(nowUtc.Date, TimeSpan.Zero).ToOffset(moscow);
|
||||
var midnight = new DateTimeOffset(nowUtc.Date, TimeSpan.Zero).ToOffset(MoscowTime.Offset);
|
||||
return new PageFilter(
|
||||
From: midnight.AddDays(-1),
|
||||
To: midnight.AddDays(7),
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Globalization;
|
||||
using Marathon.UI.Resources;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Marathon.UI.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Single source of truth for the sport-code → display-label mapping that
|
||||
/// every list/detail page in the RCL needs. Resolves the four canonical
|
||||
/// data-sport-treeId values from the marathonbet.by spike (6 = basketball,
|
||||
/// 11 = football, 22723 = tennis, 43658 = hockey) against shared
|
||||
/// localization resources, with a stable invariant fallback for unknown
|
||||
/// codes so the UI degrades gracefully if the bookmaker adds a new sport.
|
||||
/// </summary>
|
||||
public static class SportLabels
|
||||
{
|
||||
public static string Resolve(IStringLocalizer<SharedResource> localizer, int code) =>
|
||||
code switch
|
||||
{
|
||||
6 => localizer["Sport.Basketball"],
|
||||
11 => localizer["Sport.Football"],
|
||||
22723 => localizer["Sport.Tennis"],
|
||||
43658 => localizer["Sport.Hockey"],
|
||||
_ => string.Format(CultureInfo.InvariantCulture, "Sport {0}", code),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user