📘

This is about the data/domain layer

If you just want to query the user data and take care of the UI yourself, this part describes how to do so. For a quick start with built-in customisable UI components, you can use our Messaging UI Components.

Messaging Core SDK dependency

If you don't want the UI components, you can add a dependency only on messaging-core instead of messaging-ui:

implementation 'com.nabla.nabla-android:messaging-core:1.0.0-alpha06'

Watch a paginated list

Either for the list of conversations or the list of a conversation’s content, the functions watchConversations() and watchConversationItems(conversationId: ConversationId) will return a Flow of WatchPaginatedResponse<T> where T is the content to be loaded gradually, e.g. conversations or messages.

The returned flow will emit a new value whenever the concerned data changes. Change can be of any type, for instance in the case of a conversation’s content it can be:

  • New messages arriving in the conversation either from the current user or distant users.
  • Loaded messages changing in any way, for instance:
    • their status changing from “Sending” to “Sent”;
    • or their content being deleted;
    • or their author changing their avatar, etc.
  • New pages loaded using the loadMore trigger.

WatchPaginatedResponse also provides a loadMore callback to load more content, precisely an additional page. This callback is suspend and its Result<Unit> return type informs wether the operation was successful or not.

Watch conversations list

You can watch the list of conversations a user has access to. This watcher will be called every-time there's a change in those conversations and will always return all the data (You don't need to store them to perform a diff):

coroutineScope.launch {
  NablaMessagingClient.getInstance().watchConversations()
    .catch { error ->
      // TODO: handle error
    }
    .collect { result ->
      // You can update your UI with the list of conversations:
      val conversations = result.content

      // To load more conversation, you can use the loadMore callback
      // It will be null if there are no more elements to load
      val loadMoreCallback = result.loadMore
    }
}

The loadMoreCallback is simply a nullable suspend function you can call to load more elements.

coroutineScope.launch {
  if (loadMoreCallback != null) {
    loadMoreCallback()
      .onFailure { error -> /* TODO: handle error */ }
      .onSuccess {
        /* More conversations have been loaded, 
        the watchConversations callback will be called 
        with those new conversations */
      }
  }
}

🚧

Don't forget to update your loadMoreCallback reference every-time you get a new result.loadMore value.

Create a new conversation

If you want your user to be able to start a new conversation, you can allow to create it into your app directly:

coroutineScope.launch {
  NablaMessagingClient.getInstance().createConversation()
    .onFailure { error -> /* TODO: Handle error */ }
    .onSuccess { conversation ->
      // Conversation is created, watchConversations will be called with that new conversation
    }
}

Watch a conversation

To watch the items (message and conversation activity) of a conversation, the same pagination principles applies:

coroutineScope.launch {
  NablaMessagingClient.getInstance().watchConversationItems(conversationId)
    .catch { error ->
      // TODO: handle error
    }
    .collect { result ->
      // You can update your UI with the new data
      val items = result.content.items

      // To load more items, you can use the loadMore callback
      val loadMoreCallback = result.loadMore
    }
}

You can also watch for a conversation details update (like a Provider typing status):

coroutineScope.launch {
  NablaMessagingClient.getInstance().watchConversation(conversationId)
    .catch { error ->
      // TODO: handle error
    }
    .collect { result ->
      // You can update your UI with the new data
      val conversation = result.content
    }
}

Send a new message

Creating and sending your message

The user can send a message in an existing conversation:

coroutineScope.launch {
  NablaMessagingClient.getInstance()
    .sendMessage(
      input = MessageInput.Text(text = "Text of the message"),
      conversationId = conversationId,
    )
    .onFailure { /* handle error */ }
    .onSuccess { /* message sent successfully 🙌 */ }
}

📘

Message sending is asynchronous

Note that here you don't need to handle failure or success directly: As soon as you call sendMessage(..) the new message will be locally added to the conversation: its status will be SendStatus.Sending and its id of type MessageId.Local.

It means that watchConversationItems will be called immediately after calling this method and the message will be included in the appropriate state, the SDK will then take care of sending it automatically.

Handling failure

If sending the message fails, the message will still be included in watchConversationItems but its status will
be SendStatus.ErrorSending. You can then retry sending it:

val erredMessage: Message = yourMessageInErrorSendingStatus

coroutineScope.launch {
  NablaMessagingClient.getInstance().retrySendingMessage(erredMessage.id, conversationId)
    .onFailure { /* No-op */ }
    .onSuccess { /* No-op */ }
}

Different types of messages

You can send following types of messages:

  • Text
  • Image
  • Video
  • Document
  • Audio

Here is how to create each of them:

val newTextMessage = MessageInput.Text(text = "Hello world!")

val newImageMessage = MessageInput.Media.Image(
  mediaSource = FileSource.Local(
    FileLocal.Image(
      Uri("file:///uri/from/android/img.jpg"),
      fileName = "My image",
      mimeType = MimeType.Image.Jpeg,
    ),
  ),
)

val newVideoMessage = MessageInput.Media.Video(
  mediaSource = FileSource.Local(
    FileLocal.Video(
      Uri("file:///uri/from/android/video.mp4"),
      fileName = "My video",
      mimeType = MimeType.Video.Mp4,
    ),
  ),
)

val newDocumentMessage = MessageInput.Media.Document(
  mediaSource = FileSource.Local(
    FileLocal.Document(
      Uri("file:///uri/from/android/file.pdf"),
      fileName = "My prescription",
      mimeType = MimeType.Application.Pdf,
    )
  ),
)

val newVoiceMessage = MessageInput.Media.Audio(
  mediaSource = FileSource.Local(
    FileLocal.Audio(
      Uri("file:///uri/from/android/audio.mp3"),
      fileName = "My voice message",
      mimeType = MimeType.Audio.Mp3,
      estimatedDurationMs = 42_000L,
    )
  ),
)

Did this page help you?