Files
BotPages/BotPages.Core/Navigation/NavigationService.cs
FrigaT 57b3706241
All checks were successful
CI / build-test (push) Successful in 29s
Release / pack-and-publish (release) Successful in 32s
Выделены отдельные расширения
2025-12-06 07:52:01 +03:00

116 lines
4.0 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using BotPages.Core.Abstractions;
using BotPages.Core.Routing;
using System.Collections.Concurrent;
namespace BotPages.Core;
/// <summary>
/// Сервис навигации между страницами.
/// Позволяет выполнять переходы, замену страниц и передачу аргументов.
/// </summary>
public sealed class NavigationService
{
private readonly RoutesRegistry _routes;
private readonly Dictionary<Type, Page> _singletonPages = new();
private readonly ConcurrentDictionary<CompositeSessionKey, Page> _sessionPages = new();
private Type? _defaultPage;
/// <summary>
/// Создать сервис навигации.
/// </summary>
internal NavigationService(RoutesRegistry routes)
{
_routes = routes;
}
internal void AddDefaultPage<TPage>() where TPage : SingletonPage
{
_defaultPage = typeof(TPage);
}
/// <summary>
/// Перейти по маршруту без аргументов.
/// </summary>
public Task GoToHomeAsync(PageContext ctx, CancellationToken ct)
{
return NavigateAsync(_defaultPage!, ctx, null, ct);
}
/// <summary>
/// Перейти по маршруту без аргументов.
/// </summary>
public Task GoToAsync(string route, PageContext ctx, CancellationToken ct)
{
var pageType = _routes.Resolve(route);
return NavigateAsync(pageType!, ctx, null, ct);
}
/// <summary>
/// Перейти по маршруту с аргументами.
/// </summary>
public Task GoToAsync<TArgs>(string route, TArgs args, PageContext ctx, CancellationToken ct)
{
var pageType = _routes.Resolve(route);
return NavigateAsync(pageType!, ctx, args!, ct);
}
/// <summary>
/// Перейти на страницу без аргументов.
/// </summary>
public Task GoToAsync<TPage>(PageContext ctx, CancellationToken ct) where TPage : Page
=> NavigateAsync(typeof(TPage), ctx, null, ct);
/// <summary>
/// Перейти на страницу с аргументами.
/// </summary>
public Task GoToAsync<TPage, TArgs>(PageContext ctx, TArgs args, CancellationToken ct) where TPage : StatefullPage<TArgs>
=> NavigateAsync(typeof(TPage), ctx, args!, ct);
/// <summary>
/// Заменить текущую страницу.
/// </summary>
public Task ReplaceWithAsync<TPage>(PageContext ctx, CancellationToken ct) where TPage : Page
=> NavigateAsync(typeof(TPage), ctx, null, ct, replace: true);
internal async Task NavigateAsync(Type pageType, PageContext ctx, object? args, CancellationToken ct, bool replace = false)
{
Page? page;
if (typeof(SingletonPage).IsAssignableFrom(pageType))
{
// Singleton: один объект на всё приложение
if (!_singletonPages.TryGetValue(pageType, out page))
{
page = (Page)Activator.CreateInstance(pageType)!;
_singletonPages[pageType] = page;
}
}
else
{
// Stateful: новый объект на пользователя
page = (Page)Activator.CreateInstance(pageType)!;
}
if (_sessionPages.TryGetValue(ctx.SessionKey, out var currentPage))
{
if (currentPage.GetType() != pageType || replace)
{
await currentPage.OnLeave(ctx, ct);
}
}
_sessionPages[ctx.SessionKey] = page;
if (args is null)
await page.OnEnter(ctx, ct);
else
await (page as dynamic).OnEnter(ctx, (dynamic)args, ct);
}
/// <summary>
/// Восстановить текущую страницу из StateStorage.
/// </summary>
public Page? ResolveCurrentPage(PageContext ctx)
=> _sessionPages.TryGetValue(ctx.SessionKey, out var page) ? page : null;
}