summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoffrey-bion <joffrey.bion@gmail.com>2020-12-10 23:06:00 +0100
committerjoffrey-bion <joffrey.bion@gmail.com>2020-12-11 02:16:49 +0100
commiteef32bd9307a3a9f1ee3b532db2fb1f7cf37927a (patch)
treef139487db3866a687e699a01f3ed4baa5e44faf2
parentDecouple some sagas from routes (diff)
downloadseven-wonders-eef32bd9307a3a9f1ee3b532db2fb1f7cf37927a.tar.gz
seven-wonders-eef32bd9307a3a9f1ee3b532db2fb1f7cf37927a.tar.bz2
seven-wonders-eef32bd9307a3a9f1ee3b532db2fb1f7cf37927a.zip
Allow owner to leave/disband the game
Resolves: https://github.com/joffrey-bion/seven-wonders/issues/51
-rw-r--r--sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt18
-rw-r--r--sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Lobby.kt26
-rw-r--r--sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserController.kt11
-rw-r--r--sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/LobbyController.kt41
-rw-r--r--sw-server/src/main/kotlin/org/luxons/sevenwonders/server/lobby/Lobby.kt4
-rw-r--r--sw-server/src/test/kotlin/org/luxons/sevenwonders/server/SevenWondersTest.kt19
-rw-r--r--sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserControllerTest.kt11
-rw-r--r--sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/LobbyControllerTest.kt40
-rw-r--r--sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/Lobby.kt46
-rw-r--r--sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt5
-rw-r--r--sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/ApiActions.kt2
-rw-r--r--sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt9
-rw-r--r--sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt12
13 files changed, 166 insertions, 78 deletions
diff --git a/sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt b/sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt
index 5dda0292..6cc50f44 100644
--- a/sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt
+++ b/sw-client/src/commonMain/kotlin/org/luxons/sevenwonders/client/SevenWondersClient.kt
@@ -3,10 +3,10 @@ package org.luxons.sevenwonders.client
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationStrategy
-import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer
import org.hildan.krossbow.stomp.StompClient
import org.hildan.krossbow.stomp.config.HeartBeat
@@ -19,9 +19,7 @@ import org.hildan.krossbow.stomp.sendEmptyMsg
import org.luxons.sevenwonders.model.PlayerMove
import org.luxons.sevenwonders.model.PlayerTurnInfo
import org.luxons.sevenwonders.model.Settings
-import org.luxons.sevenwonders.model.api.ConnectedPlayer
-import org.luxons.sevenwonders.model.api.LobbyDTO
-import org.luxons.sevenwonders.model.api.SEVEN_WONDERS_WS_ENDPOINT
+import org.luxons.sevenwonders.model.api.*
import org.luxons.sevenwonders.model.api.actions.*
import org.luxons.sevenwonders.model.api.errors.ErrorDTO
import org.luxons.sevenwonders.model.cards.PreparedCard
@@ -69,8 +67,8 @@ class SevenWondersSession(private val stompSession: StompSessionWithKxSerializat
deserializer = ConnectedPlayer.serializer(),
)
- suspend fun watchGames(): Flow<List<LobbyDTO>> =
- stompSession.subscribe("/topic/games", ListSerializer(LobbyDTO.serializer()))
+ suspend fun watchGames(): Flow<GameListEvent> =
+ stompSession.subscribe("/topic/games", GameListEventWrapper.serializer()).map { it.event }
suspend fun createGame(gameName: String) {
stompSession.convertAndSend("/app/lobby/create", CreateGameAction(gameName), CreateGameAction.serializer())
@@ -87,6 +85,12 @@ class SevenWondersSession(private val stompSession: StompSessionWithKxSerializat
stompSession.sendEmptyMsg("/app/lobby/leave")
}
+ suspend fun disbandLobby() {
+ stompSession.sendEmptyMsg("/app/lobby/disband")
+ }
+
+ suspend fun watchLobbyLeft(): Flow<Long> = stompSession.subscribe("/user/queue/lobby/left", Long.serializer())
+
suspend fun addBot(displayName: String) {
stompSession.convertAndSend("/app/lobby/addBot", AddBotAction(displayName), AddBotAction.serializer())
}
@@ -164,4 +168,4 @@ suspend fun SevenWondersSession.joinGameAndWaitLobby(gameId: Long): LobbyDTO {
val joinedLobbies = watchLobbyJoined()
joinGame(gameId)
return joinedLobbies.first()
-} \ No newline at end of file
+}
diff --git a/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Lobby.kt b/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Lobby.kt
index e1e978f7..62bac3b1 100644
--- a/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Lobby.kt
+++ b/sw-common-model/src/commonMain/kotlin/org/luxons/sevenwonders/model/api/Lobby.kt
@@ -1,10 +1,36 @@
package org.luxons.sevenwonders.model.api
+import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.luxons.sevenwonders.model.wonders.PreGameWonder
const val SEVEN_WONDERS_WS_ENDPOINT = "/seven-wonders-websocket"
+// workaround for https://github.com/Kotlin/kotlinx.serialization/issues/1194
+@Serializable
+data class GameListEventWrapper(
+ val event: GameListEvent
+)
+
+fun GameListEvent.wrap(): GameListEventWrapper = GameListEventWrapper(this)
+
+@Serializable
+sealed class GameListEvent {
+
+ @SerialName("ReplaceList")
+ @Serializable
+ data class ReplaceList(val lobbies: List<LobbyDTO>) : GameListEvent()
+
+ @SerialName("CreateOrUpdate")
+ @Serializable
+ data class CreateOrUpdate(val lobby: LobbyDTO) : GameListEvent()
+
+ @SerialName("Delete")
+ @Serializable
+ data class Delete(val lobbyId: Long) : GameListEvent()
+}
+
+@Serializable
enum class State {
LOBBY,
PLAYING,
diff --git a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserController.kt b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserController.kt
index 587afc10..d6573f8f 100644
--- a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserController.kt
+++ b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserController.kt
@@ -1,8 +1,11 @@
package org.luxons.sevenwonders.server.controllers
+import org.luxons.sevenwonders.model.api.GameListEvent
+import org.luxons.sevenwonders.model.api.GameListEventWrapper
import org.luxons.sevenwonders.model.api.LobbyDTO
import org.luxons.sevenwonders.model.api.actions.CreateGameAction
import org.luxons.sevenwonders.model.api.actions.JoinGameAction
+import org.luxons.sevenwonders.model.api.wrap
import org.luxons.sevenwonders.server.ApiMisuseException
import org.luxons.sevenwonders.server.api.toDTO
import org.luxons.sevenwonders.server.lobby.Lobby
@@ -36,9 +39,9 @@ class GameBrowserController(
* @return the current list of [Lobby]s
*/
@SubscribeMapping("/games") // prefix /topic not shown
- fun listGames(principal: Principal): List<LobbyDTO> {
+ fun listGames(principal: Principal): GameListEventWrapper {
logger.info("Player '{}' subscribed to /topic/games", principal.name)
- return lobbyRepository.list().map { it.toDTO() }
+ return GameListEvent.ReplaceList(lobbyRepository.list().map { it.toDTO() }).wrap()
}
/**
@@ -61,9 +64,7 @@ class GameBrowserController(
// notify everyone that a new game exists
val lobbyDto = lobby.toDTO()
- // we need to pass a non-generic type (array is fine) so that Spring doesn't break when trying to find a
- // serializer from Kotlinx Serialization
- template.convertAndSend("/topic/games", listOf(lobbyDto).toTypedArray())
+ template.convertAndSend("/topic/games", GameListEvent.CreateOrUpdate(lobbyDto).wrap())
return lobbyDto
}
diff --git a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/LobbyController.kt b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/LobbyController.kt
index 7757c9d9..6cc404e9 100644
--- a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/LobbyController.kt
+++ b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/controllers/LobbyController.kt
@@ -3,10 +3,12 @@ package org.luxons.sevenwonders.server.controllers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.luxons.sevenwonders.bot.SevenWondersBot
+import org.luxons.sevenwonders.model.api.GameListEvent
import org.luxons.sevenwonders.model.api.actions.AddBotAction
import org.luxons.sevenwonders.model.api.actions.ReassignWondersAction
import org.luxons.sevenwonders.model.api.actions.ReorderPlayersAction
import org.luxons.sevenwonders.model.api.actions.UpdateSettingsAction
+import org.luxons.sevenwonders.model.api.wrap
import org.luxons.sevenwonders.model.hideHandsAndWaitForReadiness
import org.luxons.sevenwonders.server.api.toDTO
import org.luxons.sevenwonders.server.lobby.Lobby
@@ -44,12 +46,38 @@ class LobbyController(
val player = principal.player
val lobby = player.lobby
lobby.removePlayer(principal.name)
+ logger.info("Player {} left game '{}'", player, lobby.name)
+ template.convertAndSendToUser(player.username, "/queue/lobby/left", lobby.id)
+
if (lobby.getPlayers().isEmpty()) {
- lobbyRepository.remove(lobby.id)
+ deleteLobby(lobby)
+ } else {
+ sendLobbyUpdateToPlayers(lobby)
}
+ }
- logger.info("Player {} left game '{}'", player, lobby.name)
- sendLobbyUpdateToPlayers(lobby)
+ /**
+ * Disbands the current group, making everyone leave the lobby.
+ *
+ * @param principal the connected user's information
+ */
+ @MessageMapping("/lobby/disband")
+ fun disband(principal: Principal) {
+ val player = principal.player
+ val lobby = player.ownedLobby
+
+ lobby.getPlayers().forEach {
+ it.leave()
+ template.convertAndSendToUser(it.username, "/queue/lobby/left", lobby.id)
+ }
+ logger.info("Player {} disbanded game '{}'", player, lobby.name)
+ deleteLobby(lobby)
+ }
+
+ private fun deleteLobby(lobby: Lobby) {
+ lobbyRepository.remove(lobby.id)
+ template.convertAndSend("/topic/games", GameListEvent.Delete(lobby.id).wrap())
+ logger.info("Game '{}' removed", lobby.name)
}
/**
@@ -110,12 +138,11 @@ class LobbyController(
}
internal fun sendLobbyUpdateToPlayers(lobby: Lobby) {
+ val lobbyDto = lobby.toDTO()
lobby.getPlayers().forEach {
- template.convertAndSendToUser(it.username, "/queue/lobby/updated", lobby.toDTO())
+ template.convertAndSendToUser(it.username, "/queue/lobby/updated", lobbyDto)
}
- // we need to pass a non-generic type (array is fine) so that Spring doesn't break when trying to find a
- // serializer from Kotlinx Serialization
- template.convertAndSend("/topic/games", listOf(lobby.toDTO()).toTypedArray())
+ template.convertAndSend("/topic/games", GameListEvent.CreateOrUpdate(lobbyDto).wrap())
}
/**
diff --git a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/lobby/Lobby.kt b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/lobby/Lobby.kt
index 6ebe39c1..4176c485 100644
--- a/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/lobby/Lobby.kt
+++ b/sw-server/src/main/kotlin/org/luxons/sevenwonders/server/lobby/Lobby.kt
@@ -101,7 +101,7 @@ class Lobby(
@Synchronized
fun removePlayer(username: String): Player {
- val playerIndex = find(username)
+ val playerIndex = players.indexOfFirst { it.username == username }
if (playerIndex < 0) {
throw UnknownPlayerException(username)
}
@@ -115,8 +115,6 @@ class Lobby(
return player
}
- private fun find(username: String): Int = players.indexOfFirst { it.username == username }
-
fun setEndOfGame() {
state = State.FINISHED
}
diff --git a/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/SevenWondersTest.kt b/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/SevenWondersTest.kt
index db50609a..ccc2e548 100644
--- a/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/SevenWondersTest.kt
+++ b/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/SevenWondersTest.kt
@@ -10,16 +10,14 @@ import org.junit.runner.RunWith
import org.luxons.sevenwonders.client.SevenWondersClient
import org.luxons.sevenwonders.client.SevenWondersSession
import org.luxons.sevenwonders.client.joinGameAndWaitLobby
+import org.luxons.sevenwonders.model.api.GameListEvent
import org.luxons.sevenwonders.model.api.LobbyDTO
import org.luxons.sevenwonders.server.test.runAsyncTest
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
import org.springframework.boot.web.server.LocalServerPort
import org.springframework.test.context.junit4.SpringRunner
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertNotNull
-import kotlin.test.assertNull
+import kotlin.test.*
@OptIn(FlowPreview::class)
@RunWith(SpringRunner::class)
@@ -94,18 +92,17 @@ class SevenWondersTest {
val otherSession = newPlayer("OtherPlayer")
val games = otherSession.watchGames().produceIn(this)
- var receivedLobbies = withTimeout(500) { games.receive() }
- assertNotNull(receivedLobbies)
- assertEquals(0, receivedLobbies.size)
+ val initialListEvent = withTimeout(500) { games.receive() }
+ assertTrue(initialListEvent is GameListEvent.ReplaceList)
+ assertEquals(0, initialListEvent.lobbies.size)
val ownerSession = newPlayer("GameOwner")
val gameName = "Test Game"
val createdLobby = ownerSession.createGameAndWaitLobby(gameName)
- receivedLobbies = withTimeout(500) { games.receive() }
- assertNotNull(receivedLobbies)
- assertEquals(1, receivedLobbies.size)
- val receivedLobby = receivedLobbies[0]
+ val afterGameListEvent = withTimeout(500) { games.receive() }
+ assertTrue(afterGameListEvent is GameListEvent.CreateOrUpdate)
+ val receivedLobby = afterGameListEvent.lobby
assertEquals(createdLobby.id, receivedLobby.id)
assertEquals(createdLobby.name, receivedLobby.name)
diff --git a/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserControllerTest.kt b/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserControllerTest.kt
index 648c924b..df2e02ee 100644
--- a/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserControllerTest.kt
+++ b/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/GameBrowserControllerTest.kt
@@ -2,6 +2,7 @@ package org.luxons.sevenwonders.server.controllers
import org.junit.Before
import org.junit.Test
+import org.luxons.sevenwonders.model.api.GameListEvent
import org.luxons.sevenwonders.model.api.actions.CreateGameAction
import org.luxons.sevenwonders.model.api.actions.JoinGameAction
import org.luxons.sevenwonders.server.controllers.GameBrowserController.UserAlreadyInGameException
@@ -32,8 +33,8 @@ class GameBrowserControllerTest {
@Test
fun listGames_initiallyEmpty() {
val principal = TestPrincipal("testuser")
- val games = gameBrowserController.listGames(principal)
- assertTrue(games.isEmpty())
+ val gameListEvent = gameBrowserController.listGames(principal).event as GameListEvent.ReplaceList
+ assertTrue(gameListEvent.lobbies.isEmpty())
}
@Test
@@ -47,9 +48,9 @@ class GameBrowserControllerTest {
assertEquals("Test Game", createdLobby.name)
- val games = gameBrowserController.listGames(principal)
- assertFalse(games.isEmpty())
- val lobby = games.iterator().next()
+ val gameListEvent = gameBrowserController.listGames(principal).event as GameListEvent.ReplaceList
+ assertFalse(gameListEvent.lobbies.isEmpty())
+ val lobby = gameListEvent.lobbies.first()
assertEquals(lobby, createdLobby)
assertEquals(player.username, lobby.players[0].username)
}
diff --git a/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/LobbyControllerTest.kt b/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/LobbyControllerTest.kt
index 73a24ef0..097e4792 100644
--- a/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/LobbyControllerTest.kt
+++ b/sw-server/src/test/kotlin/org/luxons/sevenwonders/server/controllers/LobbyControllerTest.kt
@@ -14,12 +14,8 @@ import org.luxons.sevenwonders.server.repositories.LobbyRepository
import org.luxons.sevenwonders.server.repositories.PlayerNotFoundException
import org.luxons.sevenwonders.server.repositories.PlayerRepository
import org.luxons.sevenwonders.server.test.mockSimpMessagingTemplate
-import java.util.HashMap
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-import kotlin.test.assertFalse
-import kotlin.test.assertSame
-import kotlin.test.assertTrue
+import java.util.*
+import kotlin.test.*
class LobbyControllerTest {
@@ -69,7 +65,7 @@ class LobbyControllerTest {
}
@Test
- fun leave_succeedsWhenInALobby_asOwner() {
+ fun leave_ownerAloneInLobby_succeedsAndRemovesLobby() {
val player = playerRepository.createOrUpdate("testuser", "Test User")
val lobby = lobbyRepository.create("Test Game", player)
@@ -82,7 +78,29 @@ class LobbyControllerTest {
}
@Test
- fun leave_succeedsWhenInALobby_asPeasant() {
+ fun leave_ownerInLobbyWithOthers_succeedsAndTransfersOwnership() {
+ val player1 = playerRepository.createOrUpdate("testuser", "Test User")
+ val lobby = lobbyRepository.create("Test Game", player1)
+ val player2 = addPlayer(lobby, "testuser2")
+
+ val principal = TestPrincipal("testuser")
+ lobbyController.leave(principal)
+
+ assertTrue(lobbyRepository.list().contains(lobby))
+ assertFalse(lobby.getPlayers().contains(player1))
+ assertEquals(lobby.owner, player2)
+
+ assertTrue(player2.isGameOwner)
+ assertTrue(player2.isInLobby)
+ assertFalse(player2.isInGame)
+
+ assertFalse(player1.isGameOwner)
+ assertFalse(player1.isInLobby)
+ assertFalse(player1.isInGame)
+ }
+
+ @Test
+ fun leave_succeedsWhenInALobby_asJoiner() {
val player = playerRepository.createOrUpdate("testuser", "Test User")
val lobby = lobbyRepository.create("Test Game", player)
val player2 = addPlayer(lobby, "testuser2")
@@ -118,7 +136,7 @@ class LobbyControllerTest {
}
@Test
- fun reorderPlayers_failsForPeasant() {
+ fun reorderPlayers_failsForJoiner() {
val player = playerRepository.createOrUpdate("testuser", "Test User")
val lobby = lobbyRepository.create("Test Game", player)
@@ -157,7 +175,7 @@ class LobbyControllerTest {
}
@Test
- fun updateSettings_failsForPeasant() {
+ fun updateSettings_failsForJoiner() {
val player = playerRepository.createOrUpdate("testuser", "Test User")
val lobby = lobbyRepository.create("Test Game", player)
@@ -189,7 +207,7 @@ class LobbyControllerTest {
}
@Test
- fun startGame_failsForPeasant() {
+ fun startGame_failsForJoiner() {
val player = playerRepository.createOrUpdate("testuser", "Test User")
val lobby = lobbyRepository.create("Test Game", player)
diff --git a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/Lobby.kt b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/Lobby.kt
index 3fa85b0a..478f4f4f 100644
--- a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/Lobby.kt
+++ b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/components/lobby/Lobby.kt
@@ -1,33 +1,21 @@
package org.luxons.sevenwonders.ui.components.lobby
-import com.palantir.blueprintjs.Elevation
-import com.palantir.blueprintjs.Intent
-import com.palantir.blueprintjs.bpButton
-import com.palantir.blueprintjs.bpButtonGroup
-import com.palantir.blueprintjs.bpCard
-import com.palantir.blueprintjs.bpDivider
-import com.palantir.blueprintjs.bpNonIdealState
+import com.palantir.blueprintjs.*
import kotlinx.css.*
-import kotlinx.css.properties.*
+import kotlinx.css.properties.transform
+import kotlinx.css.properties.translate
import org.luxons.sevenwonders.model.api.LobbyDTO
import org.luxons.sevenwonders.model.api.PlayerDTO
-import org.luxons.sevenwonders.model.wonders.AssignedWonder
-import org.luxons.sevenwonders.model.wonders.WonderSide
-import org.luxons.sevenwonders.model.wonders.deal
-import org.luxons.sevenwonders.model.wonders.withRandomSide
-import org.luxons.sevenwonders.model.wonders.withSide
+import org.luxons.sevenwonders.model.wonders.*
import org.luxons.sevenwonders.ui.components.GlobalStyles
-import org.luxons.sevenwonders.ui.redux.RequestAddBot
-import org.luxons.sevenwonders.ui.redux.RequestLeaveLobby
-import org.luxons.sevenwonders.ui.redux.RequestReassignWonders
-import org.luxons.sevenwonders.ui.redux.RequestReorderPlayers
-import org.luxons.sevenwonders.ui.redux.RequestStartGame
-import org.luxons.sevenwonders.ui.redux.connectStateAndDispatch
+import org.luxons.sevenwonders.ui.redux.*
import react.RBuilder
import react.RComponent
import react.RProps
import react.RState
-import react.dom.*
+import react.dom.h2
+import react.dom.h3
+import react.dom.h4
import styled.css
import styled.styledDiv
import styled.styledH2
@@ -43,6 +31,7 @@ interface LobbyDispatchProps : RProps {
var startGame: () -> Unit
var addBot: (displayName: String) -> Unit
var leaveLobby: () -> Unit
+ var disbandLobby: () -> Unit
var reorderPlayers: (orderedPlayers: List<String>) -> Unit
var reassignWonders: (wonders: List<AssignedWonder>) -> Unit
}
@@ -84,6 +73,8 @@ class LobbyPresenter(props: LobbyProps) : RComponent<LobbyProps, RState>(props)
bpButtonGroup {
startButton(currentGame, currentPlayer)
addBotButton(currentGame)
+ leaveButton()
+ disbandButton()
}
} else {
leaveButton()
@@ -217,7 +208,7 @@ class LobbyPresenter(props: LobbyProps) : RComponent<LobbyProps, RState>(props)
private fun RBuilder.leaveButton() {
bpButton(
large = true,
- intent = Intent.DANGER,
+ intent = Intent.WARNING,
icon = "arrow-left",
title = "Leave the lobby and go back to the game browser",
onClick = { props.leaveLobby() },
@@ -225,6 +216,18 @@ class LobbyPresenter(props: LobbyProps) : RComponent<LobbyProps, RState>(props)
+"LEAVE"
}
}
+
+ private fun RBuilder.disbandButton() {
+ bpButton(
+ large = true,
+ intent = Intent.DANGER,
+ icon = "delete",
+ title = "Disband the group and go back to the game browser",
+ onClick = { props.disbandLobby() },
+ ) {
+ +"DISBAND"
+ }
+ }
}
fun RBuilder.lobby() = lobby {}
@@ -239,6 +242,7 @@ private val lobby = connectStateAndDispatch<LobbyStateProps, LobbyDispatchProps,
startGame = { dispatch(RequestStartGame()) }
addBot = { name -> dispatch(RequestAddBot(name)) }
leaveLobby = { dispatch(RequestLeaveLobby()) }
+ disbandLobby = { dispatch(RequestDisbandLobby()) }
reorderPlayers = { orderedPlayers -> dispatch(RequestReorderPlayers(orderedPlayers)) }
reassignWonders = { wonders -> dispatch(RequestReassignWonders(wonders)) }
},
diff --git a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt
index cd9443df..c5ffafa0 100644
--- a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt
+++ b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Actions.kt
@@ -3,6 +3,7 @@ package org.luxons.sevenwonders.ui.redux
import org.luxons.sevenwonders.model.PlayerMove
import org.luxons.sevenwonders.model.PlayerTurnInfo
import org.luxons.sevenwonders.model.api.ConnectedPlayer
+import org.luxons.sevenwonders.model.api.GameListEvent
import org.luxons.sevenwonders.model.api.LobbyDTO
import org.luxons.sevenwonders.model.cards.PreparedCard
import redux.RAction
@@ -11,12 +12,14 @@ data class FatalError(val message: String) : RAction
data class SetCurrentPlayerAction(val player: ConnectedPlayer) : RAction
-data class UpdateGameListAction(val games: List<LobbyDTO>) : RAction
+data class UpdateGameListAction(val event: GameListEvent) : RAction
data class UpdateLobbyAction(val lobby: LobbyDTO) : RAction
data class EnterLobbyAction(val lobby: LobbyDTO) : RAction
+data class LeaveLobbyAction(val lobbyId: Long) : RAction
+
data class EnterGameAction(val lobby: LobbyDTO, val turnInfo: PlayerTurnInfo) : RAction
data class TurnInfoEvent(val turnInfo: PlayerTurnInfo) : RAction
diff --git a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/ApiActions.kt b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/ApiActions.kt
index d259da81..87bacf62 100644
--- a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/ApiActions.kt
+++ b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/ApiActions.kt
@@ -23,6 +23,8 @@ class RequestStartGame : RAction
class RequestLeaveLobby : RAction
+class RequestDisbandLobby : RAction
+
class RequestLeaveGame : RAction
class RequestSayReady : RAction
diff --git a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt
index 7789cabb..b1d730a0 100644
--- a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt
+++ b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/Reducers.kt
@@ -4,9 +4,9 @@ import org.luxons.sevenwonders.model.MoveType
import org.luxons.sevenwonders.model.PlayerMove
import org.luxons.sevenwonders.model.PlayerTurnInfo
import org.luxons.sevenwonders.model.api.ConnectedPlayer
+import org.luxons.sevenwonders.model.api.GameListEvent
import org.luxons.sevenwonders.model.api.LobbyDTO
import org.luxons.sevenwonders.model.api.PlayerDTO
-import org.luxons.sevenwonders.model.api.State
import org.luxons.sevenwonders.model.cards.CardBack
import org.luxons.sevenwonders.model.cards.HandCard
import org.luxons.sevenwonders.model.resources.ResourceTransactionOptions
@@ -53,7 +53,11 @@ fun rootReducer(state: SwState, action: RAction): SwState = state.copy(
)
private fun gamesReducer(games: Map<Long, LobbyDTO>, action: RAction): Map<Long, LobbyDTO> = when (action) {
- is UpdateGameListAction -> (games + action.games.associateBy { it.id }).filterValues { it.state != State.FINISHED }
+ is UpdateGameListAction -> when (action.event) {
+ is GameListEvent.ReplaceList -> action.event.lobbies.associateBy { it.id }
+ is GameListEvent.CreateOrUpdate -> games + (action.event.lobby.id to action.event.lobby)
+ is GameListEvent.Delete -> games - action.event.lobbyId
+ }
else -> games
}
@@ -64,6 +68,7 @@ private fun currentPlayerReducer(currentPlayer: ConnectedPlayer?, action: RActio
private fun currentLobbyReducer(currentLobby: LobbyDTO?, action: RAction): LobbyDTO? = when (action) {
is EnterLobbyAction -> action.lobby
+ is LeaveLobbyAction -> null
is UpdateLobbyAction -> action.lobby
is PlayerReadyEvent -> currentLobby?.let { l ->
l.copy(players = l.players.map { p -> if (p.username == action.username) p.copy(isReady = true) else p })
diff --git a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt
index c2d26e0f..c9f73111 100644
--- a/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt
+++ b/sw-ui/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt
@@ -80,6 +80,8 @@ private fun SwSagaContext.launchApiActionHandlersIn(scope: CoroutineScope, sessi
scope.launchOnEach<RequestCreateGame> { session.createGame(it.gameName) }
scope.launchOnEach<RequestJoinGame> { session.joinGame(it.gameId) }
+ scope.launchOnEach<RequestLeaveLobby> { session.leaveLobby() }
+ scope.launchOnEach<RequestDisbandLobby> { session.disbandLobby() }
scope.launchOnEach<RequestAddBot> { session.addBot(it.botDisplayName) }
scope.launchOnEach<RequestReorderPlayers> { session.reorderPlayers(it.orderedPlayers) }
@@ -94,10 +96,11 @@ private fun SwSagaContext.launchApiActionHandlersIn(scope: CoroutineScope, sessi
private fun SwSagaContext.launchNavigationHandlers(scope: CoroutineScope, session: SevenWondersSession) {
- // FIXME map this actions like others and await server event instead
- scope.launchOnEach<RequestLeaveLobby> {
- session.leaveLobby()
- dispatch(Navigate(Route.GAME_BROWSER))
+ scope.launch {
+ session.watchLobbyLeft().collect { leftLobbyId ->
+ dispatch(LeaveLobbyAction(leftLobbyId))
+ dispatch(Navigate(Route.GAME_BROWSER))
+ }
}
// FIXME map this actions like others and await server event instead
@@ -113,4 +116,3 @@ private fun SwSagaContext.launchNavigationHandlers(scope: CoroutineScope, sessio
}
}
}
-
bgstack15