[backend] address review
This commit is contained in:
parent
1e1364e2bb
commit
149fae3363
7 changed files with 55 additions and 56 deletions
|
@ -81,7 +81,7 @@ public class NoteRenderer(
|
||||||
? await GetAttachmentsAsync([note])
|
? await GetAttachmentsAsync([note])
|
||||||
: [..data.Attachments.Where(p => note.FileIds.Contains(p.Id))];
|
: [..data.Attachments.Where(p => note.FileIds.Contains(p.Id))];
|
||||||
|
|
||||||
if (user == null && security.Value.PublicPreview == Enums.PublicPreview.RestrictedNoMedia) //TODO
|
if (user == null && security.Value.PublicPreview == Enums.PublicPreview.RestrictedNoMedia)
|
||||||
attachments = [];
|
attachments = [];
|
||||||
|
|
||||||
var reactions = data?.Reactions == null
|
var reactions = data?.Reactions == null
|
||||||
|
@ -104,8 +104,6 @@ public class NoteRenderer(
|
||||||
|
|
||||||
var sensitive = note.Cw != null || attachments.Any(p => p.Sensitive);
|
var sensitive = note.Cw != null || attachments.Any(p => p.Sensitive);
|
||||||
|
|
||||||
// TODO: canDisplayInlineMedia oauth_token flag that marks all media as "other" so they end up as links
|
|
||||||
// (and doesn't remove the attachments)
|
|
||||||
var inlineMedia = attachments.Select(p => new MfmInlineMedia(p.Type switch
|
var inlineMedia = attachments.Select(p => new MfmInlineMedia(p.Type switch
|
||||||
{
|
{
|
||||||
AttachmentType.Audio => MfmInlineMedia.MediaType.Audio,
|
AttachmentType.Audio => MfmInlineMedia.MediaType.Audio,
|
||||||
|
@ -116,6 +114,7 @@ public class NoteRenderer(
|
||||||
|
|
||||||
string? content = null;
|
string? content = null;
|
||||||
if (data?.Source != true)
|
if (data?.Source != true)
|
||||||
|
{
|
||||||
if (text != null || quoteUri != null || quoteInaccessible || replyInaccessible)
|
if (text != null || quoteUri != null || quoteInaccessible || replyInaccessible)
|
||||||
{
|
{
|
||||||
(content, inlineMedia) = await mfmConverter.ToHtmlAsync(text ?? "", mentionedUsers, note.UserHost, quoteUri,
|
(content, inlineMedia) = await mfmConverter.ToHtmlAsync(text ?? "", mentionedUsers, note.UserHost, quoteUri,
|
||||||
|
@ -127,6 +126,7 @@ public class NoteRenderer(
|
||||||
{
|
{
|
||||||
content = "";
|
content = "";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var account = data?.Accounts?.FirstOrDefault(p => p.Id == note.UserId) ??
|
var account = data?.Accounts?.FirstOrDefault(p => p.Id == note.UserId) ??
|
||||||
await userRenderer.RenderAsync(note.User, user);
|
await userRenderer.RenderAsync(note.User, user);
|
||||||
|
@ -216,8 +216,6 @@ public class NoteRenderer(
|
||||||
{
|
{
|
||||||
var files = attachments.Where(p => edit.FileIds.Contains(p.Id)).ToList();
|
var files = attachments.Where(p => edit.FileIds.Contains(p.Id)).ToList();
|
||||||
|
|
||||||
// TODO: canDisplayInlineMedia oauth_token flag that marks all media as "other" so they end up as links
|
|
||||||
// (and doesn't remove the attachments)
|
|
||||||
var inlineMedia = files.Select(p => new MfmInlineMedia(p.Type switch
|
var inlineMedia = files.Select(p => new MfmInlineMedia(p.Type switch
|
||||||
{
|
{
|
||||||
AttachmentType.Audio => MfmInlineMedia.MediaType.Audio,
|
AttachmentType.Audio => MfmInlineMedia.MediaType.Audio,
|
||||||
|
|
|
@ -13,13 +13,13 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.Sql("""CREATE INDEX IX_drive_file_accessUrl ON drive_file (COALESCE("webpublicUrl", url));""");
|
migrationBuilder.Sql("""CREATE INDEX "IX_drive_file_accessUrl" ON "drive_file" (COALESCE("webpublicUrl", url));""");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.Sql("""DROP INDEX IX_drive_file_accessUrl;""");
|
migrationBuilder.Sql("""DROP INDEX "IX_drive_file_accessUrl";""");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace Iceshrimp.Backend.Core.Database.Migrations
|
||||||
defaultValue: false);
|
defaultValue: false);
|
||||||
|
|
||||||
// automatically enable inline media for pleroma clients, as pleroma itself does support it
|
// automatically enable inline media for pleroma clients, as pleroma itself does support it
|
||||||
migrationBuilder.Sql("""UPDATE oauth_token SET "supportsInlineMedia"=TRUE WHERE "isPleroma"=TRUE;""");
|
migrationBuilder.Sql("""UPDATE "oauth_token" SET "supportsInlineMedia"=TRUE WHERE "isPleroma"=TRUE;""");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
|
@ -14,7 +14,7 @@ using HtmlParser = AngleSharp.Html.Parser.HtmlParser;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion;
|
namespace Iceshrimp.Backend.Core.Helpers.LibMfm.Conversion;
|
||||||
|
|
||||||
public record MfmInlineMedia(MfmInlineMedia.MediaType Type, string Src, string? Alt)
|
public readonly record struct MfmInlineMedia(MfmInlineMedia.MediaType Type, string Src, string? Alt)
|
||||||
{
|
{
|
||||||
public enum MediaType
|
public enum MediaType
|
||||||
{
|
{
|
||||||
|
@ -34,6 +34,12 @@ public record MfmInlineMedia(MfmInlineMedia.MediaType Type, string Src, string?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Resulting data after HTML to MFM conversion</summary>
|
||||||
|
public readonly record struct HtmlMfmData(string Mfm, List<MfmInlineMedia> InlineMedia);
|
||||||
|
|
||||||
|
/// <summary>Resulting data after MFM to HTML conversion</summary>
|
||||||
|
public readonly record struct MfmHtmlData(string Html, List<MfmInlineMedia> InlineMedia);
|
||||||
|
|
||||||
public class MfmConverter(
|
public class MfmConverter(
|
||||||
IOptions<Config.InstanceSection> config
|
IOptions<Config.InstanceSection> config
|
||||||
) : ISingletonService
|
) : ISingletonService
|
||||||
|
@ -41,10 +47,10 @@ public class MfmConverter(
|
||||||
public AsyncLocal<bool> SupportsHtmlFormatting { get; } = new();
|
public AsyncLocal<bool> SupportsHtmlFormatting { get; } = new();
|
||||||
public AsyncLocal<bool> SupportsInlineMedia { get; } = new();
|
public AsyncLocal<bool> SupportsInlineMedia { get; } = new();
|
||||||
|
|
||||||
public static async Task<(string Mfm, List<MfmInlineMedia> InlineMedia)> FromHtmlAsync(string? html, List<Note.MentionedUser>? mentions = null)
|
public static async Task<HtmlMfmData> FromHtmlAsync(string? html, List<Note.MentionedUser>? mentions = null)
|
||||||
{
|
{
|
||||||
var media = new List<MfmInlineMedia>();
|
var media = new List<MfmInlineMedia>();
|
||||||
if (html == null) return ("", media);
|
if (html == null) return new HtmlMfmData("", media);
|
||||||
|
|
||||||
// Ensure compatibility with AP servers that send both <br> as well as newlines
|
// Ensure compatibility with AP servers that send both <br> as well as newlines
|
||||||
var regex = new Regex(@"<br\s?\/?>\r?\n", RegexOptions.IgnoreCase);
|
var regex = new Regex(@"<br\s?\/?>\r?\n", RegexOptions.IgnoreCase);
|
||||||
|
@ -54,12 +60,12 @@ public class MfmConverter(
|
||||||
html = html.Replace("\u00A0", " ");
|
html = html.Replace("\u00A0", " ");
|
||||||
|
|
||||||
var dom = await new HtmlParser().ParseDocumentAsync(html);
|
var dom = await new HtmlParser().ParseDocumentAsync(html);
|
||||||
if (dom.Body == null) return ("", media);
|
if (dom.Body == null) return new HtmlMfmData("", media);
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
var parser = new MfmHtmlParser(mentions ?? [], media);
|
var parser = new MfmHtmlParser(mentions ?? [], media);
|
||||||
dom.Body.ChildNodes.Select(parser.ParseNode).ToList().ForEach(s => sb.Append(s));
|
dom.Body.ChildNodes.Select(parser.ParseNode).ToList().ForEach(s => sb.Append(s));
|
||||||
return (sb.ToString().Trim(), media);
|
return new HtmlMfmData(sb.ToString().Trim(), media);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<List<string>> ExtractMentionsFromHtmlAsync(string? html)
|
public static async Task<List<string>> ExtractMentionsFromHtmlAsync(string? html)
|
||||||
|
@ -80,7 +86,7 @@ public class MfmConverter(
|
||||||
return parser.Mentions;
|
return parser.Mentions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(string Html, List<MfmInlineMedia> InlineMedia)> ToHtmlAsync(
|
public async Task<MfmHtmlData> ToHtmlAsync(
|
||||||
IEnumerable<IMfmNode> nodes, List<Note.MentionedUser> mentions, string? host, string? quoteUri = null,
|
IEnumerable<IMfmNode> nodes, List<Note.MentionedUser> mentions, string? host, string? quoteUri = null,
|
||||||
bool quoteInaccessible = false, bool replyInaccessible = false, string rootElement = "p",
|
bool quoteInaccessible = false, bool replyInaccessible = false, string rootElement = "p",
|
||||||
List<Emoji>? emoji = null, List<MfmInlineMedia>? media = null
|
List<Emoji>? emoji = null, List<MfmInlineMedia>? media = null
|
||||||
|
@ -109,7 +115,7 @@ public class MfmConverter(
|
||||||
}
|
}
|
||||||
|
|
||||||
var usedMedia = new List<MfmInlineMedia>();
|
var usedMedia = new List<MfmInlineMedia>();
|
||||||
foreach (var node in nodeList) element.AppendNodes(FromMfmNode(document, node, mentions, host, ref usedMedia, emoji, media));
|
foreach (var node in nodeList) element.AppendNodes(FromMfmNode(document, node, mentions, host, usedMedia, emoji, media));
|
||||||
|
|
||||||
if (quoteUri != null)
|
if (quoteUri != null)
|
||||||
{
|
{
|
||||||
|
@ -149,10 +155,10 @@ public class MfmConverter(
|
||||||
|
|
||||||
await using var sw = new StringWriter();
|
await using var sw = new StringWriter();
|
||||||
await element.ToHtmlAsync(sw);
|
await element.ToHtmlAsync(sw);
|
||||||
return (sw.ToString(), usedMedia);
|
return new MfmHtmlData(sw.ToString(), usedMedia);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(string Html, List<MfmInlineMedia> InlineMedia)> ToHtmlAsync(
|
public async Task<MfmHtmlData> ToHtmlAsync(
|
||||||
string mfm, List<Note.MentionedUser> mentions, string? host, string? quoteUri = null,
|
string mfm, List<Note.MentionedUser> mentions, string? host, string? quoteUri = null,
|
||||||
bool quoteInaccessible = false, bool replyInaccessible = false, string rootElement = "p",
|
bool quoteInaccessible = false, bool replyInaccessible = false, string rootElement = "p",
|
||||||
List<Emoji>? emoji = null, List<MfmInlineMedia>? media = null
|
List<Emoji>? emoji = null, List<MfmInlineMedia>? media = null
|
||||||
|
@ -164,19 +170,19 @@ public class MfmConverter(
|
||||||
}
|
}
|
||||||
|
|
||||||
private INode FromMfmNode(
|
private INode FromMfmNode(
|
||||||
IDocument document, IMfmNode node, List<Note.MentionedUser> mentions, string? host, ref List<MfmInlineMedia> usedMedia,
|
IDocument document, IMfmNode node, List<Note.MentionedUser> mentions, string? host, List<MfmInlineMedia> usedMedia,
|
||||||
List<Emoji>? emoji = null, List<MfmInlineMedia>? media = null
|
List<Emoji>? emoji = null, List<MfmInlineMedia>? media = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
switch (node)
|
switch (node)
|
||||||
{
|
{
|
||||||
case MfmFnNode { Name: "media" } fn when media != null:
|
case MfmFnNode { Name: "media" } fn when media is { Count: > 0 }:
|
||||||
{
|
{
|
||||||
var urlNode = fn.Children.FirstOrDefault();
|
var urlNode = fn.Children.FirstOrDefault();
|
||||||
if (urlNode is MfmUrlNode url)
|
if (urlNode is MfmUrlNode url)
|
||||||
{
|
{
|
||||||
var current = media.FirstOrDefault(m => m.Src == url.Url);
|
MfmInlineMedia? maybeCurrent = media.FirstOrDefault(m => m.Src == url.Url);
|
||||||
if (current != null)
|
if (maybeCurrent is { } current)
|
||||||
{
|
{
|
||||||
usedMedia.Add(current);
|
usedMedia.Add(current);
|
||||||
|
|
||||||
|
@ -220,7 +226,7 @@ public class MfmConverter(
|
||||||
{
|
{
|
||||||
var el = CreateInlineFormattingElement(document, "i");
|
var el = CreateInlineFormattingElement(document, "i");
|
||||||
AddHtmlMarkup(document, el, "*");
|
AddHtmlMarkup(document, el, "*");
|
||||||
AppendChildren(el, document, node, mentions, host, ref usedMedia);
|
AppendChildren(el, document, node, mentions, host, usedMedia);
|
||||||
AddHtmlMarkup(document, el, "*");
|
AddHtmlMarkup(document, el, "*");
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
@ -229,21 +235,21 @@ public class MfmConverter(
|
||||||
{
|
{
|
||||||
var el = CreateInlineFormattingElement(document, "b");
|
var el = CreateInlineFormattingElement(document, "b");
|
||||||
AddHtmlMarkup(document, el, "**");
|
AddHtmlMarkup(document, el, "**");
|
||||||
AppendChildren(el, document, node, mentions, host, ref usedMedia);
|
AppendChildren(el, document, node, mentions, host, usedMedia);
|
||||||
AddHtmlMarkup(document, el, "**");
|
AddHtmlMarkup(document, el, "**");
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
case MfmSmallNode:
|
case MfmSmallNode:
|
||||||
{
|
{
|
||||||
var el = document.CreateElement("small");
|
var el = document.CreateElement("small");
|
||||||
AppendChildren(el, document, node, mentions, host, ref usedMedia);
|
AppendChildren(el, document, node, mentions, host, usedMedia);
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
case MfmStrikeNode:
|
case MfmStrikeNode:
|
||||||
{
|
{
|
||||||
var el = CreateInlineFormattingElement(document, "del");
|
var el = CreateInlineFormattingElement(document, "del");
|
||||||
AddHtmlMarkup(document, el, "~~");
|
AddHtmlMarkup(document, el, "~~");
|
||||||
AppendChildren(el, document, node, mentions, host, ref usedMedia);
|
AppendChildren(el, document, node, mentions, host, usedMedia);
|
||||||
AddHtmlMarkup(document, el, "~~");
|
AddHtmlMarkup(document, el, "~~");
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
@ -252,7 +258,7 @@ public class MfmConverter(
|
||||||
{
|
{
|
||||||
var el = CreateInlineFormattingElement(document, "i");
|
var el = CreateInlineFormattingElement(document, "i");
|
||||||
AddHtmlMarkup(document, el, "*");
|
AddHtmlMarkup(document, el, "*");
|
||||||
AppendChildren(el, document, node, mentions, host, ref usedMedia);
|
AppendChildren(el, document, node, mentions, host, usedMedia);
|
||||||
AddHtmlMarkup(document, el, "*");
|
AddHtmlMarkup(document, el, "*");
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
@ -267,7 +273,7 @@ public class MfmConverter(
|
||||||
case MfmCenterNode:
|
case MfmCenterNode:
|
||||||
{
|
{
|
||||||
var el = document.CreateElement("div");
|
var el = document.CreateElement("div");
|
||||||
AppendChildren(el, document, node, mentions, host, ref usedMedia);
|
AppendChildren(el, document, node, mentions, host, usedMedia);
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
case MfmEmojiCodeNode emojiCodeNode:
|
case MfmEmojiCodeNode emojiCodeNode:
|
||||||
|
@ -357,7 +363,7 @@ public class MfmConverter(
|
||||||
{
|
{
|
||||||
var el = CreateInlineFormattingElement(document, "blockquote");
|
var el = CreateInlineFormattingElement(document, "blockquote");
|
||||||
AddHtmlMarkup(document, el, "> ");
|
AddHtmlMarkup(document, el, "> ");
|
||||||
AppendChildren(el, document, node, mentions, host, ref usedMedia);
|
AppendChildren(el, document, node, mentions, host, usedMedia);
|
||||||
el.AppendChild(document.CreateElement("br"));
|
el.AppendChild(document.CreateElement("br"));
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
@ -391,7 +397,7 @@ public class MfmConverter(
|
||||||
case MfmPlainNode:
|
case MfmPlainNode:
|
||||||
{
|
{
|
||||||
var el = document.CreateElement("span");
|
var el = document.CreateElement("span");
|
||||||
AppendChildren(el, document, node, mentions, host, ref usedMedia);
|
AppendChildren(el, document, node, mentions, host, usedMedia);
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -403,11 +409,11 @@ public class MfmConverter(
|
||||||
|
|
||||||
private void AppendChildren(
|
private void AppendChildren(
|
||||||
INode element, IDocument document, IMfmNode parent,
|
INode element, IDocument document, IMfmNode parent,
|
||||||
List<Note.MentionedUser> mentions, string? host, ref List<MfmInlineMedia> usedMedia,
|
List<Note.MentionedUser> mentions, string? host, List<MfmInlineMedia> usedMedia,
|
||||||
List<Emoji>? emoji = null, List<MfmInlineMedia>? media = null
|
List<Emoji>? emoji = null, List<MfmInlineMedia>? media = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
foreach (var node in parent.Children) element.AppendNodes(FromMfmNode(document, node, mentions, host, ref usedMedia, emoji, media));
|
foreach (var node in parent.Children) element.AppendNodes(FromMfmNode(document, node, mentions, host, usedMedia, emoji, media));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IElement CreateInlineFormattingElement(IDocument document, string name)
|
private IElement CreateInlineFormattingElement(IDocument document, string name)
|
||||||
|
|
|
@ -87,7 +87,7 @@ internal class HtmlParser(IEnumerable<Note.MentionedUser> mentions, ICollection<
|
||||||
if (node is not HtmlElement el) return node.TextContent;
|
if (node is not HtmlElement el) return node.TextContent;
|
||||||
|
|
||||||
var src = el.GetAttribute("src");
|
var src = el.GetAttribute("src");
|
||||||
if (!Uri.IsWellFormedUriString(src, UriKind.Absolute))
|
if (src == null || !Uri.TryCreate(src, UriKind.Absolute, out var uri) && uri is { Scheme: "http" or "https" })
|
||||||
return node.TextContent;
|
return node.TextContent;
|
||||||
|
|
||||||
var alt = el.GetAttribute("alt") ?? el.GetAttribute("title");
|
var alt = el.GetAttribute("alt") ?? el.GetAttribute("title");
|
||||||
|
|
|
@ -494,10 +494,7 @@ public class NoteService(
|
||||||
if (node is MfmFnNode { Name: "media" } fn)
|
if (node is MfmFnNode { Name: "media" } fn)
|
||||||
{
|
{
|
||||||
var urlNode = fn.Children.FirstOrDefault();
|
var urlNode = fn.Children.FirstOrDefault();
|
||||||
if (urlNode is MfmUrlNode url)
|
if (urlNode is MfmUrlNode url) urls.Add(url.Url);
|
||||||
{
|
|
||||||
urls.Add(url.Url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
urls.AddRange(GetInlineMediaUrls(node.Children));
|
urls.AddRange(GetInlineMediaUrls(node.Children));
|
||||||
|
@ -1075,9 +1072,7 @@ public class NoteService(
|
||||||
List<MfmInlineMedia>? htmlInlineMedia = null;
|
List<MfmInlineMedia>? htmlInlineMedia = null;
|
||||||
|
|
||||||
if (text == null)
|
if (text == null)
|
||||||
{
|
|
||||||
(text, htmlInlineMedia) = await MfmConverter.FromHtmlAsync(note.Content, mentionData.Mentions);
|
(text, htmlInlineMedia) = await MfmConverter.FromHtmlAsync(note.Content, mentionData.Mentions);
|
||||||
}
|
|
||||||
|
|
||||||
var cw = note.Summary;
|
var cw = note.Summary;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue