diff --git a/Selector.MAUI/Constants.cs b/Selector.MAUI/Constants.cs new file mode 100644 index 0000000..1ec2a86 --- /dev/null +++ b/Selector.MAUI/Constants.cs @@ -0,0 +1,10 @@ +using System; +namespace Selector.MAUI +{ + public static class Constants + { + public const string JwtPrefKey = "last_jwt_key"; + public const string StartPagePrefKey = "start_page"; + } +} + diff --git a/Selector.MAUI/Extensions/ServiceExtensions.cs b/Selector.MAUI/Extensions/ServiceExtensions.cs new file mode 100644 index 0000000..b4d6350 --- /dev/null +++ b/Selector.MAUI/Extensions/ServiceExtensions.cs @@ -0,0 +1,21 @@ +using System; +using Selector.MAUI.Services; +using Selector.SignalR; + +namespace Selector.MAUI.Extensions; + +public static class ServiceExtensions +{ + public static IServiceCollection AddHubs(this IServiceCollection services) + { + services.AddSingleton() + .AddSingleton(); + + services.AddSingleton(); + + services.AddSingleton(); + + return services; + } +} + diff --git a/Selector.MAUI/MauiProgram.cs b/Selector.MAUI/MauiProgram.cs index 3f9deee..16228ca 100644 --- a/Selector.MAUI/MauiProgram.cs +++ b/Selector.MAUI/MauiProgram.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Selector.MAUI.Data; using Selector.MAUI.Services; using Selector.SignalR; +using Selector.MAUI.Extensions; namespace Selector.MAUI; @@ -31,12 +32,10 @@ public static class MauiProgram builder.Services.AddHttpClient() .AddTransient(); - builder.Services.AddSingleton(); + builder.Services.AddSingleton() + .AddTransient(); - builder.Services.AddSingleton() - .AddSingleton(); - - builder.Services.AddSingleton(); + builder.Services.AddHubs(); return builder.Build(); } diff --git a/Selector.MAUI/Pages/Index.razor b/Selector.MAUI/Pages/Index.razor index a375c4c..633d5d5 100644 --- a/Selector.MAUI/Pages/Index.razor +++ b/Selector.MAUI/Pages/Index.razor @@ -1,8 +1,6 @@ @page "/app" @using Selector.SignalR -@inject NowHubClient nowClient -@inject NowHubCache nowCache -@inject PastHubClient pastClient +@inject HubManager hubManager @inject ILogger logger

run that

@@ -11,16 +9,6 @@ protected async override Task OnInitializedAsync() { - - if (nowClient.State == Microsoft.AspNetCore.SignalR.Client.HubConnectionState.Disconnected) - { - logger.LogInformation("Starting now hub connection"); - - await nowClient.StartAsync(); - await pastClient.StartAsync(); - nowCache.BindClient(); - await nowClient.OnConnected(); - } - + await hubManager.EnsureConnected(); } } \ No newline at end of file diff --git a/Selector.MAUI/Pages/Launch.razor b/Selector.MAUI/Pages/Launch.razor index 36c4eb0..a56fd1b 100644 --- a/Selector.MAUI/Pages/Launch.razor +++ b/Selector.MAUI/Pages/Launch.razor @@ -3,6 +3,8 @@ @inject ILogger logger; @inject NavigationManager NavManager; @inject SessionManager sessionManager; +@inject HubManager hubManager; +@inject StartPageManager startManager;

Loading...

@@ -15,8 +17,11 @@ if (sessionManager.IsLoggedIn) { + await hubManager.EnsureConnected(); + logger.LogInformation("User logged in, navigating to main app"); - NavManager.NavigateTo("/app"); + + startManager.NavigateToStartPage(); } else { diff --git a/Selector.MAUI/Pages/Past.razor b/Selector.MAUI/Pages/Past.razor index 3ecaf25..bc5b920 100644 --- a/Selector.MAUI/Pages/Past.razor +++ b/Selector.MAUI/Pages/Past.razor @@ -1,7 +1,5 @@ @page "/past" @using Selector.SignalR; -@using System.Linq; -@implements IDisposable

Past

diff --git a/Selector.MAUI/Pages/Settings.razor b/Selector.MAUI/Pages/Settings.razor index 4443e07..81a0fd5 100644 --- a/Selector.MAUI/Pages/Settings.razor +++ b/Selector.MAUI/Pages/Settings.razor @@ -1,8 +1,23 @@ @page "/settings" -

Settings

+
+

Settings

- +
+ + Start Page + + +
+
+ SignOut()) Text="Sign Out" ButtonStyle="ButtonStyle.Danger" /> +
+ + +
@code { @@ -10,10 +25,19 @@ private SessionManager sessionManager { get; set; } [Inject] private NavigationManager navigationManager { get; set; } + [Inject] + private StartPageManager startManager { get; set; } + + private string currentStartPage { get; set; } protected async override Task OnInitializedAsync() { + currentStartPage = startManager.GetStartPage(); + } + private void OnStartPageChange(object value) + { + startManager.SetStartPage((string) value); } private void SignOut() diff --git a/Selector.MAUI/Pages/Settings.razor.css b/Selector.MAUI/Pages/Settings.razor.css new file mode 100644 index 0000000..adf2c5c --- /dev/null +++ b/Selector.MAUI/Pages/Settings.razor.css @@ -0,0 +1,20 @@ +body { +} + +.form-container { + max-width: 300px; + margin: auto; +} + +.row { + padding-top: 5px; + padding-bottom: 5px; +} + +::deep h3 { + text-align: left; +} + +h1 { + padding-bottom: 20px; +} \ No newline at end of file diff --git a/Selector.MAUI/Selector.MAUI.csproj b/Selector.MAUI/Selector.MAUI.csproj index b49b7b8..b493a0a 100644 --- a/Selector.MAUI/Selector.MAUI.csproj +++ b/Selector.MAUI/Selector.MAUI.csproj @@ -98,10 +98,12 @@ + + diff --git a/Selector.MAUI/Services/HubManager.cs b/Selector.MAUI/Services/HubManager.cs new file mode 100644 index 0000000..44838aa --- /dev/null +++ b/Selector.MAUI/Services/HubManager.cs @@ -0,0 +1,43 @@ +using System; +using Microsoft.AspNetCore.SignalR.Client; +using Microsoft.Extensions.Logging; +using Selector.SignalR; +namespace Selector.MAUI.Services; + +public class HubManager +{ + private readonly NowHubClient nowClient; + private readonly NowHubCache nowCache; + private readonly PastHubClient pastClient; + private readonly ILogger logger; + + public HubManager(NowHubClient nowClient, NowHubCache nowCache, PastHubClient pastClient, ILogger logger) + { + this.nowClient = nowClient; + this.nowCache = nowCache; + this.pastClient = pastClient; + this.logger = logger; + } + + + public async Task EnsureConnected() + { + if (nowClient.State == HubConnectionState.Disconnected) + { + logger.LogInformation("Starting now hub connection"); + + await nowClient.StartAsync(); + nowCache.BindClient(); + await nowClient.OnConnected(); + } + + if (pastClient.State == HubConnectionState.Disconnected) + { + logger.LogInformation("Starting past hub connection"); + + await pastClient.StartAsync(); + await pastClient.OnConnected(); + } + } +} + diff --git a/Selector.MAUI/Services/SessionManager.cs b/Selector.MAUI/Services/SessionManager.cs index c1ba20f..0026b1e 100644 --- a/Selector.MAUI/Services/SessionManager.cs +++ b/Selector.MAUI/Services/SessionManager.cs @@ -5,9 +5,7 @@ namespace Selector.MAUI.Services; public class SessionManager { - private const string jwt_keychain_key = "last_jwt_key"; - - private string lastStoredKey; + private string lastStoredKey; private DateTime lastRefresh; private readonly ISelectorNetClient _selectorNetClient; private readonly ILogger _logger; @@ -23,7 +21,7 @@ public class SessionManager public async Task LoadUserFromDisk() { //var lastToken = await SecureStorage.Default.GetAsync(jwt_keychain_key); - var lastToken = Preferences.Default.Get(jwt_keychain_key, string.Empty); + var lastToken = Preferences.Default.Get(Constants.JwtPrefKey, string.Empty); lastStoredKey = lastToken; @@ -62,7 +60,7 @@ public class SessionManager //await SecureStorage.Default.SetAsync(jwt_keychain_key, lastStoredKey); // I know, but I can't get secure storage to work - Preferences.Default.Set(jwt_keychain_key, lastStoredKey); + Preferences.Default.Set(Constants.JwtPrefKey, lastStoredKey); break; case SelectorNetClient.TokenResponseStatus.Malformed: diff --git a/Selector.MAUI/Services/StartPageManager.cs b/Selector.MAUI/Services/StartPageManager.cs new file mode 100644 index 0000000..ac12b87 --- /dev/null +++ b/Selector.MAUI/Services/StartPageManager.cs @@ -0,0 +1,62 @@ +using System; +using Microsoft.AspNetCore.Components; + +namespace Selector.MAUI.Services; + +public class StartPageManager +{ + private readonly NavigationManager navManager; + + public string[] StartPages { get; } = new[] + { + Home, Now, Past + }; + + public const string Home = "Home"; + public const string Now = "Now"; + public const string Past = "Past"; + + public StartPageManager(NavigationManager navManager) + { + this.navManager = navManager; + } + + public string GetStartPage() + { + var savedStartPage = Preferences.Default.Get(Constants.StartPagePrefKey, string.Empty); + + if (!string.IsNullOrWhiteSpace(savedStartPage)) + { + return savedStartPage; + } + else + { + return Home; + } + } + + public void NavigateToStartPage() + { + var startPage = GetStartPage(); + + switch (startPage) + { + case Now: + navManager.NavigateTo("/now"); + break; + case Past: + navManager.NavigateTo("/past"); + break; + case Home: + default: + navManager.NavigateTo("/app"); + break; + } + } + + public void SetStartPage(string value) + { + Preferences.Default.Set(Constants.StartPagePrefKey, value); + } +} + diff --git a/Selector.MAUI/Shared/SignatureImage.razor b/Selector.MAUI/Shared/SignatureImage.razor new file mode 100644 index 0000000..fe4abc3 --- /dev/null +++ b/Selector.MAUI/Shared/SignatureImage.razor @@ -0,0 +1,16 @@ + + +@code { + +} + diff --git a/Selector.MAUI/wwwroot/index.html b/Selector.MAUI/wwwroot/index.html index 98322e6..a429310 100644 --- a/Selector.MAUI/wwwroot/index.html +++ b/Selector.MAUI/wwwroot/index.html @@ -11,7 +11,7 @@ - + diff --git a/Selector.SignalR/NowHubCache.cs b/Selector.SignalR/NowHubCache.cs index 56a0335..a1a8901 100644 --- a/Selector.SignalR/NowHubCache.cs +++ b/Selector.SignalR/NowHubCache.cs @@ -13,7 +13,10 @@ public class NowHubCache public List LastCards { get; private set; } = new(); private readonly object updateLock = new(); - public PlayCount LastPlayCount { get; private set; } + private readonly object bindingLock = new(); + private bool isBound = false; + + public PlayCount LastPlayCount { get; private set; } public CurrentlyPlayingDTO LastPlaying { get; private set; } public event EventHandler NewAudioFeature; @@ -29,74 +32,83 @@ public class NowHubCache public void BindClient() { - _connection.OnNewAudioFeature(af => + lock(bindingLock) { - lock (updateLock) + if(!isBound) { - logger.LogInformation("New audio features received: {0}", af); - LastFeature = af; - NewAudioFeature?.Invoke(this, null); - } - }); + _connection.OnNewAudioFeature(af => + { + lock (updateLock) + { + logger.LogInformation("New audio features received: {0}", af); + LastFeature = af; + NewAudioFeature?.Invoke(this, null); + } + }); - _connection.OnNewCard(c => - { - lock(updateLock) - { - logger.LogInformation("New card received: {0}", c); - LastCards.Add(c); - NewCard?.Invoke(this, null); + _connection.OnNewCard(c => + { + lock (updateLock) + { + logger.LogInformation("New card received: {0}", c); + LastCards.Add(c); + NewCard?.Invoke(this, null); + } + }); + + _connection.OnNewPlayCount(pc => + { + lock (updateLock) + { + logger.LogInformation("New play count received: {0}", pc); + LastPlayCount = pc; + NewPlayCount?.Invoke(this, null); + } + }); + + _connection.OnNewPlaying(async np => + { + try + { + lock (updateLock) + { + logger.LogInformation("New now playing recieved: {0}", np); + LastPlaying = np; + LastCards.Clear(); + NewNowPlaying?.Invoke(this, null); + } + + 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"); + } + }); + + isBound = true; } - }); - - _connection.OnNewPlayCount(pc => - { - lock (updateLock) - { - logger.LogInformation("New play count received: {0}", pc); - LastPlayCount = pc; - NewPlayCount?.Invoke(this, null); - } - }); - - _connection.OnNewPlaying(async np => - { - try - { - lock (updateLock) - { - logger.LogInformation("New now playing recieved: {0}", np); - LastPlaying = np; - LastCards.Clear(); - NewNowPlaying?.Invoke(this, null); - } - - 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"); - } - }); + } } }