From a822fda6acea904e702cf8123b8b0f896b3cfae3 Mon Sep 17 00:00:00 2001 From: "Md. Touhidur Rahman" Date: Fri, 12 Sep 2025 00:51:09 +0600 Subject: [PATCH 1/9] flash chat button on new message --- .../unciv/logic/multiplayer/chat/ChatStore.kt | 45 +++++++++++++--- .../src/com/unciv/ui/images/IconTextButton.kt | 8 +-- .../ui/screens/worldscreen/chat/ChatButton.kt | 54 ++++++++++++++++++- .../ui/screens/worldscreen/chat/ChatPopup.kt | 6 ++- core/src/com/unciv/utils/StringExtensions.kt | 14 +++-- 5 files changed, 108 insertions(+), 19 deletions(-) diff --git a/core/src/com/unciv/logic/multiplayer/chat/ChatStore.kt b/core/src/com/unciv/logic/multiplayer/chat/ChatStore.kt index 910df82725..82dbbc2df4 100644 --- a/core/src/com/unciv/logic/multiplayer/chat/ChatStore.kt +++ b/core/src/com/unciv/logic/multiplayer/chat/ChatStore.kt @@ -1,7 +1,9 @@ package com.unciv.logic.multiplayer.chat import com.badlogic.gdx.Gdx +import com.unciv.UncivGame import com.unciv.ui.screens.worldscreen.chat.ChatPopup +import com.unciv.utils.toUUIDOrNull import java.util.Collections.synchronizedMap import java.util.LinkedList import java.util.Queue @@ -10,6 +12,8 @@ import java.util.UUID data class Chat( val gameId: UUID, ) { + var read = true + // pairs private val messages: MutableList> = mutableListOf(INITIAL_MESSAGE) @@ -52,6 +56,8 @@ object ChatStore { */ var chatPopup: ChatPopup? = null + var hasGlobalMessage = false + private var gameIdToChat: MutableMap = synchronizedMap(mutableMapOf()) /** When no [ChatPopup] is open to receive these oddities, we keep them here. @@ -72,21 +78,34 @@ object ChatStore { globalMessages = LinkedList() } - fun relayChatMessage(chat: Response.Chat) { + fun relayChatMessage(incomingChatMsg: Response.Chat) { Gdx.app.postRunnable { - if (chat.gameId == null || chat.gameId.isBlank()) { - relayGlobalMessage(chat.message, chat.civName) + if (incomingChatMsg.gameId == null || incomingChatMsg.gameId.isBlank()) { + relayGlobalMessage(incomingChatMsg.message, incomingChatMsg.civName) } else { val gameId = try { - UUID.fromString(chat.gameId) + UUID.fromString(incomingChatMsg.gameId) } catch (_: Throwable) { // Discard messages with invalid UUID return@postRunnable } - getChatByGameId(gameId).addMessage(chat.civName, chat.message) - if (chatPopup?.chat?.gameId == gameId) { - chatPopup?.addMessage(chat.civName, chat.message) + val chat = (chatPopup?.chat ?: getChatByGameId(gameId)) + chat.addMessage(incomingChatMsg.civName, incomingChatMsg.message) + if (gameId.equals(chatPopup?.chat?.gameId)) { + chatPopup?.addMessage(incomingChatMsg.civName, incomingChatMsg.message) + } + + if (chatPopup == null && incomingChatMsg.civName != "System") { + if (gameId.equals(UncivGame.Current.worldScreen?.gameInfo?.gameId?.toUUIDOrNull())) { + if (UncivGame.Current.worldScreen?.gameInfo?.currentPlayer != incomingChatMsg.civName) { + UncivGame.Current.worldScreen?.chatButton?.chat?.read = false + UncivGame.Current.worldScreen?.chatButton?.startFlashing() + } + } else { + // some other game not currently on screen has a message + chat.read = false + } } } } @@ -103,7 +122,17 @@ object ChatStore { fun relayGlobalMessage(message: String, civName: String = "System") { Gdx.app.postRunnable { - chatPopup?.addMessage(civName, message, suffix = "one time") ?: globalMessages.add(Pair(civName, message)) + if (civName != "System") { + hasGlobalMessage = chatPopup == null + if (hasGlobalMessage) UncivGame.Current.worldScreen?.chatButton?.startFlashing() + } + + chatPopup?.addMessage(civName, message, suffix = "one time") ?: globalMessages.add( + Pair( + civName, + message + ) + ) } } } diff --git a/core/src/com/unciv/ui/images/IconTextButton.kt b/core/src/com/unciv/ui/images/IconTextButton.kt index ba0f8e0ed6..5a7a8921a3 100644 --- a/core/src/com/unciv/ui/images/IconTextButton.kt +++ b/core/src/com/unciv/ui/images/IconTextButton.kt @@ -7,8 +7,8 @@ import com.badlogic.gdx.scenes.scene2d.ui.Cell import com.badlogic.gdx.scenes.scene2d.ui.Label import com.badlogic.gdx.utils.Align import com.unciv.Constants -import com.unciv.ui.components.fonts.Fonts import com.unciv.ui.components.extensions.toLabel +import com.unciv.ui.components.fonts.Fonts import com.unciv.ui.screens.basescreen.BaseScreen /** @@ -23,10 +23,11 @@ open class IconTextButton( text: String, val icon: Actor? = null, fontSize: Int = Constants.defaultFontSize, - fontColor: Color = Color.WHITE -): Button(BaseScreen.skin) { + val fontColor: Color = Color.WHITE +) : Button(BaseScreen.skin) { /** [Label] instance produced by, and with content and formatting as specified in [String.toLabel]. */ val label = text.toLabel(fontColor, fontSize, hideIcons = true) // Since by definition we already have an icon + /** Table cell containing the [icon] if any, or `null` (that is, when no [icon] was supplied, the Cell will exist but have no Actor). */ val iconCell: Cell = if (icon != null) { @@ -37,6 +38,7 @@ open class IconTextButton( } else { add().padRight(fontSize / 2f) } + /** Table cell instance containing the [label]. */ val labelCell: Cell