Core module
Nabla Android Messaging core module
This guide is about the data/domain layer
This page describes how to query the patient data, allowing you to build your own UI. If you'd rather use our built-in customisable UI components, check out the Messaging UI Components page.
Watchers and cached data
Our core SDK has a cache layer that persists the latest fetched data from the network to a disk cache, allowing to retrieve those data without network at any time.
Every watcher returns a Response
object that contains 3 things:
- A
isDataFresh
boolean property that indicates if the emitted data is fresh or not. - A
refreshingState
property that has 3 different states:Refreshing
: the data emitted comes from the cache, we are refreshing it in background.isDataFresh
should befalse
when that happens, and you can expect another emit following with the result of the background refresh.Refreshed
: the data emitted comes from the network or is local only and no follow-up attempt to refresh it will be made.isDataFresh
should betrue
when that happens.ErrorWhileRefreshing
: the background refresh of the data has failed and no follow-up attempt to refresh will be made.isDataFresh
should befalse
when that happens.
- A
data
property that contains the data itself
If no cached data are available and the network fetch fails, the
Flow
will throw an error, so you need to implementcatch
even for cached flows.
For the vast majority of apps, showing cached data is a good user experience, especially if you're showing a hint to the user when it's not fresh. If your app needs to show only fresh data, you can filter items that aren't fresh and you can relaunch the watcher to trigger a new network fetch if needed.
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 WatchPaginatedResult<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.
WatchPaginatedResult
also provides a loadMore
callback to load more content, precisely an additional page. This callback is suspend
and its Result<Unit>
return type informs whether the operation was successful or not.
Watch patient 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 {
NablaClient.getInstance().messagingClient.watchConversations()
.catch { error ->
// TODO: handle error
}
.collect { response ->
// You can update your UI with the list of conversations:
val conversations = result.data.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.data.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 newresult.loadMore
value.
Create a new conversation
If you want your Patient to be able to start a new conversation, you can allow to create it into your app directly:
NablaClient.getInstance().messagingClient.startConversation()
Note that this conversation is created locally on the device first and will be created server side once the Patient sends a first message in it.
Watch a conversation
To watch the items (message and conversation activity) of a conversation, the same pagination principles applies:
coroutineScope.launch {
NablaClient.getInstance().messagingClient.watchConversationItems(conversationId)
.catch { error ->
// TODO: handle error
}
.collect { response ->
// You can update your UI with the new data
val items = response.data.content.items
// To load more items, you can use the loadMore callback
val loadMoreCallback = response.data.loadMore
}
}
You can also watch for a conversation details update (like a Provider
typing status):
coroutineScope.launch {
NablaClient.getInstance().messagingClient.watchConversation(conversationId)
.catch { error ->
// TODO: handle error
}
.collect { response ->
// You can update your UI with the new data
val conversation = response.data
}
}
Send a new message
Creating and sending your message
The user can send a message in an existing conversation:
coroutineScope.launch {
NablaClient.getInstance().messagingClient
.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: itsstatus
will beSendStatus.Sending
and its id of typeMessageId.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,
)
),
)
Updated 8 months ago