Iceshrimp.NET/Iceshrimp.Frontend/Pages/SingleNote.razor

149 lines
No EOL
4.7 KiB
Text

@page "/notes/{NoteId}"
@using Iceshrimp.Frontend.Components
@using Iceshrimp.Frontend.Components.Note
@using Iceshrimp.Frontend.Core.Services
@using Iceshrimp.Frontend.Core.Services.StateServicePatterns
@using Iceshrimp.Shared.Schemas.Web
@inject ApiService ApiService
@inject IJSRuntime Js
@inject MessageService MessageService
@inject StateService State
@inject NavigationManager Navigation
@implements IDisposable
@if (_init)
{
<div @ref="Scroller" class="scroller">
<div class="wrapper">
<div class="container">
@if (Ascendants != null)
{
<div class="ascendants">
@foreach (var note in Ascendants)
{
<AscendedNote Note="note"/>
}
</div>
}
<div @ref="RootNoteRef" class="root-note">
<Note NoteResponse="RootNote"></Note>
</div>
@if (Descendants != null)
{
<div class="descendants">
@foreach (var element in Descendants)
{
<RecursiveNote Note="element" Depth="0" MaxDepth="_depth"/>
}
</div>
}
</div>
</div>
</div>
}
else
{
<div>Loading</div>
}
@if (_error)
{
<div>This note does not exist!</div>
}
@code {
[Parameter] public string? NoteId { get; set; }
public NoteResponse? RootNote { get; set; }
private IList<NoteResponse>? Descendants { get; set; }
private IList<NoteResponse>? Ascendants { get; set; }
private IJSInProcessObjectReference Module { get; set; } = null!;
private ElementReference RootNoteRef { get; set; }
private bool _init;
private bool _error;
private int _depth = 20;
private ElementReference Scroller { get; set; }
private IDisposable? _locationChangingHandlerDisposable;
protected override async Task OnParametersSetAsync()
{
_init = false;
if (NoteId == null)
{
_error = true;
return;
}
RootNote = await ApiService.Notes.GetNote(NoteId);
if (RootNote == null)
{
_error = true;
return;
}
Descendants = await ApiService.Notes.GetNoteDescendants(NoteId, _depth);
Ascendants = await ApiService.Notes.GetNoteAscendants(NoteId, default);
_init = true;
StateHasChanged();
}
protected override void OnInitialized()
{
if (NoteId != null) MessageService.Register(NoteId, OnNoteChanged);
_locationChangingHandlerDisposable = Navigation.RegisterLocationChangingHandler(LocationChangeHandler);
}
private void OnNoteChanged(object? _, NoteResponse note)
{
var __ = Refresh();
}
private async Task Refresh()
{
if (NoteId == null) throw new InvalidOperationException("RefreshNote called under impossible circumstances");
Descendants = await ApiService.Notes.GetNoteDescendants(NoteId, default);
Ascendants = await ApiService.Notes.GetNoteAscendants(NoteId, default);
StateHasChanged();
}
private ValueTask LocationChangeHandler(LocationChangingContext arg)
{
SaveState();
return ValueTask.CompletedTask;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
Module = (IJSInProcessObjectReference)await Js.InvokeAsync<IJSObjectReference>("import", "/Pages/SingleNote.razor.js");
}
if (_init)
{
var state = State.SingleNote.GetState(NoteId!);
if (state != null)
{
Module.InvokeVoid("SetScrollTop", Scroller, state.ScrollTop);
}
else
{
Module.InvokeVoid("ScrollIntoView", RootNoteRef);
}
}
}
private void SaveState()
{
if (NoteId == null) return;
var scrollTop = Module.Invoke<float>("GetScrollTop", Scroller);
var state = new SingleNoteState { ScrollTop = scrollTop };
State.SingleNote.SetState(NoteId, state);
}
public void Dispose()
{
if (NoteId != null) MessageService.Unregister(NoteId, OnNoteChanged);
SaveState();
_locationChangingHandlerDisposable?.Dispose();
}
}