mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-24 03:53:12 -04:00
flash chat button on new message
This commit is contained in:
parent
530873c6f5
commit
a822fda6ac
@ -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
|
||||
|
||||
// <civName, message> pairs
|
||||
private val messages: MutableList<Pair<String, String>> = mutableListOf(INITIAL_MESSAGE)
|
||||
|
||||
@ -52,6 +56,8 @@ object ChatStore {
|
||||
*/
|
||||
var chatPopup: ChatPopup? = null
|
||||
|
||||
var hasGlobalMessage = false
|
||||
|
||||
private var gameIdToChat: MutableMap<UUID, Chat> = 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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Actor> =
|
||||
if (icon != null) {
|
||||
@ -37,6 +38,7 @@ open class IconTextButton(
|
||||
} else {
|
||||
add().padRight(fontSize / 2f)
|
||||
}
|
||||
|
||||
/** Table cell instance containing the [label]. */
|
||||
val labelCell: Cell<Label> = add(label)
|
||||
|
||||
|
@ -1,22 +1,38 @@
|
||||
package com.unciv.ui.screens.worldscreen.chat
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.utils.Disposable
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.multiplayer.chat.ChatStore
|
||||
import com.unciv.logic.multiplayer.chat.ChatWebSocket
|
||||
import com.unciv.logic.multiplayer.chat.Message
|
||||
import com.unciv.ui.components.input.onClick
|
||||
import com.unciv.ui.images.IconTextButton
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||
import com.unciv.utils.Concurrency
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
class ChatButton(val worldScreen: WorldScreen) : IconTextButton(
|
||||
"Chat", ImageGetter.getImage("OtherIcons/Chat"), 23
|
||||
) {
|
||||
), Disposable {
|
||||
val chat = ChatStore.getChatByGameId(worldScreen.gameInfo.gameId)
|
||||
|
||||
init {
|
||||
width = 95f
|
||||
iconCell.pad(3f).center()
|
||||
|
||||
if (!chat.read || ChatStore.hasGlobalMessage) {
|
||||
startFlashing()
|
||||
}
|
||||
|
||||
onClick {
|
||||
ChatPopup(worldScreen).open()
|
||||
stopFlashing()
|
||||
ChatPopup(chat, worldScreen).open()
|
||||
}
|
||||
|
||||
refreshVisibility()
|
||||
@ -45,4 +61,38 @@ class ChatButton(val worldScreen: WorldScreen) : IconTextButton(
|
||||
worldScreen.techPolicyAndDiplomacy.x.coerceAtLeast(1f),
|
||||
worldScreen.techPolicyAndDiplomacy.y - height - 1f
|
||||
)
|
||||
|
||||
private var flashJob: Job? = null
|
||||
|
||||
fun startFlashing(targetColor: Color = Color.ORANGE, interval: Duration = 500.milliseconds) {
|
||||
flashJob?.cancel()
|
||||
flashJob = Concurrency.run("ChatButton color flash") {
|
||||
var isAlternatingColor = true
|
||||
while (true) {
|
||||
Gdx.app.postRunnable {
|
||||
if (isAlternatingColor) {
|
||||
icon?.color = targetColor
|
||||
label.color = targetColor
|
||||
} else {
|
||||
icon?.color = fontColor
|
||||
label.color = fontColor
|
||||
}
|
||||
|
||||
isAlternatingColor = !isAlternatingColor
|
||||
}
|
||||
|
||||
delay(interval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopFlashing() {
|
||||
flashJob?.cancel()
|
||||
icon?.color = fontColor
|
||||
label.color = fontColor
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
flashJob?.cancel()
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.multiplayer.chat.Chat
|
||||
import com.unciv.logic.multiplayer.chat.ChatStore
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.components.extensions.coerceLightnessAtLeast
|
||||
@ -28,6 +29,7 @@ private val civChatColorsMap = mapOf<String, Color>(
|
||||
)
|
||||
|
||||
class ChatPopup(
|
||||
val chat: Chat,
|
||||
private val worldScreen: WorldScreen,
|
||||
) : Popup(screen = worldScreen, scrollable = Scrollability.None) {
|
||||
companion object {
|
||||
@ -35,8 +37,6 @@ class ChatPopup(
|
||||
const val CIVNAME_COLOR_MIN_LIGHTNESS = 0.60f
|
||||
}
|
||||
|
||||
val chat = ChatStore.getChatByGameId(worldScreen.gameInfo.gameId)
|
||||
|
||||
private val chatTable = Table(skin)
|
||||
private val scrollPane = ScrollPane(chatTable, skin)
|
||||
private val messageField = UncivTextField(
|
||||
@ -44,7 +44,9 @@ class ChatPopup(
|
||||
)
|
||||
|
||||
init {
|
||||
chat.read = true
|
||||
ChatStore.chatPopup = this
|
||||
ChatStore.hasGlobalMessage = false
|
||||
chatTable.defaults().growX().pad(5f).center()
|
||||
|
||||
/**
|
||||
|
@ -4,12 +4,18 @@ import yairm210.purity.annotations.Pure
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Checks if a [String] is a valid UUID
|
||||
* Tries to convert a [String] to a valid [UUID]
|
||||
* and returns `null` upon failure
|
||||
*/
|
||||
@Pure
|
||||
fun String.isUUID(): Boolean = try {
|
||||
fun String.toUUIDOrNull(): UUID? = try {
|
||||
UUID.fromString(this)
|
||||
true
|
||||
} catch (_: Throwable) {
|
||||
false
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a [String] is a valid [UUID]
|
||||
*/
|
||||
@Pure
|
||||
fun String.isUUID(): Boolean = toUUIDOrNull() != null
|
||||
|
Loading…
x
Reference in New Issue
Block a user