Merge pull request #4945 from nextcloud/reaction_list

Reaction list
This commit is contained in:
Marcel Hibbe 2025-05-14 16:08:23 +00:00 committed by GitHub
commit af1219290d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 110 additions and 25 deletions

View File

@ -41,10 +41,13 @@ import com.nextcloud.talk.utils.ConversationUtils
import com.nextcloud.talk.utils.DateConstants import com.nextcloud.talk.utils.DateConstants
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.SpreedFeatures import com.nextcloud.talk.utils.SpreedFeatures
import com.vanniktech.emoji.Emoji
import com.vanniktech.emoji.EmojiPopup import com.vanniktech.emoji.EmojiPopup
import com.vanniktech.emoji.EmojiTextView import com.vanniktech.emoji.EmojiTextView
import com.vanniktech.emoji.installDisableKeyboardInput import com.vanniktech.emoji.installDisableKeyboardInput
import com.vanniktech.emoji.installForceSingleEmoji import com.vanniktech.emoji.installForceSingleEmoji
import com.vanniktech.emoji.recent.RecentEmojiManager
import com.vanniktech.emoji.search.SearchEmojiManager
import io.reactivex.Observer import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
@ -246,30 +249,35 @@ class MessageActionsDialog(
isPermitted(hasChatPermission) && isPermitted(hasChatPermission) &&
isReactableMessageType(message) isReactableMessageType(message)
) { ) {
checkAndSetEmojiSelfReaction(dialogMessageActionsBinding.emojiThumbsUp) val recentEmojiManager = RecentEmojiManager(context, MAX_RECENTS)
dialogMessageActionsBinding.emojiThumbsUp.setOnClickListener { val recentEmojis = recentEmojiManager.getRecentEmojis()
clickOnEmoji(message, dialogMessageActionsBinding.emojiThumbsUp.text.toString()) val searchEmojiManager = SearchEmojiManager()
val initialSearchKeywords = listOf(
"thumbsup",
"thumbsdown",
"heart",
"joy",
"confused",
"cry",
"pray",
"fire"
)
val initialEmojisFromSearch = mutableSetOf<Emoji>()
initialSearchKeywords.forEach { keyword ->
val searchResults = searchEmojiManager.search(keyword)
if (searchResults.isNotEmpty()) {
initialEmojisFromSearch.add(searchResults[ZERO_INDEX].component1())
recentEmojiManager.addEmoji(searchResults[ZERO_INDEX].component1())
} }
checkAndSetEmojiSelfReaction(dialogMessageActionsBinding.emojiThumbsDown) if (initialEmojisFromSearch.size >= MAX_RECENTS) {
dialogMessageActionsBinding.emojiThumbsDown.setOnClickListener { return@forEach
clickOnEmoji(message, dialogMessageActionsBinding.emojiThumbsDown.text.toString())
} }
checkAndSetEmojiSelfReaction(dialogMessageActionsBinding.emojiLaugh)
dialogMessageActionsBinding.emojiLaugh.setOnClickListener {
clickOnEmoji(message, dialogMessageActionsBinding.emojiLaugh.text.toString())
}
checkAndSetEmojiSelfReaction(dialogMessageActionsBinding.emojiHeart)
dialogMessageActionsBinding.emojiHeart.setOnClickListener {
clickOnEmoji(message, dialogMessageActionsBinding.emojiHeart.text.toString())
}
checkAndSetEmojiSelfReaction(dialogMessageActionsBinding.emojiConfused)
dialogMessageActionsBinding.emojiConfused.setOnClickListener {
clickOnEmoji(message, dialogMessageActionsBinding.emojiConfused.text.toString())
}
checkAndSetEmojiSelfReaction(dialogMessageActionsBinding.emojiSad)
dialogMessageActionsBinding.emojiSad.setOnClickListener {
clickOnEmoji(message, dialogMessageActionsBinding.emojiSad.text.toString())
} }
val combinedEmojis = (recentEmojis + initialEmojisFromSearch).toList().distinct().take(MAX_RECENTS)
setupEmojiView(combinedEmojis, recentEmojiManager)
dialogMessageActionsBinding.emojiMore.setOnClickListener { dialogMessageActionsBinding.emojiMore.setOnClickListener {
dismiss() dismiss()
@ -281,6 +289,50 @@ class MessageActionsDialog(
} }
} }
private fun setupEmojiView(combinedEmojis: List<Emoji>, recentEmojiManager: RecentEmojiManager) {
val emojiSearchKeywords = mapOf(
"👍" to "thumbsup",
"👎" to "thumbsdown",
"❤️" to "heart",
"😂" to "joy",
"😕" to "confused",
"😢" to "cry",
"🙏" to "pray",
"🔥" to "fire"
)
val emojiTextViews = listOf(
dialogMessageActionsBinding.emojiThumbsUp,
dialogMessageActionsBinding.emojiThumbsDown,
dialogMessageActionsBinding.emojiHeart,
dialogMessageActionsBinding.emojiLaugh,
dialogMessageActionsBinding.emojiConfused,
dialogMessageActionsBinding.emojiCry,
dialogMessageActionsBinding.emojiPray,
dialogMessageActionsBinding.emojiFire
)
emojiTextViews.forEachIndexed { index, textView ->
val emoji = combinedEmojis.getOrNull(index)?.unicode
if (emoji != null) {
textView.text = emoji
checkAndSetEmojiSelfReaction(textView)
textView.setOnClickListener {
clickOnEmoji(message, emoji)
val keyword = emojiSearchKeywords[emoji] ?: ""
val result = SearchEmojiManager().search(keyword)
if (result.isNotEmpty()) {
recentEmojiManager.addEmoji(result[ZERO_INDEX].component1())
recentEmojiManager.persist()
}
}
textView.visibility = View.VISIBLE
} else {
textView.visibility = View.GONE
}
}
}
private fun isPermitted(hasChatPermission: Boolean): Boolean { private fun isPermitted(hasChatPermission: Boolean): Boolean {
return hasChatPermission && ConversationEnums.ConversationReadOnlyState.CONVERSATION_READ_ONLY != return hasChatPermission && ConversationEnums.ConversationReadOnlyState.CONVERSATION_READ_ONLY !=
currentConversation?.conversationReadOnlyState currentConversation?.conversationReadOnlyState
@ -531,5 +583,7 @@ class MessageActionsDialog(
private const val DELAY: Long = 200 private const val DELAY: Long = 200
private const val AGE_THRESHOLD_FOR_EDIT_MESSAGE: Long = 86400000 private const val AGE_THRESHOLD_FOR_EDIT_MESSAGE: Long = 86400000
private const val ACTOR_BOTS = "bots" private const val ACTOR_BOTS = "bots"
private const val ZERO_INDEX = 0
private const val MAX_RECENTS = 8
} }
} }

View File

@ -17,9 +17,15 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
android:scrollbars="none">
<LinearLayout <LinearLayout
android:id="@+id/emojiBar" android:id="@+id/emojiBar"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/standard_quarter_margin" android:layout_marginStart="@dimen/standard_quarter_margin"
android:layout_marginTop="@dimen/standard_half_margin" android:layout_marginTop="@dimen/standard_half_margin"
@ -84,7 +90,7 @@
android:textSize="20sp" /> android:textSize="20sp" />
<com.vanniktech.emoji.EmojiTextView <com.vanniktech.emoji.EmojiTextView
android:id="@+id/emojiSad" android:id="@+id/emojiCry"
android:layout_width="@dimen/reaction_bottom_sheet_layout_size" android:layout_width="@dimen/reaction_bottom_sheet_layout_size"
android:layout_height="@dimen/reaction_bottom_sheet_layout_size" android:layout_height="@dimen/reaction_bottom_sheet_layout_size"
android:layout_marginLeft="@dimen/standard_quarter_margin" android:layout_marginLeft="@dimen/standard_quarter_margin"
@ -94,6 +100,28 @@
android:text="@string/emoji_sad" android:text="@string/emoji_sad"
android:textSize="20sp" /> android:textSize="20sp" />
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/emojiPray"
android:layout_width="@dimen/reaction_bottom_sheet_layout_size"
android:layout_height="@dimen/reaction_bottom_sheet_layout_size"
android:layout_marginLeft="@dimen/standard_quarter_margin"
android:layout_marginRight="@dimen/standard_quarter_margin"
android:cursorVisible="false"
android:gravity="center"
android:text="@string/emoji_pray"
android:textSize="20sp" />
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/emojiFire"
android:layout_width="@dimen/reaction_bottom_sheet_layout_size"
android:layout_height="@dimen/reaction_bottom_sheet_layout_size"
android:layout_marginLeft="@dimen/standard_quarter_margin"
android:layout_marginRight="@dimen/standard_half_margin"
android:cursorVisible="false"
android:gravity="center"
android:text="@string/emoji_fire"
android:textSize="20sp" />
<com.vanniktech.emoji.EmojiEditText <com.vanniktech.emoji.EmojiEditText
android:id="@+id/emojiMore" android:id="@+id/emojiMore"
android:layout_width="0dp" android:layout_width="0dp"
@ -106,6 +134,7 @@
android:paddingStart="@dimen/zero" android:paddingStart="@dimen/zero"
android:paddingEnd="@dimen/standard_padding" /> android:paddingEnd="@dimen/standard_padding" />
</LinearLayout> </LinearLayout>
</HorizontalScrollView>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -384,6 +384,8 @@ How to translate with transifex:
<string name="emoji_heart" translatable="false">❤️</string> <string name="emoji_heart" translatable="false">❤️</string>
<string name="emoji_confused" translatable="false">😯</string> <string name="emoji_confused" translatable="false">😯</string>
<string name="emoji_sad" translatable="false">😢</string> <string name="emoji_sad" translatable="false">😢</string>
<string name="emoji_pray" translatable="false">🙏</string>
<string name="emoji_fire" translatable="false">🔥</string>
<string name="emoji_more" translatable="false">More emojis</string> <string name="emoji_more" translatable="false">More emojis</string>
<string name="dontClear">Don\'t clear</string> <string name="dontClear">Don\'t clear</string>
<string name="today">Today</string> <string name="today">Today</string>