Files
decePubClient/Extensions/GenericExtensions.cs
T
thepraandClaude Opus 5.5 96d1db1008 Fix logout, the client log store and empty name attributes
Logout:
- It navigated to "authenticatio/logout", a misspelled route.
- The correct template route, authentication/logout, would throw anyway. Its
  RemoteAuthenticatorView casts the AuthenticationStateProvider to the OIDC
  template's service, and the app registers TokenAuthStateProvider instead.
- It now signs out through TokenAuthStateProvider.LogoutAsync, as the code
  commented out in the page intended, and returns home. LoginDisplay's sign-out
  goes through it as well.
- This also drops the obsolete SignOutSessionStateManager, the build's last
  warning.

The IndexedDB schema never declared the ClientLogs store that IStorage.AddLog
writes to, so every client log write failed inside its try/catch. Version 2 adds
the store, keyed by the Id AddLog assigns.

DInputBase.NameAttribute renders no name instead of name="" when the form maps
no field names.

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

164 lines
4.1 KiB
C#

using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Web;
using Blazored.LocalStorage;
using decePubClient.Models;
using decePubClient.Models.Types;
using decePubClient.Resources;
using decePubClient.Services;
using DnetIndexedDb;
using DnetIndexedDb.Fluent;
using DnetIndexedDb.Models;
using Markdig;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.Localization;
namespace decePubClient.Extensions;
public static class GenericExtensions
{
public static IServiceCollection AddIndexedDb(this IServiceCollection services)
{
return services.AddIndexedDbDatabase<IndexedDb>(options =>
{
var model = new IndexedDbDatabaseModel()
.WithName("data")
.WithVersion(2)
.WithModelId(1);
model.AddStore(nameof(Message))
.WithKey(nameof(Message.MessageId))
.AddUniqueIndex(nameof(Message.MessageId))
.AddIndex(nameof(Message.RootMessageId))
.AddIndex(nameof(Message.User))
.AddIndex(nameof(Message.MessageType))
.AddIndex(nameof(Message.Title))
.AddIndex(nameof(Message.Content))
.AddIndex(nameof(Message.CreatedAt))
.AddIndex(nameof(Message.Medias));
// IStorage.AddLog numbers entries itself (max key + 1), so the key is Id rather than generated. Version 2
// added this store; before it, every log write failed inside AddLog's try/catch.
model.AddStore(nameof(ClientLogs))
.WithKey(nameof(ClientLogs.Id))
.AddUniqueIndex(nameof(ClientLogs.Id))
.AddIndex(nameof(ClientLogs.TimeStamp));
options.UseDatabase(model);
});
}
public static string GetPassedTime(this DateTime dateTime, IStringLocalizer localizer)
{
var timeframe = DateTime.Now - dateTime.ToLocalTime();
switch ((int)timeframe.TotalHours)
{
case >= 24:
return string.Format(localizer["{0}d"], (int)timeframe.TotalDays);
case >= 1 and < 24:
return string.Format(localizer["{0}h"], (int)timeframe.TotalHours);
case 0:
return string.Format(localizer["{0}m"], (int)timeframe.TotalMinutes);
default:
return default;
}
}
public static string GetFileSize(this long size, IStringLocalizer<AllStrings> localizer)
{
switch (size)
{
case >= 1_048_576:
return string.Format(localizer["{0:N0}MB"], size / 1_048_576);
case < 1_048_576:
return string.Format(localizer["{0}KB"], size / 1_024);
}
}
public static string ParseContent(this string content, ContentType contentType)
{
switch (contentType)
{
case ContentType.PlainText:
return content;
case ContentType.HTML:
return ((MarkupString)content).Value;
case ContentType.Markdown:
return Markdown.ToHtml(content);
default:
return default;
}
}
public static string GetMessageTypeIcon(this MessageType messageType)
{
switch (messageType)
{
case MessageType.Public:
return "ion-md-globe";
case MessageType.Unlisted:
return "ion-md-unlock";
case MessageType.FollowersOnly:
return "ion-md-lock";
case MessageType.Direct:
return "ion-md-paper-plane";
default:
return default;
}
}
public static string GetTimelineTypeIcon(this TimelineType timelineType)
{
switch (timelineType)
{
case TimelineType.Home:
return "ion-md-home";
case TimelineType.Local:
return "ion-md-people";
case TimelineType.Federation:
return "ion-md-globe";
default:
return default;
}
}
public static string GetContentTypeIcon(this ContentType contentType)
{
switch (contentType)
{
case ContentType.PlainText:
return "ion-md-quote";
case ContentType.HTML:
return "ion-logo-html5";
case ContentType.Markdown:
return "ion-logo-markdown";
default:
return default;
}
}
public static string GetMediaTypeIcon(this MediaType mediaType)
{
switch (mediaType)
{
case MediaType.Images:
return "ion-md-images";
case MediaType.Video:
return "ion-md-videocam";
case MediaType.Documents:
return "ion-md-document";
case MediaType.Audio:
return "ion-md-volume-high";
default:
return default;
}
}
}