@rendermode @(new InteractiveServerRenderMode(prerender: false)) @implements IAsyncDisposable @inject ApiClientService ApiClient @inject AuthenticationStateProvider AuthStateProvider @inject NavigationManager Navigation @inject IJSRuntime JS
@if (IsOpen) {

@AppLocalization.Text("notification.notifications", "Notifications")

@UnreadCount @AppLocalization.Text("notification.unread", "unread")

@if (Notifications.Any(item => item.IsRead)) { }
@if (IsLoading) {
@AppLocalization.Text("notification.loading", "Loading notifications...")
} else if (Notifications.Count == 0) {

@AppLocalization.Text("notification.noneYet", "No notifications yet")

@AppLocalization.Text("notification.emptyState", "Your task alerts will show up here.")

} else { @foreach (var item in Notifications) { } }
}
@code { private bool IsOpen; private bool IsLoading; private bool showCleanupConfirmDialog; private int UnreadCount; private List Notifications = new(); protected override async Task OnInitializedAsync() { await RefreshAsync(); } protected override async Task OnAfterRenderAsync(bool firstRender) { await JS.InvokeVoidAsync("initIcons"); } private async Task ToggleOpenAsync() { IsOpen = !IsOpen; if (IsOpen) { await RefreshAsync(); } } private void Close() { IsOpen = false; } private async Task RefreshAsync() { IsLoading = true; try { var authState = await AuthStateProvider.GetAuthenticationStateAsync(); if (authState.User.Identity?.IsAuthenticated != true) { Notifications.Clear(); UnreadCount = 0; return; } var client = ApiClient.CreateClient(authState.User); var unreadTask = client.GetFromJsonAsync("Notification/unread-count"); var itemsTask = client.GetFromJsonAsync>("Notification/mine?take=10"); await Task.WhenAll(unreadTask, itemsTask); UnreadCount = unreadTask.Result; Notifications = (itemsTask.Result ?? new List()) .Select(item => { item.TargetUrl = BuildTargetUrl(item); return item; }) .ToList(); } catch (Exception ex) { Console.WriteLine($"[DEBUG NotificationBell] Failed to refresh: {ex.Message}"); } finally { IsLoading = false; await InvokeAsync(StateHasChanged); } } private void RequestCleanupConfirmation() { showCleanupConfirmDialog = true; } private void CloseCleanupConfirmDialog() { showCleanupConfirmDialog = false; } private async Task ConfirmCleanupAsync() { showCleanupConfirmDialog = false; await ClearReadNotificationsAsync(); } private async Task ClearReadNotificationsAsync() { try { var authState = await AuthStateProvider.GetAuthenticationStateAsync(); if (authState.User.Identity?.IsAuthenticated != true) { return; } var client = ApiClient.CreateClient(authState.User); var response = await client.DeleteAsync("Notification/read"); if (response.IsSuccessStatusCode) { await RefreshAsync(); } } catch (Exception ex) { Console.WriteLine($"[DEBUG NotificationBell] Clear read notifications failed: {ex.Message}"); } } private async Task MarkReadAsync(NotificationDto item) { if (item.IsRead) { Navigation.NavigateTo(GetTargetUrl(item)); return; } try { var authState = await AuthStateProvider.GetAuthenticationStateAsync(); if (authState.User.Identity?.IsAuthenticated != true) { return; } var client = ApiClient.CreateClient(authState.User); var response = await client.PostAsync($"Notification/{item.Id}/read", null); if (response.IsSuccessStatusCode) { item.IsRead = true; if (UnreadCount > 0) { UnreadCount--; } await InvokeAsync(StateHasChanged); Navigation.NavigateTo(GetTargetUrl(item)); } } catch (Exception ex) { Console.WriteLine($"[DEBUG NotificationBell] MarkRead failed: {ex.Message}"); } } private async Task OpenNotificationAsync(NotificationDto item) { await MarkReadAsync(item); IsOpen = false; } private static string GetTargetUrl(NotificationDto item) { if (!string.IsNullOrWhiteSpace(item.TargetUrl)) { return item.TargetUrl; } return BuildTargetUrl(item); } private static string BuildTargetUrl(NotificationDto item) { return NotificationNavigation.BuildTargetUrl(item.SourceType, item.SourceId, item.NotificationType); } private static string FormatSender(string? senderName) { return string.IsNullOrWhiteSpace(senderName) ? "System" : senderName; } private static string FormatTime(DateTime? createdAt) { if (!createdAt.HasValue) { return string.Empty; } var value = createdAt.Value.ToLocalTime(); return value.ToString("dd MMM, HH:mm"); } public ValueTask DisposeAsync() => ValueTask.CompletedTask; }