Skip to content

Feature/convov3 pt3#2036

Merged
ThomasSession merged 29 commits into
devfrom
feature/convov3-pt3
Mar 15, 2026
Merged

Feature/convov3 pt3#2036
ThomasSession merged 29 commits into
devfrom
feature/convov3-pt3

Conversation

@ThomasSession

Copy link
Copy Markdown
Collaborator

More ConversationV3 logic.
Added dialogs
Scroll logic - Including to specific quoted messages
Highlight logic
Expandable "read more" messages
"Jump to bottom" button and logic
Cleaned up logic around UI rules, pagination and message access
Immutable lists to cater for Stable compose state

Copilot AI 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.

Pull request overview

This PR expands the ConversationV3 Compose implementation with richer message rendering and conversation-level behaviors (dialogs, scrolling/highlighting, expandable text), and refactors state models to use immutable collections for better Compose stability.

Changes:

  • Introduces centralized scroll/highlight control (scroll-to-message, jump-to-bottom, highlight animation, scroll state reporting).
  • Adds new UI components and behaviors (dialogs layer, quote-tap scrolling, link open command, expandable “read more” text).
  • Migrates various Compose view-state models to kotlinx.collections.immutable and adds stability annotations.

Reviewed changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
gradle/libs.versions.toml Adds version/catalog entry for kotlinx-collections-immutable.
app/build.gradle.kts Adds kotlinx-collections-immutable dependency to the app module.
app/src/test/java/org/thoughtcrime/securesms/conversation/v3/ConversationUIRulesTest.kt Adds unit tests for ConversationV3 UI rules (date breaks, clustering, author label).
app/src/main/java/org/thoughtcrime/securesms/util/AvatarUtils.kt Marks avatar UI models immutable and switches to ImmutableList for Compose stability.
app/src/main/java/org/thoughtcrime/securesms/mediasend/MediaSendViewModel.kt Updates InputbarViewModel constructor call to use context parameter.
app/src/main/java/org/thoughtcrime/securesms/database/model/Quote.kt Adds quoteMessageId to support scroll-to-quoted-message behavior.
app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt Populates quoteMessageId when building Quote from DB data.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/compose/message/MessageText.kt Adds optional onTextLayout callback forwarding; minor refactor of imports/comments.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/compose/message/MessageQuote.kt Adds quote click handling and introduces QuoteMessageData with quotedMessageId.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/compose/message/MessageMedia.kt Switches media items to ImmutableList and updates previews/highlight plumbing.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/compose/message/MessageLink.kt Makes link previews clickable and routes open-url via ConversationCommand.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/compose/message/MessageEffects.kt Adds rememberHighlightAlpha() and refactors highlight modifier to accept alpha directly.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/compose/message/ExpandableMessageText.kt Adds expandable “read more” message text with measurement-based expansion delta.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/compose/message/DocumentMessage.kt Updates previews to use persistent immutable lists.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/compose/message/BaseMessage.kt Thread-wide refactor: immutable state models, highlight plumbing, quote/link commands, expandable text wiring.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/compose/conversation/ConversationV3Dialogs.kt Adds a ConversationV3 dialogs layer for URL, delete, emoji clear, community join, etc.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/compose/conversation/ConversationScreen.kt Adds list controller integration, scroll-to-bottom button, scroll state reporting, and dialogs hookup.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/compose/conversation/ConversationListState.kt Introduces a single controller for paging-aware scroll-to-message and highlight targeting.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/compose/conversation/ConversationElements.kt Adds scroll-to-bottom UI component and adjusts unread marker spacing.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/ConversationV3ViewModel.kt Introduces scroll events, unread badge logic, dialogs state, and unifies commands under ConversationCommand.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/ConversationV3NavHost.kt Adds support for a pending “scroll to message” request on entry, with consume callback.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/ConversationUIRules.kt Adds centralized UI rules for clustering/date breaks/author labels.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/ConversationPagingSource.kt Adds overlap-window paging to compute correct UI state across page boundaries.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/ConversationModels.kt Adds new ConversationV3 models for scroll state and dialog state.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/ConversationDataMapper.kt Uses ConversationUIRules, emits immutable content groups, and maps quoted message IDs for scrolling.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/ConversationCommand.kt Adds a unified command contract for navigation/screen/message/dialog actions.
app/src/main/java/org/thoughtcrime/securesms/conversation/v3/ConversationActivityV3.kt Adds pending-scroll handling via intent extras and forwards to nav host.
app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt Updates InputbarViewModel constructor call to use context parameter.
app/src/main/java/org/thoughtcrime/securesms/InputbarViewModel.kt Switches dependency from Application to Context for string/resource access.
.gitignore Fixes script negation line formatting and ignores /.claude.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@SessionHero01

This comment was marked as resolved.

if (lastLoadedIndex < 0) return false

val previousItemCount = itemCount
this[lastLoadedIndex]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What does this statement do?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Had to figure this out but with LazyPagingItems, get(index) notifies Paging that this item was accessed, which can trigger prefetch/append work.
Here that is being used on purpose to “touch” the end of the loaded snapshot so Paging loads the next older page.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

A comment here would prevent an accidental removal of this statement in the future.

animateToPositioned(index)
} else {
lazyListState.scrollToItem(index)
yield()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Interesting...what does this one do? And how reliable is to say after yielding, the list state is at the final destination?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Its the coroutine yield. It can help assure the items are laid out though there can't be any guarantee.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It should give Compose a chance to re-measure after the instant scroll

Comment on lines +319 to +324
for (i in snapshot.indices) {
if ((snapshot[i] as? ConversationItem.Message)?.data?.id == messageId) {
return i
}
}
return null

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Isn't there a indexOfFirstOrNull for this kind of thing?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I don't think so. Is there a particular one you're thinking of?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I thought there is a snapshot.indexOfFirstOrNull Kotlin official function you can call to find out the first index that satisfies a lambda. But perhaps the snapshot here is not a List?

/**
* Helper class for UI rules in the conversation
*/
internal object ConversationUIRules {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't think we need to use internal - we are in the app module and it doesnt make sense

@ThomasSession
ThomasSession enabled auto-merge March 11, 2026 23:40
@ThomasSession
ThomasSession merged commit fdd992f into dev Mar 15, 2026
9 of 10 checks passed
@ThomasSession
ThomasSession deleted the feature/convov3-pt3 branch March 15, 2026 22:31
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.

3 participants