show x when sending failed

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2024-12-10 14:35:56 +01:00
parent a78c9e1c08
commit e1c1574d6c
No known key found for this signature in database
GPG Key ID: C793F8B59F43CE7B
8 changed files with 115 additions and 32 deletions

View File

@ -114,7 +114,19 @@ class OutcomingTextMessageViewHolder(itemView: View) :
binding.messageQuote.quotedChatMessageView.visibility = View.GONE
}
setReadStatus(message.readStatus)
when (message.readStatus) {
ReadStatus.READ -> updateReadStatus(R.drawable.ic_check_all, context.resources?.getString(R.string.nc_message_read))
ReadStatus.SENT -> updateReadStatus(R.drawable.ic_check, context.resources?.getString(R.string.nc_message_sent))
ReadStatus.SENDING -> updateSendingStatus()
ReadStatus.FAILED -> updateReadStatus(
R.drawable.ic_baseline_close_24,
"failed"
)
else -> null
}
itemView.setTag(R.string.replyable_message_view_tag, message.replyable)
@ -129,29 +141,27 @@ class OutcomingTextMessageViewHolder(itemView: View) :
)
}
private fun setReadStatus(readStatus: Enum<ReadStatus>) {
val readStatusDrawableInt = when (readStatus) {
ReadStatus.READ -> R.drawable.ic_check_all
ReadStatus.SENT -> R.drawable.ic_check
else -> null
}
val readStatusContentDescriptionString = when (readStatus) {
ReadStatus.READ -> context.resources?.getString(R.string.nc_message_read)
ReadStatus.SENT -> context.resources?.getString(R.string.nc_message_sent)
else -> null
}
readStatusDrawableInt?.let { drawableInt ->
private fun updateReadStatus(readStatusDrawableInt: Int, description: String?) {
binding.sendingProgress.visibility = View.GONE
binding.checkMark.visibility = View.VISIBLE
readStatusDrawableInt.let { drawableInt ->
ResourcesCompat.getDrawable(context.resources, drawableInt, null)?.let {
binding.checkMark.setImageDrawable(it)
viewThemeUtils.talk.themeMessageCheckMark(binding.checkMark)
}
}
binding.checkMark.contentDescription = readStatusContentDescriptionString
binding.checkMark.contentDescription = description
}
private fun updateSendingStatus() {
binding.sendingProgress.visibility = View.VISIBLE
binding.checkMark.visibility = View.GONE
viewThemeUtils.material.colorProgressBar(binding.sendingProgress)
}
private fun longClickOnReaction(chatMessage: ChatMessage) {
commonMessageInterface.onLongClickReactions(chatMessage)
}

View File

@ -587,7 +587,7 @@ class ChatActivity :
list.forEachIndexed { _, qMsg ->
val temporaryChatMessage = ChatMessage()
temporaryChatMessage.jsonMessageId = TEMPORARY_MESSAGE_ID_INT
temporaryChatMessage.actorId = "-3"
temporaryChatMessage.actorId = TEMPORARY_MESSAGE_ID_STRING
temporaryChatMessage.timestamp = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS
temporaryChatMessage.message = qMsg.message.toString()
temporaryChatMessage.tempMessageId = qMsg.id
@ -813,6 +813,8 @@ class ChatActivity :
}
is MessageInputViewModel.SendChatMessageErrorState -> {
binding.messagesListView.smoothScrollToPosition(0)
// if (state.e is HttpException) {
// val code = state.e.code()
// if (code.toString().startsWith("2")) {
@ -2933,7 +2935,11 @@ class ChatActivity :
if (message.item is ChatMessage) {
val chatMessage = message.item as ChatMessage
if (chatMessage.jsonMessageId <= xChatLastCommonRead) {
if (chatMessage.sendingFailed) {
chatMessage.readStatus = ReadStatus.FAILED
} else if (chatMessage.isTempMessage) {
chatMessage.readStatus = ReadStatus.SENDING
} else if (chatMessage.jsonMessageId <= xChatLastCommonRead) {
chatMessage.readStatus = ReadStatus.READ
} else {
chatMessage.readStatus = ReadStatus.SENT
@ -3439,7 +3445,7 @@ class ChatActivity :
val message = iMessage as ChatMessage
if (hasVisibleItems(message) &&
!isSystemMessage(message) &&
message.id != "-3"
message.id != TEMPORARY_MESSAGE_ID_STRING
) {
MessageActionsDialog(
this,
@ -3863,7 +3869,7 @@ class ChatActivity :
CONTENT_TYPE_SYSTEM_MESSAGE -> !TextUtils.isEmpty(message.systemMessage)
CONTENT_TYPE_UNREAD_NOTICE_MESSAGE -> message.id == UNREAD_MESSAGES_MARKER_ID.toString()
CONTENT_TYPE_CALL_STARTED -> message.id == "-2"
CONTENT_TYPE_TEMP -> message.id == "-3"
CONTENT_TYPE_TEMP -> message.id == TEMPORARY_MESSAGE_ID_STRING
CONTENT_TYPE_DECK_CARD -> message.isDeckCard()
else -> false

View File

@ -115,11 +115,13 @@ data class ChatMessage(
var openWhenDownloaded: Boolean = true,
var isTempMessage: Boolean = false,
var isTempMessage: Boolean = false, // TODO: replace logic from message drafts with logic from temp message sending
var tempMessageId: Int = -1,
var tempMessageId: Int = -1, // TODO: replace logic from message drafts with logic from temp message sending
var referenceId: String? = null
var referenceId: String? = null,
var sendingFailed: Boolean = true
) : MessageContentType,
MessageContentType.Image {

View File

@ -186,7 +186,7 @@ class OfflineFirstChatRepository @Inject constructor(
)
if (list.isNotEmpty()) {
updateUiMessages(
chatMessages = list,
receivedChatMessages = list,
lookIntoFuture = false,
showUnreadMessagesMarker = false
)
@ -308,7 +308,7 @@ class OfflineFirstChatRepository @Inject constructor(
showUnreadMessagesMarker = showUnreadMessagesMarker && !weHaveMessagesFromOurself
updateUiMessages(
chatMessages = chatMessages,
receivedChatMessages = chatMessages,
lookIntoFuture = true,
showUnreadMessagesMarker = showUnreadMessagesMarker
)
@ -335,7 +335,7 @@ class OfflineFirstChatRepository @Inject constructor(
}
private suspend fun updateUiMessages(
chatMessages : List<ChatMessage>,
receivedChatMessages : List<ChatMessage>,
lookIntoFuture: Boolean,
showUnreadMessagesMarker: Boolean
) {
@ -346,11 +346,11 @@ class OfflineFirstChatRepository @Inject constructor(
oldTempMessages.forEach { _removeMessageFlow.emit(it) }
// add new messages to UI
val tripleChatMessages = Triple(lookIntoFuture, showUnreadMessagesMarker, chatMessages)
val tripleChatMessages = Triple(lookIntoFuture, showUnreadMessagesMarker, receivedChatMessages)
_messageFlow.emit(tripleChatMessages)
// remove temp messages from DB that are now found in the new messages
val chatMessagesReferenceIds = chatMessages.mapTo(HashSet(chatMessages.size)) { it.referenceId }
val chatMessagesReferenceIds = receivedChatMessages.mapTo(HashSet(receivedChatMessages.size)) { it.referenceId }
val tempChatMessagesThatCanBeReplaced = oldTempMessages.filter { it.referenceId in chatMessagesReferenceIds }
chatDao.deleteTempChatMessages(
internalConversationId,
@ -815,6 +815,17 @@ class OfflineFirstChatRepository @Inject constructor(
emit(Result.success(chatMessageModel))
} catch (e: Exception) {
Log.e(TAG, "Error when sending message", e)
val failedMessage = chatDao.getTempMessageForConversation(internalConversationId, referenceId).first()
failedMessage.sendingFailed = true
chatDao.updateChatMessage(failedMessage)
val failedMessageModel = failedMessage.asModel()
_removeMessageFlow.emit(failedMessageModel)
val tripleChatMessages = Triple(true, false, listOf(failedMessageModel))
_messageFlow.emit(tripleChatMessages)
emit(Result.failure(e))
}
}
@ -877,7 +888,7 @@ class OfflineFirstChatRepository @Inject constructor(
internalId = internalConversationId + "@_temp_" + currentTimeMillies,
internalConversationId = internalConversationId,
id = currentTimeMillies,
message = message + " (temp)",
message = message,
deleted = false,
token = conversationModel.token,
actorId = currentUser.userId!!,

View File

@ -48,6 +48,18 @@ interface ChatMessagesDao {
)
fun getTempMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>>
@Query(
"""
SELECT *
FROM ChatMessages
WHERE internalConversationId = :internalConversationId
AND referenceId = :referenceId
AND isTemporary = 1
ORDER BY timestamp DESC, id DESC
"""
)
fun getTempMessageForConversation(internalConversationId: String, referenceId: String): Flow<ChatMessageEntity>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun upsertChatMessages(chatMessages: List<ChatMessageEntity>)

View File

@ -10,6 +10,7 @@ package com.nextcloud.talk.data.database.mappers
import com.nextcloud.talk.models.json.chat.ChatMessageJson
import com.nextcloud.talk.data.database.model.ChatMessageEntity
import com.nextcloud.talk.chat.data.model.ChatMessage
import com.nextcloud.talk.models.json.chat.ReadStatus
fun ChatMessageJson.asEntity(accountId: Long) =
ChatMessageEntity(
@ -64,9 +65,22 @@ fun ChatMessageEntity.asModel() =
lastEditActorType = lastEditActorType,
lastEditTimestamp = lastEditTimestamp,
isDeleted = deleted,
referenceId = referenceId
referenceId = referenceId,
isTempMessage = isTemporary,
sendingFailed = sendingFailed,
readStatus = setStatus(isTemporary, sendingFailed)
)
fun setStatus(isTemporary: Boolean, sendingFailed: Boolean): ReadStatus {
return if (sendingFailed) {
ReadStatus.FAILED
} else if (isTemporary) {
ReadStatus.SENDING
} else {
ReadStatus.NONE
}
}
fun ChatMessageJson.asModel() =
ChatMessage(
jsonMessageId = id.toInt(),

View File

@ -9,5 +9,7 @@ package com.nextcloud.talk.models.json.chat
enum class ReadStatus {
NONE,
SENT,
READ
READ,
SENDING,
FAILED
}

View File

@ -84,6 +84,32 @@
app:layout_alignSelf="center"
app:tint="@color/high_emphasis_text" />
<ImageView
android:id="@+id/sending_failed"
android:layout_width="wrap_content"
android:layout_height="@dimen/message_bubble_checkmark_height"
android:layout_below="@id/messageTime"
android:layout_marginStart="8dp"
android:contentDescription="@null"
app:layout_alignSelf="center"
app:tint="@color/high_emphasis_text" />
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/sending_progress"
android:layout_width="wrap_content"
android:layout_height="@dimen/message_bubble_checkmark_height"
android:layout_below="@id/messageTime"
android:layout_marginStart="8dp"
android:contentDescription="@null"
android:indeterminate="true"
android:visibility="gone"
app:indicatorColor="@color/colorPrimary"
app:indicatorSize="14dp"
app:trackColor="@color/colorPrimary"
app:trackThickness="2dp"
tools:visibility="visible"
/>
<include
android:id="@+id/reactions"
layout="@layout/reactions_inside_message" />