@@ -104,13 +104,13 @@
@if (OnMessageReply.HasDelegate)
{
-
+
}
@if (OnMessageBoost.HasDelegate)
{
-
@if (Message.BoostsCounter != 0)
@@ -121,7 +121,7 @@
}
@if (OnMessageFavourite.HasDelegate)
{
-
@@ -138,28 +138,29 @@
@if (OnUserDirectMessage.HasDelegate)
{
-
+
@Localizer["Direct message"]
}
@if (OnUserSilence.HasDelegate)
{
-
+
@Localizer["Mute"]
}
@if (OnUserBlock.HasDelegate)
{
-
+
@Localizer["Block"]
}
- @if (@*Message.User.UserName == CurrentUserName &&*@ OnMessageDelete.HasDelegate)
+ @* Once signed in for real: only for the author's own messages (Message.User.UserId == CascadingState.User.Id). *@
+ @if (OnMessageDelete.HasDelegate)
{
-
+
@Localizer["Delete"]
@@ -184,8 +185,6 @@
@code {
- [CascadingParameter] Task AuthState { get; set; }
- [CascadingParameter] CascadingState CascadingState { get; set; }
[Parameter] public Message Message { get; set; } = new();
[Parameter] public EventCallback OnMessageReply { get; set; }
[Parameter] public EventCallback OnMessageBoost { get; set; }
@@ -200,15 +199,9 @@
[Parameter] public bool Highlighted { get; set; }
[Parameter] public bool IncludeExpand { get; set; } = true;
- bool isAnswering { get; set; } = false;
+ bool isAnswering;
- string CurrentUserName
- {
- get
- {
- return AuthState.Result.User.Identity?.Name;
- }
- }
+ protected override void OnInitialized() => IsLoading = false;
async Task DeleteMessage()
{
@@ -222,11 +215,8 @@
await OnMessageReply.InvokeAsync(messageForm);
}
- async Task Reply()
+ void Reply()
{
- await Task.Run(() =>
- {
- });
isAnswering = !isAnswering;
}
-}
\ No newline at end of file
+}
diff --git a/Components/Daisy/DataDisplay/DAccordion.razor b/Components/Daisy/DataDisplay/DAccordion.razor
index 4b0bb7a..67e51e9 100644
--- a/Components/Daisy/DataDisplay/DAccordion.razor
+++ b/Components/Daisy/DataDisplay/DAccordion.razor
@@ -1,4 +1,4 @@
-@inherits DComponentBase
+@inherits DComponentBase
@* daisyUI Accordion: https://daisyui.com/components/accordion/ . Instead of daisyUI's radio group, the open item is an
index in C# (@bind-Active, -1 = none). Each DAccordionItem registers itself through the cascading value and renders a
@@ -19,7 +19,7 @@
/// Join the items into one block (join join-vertical, join-item).
[Parameter] public bool Joined { get; set; }
- readonly List items = new();
+ readonly List items = [];
string CssClass => Cx(Joined ? "join join-vertical rounded-box neo-raised" : "flex flex-col gap-2", Class);
diff --git a/Components/Daisy/DataDisplay/DAvatar.razor b/Components/Daisy/DataDisplay/DAvatar.razor
index 3d7a73b..1a6caf5 100644
--- a/Components/Daisy/DataDisplay/DAvatar.razor
+++ b/Components/Daisy/DataDisplay/DAvatar.razor
@@ -18,6 +18,7 @@
@code {
[Parameter] public string Src { get; set; }
+ // "" on purpose (the documented exception to "no string defaults"): an empty alt marks a decorative image.
[Parameter] public string Alt { get; set; } = "";
/// Shown in the frame when there is no Src, e.g. initials with Placeholder.
[Parameter] public RenderFragment ChildContent { get; set; }
diff --git a/Components/Daisy/DataDisplay/DCarousel.razor b/Components/Daisy/DataDisplay/DCarousel.razor
index e6ba869..baa00a1 100644
--- a/Components/Daisy/DataDisplay/DCarousel.razor
+++ b/Components/Daisy/DataDisplay/DCarousel.razor
@@ -1,5 +1,4 @@
-@inherits DComponentBase
-@inject NavigationManager NavigationManager
+@inherits DComponentBase
@* daisyUI Carousel: https://daisyui.com/components/carousel/ . A scroll-snap strip of DCarouselItem children; the
user scrolls or swipes it. Give each item w-full for one slide at a time. ShowNavigation (previous/next on every
@@ -25,6 +24,7 @@
@code {
+ [Inject] NavigationManager NavigationManager { get; set; }
[Parameter] public RenderFragment ChildContent { get; set; }
[Parameter] public DCarouselSnap Snap { get; set; }
[Parameter] public DDirection Direction { get; set; }
@@ -36,7 +36,7 @@
[Parameter] public string NextText { get; set; } = "Next slide";
[Parameter] public string SlideText { get; set; } = "Slide";
- readonly List items = new();
+ readonly List items = [];
readonly string uid = "d" + Guid.NewGuid().ToString("N")[..8];
RenderFragment Strip => @@ChildContent
;
diff --git a/Components/Daisy/DataDisplay/DHoverGallery.razor b/Components/Daisy/DataDisplay/DHoverGallery.razor
index f49fc93..cbccf36 100644
--- a/Components/Daisy/DataDisplay/DHoverGallery.razor
+++ b/Components/Daisy/DataDisplay/DHoverGallery.razor
@@ -16,6 +16,7 @@
@code {
[Parameter] public IEnumerable Images { get; set; }
+ // "" on purpose (the documented exception to "no string defaults"): an empty alt marks a decorative image.
[Parameter] public string Alt { get; set; } = "";
[Parameter] public RenderFragment ChildContent { get; set; }
}
diff --git a/Components/Daisy/Navigation/DTabs.razor b/Components/Daisy/Navigation/DTabs.razor
index ed95073..98d9ce7 100644
--- a/Components/Daisy/Navigation/DTabs.razor
+++ b/Components/Daisy/Navigation/DTabs.razor
@@ -1,4 +1,4 @@
-@inherits DComponentBase
+@inherits DComponentBase
@* daisyUI Tab: https://daisyui.com/components/tab/ . Each DTab child registers itself through the cascading value;
DTabs renders the tab buttons and the active tab's content. The active tab is an index (@bind-Active). *@
@@ -35,7 +35,7 @@
[Parameter] public bool Bottom { get; set; }
[Parameter] public string ContentClass { get; set; } = "pt-4";
- readonly List tabs = new();
+ readonly List tabs = [];
string CssClass => Cx("tabs", VariantClass, SizeClass, Bottom ? "tabs-bottom" : null, Class);
diff --git a/Components/ErrorViewer.razor b/Components/ErrorViewer.razor
new file mode 100644
index 0000000..1b403c4
--- /dev/null
+++ b/Components/ErrorViewer.razor
@@ -0,0 +1,58 @@
+@implements IDisposable
+
+@* In-page errors (b2b.next's WidmannErrorViewer): the validation messages of the cascading EditContext, then
+ AdditionalError (one message) and AdditionalErrors (a list; never join a list into one string), one each.
+ Renders nothing when there is nothing to show. Messages are text, not markup: they can contain file names. *@
+@if (HasContent)
+{
+
+
+ @foreach (var message in Messages)
+ {
+ - @message
+ }
+
+
+}
+
+@code {
+ [CascadingParameter] EditContext EditContext { get; set; }
+ [Parameter] public string ContainerClass { get; set; }
+ [Parameter] public ErrorViewerSeverity Severity { get; set; }
+ [Parameter] public string AdditionalError { get; set; }
+ [Parameter] public IEnumerable AdditionalErrors { get; set; } = [];
+
+ EditContext subscribedEditContext;
+
+ IEnumerable Messages =>
+ (EditContext?.GetValidationMessages() ?? [])
+ .Append(AdditionalError)
+ .Concat(AdditionalErrors)
+ .Where(message => !string.IsNullOrEmpty(message));
+
+ bool HasContent => Messages.Any();
+
+ static string Cx(params string[] classes) => DCx.Join(classes);
+
+ protected override void OnParametersSet()
+ {
+ if (EditContext == subscribedEditContext)
+ return;
+
+ Unsubscribe();
+ subscribedEditContext = EditContext;
+ if (subscribedEditContext is not null)
+ subscribedEditContext.OnValidationStateChanged += OnValidationStateChanged;
+ }
+
+ void OnValidationStateChanged(object sender, ValidationStateChangedEventArgs e) => _ = InvokeAsync(StateHasChanged);
+
+ void Unsubscribe()
+ {
+ if (subscribedEditContext is not null)
+ subscribedEditContext.OnValidationStateChanged -= OnValidationStateChanged;
+ }
+
+ public void Dispose() => Unsubscribe();
+}
diff --git a/Components/ErrorViewerSeverity.cs b/Components/ErrorViewerSeverity.cs
new file mode 100644
index 0000000..e1e77e9
--- /dev/null
+++ b/Components/ErrorViewerSeverity.cs
@@ -0,0 +1,7 @@
+namespace decePubClient.Components;
+
+public enum ErrorViewerSeverity
+{
+ Error,
+ Warning
+}
diff --git a/Components/MessageUpsertForm.razor b/Components/MessageUpsertForm.razor
index b6f89b2..d9395d1 100644
--- a/Components/MessageUpsertForm.razor
+++ b/Components/MessageUpsertForm.razor
@@ -4,15 +4,15 @@
-
- @(MessageForm.Content?.Length ?? 0)/5000
+ @ContentLength/@MaxContentLength
@if (showPreviewButton && isPreviewOpen && MessageForm.Content is { Length: > 0 })
{
@@ -30,10 +30,6 @@