mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-18 19:19:33 +01:00
improve send status handling
replace sendingFailed with SendStatus add auto migration (incl deleting of column sendingFailed) Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
parent
8c066eb521
commit
86bfaa8657
@ -2,7 +2,7 @@
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 17,
|
||||
"identityHash": "c6e75dfc4f897469a82de6aeff6208c1",
|
||||
"identityHash": "5bc4247e179307faa995552da5d34324",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "User",
|
||||
@ -445,7 +445,7 @@
|
||||
},
|
||||
{
|
||||
"tableName": "ChatMessages",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `token` TEXT NOT NULL, `id` INTEGER NOT NULL, `internalConversationId` TEXT NOT NULL, `actorDisplayName` TEXT NOT NULL, `message` TEXT NOT NULL, `actorId` TEXT NOT NULL, `actorType` TEXT NOT NULL, `deleted` INTEGER NOT NULL, `expirationTimestamp` INTEGER NOT NULL, `isReplyable` INTEGER NOT NULL, `isTemporary` INTEGER NOT NULL, `lastEditActorDisplayName` TEXT, `lastEditActorId` TEXT, `lastEditActorType` TEXT, `lastEditTimestamp` INTEGER, `markdown` INTEGER, `messageParameters` TEXT, `messageType` TEXT NOT NULL, `parent` INTEGER, `reactions` TEXT, `reactionsSelf` TEXT, `referenceId` TEXT, `sendingFailed` INTEGER NOT NULL, `sendStatus` TEXT, `silent` INTEGER NOT NULL, `systemMessage` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, PRIMARY KEY(`internalId`), FOREIGN KEY(`internalConversationId`) REFERENCES `Conversations`(`internalId`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `token` TEXT NOT NULL, `id` INTEGER NOT NULL, `internalConversationId` TEXT NOT NULL, `actorDisplayName` TEXT NOT NULL, `message` TEXT NOT NULL, `actorId` TEXT NOT NULL, `actorType` TEXT NOT NULL, `deleted` INTEGER NOT NULL, `expirationTimestamp` INTEGER NOT NULL, `isReplyable` INTEGER NOT NULL, `isTemporary` INTEGER NOT NULL, `lastEditActorDisplayName` TEXT, `lastEditActorId` TEXT, `lastEditActorType` TEXT, `lastEditTimestamp` INTEGER, `markdown` INTEGER, `messageParameters` TEXT, `messageType` TEXT NOT NULL, `parent` INTEGER, `reactions` TEXT, `reactionsSelf` TEXT, `referenceId` TEXT, `sendStatus` TEXT, `silent` INTEGER NOT NULL, `systemMessage` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, PRIMARY KEY(`internalId`), FOREIGN KEY(`internalConversationId`) REFERENCES `Conversations`(`internalId`) ON UPDATE CASCADE ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "internalId",
|
||||
@ -581,12 +581,6 @@
|
||||
"columnName": "referenceId",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "sendingFailed",
|
||||
"columnName": "sendingFailed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "sendStatus",
|
||||
"columnName": "sendStatus",
|
||||
@ -730,7 +724,7 @@
|
||||
],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c6e75dfc4f897469a82de6aeff6208c1')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5bc4247e179307faa995552da5d34324')"
|
||||
]
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedA
|
||||
import com.nextcloud.talk.chat.ChatActivity
|
||||
import com.nextcloud.talk.chat.data.ChatMessageRepository
|
||||
import com.nextcloud.talk.chat.data.model.ChatMessage
|
||||
import com.nextcloud.talk.data.database.model.SendStatus
|
||||
import com.nextcloud.talk.data.network.NetworkMonitor
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.ItemCustomOutcomingTextMessageBinding
|
||||
@ -184,7 +185,7 @@ class OutcomingTextMessageViewHolder(itemView: View) :
|
||||
binding.checkMark.visibility = View.INVISIBLE
|
||||
binding.sendingProgress.visibility = View.GONE
|
||||
|
||||
if (message.sendingFailed) {
|
||||
if (message.sendStatus == SendStatus.FAILED) {
|
||||
updateStatus(R.drawable.baseline_error_outline_24, context.resources?.getString(R.string.nc_message_failed))
|
||||
} else if (message.isTemporary) {
|
||||
updateStatus(R.drawable.baseline_schedule_24, context.resources?.getString(R.string.nc_message_sending))
|
||||
|
@ -200,7 +200,7 @@ class MessageInputFragment : Fragment() {
|
||||
val connectionGained = (!wasOnline && isOnline)
|
||||
Log.d(TAG, "isOnline: $isOnline\nwasOnline: $wasOnline\nconnectionGained: $connectionGained")
|
||||
if (connectionGained) {
|
||||
chatActivity.messageInputViewModel.sendTempMessages(
|
||||
chatActivity.messageInputViewModel.sendUnsentMessages(
|
||||
chatActivity.conversationUser!!.getCredentials(),
|
||||
ApiUtils.getUrlForChat(
|
||||
chatActivity.chatApiVersion,
|
||||
|
@ -110,7 +110,7 @@ interface ChatMessageRepository : LifecycleAwareManager {
|
||||
|
||||
suspend fun editTempChatMessage(message: ChatMessage, editedMessageText: String): Flow<Boolean>
|
||||
|
||||
suspend fun sendTempChatMessages(credentials: String, url: String)
|
||||
suspend fun sendUnsentChatMessages(credentials: String, url: String)
|
||||
|
||||
suspend fun deleteTempMessage(chatMessage: ChatMessage)
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import android.util.Log
|
||||
import com.bluelinelabs.logansquare.annotation.JsonIgnore
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.data.database.model.SendStatus
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.json.chat.ChatUtils.Companion.getParsedMessage
|
||||
import com.nextcloud.talk.models.json.chat.ReadStatus
|
||||
@ -119,7 +120,7 @@ data class ChatMessage(
|
||||
|
||||
var referenceId: String? = null,
|
||||
|
||||
var sendingFailed: Boolean = true,
|
||||
var sendStatus: SendStatus? = null,
|
||||
|
||||
var silent: Boolean = false
|
||||
|
||||
|
@ -215,7 +215,8 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
sendTempChatMessages(credentials, urlForChatting)
|
||||
// this call could be deleted when we have a worker to send messages..
|
||||
sendUnsentChatMessages(credentials, urlForChatting)
|
||||
|
||||
// delay is a dirty workaround to make sure messages are added to adapter on initial load before dealing
|
||||
// with them (otherwise there is a race condition).
|
||||
@ -845,8 +846,6 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "sending chat message: " + message)
|
||||
|
||||
return flow {
|
||||
val response = network.sendChatMessage(
|
||||
credentials,
|
||||
@ -881,7 +880,6 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
referenceId
|
||||
).firstOrNull()
|
||||
failedMessage?.let {
|
||||
// it.sendingFailed = true
|
||||
it.sendStatus = SendStatus.FAILED
|
||||
chatDao.updateChatMessage(it)
|
||||
|
||||
@ -903,7 +901,6 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
referenceId: String
|
||||
): Flow<Result<ChatMessage?>> {
|
||||
val messageToResend = chatDao.getTempMessageForConversation(internalConversationId, referenceId).first()
|
||||
// messageToResend.sendingFailed = false
|
||||
messageToResend.sendStatus = SendStatus.PENDING
|
||||
chatDao.updateChatMessage(messageToResend)
|
||||
|
||||
@ -960,8 +957,8 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendTempChatMessages(credentials: String, url: String) {
|
||||
val tempMessages = chatDao.getPendingOrFailedMessagesForConversation(internalConversationId).first()
|
||||
override suspend fun sendUnsentChatMessages(credentials: String, url: String) {
|
||||
val tempMessages = chatDao.getTempUnsentMessagesForConversation(internalConversationId).first()
|
||||
tempMessages.sortedBy { it.internalId }.onEach {
|
||||
sendChatMessage(
|
||||
credentials,
|
||||
@ -1055,7 +1052,7 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
actorDisplayName = currentUser.displayName!!,
|
||||
referenceId = referenceId,
|
||||
isTemporary = true,
|
||||
sendingFailed = false,
|
||||
sendStatus = SendStatus.PENDING,
|
||||
silent = sendWithoutNotification
|
||||
)
|
||||
return entity
|
||||
|
@ -169,9 +169,9 @@ class MessageInputViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun sendTempMessages(credentials: String, url: String) {
|
||||
fun sendUnsentMessages(credentials: String, url: String) {
|
||||
viewModelScope.launch {
|
||||
chatRepository.sendTempChatMessages(
|
||||
chatRepository.sendUnsentChatMessages(
|
||||
credentials,
|
||||
url
|
||||
)
|
||||
|
@ -56,22 +56,11 @@ interface ChatMessagesDao {
|
||||
FROM ChatMessages
|
||||
WHERE internalConversationId = :internalConversationId
|
||||
AND isTemporary = 1
|
||||
AND (sendStatus = 'PENDING' OR sendStatus = 'FAILED')
|
||||
AND sendStatus != 'SENT_PENDING_ACK'
|
||||
ORDER BY timestamp DESC, id DESC
|
||||
"""
|
||||
)
|
||||
fun getPendingOrFailedMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT *
|
||||
FROM ChatMessages
|
||||
WHERE internalConversationId = :internalConversationId
|
||||
AND (isTemporary = 1 OR sendStatus = 'SENT_PENDING_ACK')
|
||||
ORDER BY timestamp DESC, id DESC
|
||||
"""
|
||||
)
|
||||
fun getTempOrSendingAckMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>>
|
||||
fun getTempUnsentMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
|
@ -68,7 +68,7 @@ fun ChatMessageEntity.asModel() =
|
||||
isDeleted = deleted,
|
||||
referenceId = referenceId,
|
||||
isTemporary = isTemporary,
|
||||
sendingFailed = sendingFailed,
|
||||
sendStatus = sendStatus,
|
||||
readStatus = ReadStatus.NONE,
|
||||
silent = silent
|
||||
)
|
||||
|
@ -64,7 +64,6 @@ data class ChatMessageEntity(
|
||||
@ColumnInfo(name = "reactions") var reactions: LinkedHashMap<String, Int>? = null,
|
||||
@ColumnInfo(name = "reactionsSelf") var reactionsSelf: ArrayList<String>? = null,
|
||||
@ColumnInfo(name = "referenceId") var referenceId: String? = null,
|
||||
@ColumnInfo(name = "sendingFailed") var sendingFailed: Boolean = false,
|
||||
@ColumnInfo(name = "sendStatus") var sendStatus: SendStatus? = null,
|
||||
@ColumnInfo(name = "silent") var silent: Boolean = false,
|
||||
@ColumnInfo(name = "systemMessage") var systemMessageType: ChatMessage.SystemMessageType,
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025 Your Name <your@email.com>
|
||||
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
|
@ -1,18 +1,31 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-FileCopyrightText: 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package com.nextcloud.talk.data.source.local
|
||||
|
||||
import android.util.Log
|
||||
import androidx.room.DeleteColumn
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import java.sql.SQLException
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
object Migrations {
|
||||
|
||||
//region Auto migrations
|
||||
|
||||
@DeleteColumn(tableName = "ChatMessages", columnName = "sendingFailed")
|
||||
class AutoMigration16To17 : AutoMigrationSpec
|
||||
|
||||
//endregion
|
||||
|
||||
//region Manual migrations
|
||||
|
||||
val MIGRATION_6_8 = object : Migration(6, 8) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
Log.i("Migrations", "Migrating 6 to 8")
|
||||
@ -76,12 +89,7 @@ object Migrations {
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_16_17 = object : Migration(16, 17) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
Log.i("Migrations", "Migrating 16 to 17")
|
||||
addSendStatus(db)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
fun migrateToRoom(db: SupportSQLiteDatabase) {
|
||||
db.execSQL(
|
||||
@ -326,17 +334,6 @@ object Migrations {
|
||||
}
|
||||
}
|
||||
|
||||
private fun addSendStatus(db: SupportSQLiteDatabase) {
|
||||
try {
|
||||
db.execSQL(
|
||||
"ALTER TABLE ChatMessages " +
|
||||
"ADD COLUMN sendStatus TEXT NOT NULL DEFAULT 'PENDING'"
|
||||
)
|
||||
} catch (e: SQLException) {
|
||||
Log.i("Migrations", "Something went wrong when adding column sendStatus to table ChatMessages")
|
||||
}
|
||||
}
|
||||
|
||||
fun addTempMessagesSupport(db: SupportSQLiteDatabase) {
|
||||
try {
|
||||
db.execSQL(
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-FileCopyrightText: 2023-2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-FileCopyrightText: 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* SPDX-FileCopyrightText: 2017-2020 Mario Danic <mario@lovelyhq.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
@ -23,6 +23,7 @@ import com.nextcloud.talk.data.database.dao.ConversationsDao
|
||||
import com.nextcloud.talk.data.database.model.ChatBlockEntity
|
||||
import com.nextcloud.talk.data.database.model.ChatMessageEntity
|
||||
import com.nextcloud.talk.data.database.model.ConversationEntity
|
||||
import com.nextcloud.talk.data.source.local.Migrations.AutoMigration16To17
|
||||
import com.nextcloud.talk.data.source.local.converters.ArrayListConverter
|
||||
import com.nextcloud.talk.data.source.local.converters.CapabilitiesConverter
|
||||
import com.nextcloud.talk.data.source.local.converters.ExternalSignalingServerConverter
|
||||
@ -52,7 +53,8 @@ import java.util.Locale
|
||||
],
|
||||
version = 17,
|
||||
autoMigrations = [
|
||||
AutoMigration(from = 9, to = 10)
|
||||
AutoMigration(from = 9, to = 10),
|
||||
AutoMigration(from = 16, to = 17, spec = AutoMigration16To17::class)
|
||||
],
|
||||
exportSchema = true
|
||||
)
|
||||
@ -110,7 +112,7 @@ abstract class TalkDatabase : RoomDatabase() {
|
||||
return Room
|
||||
.databaseBuilder(context.applicationContext, TalkDatabase::class.java, dbName)
|
||||
// comment out openHelperFactory to view the database entries in Android Studio for debugging
|
||||
// .openHelperFactory(factory)
|
||||
.openHelperFactory(factory)
|
||||
.addMigrations(
|
||||
Migrations.MIGRATION_6_8,
|
||||
Migrations.MIGRATION_7_8,
|
||||
@ -120,8 +122,7 @@ abstract class TalkDatabase : RoomDatabase() {
|
||||
Migrations.MIGRATION_12_13,
|
||||
Migrations.MIGRATION_13_14,
|
||||
Migrations.MIGRATION_14_15,
|
||||
Migrations.MIGRATION_15_16,
|
||||
Migrations.MIGRATION_16_17
|
||||
Migrations.MIGRATION_15_16
|
||||
)
|
||||
.allowMainThreadQueries()
|
||||
.addCallback(
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Nextcloud Talk - Android Client
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2025 Your Name <your@email.com>
|
||||
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
|
@ -18,6 +18,7 @@ import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.chat.ChatActivity
|
||||
import com.nextcloud.talk.chat.data.model.ChatMessage
|
||||
import com.nextcloud.talk.data.database.model.SendStatus
|
||||
import com.nextcloud.talk.data.network.NetworkMonitor
|
||||
import com.nextcloud.talk.databinding.DialogTempMessageActionsBinding
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
@ -58,9 +59,10 @@ class TempMessageActionsDialog(
|
||||
|
||||
private fun initMenuItems() {
|
||||
this.lifecycleScope.launch {
|
||||
initResendMessage(message.sendingFailed && networkMonitor.isOnline.value)
|
||||
initMenuEditMessage(message.sendingFailed || !networkMonitor.isOnline.value)
|
||||
initMenuDeleteMessage(message.sendingFailed || !networkMonitor.isOnline.value)
|
||||
val sendingFailed = message.sendStatus == SendStatus.FAILED
|
||||
initResendMessage(sendingFailed && networkMonitor.isOnline.value)
|
||||
initMenuEditMessage(sendingFailed || !networkMonitor.isOnline.value)
|
||||
initMenuDeleteMessage(sendingFailed || !networkMonitor.isOnline.value)
|
||||
initMenuItemCopy()
|
||||
}
|
||||
}
|
||||
|
@ -30,16 +30,9 @@ class DummyChatMessagesDaoImpl : ChatMessagesDao {
|
||||
override fun getTempMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>> =
|
||||
flowOf()
|
||||
|
||||
override fun getPendingOrFailedMessagesForConversation(
|
||||
internalConversationId: String
|
||||
): Flow<List<ChatMessageEntity>> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun getTempOrSendingAckMessagesForConversation(
|
||||
internalConversationId: String
|
||||
): Flow<List<ChatMessageEntity>> {
|
||||
TODO("Not yet implemented")
|
||||
override fun getTempUnsentMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>> {
|
||||
// nothing to return here as long this class is only used for the Search window
|
||||
return flowOf()
|
||||
}
|
||||
|
||||
override fun getTempMessageForConversation(
|
||||
|
Loading…
Reference in New Issue
Block a user