[frontend] Bind the Timeline Intersection Observer to the scroller and increase root margin

This commit is contained in:
Lilian 2024-04-24 19:09:47 +02:00
parent 35de6a472c
commit 04e3ed0195
No known key found for this signature in database
GPG key ID: 007CA12D692829E1
10 changed files with 60 additions and 27 deletions

View file

@ -1,22 +1,32 @@
@using Iceshrimp.Shared.Schemas
@using Ljbc1994.Blazor.IntersectionObserver
@using Ljbc1994.Blazor.IntersectionObserver.API
@using Ljbc1994.Blazor.IntersectionObserver.Components
@inject IJSRuntime Js
<IntersectionObserve OnChange="@(entry => Change(Note.Id, entry))">
@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.
// 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.
{
<IntersectionObserve Options="new IntersectionObserverOptions(){ Root = Scroller, RootMargin = margin}" OnChange="@(entry => Change(entry))">
<div class="tgt" @ref="context.Ref.Current">
@if (_isIntersecting)
{
<TimelineNote Note="Note" />
}else {
<div class="placeholder" style="height: @(Height ?? 150)px"></div>
<div class="placeholder" style="height: @(Height ?? 150)px"></div>
}
</div>
</IntersectionObserve>
}
@code {
[Parameter] public required NoteResponse Note { get; set; }
private IJSObjectReference? _module;
private int? Height { get; set; } = null;
private bool _isIntersecting = true;
[Parameter][EditorRequired] public required NoteResponse Note { get; set; }
[Parameter][EditorRequired] public ElementReference Scroller { get; set; }
private IJSObjectReference? _module;
private int? Height { get; set; } = null;
private bool _isIntersecting = true;
private string margin = "200%";
private bool _init = false;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
@ -24,12 +34,22 @@
{
_module = await Js.InvokeAsync<IJSObjectReference>("import",
"./Components/LazyNote.razor.js");
}
}
private async Task Change(string id, IntersectionObserverEntry entry)
protected override void OnAfterRender(bool firstRender)
{
if (id == Note.Id && entry.IsIntersecting == false)
if (firstRender)
{
_init = true;
}
}
private async Task Change (IntersectionObserverEntry entry)
{
if (entry.IsIntersecting == false)
{
Height = await GetHeight();
_isIntersecting = false;
@ -37,7 +57,7 @@
return;
}
if (id == Note.Id && entry.IsIntersecting)
if (entry.IsIntersecting)
{
_isIntersecting = true;
}

View file

@ -0,0 +1,5 @@
<h3>RecursiveNote</h3>
@code {
}

View file

@ -6,13 +6,15 @@
@inject ApiService ApiService
@if (_init)
{
@foreach (var note in Timeline)
{
<LazyNote Note="note" />
}
<IntersectionObserve OnChange="entry => OnEnd(entry)">
<div @ref="context.Ref.Current" class="end">END!</div>
</IntersectionObserve>
<div @ref="Scroller" class="scroller">
@foreach (var note in Timeline)
{
<LazyNote Scroller="Scroller" Note="note" />
}
<IntersectionObserve OnChange="entry => OnEnd(entry)">
<div @ref="context.Ref.Current" class="end">END!</div>
</IntersectionObserve>
</div>
}
else
{
@ -25,6 +27,7 @@ else
private string? MaxId { get; set; }
private string? MinId { get; set; }
private bool LockFetch { get; set; }
public ElementReference Scroller { get; set; }
private async Task Initialize()
{

View file

@ -0,0 +1,8 @@
.scroller {
display: flex;
flex-direction: column;
overflow: scroll;
max-height: 100vh;
width: 100%;
align-items: center;
}

View file

@ -73,5 +73,6 @@ internal class SessionService
LocalStorage.SetItem("last_user", user.Id);
((IJSInProcessRuntime)Js).InvokeVoid("eval",
$"document.cookie = \"session={user.Id}; expires=Fri, 31 Dec 9999 23:59:59 GMT; SameSite=Strict\"");
// Security implications of this need a second pass? user.Id should never be user controllable, but still.
}
}

View file

@ -0,0 +1,6 @@
@page "/SingleNote"
<h3>SingleNote</h3>
@code {
}

View file

@ -1,8 +1,6 @@
@page "/"
@using Iceshrimp.Frontend.Components
<div class="scroller">
<TimelineComponent />
</div>
<TimelineComponent />
@code {

View file

@ -1,8 +0,0 @@
.scroller {
display: flex;
flex-direction: column;
overflow: scroll;
max-height: 100vh;
width: 100%;
align-items: center;
}