diff --git a/.idea/artifacts/composeApp_js.xml b/.idea/artifacts/composeApp_js.xml
new file mode 100644
index 0000000..54559a3
--- /dev/null
+++ b/.idea/artifacts/composeApp_js.xml
@@ -0,0 +1,6 @@
+
+
+ $PROJECT_DIR$/composeApp/build/libs
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/composeApp_jvm.xml b/.idea/artifacts/composeApp_jvm.xml
new file mode 100644
index 0000000..df75785
--- /dev/null
+++ b/.idea/artifacts/composeApp_jvm.xml
@@ -0,0 +1,6 @@
+
+
+ $PROJECT_DIR$/composeApp/build/libs
+
+
+
\ No newline at end of file
diff --git a/.idea/artifacts/composeApp_wasm_js.xml b/.idea/artifacts/composeApp_wasm_js.xml
new file mode 100644
index 0000000..2c0f6b1
--- /dev/null
+++ b/.idea/artifacts/composeApp_wasm_js.xml
@@ -0,0 +1,6 @@
+
+
+ $PROJECT_DIR$/composeApp/build/libs
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..cebe225
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminScreen.kt
new file mode 100644
index 0000000..a4f0bb1
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminScreen.kt
@@ -0,0 +1,23 @@
+package hu.mokegyesulet.it.dunestrat.feature.entersteps.admin
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.viewmodel.compose.viewModel
+import hu.mokegyesulet.it.dunestrat.model.Player
+import hu.mokegyesulet.it.dunestrat.ui.step.EnterStepsCard
+
+@Composable
+fun EnterStepsAdminScreen(players: List) {
+ val viewModel = viewModel { EnterStepsAdminViewModel(players) }
+ val steps = viewModel.stepStates.value
+ LazyColumn(
+ modifier = Modifier.fillMaxWidth(1f),
+ ) {
+ itemsIndexed(steps) { index, step ->
+ EnterStepsCard(step, viewModel.onStepCardStateChanged(index))
+ }
+ }
+}
diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminViewModel.kt
new file mode 100644
index 0000000..e16a529
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/entersteps/admin/EnterStepsAdminViewModel.kt
@@ -0,0 +1,45 @@
+package hu.mokegyesulet.it.dunestrat.feature.entersteps.admin
+
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.ViewModel
+import hu.mokegyesulet.it.dunestrat.model.Player
+import hu.mokegyesulet.it.dunestrat.ui.step.StepCardEvent
+import hu.mokegyesulet.it.dunestrat.ui.step.StepCardState
+
+class EnterStepsAdminViewModel(players: List) : ViewModel() {
+ private val _stepStates: MutableList = ArrayList()
+ init {
+ for (p in players) {
+ _stepStates.add(StepCardState(p.id))
+ }
+ }
+ val stepStates: MutableState>
+ get() = mutableStateOf(_stepStates)
+
+ private fun onStepCardStateChanged(
+ event: StepCardEvent,
+ playerIndex: Int,
+ ) {
+ when (event) {
+ is StepCardEvent.EnterFieldsChanged -> _stepStates[playerIndex] =
+ _stepStates[playerIndex].copy(enterFields = event.enterFields)
+
+ is StepCardEvent.LeaveFieldsChanged -> _stepStates[playerIndex] =
+ _stepStates[playerIndex].copy(leaveFields = event.leaveFields)
+
+ is StepCardEvent.BuildHarvestersChanged -> _stepStates[playerIndex] =
+ _stepStates[playerIndex].copy(buildHarvesters = event.buildHarvesters)
+
+ is StepCardEvent.PurchaseWeaponsChanged -> {
+ val x = _stepStates[playerIndex].purchaseWeapons.toMutableMap()
+ x[event.weaponType] = event.amount
+ _stepStates[playerIndex] = _stepStates[playerIndex].copy(purchaseWeapons = x)
+ }
+ }
+ }
+
+ fun onStepCardStateChanged(playerIndex: Int): (StepCardEvent) -> Unit = { e ->
+ onStepCardStateChanged(e, playerIndex)
+ }
+}
diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt
index 0787087..79da864 100644
--- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt
+++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenu.kt
@@ -1,66 +1,109 @@
package hu.mokegyesulet.it.dunestrat.feature.mainmenu
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.material3.Button
-import androidx.compose.material3.Text
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
+import hu.mokegyesulet.it.dunestrat.model.Game
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainMenu(
- onPlayerStepInput: () -> Unit,
- onPlaceholder2: () -> Unit,
- onTestData: () -> Unit,
+ onInputMovesClick: (Int) -> Unit,
+ onStat: (Int) -> Unit,
+ onNewGameClick: () -> Unit,
) {
- val viewModel: MainMenuViewModel = viewModel { MainMenuViewModel() }
+ val viewModel = viewModel { MainMenuViewModel() }
+
val loggedIn by viewModel.isLoggedIn.collectAsState(initial = false)
- val deserts by viewModel.deserts.collectAsState(initial = emptyList())
+
if (loggedIn) {
- Column(
- modifier = Modifier.fillMaxSize(),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.SpaceEvenly,
- ) {
- Button(
- onClick = onPlayerStepInput,
- ) {
- Text(text = "Player step input")
- }
+ val games by viewModel.games.collectAsStateWithLifecycle()
+ val selectedGame by viewModel.selectedGame
+ val players by viewModel.players
- Button(
- onClick = {},
- ) {
- Text(text = "Placeholder 2")
- }
+ Row(
+ modifier = Modifier.fillMaxSize().padding(50.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ ) {
+ LazyColumn {
+ item {
+ Button(onClick = onNewGameClick) {
+ Row {
+ Icon(Icons.Filled.Add, contentDescription = "Add new game")
+ Text(text = "Új játék")
+ }
+ }
+ }
- Button(
- onClick = onTestData,
- ) {
- Text(text = "Test data")
+ items(games) { game: Game ->
+ Card(
+ border = if (selectedGame == game) {
+ BorderStroke(2.dp, Color.Red)
+ } else {
+ null
+ },
+ modifier = Modifier.clickable {
+ viewModel.onEvent(MainMenuViewModel.Event.SelectGame(game))
+ }.padding(4.dp),
+ ) {
+ Text(
+ text = game.name,
+ modifier = Modifier.padding(8.dp),
+ )
+ }
+ }
}
-
- Button(
- onClick = viewModel::onLogout,
+ Column(
+ modifier = Modifier.fillMaxHeight(),
+ verticalArrangement = Arrangement.SpaceBetween,
) {
- Text(text = "Log out")
+ Button(
+ onClick = { onInputMovesClick(selectedGame?.id ?: -1) },
+ enabled = selectedGame != null,
+ ) {
+ Text(text = "Lépések bevitele")
+ }
+ LazyColumn(
+ modifier = Modifier.weight(1f)
+ .wrapContentHeight(),
+ ) {
+ items(players.toList()) { player ->
+ Text(text = "${player.id}: ${player.water} víz, ${player.spice} fűszer")
+ }
+ }
+ Button(
+ onClick = { onStat(selectedGame?.id ?: -1) },
+ enabled = selectedGame != null,
+ ) {
+ Text(text = "Statisztika")
+ }
}
}
} else {
- Column(
+ Box(
modifier = Modifier.fillMaxSize(),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center,
) {
Button(
- onClick = viewModel::onLogin,
+ onClick = {
+ viewModel.onLogin()
+ },
+ modifier = Modifier.align(Alignment.Center),
) {
- Text(text = "Log in")
+ Text(text = "Bejelentkezés")
}
}
}
diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt
index 242159e..2a97070 100644
--- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt
+++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/mainmenu/MainMenuViewModel.kt
@@ -1,18 +1,22 @@
package hu.mokegyesulet.it.dunestrat.feature.mainmenu
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import hu.mokegyesulet.it.dunestrat.backend.AuthStatus
import hu.mokegyesulet.it.dunestrat.backend.SupabaseAuth
import hu.mokegyesulet.it.dunestrat.backend.SupabaseRepository
+import hu.mokegyesulet.it.dunestrat.model.Game
+import hu.mokegyesulet.it.dunestrat.model.GameState
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
class MainMenuViewModel : ViewModel() {
- val deserts = SupabaseRepository.getDeserts()
-
fun onLogin() {
viewModelScope.launch {
SupabaseAuth.logIn()
@@ -29,4 +33,38 @@ class MainMenuViewModel : ViewModel() {
get() = SupabaseAuth.authStatus.map {
it is AuthStatus.Authenticated
}
+
+ val games = SupabaseRepository.getGames().stateIn(
+ viewModelScope,
+ SharingStarted.WhileSubscribed(),
+ emptyList(),
+ )
+
+ val selectedGame = mutableStateOf(null)
+
+ val latestGameState = mutableStateOf(null)
+
+ val players = derivedStateOf {
+ latestGameState.value?.players ?: emptySet()
+ }
+
+ sealed class Event {
+ data class SelectGame(val game: Game) : Event()
+ }
+ fun onEvent(event: Event) {
+ when (event) {
+ is Event.SelectGame -> {
+ if (selectedGame.value != event.game) {
+ selectedGame.value = event.game
+ viewModelScope.launch {
+ latestGameState.value =
+ SupabaseRepository.getLatestGameStateByGameId(event.game.id)
+ }
+ } else {
+ selectedGame.value = null
+ latestGameState.value = null
+ }
+ }
+ }
+ }
}
diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt
index 97d4c14..968cd3d 100644
--- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt
+++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/feature/testdata/TestDataScreen.kt
@@ -15,7 +15,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
-fun TestDataScreen() {
+fun TestDataScreen(onTestBack: () -> Unit) {
val viewModel = viewModel { TestDataViewModel() }
val deserts by viewModel.deserts.collectAsStateWithLifecycle()
val games by viewModel.games.collectAsStateWithLifecycle()
@@ -51,6 +51,7 @@ fun TestDataScreen() {
Text(text = it.id.toString())
}
}
+
Button(onClick = { viewModel.initDesert() }) { Text("Init test desert") }
Button(onClick = { viewModel.initGame(deserts.last()) }) { Text("Init test game") }
Button(onClick = {
@@ -59,5 +60,6 @@ fun TestDataScreen() {
Button(onClick = {
viewModel.initPlayerSteps(gameStates.last())
}) { Text("Init test steps") }
+ Button(onClick = onTestBack) { Text("Vissza") }
}
}
diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt
index 3a2ba64..c49eae8 100644
--- a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt
+++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/navigation/NavGraph.kt
@@ -19,14 +19,16 @@ fun NavGraph(navController: NavHostController = rememberNavController()) {
) {
composable {
MainMenu(
- onPlayerStepInput = { navController.navigate(Screen.PlayerStepInput(32)) },
- onPlaceholder2 = {},
- onTestData = { navController.navigate(Screen.TestData) },
+ onStat = {},
+ onInputMovesClick = { gameId ->
+ navController.navigate(Screen.PlayerStepInput(gameId))
+ },
+ onNewGameClick = {
+ navController.navigate(Screen.InitScreen)
+ },
)
}
- composable { TestDataScreen() }
-
composable {
InitScreen(
onNavigateBack = {
@@ -48,5 +50,6 @@ fun NavGraph(navController: NavHostController = rememberNavController()) {
},
)
}
+ composable { TestDataScreen(onTestBack = { navController.navigateUp() }) }
}
}
diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/EnterStepsCard.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/EnterStepsCard.kt
new file mode 100644
index 0000000..49a1ae5
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/EnterStepsCard.kt
@@ -0,0 +1,61 @@
+package hu.mokegyesulet.it.dunestrat.ui.step
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.material3.Card
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.runtime.Composable
+import hu.mokegyesulet.it.dunestrat.model.Weapon
+
+@Composable
+fun EnterStepsCard(
+ stepCardState: StepCardState,
+ onStepCardStateChanged: (StepCardEvent) -> Unit,
+) {
+ Card {
+ Column {
+ Text("Player: ${stepCardState.playerId}")
+ Row {
+ Text("Lelép:")
+ TextField(
+ value = stepCardState.leaveFields,
+ onValueChange = {
+ onStepCardStateChanged(StepCardEvent.LeaveFieldsChanged(it))
+ },
+ )
+ }
+ Row {
+ Text("Rálép:")
+ TextField(
+ value = stepCardState.enterFields,
+ onValueChange = {
+ onStepCardStateChanged(StepCardEvent.EnterFieldsChanged(it))
+ },
+ )
+ }
+ Row {
+ Text("Fegyver:")
+ for (w in Weapon.entries) {
+ TextField(
+ value = stepCardState.purchaseWeapons[w].toString(),
+ onValueChange = {
+ onStepCardStateChanged(
+ StepCardEvent.PurchaseWeaponsChanged(w, it.toInt()),
+ )
+ },
+ )
+ }
+ }
+ Row {
+ Text("Harvester:")
+ TextField(
+ value = stepCardState.buildHarvesters,
+ onValueChange = {
+ onStepCardStateChanged(StepCardEvent.BuildHarvestersChanged(it))
+ },
+ )
+ }
+ }
+ }
+}
diff --git a/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/StepCardState.kt b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/StepCardState.kt
new file mode 100644
index 0000000..431799a
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/hu/mokegyesulet/it/dunestrat/ui/step/StepCardState.kt
@@ -0,0 +1,22 @@
+package hu.mokegyesulet.it.dunestrat.ui.step
+
+import hu.mokegyesulet.it.dunestrat.model.Weapon
+
+data class StepCardState(
+ val playerId: Int,
+ val leaveFields: String = "",
+ val enterFields: String = "",
+ val purchaseWeapons: Map = mapOf(
+ Weapon.LASGUN to 0,
+ Weapon.PISTOL to 0,
+ Weapon.CRYSKNIFE to 0,
+ Weapon.LEGION to 0,
+ ),
+ val buildHarvesters: String = "",
+)
+sealed class StepCardEvent {
+ class LeaveFieldsChanged(val leaveFields: String) : StepCardEvent()
+ class EnterFieldsChanged(val enterFields: String) : StepCardEvent()
+ class PurchaseWeaponsChanged(val weaponType: Weapon, val amount: Int) : StepCardEvent()
+ class BuildHarvestersChanged(val buildHarvesters: String) : StepCardEvent()
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 55299ca..2c934a5 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-agp = "8.12.3"
+agp = "8.13.2"
android-compileSdk = "36"
android-minSdk = "24"
android-targetSdk = "36"