Skip to content

Add Compose-based RS post list for self-hosted sites#22596

Open
nbradbury wants to merge 49 commits intotrunkfrom
feature/rs-self-hosted-post-list
Open

Add Compose-based RS post list for self-hosted sites#22596
nbradbury wants to merge 49 commits intotrunkfrom
feature/rs-self-hosted-post-list

Conversation

@nbradbury
Copy link
Contributor

@nbradbury nbradbury commented Feb 13, 2026

Description

Adds a new Compose-based post list screen powered by wordpress-rs for self-hosted sites with
application passwords enabled, gated behind the "New Post List" experimental feature flag.

This will eventually replace the existing FluxC-based post list with one that uses WpSelfHostedService from
the wordpress-rs library for all data operations.

Limitations

There is a lot of missing functionality which will be added over time:

  • Search
  • Post context menus
  • Refreshing after post uploaded/changed
  • Tapping a trashed post should ask whether to move it to drafts before opening it

Also, note that opening a post in the editor only works if the post already exists in FluxC (since the editor loads the local post from FluxC). For this reason I recommend refreshing the "old" post list before enabling the feature flag for the new post list to ensure posts are loaded.

New files

  • PostRsListActivity — Compose activity with FluxC bridge for opening posts in the editor
  • PostRsListViewModel — manages observable collections per tab via wordpress-rs
  • PostRsListTab — enum defining Published, Drafts, Scheduled, Trashed tabs
  • PostRsListUiState — UI state models and PostItemState-to-UI mapping
  • WpSelfHostedServiceProvider — singleton that creates/caches WpSelfHostedService per site
  • PostRsListScreen — main Scaffold with tabs, pager, and FAB
  • PostRsTabListScreen — per-tab content with pull-to-refresh and pagination
  • PostRsListItem — post row with content, shimmer placeholder, and error states
postsrs-architecture

Key behaviors

  • Feature gate: experimental flag enabled AND !site.isWPCom AND site.hasApplicationPassword()
  • All wordpress-rs FFI calls run on Dispatchers.IO
  • Tapping a post opens the editor (assuming the post exists in the FluxC store)
  • FAB and empty-state button for creating new posts
  • Pull-to-refresh, infinite scroll pagination, per-tab empty messages
  • Relative date formatting, HTML-stripped excerpts

Testing instructions

Enable RS Post List:

  1. Log in to a self-hosted WordPress site with application passwords
  2. Go to Me > App Settings > Experimental Features
  3. Enable "RS Post List"
  • The toggle saves and persists

Verify post list:

  1. Navigate to My Site > Posts
  • The new Compose-based post list opens (not the old one)
  • Published, Drafts, Scheduled, and Trashed tabs are visible
  1. Swipe between tabs
  • Each tab loads its posts correctly
  • Empty tabs show the appropriate empty message and "Create a post" button
  1. Pull down to refresh
  • Pull-to-refresh indicator appears and posts reload
  1. Scroll to the bottom of a tab with many posts
  • Loading indicator appears and more posts load
  1. Tap a post
  • The post editor opens with the correct post (assuming it exists in FluxC already)
  1. Tap the FAB or "Create a post" button
  • A new post editor opens
  1. Tap the back arrow
  • Returns to My Site
rsposts.mp4

nbradbury and others added 12 commits February 12, 2026 14:40
Introduce a new post list screen powered by wordpress-rs for self-hosted
sites with application passwords, gated behind an RS_POST_LIST
experimental feature flag. Uses WpSelfHostedService with observable
metadata collections for reactive data loading with caching, pagination,
and pull-to-refresh across Published, Drafts, Scheduled, and Trashed tabs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cription

- Register DatabaseChangeNotifier with WpApiCache so SQLite update hooks
  fire and observers get notified when data changes
- Load items explicitly after refresh/loadNextPage as a safety net
- Clear isLoading on refresh errors to prevent stuck loading state
- Use PrimaryScrollableTabRow to prevent tab labels from wrapping
- Update experimental feature description to mention application passwords

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All UniFFI/wordpress-rs operations (service creation, collection setup,
refresh, loadItems, loadNextPage, listInfo) now run on Dispatchers.IO
instead of blocking the main thread.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…button, strip HTML

- Reduce shimmer alpha range for a subtler placeholder effect
- Use existing per-tab empty state strings from the old post list
- Add "Create a post" button on empty state via ActivityLauncher
- Strip HTML tags from post excerpts using HtmlCompat

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Parse ISO 8601 dates and display as relative time spans
(e.g. "5 min ago", "2 days ago", "In 3 hours").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove PostRsListUiEvent sealed class, unused _events/events
SharedFlow, and unused refreshAllTabs() method.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace force-unwraps on site credentials with require() check
- Remove unused clearService() method
- Fix SimpleDateFormat thread safety by creating per-call instances
- Fix date timezone: post.date is site-local, not UTC
- Replace all hardcoded strings with string resources:
  - Status labels use existing post_status_* resources
  - Error messages use error_generic and existing strings
  - "(Untitled)" uses untitled_in_parentheses
  - "Failed to load post" and "Custom" status added as new resources
- Inject ResourceProvider into ViewModel for string resolution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bridges from wordpress-rs remote post IDs to FluxC local post IDs
by looking up or fetching the post via PostStore before launching
the editor. Also adds KDocs to key methods.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve all detekt issues: suppress TooGenericExceptionCaught for FFI
calls, refactor ReturnCount violations, and extract helpers to fix
LongMethod in initTab. Also add a FloatingActionButton for creating
new posts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use colorOnSurface/surface instead of colorPrimary to match the
existing WordPress.FloatingActionButton style used elsewhere.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dangermattic
Copy link
Collaborator

dangermattic commented Feb 13, 2026

4 Warnings
⚠️ This PR is larger than 300 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.
⚠️ Class PostRsListViewModel is missing tests, but unit-tests-exemption label was set to ignore this.
⚠️ Class WpSelfHostedServiceProvider is missing tests, but unit-tests-exemption label was set to ignore this.
⚠️ PR is not assigned to a milestone.

Generated by 🚫 Danger

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Feb 13, 2026

Project manifest changes for WordPress

The following changes in the WordPress's merged AndroidManifest.xml file were detected (build variant: jetpackVanillaRelease):

--- ./build/reports/diff_manifest/WordPress/jetpackVanillaRelease/base_manifest.txt	2026-02-14 16:24:54.689960206 +0000
+++ ./build/reports/diff_manifest/WordPress/jetpackVanillaRelease/head_manifest.txt	2026-02-14 16:24:57.379967288 +0000
@@ -599,6 +599,10 @@
             android:launchMode="singleTop"
             android:theme="@style/WordPress.NoActionBar" />
         <activity
+            android:name="org.wordpress.android.ui.postsrs.PostRsListActivity"
+            android:label=""
+            android:theme="@style/WordPress.NoActionBar" />
+        <activity
             android:name="org.wordpress.android.ui.pages.PagesActivity"
             android:label="@string/my_site_btn_site_pages"
             android:launchMode="singleTop"

Go to https://buildkite.com/automattic/wordpress-android/builds/25012/canvas?sid=019c5cf2-0588-4c19-99bb-8eec3717cf21, click on the Artifacts tab and audit the files.

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Feb 13, 2026

Project manifest changes for WordPress

The following changes in the WordPress's merged AndroidManifest.xml file were detected (build variant: wordpressVanillaRelease):

--- ./build/reports/diff_manifest/WordPress/wordpressVanillaRelease/base_manifest.txt	2026-02-14 16:23:42.083478583 +0000
+++ ./build/reports/diff_manifest/WordPress/wordpressVanillaRelease/head_manifest.txt	2026-02-14 16:23:44.193456187 +0000
@@ -405,6 +405,10 @@
             android:launchMode="singleTop"
             android:theme="@style/WordPress.NoActionBar" />
         <activity
+            android:name="org.wordpress.android.ui.postsrs.PostRsListActivity"
+            android:label=""
+            android:theme="@style/WordPress.NoActionBar" />
+        <activity
             android:name="org.wordpress.android.ui.pages.PagesActivity"
             android:label="@string/my_site_btn_site_pages"
             android:launchMode="singleTop"

Go to https://buildkite.com/automattic/wordpress-android/builds/25012/canvas?sid=019c5cf2-0587-420a-96b6-15365dac2349, click on the Artifacts tab and audit the files.

nbradbury and others added 4 commits February 13, 2026 07:36
…fety

- Save/restore pendingPostRemoteId across configuration changes
- Show a loading indicator while fetching a post for the editor
- Show a toast when post fetch fails
- Add @synchronized to getOrCreateCache for thread safety
- Document main-thread access invariant on ViewModel maps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@wpmobilebot
Copy link
Contributor

wpmobilebot commented Feb 13, 2026

App Icon📲 You can test the changes from this Pull Request in Jetpack Android by scanning the QR code below to install the corresponding build.

App NameJetpack Android
FlavorJalapeno
Build TypeDebug
Versionpr22596-34e098d
Build Number1484
Application IDcom.jetpack.android.prealpha
Commit34e098d
Installation URL43qu201njk4n0
Note: Google Login is not supported on these builds.

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Feb 13, 2026

App Icon📲 You can test the changes from this Pull Request in WordPress Android by scanning the QR code below to install the corresponding build.

App NameWordPress Android
FlavorJalapeno
Build TypeDebug
Versionpr22596-34e098d
Build Number1484
Application IDorg.wordpress.android.prealpha
Commit34e098d
Installation URL3qj75en3qqjd8
Note: Google Login is not supported on these builds.

nbradbury and others added 7 commits February 13, 2026 08:41
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
nbradbury and others added 13 commits February 13, 2026 10:25
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…lank line

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…status

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…Model

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Scheduled posts show an abbreviated absolute date, posts less than a
year old show relative time, and older posts show an absolute date.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The site-local date was being compared against UTC system time,
causing incorrect results like "In 4hr." for just-published posts.
Now uses post.dateGmt which is already a UTC Date object.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added verticalScroll to EmptyContent and ErrorContent so
PullToRefreshBox can detect the pull gesture via nested scrolling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nbradbury
Copy link
Contributor Author

@claude

@claude

This comment was marked as outdated.

nbradbury and others added 9 commits February 13, 2026 14:10
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nbradbury nbradbury marked this pull request as ready for review February 13, 2026 20:36
nbradbury and others added 2 commits February 14, 2026 08:42
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use snapshotFlow on settledPage instead of LaunchedEffect inside
the HorizontalPager content so that only the visible tab is
initialized on launch and others are deferred until swiped to.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants