Disable "Edited" click listener for outgoing messages.

This commit is contained in:
Clark
2024-05-29 16:53:28 -04:00
committed by Cody Henthorne
parent 8bb3d71472
commit b41bf66133
11 changed files with 71 additions and 19 deletions

View File

@@ -313,7 +313,7 @@ class V2ConversationItemShapeTest {
override fun goToMediaPreview(parent: ConversationItem?, sharedElement: View?, args: MediaIntentFactory.MediaPreviewArgs?) = Unit
override fun onEditedIndicatorClicked(messageRecord: MessageRecord) = Unit
override fun onEditedIndicatorClicked(conversationMessage: ConversationMessage) = Unit
override fun onShowGroupDescriptionClicked(groupName: String, description: String, shouldLinkifyWebLinks: Boolean) = Unit

View File

@@ -280,7 +280,7 @@ class InternalConversationTestFragment : Fragment(R.layout.conversation_test_fra
Toast.makeText(requireContext(), "Can't touch this.", Toast.LENGTH_SHORT).show()
}
override fun onEditedIndicatorClicked(messageRecord: MessageRecord) {
override fun onEditedIndicatorClicked(conversationMessage: ConversationMessage) {
Toast.makeText(requireContext(), "Can't touch this.", Toast.LENGTH_SHORT).show()
}

View File

@@ -125,7 +125,7 @@ public interface BindableConversationItem extends Unbindable, GiphyMp4Playable,
void onViewGiftBadgeClicked(@NonNull MessageRecord messageRecord);
void onGiftBadgeRevealed(@NonNull MessageRecord messageRecord);
void goToMediaPreview(ConversationItem parent, View sharedElement, MediaIntentFactory.MediaPreviewArgs args);
void onEditedIndicatorClicked(@NonNull MessageRecord messageRecord);
void onEditedIndicatorClicked(@NonNull ConversationMessage conversationMessage);
void onShowGroupDescriptionClicked(@NonNull String groupName, @NonNull String description, boolean shouldLinkifyWebLinks);
void onJoinCallLink(@NonNull CallLinkRootKey callLinkRootKey);
void onShowSafetyTips(boolean forGroup);

View File

@@ -1730,7 +1730,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
if (MessageRecordUtil.isEditMessage(current)) {
activeFooter.getDateView().setOnClickListener(v -> {
if (eventListener != null) {
eventListener.onEditedIndicatorClicked(current);
eventListener.onEditedIndicatorClicked(conversationMessage);
}
});
} else {

View File

@@ -273,7 +273,7 @@ class ScheduledMessagesBottomSheet : FixedRoundedCornerBottomSheetDialogFragment
override fun onViewGiftBadgeClicked(messageRecord: MessageRecord) = Unit
override fun onActivatePaymentsClicked() = Unit
override fun onSendPaymentClicked(recipientId: RecipientId) = Unit
override fun onEditedIndicatorClicked(messageRecord: MessageRecord) = Unit
override fun onEditedIndicatorClicked(conversationMessage: ConversationMessage) = Unit
override fun onShowSafetyTips(forGroup: Boolean) = Unit
override fun onReportSpamLearnMoreClicked() = Unit
override fun onMessageRequestAcceptOptionsClicked() = Unit

View File

@@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.conversation.ConversationAdapter
import org.thoughtcrime.securesms.conversation.ConversationAdapterBridge
import org.thoughtcrime.securesms.conversation.ConversationBottomSheetCallback
import org.thoughtcrime.securesms.conversation.ConversationItemDisplayMode
import org.thoughtcrime.securesms.conversation.ConversationMessage
import org.thoughtcrime.securesms.conversation.colors.Colorizer
import org.thoughtcrime.securesms.conversation.colors.RecyclerViewColorizer
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart
@@ -253,9 +254,9 @@ class MessageQuotesBottomSheet : FixedRoundedCornerBottomSheetDialogFragment() {
getAdapterListener().onSendPaymentClicked(recipientId)
}
override fun onEditedIndicatorClicked(messageRecord: MessageRecord) {
override fun onEditedIndicatorClicked(conversationMessage: ConversationMessage) {
dismiss()
getAdapterListener().onEditedIndicatorClicked(messageRecord)
getAdapterListener().onEditedIndicatorClicked(conversationMessage)
}
override fun onShowSafetyTips(forGroup: Boolean) = Unit

View File

@@ -164,7 +164,7 @@ class EditMessageHistoryDialog : FixedRoundedCornerBottomSheetDialogFragment() {
override fun onViewGiftBadgeClicked(messageRecord: MessageRecord) = Unit
override fun onActivatePaymentsClicked() = Unit
override fun onSendPaymentClicked(recipientId: RecipientId) = Unit
override fun onEditedIndicatorClicked(messageRecord: MessageRecord) = Unit
override fun onEditedIndicatorClicked(conversationMessage: ConversationMessage) = Unit
override fun onShowSafetyTips(forGroup: Boolean) = Unit
override fun onReportSpamLearnMoreClicked() = Unit
override fun onMessageRequestAcceptOptionsClicked() = Unit

View File

@@ -294,6 +294,7 @@ import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.Debouncer
import org.thoughtcrime.securesms.util.DeleteDialog
import org.thoughtcrime.securesms.util.Dialogs
import org.thoughtcrime.securesms.util.DoubleClickDebouncer
import org.thoughtcrime.securesms.util.DrawableUtil
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.FullscreenHelper
@@ -469,6 +470,7 @@ class ConversationFragment :
private val conversationTooltips = ConversationTooltips(this)
private val colorizer = Colorizer()
private val textDraftSaveDebouncer = Debouncer(500)
private val doubleTapToEditDebouncer = DoubleClickDebouncer(200)
private val recentEmojis: RecentEmojiPageModel by lazy { RecentEmojiPageModel(AppDependencies.application, TextSecurePreferences.RECENT_STORAGE_KEY) }
private lateinit var layoutManager: ConversationLayoutManager
@@ -2783,16 +2785,20 @@ class ConversationFragment :
override fun onItemDoubleClick(item: MultiselectPart) {
Log.d(TAG, "onItemDoubleClick")
if (!isValidEditMessageSend(item.getMessageRecord(), System.currentTimeMillis())) {
onDoubleTapToEdit(item.conversationMessage)
}
private fun onDoubleTapToEdit(conversationMessage: ConversationMessage) {
if (!isValidEditMessageSend(conversationMessage.getMessageRecord(), System.currentTimeMillis())) {
return
}
if (SignalStore.uiHints().hasSeenDoubleTapEditEducationSheet) {
onDoubleTapEditEducationSheetNext(item.conversationMessage)
onDoubleTapEditEducationSheetNext(conversationMessage)
return
}
DoubleTapEditEducationSheet(item).show(childFragmentManager, DoubleTapEditEducationSheet.KEY)
DoubleTapEditEducationSheet(conversationMessage).show(childFragmentManager, DoubleTapEditEducationSheet.KEY)
}
override fun onMessageWithErrorClicked(messageRecord: MessageRecord) {
@@ -3003,9 +3009,12 @@ class ConversationFragment :
requireActivity().startActivity(MediaIntentFactory.create(requireActivity(), args), options.toBundle())
}
override fun onEditedIndicatorClicked(messageRecord: MessageRecord) {
if (messageRecord.isOutgoing) {
EditMessageHistoryDialog.show(childFragmentManager, messageRecord.toRecipient.id, messageRecord)
override fun onEditedIndicatorClicked(conversationMessage: ConversationMessage) {
val messageRecord = conversationMessage.messageRecord
if (conversationMessage.messageRecord.isOutgoing) {
if (!doubleTapToEditDebouncer.onClick { EditMessageHistoryDialog.show(childFragmentManager, messageRecord.toRecipient.id, messageRecord) }) {
onDoubleTapToEdit(conversationMessage)
}
} else {
EditMessageHistoryDialog.show(childFragmentManager, messageRecord.fromRecipient.id, messageRecord)
}

View File

@@ -9,14 +9,13 @@ import com.google.android.material.button.MaterialButton
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialogFragment
import org.thoughtcrime.securesms.conversation.ConversationMessage
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.fragments.requireListener
/**
* Shows an education sheet to users explaining how double tapping a sent message within 24hrs will allow them to edit it
*/
class DoubleTapEditEducationSheet(private val item: MultiselectPart) : FixedRoundedCornerBottomSheetDialogFragment() {
class DoubleTapEditEducationSheet(private val conversationMessage: ConversationMessage) : FixedRoundedCornerBottomSheetDialogFragment() {
companion object {
const val KEY = "DOUBLE_TAP_EDIT_EDU"
@@ -32,14 +31,14 @@ class DoubleTapEditEducationSheet(private val item: MultiselectPart) : FixedRoun
SignalStore.uiHints().hasSeenDoubleTapEditEducationSheet = true
view.findViewById<MaterialButton>(R.id.got_it).setOnClickListener {
requireListener<Callback>().onDoubleTapEditEducationSheetNext(item.conversationMessage)
requireListener<Callback>().onDoubleTapEditEducationSheetNext(conversationMessage)
dismissAllowingStateLoss()
}
}
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
requireListener<Callback>().onDoubleTapEditEducationSheetNext(item.conversationMessage)
requireListener<Callback>().onDoubleTapEditEducationSheetNext(conversationMessage)
}
interface Callback {

View File

@@ -677,7 +677,7 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
}
binding.footerDate.setOnClickListener {
conversationContext.clickListener.onEditedIndicatorClicked(record)
conversationContext.clickListener.onEditedIndicatorClicked(conversationMessage)
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.util
import android.os.Handler
import android.os.Looper
import java.util.concurrent.TimeUnit
/**
* A class to throttle on click events. If multiple clicks happen in succession (within the threshold)
* We ignore both and let the caller know it was a double click.
*/
class DoubleClickDebouncer(private val threshold: Long) {
private val handler = Handler(Looper.getMainLooper())
constructor(threshold: Long, timeUnit: TimeUnit) : this(timeUnit.toMillis(threshold))
private var clickEnqueued = false
/**
* Returns true if the click is enqueued, otherwise its a double click
*/
fun onClick(runnable: Runnable?): Boolean {
handler.removeCallbacksAndMessages(null)
if (!clickEnqueued) {
handler.postDelayed({
runnable!!.run()
clickEnqueued = false
}, threshold)
clickEnqueued = true
} else {
clickEnqueued = false
}
return clickEnqueued
}
fun clear() {
handler.removeCallbacksAndMessages(null)
}
}