Skip to main content

Working with Offline Support#

When using the offline library, you first have to build a ChatDomain instance as described on the Getting Started page.

ChatDomain is a singleton, just like ChatClient. This means that you'll always have just one instance of it in your application. After building it, you can access it with the instance method:

val chatDomain = ChatDomain.instance()

ChatDomain is user specific, so you'll want to disconnect your user and create a new ChatClient and ChatDomain when the user in your app changes. You can do this using ChatClient, which will also disconnect ChatDomain automatically:

ChatClient.instance().disconnect()

Flows or LiveData#

There are two different ChatDomain interfaces available in the offline SDK, in different packages:

  • *.offline.ChatDomain: Exposes its state as StateFlow objects you can collect. You can use this if you're using Kotlin coroutines in your app.
  • *.livedata.ChatDomain: Exposes state as LiveData objects. Use this if you're writing Java code or prefer LiveData to Flows.
note

Make sure you build the same version of ChatDomain that you're using in your code elsewhere, otherwise it might not be initialized.

If you want to use both Flows and LiveData, only initialize the LiveData-based ChatDomain, as that will also initialize the Flow-based one internally.

Retry Policy#

By default, the retry policy for the ChatDomain is set to retry three times and wait attempt * 1000 milliseconds between attempts. It will also retry when the connection recovers. You can set your own RetryPolicy like this:

chatDomain.retryPolicy = object : RetryPolicy {    override fun shouldRetry(client: ChatClient, attempt: Int, error: ChatError): Boolean {        return attempt < 3    }
    override fun retryTimeout(client: ChatClient, attempt: Int, error: ChatError): Int {        return 1000 * attempt    }}

Operations#

You can use ChatDomain to perform many of the same operations as ChatClient. If you use ChatDomain, these calls will have offline support, including optimistic UI updates and auto-retries.

For example, you can send a message using ChatDomain:

val message = Message(text = "Hello world")chatDomain.sendMessage(message)    .enqueue { result ->        if (result.isSuccess) {            val message = result.data()        }    }

The operations on ChatDomain return Call objects.

Some other basic functions available on ChatDomain:

  • createChannel()
  • sendMessage()
  • editMessage()
  • deleteMessage()
  • sendReaction()
  • deleteReaction()
  • keystroke()
  • stopTyping()
  • markRead()
note

See the API documentation for ChatDomain for the full list of available features and more info about them.

Let's see how some of the most common operations work.

Watching a Channel#

The following example shows how to get the messages, reads and typing information for a channel:

chatDomain.watchChannel(cid = "messaging:123", messageLimit = 0)     .enqueue { result ->         if (result.isSuccess) {             val channelController = result.data()              // StateFlow / LiveData objects to observe             channelController.messages             channelController.reads             channelController.typing         }     }

You can load more messages for a channel like this:

chatDomain.loadOlderMessages("messaging:123", 10)    .enqueue { result ->        if (result.isSuccess) {            val channel: Channel = result.data()        }    }

Querying Channels#

Another common operation for chat is showing a list of channels. You can query channels with ChatDomain like this:

val members = listOf("thierry")val filter = Filters.and(    Filters.eq("type", "messaging"),    Filters.`in`("members", members),)val sort = QuerySort<Channel>()
chatDomain.queryChannels(filter, sort)    .enqueue { result ->        if (result.isSuccess) {            val queryChannelsController = result.data()
            // StateFlow / LiveData objects to observe             queryChannelsController.channels            queryChannelsController.loading            queryChannelsController.endOfChannels        }    }

Then you can load more channels for a particular query:

chatDomain.queryChannelsLoadMore(filter, sort).enqueue { /* ... */ }

If successful, this call will update the list of channels that you're observing using the controller from the original query call.

Querying Threads#

Here's how you can retrieve the messages for a thread:

chatDomain.getThread(cid, parentId)    .enqueue { result ->        if (result.isSuccess) {            val threadController = result.data()
            // StateFlow / LiveData objects to observe             threadController.messages            threadController.loadingOlderMessages            threadController.endOfOlderMessages        }    }

Then, you can load messages in the thread like this:

chatDomain.threadLoadMore(cid, parentId, messageLimit)    .enqueue { result ->        if (result.isSuccess) {            val messages: List<Message> = result.data()        }    }

Unread Counts#

ChatDomain provides two different counts for a user. Here's how to retrieve them:

// StateFlow / LiveData objects to observe val totalUnreadCount = chatDomain.totalUnreadCountval unreadChannelCount = chatDomain.channelUnreadCount
  • totalUnreadCount is the unread message count for the current user across all channels.
  • unreadChannelCount tells you the number of channels that the current user has unread messages in.

Did you find this page helpful?