Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Showing temporary messages when queued #4251

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .idea/inspectionProfiles/ktlint.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* SPDX-License-Identifier: GPL-3.0-or-later
*/

package com.nextcloud.talk.adapters.messages

import android.view.View
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.ViewCompat
import autodagger.AutoInjector
import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.chat.data.model.ChatMessage
import com.nextcloud.talk.databinding.ItemTemporaryMessageBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.DisplayUtils
import com.stfalcon.chatkit.messages.MessagesListAdapter
import javax.inject.Inject

@AutoInjector(NextcloudTalkApplication::class)
class TemporaryMessageViewHolder(outgoingView: View, payload: Any) :
MessagesListAdapter.OutcomingMessageViewHolder<ChatMessage>(outgoingView) {

private val binding: ItemTemporaryMessageBinding = ItemTemporaryMessageBinding.bind(outgoingView)

@Inject
lateinit var viewThemeUtils: ViewThemeUtils

override fun onBind(message: ChatMessage?) {
super.onBind(message)
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)

val bgBubbleColor = bubble.resources.getColor(R.color.bg_message_list_incoming_bubble, null)
val layout = R.drawable.shape_outcoming_message
val bubbleDrawable = DisplayUtils.getMessageSelector(
bgBubbleColor,
ResourcesCompat.getColor(bubble.resources, R.color.transparent, null),
bgBubbleColor,
layout
)
ViewCompat.setBackground(bubble, bubbleDrawable)

}

override fun viewDetached() {
// unused atm
}

override fun viewAttached() {
// unused atm
}

override fun viewRecycled() {
// unused atm
}
}
43 changes: 42 additions & 1 deletion app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ import com.nextcloud.talk.adapters.messages.PreviewMessageViewHolder
import com.nextcloud.talk.adapters.messages.SystemMessageInterface
import com.nextcloud.talk.adapters.messages.SystemMessageViewHolder
import com.nextcloud.talk.adapters.messages.TalkMessagesListAdapter
import com.nextcloud.talk.adapters.messages.TemporaryMessageViewHolder
import com.nextcloud.talk.adapters.messages.UnreadNoticeMessageViewHolder
import com.nextcloud.talk.adapters.messages.VoiceMessageInterface
import com.nextcloud.talk.api.NcApi
Expand Down Expand Up @@ -528,6 +529,32 @@ class ChatActivity :
private fun initObservers() {
Log.d(TAG, "initObservers Called")

messageInputViewModel.messageQueueFlow.observe(this) { list ->
for (message in list) {
Log.d("Julius", "Message recieved: $message")
val temporaryChatMessage = ChatMessage()
temporaryChatMessage.jsonMessageId = -3
temporaryChatMessage.actorId = "-3"
temporaryChatMessage.timestamp = System.currentTimeMillis() / 1000
temporaryChatMessage.message = message
adapter?.addToStart(temporaryChatMessage, true)
}
}

messageInputViewModel.messageQueueSizeFlow.observe(this) { size ->
if (size == 0) {
var i = 0
var pos = adapter?.getMessagePositionById("-3")
while (pos != null && pos > -1) {
adapter?.items?.removeAt(pos)
i++
pos = adapter?.getMessagePositionById("-3")
}
Log.d("Julius", "End i: $i")
}

}

this.lifecycleScope.launch {
chatViewModel.getConversationFlow
.onEach { conversationModel ->
Expand Down Expand Up @@ -613,6 +640,7 @@ class ChatActivity :
withUrl = urlForChatting
)
}
messageInputViewModel.getTempMessagesFromMessageQueue(roomToken)
}

is ChatViewModel.GetCapabilitiesErrorState -> {
Expand Down Expand Up @@ -1162,6 +1190,17 @@ class ChatActivity :
this
)

messageHolders.registerContentType(
CONTENT_TYPE_TEMP,
TemporaryMessageViewHolder::class.java,
payload,
R.layout.item_temporary_message,
TemporaryMessageViewHolder::class.java,
payload,
R.layout.item_temporary_message,
this
)

messageHolders.registerContentType(
CONTENT_TYPE_SYSTEM_MESSAGE,
SystemMessageViewHolder::class.java,
Expand Down Expand Up @@ -2322,8 +2361,8 @@ class ChatActivity :
try {
EmojiCompat.get().process(currentConversation?.displayName as CharSequence).toString()
} catch (e: java.lang.IllegalStateException) {
Log.e(TAG, "setActionBarTitle failed $e")
currentConversation?.displayName
error(e)
}
} else {
""
Expand Down Expand Up @@ -3439,6 +3478,7 @@ class ChatActivity :
CONTENT_TYPE_SYSTEM_MESSAGE -> !TextUtils.isEmpty(message.systemMessage)
CONTENT_TYPE_UNREAD_NOTICE_MESSAGE -> message.id == "-1"
CONTENT_TYPE_CALL_STARTED -> message.id == "-2"
CONTENT_TYPE_TEMP -> message.id == "-3"

else -> false
}
Expand Down Expand Up @@ -3620,6 +3660,7 @@ class ChatActivity :
private const val CONTENT_TYPE_VOICE_MESSAGE: Byte = 5
private const val CONTENT_TYPE_POLL: Byte = 6
private const val CONTENT_TYPE_LINK_PREVIEW: Byte = 7
private const val CONTENT_TYPE_TEMP: Byte = 8
private const val NEW_MESSAGES_POPUP_BUBBLE_DELAY: Long = 200
private const val GET_ROOM_INFO_DELAY_NORMAL: Long = 30000
private const val GET_ROOM_INFO_DELAY_LOBBY: Long = 5000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ import com.nextcloud.talk.utils.text.Spans
import com.otaliastudios.autocomplete.Autocomplete
import com.stfalcon.chatkit.commons.models.IMessage
import com.vanniktech.emoji.EmojiPopup
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -184,13 +183,15 @@ class MessageInputFragment : Fragment() {
val connectionGained = (!wasOnline && isOnline)
wasOnline = !binding.fragmentMessageInputView.isShown
Log.d(TAG, "isOnline: $isOnline\nwasOnline: $wasOnline\nconnectionGained: $connectionGained")
delay(500)
// delay(2000)
handleMessageQueue(isOnline)
handleUI(isOnline, connectionGained)
}.collect()
}

chatActivity.messageInputViewModel.messageQueueSizeFlow.observe(viewLifecycleOwner) { size ->
Log.d("Julius", "MessageQueueSizeFlow recieved: $size")

if (size > 0) {
binding.fragmentConnectionLost.text = getString(R.string.connection_lost_queued, size)
} else {
Expand Down Expand Up @@ -233,7 +234,7 @@ class MessageInputFragment : Fragment() {
binding.fragmentConnectionLost.clearAnimation()
binding.fragmentConnectionLost.visibility = View.GONE
binding.fragmentConnectionLost.setBackgroundColor(resources.getColor(R.color.hwSecurityRed))
binding.fragmentConnectionLost.text = getString(R.string.connection_lost_sent_messages_are_queued)
// binding.fragmentConnectionLost.text = getString(R.string.connection_lost_sent_messages_are_queued)
binding.fragmentConnectionLost.visibility = View.VISIBLE
binding.fragmentMessageInputView.attachmentButton.isEnabled = false
binding.fragmentMessageInputView.recordAudioButton.isEnabled = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import java.lang.Thread.sleep
import java.util.concurrent.TimeUnit
import javax.inject.Inject

class OfflineFirstChatRepository @Inject constructor(
Expand Down Expand Up @@ -311,55 +313,63 @@ class OfflineFirstChatRepository @Inject constructor(
Log.d(TAG, "An online request is made!!!!!!!!!!!!!!!!!!!!")
val fieldMap = bundle.getSerializable(BundleKeys.KEY_FIELD_MAP) as HashMap<String, Int>

try {
val result = network.pullChatMessages(credentials, urlForChatting, fieldMap)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
// .timeout(3, TimeUnit.SECONDS)
.map { it ->
when (it.code()) {
HTTP_CODE_OK -> {
Log.d(TAG, "getMessagesFromServer HTTP_CODE_OK")
newXChatLastCommonRead = it.headers()["X-Chat-Last-Common-Read"]?.let {
Integer.parseInt(it)
var attempts = 0
while (attempts < 2) {
sleep(100)
try {
val result = network.pullChatMessages(credentials, urlForChatting, fieldMap)
.subscribeOn(Schedulers.io())
.timeout(5, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.map { it ->
when (it.code()) {
HTTP_CODE_OK -> {
Log.d(TAG, "getMessagesFromServer HTTP_CODE_OK")
newXChatLastCommonRead = it.headers()["X-Chat-Last-Common-Read"]?.let {
Integer.parseInt(it)
}

return@map Pair(
HTTP_CODE_OK,
(it.body() as ChatOverall).ocs!!.data!!
)
}

return@map Pair(
HTTP_CODE_OK,
(it.body() as ChatOverall).ocs!!.data!!
)
}

HTTP_CODE_NOT_MODIFIED -> {
Log.d(TAG, "getMessagesFromServer HTTP_CODE_NOT_MODIFIED")
HTTP_CODE_NOT_MODIFIED -> {
Log.d(TAG, "getMessagesFromServer HTTP_CODE_NOT_MODIFIED")

return@map Pair(
HTTP_CODE_NOT_MODIFIED,
listOf<ChatMessageJson>()
)
}
return@map Pair(
HTTP_CODE_NOT_MODIFIED,
listOf<ChatMessageJson>()
)
}

HTTP_CODE_PRECONDITION_FAILED -> {
Log.d(TAG, "getMessagesFromServer HTTP_CODE_PRECONDITION_FAILED")
HTTP_CODE_PRECONDITION_FAILED -> {
Log.d(TAG, "getMessagesFromServer HTTP_CODE_PRECONDITION_FAILED")

return@map Pair(
HTTP_CODE_PRECONDITION_FAILED,
listOf<ChatMessageJson>()
)
}
return@map Pair(
HTTP_CODE_PRECONDITION_FAILED,
listOf<ChatMessageJson>()
)
}

else -> {
return@map Pair(
HTTP_CODE_PRECONDITION_FAILED,
listOf<ChatMessageJson>()
)
else -> {
return@map Pair(
HTTP_CODE_PRECONDITION_FAILED,
listOf<ChatMessageJson>()
)
}
}
}
.blockingSingle()
return result
} catch (e: Exception) {
Log.e(TAG, "attempt: $attempts Something went wrong when pulling chat messages", e)
attempts++
if (attempts == 2) {
// TODO show user message failed
}
.blockingSingle()
return result
} catch (e: Exception) {
Log.e(TAG, "Something went wrong when pulling chat messages", e)
}
}
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ class MessageInputViewModel @Inject constructor(
val messageQueueSizeFlow: LiveData<Int>
get() = _messageQueueSizeFlow.asLiveData()

private val _messageQueueFlow: MutableLiveData<List<String>> = MutableLiveData()
val messageQueueFlow: LiveData<List<String>>
get() = _messageQueueFlow

@Suppress("LongParameterList")
fun sendChatMessage(
roomToken: String,
Expand All @@ -138,6 +142,7 @@ class MessageInputViewModel @Inject constructor(
messageQueue.add(QueuedMessage(message, displayName, replyTo, sendWithoutNotification))
dataStore.saveMessageQueue(roomToken, messageQueue)
_messageQueueSizeFlow.update { messageQueue.size }
_messageQueueFlow.postValue(listOf(message.toString()))
return
}

Expand Down Expand Up @@ -261,6 +266,17 @@ class MessageInputViewModel @Inject constructor(
msg.sendWithoutNotification!!
)
}
_messageQueueSizeFlow.tryEmit(0)
}

fun getTempMessagesFromMessageQueue(roomToken: String) {
val queue = dataStore.getMessageQueue(roomToken)
val list = mutableListOf<String>()
for (msg in queue) {
Log.d("Julius", "Msg: ${msg.message}")
list.add(msg.message.toString())
}
_messageQueueFlow.postValue(list)
}

fun switchToMessageQueue(shouldQueue: Boolean) {
Expand Down
Loading
Loading