Custom emoji, downvotes, blurhash and focal points on the cards
What other platforms send now shows as they meant it: - custom emoji (a post's or account's :shortcode:) as their pictures in posts, names, content warnings, bios and fields: only the shortcodes the server listed, only http(s) pictures, never inside a tag, the shortcode kept as alt text; - a threadiverse post's downvotes next to its favourites; - blurhash placeholders until a picture loads (wwwroot/js/blurhash.js, a small decoder that watches the page), and a picture's focal point; - an event's online link, participants and capacity, and whether it is tentative or cancelled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
5e8fdc43af
commit
2024798732
9 files changed
+236
-9
No files matched your search
@@ -0,0 +1,47 @@
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using decePubClient.Models.Mastodon;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace decePubClient.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// A post's or an account's custom emoji (`:shortcode:` among its <c>emojis</c>) shown as their pictures. The shortcode
|
||||
/// stays as the picture's alt text, so it is what a screen reader reads and what is copied. Only shortcodes the
|
||||
/// server listed for that post or account are replaced, and only with an http(s) picture.
|
||||
/// </summary>
|
||||
public static partial class CustomEmojis
|
||||
{
|
||||
[GeneratedRegex(@":([A-Za-z0-9_+-]{1,64}):")]
|
||||
private static partial Regex Shortcode();
|
||||
|
||||
[GeneratedRegex(@"(<[^>]*>)")]
|
||||
private static partial Regex Tag();
|
||||
|
||||
/// <summary>Plain text (a name, a content warning): escaped, then its shortcodes as pictures.</summary>
|
||||
public static MarkupString Text(string text, IReadOnlyCollection<CustomEmoji> emojis) =>
|
||||
new(Replace(WebUtility.HtmlEncode(text ?? string.Empty), emojis));
|
||||
|
||||
/// <summary>HTML the server sanitized: the shortcodes in its text, never inside a tag.</summary>
|
||||
public static string Html(string html, IReadOnlyCollection<CustomEmoji> emojis) =>
|
||||
emojis.Count == 0 || string.IsNullOrEmpty(html)
|
||||
? html
|
||||
: string.Concat(Tag().Split(html).Select(part => part.StartsWith('<') ? part : Replace(part, emojis)));
|
||||
|
||||
static string Replace(string text, IReadOnlyCollection<CustomEmoji> emojis)
|
||||
{
|
||||
if (emojis.Count == 0 || !text.Contains(':'))
|
||||
return text;
|
||||
var known = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
foreach (var emoji in emojis)
|
||||
if (!string.IsNullOrEmpty(emoji.Shortcode) && Uri.TryCreate(emoji.Url, UriKind.Absolute, out var url)
|
||||
&& url.Scheme is "https" or "http")
|
||||
known.TryAdd(emoji.Shortcode, url.AbsoluteUri);
|
||||
return Shortcode().Replace(text, match => known.TryGetValue(match.Groups[1].Value, out var url)
|
||||
? $"<img class=\"custom-emoji\" src=\"{WebUtility.HtmlEncode(url)}\" alt=\"{match.Value}\" title=\"{match.Value}\" draggable=\"false\" />"
|
||||
: match.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user