mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-19 19:49:33 +01:00
Merge pull request #4747 from nextcloud/fix_edit_bot_messages
Edit checkbox messages directly
This commit is contained in:
commit
5d78fed901
@ -13,27 +13,38 @@ import android.content.Context
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.widget.CheckBox
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.text.toSpanned
|
||||||
import autodagger.AutoInjector
|
import autodagger.AutoInjector
|
||||||
import coil.load
|
import coil.load
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||||
import com.nextcloud.talk.chat.ChatActivity
|
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.chat.data.model.ChatMessage
|
||||||
|
import com.nextcloud.talk.data.user.model.User
|
||||||
import com.nextcloud.talk.databinding.ItemCustomIncomingTextMessageBinding
|
import com.nextcloud.talk.databinding.ItemCustomIncomingTextMessageBinding
|
||||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||||
import com.nextcloud.talk.utils.ApiUtils
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
|
import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability
|
||||||
import com.nextcloud.talk.utils.ChatMessageUtils
|
import com.nextcloud.talk.utils.ChatMessageUtils
|
||||||
import com.nextcloud.talk.utils.DateUtils
|
import com.nextcloud.talk.utils.DateUtils
|
||||||
|
import com.nextcloud.talk.utils.SpreedFeatures
|
||||||
import com.nextcloud.talk.utils.TextMatchers
|
import com.nextcloud.talk.utils.TextMatchers
|
||||||
|
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||||
import com.nextcloud.talk.utils.message.MessageUtils
|
import com.nextcloud.talk.utils.message.MessageUtils
|
||||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||||
import com.stfalcon.chatkit.messages.MessageHolders
|
import com.stfalcon.chatkit.messages.MessageHolders
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.util.Date
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AutoInjector(NextcloudTalkApplication::class)
|
@AutoInjector(NextcloudTalkApplication::class)
|
||||||
@ -57,26 +68,41 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var dateUtils: DateUtils
|
lateinit var dateUtils: DateUtils
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var currentUserProvider: CurrentUserProviderNew
|
||||||
|
|
||||||
lateinit var commonMessageInterface: CommonMessageInterface
|
lateinit var commonMessageInterface: CommonMessageInterface
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var chatRepository: ChatMessageRepository
|
||||||
|
|
||||||
|
private var job: Job? = null
|
||||||
|
|
||||||
override fun onBind(message: ChatMessage) {
|
override fun onBind(message: ChatMessage) {
|
||||||
super.onBind(message)
|
super.onBind(message)
|
||||||
sharedApplication!!.componentApplication.inject(this)
|
sharedApplication!!.componentApplication.inject(this)
|
||||||
|
|
||||||
setAvatarAndAuthorOnMessageItem(message)
|
setAvatarAndAuthorOnMessageItem(message)
|
||||||
colorizeMessageBubble(message)
|
colorizeMessageBubble(message)
|
||||||
|
|
||||||
itemView.isSelected = false
|
itemView.isSelected = false
|
||||||
|
val user = currentUserProvider.currentUser.blockingGet()
|
||||||
|
val hasCheckboxes = processCheckboxes(
|
||||||
|
message,
|
||||||
|
user
|
||||||
|
)
|
||||||
|
processMessage(message, hasCheckboxes)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processMessage(message: ChatMessage, hasCheckboxes: Boolean) {
|
||||||
var textSize = context.resources!!.getDimension(R.dimen.chat_text_size)
|
var textSize = context.resources!!.getDimension(R.dimen.chat_text_size)
|
||||||
|
if (!hasCheckboxes) {
|
||||||
|
binding.messageText.visibility = View.VISIBLE
|
||||||
|
binding.checkboxContainer.visibility = View.GONE
|
||||||
var processedMessageText = messageUtils.enrichChatMessageText(
|
var processedMessageText = messageUtils.enrichChatMessageText(
|
||||||
binding.messageText.context,
|
binding.messageText.context,
|
||||||
message,
|
message,
|
||||||
true,
|
true,
|
||||||
viewThemeUtils
|
viewThemeUtils
|
||||||
)
|
)
|
||||||
|
|
||||||
processedMessageText = messageUtils.processMessageParameters(
|
processedMessageText = messageUtils.processMessageParameters(
|
||||||
binding.messageText.context,
|
binding.messageText.context,
|
||||||
viewThemeUtils,
|
viewThemeUtils,
|
||||||
@ -84,7 +110,6 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
|
|||||||
message,
|
message,
|
||||||
itemView
|
itemView
|
||||||
)
|
)
|
||||||
|
|
||||||
val messageParameters = message.messageParameters
|
val messageParameters = message.messageParameters
|
||||||
if (
|
if (
|
||||||
(messageParameters == null || messageParameters.size <= 0) &&
|
(messageParameters == null || messageParameters.size <= 0) &&
|
||||||
@ -94,9 +119,12 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
|
|||||||
itemView.isSelected = true
|
itemView.isSelected = true
|
||||||
binding.messageAuthor.visibility = View.GONE
|
binding.messageAuthor.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
||||||
binding.messageText.text = processedMessageText
|
binding.messageText.text = processedMessageText
|
||||||
|
} else {
|
||||||
|
binding.messageText.visibility = View.GONE
|
||||||
|
binding.checkboxContainer.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
if (message.lastEditTimestamp != 0L && !message.isDeleted) {
|
if (message.lastEditTimestamp != 0L && !message.isDeleted) {
|
||||||
binding.messageEditIndicator.visibility = View.VISIBLE
|
binding.messageEditIndicator.visibility = View.VISIBLE
|
||||||
@ -105,7 +133,7 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
|
|||||||
binding.messageEditIndicator.visibility = View.GONE
|
binding.messageEditIndicator.visibility = View.GONE
|
||||||
binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
|
binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
|
||||||
}
|
}
|
||||||
|
binding.messageTime.setTextColor(ContextCompat.getColor(context, R.color.no_emphasis_text))
|
||||||
// parent message handling
|
// parent message handling
|
||||||
if (!message.isDeleted && message.parentMessageId != null) {
|
if (!message.isDeleted && message.parentMessageId != null) {
|
||||||
processParentMessage(message)
|
processParentMessage(message)
|
||||||
@ -127,6 +155,105 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun processCheckboxes(chatMessage: ChatMessage, user: User): Boolean {
|
||||||
|
val chatActivity = commonMessageInterface as ChatActivity
|
||||||
|
val message = chatMessage.message!!.toSpanned()
|
||||||
|
val messageTextView = binding.messageText
|
||||||
|
val checkBoxContainer = binding.checkboxContainer
|
||||||
|
val isOlderThanTwentyFourHours = chatMessage
|
||||||
|
.createdAt
|
||||||
|
.before(Date(System.currentTimeMillis() - AGE_THRESHOLD_FOR_EDIT_MESSAGE))
|
||||||
|
|
||||||
|
val messageIsEditable = hasSpreedFeatureCapability(
|
||||||
|
user.capabilities?.spreedCapability!!,
|
||||||
|
SpreedFeatures.EDIT_MESSAGES
|
||||||
|
) &&
|
||||||
|
!isOlderThanTwentyFourHours
|
||||||
|
|
||||||
|
checkBoxContainer.removeAllViews()
|
||||||
|
val regex = """(- \[(X|x| )])\s*(.+)""".toRegex(RegexOption.MULTILINE)
|
||||||
|
val matches = regex.findAll(message)
|
||||||
|
|
||||||
|
if (matches.none()) return false
|
||||||
|
|
||||||
|
val firstPart = message.toString().substringBefore("\n- [")
|
||||||
|
messageTextView.text = messageUtils.enrichChatMessageText(
|
||||||
|
binding.messageText.context, firstPart, true, viewThemeUtils
|
||||||
|
)
|
||||||
|
|
||||||
|
val checkboxList = mutableListOf<CheckBox>()
|
||||||
|
|
||||||
|
matches.forEach { matchResult ->
|
||||||
|
val isChecked = matchResult.groupValues[CHECKED_GROUP_INDEX] == "X" ||
|
||||||
|
matchResult.groupValues[CHECKED_GROUP_INDEX] == "x"
|
||||||
|
val taskText = matchResult.groupValues[TASK_TEXT_GROUP_INDEX].trim()
|
||||||
|
|
||||||
|
val checkBox = CheckBox(checkBoxContainer.context).apply {
|
||||||
|
text = taskText
|
||||||
|
this.isChecked = isChecked
|
||||||
|
this.isEnabled = (
|
||||||
|
chatMessage.actorType == "bots" ||
|
||||||
|
chatActivity.userAllowedByPrivilages(chatMessage)
|
||||||
|
) && messageIsEditable
|
||||||
|
|
||||||
|
setTextColor(ContextCompat.getColor(context, R.color.no_emphasis_text))
|
||||||
|
|
||||||
|
setOnCheckedChangeListener { _, _ ->
|
||||||
|
updateCheckboxStates(chatMessage, user, checkboxList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkBoxContainer.addView(checkBox)
|
||||||
|
checkboxList.add(checkBox)
|
||||||
|
viewThemeUtils.platform.themeCheckbox(checkBox)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateCheckboxStates(chatMessage: ChatMessage, user: User, checkboxes: List<CheckBox>) {
|
||||||
|
job = CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val apiVersion: Int = ApiUtils.getChatApiVersion(
|
||||||
|
user.capabilities?.spreedCapability!!,
|
||||||
|
intArrayOf(1)
|
||||||
|
)
|
||||||
|
val updatedMessage = updateMessageWithCheckboxStates(chatMessage.message!!, checkboxes)
|
||||||
|
chatRepository.editChatMessage(
|
||||||
|
user.getCredentials(),
|
||||||
|
ApiUtils.getUrlForChatMessage(apiVersion, user.baseUrl!!, chatMessage.token!!, chatMessage.id),
|
||||||
|
updatedMessage
|
||||||
|
).collect { result ->
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (result.isSuccess) {
|
||||||
|
val editedMessage = result.getOrNull()?.ocs?.data!!.parentMessage!!
|
||||||
|
Log.d(TAG, "EditedMessage: $editedMessage")
|
||||||
|
binding.messageEditIndicator.apply {
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
binding.messageTime.text =
|
||||||
|
dateUtils.getLocalTimeStringFromTimestamp(editedMessage.lastEditTimestamp!!)
|
||||||
|
} else {
|
||||||
|
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateMessageWithCheckboxStates(originalMessage: String, checkboxes: List<CheckBox>): String {
|
||||||
|
var updatedMessage = originalMessage
|
||||||
|
val regex = """(- \[(X|x| )])\s*(.+)""".toRegex(RegexOption.MULTILINE)
|
||||||
|
|
||||||
|
checkboxes.forEach { _ ->
|
||||||
|
updatedMessage = regex.replace(updatedMessage) { matchResult ->
|
||||||
|
val taskText = matchResult.groupValues[TASK_TEXT_GROUP_INDEX].trim()
|
||||||
|
val checkboxState = if (checkboxes.find { it.text == taskText }?.isChecked == true) "X" else " "
|
||||||
|
"- [$checkboxState] $taskText"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updatedMessage
|
||||||
|
}
|
||||||
|
|
||||||
private fun longClickOnReaction(chatMessage: ChatMessage) {
|
private fun longClickOnReaction(chatMessage: ChatMessage) {
|
||||||
commonMessageInterface.onLongClickReactions(chatMessage)
|
commonMessageInterface.onLongClickReactions(chatMessage)
|
||||||
}
|
}
|
||||||
@ -231,8 +358,16 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
|
|||||||
this.commonMessageInterface = commonMessageInterface
|
this.commonMessageInterface = commonMessageInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun viewDetached() {
|
||||||
|
super.viewDetached()
|
||||||
|
job?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TEXT_SIZE_MULTIPLIER = 2.5
|
const val TEXT_SIZE_MULTIPLIER = 2.5
|
||||||
private val TAG = IncomingTextMessageViewHolder::class.java.simpleName
|
private val TAG = IncomingTextMessageViewHolder::class.java.simpleName
|
||||||
|
private const val CHECKED_GROUP_INDEX = 2
|
||||||
|
private const val TASK_TEXT_GROUP_INDEX = 3
|
||||||
|
private const val AGE_THRESHOLD_FOR_EDIT_MESSAGE: Long = 86400000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,31 +13,43 @@ import android.content.Context
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.widget.CheckBox
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import androidx.core.text.toSpanned
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import autodagger.AutoInjector
|
import autodagger.AutoInjector
|
||||||
import coil.load
|
import coil.load
|
||||||
import com.google.android.flexbox.FlexboxLayout
|
import com.google.android.flexbox.FlexboxLayout
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||||
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||||
import com.nextcloud.talk.chat.ChatActivity
|
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.chat.data.model.ChatMessage
|
||||||
import com.nextcloud.talk.data.network.NetworkMonitor
|
import com.nextcloud.talk.data.network.NetworkMonitor
|
||||||
|
import com.nextcloud.talk.data.user.model.User
|
||||||
import com.nextcloud.talk.databinding.ItemCustomOutcomingTextMessageBinding
|
import com.nextcloud.talk.databinding.ItemCustomOutcomingTextMessageBinding
|
||||||
import com.nextcloud.talk.models.json.chat.ReadStatus
|
import com.nextcloud.talk.models.json.chat.ReadStatus
|
||||||
|
import com.nextcloud.talk.models.json.conversations.ConversationEnums
|
||||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||||
import com.nextcloud.talk.utils.ApiUtils
|
import com.nextcloud.talk.utils.ApiUtils
|
||||||
|
import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability
|
||||||
import com.nextcloud.talk.utils.DateUtils
|
import com.nextcloud.talk.utils.DateUtils
|
||||||
|
import com.nextcloud.talk.utils.SpreedFeatures
|
||||||
import com.nextcloud.talk.utils.TextMatchers
|
import com.nextcloud.talk.utils.TextMatchers
|
||||||
|
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||||
import com.nextcloud.talk.utils.message.MessageUtils
|
import com.nextcloud.talk.utils.message.MessageUtils
|
||||||
import com.stfalcon.chatkit.messages.MessageHolders.OutcomingTextMessageViewHolder
|
import com.stfalcon.chatkit.messages.MessageHolders.OutcomingTextMessageViewHolder
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.util.Date
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AutoInjector(NextcloudTalkApplication::class)
|
@AutoInjector(NextcloudTalkApplication::class)
|
||||||
@ -65,16 +77,39 @@ class OutcomingTextMessageViewHolder(itemView: View) :
|
|||||||
|
|
||||||
lateinit var commonMessageInterface: CommonMessageInterface
|
lateinit var commonMessageInterface: CommonMessageInterface
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var chatRepository: ChatMessageRepository
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var currentUserProvider: CurrentUserProviderNew
|
||||||
|
|
||||||
|
private var job: Job? = null
|
||||||
|
|
||||||
@Suppress("Detekt.LongMethod")
|
@Suppress("Detekt.LongMethod")
|
||||||
override fun onBind(message: ChatMessage) {
|
override fun onBind(message: ChatMessage) {
|
||||||
super.onBind(message)
|
super.onBind(message)
|
||||||
sharedApplication!!.componentApplication.inject(this)
|
sharedApplication!!.componentApplication.inject(this)
|
||||||
realView.isSelected = false
|
val user = currentUserProvider.currentUser.blockingGet()
|
||||||
|
val hasCheckboxes = processCheckboxes(
|
||||||
|
message,
|
||||||
|
user
|
||||||
|
)
|
||||||
|
processMessage(message, hasCheckboxes)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("Detekt.LongMethod")
|
||||||
|
private fun processMessage(message: ChatMessage, hasCheckboxes: Boolean) {
|
||||||
|
var isBubbled = true
|
||||||
val layoutParams = binding.messageTime.layoutParams as FlexboxLayout.LayoutParams
|
val layoutParams = binding.messageTime.layoutParams as FlexboxLayout.LayoutParams
|
||||||
layoutParams.isWrapBefore = false
|
|
||||||
var textSize = context.resources.getDimension(R.dimen.chat_text_size)
|
var textSize = context.resources.getDimension(R.dimen.chat_text_size)
|
||||||
|
if (!hasCheckboxes) {
|
||||||
|
realView.isSelected = false
|
||||||
|
layoutParams.isWrapBefore = false
|
||||||
viewThemeUtils.platform.colorTextView(binding.messageTime, ColorRole.ON_SURFACE_VARIANT)
|
viewThemeUtils.platform.colorTextView(binding.messageTime, ColorRole.ON_SURFACE_VARIANT)
|
||||||
|
|
||||||
|
binding.messageText.visibility = View.VISIBLE
|
||||||
|
binding.checkboxContainer.visibility = View.GONE
|
||||||
|
|
||||||
var processedMessageText = messageUtils.enrichChatMessageText(
|
var processedMessageText = messageUtils.enrichChatMessageText(
|
||||||
binding.messageText.context,
|
binding.messageText.context,
|
||||||
message,
|
message,
|
||||||
@ -89,7 +124,6 @@ class OutcomingTextMessageViewHolder(itemView: View) :
|
|||||||
itemView
|
itemView
|
||||||
)
|
)
|
||||||
|
|
||||||
var isBubbled = true
|
|
||||||
if (
|
if (
|
||||||
(message.messageParameters == null || message.messageParameters!!.size <= 0) &&
|
(message.messageParameters == null || message.messageParameters!!.size <= 0) &&
|
||||||
TextMatchers.isMessageWithSingleEmoticonOnly(message.text)
|
TextMatchers.isMessageWithSingleEmoticonOnly(message.text)
|
||||||
@ -100,13 +134,14 @@ class OutcomingTextMessageViewHolder(itemView: View) :
|
|||||||
isBubbled = false
|
isBubbled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
setBubbleOnChatMessage(message)
|
|
||||||
|
|
||||||
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
|
||||||
binding.messageTime.layoutParams = layoutParams
|
binding.messageTime.layoutParams = layoutParams
|
||||||
viewThemeUtils.platform.colorTextView(binding.messageText, ColorRole.ON_SURFACE_VARIANT)
|
viewThemeUtils.platform.colorTextView(binding.messageText, ColorRole.ON_SURFACE_VARIANT)
|
||||||
binding.messageText.text = processedMessageText
|
binding.messageText.text = processedMessageText
|
||||||
|
} else {
|
||||||
|
binding.messageText.visibility = View.GONE
|
||||||
|
binding.checkboxContainer.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
||||||
if (message.lastEditTimestamp != 0L && !message.isDeleted) {
|
if (message.lastEditTimestamp != 0L && !message.isDeleted) {
|
||||||
binding.messageEditIndicator.visibility = View.VISIBLE
|
binding.messageEditIndicator.visibility = View.VISIBLE
|
||||||
binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.lastEditTimestamp!!)
|
binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.lastEditTimestamp!!)
|
||||||
@ -114,7 +149,8 @@ class OutcomingTextMessageViewHolder(itemView: View) :
|
|||||||
binding.messageEditIndicator.visibility = View.GONE
|
binding.messageEditIndicator.visibility = View.GONE
|
||||||
binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
|
binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
|
||||||
}
|
}
|
||||||
|
binding.messageTime.setTextColor(ContextCompat.getColor(context, R.color.no_emphasis_text))
|
||||||
|
setBubbleOnChatMessage(message)
|
||||||
// parent message handling
|
// parent message handling
|
||||||
if (!message.isDeleted && message.parentMessageId != null) {
|
if (!message.isDeleted && message.parentMessageId != null) {
|
||||||
processParentMessage(message)
|
processParentMessage(message)
|
||||||
@ -161,6 +197,106 @@ class OutcomingTextMessageViewHolder(itemView: View) :
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun processCheckboxes(chatMessage: ChatMessage, user: User): Boolean {
|
||||||
|
val chatActivity = commonMessageInterface as ChatActivity
|
||||||
|
val message = chatMessage.message!!.toSpanned()
|
||||||
|
val messageTextView = binding.messageText
|
||||||
|
val checkBoxContainer = binding.checkboxContainer
|
||||||
|
val isOlderThanTwentyFourHours = chatMessage
|
||||||
|
.createdAt
|
||||||
|
.before(Date(System.currentTimeMillis() - AGE_THRESHOLD_FOR_EDIT_MESSAGE))
|
||||||
|
val messageIsEditable = hasSpreedFeatureCapability(
|
||||||
|
user.capabilities?.spreedCapability!!,
|
||||||
|
SpreedFeatures.EDIT_MESSAGES
|
||||||
|
) && !isOlderThanTwentyFourHours
|
||||||
|
|
||||||
|
val isNoTimeLimitOnNoteToSelf = hasSpreedFeatureCapability(
|
||||||
|
user.capabilities?.spreedCapability!!,
|
||||||
|
SpreedFeatures
|
||||||
|
.EDIT_MESSAGES_NOTE_TO_SELF
|
||||||
|
) && chatActivity.currentConversation?.type == ConversationEnums.ConversationType.NOTE_TO_SELF
|
||||||
|
|
||||||
|
checkBoxContainer.removeAllViews()
|
||||||
|
val regex = """(- \[(X|x| )])\s*(.+)""".toRegex(RegexOption.MULTILINE)
|
||||||
|
val matches = regex.findAll(message)
|
||||||
|
|
||||||
|
if (matches.none()) return false
|
||||||
|
|
||||||
|
val firstPart = message.toString().substringBefore("\n- [")
|
||||||
|
messageTextView.text = messageUtils.enrichChatMessageText(
|
||||||
|
binding.messageText.context, firstPart, true, viewThemeUtils
|
||||||
|
)
|
||||||
|
|
||||||
|
val checkboxList = mutableListOf<CheckBox>()
|
||||||
|
|
||||||
|
matches.forEach { matchResult ->
|
||||||
|
val isChecked = matchResult.groupValues[CHECKED_GROUP_INDEX] == "X" ||
|
||||||
|
matchResult.groupValues[CHECKED_GROUP_INDEX] == "x"
|
||||||
|
val taskText = matchResult.groupValues[TASK_TEXT_GROUP_INDEX].trim()
|
||||||
|
|
||||||
|
val checkBox = CheckBox(checkBoxContainer.context).apply {
|
||||||
|
text = taskText
|
||||||
|
this.isChecked = isChecked
|
||||||
|
this.isEnabled = messageIsEditable || isNoTimeLimitOnNoteToSelf
|
||||||
|
|
||||||
|
setTextColor(ContextCompat.getColor(context, R.color.no_emphasis_text))
|
||||||
|
|
||||||
|
setOnCheckedChangeListener { _, _ ->
|
||||||
|
updateCheckboxStates(chatMessage, user, checkboxList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkBoxContainer.addView(checkBox)
|
||||||
|
checkboxList.add(checkBox)
|
||||||
|
viewThemeUtils.platform.themeCheckbox(checkBox)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateCheckboxStates(chatMessage: ChatMessage, user: User, checkboxes: List<CheckBox>) {
|
||||||
|
job = CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val apiVersion: Int = ApiUtils.getChatApiVersion(
|
||||||
|
user.capabilities?.spreedCapability!!,
|
||||||
|
intArrayOf(1)
|
||||||
|
)
|
||||||
|
val updatedMessage = updateMessageWithCheckboxStates(chatMessage.message!!, checkboxes)
|
||||||
|
chatRepository.editChatMessage(
|
||||||
|
user.getCredentials(),
|
||||||
|
ApiUtils.getUrlForChatMessage(apiVersion, user.baseUrl!!, chatMessage.token!!, chatMessage.id),
|
||||||
|
updatedMessage
|
||||||
|
).collect { result ->
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (result.isSuccess) {
|
||||||
|
val editedMessage = result.getOrNull()?.ocs?.data!!.parentMessage!!
|
||||||
|
Log.d(TAG, "EditedMessage: $editedMessage")
|
||||||
|
binding.messageEditIndicator.apply {
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
binding.messageTime.text =
|
||||||
|
dateUtils.getLocalTimeStringFromTimestamp(editedMessage.lastEditTimestamp!!)
|
||||||
|
} else {
|
||||||
|
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateMessageWithCheckboxStates(originalMessage: String, checkboxes: List<CheckBox>): String {
|
||||||
|
var updatedMessage = originalMessage
|
||||||
|
val regex = """(- \[(X|x| )])\s*(.+)""".toRegex(RegexOption.MULTILINE)
|
||||||
|
|
||||||
|
checkboxes.forEach { _ ->
|
||||||
|
updatedMessage = regex.replace(updatedMessage) { matchResult ->
|
||||||
|
val taskText = matchResult.groupValues[TASK_TEXT_GROUP_INDEX].trim()
|
||||||
|
val checkboxState = if (checkboxes.find { it.text == taskText }?.isChecked == true) "X" else " "
|
||||||
|
"- [$checkboxState] $taskText"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updatedMessage
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateStatus(readStatusDrawableInt: Int, description: String?) {
|
private fun updateStatus(readStatusDrawableInt: Int, description: String?) {
|
||||||
binding.sendingProgress.visibility = View.GONE
|
binding.sendingProgress.visibility = View.GONE
|
||||||
binding.checkMark.visibility = View.VISIBLE
|
binding.checkMark.visibility = View.VISIBLE
|
||||||
@ -245,8 +381,16 @@ class OutcomingTextMessageViewHolder(itemView: View) :
|
|||||||
this.commonMessageInterface = commonMessageInterface
|
this.commonMessageInterface = commonMessageInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun viewDetached() {
|
||||||
|
super.viewDetached()
|
||||||
|
job?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TEXT_SIZE_MULTIPLIER = 2.5
|
const val TEXT_SIZE_MULTIPLIER = 2.5
|
||||||
private val TAG = OutcomingTextMessageViewHolder::class.java.simpleName
|
private val TAG = OutcomingTextMessageViewHolder::class.java.simpleName
|
||||||
|
private const val CHECKED_GROUP_INDEX = 2
|
||||||
|
private const val TASK_TEXT_GROUP_INDEX = 3
|
||||||
|
private const val AGE_THRESHOLD_FOR_EDIT_MESSAGE: Long = 86400000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ class MessageUtils(val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enrichChatMessageText(
|
fun enrichChatMessageText(
|
||||||
context: Context,
|
context: Context,
|
||||||
message: String,
|
message: String,
|
||||||
incoming: Boolean,
|
incoming: Boolean,
|
||||||
|
@ -64,6 +64,17 @@
|
|||||||
app:layout_wrapBefore="true"
|
app:layout_wrapBefore="true"
|
||||||
tools:text="Talk to you later!" />
|
tools:text="Talk to you later!" />
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/checkboxContainer"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_below="@id/messageText"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:visibility="gone">
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@id/messageTime"
|
android:id="@id/messageTime"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -86,14 +97,12 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/messageText"
|
android:layout_below="@id/messageText"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:gravity="end"
|
|
||||||
android:alpha="0.6"
|
android:alpha="0.6"
|
||||||
android:textColor="@color/no_emphasis_text"
|
android:textColor="@color/no_emphasis_text"
|
||||||
android:textIsSelectable="false"
|
android:textIsSelectable="false"
|
||||||
app:layout_alignSelf="flex_end"
|
app:layout_alignSelf="flex_end"
|
||||||
android:text = "@string/hint_edited_message"
|
android:text = "@string/hint_edited_message"
|
||||||
android:textSize="12sp">
|
android:textSize="12sp">
|
||||||
|
|
||||||
</TextView>
|
</TextView>
|
||||||
|
|
||||||
<include
|
<include
|
||||||
|
@ -41,8 +41,21 @@
|
|||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textColorHighlight="@color/nc_grey"
|
android:textColorHighlight="@color/nc_grey"
|
||||||
android:textIsSelectable="false"
|
android:textIsSelectable="false"
|
||||||
|
app:layout_alignSelf="flex_start"
|
||||||
|
app:layout_flexGrow="1"
|
||||||
tools:text="Talk to you later!" />
|
tools:text="Talk to you later!" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/checkboxContainer"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:layout_below="@id/messageText"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@id/messageTime"
|
android:id="@id/messageTime"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -54,7 +67,6 @@
|
|||||||
android:textColor="@color/no_emphasis_text"
|
android:textColor="@color/no_emphasis_text"
|
||||||
android:textIsSelectable="false"
|
android:textIsSelectable="false"
|
||||||
app:layout_alignSelf="flex_end"
|
app:layout_alignSelf="flex_end"
|
||||||
android:layout_gravity="end"
|
|
||||||
app:layout_flexGrow="1"
|
app:layout_flexGrow="1"
|
||||||
app:layout_wrapBefore="false"
|
app:layout_wrapBefore="false"
|
||||||
tools:text="10:35" />
|
tools:text="10:35" />
|
||||||
@ -68,7 +80,6 @@
|
|||||||
android:alpha="0.6"
|
android:alpha="0.6"
|
||||||
android:textColor="@color/no_emphasis_text"
|
android:textColor="@color/no_emphasis_text"
|
||||||
android:textIsSelectable="false"
|
android:textIsSelectable="false"
|
||||||
android:gravity="end"
|
|
||||||
app:layout_alignSelf="flex_end"
|
app:layout_alignSelf="flex_end"
|
||||||
android:text = "@string/hint_edited_message"
|
android:text = "@string/hint_edited_message"
|
||||||
android:textSize="12sp">
|
android:textSize="12sp">
|
||||||
@ -83,7 +94,6 @@
|
|||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:contentDescription="@null"
|
android:contentDescription="@null"
|
||||||
app:layout_alignSelf="flex_end"
|
app:layout_alignSelf="flex_end"
|
||||||
android:gravity="end"
|
|
||||||
app:tint="@color/high_emphasis_text"
|
app:tint="@color/high_emphasis_text"
|
||||||
tools:src="@drawable/ic_check_all" />
|
tools:src="@drawable/ic_check_all" />
|
||||||
|
|
||||||
@ -94,7 +104,7 @@
|
|||||||
android:layout_below="@id/messageTime"
|
android:layout_below="@id/messageTime"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:contentDescription="@null"
|
android:contentDescription="@null"
|
||||||
app:layout_alignSelf="center"
|
app:layout_alignSelf="flex_end"
|
||||||
app:tint="@color/high_emphasis_text"
|
app:tint="@color/high_emphasis_text"
|
||||||
tools:src="@drawable/ic_warning_white"/>
|
tools:src="@drawable/ic_warning_white"/>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user