Skip to content

Commit

Permalink
use repository in MessageInputViewModel instead datasource
Browse files Browse the repository at this point in the history
(as datasources should be only used in repositories)

use coroutines instead RxJava for api calls triggered by MessageInputViewModel

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
  • Loading branch information
mahibi committed Nov 6, 2024
1 parent c5c55e8 commit d335f4f
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 149 deletions.
6 changes: 0 additions & 6 deletions app/src/main/java/com/nextcloud/talk/api/NcApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -388,12 +388,6 @@ Observable<ChatOverallSingleMessage> sendChatMessage(@Header("Authorization") St
@Field("referenceId") String referenceId
);

@FormUrlEncoded
@PUT
Observable<ChatOverallSingleMessage> editChatMessage(@Header("Authorization") String authorization,
@Url String url,
@Field("message") String message);

@GET
Observable<Response<ChatShareOverall>> getSharedItems(
@Header("Authorization") String authorization,
Expand Down
21 changes: 21 additions & 0 deletions app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package com.nextcloud.talk.api

import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
import com.nextcloud.talk.models.json.conversations.RoomOverall
import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.models.json.participants.AddParticipantOverall
Expand Down Expand Up @@ -116,4 +117,24 @@ interface NcApiCoroutines {

@DELETE
suspend fun unarchiveConversation(@Header("Authorization") authorization: String, @Url url: String): GenericOverall

@FormUrlEncoded
@POST
suspend fun sendChatMessage(
@Header("Authorization") authorization: String,
@Url url: String,
@Field("message") message: CharSequence,
@Field("actorDisplayName") actorDisplayName: String,
@Field("replyTo") replyTo: Int,
@Field("silent") sendWithoutNotification: Boolean,
@Field("referenceId") referenceId: String
): ChatOverallSingleMessage

@FormUrlEncoded
@PUT
suspend fun editChatMessage(
@Header("Authorization") authorization: String,
@Url url: String,
@Field("message") message: String
): ChatOverallSingleMessage
}
25 changes: 12 additions & 13 deletions app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import retrofit2.HttpException
import java.io.File
import java.io.IOException
import java.net.HttpURLConnection
Expand Down Expand Up @@ -762,18 +761,18 @@ class ChatActivity :
}

is MessageInputViewModel.SendChatMessageErrorState -> {
if (state.e is HttpException) {
val code = state.e.code()
if (code.toString().startsWith("2")) {
myFirstMessage = state.message

if (binding.unreadMessagesPopup.isShown) {
binding.unreadMessagesPopup.visibility = View.GONE
}

binding.messagesListView.smoothScrollToPosition(0)
}
}
// if (state.e is HttpException) {
// val code = state.e.code()
// if (code.toString().startsWith("2")) {
// myFirstMessage = state.message
//
// if (binding.unreadMessagesPopup.isShown) {
// binding.unreadMessagesPopup.visibility = View.GONE
// }
//
// binding.messagesListView.smoothScrollToPosition(0)
// }
// }
}

else -> {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import android.os.Bundle
import com.nextcloud.talk.chat.data.io.LifecycleAwareManager
import com.nextcloud.talk.chat.data.model.ChatMessage
import com.nextcloud.talk.models.domain.ConversationModel
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow

Expand Down Expand Up @@ -69,4 +70,16 @@ interface ChatMessageRepository : LifecycleAwareManager {
* Destroys unused resources.
*/
fun handleChatOnBackPress()

suspend fun sendChatMessage(
credentials: String,
url: String,
message: CharSequence,
displayName: String,
replyTo: Int,
sendWithoutNotification: Boolean,
referenceId: String
): Flow<Result<ChatOverallSingleMessage>>

suspend fun editChatMessage(credentials: String, url: String, text: String): Flow<Result<ChatOverallSingleMessage>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,23 @@ interface ChatNetworkDataSource {
): Observable<GenericOverall>

fun leaveRoom(credentials: String, url: String): Observable<GenericOverall>
fun sendChatMessage(
suspend fun sendChatMessage(
credentials: String,
url: String,
message: CharSequence,
displayName: String,
replyTo: Int,
sendWithoutNotification: Boolean,
referenceId: String
): Observable<ChatOverallSingleMessage>
): ChatOverallSingleMessage

fun pullChatMessages(credentials: String, url: String, fieldMap: HashMap<String, Int>): Observable<Response<*>>
fun deleteChatMessage(credentials: String, url: String): Observable<ChatOverallSingleMessage>
fun createRoom(credentials: String, url: String, map: Map<String, String>): Observable<RoomOverall>
fun setChatReadMarker(credentials: String, url: String, previousMessageId: Int): Observable<GenericOverall>
fun editChatMessage(credentials: String, url: String, text: String): Observable<ChatOverallSingleMessage>

suspend fun editChatMessage(credentials: String, url: String, text: String): ChatOverallSingleMessage

fun listBans(credentials: String, url: String): Observable<List<TalkBan>>
fun banActor(
credentials: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.domain.ConversationModel
import com.nextcloud.talk.models.json.chat.ChatMessageJson
import com.nextcloud.talk.models.json.chat.ChatOverall
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
import com.nextcloud.talk.utils.preferences.AppPreferences
Expand All @@ -36,6 +37,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand Down Expand Up @@ -731,6 +733,50 @@ class OfflineFirstChatRepository @Inject constructor(
scope.cancel()
}

override suspend fun sendChatMessage(
credentials: String,
url: String,
message: CharSequence,
displayName: String,
replyTo: Int,
sendWithoutNotification: Boolean,
referenceId: String
): Flow<Result<ChatOverallSingleMessage>> =
flow {
try {
val response = network.sendChatMessage(
credentials,
url,
message,
displayName,
replyTo,
sendWithoutNotification,
referenceId
)
emit(Result.success(response))
} catch (e: Exception) {
emit(Result.failure(e))
}
}

override suspend fun editChatMessage(
credentials: String,
url: String,
text: String
): Flow<Result<ChatOverallSingleMessage>> =
flow {
try {
val response = network.editChatMessage(
credentials,
url,
text
)
emit(Result.success(response))
} catch (e: Exception) {
emit(Result.failure(e))
}
}

companion object {
val TAG = OfflineFirstChatRepository::class.simpleName
private const val HTTP_CODE_OK: Int = 200
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package com.nextcloud.talk.chat.data.network

import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.api.NcApiCoroutines
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.domain.ConversationModel
import com.nextcloud.talk.models.json.capabilities.SpreedCapability
Expand All @@ -21,7 +22,8 @@ import com.nextcloud.talk.utils.message.SendMessageUtils
import io.reactivex.Observable
import retrofit2.Response

class RetrofitChatNetwork(private val ncApi: NcApi) : ChatNetworkDataSource {
class RetrofitChatNetwork(private val ncApi: NcApi, private val ncApiCoroutines: NcApiCoroutines) :
ChatNetworkDataSource {
override fun getRoom(user: User, roomToken: String): Observable<ConversationModel> {
val credentials: String = ApiUtils.getCredentials(user.username, user.token)!!
val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V3, 1))
Expand Down Expand Up @@ -137,26 +139,24 @@ class RetrofitChatNetwork(private val ncApi: NcApi) : ChatNetworkDataSource {
it
}

override fun sendChatMessage(
override suspend fun sendChatMessage(
credentials: String,
url: String,
message: CharSequence,
displayName: String,
replyTo: Int,
sendWithoutNotification: Boolean,
referenceId: String
): Observable<ChatOverallSingleMessage> =
ncApi.sendChatMessage(
): ChatOverallSingleMessage =
ncApiCoroutines.sendChatMessage(
credentials,
url,
message,
displayName,
replyTo,
sendWithoutNotification,
referenceId
).map {
it
}
)

override fun pullChatMessages(
credentials: String,
Expand All @@ -180,10 +180,8 @@ class RetrofitChatNetwork(private val ncApi: NcApi) : ChatNetworkDataSource {
previousMessageId: Int
): Observable<GenericOverall> = ncApi.setChatReadMarker(credentials, url, previousMessageId).map { it }

override fun editChatMessage(credentials: String, url: String, text: String): Observable<ChatOverallSingleMessage> =
ncApi.editChatMessage(credentials, url, text).map {
it
}
override suspend fun editChatMessage(credentials: String, url: String, text: String): ChatOverallSingleMessage =
ncApiCoroutines.editChatMessage(credentials, url, text)

override fun listBans(credentials: String, url: String): Observable<List<TalkBan>> =
ncApi.listBans(credentials, url).map {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,24 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.nextcloud.talk.chat.data.ChatMessageRepository
import com.nextcloud.talk.chat.data.io.AudioFocusRequestManager
import com.nextcloud.talk.chat.data.io.AudioRecorderManager
import com.nextcloud.talk.chat.data.io.MediaPlayerManager
import com.nextcloud.talk.chat.data.network.ChatNetworkDataSource
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
import com.nextcloud.talk.utils.message.SendMessageUtils
import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.commons.models.IMessage
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.lang.Thread.sleep
import javax.inject.Inject

class MessageInputViewModel @Inject constructor(
private val chatNetworkDataSource: ChatNetworkDataSource,
private val chatRepository: ChatMessageRepository,
private val audioRecorderManager: AudioRecorderManager,
private val mediaPlayerManager: MediaPlayerManager,
private val audioFocusRequestManager: AudioFocusRequestManager,
Expand Down Expand Up @@ -106,7 +105,7 @@ class MessageInputViewModel @Inject constructor(
sealed interface ViewState
object SendChatMessageStartState : ViewState
class SendChatMessageSuccessState(val message: CharSequence) : ViewState
class SendChatMessageErrorState(val e: Throwable, val message: CharSequence) : ViewState
class SendChatMessageErrorState(val message: CharSequence) : ViewState
private val _sendChatMessageViewState: MutableLiveData<ViewState> = MutableLiveData(SendChatMessageStartState)
val sendChatMessageViewState: LiveData<ViewState>
get() = _sendChatMessageViewState
Expand Down Expand Up @@ -156,59 +155,41 @@ class MessageInputViewModel @Inject constructor(
return
}

chatNetworkDataSource.sendChatMessage(
credentials,
url,
message,
displayName,
replyTo,
sendWithoutNotification,
referenceId
).subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(object : Observer<ChatOverallSingleMessage> {
override fun onSubscribe(d: Disposable) {
disposableSet.add(d)
}

override fun onError(e: Throwable) {
_sendChatMessageViewState.value = SendChatMessageErrorState(e, message)
}

override fun onComplete() {
// unused atm
}
viewModelScope.launch {
chatRepository.sendChatMessage(
credentials,
url,
message,
displayName,
replyTo,
sendWithoutNotification,
referenceId
).collect { result ->
if (result.isSuccess) {
Log.d(TAG, "received ref id: " + (result.getOrNull()?.ocs?.data?.referenceId ?: "none"))

override fun onNext(t: ChatOverallSingleMessage) {
Log.d(TAG, "received ref id: " + (t.ocs?.data?.referenceId ?: "none"))
// TODO check ref id and replace temp message
_sendChatMessageViewState.value = SendChatMessageSuccessState(message)
} else {
_sendChatMessageViewState.value = SendChatMessageErrorState(message)
}
})
}
}
}

fun editChatMessage(credentials: String, url: String, text: String) {
chatNetworkDataSource.editChatMessage(credentials, url, text)
.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(object : Observer<ChatOverallSingleMessage> {
override fun onSubscribe(d: Disposable) {
disposableSet.add(d)
}

override fun onError(e: Throwable) {
Log.e(TAG, "failed to edit message", e)
viewModelScope.launch {
chatRepository.editChatMessage(
credentials,
url,
text
).collect { result ->
if (result.isSuccess) {
_editMessageViewState.value = EditMessageSuccessState(result.getOrNull()!!)
} else {
_editMessageViewState.value = EditMessageErrorState
}

override fun onComplete() {
// unused atm
}

override fun onNext(messageEdited: ChatOverallSingleMessage) {
_editMessageViewState.value = EditMessageSuccessState(messageEdited)
}
})
}
}
}

fun reply(message: IMessage?) {
Expand Down
Loading

0 comments on commit d335f4f

Please sign in to comment.