Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic Multi-Trackers, Inbox Middleware #726

Merged
merged 24 commits into from
Nov 1, 2023
Merged

Conversation

EricBAndrews
Copy link
Member

@EricBAndrews EricBAndrews commented Oct 20, 2023

Checklist

Pull Request Information

About this Pull Request

This PR replaces the existing inbox backend with a new, middleware-model based implementation. To support this, it adds a set of new generic multi-feed trackers. In short, here's how those work:

All of the loading and filtering operations are performed by the child trackers. These look more or less identical to the existing trackers; they extend BasicTracker, which is much like FeedTracking but built with generic trackers in mind. Child trackers augment this normal tracker functionality with the ability to treat their arrays as streams via two new methods: nextItemSortVal, which returns the sorting value of the next item in the stream, and consumeNextItem, which returns the next item in the stream and increments the cursor.

To the consumer, the streams abstract loading operations; the consumer simply awaits the next item and it appears, regardless of whether data needed to be fetched. To avoid the case where multiple threads invoke loading at the same time, loadNextPage has been reworked: it now requires the caller to specify a page, and using a semaphore it ensures that only one thread accesses the function at a time. If the page is not the expected next page, the call is ignored. This has the consequence that threads can effectively await a loadNextPage called by a different thread.

ParentTracker then uses stream system this to efficiently aggregate child trackers: provided all child trackers are sorted on the same criterion, the parent tracker can "fetch a new page" by effectively just merge sorting the streams of its child trackers. This ensures that loading behavior is only invoked when needed, and handles cases where one child tracker has many more items than the others. Since the child trackers are themselves just trackers, they can be used as normal trackers in single-stream views while at the same time feeding a multi-feed.

This implementation is pushing the limits of what Swift really wants to do with generics--the Item associated with the parent and the child must be related, such that items of the child type are convertable to items of the parent type and all can be sorted on the same criteria. This latter condition unfortunately proved impossible (at least for me) to pull off in a fully compiler-safe way; there is therefore only a single TrackerSortType, which must enumerate all possible sorts for a tracker. If a given TrackerItem does not implement a given TrackerSortType, that should be expressed in assertions.

To use the generic trackers, implement:

  • ParentTrackerItem conformance for the parent type, which should probably be an enum of child types
  • ChildTrackerItem conformance for all child types
  • Create trackers for each child type, extending ChildTracker and using the appropriate ChildTrackerItem. These should be very quick and easy, since only fetchPage needs to be overridden and everything else is handled by generics
  • Create tracker for the parent type. Usually this can be a simple extension of ParentTracker with the ParentTrackerType

This PR also changes the structure of how the inbox is organized. The trackers are all held in InboxView; they are passed to the per-item views as ObservedObjects. The per-stream views are now their own views rather than functions on the main view, and some of the structure of the individual items has been lightly refactored. None of the changes here are major.

A note to reviewers

The line count on this is simply obscene. It's not as bad as it looks, though, since a ton of those changes are just moving logic around, notably splitting Inbox View Logic into the models and moving some things between repositories.

The core new code is limited to BasicTracker, ChildTracker, and ParentTracker; almost everything else is refactoring or very thin (protocols, extending classes, etc.)

Behavior Changes

Blocking a user no longer removes them from the feed. The code is there, but not used. Since those users just show up again when the inbox is refreshed, I disabled that behavior in order to present a more consistent experience; long-term, we should look into filtering blocked users more robustly, but that's out of scope here.

There is one issue I am aware of: refreshing the inbox does not refresh the read status of replies or mentions, though it does refresh messages. I'm losing sanity trying to debug that one, and it's so minor that I'm willing to call it acceptable for the sake of getting this PR in, but if anybody has any insight into why I'd love to hear it.

@EricBAndrews EricBAndrews force-pushed the eric/inbox-middleware branch 2 times, most recently from d0336d8 to 2a8f58e Compare October 28, 2023 02:50
@EricBAndrews EricBAndrews marked this pull request as ready for review October 28, 2023 02:50
@EricBAndrews EricBAndrews requested a review from a team as a code owner October 28, 2023 02:50
@EricBAndrews EricBAndrews requested review from WestonHanners and ShadowJonathan and removed request for a team October 28, 2023 02:50
@EricBAndrews EricBAndrews force-pushed the eric/inbox-middleware branch from 2a8f58e to 32f29b2 Compare October 28, 2023 16:53
@Sjmarf
Copy link
Member

Sjmarf commented Oct 31, 2023

I'll do a proper review of this today 👍

Copy link
Member

@Sjmarf Sjmarf left a comment

Choose a reason for hiding this comment

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

It appears that tapping on a reply in the inbox feed no longer opens the post

Mlem/Models/Trackers/Feed/ParentTracker.swift Outdated Show resolved Hide resolved
Mlem/Models/Trackers/Feed/ChildTrackerProtocol.swift Outdated Show resolved Hide resolved
Mlem/Models/Trackers/Feed/ParentTracker.swift Outdated Show resolved Hide resolved
Mlem/Models/Trackers/Feed/ParentTracker.swift Outdated Show resolved Hide resolved
Mlem/Models/Trackers/Feed/ChildTracker.swift Outdated Show resolved Hide resolved
Mlem/API/Models/ScoringOperation.swift Show resolved Hide resolved
Mlem/Models/Content/Inbox/MentionModel.swift Show resolved Hide resolved
Mlem/Models/Trackers/Feed/TrackerItem.swift Show resolved Hide resolved
@EricBAndrews EricBAndrews requested a review from Sjmarf November 1, 2023 01:23
Copy link
Member

@Sjmarf Sjmarf left a comment

Choose a reason for hiding this comment

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

LGTM 👍

@EricBAndrews EricBAndrews force-pushed the eric/inbox-middleware branch from e5739f0 to e0e974c Compare November 1, 2023 15:07
@EricBAndrews EricBAndrews merged commit 7c024dd into dev Nov 1, 2023
4 checks passed
@EricBAndrews EricBAndrews deleted the eric/inbox-middleware branch November 1, 2023 15:26
@EricBAndrews EricBAndrews mentioned this pull request Nov 10, 2023
4 tasks
This was referenced Mar 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants