Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AND-320] Swipe to reply (XML artifact) #5626

Merged
merged 10 commits into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
### ⬆️ Improved

### ✅ Added
- Added Swipe To Reply feature to the MessageListView. [#5626](https://github.com/GetStream/stream-chat-android/pull/5626)

### ⚠️ Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package io.getstream.chat.android
import io.getstream.chat.android.models.Attachment
import io.getstream.chat.android.models.AttachmentType
import io.getstream.chat.android.models.Channel
import io.getstream.chat.android.models.ChannelCapabilities
import io.getstream.chat.android.models.ChannelConfig
import io.getstream.chat.android.models.ChannelInfo
import io.getstream.chat.android.models.ChannelMute
Expand Down Expand Up @@ -49,6 +50,7 @@ public fun positiveRandomInt(maxInt: Int = Int.MAX_VALUE - 1): Int =
public fun positiveRandomLong(maxLong: Long = Long.MAX_VALUE - 1): Long =
Random.nextLong(1, maxLong + 1)

public fun randomFloat(): Float = Random.nextFloat()
public fun randomInt(): Int = Random.nextInt()
public fun randomIntBetween(min: Int, max: Int): Int = Random.nextInt(min, max + 1)
public fun randomLong(): Long = Random.nextLong()
Expand Down Expand Up @@ -560,3 +562,48 @@ public fun randomReactionGroup(
firstReactionAt = firstReactionAt,
lastReactionAt = lastReactionAt,
)

public fun allChannelCapabilities(): Set<String> = setOf(
ChannelCapabilities.BAN_CHANNEL_MEMBERS,
ChannelCapabilities.CONNECT_EVENTS,
ChannelCapabilities.DELETE_ANY_MESSAGE,
ChannelCapabilities.DELETE_CHANNEL,
ChannelCapabilities.DELETE_OWN_MESSAGE,
ChannelCapabilities.FLAG_MESSAGE,
ChannelCapabilities.FREEZE_CHANNEL,
ChannelCapabilities.LEAVE_CHANNEL,
ChannelCapabilities.JOIN_CHANNEL,
ChannelCapabilities.MUTE_CHANNEL,
ChannelCapabilities.PIN_MESSAGE,
ChannelCapabilities.QUOTE_MESSAGE,
ChannelCapabilities.READ_EVENTS,
ChannelCapabilities.SEARCH_MESSAGES,
ChannelCapabilities.SEND_CUSTOM_EVENTS,
ChannelCapabilities.SEND_LINKS,
ChannelCapabilities.SEND_MESSAGE,
ChannelCapabilities.SEND_REACTION,
ChannelCapabilities.SEND_REPLY,
ChannelCapabilities.SET_CHANNEL_COOLDOWN,
ChannelCapabilities.UPDATE_ANY_MESSAGE,
ChannelCapabilities.UPDATE_CHANNEL,
ChannelCapabilities.UPDATE_CHANNEL_MEMBERS,
ChannelCapabilities.UPDATE_OWN_MESSAGE,
ChannelCapabilities.UPLOAD_FILE,
ChannelCapabilities.TYPING_EVENTS,
ChannelCapabilities.SLOW_MODE,
ChannelCapabilities.SKIP_SLOW_MODE,
ChannelCapabilities.JOIN_CALL,
ChannelCapabilities.CREATE_CALL,
ChannelCapabilities.CAST_POLL_VOTE,
ChannelCapabilities.SEND_POLL,
)

public fun randomChannelCapabilities(
exclude: Set<String> = emptySet(),
include: Set<String> = emptySet(),
): Set<String> =
allChannelCapabilities()
.minus(exclude)
.shuffled()
.let { it.take(positiveRandomInt(it.size)) }
.toSet() + include
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ public class MessageComposerController(
setMessageMode(MessageMode.MessageThread(messageAction.message))
}
is Reply -> {
messageActions.value = messageActions.value + messageAction
messageActions.value = (messageActions.value.filterNot { it is Reply } + messageAction).toSet()
}
is Edit -> {
setMessageInputInternal(messageAction.message.text, MessageInput.Source.Edit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2542,7 +2542,7 @@ public abstract interface class io/getstream/chat/android/ui/feature/messages/li

public final class io/getstream/chat/android/ui/feature/messages/list/MessageListViewStyle : io/getstream/chat/android/ui/helper/ViewStyle {
public static final field Companion Lio/getstream/chat/android/ui/feature/messages/list/MessageListViewStyle$Companion;
public fun <init> (Lio/getstream/chat/android/ui/feature/messages/list/ScrollButtonViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageListView$NewMessagesBehaviour;Lio/getstream/chat/android/ui/feature/messages/list/MessageListItemStyle;Lio/getstream/chat/android/ui/feature/messages/list/GiphyViewHolderStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageReplyStyle;Lio/getstream/chat/android/ui/feature/messages/list/UnreadLabelButtonStyle;ZIIZIZIIIZIIZIIZIZIIZZZZZZLio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;IILio/getstream/chat/android/ui/font/TextStyle;ILio/getstream/chat/android/ui/font/TextStyle;IIIIIIZIIIIIIIIIIIIZZ)V
public fun <init> (Lio/getstream/chat/android/ui/feature/messages/list/ScrollButtonViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageListView$NewMessagesBehaviour;Lio/getstream/chat/android/ui/feature/messages/list/MessageListItemStyle;Lio/getstream/chat/android/ui/feature/messages/list/GiphyViewHolderStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageReplyStyle;Lio/getstream/chat/android/ui/feature/messages/list/UnreadLabelButtonStyle;ZIIZIZIIIZIIZIIZIZIIZZZZZZLio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;IILio/getstream/chat/android/ui/font/TextStyle;ILio/getstream/chat/android/ui/font/TextStyle;IIIIIIZIIIIIIIIIIIIZZZI)V
public final fun component1 ()Lio/getstream/chat/android/ui/feature/messages/list/ScrollButtonViewStyle;
public final fun component10 ()I
public final fun component11 ()Z
Expand Down Expand Up @@ -2601,11 +2601,13 @@ public final class io/getstream/chat/android/ui/feature/messages/list/MessageLis
public final fun component6 ()Lio/getstream/chat/android/ui/feature/messages/list/MessageReplyStyle;
public final fun component60 ()Z
public final fun component61 ()Z
public final fun component62 ()Z
public final fun component63 ()I
public final fun component7 ()Lio/getstream/chat/android/ui/feature/messages/list/UnreadLabelButtonStyle;
public final fun component8 ()Z
public final fun component9 ()I
public final fun copy (Lio/getstream/chat/android/ui/feature/messages/list/ScrollButtonViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageListView$NewMessagesBehaviour;Lio/getstream/chat/android/ui/feature/messages/list/MessageListItemStyle;Lio/getstream/chat/android/ui/feature/messages/list/GiphyViewHolderStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageReplyStyle;Lio/getstream/chat/android/ui/feature/messages/list/UnreadLabelButtonStyle;ZIIZIZIIIZIIZIIZIZIIZZZZZZLio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;IILio/getstream/chat/android/ui/font/TextStyle;ILio/getstream/chat/android/ui/font/TextStyle;IIIIIIZIIIIIIIIIIIIZZ)Lio/getstream/chat/android/ui/feature/messages/list/MessageListViewStyle;
public static synthetic fun copy$default (Lio/getstream/chat/android/ui/feature/messages/list/MessageListViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/ScrollButtonViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageListView$NewMessagesBehaviour;Lio/getstream/chat/android/ui/feature/messages/list/MessageListItemStyle;Lio/getstream/chat/android/ui/feature/messages/list/GiphyViewHolderStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageReplyStyle;Lio/getstream/chat/android/ui/feature/messages/list/UnreadLabelButtonStyle;ZIIZIZIIIZIIZIIZIZIIZZZZZZLio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;IILio/getstream/chat/android/ui/font/TextStyle;ILio/getstream/chat/android/ui/font/TextStyle;IIIIIIZIIIIIIIIIIIIZZIILjava/lang/Object;)Lio/getstream/chat/android/ui/feature/messages/list/MessageListViewStyle;
public final fun copy (Lio/getstream/chat/android/ui/feature/messages/list/ScrollButtonViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageListView$NewMessagesBehaviour;Lio/getstream/chat/android/ui/feature/messages/list/MessageListItemStyle;Lio/getstream/chat/android/ui/feature/messages/list/GiphyViewHolderStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageReplyStyle;Lio/getstream/chat/android/ui/feature/messages/list/UnreadLabelButtonStyle;ZIIZIZIIIZIIZIIZIZIIZZZZZZLio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;IILio/getstream/chat/android/ui/font/TextStyle;ILio/getstream/chat/android/ui/font/TextStyle;IIIIIIZIIIIIIIIIIIIZZZI)Lio/getstream/chat/android/ui/feature/messages/list/MessageListViewStyle;
public static synthetic fun copy$default (Lio/getstream/chat/android/ui/feature/messages/list/MessageListViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/ScrollButtonViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageListView$NewMessagesBehaviour;Lio/getstream/chat/android/ui/feature/messages/list/MessageListItemStyle;Lio/getstream/chat/android/ui/feature/messages/list/GiphyViewHolderStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageViewStyle;Lio/getstream/chat/android/ui/feature/messages/list/MessageReplyStyle;Lio/getstream/chat/android/ui/feature/messages/list/UnreadLabelButtonStyle;ZIIZIZIIIZIIZIIZIZIIZZZZZZLio/getstream/chat/android/ui/font/TextStyle;Lio/getstream/chat/android/ui/font/TextStyle;IILio/getstream/chat/android/ui/font/TextStyle;ILio/getstream/chat/android/ui/font/TextStyle;IIIIIIZIIIIIIIIIIIIZZZIIILjava/lang/Object;)Lio/getstream/chat/android/ui/feature/messages/list/MessageListViewStyle;
public fun equals (Ljava/lang/Object;)Z
public final fun getAudioRecordPlayerViewStyle ()Lio/getstream/chat/android/ui/feature/messages/list/MessageViewStyle;
public final fun getBackgroundColor ()I
Expand Down Expand Up @@ -2659,6 +2661,8 @@ public final class io/getstream/chat/android/ui/feature/messages/list/MessageLis
public final fun getScrollButtonEndMargin ()I
public final fun getScrollButtonViewStyle ()Lio/getstream/chat/android/ui/feature/messages/list/ScrollButtonViewStyle;
public final fun getShowReactionsForUnsentMessages ()Z
public final fun getSwipeToReplyEnabled ()Z
public final fun getSwipeToReplyIcon ()I
public final fun getThreadMessagesStart ()I
public final fun getThreadReplyIcon ()I
public final fun getThreadsEnabled ()Z
Expand Down Expand Up @@ -2795,7 +2799,7 @@ public abstract class io/getstream/chat/android/ui/feature/messages/list/adapter
public fun <init> (Landroid/view/View;)V
public abstract fun bindData (Lio/getstream/chat/android/ui/feature/messages/list/adapter/MessageListItem;Lio/getstream/chat/android/ui/feature/messages/list/adapter/MessageListItemPayloadDiff;)V
protected final fun getContext ()Landroid/content/Context;
protected final fun getData ()Lio/getstream/chat/android/ui/feature/messages/list/adapter/MessageListItem;
public final fun getData ()Lio/getstream/chat/android/ui/feature/messages/list/adapter/MessageListItem;
public fun messageContainerView ()Landroid/view/View;
public fun onAttachedToWindow ()V
public fun onDetachedFromWindow ()V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public data class MessageListItemStyle(
internal val MESSAGE_STROKE_COLOR_MINE = R.color.stream_ui_literal_transparent
internal const val MESSAGE_STROKE_WIDTH_MINE: Float = 0f
internal val MESSAGE_STROKE_COLOR_THEIRS = R.color.stream_ui_grey_whisper
internal val MESSAGE_STROKE_WIDTH_THEIRS: Float = 1.dpToPxPrecise()
internal val MESSAGE_STROKE_WIDTH_THEIRS: Float by lazy { 1.dpToPxPrecise() }

private const val BASE_MESSAGE_MAX_WIDTH_FACTOR = 1
private const val DEFAULT_MESSAGE_MAX_WIDTH_FACTOR = 0.75f
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import android.widget.Toast
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ItemAnimator
Expand Down Expand Up @@ -108,6 +109,8 @@ import io.getstream.chat.android.ui.feature.messages.list.background.MessageBack
import io.getstream.chat.android.ui.feature.messages.list.background.MessageBackgroundFactoryImpl
import io.getstream.chat.android.ui.feature.messages.list.internal.HiddenMessageListItemPredicate
import io.getstream.chat.android.ui.feature.messages.list.internal.MessageListScrollHelper
import io.getstream.chat.android.ui.feature.messages.list.internal.SwipeReplyCallback
import io.getstream.chat.android.ui.feature.messages.list.internal.canReplyToMessage
import io.getstream.chat.android.ui.feature.messages.list.internal.poll.AllPollOptionsDialogFragment
import io.getstream.chat.android.ui.feature.messages.list.internal.poll.PollResultsDialogFragment
import io.getstream.chat.android.ui.feature.messages.list.options.message.MessageOptionItem
Expand Down Expand Up @@ -666,6 +669,7 @@ public class MessageListView : ConstraintLayout {

initRecyclerView()
initScrollHelper()
initSwipeToReply()
initLoadingView()
initEmptyStateView()
messageListViewStyle?.unreadLabelButtonStyle?.let { initUnreadLabelButton(it) }
Expand Down Expand Up @@ -727,6 +731,30 @@ public class MessageListView : ConstraintLayout {
}
}

private fun initSwipeToReply() {
messageListViewStyle?.swipeToReplyIcon
?.takeUnless { messageListViewStyle?.swipeToReplyEnabled == false }
?.let(context::getDrawable)
?.let { swipeToReplyIcon ->
SwipeReplyCallback(swipeToReplyIcon) { message ->
message
?.let { messageListViewStyle?.canReplyToMessage(it, ownCapabilities) }
?: false
}.let { swipeReplyCallback ->
ItemTouchHelper(swipeReplyCallback).let { itemTouchHelper ->
swipeReplyCallback.onReply = {
// We need to detach and attach the itemTouchHelper to the RecyclerView to make it work
// after the reply action is completed.
itemTouchHelper.attachToRecyclerView(null)
itemTouchHelper.attachToRecyclerView(binding.chatMessagesRV)
messageReplyHandler.onMessageReply(it.cid, it)
}
itemTouchHelper.attachToRecyclerView(binding.chatMessagesRV)
}
}
}
}

private fun configureAttributes(attributeSet: AttributeSet?) {
context.obtainStyledAttributes(
attributeSet,
Expand Down
Loading
Loading