diff --git a/.github/workflows/todo-sample.cd.yml b/.github/workflows/todo-sample.cd.yml
index c610a675c3..8f816d4215 100644
--- a/.github/workflows/todo-sample.cd.yml
+++ b/.github/workflows/todo-sample.cd.yml
@@ -47,7 +47,7 @@ jobs:
env:
ServerAddress: ${{ env.SERVER_ADDRESS }}
WebAppRender.BlazorMode: BlazorWebAssembly
- WebAppRender.PreRenderEnabled: true
+ WebAppRender.PrerenderEnabled: true
ApplicationInsights.ConnectionString: ${{ secrets.APPLICATION_INSIGHTS_CONNECTION_STRING }}
AdsPushVapid.PublicKey: ${{ secrets.TODO_PUBLIC_VAPIDKEY }}
diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor
index fbb0b823a2..6858b979df 100644
--- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor
+++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Components/App.razor
@@ -63,7 +63,7 @@
@if (HttpContext.Request.IsCrawlerClient() is false)
{
-
+
@if (AppRenderMode.PwaEnabled)
{
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppComponentBase.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppComponentBase.cs
index c55f0867af..a3968fde89 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppComponentBase.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppComponentBase.cs
@@ -20,7 +20,7 @@ public partial class AppComponentBase : ComponentBase, IAsyncDisposable
[AutoInject] protected IPrerenderStateService PrerenderStateService = default!;
///
- ///
+ ///
///
[AutoInject] protected PubSubService PubSubService = default!;
@@ -38,6 +38,8 @@ public partial class AppComponentBase : ComponentBase, IAsyncDisposable
[AutoInject] protected SnackBarService SnackBarService = default!;
+ [AutoInject] protected ITelemetryContext TelemetryContext = default!;
+
private readonly CancellationTokenSource cts = new();
protected CancellationToken CurrentCancellationToken => cts.Token;
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppInitializer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppInitializer.cs
index 5e5fe8e0e9..3becead072 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppInitializer.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/AppInitializer.cs
@@ -1,4 +1,5 @@
//+:cnd:noEmit
+using Microsoft.Extensions.Logging;
//#if (signalr == true)
using Microsoft.AspNetCore.SignalR.Client;
//#endif
@@ -14,17 +15,18 @@ public partial class AppInitializer : AppComponentBase
//#if (notification == true)
[AutoInject] private IPushNotificationService pushNotificationService = default!;
//#endif
+ [AutoInject] private Navigator navigator = default!;
[AutoInject] private IJSRuntime jsRuntime = default!;
[AutoInject] private Bit.Butil.Console console = default!;
[AutoInject] private IStorageService storageService = default!;
+ [AutoInject] private ILogger logger = default!;
[AutoInject] private SnackBarService snackBarService = default!;
[AutoInject] private AuthenticationManager authManager = default!;
- [AutoInject] private ITelemetryContext telemetryContext = default!;
[AutoInject] private NavigationManager navigationManager = default!;
[AutoInject] private CultureInfoManager cultureInfoManager = default!;
[AutoInject] private IBitDeviceCoordinator bitDeviceCoordinator = default!;
- protected async override Task OnInitAsync()
+ protected override async Task OnInitAsync()
{
AuthenticationManager.AuthenticationStateChanged += AuthenticationStateChanged;
@@ -51,9 +53,12 @@ protected override async Task OnAfterFirstRenderAsync()
{
await base.OnAfterFirstRenderAsync();
+ TelemetryContext.UserAgent = await navigator.GetUserAgent();
+ TelemetryContext.TimeZone = await jsRuntime.GetTimeZone();
+ TelemetryContext.Culture = CultureInfo.CurrentCulture.Name;
if (AppPlatform.IsBlazorHybrid is false)
{
- telemetryContext.OS = await jsRuntime.GetBrowserPlatform();
+ TelemetryContext.OS = await jsRuntime.GetBrowserPlatform();
}
}
@@ -62,8 +67,8 @@ private async void AuthenticationStateChanged(Task task)
try
{
var user = (await task).User;
- telemetryContext.UserId = user.IsAuthenticated() ? user.GetUserId() : null;
- telemetryContext.UserSessionId = user.IsAuthenticated() ? user.GetSessionId() : null;
+ TelemetryContext.UserId = user.IsAuthenticated() ? user.GetUserId() : null;
+ TelemetryContext.UserSessionId = user.IsAuthenticated() ? user.GetSessionId() : null;
//#if (signalr == true)
if (InPrerenderSession is false)
@@ -97,7 +102,7 @@ private async Task ConnectSignalR()
hubConnection = new HubConnectionBuilder()
.WithAutomaticReconnect()
- .WithUrl($"{Configuration.GetServerAddress()}/app-hub?access_token={access_token}", options =>
+ .WithUrl($"{HttpClient.BaseAddress}app-hub?access_token={access_token}", options =>
{
options.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets;
// Avoid enabling long polling or Server-Sent Events. Focus on resolving the issue with WebSockets instead.
@@ -123,8 +128,35 @@ private async Task ConnectSignalR()
// You can also leverage IPubSubService to notify other components in the application.
});
+ hubConnection.Closed += HubConnectionDisconnected;
+ hubConnection.Reconnected += HubConnectionConnected;
+ hubConnection.Reconnecting += HubConnectionDisconnected;
+
await hubConnection.StartAsync(CurrentCancellationToken);
+
+ await HubConnectionConnected(null);
+ }
+
+ private async Task HubConnectionConnected(string? arg)
+ {
+ TelemetryContext.IsOnline = true;
+ logger.LogInformation("SignalR connection established.");
+ }
+
+ private async Task HubConnectionDisconnected(Exception? exception)
+ {
+ TelemetryContext.IsOnline = false;
+
+ if (exception is null)
+ {
+ logger.LogInformation("SignalR connection lost."); // Was triggered intentionally by either server or client.
+ }
+ else
+ {
+ ExceptionHandler.Handle(exception);
+ }
}
+
//#endif
private async Task SetupBodyClasses()
@@ -162,6 +194,9 @@ protected override async ValueTask DisposeAsync(bool disposing)
//#if (signalr == true)
if (hubConnection is not null)
{
+ hubConnection.Closed -= HubConnectionDisconnected;
+ hubConnection.Reconnected -= HubConnectionConnected;
+ hubConnection.Reconnecting -= HubConnectionDisconnected;
await hubConnection.DisposeAsync();
}
//#endif
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor
index cd11df9352..baa204a647 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor
@@ -7,13 +7,13 @@
@Localizer[nameof(AppStrings.CurrentSession)]
+ SecondaryText="@currentSession.Address"
+ TertiaryText="@($"{currentSession.IP} - {GetLastSeenOn(currentSession.RenewedOn)}")"
+ Size="BitPersonaSize.Size48"
+ Presence="@GetPresence(currentSession.RenewedOn)"
+ Styles="@(new() { Image = "width:50%;height:50%" })"
+ ImageInitials="✓"
+ ImageUrl="@($"/_content/Boilerplate.Client.Core/images/os/{GetImageUrl(currentSession.Device)}")" />
}
@@ -28,9 +28,9 @@
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor.cs
index dcaf573636..c518e41b2b 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SessionsSection.razor.cs
@@ -75,10 +75,17 @@ private static string GetImageUrl(string? device)
return "apple.png";
}
- private static BitPersonaPresence GetPresence(string? lastSeenOn)
+ private BitPersonaPresence GetPresence(DateTimeOffset renewedOn)
{
- return lastSeenOn == AppStrings.Online ? BitPersonaPresence.Online
- : lastSeenOn == AppStrings.Recently ? BitPersonaPresence.Away
- : BitPersonaPresence.Offline;
+ return DateTimeOffset.UtcNow - renewedOn < TimeSpan.FromMinutes(5) ? BitPersonaPresence.Online
+ : DateTimeOffset.UtcNow - renewedOn < TimeSpan.FromMinutes(15) ? BitPersonaPresence.Away
+ : BitPersonaPresence.Offline;
+ }
+
+ private string GetLastSeenOn(DateTimeOffset renewedOn)
+ {
+ return DateTimeOffset.UtcNow - renewedOn < TimeSpan.FromMinutes(5) ? Localizer[nameof(AppStrings.Online)]
+ : DateTimeOffset.UtcNow - renewedOn < TimeSpan.FromMinutes(15) ? Localizer[nameof(AppStrings.Recently)]
+ : renewedOn.ToLocalTime().ToString("g");
}
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/TodoPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/TodoPage.razor
index 532a1a0957..cdb9aee16f 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/TodoPage.razor
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Todo/TodoPage.razor
@@ -86,7 +86,7 @@
Styles="@(new() { Label = todo.IsDone ? "text-decoration:line-through" : "" })"
DefaultValue="todo.IsDone"
OnChange="() => ToggleIsDone(todo)" />
- @todo.Date.ToLocalTime().ToString("yyyy MMMM dd, HH:mm:ss")
+ @todo.Date.ToLocalTime().ToString("F")
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20240729183448_InitialMigration.Designer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20240729183448_InitialMigration.Designer.cs
deleted file mode 100644
index 5738ce8a4f..0000000000
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20240729183448_InitialMigration.Designer.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-//
-using System;
-using Boilerplate.Client.Core.Data;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-
-#nullable disable
-
-namespace Boilerplate.Client.Core.Data.Migrations
-{
- [DbContext(typeof(OfflineDbContext))]
- [Migration("20240729183448_InitialMigration")]
- partial class InitialMigration
- {
- ///
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder.HasAnnotation("ProductVersion", "8.0.7");
-
- modelBuilder.Entity("Boilerplate.Shared.Dtos.Identity.UserDto", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("TEXT");
-
- b.Property("BirthDate")
- .HasColumnType("INTEGER");
-
- b.Property("Email")
- .HasColumnType("TEXT");
-
- b.Property("FullName")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property("Gender")
- .HasColumnType("INTEGER");
-
- b.Property("Password")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property("PhoneNumber")
- .HasColumnType("TEXT");
-
- b.Property("ProfileImageName")
- .HasColumnType("TEXT");
-
- b.Property("UserName")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.ToTable("Users");
-
- b.HasData(
- new
- {
- Id = new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"),
- BirthDate = 1306790461440000000L,
- Email = "test@bitplatform.dev",
- FullName = "Boilerplate test account",
- Gender = 2,
- Password = "123456",
- PhoneNumber = "+31684207362",
- UserName = "test"
- });
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20241030140343_InitialMigration.Designer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20241030140343_InitialMigration.Designer.cs
new file mode 100644
index 0000000000..046b42efe0
--- /dev/null
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20241030140343_InitialMigration.Designer.cs
@@ -0,0 +1,72 @@
+//
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Boilerplate.Client.Core.Data.Migrations;
+
+[DbContext(typeof(OfflineDbContext))]
+[Migration("20241030140343_InitialMigration")]
+partial class InitialMigration
+{
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "8.0.0");
+
+ modelBuilder.Entity("Boilerplate.Shared.Dtos.Identity.UserDto", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("BirthDate")
+ .HasColumnType("INTEGER");
+
+ b.Property("Email")
+ .HasColumnType("TEXT");
+
+ b.Property("FullName")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Gender")
+ .HasColumnType("INTEGER");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneNumber")
+ .HasColumnType("TEXT");
+
+ b.Property("ProfileImageName")
+ .HasColumnType("TEXT");
+
+ b.Property("UserName")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("Users");
+
+ b.HasData(
+ new
+ {
+ Id = new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"),
+ BirthDate = 1306790461440000000L,
+ Email = "test@bitplatform.dev",
+ FullName = "Boilerplate test account",
+ Gender = 0,
+ Password = "123456",
+ PhoneNumber = "+31684207362",
+ UserName = "test"
+ });
+ });
+#pragma warning restore 612, 618
+ }
+}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20240729183448_InitialMigration.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20241030140343_InitialMigration.cs
similarity index 96%
rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20240729183448_InitialMigration.cs
rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20241030140343_InitialMigration.cs
index a62a7b6db8..d859fcd8ef 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20240729183448_InitialMigration.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/20241030140343_InitialMigration.cs
@@ -33,7 +33,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
migrationBuilder.InsertData(
table: "Users",
columns: new[] { "Id", "BirthDate", "Email", "FullName", "Gender", "Password", "PhoneNumber", "ProfileImageName", "UserName" },
- values: new object[] { new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), 1306790461440000000L, "test@bitplatform.dev", "Boilerplate test account", 2, "123456", "+31684207362", null, "test" });
+ values: new object[] { new Guid("8ff71671-a1d6-4f97-abb9-d87d7b47d6e7"), 1306790461440000000L, "test@bitplatform.dev", "Boilerplate test account", 0, "123456", "+31684207362", null, "test" });
}
///
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/OfflineDbContextModelSnapshot.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/OfflineDbContextModelSnapshot.cs
index 23b5541582..13cca205c7 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/OfflineDbContextModelSnapshot.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/Migrations/OfflineDbContextModelSnapshot.cs
@@ -12,7 +12,7 @@ partial class OfflineDbContextModelSnapshot : ModelSnapshot
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
- modelBuilder.HasAnnotation("ProductVersion", "8.0.7");
+ modelBuilder.HasAnnotation("ProductVersion", "8.0.0");
modelBuilder.Entity("Boilerplate.Shared.Dtos.Identity.UserDto", b =>
{
@@ -58,7 +58,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
BirthDate = 1306790461440000000L,
Email = "test@bitplatform.dev",
FullName = "Boilerplate test account",
- Gender = 2,
+ Gender = 0,
Password = "123456",
PhoneNumber = "+31684207362",
UserName = "test"
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IJSRuntimeExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IJSRuntimeExtensions.cs
index 48518d9a2f..0f67bd17ca 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IJSRuntimeExtensions.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IJSRuntimeExtensions.cs
@@ -13,6 +13,11 @@ public static ValueTask GetBrowserPlatform(this IJSRuntime jsRuntime)
return jsRuntime.InvokeAsync("App.getPlatform");
}
+ public static ValueTask GetTimeZone(this IJSRuntime jsRuntime)
+ {
+ return jsRuntime.InvokeAsync("App.getTimeZone");
+ }
+
public static ValueTask ApplyBodyElementClasses(this IJSRuntime jsRuntime, List cssClasses, Dictionary cssVariables)
{
return jsRuntime.InvokeVoidAsync("App.applyBodyElementClasses", cssClasses, cssVariables);
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Scripts/app.ts b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Scripts/app.ts
index c30141ce57..51a3499a82 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Scripts/app.ts
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Scripts/app.ts
@@ -9,6 +9,10 @@ class App {
return (navigator as any).userAgentData?.platform || navigator?.platform;
}
+ public static getTimeZone(): string {
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
+ }
+
//#if (notification == true)
public static async getDeviceInstallation(vapidPublicKey: string) {
if (await Notification.requestPermission() != "granted")
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppTelemetryContext.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppTelemetryContext.cs
index 4b7085ebdf..2ed22276d9 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppTelemetryContext.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AppTelemetryContext.cs
@@ -1,4 +1,5 @@
-using System.Runtime.InteropServices;
+//+:cnd:noEmit
+using System.Runtime.InteropServices;
namespace Boilerplate.Client.Core.Services;
@@ -15,4 +16,14 @@ public class AppTelemetryContext : ITelemetryContext
public virtual string? AppVersion { get; set; } = typeof(AppTelemetryContext).Assembly.GetName().Version?.ToString();
public virtual string? WebView { get; set; }
+
+ public virtual string? UserAgent { get; set; }
+
+ public string? TimeZone { get; set; }
+
+ public string? Culture { get; set; } = CultureInfo.CurrentCulture.Name;
+
+ //#if (signalr == true)
+ public bool IsOnline { get; set; }
+ //#endif
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/ITelemetryContext.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/ITelemetryContext.cs
index 1994fa7260..151724f57a 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/ITelemetryContext.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/ITelemetryContext.cs
@@ -1,4 +1,5 @@
-namespace Boilerplate.Client.Core.Services.Contracts;
+//+:cnd:noEmit
+namespace Boilerplate.Client.Core.Services.Contracts;
public interface ITelemetryContext
{
@@ -34,6 +35,14 @@ public static ITelemetryContext? Current
public string? OS { get; set; }
public string? AppVersion { get; set; }
-
public string? WebView { get; set; }
+
+ public string? UserAgent { get; set; }
+
+ public string? TimeZone { get; set; }
+ public string? Culture { get; set; }
+
+ //#if (signalr == true)
+ public bool IsOnline { get; set; }
+ //#endif
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ExceptionHandlerBase.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ExceptionHandlerBase.cs
index 4c3b4a4880..ce80ffe334 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ExceptionHandlerBase.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ExceptionHandlerBase.cs
@@ -1,4 +1,4 @@
-//-:cnd:noEmit
+//+:cnd:noEmit
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using System.Runtime.CompilerServices;
@@ -32,6 +32,12 @@ public void Handle(Exception exp,
parameters[nameof(TelemetryContext.AppSessionId)] = TelemetryContext.AppSessionId;
parameters[nameof(TelemetryContext.AppVersion)] = TelemetryContext.AppVersion;
parameters[nameof(TelemetryContext.OS)] = TelemetryContext.OS;
+ parameters[nameof(TelemetryContext.UserAgent)] = TelemetryContext.UserAgent;
+ parameters[nameof(TelemetryContext.TimeZone)] = TelemetryContext.TimeZone;
+ parameters[nameof(TelemetryContext.Culture)] = TelemetryContext.Culture;
+ //#if (signalr == true)
+ parameters[nameof(TelemetryContext.IsOnline)] = TelemetryContext.IsOnline;
+ //#endif
if (AppPlatform.IsBlazorHybrid)
{
parameters[nameof(TelemetryContext.WebView)] = TelemetryContext.WebView;
@@ -44,19 +50,28 @@ protected virtual void Handle(Exception exception, Dictionary pa
{
var isDevEnv = AppEnvironment.IsDev();
- string exceptionMessage = (exception as KnownException)?.Message ??
+ using (var scope = Logger.BeginScope(parameters.ToDictionary(i => i.Key, i => i.Value ?? string.Empty)))
+ {
+ var exceptionMessage = exception.Message;
+ var innerException = exception.InnerException;
+
+ while (innerException is not null)
+ {
+ exceptionMessage += $"{Environment.NewLine}{innerException.Message}";
+ innerException = innerException.InnerException;
+ }
+
+ Logger.LogError(exception, exceptionMessage);
+ }
+
+ string displayableExceptionMessage = (exception as KnownException)?.Message ??
(isDevEnv ? exception.ToString() : Localizer[nameof(AppStrings.UnknownException)]);
+ MessageBoxService.Show(displayableExceptionMessage, Localizer[nameof(AppStrings.Error)]);
+
if (isDevEnv)
{
Debugger.Break();
}
-
- using (var scope = Logger.BeginScope(parameters.ToDictionary(i => i.Key, i => i.Value ?? string.Empty)))
- {
- Logger.LogError(exception, exceptionMessage);
- }
-
- MessageBoxService.Show(exceptionMessage, Localizer[nameof(AppStrings.Error)]);
}
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PushNotificationServiceBase.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PushNotificationServiceBase.cs
index bff1737d24..ed43ab5e26 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PushNotificationServiceBase.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/PushNotificationServiceBase.cs
@@ -23,6 +23,12 @@ public async Task RegisterDevice(CancellationToken cancellationToken)
var deviceInstallation = await GetDeviceInstallation(cancellationToken);
+ if (deviceInstallation is null)
+ {
+ Logger.LogInformation("Could not retrieve device installation"); // Browser's incognito mode etc.
+ return;
+ }
+
await pushNotificationController.RegisterDevice(deviceInstallation, cancellationToken);
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.json
index 6ef2df5903..c78c02f339 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.json
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/appsettings.json
@@ -1,6 +1,6 @@
{
//#if (api == "Integrated")
- "ServerAddress": "/",
+ "ServerAddress": "http://localhost:5030/",
//#endif
//#if (IsInsideProjectTemplate)
/*
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Boilerplate.Client.Maui.csproj b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Boilerplate.Client.Maui.csproj
index 2fe1328118..7977ae1e6c 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Boilerplate.Client.Maui.csproj
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Boilerplate.Client.Maui.csproj
@@ -30,7 +30,6 @@
BeforeBuildTasks;
$(ResolveStaticWebAssetsInputsDependsOn)
- false
@@ -76,6 +75,7 @@
+
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/AndroidManifest.xml b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/AndroidManifest.xml
index 458e80e9f0..160ca5411f 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/AndroidManifest.xml
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/AndroidManifest.xml
@@ -1,4 +1,5 @@
+
\ No newline at end of file
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainActivity.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainActivity.cs
index b36da5b143..c8b9af599a 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainActivity.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/MainActivity.cs
@@ -45,6 +45,9 @@ public partial class MainActivity : MauiAppCompatActivity
protected override void OnCreate(Bundle? savedInstanceState)
{
+ // https://github.com/dotnet/maui/issues/24742
+ Theme?.ApplyStyle(Resource.Style.OptOutEdgeToEdgeEnforcement, force: false);
+
base.OnCreate(savedInstanceState);
var url = Intent?.DataString;
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Resources/values/styles.xml b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Resources/values/styles.xml
new file mode 100644
index 0000000000..f7567fa256
--- /dev/null
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Resources/values/styles.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Resources/values/values-v35/styles.xml b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Resources/values/values-v35/styles.xml
new file mode 100644
index 0000000000..955b6c267c
--- /dev/null
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Resources/values/values-v35/styles.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Services/AndroidPushNotificationService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Services/AndroidPushNotificationService.cs
index e1ebff36f9..a7eb06e454 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Services/AndroidPushNotificationService.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/Android/Services/AndroidPushNotificationService.cs
@@ -28,7 +28,7 @@ public override async Task GetDeviceInstallation(Cancella
{
try
{
- using CancellationTokenSource cts = new(TimeSpan.FromSeconds(30));
+ using CancellationTokenSource cts = new(TimeSpan.FromSeconds(15));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cts.Token);
while (string.IsNullOrEmpty(Token))
@@ -39,12 +39,9 @@ public override async Task GetDeviceInstallation(Cancella
await Task.Delay(TimeSpan.FromSeconds(1), linkedCts.Token);
}
}
- finally
+ catch (Exception exp)
{
- if (Token is null)
- {
- Logger.LogError("Unable to resolve token for FCMv1.");
- }
+ throw new InvalidOperationException("Unable to resolve token for FCMv1.", exp);
}
var installation = new DeviceInstallationDto
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Services/MacCatalystPushNotificationService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Services/MacCatalystPushNotificationService.cs
index cb6e1f4901..6efef0f826 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Services/MacCatalystPushNotificationService.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/MacCatalyst/Services/MacCatalystPushNotificationService.cs
@@ -24,7 +24,7 @@ public async override Task IsNotificationSupported(CancellationToken cance
public override async Task GetDeviceInstallation(CancellationToken cancellationToken)
{
- using CancellationTokenSource cts = new(TimeSpan.FromSeconds(30));
+ using CancellationTokenSource cts = new(TimeSpan.FromSeconds(15));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cts.Token);
try
@@ -37,12 +37,9 @@ public override async Task GetDeviceInstallation(Cancella
await Task.Delay(TimeSpan.FromSeconds(1), linkedCts.Token);
}
}
- finally
+ catch (Exception exp)
{
- if (Token is null)
- {
- Logger.LogError("Unable to resolve token for APNS.");
- }
+ throw new InvalidOperationException("Unable to resolve token for APNS.", exp);
}
var installation = new DeviceInstallationDto
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Debug.plist b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Development.plist
similarity index 100%
rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Debug.plist
rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Development.plist
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Release.plist b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Production.plist
similarity index 100%
rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Release.plist
rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Entitlements.Production.plist
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Services/iOSPushNotificationService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Services/iOSPushNotificationService.cs
index 388630f4c2..9cd1918814 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Services/iOSPushNotificationService.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Platforms/iOS/Services/iOSPushNotificationService.cs
@@ -24,7 +24,7 @@ public async override Task IsNotificationSupported(CancellationToken cance
public override async Task GetDeviceInstallation(CancellationToken cancellationToken)
{
- using CancellationTokenSource cts = new(TimeSpan.FromSeconds(30));
+ using CancellationTokenSource cts = new(TimeSpan.FromSeconds(15));
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cts.Token);
try
@@ -37,12 +37,9 @@ public override async Task GetDeviceInstallation(Cancella
await Task.Delay(TimeSpan.FromSeconds(1), linkedCts.Token);
}
}
- finally
+ catch (Exception exp)
{
- if (Token is null)
- {
- Logger.LogError("Unable to resolve token for APNS.");
- }
+ throw new InvalidOperationException("Unable to resolve token for APNS.", exp);
}
var installation = new DeviceInstallationDto
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiTelemetryInitializer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiTelemetryInitializer.cs
index f09f13cb75..9a3212603c 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiTelemetryInitializer.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Maui/Services/MauiTelemetryInitializer.cs
@@ -1,4 +1,5 @@
-using Microsoft.ApplicationInsights.Channel;
+//+:cnd:noEmit
+using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.Extensibility;
namespace Boilerplate.Client.Maui.Services;
@@ -16,6 +17,12 @@ public void Initialize(ITelemetry telemetry)
telemetry.Context.GlobalProperties[nameof(ITelemetryContext.UserSessionId)] = ITelemetryContext.Current.UserSessionId?.ToString();
telemetry.Context.GlobalProperties[nameof(ITelemetryContext.WebView)] = ITelemetryContext.Current.WebView;
+ telemetry.Context.GlobalProperties[nameof(ITelemetryContext.UserAgent)] = ITelemetryContext.Current.UserAgent;
+ telemetry.Context.GlobalProperties[nameof(ITelemetryContext.TimeZone)] = ITelemetryContext.Current.TimeZone;
+ telemetry.Context.GlobalProperties[nameof(ITelemetryContext.Culture)] = ITelemetryContext.Current.Culture;
+ //#if (signalr == true)
+ telemetry.Context.GlobalProperties[nameof(ITelemetryContext.IsOnline)] = ITelemetryContext.Current.IsOnline.ToString().ToLowerInvariant();
+ //#endif
}
telemetry.Context.Session.IsFirst = VersionTracking.IsFirstLaunchEver;
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/appsettings.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/appsettings.json
index 5ce72aad16..5fe62e7f93 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/appsettings.json
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/appsettings.json
@@ -2,7 +2,7 @@
"WebAppRender": {
"PrerenderEnabled": false,
"BlazorMode": "BlazorServer",
- "BlazorMode_Comment": "Default value of Client.Core/appsettings.Production.json is BlazorAuto"
+ "BlazorMode_Comment": "BlazorServer, BlazorWebAssembly and BlazorAuto. Default value of Client.Core/appsettings.Production.json is BlazorAuto"
},
//#if (notification == true)
"AdsPushVapid_Comment": "https://github.com/adessoTurkey-dotNET/AdsPush",
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsTelemetryInitializer.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsTelemetryInitializer.cs
index 49cf1b2731..6d0d05fc4e 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsTelemetryInitializer.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Windows/Services/WindowsTelemetryInitializer.cs
@@ -1,4 +1,5 @@
-using Microsoft.ApplicationInsights.Channel;
+//+:cnd:noEmit
+using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.Extensibility;
namespace Boilerplate.Client.Windows.Services;
@@ -16,6 +17,12 @@ public void Initialize(ITelemetry telemetry)
telemetry.Context.GlobalProperties[nameof(ITelemetryContext.UserSessionId)] = ITelemetryContext.Current.UserSessionId?.ToString();
telemetry.Context.GlobalProperties[nameof(ITelemetryContext.WebView)] = ITelemetryContext.Current.WebView;
+ telemetry.Context.GlobalProperties[nameof(ITelemetryContext.UserAgent)] = ITelemetryContext.Current.UserAgent;
+ telemetry.Context.GlobalProperties[nameof(ITelemetryContext.TimeZone)] = ITelemetryContext.Current.TimeZone;
+ telemetry.Context.GlobalProperties[nameof(ITelemetryContext.Culture)] = ITelemetryContext.Current.Culture;
+ //#if (signalr == true)
+ telemetry.Context.GlobalProperties[nameof(ITelemetryContext.IsOnline)] = ITelemetryContext.Current.IsOnline.ToString().ToLowerInvariant();
+ //#endif
}
}
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs
index 986753a196..afaac9335c 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs
@@ -44,23 +44,17 @@ public async Task> GetUserSessions(CancellationToken cancel
?? throw new ResourceNotFoundException();
return user.Sessions
- .OrderByDescending(s => s.RenewedOn)
.Select(us =>
{
var dto = us.Map();
- var lastSeenDateTime = us.RenewedOn ?? us.StartedOn;
+ dto.RenewedOn = us.RenewedOn ?? us.StartedOn;
- dto.LastSeenOn = DateTimeOffset.UtcNow - lastSeenDateTime < TimeSpan.FromMinutes(5)
- ? Localizer[nameof(AppStrings.Online)]
- : DateTimeOffset.UtcNow - lastSeenDateTime < TimeSpan.FromMinutes(15)
- ? Localizer[nameof(AppStrings.Recently)]
- : lastSeenDateTime.Humanize(culture: CultureInfo.CurrentUICulture);
-
- dto.IsValid = DateTimeOffset.UtcNow - lastSeenDateTime < AppSettings.Identity.RefreshTokenExpiration;
+ dto.IsValid = DateTimeOffset.UtcNow - dto.RenewedOn < AppSettings.Identity.RefreshTokenExpiration;
return dto;
})
+ .OrderByDescending(us => us.RenewedOn)
.ToList();
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs
index 1d9cef9d7c..b8e2589ee5 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs
@@ -69,7 +69,7 @@ public static void AddServerApiProjectServices(this WebApplicationBuilder builde
var webClientUrl = configuration.Get()!.WebClientUrl;
policy.SetIsOriginAllowed(origin =>
- LocalhostOriginRegex().IsMatch(origin) ||
+ AllowedOriginsRegex().IsMatch(origin) ||
(string.IsNullOrEmpty(webClientUrl) is false && string.Equals(origin, webClientUrl, StringComparison.InvariantCultureIgnoreCase)))
.AllowAnyHeader()
.AllowAnyMethod()
@@ -406,8 +406,8 @@ private static void AddSwaggerGen(WebApplicationBuilder builder)
}
///
- /// For either Blazor Hybrid web view or localhost in dev environment.
+ /// For either Blazor Hybrid web view, localhost, dev tunnels etc in dev environment.
///
- [GeneratedRegex(@"^(http|https|app):\/\/(localhost|0\.0\.0\.0|0\.0\.0\.1|127\.0\.0\.1)(:\d+)?(\/.*)?$")]
- private static partial Regex LocalhostOriginRegex();
+ [GeneratedRegex(@"^(http|https|app):\/\/(localhost|0\.0\.0\.0|0\.0\.0\.1|127\.0\.0\.1|.*?devtunnels\.ms|.*?github\.dev)(:\d+)?(\/.*)?$")]
+ private static partial Regex AllowedOriginsRegex();
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/.config/dotnet-tools.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/.config/dotnet-tools.json
new file mode 100644
index 0000000000..40d4373bd4
--- /dev/null
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/.config/dotnet-tools.json
@@ -0,0 +1,12 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "dotnet-ef": {
+ "version": "8.0.10",
+ "commands": [
+ "dotnet-ef"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserSessionDto.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserSessionDto.cs
index cb7b7c6d67..d2bb317b9a 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserSessionDto.cs
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Identity/UserSessionDto.cs
@@ -10,7 +10,7 @@ public partial class UserSessionDto
public string? Address { get; set; }
- public string? LastSeenOn { get; set; }
+ public DateTimeOffset RenewedOn { get; set; }
public bool IsValid { get; set; }
}
diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.nl.resx b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.nl.resx
index 6fc0f1473c..3873a39d58 100644
--- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.nl.resx
+++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.nl.resx
@@ -210,9 +210,6 @@
Taal selecteren
-
-
- Instellingen
Profiel
@@ -806,9 +803,6 @@
2FA
-
-
-
Het resetten van de gedeelde 2fa-sleutel moet 2fa uitschakelen totdat een 2fa-token op basis van de nieuwe gedeelde sleutel is gevalideerd.