[frontend/components] Fix new descendants not showing up in single note view.

This commit is contained in:
Lilian 2025-02-04 19:45:55 +01:00
parent 60f100385d
commit e1e78a1052
No known key found for this signature in database
6 changed files with 69 additions and 37 deletions

View file

@ -2,6 +2,7 @@
@using Iceshrimp.Frontend.Components.Note @using Iceshrimp.Frontend.Components.Note
@using Iceshrimp.Frontend.Core.Miscellaneous @using Iceshrimp.Frontend.Core.Miscellaneous
@using Iceshrimp.Frontend.Core.Services @using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Core.Services.NoteStore
@using Iceshrimp.Frontend.Localization @using Iceshrimp.Frontend.Localization
@using Iceshrimp.MfmSharp @using Iceshrimp.MfmSharp
@using Iceshrimp.Shared.Schemas.Web @using Iceshrimp.Shared.Schemas.Web
@ -16,6 +17,7 @@
@inject GlobalComponentSvc GlobalComponentSvc @inject GlobalComponentSvc GlobalComponentSvc
@inject MessageService MessageService @inject MessageService MessageService
@inject SettingsService Settings; @inject SettingsService Settings;
@inject NoteActions NoteActions;
<dialog @onkeydown="HandleKeyDown" class="dialog" @ref="Dialog"> <dialog @onkeydown="HandleKeyDown" class="dialog" @ref="Dialog">
<div class="compose"> <div class="compose">
<div class="header"> <div class="header">
@ -23,7 +25,8 @@
<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"/>
<StateButton OnClick="SendNote" @ref="SendButton" ExtraClasses="post-btn" AriaLabel="post" Disabled="@(NoteLength - ((NoteDraft.Cw?.Length ?? 0) + NoteDraft.Text.Length) < 0)"> <StateButton OnClick="SendNote" @ref="SendButton" ExtraClasses="post-btn" AriaLabel="post"
Disabled="@(NoteLength - ((NoteDraft.Cw?.Length ?? 0) + NoteDraft.Text.Length) < 0)">
<Initial> <Initial>
@Loc["ComposeNote"]<Icon Name="Icons.PaperPlaneRight"/> @Loc["ComposeNote"]<Icon Name="Icons.PaperPlaneRight"/>
</Initial> </Initial>
@ -50,7 +53,8 @@
aria-label="content warning"/> aria-label="content warning"/>
<hr class="separator"/> <hr class="separator"/>
} }
<textarea @ref="Textarea" @bind="NoteDraft.Text" @bind:event="oninput" class="textarea" placeholder="@TextPlaceholder" rows="5" <textarea @ref="Textarea" @bind="NoteDraft.Text" @bind:event="oninput" class="textarea"
placeholder="@TextPlaceholder" rows="5"
cols="35" autofocus="autofocus" aria-label="note text"></textarea> cols="35" autofocus="autofocus" aria-label="note text"></textarea>
<div class="footer"> <div class="footer">
<div class="buttons"> <div class="buttons">
@ -69,7 +73,8 @@
</button> </button>
</div> </div>
<div class="buttons"> <div class="buttons">
<span title="@Loc["Character limit"]" aria-label="character limit">@(NoteLength - ((NoteDraft.Cw?.Length ?? 0) + NoteDraft.Text.Length))</span> <span title="@Loc["Character limit"]"
aria-label="character limit">@(NoteLength - ((NoteDraft.Cw?.Length ?? 0) + NoteDraft.Text.Length))</span>
<button class="btn" title="@Loc["Preview"]" @onclick="() => Preview = !Preview" <button class="btn" title="@Loc["Preview"]" @onclick="() => Preview = !Preview"
aria-label="preview"> aria-label="preview">
<Icon Name="Icons.Binoculars" Size="1.3rem"></Icon> <Icon Name="Icons.Binoculars" Size="1.3rem"></Icon>
@ -116,12 +121,7 @@
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?" }, { "reply", "Reply goes here!" }, { "quote", "Quote this post!" } };
{
{ "default", "What's on your mind?" },
{ "reply", "Reply goes here!" },
{ "quote", "Quote this post!" }
};
private async Task HandleKeyDown(KeyboardEventArgs e) private async Task HandleKeyDown(KeyboardEventArgs e)
{ {
@ -162,9 +162,7 @@
new DropdownElement<NoteVisibility> new DropdownElement<NoteVisibility>
{ {
#pragma warning disable BL0005 // Setting this outside the component is fine until this is reworked #pragma warning disable BL0005 // Setting this outside the component is fine until this is reworked
Icon = DropdownIcon(vis), Icon = DropdownIcon(vis), Content = DropdownContent(vis), Selection = vis
Content = DropdownContent(vis),
Selection = vis
#pragma warning restore BL0005 #pragma warning restore BL0005
}) })
.ToList(); .ToList();
@ -259,12 +257,7 @@
var settings = await Settings.GetUserSettingsAsync(); var settings = await Settings.GetUserSettingsAsync();
ReplyOrQuote = null; ReplyOrQuote = null;
Attachments = new List<DriveFileResponse>(); Attachments = new List<DriveFileResponse>();
NoteDraft = new NoteCreateRequest NoteDraft = new NoteCreateRequest { Text = "", Visibility = settings.DefaultNoteVisibility, Cw = null };
{
Text = "",
Visibility = settings.DefaultNoteVisibility,
Cw = null
};
TextPlaceholder = AvailablePlaceholders["default"]; TextPlaceholder = AvailablePlaceholders["default"];
} }
@ -295,8 +288,7 @@
if (ReplyOrQuote != null) if (ReplyOrQuote != null)
{ {
var res = await ApiService.Notes.GetNoteAsync(ReplyOrQuote.Id); await NoteActions.RefetchNoteAsync(ReplyOrQuote);
if (res != null) _ = MessageService.UpdateNoteAsync(res);
} }
SendButton.State = StateButton.StateEnum.Success; SendButton.State = StateButton.StateEnum.Success;

View file

@ -1,10 +1,12 @@
@* ReSharper disable once RedundantUsingDirective *@ @* ReSharper disable once RedundantUsingDirective *@
@using Iceshrimp.Frontend.Components.Note @using Iceshrimp.Frontend.Components.Note
@using Iceshrimp.Frontend.Core.Services @using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Core.Services.NoteStore
@using Iceshrimp.Shared.Schemas.Web @using Iceshrimp.Shared.Schemas.Web
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject MessageService MessageService @inject MessageService MessageService
@inject ApiService Api @inject ApiService Api
@inject RelatedStore Store
@implements IDisposable @implements IDisposable
@if (_hidden == false) @if (_hidden == false)
@ -69,12 +71,11 @@
[Parameter] [EditorRequired] public required int Depth { get; set; } [Parameter] [EditorRequired] public required int Depth { get; set; }
[Parameter] [EditorRequired] public required int MaxDepth { get; set; } [Parameter] [EditorRequired] public required int MaxDepth { get; set; }
private bool _indented = false; private bool _indented = false;
private IDisposable _noteChangedHandler = null!;
private bool _hidden = false; private bool _hidden = false;
protected override void OnInitialized() protected override void OnInitialized()
{ {
_noteChangedHandler = MessageService.Register(Note.Id, OnNoteChanged, MessageService.Type.Updated); Store.NoteChanged += OnNoteChanged;
if (Depth > 0 || Note.Descendants?.Count > 0) if (Depth > 0 || Note.Descendants?.Count > 0)
{ {
_indented = true; _indented = true;
@ -89,16 +90,20 @@
} }
private void OnNoteChanged(object? _, NoteResponse note) private void OnNoteChanged(object? _, NoteResponse note)
{
if (note.Id == Note.Id)
{ {
var __ = Refresh(); var __ = Refresh();
} }
}
private async Task Refresh() private async Task Refresh()
{ {
if (Depth < MaxDepth) if (Depth < MaxDepth)
{ {
var res = await Api.Notes.GetNoteDescendantsAsync(Note.Id, MaxDepth - Depth); var res = await Store.GetDescendantsAsync(Note.Id, MaxDepth - Depth, true);
Note.Descendants = res; if (res == null) return;
Note.Descendants = res.Select(p => p.Value).ToList();
StateHasChanged(); StateHasChanged();
} }
} }
@ -110,6 +115,6 @@
public void Dispose() public void Dispose()
{ {
_noteChangedHandler.Dispose(); Store.NoteChanged -= OnNoteChanged;
} }
} }

View file

@ -15,6 +15,20 @@ internal class NoteActions(
stateSynchronizer.Broadcast(note); stateSynchronizer.Broadcast(note);
} }
public async Task RefetchNoteAsync(NoteBase note)
{
try
{
var res = await api.Notes.GetNoteAsync(note.Id);
if (res == null) return;
Broadcast(res);
}
catch (ApiException e)
{
logger.LogError(e, "Failed to fetch note.");
}
}
public async Task ToggleLikeAsync(NoteBase note) public async Task ToggleLikeAsync(NoteBase note)
{ {
if (note.Liked) if (note.Liked)

View file

@ -36,7 +36,6 @@ internal class RelatedStore : NoteMessageProvider, IDisposable
note.Attachments = noteResponse.Attachments; note.Attachments = noteResponse.Attachments;
note.Reactions = noteResponse.Reactions; note.Reactions = noteResponse.Reactions;
// container.Value.Notes[noteResponse.Id] = note;
NoteChangedHandlers.First(p => p.Key == note.Id).Value.Invoke(this, note); NoteChangedHandlers.First(p => p.Key == note.Id).Value.Invoke(this, note);
NoteChanged?.Invoke(this, note); NoteChanged?.Invoke(this, note);
} }
@ -64,7 +63,8 @@ internal class RelatedStore : NoteMessageProvider, IDisposable
input.Replies = updated.Replies; input.Replies = updated.Replies;
input.Attachments = updated.Attachments; input.Attachments = updated.Attachments;
input.Reactions = updated.Reactions; input.Reactions = updated.Reactions;
NoteChangedHandlers.First(p => p.Key == input.Id).Value.Invoke(this, input); var handler = NoteChangedHandlers.FirstOrDefault(p => p.Key == input.Id);
handler.Value?.Invoke(this, input);
NoteChanged?.Invoke(this, input); NoteChanged?.Invoke(this, input);
} }
@ -82,10 +82,14 @@ internal class RelatedStore : NoteMessageProvider, IDisposable
return await FetchAscendantsAsync(id, limit); return await FetchAscendantsAsync(id, limit);
} }
public async Task<SortedList<string, NoteResponse>?> GetDescendantsAsync(string id, int? limit) public async Task<SortedList<string, NoteResponse>?> GetDescendantsAsync(
string id, int? limit, bool refresh = false
)
{ {
if (refresh) return await FetchDescendantsAsync(id, limit);
var success = Descendants.TryGetValue(id, out var value); var success = Descendants.TryGetValue(id, out var value);
if (success) return value?.Notes ?? throw new InvalidOperationException("Key somehow had no associated value."); if (success)
return value?.Notes ?? throw new InvalidOperationException("Key somehow had no associated value.");
return await FetchDescendantsAsync(id, limit); return await FetchDescendantsAsync(id, limit);
} }

View file

@ -57,7 +57,8 @@ internal class TimelineStore : NoteMessageProvider, IAsyncDisposable, IStreaming
note.Attachments = changedNote.Attachments; note.Attachments = changedNote.Attachments;
note.Reactions = changedNote.Reactions; note.Reactions = changedNote.Reactions;
NoteChangedHandlers.First(p => p.Key == note.Id).Value.Invoke(this, note); var handler = NoteChangedHandlers.FirstOrDefault(p => p.Key == note.Id);
handler.Value?.Invoke(this, note);
} }
} }
} }

View file

@ -218,6 +218,7 @@
{ {
_locationChangingHandlerDisposable = Navigation.RegisterLocationChangingHandler(LocationChangeHandler); _locationChangingHandlerDisposable = Navigation.RegisterLocationChangingHandler(LocationChangeHandler);
StateSynchronizer.NoteDeleted += OnNoteDeleted; StateSynchronizer.NoteDeleted += OnNoteDeleted;
NoteStore.AnyNoteChanged += OnNoteChanged;
} }
private void OnNoteDeleted(object? _, NoteBase note) private void OnNoteDeleted(object? _, NoteBase note)
@ -235,6 +236,20 @@
StateHasChanged(); StateHasChanged();
} }
private void OnNoteChanged(object? _, NoteResponse note)
{
if (note.Id == NoteId)
{
var __ = Refresh();
}
}
private async Task Refresh()
{
if (NoteId != null) Descendants = await RelatedStore.GetDescendantsAsync(NoteId, _depth, true);
StateHasChanged();
}
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
private ValueTask LocationChangeHandler(LocationChangingContext arg) private ValueTask LocationChangeHandler(LocationChangingContext arg)
{ {
@ -322,5 +337,6 @@
SaveState(); SaveState();
_locationChangingHandlerDisposable?.Dispose(); _locationChangingHandlerDisposable?.Dispose();
StateSynchronizer.NoteDeleted -= OnNoteDeleted; StateSynchronizer.NoteDeleted -= OnNoteDeleted;
NoteStore.AnyNoteChanged -= OnNoteChanged;
} }
} }