I would like to make my code more readable by simplifying the observers of the MutableLiveData objects. But I'm not really sure how to do that. I thought about moving the contents of each observe method into a separate class, but that would only improve the readability of the fragment and the new class would be confusing again.
private fun HomeViewModel.setupObservers() {
messages.observe(viewLifecycleOwner) { response ->
when (response) {
is Resource.Success -> {
response.data?.let { messagesResponse ->
getMessagesWithImageUrl(chatID, messagesResponse)
}
}
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
hideProgressBarLoadMessages()
hideProgressBarSendMessage()
}
is Resource.Loading -> {}
}
}
messagesWithImageUrl.observe(viewLifecycleOwner) {response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
hideProgressBarLoadMessages()
hideProgressBarSendMessage()
}
is Resource.Loading -> {}
is Resource.Success -> {
response.data?.let { messagesResponse ->
adapterMessage
.differ
.submitList(messagesResponse.thread.values.toList())
binding.recyclerViewMessages
.scrollToPosition(adapterMessage.itemCount-1)
hideProgressBarLoadMessages()
hideProgressBarSendMessage()
}
}
}
}
messageMediaUpload.observe(viewLifecycleOwner) { response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
}
is Resource.Loading -> {
showProgressBarSendMessage()
}
is Resource.Success -> {
response.data?.let {
it.metadata?.let { metadata ->
metadata.name?.let { imageName ->
val content = imageName.dropLast(4)
viewModel.sendMessage(
POSTMessage(content, media = true),
POSTLastMessage(content, media = true, msgRead = true),
chatID,
receiverID)
}
}
}
}
}
}
username.observe(viewLifecycleOwner) { response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
}
is Resource.Loading -> { /* TODO: some loading animation */ }
is Resource.Success -> {
response.data?.let { username ->
binding.headlineText.text = username
}
}
}
}
sendMessage.observe(viewLifecycleOwner) { response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
hideProgressBarSendMessage()
}
is Resource.Loading -> {
showProgressBarSendMessage()
}
is Resource.Success -> {}
}
}
setMessageRead.observe(viewLifecycleOwner) { response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
}
is Resource.Loading -> {}
is Resource.Success -> {}
}
}
}
My Resource class looks like this:
sealed class Resource<T>(
val data: T? = null,
val message: String? = null) {
class Success<T>(data: T) : Resource<T>(data)
class Error<T>(message: String, data: T? = null) : Resource<T>(data, message)
class Loading<T> : Resource<T>()
}
If I changed it like this:
sealed interface Resource<T> {
class Success<T>(val data: T) : Resource<T>
class Error<T>(val message: String) : Resource<T>
class Loading<T> : Resource<T>
}
Then I have to modify methods in my ViewModel as follows:
//old Resource
fun getChats() = viewModelScope.launch {
chats.postValue(Resource.Loading())
homeRepository.getChats().collect() {
it.data?.let { getChats ->
setChatIDs(getChats)
getUnreadMessages(getChats)
}
chats.postValue(it)
}
}
//new Resource
private fun getChats() = viewModelScope.launch {
chats.postValue(Resource.Loading())
homeRepository.getChats().collect() {
if(it is Resource.Success) {
val data = (it as Resource.Success).data
setChatIDs(data)
getUnreadMessages(data)
}
chats.postValue(it)
}
}
That would make my Oberver methods more readable, but I'm not sure if that would be bad practise in the ViewModel.