Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve SignalR implementation in Boilerplate (#8557) #8643

Merged
merged 11 commits into from
Sep 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//+:cnd:noEmit
using Microsoft.Extensions.Logging;
//#if (signalr == true)
using Microsoft.AspNetCore.SignalR.Client;
//#endif

namespace Boilerplate.Client.Core.Components;

public partial class AppInitializer : AppComponentBase
{
//#if (signalr == true)
private HubConnection? hubConnection;
//#endif

[AutoInject] private MessageBoxService messageBoxService = default!;
[AutoInject] private AuthenticationManager authManager = default!;
[AutoInject] private IJSRuntime jsRuntime = default!;
[AutoInject] private IBitDeviceCoordinator bitDeviceCoordinator = default!;
[AutoInject] private IStorageService storageService = default!;
[AutoInject] private CultureInfoManager cultureInfoManager = default!;
[AutoInject] private ILogger<AuthenticationManager> authLogger = default!;

protected async override Task OnInitAsync()
{
AuthenticationManager.AuthenticationStateChanged += AuthenticationStateChanged;

AuthenticationStateChanged(AuthenticationManager.GetAuthenticationStateAsync());

if (AppPlatform.IsBlazorHybrid)
{
if (CultureInfoManager.MultilingualEnabled)
{
cultureInfoManager.SetCurrentCulture(await storageService.GetItem("Culture") ?? // 1- User settings
CultureInfo.CurrentUICulture.Name); // 2- OS settings
}

await SetupBodyClasses();
}

await base.OnInitAsync();
}

protected override async Task OnAfterFirstRenderAsync()
{
await base.OnAfterFirstRenderAsync();

if (AppPlatform.IsBlazorHybrid is false)
{
AppPlatform.OSDescription = await jsRuntime.GetBrowserPlatform();
}
}

private async void AuthenticationStateChanged(Task<AuthenticationState> task)
{
try
{
var user = (await AuthenticationStateTask).User;

var (isUserAuthenticated, userId, userName, email, sessionId) = user.IsAuthenticated() ? (user.IsAuthenticated(), user.GetUserId().ToString(), user.GetUserName(), user.GetEmail(), user.GetSessionId()) : default;

LogAuthenticationState(authLogger, isUserAuthenticated, userId, userName, email, sessionId);

//#if (signalr == true)
if (InPrerenderSession is false)
{
await ConnectSignalR();
}
//#endif
}
catch (Exception exp)
{
ExceptionHandler.Handle(exp);
}
}

[LoggerMessage(Level = LogLevel.Information, Message = "Authentication State: {IsUserAuthenticated}, {UserId}, {UserName}, {Email}, {UserSessionId}")]
private static partial void LogAuthenticationState(ILogger logger, bool isUserAuthenticated, string userId, string userName, string? email, string? userSessionId);

//#if (signalr == true)
private async Task ConnectSignalR()
{
if (hubConnection is not null)
{
await hubConnection.DisposeAsync();
}

var access_token = await AuthTokenProvider.GetAccessTokenAsync();

hubConnection = new HubConnectionBuilder()
.WithUrl($"{Configuration.GetServerAddress()}/app-hub?access_token={access_token}")
.Build();

hubConnection.On<string>("TwoFactorToken", async (token) =>
{
await messageBoxService.Show(Localizer[nameof(AppStrings.TwoFactorTokenPushText), token]);

// The following code block is not required for Bit.BlazorUI components to perform UI changes. However, it may be necessary in other scenarios.
/*await InvokeAsync(async () =>
{
StateHasChanged();
});*/

// You can also leverage IPubSubService to notify other components in the application.
});

await hubConnection.StartAsync(CurrentCancellationToken);
}
//#endif

private async Task SetupBodyClasses()
{
var cssClasses = new List<string> { };

if (AppPlatform.IsWindows)
{
cssClasses.Add("bit-windows");
}
else if (AppPlatform.IsMacOS)
{
cssClasses.Add("bit-macos");
}
else if (AppPlatform.IsIOS)
{
cssClasses.Add("bit-ios");
}
else if (AppPlatform.IsAndroid)
{
cssClasses.Add("bit-android");
}

var cssVariables = new Dictionary<string, string>();
var statusBarHeight = bitDeviceCoordinator.GetStatusBarHeight();

if (AppPlatform.IsMacOS is false)
{
//For iOS this is handled in css using safe-area env() variables
//For Android there's an issue with keyboard in fullscreen mode. more info: https://github.com/bitfoundation/bitplatform/issues/5626
//For Windows there's an issue with TitleBar. more info: https://github.com/bitfoundation/bitplatform/issues/5695
statusBarHeight = 0;
}

cssVariables.Add("--bit-status-bar-height", $"{statusBarHeight.ToString("F3", CultureInfo.InvariantCulture)}px");
await jsRuntime.ApplyBodyElementClasses(cssClasses, cssVariables);
}

//#if (signalr == true)
protected override async ValueTask DisposeAsync(bool disposing)
{
if (hubConnection is not null)
{
await hubConnection.DisposeAsync();
}

await base.DisposeAsync(disposing);
}
//#endif
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ protected override async Task OnInitializedAsync()
{
try
{
authManager.AuthenticationStateChanged += IsUserAuthenticated;
authManager.AuthenticationStateChanged += AuthenticationStateChanged;

isUserAuthenticated = await prerenderStateService.GetValue(async () => (await AuthenticationStateTask).User.IsAuthenticated());

Expand Down Expand Up @@ -64,7 +64,7 @@ private void ToggleMenuHandler()
isMenuOpen = !isMenuOpen;
}

private async void IsUserAuthenticated(Task<AuthenticationState> task)
private async void AuthenticationStateChanged(Task<AuthenticationState> task)
{
try
{
Expand All @@ -91,7 +91,7 @@ protected virtual void Dispose(bool disposing)
{
if (disposed || disposing is false) return;

authManager.AuthenticationStateChanged -= IsUserAuthenticated;
authManager.AuthenticationStateChanged -= AuthenticationStateChanged;

unsubscribeCultureChange();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,4 @@
<div class="content-container danger-panel">
<DeleteAccountSection />
</div>
</div>

@*#if (signalr == true)*@
<BitSnackBar @ref="snackBar" />
@*#endif*@
</div>
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
//+:cnd:noEmit
//#if (signalr == true)
using Microsoft.AspNetCore.SignalR.Client;
//#endif
using Boilerplate.Shared.Dtos.Identity;
using Boilerplate.Shared.Controllers.Identity;

Expand All @@ -12,10 +9,6 @@ public partial class ProfilePage
{
private UserDto? user;
private bool isLoading;
//#if (signalr == true)
private HubConnection? hubConnection;
private BitSnackBar snackBar = default!;
//#endif

[AutoInject] private IUserController userController = default!;

Expand All @@ -35,40 +28,4 @@ protected override async Task OnInitAsync()

await base.OnInitAsync();
}

//#if (signalr == true)
protected async override Task OnAfterFirstRenderAsync()
{
await base.OnAfterFirstRenderAsync();

var access_token = await AuthTokenProvider.GetAccessTokenAsync();

hubConnection = new HubConnectionBuilder()
.WithUrl($"{Configuration.GetServerAddress()}/identity-hub?access_token={access_token}")
.Build();

hubConnection.On<UserSessionDto>("NewUserSession", async (userSession) =>
{
await snackBar.Info(Localizer[nameof(AppStrings.NewUserSessionSnackbarTitle)], Localizer[nameof(AppStrings.DeviceDetails), userSession.Device!]);

// The following code block is not required for Bit.BlazorUI components to perform UI changes. However, it may be necessary in other scenarios.
/*await InvokeAsync(async () =>
{
StateHasChanged();
});*/
});

await hubConnection.StartAsync();
}

protected override async ValueTask DisposeAsync(bool disposing)
{
if (hubConnection is not null)
{
await hubConnection.DisposeAsync();
}

await base.DisposeAsync(disposing);
}
//#endif
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<CascadingAuthenticationState>
<AuthenticationStateLogger />
<AppInitializer />
<LayoutView Layout="@typeof(MainLayout)">
<Router AppAssembly="@GetType().Assembly"
AdditionalAssemblies="@(AssemblyLoadContext.Default.Assemblies.Where(asm => asm.GetName().Name?.Contains("Boilerplate") is true))">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,73 +2,6 @@

public partial class Routes
{
[AutoInject] IJSRuntime jsRuntime = default!;
[AutoInject] IBitDeviceCoordinator bitDeviceCoordinator = default!;
[AutoInject] IStorageService storageService = default!;
[AutoInject] CultureInfoManager cultureInfoManager = default!;

protected override async Task OnInitializedAsync()
{
if (AppPlatform.IsBlazorHybrid)
{
if (CultureInfoManager.MultilingualEnabled)
{
cultureInfoManager.SetCurrentCulture(await storageService.GetItem("Culture") ?? // 1- User settings
CultureInfo.CurrentUICulture.Name); // 2- OS settings
}

await SetupBodyClasses();
}

await base.OnInitializedAsync();
}

private async Task SetupBodyClasses()
{
var cssClasses = new List<string> { };

if (AppPlatform.IsWindows)
{
cssClasses.Add("bit-windows");
}
else if (AppPlatform.IsMacOS)
{
cssClasses.Add("bit-macos");
}
else if (AppPlatform.IsIOS)
{
cssClasses.Add("bit-ios");
}
else if (AppPlatform.IsAndroid)
{
cssClasses.Add("bit-android");
}

var cssVariables = new Dictionary<string, string>();
var statusBarHeight = bitDeviceCoordinator.GetStatusBarHeight();

if (AppPlatform.IsMacOS is false)
{
//For iOS this is handled in css using safe-area env() variables
//For Android there's an issue with keyboard in fullscreen mode. more info: https://github.com/bitfoundation/bitplatform/issues/5626
//For Windows there's an issue with TitleBar. more info: https://github.com/bitfoundation/bitplatform/issues/5695
statusBarHeight = 0;
}

cssVariables.Add("--bit-status-bar-height", $"{statusBarHeight.ToString("F3", CultureInfo.InvariantCulture)}px");
await jsRuntime.ApplyBodyElementClasses(cssClasses, cssVariables);
}

protected async override Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);

if (firstRender && AppPlatform.IsBlazorHybrid is false)
{
AppPlatform.OSDescription = await jsRuntime.GetBrowserPlatform();
}
}

[AutoInject] NavigationManager? navigationManager { set => universalLinksNavigationManager = value; get => universalLinksNavigationManager; }
public static NavigationManager? universalLinksNavigationManager;

Expand Down
Loading
Loading