Skip to content

RS unified comment detail screen - #23051

Merged
nbradbury merged 26 commits into
trunkfrom
feature/rs-unified-comments-detail
Jul 2, 2026
Merged

RS unified comment detail screen#23051
nbradbury merged 26 commits into
trunkfrom
feature/rs-unified-comments-detail

Conversation

@nbradbury

@nbradbury nbradbury commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Description

This PR is the first of several to complete the abandoned Unified Comments project and update it to use wordpress-rs. Here we add the unified comment detail screen, which was never worked on by the previous team.

The unified comment detail screen supports viewing, moderation (approve/unapprove, spam, trash, delete), reply (with @-mention suggestions, full-screen expand and draft autosave), like, edit, and a copy/share more-menu — styled to match the legacy comment detail.

Known limitations (to resolve when the comment list also moves to wordpress-rs):

  • A brand-new reply doesn't appear in the (FluxC) list until it's refreshed from the server.
  • Liking and the edit screen stay on FluxC.

Testing instructions

Requires a WP.com or self-hosted application-password site.

Enable the feature:

  1. Me → App Settings → Experimental Features
  • Verify "RS Unified Comment Detail" appears (debug builds only) and enable it

Open and act on a comment:

  1. My Site → Comments → tap a comment
  • Verify the detail opens and shows the comment, with a readable layout and a working back arrow
  1. Try Approve/Unapprove, Spam, Like, and the More menu (Edit, Trash, Copy link, Share, Delete)
  • Verify each action succeeds; an approved comment shows "Approved" with an accent-colored icon
  1. Trash or Spam a comment, then return to the list
  • Verify the list reflects the change
  1. Reply to a comment
  • Verify the reply is published (replying to a pending comment also approves it)

nbradbury and others added 10 commits June 30, 2026 08:31
Replace the abandoned unified comment detail stub with a working screen for
the site-comments path: view, moderation (approve/unapprove, spam, trash),
like, edit, and reply. The list auto-refreshes via the local comment cache.

Convert the UNIFIED_COMMENTS_DETAILS gate from a remote/build @feature flag to
a debug-only Experimental Feature, and drop the now-unused config + BuildConfig
field. Adds ViewModel unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Format the comment date as a relative time span via DateTimeUtilsWrapper
- Open the post in the Reader when the post title is tapped
- Approve an unapproved comment when replying to it (legacy parity)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Copy the comment link to the clipboard
- Share the comment link via a chooser
- Permanently delete a trashed/spam comment, with confirmation

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reuse the shared reader comment box for the reply input, adding:
- @-mention user suggestions (WP.com-accessed sites)
- full-screen reply expand
- per-comment draft autosave, cleared after a successful send

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Register UnifiedCommentDetailsViewModel in ViewModelModule (fixes a crash
  when opening the screen: "View model not found")
- Use explicit text sizes so the comment text is readable (the Material3
  textAppearance attrs didn't resolve in this activity's theme)
- Handle toolbar up navigation so the back arrow works
- Replace the action buttons with the legacy icon-button footer
  (Approve / Spam / Like / More), moving Edit, Trash, Copy link, Share
  and Delete permanently into the More overflow

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the unused binding field (inline the inflate) and the no-op
component().inject(this) call — the activity injects nothing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Style the footer's moderate and like buttons per state like the old
comment detail: accent-tinted icon + label at full opacity when on
(approved / liked), colorOnSurface at medium opacity when off. Approved
comments now read "Approved" rather than "Unapprove", and trashed
comments show "Restore" with the undo icon.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add CommentsRsDataSource (load/moderate/trash/delete/reply via the
wordpress-rs /wp/v2/comments endpoint, like postsrs/pagesrs) and rewrite
UnifiedCommentDetailsViewModel to use it. FluxC is kept for liking (rs has
no comment like action) and to resync the still-FluxC comment list cache
after each rs write. Tests updated to mock the rs data source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use a generic key (rs_unified_comments) so the flag can also gate a future
wordpress-rs comment list, label it "RS Unified Comment Detail", and base the
description on the New Pages List one (WP.com + application-password sites).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dangermattic

dangermattic commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator
2 Warnings
⚠️ This PR is larger than 300 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.
⚠️ PR is not assigned to a milestone.

Generated by 🚫 Danger

@nbradbury nbradbury changed the title RS unified comment detail screen (behind experimental flag) RS unified comment detail screen Jun 30, 2026
@wpmobilebot

wpmobilebot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

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
Build TypeDebug
Versionpr23051-be0a6b9
Build Number1498
Application IDcom.jetpack.android.prealpha
Commitbe0a6b9
Installation URL0p7ngnb2j2j38
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot

wpmobilebot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

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
Build TypeDebug
Versionpr23051-be0a6b9
Build Number1498
Application IDorg.wordpress.android.prealpha
Commitbe0a6b9
Installation URL5sck660t6q7lg
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

nbradbury and others added 2 commits June 30, 2026 15:56
Use INVISIBLE (not GONE) for the scroll view during load so its weighted
space is preserved, and show the Spam button up front instead of only once
the comment has loaded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Log the swallowed share ActivityNotFoundException, suppress the
intentional generic catch and guard-clause return count (matching the
existing rs code), and drop an unused test import.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 64.90385% with 73 lines in your changes missing coverage. Please review.
✅ Project coverage is 37.58%. Comparing base (46d50de) to head (be0a6b9).

Files with missing lines Patch % Lines
...comments/unified/UnifiedCommentDetailsViewModel.kt 73.46% 4 Missing and 35 partials ⚠️
...ndroid/ui/comments/unified/CommentsRsDataSource.kt 41.50% 31 Missing ⚠️
...org/wordpress/android/fluxc/store/CommentsStore.kt 0.00% 2 Missing ⚠️
...ava/org/wordpress/android/ui/ActivityLauncher.java 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            trunk   #23051      +/-   ##
==========================================
+ Coverage   37.53%   37.58%   +0.04%     
==========================================
  Files        2327     2328       +1     
  Lines      125628   125829     +201     
  Branches    17253    17320      +67     
==========================================
+ Hits        47159    47289     +130     
- Misses      74632    74668      +36     
- Partials     3837     3872      +35     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

nbradbury and others added 6 commits June 30, 2026 17:58
Return an RsResult carrying WpError.errorMessage from CommentsRsDataSource
writes and show that server message on moderation/reply failures (falling
back to the generic string). Extract the rs<->app comment status mapping to
testable functions and add CommentsRsStatusMapperTest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Use the comment's GMT date for relative time so it's correct on non-UTC
  sites (WpGmtDateTime is an absolute Date; the local date field had no offset)
- Guard onReplyClicked against a second in-flight reply
- Remove the comment from the FluxC cache on permanent delete instead of
  leaving an orphaned "deleted" row (new CommentsStore.removeCommentByRemoteId)
- Drop the fragment's redundant currentState shadow; read uiState.value directly

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…backs

Extract the repeated no-network check into an isOffline() helper, collapse the
onConfirm/onCollapse null guards, and fix an import ordering.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…etwork

The rs comment detail loads over the network, so guard in the activity's
onCreate: when offline, show a toast and finish before the screen appears.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
It was mapped from the rs response but never read (createReply uses the
comment's remote id as the parent).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@nbradbury
nbradbury marked this pull request as ready for review July 1, 2026 14:42
@nbradbury
nbradbury requested a review from adalpari July 1, 2026 14:42
nbradbury and others added 4 commits July 1, 2026 10:51
Some sites reject DELETE ?force=false with "Invalid parameter(s): force".
Route trash through update(status=trash) like spam/approve, and drop the
now-unused CommentsRsDataSource.trash().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tint the Trash and Delete permanently more-menu items and the "Trashed"
status label with the error colour, matching the legacy comment detail.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@adalpari

adalpari commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Clauide is pointing some potential bugs. Could you have a look at them and see if they are false positives?

Correctness bugs

1. Moderation during loading acts on the wrong status (silent data mutation)

UnifiedCommentDetailsFragment.kt:237 · CONFIRMED

The action footer (Approve/Spam/Like) stays visible and clickable while the comment is still loading — only the scroll view is hidden. The ViewModel handlers don't guard on loadedComment, so currentStatus() returns the default CommentStatus.ALL.

Failure: Open a TRASH or SPAM comment and tap moderate before load completes → onApproveClicked computes newStatus=APPROVED and un-trashes/un-spams the comment server-side, silently changing its status to the wrong value.

---
2. Like always fails on self-hosted app-password sites

UnifiedCommentDetailsFragment.kt:128 · CONFIRMED

The Like button is always shown, but commentsStore.likeComment() rejects non-WPCom-REST sites. This feature explicitly supports self-hosted application-password sites, where site.isUsingWpComRestApi is false.

Failure: Tap Like on a self-hosted site → likeComment returns CommentError(INVALID_INPUT, "Can't like a comment on XMLRPC API"), the optimistic flag reverts, and the user sees an error snackbar every time. Legacy CommentDetailFragment simply hid the button for these sites.

---
3. Post-title Reader opens with blogId=0 on self-hosted sites

UnifiedCommentDetailsViewModel.kt:164 (also :161) · CONFIRMED

onPostTitleClicked uses site.siteId as the Reader blog id, which is 0 for self-hosted app-password sites (supported per the class doc).

Failure: Tap the post title → OpenPostInReader(0, postId) launches the Reader with blogId=0, which can't be resolved → error/blank screen instead of the post.

---
4. Like double-tap race

UnifiedCommentDetailsViewModel.kt:134 · CONFIRMED

onLikeClicked has no in-flight guard and derives the target state from optimistic uiState.

Failure: Fast double-tap → first tap sends like=true, second reads isLiked=true and sends like=false before the first resolves. Racing requests can settle in either order, desyncing UI from server. (Note: onReplyClicked already got an in-flight guard in this PR — Like didn't.)

---
5. Approve-after-reply failure is swallowed

UnifiedCommentDetailsViewModel.kt:202 · CONFIRMED

Failure: Reply to an unapproved comment; reply succeeds, then approveAfterReply() runs moderate(APPROVED). If that returns RsResult.Error, the code silently reverts status to UNAPPROVED with no snackbar — user believes the comment was approved (legacy parity behavior) while it stays pending.

nbradbury and others added 4 commits July 2, 2026 07:39
- Ignore moderation taps while the comment is still loading: the footer
  stays visible during load and the default ui state would compute (and
  apply server-side) the wrong target status
- Hide the Like button on self-hosted application-password sites, where
  FluxC's likeComment always fails (legacy canLike parity)
- Don't open the Reader from the post title when the site has no WP.com
  blog id (self-hosted app-password sites), since it can't resolve the post
- Guard onLikeClicked against a second tap while a like is in flight,
  matching the existing reply guard

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Don't reset the ui state when refreshing after an edit: moderation/like
  during the reload acted on a default status (wrong toggle direction),
  and a failed refresh blanked the screen and force-closed it
- Fall back to the legacy detail on sites the rs client can't authenticate
  (no WP.com REST and no application password), like the rs pages gating
- Render the comment body with the shared comment HTML renderer (inline
  images, emoticons, whitespace trim) and tappable links, and only
  re-parse when the HTML changes
- Confirm before trashing, since the rs path commits immediately and has
  no undo affordance like the legacy list flow
- Run the offline bail-out only on first launch so rotating/restoring an
  already-loaded screen while offline doesn't kill it
- Key reply drafts and the full-screen dialog tag by the local site id;
  the WP.com blog id is 0 for all self-hosted app-password sites, which
  collided drafts across sites

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@nbradbury

Copy link
Copy Markdown
Contributor Author

@adalpari I fixed all but #5, then updated to Fable 5 and ran a review which uncovered some more issues. Everything has been addressed so ready for round two.

@adalpari adalpari left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

@nbradbury
nbradbury merged commit 4c38cbe into trunk Jul 2, 2026
27 of 28 checks passed
@nbradbury
nbradbury deleted the feature/rs-unified-comments-detail branch July 2, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants