Compare commits
10 commits
dev
...
wip/user-e
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cfe269f235 | ||
![]() |
509d001a35 | ||
![]() |
5fd1aa72f1 | ||
![]() |
e09f2bac1a | ||
![]() |
a92f01e7e1 | ||
![]() |
3509d94309 | ||
![]() |
cfcccf7654 | ||
![]() |
53c3fc9edc | ||
![]() |
6293c138a4 | ||
![]() |
72c68d2d5a |
20 changed files with 136 additions and 60 deletions
|
@ -1,7 +1,6 @@
|
||||||
using Iceshrimp.Backend.Core.Configuration;
|
using Iceshrimp.Backend.Core.Configuration;
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
|
||||||
using Iceshrimp.Shared.Schemas.Web;
|
using Iceshrimp.Shared.Schemas.Web;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
@ -10,16 +9,17 @@ namespace Iceshrimp.Backend.Controllers.Web.Renderers;
|
||||||
|
|
||||||
public class UserRenderer(IOptions<Config.InstanceSection> config, DatabaseContext db)
|
public class UserRenderer(IOptions<Config.InstanceSection> config, DatabaseContext db)
|
||||||
{
|
{
|
||||||
public async Task<UserResponse> RenderOne(User user, UserRendererDto? data = null)
|
private UserResponse Render(User user, UserRendererDto data)
|
||||||
{
|
{
|
||||||
var instance = user.IsLocalUser
|
var instance = user.IsRemoteUser ? data.InstanceData.FirstOrDefault(p => p.Host == user.Host) : null;
|
||||||
? null
|
|
||||||
: (data?.InstanceData ?? await GetInstanceData([user])).FirstOrDefault(p => p.Host == user.Host);
|
|
||||||
|
|
||||||
//TODO: populate the below two lines for local users
|
//TODO: populate the below two lines for local users
|
||||||
var instanceName = user.IsLocalUser ? config.Value.AccountDomain : instance?.Name;
|
var instanceName = user.IsLocalUser ? config.Value.AccountDomain : instance?.Name;
|
||||||
var instanceIcon = user.IsLocalUser ? null : instance?.FaviconUrl;
|
var instanceIcon = user.IsLocalUser ? null : instance?.FaviconUrl;
|
||||||
|
|
||||||
|
if (!data.Emojis.TryGetValue(user.Id, out var emoji))
|
||||||
|
throw new Exception("DTO didn't contain emoji for user");
|
||||||
|
|
||||||
return new UserResponse
|
return new UserResponse
|
||||||
{
|
{
|
||||||
Id = user.Id,
|
Id = user.Id,
|
||||||
|
@ -30,10 +30,20 @@ public class UserRenderer(IOptions<Config.InstanceSection> config, DatabaseConte
|
||||||
BannerUrl = user.BannerUrl,
|
BannerUrl = user.BannerUrl,
|
||||||
InstanceName = instanceName,
|
InstanceName = instanceName,
|
||||||
InstanceIconUrl = instanceIcon,
|
InstanceIconUrl = instanceIcon,
|
||||||
|
Emojis = emoji,
|
||||||
MovedTo = user.MovedToUri
|
MovedTo = user.MovedToUri
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<UserResponse> RenderOne(User user)
|
||||||
|
{
|
||||||
|
var instanceData = await GetInstanceData([user]);
|
||||||
|
var emojis = await GetEmojis([user]);
|
||||||
|
var data = new UserRendererDto { Emojis = emojis, InstanceData = instanceData };
|
||||||
|
|
||||||
|
return Render(user, data);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<List<Instance>> GetInstanceData(IEnumerable<User> users)
|
private async Task<List<Instance>> GetInstanceData(IEnumerable<User> users)
|
||||||
{
|
{
|
||||||
var hosts = users.Select(p => p.Host).Where(p => p != null).Distinct().Cast<string>();
|
var hosts = users.Select(p => p.Host).Where(p => p != null).Distinct().Cast<string>();
|
||||||
|
@ -43,12 +53,40 @@ public class UserRenderer(IOptions<Config.InstanceSection> config, DatabaseConte
|
||||||
public async Task<IEnumerable<UserResponse>> RenderMany(IEnumerable<User> users)
|
public async Task<IEnumerable<UserResponse>> RenderMany(IEnumerable<User> users)
|
||||||
{
|
{
|
||||||
var userList = users.ToList();
|
var userList = users.ToList();
|
||||||
var data = new UserRendererDto { InstanceData = await GetInstanceData(userList) };
|
var data = new UserRendererDto
|
||||||
return await userList.Select(p => RenderOne(p, data)).AwaitAllAsync();
|
{
|
||||||
|
InstanceData = await GetInstanceData(userList), Emojis = await GetEmojis(userList)
|
||||||
|
};
|
||||||
|
|
||||||
|
return userList.Select(p => Render(p, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UserRendererDto
|
private async Task<Dictionary<string, List<EmojiResponse>>> GetEmojis(ICollection<User> users)
|
||||||
{
|
{
|
||||||
public List<Instance>? InstanceData;
|
var ids = users.SelectMany(p => p.Emojis).ToList();
|
||||||
|
if (ids.Count == 0) return users.ToDictionary<User, string, List<EmojiResponse>>(p => p.Id, _ => []);
|
||||||
|
|
||||||
|
var emoji = await db.Emojis
|
||||||
|
.Where(p => ids.Contains(p.Id))
|
||||||
|
.Select(p => new EmojiResponse
|
||||||
|
{
|
||||||
|
Id = p.Id,
|
||||||
|
Name = p.Name,
|
||||||
|
Uri = p.Uri,
|
||||||
|
Aliases = p.Aliases,
|
||||||
|
Category = p.Category,
|
||||||
|
PublicUrl = p.PublicUrl,
|
||||||
|
License = p.License,
|
||||||
|
Sensitive = p.Sensitive
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return users.ToDictionary(p => p.Id, p => emoji.Where(e => p.Emojis.Contains(e.Id)).ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UserRendererDto
|
||||||
|
{
|
||||||
|
public required List<Instance> InstanceData;
|
||||||
|
public required Dictionary<string, List<EmojiResponse>> Emojis;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,13 +5,14 @@
|
||||||
@code {
|
@code {
|
||||||
[Parameter] [EditorRequired] public required string? Text { get; set; }
|
[Parameter] [EditorRequired] public required string? Text { get; set; }
|
||||||
[Parameter] public List<EmojiResponse> Emoji { get; set; } = [];
|
[Parameter] public List<EmojiResponse> Emoji { get; set; } = [];
|
||||||
|
[Parameter] public bool Simple { get; set; } = false;
|
||||||
private MarkupString TextBody { get; set; }
|
private MarkupString TextBody { get; set; }
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
if (Text != null)
|
if (Text != null)
|
||||||
{
|
{
|
||||||
TextBody = await MfmRenderer.RenderString(Text, Emoji);
|
TextBody = await MfmRenderer.RenderString(Text, Emoji, Simple);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@
|
||||||
{
|
{
|
||||||
if (Text != null)
|
if (Text != null)
|
||||||
{
|
{
|
||||||
TextBody = await MfmRenderer.RenderString(Text, Emoji);
|
TextBody = await MfmRenderer.RenderString(Text, Emoji, Simple);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -46,6 +46,23 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::deep {
|
||||||
|
.emoji.simple {
|
||||||
|
> img {
|
||||||
|
height: 1.25em;
|
||||||
|
vertical-align: -0.25em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep {
|
||||||
|
.emoji.simple {
|
||||||
|
> img:hover {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
::deep {
|
::deep {
|
||||||
.quote-node {
|
.quote-node {
|
||||||
/*Copying the appearance of -js*/
|
/*Copying the appearance of -js*/
|
||||||
|
|
|
@ -7,14 +7,7 @@
|
||||||
<div class="renote-info">
|
<div class="renote-info">
|
||||||
<span class="user">
|
<span class="user">
|
||||||
<Icon Name="Icons.Repeat"/> Renoted by
|
<Icon Name="Icons.Repeat"/> Renoted by
|
||||||
@if (NoteResponse.User.DisplayName != null)
|
<UserDisplayName User="@NoteResponse.User"/>
|
||||||
{
|
|
||||||
@NoteResponse.User.DisplayName
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@NoteResponse.User.Username
|
|
||||||
}
|
|
||||||
</span>
|
</span>
|
||||||
<span class="metadata">
|
<span class="metadata">
|
||||||
<NoteMetadata Visibility="NoteResponse.Visibility" InstanceName="@null" CreatedAt="DateTime.Parse(NoteResponse.CreatedAt)"/>
|
<NoteMetadata Visibility="NoteResponse.Visibility" InstanceName="@null" CreatedAt="DateTime.Parse(NoteResponse.CreatedAt)"/>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
@if (NoteBase.Cw != null)
|
@if (NoteBase.Cw != null)
|
||||||
{
|
{
|
||||||
<div class="cw">
|
<div class="cw">
|
||||||
<span class="cw-field">@NoteBase.Cw</span><button class="cw-button" @onclick="ToggleCw" @onclick:stopPropagation="true">Toggle CW</button>
|
<span class="cw-field"><MfmText Text="@NoteBase.Cw" Emoji="@NoteBase.Emoji" Simple="@true"/></span><button class="cw-button" @onclick="ToggleCw" @onclick:stopPropagation="true">Toggle CW</button>
|
||||||
</div>
|
</div>
|
||||||
<div hidden="@_cwToggle" class="note-body @(_cwToggle ? "hidden" : "") @(Indented ? "indent" : "")" @ref="Body">
|
<div hidden="@_cwToggle" class="note-body @(_cwToggle ? "hidden" : "") @(Indented ? "indent" : "")" @ref="Body">
|
||||||
<span>
|
<span>
|
||||||
|
|
|
@ -5,12 +5,7 @@
|
||||||
@inject ComposeService ComposeService
|
@inject ComposeService ComposeService
|
||||||
@inject SessionService Session;
|
@inject SessionService Session;
|
||||||
<div class="note-header">
|
<div class="note-header">
|
||||||
<NoteUserInfo
|
<NoteUserInfo User="@Note.User" Indented="Indented"/>
|
||||||
AvatarUrl="@Note.User.AvatarUrl"
|
|
||||||
DisplayName="@Note.User.DisplayName"
|
|
||||||
Username="@Note.User.Username"
|
|
||||||
Host="@Note.User.Host"
|
|
||||||
Indented="Indented"/>
|
|
||||||
<NoteMetadata
|
<NoteMetadata
|
||||||
Visibility="@Note.Visibility"
|
Visibility="@Note.Visibility"
|
||||||
InstanceName="@Note.User.InstanceName"
|
InstanceName="@Note.User.InstanceName"
|
||||||
|
|
|
@ -1,27 +1,25 @@
|
||||||
|
@using Iceshrimp.Shared.Schemas.Web
|
||||||
@inject NavigationManager Nav
|
@inject NavigationManager Nav
|
||||||
|
|
||||||
@if (Indented == false)
|
@if (Indented == false)
|
||||||
{
|
{
|
||||||
<img @onclick="OpenProfile" class="user-avatar" src="@AvatarUrl" alt="@(DisplayName ?? Username)" role="link"/>
|
<img @onclick="OpenProfile" class="user-avatar" src="@(User.AvatarUrl ?? $"/identicon/{User.Id}")" alt="@(User.DisplayName ?? User.Username)" role="link"/>
|
||||||
}
|
}
|
||||||
<div class="name-section">
|
<div class="name-section">
|
||||||
<span class="display-name">@(DisplayName ?? Username)</span>
|
<span class="display-name"><UserDisplayName User="@User"/></span>
|
||||||
<span class="identifier">@@@Username@(Host != null ? $"@{Host}" : "")</span>
|
<span class="identifier">@@@User.Username@(User.Host != null ? $"@{User.Host}" : "")</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] [EditorRequired] public required string AvatarUrl { get; set; }
|
[Parameter] [EditorRequired] public required UserResponse User { get; set; }
|
||||||
[Parameter] [EditorRequired] public required string? DisplayName { get; set; }
|
[Parameter] [EditorRequired] public required bool Indented { get; set; }
|
||||||
[Parameter] [EditorRequired] public required string Username { get; set; }
|
|
||||||
[Parameter] [EditorRequired] public required bool Indented { get; set; }
|
|
||||||
[Parameter] [EditorRequired] public required string? Host { get; set; }
|
|
||||||
|
|
||||||
private void OpenProfile()
|
private void OpenProfile()
|
||||||
{
|
{
|
||||||
var path = $"@{Username}";
|
var path = $"@{User.Username}";
|
||||||
if (Host?.Length > 0)
|
if (User.Host?.Length > 0)
|
||||||
{
|
{
|
||||||
path += $"@{Host}";
|
path += $"@{User.Host}";
|
||||||
}
|
}
|
||||||
|
|
||||||
Nav.NavigateTo($"/{path}");
|
Nav.NavigateTo($"/{path}");
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<div @onclick="() => OpenProfile(el.Username, el.Host)" class="detail-entry">
|
<div @onclick="() => OpenProfile(el.Username, el.Host)" class="detail-entry">
|
||||||
<img class="icon" src="@el.AvatarUrl"/>
|
<img class="icon" src="@el.AvatarUrl"/>
|
||||||
<div class="name-section">
|
<div class="name-section">
|
||||||
<div class="displayname">@(el.DisplayName ?? el.Username)</div>
|
<div class="displayname"><UserDisplayName User="@el"/></div>
|
||||||
<div class="username">@@@el.Username@(el.Host != null ? $"@{el.Host}" : "")</div>
|
<div class="username">@@@el.Username@(el.Host != null ? $"@{el.Host}" : "")</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<div @onclick="() => OpenProfile(el.Username, el.Host)" class="detail-entry">
|
<div @onclick="() => OpenProfile(el.Username, el.Host)" class="detail-entry">
|
||||||
<img class="icon" src="@el.AvatarUrl"/>
|
<img class="icon" src="@el.AvatarUrl"/>
|
||||||
<div class="name-section">
|
<div class="name-section">
|
||||||
<div class="displayname">@(el.DisplayName ?? el.Username)</div>
|
<div class="displayname"><UserDisplayName User="@el"/></div>
|
||||||
<div class="username">@@@el.Username@(el.Host != null ? $"@{el.Host}" : "")</div>
|
<div class="username">@@@el.Username@(el.Host != null ? $"@{el.Host}" : "")</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<div class="notification-body">
|
<div class="notification-body">
|
||||||
@if (NotificationResponse is { User: not null })
|
@if (NotificationResponse is { User: not null })
|
||||||
{
|
{
|
||||||
<span @onclick="OpenProfile" class="display-name">@(NotificationResponse.User.DisplayName ?? NotificationResponse.User.Username)</span>
|
<span @onclick="OpenProfile" class="display-name"><UserDisplayName User="@NotificationResponse.User"/></span>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (NotificationResponse is { Note: not null, Type: "like", User: not null })
|
@if (NotificationResponse is { Note: not null, Type: "like", User: not null })
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
@if (UserProfile.Bio != null)
|
@if (UserProfile.Bio != null)
|
||||||
{
|
{
|
||||||
<div class="bio">
|
<div class="bio">
|
||||||
<MfmText Text="@UserProfile.Bio"/>
|
<MfmText Text="@UserProfile.Bio" Emoji="@Emojis"/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="data">
|
<div class="data">
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
@foreach (var field in UserProfile.Fields)
|
@foreach (var field in UserProfile.Fields)
|
||||||
{
|
{
|
||||||
<ProfileInfoField Field="field"/>
|
<ProfileInfoField Field="field" Emojis="@Emojis"/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -63,4 +63,5 @@
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] [EditorRequired] public required UserProfileResponse UserProfile { get; set; }
|
[Parameter] [EditorRequired] public required UserProfileResponse UserProfile { get; set; }
|
||||||
|
[Parameter] [EditorRequired] public required List<EmojiResponse> Emojis { get; set; }
|
||||||
}
|
}
|
|
@ -6,13 +6,14 @@
|
||||||
{
|
{
|
||||||
<Icon class="verified" Name="Icons.SealCheck" Size="1.3em"/>
|
<Icon class="verified" Name="Icons.SealCheck" Size="1.3em"/>
|
||||||
}
|
}
|
||||||
@Field.Name
|
<MfmText Text="@Field.Name.Split('\n')[0]" Emoji="@Emojis" Simple="@true"></MfmText>
|
||||||
</span>
|
</span>
|
||||||
<span class="field-value">
|
<span class="field-value">
|
||||||
<MfmText Text="@Field.Value"></MfmText>
|
<MfmText Text="@Field.Value.Split('\n')[0]" Emoji="@Emojis" Simple="@true"></MfmText>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] [EditorRequired] public required UserProfileField Field { get; set; }
|
[Parameter] [EditorRequired] public required UserProfileField Field { get; set; }
|
||||||
|
[Parameter] [EditorRequired] public required List<EmojiResponse> Emojis { get; set; }
|
||||||
}
|
}
|
|
@ -13,7 +13,7 @@
|
||||||
<div @onclick="() => OpenProfile(el.Username, el.Host)" class="detail-entry">
|
<div @onclick="() => OpenProfile(el.Username, el.Host)" class="detail-entry">
|
||||||
<img class="icon" src="@el.AvatarUrl"/>
|
<img class="icon" src="@el.AvatarUrl"/>
|
||||||
<div class="name-section">
|
<div class="name-section">
|
||||||
<div class="displayname">@(el.DisplayName ?? el.Username)</div>
|
<div class="displayname"><UserDisplayName User="@el"/></div>
|
||||||
<div class="username">@@@el.Username@(el.Host != null ? $"@{el.Host}" : "")</div>
|
<div class="username">@@@el.Username@(el.Host != null ? $"@{el.Host}" : "")</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
13
Iceshrimp.Frontend/Components/UserDisplayName.razor
Normal file
13
Iceshrimp.Frontend/Components/UserDisplayName.razor
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
@using Iceshrimp.Shared.Schemas.Web
|
||||||
|
@if (User.DisplayName != null && User.Emojis.Count != 0)
|
||||||
|
{
|
||||||
|
<MfmText Text="@User.DisplayName.Split('\n')[0]" Emoji="@User.Emojis" Simple="@true"/>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@(User.DisplayName ?? User.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter] [EditorRequired] public required UserResponse User { get; set; }
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="name-section">
|
<div class="name-section">
|
||||||
<div class="name">
|
<div class="name">
|
||||||
@(User.DisplayName ?? User.Username)
|
<UserDisplayName User="@User"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="identifier">
|
<div class="identifier">
|
||||||
@@@User.Username
|
@@@User.Username
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
@if (UserProfile.Bio != null)
|
@if (UserProfile.Bio != null)
|
||||||
{
|
{
|
||||||
<div class="bio">
|
<div class="bio">
|
||||||
<MfmText Text="@UserProfile.Bio"/>
|
<MfmText Text="@UserProfile.Bio" Emoji="@User.Emojis"/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,18 +8,18 @@ namespace Iceshrimp.Frontend.Core.Miscellaneous;
|
||||||
|
|
||||||
public static class MfmRenderer
|
public static class MfmRenderer
|
||||||
{
|
{
|
||||||
public static async Task<MarkupString> RenderString(string text, List<EmojiResponse> emoji)
|
public static async Task<MarkupString> RenderString(string text, List<EmojiResponse> emoji, bool simple = false)
|
||||||
{
|
{
|
||||||
var res = Mfm.parse(text);
|
var res = simple ? Mfm.parseSimple(text) : Mfm.parse(text);
|
||||||
var context = BrowsingContext.New();
|
var context = BrowsingContext.New();
|
||||||
var document = await context.OpenNewAsync();
|
var document = await context.OpenNewAsync();
|
||||||
var renderedMfm = RenderMultipleNodes(res, document, emoji);
|
var renderedMfm = RenderMultipleNodes(res, document, emoji, simple);
|
||||||
var html = renderedMfm.ToHtml();
|
var html = renderedMfm.ToHtml();
|
||||||
return new MarkupString(html);
|
return new MarkupString(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static INode RenderMultipleNodes(
|
private static INode RenderMultipleNodes(
|
||||||
IEnumerable<MfmNodeTypes.MfmNode> nodes, IDocument document, List<EmojiResponse> emoji
|
IEnumerable<MfmNodeTypes.MfmNode> nodes, IDocument document, List<EmojiResponse> emoji, bool simple
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var el = document.CreateElement("span");
|
var el = document.CreateElement("span");
|
||||||
|
@ -29,7 +29,7 @@ public static class MfmRenderer
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
el.AppendNodes(RenderNode(node, document, emoji));
|
el.AppendNodes(RenderNode(node, document, emoji, simple));
|
||||||
}
|
}
|
||||||
catch (NotImplementedException e)
|
catch (NotImplementedException e)
|
||||||
{
|
{
|
||||||
|
@ -42,7 +42,7 @@ public static class MfmRenderer
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static INode RenderNode(MfmNodeTypes.MfmNode node, IDocument document, List<EmojiResponse> emoji)
|
private static INode RenderNode(MfmNodeTypes.MfmNode node, IDocument document, List<EmojiResponse> emoji, bool simple)
|
||||||
{
|
{
|
||||||
// Hard wrap makes this impossible to read
|
// Hard wrap makes this impossible to read
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
|
@ -55,7 +55,7 @@ public static class MfmRenderer
|
||||||
MfmNodeTypes.MfmSearchNode mfmSearchNode => throw new NotImplementedException($"{mfmSearchNode.GetType()}"),
|
MfmNodeTypes.MfmSearchNode mfmSearchNode => throw new NotImplementedException($"{mfmSearchNode.GetType()}"),
|
||||||
MfmNodeTypes.MfmBlockNode mfmBlockNode => throw new NotImplementedException($"{mfmBlockNode.GetType()}"),
|
MfmNodeTypes.MfmBlockNode mfmBlockNode => throw new NotImplementedException($"{mfmBlockNode.GetType()}"),
|
||||||
MfmNodeTypes.MfmBoldNode mfmBoldNode => MfmBoldNode(mfmBoldNode, document),
|
MfmNodeTypes.MfmBoldNode mfmBoldNode => MfmBoldNode(mfmBoldNode, document),
|
||||||
MfmNodeTypes.MfmEmojiCodeNode mfmEmojiCodeNode => MfmEmojiCodeNode(mfmEmojiCodeNode, document, emoji),
|
MfmNodeTypes.MfmEmojiCodeNode mfmEmojiCodeNode => MfmEmojiCodeNode(mfmEmojiCodeNode, document, emoji, simple),
|
||||||
MfmNodeTypes.MfmFnNode mfmFnNode => throw new NotImplementedException($"{mfmFnNode.GetType()}"),
|
MfmNodeTypes.MfmFnNode mfmFnNode => throw new NotImplementedException($"{mfmFnNode.GetType()}"),
|
||||||
MfmNodeTypes.MfmHashtagNode mfmHashtagNode => MfmHashtagNode(mfmHashtagNode, document),
|
MfmNodeTypes.MfmHashtagNode mfmHashtagNode => MfmHashtagNode(mfmHashtagNode, document),
|
||||||
MfmNodeTypes.MfmInlineCodeNode mfmInlineCodeNode => MfmInlineCodeNode(mfmInlineCodeNode, document),
|
MfmNodeTypes.MfmInlineCodeNode mfmInlineCodeNode => MfmInlineCodeNode(mfmInlineCodeNode, document),
|
||||||
|
@ -79,7 +79,7 @@ public static class MfmRenderer
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
rendered.AppendNodes(RenderNode(childNode, document, emoji));
|
rendered.AppendNodes(RenderNode(childNode, document, emoji, simple));
|
||||||
}
|
}
|
||||||
catch (NotImplementedException e)
|
catch (NotImplementedException e)
|
||||||
{
|
{
|
||||||
|
@ -157,11 +157,11 @@ public static class MfmRenderer
|
||||||
}
|
}
|
||||||
|
|
||||||
private static INode MfmEmojiCodeNode(
|
private static INode MfmEmojiCodeNode(
|
||||||
MfmNodeTypes.MfmEmojiCodeNode node, IDocument document, List<EmojiResponse> emojiList
|
MfmNodeTypes.MfmEmojiCodeNode node, IDocument document, List<EmojiResponse> emojiList, bool simple
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var el = document.CreateElement("span");
|
var el = document.CreateElement("span");
|
||||||
el.ClassName = "emoji";
|
el.ClassName = simple ? "emoji simple" : "emoji";
|
||||||
|
|
||||||
var emoji = emojiList.Find(p => p.Name == node.Name);
|
var emoji = emojiList.Find(p => p.Name == node.Name);
|
||||||
if (emoji is null)
|
if (emoji is null)
|
||||||
|
@ -173,6 +173,7 @@ public static class MfmRenderer
|
||||||
var image = document.CreateElement("img");
|
var image = document.CreateElement("img");
|
||||||
image.SetAttribute("src", emoji.PublicUrl);
|
image.SetAttribute("src", emoji.PublicUrl);
|
||||||
image.SetAttribute("alt", node.Name);
|
image.SetAttribute("alt", node.Name);
|
||||||
|
image.SetAttribute("title", $":{emoji.Name}:");
|
||||||
el.AppendChild(image);
|
el.AppendChild(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
Token = res.Token!,
|
Token = res.Token!,
|
||||||
Host = res.User.Host,
|
Host = res.User.Host,
|
||||||
IsAdmin = res.IsAdmin ?? false,
|
IsAdmin = res.IsAdmin ?? false,
|
||||||
|
Emojis = res.User.Emojis,
|
||||||
MovedTo = res.User.MovedTo
|
MovedTo = res.User.MovedTo
|
||||||
});
|
});
|
||||||
SessionService.SetSession(res.User.Id);
|
SessionService.SetSession(res.User.Id);
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="name-section">
|
<div class="name-section">
|
||||||
<div class="name">
|
<div class="name">
|
||||||
@(UserResponse.DisplayName ?? UserResponse.Username)
|
<UserDisplayName User="@UserResponse"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="identifier">
|
<div class="identifier">
|
||||||
@@@UserResponse.Username
|
@@@UserResponse.Username
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
</div>
|
</div>
|
||||||
<FollowButton User="UserResponse" UserProfile="Profile"/>
|
<FollowButton User="UserResponse" UserProfile="Profile"/>
|
||||||
</div>
|
</div>
|
||||||
<ProfileInfo UserProfile="Profile"/>
|
<ProfileInfo Emojis="@UserResponse.Emojis" UserProfile="Profile"/>
|
||||||
</div>
|
</div>
|
||||||
@if (UserNotes.Count > 0)
|
@if (UserNotes.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -188,6 +188,7 @@ module private MfmParser =
|
||||||
// References
|
// References
|
||||||
let node, nodeRef = createParserForwardedToRef ()
|
let node, nodeRef = createParserForwardedToRef ()
|
||||||
let inlineNode, inlineNodeRef = createParserForwardedToRef ()
|
let inlineNode, inlineNodeRef = createParserForwardedToRef ()
|
||||||
|
let simple, simpleRef = createParserForwardedToRef ()
|
||||||
|
|
||||||
let seqFlatten items =
|
let seqFlatten items =
|
||||||
seq {
|
seq {
|
||||||
|
@ -375,6 +376,11 @@ module private MfmParser =
|
||||||
fnNode
|
fnNode
|
||||||
charNode ]
|
charNode ]
|
||||||
|
|
||||||
|
let simpleNodeSeq =
|
||||||
|
[ plainNode
|
||||||
|
emojiCodeNode
|
||||||
|
charNode ]
|
||||||
|
|
||||||
//TODO: still missing: FnNode
|
//TODO: still missing: FnNode
|
||||||
|
|
||||||
let blockNodeSeq =
|
let blockNodeSeq =
|
||||||
|
@ -386,9 +392,13 @@ module private MfmParser =
|
||||||
do nodeRef.Value <- choice <| seqAttempt (seqFlatten <| nodeSeq)
|
do nodeRef.Value <- choice <| seqAttempt (seqFlatten <| nodeSeq)
|
||||||
|
|
||||||
do inlineNodeRef.Value <- choice <| (seqAttempt inlineNodeSeq) |>> fun v -> v :?> MfmInlineNode
|
do inlineNodeRef.Value <- choice <| (seqAttempt inlineNodeSeq) |>> fun v -> v :?> MfmInlineNode
|
||||||
|
|
||||||
|
do simpleRef.Value <- choice <| seqAttempt simpleNodeSeq
|
||||||
|
|
||||||
// Final parse command
|
// Final parse command
|
||||||
let parse = spaces >>. manyTill node eof .>> spaces
|
let parse = spaces >>. manyTill node eof .>> spaces
|
||||||
|
|
||||||
|
let parseSimple = spaces >>. manyTill simple eof .>> spaces
|
||||||
|
|
||||||
open MfmParser
|
open MfmParser
|
||||||
|
|
||||||
|
@ -397,3 +407,8 @@ module Mfm =
|
||||||
match runParserOnString parse 0 "" str with
|
match runParserOnString parse 0 "" str with
|
||||||
| Success(result, _, _) -> aggregateText result
|
| Success(result, _, _) -> aggregateText result
|
||||||
| Failure(s, _, _) -> failwith $"Failed to parse MFM: {s}"
|
| Failure(s, _, _) -> failwith $"Failed to parse MFM: {s}"
|
||||||
|
|
||||||
|
let parseSimple str =
|
||||||
|
match runParserOnString parseSimple 0 "" str with
|
||||||
|
| Success(result, _, _) -> aggregateText result
|
||||||
|
| Failure(s, _, _) -> failwith $"Failed to parse MFM: {s}"
|
||||||
|
|
|
@ -14,5 +14,7 @@ public class UserResponse
|
||||||
public required string? InstanceName { get; set; }
|
public required string? InstanceName { get; set; }
|
||||||
public required string? InstanceIconUrl { get; set; }
|
public required string? InstanceIconUrl { get; set; }
|
||||||
|
|
||||||
|
public List<EmojiResponse> Emojis { get; set; } = [];
|
||||||
|
|
||||||
[JI(Condition = WhenWritingNull)] public string? MovedTo { get; set; }
|
[JI(Condition = WhenWritingNull)] public string? MovedTo { get; set; }
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue