diff --git a/CHANGELOG.md b/CHANGELOG.md
index acacff44c01..4d652bfbecb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,10 +21,6 @@
### ❌ Removed
-## stream-chat-android-core
-### ✅ Added
-- Expose `UserId` alias that Represents a user id. [#5616](https://github.com/GetStream/stream-chat-android/pull/5616)
-
## stream-chat-android-offline
### 🐞 Fixed
@@ -53,7 +49,6 @@
### ⬆️ Improved
### ✅ Added
-- Added `DateFormatter::formatRelativeDate` method to format a relative date. [#5587](https://github.com/GetStream/stream-chat-android/pull/5587)
### ⚠️ Changed
@@ -61,10 +56,8 @@
## stream-chat-android-ui-components
### 🐞 Fixed
-- Fix poll attachment picker not hidden if disabled in the dashboard. [#5562](https://github.com/GetStream/stream-chat-android/pull/5562)
### ⬆️ Improved
-- `DateDividerViewHolder` now uses `DateFormatter::formatRelativeDate` to format the date. [#5587](https://github.com/GetStream/stream-chat-android/pull/5587)
### ✅ Added
@@ -72,6 +65,45 @@
### ❌ Removed
+## stream-chat-android-compose
+### 🐞 Fixed
+
+### ⬆️ Improved
+- Autofocus the input fields in the poll creation screen. [#5629](https://github.com/GetStream/stream-chat-android/pull/5629)
+
+### ✅ Added
+
+### ⚠️ Changed
+
+### ❌ Removed
+
+## stream-chat-android-markdown-transformer
+### 🐞 Fixed
+
+### ⬆️ Improved
+
+### ✅ Added
+
+### ⚠️ Changed
+
+### ❌ Removed
+
+# February 07th, 2025 - 6.11.0
+## stream-chat-android-core
+### ✅ Added
+- Expose `UserId` alias that Represents a user id. [#5616](https://github.com/GetStream/stream-chat-android/pull/5616)
+
+## stream-chat-android-ui-common
+### ✅ Added
+- Added `DateFormatter::formatRelativeDate` method to format a relative date. [#5587](https://github.com/GetStream/stream-chat-android/pull/5587)
+
+## stream-chat-android-ui-components
+### 🐞 Fixed
+- Fix poll attachment picker not hidden if disabled in the dashboard. [#5562](https://github.com/GetStream/stream-chat-android/pull/5562)
+
+### ⬆️ Improved
+- `DateDividerViewHolder` now uses `DateFormatter::formatRelativeDate` to format the date. [#5587](https://github.com/GetStream/stream-chat-android/pull/5587)
+
## stream-chat-android-compose
### 🐞 Fixed
- Fix poll attachment picker not hidden if disabled in the dashboard. [#5562](https://github.com/GetStream/stream-chat-android/pull/5562)
@@ -103,19 +135,6 @@
### ⚠️ Changed
- 🚨 Breaking change: Change `@Composable public fun MessageContainer` function to `@Composable public fun LazyItemScope.MessageContainer`. [#5593](https://github.com/GetStream/stream-chat-android/pull/5593)
-### ❌ Removed
-
-## stream-chat-android-markdown-transformer
-### 🐞 Fixed
-
-### ⬆️ Improved
-
-### ✅ Added
-
-### ⚠️ Changed
-
-### ❌ Removed
-
# January 23th, 2025 - 6.10.0
## Common changes for all artifacts
### ⬆️ Improved
diff --git a/Gemfile.lock b/Gemfile.lock
index b9092ab280c..4d8a89d97a3 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -186,7 +186,7 @@ GEM
puma (6.4.3)
nio4r (~> 2.0)
racc (1.8.1)
- rack (3.1.8)
+ rack (3.1.10)
rack-protection (4.1.0)
base64 (>= 0.1.0)
logger (>= 1.6.0)
diff --git a/buildSrc/src/main/kotlin/io/getstream/chat/android/Configuration.kt b/buildSrc/src/main/kotlin/io/getstream/chat/android/Configuration.kt
index ca648c03ebb..d7f06bc304b 100644
--- a/buildSrc/src/main/kotlin/io/getstream/chat/android/Configuration.kt
+++ b/buildSrc/src/main/kotlin/io/getstream/chat/android/Configuration.kt
@@ -6,7 +6,7 @@ object Configuration {
const val sampleTargetSdk = 34
const val minSdk = 21
const val majorVersion = 6
- const val minorVersion = 10
+ const val minorVersion = 11
const val patchVersion = 0
const val versionName = "$majorVersion.$minorVersion.$patchVersion"
const val snapshotVersionName = "$majorVersion.$minorVersion.${patchVersion + 1}-SNAPSHOT"
diff --git a/metrics/size.json b/metrics/size.json
index 32ac1351bc6..b2fa9a45c73 100644
--- a/metrics/size.json
+++ b/metrics/size.json
@@ -3,7 +3,7 @@
"stream-chat-android-client": 15308,
"stream-chat-android-offline": 15620,
"stream-chat-android-ui-components": 20992,
- "stream-chat-android-compose": 22388
+ "stream-chat-android-compose": 22392
},
"release": {
"stream-chat-android-client": 3244,
diff --git a/stream-chat-android-ai-assistant/detekt-baseline.xml b/stream-chat-android-ai-assistant/detekt-baseline.xml
index 251e2b443fc..97a8cda418e 100644
--- a/stream-chat-android-ai-assistant/detekt-baseline.xml
+++ b/stream-chat-android-ai-assistant/detekt-baseline.xml
@@ -2,27 +2,14 @@
- FunctionNaming:AiMessageText.kt$@Composable private fun ClickableText( text: AnnotatedString, modifier: Modifier = Modifier, style: TextStyle = TextStyle.Default, maxLines: Int = Int.MAX_VALUE, onLongPress: () -> Unit, onClick: (Int) -> Unit, )
- FunctionNaming:AiMessageText.kt$@Composable public fun AiMessageText( message: Message, currentUser: User?, typingState: TypingState, modifier: Modifier = Modifier, onAnimationState: (Boolean) -> Unit, onLongItemClick: (Message) -> Unit, onLinkClick: ((Message, String) -> Unit)? = null, )
- FunctionNaming:AiMessagesScreen.kt$@Composable internal fun BoxScope.DefaultAiStartButton( isAiStarted: Boolean, onStartAiAssistant: () -> Unit, onStopAiAssistant: () -> Unit, )
- FunctionNaming:AiMessagesScreen.kt$@Composable internal fun DefaultBottomBarContent( viewModelFactory: MessagesViewModelFactory, onComposerLinkPreviewClick: ((LinkPreview) -> Unit)? = null, skipPushNotification: Boolean = false, skipEnrichUrl: Boolean = false, )
- FunctionNaming:AiMessagesScreen.kt$@Composable internal fun DefaultTopBarContent( viewModelFactory: MessagesViewModelFactory, backAction: BackAction, onHeaderTitleClick: (channel: Channel) -> Unit, onChannelAvatarClick: () -> Unit, )
- FunctionNaming:AiMessagesScreen.kt$@Composable public fun AiMessagesScreen( viewModelFactory: MessagesViewModelFactory, isAiStarted: Boolean, onStartAiAssistant: () -> Unit, onStopAiAssistant: () -> Unit, showHeader: Boolean = true, typingState: TypingState, reactionSorting: ReactionSorting = ReactionSortingByFirstReactionAt, onBackPressed: () -> Unit = {}, onComposerLinkPreviewClick: ((LinkPreview) -> Unit)? = null, onHeaderTitleClick: (channel: Channel) -> Unit = {}, onChannelAvatarClick: () -> Unit = {}, onMessageLinkClick: ((Message, String) -> Unit)? = null, onUserAvatarClick: (User) -> Unit = {}, skipPushNotification: Boolean = false, skipEnrichUrl: Boolean = false, threadMessagesStart: ThreadMessagesStart = ThreadMessagesStart.BOTTOM, aiStartButton: @Composable BoxScope.() -> Unit = { DefaultAiStartButton( isAiStarted = isAiStarted, onStartAiAssistant = onStartAiAssistant, onStopAiAssistant = onStopAiAssistant, ) }, topBarContent: @Composable (BackAction) -> Unit = { DefaultTopBarContent( viewModelFactory = viewModelFactory, backAction = it, onHeaderTitleClick = onHeaderTitleClick, onChannelAvatarClick = onChannelAvatarClick, ) }, bottomBarContent: @Composable (isAnimating: Boolean) -> Unit = { DefaultBottomBarContent( viewModelFactory = viewModelFactory, onComposerLinkPreviewClick = onComposerLinkPreviewClick, skipPushNotification = skipPushNotification, skipEnrichUrl = skipEnrichUrl, ) }, )
- FunctionNaming:AiRegularMessageContent.kt$@Composable internal fun DefaultMessageTextContent( message: Message, currentUser: User?, typingState: TypingState, onAnimationState: (Boolean) -> Unit, onLongItemClick: (Message) -> Unit, onLinkClick: ((Message, String) -> Unit)? = null, )
- FunctionNaming:AiRegularMessageContent.kt$@Composable public fun AiRegularMessageContent( messageItem: MessageItemState, modifier: Modifier = Modifier, onLongItemClick: (Message) -> Unit = {}, onGiphyActionClick: (GiphyAction) -> Unit = {}, onQuotedMessageClick: (Message) -> Unit = {}, onAnimationState: (Boolean) -> Unit, onLinkClick: ((Message, String) -> Unit)? = null, typingState: TypingState, onMediaGalleryPreviewResult: (MediaGalleryPreviewResult?) -> Unit = {}, )
- FunctionNaming:AiTypingIndicator.kt$@Composable private fun SingleDot( scale: Float, shimmerInstance: Shimmer, )
- FunctionNaming:AiTypingIndicator.kt$@Composable public fun AiTypingIndicator( modifier: Modifier = Modifier, text: String, textStyle: TextStyle = TextStyle( fontSize = 16.sp, color = ChatTheme.colors.textHighEmphasis, ), )
LongMethod:AiMessageContentFactory.kt$AiMessageContentFactory$@Composable override fun MessageFooterContent(messageItem: MessageItemState)
LongMethod:AiMessageText.kt$@Composable public fun AiMessageText( message: Message, currentUser: User?, typingState: TypingState, modifier: Modifier = Modifier, onAnimationState: (Boolean) -> Unit, onLongItemClick: (Message) -> Unit, onLinkClick: ((Message, String) -> Unit)? = null, )
- LongMethod:AiMessagesScreen.kt$@Composable public fun AiMessagesScreen( viewModelFactory: MessagesViewModelFactory, isAiStarted: Boolean, onStartAiAssistant: () -> Unit, onStopAiAssistant: () -> Unit, showHeader: Boolean = true, typingState: TypingState, reactionSorting: ReactionSorting = ReactionSortingByFirstReactionAt, onBackPressed: () -> Unit = {}, onComposerLinkPreviewClick: ((LinkPreview) -> Unit)? = null, onHeaderTitleClick: (channel: Channel) -> Unit = {}, onChannelAvatarClick: () -> Unit = {}, onMessageLinkClick: ((Message, String) -> Unit)? = null, onUserAvatarClick: (User) -> Unit = {}, skipPushNotification: Boolean = false, skipEnrichUrl: Boolean = false, threadMessagesStart: ThreadMessagesStart = ThreadMessagesStart.BOTTOM, aiStartButton: @Composable BoxScope.() -> Unit = { DefaultAiStartButton( isAiStarted = isAiStarted, onStartAiAssistant = onStartAiAssistant, onStopAiAssistant = onStopAiAssistant, ) }, topBarContent: @Composable (BackAction) -> Unit = { DefaultTopBarContent( viewModelFactory = viewModelFactory, backAction = it, onHeaderTitleClick = onHeaderTitleClick, onChannelAvatarClick = onChannelAvatarClick, ) }, bottomBarContent: @Composable (isAnimating: Boolean) -> Unit = { DefaultBottomBarContent( viewModelFactory = viewModelFactory, onComposerLinkPreviewClick = onComposerLinkPreviewClick, skipPushNotification = skipPushNotification, skipEnrichUrl = skipEnrichUrl, ) }, )
LongMethod:AiRegularMessageContent.kt$@Composable public fun AiRegularMessageContent( messageItem: MessageItemState, modifier: Modifier = Modifier, onLongItemClick: (Message) -> Unit = {}, onGiphyActionClick: (GiphyAction) -> Unit = {}, onQuotedMessageClick: (Message) -> Unit = {}, onAnimationState: (Boolean) -> Unit, onLinkClick: ((Message, String) -> Unit)? = null, typingState: TypingState, onMediaGalleryPreviewResult: (MediaGalleryPreviewResult?) -> Unit = {}, )
MagicNumber:AiMessageText.kt$10
MagicNumber:AiMessagesScreen.kt$0.75f
MagicNumber:AiMessagesScreen.kt$300f
MagicNumber:AiTypingIndicator.kt$0.55f
MagicNumber:AiTypingIndicator.kt$4
- TopLevelPropertyNaming:AiMessageText.kt$/** * The tag used to annotate URLs in the message text. */ internal const val AnnotationTagUrl: AnnotationTag = "URL"
- TopLevelPropertyNaming:AiMessageText.kt$/** * The tag used to annotate emails in the message text. */ internal const val AnnotationTagEmail: AnnotationTag = "EMAIL"
TopLevelPropertyNaming:AiTypingIndicator.kt$private const val delayUnit = 200
diff --git a/stream-chat-android-compose/api/stream-chat-android-compose.api b/stream-chat-android-compose/api/stream-chat-android-compose.api
index 971145c9b04..c7abe4f9d31 100644
--- a/stream-chat-android-compose/api/stream-chat-android-compose.api
+++ b/stream-chat-android-compose/api/stream-chat-android-compose.api
@@ -1774,18 +1774,20 @@ public final class io/getstream/chat/android/compose/ui/messages/attachments/pol
public final class io/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchInput {
public static final field $stable I
- public synthetic fun (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
- public synthetic fun (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
+ public synthetic fun (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/Object;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Ljava/lang/Object;
- public final fun component4-PjHm6EE ()I
- public final fun copy-YyDlPXQ (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;I)Lio/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchInput;
- public static synthetic fun copy-YyDlPXQ$default (Lio/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchInput;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;IILjava/lang/Object;)Lio/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchInput;
+ public final fun component4 ()Ljava/lang/Object;
+ public final fun component5-PjHm6EE ()I
+ public final fun copy-l6dddJE (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;I)Lio/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchInput;
+ public static synthetic fun copy-l6dddJE$default (Lio/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchInput;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;IILjava/lang/Object;)Lio/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchInput;
public fun equals (Ljava/lang/Object;)Z
public final fun getDescription ()Ljava/lang/String;
public final fun getKeyboardType-PjHm6EE ()I
public final fun getMaxValue ()Ljava/lang/Object;
+ public final fun getMinValue ()Ljava/lang/Object;
public final fun getValue ()Ljava/lang/Object;
public fun hashCode ()I
public final fun setValue (Ljava/lang/Object;)V
diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionInput.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionInput.kt
index 9d237e29880..b802f5e0c93 100644
--- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionInput.kt
+++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionInput.kt
@@ -30,9 +30,13 @@ import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.res.stringResource
@@ -69,6 +73,7 @@ import io.getstream.chat.android.compose.ui.util.buildAnnotatedMessageText
* @param keyboardOptions The [KeyboardOptions] to be applied to the input.
* @param decorationBox Composable function that represents the input field decoration as it's filled with content.
*/
+@Suppress("LongMethod")
@Composable
public fun PollOptionInput(
value: String,
@@ -86,6 +91,7 @@ public fun PollOptionInput(
val typography = ChatTheme.typography
val colors = ChatTheme.colors
val textColor = ChatTheme.colors.textHighEmphasis
+ val focusRequester = remember { FocusRequester() }
Box(modifier = modifier.height(ChatTheme.dimens.pollOptionInputHeight)) {
BasicTextField(
@@ -94,6 +100,7 @@ public fun PollOptionInput(
.clip(shape = shape)
.background(ChatTheme.colors.inputBackground)
.padding(innerPadding)
+ .focusRequester(focusRequester)
.semantics { contentDescription = description },
value = value,
onValueChange = {
@@ -139,6 +146,11 @@ public fun PollOptionInput(
)
}
}
+
+ // Request focus initially when the Input is first drawn.
+ LaunchedEffect(Unit) {
+ focusRequester.requestFocus()
+ }
}
@Preview
diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactory.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactory.kt
index c096714e718..598a81b95ba 100644
--- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactory.kt
+++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/factory/AttachmentsPickerPollTabFactory.kt
@@ -118,7 +118,7 @@ public class AttachmentsPickerPollTabFactory : AttachmentsPickerTabFactory {
val pollSwitchItemFactory = ChatTheme.pollSwitchitemFactory
var optionItemList by remember { mutableStateOf(emptyList()) }
var switchItemList: List by remember { mutableStateOf(pollSwitchItemFactory.providePollSwitchItemList()) }
- var hasErrorOnOptions by remember { mutableStateOf(false) }
+ var hasError by remember { mutableStateOf(false) }
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
@@ -139,7 +139,7 @@ public class AttachmentsPickerPollTabFactory : AttachmentsPickerTabFactory {
.background(ChatTheme.colors.appBackground),
) {
val (question, onQuestionChanged) = rememberSaveable { mutableStateOf("") }
- val isEnabled = question.isNotBlank() && optionItemList.any { it.title.isNotBlank() } && !hasErrorOnOptions
+ val isEnabled = question.isNotBlank() && optionItemList.any { it.title.isNotBlank() } && !hasError
val hasChanges = question.isNotBlank() || optionItemList.any { it.title.isNotBlank() }
var isShowingDiscardDialog by remember { mutableStateOf(false) }
@@ -175,7 +175,7 @@ public class AttachmentsPickerPollTabFactory : AttachmentsPickerTabFactory {
onQuestionsChanged = {
optionItemList = it
switchItemList = updateMaxVotesAllowedSwitch(optionItemList, switchItemList)
- hasErrorOnOptions = it.fastAny { item -> item.pollOptionError != null }
+ hasError = hasError(optionItemList, switchItemList)
},
)
@@ -185,7 +185,7 @@ public class AttachmentsPickerPollTabFactory : AttachmentsPickerTabFactory {
pollSwitchItems = switchItemList,
onSwitchesChanged = {
switchItemList = it
- hasErrorOnOptions = it.fastAny { item -> item.pollOptionError != null }
+ hasError = hasError(optionItemList, switchItemList)
},
)
@@ -202,6 +202,28 @@ public class AttachmentsPickerPollTabFactory : AttachmentsPickerTabFactory {
}
}
+/**
+ * Checks if there are any errors in the 'options' list, or any errors or missing fields in the 'switches' list.
+ */
+private fun hasError(
+ options: List,
+ switches: List,
+): Boolean {
+ // Check errors in options
+ val hasErrorInOptions = options.fastAny { item ->
+ item.pollOptionError != null
+ }
+ // Check errors or missing fields in switches
+ val hasErrorInSwitches = switches.fastAny { item ->
+ val hasError = item.pollOptionError != null
+ val isMissingMandatoryInput = item.enabled &&
+ item.pollSwitchInput != null &&
+ item.pollSwitchInput.value.toString().isEmpty()
+ hasError || isMissingMandatoryInput
+ }
+ return hasErrorInOptions || hasErrorInSwitches
+}
+
/**
* Updates the max votes allowed switch based on the number of options available.
*
diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchItem.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchItem.kt
index 7bcf9fa7d0c..c479df0d2c0 100644
--- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchItem.kt
+++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchItem.kt
@@ -26,6 +26,7 @@ import java.util.UUID
* @property title The title of this poll item.
* @property enabled Indicates if this switch is enabled or not.
* @property key The key that identifies this poll item.
+ * @property pollSwitchInput Optional input field to be presented when the switch is enabled.
* @property pollOptionError Indicates this option has an error.
*/
@Immutable
@@ -42,12 +43,14 @@ public data class PollSwitchItem(
*
* @property value The default value of the switch.
* @property description The description of the input in the switch (shown as hint/contentDescription).
- * @property maxValue The maximum vale of the switch. Normally, you can use the limit of the decimal format of the [value].
+ * @property minValue The minimum value of the switch. Normally, you can use the limit of the decimal format of the [value].
+ * @property maxValue The maximum value of the switch. Normally, you can use the limit of the decimal format of the [value].
* @property keyboardType The type of the input of the switch and decide the keyboard type of the input.
*/
public data class PollSwitchInput(
public var value: Any,
public val description: String = "",
+ public val minValue: Any? = null,
public val maxValue: Any? = null,
public val keyboardType: KeyboardType = KeyboardType.Text,
)
diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchList.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchList.kt
index cb8c8dba00a..da4f26e335e 100644
--- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchList.kt
+++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/poll/PollSwitchList.kt
@@ -68,7 +68,6 @@ import io.getstream.chat.android.compose.ui.theme.ChatTheme
* @param onSwitchesChanged A lambda that will be executed when a switch on the list is changed.
* @param itemHeightSize The height size of the question item.
* @param itemInnerPadding The inner padding size of the question item.
- * It provides the index information [from] and [to] as a receiver, so you must swap the item of the [questions] list.
*/
@Composable
public fun PollSwitchList(
@@ -189,24 +188,34 @@ public fun PollSwitchList(
if (switchInput.keyboardType == KeyboardType.Number ||
switchInput.keyboardType == KeyboardType.Decimal
) {
- val newInt = if (newValue.isBlank()) 0 else newValue.toInt()
- val maxInt = switchInput.maxValue?.toString()?.toInt() ?: 0
-
- if (newInt > maxInt) {
- this[index] = item.copy(
- pollSwitchInput = item.pollSwitchInput.copy(value = newValue),
- pollOptionError = PollOptionNumberExceed(
- context.getString(
- R.string.stream_compose_poll_option_error_exceed,
- maxInt,
- ),
- ),
- )
- } else {
+ if (newValue.isBlank()) {
+ // If newValue is empty, don't validate
this[index] = item.copy(
pollSwitchInput = item.pollSwitchInput.copy(value = newValue),
pollOptionError = null,
)
+ } else {
+ // Validate min/max range
+ val min = switchInput.minValue?.toString()?.toIntOrNull() ?: 0
+ val max = switchInput.maxValue?.toString()?.toIntOrNull() ?: 0
+ val value = newValue.toInt() // assume it is always numeric
+ if (value < min || value > max) {
+ this[index] = item.copy(
+ pollSwitchInput = item.pollSwitchInput.copy(value = newValue),
+ pollOptionError = PollOptionNumberExceed(
+ context.getString(
+ R.string.stream_compose_poll_option_error_exceed,
+ min,
+ max,
+ ),
+ ),
+ )
+ } else {
+ this[index] = item.copy(
+ pollSwitchInput = item.pollSwitchInput.copy(value = newValue),
+ pollOptionError = null,
+ )
+ }
}
} else {
this[index] = item.copy(
diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/PollSwitchItemFactory.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/PollSwitchItemFactory.kt
index fb3420da3b4..767ed19111f 100644
--- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/PollSwitchItemFactory.kt
+++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/PollSwitchItemFactory.kt
@@ -61,8 +61,9 @@ public class DefaultPollSwitchItemFactory(
PollSwitchItem(
title = context.getString(R.string.stream_compose_poll_option_switch_multiple_answers),
pollSwitchInput = PollSwitchInput(
- value = 0,
+ value = "",
description = context.getString(R.string.stream_compose_poll_option_max_number_of_answers_hint),
+ minValue = 1,
maxValue = 2,
keyboardType = KeyboardType.Decimal,
),
diff --git a/stream-chat-android-compose/src/main/res/values/strings.xml b/stream-chat-android-compose/src/main/res/values/strings.xml
index 275ad9eeac6..64b847a90a9 100644
--- a/stream-chat-android-compose/src/main/res/values/strings.xml
+++ b/stream-chat-android-compose/src/main/res/values/strings.xml
@@ -209,7 +209,7 @@
Add an option
Add an option
This is already an option
- Type a number under %d
+ Type a number between %d and %d
Discard poll
Are you sure want to discard your poll?
Keep Editing
diff --git a/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollOptionInputTest.kt b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollOptionInputTest.kt
new file mode 100644
index 00000000000..0e9909954f5
--- /dev/null
+++ b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/poll/PollOptionInputTest.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014-2025 Stream.io Inc. All rights reserved.
+ *
+ * Licensed under the Stream License;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.getstream.chat.android.compose.ui.components.poll
+
+import app.cash.paparazzi.DeviceConfig
+import app.cash.paparazzi.Paparazzi
+import io.getstream.chat.android.compose.ui.SnapshotTest
+import org.junit.Rule
+import org.junit.Test
+
+internal class PollOptionInputTest : SnapshotTest {
+
+ @get:Rule
+ override val paparazzi: Paparazzi = Paparazzi(deviceConfig = DeviceConfig.PIXEL_2)
+
+ @Test
+ fun `empty input`() {
+ snapshotWithDarkMode {
+ PollOptionInput(
+ value = "",
+ onValueChange = {},
+ description = "Description",
+ decorationBox = { innerTextField -> innerTextField.invoke() },
+ )
+ }
+ }
+
+ @Test
+ fun `with input`() {
+ snapshotWithDarkMode {
+ PollOptionInput(
+ value = "Entered text",
+ onValueChange = {},
+ description = "Description",
+ decorationBox = { innerTextField -> innerTextField.invoke() },
+ )
+ }
+ }
+}
diff --git a/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/util/DefaultPollSwitchItemFactoryTest.kt b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/util/DefaultPollSwitchItemFactoryTest.kt
index eae51fa94d2..941eff0665b 100644
--- a/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/util/DefaultPollSwitchItemFactoryTest.kt
+++ b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/util/DefaultPollSwitchItemFactoryTest.kt
@@ -51,7 +51,8 @@ internal class DefaultPollSwitchItemFactoryTest {
Assertions.assertEquals("Multiple answers", items[0].title)
Assertions.assertEquals("maxVotesAllowed", items[0].key)
Assertions.assertFalse(items[0].enabled)
- Assertions.assertEquals(0, items[0].pollSwitchInput?.value)
+ Assertions.assertEquals("", items[0].pollSwitchInput?.value)
+ Assertions.assertEquals(1, items[0].pollSwitchInput?.minValue)
Assertions.assertEquals(2, items[0].pollSwitchInput?.maxValue)
Assertions.assertEquals("Max number of answers", items[0].pollSwitchInput?.description)
Assertions.assertEquals(KeyboardType.Decimal, items[0].pollSwitchInput?.keyboardType)
diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollOptionInputTest_empty_input.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollOptionInputTest_empty_input.png
new file mode 100644
index 00000000000..27d01576761
Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollOptionInputTest_empty_input.png differ
diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollOptionInputTest_with_input.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollOptionInputTest_with_input.png
new file mode 100644
index 00000000000..ee392b33c39
Binary files /dev/null and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollOptionInputTest_with_input.png differ
diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.poll_PollUITest_snapshot_AttachmentsPickerPollTabFactory_content_dark_mode_composable.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.poll_PollUITest_snapshot_AttachmentsPickerPollTabFactory_content_dark_mode_composable.png
index 7f8ed6c85a2..a4fe9cfb78d 100644
Binary files a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.poll_PollUITest_snapshot_AttachmentsPickerPollTabFactory_content_dark_mode_composable.png and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.poll_PollUITest_snapshot_AttachmentsPickerPollTabFactory_content_dark_mode_composable.png differ
diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.poll_PollUITest_snapshot_AttachmentsPickerPollTabFactory_content_light_mode_composable.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.poll_PollUITest_snapshot_AttachmentsPickerPollTabFactory_content_light_mode_composable.png
index bec928454b5..fc5d337027e 100644
Binary files a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.poll_PollUITest_snapshot_AttachmentsPickerPollTabFactory_content_light_mode_composable.png and b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.poll_PollUITest_snapshot_AttachmentsPickerPollTabFactory_content_light_mode_composable.png differ
diff --git a/stream-chat-android-ui-utils/detekt-baseline.xml b/stream-chat-android-ui-utils/detekt-baseline.xml
index dba38f7b560..dcd115ea62b 100644
--- a/stream-chat-android-ui-utils/detekt-baseline.xml
+++ b/stream-chat-android-ui-utils/detekt-baseline.xml
@@ -3,6 +3,5 @@
LongMethod:ChannelKtTest.kt$ChannelKtTest.Companion$@JvmStatic fun arguments()
- LongParameterList:Channel.kt$( context: Context, currentUser: User?, @StringRes userOnlineResId: Int, @StringRes userLastSeenJustNowResId: Int, @StringRes userLastSeenResId: Int, @PluralsRes memberCountResId: Int, @StringRes memberCountWithOnlineResId: Int, )