From e65c76ca39e2c2ea1fd225224bfa0570d5e40f62 Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Wed, 13 Mar 2024 03:40:31 +0100 Subject: [PATCH] [backend/masto-client] Render quote uris inline (ISH-177) --- .../Mastodon/Renderers/NoteRenderer.cs | 14 ++++++---- .../Helpers/LibMfm/Conversion/MfmConverter.cs | 28 ++++++++++++++++--- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs index 8556854a..478d63e9 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/Renderers/NoteRenderer.cs @@ -28,12 +28,16 @@ public class NoteRenderer( var quote = note is { Renote: not null, IsQuote: true } && recurse > 0 ? await RenderAsync(note.Renote, user, data, --recurse) : null; - var text = note.Text; + var text = note.Text; + string? quoteUri = null; + if (note is { Renote: not null, IsQuote: true } && text != null) { - var quoteUri = note.Renote?.Url ?? note.Renote?.Uri ?? note.Renote?.GetPublicUriOrNull(config.Value); - if (quoteUri != null) - text += $"\n\nRE: {quoteUri}"; //TODO: render as inline quote + var qUri = note.Renote?.Url ?? note.Renote?.Uri ?? note.Renote?.GetPublicUriOrNull(config.Value); + var alt = note.Renote?.Uri; + + if (qUri != null && !text.Contains(qUri) && (alt == null || qUri == alt || !text.Contains(alt))) + quoteUri = qUri; } var liked = data?.LikedNotes?.Contains(note.Id) ?? @@ -69,7 +73,7 @@ public class NoteRenderer( .ToList(); var content = text != null && data?.Source != true - ? await mfmConverter.ToHtmlAsync(text, mentionedUsers, note.UserHost) + ? await mfmConverter.ToHtmlAsync(text, mentionedUsers, note.UserHost, quoteUri) : null; var account = data?.Accounts?.FirstOrDefault(p => p.Id == note.UserId) ?? diff --git a/Iceshrimp.Backend/Core/Helpers/LibMfm/Conversion/MfmConverter.cs b/Iceshrimp.Backend/Core/Helpers/LibMfm/Conversion/MfmConverter.cs index 7f9431e1..cd0561ec 100644 --- a/Iceshrimp.Backend/Core/Helpers/LibMfm/Conversion/MfmConverter.cs +++ b/Iceshrimp.Backend/Core/Helpers/LibMfm/Conversion/MfmConverter.cs @@ -54,7 +54,9 @@ public class MfmConverter(IOptions config) return parser.Mentions; } - public async Task ToHtmlAsync(IEnumerable nodes, List mentions, string? host) + public async Task ToHtmlAsync( + IEnumerable nodes, List mentions, string? host, string? quoteUri = null + ) { var context = BrowsingContext.New(); var document = await context.OpenNewAsync(); @@ -62,15 +64,33 @@ public class MfmConverter(IOptions config) foreach (var node in nodes) element.AppendNodes(FromMfmNode(document, node, mentions, host)); + if (quoteUri != null) + { + var a = document.CreateElement("a"); + a.SetAttribute("href", quoteUri); + a.TextContent = quoteUri.StartsWith("https://") ? quoteUri[8..] : quoteUri[7..]; + var quote = document.CreateElement("span"); + quote.ClassList.Add("quote-inline"); + quote.AppendChild(document.CreateElement("br")); + quote.AppendChild(document.CreateElement("br")); + var re = document.CreateElement("span"); + re.TextContent = "RE: "; + quote.AppendChild(re); + quote.AppendChild(a); + element.AppendChild(quote); + } + await using var sw = new StringWriter(); await element.ToHtmlAsync(sw); return sw.ToString(); } - public async Task ToHtmlAsync(string mfm, List mentions, string? host) + public async Task ToHtmlAsync( + string mfm, List mentions, string? host, string? quoteUri = null + ) { var nodes = MfmParser.Parse(mfm); - return await ToHtmlAsync(nodes, mentions, host); + return await ToHtmlAsync(nodes, mentions, host, quoteUri); } private INode FromMfmNode(IDocument document, MfmNode node, List mentions, string? host) @@ -265,7 +285,7 @@ public class MfmConverter(IOptions config) var markupNode = new MfmTextNode { Text = chars }; node.Children = node.Children.Prepend(markupNode).Append(markupNode); } - + private void AddHtmlMarkupStartOnly(MfmNode node, string chars) { if (SupportsHtmlFormatting) return;