浏览代码

Merge pull request #3630 from nextcloud/issue-3569-1-developing-data-layer-for-chats

Preparing for federation - Developing data layer for chats
Marcel Hibbe 1 年之前
父节点
当前提交
a3ca0a42d7

文件差异内容过多而无法显示
+ 344 - 477
app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt


+ 18 - 0
app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt

@@ -22,11 +22,15 @@ package com.nextcloud.talk.chat.data
 
 import com.nextcloud.talk.data.user.model.User
 import com.nextcloud.talk.models.domain.ConversationModel
+import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
+import com.nextcloud.talk.models.json.conversations.RoomOverall
 import com.nextcloud.talk.models.json.conversations.RoomsOverall
 import com.nextcloud.talk.models.json.generic.GenericOverall
 import com.nextcloud.talk.models.json.reminder.Reminder
 import io.reactivex.Observable
+import retrofit2.Response
 
+@Suppress("LongParameterList", "TooManyFunctions")
 interface ChatRepository {
     fun getRoom(user: User, roomToken: String): Observable<ConversationModel>
     fun joinRoom(user: User, roomToken: String, roomPassword: String): Observable<ConversationModel>
@@ -47,4 +51,18 @@ interface ChatRepository {
         objectId: String,
         metadata: String
     ): Observable<GenericOverall>
+    fun leaveRoom(credentials: String, url: String): Observable<GenericOverall>
+    fun sendChatMessage(
+        credentials: String,
+        url: String,
+        message: CharSequence,
+        displayName: String,
+        replyTo: Int,
+        sendWithoutNotification: Boolean
+    ): Observable<GenericOverall>
+    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>
 }

+ 51 - 2
app/src/main/java/com/nextcloud/talk/chat/data/ChatRepositoryImpl.kt → app/src/main/java/com/nextcloud/talk/chat/data/network/NetworkChatRepositoryImpl.kt

@@ -18,18 +18,22 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-package com.nextcloud.talk.chat.data
+package com.nextcloud.talk.chat.data.network
 
 import com.nextcloud.talk.api.NcApi
+import com.nextcloud.talk.chat.data.ChatRepository
 import com.nextcloud.talk.data.user.model.User
 import com.nextcloud.talk.models.domain.ConversationModel
+import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
+import com.nextcloud.talk.models.json.conversations.RoomOverall
 import com.nextcloud.talk.models.json.conversations.RoomsOverall
 import com.nextcloud.talk.models.json.generic.GenericOverall
 import com.nextcloud.talk.models.json.reminder.Reminder
 import com.nextcloud.talk.utils.ApiUtils
 import io.reactivex.Observable
+import retrofit2.Response
 
-class ChatRepositoryImpl(private val ncApi: NcApi) : ChatRepository {
+class NetworkChatRepositoryImpl(private val ncApi: NcApi) : ChatRepository {
     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.APIv4, ApiUtils.APIv3, 1))
@@ -120,4 +124,49 @@ class ChatRepositoryImpl(private val ncApi: NcApi) : ChatRepository {
     ): Observable<GenericOverall> {
         return ncApi.sendLocation(credentials, url, objectType, objectId, metadata).map { it }
     }
+
+    override fun leaveRoom(credentials: String, url: String): Observable<GenericOverall> {
+        return ncApi.leaveRoom(credentials, url).map { it }
+    }
+
+    override fun sendChatMessage(
+        credentials: String,
+        url: String,
+        message: CharSequence,
+        displayName: String,
+        replyTo: Int,
+        sendWithoutNotification: Boolean
+    ): Observable<GenericOverall> {
+        return ncApi.sendChatMessage(credentials, url, message, displayName, replyTo, sendWithoutNotification).map {
+            it
+        }
+    }
+
+    override fun pullChatMessages(
+        credentials: String,
+        url: String,
+        fieldMap: HashMap<String, Int>
+    ): Observable<Response<*>> {
+        return ncApi.pullChatMessages(credentials, url, fieldMap).map { it }
+    }
+
+    override fun deleteChatMessage(credentials: String, url: String): Observable<ChatOverallSingleMessage> {
+        return ncApi.deleteChatMessage(credentials, url).map { it }
+    }
+
+    override fun createRoom(credentials: String, url: String, map: Map<String, String>): Observable<RoomOverall> {
+        return ncApi.createRoom(credentials, url, map).map { it }
+    }
+
+    override fun setChatReadMarker(
+        credentials: String,
+        url: String,
+        previousMessageId: Int
+    ): Observable<GenericOverall> {
+        return ncApi.setChatReadMarker(credentials, url, previousMessageId).map { it }
+    }
+
+    override fun editChatMessage(credentials: String, url: String, text: String): Observable<ChatOverallSingleMessage> {
+        return ncApi.editChatMessage(credentials, url, text).map { it }
+    }
 }

+ 357 - 20
app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt

@@ -21,29 +21,63 @@
 package com.nextcloud.talk.chat.viewmodels
 
 import android.util.Log
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import com.nextcloud.talk.chat.data.ChatRepository
 import com.nextcloud.talk.data.user.model.User
 import com.nextcloud.talk.models.domain.ConversationModel
+import com.nextcloud.talk.models.domain.ReactionAddedModel
+import com.nextcloud.talk.models.domain.ReactionDeletedModel
+import com.nextcloud.talk.models.json.chat.ChatMessage
+import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
+import com.nextcloud.talk.models.json.conversations.RoomOverall
 import com.nextcloud.talk.models.json.conversations.RoomsOverall
 import com.nextcloud.talk.models.json.generic.GenericOverall
 import com.nextcloud.talk.models.json.reminder.Reminder
+import com.nextcloud.talk.repositories.reactions.ReactionsRepository
 import com.nextcloud.talk.utils.ConversationUtils
 import io.reactivex.Observer
 import io.reactivex.android.schedulers.AndroidSchedulers
 import io.reactivex.disposables.Disposable
 import io.reactivex.schedulers.Schedulers
+import retrofit2.Response
 import javax.inject.Inject
 
-class ChatViewModel @Inject constructor(private val repository: ChatRepository) :
-    ViewModel() {
+@Suppress("TooManyFunctions", "LongParameterList")
+class ChatViewModel @Inject constructor(
+    private val chatRepository: ChatRepository,
+    private val reactionsRepository: ReactionsRepository
+) : ViewModel() {
 
+    object LifeCycleObserver : DefaultLifecycleObserver {
+        enum class LifeCycleFlag {
+            PAUSED,
+            RESUMED
+        }
+        lateinit var currentLifeCycleFlag: LifeCycleFlag
+        public val disposableSet = mutableSetOf<Disposable>()
+
+        override fun onResume(owner: LifecycleOwner) {
+            super.onResume(owner)
+            currentLifeCycleFlag = LifeCycleFlag.RESUMED
+        }
+
+        override fun onPause(owner: LifecycleOwner) {
+            super.onPause(owner)
+            currentLifeCycleFlag = LifeCycleFlag.PAUSED
+            disposableSet.forEach { disposable -> disposable.dispose() }
+            disposableSet.clear()
+        }
+    }
+
+    private val _getFieldMapForChat: MutableLiveData<HashMap<String, Int>> = MutableLiveData()
+    val getFieldMapForChat: LiveData<HashMap<String, Int>>
+        get() = _getFieldMapForChat
     sealed interface ViewState
 
-    object GetRoomStartState : ViewState
-    object GetRoomErrorState : ViewState
     object GetReminderStartState : ViewState
     open class GetReminderExistState(val reminder: Reminder) : ViewState
 
@@ -63,6 +97,8 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
     val getNoteToSelfAvaliability: LiveData<ViewState>
         get() = _getNoteToSelfAvaliability
 
+    object GetRoomStartState : ViewState
+    object GetRoomErrorState : ViewState
     open class GetRoomSuccessState(val conversationModel: ConversationModel) : ViewState
 
     private val _getRoomViewState: MutableLiveData<ViewState> = MutableLiveData(GetRoomStartState)
@@ -77,9 +113,72 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
     val joinRoomViewState: LiveData<ViewState>
         get() = _joinRoomViewState
 
+    object LeaveRoomStartState : ViewState
+    class LeaveRoomSuccessState(val funToCallWhenLeaveSuccessful: (() -> Unit)?) : ViewState
+    private val _leaveRoomViewState: MutableLiveData<ViewState> = MutableLiveData(LeaveRoomStartState)
+    val leaveRoomViewState: LiveData<ViewState>
+        get() = _leaveRoomViewState
+
+    object SendChatMessageStartState : ViewState
+    class SendChatMessageSuccessState(val message: CharSequence) : ViewState
+    class SendChatMessageErrorState(val e: Throwable, val message: CharSequence) : ViewState
+    private val _sendChatMessageViewState: MutableLiveData<ViewState> = MutableLiveData(SendChatMessageStartState)
+    val sendChatMessageViewState: LiveData<ViewState>
+        get() = _sendChatMessageViewState
+
+    object PullChatMessageStartState : ViewState
+    class PullChatMessageSuccessState(val response: Response<*>, val lookIntoFuture: Boolean) : ViewState
+    object PullChatMessageErrorState : ViewState
+    object PullChatMessageCompleteState : ViewState
+    private val _pullChatMessageViewState: MutableLiveData<ViewState> = MutableLiveData(PullChatMessageStartState)
+    val pullChatMessageViewState: LiveData<ViewState>
+        get() = _pullChatMessageViewState
+
+    object DeleteChatMessageStartState : ViewState
+    class DeleteChatMessageSuccessState(val msg: ChatOverallSingleMessage) : ViewState
+    object DeleteChatMessageErrorState : ViewState
+    private val _deleteChatMessageViewState: MutableLiveData<ViewState> = MutableLiveData(DeleteChatMessageStartState)
+    val deleteChatMessageViewState: LiveData<ViewState>
+        get() = _deleteChatMessageViewState
+
+    object CreateRoomStartState : ViewState
+    object CreateRoomErrorState : ViewState
+    class CreateRoomSuccessState(val roomOverall: RoomOverall) : ViewState
+
+    private val _createRoomViewState: MutableLiveData<ViewState> = MutableLiveData(CreateRoomStartState)
+    val createRoomViewState: LiveData<ViewState>
+        get() = _createRoomViewState
+
+    object ReactionAddedStartState : ViewState
+    class ReactionAddedSuccessState(val reactionAddedModel: ReactionAddedModel) : ViewState
+    private val _reactionAddedViewState: MutableLiveData<ViewState> = MutableLiveData(ReactionAddedStartState)
+    val reactionAddedViewState: LiveData<ViewState>
+        get() = _reactionAddedViewState
+
+    object ReactionDeletedStartState : ViewState
+    class ReactionDeletedSuccessState(val reactionDeletedModel: ReactionDeletedModel) : ViewState
+    private val _reactionDeletedViewState: MutableLiveData<ViewState> = MutableLiveData(ReactionDeletedStartState)
+    val reactionDeletedViewState: LiveData<ViewState>
+        get() = _reactionDeletedViewState
+
+    object EditMessageStartState : ViewState
+    object EditMessageErrorState : ViewState
+    class EditMessageSuccessState(val messageEdited: ChatOverallSingleMessage) : ViewState
+
+    private val _editMessageViewState: MutableLiveData<ViewState> = MutableLiveData(EditMessageStartState)
+    val editMessageViewState: LiveData<ViewState>
+        get() = _editMessageViewState
+
+    fun refreshChatParams(pullChatMessagesFieldMap: HashMap<String, Int>) {
+        if (pullChatMessagesFieldMap != _getFieldMapForChat.value) {
+            _getFieldMapForChat.postValue(pullChatMessagesFieldMap)
+            Log.d(TAG, "FieldMap Refreshed with $pullChatMessagesFieldMap vs ${_getFieldMapForChat.value}")
+        }
+    }
+
     fun getRoom(user: User, token: String) {
         _getRoomViewState.value = GetRoomStartState
-        repository.getRoom(user, token)
+        chatRepository.getRoom(user, token)
             .subscribeOn(Schedulers.io())
             ?.observeOn(AndroidSchedulers.mainThread())
             ?.subscribe(GetRoomObserver())
@@ -87,34 +186,198 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
 
     fun joinRoom(user: User, token: String, roomPassword: String) {
         _joinRoomViewState.value = JoinRoomStartState
-        repository.joinRoom(user, token, roomPassword)
+        chatRepository.joinRoom(user, token, roomPassword)
             .subscribeOn(Schedulers.io())
             ?.observeOn(AndroidSchedulers.mainThread())
             ?.retry(JOIN_ROOM_RETRY_COUNT)
             ?.subscribe(JoinRoomObserver())
     }
 
+    fun leaveRoom(credentials: String, url: String, funToCallWhenLeaveSuccessful: (() -> Unit)?) {
+        val startNanoTime = System.nanoTime()
+        chatRepository.leaveRoom(credentials, url)
+            .subscribeOn(Schedulers.io())
+            ?.observeOn(AndroidSchedulers.mainThread())
+            ?.subscribe(object : Observer<GenericOverall> {
+                override fun onSubscribe(d: Disposable) {
+                    LifeCycleObserver.disposableSet.add(d)
+                }
+
+                override fun onError(e: Throwable) {
+                    Log.e(TAG, "leaveRoom - leaveRoom - ERROR", e)
+                }
+
+                override fun onComplete() {
+                    Log.d(TAG, "leaveRoom - leaveRoom - completed: $startNanoTime")
+                }
+
+                override fun onNext(t: GenericOverall) {
+                    _leaveRoomViewState.value = LeaveRoomSuccessState(funToCallWhenLeaveSuccessful)
+                }
+            })
+    }
+
+    fun createRoom(credentials: String, url: String, queryMap: Map<String, String>) {
+        chatRepository.createRoom(credentials, url, queryMap)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+            .subscribe(object : Observer<RoomOverall> {
+                override fun onSubscribe(d: Disposable) {
+                    LifeCycleObserver.disposableSet.add(d)
+                }
+
+                override fun onError(e: Throwable) {
+                    _createRoomViewState.value = CreateRoomErrorState
+                    Log.e(TAG, e.message, e)
+                }
+
+                override fun onComplete() {
+                    // unused atm
+                }
+
+                override fun onNext(t: RoomOverall) {
+                    _createRoomViewState.value = CreateRoomSuccessState(t)
+                }
+            })
+    }
+
+    fun sendChatMessage(
+        credentials: String,
+        url: String,
+        message: CharSequence,
+        displayName: String,
+        replyTo: Int,
+        sendWithoutNotification: Boolean
+    ) {
+        chatRepository.sendChatMessage(
+            credentials,
+            url,
+            message,
+            displayName,
+            replyTo,
+            sendWithoutNotification
+        ).subscribeOn(Schedulers.io())
+            ?.observeOn(AndroidSchedulers.mainThread())
+            ?.subscribe(object : Observer<GenericOverall> {
+                override fun onSubscribe(d: Disposable) {
+                    LifeCycleObserver.disposableSet.add(d)
+                }
+
+                override fun onError(e: Throwable) {
+                    _sendChatMessageViewState.value = SendChatMessageErrorState(e, message)
+                }
+
+                override fun onComplete() {
+                    // unused atm
+                }
+
+                override fun onNext(t: GenericOverall) {
+                    _sendChatMessageViewState.value = SendChatMessageSuccessState(message)
+                }
+            })
+    }
+
+    fun pullChatMessages(credentials: String, url: String) {
+        chatRepository.pullChatMessages(credentials, url, _getFieldMapForChat.value!!)
+            .subscribeOn(Schedulers.io())
+            .takeUntil { (LifeCycleObserver.currentLifeCycleFlag == LifeCycleObserver.LifeCycleFlag.PAUSED) }
+            ?.observeOn(AndroidSchedulers.mainThread())
+            ?.subscribe(object : Observer<Response<*>> {
+                override fun onSubscribe(d: Disposable) {
+                    Log.d(TAG, "pullChatMessages - pullChatMessages SUBSCRIBE")
+                    LifeCycleObserver.disposableSet.add(d)
+                }
+
+                override fun onError(e: Throwable) {
+                    Log.e(TAG, "pullChatMessages - pullChatMessages ERROR", e)
+                    _pullChatMessageViewState.value = PullChatMessageErrorState
+                }
+
+                override fun onComplete() {
+                    Log.d(TAG, "pullChatMessages - pullChatMessages COMPLETE")
+                    _pullChatMessageViewState.value = PullChatMessageCompleteState
+                }
+
+                override fun onNext(response: Response<*>) {
+                    val lookIntoFuture = getFieldMapForChat.value?.get("lookIntoFuture") == 1
+                    _pullChatMessageViewState.value = PullChatMessageSuccessState(response, lookIntoFuture)
+                }
+            })
+    }
+
+    fun deleteChatMessages(credentials: String, url: String, messageId: String) {
+        chatRepository.deleteChatMessage(credentials, url)
+            .subscribeOn(Schedulers.io())
+            ?.observeOn(AndroidSchedulers.mainThread())
+            ?.subscribe(object : Observer<ChatOverallSingleMessage> {
+                override fun onSubscribe(d: Disposable) {
+                    LifeCycleObserver.disposableSet.add(d)
+                }
+
+                override fun onError(e: Throwable) {
+                    Log.e(
+                        TAG,
+                        "Something went wrong when trying to delete message with id " +
+                            messageId,
+                        e
+                    )
+                    _deleteChatMessageViewState.value = DeleteChatMessageErrorState
+                }
+
+                override fun onComplete() {
+                    // unused atm
+                }
+
+                override fun onNext(t: ChatOverallSingleMessage) {
+                    _deleteChatMessageViewState.value = DeleteChatMessageSuccessState(t)
+                }
+            })
+    }
+
+    fun setChatReadMarker(credentials: String, url: String, previousMessageId: Int) {
+        chatRepository.setChatReadMarker(credentials, url, previousMessageId)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+            .subscribe(object : Observer<GenericOverall> {
+                override fun onSubscribe(d: Disposable) {
+                    LifeCycleObserver.disposableSet.add(d)
+                }
+
+                override fun onError(e: Throwable) {
+                    Log.e(TAG, e.message, e)
+                }
+
+                override fun onComplete() {
+                    // unused atm
+                }
+
+                override fun onNext(t: GenericOverall) {
+                    // unused atm
+                }
+            })
+    }
+
     fun setReminder(user: User, roomToken: String, messageId: String, timestamp: Int) {
-        repository.setReminder(user, roomToken, messageId, timestamp)
+        chatRepository.setReminder(user, roomToken, messageId, timestamp)
             .subscribeOn(Schedulers.io())
             ?.observeOn(AndroidSchedulers.mainThread())
             ?.subscribe(SetReminderObserver())
     }
 
     fun getReminder(user: User, roomToken: String, messageId: String) {
-        repository.getReminder(user, roomToken, messageId)
+        chatRepository.getReminder(user, roomToken, messageId)
             .subscribeOn(Schedulers.io())
             ?.observeOn(AndroidSchedulers.mainThread())
             ?.subscribe(GetReminderObserver())
     }
 
     fun deleteReminder(user: User, roomToken: String, messageId: String) {
-        repository.deleteReminder(user, roomToken, messageId)
+        chatRepository.deleteReminder(user, roomToken, messageId)
             .subscribeOn(Schedulers.io())
             ?.observeOn(AndroidSchedulers.mainThread())
             ?.subscribe(object : Observer<GenericOverall> {
                 override fun onSubscribe(d: Disposable) {
-                    // unused atm
+                    LifeCycleObserver.disposableSet.add(d)
                 }
 
                 override fun onNext(genericOverall: GenericOverall) {
@@ -132,12 +395,12 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
     }
 
     fun shareToNotes(credentials: String, url: String, message: String, displayName: String) {
-        repository.shareToNotes(credentials, url, message, displayName)
+        chatRepository.shareToNotes(credentials, url, message, displayName)
             .subscribeOn(Schedulers.io())
             ?.observeOn(AndroidSchedulers.mainThread())
             ?.subscribe(object : Observer<GenericOverall> {
                 override fun onSubscribe(d: Disposable) {
-                    // unused atm
+                    LifeCycleObserver.disposableSet.add(d)
                 }
 
                 override fun onNext(genericOverall: GenericOverall) {
@@ -155,18 +418,18 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
     }
 
     fun checkForNoteToSelf(credentials: String, baseUrl: String, includeStatus: Boolean) {
-        repository.checkForNoteToSelf(credentials, baseUrl, includeStatus).subscribeOn(Schedulers.io())
+        chatRepository.checkForNoteToSelf(credentials, baseUrl, includeStatus).subscribeOn(Schedulers.io())
             ?.observeOn(AndroidSchedulers.mainThread())
             ?.subscribe(CheckForNoteToSelfObserver())
     }
 
     fun shareLocationToNotes(credentials: String, url: String, objectType: String, objectId: String, metadata: String) {
-        repository.shareLocationToNotes(credentials, url, objectType, objectId, metadata)
+        chatRepository.shareLocationToNotes(credentials, url, objectType, objectId, metadata)
             .subscribeOn(Schedulers.io())
             ?.observeOn(AndroidSchedulers.mainThread())
             ?.subscribe(object : Observer<GenericOverall> {
                 override fun onSubscribe(d: Disposable) {
-                    // unused atm
+                    LifeCycleObserver.disposableSet.add(d)
                 }
 
                 override fun onNext(genericOverall: GenericOverall) {
@@ -183,9 +446,83 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
             })
     }
 
+    fun deleteReaction(roomToken: String, chatMessage: ChatMessage, emoji: String) {
+        reactionsRepository.deleteReaction(roomToken, chatMessage, emoji)
+            .subscribeOn(Schedulers.io())
+            ?.observeOn(AndroidSchedulers.mainThread())
+            ?.subscribe(object : Observer<ReactionDeletedModel> {
+                override fun onSubscribe(d: Disposable) {
+                    LifeCycleObserver.disposableSet.add(d)
+                }
+
+                override fun onError(e: Throwable) {
+                    Log.d(TAG, "$e")
+                }
+
+                override fun onComplete() {
+                    // unused atm
+                }
+
+                override fun onNext(reactionDeletedModel: ReactionDeletedModel) {
+                    if (reactionDeletedModel.success) {
+                        _reactionDeletedViewState.value = ReactionDeletedSuccessState(reactionDeletedModel)
+                    }
+                }
+            })
+    }
+
+    fun addReaction(roomToken: String, chatMessage: ChatMessage, emoji: String) {
+        reactionsRepository.addReaction(roomToken, chatMessage, emoji)
+            .subscribeOn(Schedulers.io())
+            ?.observeOn(AndroidSchedulers.mainThread())
+            ?.subscribe(object : Observer<ReactionAddedModel> {
+                override fun onSubscribe(d: Disposable) {
+                    LifeCycleObserver.disposableSet.add(d)
+                }
+
+                override fun onError(e: Throwable) {
+                    Log.d(TAG, "$e")
+                }
+
+                override fun onComplete() {
+                    // unused atm
+                }
+
+                override fun onNext(reactionAddedModel: ReactionAddedModel) {
+                    if (reactionAddedModel.success) {
+                        _reactionAddedViewState.value = ReactionAddedSuccessState(reactionAddedModel)
+                    }
+                }
+            })
+    }
+
+    fun editChatMessage(credentials: String, url: String, text: String) {
+        chatRepository.editChatMessage(credentials, url, text)
+            .subscribeOn(Schedulers.io())
+            ?.observeOn(AndroidSchedulers.mainThread())
+            ?.subscribe(object : Observer<ChatOverallSingleMessage> {
+                override fun onSubscribe(d: Disposable) {
+                    LifeCycleObserver.disposableSet.add(d)
+                }
+
+                override fun onError(e: Throwable) {
+                    Log.e(TAG, "failed to edit message", e)
+                    _editMessageViewState.value = EditMessageErrorState
+                }
+
+                override fun onComplete() {
+                    // unused atm
+                }
+
+                override fun onNext(messageEdited: ChatOverallSingleMessage) {
+                    _editMessageViewState.value = EditMessageSuccessState(messageEdited)
+                }
+            })
+    }
+
     inner class GetRoomObserver : Observer<ConversationModel> {
         override fun onSubscribe(d: Disposable) {
-            // unused atm
+            LifeCycleObserver.disposableSet.add(d)
         }
 
         override fun onNext(conversationModel: ConversationModel) {
@@ -204,7 +541,7 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
 
     inner class JoinRoomObserver : Observer<ConversationModel> {
         override fun onSubscribe(d: Disposable) {
-            // unused atm
+            LifeCycleObserver.disposableSet.add(d)
         }
 
         override fun onNext(conversationModel: ConversationModel) {
@@ -223,7 +560,7 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
 
     inner class SetReminderObserver : Observer<Reminder> {
         override fun onSubscribe(d: Disposable) {
-            // unused atm
+            LifeCycleObserver.disposableSet.add(d)
         }
 
         override fun onNext(reminder: Reminder) {
@@ -241,7 +578,7 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
 
     inner class GetReminderObserver : Observer<Reminder> {
         override fun onSubscribe(d: Disposable) {
-            // unused atm
+            LifeCycleObserver.disposableSet.add(d)
         }
 
         override fun onNext(reminder: Reminder) {
@@ -260,7 +597,7 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
 
     inner class CheckForNoteToSelfObserver : Observer<RoomsOverall> {
         override fun onSubscribe(d: Disposable) {
-            // unused atm
+            LifeCycleObserver.disposableSet.add(d)
         }
 
         override fun onNext(roomsOverall: RoomsOverall) {

+ 2 - 2
app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt

@@ -27,7 +27,7 @@ package com.nextcloud.talk.dagger.modules
 
 import com.nextcloud.talk.api.NcApi
 import com.nextcloud.talk.chat.data.ChatRepository
-import com.nextcloud.talk.chat.data.ChatRepositoryImpl
+import com.nextcloud.talk.chat.data.network.NetworkChatRepositoryImpl
 import com.nextcloud.talk.conversation.repository.ConversationRepository
 import com.nextcloud.talk.conversation.repository.ConversationRepositoryImpl
 import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepository
@@ -146,7 +146,7 @@ class RepositoryModule {
 
     @Provides
     fun provideChatRepository(ncApi: NcApi): ChatRepository {
-        return ChatRepositoryImpl(ncApi)
+        return NetworkChatRepositoryImpl(ncApi)
     }
 
     @Provides

+ 1 - 0
app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.kt

@@ -24,6 +24,7 @@
 
 package com.nextcloud.talk.models.json.chat
 
+@Suppress("UtilityClassWithPublicConstructor")
 class ChatUtils {
     companion object {
         fun getParsedMessage(

部分文件因为文件数量过多而无法显示