Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Aetherphone/Apps/Aethergram/AethergramApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,10 @@ private void DrawFeedList(Rect listRect, SocialFeedScope scope)
var snapshot = store.Feed(scope);
using (AppSurface.Begin(listRect))
{
if (ImGui.GetScrollY() < 1f)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: OnOpened does not reset feed scroll (only RefreshActiveFeed sets feedScrollTopPending) and ImGui keeps the child's scroll offset, so a user who leaves the feed scrolled down can go a long time without ever satisfying this guard. That is exactly the browse-all-day case in the issue, so the mitigation may fire less often than expected.

{
store.TrimFeed(scope, SocialFeedStore.FeedCap);
}
stories.DrawTray(theme);
if (snapshot.Length == 0)
{
Expand Down
5 changes: 5 additions & 0 deletions src/Aetherphone/Apps/Chirper/ChirperApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,11 @@ private void DrawFeedList(Rect listRect, SocialFeedScope scope)
var snapshot = store.Feed(scope);
using (AppSurface.Begin(listRect))
{
if (ImGui.GetScrollY() < 1f)
{
store.TrimFeed(scope, SocialFeedStore.FeedCap);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: stray blank line inside the block.

}
if (snapshot.Length == 0)
{
var message = store.IsLoading(scope) ? Loc.T(L.Common.Loading) :
Expand Down
20 changes: 20 additions & 0 deletions src/Aetherphone/Core/Social/FeedLane.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,26 @@ public void ApplyMore(TPost[] incoming, string? nextCursor)
}
}

public void Trim(int max)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking, answering the open question: option 1 is the right call and option 3 does not work. Per-page cursors cannot rewind a trim, because refreshes prepend to the head, so a recorded page boundary drifts deeper over the day. In the issue's own scenario (head growth from repeated app opens) the page-1 boundary ends up past the cap, and a boundary-aligned trim either refuses to fire or rewinds to a cursor from hours ago. A real rewind needs a cursor the client can derive from a post id or timestamp, which is a server change.

{
if (max <= 0)
{
return;
}

lock (gate)
{
if (items.Length <= max)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: please add coverage to FeedLaneTests.cs. Trim is pure and needs no ImGui, so keep-newest, under-cap no-op and cursor-survives-trim are three cheap tests.

{
return;
}

var trimmed = new TPost[max];
Array.Copy(items, trimmed, max);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: Trim reads and writes items under gate, but ReplacePost, ApplySavedEverywhere and RemoveAuthorEverywhere all assign lane.Items without it. A background update landing between the read and the write here is silently dropped, so a like or follow flag can revert until the next refresh. Pre-existing pattern rather than something you introduced, just widened by one more writer.

items = trimmed;
}
}

public void Clear()
{
lock (gate)
Expand Down
4 changes: 4 additions & 0 deletions src/Aetherphone/Core/Social/SocialFeedStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ private void OnSessionChanged()

public bool LoadingMore(SocialFeedScope scope) => Lane(scope).LoadingMore;

public const int FeedCap = 300;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: the cap constant lives here, but the policy that decides when to apply it (the scroll check) lives in each app's draw method, so a new social feed opts out just by forgetting a line. FeedVirtualizer is the seam all three feeds already share: give BeginFrame an optional trimmable source and the cap, the scroll guard and Velvet coverage all land in one place.


public void TrimFeed(SocialFeedScope scope, int max) => Lane(scope).Trim(max);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: Velvet is a third social feed with the same problem and it is not covered here. It has its own VelvetStore holding FeedLane<VelvetPostDto>[], and the same uncapped draw loop at VelvetShell.Feed.cs:68, so it keeps growing all day after this merges. The description is right that Chirper comes along for free, but Velvet does not.


private FeedLane<PostDto> Lane(SocialFeedScope scope) =>
scope == SocialFeedScope.ForYou ? forYouLane : followingLane;

Expand Down