diff --git a/.github/workflows/admin-sample.cd.yml b/.github/workflows/admin-sample.cd.yml index 3df32a5f0b..4f26fb8605 100644 --- a/.github/workflows/admin-sample.cd.yml +++ b/.github/workflows/admin-sample.cd.yml @@ -162,7 +162,7 @@ jobs: - name: Publish run: | cd AdminPanel\src\Client\AdminPanel.Client.Windows\ - dotnet publish AdminPanel.Client.Windows.csproj -c Release -o .\publish-result -r win-x86 -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" + dotnet publish AdminPanel.Client.Windows.csproj -c Release -o .\publish-result -r win-x86 -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:CompressionEnabled=false dotnet tool restore dotnet vpk pack -u AdminPanel.Client.Windows -v "${{ vars.APPLICATION_DISPLAY_VERSION }}" -p .\publish-result -e AdminPanel.Client.Windows.exe -r win-x86 --framework net9.0-x86-desktop,webview2 --icon .\wwwroot\favicon.ico --packTitle 'AdminPanel' @@ -236,7 +236,7 @@ jobs: dotnet build AdminPanel/src/Client/AdminPanel.Client.Maui/AdminPanel.Client.Maui.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" --no-restore -c Release - name: Build aab - run: dotnet build AdminPanel/src/Client/AdminPanel.Client.Maui/AdminPanel.Client.Maui.csproj -c Release -p:AndroidPackageFormat=aab -p:AndroidKeyStore=true -p:AndroidSigningKeyStore="AdminPanel.keystore" -p:AndroidSigningKeyAlias=bitplatform -p:AndroidSigningKeyPass="${{ secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD }}" -p:AndroidSigningStorePass="${{ secrets.ANDROID_RELEASE_SIGNING_PASSWORD }}" -p:ApplicationDisplayVersion="${{ vars.APPLICATION_DISPLAY_VERSION }}" -p:ApplicationVersion="${{ vars.APPLICATION_VERSION }}" -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:ApplicationTitle="AdminPanel" -p:ApplicationId="com.bitplatform.AdminPanel.Template" -f net9.0-android + run: dotnet publish AdminPanel/src/Client/AdminPanel.Client.Maui/AdminPanel.Client.Maui.csproj -c Release -p:AndroidPackageFormat=aab -p:AndroidKeyStore=true -p:AndroidSigningKeyStore="AdminPanel.keystore" -p:AndroidSigningKeyAlias=bitplatform -p:AndroidSigningKeyPass="${{ secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD }}" -p:AndroidSigningStorePass="${{ secrets.ANDROID_RELEASE_SIGNING_PASSWORD }}" -p:ApplicationDisplayVersion="${{ vars.APPLICATION_DISPLAY_VERSION }}" -p:ApplicationVersion="${{ vars.APPLICATION_VERSION }}" -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:ApplicationTitle="AdminPanel" -p:ApplicationId="com.bitplatform.AdminPanel.Template" -p:CompressionEnabled=false -f net9.0-android - name: Upload artifact uses: actions/upload-artifact@v4 @@ -309,7 +309,7 @@ jobs: dotnet build AdminPanel/src/Client/AdminPanel.Client.Maui/AdminPanel.Client.Maui.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" --no-restore -c Release - name: Build ipa - run: dotnet publish AdminPanel/src/Client/AdminPanel.Client.Maui/AdminPanel.Client.Maui.csproj -p:RuntimeIdentifier=ios-arm64 -c Release -p:ArchiveOnBuild=true -p:CodesignKey="iPhone Distribution" -p:CodesignProvision="AdminPanel" -p:ApplicationDisplayVersion="${{ vars.APPLICATION_DISPLAY_VERSION }}" -p:ApplicationVersion="${{ vars.APPLICATION_VERSION }}" -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:ApplicationTitle="AdminPanel" -p:ApplicationId="com.bitplatform.AdminPanel.Template" -f net9.0-ios + run: dotnet publish AdminPanel/src/Client/AdminPanel.Client.Maui/AdminPanel.Client.Maui.csproj -p:RuntimeIdentifier=ios-arm64 -c Release -p:ArchiveOnBuild=true -p:CodesignKey="iPhone Distribution" -p:CodesignProvision="AdminPanel" -p:ApplicationDisplayVersion="${{ vars.APPLICATION_DISPLAY_VERSION }}" -p:ApplicationVersion="${{ vars.APPLICATION_VERSION }}" -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:ApplicationTitle="AdminPanel" -p:ApplicationId="com.bitplatform.AdminPanel.Template" -p:CompressionEnabled=false -f net9.0-ios - name: Upload artifact uses: actions/upload-artifact@v4 diff --git a/.github/workflows/blazorui.demo.cd.yml b/.github/workflows/blazorui.demo.cd.yml index 1217f45e80..4babd7c6d2 100644 --- a/.github/workflows/blazorui.demo.cd.yml +++ b/.github/workflows/blazorui.demo.cd.yml @@ -1,4 +1,4 @@ -name: Blazor UI Demo CD +name: Blazor UI Demo CD env: SERVER_ADDRESS: 'https://blazorui.bitplatform.dev/api/' @@ -120,7 +120,7 @@ jobs: - name: Publish run: | cd src\BlazorUI\Demo\Client\Bit.BlazorUI.Demo.Client.Windows\ - dotnet publish Bit.BlazorUI.Demo.Client.Windows.csproj -c Release -o .\publish-result -r win-x86 -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" + dotnet publish Bit.BlazorUI.Demo.Client.Windows.csproj -c Release -o .\publish-result -r win-x86 -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:CompressionEnabled=false dotnet tool restore dotnet vpk pack -u Bit.BlazorUI.Demo.Client.Windows -v "${{ vars.APPLICATION_DISPLAY_VERSION }}" -p .\publish-result -e Bit.BlazorUI.Demo.Client.Windows.exe -r win-x86 --framework net9.0-x86-desktop,webview2 --icon .\wwwroot\favicon.ico --packTitle 'Bit Blazor UI' @@ -172,7 +172,7 @@ jobs: run: dotnet build src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -c Release - name: Build aab - run: dotnet build src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj -c Release -p:AndroidPackageFormat=aab -p:AndroidKeyStore=true -p:AndroidSigningKeyStore="BitBlazorUIDemo.keystore" -p:AndroidSigningKeyAlias=bitplatform -p:AndroidSigningKeyPass="${{ secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD }}" -p:AndroidSigningStorePass="${{ secrets.ANDROID_RELEASE_SIGNING_PASSWORD }}" -p:ApplicationDisplayVersion="${{ vars.APPLICATION_DISPLAY_VERSION }}" -p:ApplicationVersion="${{ vars.APPLICATION_VERSION }}" -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -f net9.0-android + run: dotnet publish src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj -c Release -p:AndroidPackageFormat=aab -p:AndroidKeyStore=true -p:AndroidSigningKeyStore="BitBlazorUIDemo.keystore" -p:AndroidSigningKeyAlias=bitplatform -p:AndroidSigningKeyPass="${{ secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD }}" -p:AndroidSigningStorePass="${{ secrets.ANDROID_RELEASE_SIGNING_PASSWORD }}" -p:ApplicationDisplayVersion="${{ vars.APPLICATION_DISPLAY_VERSION }}" -p:ApplicationVersion="${{ vars.APPLICATION_VERSION }}" -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:CompressionEnabled=false -f net9.0-android - name: Upload artifact uses: actions/upload-artifact@v4 @@ -230,7 +230,7 @@ jobs: run: dotnet build src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -c Release - name: Build ipa - run: dotnet publish src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj -p:RuntimeIdentifier=ios-arm64 -c Release -p:ArchiveOnBuild=true -p:CodesignKey="iPhone Distribution" -p:CodesignProvision="Bit Blazor UI Demo" -p:ApplicationDisplayVersion="${{ vars.APPLICATION_DISPLAY_VERSION }}" -p:ApplicationVersion="${{ vars.APPLICATION_VERSION }}" -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -f net9.0-ios + run: dotnet publish src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj -p:RuntimeIdentifier=ios-arm64 -c Release -p:ArchiveOnBuild=true -p:CodesignKey="iPhone Distribution" -p:CodesignProvision="Bit Blazor UI Demo" -p:ApplicationDisplayVersion="${{ vars.APPLICATION_DISPLAY_VERSION }}" -p:ApplicationVersion="${{ vars.APPLICATION_VERSION }}" -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:CompressionEnabled=false -f net9.0-ios - name: Upload artifact uses: actions/upload-artifact@v4 diff --git a/.github/workflows/todo-sample.cd.yml b/.github/workflows/todo-sample.cd.yml index ef492cf4cb..5b7cc8156a 100644 --- a/.github/workflows/todo-sample.cd.yml +++ b/.github/workflows/todo-sample.cd.yml @@ -173,7 +173,7 @@ jobs: - name: Publish run: | cd TodoSample\src\Client\TodoSample.Client.Windows\ - dotnet publish TodoSample.Client.Windows.csproj -c Release -o .\publish-result -r win-x86 -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" + dotnet publish TodoSample.Client.Windows.csproj -c Release -o .\publish-result -r win-x86 -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:CompressionEnabled=false dotnet tool restore dotnet vpk pack -u TodoSample.Client.Windows -v "${{ vars.APPLICATION_DISPLAY_VERSION }}" -p .\publish-result -e TodoSample.Client.Windows.exe -r win-x86 --framework net8.0-x86-desktop,webview2 --icon .\wwwroot\favicon.ico --packTitle TodoSample @@ -271,7 +271,7 @@ jobs: dotnet build TodoSample/src/Client/TodoSample.Client.Maui/TodoSample.Client.Maui.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" --no-restore -c Release - name: Build aab - run: dotnet build TodoSample/src/Client/TodoSample.Client.Maui/TodoSample.Client.Maui.csproj -c Release -p:AndroidPackageFormat=aab -p:AndroidKeyStore=true -p:AndroidSigningKeyStore="TodoSample.keystore" -p:AndroidSigningKeyAlias=bitplatform -p:AndroidSigningKeyPass="${{ secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD }}" -p:AndroidSigningStorePass="${{ secrets.ANDROID_RELEASE_SIGNING_PASSWORD }}" -p:ApplicationDisplayVersion="${{ vars.APPLICATION_DISPLAY_VERSION }}" -p:ApplicationVersion="${{ vars.APPLICATION_VERSION }}" -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:ApplicationTitle="TodoSample" -p:ApplicationId="com.bitplatform.Todo.Template" -f net8.0-android + run: dotnet publish TodoSample/src/Client/TodoSample.Client.Maui/TodoSample.Client.Maui.csproj -c Release -p:AndroidPackageFormat=aab -p:AndroidKeyStore=true -p:AndroidSigningKeyStore="TodoSample.keystore" -p:AndroidSigningKeyAlias=bitplatform -p:AndroidSigningKeyPass="${{ secrets.ANDROID_RELEASE_KEYSTORE_PASSWORD }}" -p:AndroidSigningStorePass="${{ secrets.ANDROID_RELEASE_SIGNING_PASSWORD }}" -p:ApplicationDisplayVersion="${{ vars.APPLICATION_DISPLAY_VERSION }}" -p:ApplicationVersion="${{ vars.APPLICATION_VERSION }}" -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:ApplicationTitle="TodoSample" -p:ApplicationId="com.bitplatform.Todo.Template" -p:CompressionEnabled=false -f net8.0-android - name: Upload artifact uses: actions/upload-artifact@v4 @@ -368,7 +368,7 @@ jobs: dotnet build TodoSample/src/Client/TodoSample.Client.Maui/TodoSample.Client.Maui.csproj -t:BeforeBuildTasks -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" --no-restore -c Release - name: Build ipa - run: dotnet publish TodoSample/src/Client/TodoSample.Client.Maui/TodoSample.Client.Maui.csproj -p:RuntimeIdentifier=ios-arm64 -c Release -p:ArchiveOnBuild=true -p:CodesignKey="iPhone Distribution" -p:CodesignProvision="TodoTemplate" -p:ApplicationDisplayVersion="${{ vars.APPLICATION_DISPLAY_VERSION }}" -p:ApplicationVersion="${{ vars.APPLICATION_VERSION }}" -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:ApplicationTitle="Todo" -p:ApplicationId="com.bitplatform.Todo.Template" -f net8.0-ios + run: dotnet publish TodoSample/src/Client/TodoSample.Client.Maui/TodoSample.Client.Maui.csproj -p:RuntimeIdentifier=ios-arm64 -c Release -p:ArchiveOnBuild=true -p:CodesignKey="iPhone Distribution" -p:CodesignProvision="TodoTemplate" -p:ApplicationDisplayVersion="${{ vars.APPLICATION_DISPLAY_VERSION }}" -p:ApplicationVersion="${{ vars.APPLICATION_VERSION }}" -p:Version="${{ vars.APPLICATION_DISPLAY_VERSION}}" -p:ApplicationTitle="Todo" -p:ApplicationId="com.bitplatform.Todo.Template" -p:CompressionEnabled=false -f net8.0-ios - name: Upload artifact uses: actions/upload-artifact@v4 diff --git a/src/Besql/Bit.Besql/wwwroot/bit-besql.js b/src/Besql/Bit.Besql/wwwroot/bit-besql.js index 40f2c9d96b..80eea970b8 100644 --- a/src/Besql/Bit.Besql/wwwroot/bit-besql.js +++ b/src/Besql/Bit.Besql/wwwroot/bit-besql.js @@ -1,5 +1,5 @@ var BitBesql = BitBesql || {}; -BitBesql.version = window['bit-besql version'] = '9.0.0'; +BitBesql.version = window['bit-besql version'] = '9.0.1'; async function synchronizeDbWithCache(file) { diff --git a/src/Bit.Build.props b/src/Bit.Build.props index acbd21534e..d027cff25e 100644 --- a/src/Bit.Build.props +++ b/src/Bit.Build.props @@ -25,7 +25,7 @@ https://github.com/bitfoundation/bitplatform https://avatars.githubusercontent.com/u/22663390 - 9.0.0 + 9.0.1 https://github.com/bitfoundation/bitplatform/releases/tag/v-$(ReleaseVersion) $(ReleaseVersion) diff --git a/src/BlazorUI/Bit.BlazorUI.Assets/package-lock.json b/src/BlazorUI/Bit.BlazorUI.Assets/package-lock.json index 3eaa56c719..c44ce790e7 100644 --- a/src/BlazorUI/Bit.BlazorUI.Assets/package-lock.json +++ b/src/BlazorUI/Bit.BlazorUI.Assets/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "devDependencies": { - "sass": "1.80.5" + "sass": "1.81.0" } }, "node_modules/@parcel/watcher": { @@ -437,7 +437,7 @@ } }, "node_modules/sass": { - "version": "1.80.5", + "version": "1.81.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, diff --git a/src/BlazorUI/Bit.BlazorUI.Assets/package.json b/src/BlazorUI/Bit.BlazorUI.Assets/package.json index 86b9c82cc5..dc8d9024e1 100644 --- a/src/BlazorUI/Bit.BlazorUI.Assets/package.json +++ b/src/BlazorUI/Bit.BlazorUI.Assets/package.json @@ -1,5 +1,5 @@ { "devDependencies": { - "sass": "1.80.5" + "sass": "1.81.0" } } diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/package-lock.json b/src/BlazorUI/Bit.BlazorUI.Extras/package-lock.json index aed183cc40..e6437ed41f 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/package-lock.json +++ b/src/BlazorUI/Bit.BlazorUI.Extras/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } }, @@ -887,7 +887,7 @@ } }, "node_modules/sass": { - "version": "1.80.5", + "version": "1.81.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, diff --git a/src/BlazorUI/Bit.BlazorUI.Extras/package.json b/src/BlazorUI/Bit.BlazorUI.Extras/package.json index 8f2181ad89..7d426923cd 100644 --- a/src/BlazorUI/Bit.BlazorUI.Extras/package.json +++ b/src/BlazorUI/Bit.BlazorUI.Extras/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } } diff --git a/src/BlazorUI/Bit.BlazorUI.Icons/package-lock.json b/src/BlazorUI/Bit.BlazorUI.Icons/package-lock.json index 7acb7a7739..349aa76f10 100644 --- a/src/BlazorUI/Bit.BlazorUI.Icons/package-lock.json +++ b/src/BlazorUI/Bit.BlazorUI.Icons/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "devDependencies": { - "sass": "1.80.5" + "sass": "1.81.0" } }, "node_modules/@parcel/watcher": { @@ -437,7 +437,7 @@ } }, "node_modules/sass": { - "version": "1.80.5", + "version": "1.81.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, diff --git a/src/BlazorUI/Bit.BlazorUI.Icons/package.json b/src/BlazorUI/Bit.BlazorUI.Icons/package.json index 86b9c82cc5..dc8d9024e1 100644 --- a/src/BlazorUI/Bit.BlazorUI.Icons/package.json +++ b/src/BlazorUI/Bit.BlazorUI.Icons/package.json @@ -1,5 +1,5 @@ { "devDependencies": { - "sass": "1.80.5" + "sass": "1.81.0" } } diff --git a/src/BlazorUI/Bit.BlazorUI.SourceGenerators/Bit.BlazorUI.SourceGenerators.csproj b/src/BlazorUI/Bit.BlazorUI.SourceGenerators/Bit.BlazorUI.SourceGenerators.csproj index ad1f0f2270..e357d464b3 100644 --- a/src/BlazorUI/Bit.BlazorUI.SourceGenerators/Bit.BlazorUI.SourceGenerators.csproj +++ b/src/BlazorUI/Bit.BlazorUI.SourceGenerators/Bit.BlazorUI.SourceGenerators.csproj @@ -1,7 +1,7 @@  netstandard2.0 - 12.0 + 13.0 enable true diff --git a/src/BlazorUI/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj b/src/BlazorUI/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj index f204e95372..f877b1856b 100644 --- a/src/BlazorUI/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj +++ b/src/BlazorUI/Bit.BlazorUI.Tests/Bit.BlazorUI.Tests.csproj @@ -3,7 +3,7 @@ net9.0;net8.0 false - 11.0 + 13.0 diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.razor.cs index f7bee17cbb..4a02682826 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/FileUpload/BitFileUpload.razor.cs @@ -192,12 +192,12 @@ public partial class BitFileUpload : BitComponentBase, IAsyncDisposable /// - /// All selected files. + /// A list of all of the selected files to upload. /// public IReadOnlyList? Files { get; private set; } /// - /// The general status of the upload. + /// The current status of the file uploader. /// public BitFileUploadStatus UploadStatus { get; private set; } @@ -207,7 +207,7 @@ public partial class BitFileUpload : BitComponentBase, IAsyncDisposable public string? InputId { get; private set; } /// - /// Indicates that the FileUpload is in the middle of removing a file. + /// Indicates that the file upload is in the middle of removing a file. /// public bool IsRemoving { get; private set; } @@ -328,7 +328,7 @@ public async Task Browse() } /// - /// Resets the file-upload. + /// Resets the file upload. /// public async Task Reset() { diff --git a/src/BlazorUI/Bit.BlazorUI/Scripts/general.ts b/src/BlazorUI/Bit.BlazorUI/Scripts/general.ts index 19a2b5482e..2c3b7f298c 100644 --- a/src/BlazorUI/Bit.BlazorUI/Scripts/general.ts +++ b/src/BlazorUI/Bit.BlazorUI/Scripts/general.ts @@ -1,4 +1,4 @@ -(BitBlazorUI as any).version = (window as any)['bit-blazorui version'] = '9.0.0'; +(BitBlazorUI as any).version = (window as any)['bit-blazorui version'] = '9.0.1'; interface DotNetObject { invokeMethod(methodIdentifier: string, ...args: any[]): T; diff --git a/src/BlazorUI/Bit.BlazorUI/package-lock.json b/src/BlazorUI/Bit.BlazorUI/package-lock.json index 1aa9f752e8..46e8db6a72 100644 --- a/src/BlazorUI/Bit.BlazorUI/package-lock.json +++ b/src/BlazorUI/Bit.BlazorUI/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } }, @@ -887,7 +887,7 @@ } }, "node_modules/sass": { - "version": "1.80.5", + "version": "1.81.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, diff --git a/src/BlazorUI/Bit.BlazorUI/package.json b/src/BlazorUI/Bit.BlazorUI/package.json index 8f2181ad89..7d426923cd 100644 --- a/src/BlazorUI/Bit.BlazorUI/package.json +++ b/src/BlazorUI/Bit.BlazorUI/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } } diff --git a/src/BlazorUI/Clean.sh b/src/BlazorUI/Clean.sh index fe004842e6..04061b11a9 100644 --- a/src/BlazorUI/Clean.sh +++ b/src/BlazorUI/Clean.sh @@ -1,23 +1,23 @@ #!/bin/bash -# This batch script is designed for comprehensive cleaning of your project by deleting unnecessary files. -# It's crucial to close any Integrated Development Environment (IDE), such as vs code, etc., before executing this script to prevent any conflicts or loss of unsaved data. -# Please note that the commands included in this script are specifically tailored for the Linux/macOS +# This batch script cleans your project by deleting unnecessary files. +# It is important to close any IDEs, such as vs for mac, before running this script to prevent conflicts or data loss. +# The commands in this script are specifically designed for macOS/Linux. -# Runs dotnet clean for each csproj file +# Runs the dotnet clean command for each .csproj file. for csproj in $(find . -name '*.csproj'); do dotnet clean $csproj done -# Delete specified directories +# Deletes specified directories for dir in $(find . -type d \( -name "bin" -o -name "obj" -o -name "node_modules" -o -name "Packages" -o -name ".vs" -o -name "TestResults" -o -name "AppPackages" -o -name ".meteor" \)); do rm -rf $dir done -# Delete specified files +# Deletes specified files for file in $(find . -type f \( -name "*.csproj.user" -o -name "Resources.designer.cs" -o -name "*.css" -o -name "*.min.css" -o -name "*.js" -o -name "*.min.js" -o -name "*.map" \)); do rm -f $file done -# Delete empty directories +# Deletes empty directories. find . -type d -empty -delete \ No newline at end of file diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj index 04d65f1029..72919897b2 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Server/Bit.BlazorUI.Demo.Server.csproj @@ -5,11 +5,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Shared/Bit.BlazorUI.Demo.Shared.csproj b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Shared/Bit.BlazorUI.Demo.Shared.csproj index 7413a7365b..7f73765f4e 100644 --- a/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Shared/Bit.BlazorUI.Demo.Shared.csproj +++ b/src/BlazorUI/Demo/Bit.BlazorUI.Demo.Shared/Bit.BlazorUI.Demo.Shared.csproj @@ -5,11 +5,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj index 342f95da8f..b0a3aff49b 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Bit.BlazorUI.Demo.Client.Core.csproj @@ -16,11 +16,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor index 4796099a5d..8de5f60f8a 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor @@ -9,7 +9,8 @@ ComponentDescription="BitFileUpload component wraps the HTML file input element(s) and uploads them to a given URL. The files can be removed by specifying the URL they have been uploaded. Moreover, it provides several other options including single or multiple or automatic file uploading. By automatic, it means the files can be automatically uploaded after being selected. It is possible to specify a maximum size for each file if need be. Additionally, by specifying file extensions, the files can be restricted to certain types." ComponentParameters="componentParameters" ComponentSubClasses="componentSubClasses" - ComponentSubEnums="componentSubEnums"> + ComponentSubEnums="componentSubEnums" + ComponentPublicMembers="componentPublicMembers"> diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor.cs index e3f94ef8a7..4eca488a6e 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/FileUpload/BitFileUploadDemo.razor.cs @@ -378,6 +378,92 @@ public partial class BitFileUploadDemo } ]; + private readonly List componentPublicMembers = + [ + new() + { + Name = "Files", + Type = "IReadOnlyList?", + DefaultValue = "null", + Description = "A list of all of the selected files to upload.", + LinkType = LinkType.Link, + Href = "#file-info" + }, + new() + { + Name = "UploadStatus", + Type = "BitFileUploadStatus", + DefaultValue = "", + Description = "The current status of the file uploader.", + LinkType = LinkType.Link, + Href = "#upload-status-enum" + }, + new() + { + Name = "InputId", + Type = "string?", + DefaultValue = "", + Description = "The id of the file input element.", + }, + new() + { + Name = "IsRemoving", + Type = "bool", + DefaultValue = "false", + Description = "Indicates that the file upload is in the middle of removing a file.", + }, + new() + { + Name = "Upload", + Type = "(BitFileInfo? fileInfo = null, string? uploadUrl = null) => Task", + DefaultValue = "", + Description = "Starts uploading the file(s).", + LinkType = LinkType.Link, + Href = "#file-info" + }, + new() + { + Name = "PauseUpload", + Type = "(BitFileInfo? fileInfo = null) => void", + DefaultValue = "", + Description = "Pauses the upload.", + LinkType = LinkType.Link, + Href = "#file-info" + }, + new() + { + Name = "CancelUpload", + Type = "(BitFileInfo? fileInfo = null) => void", + DefaultValue = "", + Description = "Cancels the upload.", + LinkType = LinkType.Link, + Href = "#file-info" + }, + new() + { + Name = "RemoveFile", + Type = "(BitFileInfo? fileInfo = null) => void", + DefaultValue = "", + Description = "Removes a file by calling the RemoveUrl if the file upload is already started.", + LinkType = LinkType.Link, + Href = "#file-info" + }, + new() + { + Name = "Browse", + Type = "Task", + DefaultValue = "", + Description = "Opens a file selection dialog.", + }, + new() + { + Name = "Reset", + Type = "Task", + DefaultValue = "", + Description = "Resets the file upload.", + } + ]; + [Inject] private IJSRuntime _js { get; set; } = default!; diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package-lock.json b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package-lock.json index 77b35e2ab3..c729e921b9 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package-lock.json +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } }, @@ -887,7 +887,7 @@ } }, "node_modules/sass": { - "version": "1.80.5", + "version": "1.81.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package.json b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package.json index 8f2181ad89..7d426923cd 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package.json +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj index bede5c90dc..793dde9936 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Maui/Bit.BlazorUI.Demo.Client.Maui.csproj @@ -85,12 +85,12 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/Bit.BlazorUI.Demo.Client.Web.csproj b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/Bit.BlazorUI.Demo.Client.Web.csproj index 9669882b93..1e3a1d062f 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/Bit.BlazorUI.Demo.Client.Web.csproj +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/Bit.BlazorUI.Demo.Client.Web.csproj @@ -24,13 +24,13 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/wwwroot/service-worker.published.js b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/wwwroot/service-worker.published.js index ee4d0babb7..85816e325d 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/wwwroot/service-worker.published.js +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Web/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 9.0.0 +// bit version: 9.0.1 // https://github.com/bitfoundation/bitplatform/tree/develop/src/Bswup self.assetsInclude = []; diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/Bit.BlazorUI.Demo.Client.Windows.csproj b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/Bit.BlazorUI.Demo.Client.Windows.csproj index fdad3c0caa..de6d94d797 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/Bit.BlazorUI.Demo.Client.Windows.csproj +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Windows/Bit.BlazorUI.Demo.Client.Windows.csproj @@ -17,16 +17,16 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/BlazorUI/Demo/Directory.Build.props b/src/BlazorUI/Demo/Directory.Build.props index 1eebaccf11..2edfb46a1b 100644 --- a/src/BlazorUI/Demo/Directory.Build.props +++ b/src/BlazorUI/Demo/Directory.Build.props @@ -1,7 +1,7 @@ - + - 12.0 + 13.0 enable enable true diff --git a/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js b/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js index 789b35f29c..c0266c3a56 100644 --- a/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js +++ b/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.js @@ -1,4 +1,4 @@ -// bit version: 9.0.0 +// bit version: 9.0.1 self.assetsExclude = [/\.scp\.css$/, /weather\.json$/]; self.caseInsensitiveUrl = true; diff --git a/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js b/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js index b4360e0b02..92efbc677b 100644 --- a/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js +++ b/src/Bswup/Bit.Bswup.Demo/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 9.0.0 +// bit version: 9.0.1 self.assetsExclude = [/\.scp\.css$/, /weather\.json$/]; self.caseInsensitiveUrl = true; diff --git a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.js b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.js index ef6e3c3556..59d16a2f49 100644 --- a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.js +++ b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.js @@ -1,4 +1,4 @@ -// bit version: 9.0.0 +// bit version: 9.0.1 // In development, always fetch from the network and do not enable offline support. // This is because caching would make development more difficult (changes would not diff --git a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.published.js b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.published.js index 535ec9404b..65741cc8e7 100644 --- a/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.published.js +++ b/src/Bswup/Bit.Bswup.NewDemo/Bit.Bswup.NewDemo.Client/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 9.0.0 +// bit version: 9.0.1 self.assetsInclude = []; self.assetsExclude = [ diff --git a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts index e6ca5159d3..b314973b1c 100644 --- a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts +++ b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.progress.ts @@ -1,4 +1,4 @@ -window['bit-bswup.progress version'] = '9.0.0'; +window['bit-bswup.progress version'] = '9.0.1'; ; (function () { (window as any).startBswupProgress = (autoReload: boolean, diff --git a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts index fed9c4da09..0f153a64b4 100644 --- a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts +++ b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.sw.ts @@ -1,4 +1,4 @@ -self['bit-bswup.sw version'] = '9.0.0'; +self['bit-bswup.sw version'] = '9.0.1'; interface Window { clients: any diff --git a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts index c43362c9af..31e9999cae 100644 --- a/src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts +++ b/src/Bswup/Bit.Bswup/Scripts/bit-bswup.ts @@ -1,5 +1,5 @@ const BitBswup = {} as any; -BitBswup.version = window['bit-bswup version'] = '9.0.0'; +BitBswup.version = window['bit-bswup version'] = '9.0.1'; declare const Blazor: any; diff --git a/src/Bswup/FullDemo/Client/wwwroot/service-worker.js b/src/Bswup/FullDemo/Client/wwwroot/service-worker.js index bf48ccea85..67bf7801e0 100644 --- a/src/Bswup/FullDemo/Client/wwwroot/service-worker.js +++ b/src/Bswup/FullDemo/Client/wwwroot/service-worker.js @@ -1,4 +1,4 @@ -// bit version: 9.0.0 +// bit version: 9.0.1 // In development, always fetch from the network and do not enable offline support. // This is because caching would make development more difficult (changes would not diff --git a/src/Bswup/FullDemo/Client/wwwroot/service-worker.published.js b/src/Bswup/FullDemo/Client/wwwroot/service-worker.published.js index 66e6b6757c..e84aa4c2dc 100644 --- a/src/Bswup/FullDemo/Client/wwwroot/service-worker.published.js +++ b/src/Bswup/FullDemo/Client/wwwroot/service-worker.published.js @@ -1,4 +1,4 @@ -// bit version: 9.0.0 +// bit version: 9.0.1 self.assetsInclude = []; self.assetsExclude = [/\.scp\.css$/, /weather\.json$/]; diff --git a/src/Bup/Bit.Bup/Scripts/bit-bup.progress.ts b/src/Bup/Bit.Bup/Scripts/bit-bup.progress.ts index 922a932007..553b12cb31 100644 --- a/src/Bup/Bit.Bup/Scripts/bit-bup.progress.ts +++ b/src/Bup/Bit.Bup/Scripts/bit-bup.progress.ts @@ -1,4 +1,4 @@ -window['bit-bup.progress version'] = '9.0.0'; +window['bit-bup.progress version'] = '9.0.1'; ; (function () { (window as any).startBupProgress = (showLogs: boolean, showAssets: boolean, appContainerSelector: string, hideApp: boolean, autoHide: boolean) => { diff --git a/src/Bup/Bit.Bup/Scripts/bit-bup.ts b/src/Bup/Bit.Bup/Scripts/bit-bup.ts index 1000250240..933a5fef3b 100644 --- a/src/Bup/Bit.Bup/Scripts/bit-bup.ts +++ b/src/Bup/Bit.Bup/Scripts/bit-bup.ts @@ -1,5 +1,5 @@ var BitBup = BitBup || {}; -BitBup.version = window['bit-bup version'] = '9.0.0'; +BitBup.version = window['bit-bup version'] = '9.0.1'; declare const Blazor: any; diff --git a/src/Butil/Bit.Butil/Scripts/butil.ts b/src/Butil/Bit.Butil/Scripts/butil.ts index 3bb94da1f2..d65ce8821c 100644 --- a/src/Butil/Bit.Butil/Scripts/butil.ts +++ b/src/Butil/Bit.Butil/Scripts/butil.ts @@ -1,2 +1,2 @@ var BitButil = BitButil || {}; -BitButil.version = window['bit-butil version'] = '9.0.0'; \ No newline at end of file +BitButil.version = window['bit-butil version'] = '9.0.1'; \ No newline at end of file diff --git a/src/Clean.sh b/src/Clean.sh index 3d65047357..04061b11a9 100755 --- a/src/Clean.sh +++ b/src/Clean.sh @@ -1,23 +1,23 @@ #!/bin/bash -# This batch script is designed for comprehensive cleaning of your project by deleting unnecessary files. -# It's crucial to close any Integrated Development Environment (IDE), such as vs code, etc., before executing this script to prevent any conflicts or loss of unsaved data. -# Please note that the commands included in this script are specifically tailored for the Linux/macOS +# This batch script cleans your project by deleting unnecessary files. +# It is important to close any IDEs, such as vs for mac, before running this script to prevent conflicts or data loss. +# The commands in this script are specifically designed for macOS/Linux. -# Runs dotnet clean for each csproj file +# Runs the dotnet clean command for each .csproj file. for csproj in $(find . -name '*.csproj'); do dotnet clean $csproj done -# Delete specified directories -for dir in $(find . -type d \( -name "bin" -o -name "obj" -o -name "node_modules" -o -name "Packages" -o -name ".vs" -o -name "TestResults" -o -name "AppPackages" -o -name ".meteor" -o -name "App_Data" \)); do +# Deletes specified directories +for dir in $(find . -type d \( -name "bin" -o -name "obj" -o -name "node_modules" -o -name "Packages" -o -name ".vs" -o -name "TestResults" -o -name "AppPackages" -o -name ".meteor" \)); do rm -rf $dir done -# Delete specified files -for file in $(find . -type f \( -name "*.csproj.user" -o -name "Resources.designer.cs" -o -name "*.css" -o -name "*.min.css" -o -name "*.js" -o -name "*.min.js" -o -name "*.map" -o -name "*.pubxml*" \)); do +# Deletes specified files +for file in $(find . -type f \( -name "*.csproj.user" -o -name "Resources.designer.cs" -o -name "*.css" -o -name "*.min.css" -o -name "*.js" -o -name "*.min.js" -o -name "*.map" \)); do rm -f $file done -# Delete empty directories +# Deletes empty directories. find . -type d -empty -delete \ No newline at end of file diff --git a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty.Client/BlazorEmpty.Client.csproj b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty.Client/BlazorEmpty.Client.csproj index 234e73e436..6f865c4156 100644 --- a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty.Client/BlazorEmpty.Client.csproj +++ b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty.Client/BlazorEmpty.Client.csproj @@ -1,4 +1,4 @@ - + @@ -17,14 +17,14 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty/BlazorEmpty.csproj b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty/BlazorEmpty.csproj index 212b0ccb12..3f0ae4bcf7 100644 --- a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty/BlazorEmpty.csproj +++ b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/BlazorEmpty/BlazorEmpty.csproj @@ -1,4 +1,4 @@ - + @@ -19,14 +19,14 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/Clean.sh b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/Clean.sh index fe004842e6..04061b11a9 100644 --- a/src/Templates/BlazorEmpty/Bit.BlazorEmpty/Clean.sh +++ b/src/Templates/BlazorEmpty/Bit.BlazorEmpty/Clean.sh @@ -1,23 +1,23 @@ #!/bin/bash -# This batch script is designed for comprehensive cleaning of your project by deleting unnecessary files. -# It's crucial to close any Integrated Development Environment (IDE), such as vs code, etc., before executing this script to prevent any conflicts or loss of unsaved data. -# Please note that the commands included in this script are specifically tailored for the Linux/macOS +# This batch script cleans your project by deleting unnecessary files. +# It is important to close any IDEs, such as vs for mac, before running this script to prevent conflicts or data loss. +# The commands in this script are specifically designed for macOS/Linux. -# Runs dotnet clean for each csproj file +# Runs the dotnet clean command for each .csproj file. for csproj in $(find . -name '*.csproj'); do dotnet clean $csproj done -# Delete specified directories +# Deletes specified directories for dir in $(find . -type d \( -name "bin" -o -name "obj" -o -name "node_modules" -o -name "Packages" -o -name ".vs" -o -name "TestResults" -o -name "AppPackages" -o -name ".meteor" \)); do rm -rf $dir done -# Delete specified files +# Deletes specified files for file in $(find . -type f \( -name "*.csproj.user" -o -name "Resources.designer.cs" -o -name "*.css" -o -name "*.min.css" -o -name "*.js" -o -name "*.min.js" -o -name "*.map" \)); do rm -f $file done -# Delete empty directories +# Deletes empty directories. find . -type d -empty -delete \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json b/src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json index 43bd4ed45d..c3a0e0bdaf 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json @@ -357,10 +357,10 @@ "modifiers": [ { "copyOnly": [ - "global.json", "**/HomePage.razor", "**/*.svg", - "**/*.png" + "**/*.png", + "**/*.sh" ], "exclude": [ ".vs/**", diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/ClientAppCoordinator.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/ClientAppCoordinator.cs index 96ce2e3da5..ff7dc2ef67 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/ClientAppCoordinator.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/ClientAppCoordinator.cs @@ -21,7 +21,6 @@ public partial class ClientAppCoordinator : AppComponentBase //#if (signalR == true) private HubConnection? hubConnection; [AutoInject] private Notification notification = default!; - [AutoInject] private ILogger signalRLogger = default!; //#endif //#if (notification == true) [AutoInject] private IPushNotificationService pushNotificationService = default!; @@ -34,6 +33,7 @@ public partial class ClientAppCoordinator : AppComponentBase [AutoInject] private IStorageService storageService = default!; [AutoInject] private AuthenticationManager authManager = default!; [AutoInject] private ILogger navigatorLogger = default!; + [AutoInject] private ILogger logger = default!; [AutoInject] private CultureInfoManager cultureInfoManager = default!; [AutoInject] private ILogger authLogger = default!; [AutoInject] private IBitDeviceCoordinator bitDeviceCoordinator = default!; @@ -90,10 +90,22 @@ private void NavigationManager_LocationChanged(object? sender, LocationChangedEv navigatorLogger.LogInformation("Navigation's location changed to {Location}", e.Location); } + private SemaphoreSlim semaphore = new(1, 1); + + /// + /// This code manages the association of a user with sensitive services, such as SignalR, push notifications, App Insights, and others, + /// ensuring the user is correctly set or cleared as needed. + /// private async void AuthenticationStateChanged(Task task) { try { + await semaphore.WaitAsync(CurrentCancellationToken); + // About Semaphore: The following code may take significant time to execute. + // During this period, the authentication state could change. For instance, the app might start with the user authenticated, but they could sign out while this method is running. + // To handle such scenarios, we must ensure the code below runs in a safe and sequential manner, preventing parallel execution. + // Without this safeguard, SignalR or push notifications might incorrectly associate the device with a user who has already signed out. + var user = (await task).User; var isAuthenticated = user.IsAuthenticated(); TelemetryContext.UserId = isAuthenticated ? user.GetUserId() : null; @@ -128,6 +140,10 @@ private async void AuthenticationStateChanged(Task task) { ExceptionHandler.Handle(exp); } + finally + { + semaphore.Release(); + } } //#if (signalR == true) @@ -148,15 +164,15 @@ private async Task ConnectSignalR() // WebSockets should be enabled on services like IIS or Cloudflare CDN, offering significantly better performance. options.AccessTokenProvider = async () => { - var access_token = await AuthTokenProvider.GetAccessToken(); + var accessToken = await AuthTokenProvider.GetAccessToken(); - if (string.IsNullOrEmpty(access_token) is false && - AuthTokenProvider.ParseAccessToken(access_token, validateExpiry: true).IsAuthenticated() is false) + if (string.IsNullOrEmpty(accessToken) is false && + AuthTokenProvider.ParseAccessToken(accessToken, validateExpiry: true).IsAuthenticated() is false) { - return await AuthenticationManager.RefreshToken(requestedBy: nameof(HubConnectionBuilder), CurrentCancellationToken); + return await AuthenticationManager.RefreshToken(requestedBy: nameof(HubConnectionBuilder)); } - return access_token; + return accessToken; }; }) .Build(); @@ -185,48 +201,46 @@ private async Task ConnectSignalR() hubConnection.On(SignalREvents.PUBLISH_MESSAGE, async (message) => { - signalRLogger.LogInformation("Message {Message} received from server.", message); + logger.LogInformation("Message {Message} received from server.", message); PubSubService.Publish(message); }); try { + hubConnection.Closed += HubConnectionStateChange; + hubConnection.Reconnected += HubConnectionConnected; + hubConnection.Reconnecting += HubConnectionStateChange; + await hubConnection.StartAsync(CurrentCancellationToken); await HubConnectionConnected(null); } catch (Exception exp) { - await HubConnectionDisconnected(exp); - } - finally - { - hubConnection.Closed += HubConnectionDisconnected; - hubConnection.Reconnected += HubConnectionConnected; - hubConnection.Reconnecting += HubConnectionDisconnected; + await HubConnectionStateChange(exp); } } - private async Task HubConnectionConnected(string? connectionId) + private async Task HubConnectionConnected(string? _) { PubSubService.Publish(ClientPubSubMessages.IS_ONLINE_CHANGED, true); - signalRLogger.LogInformation("SignalR connection {ConnectionId} established.", connectionId); + logger.LogInformation("SignalR connection established."); } - private async Task HubConnectionDisconnected(Exception? exception) + private async Task HubConnectionStateChange(Exception? exception) { - PubSubService.Publish(ClientPubSubMessages.IS_ONLINE_CHANGED, false); + PubSubService.Publish(ClientPubSubMessages.IS_ONLINE_CHANGED, exception is null && hubConnection!.State is HubConnectionState.Connected); if (exception is null) { - signalRLogger.LogInformation("SignalR connection lost."); // Was triggered intentionally by either server or client. + logger.LogInformation("SignalR state changed {State}", hubConnection!.State); } else { - signalRLogger.LogWarning(exception, "SignalR connection lost."); + logger.LogWarning(exception, "SignalR connection lost."); if (exception is HubException && exception.Message.EndsWith(nameof(AppStrings.UnauthorizedException))) { - await AuthenticationManager.RefreshToken(requestedBy: nameof(HubException), CurrentCancellationToken); + await AuthenticationManager.RefreshToken(requestedBy: nameof(HubException)); } } } @@ -269,9 +283,9 @@ protected override async ValueTask DisposeAsync(bool disposing) //#if (signalR == true) if (hubConnection is not null) { - hubConnection.Closed -= HubConnectionDisconnected; + hubConnection.Closed -= HubConnectionStateChange; hubConnection.Reconnected -= HubConnectionConnected; - hubConnection.Reconnecting -= HubConnectionDisconnected; + hubConnection.Reconnecting -= HubConnectionStateChange; await hubConnection.DisposeAsync(); } //#endif diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor index a7568db0a9..a4741d0f4a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor @@ -31,17 +31,32 @@ - - + Categories + + Clear logs + Nothing to show! - + @($"{logIndex.index + 1}. [{logIndex.item.CreatedOn.ToString("HH:mm:ss")}]") - @($"{logIndex.item.Category}: {logIndex.item.Message}") + + [@logIndex.item.Category] + @logIndex.item.Message @@ -71,4 +93,28 @@ Variant="BitVariant.Text" IconName="@BitIconName.Up" /> + + +
+ + Log details + + + + +
+ +
@GetContent(selectedLog)
+
+
+
\ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.cs index 9e6e616f4c..bd8fbdbb21 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.cs @@ -10,10 +10,15 @@ public partial class DiagnosticModal : IDisposable { private bool isOpen; private string? searchText; + private bool isLogModalOpen; + private DiagnosticLog? selectedLog; private bool isDescendingSort = true; private Action unsubscribe = default!; + private string[] defaultCategoryItems = []; + private IEnumerable filterCategories = []; private IEnumerable filterLogLevels = []; private IEnumerable allLogs = default!; + private BitDropdownItem[] allCategoryItems = []; private IEnumerable filteredLogs = default!; private BitBasicList<(DiagnosticLog, int)> logStackRef = default!; private readonly BitDropdownItem[] logLevelItems = Enum.GetValues().Select(v => new BitDropdownItem() { Value = v, Text = v.ToString() }).ToArray(); @@ -29,7 +34,7 @@ protected override Task OnInitAsync() unsubscribe = PubSubService.Subscribe(ClientPubSubMessages.SHOW_DIAGNOSTIC_MODAL, async _ => { isOpen = true; - allLogs = [.. DiagnosticLogger.Store]; + ResetLogs(); HandleOnLogLevelFilter(defaultFilterLogLevels); await InvokeAsync(StateHasChanged); }); @@ -50,6 +55,12 @@ private void HandleOnLogLevelFilter(IEnumerable logLevels) FilterLogs(); } + private void HandleOnCategoryFilter(IEnumerable categories) + { + filterCategories = categories; + FilterLogs(); + } + private void HandleOnSortClick() { isDescendingSort = !isDescendingSort; @@ -59,7 +70,8 @@ private void HandleOnSortClick() private void FilterLogs() { filteredLogs = allLogs.WhereIf(string.IsNullOrEmpty(searchText) is false, l => l.Message?.Contains(searchText!, StringComparison.InvariantCultureIgnoreCase) is true || l.Category?.Contains(searchText!, StringComparison.InvariantCultureIgnoreCase) is true) - .Where(l => filterLogLevels.Contains(l.Level)); + .Where(l => filterLogLevels.Contains(l.Level)) + .Where(l => filterCategories.Contains(l.Category)); if (isDescendingSort) { filteredLogs = filteredLogs.OrderByDescending(l => l.CreatedOn); @@ -75,11 +87,25 @@ private async Task CopyTelemetry() await clipboard.WriteText(string.Join(Environment.NewLine, telemetryContext.ToDictionary().Select(c => $"{c.Key}: {c.Value}"))); } - private async Task CopyException(DiagnosticLog log) + private async Task CopyException(DiagnosticLog? log) { - var stateToCopy = string.Join(Environment.NewLine, log.State?.Select(i => $"{i.Key}: {i.Value}") ?? []); + if (log is null) return; + + await clipboard.WriteText(GetContent(log)); + } + + private async Task OpenLog(DiagnosticLog log) + { + selectedLog = log; + isLogModalOpen = true; + } + + private static string GetContent(DiagnosticLog? log) + { + if (log is null) return string.Empty; - await clipboard.WriteText($"{log.Category}{Environment.NewLine}{log.Message}{Environment.NewLine}{log.Exception?.ToString()}{Environment.NewLine}{stateToCopy}"); + var stateToCopy = string.Join(Environment.NewLine, log.State?.Select(i => $"{i.Key}: {i.Value}") ?? []); + return $"{log.Category}{Environment.NewLine}{log.Message}{Environment.NewLine}{log.Exception?.ToString()}{Environment.NewLine}{stateToCopy}"; } private async Task GoTop() @@ -90,12 +116,30 @@ private async Task GoTop() private async Task ClearLogs() { DiagnosticLogger.Store.Clear(); - allLogs = []; + ResetLogs(); + } + + private async Task ReloadLogs() + { + ResetLogs(); + } + + private void ResetLogs() + { + allLogs = [.. DiagnosticLogger.Store]; + + defaultCategoryItems = allLogs.Select(l => l.Category!) + .Where(c => string.IsNullOrWhiteSpace(c) is false) + .Distinct().Order().ToArray(); + + filterCategories = defaultCategoryItems; + allCategoryItems = defaultCategoryItems.Select(c => new BitDropdownItem() { Text = c, Value = c }).ToArray(); + FilterLogs(); } - private static BitColor GetColor(LogLevel level) + private static BitColor GetColor(LogLevel? level) { return level switch { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.scss b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.scss index 1d1a732cc7..f365a8aeeb 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.scss +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/DiagnosticModal.razor.scss @@ -28,4 +28,13 @@ section { transform: translateX(-50%); bottom: calc(var(--app-inset-bottom) + 1rem); } + + .log-modal { + overflow: auto; + max-width: 40rem; + max-height: 40rem; + white-space: nowrap; + height: min(20rem, var(--app-height) - 4rem); + width: min(40rem, calc(var(--app-width) - 2rem)); + } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootLayout.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootLayout.razor.cs index 404f2d30df..03bfdc88d3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootLayout.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/RootLayout.razor.cs @@ -62,7 +62,7 @@ protected override async Task OnInitializedAsync() unsubscribers.Add(pubSubService.Subscribe(ClientPubSubMessages.IS_ONLINE_CHANGED, async payload => { - telemetryContext.IsOnline = isOnline; + telemetryContext.IsOnline = isOnline = (bool?)payload; await InvokeAsync(StateHasChanged); })); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SnackBar.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SnackBar.razor index 8b06dcce83..4e87fa8303 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SnackBar.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/SnackBar.razor @@ -4,4 +4,5 @@ Multiline AutoDismiss Class="snackbar" + Position="BitSnackBarPosition.TopCenter" AutoDismissTime="TimeSpan.FromSeconds(10)" /> \ No newline at end of file diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs index f1007c7668..1686228ba8 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Layout/UserMenu.razor.cs @@ -1,4 +1,5 @@ -using Boilerplate.Shared.Dtos.Identity; +using Boilerplate.Shared.Controllers.Identity; +using Boilerplate.Shared.Dtos.Identity; namespace Boilerplate.Client.Core.Components.Layout; @@ -13,9 +14,10 @@ public partial class UserMenu private BitChoiceGroupItem[] cultures = default!; [AutoInject] private Cookie cookie = default!; + [AutoInject] private ThemeService themeService = default!; + [AutoInject] private CultureService cultureService = default!; + [AutoInject] private IUserController userController = default!; [AutoInject] private CultureInfoManager cultureInfoManager = default!; - [AutoInject] private ThemeService themeService { get; set; } = default!; - [AutoInject] private CultureService cultureService { get; set; } = default!; [CascadingParameter] private BitDir? currentDir { get; set; } @@ -40,10 +42,10 @@ protected override async Task OnInitAsync() await InvokeAsync(StateHasChanged); }); - user = (await PrerenderStateService.GetValue(() => HttpClient.GetFromJsonAsync("api/User/GetCurrentUser", JsonSerializerOptions.GetTypeInfo(), CurrentCancellationToken)))!; + user = await userController.GetCurrentUser(CurrentCancellationToken); - var access_token = await PrerenderStateService.GetValue(AuthTokenProvider.GetAccessToken); - profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage?access_token={access_token}").ToString(); + var accessToken = await PrerenderStateService.GetValue(AuthTokenProvider.GetAccessToken); + profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage?access_token={accessToken}").ToString(); await base.OnInitAsync(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor index a96b81ffa0..a428ba0b65 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor @@ -18,14 +18,17 @@ } else { - - + @if (isUploading is false) { - - @Localizer[nameof(AppStrings.UploadNewProfileImage)] - + + @Localizer[nameof(AppStrings.UploadNewProfileImage)] + } else { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs index da318e973f..71df207b71 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs @@ -17,17 +17,18 @@ public partial class ProfileSection private string? profileImageUrl; private string? profileImageUploadUrl; private string? removeProfileImageHttpUrl; + private BitFileUpload fileUploadRef = default!; private readonly EditUserDto editUserDto = new(); protected override async Task OnInitAsync() { - var access_token = await PrerenderStateService.GetValue(AuthTokenProvider.GetAccessToken); + var accessToken = await PrerenderStateService.GetValue(AuthTokenProvider.GetAccessToken); - removeProfileImageHttpUrl = $"api/Attachment/RemoveProfileImage?access_token={access_token}"; + removeProfileImageHttpUrl = $"api/Attachment/RemoveProfileImage?access_token={accessToken}"; - profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage?access_token={access_token}").ToString(); - profileImageUploadUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/UploadProfileImage?access_token={access_token}").ToString(); + profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage?access_token={accessToken}").ToString(); + profileImageUploadUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/UploadProfileImage?access_token={accessToken}").ToString(); await base.OnInitAsync(); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.cs index 6ac9471092..87eda360e3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/SettingsPage.razor.cs @@ -30,10 +30,10 @@ protected override async Task OnInitAsync() try { - user = await userController.GetCurrentUser(CurrentCancellationToken); + user = user = (await PrerenderStateService.GetValue(() => HttpClient.GetFromJsonAsync("api/User/GetCurrentUser", JsonSerializerOptions.GetTypeInfo(), CurrentCancellationToken)))!; - var access_token = await PrerenderStateService.GetValue(AuthTokenProvider.GetAccessToken); - profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage?access_token={access_token}").ToString(); + var accessToken = await PrerenderStateService.GetValue(AuthTokenProvider.GetAccessToken); + profileImageUrl = new Uri(AbsoluteServerAddress, $"/api/Attachment/GetProfileImage?access_token={accessToken}").ToString(); } finally { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor index 0abf13e725..18ecebf61c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor @@ -15,7 +15,7 @@ { @if (isOtpRequested is false) { - + } else { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.cs index a70d77046e..90728d88c6 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPage.razor.cs @@ -34,6 +34,7 @@ public partial class SignInPage : IDisposable private bool isWaiting; private bool isOtpRequested; private bool requiresTwoFactor; + private SignInPanelTab currentSignInPanelTab; private readonly SignInRequestDto model = new(); private Action unsubscribeIdentityHeaderBackLinkClicked = default!; @@ -119,6 +120,8 @@ private async Task DoSignIn() { if (requiresTwoFactor && string.IsNullOrWhiteSpace(model.TwoFactorCode)) return; + CleanModel(); + requiresTwoFactor = await AuthenticationManager.SignIn(model, CurrentCancellationToken); if (requiresTwoFactor is false) @@ -140,41 +143,51 @@ private async Task DoSignIn() } } - private Task ResendOtp() => SendOtp(true); - private Task SendOtp() => SendOtp(false); - private async Task SendOtp(bool resend) { - if (model.Email is null && model.PhoneNumber is null) return; - - if (model.Email is not null && new EmailAddressAttribute().IsValid(model.Email) is false) + try { - SnackBarService.Error(string.Format(AppStrings.EmailAddressAttribute_ValidationError, AppStrings.Email)); - return; - } + CleanModel(); - if (model.PhoneNumber is not null && new PhoneAttribute().IsValid(model.PhoneNumber) is false) - { - SnackBarService.Error(string.Format(AppStrings.PhoneAttribute_ValidationError, AppStrings.PhoneNumber)); - return; - } + if (model.Email is null && model.PhoneNumber is null) return; - var request = new IdentityRequestDto { UserName = model.UserName, Email = model.Email, PhoneNumber = model.PhoneNumber }; + if (model.Email is not null && new EmailAddressAttribute().IsValid(model.Email) is false) + { + SnackBarService.Error(string.Format(AppStrings.EmailAddressAttribute_ValidationError, AppStrings.Email)); + return; + } - if (resend is false) - { - isOtpRequested = true; + if (model.PhoneNumber is not null && new PhoneAttribute().IsValid(model.PhoneNumber) is false) + { + SnackBarService.Error(string.Format(AppStrings.PhoneAttribute_ValidationError, AppStrings.PhoneNumber)); + return; + } - PubSubService.Publish(ClientPubSubMessages.UPDATE_IDENTITY_HEADER_BACK_LINK, OtpPayload); - } + var request = new IdentityRequestDto { UserName = model.UserName, Email = model.Email, PhoneNumber = model.PhoneNumber }; + + if (resend is false) + { + isOtpRequested = true; + + PubSubService.Publish(ClientPubSubMessages.UPDATE_IDENTITY_HEADER_BACK_LINK, OtpPayload); + } - await identityController.SendOtp(request, ReturnUrlQueryString, CurrentCancellationToken); + await identityController.SendOtp(request, ReturnUrlQueryString, CurrentCancellationToken); + } + catch (KnownException e) + { + SnackBarService.Error(e.Message); + } } + private Task ResendOtp() => SendOtp(true); + private Task SendOtp() => SendOtp(false); private async Task SendTfaToken() { try { + CleanModel(); + await identityController.SendTwoFactorToken(model, CurrentCancellationToken); SnackBarService.Success(Localizer[nameof(AppStrings.TfaTokenSentMessage)]); @@ -185,6 +198,24 @@ private async Task SendTfaToken() } } + private void HandleOnSignInPanelTabChange(SignInPanelTab tab) + { + currentSignInPanelTab = tab; + } + + private void CleanModel() + { + if (currentSignInPanelTab is SignInPanelTab.Email) + { + model.PhoneNumber = null; + } + + if (currentSignInPanelTab is SignInPanelTab.Phone) + { + model.Email = null; + } + } + public void Dispose() { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor index 28620fea49..b621dbc922 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanel.razor @@ -17,8 +17,14 @@ - - + + + + + + + @if (selectedKey is EmailKey) + { - - - + } + else + { - - - - + } +
OnTabChange { get; set; } + private const string EmailKey = nameof(EmailKey); private const string PhoneKey = nameof(PhoneKey); - private string selectedKey = EmailKey; + private string? selectedKey = EmailKey; - private void OnSelectedKeyChanged(string key) + private async Task HandleOnPivotChange(BitPivotItem item) { - selectedKey = key; + selectedKey = item.Key; - if (key == EmailKey) + if (item.Key is EmailKey) { - Model.PhoneNumber = null; + await OnTabChange.InvokeAsync(SignInPanelTab.Email); } - if (key == PhoneKey) + if (item.Key is PhoneKey) { - Model.Email = null; + await OnTabChange.InvokeAsync(SignInPanelTab.Phone); } } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanelTab.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanelTab.cs new file mode 100644 index 0000000000..d3e3615f1d --- /dev/null +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignIn/SignInPanelTab.cs @@ -0,0 +1,7 @@ +namespace Boilerplate.Client.Core.Components.Pages.Identity.SignIn; + +public enum SignInPanelTab +{ + Email, + Phone +} diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor index d6fbcea999..5fe4d9240f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Identity/SignUp/SignUpPage.razor @@ -45,7 +45,7 @@ - + /// Indicates the connection status, with default behavior tied to the SignalR connection status. - /// For projects without SignalR, allows this value to be updated based on server responses: + /// also allows this value to be updated based on server responses: /// - When the first response is received from the server, this value becomes true (Online). /// - When a server connection exception occurs, it becomes false (Offline). /// By default, this value is null (Unknown). diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/OfflineDbContext.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/OfflineDbContext.cs index c88dc5e81c..8eb8dbbbd7 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/OfflineDbContext.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Data/OfflineDbContext.cs @@ -4,7 +4,7 @@ namespace Boilerplate.Client.Core.Data; -public partial class OfflineDbContext : DbContext +public partial class OfflineDbContext(DbContextOptions options) : DbContext(options) { public virtual DbSet Users { get; set; } @@ -32,31 +32,4 @@ protected override void ConfigureConventions(ModelConfigurationBuilder configura configurationBuilder.Properties().HaveConversion(); configurationBuilder.Properties().HaveConversion(); } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - var isRunningInsideDocker = Directory.Exists("/container_volume"); // Blazor Server - Docker (It's supposed to be a mounted volume named /container_volume) - var dirPath = isRunningInsideDocker ? "/container_volume" - : AppPlatform.IsBlazorHybridOrBrowser ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "AC87AA5B-4B37-4E52-8468-2D5DF24AF256") - : Directory.GetCurrentDirectory(); // Blazor server (Non docker Linux, macOS or Windows) - - dirPath = Path.Combine(dirPath, "App_Data"); - - Directory.CreateDirectory(dirPath); - - var dbPath = Path.Combine(dirPath, "Offline.db"); - - optionsBuilder - .UseSqlite($"Data Source={dbPath}"); - - if (AppEnvironment.IsProd()) - { - optionsBuilder.UseModel(OfflineDbContextModel.Instance); - } - - optionsBuilder.EnableSensitiveDataLogging(AppEnvironment.IsDev()) - .EnableDetailedErrors(AppEnvironment.IsDev()); - - base.OnConfiguring(optionsBuilder); - } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs index a1dda1fb46..08187d693f 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs @@ -1,6 +1,7 @@ //+:cnd:noEmit //#if (offlineDb == true) using Boilerplate.Client.Core.Data; +using Microsoft.EntityFrameworkCore; //#endif //#if (appInsights == true) using BlazorApplicationInsights; @@ -58,7 +59,7 @@ public static IServiceCollection AddClientCoreProjectServices(this IServiceColle services.AddScoped(serviceProvider => transportHandler => { var constructedHttpMessageHandler = ActivatorUtilities.CreateInstance(serviceProvider, - [ActivatorUtilities.CreateInstance(serviceProvider, + [ActivatorUtilities.CreateInstance(serviceProvider, [ActivatorUtilities.CreateInstance(serviceProvider, [ActivatorUtilities.CreateInstance(serviceProvider, [ActivatorUtilities.CreateInstance(serviceProvider, [transportHandler])])])])]); @@ -66,9 +67,8 @@ public static IServiceCollection AddClientCoreProjectServices(this IServiceColle }); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); services.AddScoped(serviceProvider => { var transportHandler = serviceProvider.GetRequiredService(); @@ -81,7 +81,30 @@ public static IServiceCollection AddClientCoreProjectServices(this IServiceColle { AppContext.SetSwitch("Microsoft.EntityFrameworkCore.Issue31751", true); } - services.AddBesqlDbContextFactory(); + services.AddBesqlDbContextFactory((optionsBuilder) => + { + var isRunningInsideDocker = Directory.Exists("/container_volume"); // Blazor Server - Docker (It's supposed to be a mounted volume named /container_volume) + var dirPath = isRunningInsideDocker ? "/container_volume" + : AppPlatform.IsBlazorHybridOrBrowser ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "AC87AA5B-4B37-4E52-8468-2D5DF24AF256") + : Directory.GetCurrentDirectory(); // Blazor server (Non docker Linux, macOS or Windows) + + dirPath = Path.Combine(dirPath, "App_Data"); + + Directory.CreateDirectory(dirPath); + + var dbPath = Path.Combine(dirPath, "Offline.db"); + + optionsBuilder + .UseSqlite($"Data Source={dbPath}"); + + if (AppEnvironment.IsProd()) + { + optionsBuilder.UseModel(OfflineDbContextModel.Instance); + } + + optionsBuilder.EnableSensitiveDataLogging(AppEnvironment.IsDev()) + .EnableDetailedErrors(AppEnvironment.IsDev()); + }); //#endif //#if (appInsights == true) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AuthenticationManager.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AuthenticationManager.cs index 524c4088c2..85483850fa 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AuthenticationManager.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/AuthenticationManager.cs @@ -5,11 +5,6 @@ namespace Boilerplate.Client.Core.Services; public partial class AuthenticationManager : AuthenticationStateProvider { - /// - /// To prevent multiple simultaneous refresh token requests. - /// - private readonly SemaphoreSlim semaphore = new(1, maxCount: 1); - [AutoInject] private Cookie cookie = default!; [AutoInject] private IJSRuntime jsRuntime = default!; [AutoInject] private IStorageService storageService = default!; @@ -79,44 +74,53 @@ public async Task SignOut(CancellationToken cancellationToken) } } - public async Task RefreshToken(string requestedBy, CancellationToken cancellationToken) + /// + /// To prevent multiple simultaneous refresh token requests. + /// + private TaskCompletionSource? refreshTokenTsc = null; + + public Task RefreshToken(string requestedBy) { - try + if (refreshTokenTsc is null) + { + refreshTokenTsc = new(); + _ = RefreshTokenImplementation(); + } + + return refreshTokenTsc.Task; + + async Task RefreshTokenImplementation() { - var access_token_BeforeLockValue = await tokenProvider.GetAccessToken(); - await semaphore.WaitAsync(); - var access_token_AfterLockValue = await tokenProvider.GetAccessToken(); - if (access_token_BeforeLockValue != access_token_AfterLockValue) - return access_token_AfterLockValue; // It was renewed by a concurrent refresh token request. authLogger.LogInformation("Refreshing access token requested by {RequestedBy}", requestedBy); try { - string? refresh_token = await storageService.GetItem("refresh_token"); - if (string.IsNullOrEmpty(refresh_token)) + string? refreshToken = await storageService.GetItem("refresh_token"); + if (string.IsNullOrEmpty(refreshToken)) throw new UnauthorizedException(localizer[nameof(AppStrings.YouNeedToSignIn)]); - var refreshTokenResponse = await identityController.Refresh(new() { RefreshToken = refresh_token }, cancellationToken); + var refreshTokenResponse = await identityController.Refresh(new() { RefreshToken = refreshToken }, default); await StoreTokens(refreshTokenResponse); - return refreshTokenResponse.AccessToken!; + refreshTokenTsc.SetResult(refreshTokenResponse.AccessToken!); } catch (Exception exp) { - if (exp is UnauthorizedException) - { - // refresh_token is either invalid or expired. - await ClearTokens(); - } exceptionHandler.Handle(exp, new() { { "AdditionalData", "Refreshing access token failed." }, { "RefreshTokenRequestedBy", requestedBy } }); - return null; + + if (exp is UnauthorizedException) // refresh token is also invalid. + { + await ClearTokens(); + } + + refreshTokenTsc.SetResult(null); + } + finally + { + refreshTokenTsc = null; } - } - finally - { - semaphore.Release(); } } @@ -133,9 +137,9 @@ public override async Task GetAuthenticationStateAsync() { try { - var access_token = await prerenderStateService.GetValue(() => tokenProvider.GetAccessToken()); + var accessToken = await prerenderStateService.GetValue(() => tokenProvider.GetAccessToken()); - return new AuthenticationState(tokenProvider.ParseAccessToken(access_token, validateExpiry: false)); + return new AuthenticationState(tokenProvider.ParseAccessToken(accessToken, validateExpiry: false)); } catch (Exception exp) { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientPubSubMessages.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientPubSubMessages.cs index 83a9d3b13a..1fd6a767c5 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientPubSubMessages.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/ClientPubSubMessages.cs @@ -1,4 +1,6 @@ //+:cnd:noEmit +using Boilerplate.Client.Core.Components; + namespace Boilerplate.Client.Core.Services; public static partial class ClientPubSubMessages @@ -10,6 +12,9 @@ public static partial class ClientPubSubMessages public const string OPEN_NAV_PANEL = nameof(OPEN_NAV_PANEL); public const string CULTURE_CHANGED = nameof(CULTURE_CHANGED); public const string PROFILE_UPDATED = nameof(PROFILE_UPDATED); + /// + /// + /// public const string IS_ONLINE_CHANGED = nameof(IS_ONLINE_CHANGED); public const string PAGE_TITLE_CHANGED = nameof(PAGE_TITLE_CHANGED); public const string ROUTE_DATA_UPDATED = nameof(ROUTE_DATA_UPDATED); diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IAuthTokenProvider.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IAuthTokenProvider.cs index e6d48dab95..842adfefe9 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IAuthTokenProvider.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/Contracts/IAuthTokenProvider.cs @@ -8,12 +8,12 @@ public interface IAuthTokenProvider public ClaimsPrincipal Anonymous() => new(new ClaimsIdentity()); - public ClaimsPrincipal ParseAccessToken(string? access_token, bool validateExpiry) + public ClaimsPrincipal ParseAccessToken(string? accessToken, bool validateExpiry) { - if (string.IsNullOrEmpty(access_token) is true) + if (string.IsNullOrEmpty(accessToken) is true) return Anonymous(); - var claims = ReadClaims(access_token, validateExpiry); + var claims = ReadClaims(accessToken, validateExpiry); if (claims is null) return Anonymous(); @@ -25,9 +25,9 @@ public ClaimsPrincipal ParseAccessToken(string? access_token, bool validateExpir return claimPrinciple; } - private IEnumerable? ReadClaims(string access_token, bool validateExpiry) + private IEnumerable? ReadClaims(string accessToken, bool validateExpiry) { - var parsedClaims = DeserializeAccessToken(access_token); + var parsedClaims = DeserializeAccessToken(accessToken); if (validateExpiry && long.TryParse(parsedClaims["exp"].ToString(), out var expSeconds)) { @@ -55,10 +55,10 @@ public ClaimsPrincipal ParseAccessToken(string? access_token, bool validateExpir return claims; } - private Dictionary DeserializeAccessToken(string access_token) + private Dictionary DeserializeAccessToken(string accessToken) { // Split the token to get the payload - string base64UrlPayload = access_token.Split('.')[1]; + string base64UrlPayload = accessToken.Split('.')[1]; // Convert the payload from Base64Url format to Base64 string base64Payload = ConvertBase64UrlToBase64(base64UrlPayload); 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 99c74936bd..9e471ce55b 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,6 @@ //+:cnd:noEmit +using Boilerplate.Client.Core.Components; + namespace Boilerplate.Client.Core.Services.Contracts; public interface ITelemetryContext @@ -46,6 +48,9 @@ public static ITelemetryContext? Current public string? Environment { get; set; } + /// + /// + /// public bool? IsOnline { get; set; } public Dictionary ToDictionary(Dictionary? additionalParameters = null) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/AuthDelegatingHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/AuthDelegatingHandler.cs index 3c35813586..c2ef31f84b 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/AuthDelegatingHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/AuthDelegatingHandler.cs @@ -16,6 +16,7 @@ public partial class AuthDelegatingHandler(IJSRuntime jsRuntime, protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + var logScopeData = (Dictionary)request.Options.GetValueOrDefault(RequestOptionNames.LogScopeData)!; var isInternalRequest = request.RequestUri!.ToString().StartsWith(absoluteServerAddress, StringComparison.InvariantCultureIgnoreCase); try @@ -23,50 +24,53 @@ protected override async Task SendAsync(HttpRequestMessage if (isInternalRequest && /* We will restrict sending the access token to our own server only. */ request.Headers.Authorization is null) { - var access_token = await tokenProvider.GetAccessToken(); - if (string.IsNullOrEmpty(access_token) is false && HasAuthorizedApiAttribute(request)) + var accessToken = await tokenProvider.GetAccessToken(); + if (string.IsNullOrEmpty(accessToken) is false && HasAuthorizedApiAttribute(request)) { - if (tokenProvider.ParseAccessToken(access_token, validateExpiry: true).IsAuthenticated() is false) + if (tokenProvider.ParseAccessToken(accessToken, validateExpiry: true).IsAuthenticated() is false) + { + logScopeData["ClientSideAccessTokenValidationFailed"] = true; throw new UnauthorizedException(localizer[nameof(AppStrings.YouNeedToSignIn)]); + } } - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", access_token); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); } return await base.SendAsync(request, cancellationToken); } catch (KnownException _) when (_ is ForbiddenException or UnauthorizedException) { + if (isInternalRequest is false) + throw; + // Notes about ForbiddenException (403): // Let's update the access token by refreshing it when a refresh token is available. // Following this procedure, the newly acquired access token may now include the necessary roles or claims. if (AppPlatform.IsBlazorHybrid is false && jsRuntime.IsInitialized() is false) - throw; // We don't have access to refresh_token during pre-rendering. + throw; // We don't have access to refreshToken during pre-rendering. var isRefreshTokenRequest = request.RequestUri?.LocalPath?.Contains(IIdentityController.RefreshUri, StringComparison.InvariantCultureIgnoreCase) is true; if (isRefreshTokenRequest) throw; // To prevent refresh token loop - var refresh_token = await storageService.GetItem("refresh_token"); - if (string.IsNullOrEmpty(refresh_token)) throw; + var refreshToken = await storageService.GetItem("refresh_token"); + if (string.IsNullOrEmpty(refreshToken)) throw; var authManager = serviceProvider.GetRequiredService(); - // In the AuthenticationStateProvider, the access_token is refreshed using the refresh_token (if available). - var access_token = await authManager.RefreshToken(requestedBy: nameof(AuthDelegatingHandler), cancellationToken); - - if (string.IsNullOrEmpty(access_token)) - throw; + logScopeData["RefreshTokenRequested"] = true; + var accessToken = await authManager.RefreshToken(requestedBy: nameof(AuthDelegatingHandler)); - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", access_token); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); return await base.SendAsync(request, cancellationToken); } } /// - /// + /// /// private static bool HasAuthorizedApiAttribute(HttpRequestMessage request) { diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/ExceptionDelegatingHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/ExceptionDelegatingHandler.cs index 1a7bf35e3d..6d5313e969 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/ExceptionDelegatingHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/ExceptionDelegatingHandler.cs @@ -1,24 +1,30 @@ //+:cnd:noEmit +using System.Diagnostics; using System.Net; namespace Boilerplate.Client.Core.Services.HttpMessageHandlers; -public partial class ExceptionDelegatingHandler(IStringLocalizer localizer, - //#if (signalR != true) - PubSubService pubSubService, - //#endif +public partial class ExceptionDelegatingHandler(PubSubService pubSubService, + IStringLocalizer localizer, JsonSerializerOptions jsonSerializerOptions, AbsoluteServerAddressProvider absoluteServerAddress, HttpMessageHandler handler) : DelegatingHandler(handler) { protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + var logScopeData = (Dictionary)request.Options.GetValueOrDefault(RequestOptionNames.LogScopeData)!; + bool serverCommunicationSuccess = false; var isInternalRequest = request.RequestUri!.ToString().StartsWith(absoluteServerAddress, StringComparison.InvariantCultureIgnoreCase); try { var response = await base.SendAsync(request, cancellationToken); + if (response.Headers.TryGetValues("Request-Id", out var requestId)) + { + logScopeData["RequestId"] = requestId.First(); + } + logScopeData["HttpStatusCode"] = response.StatusCode; serverCommunicationSuccess = true; @@ -53,6 +59,8 @@ response.IsSuccessStatusCode is false && response.EnsureSuccessStatusCode(); + request.Options.Set(new(RequestOptionNames.LogLevel), LogLevel.Information); + return response; } catch (Exception exp) when ((exp is HttpRequestException && serverCommunicationSuccess is false) @@ -61,7 +69,6 @@ response.IsSuccessStatusCode is false && { throw new ServerConnectionException(localizer[nameof(AppStrings.ServerConnectionException)], exp); } - //#if (signalR != true) finally { if (isInternalRequest) @@ -69,6 +76,5 @@ response.IsSuccessStatusCode is false && pubSubService.Publish(ClientPubSubMessages.IS_ONLINE_CHANGED, serverCommunicationSuccess); } } - //#endif } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/HttpMessageHandlersChainFactory.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/HttpMessageHandlersChainFactory.cs index f62e4d81f9..484783f8e4 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/HttpMessageHandlersChainFactory.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/HttpMessageHandlersChainFactory.cs @@ -8,7 +8,7 @@ /// The returned chain will include the following handlers, in order: /// /// 1. -/// 2. +/// 2. /// 3. /// 4. /// 5. diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/LoggingDelegatingHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/LoggingDelegatingHandler.cs index da6a471c21..ee015d9cf0 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/LoggingDelegatingHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/LoggingDelegatingHandler.cs @@ -8,31 +8,23 @@ internal class LoggingDelegatingHandler(ILogger logger, HttpMessageH protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { logger.LogInformation("Sending HTTP request {Method} {Uri}", request.Method, request.RequestUri); + request.Options.Set(new(RequestOptionNames.LogLevel), LogLevel.Warning); + request.Options.Set(new(RequestOptionNames.LogScopeData), new Dictionary()); var stopwatch = Stopwatch.StartNew(); - int? responseStatusCode = null; try { - var response = await base.SendAsync(request, cancellationToken); - responseStatusCode = (int?)response.StatusCode; - return response; - } - catch (RestException exp) - { - responseStatusCode = (int)exp.StatusCode; - throw; - } - catch (HttpRequestException exp) - { - responseStatusCode = (int?)exp.StatusCode; - throw; + return await base.SendAsync(request, cancellationToken); } finally { - logger.Log(responseStatusCode is null or >= 400 ? LogLevel.Warning : LogLevel.Information, "Received HTTP response for {Uri} after {Duration}ms - {StatusCode}", + var logLevel = (LogLevel)request.Options.GetValueOrDefault(RequestOptionNames.LogLevel)!; + var logScopeData = (Dictionary)request.Options.GetValueOrDefault(RequestOptionNames.LogScopeData)!; + + using var scope = logger.BeginScope(logScopeData); + logger.Log(logLevel, "Received HTTP response for {Uri} after {Duration}ms", request.RequestUri, - stopwatch.ElapsedMilliseconds, - responseStatusCode); + stopwatch.ElapsedMilliseconds); } } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegationHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegatingHandler.cs similarity index 92% rename from src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegationHandler.cs rename to src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegatingHandler.cs index 800333e55f..4f713968d3 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegationHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestHeadersDelegatingHandler.cs @@ -3,7 +3,7 @@ namespace Boilerplate.Client.Core.Services.HttpMessageHandlers; -public partial class RequestHeadersDelegationHandler(HttpMessageHandler handler) +public partial class RequestHeadersDelegatingHandler(HttpMessageHandler handler) : DelegatingHandler(handler) { protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestOptionNames.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestOptionNames.cs index 1ff3539ab3..bb6d8133eb 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestOptionNames.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RequestOptionNames.cs @@ -6,9 +6,11 @@ /// public partial class RequestOptionNames { - public const string IControllerType = nameof(IControllerType); + public const string LogLevel = nameof(LogLevel); public const string ActionName = nameof(ActionName); - public const string ActionParametersInfo = nameof(ActionParametersInfo); public const string RequestType = nameof(RequestType); public const string ResponseType = nameof(ResponseType); + public const string LogScopeData = nameof(LogScopeData); + public const string IControllerType = nameof(IControllerType); + public const string ActionParametersInfo = nameof(ActionParametersInfo); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RetryDelegatingHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RetryDelegatingHandler.cs index 8e50cf5eb2..4522bf2c9c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RetryDelegatingHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Services/HttpMessageHandlers/RetryDelegatingHandler.cs @@ -9,10 +9,12 @@ public partial class RetryDelegatingHandler(HttpMessageHandler handler) protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + var logScopeData = (Dictionary)request.Options.GetValueOrDefault(RequestOptionNames.LogScopeData)!; var delays = GetDelaySequence(scaleFirstTry: TimeSpan.FromSeconds(3)).Take(3).ToArray(); Exception? lastExp = null; + int retryCount = 0; foreach (var delay in delays) { try @@ -23,7 +25,8 @@ protected override async Task SendAsync(HttpRequestMessage { if (HasNoRetryPolicyAttribute(request)) throw; - + retryCount++; + logScopeData["RetryCount"] = retryCount; lastExp = exp; await Task.Delay(delay, cancellationToken); } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package-lock.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package-lock.json index 770ab6d2b3..b96eb801b9 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package-lock.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } }, @@ -887,7 +887,7 @@ } }, "node_modules/sass": { - "version": "1.80.5", + "version": "1.81.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package.json b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package.json index 8f2181ad89..7d426923cd 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package.json +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js index da582b35c7..6a6fbe8821 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Web/wwwroot/service-worker.published.js @@ -1,5 +1,5 @@ //+:cnd:noEmit -// bit version: 9.0.0 +// bit version: 9.0.1 // https://github.com/bitfoundation/bitplatform/tree/develop/src/Bswup //#if (notification == true) diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Build.props b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Build.props index 31777cc84c..bf2210e1c7 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Build.props +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Build.props @@ -1,8 +1,7 @@ - + - 12.0 enable enable true @@ -33,6 +32,7 @@ + 12.0 14.0 14.0 26.0 @@ -44,6 +44,7 @@ + 13.0 16.4 16.4 26.0 diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props index 999ded2dca..919457c075 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages.props @@ -1,14 +1,14 @@  - - - - - - - - + + + + + + + + @@ -20,7 +20,7 @@ - + @@ -42,7 +42,7 @@ - + @@ -52,7 +52,7 @@ - + @@ -63,7 +63,7 @@ - + diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages8.props b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages8.props index 6c9ba3edaa..57bbca2cae 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages8.props +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Directory.Packages8.props @@ -1,14 +1,14 @@  - - - - - - - - + + + + + + + + @@ -20,7 +20,7 @@ - + @@ -42,7 +42,7 @@ - + @@ -63,7 +63,7 @@ - + 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 c2165f0c70..23991fc5fa 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 @@ -333,7 +333,7 @@ private static void AddIdentity(WebApplicationBuilder builder) { OnMessageReceived = async context => { - // The server accepts the access_token from either the authorization header, the cookie, or the request URL query string + // The server accepts the accessToken from either the authorization header, the cookie, or the request URL query string context.Token ??= context.Request.Query.ContainsKey("access_token") ? context.Request.Query["access_token"] : context.Request.Cookies["access_token"]; } }; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ServerExceptionHandler.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ServerExceptionHandler.cs index f0d31bcd1c..c5b1b653ea 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ServerExceptionHandler.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Services/ServerExceptionHandler.cs @@ -1,5 +1,4 @@ using System.Net; -using System.Diagnostics; using Microsoft.Net.Http.Headers; using Microsoft.AspNetCore.Diagnostics; @@ -13,7 +12,7 @@ public partial class ServerExceptionHandler : SharedExceptionHandler, IException public async ValueTask TryHandleAsync(HttpContext httpContext, Exception e, CancellationToken cancellationToken) { // Using the Request-Id header, one can find the log for server-related exceptions - httpContext.Response.Headers.Append(HeaderNames.RequestId, Activity.Current?.Id ?? httpContext.TraceIdentifier); + httpContext.Response.Headers.Append(HeaderNames.RequestId, httpContext.TraceIdentifier); var exception = UnWrapException(e); var knownException = exception as KnownException; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/wwwroot/scripts/swagger-utils.js b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/wwwroot/scripts/swagger-utils.js index 507211963f..154c64bc12 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/wwwroot/scripts/swagger-utils.js +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/wwwroot/scripts/swagger-utils.js @@ -39,8 +39,8 @@ const overrideSwaggerAuthorizeEvent = (swagger) => { originalAuthorize(args); if (!getCookie(ACCESS_TOKEN_COOKIE_NAME)) { - const access_token = args.bearerAuth.value; - const jwt = parseJwt(access_token); + const accessToken = args.bearerAuth.value; + const jwt = parseJwt(accessToken); setCookie(ACCESS_TOKEN_COOKIE_NAME, args.bearerAuth.value, parseInt(jwt.exp)); } @@ -204,17 +204,17 @@ const signIn = async (swagger, userName, email, phone, password) => { }) if (response.ok) { const result = await response.json(); - const access_token = result.accessToken; + const accessToken = result.accessToken; accessTokenExpiresIn = result.expiresIn; - const authorizationObject = getAuthorizationRequestObject(access_token); + const authorizationObject = getAuthorizationRequestObject(accessToken); swagger.authActions.authorize(authorizationObject); } else { alert(await response.text()) } } -const getAuthorizationRequestObject = (access_token) => { +const getAuthorizationRequestObject = (accessToken) => { return { "bearerAuth": { "name": "Bearer", @@ -224,7 +224,7 @@ const getAuthorizationRequestObject = (access_token) => { "name": "Authorization", "in": "header" }, - value: access_token + value: accessToken }, }; } diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Services.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Services.cs index 876f94037b..f156819583 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Services.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/Program.Services.cs @@ -90,7 +90,7 @@ private static void AddBlazor(WebApplicationBuilder builder) foreach (var xHeader in currentRequest.Headers.Where(h => h.Key.StartsWith("X-", StringComparison.InvariantCultureIgnoreCase))) { - httpClient.DefaultRequestHeaders.Add(xHeader.Key, string.Join(',', xHeader.Value!)); + httpClient.DefaultRequestHeaders.Add(xHeader.Key, string.Join(',', xHeader.Value.AsEnumerable())); } if (httpClient.DefaultRequestHeaders.Contains(forwardedHeadersOptions.ForwardedForHeaderName) is false && @@ -110,7 +110,7 @@ private static void AddBlazor(WebApplicationBuilder builder) if (currentRequest.Headers.TryGetValue(HeaderNames.Referer, out headerValues)) { - httpClient.DefaultRequestHeaders.Add(HeaderNames.Referer, string.Join(',', headerValues!)); + httpClient.DefaultRequestHeaders.Add(HeaderNames.Referer, string.Join(',', headerValues.AsEnumerable())); } return httpClient; diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx index 1f2165350a..7a58842f3c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx @@ -210,9 +210,6 @@ انتخاب زبان - - - تنظیمات پروفایل @@ -809,9 +806,6 @@ 2FA - - - 2FA تنظیم مجدد کلید مشترک 2fa باید 2fa را غیرفعال کند تا زمانی که یک توکن 2fa بر اساس کلید مشترک جدید تأیید شود. diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx index f3b7ac1621..5e7af87c7a 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx @@ -210,9 +210,6 @@ Select language - - - Settings Profile diff --git a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/Contracts/IPrerenderStateService.cs b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/Contracts/IPrerenderStateService.cs index 84c4acb4ec..e36f35c38c 100644 --- a/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/Contracts/IPrerenderStateService.cs +++ b/src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/Contracts/IPrerenderStateService.cs @@ -6,7 +6,7 @@ namespace Boilerplate.Shared.Services.Contracts; /// The Client.Core codebase is designed to support various Blazor hosting models, including Hybrid and WebAssembly, /// which may or may not enable pre-rendering. To ensure consistent behavior across all scenarios, /// the `IPrerenderStateService` interface is introduced. This service provides the `GetValue` method for data retrieval, -/// such as during the `OnInitAsync` method in components like UserMenu. +/// such as during the `OnInitAsync` method in components like SettingsPage. /// /// The `WebPrerenderStateService` implementation of `IPrerenderStateService` facilitates pre-rendering by using /// PersistentComponentState to persist data across renders, simplifies the process of managing application state in pre-rendering scenarios outlined in diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Client/Bit.Websites.Careers.Client.csproj b/src/Websites/Careers/src/Bit.Websites.Careers.Client/Bit.Websites.Careers.Client.csproj index 71a5eca69c..0ca0bc602d 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Client/Bit.Websites.Careers.Client.csproj +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Client/Bit.Websites.Careers.Client.csproj @@ -22,15 +22,15 @@ - - + + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Client/package-lock.json b/src/Websites/Careers/src/Bit.Websites.Careers.Client/package-lock.json index 3241913fc0..46f0abd80e 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Client/package-lock.json +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Client/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } }, @@ -887,7 +887,7 @@ } }, "node_modules/sass": { - "version": "1.80.5", + "version": "1.81.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Client/package.json b/src/Websites/Careers/src/Bit.Websites.Careers.Client/package.json index 8f2181ad89..7d426923cd 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Client/package.json +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Client/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } } diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Server/Bit.Websites.Careers.Server.csproj b/src/Websites/Careers/src/Bit.Websites.Careers.Server/Bit.Websites.Careers.Server.csproj index 51d7ea4526..b7a3b80a17 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Server/Bit.Websites.Careers.Server.csproj +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Server/Bit.Websites.Careers.Server.csproj @@ -10,11 +10,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Careers/src/Bit.Websites.Careers.Shared/Bit.Websites.Careers.Shared.csproj b/src/Websites/Careers/src/Bit.Websites.Careers.Shared/Bit.Websites.Careers.Shared.csproj index 72da2da997..edc5a2be54 100644 --- a/src/Websites/Careers/src/Bit.Websites.Careers.Shared/Bit.Websites.Careers.Shared.csproj +++ b/src/Websites/Careers/src/Bit.Websites.Careers.Shared/Bit.Websites.Careers.Shared.csproj @@ -6,11 +6,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Careers/src/Directory.Build.props b/src/Websites/Careers/src/Directory.Build.props index 8fd44b1f06..05b025af10 100644 --- a/src/Websites/Careers/src/Directory.Build.props +++ b/src/Websites/Careers/src/Directory.Build.props @@ -1,7 +1,7 @@ - + - 12.0 + 13.0 enable enable $(NoWarn);CS1998;CS1591 diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Bit.Websites.Platform.Client.csproj b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Bit.Websites.Platform.Client.csproj index 5f7ccd28c1..b9f50c8ad6 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Bit.Websites.Platform.Client.csproj +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Bit.Websites.Platform.Client.csproj @@ -22,16 +22,16 @@ - - - + + + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor index b70e2346bd..46e75c66d7 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor @@ -167,8 +167,8 @@ rm $HOME/dotnet.tar.gz }
  • -
    Install Bit Boilerplate project template
    - dotnet new install Bit.Boilerplate::9.0.0 +
    Install Bit Boilerplate project template
    + dotnet new install Bit.Boilerplate::9.0.1
  • @if (showCrossPlatform && devOS is "Windows") { diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor.cs b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor.cs index 20b77f73cf..f79e927c35 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor.cs +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates03GettingStartedPage.razor.cs @@ -38,7 +38,7 @@ public partial class Templates03GettingStartedPage command:"dotnet nuget add source \"https://api.nuget.org/v3/index.json\" --name \"nuget.org\"; dotnet workload install wasm-tools;"), (text:@"echo 'Install the Bit.Boilerplate project template https://www.nuget.org/packages/Boilerplate.Templates';", - command:"dotnet new install Bit.Boilerplate::9.0.0;") + command:"dotnet new install Bit.Boilerplate::9.0.1;") ]; if (enableVirtualization) diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates04ProjectStructurePage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates04ProjectStructurePage.razor index c5b975bf51..70eb0d33a2 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates04ProjectStructurePage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates04ProjectStructurePage.razor @@ -41,8 +41,8 @@

    In addition to the .editorconfig and .gitignore files, - which have clear purposes, there are two additional files: Clean.bat and Clean.sh. These scripts are designed to remove all auto-generated files from your project. - To understand what are these extra files, please refer to the comments within these scripts. + which have clear purposes, there are two additional files: Clean.bat (Windows) and Clean.sh (macOS/Linux). These scripts are designed to remove all auto-generated files from your project. + To understand what are these extra files, please refer to the comments within these scripts. In short, if you face a tough build issue, close your IDE and run the `Clean.bat` or `Clean.sh` file.

    There is also a file named global.json, which mitigates potential issues arising from the use of different .NET SDK versions by team members. diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates06RunProjectPage.razor b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates06RunProjectPage.razor index 12c8c2cace..d4b94a76c0 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates06RunProjectPage.razor +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/Pages/Templates/Templates06RunProjectPage.razor @@ -96,6 +96,9 @@ By clicking on Open a Remote Window and selecting Reopen in Container from the dropdown, you can run the project in Docker in the background. This approach allows Visual Studio Code to operate within Docker, along with all its extensions and SDKs, leading to a much faster development experience.
    +
    + If you're developing on Windows, it is recommended to run bash ./Clean.sh inside container the first time you open the project. +
    diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/package-lock.json b/src/Websites/Platform/src/Bit.Websites.Platform.Client/package-lock.json index d98bbcbfdb..0a9d8846df 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/package-lock.json +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } }, @@ -887,7 +887,7 @@ } }, "node_modules/sass": { - "version": "1.80.5", + "version": "1.81.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Client/package.json b/src/Websites/Platform/src/Bit.Websites.Platform.Client/package.json index 8f2181ad89..7d426923cd 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Client/package.json +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Client/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } } diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj index 76695c36db..4ae0b16dc9 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Server/Bit.Websites.Platform.Server.csproj @@ -10,11 +10,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Platform/src/Bit.Websites.Platform.Shared/Bit.Websites.Platform.Shared.csproj b/src/Websites/Platform/src/Bit.Websites.Platform.Shared/Bit.Websites.Platform.Shared.csproj index 72da2da997..edc5a2be54 100644 --- a/src/Websites/Platform/src/Bit.Websites.Platform.Shared/Bit.Websites.Platform.Shared.csproj +++ b/src/Websites/Platform/src/Bit.Websites.Platform.Shared/Bit.Websites.Platform.Shared.csproj @@ -6,11 +6,11 @@
    - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Platform/src/Directory.Build.props b/src/Websites/Platform/src/Directory.Build.props index d008fee570..b33cd4b587 100644 --- a/src/Websites/Platform/src/Directory.Build.props +++ b/src/Websites/Platform/src/Directory.Build.props @@ -1,4 +1,4 @@ - + preview diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Client/Bit.Websites.Sales.Client.csproj b/src/Websites/Sales/src/Bit.Websites.Sales.Client/Bit.Websites.Sales.Client.csproj index 6ec719d9e1..3601172eeb 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Client/Bit.Websites.Sales.Client.csproj +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Client/Bit.Websites.Sales.Client.csproj @@ -22,15 +22,15 @@ - - + + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Client/package-lock.json b/src/Websites/Sales/src/Bit.Websites.Sales.Client/package-lock.json index 524b6e415c..731e52e4ea 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Client/package-lock.json +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Client/package-lock.json @@ -6,7 +6,7 @@ "": { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } }, @@ -887,7 +887,7 @@ } }, "node_modules/sass": { - "version": "1.80.5", + "version": "1.81.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.5.tgz", "integrity": "sha512-TQd2aoQl/+zsxRMEDSxVdpPIqeq9UFc6pr7PzkugiTx3VYCFPUaa3P4RrBQsqok4PO200Vkz0vXQBNlg7W907g==", "dev": true, diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Client/package.json b/src/Websites/Sales/src/Bit.Websites.Sales.Client/package.json index 8f2181ad89..7d426923cd 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Client/package.json +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Client/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "esbuild": "0.24.0", - "sass": "1.80.5", + "sass": "1.81.0", "typescript": "5.6.3" } } diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Bit.Websites.Sales.Server.csproj b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Bit.Websites.Sales.Server.csproj index 126f6d4a14..0bbe2d3008 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Server/Bit.Websites.Sales.Server.csproj +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Server/Bit.Websites.Sales.Server.csproj @@ -10,11 +10,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Sales/src/Bit.Websites.Sales.Shared/Bit.Websites.Sales.Shared.csproj b/src/Websites/Sales/src/Bit.Websites.Sales.Shared/Bit.Websites.Sales.Shared.csproj index 72da2da997..edc5a2be54 100644 --- a/src/Websites/Sales/src/Bit.Websites.Sales.Shared/Bit.Websites.Sales.Shared.csproj +++ b/src/Websites/Sales/src/Bit.Websites.Sales.Shared/Bit.Websites.Sales.Shared.csproj @@ -6,11 +6,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Websites/Sales/src/Directory.Build.props b/src/Websites/Sales/src/Directory.Build.props index a797397358..0562fa123c 100644 --- a/src/Websites/Sales/src/Directory.Build.props +++ b/src/Websites/Sales/src/Directory.Build.props @@ -1,7 +1,7 @@ - + - 12.0 + 13.0 enable enable $(NoWarn);CS1998;CS1591