Implement queued messages for offline support

Signed-off-by: rapterjet2004 <juliuslinus1@gmail.com>
This commit is contained in:
rapterjet2004 2024-09-10 07:38:12 -05:00 committed by Marcel Hibbe
parent 3d2df9bb94
commit 569be55395
No known key found for this signature in database
GPG Key ID: C793F8B59F43CE7B
4 changed files with 43 additions and 17 deletions

View File

@ -73,6 +73,7 @@ import com.nextcloud.talk.utils.text.Spans
import com.otaliastudios.autocomplete.Autocomplete import com.otaliastudios.autocomplete.Autocomplete
import com.stfalcon.chatkit.commons.models.IMessage import com.stfalcon.chatkit.commons.models.IMessage
import com.vanniktech.emoji.EmojiPopup import com.vanniktech.emoji.EmojiPopup
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -141,6 +142,11 @@ class MessageInputFragment : Fragment() {
saveState() saveState()
} }
override fun onResume() {
super.onResume()
chatActivity.messageInputViewModel.restoreMessageQueue(chatActivity.roomToken)
}
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
if (mentionAutocomplete != null && mentionAutocomplete!!.isPopupShowing) { if (mentionAutocomplete != null && mentionAutocomplete!!.isPopupShowing) {
@ -178,12 +184,19 @@ class MessageInputFragment : Fragment() {
val connectionGained = (!wasOnline && isOnline) val connectionGained = (!wasOnline && isOnline)
wasOnline = !binding.fragmentMessageInputView.isShown wasOnline = !binding.fragmentMessageInputView.isShown
Log.d(TAG, "isOnline: $isOnline\nwasOnline: $wasOnline\nconnectionGained: $connectionGained") Log.d(TAG, "isOnline: $isOnline\nwasOnline: $wasOnline\nconnectionGained: $connectionGained")
delay(500)
// FIXME timeout exception - maybe something to do with the room? handleMessageQueue(isOnline)
// handleMessageQueue(isOnline)
handleUI(isOnline, connectionGained) handleUI(isOnline, connectionGained)
}.collect() }.collect()
} }
chatActivity.messageInputViewModel.messageQueueSizeFlow.observe(viewLifecycleOwner) { size ->
if (size > 0) {
binding.fragmentConnectionLost.text = getString(R.string.connection_lost_queued, size)
} else {
binding.fragmentConnectionLost.text = getString(R.string.connection_lost_sent_messages_are_queued)
}
}
} }
private fun handleUI(isOnline: Boolean, connectionGained: Boolean) { private fun handleUI(isOnline: Boolean, connectionGained: Boolean) {
@ -220,12 +233,9 @@ class MessageInputFragment : Fragment() {
binding.fragmentConnectionLost.clearAnimation() binding.fragmentConnectionLost.clearAnimation()
binding.fragmentConnectionLost.visibility = View.GONE binding.fragmentConnectionLost.visibility = View.GONE
binding.fragmentConnectionLost.setBackgroundColor(resources.getColor(R.color.hwSecurityRed)) binding.fragmentConnectionLost.setBackgroundColor(resources.getColor(R.color.hwSecurityRed))
binding.fragmentConnectionLost.text =
getString(R.string.connection_lost_sent_messages_are_queued)
binding.fragmentConnectionLost.visibility = View.VISIBLE binding.fragmentConnectionLost.visibility = View.VISIBLE
binding.fragmentMessageInputView.attachmentButton.isEnabled = false binding.fragmentMessageInputView.attachmentButton.isEnabled = false
binding.fragmentMessageInputView.recordAudioButton.isEnabled = false binding.fragmentMessageInputView.recordAudioButton.isEnabled = false
binding.fragmentMessageInputView.messageInput.isEnabled = false
} }
} }

View File

@ -14,6 +14,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import com.nextcloud.talk.chat.data.io.AudioFocusRequestManager import com.nextcloud.talk.chat.data.io.AudioFocusRequestManager
import com.nextcloud.talk.chat.data.io.AudioRecorderManager import com.nextcloud.talk.chat.data.io.AudioRecorderManager
import com.nextcloud.talk.chat.data.io.MediaPlayerManager import com.nextcloud.talk.chat.data.io.MediaPlayerManager
@ -26,6 +27,8 @@ import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import javax.inject.Inject import javax.inject.Inject
class MessageInputViewModel @Inject constructor( class MessageInputViewModel @Inject constructor(
@ -51,7 +54,7 @@ class MessageInputViewModel @Inject constructor(
) )
private var isQueueing: Boolean = false private var isQueueing: Boolean = false
private val messageQueue: MutableList<QueuedMessage> = mutableListOf() private var messageQueue: MutableList<QueuedMessage> = mutableListOf()
override fun onResume(owner: LifecycleOwner) { override fun onResume(owner: LifecycleOwner) {
super.onResume(owner) super.onResume(owner)
@ -119,6 +122,10 @@ class MessageInputViewModel @Inject constructor(
val isVoicePreviewPlaying: LiveData<Boolean> val isVoicePreviewPlaying: LiveData<Boolean>
get() = _isVoicePreviewPlaying get() = _isVoicePreviewPlaying
private val _messageQueueSizeFlow = MutableStateFlow(messageQueue.size)
val messageQueueSizeFlow: LiveData<Int>
get() = _messageQueueSizeFlow.asLiveData()
@Suppress("LongParameterList") @Suppress("LongParameterList")
fun sendChatMessage( fun sendChatMessage(
roomToken: String, roomToken: String,
@ -132,6 +139,7 @@ class MessageInputViewModel @Inject constructor(
if (isQueueing) { if (isQueueing) {
messageQueue.add(QueuedMessage(message, displayName, replyTo, sendWithoutNotification)) messageQueue.add(QueuedMessage(message, displayName, replyTo, sendWithoutNotification))
dataStore.saveMessageQueue(roomToken, messageQueue) dataStore.saveMessageQueue(roomToken, messageQueue)
_messageQueueSizeFlow.update { messageQueue.size }
return return
} }
@ -259,4 +267,9 @@ class MessageInputViewModel @Inject constructor(
fun switchToMessageQueue(shouldQueue: Boolean) { fun switchToMessageQueue(shouldQueue: Boolean) {
isQueueing = shouldQueue isQueueing = shouldQueue
} }
fun restoreMessageQueue(roomToken: String) {
messageQueue = dataStore.getMessageQueue(roomToken)
_messageQueueSizeFlow.tryEmit(messageQueue.size)
}
} }

View File

@ -484,7 +484,7 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
var queueStr = "" var queueStr = ""
queue?.let { queue?.let {
for (msg in queue) { for (msg in queue) {
val msgStr = "[${msg.message},${msg.replyTo},${msg.displayName},${msg.sendWithoutNotification}]" val msgStr = "${msg.message},${msg.replyTo},${msg.displayName},${msg.sendWithoutNotification}^"
queueStr += msgStr queueStr += msgStr
} }
} }
@ -500,9 +500,10 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
val queue: MutableList<MessageInputViewModel.QueuedMessage> = mutableListOf() val queue: MutableList<MessageInputViewModel.QueuedMessage> = mutableListOf()
if (queueStr.isEmpty()) return queue if (queueStr.isEmpty()) return queue
for (msgStr in queueStr.split("]")) { for (msgStr in queueStr.split("^")) {
try { try {
val msgArray = msgStr.replace("[", "").split(",") if (msgStr.isNotEmpty()) {
val msgArray = msgStr.split(",")
val message = msgArray[MESSAGE_INDEX] val message = msgArray[MESSAGE_INDEX]
val replyTo = msgArray[REPLY_TO_INDEX].toInt() val replyTo = msgArray[REPLY_TO_INDEX].toInt()
val displayName = msgArray[DISPLY_NAME_INDEX] val displayName = msgArray[DISPLY_NAME_INDEX]
@ -510,8 +511,9 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
val qMsg = MessageInputViewModel.QueuedMessage(message, displayName, replyTo, silent) val qMsg = MessageInputViewModel.QueuedMessage(message, displayName, replyTo, silent)
queue.add(qMsg) queue.add(qMsg)
}
} catch (e: IndexOutOfBoundsException) { } catch (e: IndexOutOfBoundsException) {
Log.e(TAG, "Message string: $msgStr\n $e") Log.e(TAG, "Message string: $msgStr\n Queue String: $queueStr \n$e")
} }
} }

View File

@ -802,6 +802,7 @@ How to translate with transifex:
<string name="show_banned_participants">Show banned participants</string> <string name="show_banned_participants">Show banned participants</string>
<string name="bans_list">Bans list</string> <string name="bans_list">Bans list</string>
<string name="connection_lost_sent_messages_are_queued">Connection lost - Sent messages are queued</string> <string name="connection_lost_sent_messages_are_queued">Connection lost - Sent messages are queued</string>
<string name="connection_lost_queued">Connection lost - %1$d are queued</string>
<string name="connection_established">Connection established</string> <string name="connection_established">Connection established</string>
<string name="message_deleted_by_you">Message deleted by you</string> <string name="message_deleted_by_you">Message deleted by you</string>
<string name="unban">Unban</string> <string name="unban">Unban</string>