GetStream / stream-chat-android

:speech_balloon: Android Chat SDK ➜ Stream Chat API. UI component libraries for chat apps. Kotlin & Jetpack Compose messaging SDK for Android chat
https://getstream.io/chat/sdk/android/
Other
1.47k stars 274 forks source link

Missing channel config for channel type messaging #486

Closed jetaggart closed 4 years ago

jetaggart commented 4 years ago

Version: 4.2.6 Source code: https://github.com/psylinse/the-stream-android

Issue with viewing a one-on-one channel with latest:

E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-3
    Process: io.getstream.thestream, PID: 30948
    java.lang.IllegalStateException: Missing channel config for channel type messaging
        at io.getstream.chat.android.livedata.ChatDomainImpl.getChannelConfig(ChatDomainImpl.kt:682)
        at io.getstream.chat.android.livedata.controller.ChannelControllerImpl.getConfig(ChannelControllerImpl.kt:164)
        at io.getstream.chat.android.livedata.controller.ChannelControllerImpl.toChannel(ChannelControllerImpl.kt:1015)
        at io.getstream.chat.android.livedata.controller.QueryChannelsControllerImpl.addChannels(QueryChannelsControllerImpl.kt:234)
        at io.getstream.chat.android.livedata.controller.QueryChannelsControllerImpl.handleEvent(QueryChannelsControllerImpl.kt:82)
        at io.getstream.chat.android.livedata.controller.QueryChannelsControllerImpl.handleEvents(QueryChannelsControllerImpl.kt:64)
        at io.getstream.chat.android.livedata.EventHandlerImpl.handleEventsInternal$library_release(EventHandlerImpl.kt:228)
        at io.getstream.chat.android.livedata.EventHandlerImpl$handleEventsInternal$1.invokeSuspend(Unknown Source:12)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

Here's the code that creates it:

    fun createPrivateChannel(otherUser: String): Channel {
        val users = listOf(user.id, otherUser)

        return client
            .createChannel(ModelType.channel_messaging, users)
            .execute()
            .data()
    }

And here's my ChannelActivity:

class ChannelActivity : AppCompatActivity(), MessageInputView.PermissionRequestListener {
    private lateinit var binding: ActivityChannelBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val channelType = intent.getStringExtra(EXTRA_CHANNEL_TYPE)!!
        val channelId = intent.getStringExtra(EXTRA_CHANNEL_ID)!!

        binding = DataBindingUtil.setContentView(this, R.layout.activity_channel)
        binding.lifecycleOwner = this

        initViewModel(channelType, channelId)
    }

    override fun onActivityResult(
        requestCode: Int,
        resultCode: Int,
        data: Intent?
    ) {
        super.onActivityResult(requestCode, resultCode, data)
        binding.messageInput.captureMedia(requestCode, resultCode, data)
    }

    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String?>,
        grantResults: IntArray
    ) {
        binding.messageInput.permissionResult(requestCode, permissions, grantResults)
    }

    override fun openPermissionRequest() {
        PermissionChecker.permissionCheck(this, null)
    }

    private fun initViewModel(
        channelType: String,
        channelId: String
    ) {
        val viewModelFactory = ChannelViewModelFactory(application, channelType, channelId)
        val viewModel = ViewModelProvider(this, viewModelFactory).get(ChannelViewModel::class.java)

        viewModel.initialized.observe(this, Observer {
            binding.viewModel = viewModel
            binding.messageList.setViewModel(viewModel, this)
            binding.messageInput.setViewModel(viewModel, this)
            binding.channelHeader.setViewModel(viewModel, this)
            binding.messageInput.setPermissionRequestListener(this)
        })
    }

    companion object {
        private const val EXTRA_CHANNEL_TYPE = "com.example.thestream.CHANNEL_TYPE"
        private const val EXTRA_CHANNEL_ID = "com.example.thestream.CHANNEL_ID"

        fun newIntent(context: Context, channel: Channel): Intent {
            val intent = Intent(context, ChannelActivity::class.java)
            intent.putExtra(EXTRA_CHANNEL_TYPE, channel.type)
            intent.putExtra(EXTRA_CHANNEL_ID, channel.id)
            return intent
        }
    }
}
jetaggart commented 4 years ago

Hmm, it's even stranger. My implementation for ChannelActivity works fine with livestream channel types, but fails in various ways for these 1-on-1 channels. I went through it again and received this stack trace consistently and can't repro the first stack:

E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-4
    Process: io.getstream.thestream, PID: 32075
    java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter role
        at io.getstream.chat.android.client.models.Member.<init>(Unknown Source:7)
        at io.getstream.chat.android.client.models.Member.<init>(Member.kt:19)
        at io.getstream.chat.android.livedata.entity.MemberEntity.toMember(MemberEntity.kt:41)
        at io.getstream.chat.android.livedata.entity.ChannelEntity.toChannel(ChannelEntity.kt:98)
        at io.getstream.chat.android.livedata.ChatDomainImpl.selectAndEnrichChannels$library_release(ChatDomainImpl.kt:657)
        at io.getstream.chat.android.livedata.ChatDomainImpl$selectAndEnrichChannels$2.invokeSuspend(Unknown Source:12)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

The createPrivateChannel method creates the channel just fine, it only crashes when viewing.

jetaggart commented 4 years ago

Might be dependent on this: https://github.com/GetStream/stream-chat-android/issues/474

jetaggart commented 4 years ago

Just found this issue: https://github.com/GetStream/stream-chat-android/issues/481

tschellenbach commented 4 years ago

Hi @psylinse i'm going to look into this and get back to you

jetaggart commented 4 years ago

I just received this error again on 4.2.7:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: io.getstream.thestream, PID: 3231
    java.lang.IllegalStateException: Missing channel config for channel type messaging
        at io.getstream.chat.android.livedata.ChatDomainImpl.getChannelConfig(ChatDomainImpl.kt:682)
        at io.getstream.chat.android.livedata.controller.ChannelControllerImpl.getConfig(ChannelControllerImpl.kt:164)
        at io.getstream.chat.android.livedata.controller.ChannelControllerImpl.toChannel(ChannelControllerImpl.kt:1015)
        at com.getstream.sdk.chat.viewmodel.ChannelViewModel.getChannel(ChannelViewModel.java:148)
        at com.getstream.sdk.chat.view.MessageListView.getChannel(MessageListView.java:294)
        at com.getstream.sdk.chat.view.MessageListView.setAdapterWithStyle(MessageListView.java:124)
        at com.getstream.sdk.chat.view.MessageListView.setViewModel(MessageListView.java:282)
        at io.getstream.thestream.ChannelActivity$initViewModel$1.onChanged(ChannelActivity.kt:61)
        at io.getstream.thestream.ChannelActivity$initViewModel$1.onChanged(ChannelActivity.kt:17)

However, it's inconsistent. I wonder if this is a race condition with how I'm logging in. I don't wait for the setUser call to come back, I just assume it's good. Would that explain this?

How I log in: https://github.com/psylinse/the-stream-android/blob/3-channels/android/app/src/main/java/io/getstream/thestream/services/ChatService.kt#L18-L35

jetaggart commented 4 years ago

I think it's a race condition. In ChatDomainImpl there's an initJob which runs async. If I manage to get to a chat room before this has ran, it'll fail to find any configs. However, it's not with setUser and is instead on ChatDomain which I don't see a way of waiting for that to fully initialize.

tschellenbach commented 4 years ago

you can await the initJob. What's the full flow for triggering this? I see 2 solutions:

a. Wait for the init job to complete (on the other hand, on first load the DB will be empty, so this doesn't help) b. reevaluate if we need these configs to begin with (perhaps just default to enabling the features)

jetaggart commented 4 years ago

I'm just using a snippet given to me in another issue:

https://github.com/psylinse/the-stream-android/blob/3-channels/android/app/src/main/java/io/getstream/thestream/services/ChatService.kt#L24-L31

Flow is: 1) log in with above snippet 2) user starts a 1-on-1 channel with another user

maxmaxandr commented 4 years ago

Has the same troubles on 4.2.9-beta

I/Chat:ChatDomain EventHandler: Handling event of type notification.added_to_channel for cid messaging:721, event batch size is 1
E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
    java.lang.IllegalStateException: Missing channel config for channel type messaging
        at io.getstream.chat.android.livedata.ChatDomainImpl.getChannelConfig(ChatDomainImpl.kt:687)
        at io.getstream.chat.android.livedata.controller.ChannelControllerImpl.getConfig(ChannelControllerImpl.kt:164)
        at io.getstream.chat.android.livedata.controller.ChannelControllerImpl.toChannel(ChannelControllerImpl.kt:1030)
        at io.getstream.chat.android.livedata.controller.QueryChannelsControllerImpl.addChannels(QueryChannelsControllerImpl.kt:237)
        at io.getstream.chat.android.livedata.controller.QueryChannelsControllerImpl.addChannelIfFilterMatches(QueryChannelsControllerImpl.kt:79)
        at io.getstream.chat.android.livedata.controller.QueryChannelsControllerImpl.handleEvent(QueryChannelsControllerImpl.kt:86)
        at io.getstream.chat.android.livedata.controller.QueryChannelsControllerImpl.handleEvents(QueryChannelsControllerImpl.kt:63)
        at io.getstream.chat.android.livedata.EventHandlerImpl.handleEventsInternal$livedata_release(EventHandlerImpl.kt:255)
        at io.getstream.chat.android.livedata.EventHandlerImpl$handleEventsInternal$1.invokeSuspend(Unknown Source:12)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
tschellenbach commented 4 years ago

Hi @maxmaxandr do you have some more details on how you replicate this? Does it always crash on notification.added_to_channel? I would expect the configs for messaging chat type to already be loaded at this point...

jetaggart commented 4 years ago

@tschellenbach I don't know if this helps but here is a fully working repo with the issue: https://github.com/psylinse/the-stream-android/blob/3-channels

If you log in quickly and get to a chat view, I believe it's a race between ChatDomain loading channel configs and the user getting to that screen.

maxmaxandr commented 4 years ago

Hi @maxmaxandr do you have some more details on how you replicate this? Does it always crash on notification.added_to_channel? I would expect the configs for messaging chat type to already be loaded at this point...

I can't catch the reason of this crash. It happens sometimes, maybe there are race conditions while chat is initializing. For me, It happens when the application has cold start.

tschellenbach commented 4 years ago

Alright, I understand the issue and it's actually a pretty easy fix. Will keep you posted.

tschellenbach commented 4 years ago

So there are scenarios where we don't have the configs available yet.

  1. The configs never loaded before
  2. The local DB is slower than API calls (rare)
  3. You are using a channel type you didn't use before

In the master branch of livedata we're now falling back to some sensible defaults for the channel config.

tschellenbach commented 4 years ago

Fixed in 4.2.10-beta https://github.com/GetStream/stream-chat-android/releases/tag/4.2.10-beta