Skip to content

Commit

Permalink
(crash) fix message list crash (#5219)
Browse files Browse the repository at this point in the history
* (crash) fix message list crash

* code clean up

* detekt, spotless, api dump

* add CHANGELOG

* code clean up
  • Loading branch information
kanat authored Mar 22, 2024
1 parent 4865944 commit cead6ae
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
## stream-chat-android-ui-components
### 🐞 Fixed
- Fixed the crash happening while editing a message with a recording attachment. [#5220](https://github.com/GetStream/stream-chat-android/pull/5220)
- Fixed intermittent crash when opening a channel. [#5219](https://github.com/GetStream/stream-chat-android/pull/5219)

### ⬆️ Improved

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2779,6 +2779,7 @@ public final class io/getstream/chat/android/ui/feature/messages/list/adapter/Me
public static final field THREAD_SEPARATOR I
public static final field TYPING_INDICATOR I
public static final field UNREAD_SEPARATOR I
public final fun toString (I)Ljava/lang/String;
}

public abstract interface class io/getstream/chat/android/ui/feature/messages/list/adapter/MessageListListenerContainer {
Expand Down
1 change: 0 additions & 1 deletion stream-chat-android-ui-components/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@
<ID>NestedBlockDepth:MessageListHeaderView.kt$MessageListHeaderView$private fun renderSubtitleState()</ID>
<ID>ParameterListWrapping:MessageComposerViewModelBinding.kt$MessageComposerViewModelDefaults$(Attachment)</ID>
<ID>ParameterListWrapping:MessageComposerViewModelBinding.kt$MessageComposerViewModelDefaults$(Boolean)</ID>
<ID>RethrowCaughtException:BaseMessageItemViewHolder.kt$BaseMessageItemViewHolder$throw e</ID>
<ID>ReturnCount:AttachmentDestination.kt$AttachmentDestination$public fun showAttachment(message: Message, attachment: Attachment)</ID>
<ID>ReturnCount:ChatFontsImpl.kt$ChatFontsImpl$private fun getFont(@FontRes fontRes: Int): Typeface?</ID>
<ID>ReturnCount:ChatFontsImpl.kt$ChatFontsImpl$private fun getFont(fontPath: String): Typeface?</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import android.view.View
import androidx.annotation.CallSuper
import androidx.recyclerview.widget.RecyclerView
import io.getstream.chat.android.ui.common.internal.animateHighlight
import io.getstream.log.taggedLogger

/**
* Base ViewHolder used for displaying messages in
Expand All @@ -31,6 +32,8 @@ public abstract class BaseMessageItemViewHolder<T : MessageListItem>(
itemView: View,
) : RecyclerView.ViewHolder(itemView) {

private val logger by taggedLogger("Chat:MessageItemBaseVH")

/**
* The data that was last bound to this ViewHolder via [bindData].
* Can be used for listeners that need to pass along the currently
Expand Down Expand Up @@ -59,6 +62,7 @@ public abstract class BaseMessageItemViewHolder<T : MessageListItem>(
try {
bindData(messageListItem, diff)
} catch (e: Throwable) {
logger.e(e) { "[bindListItem] failed: $e" }
throw e
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package io.getstream.chat.android.ui.feature.messages.list.adapter

import android.view.View
import android.view.ViewGroup
import io.getstream.chat.android.ui.ChatUI
import io.getstream.chat.android.ui.feature.messages.common.AudioRecordPlayerViewStyle
Expand Down Expand Up @@ -53,6 +52,7 @@ import io.getstream.chat.android.ui.feature.messages.list.adapter.viewholder.imp
import io.getstream.chat.android.ui.feature.messages.list.adapter.viewholder.impl.MediaAttachmentsViewHolder
import io.getstream.chat.android.ui.feature.messages.list.adapter.viewholder.impl.MessageDeletedViewHolder
import io.getstream.chat.android.ui.feature.messages.list.adapter.viewholder.impl.MessagePlainTextViewHolder
import io.getstream.chat.android.ui.feature.messages.list.adapter.viewholder.internal.EmptyViewHolder
import io.getstream.chat.android.ui.feature.messages.list.adapter.viewholder.internal.ErrorMessageViewHolder
import io.getstream.chat.android.ui.feature.messages.list.adapter.viewholder.internal.LoadingMoreViewHolder
import io.getstream.chat.android.ui.feature.messages.list.adapter.viewholder.internal.SystemMessageViewHolder
Expand Down Expand Up @@ -174,6 +174,27 @@ public open class MessageListItemViewHolderFactory {
return MessageListItemViewTypeMapper.getViewTypeValue(item, attachmentFactoryManager)
}

internal fun getItemViewType(viewHolder: BaseMessageItemViewHolder<out MessageListItem>): Int {
return when (viewHolder) {
is DateDividerViewHolder -> DATE_DIVIDER
is MessageDeletedViewHolder -> MESSAGE_DELETED
is MessagePlainTextViewHolder -> PLAIN_TEXT
is CustomAttachmentsViewHolder -> CUSTOM_ATTACHMENTS
is LoadingMoreViewHolder -> LOADING_INDICATOR
is ThreadSeparatorViewHolder -> THREAD_SEPARATOR
is GiphyViewHolder -> GIPHY
is SystemMessageViewHolder -> SYSTEM_MESSAGE
is ErrorMessageViewHolder -> ERROR_MESSAGE
is EmptyViewHolder -> viewHolder.viewType
is LinkAttachmentsViewHolder -> LINK_ATTACHMENTS
is GiphyAttachmentViewHolder -> GIPHY_ATTACHMENT
is FileAttachmentsViewHolder -> FILE_ATTACHMENTS
is MediaAttachmentsViewHolder -> MEDIA_ATTACHMENT
is UnreadSeparatorViewHolder -> UNREAD_SEPARATOR
else -> throw IllegalArgumentException("Unhandled MessageList view holder: $viewHolder")
}
}

/**
* Creates a new ViewHolder to be used in the Message List.
* The [viewType] parameter is determined by [getItemViewType].
Expand All @@ -189,17 +210,17 @@ public open class MessageListItemViewHolderFactory {
CUSTOM_ATTACHMENTS -> createCustomAttachmentsViewHolder(parentView)
LOADING_INDICATOR -> createLoadingMoreViewHolder(parentView)
THREAD_SEPARATOR -> createThreadSeparatorViewHolder(parentView)
TYPING_INDICATOR -> createEmptyMessageItemViewHolder(parentView)
TYPING_INDICATOR -> createEmptyMessageItemViewHolder(parentView, viewType)
GIPHY -> createGiphyMessageItemViewHolder(parentView)
SYSTEM_MESSAGE -> createSystemMessageItemViewHolder(parentView)
ERROR_MESSAGE -> createErrorMessageItemViewHolder(parentView)
THREAD_PLACEHOLDER -> createEmptyMessageItemViewHolder(parentView)
THREAD_PLACEHOLDER -> createEmptyMessageItemViewHolder(parentView, viewType)
LINK_ATTACHMENTS -> createLinkAttachmentsViewHolder(parentView)
GIPHY_ATTACHMENT -> createGiphyAttachmentViewHolder(parentView)
FILE_ATTACHMENTS -> createFileAttachmentsViewHolder(parentView)
MEDIA_ATTACHMENT -> createMediaAttachmentsViewHolder(parentView)
UNREAD_SEPARATOR -> createUnreadSeparatorViewHolder(parentView)
START_OF_THE_CHANNEL -> createEmptyMessageItemViewHolder(parentView)
START_OF_THE_CHANNEL -> createEmptyMessageItemViewHolder(parentView, viewType)
else -> throw IllegalArgumentException("Unhandled MessageList view type: $viewType")
}
}
Expand Down Expand Up @@ -378,11 +399,9 @@ public open class MessageListItemViewHolderFactory {
*/
private fun createEmptyMessageItemViewHolder(
parentView: ViewGroup,
viewType: Int,
): BaseMessageItemViewHolder<MessageListItem> {
return object :
BaseMessageItemViewHolder<MessageListItem>(View(parentView.context)) {
override fun bindData(data: MessageListItem, diff: MessageListItemPayloadDiff?) = Unit
}
return EmptyViewHolder(parentView, viewType)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,27 @@ public object MessageListItemViewType {
public const val LINK_ATTACHMENTS: Int = OFFSET + 15
public const val UNREAD_SEPARATOR: Int = OFFSET + 16
public const val START_OF_THE_CHANNEL: Int = OFFSET + 17

public fun toString(viewType: Int): String {
return when (viewType) {
DATE_DIVIDER -> "DATE_DIVIDER"
MESSAGE_DELETED -> "MESSAGE_DELETED"
PLAIN_TEXT -> "PLAIN_TEXT"
CUSTOM_ATTACHMENTS -> "CUSTOM_ATTACHMENTS"
LOADING_INDICATOR -> "LOADING_INDICATOR"
THREAD_SEPARATOR -> "THREAD_SEPARATOR"
TYPING_INDICATOR -> "TYPING_INDICATOR"
GIPHY -> "GIPHY"
SYSTEM_MESSAGE -> "SYSTEM_MESSAGE"
ERROR_MESSAGE -> "ERROR_MESSAGE"
THREAD_PLACEHOLDER -> "THREAD_PLACEHOLDER"
GIPHY_ATTACHMENT -> "GIPHY_ATTACHMENT"
MEDIA_ATTACHMENT -> "MEDIA_ATTACHMENT"
FILE_ATTACHMENTS -> "FILE_ATTACHMENTS"
LINK_ATTACHMENTS -> "LINK_ATTACHMENTS"
UNREAD_SEPARATOR -> "UNREAD_SEPARATOR"
START_OF_THE_CHANNEL -> "START_OF_THE_CHANNEL"
else -> "UNKNOWN"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import io.getstream.chat.android.ui.feature.messages.list.adapter.BaseMessageIte
import io.getstream.chat.android.ui.feature.messages.list.adapter.MessageListItem
import io.getstream.chat.android.ui.feature.messages.list.adapter.MessageListItemPayloadDiff
import io.getstream.chat.android.ui.feature.messages.list.adapter.MessageListItemViewHolderFactory
import io.getstream.chat.android.ui.feature.messages.list.adapter.MessageListItemViewType
import io.getstream.log.taggedLogger

internal class MessageListItemAdapter(
Expand All @@ -42,7 +43,8 @@ internal class MessageListItemAdapter(
override fun getItemId(position: Int): Long = getItem(position).getStableId()

override fun getItemViewType(position: Int): Int {
return viewHolderFactory.getItemViewType(getItem(position))
val item = getItem(position)
return viewHolderFactory.getItemViewType(item)
}

override fun onCreateViewHolder(
Expand All @@ -53,7 +55,8 @@ internal class MessageListItemAdapter(
}

override fun onBindViewHolder(holder: BaseMessageItemViewHolder<out MessageListItem>, position: Int) {
holder.bindListItem(getItem(position), FULL_MESSAGE_LIST_ITEM_PAYLOAD_DIFF)
val item = getItem(position)
holder.bindListItem(item, FULL_MESSAGE_LIST_ITEM_PAYLOAD_DIFF)
}

override fun onBindViewHolder(
Expand All @@ -70,7 +73,17 @@ internal class MessageListItemAdapter(
.fold(EMPTY_MESSAGE_LIST_ITEM_PAYLOAD_DIFF) { acc, messageListItemPayloadDiff ->
acc + messageListItemPayloadDiff
}
holder.bindListItem(getItem(position), diff)
val item = getItem(position)
val itemViewType = viewHolderFactory.getItemViewType(item)
val holderViewType = viewHolderFactory.getItemViewType(holder)
if (itemViewType != holderViewType) {
logger.w {
"[onBindViewHolder] viewType mismatch; item: ${MessageListItemViewType.toString(itemViewType)}" +
", viewHolder: ${MessageListItemViewType.toString(holderViewType)}"
}
return
}
holder.bindListItem(item, diff)
}

override fun onViewRecycled(holder: BaseMessageItemViewHolder<out MessageListItem>) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2014-2024 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.ui.feature.messages.list.adapter.viewholder.internal

import android.view.View
import android.view.ViewGroup
import io.getstream.chat.android.ui.feature.messages.list.adapter.BaseMessageItemViewHolder
import io.getstream.chat.android.ui.feature.messages.list.adapter.MessageListItem
import io.getstream.chat.android.ui.feature.messages.list.adapter.MessageListItemPayloadDiff

internal class EmptyViewHolder(
parentView: ViewGroup,
val viewType: Int,
) : BaseMessageItemViewHolder<MessageListItem>(View(parentView.context)) {
override fun bindData(data: MessageListItem, diff: MessageListItemPayloadDiff?) = Unit
}

0 comments on commit cead6ae

Please sign in to comment.