Skip to content

Add configurable character limits for poll creation#6429

Closed
ryanhurststrava wants to merge 3 commits into
GetStream:v6from
ryanhurststrava:feature/poll_character_limits
Closed

Add configurable character limits for poll creation#6429
ryanhurststrava wants to merge 3 commits into
GetStream:v6from
ryanhurststrava:feature/poll_character_limits

Conversation

@ryanhurststrava
Copy link
Copy Markdown

@ryanhurststrava ryanhurststrava commented May 8, 2026

🎯 Goal

Add configurable character limits for poll titles and answer options to provide developers with flexibility in controlling input length during poll creation. This addresses use cases where different character limits may be needed based on UI constraints, localization requirements, or business rules.

🛠 Implementation

New Files Created

CreatePollDialogFragment.kt

  • Added new dialog fragment for creating polls with configurable character limits
  • Implemented newInstance() factory method with optional titleCharLimit and answerCharLimit parameters
  • Parameters are passed via Bundle arguments using keys ARG_TITLE_CHAR_LIMIT and ARG_ANSWER_CHAR_LIMIT
  • Applied character limits using InputFilter.LengthFilter on the question/title EditText field
  • Added CreatePollDialogListener interface for handling poll creation and dismissal events
  • Integrated with CreatePollViewModel for state management
  • Full edge-to-edge UI support with proper inset handling

OptionsAdapter.kt

  • Created RecyclerView adapter for poll options with configurable character limits
  • Accepts optional answerCharLimit parameter that's applied to each option EditText
  • Implements InputFilter.LengthFilter for answer options when limit is specified
  • Includes duplicate detection and error state management
  • Uses DiffUtil for efficient list updates
  • Stable IDs implementation for better RecyclerView performance

Key Design Decisions

  • Backward Compatibility: Both titleCharLimit and answerCharLimit default to null, meaning no character limit is enforced unless explicitly specified
  • Optional Configuration: Developers can choose to set limits on title only, answers only, both, or neither
  • InputFilter Approach: Used Android's native InputFilter.LengthFilter for enforcing limits at the input level

Usage Example

CreatePollDialogFragment.newInstance(
    createPollDialogListener = myListener,
    titleCharLimit = 100,
    answerCharLimit = 50
)

📱 UI Changes

Character limits are enforced at the input level using InputFilter, preventing users from exceeding the specified length.

1778192411.mp4

✅ Testing

Manual Testing Steps

  1. Create a poll without specifying limits - verify no restrictions are applied (backward compatibility)
  2. Create a poll with titleCharLimit = 100 - verify title input stops at 100 characters
  3. Create a poll with answerCharLimit = 50 - verify each answer option stops at 50 characters
  4. Create a poll with both limits - verify both work independently
  5. Test edge cases: limit of 1, very large limits, and null values

Expected Behavior

  • When limits are not specified, users can type unlimited characters (default behavior)
  • When limits are specified, InputFilter.LengthFilter prevents input beyond the limit
  • No runtime crashes or errors when switching between limited and unlimited configurations

Contributor Checklist

  • I have signed the Stream CLA (required)
  • I have assigned a reviewer (code owner)
  • This PR targets the develop branch
  • I have added unit tests for new code
  • I have updated documentation (KDocs included in code)

Reviewer Checklist

  • UI Components sample app has been tested
  • Compose sample app has been tested (N/A - UI Components only)
  • Visuals are correct
  • Feature is validated
  • KDocs have been reviewed
  • SDK size impact has been reviewed from CI logs

🎉 GIF

Screenshots and demo to be added

Co-Authored-By: Claude Sonnet 4.5 noreply@anthropic.com

Summary by CodeRabbit

  • New Features
    • Poll questions now support optional character limit configuration
    • Poll answer options now support optional character limit configuration
    • These limits help maintain consistent formatting and prevent excessively long poll content

Review Change Stack

Add optional titleCharLimit and answerCharLimit parameters to CreatePollDialogFragment.newInstance() to allow developers to enforce character limits on poll titles and answer options during
poll creation.

The limits are passed via Bundle arguments and applied using InputFilter.LengthFilter. Both parameters default to null, meaning no character limit is enforced by default, maintaining backward
compatibility.

Usage:
CreatePollDialogFragment.newInstance(
  listener,
  titleCharLimit = 100,
  answerCharLimit = 50
)
@ryanhurststrava ryanhurststrava requested a review from a team as a code owner May 8, 2026 17:06
@ryanhurststrava ryanhurststrava changed the base branch from develop to v6 May 8, 2026 17:14
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Walkthrough

This PR adds optional character limit constraints to poll creation dialogs. The CreatePollDialogFragment.newInstance() method now accepts questionTextLimit and optionTextLimit parameters, storing them in fragment arguments. The poll question field and answer option fields apply InputFilter.LengthFilter when limits are provided via arguments and adapter parameters.

Changes

Poll Creation UI Character Limits

Layer / File(s) Summary
Options Adapter Text Limit Support
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/poll/OptionsAdapter.kt
OptionsAdapter constructor adds optional optionTextLimit: Int? parameter; secondary constructor overload maintains backward compatibility. OptionViewHolder conditionally applies InputFilter.LengthFilter to option text field when limit is non-null. onCreateViewHolder passes limit to each OptionViewHolder.
Poll Dialog Limit Configuration
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/poll/CreatePollDialogFragment.kt
newInstance() factory method expanded with @JvmOverloads to accept optional questionTextLimit and optionTextLimit parameters; stores non-null values in fragment arguments. setupDialog() reads question limit and applies InputFilter.LengthFilter to question input field. Lazy optionsAdapter initialization reads option limit from arguments and passes to adapter. Argument key constants added to companion object.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A poll so fine, with limits in place,
Question and answer fields keep their space,
Fragment arguments carry the size with care,
InputFilters guard each constraint so fair,
Character counts dance, balanced with grace!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding configurable character limits for poll creation in the Android UI components.
Description check ✅ Passed The description is well-structured and comprehensive, covering Goal, Implementation with detailed file descriptions, design decisions, usage examples, UI changes, manual testing steps, and contributor/reviewer checklists. However, unit tests and sample app testing remain incomplete.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

Copy link
Copy Markdown
Contributor

@VelikovPetar VelikovPetar left a comment

Choose a reason for hiding this comment

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

Hi @ryanhurststrava,

Thank you for the contribution!

I've left several remarks concerning breaking changes in the public API, which we are not allowed to make, unless it is a major release.

I have an additional question: The changes that you made, indicate that you directly call CreatePollDialogFragment.newInstance from your code - can you confirm that my assumption is correct?
Otherwise, there is no other public way to inject these values, which means that if you are using the XML SDK as-is (without intercepting the poll click), it will not be possible to customise these values.

.setCreatePollDialogListener(createPollDialogListener)
public fun newInstance(
createPollDialogListener: CreatePollDialogListener,
titleCharLimit: Int? = null,
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.

Adding the titleChatLimit/answerChatLimit here would also be a breaking change for Java integrations. We should add a JvmOverloads annotation to this method, so that the corresponding overloads are generated for the java code.
I would also rename the parameters to:

  1. questionTextLimit
  2. optionTextLimit
    To be better aligned with the class naming.

import io.getstream.chat.android.ui.utils.extensions.streamThemeInflater

public class OptionsAdapter(
private val answerCharLimit: Int?,
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.

As the OptionsAdapter is a public facing API, adding a mandatory argument at the start of the constructor would be a breaking change as customers who potentially instantiate the OptionsAdapter manually, would encounter a compilation error when updating the SDK.

In order to prevent a breaking both for Java and Kotlin consumers of the API, we would need to introduce a secondary constructor:

// Extend the default constructor with the new parameter

public class OptionsAdapter(
    private val optionTextLimit: Int?,
    private val onOptionChange: (id: Int, text: String) -> Unit,
) : ListAdapter<PollAnswer, OptionsAdapter.OptionViewHolder>(OptionDiffCallback) {

    // Introduce the new constructor, mimicking the original, primary constructor signature

    /**
     * Builds an [OptionsAdapter] instance without providing option text limit.
     *
     * @param onOptionChange Callback invoked when the option text changes.
     */
    public constructor(onOptionChange: (id: Int, text: String) -> Unit) : this(null, onOptionChange)


    // ....
}

I would also rename the answerCharLimit to optionTextLimit, to be a bit more aligned with the class name.

@VelikovPetar VelikovPetar added pr:new-feature New feature labels May 11, 2026
@ryanhurststrava
Copy link
Copy Markdown
Author

The changes that you made, indicate that you directly call CreatePollDialogFragment.newInstance from your code - can you confirm that my assumption is correct?

yeah this is correct 👍

Improvements:
- Rename parameters for better alignment with class naming:
  * titleCharLimit -> questionTextLimit
  * answerCharLimit -> optionTextLimit
- Add @jvmoverloads annotation to CreatePollDialogFragment.newInstance()
  to prevent breaking changes for Java integrations
- Add secondary constructor to OptionsAdapter for backward compatibility,
  maintaining original signature without optionTextLimit parameter

This ensures the API remains backward compatible for both Kotlin and Java
consumers while adding the new character limit functionality.
@ryanhurststrava ryanhurststrava force-pushed the feature/poll_character_limits branch from 0816c63 to 16f339e Compare May 11, 2026 16:55
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/poll/CreatePollDialogFragment.kt (2)

97-99: ⚡ Quick win

Preserve existing question-field filters when adding the length filter

At Line 98, replacing binding.question.filters can drop any existing filters configured on the view. Appending is safer.

Proposed change
         arguments?.getInt(ARG_QUESTION_TEXT_LIMIT)?.takeIf { it > 0 }?.let { limit ->
-            binding.question.filters = arrayOf(InputFilter.LengthFilter(limit))
+            binding.question.filters = binding.question.filters + InputFilter.LengthFilter(limit)
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/poll/CreatePollDialogFragment.kt`
around lines 97 - 99, The current assignment to binding.question.filters
replaces any existing InputFilters and may drop previously configured behavior;
instead, read the existing filters from binding.question.filters (nullable),
create a new array that contains all existing filters plus the new
InputFilter.LengthFilter(limit) constructed from ARG_QUESTION_TEXT_LIMIT, and
set that combined array back on binding.question.filters so existing filters are
preserved while adding the length limit.

195-198: ⚡ Quick win

Normalize invalid limits in the factory to match runtime behavior

At Lines 196-197, any non-null value is persisted, but only values > 0 are actually used later (Lines 62, 97). Centralizing the > 0 normalization in newInstance avoids silently carrying ineffective argument values.

Proposed change
         public fun newInstance(
             createPollDialogListener: CreatePollDialogListener,
             questionTextLimit: Int? = null,
             optionTextLimit: Int? = null,
         ): CreatePollDialogFragment {
+            val normalizedQuestionTextLimit = questionTextLimit?.takeIf { it > 0 }
+            val normalizedOptionTextLimit = optionTextLimit?.takeIf { it > 0 }
             return CreatePollDialogFragment().apply {
                 arguments = Bundle().apply {
-                    questionTextLimit?.let { putInt(ARG_QUESTION_TEXT_LIMIT, it) }
-                    optionTextLimit?.let { putInt(ARG_OPTION_TEXT_LIMIT, it) }
+                    normalizedQuestionTextLimit?.let { putInt(ARG_QUESTION_TEXT_LIMIT, it) }
+                    normalizedOptionTextLimit?.let { putInt(ARG_OPTION_TEXT_LIMIT, it) }
                 }
             }.setCreatePollDialogListener(createPollDialogListener)
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/poll/CreatePollDialogFragment.kt`
around lines 195 - 198, The factory should normalize invalid (non-positive)
limits so only values actually used at runtime are stored; update
CreatePollDialogFragment.newInstance to only putInt(ARG_QUESTION_TEXT_LIMIT, it)
and putInt(ARG_OPTION_TEXT_LIMIT, it) when the provided questionTextLimit and
optionTextLimit are > 0 (currently any non-null is stored), ensuring the
arguments match the runtime checks performed in CreatePollDialogFragment (see
usages around lines where question/option limits are checked, e.g., the checks
at lines 62 and 97).
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/poll/OptionsAdapter.kt (1)

72-75: ⚡ Quick win

Preserve existing EditText filters when applying the new limit

At Line 74, assigning a new array replaces any pre-existing filters on binding.option. Appending the new LengthFilter avoids dropping other constraints.

Proposed change
         init {
             optionTextLimit?.let { limit ->
-                binding.option.filters = arrayOf(InputFilter.LengthFilter(limit))
+                binding.option.filters = binding.option.filters + InputFilter.LengthFilter(limit)
             }
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/poll/OptionsAdapter.kt`
around lines 72 - 75, The init block in OptionsAdapter replaces any existing
InputFilters on binding.option by assigning a new array; instead, retrieve the
current filters from binding.option.filters (handle null/empty), create a new
array that appends the new InputFilter.LengthFilter(limit) to the existing
filters, and assign that combined array back to binding.option.filters so
existing constraints are preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/poll/CreatePollDialogFragment.kt`:
- Around line 97-99: The current assignment to binding.question.filters replaces
any existing InputFilters and may drop previously configured behavior; instead,
read the existing filters from binding.question.filters (nullable), create a new
array that contains all existing filters plus the new
InputFilter.LengthFilter(limit) constructed from ARG_QUESTION_TEXT_LIMIT, and
set that combined array back on binding.question.filters so existing filters are
preserved while adding the length limit.
- Around line 195-198: The factory should normalize invalid (non-positive)
limits so only values actually used at runtime are stored; update
CreatePollDialogFragment.newInstance to only putInt(ARG_QUESTION_TEXT_LIMIT, it)
and putInt(ARG_OPTION_TEXT_LIMIT, it) when the provided questionTextLimit and
optionTextLimit are > 0 (currently any non-null is stored), ensuring the
arguments match the runtime checks performed in CreatePollDialogFragment (see
usages around lines where question/option limits are checked, e.g., the checks
at lines 62 and 97).

In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/poll/OptionsAdapter.kt`:
- Around line 72-75: The init block in OptionsAdapter replaces any existing
InputFilters on binding.option by assigning a new array; instead, retrieve the
current filters from binding.option.filters (handle null/empty), create a new
array that appends the new InputFilter.LengthFilter(limit) to the existing
filters, and assign that combined array back to binding.option.filters so
existing constraints are preserved.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a9faa812-8381-4995-a1f7-630eddbf4750

📥 Commits

Reviewing files that changed from the base of the PR and between 7ad0cda and 16f339e.

📒 Files selected for processing (2)
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/poll/CreatePollDialogFragment.kt
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/composer/attachment/picker/poll/OptionsAdapter.kt

@ryanhurststrava
Copy link
Copy Markdown
Author

update: I need some additional configuration here to support hiding of the comment and suggestion fields when creating a poll.

I opened a new pr #6435 with some more customization

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

Labels

pr:new-feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants