Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AND-5] Expand message theming #5466

Merged
merged 14 commits into from
Nov 11, 2024
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
### ✅ Added

### ⚠️ Changed
- Expanded `MessageTheme` to allow customizing the message components. [#5466](https://github.com/GetStream/stream-chat-android/pull/5466)

### ❌ Removed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import io.getstream.chat.android.compose.ui.theme.AttachmentPickerTheme
import io.getstream.chat.android.compose.ui.theme.ChatTheme
import io.getstream.chat.android.compose.ui.theme.MessageComposerTheme
import io.getstream.chat.android.compose.ui.theme.MessageOptionsTheme
import io.getstream.chat.android.compose.ui.theme.MessageTheme
import io.getstream.chat.android.compose.ui.theme.ReactionOptionsTheme
import io.getstream.chat.android.compose.ui.theme.StreamColors
import io.getstream.chat.android.compose.ui.theme.StreamShapes
Expand Down Expand Up @@ -151,6 +152,7 @@ class MessagesActivity : BaseConnectedActivity() {
val typography = StreamTypography.defaultTypography()
val shapes = StreamShapes.defaultShapes()
val messageComposerTheme = MessageComposerTheme.defaultTheme(isInDarkMode, typography, shapes, colors)
val ownMessageTheme = MessageTheme.defaultOwnTheme(isInDarkMode, typography, shapes, colors)
ChatTheme(
isInDarkMode = isInDarkMode,
colors = colors,
Expand Down Expand Up @@ -182,6 +184,7 @@ class MessagesActivity : BaseConnectedActivity() {
messageOptionsTheme = MessageOptionsTheme.defaultTheme(
optionVisibility = MessageOptionItemVisibility(),
),
ownMessageTheme = ownMessageTheme,
) {
SetupContent()
}
Expand Down
97 changes: 84 additions & 13 deletions stream-chat-android-compose/api/stream-chat-android-compose.api

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions stream-chat-android-compose/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
<ID>LongMethod:MediaGalleryPreviewActivity.kt$MediaGalleryPreviewActivity$@Composable private fun MediaGalleryItem( index: Int, attachment: Attachment, user: User, pagerState: PagerState, )</ID>
<ID>LongMethod:MediaGalleryPreviewActivity.kt$MediaGalleryPreviewActivity$@Composable private fun VideoPreviewContent( attachment: Attachment, pagerState: PagerState, page: Int, onPlaybackError: () -> Unit, )</ID>
<ID>LongMethod:MessageComposer.kt$@Composable internal fun DefaultComposerIntegrations( messageInputState: MessageComposerState, onAttachmentsClick: () -> Unit, onCommandsClick: () -> Unit, ownCapabilities: Set&lt;String>, )</ID>
<ID>LongMethod:MessageItem.kt$@Composable public fun RegularMessageContent( messageItem: MessageItemState, modifier: Modifier = Modifier, onLongItemClick: (Message) -> Unit = {}, onGiphyActionClick: (GiphyAction) -> Unit = {}, onQuotedMessageClick: (Message) -> Unit = {}, onLinkClick: ((Message, String) -> Unit)? = null, onUserMentionClick: (User) -> Unit = {}, onMediaGalleryPreviewResult: (MediaGalleryPreviewResult?) -> Unit = {}, )</ID>
<ID>LongMethod:MessageOptions.kt$@Composable public fun defaultMessageOptionsState( selectedMessage: Message, currentUser: User?, isInThread: Boolean, ownCapabilities: Set&lt;String>, ): List&lt;MessageOptionItemState></ID>
<ID>LongMethod:MessageTheme.kt$MessageTheme.Companion$@Composable @Suppress("DEPRECATION_ERROR") private fun defaultTheme( own: Boolean, isInDarkMode: Boolean, typography: StreamTypography, shapes: StreamShapes, colors: StreamColors, ): MessageTheme</ID>
<ID>LongMethod:PollCreationDiscardDialog.kt$@Composable public fun PollCreationDiscardDialog( usePlatformDefaultWidth: Boolean = false, onCancelClicked: () -> Unit, onDiscardClicked: () -> Unit, )</ID>
<ID>LongMethod:PollMessageContent.kt$@Composable private fun PollOptionItem( modifier: Modifier = Modifier, poll: Poll, option: Option, voteCount: Int, totalVoteCount: Int, users: List&lt;User>, checkedCount: Int, checked: Boolean, onCastVote: () -> Unit, onRemoveVote: () -> Unit, )</ID>
<ID>LongMethod:PollOptionList.kt$@Composable public fun PollOptionList( modifier: Modifier = Modifier, lazyListState: LazyListState = rememberLazyListState(), title: String = stringResource(id = R.string.stream_compose_poll_option_title), optionItems: List&lt;PollOptionItem> = emptyList(), onQuestionsChanged: (List&lt;PollOptionItem>) -> Unit, itemHeightSize: Dp = ChatTheme.dimens.pollOptionInputHeight, itemInnerPadding: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 4.dp), )</ID>
Expand Down Expand Up @@ -82,12 +84,9 @@
<ID>MaxLineLength:MessagesScreen.kt$allowUserSuggestedOptions = action.switches.any { it.key == "allowUserSuggestedOptions" &amp;&amp; it.enabled }</ID>
<ID>MaxLineLength:PollCreationHeader.kt$*</ID>
<ID>MaxLineLength:PollSwitchItem.kt$PollSwitchInput$*</ID>
<ID>MaxLineLength:QuotedMessageTextFormatter.kt$QuotedMessageTextFormatter.Companion$private</ID>
<ID>MaxLineLength:StreamAttachmentFactories.kt$StreamAttachmentFactories$mediaGalleryPreviewLauncher: ManagedActivityResultLauncher&lt;MediaGalleryPreviewContract.Input, MediaGalleryPreviewResult?></ID>
<ID>MaxLineLength:StreamColors.kt$StreamColors$*</ID>
<ID>MaxLineLength:WaveformSlider.kt$// StreamLog.v("WaveformTrack") { "[onDraw] startIdx: $startIdx, totalBars: $totalBars, visibleBarLimit: $visibleBarLimit, waveformData.size: ${waveformData.size}" }</ID>
<ID>ParameterListWrapping:QuotedMessageTextFormatter.kt$QuotedMessageTextFormatter.Companion$(Boolean)</ID>
<ID>ParameterListWrapping:QuotedMessageTextFormatter.kt$QuotedMessageTextFormatter.Companion$(isInDarkMode: Boolean, typography: StreamTypography, colors: StreamColors)</ID>
<ID>ReturnCount:ChannelListViewModel.kt$ChannelListViewModel$private suspend fun loadMoreQueryChannels()</ID>
<ID>ReturnCount:ChannelListViewModel.kt$ChannelListViewModel$private suspend fun loadMoreQueryMessages()</ID>
<ID>TooManyFunctions:MediaGalleryPreviewActivity.kt$MediaGalleryPreviewActivity : AppCompatActivity</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,11 @@ import io.getstream.chat.android.ui.common.state.messages.poll.PollSelectionType
* @param messageItem The message item to show the content for.
* @param modifier Modifier for styling.
* @param onCastVote Callback when a user cast a vote on an option.
* @param onMoreOption Callback when a user clicked seeing more options.
* @param onRemoveVote Callback when a user remove a vote on an option.
* @param selectPoll Callback when a user selects a poll.
* @param onAddAnswer Callback when a user adds a new answer to the poll.
* @param onClosePoll Callback when a user closes a poll.
* @param onAddPollOption Callback when a user adds a new option to the poll.
* @param onAddAnswer Callback when a user adds a new answer to the poll.
* @param onLongItemClick Handler when the user selects a message, on long tap.
*/
@Suppress("LongParameterList", "LongMethod")
Expand All @@ -103,10 +102,15 @@ public fun PollMessageContent(
val position = messageItem.groupPosition
val ownsMessage = messageItem.isMine

val messageBubbleShape = when {
position.contains(MessagePosition.TOP) || position.contains(MessagePosition.MIDDLE) -> RoundedCornerShape(16.dp)
else -> {
if (ownsMessage) ChatTheme.shapes.myMessageBubble else ChatTheme.shapes.otherMessageBubble
val isTopOrMiddleInGroup = position.contains(MessagePosition.TOP) || position.contains(MessagePosition.MIDDLE)
val messageBubbleShape = when (ownsMessage) {
true -> when (isTopOrMiddleInGroup) {
true -> ChatTheme.ownMessageTheme.backgroundShapes.regular
else -> ChatTheme.ownMessageTheme.backgroundShapes.bottom
}
else -> when (isTopOrMiddleInGroup) {
true -> ChatTheme.otherMessageTheme.backgroundShapes.regular
else -> ChatTheme.otherMessageTheme.backgroundShapes.bottom
}
}

Expand All @@ -115,10 +119,9 @@ public fun PollMessageContent(
true -> ChatTheme.ownMessageTheme.deletedBackgroundColor
else -> ChatTheme.otherMessageTheme.deletedBackgroundColor
}

else -> when (ownsMessage) {
true -> ChatTheme.colors.linkBackground
else -> ChatTheme.otherMessageTheme.backgroundColor
true -> ChatTheme.ownMessageTheme.poll.backgroundColor
else -> ChatTheme.otherMessageTheme.poll.backgroundColor
JcMinarro marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import io.getstream.chat.android.compose.ui.attachments.content.QuotedMessageAttachmentContent
import io.getstream.chat.android.compose.ui.theme.ChatTheme
import io.getstream.chat.android.compose.ui.util.padding
import io.getstream.chat.android.models.Message
import io.getstream.chat.android.models.User
import io.getstream.chat.android.ui.common.utils.extensions.isMine
Expand Down Expand Up @@ -51,22 +52,35 @@ public fun QuotedMessageContent(
},
) {
val messageBubbleShape = if (message.isMine(currentUser)) {
ChatTheme.shapes.myMessageBubble
ChatTheme.ownMessageTheme.quoted.backgroundShape
} else {
ChatTheme.shapes.otherMessageBubble
ChatTheme.otherMessageTheme.quoted.backgroundShape
}

// The quoted section color depends on the author of the reply.
val messageBubbleColor = if (replyMessage?.isMine(currentUser) != false) {
ChatTheme.ownMessageTheme.quotedBackgroundColor
ChatTheme.ownMessageTheme.quoted.backgroundColor
} else {
ChatTheme.otherMessageTheme.quotedBackgroundColor
ChatTheme.otherMessageTheme.quoted.backgroundColor
}

val messageBubblePadding = if (replyMessage?.isMine(currentUser) != false) {
ChatTheme.ownMessageTheme.quoted.contentPadding
} else {
ChatTheme.otherMessageTheme.quoted.contentPadding
}

val messageBubbleBorder = if (replyMessage?.isMine(currentUser) != false) {
ChatTheme.ownMessageTheme.quoted.backgroundBorder
} else {
ChatTheme.otherMessageTheme.quoted.backgroundBorder
}

MessageBubble(
modifier = modifier,
modifier = modifier.padding(messageBubblePadding),
shape = messageBubbleShape,
color = messageBubbleColor,
border = messageBubbleBorder,
content = {
Row {
attachmentContent(message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package io.getstream.chat.android.compose.ui.messages.list
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.AnimationConstants
import androidx.compose.animation.core.tween
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
Expand All @@ -37,7 +36,6 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
Expand Down Expand Up @@ -78,6 +76,7 @@ import io.getstream.chat.android.compose.ui.theme.ChatTheme
import io.getstream.chat.android.compose.ui.util.isEmojiOnlyWithoutBubble
import io.getstream.chat.android.compose.ui.util.isErrorOrFailed
import io.getstream.chat.android.compose.ui.util.isUploading
import io.getstream.chat.android.compose.ui.util.padding
import io.getstream.chat.android.models.Message
import io.getstream.chat.android.models.Option
import io.getstream.chat.android.models.Poll
Expand Down Expand Up @@ -109,7 +108,9 @@ import io.getstream.chat.android.ui.common.state.messages.poll.PollSelectionType
* @param onReactionsClick Handler when the user taps on message reactions.
* @param onThreadClick Handler for thread clicks, if this message has a thread going.
* @param onCastVote Handler for casting a vote on an option.
* @param onMoreOption Handler for seeing more options.
* @param onRemoveVote Handler for removing a vote on an option.
* @param selectPoll Handler for selecting a poll.
* @param onAddAnswer Handler for adding an answer to a poll.
* @param onClosePoll Handler for closing a poll.
* @param onAddPollOption Handler for adding a poll option.
* @param onGiphyActionClick Handler when the user taps on an action button in a giphy message item.
Expand Down Expand Up @@ -443,7 +444,8 @@ internal fun DefaultMessageItemTrailingContent(
*/
@Suppress("LongParameterList")
@Composable
internal fun DefaultMessageItemCenterContent(
public fun DefaultMessageItemCenterContent(
modifier: Modifier = Modifier,
messageItem: MessageItemState,
onLongItemClick: (Message) -> Unit = {},
onGiphyActionClick: (GiphyAction) -> Unit = {},
Expand All @@ -459,7 +461,7 @@ internal fun DefaultMessageItemCenterContent(
onClosePoll: (String) -> Unit,
onAddPollOption: (poll: Poll, option: String) -> Unit,
) {
val modifier = Modifier.widthIn(max = ChatTheme.dimens.messageItemMaxWidth)
val finalModifier = modifier.widthIn(max = ChatTheme.dimens.messageItemMaxWidth)
if (messageItem.message.isPoll()) {
val poll = messageItem.message.poll
LaunchedEffect(key1 = poll) {
Expand All @@ -469,7 +471,7 @@ internal fun DefaultMessageItemCenterContent(
}

PollMessageContent(
modifier = modifier,
modifier = finalModifier,
messageItem = messageItem,
onCastVote = onCastVote,
onRemoveVote = onRemoveVote,
Expand All @@ -481,17 +483,16 @@ internal fun DefaultMessageItemCenterContent(
)
} else if (messageItem.message.isEmojiOnlyWithoutBubble()) {
EmojiMessageContent(
modifier = modifier,
modifier = finalModifier,
messageItem = messageItem,
onLongItemClick = onLongItemClick,
onGiphyActionClick = onGiphyActionClick,
onMediaGalleryPreviewResult = onMediaGalleryPreviewResult,
onQuotedMessageClick = onQuotedMessageClick,
onLinkClick = onLinkClick,
)
} else {
RegularMessageContent(
modifier = modifier,
modifier = finalModifier,
messageItem = messageItem,
onLongItemClick = onLongItemClick,
onGiphyActionClick = onGiphyActionClick,
Expand All @@ -511,17 +512,15 @@ internal fun DefaultMessageItemCenterContent(
* @param onLongItemClick Handler when the user selects a message, on long tap.
* @param onGiphyActionClick Handler when the user taps on an action button in a giphy message item.
* @param onQuotedMessageClick Handler for quoted message click action.
* @param onLinkClick Handler for clicking on a link in the message.
* @param onMediaGalleryPreviewResult Handler used when the user selects an option in the Media Gallery Preview screen.
*/
@Composable
internal fun EmojiMessageContent(
public fun EmojiMessageContent(
messageItem: MessageItemState,
modifier: Modifier = Modifier,
onLongItemClick: (Message) -> Unit = {},
onGiphyActionClick: (GiphyAction) -> Unit = {},
onQuotedMessageClick: (Message) -> Unit = {},
onLinkClick: ((Message, String) -> Unit)? = null,
onMediaGalleryPreviewResult: (MediaGalleryPreviewResult?) -> Unit = {},
) {
val message = messageItem.message
Expand Down Expand Up @@ -570,7 +569,7 @@ internal fun EmojiMessageContent(
* @param onMediaGalleryPreviewResult Handler when the user selects an option in the Media Gallery Preview screen.
*/
@Composable
internal fun RegularMessageContent(
public fun RegularMessageContent(
messageItem: MessageItemState,
modifier: Modifier = Modifier,
onLongItemClick: (Message) -> Unit = {},
Expand All @@ -584,16 +583,26 @@ internal fun RegularMessageContent(
val position = messageItem.groupPosition
val ownsMessage = messageItem.isMine

val messageBubblePadding = when (ownsMessage) {
true -> ChatTheme.ownMessageTheme.contentPadding
else -> ChatTheme.otherMessageTheme.contentPadding
}

val messageBubbleShape = getMessageBubbleShape(position = position, ownsMessage = ownsMessage)

val messageBubbleColor = getMessageBubbleColor(message = message, ownsMessage = ownsMessage)

val messageBubbleBorder = when (ownsMessage) {
true -> ChatTheme.ownMessageTheme.backgroundBorder
else -> ChatTheme.otherMessageTheme.backgroundBorder
}

if (!messageItem.isErrorOrFailed()) {
MessageBubble(
modifier = modifier,
modifier = modifier.padding(messageBubblePadding),
shape = messageBubbleShape,
color = messageBubbleColor,
border = if (messageItem.isMine) null else BorderStroke(1.dp, ChatTheme.colors.borders),
border = messageBubbleBorder,
content = {
MessageContent(
message = message,
Expand Down Expand Up @@ -647,9 +656,16 @@ internal fun RegularMessageContent(
*/
@Composable
private fun getMessageBubbleShape(position: List<MessagePosition>, ownsMessage: Boolean): Shape {
return when {
position.contains(MessagePosition.TOP) || position.contains(MessagePosition.MIDDLE) -> RoundedCornerShape(16.dp)
else -> if (ownsMessage) ChatTheme.shapes.myMessageBubble else ChatTheme.shapes.otherMessageBubble
val isTopOrMiddleInGroup = position.contains(MessagePosition.TOP) || position.contains(MessagePosition.MIDDLE)
return when (ownsMessage) {
true -> when (isTopOrMiddleInGroup) {
true -> ChatTheme.ownMessageTheme.backgroundShapes.regular
else -> ChatTheme.ownMessageTheme.backgroundShapes.bottom
}
else -> when (isTopOrMiddleInGroup) {
true -> ChatTheme.otherMessageTheme.backgroundShapes.regular
else -> ChatTheme.otherMessageTheme.backgroundShapes.bottom
}
}
}

Expand All @@ -664,16 +680,13 @@ private fun getMessageBubbleShape(position: List<MessagePosition>, ownsMessage:
private fun getMessageBubbleColor(message: Message, ownsMessage: Boolean): Color {
return when {
message.isGiphyEphemeral() -> ChatTheme.colors.giphyMessageBackground
message.isDeleted() -> if (ownsMessage) {
ChatTheme.ownMessageTheme.deletedBackgroundColor
} else {
ChatTheme.otherMessageTheme.deletedBackgroundColor
message.isDeleted() -> when (ownsMessage) {
true -> ChatTheme.ownMessageTheme.deletedBackgroundColor
else -> ChatTheme.otherMessageTheme.deletedBackgroundColor
}

else -> if (ownsMessage) {
ChatTheme.ownMessageTheme.backgroundColor
} else {
ChatTheme.otherMessageTheme.backgroundColor
else -> when (ownsMessage) {
true -> ChatTheme.ownMessageTheme.backgroundColor
else -> ChatTheme.otherMessageTheme.backgroundColor
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,13 @@ public fun ChatTheme(
ownMessageTheme: MessageTheme = MessageTheme.defaultOwnTheme(
isInDarkMode = isInDarkMode,
typography = typography,
shapes = shapes,
colors = colors,
),
otherMessageTheme: MessageTheme = MessageTheme.defaultOtherTheme(
isInDarkMode = isInDarkMode,
typography = typography,
shapes = shapes,
colors = colors,
),
messageDateSeparatorTheme: MessageDateSeparatorTheme = MessageDateSeparatorTheme.defaultTheme(
Expand All @@ -320,6 +322,7 @@ public fun ChatTheme(
messageTextFormatter: MessageTextFormatter = MessageTextFormatter.defaultFormatter(
autoTranslationEnabled = autoTranslationEnabled,
typography = typography,
shapes = shapes,
colors = colors,
ownMessageTheme = ownMessageTheme,
otherMessageTheme = otherMessageTheme,
Expand All @@ -328,6 +331,7 @@ public fun ChatTheme(
autoTranslationEnabled = autoTranslationEnabled,
context = LocalContext.current,
typography = typography,
shapes = shapes,
colors = colors,
ownMessageTheme = ownMessageTheme,
otherMessageTheme = otherMessageTheme,
Expand Down
Loading
Loading