Repository navigation
feat(chat): let an eligible non-member read a group before joining - #1537
Merged
Merged
Conversation
A viewer outside a group who meets its balance rule saw the same blurred transcript as one who didn't, because isGatedPreview answered both "blur the transcript" and "replace the composer". It is now two flags, named after iOS's ConversationGatePresentation: obscuresTranscript and replacesComposer. The blur comes off only when the viewer is a known non-member, the access is Eligible, and the group has listener rules. messaging.v1.ViewMode only serves a non-member the full transcript when they satisfy a listener rule; a group with no listener rules also computes Eligible but has nothing to serve. A null access or null membership keeps the blur, so the transcript fades in after the balance arrives rather than showing and then blurring. A balance that drops re-emits Blocked and the blur returns. openTranscript skips groups the viewer is outside of, and the group feed only carries joined groups, so nothing loaded an eligible viewer's messages. They are now fetched when readsFromOutside turns true, keyed on the chat so a dip and recovery refetches. Two guards follow from showing messages to a non-member: the read pointer is not advanced for someone outside the group, since the server refuses it, and OnGroupResolved clears groupAccess when membership or rules change, so a stale Eligible cannot unblur a group whose rules tightened. A succeeded join no longer holds the blur. The only viewer who can join is already reading the transcript sharp. It still holds the composer back through the checkmark.
bmc08gt
added a commit
that referenced
this pull request
Sep 23, 2026
#1540) With #1537 an eligible non-member reads a group's transcript under the Join gate, but resolveCapabilities looks only at the message, so every other person's message offered Reply. Swiping or picking Reply set replyingTo behind the gate, and the composer later opened quoting it once the viewer joined. A former member's own messages offered Edit and Delete the same way. resolveCapabilities takes canPost, and a viewer who cannot post keeps only Copy and Report. The transcript mapping re-runs when the viewer joins or leaves, and the selection bar narrows its set with readOnly() if they left after the row was mapped, as it already does with the edit and delete windows.
bmc08gt
added a commit
that referenced
this pull request
Sep 23, 2026
* feat(chat): let an eligible non-member read a group before joining A viewer outside a group who meets its balance rule saw the same blurred transcript as one who didn't, because isGatedPreview answered both "blur the transcript" and "replace the composer". It is now two flags, named after iOS's ConversationGatePresentation: obscuresTranscript and replacesComposer. The blur comes off only when the viewer is a known non-member, the access is Eligible, and the group has listener rules. messaging.v1.ViewMode only serves a non-member the full transcript when they satisfy a listener rule; a group with no listener rules also computes Eligible but has nothing to serve. A null access or null membership keeps the blur, so the transcript fades in after the balance arrives rather than showing and then blurring. A balance that drops re-emits Blocked and the blur returns. openTranscript skips groups the viewer is outside of, and the group feed only carries joined groups, so nothing loaded an eligible viewer's messages. They are now fetched when readsFromOutside turns true, keyed on the chat so a dip and recovery refetches. Two guards follow from showing messages to a non-member: the read pointer is not advanced for someone outside the group, since the server refuses it, and OnGroupResolved clears groupAccess when membership or rules change, so a stale Eligible cannot unblur a group whose rules tightened. A succeeded join no longer holds the blur. The only viewer who can join is already reading the transcript sharp. It still holds the composer back through the checkmark. * fix(chat): stop a viewer outside a group from replying to its messages With #1537 an eligible non-member reads a group's transcript under the Join gate, but resolveCapabilities looks only at the message, so every other person's message offered Reply. Swiping or picking Reply set replyingTo behind the gate, and the composer later opened quoting it once the viewer joined. A former member's own messages offered Edit and Delete the same way. resolveCapabilities takes canPost, and a viewer who cannot post keeps only Copy and Report. The transcript mapping re-runs when the viewer joins or leaves, and the selection bar narrows its set with readOnly() if they left after the row was mapped, as it already does with the edit and delete windows.
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.
A viewer outside a group who meets its balance rule (
GroupAccess.Eligible) saw the same blurred transcript as one who didn't.isGatedPreviewanswered two questions at once: whether to blur the transcript and whether the Join gate replaces the composer. This splits it intoobscuresTranscriptandreplacesComposer, the same split iOS makes inConversationGatePresentation.When the blur comes off
readsFromOutsideis true only when all of these hold:isMember == false)groupAccessisEligibleThe last condition comes from
messaging.v1.ViewMode. The server gives a non-member the full transcript only when they satisfy a listener rule. A group with no listener rules also computesEligible, but there is nothing to read.Anything unknown keeps the blur on. A null
groupAccessor a null membership stays blurred, so an eligible viewer sees the blur fade once the balance arrives. It never goes from sharp to blurred.BlurredContentalready snaps on and fades off.Blockedstays blurred. If the balance drops while the chat is open, access goes back toBlockedand the blur returns.The gate still replaces the composer for every viewer outside the group, eligible or not.
Loading the transcript
Before this change, nothing fetched an eligible viewer's messages.
openTranscriptskips groups the viewer is outside of, and the group feed only includes joined groups. The view model now callsloadMessageswhenreadsFromOutsideturns true. The call is keyed on the chat, so a balance that dips and recovers triggers a new fetch.Guards
OnGroupResolvedresetsgroupAccessto null when membership or rules change. Without this, a staleEligiblecould unblur a group whose rules just tightened until the balance flow caught up.Join success
joinProgress.successno longer holds the blur, because only an eligible viewer can join and that viewer already sees the transcript sharp. It still keeps the composer back until the checkmark finishes.Tests
ChatViewModelStateTestcovers: Eligible is unblurred with the gate shown, null access is blurred, Blocked is blurred, Eligible → Blocked brings the blur back, a group with no listener rules stays blurred, and the join hold.GroupTranscriptScreenshotTest.rendersTranscriptFromOutsideTheGroupdraws Eligible, Blocked and pending access side by side. Robolectric can't draw Haze orModifier.blur, so a heavy fade stands in for the blur there.