diff options
author | Joffrey Bion <joffrey.bion@booking.com> | 2020-03-26 20:50:25 +0100 |
---|---|---|
committer | Joffrey Bion <joffrey.bion@booking.com> | 2020-03-27 10:59:39 +0100 |
commit | e600544531764f67a5917483526d8d1941454640 (patch) | |
tree | 186f23e11b4300cdc53393ff7891592280bbc961 | |
parent | Use plain redux compose when devtools not present (diff) | |
download | seven-wonders-e600544531764f67a5917483526d8d1941454640.tar.gz seven-wonders-e600544531764f67a5917483526d8d1941454640.tar.bz2 seven-wonders-e600544531764f67a5917483526d8d1941454640.zip |
Rework sagas and router to sub/unsubscribe properly
7 files changed, 89 insertions, 61 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 4acbccd9..c4293c2a 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 @@ -88,8 +88,11 @@ class SevenWondersSession(private val stompSession: StompSessionWithKxSerializat suspend fun watchLobbyUpdates(gameId: Long): StompSubscription<LobbyDTO> = stompSession.subscribe("/topic/lobby/$gameId/updated", LobbyDTO.serializer()) - suspend fun watchGameStart(gameId: Long): StompSubscription<Unit> = - stompSession.subscribeEmptyMsg("/topic/lobby/$gameId/started") + suspend fun awaitGameStart(gameId: Long) { + val gameStartSubscription = stompSession.subscribeEmptyMsg("/topic/lobby/$gameId/started") + gameStartSubscription.messages.receive() + gameStartSubscription.unsubscribe() + } suspend fun startGame() { stompSession.sendEmptyMsg("/app/lobby/startGame") 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 ab947609..e436d054 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 @@ -1,5 +1,6 @@ package org.luxons.sevenwonders.server +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeout import kotlinx.coroutines.withTimeoutOrNull @@ -67,10 +68,10 @@ class SevenWondersTest { session2.joinGame(lobby.id) val outsiderSession = newPlayer("Outsider") - val started = outsiderSession.watchGameStart(lobby.id).messages + val started = launch { outsiderSession.awaitGameStart(lobby.id) } ownerSession.startGame() - val nothing = withTimeoutOrNull(30) { started.receive() } + val nothing = withTimeoutOrNull(30) { started.join() } assertNull(nothing) disconnect(ownerSession, session1, session2, outsiderSession) } @@ -126,14 +127,14 @@ class SevenWondersTest { val session3 = newPlayer("Player3") session3.joinGame(lobby.id) - val gameStart1 = session1.watchGameStart(lobby.id).messages - val gameStart2 = session2.watchGameStart(lobby.id).messages - val gameStart3 = session3.watchGameStart(lobby.id).messages + val gameStart1 = launch { session1.awaitGameStart(lobby.id) } + val gameStart2 = launch { session2.awaitGameStart(lobby.id) } + val gameStart3 = launch { session3.awaitGameStart(lobby.id) } session1.startGame() - withTimeout(500) { gameStart1.receive() } - withTimeout(500) { gameStart2.receive() } - withTimeout(500) { gameStart3.receive() } + withTimeout(500) { gameStart1.join() } + withTimeout(500) { gameStart2.join() } + withTimeout(500) { gameStart3.join() } val turns1 = session1.watchTurns().messages val turns2 = session2.watchTurns().messages diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/Application.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/Application.kt index d692c256..b1244b5c 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/Application.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/components/Application.kt @@ -2,26 +2,21 @@ package org.luxons.sevenwonders.ui.components import org.luxons.sevenwonders.ui.components.game.gameScene import org.luxons.sevenwonders.ui.components.gameBrowser.gameBrowser +import org.luxons.sevenwonders.ui.components.home.home +import org.luxons.sevenwonders.ui.components.lobby.lobby +import org.luxons.sevenwonders.ui.router.Route import react.RBuilder import react.router.dom.hashRouter import react.router.dom.redirect import react.router.dom.route import react.router.dom.switch -import org.luxons.sevenwonders.ui.components.home.home -import org.luxons.sevenwonders.ui.components.lobby.lobby -import react.RProps - -interface IdProps : RProps { - val id: Long -} - fun RBuilder.application() = hashRouter { switch { - route("/games") { gameBrowser() } - route("/game") { gameScene() } - route("/lobby") { lobby() } - route("/", exact = true) { home() } + route(Route.GAME_BROWSER.path) { gameBrowser() } + route(Route.GAME.path) { gameScene() } + route(Route.LOBBY.path) { lobby() } + route(Route.HOME.path, exact = true) { home() } redirect(from = "*", to = "/") } } diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameBrowserSagas.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameBrowserSagas.kt index c5e3bdb8..38c3e60a 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameBrowserSagas.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/GameBrowserSagas.kt @@ -10,6 +10,8 @@ import org.luxons.sevenwonders.ui.redux.EnterLobbyAction import org.luxons.sevenwonders.ui.redux.RequestCreateGameAction import org.luxons.sevenwonders.ui.redux.RequestJoinGameAction import org.luxons.sevenwonders.ui.redux.UpdateGameListAction +import org.luxons.sevenwonders.ui.router.Navigate +import org.luxons.sevenwonders.ui.router.Route suspend fun SwSagaContext.gameBrowserSaga(session: SevenWondersSession) { GameBrowserSaga(session, this).run() @@ -26,6 +28,7 @@ private class GameBrowserSaga( val lobby = awaitCreateOrJoinGame() gamesSubscription.unsubscribe() sagaContext.dispatch(EnterLobbyAction(lobby)) + sagaContext.dispatch(Navigate(Route.LOBBY)) } } diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/LobbySagas.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/LobbySagas.kt index b2d83416..143fecd8 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/LobbySagas.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/LobbySagas.kt @@ -2,38 +2,40 @@ package org.luxons.sevenwonders.ui.redux.sagas import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch +import org.hildan.krossbow.stomp.StompSubscription import org.luxons.sevenwonders.client.SevenWondersSession +import org.luxons.sevenwonders.model.api.LobbyDTO import org.luxons.sevenwonders.ui.redux.EnterGameAction import org.luxons.sevenwonders.ui.redux.RequestStartGameAction import org.luxons.sevenwonders.ui.redux.UpdateLobbyAction -import org.luxons.sevenwonders.ui.router.Router +import org.luxons.sevenwonders.ui.router.Navigate +import org.luxons.sevenwonders.ui.router.Route -suspend fun SwSagaContext.lobbySaga(session: SevenWondersSession, lobbyId: Long) { +suspend fun SwSagaContext.lobbySaga(session: SevenWondersSession) { + val lobbyId = getState().currentLobbyId ?: error("Lobby saga run without a current lobby") coroutineScope { - launch { watchLobbyUpdates(session, lobbyId) } - launch { handleGameStart(session, lobbyId) } - launch { watchStartGame(session) } + val lobbyUpdatesSubscription = session.watchLobbyUpdates(lobbyId) + launch { watchLobbyUpdates(lobbyUpdatesSubscription) } + val startGameJob = launch { awaitStartGame(session) } + + awaitGameStart(session, lobbyId) + + lobbyUpdatesSubscription.unsubscribe() + startGameJob.cancel() + dispatch(Navigate(Route.GAME)) } } -private suspend fun SwSagaContext.watchLobbyUpdates(session: SevenWondersSession, lobbyId: Long) { - val lobbyUpdates = session.watchLobbyUpdates(lobbyId) - for (lobby in lobbyUpdates.messages) { - dispatch(UpdateLobbyAction(lobby)) - } +private suspend fun SwSagaContext.watchLobbyUpdates(lobbyUpdatesSubscription: StompSubscription<LobbyDTO>) { + dispatchAll(lobbyUpdatesSubscription.messages) { UpdateLobbyAction(it) } } -private suspend fun SwSagaContext.handleGameStart(session: SevenWondersSession, lobbyId: Long) { - val gameStartSubscription = session.watchGameStart(lobbyId) - gameStartSubscription.messages.receive() +private suspend fun SwSagaContext.awaitGameStart(session: SevenWondersSession, lobbyId: Long) { + session.awaitGameStart(lobbyId) dispatch(EnterGameAction(lobbyId)) - - coroutineScope { - launch { gameSaga(session) } - Router.game() - } } -private suspend fun SwSagaContext.watchStartGame(session: SevenWondersSession) = onEach<RequestStartGameAction> { +private suspend fun SwSagaContext.awaitStartGame(session: SevenWondersSession) { + next<RequestStartGameAction>() session.startGame() } diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt index ded0bc89..a0cd6f3c 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/redux/sagas/Sagas.kt @@ -1,16 +1,19 @@ package org.luxons.sevenwonders.ui.redux.sagas +import kotlinx.coroutines.coroutineScope import org.luxons.sevenwonders.client.SevenWondersClient +import org.luxons.sevenwonders.client.SevenWondersSession import org.luxons.sevenwonders.ui.redux.RequestChooseName import org.luxons.sevenwonders.ui.redux.SetCurrentPlayerAction import org.luxons.sevenwonders.ui.redux.SwState -import org.luxons.sevenwonders.ui.router.Router +import org.luxons.sevenwonders.ui.router.Route +import org.luxons.sevenwonders.ui.router.routerSaga import redux.RAction import redux.WrapperAction typealias SwSagaContext = SagaContext<SwState, RAction, WrapperAction> -suspend fun SwSagaContext.rootSaga() { +suspend fun SwSagaContext.rootSaga() = coroutineScope { val action = next<RequestChooseName>() val session = SevenWondersClient().connect("localhost:8000") console.info("Connected to Seven Wonders web socket API") @@ -18,8 +21,19 @@ suspend fun SwSagaContext.rootSaga() { val player = session.chooseName(action.playerName) dispatch(SetCurrentPlayerAction(player)) - Router.games() - gameBrowserSaga(session) - Router.lobby() - lobbySaga(session) + routerSaga(Route.GAME_BROWSER) { + when (it) { + Route.HOME -> homeSaga(session) + Route.LOBBY -> lobbySaga(session) + Route.GAME_BROWSER -> gameBrowserSaga(session) + Route.GAME -> gameSaga(session) + } + } +} + +private suspend fun SwSagaContext.homeSaga(session: SevenWondersSession): SevenWondersSession { + val action = next<RequestChooseName>() + val player = session.chooseName(action.playerName) + dispatch(SetCurrentPlayerAction(player)) + return session } diff --git a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/router/Router.kt b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/router/Router.kt index 09789ee1..19e8bd94 100644 --- a/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/router/Router.kt +++ b/sw-ui-kt/src/main/kotlin/org/luxons/sevenwonders/ui/router/Router.kt @@ -1,22 +1,32 @@ package org.luxons.sevenwonders.ui.router +import kotlinx.coroutines.Job +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import org.luxons.sevenwonders.ui.redux.sagas.SwSagaContext +import redux.RAction import kotlin.browser.window -object Router { - - fun games() { - push("/games") - } - - fun game() { - push("/game") - } +enum class Route(val path: String) { + HOME("/"), + GAME_BROWSER("/games"), + LOBBY("/lobby"), + GAME("/game"), +} - fun lobby() { - push("/lobby") - } +data class Navigate(val route: Route): RAction - private fun push(path: String) { - window.location.hash = path +suspend fun SwSagaContext.routerSaga( + startRoute: Route, + runRouteSaga: suspend SwSagaContext.(Route) -> Unit +) { + coroutineScope { + window.location.hash = startRoute.path + var currentSaga: Job = launch { runRouteSaga(startRoute) } + onEach<Navigate> { + currentSaga.cancel() + window.location.hash = it.route.path + currentSaga = launch { runRouteSaga(it.route) } + } } } |