Files
decePubClient/Extensions/ExtensionMethods.cs
T
thepraandClaude Opus 5.5 3ad4f55b11 Move the app's markup to the D components and Tailwind 4
The pages and components now use the daisyUI wrappers and the neo-* utilities
instead of Bulma and the neomorph classes:
- buttons are DButton, with pressed states as Active;
- nav links are btn / btn-active;
- the filter and composer selects are DSelect with a prefix icon;
- the composer's fields are DInput and DTextarea;
- the message options and scope pickers are DDropdown, which now close on an
  outside click and on Escape;
- the settings and admin sections are DCollapse;
- the dark-mode switch is a DToggle with sun and moon icons;
- the hue slider is a DRange on the rainbow track.

DropdownButton, OpenDownContainer and LoadingData are gone. The loader is
DLoading, whose animation runs; the old one's keyframes were never defined.
Icons coloured with Bulma's has-text-* now get daisyUI's text-info, success,
warning and error, which had no rules before.

Tailwind 3 to 4 fixes:
- space-x/y become gap, which also handles the flex-col-reverse feeds;
- !rounded-lg becomes rounded-lg!;
- drop-shadow becomes drop-shadow-sm;
- text-md becomes text-base.
The thread page marks its message with Content's new Highlighted parameter
rather than a second shadow size.

neo.css: a select's prefix icon gets the room daisyUI leaves for a span.label,
and the inner select lets the well show through.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-03 12:08:11 +02:00

153 lines
4.8 KiB
C#

using Blazored.LocalStorage;
using collAnon.Client.Services;
using decePubClient.Models;
using decePubClient.Services;
using Markdig;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using PrivaPub.ClientModels;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Text.Json;
using System.Web;
namespace decePubClient.Extensions
{
public static class ExtensionMethods
{
#region Navigation Manager
public static NameValueCollection QueryString(this NavigationManager navigationManager) =>
HttpUtility.ParseQueryString(new Uri(navigationManager.Uri).Query);
public static string QueryString(this NavigationManager navigationManager, string key) =>
navigationManager.QueryString()[key];
public static T QueryString<T>(this NavigationManager navigationManager, string key)
{
var value = navigationManager.QueryString()[key];
var converter = TypeDescriptor.GetConverter(typeof(T));
if (converter.IsValid(value))
return (T)Convert.ChangeType(value, typeof(T));
else
return default;
}
#endregion Navigation Manager
#region Setups
public static async Task SetDefaultCulture(this WebAssemblyHost host)
{
var culture = default(CultureInfo);
try
{
var storage = host.Services.GetRequiredService<ILocalStorageService>();
var publicCache = await storage.GetItemAsync<PublicCacheData>(nameof(PublicCacheData));
if (publicCache is null)
{
publicCache = new();
var status = host.Services.GetRequiredService<AppStatusService>();
var browserLanguage = status.Language();
if (browserLanguage.Contains("en"))
browserLanguage = "en-GB";
var appConfig = host.Services.GetRequiredService<AppConfiguration>();
if (!string.IsNullOrEmpty(publicCache.PageSettings.CurrentLanguageCode) &&
publicCache.PageSettings.CurrentLanguageCode.Contains("en"))
publicCache.PageSettings.CurrentLanguageCode = "en-GB";
culture = new(publicCache.PageSettings.CurrentLanguageCode ?? browserLanguage);
publicCache.PageSettings.CurrentLanguageCode = culture.TwoLetterISOLanguageName;
await storage.SetItemAsync(nameof(PublicCacheData), publicCache);
}
culture ??= new(publicCache.PageSettings.CurrentLanguageCode == "en" ? "en-GB" : publicCache.PageSettings.CurrentLanguageCode);
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
}
catch (Exception ex)
{
Console.WriteLine(ex);
culture = new("en-GB");
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
}
}
#endregion Setups
public static string GetLogLevelClass(this string logType) => logType switch
{
"Debug" => "text-base-content/60",
"Information" => "text-info",
"Warning" => "text-warning",
"Error" => "text-error",
"Fatal" => "text-error font-bold",
_ => string.Empty
};
#region Formats
public static MarkdownPipeline Pipeline =>
new MarkdownPipelineBuilder().UseGenericAttributes().Build();
public static string ToJson(this object value) =>
JsonSerializer.Serialize(value);
public static string ToBase64PngImage(this string base64Image) =>
$"data:image/png;base64,{base64Image}";
#endregion Formats
#region Models
public static string ToString(this WebResult webResult, CoalescingStringLocalizer localizer) =>
localizer["{0} - {1}", webResult.StatusCode, webResult.ErrorMessage];
public static string Timeframe(this DateTime creationDate, CoalescingStringLocalizer localizer)
{
var timeframe = DateTime.Now - creationDate.ToLocalTime();
switch ((int)timeframe.TotalHours)
{
case >= 24:
return localizer["{0} day/s", (int)timeframe.TotalDays];
case >= 1 and < 24:
return localizer["{0} hour/s", (int)timeframe.TotalHours];
case 0:
return localizer["{0} minute/s", (int)timeframe.TotalMinutes];
default:
return string.Empty;
}
}
public static AuthorizationPolicy IsAdminPolicy() =>
new AuthorizationPolicyBuilder().RequireAuthenticatedUser()
.RequireClaim(Policies.IsAdmin, true.ToString().ToLower())
.RequireClaim(Policies.IsUser, true.ToString().ToLower())
.RequireClaim(Policies.IsModerator, true.ToString().ToLower())
.Build();
public static AuthorizationPolicy IsUserPolicy() =>
new AuthorizationPolicyBuilder().RequireAuthenticatedUser()
.RequireClaim(Policies.IsUser, true.ToString().ToLower())
.Build();
public static AuthorizationPolicy IsUserModeratorPolicy() =>
new AuthorizationPolicyBuilder().RequireAuthenticatedUser()
.RequireClaim(Policies.IsModerator, true.ToString().ToLower())
.Build();
#endregion
}
}