Skip to content

refactor(messagesearch): replace FlexibleAdapter with ListAdapter - #6789

Open
rapterjet2004 wants to merge 1 commit into
masterfrom
issue-3108-remove-flexible-adapter-lib
Open

rapterjet2004 wants to merge 1 commit into
masterfrom
issue-3108-remove-flexible-adapter-lib

Conversation

@rapterjet2004

@rapterjet2004 rapterjet2004 commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Message search was the only remaining FlexibleAdapter user. Replace it with a plain ListAdapter/DiffUtil implementation, swap FlexibleUtils' null-safe lowercase in TalkSpecificViewThemeUtils, and drop the dependency along with dead items (ContactItem, SpacerItem, header items) and their layouts.

Assisted-by: Claude Code:claude-opus-5-5

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔖 Capability is checked or not needed
  • 🔙 Backport requests are created or not needed: /backport to stable-xx.x
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

Message search was the only remaining FlexibleAdapter user. Replace it
with a plain ListAdapter/DiffUtil implementation, swap FlexibleUtils'
null-safe lowercase in TalkSpecificViewThemeUtils, and drop the
dependency along with dead items (ContactItem, SpacerItem, header items)
and their layouts.

Assisted-by: Claude Code:claude-opus-5-5

Signed-off-by: rapterjet2004 <juliuslinus1@gmail.com>
@rapterjet2004 rapterjet2004 added the 3. to review Waiting for reviews label Sep 29, 2026
@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

📝 Walkthrough

Walkthrough

Message search now uses MessageSearchAdapter, which binds result and load-more rows and receives their click events. The activity submits results and adds a load-more row when more results are available. FlexibleAdapter dependencies, related adapter items, and layouts were removed. Text highlighting now uses default-locale lowercase conversion.

Priority: ⬇️ Low

Merge Risk: 🔵 Low · up to 6ebf1

Search results without message IDs may show incorrect row transitions when results update. The issue is bounded, but the identity comparison should be corrected before merging if practical.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 6ebf1

The change does not appear to expand access to messages or introduce a new external entry point. Search-result handling remains in the existing account and search flow. Some edge-case guarantees for result identity and text highlighting remain unverified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The changed click path affects displayed search results for the current account, not the repository credential or room-scoping path. Shared highlighting also affects mention-autocomplete and participant-name presentation.

Trust Boundaries and Controls

  • observed — Result and load-more clicks pass through the Activity's ViewModel callbacks rather than gaining direct repository access in the new adapter. The helper continues to supply credentials and optional room scoping to searches.

Resilience and Maintainability Implications

  • observed — Load-more clicks are not deduplicated in the adapter; the ViewModel cancels and restarts the request. The prior click path also invoked loadMore without a click guard, so this is not established as a PR-introduced exposure.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 14 functions across 3 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the main change: replacing FlexibleAdapter with ListAdapter in message search.
Description check ✅ Passed The description explains the main changes, references the issue, includes the checklist, and records AI assistance. It omits the template's screenshots and TODO sections, but the required change summa…
  • Fix all pre-merge checks with AI

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 18a28150-2811-4a6d-8c37-2a095fbb6b4b

📥 Commits

Reviewing files that changed from the base of the PR and between 5c28bcb and 6ebf1f5.

📒 Files selected for processing (15)
  • app/build.gradle.kts
  • app/src/main/java/com/nextcloud/talk/adapters/items/ContactItem.kt
  • app/src/main/java/com/nextcloud/talk/adapters/items/FlexibleItemViewType.kt
  • app/src/main/java/com/nextcloud/talk/adapters/items/GenericTextHeaderItem.kt
  • app/src/main/java/com/nextcloud/talk/adapters/items/LoadMoreResultsItem.kt
  • app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt
  • app/src/main/java/com/nextcloud/talk/adapters/items/MessagesTextHeaderItem.kt
  • app/src/main/java/com/nextcloud/talk/adapters/items/SpacerItem.kt
  • app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt
  • app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchAdapter.kt
  • app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt
  • app/src/main/res/layout/item_spacer.xml
  • app/src/main/res/layout/rv_item_contact.xml
  • app/src/main/res/layout/rv_item_title_header.xml
  • gradle/verification-metadata.xml
💤 Files with no reviewable changes (12)
  • app/src/main/res/layout/rv_item_title_header.xml
  • app/src/main/res/layout/rv_item_contact.xml
  • app/src/main/java/com/nextcloud/talk/adapters/items/MessagesTextHeaderItem.kt
  • app/src/main/res/layout/item_spacer.xml
  • app/build.gradle.kts
  • app/src/main/java/com/nextcloud/talk/adapters/items/LoadMoreResultsItem.kt
  • app/src/main/java/com/nextcloud/talk/adapters/items/FlexibleItemViewType.kt
  • app/src/main/java/com/nextcloud/talk/adapters/items/GenericTextHeaderItem.kt
  • gradle/verification-metadata.xml
  • app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt
  • app/src/main/java/com/nextcloud/talk/adapters/items/SpacerItem.kt
  • app/src/main/java/com/nextcloud/talk/adapters/items/ContactItem.kt

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

Comment on lines +82 to +98
private object ItemCallback : DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean =
when {
oldItem is Item.Result && newItem is Item.Result ->
oldItem.entry.conversationToken == newItem.entry.conversationToken &&
oldItem.entry.messageId == newItem.entry.messageId
else -> oldItem == newItem
}

override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean = oldItem == newItem
}

companion object {
private const val VIEW_TYPE_RESULT = 0
private const val VIEW_TYPE_LOAD_MORE = 1
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

sed -n '75,100p' app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchAdapter.kt
rg -n 'SearchMessageEntry\(' app/src/main/java/com/nextcloud/talk

Repository: nextcloud/talk-android

Length of output: 1332


🏁 Script executed:

set -eu
printf '%s\n' '--- adapter ---'
cat -n app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchAdapter.kt
printf '%s\n' '--- model ---'
cat -n app/src/main/java/com/nextcloud/talk/models/domain/SearchMessageEntry.kt
printf '%s\n' '--- producer ---'
cat -n app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepositoryImpl.kt
printf '%s\n' '--- result references ---'
rg -n -C 4 'MessageSearchAdapter|submitList|SearchMessageEntry|messageId|conversationToken' app/src/main/java/com/nextcloud/talk/messagesearch app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch app/src/main/java/com/nextcloud/talk/models/domain app/src/test app/src/androidTest 2>/dev/null || true

Repository: nextcloud/talk-android

Length of output: 41890


🏁 Script executed:

set -eu
printf '%s\n' '--- helper ---'
cat -n app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchHelper.kt
printf '%s\n' '--- activity relevant ---'
sed -n '110,150p' app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt | cat -n
printf '%s\n' '--- view model selection ---'
rg -n -C 6 'selectMessage|FinishedState|messageEntry.messageId' app/src/main/java/com/nextcloud/talk/messagesearch
printf '%s\n' '--- DTO ---'
rg -n -C 5 'data class UnifiedSearchEntryDto|class UnifiedSearchEntryDto|attributes' app/src/main/java/com/nextcloud/talk/models/json/unifiedsearch app/src/test/java/com/nextcloud/talk/messagesearch
printf '%s\n' '--- relevant diff ---'
git diff 5c28bcbce6bc377698a5593e66201a2ba75bddd4 6ebf1f52e5c98c58cfa88e8999a22ffe9bfa8051 -- app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchAdapter.kt

Repository: nextcloud/talk-android

Length of output: 21983


Do not treat null messageId values as shared result identities.

messageId is nullable, and the search pipeline keeps entries without it. Two entries from the same conversation can therefore satisfy the current identity comparison. This violates DiffUtil.ItemCallback’s unique-item identity contract and can produce incorrect row matching or animations. Changed content is still compared separately, so stale content or click selection is not established as the consequence.

Suggested fix
                 oldItem is Item.Result && newItem is Item.Result ->
-                    oldItem.entry.conversationToken == newItem.entry.conversationToken &&
+                    oldItem.entry.messageId != null &&
+                        oldItem.entry.conversationToken == newItem.entry.conversationToken &&
                         oldItem.entry.messageId == newItem.entry.messageId
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private object ItemCallback : DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean =
when {
oldItem is Item.Result && newItem is Item.Result ->
oldItem.entry.conversationToken == newItem.entry.conversationToken &&
oldItem.entry.messageId == newItem.entry.messageId
else -> oldItem == newItem
}
override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean = oldItem == newItem
}
companion object {
private const val VIEW_TYPE_RESULT = 0
private const val VIEW_TYPE_LOAD_MORE = 1
}
}
private object ItemCallback : DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean =
when {
oldItem is Item.Result && newItem is Item.Result ->
oldItem.entry.messageId != null &&
oldItem.entry.conversationToken == newItem.entry.conversationToken &&
oldItem.entry.messageId == newItem.entry.messageId
else -> oldItem == newItem
}
override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean = oldItem == newItem
}
companion object {
private const val VIEW_TYPE_RESULT = 0
private const val VIEW_TYPE_LOAD_MORE = 1
}
}

@github-actions

Copy link
Copy Markdown
Contributor

📱 QA build

Download app-qa-debug.apk
QR code Open the QR code for this download
Commit 6ebf1f5
Version 6789
Available until 7 days after this build

The QA build installs alongside a released Nextcloud app, so you can keep
using your existing install while testing.

Downloading the file requires a GitHub account, so open this link on the
device you want to test on, or transfer the APK to it.

@github-actions

Copy link
Copy Markdown
Contributor

Codacy

Lint

TypemasterPR
Warnings138140
Errors1818

SpotBugs

CategoryBaseNew
Bad practice77
Correctness1111
Dodgy code4545
Internationalization33
Malicious code vulnerability33
Performance88
Security1111
Total8888

Lint increased!

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

Labels

3. to review Waiting for reviews AI assisted

Projects

None yet

Development

Successfully merging this pull request may close these issues.

remove FlexibleAdapter lib

1 participant