diff --git a/CulinaryCommandApp/Components/App.razor b/CulinaryCommandApp/Components/App.razor index 88e8715..ac942af 100644 --- a/CulinaryCommandApp/Components/App.razor +++ b/CulinaryCommandApp/Components/App.razor @@ -13,7 +13,8 @@ - + + @* *@ diff --git a/CulinaryCommandApp/Components/Custom/LocationSelector.razor b/CulinaryCommandApp/Components/Custom/LocationSelector.razor index a3696b2..be2c064 100644 --- a/CulinaryCommandApp/Components/Custom/LocationSelector.razor +++ b/CulinaryCommandApp/Components/Custom/LocationSelector.razor @@ -2,6 +2,7 @@ @using CulinaryCommand.Data.Entities @using System.Text.Json @using CulinaryCommand.Services.UserContextSpace +@using System.Numerics @inject ILocationService LocationService @inject LocationState LocationState @@ -59,22 +60,31 @@ @code { private List ManagedLocations { get; set; } = new(); private bool _loadedOnce; + + protected override void OnInitialized() + { + LocationState.OnChange += StateHasChanged; + } + protected override async Task OnAfterRenderAsync(bool firstRender) { if (!firstRender || _loadedOnce) return; _loadedOnce = true; - // 1) Load cached instantly (never fails hard) + // 1) Load cached instantly ManagedLocations = await LoadCachedLocationsAsync(); - if (LocationState.CurrentLocation == null && ManagedLocations.Any()) - await LocationState.SetCurrentLocationAsync(ManagedLocations.First()); + var savedId = await JS.InvokeAsync("localStorage.getItem", LocationState.ActiveLocStorageKey); + var savedLocation = int.TryParse(savedId, out var id) + ? ManagedLocations.FirstOrDefault(l => l.Id == id) + : null; + + await LocationState.SetCurrentLocationAsync(savedLocation); StateHasChanged(); - // 2) Load fresh server data if we can resolve user context + // 2) Load fresh server data after await LoadLocationsFromServerAsync(); - StateHasChanged(); } @@ -83,6 +93,7 @@ try { var json = await JS.InvokeAsync("localStorage.getItem", "cc_locations"); + return string.IsNullOrWhiteSpace(json) ? new List() : (JsonSerializer.Deserialize>(json) ?? new List()); @@ -139,4 +150,9 @@ { NavigationManager.NavigateTo($"/settings/locations/{location.Id}"); } + + public void Dispose() + { + LocationState.OnChange -= StateHasChanged; + } } diff --git a/CulinaryCommandApp/Components/Custom/LogoutWidget.razor b/CulinaryCommandApp/Components/Custom/LogoutWidget.razor index 6b9a55c..72ad2e5 100644 --- a/CulinaryCommandApp/Components/Custom/LogoutWidget.razor +++ b/CulinaryCommandApp/Components/Custom/LogoutWidget.razor @@ -1,11 +1,19 @@ @rendermode InteractiveServer @inject NavigationManager Nav +@inject IJSRuntime JS +@inject LocationState LocationState + + @code { private async Task Logout() { + await JS.InvokeVoidAsync("localStorage.clear"); + await JS.InvokeVoidAsync("sessionStorage.clear"); + LocationState.FlushLocationState(); + Nav.NavigateTo("/signin", true); // hard reload } } diff --git a/CulinaryCommandApp/Components/Custom/ProfileDropdown.razor b/CulinaryCommandApp/Components/Custom/ProfileDropdown.razor index e4aba68..e7de7df 100644 --- a/CulinaryCommandApp/Components/Custom/ProfileDropdown.razor +++ b/CulinaryCommandApp/Components/Custom/ProfileDropdown.razor @@ -1,6 +1,9 @@ @using CulinaryCommand.Services @using CulinaryCommand.Services.UserContextSpace @inject IUserContextService UserCtx +@inject IJSRuntime JS +@inject LocationState LocationState + @inject NavigationManager Nav @rendermode InteractiveServer @@ -64,8 +67,14 @@ StateHasChanged(); } + private async Task Logout() + { + await JS.InvokeVoidAsync("localStorage.clear"); + await JS.InvokeVoidAsync("sessionStorage.clear"); + LocationState.FlushLocationState(); - private void Logout() => Nav.NavigateTo("/logout", forceLoad: true); + Nav.NavigateTo("/logout", forceLoad: true); + } diff --git a/CulinaryCommandApp/Services/LocationService.cs b/CulinaryCommandApp/Services/LocationService.cs index d81e2b9..db261b8 100644 --- a/CulinaryCommandApp/Services/LocationService.cs +++ b/CulinaryCommandApp/Services/LocationService.cs @@ -255,7 +255,7 @@ public async Task LoadAndPersistLocationsAsync(int userId) if (_locationState.CurrentLocation != null) { await _js.InvokeVoidAsync("localStorage.setItem", - "cc_activeLocationId", + LocationState.ActiveLocStorageKey, _locationState.CurrentLocation.Id); } } diff --git a/CulinaryCommandApp/Services/LocationState.cs b/CulinaryCommandApp/Services/LocationState.cs index 06cfaaf..70fd656 100644 --- a/CulinaryCommandApp/Services/LocationState.cs +++ b/CulinaryCommandApp/Services/LocationState.cs @@ -6,6 +6,7 @@ namespace CulinaryCommand.Services { public class LocationState { + public const string ActiveLocStorageKey = "cc_activeLocationId"; private readonly IJSRuntime _js; public List ManagedLocations { get; private set; } = new(); public Location? CurrentLocation { get; private set; } @@ -23,7 +24,7 @@ public async Task HydrateAsync() { if (CurrentLocation != null) return; // Already have it - var savedId = await _js.InvokeAsync("localStorage.getItem", "cc_selected_location_id"); + var savedId = await _js.InvokeAsync("localStorage.getItem", ActiveLocStorageKey); if (!string.IsNullOrEmpty(savedId) && int.TryParse(savedId, out int id)) { @@ -40,7 +41,7 @@ public async Task SetLocationsAsync(List? locations) string? savedId = null; try { - savedId = await _js.InvokeAsync("localStorage.getItem", "cc_activeLocationId"); + savedId = await _js.InvokeAsync("localStorage.getItem", ActiveLocStorageKey); } catch (InvalidOperationException) { @@ -65,9 +66,9 @@ public async Task SetCurrentLocationAsync(Location? loc) try { if (loc is null) - await _js.InvokeVoidAsync("localStorage.removeItem", "cc_activeLocationId"); + await _js.InvokeVoidAsync("localStorage.removeItem", ActiveLocStorageKey); else - await _js.InvokeVoidAsync("localStorage.setItem", "cc_activeLocationId", loc.Id.ToString()); + await _js.InvokeVoidAsync("localStorage.setItem", ActiveLocStorageKey, loc.Id.ToString()); } catch (InvalidOperationException) { @@ -77,5 +78,11 @@ public async Task SetCurrentLocationAsync(Location? loc) // OnChange?.Invoke(); NotifyStateChanged(); } + + public void FlushLocationState() + { + ManagedLocations = new List(); + CurrentLocation = null; + } } } diff --git a/CulinaryCommandApp/wwwroot/images/cc_favicon_black2.png b/CulinaryCommandApp/wwwroot/images/cc_favicon_black2.png new file mode 100644 index 0000000..9ac1dcb Binary files /dev/null and b/CulinaryCommandApp/wwwroot/images/cc_favicon_black2.png differ diff --git a/CulinaryCommandApp/wwwroot/images/cc_favicon_green_clear2.png b/CulinaryCommandApp/wwwroot/images/cc_favicon_green_clear2.png new file mode 100644 index 0000000..6dfaddc Binary files /dev/null and b/CulinaryCommandApp/wwwroot/images/cc_favicon_green_clear2.png differ diff --git a/CulinaryCommandApp/wwwroot/images/favicon.png b/CulinaryCommandApp/wwwroot/images/favicon-old.png similarity index 100% rename from CulinaryCommandApp/wwwroot/images/favicon.png rename to CulinaryCommandApp/wwwroot/images/favicon-old.png diff --git a/CulinaryCommandApp/wwwroot/images/favicon.ico b/CulinaryCommandApp/wwwroot/images/favicon.ico new file mode 100644 index 0000000..4305ade Binary files /dev/null and b/CulinaryCommandApp/wwwroot/images/favicon.ico differ