diff --git a/Wave/Components/AlertComponent.razor b/Wave/Components/AlertComponent.razor
new file mode 100644
index 0000000..fcaf06c
--- /dev/null
+++ b/Wave/Components/AlertComponent.razor
@@ -0,0 +1,45 @@
+@using Wave.Utilities
+@implements IDisposable
+@inject IMessageDisplay Messages
+
+@if (Message is {} message) {
+
+
+
+ @if (message.Title is null) {
+ @message.Body
+ } else {
+ @message.Title
+ @message.Body
+ }
+
+ @if (CanDelete) {
+
+
+
+
+
+ }
+
+}
+
+@code {
+ [Parameter]
+ public bool CanDelete { get; set; }
+
+ private IMessageDisplay.Message? Message { get; set; }
+
+ protected override void OnInitialized() {
+ Messages.OnMessage += OnMessage;
+ }
+
+ private bool OnMessage(IMessageDisplay.Message message) {
+ Message = message;
+ StateHasChanged();
+ return true;
+ }
+
+ public void Dispose() {
+ Messages.OnMessage -= OnMessage;
+ }
+}
diff --git a/Wave/Components/Layout/MainLayout.razor b/Wave/Components/Layout/MainLayout.razor
index b3260d8..9eabb2b 100644
--- a/Wave/Components/Layout/MainLayout.razor
+++ b/Wave/Components/Layout/MainLayout.razor
@@ -28,7 +28,14 @@
+
@Body
+ @if (HttpContext is null || HttpContext?.GetEndpoint()?
+ .Metadata.GetMetadata
()?
+ .Mode is not null) {
+ // for some reason that's how you test for interactive render modes
+
+ }
@@ -87,4 +94,6 @@
@code {
[CascadingParameter(Name = "UserTheme")]
private string? UserTheme { get; set; }
+ [CascadingParameter]
+ private HttpContext? HttpContext { get; set; }
}
diff --git a/Wave/Components/Pages/EmailSignup.razor b/Wave/Components/Pages/EmailSignup.razor
index 713a542..71947b7 100644
--- a/Wave/Components/Pages/EmailSignup.razor
+++ b/Wave/Components/Pages/EmailSignup.razor
@@ -7,6 +7,7 @@
@using System.Net
@using Microsoft.EntityFrameworkCore
@using Wave.Services
+@using Wave.Utilities
@inject ILogger Logger
@inject IStringLocalizer Localizer
@@ -16,15 +17,10 @@
@inject NavigationManager Navigation
@inject IAdvancedEmailSender EmailSender
@inject EmailTemplateService TemplateService
+@inject IMessageDisplay Messages
@(TitlePrefix + Localizer["Title"])
-@if (!string.IsNullOrWhiteSpace(Message)) {
-
- @Message
-
-}
-
@@ -55,8 +51,6 @@
[Parameter, SupplyParameterFromQuery(Name = "token")]
public string? Token { get; set; }
- private string Message { get; set; } = string.Empty;
-
protected override async Task OnInitializedAsync() {
if (Features.Value.EmailSubscriptions is not true)
throw new ApplicationException("Email subscriptions not enabled.");
@@ -66,19 +60,19 @@
var id = await TemplateService.ValidateTokensAsync(Id, Token, deleteToken: false);
if (id is null) {
- Message = Localizer["Failure_Message"];
+ Messages.ShowError(Localizer["Failure_Message"]);
return;
}
await using var context = await ContextFactory.CreateDbContextAsync();
var subscriber = context.Set().IgnoreQueryFilters().FirstOrDefault(s => s.Id == id);
if (subscriber is null) {
- Message = Localizer["Failure_Message"];
+ Messages.ShowError(Localizer["Failure_Message"]);
return;
}
subscriber.Unsubscribed = false;
await context.SaveChangesAsync();
- Message = Localizer["Success_Message"];
+ Messages.ShowSuccess(Localizer["Success_Message"]);
await TemplateService.ValidateTokensAsync(Id, Token, deleteToken: true);
@@ -92,7 +86,7 @@
Localizer["WelcomeEmailSubject"], Localizer["WelcomeEmailTitle"], Localizer["WelcomeEmailBody"], articles);
} catch (Exception ex) {
Logger.LogError(ex, "Error trying to confirm subscriber.");
- Message = Localizer["Failure_Message"];
+ Messages.ShowError(Localizer["Failure_Message"]);
}
}
@@ -101,7 +95,7 @@
throw new ApplicationException("Email subscriptions not enabled.");
try {
- Message = Localizer["Submit_Message"];
+ Messages.ShowSuccess(Localizer["Submit_Message"]);
await using var context = await ContextFactory.CreateDbContextAsync();
var subscriber = context.Set().IgnoreQueryFilters().FirstOrDefault(s => s.Email == Model.Email);
@@ -127,7 +121,6 @@
}
} catch (Exception ex) {
Logger.LogError(ex, "Failed to create subscriber/send confirmation mail.");
- Message = Localizer["Failure_Message"];
}
}
diff --git a/Wave/Components/Pages/ManageUsers.razor b/Wave/Components/Pages/ManageUsers.razor
index ff89070..a109daa 100644
--- a/Wave/Components/Pages/ManageUsers.razor
+++ b/Wave/Components/Pages/ManageUsers.razor
@@ -11,6 +11,7 @@
@inject RoleManager RoleManager
@inject UserManager UserManager
@inject IStringLocalizer Localizer
+@inject IMessageDisplay Toast
@(TitlePrefix + Localizer["Title"])
@@ -55,12 +56,9 @@
}
-
-
@code {
[CascadingParameter(Name = "TitlePrefix")]
private string TitlePrefix { get; set; } = default!;
- public IMessageDisplay Toast { get; set; } = null!;
private string ModalId { get; } = "UserDialog";
[CascadingParameter]
diff --git a/Wave/Components/ToastComponent.razor b/Wave/Components/ToastComponent.razor
index ae23587..d9b61db 100644
--- a/Wave/Components/ToastComponent.razor
+++ b/Wave/Components/ToastComponent.razor
@@ -1,9 +1,11 @@
@using Wave.Utilities
-@implements IMessageDisplay
+@implements IDisposable
+@inject IMessageDisplay Messages
+@rendermode InteractiveServer
- @foreach (var message in Messages) {
-
Messages.Remove(message)">
+ @foreach (var message in MessagesDisplayed) {
+
MessagesDisplayed.Remove(message)">
@if (message.Title is null) {
@message.Body
} else {
@@ -15,10 +17,19 @@
@code {
- private List
Messages { get; } = [];
+ private List MessagesDisplayed { get; } = [];
- public void ShowMessage(IMessageDisplay.Message message) {
- Messages.Add(message);
+ protected override void OnInitialized() {
+ Messages.OnMessage += OnMessage;
+ }
+
+ private bool OnMessage(IMessageDisplay.Message message) {
+ MessagesDisplayed.Add(message);
StateHasChanged();
+ return true;
+ }
+
+ public void Dispose() {
+ Messages.OnMessage -= OnMessage;
}
}
diff --git a/Wave/Program.cs b/Wave/Program.cs
index fd55c1f..52b36a9 100644
--- a/Wave/Program.cs
+++ b/Wave/Program.cs
@@ -129,6 +129,7 @@
logMessages.Add("No Email provider configured.");
}
+builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddHostedService();
diff --git a/Wave/Services/MessageService.cs b/Wave/Services/MessageService.cs
new file mode 100644
index 0000000..b9355cd
--- /dev/null
+++ b/Wave/Services/MessageService.cs
@@ -0,0 +1,19 @@
+using Wave.Utilities;
+using static Wave.Utilities.IMessageDisplay;
+
+namespace Wave.Services;
+
+public class MessageService : IMessageDisplay {
+ private Queue Messages { get; } = new();
+
+ public event Func? OnMessage;
+
+ public IReadOnlyList GetMessages() => [.. Messages];
+ public Message? Pop() => Messages.TryDequeue(out var m) ? m : null;
+
+ public void ShowMessage(Message message) {
+ if (OnMessage?.Invoke(message) is not true) {
+ Messages.Enqueue(message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Wave/Utilities/IMessageDisplay.cs b/Wave/Utilities/IMessageDisplay.cs
index e6a997e..f693fde 100644
--- a/Wave/Utilities/IMessageDisplay.cs
+++ b/Wave/Utilities/IMessageDisplay.cs
@@ -1,16 +1,21 @@
namespace Wave.Utilities;
public interface IMessageDisplay {
- public void ShowMessage(Message message);
+ IReadOnlyList GetMessages();
+ event Func? OnMessage;
+
+ Message? Pop();
+
+ void ShowMessage(Message message);
- public void ShowInfo(string message, string? title = null)
+ void ShowInfo(string message, string? title = null)
=> ShowMessage(new Message(message, "alert-info", title, DateTimeOffset.UtcNow));
- public void ShowSuccess(string message, string? title = null)
+ void ShowSuccess(string message, string? title = null)
=> ShowMessage(new Message(message, "alert-success", title, DateTimeOffset.UtcNow));
- public void ShowWarning(string message, string? title = null)
+ void ShowWarning(string message, string? title = null)
=> ShowMessage(new Message(message, "alert-warning", title, DateTimeOffset.UtcNow));
- public void ShowError(string message, string? title = null)
+ void ShowError(string message, string? title = null)
=> ShowMessage(new Message(message, "alert-error", title, DateTimeOffset.UtcNow));
- public sealed record Message(string Body, string Type, string? Title, DateTimeOffset Created);
+ sealed record Message(string Body, string Type, string? Title, DateTimeOffset Created);
}
\ No newline at end of file