using SharepointToolbox.Web.Core.Models; using SharepointToolbox.Web.Infrastructure.Persistence; namespace SharepointToolbox.Web.Services.Session; public class UserSessionService : IUserSessionService { private readonly ISessionManager _sessionManager; private readonly SettingsRepository _settingsRepo; private TenantProfile? _currentProfile; private AppSettings _settings = new(); public TenantProfile? CurrentProfile => _currentProfile; public bool HasProfile => _currentProfile is not null; public AppSettings Settings => _settings; public ReportBranding CurrentBranding => new(_settings.MspLogo, _currentProfile?.ClientLogo); public event Action? ProfileChanged; public UserSessionService(ISessionManager sessionManager, SettingsRepository settingsRepo) { _sessionManager = sessionManager; _settingsRepo = settingsRepo; // Load synchronously so Settings (esp. Lang) are available the moment the circuit // starts — culture is applied in MainLayout.OnInitialized before any page renders, // so a fire-and-forget load here would race and lose. // // Run on the thread pool (Task.Run) so LoadAsync's await continuation does NOT post // back to the circuit's SynchronizationContext. Blocking that context here with a plain // GetResult() deadlocks: the continuation can never resume on the thread we're blocking. try { _settings = Task.Run(() => _settingsRepo.LoadAsync()).GetAwaiter().GetResult(); } catch { /* use defaults */ } } public void SetProfile(TenantProfile profile) { _currentProfile = profile; ProfileChanged?.Invoke(); } public async Task ClearSessionAsync() { if (_currentProfile is not null) await _sessionManager.ClearSessionAsync(_currentProfile.TenantUrl); _currentProfile = null; ProfileChanged?.Invoke(); } public void UpdateSettings(AppSettings settings) { _settings = settings; _ = _settingsRepo.SaveAsync(settings); } }