Skip to content

Commit

Permalink
7tv live emote updates, personal emotes, badges
Browse files Browse the repository at this point in the history
(cherry picked from commit fc3de81)
  • Loading branch information
crackededed committed Dec 21, 2024
1 parent f7a98ba commit 322388f
Show file tree
Hide file tree
Showing 35 changed files with 750 additions and 269 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 = 269
versionName = "2.40.2"
versionCode = 270
versionName = "2.40.3"
}

buildTypes {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.github.andreyasadchy.xtra.model.chat

class StvBadge(
val id: String,
val url1x: String?,
val url2x: String?,
val url3x: String?,
val url4x: String?,
val name: String?,
val format: String?)
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ import kotlinx.serialization.Serializable

@Serializable
class StvGlobalResponse(
val id: String? = null,
val emotes: List<StvResponse>
)
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,9 @@ class PlayerRepository @Inject constructor(
parseStvEmotes(misc.getGlobalStvEmotes().emotes)
}

suspend fun loadStvEmotes(channelId: String): List<Emote> = withContext(Dispatchers.IO) {
parseStvEmotes(misc.getStvEmotes(channelId).emoteSet.emotes)
suspend fun loadStvEmotes(channelId: String): Pair<String?, List<Emote>> = withContext(Dispatchers.IO) {
val set = misc.getStvEmotes(channelId).emoteSet
Pair(set.id, parseStvEmotes(set.emotes))
}

private fun parseStvEmotes(response: List<StvResponse>): List<Emote> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.github.andreyasadchy.xtra.model.chat.ChatMessage
import com.github.andreyasadchy.xtra.model.chat.CheerEmote
import com.github.andreyasadchy.xtra.model.chat.Emote
import com.github.andreyasadchy.xtra.model.chat.NamePaint
import com.github.andreyasadchy.xtra.model.chat.StvBadge
import com.github.andreyasadchy.xtra.model.chat.TwitchBadge
import com.github.andreyasadchy.xtra.model.chat.TwitchEmote
import com.github.andreyasadchy.xtra.ui.view.NamePaintImageSpan
Expand All @@ -42,6 +43,12 @@ class ChatAdapter(
private val showNamePaints: Boolean,
namePaintsList: List<NamePaint>?,
paintUsersMap: Map<String, String>?,
private val showStvBadges: Boolean,
stvBadgesList: List<StvBadge>?,
stvBadgeUsersMap: Map<String, String>?,
private val showPersonalEmotes: Boolean,
personalEmoteSetsMap: Map<String, List<Emote>>?,
personalEmoteSetUsersMap: Map<String, String>?,
private val showSystemMessageEmotes: Boolean,
private val chatUrl: String?,
private val getEmoteBytes: ((String, Pair<Long, Int>) -> ByteArray?)?,
Expand Down Expand Up @@ -80,6 +87,10 @@ class ChatAdapter(
var cheerEmotes: List<CheerEmote>? = null
val namePaints: MutableList<NamePaint>? = namePaintsList?.toMutableList()
val paintUsers: MutableMap<String, String>? = paintUsersMap?.toMutableMap()
val stvBadges: MutableList<StvBadge>? = stvBadgesList?.toMutableList()
val stvBadgeUsers: MutableMap<String, String>? = stvBadgeUsersMap?.toMutableMap()
val personalEmoteSets: MutableMap<String, List<Emote>>? = personalEmoteSetsMap?.toMutableMap()
val personalEmoteSetUsers: MutableMap<String, String>? = personalEmoteSetUsersMap?.toMutableMap()
private val savedLocalTwitchEmotes = mutableMapOf<String, ByteArray>()
private val savedLocalBadges = mutableMapOf<String, ByteArray>()
private val savedLocalCheerEmotes = mutableMapOf<String, ByteArray>()
Expand All @@ -99,9 +110,10 @@ class ChatAdapter(
val pair = ChatAdapterUtils.prepareChatMessage(
chatMessage, holder.textView, enableTimestamps, timestampFormat, firstMsgVisibility, firstChatMsg, redeemedChatMsg, redeemedNoMsg,
rewardChatMsg, true, replyMessage, null, null, useRandomColors, random, useReadableColors, isLightTheme, nameDisplay, useBoldNames,
showNamePaints, namePaints, paintUsers, showSystemMessageEmotes, loggedInUser, chatUrl, getEmoteBytes, userColors, savedColors,
localTwitchEmotes, globalStvEmotes, channelStvEmotes, globalBttvEmotes, channelBttvEmotes, globalFfzEmotes, channelFfzEmotes, globalBadges,
channelBadges, cheerEmotes, savedLocalTwitchEmotes, savedLocalBadges, savedLocalCheerEmotes, savedLocalEmotes
showNamePaints, namePaints, paintUsers, showStvBadges, stvBadges, stvBadgeUsers, showPersonalEmotes, personalEmoteSets, personalEmoteSetUsers,
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 @@ -115,9 +127,10 @@ class ChatAdapter(
enableTimestamps, timestampFormat, firstMsgVisibility, firstChatMsg, redeemedChatMsg, redeemedNoMsg, rewardChatMsg, replyMessage,
{ chatMessage -> selectedMessage = chatMessage; replyClickListener?.invoke() },
{ url, name, source, format, isAnimated, emoteId -> imageClickListener?.invoke(url, name, source, format, isAnimated, emoteId) },
useRandomColors, useReadableColors, isLightTheme, nameDisplay, useBoldNames, showNamePaints, namePaints, paintUsers, showSystemMessageEmotes,
chatUrl, getEmoteBytes, fragment, dialogBackgroundColor, imageLibrary, emoteSize, badgeSize, emoteQuality, animateGifs, enableZeroWidth, messages,
userColors, savedColors, loggedInUser, localTwitchEmotes, globalStvEmotes, channelStvEmotes, globalBttvEmotes, channelBttvEmotes, globalFfzEmotes,
useRandomColors, useReadableColors, isLightTheme, nameDisplay, useBoldNames, showNamePaints, namePaints, paintUsers, showStvBadges,
stvBadges, stvBadgeUsers, showPersonalEmotes, personalEmoteSets, personalEmoteSetUsers, showSystemMessageEmotes, chatUrl, getEmoteBytes,
fragment, dialogBackgroundColor, imageLibrary, emoteSize, badgeSize, emoteQuality, animateGifs, enableZeroWidth, messages, userColors,
savedColors, loggedInUser, localTwitchEmotes, globalStvEmotes, channelStvEmotes, globalBttvEmotes, channelBttvEmotes, globalFfzEmotes,
channelFfzEmotes, globalBadges, channelBadges, cheerEmotes, selectedMessage
)
}
Expand All @@ -126,9 +139,10 @@ class ChatAdapter(
return ReplyClickedChatAdapter(
enableTimestamps, timestampFormat, firstMsgVisibility, firstChatMsg, redeemedChatMsg, redeemedNoMsg, rewardChatMsg, replyMessage,
{ url, name, source, format, isAnimated, emoteId -> imageClickListener?.invoke(url, name, source, format, isAnimated, emoteId) },
useRandomColors, useReadableColors, isLightTheme, nameDisplay, useBoldNames, showNamePaints, namePaints, paintUsers, showSystemMessageEmotes,
chatUrl, getEmoteBytes, fragment, dialogBackgroundColor, imageLibrary, emoteSize, badgeSize, emoteQuality, animateGifs, enableZeroWidth, messages,
userColors, savedColors, loggedInUser, localTwitchEmotes, globalStvEmotes, channelStvEmotes, globalBttvEmotes, channelBttvEmotes, globalFfzEmotes,
useRandomColors, useReadableColors, isLightTheme, nameDisplay, useBoldNames, showNamePaints, namePaints, paintUsers, showStvBadges,
stvBadges, stvBadgeUsers, showPersonalEmotes, personalEmoteSets, personalEmoteSetUsers, showSystemMessageEmotes, chatUrl, getEmoteBytes,
fragment, dialogBackgroundColor, imageLibrary, emoteSize, badgeSize, emoteQuality, animateGifs, enableZeroWidth, messages, userColors,
savedColors, loggedInUser, localTwitchEmotes, globalStvEmotes, channelStvEmotes, globalBttvEmotes, channelBttvEmotes, globalFfzEmotes,
channelFfzEmotes, globalBadges, channelBadges, cheerEmotes, selectedMessage
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ class ChatFragment : BaseNetworkFragment(), LifecycleListener, MessageClickedDia
fragment = this@ChatFragment,
channelId = channelId,
namePaints = viewModel.namePaints,
paintUsers = viewModel.paintUsers
paintUsers = viewModel.paintUsers,
stvBadges = viewModel.stvBadges,
stvBadgeUsers = viewModel.stvBadgeUsers,
personalEmoteSets = viewModel.personalEmoteSets,
personalEmoteSetUsers = viewModel.personalEmoteSetUsers
)
val channelLogin = args.getString(KEY_CHANNEL_LOGIN)
val helixHeaders = TwitchApiHelper.getHelixHeaders(requireContext())
Expand Down Expand Up @@ -327,6 +331,46 @@ class ChatFragment : BaseNetworkFragment(), LifecycleListener, MessageClickedDia
}
}
}
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.newStvBadge.collectLatest {
if (it != null) {
chatView.addStvBadge(it)
viewModel.newStvBadge.value = null
}
}
}
}
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.newStvBadgeUser.collectLatest {
if (it != null) {
chatView.addStvBadgeUser(it)
viewModel.newStvBadgeUser.value = null
}
}
}
}
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.newPersonalEmoteSet.collectLatest {
if (it != null) {
chatView.addPersonalEmoteSet(it)
viewModel.newPersonalEmoteSet.value = null
}
}
}
}
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.newPersonalEmoteSetUser.collectLatest {
if (it != null) {
chatView.addPersonalEmoteSetUser(it)
viewModel.newPersonalEmoteSetUser.value = null
}
}
}
}
if (chatUrl != null) {
initialize()
}
Expand Down
103 changes: 88 additions & 15 deletions app/src/main/java/com/github/andreyasadchy/xtra/ui/chat/ChatView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.github.andreyasadchy.xtra.model.chat.Emote
import com.github.andreyasadchy.xtra.model.chat.NamePaint
import com.github.andreyasadchy.xtra.model.chat.Raid
import com.github.andreyasadchy.xtra.model.chat.RoomState
import com.github.andreyasadchy.xtra.model.chat.StvBadge
import com.github.andreyasadchy.xtra.model.chat.TwitchBadge
import com.github.andreyasadchy.xtra.model.chat.TwitchEmote
import com.github.andreyasadchy.xtra.ui.view.SlidingLayout
Expand Down Expand Up @@ -108,7 +109,7 @@ class ChatView : ConstraintLayout {
_binding = ViewChatBinding.inflate(LayoutInflater.from(context), this, true)
}

fun init(fragment: Fragment, channelId: String?, namePaints: List<NamePaint>? = null, paintUsers: Map<String, String>? = null, getEmoteBytes: ((String, Pair<Long, Int>) -> ByteArray?)? = null, chatUrl: String? = null) {
fun init(fragment: Fragment, channelId: String?, namePaints: List<NamePaint>? = null, paintUsers: Map<String, String>? = null, stvBadges: List<StvBadge>? = null, stvBadgeUsers: Map<String, String>? = null, personalEmoteSets: Map<String, List<Emote>>? = null, personalEmoteSetUsers: Map<String, String>? = null, getEmoteBytes: ((String, Pair<Long, Int>) -> ByteArray?)? = null, chatUrl: String? = null) {
this.fragment = fragment
with(binding) {
adapter = ChatAdapter(
Expand All @@ -128,6 +129,12 @@ class ChatView : ConstraintLayout {
showNamePaints = context.prefs().getBoolean(C.CHAT_SHOW_PAINTS, true),
namePaintsList = namePaints,
paintUsersMap = paintUsers,
showStvBadges = context.prefs().getBoolean(C.CHAT_SHOW_STV_BADGES, true),
stvBadgesList = stvBadges,
stvBadgeUsersMap = stvBadgeUsers,
showPersonalEmotes = context.prefs().getBoolean(C.CHAT_SHOW_PERSONAL_EMOTES, true),
personalEmoteSetsMap = personalEmoteSets,
personalEmoteSetUsersMap = personalEmoteSetUsers,
showSystemMessageEmotes = context.prefs().getBoolean(C.CHAT_SYSTEM_MESSAGE_EMOTES, true),
chatUrl = chatUrl,
getEmoteBytes = getEmoteBytes,
Expand Down Expand Up @@ -433,27 +440,93 @@ class ChatView : ConstraintLayout {
}

fun addPaintUser(pair: Pair<String, String>) {
adapter.paintUsers?.let { paintUsers ->
paintUsers.entries.find { it.key == pair.first }?.let { paintUsers.remove(it.key) }
paintUsers.put(pair.first, pair.second)
adapter.paintUsers?.let {
it.remove(pair.first)
it.put(pair.first, pair.second)
}
messageDialog?.adapter?.paintUsers?.let {
it.remove(pair.first)
it.put(pair.first, pair.second)
}
replyDialog?.adapter?.paintUsers?.let {
it.remove(pair.first)
it.put(pair.first, pair.second)
}
updateUserMessages(pair.first)
}

fun addStvBadge(badge: StvBadge) {
adapter.stvBadges?.let { stvBadges ->
stvBadges.find { it.id == badge.id }?.let { stvBadges.remove(it) }
stvBadges.add(badge)
}
messageDialog?.adapter?.stvBadges?.let { stvBadges ->
stvBadges.find { it.id == badge.id }?.let { stvBadges.remove(it) }
stvBadges.add(badge)
}
replyDialog?.adapter?.stvBadges?.let { stvBadges ->
stvBadges.find { it.id == badge.id }?.let { stvBadges.remove(it) }
stvBadges.add(badge)
}
}

fun addStvBadgeUser(pair: Pair<String, String>) {
adapter.stvBadgeUsers?.let {
it.remove(pair.first)
it.put(pair.first, pair.second)
}
messageDialog?.adapter?.stvBadgeUsers?.let {
it.remove(pair.first)
it.put(pair.first, pair.second)
}
replyDialog?.adapter?.stvBadgeUsers?.let {
it.remove(pair.first)
it.put(pair.first, pair.second)
}
updateUserMessages(pair.first)
}

fun addPersonalEmoteSet(pair: Pair<String, List<Emote>>) {
adapter.personalEmoteSets?.let {
it.remove(pair.first)
it.put(pair.first, pair.second)
}
messageDialog?.adapter?.personalEmoteSets?.let {
it.remove(pair.first)
it.put(pair.first, pair.second)
}
replyDialog?.adapter?.personalEmoteSets?.let {
it.remove(pair.first)
it.put(pair.first, pair.second)
}
}

fun addPersonalEmoteSetUser(pair: Pair<String, String>) {
adapter.personalEmoteSetUsers?.let {
it.remove(pair.first)
it.put(pair.first, pair.second)
}
messageDialog?.adapter?.personalEmoteSetUsers?.let {
it.remove(pair.first)
it.put(pair.first, pair.second)
}
replyDialog?.adapter?.personalEmoteSetUsers?.let {
it.remove(pair.first)
it.put(pair.first, pair.second)
}
updateUserMessages(pair.first)
}

private fun updateUserMessages(userId: String) {
adapter.messages?.toList()?.let { messages ->
messages.filter { it.userId == pair.first }.forEach { message ->
messages.filter { it.userId == userId }.forEach { message ->
messages.indexOf(message).takeIf { it != -1 }?.let {
adapter.notifyItemChanged(it)
}
}
}
messageDialog?.adapter?.paintUsers?.let { paintUsers ->
paintUsers.entries.find { it.key == pair.first }?.let { paintUsers.remove(it.key) }
paintUsers.put(pair.first, pair.second)
}
messageDialog?.updatePaint(pair.first)
replyDialog?.adapter?.paintUsers?.let { paintUsers ->
paintUsers.entries.find { it.key == pair.first }?.let { paintUsers.remove(it.key) }
paintUsers.put(pair.first, pair.second)
}
replyDialog?.updatePaint(pair.first)
messageDialog?.updateUserMessages(userId)
replyDialog?.updateUserMessages(userId)
}

fun setUsername(username: String?) {
Expand Down
Loading

0 comments on commit 322388f

Please sign in to comment.