Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

BREAKING: Changed namespace to CurrentDevice from BlazorCurrentDevice #3

Merged
merged 1 commit into from
Apr 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions BlazorCurrentDevice-Server/Components/Pages/Home.razor
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
@page "/"
@using BlazorCurrentDevice
@inject IJSRuntime JSRuntime
@using CurrentDevice
@inject ICurrentDeviceService CurrentDeviceService
@rendermode InteractiveServer

Expand Down Expand Up @@ -108,9 +107,9 @@
{
// Get the user agent using JavaScript interop
UserAgent = await CurrentDeviceService.GetUserAgent();
Android = await CurrentDeviceService.Android();
AndroidPhone = await CurrentDeviceService.AndroidPhone();
AndroidTablet = await CurrentDeviceService.AndroidTablet();
Android = await CurrentDeviceService.Android();
Blackberry = await CurrentDeviceService.Blackberry();
BlackberryPhone = await CurrentDeviceService.BlackberryPhone();
BlackberryTablet = await CurrentDeviceService.BlackberryTablet();
Expand Down
2 changes: 1 addition & 1 deletion BlazorCurrentDevice-Server/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using BlazorCurrentDevice;
using CurrentDevice;
using BlazorCurrentDevice_Server.Components;

var builder = WebApplication.CreateBuilder(args);
Expand Down
2 changes: 1 addition & 1 deletion BlazorCurrentDevice-Wasm/Pages/Home.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@page "/"
@using BlazorCurrentDevice
@using CurrentDevice
@inject ICurrentDeviceService CurrentDeviceService

<h1 class="colour-text big-text">Blazor Current Device Demo</h1>
Expand Down Expand Up @@ -111,17 +111,17 @@
bool MeeGo { get; set; }
bool Mobile { get; set; }
bool NodeWebkit { get; set; }
string Orientation { get; set; }

Check warning on line 114 in BlazorCurrentDevice-Wasm/Pages/Home.razor

View workflow job for this annotation

GitHub Actions / deploy-to-github-pages

Non-nullable property 'Orientation' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
string OS { get; set; }

Check warning on line 115 in BlazorCurrentDevice-Wasm/Pages/Home.razor

View workflow job for this annotation

GitHub Actions / deploy-to-github-pages

Non-nullable property 'OS' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
bool Portrait { get; set; }
bool Tablet { get; set; }
bool Television { get; set; }
string Type { get; set; }

Check warning on line 119 in BlazorCurrentDevice-Wasm/Pages/Home.razor

View workflow job for this annotation

GitHub Actions / deploy-to-github-pages

Non-nullable property 'Type' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
bool Windows { get; set; }
bool WindowsPhone { get; set; }
bool WindowsTablet { get; set; }

private string UserAgent { get; set; }

Check warning on line 124 in BlazorCurrentDevice-Wasm/Pages/Home.razor

View workflow job for this annotation

GitHub Actions / deploy-to-github-pages

Non-nullable property 'UserAgent' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.


protected override async Task OnInitializedAsync()
Expand Down
2 changes: 1 addition & 1 deletion BlazorCurrentDevice-Wasm/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using BlazorCurrentDevice;
using CurrentDevice;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using BlazorCurrentDevice_Wasm;
Expand Down
12 changes: 7 additions & 5 deletions CurrentDevice/CurrentDevice.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
<PackageProjectUrl>https://github.com/hugovg/CurrentDevice</PackageProjectUrl>
<RepositoryUrl>https://github.com/hugovg/CurrentDevice</RepositoryUrl>
<RepositoryType>C#</RepositoryType>
<PackageReleaseNotes>Update to make Blazor Server/Hybrid work, Standard 2.1 net8.0</PackageReleaseNotes>
<PackageReleaseNotes>Breaking change: Namespace name changed, internal dictionary should longer cause exceptions</PackageReleaseNotes>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>1.0.1</Version>
<Version>1.0.2</Version>
<Authors>HugoVG</Authors>
<Copyright>HugoVG</Copyright>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RootNamespace>BlazorCurrentDevice</RootNamespace>
<RootNamespace>CurrentDevice</RootNamespace>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageTags>Blazor;CurrentDevice;WASM;Blazor Server;UserAgent;Web</PackageTags>
<PackageId>CurrentDevice</PackageId>
</PropertyGroup>


Expand All @@ -23,9 +25,9 @@
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="3.1.3" />
</ItemGroup>
<ItemGroup>
<None Include="../README.md" Pack="true" PackagePath="README.md" />
<None Include="../README.md" />
<None Include="../LICENSE.txt" Pack="true" PackagePath="LICENSE.txt" />

<None Include="../NugetReadMe.md" Pack="true" PackagePath="README.md" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.3" />
Expand Down
150 changes: 37 additions & 113 deletions CurrentDevice/CurrentDeviceService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,144 +2,71 @@
using Microsoft.JSInterop;
using System.Threading.Tasks;

namespace BlazorCurrentDevice
namespace CurrentDevice
{
internal class CurrentDeviceService : ICurrentDeviceService
{
private IJSRuntime JsRuntime { get; set; }

public CurrentDeviceService(IJSRuntime jSRuntime)
{
JsRuntime = jSRuntime;
}
public CurrentDeviceService(IJSRuntime jSRuntime) => JsRuntime = jSRuntime;

private Dictionary<string, bool> _deviceCache = new Dictionary<string, bool>();
private readonly Dictionary<string, bool> _deviceCache = new Dictionary<string, bool>();


public async Task<bool> MacOs()
{
return await Find("mac");
}
public async Task<bool> MacOs() => await Find("mac");

public async Task<string> Type()
{
return await Desktop() ? "desktop" :
await Tablet() ? "tablet" :
await Mobile() ? "mobile" :
await Television() ? "television" : "unknown";
}
public async Task<string> Type() =>
await Desktop() ? "desktop" :
await Tablet() ? "tablet" :
await Mobile() ? "mobile" :
await Television() ? "television" : "unknown";

public Task<bool> Windows()
{
return Find("windows");
}
public Task<bool> Windows() => Find("windows");

public async Task<bool> WindowsPhone()
{
return await Windows() && await Find("phone");
}
public async Task<bool> WindowsPhone() => await Windows() && await Find("phone");

public async Task<bool> WindowsTablet()
{
return await Windows() && await Find("touch") && !await WindowsPhone();
}
public async Task<bool> WindowsTablet() => await Windows() && await Find("touch") && !await WindowsPhone();

public Task<bool> iPhone()
{
return Find("iphone");
}
public Task<bool> iPhone() => Find("iphone");

public Task<bool> iPod()
{
return Find("ipod");
}
public Task<bool> iPod() => Find("ipod");

public Task<bool> iPad()
{
return Find("ipad");
}
public Task<bool> iPad() => Find("ipad");

public async Task<bool> Desktop()
{
return (!await Mobile()) && (!await Tablet()) && (!await Television());
}
public async Task<bool> Desktop() => !await Mobile() && !await Tablet() && (!await Television());

public async Task<bool> iOS()
{
return await iPhone() || await iPod() || await iPad();
}

public async Task<bool> Android()
{
return await Find("android");
}
public async Task<bool> iOS() => await iPhone() || await iPod() || await iPad();

public async Task<bool> AndroidPhone()
{
return await Android() && await Find("mobile");
}
public async Task<bool> Android() => await Find("android");

public async Task<bool> AndroidTablet()
{
return await Android() && !await AndroidPhone();
}
public async Task<bool> AndroidPhone() => await Android() && await Find("mobile");

public async Task<bool> Blackberry()
{
return await Find("blackberry") || await Find("bb10") || await Find("rim");
}
public async Task<bool> AndroidTablet() => await Android() && !await AndroidPhone();

public async Task<bool> BlackberryPhone()
{
return await Blackberry() && !(await Find("tablet"));
}
public async Task<bool> Blackberry() => await Find("blackberry") || await Find("bb10") || await Find("rim");

public async Task<bool> BlackberryPhone() => await Blackberry() && !(await Find("tablet"));

public async Task<bool> BlackberryTablet()
{
return await Blackberry() && await Find("tablet");
}
public async Task<bool> BlackberryTablet() => await Blackberry() && await Find("tablet");

public Task<bool> MeeGo()
{
return Find("meego");
}
public Task<bool> MeeGo() => Find("meego");

public async Task<bool> Mobile()
{
return await AndroidPhone() || await iPhone() || await iPod() || await WindowsPhone() ||
await BlackberryPhone() || await MeeGo();
}
public async Task<bool> Mobile() =>
await AndroidPhone() || await iPhone() || await iPod() || await WindowsPhone() ||
await BlackberryPhone() || await MeeGo();

public async Task<bool> Tablet()
{
return await iPad() || await AndroidTablet() || await BlackberryTablet() || await WindowsTablet();
}
public async Task<bool> Tablet() => await iPad() || await AndroidTablet() || await BlackberryTablet() || await WindowsTablet();

public async Task<bool> Television()
{
return FindIn(UserAgent ??= await GetUserAgent(), Televisions) != "unknown";
}
public async Task<bool> Television() => FindIn(UserAgent ??= await GetUserAgent(), Televisions) != "unknown";


public async Task<bool> Landscape()
{
return await Orientation() == "landscape";
}
public async Task<bool> Landscape() => await Orientation() == "landscape";

public async Task<bool> Portrait()
{
return await Orientation() == "portrait";
}
public async Task<bool> Portrait() => await Orientation() == "portrait";

public async Task<string> Orientation()
{
return FindIn(await GetOrientation(), Orientations);
}
public async Task<string> Orientation() => FindIn(await GetOrientation(), Orientations);

public async Task<string> OS()
{
return FindIn(UserAgent ??= await GetUserAgent(), OperatingSystems);
}
public async Task<string> OS() => FindIn(UserAgent ??= await GetUserAgent(), OperatingSystems);


private async Task<bool> Find(string value)
Expand All @@ -151,11 +78,11 @@ private async Task<bool> Find(string value)

UserAgent ??= await GetUserAgent();
foundValue = UserAgent.Contains(value);
_deviceCache.Add(value, foundValue);
_deviceCache.TryAdd(value, foundValue);
return foundValue;
}

private string FindIn(string searchString, string[] values)
private static string FindIn(string searchString, string[] values)
{
foreach (var value in values)
{
Expand All @@ -168,10 +95,7 @@ private string FindIn(string searchString, string[] values)
return "unknown";
}

public async Task<string> GetUserAgent()
{
return (await JsRuntime.InvokeAsync<string>("eval", "window.navigator.userAgent")).ToLower();
}
public async Task<string> GetUserAgent() => (await JsRuntime.InvokeAsync<string>("eval", "window.navigator.userAgent")).ToLower();

/// <summary>
/// We are using either Scoped or Singleton so this method will be called only once for wasm, and once per connection for server
Expand Down
2 changes: 1 addition & 1 deletion CurrentDevice/ICurrentDeviceService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Threading.Tasks;

namespace BlazorCurrentDevice
namespace CurrentDevice
{
/// <summary>
/// Service to get information about the current device
Expand Down
2 changes: 1 addition & 1 deletion CurrentDevice/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Microsoft.Extensions.DependencyInjection;

namespace BlazorCurrentDevice
namespace CurrentDevice
{
public static class ServiceCollectionExtensions
{
Expand Down
88 changes: 88 additions & 0 deletions NugetReadMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# CurrentDevice

Device/User agent detector made in Blazor for Blazor. This library is a port of [CurrentDevice](https://github.com/matthewhudson/current-device/)

No need to use JavaScript interop to detect the device or user agent. This library is a pure C# implementation of the original library.

This library is trimmable and does not rely on Javascript (no need to add a <script> tag somewhere)

### Nuget CLI
``dotnet add package CurrentDevice``

### Csproj
``<PackageReference Include="CurrentDevice" Version="1.0.2" />``

## Add reference in _Imports.razor

`@using CurrentDevice`

## Usage

### Add the service in your services method

```csharp
var builder = WebAssemblyHostBuilder.CreateDefault(args);
//... Shortend for brevity
builder.Services.AddCurrentDeviceService();
//... Shortend for brevity
await builder.Build().RunAsync();
```

### Inject the service in your component

```csharp
@code{
[Inject] ICurrentDeviceService CurrentDeviceService { get; set; }
}
```
or
```csharp
@inject ICurrentDeviceService CurrentDeviceService
```

### Usage in your component

#### Blazor WASM

```csharp
protected override async Task OnInitializedAsync()
{
UserAgent = await CurrentDeviceService.GetUserAgent();
}
```

#### Blazor Server

```csharp
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
UserAgent = await CurrentDeviceService.GetUserAgent();
StateHasChanged();
}
}
```



#### Device Methods

To see a real world example you can visit the example [here on Github](https://hugovg.github.io/CurrentDevice/)



## Technical information

### Lifetimes

Eventhough in DI it get added as scoped,
blazor WASM will treat it as a singleton [more on that here](https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/dependency-injection?view=aspnetcore-8.0#service-lifetime)
meaning that if an user changes User agents and refreshes the page it'll still display old data untill a page refresh

For blazor server it is scoped and every page request will have up to date information, interal responses get cached clientside per request so that if you check for Ipad then iOs it'll save some requests to the browser



## License
MIT
Loading