[frontend] Implement timeline streaming
This commit is contained in:
parent
213801ed4b
commit
56325b356c
5 changed files with 81 additions and 11 deletions
|
@ -1,3 +1,4 @@
|
|||
using Iceshrimp.Frontend.Core.Miscellaneous;
|
||||
using Iceshrimp.Frontend.Core.Services;
|
||||
using Iceshrimp.Frontend.Core.Services.StateServicePatterns;
|
||||
using Iceshrimp.Frontend.Enums;
|
||||
|
@ -24,6 +25,7 @@ public class VirtualScroller<T> : ComponentBase, IDisposable where T : IIdentifi
|
|||
[Parameter] [EditorRequired] public required Func<DirectionEnum, T, Task<List<T>?>> ItemProvider { get; set; }
|
||||
[Parameter] [EditorRequired] public required string StateKey { get; set; }
|
||||
[Parameter] [EditorRequired] public required Func<List<string>, List<T>> ItemProviderById { get; set; }
|
||||
[Parameter] public IStreamingItemProvider<T>? StreamingItemProvider { get; set; }
|
||||
private ScrollEnd Before { get; set; } = null!;
|
||||
private ScrollEnd After { get; set; } = null!;
|
||||
private Dictionary<string, LazyComponent> Children { get; set; } = new();
|
||||
|
@ -80,6 +82,8 @@ public class VirtualScroller<T> : ComponentBase, IDisposable where T : IIdentifi
|
|||
_setScroll = true;
|
||||
}
|
||||
|
||||
if (StreamingItemProvider != null) StreamingItemProvider.ItemPublished += OnNewItem;
|
||||
|
||||
ReRender();
|
||||
_initialized = true;
|
||||
}
|
||||
|
@ -183,6 +187,17 @@ public class VirtualScroller<T> : ComponentBase, IDisposable where T : IIdentifi
|
|||
Before.Reset();
|
||||
}
|
||||
|
||||
public void OnNewItem(object? _, T item)
|
||||
{
|
||||
var height = GetScrollY();
|
||||
if (height == 0)
|
||||
{
|
||||
var add = Items.TryAdd(item.Id, item);
|
||||
if (add is false) Logger.LogError($"Duplicate notification: {item.Id}");
|
||||
}
|
||||
ReRender();
|
||||
}
|
||||
|
||||
private async Task CallbackAfterAsync()
|
||||
{
|
||||
if (!_initialized)
|
||||
|
@ -239,5 +254,6 @@ public class VirtualScroller<T> : ComponentBase, IDisposable where T : IIdentifi
|
|||
public void Dispose()
|
||||
{
|
||||
_locationChangeHandlerDisposable?.Dispose();
|
||||
if (StreamingItemProvider != null) StreamingItemProvider.ItemPublished -= OnNewItem;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
using Iceshrimp.Shared.Helpers;
|
||||
|
||||
namespace Iceshrimp.Frontend.Core.Miscellaneous;
|
||||
|
||||
public interface IStreamingItemProvider<T> where T : IIdentifiable
|
||||
{
|
||||
event EventHandler<T> ItemPublished;
|
||||
}
|
|
@ -2,11 +2,18 @@ using Iceshrimp.Shared.Schemas.Web;
|
|||
|
||||
namespace Iceshrimp.Frontend.Core.Services.NoteStore;
|
||||
|
||||
internal class StateSynchronizer
|
||||
internal class StateSynchronizer: IAsyncDisposable
|
||||
{
|
||||
private readonly StreamingService _streamingService;
|
||||
public event EventHandler<NoteBase>? NoteChanged;
|
||||
public event EventHandler<NoteBase>? NoteDeleted;
|
||||
|
||||
public StateSynchronizer(StreamingService streamingService)
|
||||
{
|
||||
_streamingService = streamingService;
|
||||
_streamingService.NoteUpdated += NoteUpdated;
|
||||
}
|
||||
|
||||
public void Broadcast(NoteBase note)
|
||||
{
|
||||
NoteChanged?.Invoke(this, note);
|
||||
|
@ -16,4 +23,15 @@ internal class StateSynchronizer
|
|||
{
|
||||
NoteDeleted?.Invoke(this, note);
|
||||
}
|
||||
|
||||
private void NoteUpdated(object? sender, NoteResponse noteResponse)
|
||||
{
|
||||
NoteChanged?.Invoke(this, noteResponse);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
_streamingService.NoteUpdated -= NoteUpdated;
|
||||
await _streamingService.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,28 @@
|
|||
using Iceshrimp.Frontend.Core.Miscellaneous;
|
||||
using Iceshrimp.Frontend.Enums;
|
||||
using Iceshrimp.Shared.Schemas.SignalR;
|
||||
using Iceshrimp.Shared.Schemas.Web;
|
||||
using NoteEvent = (Iceshrimp.Shared.Schemas.SignalR.StreamingTimeline timeline, Iceshrimp.Shared.Schemas.Web.NoteResponse note);
|
||||
|
||||
namespace Iceshrimp.Frontend.Core.Services.NoteStore;
|
||||
|
||||
internal class TimelineStore : NoteMessageProvider, IDisposable
|
||||
internal class TimelineStore : NoteMessageProvider, IAsyncDisposable, IStreamingItemProvider<NoteResponse>
|
||||
{
|
||||
public event EventHandler<NoteResponse>? ItemPublished;
|
||||
private Dictionary<string, TimelineState> Timelines { get; set; } = new();
|
||||
private readonly ApiService _api;
|
||||
private readonly ILogger<TimelineStore> _logger;
|
||||
private readonly StateSynchronizer _stateSynchronizer;
|
||||
private readonly StreamingService _streamingService;
|
||||
|
||||
public TimelineStore(ApiService api, ILogger<TimelineStore> logger, StateSynchronizer stateSynchronizer)
|
||||
public TimelineStore(ApiService api, ILogger<TimelineStore> logger, StateSynchronizer stateSynchronizer, StreamingService streamingService)
|
||||
{
|
||||
_api = api;
|
||||
_logger = logger;
|
||||
_stateSynchronizer = stateSynchronizer;
|
||||
_streamingService = streamingService;
|
||||
_stateSynchronizer.NoteChanged += OnNoteChanged;
|
||||
_streamingService.NotePublished += OnNotePublished;
|
||||
}
|
||||
|
||||
private void OnNoteChanged(object? _, NoteBase changedNote)
|
||||
|
@ -152,6 +158,21 @@ internal class TimelineStore : NoteMessageProvider, IDisposable
|
|||
return list;
|
||||
}
|
||||
|
||||
private void OnNotePublished(object? sender, NoteEvent valueTuple)
|
||||
{
|
||||
var (timeline, response) = valueTuple;
|
||||
if (timeline == StreamingTimeline.Home)
|
||||
{
|
||||
var success = Timelines.TryGetValue("home", out var home);
|
||||
if (success)
|
||||
{
|
||||
var add = home!.Timeline.TryAdd(response.Id, response);
|
||||
if (add is false) _logger.LogError($"Duplicate note: {response.Id}");
|
||||
}
|
||||
ItemPublished?.Invoke(this, response);
|
||||
}
|
||||
}
|
||||
|
||||
public class Cursor
|
||||
{
|
||||
public required DirectionEnum Direction { get; set; }
|
||||
|
@ -163,4 +184,11 @@ internal class TimelineStore : NoteMessageProvider, IDisposable
|
|||
{
|
||||
_stateSynchronizer.NoteChanged -= OnNoteChanged;
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await _stateSynchronizer.DisposeAsync();
|
||||
await _streamingService.DisposeAsync();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
@if (NoteResponses is not null)
|
||||
{
|
||||
|
||||
<VirtualScroller InitialItems="NoteResponses" ItemProvider="Provider" StateKey="1234567890" ItemProviderById="ItemProviderById">
|
||||
<VirtualScroller InitialItems="NoteResponses" ItemProvider="Provider" StateKey="1234567890" ItemProviderById="ItemProviderById" StreamingItemProvider="Store">
|
||||
<ItemTemplate Context="note">
|
||||
<CascadingValue Value="Store" TValue="NoteMessageProvider" Name="Provider">
|
||||
<TimelineNote Note="note"></TimelineNote>
|
||||
|
|
Loading…
Add table
Reference in a new issue