private fun observeMessages() { viewModelScope.launch { channelState.filterNotNull().collectLatest { channelState ->combine(channelState.messagesState, user, channelState.reads) { state, user, reads -> when (state) { is io.getstream.chat.android.offline.plugin.state.channel.MessagesState.NoQueryActive,is io.getstream.chat.android.offline.plugin.state.channel.MessagesState.Loading, -> messagesState.copy(isLoading = true) is io.getstream.chat.android.offline.plugin.state.channel.MessagesState.OfflineNoResults -> messagesState.copy( isLoading = false, messageItems = emptyList(), ) is io.getstream.chat.android.offline.plugin.state.channel.MessagesState.Result -> { messagesState.copy( isLoading = false, messageItems = groupMessages( messages = filterMessagesToShow(state.messages), isInThread = false, reads = reads, ), isLoadingMore = false, endOfMessages = channelState.endOfOlderMessages.value, currentUser = user, ) } } } .catch { it.cause?.printStackTrace() showEmptyState() } .collect { newState -> val newLastMessage = (newState.messageItems.firstOrNull { it is MessageItemState } as? MessageItemState)?.message val hasNewMessage = lastLoadedMessage != null && messagesState.messageItems.isNotEmpty() && newLastMessage?.id != lastLoadedMessage?.id messagesState = if (hasNewMessage) { val newMessageState = getNewMessageState(newLastMessage, lastLoadedMessage) newState.copy( newMessageState = newMessageState, unreadCount = getUnreadMessageCount(newMessageState) ) } else { newState }  messagesState.messageItems.firstOrNull { it is MessageItemState && it.message.id == scrollToMessage?.id }?.let { focusMessage((it as MessageItemState).message.id) }  lastLoadedMessage = newLastMessage } } } }
Starts observing the messages in the current channel. We observe the 'messagesState', 'user' and endOfOlderMessages' states, as well as build the `newMessageState` using [getNewMessageState] and combine it into a [MessagesState] that holds all the information required for the screen.