Skip to content

Commit

Permalink
reply threads
Browse files Browse the repository at this point in the history
(cherry picked from commit 4f7c112)
  • Loading branch information
crackededed committed Nov 27, 2024
1 parent 9062bc5 commit 7d19db0
Show file tree
Hide file tree
Showing 32 changed files with 694 additions and 121 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ android {
applicationId = "com.github.andreyasadchy.xtra"
minSdk = 21
targetSdk = 35
versionCode = 263
versionName = "2.39.0"
versionCode = 264
versionName = "2.39.1"
}

buildTypes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ class ChatMessage(
val systemMsg: String? = null,
val msgId: String? = null,
val reward: ChannelPointReward? = null,
val reply: Reply? = null,
val timestamp: Long? = null,
val fullMsg: String? = null)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.github.andreyasadchy.xtra.model.chat

class Reply(
val threadParentId: String? = null,
val userLogin: String? = null,
val userName: String? = null,
val message: String? = null)
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.github.andreyasadchy.xtra.model.chat.Emote
import com.github.andreyasadchy.xtra.model.chat.TwitchBadge
import com.github.andreyasadchy.xtra.model.chat.TwitchEmote
import com.github.andreyasadchy.xtra.ui.view.chat.MessageClickedChatAdapter
import com.github.andreyasadchy.xtra.ui.view.chat.ReplyClickedChatAdapter
import com.github.andreyasadchy.xtra.util.chat.ChatAdapterUtils
import java.util.Random

Expand All @@ -31,6 +32,7 @@ class ChatAdapter(
private val redeemedChatMsg: String,
private val redeemedNoMsg: String,
private val rewardChatMsg: String,
private val replyMessage: String,
private val useRandomColors: Boolean,
private val useReadableColors: Boolean,
private val isLightTheme: Boolean,
Expand Down Expand Up @@ -76,6 +78,7 @@ class ChatAdapter(
private val savedLocalEmotes = mutableMapOf<String, ByteArray>()

var messageClickListener: ((String?) -> Unit)? = null
var replyClickListener: (() -> Unit)? = null
private var selectedMessage: ChatMessage? = null

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
Expand All @@ -86,10 +89,10 @@ class ChatAdapter(
val chatMessage = messages?.get(position) ?: return
val pair = ChatAdapterUtils.prepareChatMessage(
chatMessage, holder.textView, enableTimestamps, timestampFormat, firstMsgVisibility, firstChatMsg, redeemedChatMsg, redeemedNoMsg,
rewardChatMsg, useRandomColors, random, useReadableColors, isLightTheme, nameDisplay, useBoldNames, showSystemMessageEmotes, loggedInUser,
chatUrl, getEmoteBytes, userColors, savedColors, localTwitchEmotes, globalStvEmotes, channelStvEmotes, globalBttvEmotes, channelBttvEmotes,
globalFfzEmotes, channelFfzEmotes, globalBadges, channelBadges, cheerEmotes, savedLocalTwitchEmotes, savedLocalBadges, savedLocalCheerEmotes,
savedLocalEmotes
rewardChatMsg, true, replyMessage, null, useRandomColors, random, useReadableColors, isLightTheme, nameDisplay, useBoldNames,
showSystemMessageEmotes, loggedInUser, chatUrl, getEmoteBytes, userColors, savedColors, localTwitchEmotes, globalStvEmotes, channelStvEmotes,
globalBttvEmotes, channelBttvEmotes, globalFfzEmotes, channelFfzEmotes, globalBadges, channelBadges, cheerEmotes, savedLocalTwitchEmotes,
savedLocalBadges, savedLocalCheerEmotes, savedLocalEmotes
)
holder.bind(chatMessage, pair.first)
ChatAdapterUtils.loadImages(
Expand All @@ -100,11 +103,21 @@ class ChatAdapter(

fun createMessageClickedChatAdapter(): MessageClickedChatAdapter {
return MessageClickedChatAdapter(
enableTimestamps, timestampFormat, firstMsgVisibility, firstChatMsg, redeemedChatMsg, redeemedNoMsg, rewardChatMsg, useRandomColors,
useReadableColors, isLightTheme, nameDisplay, useBoldNames, showSystemMessageEmotes, chatUrl, getEmoteBytes, fragment, imageLibrary,
emoteSize, badgeSize, emoteQuality, animateGifs, enableZeroWidth, messages, userColors, savedColors, loggedInUser, localTwitchEmotes,
globalStvEmotes, channelStvEmotes, globalBttvEmotes, channelBttvEmotes, globalFfzEmotes, channelFfzEmotes, globalBadges, channelBadges,
cheerEmotes, selectedMessage
enableTimestamps, timestampFormat, firstMsgVisibility, firstChatMsg, redeemedChatMsg, redeemedNoMsg, rewardChatMsg, replyMessage,
{ chatMessage -> selectedMessage = chatMessage; replyClickListener?.invoke() }, useRandomColors, useReadableColors, isLightTheme, nameDisplay,
useBoldNames, showSystemMessageEmotes, chatUrl, getEmoteBytes, fragment, imageLibrary, emoteSize, badgeSize, emoteQuality, animateGifs,
enableZeroWidth, messages, userColors, savedColors, loggedInUser, localTwitchEmotes, globalStvEmotes, channelStvEmotes, globalBttvEmotes,
channelBttvEmotes, globalFfzEmotes, channelFfzEmotes, globalBadges, channelBadges, cheerEmotes, selectedMessage
)
}

fun createReplyClickedChatAdapter(): ReplyClickedChatAdapter {
return ReplyClickedChatAdapter(
enableTimestamps, timestampFormat, firstMsgVisibility, firstChatMsg, redeemedChatMsg, redeemedNoMsg, rewardChatMsg, replyMessage,
useRandomColors, useReadableColors, isLightTheme, nameDisplay, useBoldNames, showSystemMessageEmotes, chatUrl, getEmoteBytes, fragment,
imageLibrary, emoteSize, badgeSize, emoteQuality, animateGifs, enableZeroWidth, messages, userColors, savedColors, loggedInUser,
localTwitchEmotes, globalStvEmotes, channelStvEmotes, globalBttvEmotes, channelBttvEmotes, globalFfzEmotes, channelFfzEmotes, globalBadges,
channelBadges, cheerEmotes, selectedMessage
)
}

Expand Down Expand Up @@ -150,8 +163,10 @@ class ChatAdapter(
movementMethod = LinkMovementMethod.getInstance()
TooltipCompat.setTooltipText(this, chatMessage.message ?: chatMessage.systemMsg)
setOnClickListener {
selectedMessage = chatMessage
messageClickListener?.invoke(channelId)
if (selectionStart == -1 && selectionEnd == -1) {
selectedMessage = chatMessage
messageClickListener?.invoke(channelId)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import com.github.andreyasadchy.xtra.ui.player.BasePlayerFragment
import com.github.andreyasadchy.xtra.ui.player.stream.StreamPlayerFragment
import com.github.andreyasadchy.xtra.ui.view.chat.MessageClickedChatAdapter
import com.github.andreyasadchy.xtra.ui.view.chat.MessageClickedDialog
import com.github.andreyasadchy.xtra.ui.view.chat.ReplyClickedChatAdapter
import com.github.andreyasadchy.xtra.ui.view.chat.ReplyClickedDialog
import com.github.andreyasadchy.xtra.util.C
import com.github.andreyasadchy.xtra.util.LifecycleListener
import com.github.andreyasadchy.xtra.util.TwitchApiHelper
Expand All @@ -34,7 +36,7 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

@AndroidEntryPoint
class ChatFragment : BaseNetworkFragment(), LifecycleListener, MessageClickedDialog.OnButtonClickListener {
class ChatFragment : BaseNetworkFragment(), LifecycleListener, MessageClickedDialog.OnButtonClickListener, ReplyClickedDialog.OnButtonClickListener {

private var _binding: FragmentChatBinding? = null
private val binding get() = _binding!!
Expand Down Expand Up @@ -412,6 +414,10 @@ class ChatFragment : BaseNetworkFragment(), LifecycleListener, MessageClickedDia
return binding.chatView.createMessageClickedChatAdapter()
}

override fun onCreateReplyClickedChatAdapter(): ReplyClickedChatAdapter {
return binding.chatView.createReplyClickedChatAdapter()
}

fun emoteMenuIsVisible() = binding.chatView.emoteMenuIsVisible()

fun toggleEmoteMenu(enable: Boolean) = binding.chatView.toggleEmoteMenu(enable)
Expand All @@ -422,8 +428,20 @@ class ChatFragment : BaseNetworkFragment(), LifecycleListener, MessageClickedDia
binding.chatView.appendEmote(emote)
}

override fun onReplyClicked(userName: String) {
binding.chatView.reply(userName)
override fun onReplyClicked(replyId: String?, userLogin: String?, userName: String?, message: String?) {
val replyMessage = message?.let {
val name = if (userName != null && userLogin != null && !userLogin.equals(userName, true)) {
when (requireContext().prefs().getString(C.UI_NAME_DISPLAY, "0")) {
"0" -> "${userName}(${userLogin})"
"1" -> userName
else -> userLogin
}
} else {
userName ?: userLogin
}
requireContext().getString(R.string.replying_to_message, name, message)
}
binding.chatView.reply(replyId, replyMessage)
}

override fun onCopyMessageClicked(message: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ class ChatViewModel @Inject constructor(
chat?.pause()
}

override fun send(message: CharSequence) {
chat?.send(message)
override fun send(message: CharSequence, replyId: String?) {
chat?.send(message, replyId)
}

override fun onRaidClicked(raid: Raid) {
Expand Down Expand Up @@ -527,26 +527,30 @@ class ChatViewModel @Inject constructor(
}
}

override fun send(message: CharSequence) {
if (useApiCommands) {
if (message.toString().startsWith("/")) {
try {
sendCommand(message)
} catch (e: Exception) {
if (e.message == "failed integrity check" && integrity.value == null) {
integrity.value = "refresh"
override fun send(message: CharSequence, replyId: String?) {
if (replyId != null) {
sendMessage(message, replyId)
} else {
if (useApiCommands) {
if (message.toString().startsWith("/")) {
try {
sendCommand(message)
} catch (e: Exception) {
if (e.message == "failed integrity check" && integrity.value == null) {
integrity.value = "refresh"
}
}
} else {
sendMessage(message)
}
} else {
sendMessage(message)
}
} else {
if (message.toString() == "/dc" || message.toString() == "/disconnect") {
if ((chatReadIRC?.isActive ?: chatReadWebSocket?.isActive ?: eventSub?.isActive) == true) {
disconnect()
if (message.toString() == "/dc" || message.toString() == "/disconnect") {
if ((chatReadIRC?.isActive ?: chatReadWebSocket?.isActive ?: eventSub?.isActive) == true) {
disconnect()
}
} else {
sendMessage(message)
}
} else {
sendMessage(message)
}
}
}
Expand Down Expand Up @@ -935,15 +939,15 @@ class ChatViewModel @Inject constructor(
}
}

private fun sendMessage(message: CharSequence) {
private fun sendMessage(message: CharSequence, replyId: String? = null) {
if (useApiChatMessages && !helixHeaders[C.HEADER_TOKEN].isNullOrBlank()) {
viewModelScope.launch {
repository.sendMessage(helixHeaders, account.id, channelId, message.toString())?.let {
onMessage(ChatMessage(systemMsg = it))
}
}
} else {
chatWriteIRC?.send(message) ?: chatWriteWebSocket?.send(message)
chatWriteIRC?.send(message, replyId) ?: chatWriteWebSocket?.send(message, replyId)
}
val usedEmotes = hashSetOf<RecentEmote>()
val currentTime = System.currentTimeMillis()
Expand Down Expand Up @@ -1359,7 +1363,7 @@ class ChatViewModel @Inject constructor(
private var chatReplayManager: ChatReplayManager? = null
private var chatReplayManagerLocal: ChatReplayManagerLocal? = null

override fun send(message: CharSequence) {}
override fun send(message: CharSequence, replyId: String?) {}

override fun start() {
pause()
Expand Down Expand Up @@ -1822,7 +1826,7 @@ class ChatViewModel @Inject constructor(
}

abstract inner class ChatController : OnChatMessageReceivedListener {
abstract fun send(message: CharSequence)
abstract fun send(message: CharSequence, replyId: String?)
abstract fun start()
abstract fun pause()
abstract fun stop()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,8 @@ abstract class BasePlayerFragment : BaseNetworkFragment(), LifecycleListener, Sl
// player dialog
(childFragmentManager.findFragmentByTag("closeOnPip") as? BottomSheetDialogFragment)?.dismiss()
// player chat message dialog
(chatFragment?.childFragmentManager?.findFragmentByTag("closeOnPip") as? BottomSheetDialogFragment)?.dismiss()
(chatFragment?.childFragmentManager?.findFragmentByTag("messageDialog") as? BottomSheetDialogFragment)?.dismiss()
(chatFragment?.childFragmentManager?.findFragmentByTag("replyDialog") as? BottomSheetDialogFragment)?.dismiss()
} else {
playerView.useController = true
}
Expand Down
Loading

0 comments on commit 7d19db0

Please sign in to comment.