Reader: Add Discover sub-tabs (Freshly Pressed, Recommended, Latest)#22793
Draft
Reader: Add Discover sub-tabs (Freshly Pressed, Recommended, Latest)#22793
Conversation
Apply a new WordPress.TabLayout.Material3 style to the Discover sub-tab strip so it visually matches the Compose PrimaryTabRow used in the new Stats and RS post list screens: text-width indicator with rounded top, elastic animation, MD3 title-small typography, primary-colored selected text, and a surface background flush with the app bar. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Route the Recommended and Latest Discover sub-tabs through the v2
/read/streams/discover endpoint to match iOS ReaderCardService, instead
of the legacy /read/tags/{slug}/posts endpoint. Both sub-tabs hit the
same endpoint and are distinguished by the tag slug — Latest adds
sort=date while Recommended uses the server's default editorial order.
Pagination is cursor-based via an opaque page_handle stored per-stream
in AppPrefs, and first-page requests include the shared reader refresh
counter so the server rotates the visible shard on pull-to-refresh.
The tags request param uses the user's followed tag slugs, falling
back to "dailyprompt,wordpress" when the user isn't following anything
(mirroring the existing ReaderDiscoverLogic behavior).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Apply post-review cleanups to the Recommended/Latest Discover streams: drop the RECOMMENDED_PATH/LATEST_PATH aliases in favor of DISCOVER_STREAMS_PATH, remove the single-use isDiscoverStream() helper, inline the get/setPageHandleForStream helpers at their one-call-site each, and rewire handleDiscoverStreamResponse to run on the existing coroutine scope instead of spawning a raw Thread per response. Also give the Recommended and Latest sub-tab ReaderTags proper display titles (TAG_TITLE_RECOMMENDED / TAG_TITLE_LATEST) instead of reusing their lowercase slugs, and make the tabTitleResFor when-expression fail fast on unknown positions instead of silently falling back to Freshly Pressed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Match the unselected tab text color to the selected color so the Discover sub-tab strip is visually consistent with the New Stats and new RS Post List tabbed screens. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Migrate the Freshly Pressed sub-tab from the legacy v1.2 /freshly-pressed endpoint to the v2 /read/streams/freshly-pressed endpoint, matching iOS ReaderPostServiceRemote.fetchStreamCards and reusing the same pipeline already in place for the Recommended and Latest sub-tabs. Freshly Pressed now shares the cards parser, opaque page_handle pagination, and shared refresh counter with the other two Discover streams — only the per-stream page_handle is stored in its own AppPrefs key so the three cursors don't collide. Parameterise requestPostsForDiscoverStream on the tag's endpoint instead of the hardcoded DISCOVER_STREAMS_PATH constant, and extract get/setDiscoverStreamPageHandle helpers to keep the per-stream prefs selection in one place. isFreshlyPressed() now checks by tag slug (matching isRecommended / isLatest) instead of an endpoint suffix, which is more robust now that the endpoint carries the v2 path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Collapse duplication that built up while wiring the Discover sub-tabs: dedupe the three near-identical tag factories in ReaderDiscoverTabsFragment, inline the tab title res lookup, fold the three per-stream page-handle prefs into a single keyed accessor, drop the single-use isRecommended/ isLatest predicates in favor of slug comparisons, and replace the ad-hoc SupervisorJob scope in requestPostsForDiscoverStream with the injected application scope. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The detailed streams-pipeline comment duplicated the KDoc already present on ReaderPostRepository.requestPostsForDiscoverStream. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Freshly Pressed now uses the legacy v1.2 /freshly-pressed endpoint (matching web and iOS) instead of the v2 /read/streams/freshly-pressed path which returned 404. Latest matches iOS using read/streams/discover with sort=date. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…eritance Remove the duplicate member `formatRelativeEndpointForTag` in ReaderPostRepository (the companion version is sufficient), and simplify ReaderDiscoverTabsFragment to extend Fragment directly since it opted out of ViewPagerFragment behavior by returning null. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract card-parsing logic into parsePostCards/parsePostCard helpers to fix NestedBlockDepth, LoopWithTooManyJumpStatements, and TooGenericExceptionCaught detekt violations. Narrows catch blocks from Exception to JSONException. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove redundant tabSelectedTextColor (identical to tabTextColor) - Make FRESHLY_PRESSED_PATH and DISCOVER_STREAMS_PATH private - Use idiomatic mutableMapOf instead of HashMap constructor Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Collaborator
Generated by 🚫 Danger |
Contributor
|
|
Contributor
|
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## trunk #22793 +/- ##
==========================================
- Coverage 37.29% 37.26% -0.04%
==========================================
Files 2326 2326
Lines 124859 124987 +128
Branches 16961 16990 +29
==========================================
+ Hits 46563 46573 +10
- Misses 74524 74633 +109
- Partials 3772 3781 +9 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…states Set tabSelectedTextColor to colorPrimary so both states use the same color, matching the Compose PrimaryTabRow behavior in Stats and RS post list. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The OnPageChangeCallback was registered but never unregistered, causing a potential memory leak when the fragment's view was destroyed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…r is called Wraps the coroutine body in requestPostsForDiscoverStream so that uncaught exceptions (e.g. from getFollowedTagsUseCase) report FAILED to the result listener instead of propagating to the application scope. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix AppPrefsWrapper import ordering in ReaderDiscoverTabsFragment - Replace filterNot().isEmpty() with none() in Discover stream tags check Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
🤖 Build Failure AnalysisThis build has failures. Claude has analyzed them - check the build annotations for details. |
- Extract resolveInitialTabIndex in ReaderDiscoverTabsFragment as a pure companion function and cover it with unit tests - Add ReaderPostRepositoryTest covering the REQUEST_OLDER short-circuit for Recommended and Latest streams - Clarify that Freshly Pressed falls through to the regular tag-based flow, and note why REQUEST_OLDER_THAN_GAP is folded into first-page Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eams Pagination on the Recommended and Latest sub-tabs dropped posts into the middle of the list because the local sort was by date_published — which doesn't match the server's editorial (Recommended) or cursor-paginated (Latest) ordering. Route these tags to the date_tagged sort column and stamp each post with a monotonically decreasing date_tagged in server order so REQUEST_OLDER pages land at the end of the list. - ReaderTag.isDiscoverStream() identifies Recommended/Latest tags - ReaderPostTable.getSortColumnForTag() uses date_tagged for them - ReaderPostRepository.stampServerOrderOnPosts() assigns insertion-order timestamps before saveUpdatedPosts, with a null-safe oldest-date lookup and a getNumPostsWithTag fallback for stale rows from pre-fix builds Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This is a new feature, so the date_tagged fallback that compensated for posts stored before stamp logic existed isn't needed. Keep the null-safe parse around DateTimeUtils.dateFromIso8601 and let REQUEST_NEWER flow through saveUpdatedPosts unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
getOldestDateWithTag queries "SELECT datetime(date_tagged)", and SQLite's datetime() normalizes to "YYYY-MM-DD HH:MM:SS" without timezone — which DateTimeUtils.dateFromIso8601 can't parse. Every REQUEST_OLDER call was silently falling back to now, so paginated posts landed at the top of the list instead of the end. Add getOldestDateTaggedForTag that selects the raw date_tagged column so the ISO8601-with-timezone value round-trips intact, and use it from stampServerOrderOnPosts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Match the web Reader: Recommended stays on /read/streams/discover with editorial curation and page_handle pagination, but Latest now hits /read/tags/posts with orderBy=date and the user's followed tags, using before=<oldestDate> pagination (same mechanism as Freshly Pressed). This gives Latest a true date-descending stream that appends cleanly on scroll, instead of a curated stream with opaque cursor pagination that interleaved posts on the client. - ReaderDiscoverTabsFragment: new LATEST_PATH = "read/tags/posts" - ReaderTag.isDiscoverStream() now only matches Recommended (Latest uses the standard date_published sort) - ReaderPostRepository.requestPostsForLatestStream: new handler that seeds tags from getFollowedTagsUseCase and paginates by date - Obsolete DISCOVER_STREAM_TAG_SLUGS set removed; Recommended/Latest are now routed via a direct when-branch on tagSlug Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pull the duplicated "user followed tags → query param" block out of both requestPostsForDiscoverStream and requestPostsForLatestStream. Also drop the stale Latest reference from the handleDiscoverStreamResponse comment (Latest no longer flows through that method). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
requestPostsWithTag had three returns after the Latest branch was added; pull the sub-tab routing into a helper that returns Boolean so the dispatch logic has a clear name and requestPostsWithTag stays within the ReturnCount limit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Description
Replaces the single Reader Discover feed with a tabbed experience containing three sub-tabs: Freshly Pressed, Recommended, and Latest, matching the web (except for Tags which are handled separately by the app). While working on this, I had Claude examine the web reader codebase and make sure we're using the same endpoints and parameters.
Testing instructions
Screen_recording_20260414_105039.mp4