[frontend] Code cleanup
This commit is contained in:
parent
791252ccd4
commit
03fc824d34
16 changed files with 341 additions and 328 deletions
|
@ -1,4 +1,5 @@
|
||||||
@using Iceshrimp.Shared.Schemas
|
@using Iceshrimp.Shared.Schemas
|
||||||
|
@* ReSharper disable once RedundantUsingDirective *@
|
||||||
@using Iceshrimp.Frontend.Components.Note
|
@using Iceshrimp.Frontend.Components.Note
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
<div class="note">
|
<div class="note">
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
@using AngleSharp.Dom
|
|
||||||
@using Iceshrimp.Assets.PhosphorIcons
|
@using Iceshrimp.Assets.PhosphorIcons
|
||||||
@using Iceshrimp.Frontend.Core.Services
|
@using Iceshrimp.Frontend.Core.Services
|
||||||
@using Iceshrimp.Shared.Schemas
|
@using Iceshrimp.Shared.Schemas
|
||||||
|
@ -9,15 +8,17 @@
|
||||||
@inject IJSRuntime Js
|
@inject IJSRuntime Js
|
||||||
@inject ApiService ApiService
|
@inject ApiService ApiService
|
||||||
@inject ComposeService ComposeService
|
@inject ComposeService ComposeService
|
||||||
|
@inject SessionService SessionService
|
||||||
@inject IStringLocalizer<Localization> Loc;
|
@inject IStringLocalizer<Localization> Loc;
|
||||||
@inject SessionService SessionService
|
|
||||||
<dialog class="compose" @ref="Dialog">
|
<dialog class="compose" @ref="Dialog">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<button @onclick="CloseDialog">
|
<button @onclick="CloseDialog">
|
||||||
<Icon Name="Icons.X"/>
|
<Icon Name="Icons.X"/>
|
||||||
</button>
|
</button>
|
||||||
<Dropdown TBind="NoteVisibility" Elements="@DropDownCreate()" @bind-Value="NoteDraft.Visibility"/>
|
<Dropdown TBind="NoteVisibility" Elements="@DropDownCreate()" @bind-Value="NoteDraft.Visibility"/>
|
||||||
<button @onclick="SendNote" class="post-btn">@Loc["ComposePost"]<Icon Name="Icons.PaperPlaneRight"/></button>
|
<button @onclick="SendNote" class="post-btn">
|
||||||
|
@Loc["ComposePost"]<Icon Name="Icons.PaperPlaneRight"/>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@if (ReplyOrQuote != null)
|
@if (ReplyOrQuote != null)
|
||||||
{
|
{
|
||||||
|
@ -57,18 +58,19 @@
|
||||||
private string? TextPlaceholder { get; set; }
|
private string? TextPlaceholder { get; set; }
|
||||||
private EmojiPicker EmojiPicker { get; set; } = null!;
|
private EmojiPicker EmojiPicker { get; set; } = null!;
|
||||||
private ElementReference Textarea { get; set; }
|
private ElementReference Textarea { get; set; }
|
||||||
private NoteCreateRequest NoteDraft { get; set; } = new NoteCreateRequest
|
|
||||||
|
private NoteCreateRequest NoteDraft { get; set; } = new NoteCreateRequest
|
||||||
{
|
{
|
||||||
Text = "",
|
Text = "",
|
||||||
Visibility = NoteVisibility.Followers, // FIXME: Default to visibilty in settings
|
Visibility = NoteVisibility.Followers, // FIXME: Default to visibilty in settings
|
||||||
Cw = null
|
Cw = null
|
||||||
};
|
};
|
||||||
|
|
||||||
private Dictionary<string, string> AvailablePlaceholders { get; set; } = new()
|
private Dictionary<string, string> AvailablePlaceholders { get; set; } = new()
|
||||||
{
|
{
|
||||||
{ "default", "What's on your mind?" },
|
{ "default", "What's on your mind?" },
|
||||||
{ "reply", "Reply goes here!" },
|
{ "reply", "Reply goes here!" },
|
||||||
{ "quote", "Quote this post!" }
|
{ "quote", "Quote this post!" }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RenderFragment DropdownIcon(NoteVisibility vis)
|
RenderFragment DropdownIcon(NoteVisibility vis)
|
||||||
|
@ -99,7 +101,12 @@
|
||||||
{
|
{
|
||||||
return Enum.GetValues<NoteVisibility>()
|
return Enum.GetValues<NoteVisibility>()
|
||||||
.Select(vis =>
|
.Select(vis =>
|
||||||
new DropdownElement<NoteVisibility> { Icon = DropdownIcon(vis), Content = DropdownContent(vis), Selection = vis })
|
new DropdownElement<NoteVisibility>
|
||||||
|
{
|
||||||
|
Icon = DropdownIcon(vis),
|
||||||
|
Content = DropdownContent(vis),
|
||||||
|
Selection = vis
|
||||||
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +131,7 @@
|
||||||
{
|
{
|
||||||
NoteDraft.Text += $"@{el} ";
|
NoteDraft.Text += $"@{el} ";
|
||||||
}
|
}
|
||||||
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
else if (quote != null)
|
else if (quote != null)
|
||||||
|
@ -149,14 +157,15 @@
|
||||||
List<string> mentions = [];
|
List<string> mentions = [];
|
||||||
if (noteBase.User.Id != SessionService.Current!.Id)
|
if (noteBase.User.Id != SessionService.Current!.Id)
|
||||||
{
|
{
|
||||||
|
|
||||||
var userMention = noteBase.User.Username;
|
var userMention = noteBase.User.Username;
|
||||||
if (noteBase.User.Host != null)
|
if (noteBase.User.Host != null)
|
||||||
{
|
{
|
||||||
userMention += $"@{noteBase.User.Host}";
|
userMention += $"@{noteBase.User.Host}";
|
||||||
}
|
}
|
||||||
|
|
||||||
mentions.Add(userMention);
|
mentions.Add(userMention);
|
||||||
}
|
}
|
||||||
|
|
||||||
var current = $"@{SessionService.Current.Username}@{SessionService.Current.Host}";
|
var current = $"@{SessionService.Current.Username}@{SessionService.Current.Host}";
|
||||||
var mfmNodes = Mfm.parse(noteBase.Text);
|
var mfmNodes = Mfm.parse(noteBase.Text);
|
||||||
foreach (var node in mfmNodes)
|
foreach (var node in mfmNodes)
|
||||||
|
@ -166,6 +175,7 @@
|
||||||
mentions.Add(mentionNode.Acct);
|
mentions.Add(mentionNode.Acct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mentions = mentions.Distinct().ToList();
|
mentions = mentions.Distinct().ToList();
|
||||||
mentions.Remove(current);
|
mentions.Remove(current);
|
||||||
return mentions;
|
return mentions;
|
||||||
|
@ -173,9 +183,14 @@
|
||||||
|
|
||||||
private void ResetState()
|
private void ResetState()
|
||||||
{
|
{
|
||||||
ReplyOrQuote = null;
|
ReplyOrQuote = null;
|
||||||
Attachments = new List<DriveFileResponse>();
|
Attachments = new List<DriveFileResponse>();
|
||||||
NoteDraft = new NoteCreateRequest { Text = "", Visibility = NoteVisibility.Followers, Cw = null };
|
NoteDraft = new NoteCreateRequest
|
||||||
|
{
|
||||||
|
Text = "",
|
||||||
|
Visibility = NoteVisibility.Followers,
|
||||||
|
Cw = null
|
||||||
|
};
|
||||||
TextPlaceholder = AvailablePlaceholders["default"];
|
TextPlaceholder = AvailablePlaceholders["default"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,15 +235,13 @@
|
||||||
private void ToggleEmojiPicker()
|
private void ToggleEmojiPicker()
|
||||||
{
|
{
|
||||||
EmojiPicker.Toggle();
|
EmojiPicker.Toggle();
|
||||||
Console.WriteLine("showing picker");
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task AddEmoji(EmojiResponse emoji)
|
private async Task AddEmoji(EmojiResponse emoji)
|
||||||
{
|
{
|
||||||
var pos = await _module.InvokeAsync<int>("getSelectionStart");
|
var pos = await _module.InvokeAsync<int>("getSelectionStart");
|
||||||
var text = NoteDraft.Text;
|
var text = NoteDraft.Text;
|
||||||
var emojiString = $":{emoji.Name}";
|
var emojiString = $":{emoji.Name}";
|
||||||
NoteDraft.Text = text.Insert(pos, emojiString);
|
NoteDraft.Text = text.Insert(pos, emojiString);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
|
|
@ -3,38 +3,41 @@
|
||||||
@using Ljbc1994.Blazor.IntersectionObserver.API
|
@using Ljbc1994.Blazor.IntersectionObserver.API
|
||||||
@using Ljbc1994.Blazor.IntersectionObserver.Components
|
@using Ljbc1994.Blazor.IntersectionObserver.Components
|
||||||
@inject IJSRuntime Js
|
@inject IJSRuntime Js
|
||||||
@if(_init) // FIXME: We need to wait for the Component to render once before initializing the Intersection Observer.
|
@if (_init) // FIXME: We need to wait for the Component to render once before initializing the Intersection Observer.
|
||||||
// With the <IntersectionObserver> Component this is AFAIK only possible by not rendering it until then.
|
// With the <IntersectionObserver> Component this is AFAIK only possible by not rendering it until then.
|
||||||
// The proper fix for this is to change to the Service Pattern.
|
// The proper fix for this is to change to the Service Pattern.
|
||||||
// But that requires the IntersectionObserver Library to be modified to return what element an observation update is for.
|
// But that requires the IntersectionObserver Library to be modified to return what element an observation update is for.
|
||||||
{
|
{
|
||||||
<IntersectionObserve Options="new IntersectionObserverOptions(){ Root = Scroller, RootMargin = margin}" OnChange="@(entry => Change(entry))">
|
<IntersectionObserve Options="new IntersectionObserverOptions() { Root = Scroller, RootMargin = Margin }" OnChange="@(entry => Change(entry))">
|
||||||
<div class="tgt" @ref="context.Ref.Current">
|
<div class="tgt" @ref="context.Ref.Current">
|
||||||
@if (_isIntersecting)
|
@if (_isIntersecting)
|
||||||
{
|
{
|
||||||
<TimelineNote Note="Note" />
|
<TimelineNote Note="Note"/>
|
||||||
}else {
|
}
|
||||||
<div class="placeholder" style="height: @(Height ?? 150)px"></div>
|
else
|
||||||
}
|
{
|
||||||
</div>
|
<div class="placeholder" style="height: @(Height ?? 150)px"></div>
|
||||||
</IntersectionObserve>
|
}
|
||||||
|
</div>
|
||||||
|
</IntersectionObserve>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter][EditorRequired] public required NoteResponse Note { get; set; }
|
[Parameter] [EditorRequired] public required NoteResponse Note { get; set; }
|
||||||
[Parameter][EditorRequired] public ElementReference Scroller { get; set; }
|
[Parameter] [EditorRequired] public ElementReference Scroller { get; set; }
|
||||||
private IJSObjectReference? _module;
|
|
||||||
private int? Height { get; set; } = null;
|
private const string Margin = "200%";
|
||||||
private bool _isIntersecting = true;
|
private IJSObjectReference? _module;
|
||||||
private string margin = "200%";
|
private int? Height { get; set; }
|
||||||
private bool _init = false;
|
private bool _isIntersecting = true;
|
||||||
|
private bool _init;
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
_module = await Js.InvokeAsync<IJSObjectReference>("import",
|
_module = await Js.InvokeAsync<IJSObjectReference>("import",
|
||||||
"./Components/LazyNote.razor.js");
|
"./Components/LazyNote.razor.js");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,10 +47,9 @@
|
||||||
{
|
{
|
||||||
_init = true;
|
_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Change (IntersectionObserverEntry entry)
|
private async Task Change(IntersectionObserverEntry entry)
|
||||||
{
|
{
|
||||||
if (entry.IsIntersecting == false)
|
if (entry.IsIntersecting == false)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
@using AngleSharp.Dom
|
|
||||||
@using Iceshrimp.Frontend.Core.Services
|
@using Iceshrimp.Frontend.Core.Services
|
||||||
@using Iceshrimp.Shared.Schemas
|
@using Iceshrimp.Shared.Schemas
|
||||||
@inject ApiService ApiService;
|
@inject ApiService ApiService;
|
||||||
|
@ -79,7 +78,6 @@
|
||||||
Note.Reactions[index].Count++;
|
Note.Reactions[index].Count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +119,7 @@
|
||||||
|
|
||||||
public void Reply()
|
public void Reply()
|
||||||
{
|
{
|
||||||
ComposeService.ComposeDialog?.OpenDialog(Note, null);
|
ComposeService.ComposeDialog?.OpenDialog(Note);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Renote()
|
public void Renote()
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
@using Iceshrimp.Assets.PhosphorIcons
|
@using Iceshrimp.Assets.PhosphorIcons
|
||||||
@using Iceshrimp.Frontend.Core.Services
|
|
||||||
@using Iceshrimp.Shared.Schemas
|
@using Iceshrimp.Shared.Schemas
|
||||||
<div class="note-footer">
|
<div class="note-footer">
|
||||||
@if (Reactions.Count > 0)
|
@if (Reactions.Count > 0)
|
||||||
|
@ -44,7 +43,7 @@
|
||||||
</button>
|
</button>
|
||||||
<button class="btn positioned" @onclick="ToggleEmojiPicker" @onclick:stopPropagation="true">
|
<button class="btn positioned" @onclick="ToggleEmojiPicker" @onclick:stopPropagation="true">
|
||||||
<Icon Name="Icons.Smiley" Size="1.3em"/>
|
<Icon Name="Icons.Smiley" Size="1.3em"/>
|
||||||
<EmojiPicker @ref="EmojiPicker" OnEmojiSelect="React" />
|
<EmojiPicker @ref="EmojiPicker" OnEmojiSelect="React"/>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn" @onclick="Quote" @onclick:stopPropagation="true">
|
<button class="btn" @onclick="Quote" @onclick:stopPropagation="true">
|
||||||
<Icon Name="Icons.Quotes" Size="1.3em"/>
|
<Icon Name="Icons.Quotes" Size="1.3em"/>
|
||||||
|
@ -60,7 +59,7 @@
|
||||||
[Parameter] [EditorRequired] public required bool IsLiked { get; set; }
|
[Parameter] [EditorRequired] public required bool IsLiked { get; set; }
|
||||||
[Parameter] [EditorRequired] public required int Renotes { get; set; }
|
[Parameter] [EditorRequired] public required int Renotes { get; set; }
|
||||||
[Parameter] public bool RenotePossible { get; set; }
|
[Parameter] public bool RenotePossible { get; set; }
|
||||||
private EmojiPicker EmojiPicker { get; set; }
|
private EmojiPicker? EmojiPicker { get; set; }
|
||||||
|
|
||||||
[CascadingParameter] NoteComponent NoteComponent { get; set; } = null!;
|
[CascadingParameter] NoteComponent NoteComponent { get; set; } = null!;
|
||||||
|
|
||||||
|
@ -86,12 +85,11 @@
|
||||||
|
|
||||||
private void ToggleEmojiPicker()
|
private void ToggleEmojiPicker()
|
||||||
{
|
{
|
||||||
EmojiPicker.Toggle();
|
EmojiPicker?.Toggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void React(EmojiResponse emoji)
|
private void React(EmojiResponse emoji)
|
||||||
{
|
{
|
||||||
NoteComponent.React(emoji.Name, true, url: emoji.PublicUrl);
|
NoteComponent.React(emoji.Name, true, url: emoji.PublicUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
@using Iceshrimp.Shared.Schemas
|
@using Iceshrimp.Shared.Schemas
|
||||||
|
@* ReSharper disable once RedundantUsingDirective *@
|
||||||
@using Iceshrimp.Frontend.Components.Note
|
@using Iceshrimp.Frontend.Components.Note
|
||||||
@using Iceshrimp.Frontend.Localization
|
@using Iceshrimp.Frontend.Localization
|
||||||
@using Microsoft.Extensions.Localization
|
@using Microsoft.Extensions.Localization
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
@using Iceshrimp.Frontend.Core.Miscellaneous
|
|
||||||
@using Iceshrimp.Frontend.Core.Services
|
|
||||||
@using Iceshrimp.Shared.Schemas
|
|
||||||
|
|
||||||
@if (_state == State.Init)
|
@if (_state == State.Init)
|
||||||
{
|
{
|
||||||
<div class="scroller">
|
<div class="scroller">
|
||||||
|
@ -26,4 +22,3 @@
|
||||||
@code {
|
@code {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,73 +7,74 @@ namespace Iceshrimp.Frontend.Components;
|
||||||
|
|
||||||
public partial class NotificationList : IAsyncDisposable
|
public partial class NotificationList : IAsyncDisposable
|
||||||
{
|
{
|
||||||
[Inject] private StreamingService StreamingService { get; set; } = null!;
|
[Inject] private StreamingService StreamingService { get; set; } = null!;
|
||||||
[Inject] private ApiService Api { get; set; } = null!;
|
[Inject] private ApiService Api { get; set; } = null!;
|
||||||
private List<NotificationResponse> Notifications { get; set; } = [];
|
private List<NotificationResponse> Notifications { get; set; } = [];
|
||||||
private State _state = State.Loading;
|
private State _state = State.Loading;
|
||||||
private string? _minId;
|
private string? _minId;
|
||||||
|
|
||||||
private enum State
|
private enum State
|
||||||
{
|
{
|
||||||
Loading,
|
Loading,
|
||||||
Error,
|
Error,
|
||||||
Init
|
Init
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task GetNotifications()
|
private async Task GetNotifications()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var res = await Api.Notifications.GetNotifications(new PaginationQuery());
|
var res = await Api.Notifications.GetNotifications(new PaginationQuery());
|
||||||
if (res.Count > 0)
|
if (res.Count > 0)
|
||||||
{
|
{
|
||||||
Notifications = res;
|
Notifications = res;
|
||||||
_minId = res.Last().Id;
|
_minId = res.Last().Id;
|
||||||
foreach (var el in res)
|
foreach (var el in res)
|
||||||
{
|
{
|
||||||
Console.WriteLine(el.Type);
|
Console.WriteLine(el.Type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_state = State.Init;
|
_state = State.Init;
|
||||||
}
|
}
|
||||||
catch (ApiException)
|
catch (ApiException)
|
||||||
{
|
{
|
||||||
_state = State.Error;
|
_state = State.Error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadMore()
|
private async Task LoadMore()
|
||||||
{
|
{
|
||||||
var pq = new PaginationQuery { MaxId = _minId, Limit = 20 };
|
var pq = new PaginationQuery { MaxId = _minId, Limit = 20 };
|
||||||
var res = await Api.Notifications.GetNotifications(pq);
|
var res = await Api.Notifications.GetNotifications(pq);
|
||||||
if (res.Count > 0)
|
if (res.Count > 0)
|
||||||
{
|
{
|
||||||
Notifications.AddRange(res);
|
Notifications.AddRange(res);
|
||||||
_minId = res.Last().Id;
|
_minId = res.Last().Id;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
StreamingService.Notification += OnNotification;
|
StreamingService.Notification += OnNotification;
|
||||||
await StreamingService.Connect();
|
await StreamingService.Connect();
|
||||||
await GetNotifications();
|
await GetNotifications();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnNotification(object? _, NotificationResponse notificationResponse)
|
private void OnNotification(object? _, NotificationResponse notificationResponse)
|
||||||
{
|
{
|
||||||
Notifications.Insert(0, notificationResponse);
|
Notifications.Insert(0, notificationResponse);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
StreamingService.Notification -= OnNotification;
|
StreamingService.Notification -= OnNotification;
|
||||||
|
|
||||||
await StreamingService.DisposeAsync();
|
await StreamingService.DisposeAsync();
|
||||||
}
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
@using Iceshrimp.Shared.Schemas
|
@using Iceshrimp.Shared.Schemas
|
||||||
|
@* ReSharper disable once RedundantUsingDirective *@
|
||||||
@using Iceshrimp.Frontend.Components.Note
|
@using Iceshrimp.Frontend.Components.Note
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
@using Iceshrimp.Shared.Schemas
|
@using Iceshrimp.Shared.Schemas
|
||||||
|
@* ReSharper disable once RedundantUsingDirective *@
|
||||||
@using Iceshrimp.Frontend.Components.Note
|
@using Iceshrimp.Frontend.Components.Note
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
<div class="note-container" @onclick="OpenNote" id="@Note.Id">
|
<div class="note-container" @onclick="OpenNote" id="@Note.Id">
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
@using System.Collections.Specialized
|
|
||||||
@using System.Runtime.InteropServices.JavaScript
|
|
||||||
@using Iceshrimp.Frontend.Core.Miscellaneous
|
|
||||||
@using Iceshrimp.Frontend.Core.Services
|
@using Iceshrimp.Frontend.Core.Services
|
||||||
@using Iceshrimp.Frontend.Core.Services.StateServicePatterns
|
@using Iceshrimp.Frontend.Core.Services.StateServicePatterns
|
||||||
@using Iceshrimp.Shared.Schemas
|
@using Iceshrimp.Shared.Schemas
|
||||||
|
@ -8,30 +5,33 @@
|
||||||
@using Ljbc1994.Blazor.IntersectionObserver.API
|
@using Ljbc1994.Blazor.IntersectionObserver.API
|
||||||
@inject IIntersectionObserverService ObserverService
|
@inject IIntersectionObserverService ObserverService
|
||||||
@inject IJSRuntime Js
|
@inject IJSRuntime Js
|
||||||
@inject StateService StateService
|
@inject StateService StateService
|
||||||
|
|
||||||
<div @ref="@Scroller" class="scroller">
|
<div @ref="@_scroller" class="scroller">
|
||||||
<div @ref="@_padTopRef" class="padding top" style="height: @(State.PadTop + "px")"></div>
|
<div @ref="@_padTopRef" class="padding top" style="height: @(State.PadTop + "px")"></div>
|
||||||
@if(_loadingTop){
|
@if (_loadingTop)
|
||||||
<div class="target">
|
{
|
||||||
Loading!
|
<div class="target">
|
||||||
</div>
|
Loading!
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
@foreach (var el in State.RenderedList)
|
@foreach (var el in State.RenderedList)
|
||||||
{
|
{
|
||||||
|
|
||||||
<div class="target" @ref="@Ref">
|
<div class="target" @ref="@Ref">
|
||||||
<TimelineNote @key="el.Id" Note="el"></TimelineNote>
|
<TimelineNote @key="el.Id" Note="el"></TimelineNote>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if(loadingBottom){
|
@if (_loadingBottom)
|
||||||
<div class="target">
|
{
|
||||||
Loading!
|
<div class="target">
|
||||||
</div>
|
Loading!
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="target"><span>The end!<button @onclick="Down">Load more!</button></span></div>
|
<div class="target">
|
||||||
|
<span>The end!<button @onclick="Down">Load more!</button></span>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
<div @ref="@_padBotRef" class="padding bottom" style="height: @(State.PadBottom + "px")">
|
<div @ref="@_padBotRef" class="padding bottom" style="height: @(State.PadBottom + "px")">
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,10 +53,10 @@
|
||||||
private bool _overscrollBottom = false;
|
private bool _overscrollBottom = false;
|
||||||
private ElementReference _padTopRef;
|
private ElementReference _padTopRef;
|
||||||
private ElementReference _padBotRef;
|
private ElementReference _padBotRef;
|
||||||
private ElementReference Scroller;
|
private ElementReference _scroller;
|
||||||
private bool _loadingTop = false;
|
private bool _loadingTop = false;
|
||||||
private bool loadingBottom = false;
|
private bool _loadingBottom = false;
|
||||||
public bool setScroll = false;
|
private bool _setScroll = false;
|
||||||
|
|
||||||
private ElementReference Ref
|
private ElementReference Ref
|
||||||
{
|
{
|
||||||
|
@ -68,18 +68,15 @@
|
||||||
|
|
||||||
private void InitialRender(string? id)
|
private void InitialRender(string? id)
|
||||||
{
|
{
|
||||||
var a = new List<NoteResponse>();
|
State.RenderedList = NoteResponseList.Count < _count ? NoteResponseList : NoteResponseList.GetRange(0, _count);
|
||||||
a = NoteResponseList.Count < _count ? NoteResponseList : NoteResponseList.GetRange(0, _count);
|
|
||||||
|
|
||||||
State.RenderedList = a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadOlder()
|
private async Task LoadOlder()
|
||||||
{
|
{
|
||||||
loadingBottom = true;
|
_loadingBottom = true;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
await ReachedEnd.InvokeAsync();
|
await ReachedEnd.InvokeAsync();
|
||||||
loadingBottom = false;
|
_loadingBottom = false;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +95,6 @@
|
||||||
StateService.VirtualScroller.SetState("home", State);
|
StateService.VirtualScroller.SetState("home", State);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task RemoveAbove(int amount)
|
private async Task RemoveAbove(int amount)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < amount; i++)
|
for (int i = 0; i < amount; i++)
|
||||||
|
@ -109,7 +105,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
State.RenderedList.RemoveRange(0, amount);
|
State.RenderedList.RemoveRange(0, amount);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Down()
|
private async Task Down()
|
||||||
|
@ -128,10 +123,8 @@
|
||||||
var heightChange = 0;
|
var heightChange = 0;
|
||||||
foreach (var el in a)
|
foreach (var el in a)
|
||||||
{
|
{
|
||||||
if (State.Height.ContainsKey(el.Id))
|
if (State.Height.TryGetValue(el.Id, out var value))
|
||||||
{
|
heightChange += value;
|
||||||
heightChange += State.Height[el.Id];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (State.PadBottom > 0) State.PadBottom -= heightChange;
|
if (State.PadBottom > 0) State.PadBottom -= heightChange;
|
||||||
|
@ -155,6 +148,7 @@
|
||||||
State.PadBottom += height;
|
State.PadBottom += height;
|
||||||
State.Height[State.RenderedList[i].Id] = height;
|
State.Height[State.RenderedList[i].Id] = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
var index = NoteResponseList.IndexOf(State.RenderedList.First());
|
var index = NoteResponseList.IndexOf(State.RenderedList.First());
|
||||||
var a = NoteResponseList.GetRange(index - updateCount, updateCount);
|
var a = NoteResponseList.GetRange(index - updateCount, updateCount);
|
||||||
var heightChange = 0;
|
var heightChange = 0;
|
||||||
|
@ -174,8 +168,6 @@
|
||||||
|
|
||||||
private async Task SetupObservers()
|
private async Task SetupObservers()
|
||||||
{
|
{
|
||||||
// Enabling root margin causes erratic virtual scroller behavior, i've not figured out why
|
|
||||||
var options = new IntersectionObserverOptions { RootMargin = "100%" };
|
|
||||||
OvrscrlObsvTop = await ObserverService.Create(OverscrollCallbackTop);
|
OvrscrlObsvTop = await ObserverService.Create(OverscrollCallbackTop);
|
||||||
OvrscrlObsvBottom = await ObserverService.Create(OverscrollCallbackBottom);
|
OvrscrlObsvBottom = await ObserverService.Create(OverscrollCallbackBottom);
|
||||||
|
|
||||||
|
@ -192,7 +184,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async void OverscrollCallbackTop(IList<IntersectionObserverEntry> list)
|
private async void OverscrollCallbackTop(IList<IntersectionObserverEntry> list)
|
||||||
{
|
{
|
||||||
var entry = list.First();
|
var entry = list.First();
|
||||||
|
@ -242,13 +233,13 @@
|
||||||
|
|
||||||
private async Task GetScrollTop()
|
private async Task GetScrollTop()
|
||||||
{
|
{
|
||||||
var scrollTop = await Module.InvokeAsync<float>("GetScrollTop", Scroller);
|
var scrollTop = await Module.InvokeAsync<float>("GetScrollTop", _scroller);
|
||||||
State.ScrollTop = scrollTop;
|
State.ScrollTop = scrollTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetScrollTop()
|
private async Task SetScrollTop()
|
||||||
{
|
{
|
||||||
await Module.InvokeVoidAsync("SetScrollTop", State.ScrollTop, Scroller);
|
await Module.InvokeVoidAsync("SetScrollTop", State.ScrollTop, _scroller);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
|
@ -256,9 +247,8 @@
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var virtualScrollerState = StateService.VirtualScroller.GetState("home");
|
var virtualScrollerState = StateService.VirtualScroller.GetState("home");
|
||||||
State = virtualScrollerState;
|
State = virtualScrollerState;
|
||||||
setScroll = true;
|
_setScroll = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (ArgumentException)
|
catch (ArgumentException)
|
||||||
{
|
{
|
||||||
|
@ -279,13 +269,12 @@
|
||||||
await SetupObservers();
|
await SetupObservers();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setScroll)
|
if (_setScroll)
|
||||||
{
|
{
|
||||||
await SetScrollTop();
|
await SetScrollTop();
|
||||||
setScroll = false;
|
_setScroll = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await SaveState();
|
await SaveState();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -3,176 +3,188 @@ using AngleSharp.Dom;
|
||||||
using Iceshrimp.Parsing;
|
using Iceshrimp.Parsing;
|
||||||
using Iceshrimp.Shared.Schemas;
|
using Iceshrimp.Shared.Schemas;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.FSharp.Core;
|
|
||||||
|
|
||||||
namespace Iceshrimp.Frontend.Core.Miscellaneous;
|
namespace Iceshrimp.Frontend.Core.Miscellaneous;
|
||||||
|
|
||||||
public 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)
|
||||||
{
|
{
|
||||||
var res = Mfm.parse(text);
|
var res = Mfm.parse(text);
|
||||||
var context = BrowsingContext.New();
|
var context = BrowsingContext.New();
|
||||||
var document = await context.OpenNewAsync();
|
var document = await context.OpenNewAsync();
|
||||||
var renderedMfm = MfmRenderer.RenderMultipleNodes(res, document, emoji);
|
var renderedMfm = RenderMultipleNodes(res, document, emoji);
|
||||||
var html = renderedMfm.ToHtml();
|
var html = renderedMfm.ToHtml();
|
||||||
return new MarkupString(html);
|
return new MarkupString(html);
|
||||||
}
|
}
|
||||||
public static INode RenderMultipleNodes(IEnumerable<MfmNodeTypes.MfmNode> nodes, IDocument document, List<EmojiResponse> emoji)
|
|
||||||
{
|
|
||||||
var el = document.CreateElement("span");
|
|
||||||
el.SetAttribute("mfm", "mfm");
|
|
||||||
el.ClassName = "mfm";
|
|
||||||
foreach (var node in nodes)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
el.AppendNodes(RenderNode(node, document, emoji));
|
|
||||||
}
|
|
||||||
catch (NotImplementedException e)
|
|
||||||
{
|
|
||||||
var fallback = document.CreateElement("span");
|
|
||||||
fallback.TextContent = $"[Node type <{e.Message}> not implemented]";
|
|
||||||
el.AppendNodes(fallback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return el;
|
private static INode RenderMultipleNodes(
|
||||||
}
|
IEnumerable<MfmNodeTypes.MfmNode> nodes, IDocument document, List<EmojiResponse> emoji
|
||||||
private static INode RenderNode(MfmNodeTypes.MfmNode node, IDocument document, List<EmojiResponse> emoji)
|
)
|
||||||
{
|
{
|
||||||
var rendered = node switch
|
var el = document.CreateElement("span");
|
||||||
{
|
el.SetAttribute("mfm", "mfm");
|
||||||
MfmNodeTypes.MfmCenterNode mfmCenterNode => throw new NotImplementedException($"{mfmCenterNode.GetType()}"),
|
el.ClassName = "mfm";
|
||||||
MfmNodeTypes.MfmCodeBlockNode mfmCodeBlockNode => MfmCodeBlockNode(mfmCodeBlockNode, document),
|
foreach (var node in nodes)
|
||||||
MfmNodeTypes.MfmMathBlockNode mfmMathBlockNode => throw new NotImplementedException($"{mfmMathBlockNode.GetType()}"),
|
{
|
||||||
MfmNodeTypes.MfmQuoteNode mfmQuoteNode => throw new NotImplementedException($"{mfmQuoteNode.GetType()}"),
|
try
|
||||||
MfmNodeTypes.MfmSearchNode mfmSearchNode => throw new NotImplementedException($"{mfmSearchNode.GetType()}"),
|
{
|
||||||
MfmNodeTypes.MfmBlockNode mfmBlockNode => throw new NotImplementedException($"{mfmBlockNode.GetType()}"),
|
el.AppendNodes(RenderNode(node, document, emoji));
|
||||||
MfmNodeTypes.MfmBoldNode mfmBoldNode => MfmBoldNode(mfmBoldNode, document),
|
}
|
||||||
MfmNodeTypes.MfmEmojiCodeNode mfmEmojiCodeNode => MfmEmojiCodeNode(mfmEmojiCodeNode, document, emoji),
|
catch (NotImplementedException e)
|
||||||
MfmNodeTypes.MfmFnNode mfmFnNode => throw new NotImplementedException($"{mfmFnNode.GetType()}"),
|
{
|
||||||
MfmNodeTypes.MfmHashtagNode mfmHashtagNode => throw new NotImplementedException($"{mfmHashtagNode.GetType()}"),
|
var fallback = document.CreateElement("span");
|
||||||
MfmNodeTypes.MfmInlineCodeNode mfmInlineCodeNode => MfmInlineCodeNode(mfmInlineCodeNode, document),
|
fallback.TextContent = $"[Node type <{e.Message}> not implemented]";
|
||||||
MfmNodeTypes.MfmItalicNode mfmItalicNode => MfmItalicNode(mfmItalicNode, document),
|
el.AppendNodes(fallback);
|
||||||
MfmNodeTypes.MfmLinkNode mfmLinkNode => MfmLinkNode(mfmLinkNode, document),
|
}
|
||||||
MfmNodeTypes.MfmMathInlineNode mfmMathInlineNode => throw new NotImplementedException($"{mfmMathInlineNode.GetType()}"),
|
}
|
||||||
MfmNodeTypes.MfmMentionNode mfmMentionNode => MfmMentionNode(mfmMentionNode, document),
|
|
||||||
MfmNodeTypes.MfmPlainNode mfmPlainNode => throw new NotImplementedException($"{mfmPlainNode.GetType()}"),
|
|
||||||
MfmNodeTypes.MfmSmallNode mfmSmallNode => throw new NotImplementedException($"{mfmSmallNode.GetType()}"),
|
|
||||||
MfmNodeTypes.MfmStrikeNode mfmStrikeNode => throw new NotImplementedException($"{mfmStrikeNode.GetType()}"),
|
|
||||||
MfmNodeTypes.MfmTextNode mfmTextNode => MfmTextNode(mfmTextNode, document),
|
|
||||||
MfmNodeTypes.MfmUrlNode mfmUrlNode => MfmUrlNode(mfmUrlNode, document),
|
|
||||||
MfmNodeTypes.MfmInlineNode mfmInlineNode => throw new NotImplementedException($"{mfmInlineNode.GetType()}"),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(node))
|
|
||||||
};
|
|
||||||
if (node.Children.Length > 0)
|
|
||||||
{
|
|
||||||
foreach (var childNode in node.Children)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
rendered.AppendNodes(RenderNode(childNode, document, emoji));
|
|
||||||
}
|
|
||||||
catch (NotImplementedException e)
|
|
||||||
{
|
|
||||||
var fallback = document.CreateElement("span");
|
|
||||||
fallback.TextContent = $"[Node type <{e.Message}> not implemented]";
|
|
||||||
rendered.AppendNodes(fallback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rendered;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static INode MfmCodeBlockNode(MfmNodeTypes.MfmCodeBlockNode node, IDocument document)
|
return el;
|
||||||
{
|
}
|
||||||
var el = document.CreateElement("pre");
|
|
||||||
var childEl = document.CreateElement("code");
|
|
||||||
childEl.TextContent = node.Code;
|
|
||||||
el.AppendChild(childEl);
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static INode MfmInlineCodeNode(MfmNodeTypes.MfmInlineCodeNode node, IDocument document)
|
private static INode RenderNode(MfmNodeTypes.MfmNode node, IDocument document, List<EmojiResponse> emoji)
|
||||||
{
|
{
|
||||||
var el = document.CreateElement("code");
|
var rendered = node switch
|
||||||
el.TextContent = node.Code;
|
{
|
||||||
return el;
|
MfmNodeTypes.MfmCenterNode mfmCenterNode => throw new NotImplementedException($"{mfmCenterNode.GetType()}"),
|
||||||
}
|
MfmNodeTypes.MfmCodeBlockNode mfmCodeBlockNode => MfmCodeBlockNode(mfmCodeBlockNode, document),
|
||||||
|
MfmNodeTypes.MfmMathBlockNode mfmMathBlockNode =>
|
||||||
|
throw new NotImplementedException($"{mfmMathBlockNode.GetType()}"),
|
||||||
|
MfmNodeTypes.MfmQuoteNode mfmQuoteNode => throw new NotImplementedException($"{mfmQuoteNode.GetType()}"),
|
||||||
|
MfmNodeTypes.MfmSearchNode mfmSearchNode => throw new NotImplementedException($"{mfmSearchNode.GetType()}"),
|
||||||
|
MfmNodeTypes.MfmBlockNode mfmBlockNode => throw new NotImplementedException($"{mfmBlockNode.GetType()}"),
|
||||||
|
MfmNodeTypes.MfmBoldNode mfmBoldNode => MfmBoldNode(mfmBoldNode, document),
|
||||||
|
MfmNodeTypes.MfmEmojiCodeNode mfmEmojiCodeNode => MfmEmojiCodeNode(mfmEmojiCodeNode, document, emoji),
|
||||||
|
MfmNodeTypes.MfmFnNode mfmFnNode => throw new NotImplementedException($"{mfmFnNode.GetType()}"),
|
||||||
|
MfmNodeTypes.MfmHashtagNode mfmHashtagNode =>
|
||||||
|
throw new NotImplementedException($"{mfmHashtagNode.GetType()}"),
|
||||||
|
MfmNodeTypes.MfmInlineCodeNode mfmInlineCodeNode => MfmInlineCodeNode(mfmInlineCodeNode, document),
|
||||||
|
MfmNodeTypes.MfmItalicNode mfmItalicNode => MfmItalicNode(mfmItalicNode, document),
|
||||||
|
MfmNodeTypes.MfmLinkNode mfmLinkNode => MfmLinkNode(mfmLinkNode, document),
|
||||||
|
MfmNodeTypes.MfmMathInlineNode mfmMathInlineNode =>
|
||||||
|
throw new NotImplementedException($"{mfmMathInlineNode.GetType()}"),
|
||||||
|
MfmNodeTypes.MfmMentionNode mfmMentionNode => MfmMentionNode(mfmMentionNode, document),
|
||||||
|
MfmNodeTypes.MfmPlainNode mfmPlainNode => throw new NotImplementedException($"{mfmPlainNode.GetType()}"),
|
||||||
|
MfmNodeTypes.MfmSmallNode mfmSmallNode => throw new NotImplementedException($"{mfmSmallNode.GetType()}"),
|
||||||
|
MfmNodeTypes.MfmStrikeNode mfmStrikeNode => throw new NotImplementedException($"{mfmStrikeNode.GetType()}"),
|
||||||
|
MfmNodeTypes.MfmTextNode mfmTextNode => MfmTextNode(mfmTextNode, document),
|
||||||
|
MfmNodeTypes.MfmUrlNode mfmUrlNode => MfmUrlNode(mfmUrlNode, document),
|
||||||
|
MfmNodeTypes.MfmInlineNode mfmInlineNode => throw new NotImplementedException($"{mfmInlineNode.GetType()}"),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(node))
|
||||||
|
};
|
||||||
|
if (node.Children.Length > 0)
|
||||||
|
{
|
||||||
|
foreach (var childNode in node.Children)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rendered.AppendNodes(RenderNode(childNode, document, emoji));
|
||||||
|
}
|
||||||
|
catch (NotImplementedException e)
|
||||||
|
{
|
||||||
|
var fallback = document.CreateElement("span");
|
||||||
|
fallback.TextContent = $"[Node type <{e.Message}> not implemented]";
|
||||||
|
rendered.AppendNodes(fallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static INode MfmLinkNode(MfmNodeTypes.MfmLinkNode node, IDocument document)
|
return rendered;
|
||||||
{
|
}
|
||||||
var el = document.CreateElement("a");
|
|
||||||
el.SetAttribute("href", node.Url);
|
|
||||||
el.ClassName = "link-node";
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static INode MfmItalicNode(MfmNodeTypes.MfmItalicNode node, IDocument document)
|
private static INode MfmCodeBlockNode(MfmNodeTypes.MfmCodeBlockNode node, IDocument document)
|
||||||
{
|
{
|
||||||
var el = document.CreateElement("span");
|
var el = document.CreateElement("pre");
|
||||||
el.SetAttribute("style", "font-style: italic");
|
var childEl = document.CreateElement("code");
|
||||||
return el;
|
childEl.TextContent = node.Code;
|
||||||
}
|
el.AppendChild(childEl);
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
private static INode MfmEmojiCodeNode(MfmNodeTypes.MfmEmojiCodeNode node, IDocument document, List<EmojiResponse> emojiList)
|
private static INode MfmInlineCodeNode(MfmNodeTypes.MfmInlineCodeNode node, IDocument document)
|
||||||
{
|
{
|
||||||
var el = document.CreateElement("span");
|
var el = document.CreateElement("code");
|
||||||
el.ClassName = "emoji";
|
el.TextContent = node.Code;
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
var emoji = emojiList.Find(p => p.Name == node.Name);
|
private static INode MfmLinkNode(MfmNodeTypes.MfmLinkNode node, IDocument document)
|
||||||
if (emoji is null)
|
{
|
||||||
{
|
var el = document.CreateElement("a");
|
||||||
el.TextContent = node.Name;
|
el.SetAttribute("href", node.Url);
|
||||||
}
|
el.ClassName = "link-node";
|
||||||
else
|
return el;
|
||||||
{
|
}
|
||||||
var image = document.CreateElement("img");
|
|
||||||
image.SetAttribute("src", emoji.PublicUrl);
|
|
||||||
image.SetAttribute("alt", node.Name);
|
|
||||||
el.AppendChild(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
return el;
|
private static INode MfmItalicNode(MfmNodeTypes.MfmItalicNode _, IDocument document)
|
||||||
}
|
{
|
||||||
private static INode MfmUrlNode(MfmNodeTypes.MfmUrlNode node, IDocument document)
|
var el = document.CreateElement("span");
|
||||||
{
|
el.SetAttribute("style", "font-style: italic");
|
||||||
var el = document.CreateElement("a");
|
return el;
|
||||||
el.SetAttribute("href", node.Url);
|
}
|
||||||
el.ClassName = "url-node";
|
|
||||||
el.TextContent = node.Url;
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static INode MfmBoldNode(MfmNodeTypes.MfmBoldNode node, IDocument document)
|
private static INode MfmEmojiCodeNode(
|
||||||
{
|
MfmNodeTypes.MfmEmojiCodeNode node, IDocument document, List<EmojiResponse> emojiList
|
||||||
var el = document.CreateElement("strong");
|
)
|
||||||
return el;
|
{
|
||||||
}
|
var el = document.CreateElement("span");
|
||||||
private static INode MfmTextNode(MfmNodeTypes.MfmTextNode node, IDocument document)
|
el.ClassName = "emoji";
|
||||||
{
|
|
||||||
var el = document.CreateElement("span");
|
|
||||||
el.TextContent = node.Text;
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
private static INode MfmMentionNode(MfmNodeTypes.MfmMentionNode node, IDocument document)
|
|
||||||
{
|
|
||||||
var link = document.CreateElement("a");
|
|
||||||
link.SetAttribute("href", $"/@{node.Acct}");
|
|
||||||
link.ClassName = "mention";
|
|
||||||
var userPart = document.CreateElement("span");
|
|
||||||
userPart.ClassName = "user";
|
|
||||||
userPart.TextContent = $"@{node.Username}";
|
|
||||||
link.AppendChild(userPart);
|
|
||||||
if (node.Host != null)
|
|
||||||
{
|
|
||||||
var hostPart = document.CreateElement("span");
|
|
||||||
hostPart.ClassName = "host";
|
|
||||||
hostPart.TextContent = $"@{node.Host.Value}";
|
|
||||||
link.AppendChild(hostPart);
|
|
||||||
}
|
|
||||||
|
|
||||||
return link;
|
var emoji = emojiList.Find(p => p.Name == node.Name);
|
||||||
}
|
if (emoji is null)
|
||||||
|
{
|
||||||
|
el.TextContent = node.Name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var image = document.CreateElement("img");
|
||||||
|
image.SetAttribute("src", emoji.PublicUrl);
|
||||||
|
image.SetAttribute("alt", node.Name);
|
||||||
|
el.AppendChild(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static INode MfmUrlNode(MfmNodeTypes.MfmUrlNode node, IDocument document)
|
||||||
|
{
|
||||||
|
var el = document.CreateElement("a");
|
||||||
|
el.SetAttribute("href", node.Url);
|
||||||
|
el.ClassName = "url-node";
|
||||||
|
el.TextContent = node.Url;
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static INode MfmBoldNode(MfmNodeTypes.MfmBoldNode _, IDocument document)
|
||||||
|
{
|
||||||
|
var el = document.CreateElement("strong");
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static INode MfmTextNode(MfmNodeTypes.MfmTextNode node, IDocument document)
|
||||||
|
{
|
||||||
|
var el = document.CreateElement("span");
|
||||||
|
el.TextContent = node.Text;
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static INode MfmMentionNode(MfmNodeTypes.MfmMentionNode node, IDocument document)
|
||||||
|
{
|
||||||
|
var link = document.CreateElement("a");
|
||||||
|
link.SetAttribute("href", $"/@{node.Acct}");
|
||||||
|
link.ClassName = "mention";
|
||||||
|
var userPart = document.CreateElement("span");
|
||||||
|
userPart.ClassName = "user";
|
||||||
|
userPart.TextContent = $"@{node.Username}";
|
||||||
|
link.AppendChild(userPart);
|
||||||
|
if (node.Host != null)
|
||||||
|
{
|
||||||
|
var hostPart = document.CreateElement("span");
|
||||||
|
hostPart.ClassName = "host";
|
||||||
|
hostPart.TextContent = $"@{node.Host.Value}";
|
||||||
|
link.AppendChild(hostPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
return link;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using Iceshrimp.Frontend.Components;
|
using Iceshrimp.Frontend.Components;
|
||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
|
|
||||||
namespace Iceshrimp.Frontend.Core.Services;
|
namespace Iceshrimp.Frontend.Core.Services;
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
@using Iceshrimp.Frontend.Components
|
@using Iceshrimp.Frontend.Components
|
||||||
@using Iceshrimp.Frontend.Components.Note
|
@using Iceshrimp.Frontend.Components.Note
|
||||||
@using System.Text.RegularExpressions
|
@using System.Text.RegularExpressions
|
||||||
@inject ApiService Api
|
@inject ApiService Api
|
||||||
|
|
||||||
@if (_init)
|
@if (_init)
|
||||||
{
|
{
|
||||||
|
@ -64,11 +64,12 @@
|
||||||
private UserProfileResponse? Profile { get; set; }
|
private UserProfileResponse? Profile { get; set; }
|
||||||
private string? MinId { get; set; }
|
private string? MinId { get; set; }
|
||||||
private List<NoteResponse> UserNotes { get; set; } = [];
|
private List<NoteResponse> UserNotes { get; set; } = [];
|
||||||
private bool _loading = true;
|
|
||||||
private bool _init = false;
|
private bool _loading = true;
|
||||||
private bool _notFound = false;
|
private bool _init;
|
||||||
private bool _error = false;
|
private bool _notFound;
|
||||||
private bool _fetchLock = false;
|
private bool _error;
|
||||||
|
private bool _fetchLock;
|
||||||
|
|
||||||
private async Task GetNotes(string? minId)
|
private async Task GetNotes(string? minId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,8 +52,8 @@ else
|
||||||
private IList<NoteResponse>? Ascendants { get; set; }
|
private IList<NoteResponse>? Ascendants { get; set; }
|
||||||
private IJSObjectReference? Module { get; set; }
|
private IJSObjectReference? Module { get; set; }
|
||||||
private ElementReference RootNoteRef { get; set; }
|
private ElementReference RootNoteRef { get; set; }
|
||||||
private bool _init = false;
|
private bool _init;
|
||||||
private bool _error = false;
|
private bool _error;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
|
|
|
@ -101,6 +101,7 @@
|
||||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/MAX_ENUM_MEMBERS_ON_LINE/@EntryValue">1</s:Int64>
|
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/MAX_ENUM_MEMBERS_ON_LINE/@EntryValue">1</s:Int64>
|
||||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/NESTED_TERNARY_STYLE/@EntryValue">EXPANDED</s:String>
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/NESTED_TERNARY_STYLE/@EntryValue">EXPANDED</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/OTHER_BRACES/@EntryValue">NEXT_LINE</s:String>
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/OTHER_BRACES/@EntryValue">NEXT_LINE</s:String>
|
||||||
|
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/RazorCSharpFormat/RemoveBlankLinesNearBraces/@EntryValue">True</s:Boolean>
|
||||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/TYPE_DECLARATION_BRACES/@EntryValue">NEXT_LINE</s:String>
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/TYPE_DECLARATION_BRACES/@EntryValue">NEXT_LINE</s:String>
|
||||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_DECLARATION_LPAR/@EntryValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_DECLARATION_LPAR/@EntryValue">True</s:Boolean>
|
||||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARRAY_INITIALIZER_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARRAY_INITIALIZER_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||||
|
|
Loading…
Add table
Reference in a new issue