diff --git a/.gitattributes b/.gitattributes index 1ff0c423..ca8cb466 100644 --- a/.gitattributes +++ b/.gitattributes @@ -61,3 +61,6 @@ #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain + +# Make sure unix eol in shell scripts +*.sh eol=lf diff --git a/.gitignore b/.gitignore index b79da535..a16d2417 100644 --- a/.gitignore +++ b/.gitignore @@ -295,3 +295,4 @@ coverage.xml /format-report.json src/.idea /samples/ASP.NETCore/WebApplication/wwwroot/lib/ +/samples/BlazorWasm/wwwroot/lib/ diff --git a/README.md b/README.md index a335c8df..8fa5f9d2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,15 @@ - .NET Framework 4.5+ - Other runtimes which implement .NET Standard 2.0+ like .NET Core 2.0+, Xamarin.Android 8.0+, Xamarin.iOS 10.14+, etc. (For more details, please refer to [this table](https://learn.microsoft.com/en-us/dotnet/standard/net-standard?tabs=net-standard-2-0).) -https://configcat.com +Starting with v9.3.0, the ConfigCat SDK can be used in applications that employ [trimmed self-contained](https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-self-contained) or various ahead-of-time (AOT) compilation deployment models. +The SDK has been tested with the following AOT solutions: +* [Native AOT](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/) - see also [Sample .NET Console app](https://github.com/configcat/.net-sdk/tree/master/samples/ConsoleApp) +* [Mono AOT](https://www.mono-project.com/docs/advanced/aot/) - see also [Sample .NET MAUI app](https://github.com/configcat/.net-sdk/tree/master/samples/MAUI) +* [Mono AOT for WebAssembly/Emscripten (wasm-tools)](https://learn.microsoft.com/en-us/aspnet/core/blazor/webassembly-build-tools-and-aot) - see also [Sample ASP.NET Core Blazor WebAssembly app](https://github.com/configcat/.net-sdk/tree/master/samples/BlazorWasm) +* [IL2CPP](https://docs.unity3d.com/2021.3/Documentation/Manual/IL2CPP.html) - see also [Sample Unity WebGL scripts](https://github.com/configcat/.net-sdk/tree/master/samples/UnityWebGL) +* [.NET Native](https://learn.microsoft.com/en-us/windows/uwp/dotnet-native/) (UWP) +* [Crossgen2](https://devblogs.microsoft.com/dotnet/conversation-about-crossgen2/) (ReadyToRun) +* [Ngen](https://learn.microsoft.com/en-us/dotnet/framework/tools/ngen-exe-native-image-generator) (Native Image Generator) ConfigCat SDK for .NET provides easy integration for your application to ConfigCat. @@ -48,7 +56,7 @@ var client = ConfigCatClient.Get("#YOUR-SDK-KEY#"); ### 5. Get your setting value: ```c# -var isMyAwesomeFeatureEnabled = client.GetValue("isMyAwesomeFeatureEnabled", false); +var isMyAwesomeFeatureEnabled = await client.GetValueAsync("isMyAwesomeFeatureEnabled", false); if(isMyAwesomeFeatureEnabled) { @@ -74,7 +82,7 @@ Read more about [Targeting here](https://configcat.com/docs/advanced/targeting). ```c# User currentUser = new User("435170f4-8a8b-4b67-a723-505ac7cdea92"); -var isMyAwesomeFeatureEnabled = client.GetValue( +var isMyAwesomeFeatureEnabled = await client.GetValueAsync( "isMyAwesomeFeatureEnabled", defaultValue: false, user: currentUser); @@ -82,8 +90,10 @@ var isMyAwesomeFeatureEnabled = client.GetValue( ## Sample/Demo apps * [Sample Console App](https://github.com/configcat/.net-sdk/tree/master/samples/ConsoleApp) - * [Sample Web App](https://github.com/configcat/.net-sdk/tree/master/samples/ASP.NETCore) - + * [Sample Multi-page Web App](https://github.com/configcat/.net-sdk/tree/master/samples/ASP.NETCore) + * [Sample Single-page Web App](https://github.com/configcat/.net-sdk/tree/master/samples/BlazorWasm) + * [Sample Mobile/Windows Store App](https://github.com/configcat/.net-sdk/tree/master/samples/MAUI) + ## Polling Modes The ConfigCat SDK supports 3 different polling mechanisms to acquire the setting values from ConfigCat. After latest setting values are downloaded, they are stored in the internal cache then all requests are served from there. Read more about Polling Modes and how to use them at [ConfigCat Docs](https://configcat.com/docs/sdk-reference/dotnet/). diff --git a/benchmarks/ConfigCat.Client.Benchmarks/ConfigCat.Client.Benchmarks.csproj b/benchmarks/ConfigCat.Client.Benchmarks/ConfigCat.Client.Benchmarks.csproj index d6560026..2810735d 100644 --- a/benchmarks/ConfigCat.Client.Benchmarks/ConfigCat.Client.Benchmarks.csproj +++ b/benchmarks/ConfigCat.Client.Benchmarks/ConfigCat.Client.Benchmarks.csproj @@ -2,8 +2,8 @@ Exe - net48;net6.0 - 10.0 + net48;net8.0 + 12.0 enable nullable true diff --git a/benchmarks/NewVersionLib/NewVersionLib.csproj b/benchmarks/NewVersionLib/NewVersionLib.csproj index d5434022..a917a5d8 100644 --- a/benchmarks/NewVersionLib/NewVersionLib.csproj +++ b/benchmarks/NewVersionLib/NewVersionLib.csproj @@ -3,8 +3,8 @@ ConfigCatClientBenchmarks ConfigCat.Client.Benchmarks.New - net48;net6.0 - 10.0 + net48;net8.0 + 12.0 enable nullable true diff --git a/benchmarks/OldVersionLib/OldVersionLib.csproj b/benchmarks/OldVersionLib/OldVersionLib.csproj index 001c5e43..7c7c6a7f 100644 --- a/benchmarks/OldVersionLib/OldVersionLib.csproj +++ b/benchmarks/OldVersionLib/OldVersionLib.csproj @@ -5,8 +5,8 @@ to which the interals are made visible by ConfigCat.Client. --> ConfigCatClientTests ConfigCat.Client.Benchmarks.New - net48;net6.0 - 10.0 + net48;net8.0 + 12.0 enable nullable true diff --git a/samples/ASP.NETCore/README.md b/samples/ASP.NETCore/README.md index fbf0bea7..c8723403 100644 --- a/samples/ASP.NETCore/README.md +++ b/samples/ASP.NETCore/README.md @@ -1,8 +1,8 @@ -# Sample .NET Core WebApp app +# Sample ASP.NET Core MVC app -This is a simple .NET Core web application to demonstrate how to use the ConfigCat SDK. +This is a simple [ASP.NET Core MVC](https://learn.microsoft.com/en-us/aspnet/core/mvc) web application to demonstrate how to use the ConfigCat SDK. -1. Install [.NET Core](https://dotnet.microsoft.com/download) +1. Install [.NET](https://dotnet.microsoft.com/download) 2. Change dir to `/WebApplication` ```bash cd WebApplication diff --git a/samples/ASP.NETCore/WebApplication/Adapters/ConfigCatToMSLoggerAdapter.cs b/samples/ASP.NETCore/WebApplication/Adapters/ConfigCatToMSLoggerAdapter.cs index 9a9a1504..124ad32a 100644 --- a/samples/ASP.NETCore/WebApplication/Adapters/ConfigCatToMSLoggerAdapter.cs +++ b/samples/ASP.NETCore/WebApplication/Adapters/ConfigCatToMSLoggerAdapter.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using Microsoft.Extensions.Logging; -namespace WebApplication.Adapters; +namespace ConfigCat.Client.Extensions.Adapters; public class ConfigCatToMSLoggerAdapter : ConfigCat.Client.IConfigCatLogger { diff --git a/samples/ASP.NETCore/WebApplication/Program.cs b/samples/ASP.NETCore/WebApplication/Program.cs index ca84cd6e..0cf3f478 100644 --- a/samples/ASP.NETCore/WebApplication/Program.cs +++ b/samples/ASP.NETCore/WebApplication/Program.cs @@ -1,11 +1,10 @@ -using System; +using System; using ConfigCat.Client; +using ConfigCat.Client.Extensions.Adapters; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using WebApplication.Adapters; var builder = Microsoft.AspNetCore.Builder.WebApplication.CreateBuilder(args); @@ -22,7 +21,7 @@ { var logger = sp.GetRequiredService>(); - return ConfigCatClient.Get(configCatSdkKey, options => + return ConfigCatClient.Get(configCatSdkKey!, options => { options.PollingMode = PollingModes.LazyLoad(cacheTimeToLive: TimeSpan.FromSeconds(120)); options.Logger = new ConfigCatToMSLoggerAdapter(logger); diff --git a/samples/ASP.NETCore/WebApplication/WebApplication.csproj b/samples/ASP.NETCore/WebApplication/WebApplication.csproj index 1eaeb5ac..66f3be73 100644 --- a/samples/ASP.NETCore/WebApplication/WebApplication.csproj +++ b/samples/ASP.NETCore/WebApplication/WebApplication.csproj @@ -1,15 +1,15 @@ - + - net6.0 + net8.0 enable - - + + diff --git a/samples/BlazorWasm/App.razor b/samples/BlazorWasm/App.razor new file mode 100644 index 00000000..6fd3ed1b --- /dev/null +++ b/samples/BlazorWasm/App.razor @@ -0,0 +1,12 @@ + + + + + + + Not found + +

Sorry, there's nothing at this address.

+
+
+
diff --git a/samples/BlazorWasm/BlazorWasm.csproj b/samples/BlazorWasm/BlazorWasm.csproj new file mode 100644 index 00000000..e1542651 --- /dev/null +++ b/samples/BlazorWasm/BlazorWasm.csproj @@ -0,0 +1,27 @@ + + + + net8.0 + enable + enable + true + true + + + + + + + + + + + + + + + + + + + diff --git a/samples/BlazorWasm/BlazorWasm.sln b/samples/BlazorWasm/BlazorWasm.sln new file mode 100644 index 00000000..a292bea5 --- /dev/null +++ b/samples/BlazorWasm/BlazorWasm.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35027.167 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ProjectReferences", "ProjectReferences", "{73A8C054-9067-4474-B6D0-A0A924DF5DB6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConfigCatClient", "..\..\src\ConfigCatClient\ConfigCatClient.csproj", "{8F64DC62-D524-4310-A620-03D65ED829BC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWasm", "BlazorWasm.csproj", "{B0E7F85F-048E-48A5-91BA-BB73FF3D7118}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Benchmark|Any CPU = Benchmark|Any CPU + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8F64DC62-D524-4310-A620-03D65ED829BC}.Benchmark|Any CPU.ActiveCfg = Benchmark|Any CPU + {8F64DC62-D524-4310-A620-03D65ED829BC}.Benchmark|Any CPU.Build.0 = Benchmark|Any CPU + {8F64DC62-D524-4310-A620-03D65ED829BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F64DC62-D524-4310-A620-03D65ED829BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F64DC62-D524-4310-A620-03D65ED829BC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F64DC62-D524-4310-A620-03D65ED829BC}.Release|Any CPU.Build.0 = Release|Any CPU + {B0E7F85F-048E-48A5-91BA-BB73FF3D7118}.Benchmark|Any CPU.ActiveCfg = Release|Any CPU + {B0E7F85F-048E-48A5-91BA-BB73FF3D7118}.Benchmark|Any CPU.Build.0 = Release|Any CPU + {B0E7F85F-048E-48A5-91BA-BB73FF3D7118}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B0E7F85F-048E-48A5-91BA-BB73FF3D7118}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B0E7F85F-048E-48A5-91BA-BB73FF3D7118}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B0E7F85F-048E-48A5-91BA-BB73FF3D7118}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {8F64DC62-D524-4310-A620-03D65ED829BC} = {73A8C054-9067-4474-B6D0-A0A924DF5DB6} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5F653DA7-4E85-40D3-AB27-E2599966E0AC} + EndGlobalSection +EndGlobal diff --git a/samples/BlazorWasm/Layout/MainLayout.razor b/samples/BlazorWasm/Layout/MainLayout.razor new file mode 100644 index 00000000..76eb7252 --- /dev/null +++ b/samples/BlazorWasm/Layout/MainLayout.razor @@ -0,0 +1,16 @@ +@inherits LayoutComponentBase +
+ + +
+
+ About +
+ +
+ @Body +
+
+
diff --git a/samples/BlazorWasm/Layout/MainLayout.razor.css b/samples/BlazorWasm/Layout/MainLayout.razor.css new file mode 100644 index 00000000..ecf25e5b --- /dev/null +++ b/samples/BlazorWasm/Layout/MainLayout.razor.css @@ -0,0 +1,77 @@ +.page { + position: relative; + display: flex; + flex-direction: column; +} + +main { + flex: 1; +} + +.sidebar { + background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); +} + +.top-row { + background-color: #f7f7f7; + border-bottom: 1px solid #d6d5d5; + justify-content: flex-end; + height: 3.5rem; + display: flex; + align-items: center; +} + + .top-row ::deep a, .top-row ::deep .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + text-decoration: none; + } + + .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { + text-decoration: underline; + } + + .top-row ::deep a:first-child { + overflow: hidden; + text-overflow: ellipsis; + } + +@media (max-width: 640.98px) { + .top-row { + justify-content: space-between; + } + + .top-row ::deep a, .top-row ::deep .btn-link { + margin-left: 0; + } +} + +@media (min-width: 641px) { + .page { + flex-direction: row; + } + + .sidebar { + width: 250px; + height: 100vh; + position: sticky; + top: 0; + } + + .top-row { + position: sticky; + top: 0; + z-index: 1; + } + + .top-row.auth ::deep a:first-child { + flex: 1; + text-align: right; + width: 0; + } + + .top-row, article { + padding-left: 2rem !important; + padding-right: 1.5rem !important; + } +} diff --git a/samples/BlazorWasm/Layout/NavMenu.razor b/samples/BlazorWasm/Layout/NavMenu.razor new file mode 100644 index 00000000..45270106 --- /dev/null +++ b/samples/BlazorWasm/Layout/NavMenu.razor @@ -0,0 +1,29 @@ + + + + +@code { + private bool collapseNavMenu = true; + + private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; + + private void ToggleNavMenu() + { + collapseNavMenu = !collapseNavMenu; + } +} diff --git a/samples/BlazorWasm/Layout/NavMenu.razor.css b/samples/BlazorWasm/Layout/NavMenu.razor.css new file mode 100644 index 00000000..881d128a --- /dev/null +++ b/samples/BlazorWasm/Layout/NavMenu.razor.css @@ -0,0 +1,83 @@ +.navbar-toggler { + background-color: rgba(255, 255, 255, 0.1); +} + +.top-row { + height: 3.5rem; + background-color: rgba(0,0,0,0.4); +} + +.navbar-brand { + font-size: 1.1rem; +} + +.bi { + display: inline-block; + position: relative; + width: 1.25rem; + height: 1.25rem; + margin-right: 0.75rem; + top: -1px; + background-size: cover; +} + +.bi-house-door-fill-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); +} + +.bi-plus-square-fill-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); +} + +.bi-list-nested-nav-menu { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); +} + +.nav-item { + font-size: 0.9rem; + padding-bottom: 0.5rem; +} + + .nav-item:first-of-type { + padding-top: 1rem; + } + + .nav-item:last-of-type { + padding-bottom: 1rem; + } + + .nav-item ::deep a { + color: #d7d7d7; + border-radius: 4px; + height: 3rem; + display: flex; + align-items: center; + line-height: 3rem; + } + +.nav-item ::deep a.active { + background-color: rgba(255,255,255,0.37); + color: white; +} + +.nav-item ::deep a:hover { + background-color: rgba(255,255,255,0.1); + color: white; +} + +@media (min-width: 641px) { + .navbar-toggler { + display: none; + } + + .collapse { + /* Never collapse the sidebar for wide screens */ + display: block; + } + + .nav-scrollable { + /* Allow sidebar to scroll for tall menus */ + height: calc(100vh - 3.5rem); + overflow-y: auto; + } +} diff --git a/samples/BlazorWasm/Pages/Home.razor b/samples/BlazorWasm/Pages/Home.razor new file mode 100644 index 00000000..5b4b63d0 --- /dev/null +++ b/samples/BlazorWasm/Pages/Home.razor @@ -0,0 +1,23 @@ +@page "/" + +Home + +

Welcome to the ConfigCat Sample app for ASP.NET Core Blazor WebAssembly!

+ +
+ ConfigCat Logo +

❤️

+ Blazor Logo +
+
+
+

Simple feature flag.

+ +

Value returned from ConfigCat: @isAwesomeEnabled

+
+

Feature with Targeting

+

Set up to be enabled only for users with an e-mail address that contains "@@example.com"

+ + +

Value returned from ConfigCat: @isPOCEnabled

+
diff --git a/samples/BlazorWasm/Pages/Home.razor.cs b/samples/BlazorWasm/Pages/Home.razor.cs new file mode 100644 index 00000000..b246b099 --- /dev/null +++ b/samples/BlazorWasm/Pages/Home.razor.cs @@ -0,0 +1,27 @@ +using System.Diagnostics.CodeAnalysis; +using ConfigCat.Client; +using Microsoft.AspNetCore.Components; + +namespace BlazorWasm.Pages; + +public partial class Home : ComponentBase +{ + [Inject, NotNull] + private IConfigCatClient? ConfigCatClient { get; set; } + + private bool? isAwesomeEnabled; + private bool? isPOCEnabled; + private string userEmail = "configcat@example.com"; + + private async Task CheckAwesome() + { + this.isAwesomeEnabled = await ConfigCatClient.GetValueAsync("isAwesomeFeatureEnabled", false); + } + + private async Task CheckProofOfConcept() + { + var userObject = new User("#SOME-USER-ID#") { Email = this.userEmail }; + // Read more about the User Object: https://configcat.com/docs/advanced/user-object + this.isPOCEnabled = await ConfigCatClient.GetValueAsync("isPOCFeatureEnabled", false, userObject); + } +} diff --git a/samples/BlazorWasm/Pages/Home.razor.css b/samples/BlazorWasm/Pages/Home.razor.css new file mode 100644 index 00000000..f4126a8e --- /dev/null +++ b/samples/BlazorWasm/Pages/Home.razor.css @@ -0,0 +1,15 @@ +* { + text-align: center; +} + +.logos { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; +} + +.heart { + font-size: 2em; + margin-left: 0.5em; +} diff --git a/samples/BlazorWasm/Program.cs b/samples/BlazorWasm/Program.cs new file mode 100644 index 00000000..26ee80cc --- /dev/null +++ b/samples/BlazorWasm/Program.cs @@ -0,0 +1,29 @@ +using BlazorWasm; +using ConfigCat.Client; +using ConfigCat.Client.Extensions.Adapters; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); +builder.RootComponents.Add("#app"); +builder.RootComponents.Add("head::after"); + +builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); + +builder.Logging.SetMinimumLevel(builder.HostEnvironment.IsDevelopment() + ? Microsoft.Extensions.Logging.LogLevel.Information + : Microsoft.Extensions.Logging.LogLevel.Warning); + +// Register ConfigCatClient as a singleton service so you can inject it in your components. +builder.Services.AddSingleton(sp => +{ + var logger = sp.GetRequiredService>(); + + return ConfigCatClient.Get("PKDVCLf-Hq-h-kCzMp-L7Q/HhOWfwVtZ0mb30i9wi17GQ", options => + { + options.PollingMode = PollingModes.AutoPoll(); + options.Logger = new ConfigCatToMSLoggerAdapter(logger); + }); +}); + +await builder.Build().RunAsync(); diff --git a/samples/BlazorWasm/README.md b/samples/BlazorWasm/README.md new file mode 100644 index 00000000..a5e60481 --- /dev/null +++ b/samples/BlazorWasm/README.md @@ -0,0 +1,26 @@ +# Sample ASP.NET Core Blazor WebAssembly app + +This is a simple [ASP.NET Core Blazor WebAssembly](https://learn.microsoft.com/en-us/aspnet/core/blazor) application to demonstrate how to use the ConfigCat SDK. + +1. Install [.NET](https://dotnet.microsoft.com/download) +2. Run app + ```bash + dotnet run -- urls=http://localhost:5000 + ``` +3. Open http://localhost:5000 in browser + +## Ahead-of-time (AOT) compilation + +The sample app also demonstrates that the ConfigCat SDK can be used in [Blazor Wasm applications using AOT compilation](https://learn.microsoft.com/en-us/aspnet/core/blazor/webassembly-build-tools-and-aot). + +1. Make sure you have [the .NET WebAssembly build tools](https://learn.microsoft.com/en-us/aspnet/core/blazor/webassembly-build-tools-and-aot?view=aspnetcore-8.0#net-webassembly-build-tools) installed in your development environment. + ```bash + dotnet workload install wasm-tools + ``` +2. Execute the build script corresponding to your OS (`build-aot.cmd` on Windows, `build-aot.sh` on Linux). +3. Locate the web assets in the publish output directory (`bin/Release/net8.0/publish/wwwroot`). +4. Start a local web server in this directory to serve the files over HTTP. E.g. + ```bash + dotnet serve --port 5000 + ``` +5. Navigate to http://localhost:5000 in your browser. diff --git a/samples/BlazorWasm/_Imports.razor b/samples/BlazorWasm/_Imports.razor new file mode 100644 index 00000000..fb2c61e4 --- /dev/null +++ b/samples/BlazorWasm/_Imports.razor @@ -0,0 +1,10 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.AspNetCore.Components.WebAssembly.Http +@using Microsoft.JSInterop +@using BlazorWasm +@using BlazorWasm.Layout diff --git a/samples/BlazorWasm/build-aot.cmd b/samples/BlazorWasm/build-aot.cmd new file mode 100644 index 00000000..2b9612e0 --- /dev/null +++ b/samples/BlazorWasm/build-aot.cmd @@ -0,0 +1,3 @@ +@ECHO OFF + +dotnet publish -c Release -f net8.0 \ No newline at end of file diff --git a/samples/BlazorWasm/build-aot.sh b/samples/BlazorWasm/build-aot.sh new file mode 100644 index 00000000..ee34cb7d --- /dev/null +++ b/samples/BlazorWasm/build-aot.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +dotnet publish -c Release -f net8.0 \ No newline at end of file diff --git a/samples/BlazorWasm/libman.json b/samples/BlazorWasm/libman.json new file mode 100644 index 00000000..4c9a1a3d --- /dev/null +++ b/samples/BlazorWasm/libman.json @@ -0,0 +1,12 @@ +{ + "version": "1.0", + "defaultProvider": "cdnjs", + "libraries": [ + { + "provider": "jsdelivr", + "library": "bootstrap@5.1.0", + "files": [ "dist/css/bootstrap.min.css", "dist/css/bootstrap.min.css.map" ], + "destination": "wwwroot/lib/bootstrap/" + } + ] +} diff --git a/samples/BlazorWasm/wwwroot/css/app.css b/samples/BlazorWasm/wwwroot/css/app.css new file mode 100644 index 00000000..54a8aa38 --- /dev/null +++ b/samples/BlazorWasm/wwwroot/css/app.css @@ -0,0 +1,103 @@ +html, body { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +h1:focus { + outline: none; +} + +a, .btn-link { + color: #0071c1; +} + +.btn-primary { + color: #fff; + background-color: #1b6ec2; + border-color: #1861ac; +} + +.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { + box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; +} + +.content { + padding-top: 1.1rem; +} + +.valid.modified:not([type=checkbox]) { + outline: 1px solid #26b050; +} + +.invalid { + outline: 1px solid red; +} + +.validation-message { + color: red; +} + +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::after { + content: "An error has occurred." + } + +.loading-progress { + position: relative; + display: block; + width: 8rem; + height: 8rem; + margin: 20vh auto 1rem auto; +} + + .loading-progress circle { + fill: none; + stroke: #e0e0e0; + stroke-width: 0.6rem; + transform-origin: 50% 50%; + transform: rotate(-90deg); + } + + .loading-progress circle:last-child { + stroke: #1b6ec2; + stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; + transition: stroke-dasharray 0.05s ease-in-out; + } + +.loading-progress-text { + position: absolute; + text-align: center; + font-weight: bold; + inset: calc(20vh + 3.25rem) 0 auto 0.2rem; +} + + .loading-progress-text:after { + content: var(--blazor-load-percentage-text, "Loading"); + } + +code { + color: #c02d76; +} diff --git a/samples/BlazorWasm/wwwroot/favicon.png b/samples/BlazorWasm/wwwroot/favicon.png new file mode 100644 index 00000000..8422b596 Binary files /dev/null and b/samples/BlazorWasm/wwwroot/favicon.png differ diff --git a/samples/BlazorWasm/wwwroot/icon-192.png b/samples/BlazorWasm/wwwroot/icon-192.png new file mode 100644 index 00000000..166f56da Binary files /dev/null and b/samples/BlazorWasm/wwwroot/icon-192.png differ diff --git a/samples/BlazorWasm/wwwroot/index.html b/samples/BlazorWasm/wwwroot/index.html new file mode 100644 index 00000000..f1b4b941 --- /dev/null +++ b/samples/BlazorWasm/wwwroot/index.html @@ -0,0 +1,32 @@ + + + + + + + BlazorWasm + + + + + + + + +
+ + + + +
+
+ +
+ An unhandled error has occurred. + Reload + 🗙 +
+ + + + diff --git a/samples/ConsoleApp/ConsoleApp.csproj b/samples/ConsoleApp/ConsoleApp.csproj index 9213c559..87f96bca 100644 --- a/samples/ConsoleApp/ConsoleApp.csproj +++ b/samples/ConsoleApp/ConsoleApp.csproj @@ -2,14 +2,17 @@ Exe - net6.0 + net8.0 enable + true + false + true - +
diff --git a/samples/ConsoleApp/README.md b/samples/ConsoleApp/README.md index b05dfe40..3d3c8a06 100644 --- a/samples/ConsoleApp/README.md +++ b/samples/ConsoleApp/README.md @@ -1,10 +1,19 @@ -# Sample .NET Core Console app +# Sample .NET Console app -This is a simple .NET Core Console application to demonstrate how to use the ConfigCat SDK. +This is a simple .NET Console application to demonstrate how to use the ConfigCat SDK. -1. Install [.NET Core](https://dotnet.microsoft.com/download) +1. Install [.NET](https://dotnet.microsoft.com/download) 2. Run sample app: ```bash dotnet run -``` \ No newline at end of file +``` + +## Ahead-of-time (AOT) compilation + +The sample app also demonstrates that the ConfigCat SDK can be used in .NET 8+ applications compiled to native code using [Native AOT](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/). + +1. Make sure you have [the prerequisites](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/#prerequisites) installed in your development environment. +2. Execute the build script corresponding to your OS (`build-aot.cmd` on Windows, `build-aot.sh` on Linux). +3. Locate the executable in the publish output directory (`bin/Release/net8.0/win-x64/native` on Windows, `bin/Release/net8.0/linux-x64/native` on Linux). +4. Run the executable. \ No newline at end of file diff --git a/samples/ConsoleApp/build-aot.cmd b/samples/ConsoleApp/build-aot.cmd new file mode 100644 index 00000000..16123f3c --- /dev/null +++ b/samples/ConsoleApp/build-aot.cmd @@ -0,0 +1,3 @@ +@ECHO OFF + +dotnet publish -c Release -f net8.0 -r win-x64 \ No newline at end of file diff --git a/samples/ConsoleApp/build-aot.sh b/samples/ConsoleApp/build-aot.sh new file mode 100644 index 00000000..9e3d750a --- /dev/null +++ b/samples/ConsoleApp/build-aot.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +dotnet publish -c Release -f net8.0 -r linux-x64 \ No newline at end of file diff --git a/samples/MAUI/App.xaml b/samples/MAUI/App.xaml new file mode 100644 index 00000000..d105f428 --- /dev/null +++ b/samples/MAUI/App.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/samples/MAUI/App.xaml.cs b/samples/MAUI/App.xaml.cs new file mode 100644 index 00000000..05ee42cb --- /dev/null +++ b/samples/MAUI/App.xaml.cs @@ -0,0 +1,11 @@ +namespace MauiSample; + +public partial class App : Application +{ + public App() + { + InitializeComponent(); + + MainPage = new AppShell(); + } +} diff --git a/samples/MAUI/AppShell.xaml b/samples/MAUI/AppShell.xaml new file mode 100644 index 00000000..9a383472 --- /dev/null +++ b/samples/MAUI/AppShell.xaml @@ -0,0 +1,15 @@ + + + + + + diff --git a/samples/MAUI/AppShell.xaml.cs b/samples/MAUI/AppShell.xaml.cs new file mode 100644 index 00000000..ad4823a1 --- /dev/null +++ b/samples/MAUI/AppShell.xaml.cs @@ -0,0 +1,9 @@ +namespace MauiSample; + +public partial class AppShell : Shell +{ + public AppShell() + { + InitializeComponent(); + } +} diff --git a/samples/MAUI/MainPage.xaml b/samples/MAUI/MainPage.xaml new file mode 100644 index 00000000..40b2dc36 --- /dev/null +++ b/samples/MAUI/MainPage.xaml @@ -0,0 +1,37 @@ + + + + + + + +