adding pages, login working and hub connecting
This commit is contained in:
parent
562c119e18
commit
d28f28aae6
@ -1,5 +1,8 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Selector.MAUI.Data;
|
using Selector.MAUI.Data;
|
||||||
|
using Selector.MAUI.Services;
|
||||||
|
using Selector.SignalR;
|
||||||
|
|
||||||
namespace Selector.MAUI;
|
namespace Selector.MAUI;
|
||||||
|
|
||||||
@ -16,13 +19,22 @@ public static class MauiProgram
|
|||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddMauiBlazorWebView();
|
builder.Services.AddMauiBlazorWebView();
|
||||||
|
builder.Services.AddLogging(o =>
|
||||||
|
{
|
||||||
|
//o.AddConsole();
|
||||||
|
});
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
builder.Services.AddBlazorWebViewDeveloperTools();
|
builder.Services.AddBlazorWebViewDeveloperTools();
|
||||||
builder.Logging.AddDebug();
|
builder.Logging.AddDebug();
|
||||||
#endif
|
#endif
|
||||||
|
builder.Services.AddHttpClient();
|
||||||
|
|
||||||
builder.Services.AddSingleton<WeatherForecastService>();
|
builder.Services.AddTransient<ISelectorNetClient, SelectorNetClient>();
|
||||||
|
builder.Services.AddSingleton<SessionManager>();
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<NowHubClient>();
|
||||||
|
builder.Services.AddSingleton<NowHubCache>();
|
||||||
|
|
||||||
return builder.Build();
|
return builder.Build();
|
||||||
}
|
}
|
||||||
|
9
Selector.MAUI/Models/LoginModel.cs
Normal file
9
Selector.MAUI/Models/LoginModel.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
namespace Selector.MAUI.Models;
|
||||||
|
|
||||||
|
public class LoginModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -1,17 +0,0 @@
|
|||||||
@page "/counter"
|
|
||||||
|
|
||||||
<h1>Counter</h1>
|
|
||||||
|
|
||||||
<p role="status">Current count: @currentCount</p>
|
|
||||||
|
|
||||||
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private int currentCount = 0;
|
|
||||||
|
|
||||||
private void IncrementCount()
|
|
||||||
{
|
|
||||||
currentCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,26 @@
|
|||||||
@page "/"
|
@page "/app"
|
||||||
|
@using Selector.SignalR
|
||||||
|
@inject NowHubClient nowClient
|
||||||
|
@inject NowHubCache nowCache
|
||||||
|
@inject ILogger<Index> logger
|
||||||
|
|
||||||
<h1>Hello, world!</h1>
|
<h1>Hello, world!</h1>
|
||||||
|
|
||||||
Welcome to your new app.
|
Welcome to your new app.
|
||||||
|
|
||||||
<SurveyPrompt Title="How is Blazor working for you?" />
|
@code {
|
||||||
|
|
||||||
|
protected async override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (nowClient.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Disconnected)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Starting now hub connection");
|
||||||
|
|
||||||
|
await nowClient.StartAsync();
|
||||||
|
nowCache.BindClient();
|
||||||
|
await nowClient.OnConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
29
Selector.MAUI/Pages/Launch.razor
Normal file
29
Selector.MAUI/Pages/Launch.razor
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
@page "/"
|
||||||
|
@using Selector.MAUI.Services;
|
||||||
|
@inject ILogger<Login> logger;
|
||||||
|
@inject NavigationManager NavManager;
|
||||||
|
@inject SessionManager sessionManager;
|
||||||
|
|
||||||
|
<h1>Loading...</h1>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
protected async override Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
logger.LogInformation("Starting up");
|
||||||
|
|
||||||
|
//await sessionManager.LoadUserFromDisk();
|
||||||
|
|
||||||
|
if (sessionManager.IsLoggedIn)
|
||||||
|
{
|
||||||
|
logger.LogInformation("User logged in, navigating to main app");
|
||||||
|
NavManager.NavigateTo("/app");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogInformation("User not logged in, navigating to login");
|
||||||
|
NavManager.NavigateTo("/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
43
Selector.MAUI/Pages/Login.razor
Normal file
43
Selector.MAUI/Pages/Login.razor
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
@page "/login"
|
||||||
|
@inject SessionManager session
|
||||||
|
@inject NavigationManager navigation
|
||||||
|
|
||||||
|
<h1>Login</h1>
|
||||||
|
|
||||||
|
<p>@toast</p>
|
||||||
|
|
||||||
|
<EditForm Model="@loginModel" OnSubmit="@HandleSubmit">
|
||||||
|
<InputText id="username" @bind-Value="loginModel.Username" />
|
||||||
|
<InputText type="password" placeholder="Password" @bind-Value="loginModel.Password" />
|
||||||
|
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</EditForm>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private LoginModel loginModel = new();
|
||||||
|
private string toast = string.Empty;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private ILogger<Login> logger { get; set; }
|
||||||
|
|
||||||
|
private async Task HandleSubmit()
|
||||||
|
{
|
||||||
|
switch (await session.Authenticate(loginModel.Username, loginModel.Password))
|
||||||
|
{
|
||||||
|
case SelectorNetClient.TokenResponseStatus.Malformed:
|
||||||
|
toast = "Bad Request, Username or Password missing";
|
||||||
|
break;
|
||||||
|
case SelectorNetClient.TokenResponseStatus.UserSearchFailed:
|
||||||
|
toast = "User not found";
|
||||||
|
break;
|
||||||
|
case SelectorNetClient.TokenResponseStatus.BadCreds:
|
||||||
|
toast = "Login failed, try again";
|
||||||
|
break;
|
||||||
|
case SelectorNetClient.TokenResponseStatus.OK:
|
||||||
|
logger.LogInformation("Login succeeded, redirecting");
|
||||||
|
navigation.NavigateTo("/app");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
16
Selector.MAUI/Pages/Now.razor
Normal file
16
Selector.MAUI/Pages/Now.razor
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
@page "/now"
|
||||||
|
@using Selector.SignalR;
|
||||||
|
|
||||||
|
<h1>Now</h1>
|
||||||
|
|
||||||
|
@if (nowCache?.LastPlaying?.Track is not null)
|
||||||
|
{
|
||||||
|
<p role="status">@nowCache.LastPlaying.Track.Name</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Inject]
|
||||||
|
private NowHubCache nowCache { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.get-task-allow</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -31,6 +31,15 @@
|
|||||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net7.0-ios|AnyCPU'">
|
||||||
|
<CreatePackage>false</CreatePackage>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net7.0-ios|AnyCPU'">
|
||||||
|
<CreatePackage>false</CreatePackage>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst' and '$(Configuration)' == 'Debug'">
|
||||||
|
<CodeSignEntitlements>Platforms/MacCatalyst/Entitlements.Debug.plist</CodeSignEntitlements>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- App Icon -->
|
<!-- App Icon -->
|
||||||
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
|
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
|
||||||
@ -48,6 +57,39 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
|
||||||
|
<PackageReference Include="System.Net.Http.Json" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
|
||||||
|
<!-- <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.2" /> -->
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.Forms" Version="7.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="Services\" />
|
||||||
|
<None Remove="Microsoft.Extensions.Http" />
|
||||||
|
<None Remove="System.Net.Http.Json" />
|
||||||
|
<None Remove="NLog.Extensions.Logging" />
|
||||||
|
<None Remove="NLog" />
|
||||||
|
<None Remove="Microsoft.Extensions.Logging.Console" />
|
||||||
|
<None Remove="Models\" />
|
||||||
|
<None Remove="Microsoft.AspNetCore.SignalR.Client" />
|
||||||
|
<None Remove="Microsoft.AspNetCore.Components.Forms" />
|
||||||
|
<None Remove="Platforms\iOS\Entitlements.plist" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Services\" />
|
||||||
|
<Folder Include="Models\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Selector\Selector.csproj" />
|
||||||
|
<ProjectReference Include="..\Selector.SignalR\Selector.SignalR.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Remove="nlog.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- <None Include="nlog.config">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None> -->
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
110
Selector.MAUI/Services/SelectorNetClient.cs
Normal file
110
Selector.MAUI/Services/SelectorNetClient.cs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using Selector.SignalR;
|
||||||
|
|
||||||
|
namespace Selector.MAUI.Services;
|
||||||
|
|
||||||
|
public interface ISelectorNetClient
|
||||||
|
{
|
||||||
|
Task<SelectorNetClient.TokenResponse> GetToken(string username, string password);
|
||||||
|
Task<SelectorNetClient.TokenResponse> GetToken(string currentKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SelectorNetClient : ISelectorNetClient
|
||||||
|
{
|
||||||
|
private readonly HttpClient _client;
|
||||||
|
private readonly string _baseUrl;
|
||||||
|
private readonly NowHubClient _nowClient;
|
||||||
|
|
||||||
|
public SelectorNetClient(HttpClient client, NowHubClient nowClient)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
_nowClient = nowClient;
|
||||||
|
|
||||||
|
//var baseOverride = Environment.GetEnvironmentVariable("SELECTOR_BASE_URL");
|
||||||
|
|
||||||
|
//if (!string.IsNullOrWhiteSpace(baseOverride))
|
||||||
|
//{
|
||||||
|
// _baseUrl = baseOverride;
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// _baseUrl = "https://selector.sarsoo.xyz";
|
||||||
|
//}
|
||||||
|
|
||||||
|
_baseUrl = "http://localhost:5000";
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TokenResponse> GetToken(string username, string password)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNullOrEmpty(username);
|
||||||
|
ArgumentNullException.ThrowIfNullOrEmpty(password);
|
||||||
|
|
||||||
|
var result = await _client.PostAsync(_baseUrl + "/api/auth/token", new FormUrlEncodedContent(new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "Username", username },
|
||||||
|
{ "Password", password }
|
||||||
|
}));
|
||||||
|
|
||||||
|
return FormTokenResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TokenResponse> GetToken(string currentKey)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNullOrEmpty(currentKey);
|
||||||
|
|
||||||
|
var result = await _client.PostAsync(_baseUrl + "/api/auth/token", new StringContent(string.Empty));
|
||||||
|
|
||||||
|
return FormTokenResponse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TokenResponse FormTokenResponse(HttpResponseMessage result)
|
||||||
|
{
|
||||||
|
var ret = new TokenResponse();
|
||||||
|
|
||||||
|
switch (result.StatusCode)
|
||||||
|
{
|
||||||
|
case HttpStatusCode.BadRequest:
|
||||||
|
ret.Status = TokenResponseStatus.Malformed;
|
||||||
|
break;
|
||||||
|
case HttpStatusCode.NotFound:
|
||||||
|
ret.Status = TokenResponseStatus.UserSearchFailed;
|
||||||
|
break;
|
||||||
|
case HttpStatusCode.Unauthorized:
|
||||||
|
ret.Status = TokenResponseStatus.BadCreds;
|
||||||
|
break;
|
||||||
|
case HttpStatusCode.OK:
|
||||||
|
ret.Status = TokenResponseStatus.OK;
|
||||||
|
ret.Token = result.Content.ReadFromJsonAsync<TokenNetworkResponse>().Result.Token;
|
||||||
|
_nowClient.Token = ret.Token;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TokenResponse
|
||||||
|
{
|
||||||
|
public string Token { get; set; }
|
||||||
|
public TokenResponseStatus Status { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TokenNetworkResponse
|
||||||
|
{
|
||||||
|
public string Token { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TokenResponseStatus
|
||||||
|
{
|
||||||
|
Malformed, UserSearchFailed, BadCreds, OK
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TokenModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
}
|
||||||
|
}
|
84
Selector.MAUI/Services/SessionManager.cs
Normal file
84
Selector.MAUI/Services/SessionManager.cs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Selector.MAUI.Services;
|
||||||
|
|
||||||
|
public class SessionManager
|
||||||
|
{
|
||||||
|
private const string jwt_keychain_key = "last_jwt_key";
|
||||||
|
|
||||||
|
private string lastStoredKey;
|
||||||
|
private DateTime lastRefresh;
|
||||||
|
private readonly ISelectorNetClient _selectorNetClient;
|
||||||
|
private readonly ILogger<SessionManager> _logger;
|
||||||
|
|
||||||
|
public SessionManager(ISelectorNetClient selectorNetClient, ILogger<SessionManager> logger)
|
||||||
|
{
|
||||||
|
_selectorNetClient = selectorNetClient;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsLoggedIn => !string.IsNullOrWhiteSpace(lastStoredKey);
|
||||||
|
|
||||||
|
public async Task LoadUserFromDisk()
|
||||||
|
{
|
||||||
|
var lastToken = await SecureStorage.Default.GetAsync(jwt_keychain_key);
|
||||||
|
|
||||||
|
lastStoredKey = lastToken;
|
||||||
|
|
||||||
|
if(!string.IsNullOrWhiteSpace(lastToken))
|
||||||
|
{
|
||||||
|
await Authenticate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SelectorNetClient.TokenResponseStatus> Authenticate(string username, string password)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Making login token request");
|
||||||
|
|
||||||
|
var tokenResponse = await _selectorNetClient.GetToken(username, password);
|
||||||
|
|
||||||
|
return await HandleTokenResponse(tokenResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SelectorNetClient.TokenResponseStatus> Authenticate()
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Making token request with current key");
|
||||||
|
|
||||||
|
var tokenResponse = await _selectorNetClient.GetToken(lastStoredKey);
|
||||||
|
|
||||||
|
return await HandleTokenResponse(tokenResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<SelectorNetClient.TokenResponseStatus> HandleTokenResponse(SelectorNetClient.TokenResponse tokenResponse)
|
||||||
|
{
|
||||||
|
switch (tokenResponse.Status)
|
||||||
|
{
|
||||||
|
case SelectorNetClient.TokenResponseStatus.OK:
|
||||||
|
_logger.LogInformation("Token response ok");
|
||||||
|
lastStoredKey = tokenResponse.Token;
|
||||||
|
lastRefresh = DateTime.Now;
|
||||||
|
|
||||||
|
//await SecureStorage.Default.SetAsync(jwt_keychain_key, lastStoredKey);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case SelectorNetClient.TokenResponseStatus.Malformed:
|
||||||
|
_logger.LogInformation("Token request failed, missing username or password");
|
||||||
|
|
||||||
|
break;
|
||||||
|
case SelectorNetClient.TokenResponseStatus.UserSearchFailed:
|
||||||
|
_logger.LogInformation("Token request failed, no user by that name");
|
||||||
|
|
||||||
|
break;
|
||||||
|
case SelectorNetClient.TokenResponseStatus.BadCreds:
|
||||||
|
_logger.LogInformation("Token request failed, bad password");
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenResponse.Status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,15 @@
|
|||||||
@inherits LayoutComponentBase
|
@using Selector.MAUI.Services;
|
||||||
|
|
||||||
|
@inherits LayoutComponentBase
|
||||||
|
@inject SessionManager session
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<div class="sidebar">
|
@if (session.IsLoggedIn)
|
||||||
<NavMenu />
|
{
|
||||||
</div>
|
<div class="sidebar">
|
||||||
|
<NavMenu />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<main>
|
<main>
|
||||||
<div class="top-row px-4">
|
<div class="top-row px-4">
|
||||||
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
|
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
|
||||||
|
@ -15,13 +15,8 @@
|
|||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-item px-3">
|
<div class="nav-item px-3">
|
||||||
<NavLink class="nav-link" href="counter">
|
<NavLink class="nav-link" href="now">
|
||||||
<span class="oi oi-plus" aria-hidden="true"></span> Counter
|
<span class="oi oi-plus" aria-hidden="true"></span> Now
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
<div class="nav-item px-3">
|
|
||||||
<NavLink class="nav-link" href="fetchdata">
|
|
||||||
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
|
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@using System.Net.Http
|
@using System.Net.Http
|
||||||
|
@using Microsoft.Extensions.Logging
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
@using Microsoft.AspNetCore.Components.Routing
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
@using Microsoft.AspNetCore.Components.Web
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
@ -6,4 +7,5 @@
|
|||||||
@using Microsoft.JSInterop
|
@using Microsoft.JSInterop
|
||||||
@using Selector.MAUI
|
@using Selector.MAUI
|
||||||
@using Selector.MAUI.Shared
|
@using Selector.MAUI.Shared
|
||||||
|
@using Selector.MAUI.Models
|
||||||
|
@using Selector.MAUI.Services;
|
||||||
|
@ -7,26 +7,37 @@ public abstract class BaseSignalRClient: IAsyncDisposable
|
|||||||
{
|
{
|
||||||
private readonly string _baseUrl;
|
private readonly string _baseUrl;
|
||||||
protected HubConnection hubConnection;
|
protected HubConnection hubConnection;
|
||||||
|
public string Token { get; set; }
|
||||||
|
|
||||||
public BaseSignalRClient(string path)
|
public BaseSignalRClient(string path, string token)
|
||||||
{
|
{
|
||||||
var baseOverride = Environment.GetEnvironmentVariable("SELECTOR_BASE_URL");
|
//var baseOverride = Environment.GetEnvironmentVariable("SELECTOR_BASE_URL");
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(baseOverride))
|
//if (!string.IsNullOrWhiteSpace(baseOverride))
|
||||||
{
|
//{
|
||||||
_baseUrl = baseOverride;
|
// _baseUrl = baseOverride;
|
||||||
}
|
//}
|
||||||
else
|
//else
|
||||||
{
|
//{
|
||||||
_baseUrl = "https://selector.sarsoo.xyz";
|
// _baseUrl = "https://selector.sarsoo.xyz";
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
_baseUrl = "http://localhost:5000";
|
||||||
|
|
||||||
hubConnection = new HubConnectionBuilder()
|
hubConnection = new HubConnectionBuilder()
|
||||||
.WithUrl(_baseUrl + "/" + path)
|
.WithUrl(_baseUrl + "/" + path, options =>
|
||||||
|
{
|
||||||
|
options.AccessTokenProvider = () =>
|
||||||
|
{
|
||||||
|
return Task.FromResult(Token);
|
||||||
|
};
|
||||||
|
})
|
||||||
.WithAutomaticReconnect()
|
.WithAutomaticReconnect()
|
||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HubConnectionState State => hubConnection.State;
|
||||||
|
|
||||||
public ValueTask DisposeAsync()
|
public ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
return ((IAsyncDisposable)hubConnection).DisposeAsync();
|
return ((IAsyncDisposable)hubConnection).DisposeAsync();
|
||||||
|
93
Selector.SignalR/NowHubCache.cs
Normal file
93
Selector.SignalR/NowHubCache.cs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using SpotifyAPI.Web;
|
||||||
|
|
||||||
|
namespace Selector.SignalR;
|
||||||
|
|
||||||
|
public class NowHubCache
|
||||||
|
{
|
||||||
|
private readonly NowHubClient _connection;
|
||||||
|
private readonly ILogger<NowHubCache> logger;
|
||||||
|
|
||||||
|
public TrackAudioFeatures LastFeature { get; private set; }
|
||||||
|
public List<ICard> LastCards { get; private set; } = new();
|
||||||
|
private readonly object updateLock = new();
|
||||||
|
|
||||||
|
public PlayCount LastPlayCount { get; private set; }
|
||||||
|
public CurrentlyPlayingDTO LastPlaying { get; private set; }
|
||||||
|
|
||||||
|
public NowHubCache(NowHubClient connection, ILogger<NowHubCache> logger)
|
||||||
|
{
|
||||||
|
_connection = connection;
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BindClient()
|
||||||
|
{
|
||||||
|
_connection.OnNewAudioFeature(af =>
|
||||||
|
{
|
||||||
|
lock (updateLock)
|
||||||
|
{
|
||||||
|
logger.LogInformation("New audio features received: {0}", af);
|
||||||
|
LastFeature = af;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_connection.OnNewCard(c =>
|
||||||
|
{
|
||||||
|
lock(updateLock)
|
||||||
|
{
|
||||||
|
logger.LogInformation("New card received: {0}", c);
|
||||||
|
LastCards.Add(c);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_connection.OnNewPlayCount(pc =>
|
||||||
|
{
|
||||||
|
lock (updateLock)
|
||||||
|
{
|
||||||
|
logger.LogInformation("New play count received: {0}", pc);
|
||||||
|
LastPlayCount = pc;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_connection.OnNewPlaying(async np =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lock (updateLock)
|
||||||
|
{
|
||||||
|
logger.LogInformation("New now playing recieved: {0}", np);
|
||||||
|
LastPlaying = np;
|
||||||
|
LastCards.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LastPlaying?.Track is not null)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(LastPlaying.Track.Id))
|
||||||
|
{
|
||||||
|
await _connection.SendAudioFeatures(LastPlaying.Track.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _connection.SendPlayCount(
|
||||||
|
LastPlaying.Track.Name,
|
||||||
|
LastPlaying.Track.Artists.FirstOrDefault()?.Name,
|
||||||
|
LastPlaying.Track.Album?.Name,
|
||||||
|
LastPlaying.Track.Album?.Artists.FirstOrDefault()?.Name
|
||||||
|
);
|
||||||
|
|
||||||
|
await _connection.SendFacts(
|
||||||
|
LastPlaying.Track.Name,
|
||||||
|
LastPlaying.Track.Artists.FirstOrDefault()?.Name,
|
||||||
|
LastPlaying.Track.Album?.Name,
|
||||||
|
LastPlaying.Track.Album?.Artists.FirstOrDefault()?.Name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}catch(Exception e)
|
||||||
|
{
|
||||||
|
logger.LogError(e, "Error while handling new now playing");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@ public class NowHubClient: BaseSignalRClient, INowPlayingHub, IDisposable
|
|||||||
private List<IDisposable> NewCardCallbacks = new();
|
private List<IDisposable> NewCardCallbacks = new();
|
||||||
private bool disposedValue;
|
private bool disposedValue;
|
||||||
|
|
||||||
public NowHubClient(): base("nowhub")
|
public NowHubClient(string token = null): base("nowhub", token)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +37,26 @@ public class NowHubClient: BaseSignalRClient, INowPlayingHub, IDisposable
|
|||||||
NewCardCallbacks.Add(hubConnection.On(nameof(OnNewCard), action));
|
NewCardCallbacks.Add(hubConnection.On(nameof(OnNewCard), action));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnNewPlaying(Func<CurrentlyPlayingDTO, Task> action)
|
||||||
|
{
|
||||||
|
NewPlayingCallbacks.Add(hubConnection.On(nameof(OnNewPlaying), action));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnNewAudioFeature(Func<TrackAudioFeatures, Task> action)
|
||||||
|
{
|
||||||
|
NewAudioFeatureCallbacks.Add(hubConnection.On(nameof(OnNewAudioFeature), action));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnNewPlayCount(Func<PlayCount, Task> action)
|
||||||
|
{
|
||||||
|
NewPlayCountCallbacks.Add(hubConnection.On(nameof(OnNewPlayCount), action));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnNewCard(Func<ICard, Task> action)
|
||||||
|
{
|
||||||
|
NewCardCallbacks.Add(hubConnection.On(nameof(OnNewCard), action));
|
||||||
|
}
|
||||||
|
|
||||||
public Task OnConnected()
|
public Task OnConnected()
|
||||||
{
|
{
|
||||||
return hubConnection.InvokeAsync(nameof(OnConnected));
|
return hubConnection.InvokeAsync(nameof(OnConnected));
|
||||||
|
Loading…
Reference in New Issue
Block a user