CFV2 Add reply to message support.

This commit is contained in:
Alex Hart
2023-05-31 14:38:17 -03:00
committed by Cody Henthorne
parent 2bef5653b4
commit 5d546f46e4
5 changed files with 110 additions and 12 deletions

View File

@@ -29,8 +29,8 @@ public class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallbac
private final ConversationItemTouchListener itemTouchListener;
private final OnSwipeListener onSwipeListener;
ConversationItemSwipeCallback(@NonNull SwipeAvailabilityProvider swipeAvailabilityProvider,
@NonNull OnSwipeListener onSwipeListener)
public ConversationItemSwipeCallback(@NonNull SwipeAvailabilityProvider swipeAvailabilityProvider,
@NonNull OnSwipeListener onSwipeListener)
{
super(0, ItemTouchHelper.END);
this.itemTouchListener = new ConversationItemTouchListener(this::updateLatestDownCoordinate);
@@ -40,7 +40,7 @@ public class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallbac
this.canTriggerSwipe = true;
}
void attachToRecyclerView(@NonNull RecyclerView recyclerView) {
public void attachToRecyclerView(@NonNull RecyclerView recyclerView) {
recyclerView.addOnItemTouchListener(itemTouchListener);
new ItemTouchHelper(this).attachToRecyclerView(recyclerView);
}
@@ -193,11 +193,11 @@ public class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallbac
if (vibrator != null) vibrator.vibrate(SWIPE_SUCCESS_VIBE_TIME_MS);
}
interface SwipeAvailabilityProvider {
public interface SwipeAvailabilityProvider {
boolean isSwipeAvailable(ConversationMessage conversationMessage);
}
interface OnSwipeListener {
public interface OnSwipeListener {
void onSwipe(ConversationMessage conversationMessage);
}
}

View File

@@ -197,11 +197,11 @@ public final class MenuState {
.allMatch(collection -> multiselectParts.containsAll(collection.toSet()));
}
static boolean canReplyToMessage(@NonNull Recipient conversationRecipient,
boolean actionMessage,
@NonNull MessageRecord messageRecord,
boolean isDisplayingMessageRequest,
boolean isNonAdminInAnnouncementGroup)
public static boolean canReplyToMessage(@NonNull Recipient conversationRecipient,
boolean actionMessage,
@NonNull MessageRecord messageRecord,
boolean isDisplayingMessageRequest,
boolean isNonAdminInAnnouncementGroup)
{
return !actionMessage &&
!isNonAdminInAnnouncementGroup &&
@@ -215,7 +215,7 @@ public final class MenuState {
!conversationRecipient.isReleaseNotes();
}
static boolean isActionMessage(@NonNull MessageRecord messageRecord) {
public static boolean isActionMessage(@NonNull MessageRecord messageRecord) {
return messageRecord.isInMemoryMessageRecord() || messageRecord.isUpdate();
}

View File

@@ -116,6 +116,7 @@ import org.thoughtcrime.securesms.conversation.ConversationIntents
import org.thoughtcrime.securesms.conversation.ConversationIntents.ConversationScreenType
import org.thoughtcrime.securesms.conversation.ConversationItem
import org.thoughtcrime.securesms.conversation.ConversationItemSelection
import org.thoughtcrime.securesms.conversation.ConversationItemSwipeCallback
import org.thoughtcrime.securesms.conversation.ConversationMessage
import org.thoughtcrime.securesms.conversation.ConversationOptionsMenu
import org.thoughtcrime.securesms.conversation.ConversationReactionDelegate
@@ -221,6 +222,7 @@ import org.thoughtcrime.securesms.util.WindowUtil
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture
import org.thoughtcrime.securesms.util.doAfterNextLayout
import org.thoughtcrime.securesms.util.fragments.requireListener
import org.thoughtcrime.securesms.util.getRecordQuoteType
import org.thoughtcrime.securesms.util.hasAudio
import org.thoughtcrime.securesms.util.hasGiftBadge
import org.thoughtcrime.securesms.util.viewModel
@@ -546,6 +548,11 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
.getRequestReviewState()
.subscribeBy { presentRequestReviewState(it) }
.addTo(disposables)
ConversationItemSwipeCallback(
SwipeAvailabilityProvider(),
this::handleReplyToMessage
).attachToRecyclerView(binding.conversationItemRecycler)
}
private fun presentInputReadyState(inputReadyState: InputReadyState) {
@@ -893,6 +900,7 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
disposables += send
.doOnSubscribe {
composeText.setText("")
inputPanel.clearQuote()
}
.subscribeBy(
onError = { t ->
@@ -1139,7 +1147,30 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
//region Message action handling
private fun handleReplyToMessage(conversationMessage: ConversationMessage) {
// TODO [cfv2] -- Not implemented yet.
/*
TODO [cfv2]
if (isSearchRequested) {
searchViewItem.collapseActionView();
}
*/
if (inputPanel.inEditMessageMode()) {
inputPanel.exitEditMessageMode()
}
val (slideDeck, body) = viewModel.getSlideDeckAndBodyForReply(requireContext(), conversationMessage)
val author = conversationMessage.messageRecord.fromRecipient
inputPanel.setQuote(
GlideApp.with(this),
conversationMessage.messageRecord.dateSent,
author,
body,
slideDeck,
conversationMessage.messageRecord.getRecordQuoteType()
)
inputPanel.clickOnComposeInput()
}
private fun handleEditMessage(conversationMessage: ConversationMessage) {
@@ -1207,6 +1238,20 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
).subscribe()
}
private inner class SwipeAvailabilityProvider : ConversationItemSwipeCallback.SwipeAvailabilityProvider {
override fun isSwipeAvailable(conversationMessage: ConversationMessage): Boolean {
val recipient = viewModel.recipientSnapshot ?: return false
return actionMode == null && MenuState.canReplyToMessage(
recipient,
MenuState.isActionMessage(conversationMessage.messageRecord),
conversationMessage.messageRecord,
viewModel.hasMessageRequestState,
conversationGroupViewModel.isNonAdminInAnnouncementGroup()
)
}
}
//endregion
//region Scroll Handling

View File

@@ -32,6 +32,8 @@ import org.signal.paging.PagedData
import org.signal.paging.PagingConfig
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.ShortcutLauncherActivity
import org.thoughtcrime.securesms.attachments.TombstoneAttachment
import org.thoughtcrime.securesms.components.emoji.EmojiStrings
import org.thoughtcrime.securesms.components.reminder.BubbleOptOutReminder
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder
import org.thoughtcrime.securesms.components.reminder.GroupsV1MigrationSuggestionsReminder
@@ -39,6 +41,8 @@ import org.thoughtcrime.securesms.components.reminder.PendingGroupJoinRequestsRe
import org.thoughtcrime.securesms.components.reminder.Reminder
import org.thoughtcrime.securesms.components.reminder.ServiceOutageReminder
import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder
import org.thoughtcrime.securesms.contactshare.Contact
import org.thoughtcrime.securesms.contactshare.ContactUtil
import org.thoughtcrime.securesms.conversation.ConversationMessage
import org.thoughtcrime.securesms.conversation.colors.GroupAuthorNameColorHelper
import org.thoughtcrime.securesms.conversation.colors.NameColor
@@ -80,9 +84,13 @@ import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.BitmapUtil
import org.thoughtcrime.securesms.util.DrawableUtil
import org.thoughtcrime.securesms.util.MediaUtil
import org.thoughtcrime.securesms.util.SignalLocalMetrics
import org.thoughtcrime.securesms.util.Util
import org.thoughtcrime.securesms.util.hasLinkPreview
import org.thoughtcrime.securesms.util.hasSharedContact
import org.thoughtcrime.securesms.util.hasTextSlide
import org.thoughtcrime.securesms.util.isViewOnceMessage
import org.thoughtcrime.securesms.util.requireTextSlide
import java.io.IOException
import java.util.Optional
@@ -432,6 +440,46 @@ class ConversationRepository(
.subscribeOn(Schedulers.computation())
}
fun getSlideDeckAndBodyForReply(context: Context, conversationMessage: ConversationMessage): Pair<SlideDeck, CharSequence> {
val messageRecord = conversationMessage.messageRecord
return if (messageRecord.isMms && messageRecord.hasSharedContact()) {
val contact: Contact = (messageRecord as MmsMessageRecord).sharedContacts.first()
val displayName: String = ContactUtil.getDisplayName(contact)
val body: String = context.getString(R.string.ConversationActivity_quoted_contact_message, EmojiStrings.BUST_IN_SILHOUETTE, displayName)
val slideDeck = SlideDeck()
if (contact.avatarAttachment != null) {
slideDeck.addSlide(MediaUtil.getSlideForAttachment(context, contact.avatarAttachment))
}
slideDeck to body
} else if (messageRecord.isMms && messageRecord.hasLinkPreview()) {
val linkPreview = (messageRecord as MmsMessageRecord).linkPreviews.first()
val slideDeck = SlideDeck()
linkPreview.thumbnail.ifPresent {
slideDeck.addSlide(MediaUtil.getSlideForAttachment(context, it))
}
slideDeck to conversationMessage.getDisplayBody(context)
} else {
var slideDeck = if (messageRecord.isMms) {
(messageRecord as MmsMessageRecord).slideDeck
} else {
SlideDeck()
}
if (messageRecord.isViewOnceMessage()) {
val attachment = TombstoneAttachment(MediaUtil.VIEW_ONCE, true)
slideDeck = SlideDeck()
slideDeck.addSlide(MediaUtil.getSlideForAttachment(context, attachment))
}
slideDeck to conversationMessage.getDisplayBody(context)
}
}
/**
* Glide target for a contact photo which expects an error drawable, and publishes
* the result to the given emitter.

View File

@@ -28,6 +28,7 @@ import org.signal.core.util.concurrent.subscribeWithSubject
import org.signal.core.util.orNull
import org.signal.paging.ProxyPagingController
import org.thoughtcrime.securesms.components.reminder.Reminder
import org.thoughtcrime.securesms.conversation.ConversationMessage
import org.thoughtcrime.securesms.conversation.colors.GroupAuthorNameColorHelper
import org.thoughtcrime.securesms.conversation.colors.NameColor
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart
@@ -290,4 +291,8 @@ class ConversationViewModel(
.distinctUntilChanged()
.observeOn(AndroidSchedulers.mainThread())
}
fun getSlideDeckAndBodyForReply(context: Context, conversationMessage: ConversationMessage): Pair<SlideDeck, CharSequence> {
return repository.getSlideDeckAndBodyForReply(context, conversationMessage)
}
}