more structure to forms and views

shared ui got an interface to demo the components
This commit is contained in:
2026-08-28 16:20:35 -04:00
parent d1ef2956d8
commit 2e47b8bfce
50 changed files with 941 additions and 610 deletions
-3
View File
@@ -1,6 +1,5 @@
plugins {
kotlin("jvm")
// alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.compose)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.kotlin.serialization)
@@ -9,8 +8,6 @@ plugins {
dependencies {
implementation(project(":shared"))
implementation(project(":shared_ui"))
// implementation(libs.compose.icons.material)
// implementation("com.composables:icons-material-icons-filled-cmp:2.2.1")
implementation(libs.kotlinx.serialization)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.ktor.client.core)
@@ -9,6 +9,7 @@ import dev.geekback.dopewars.shared.api.TravelRequest
class ClientState(player: Player) {
var player by mutableStateOf(player)
var hasLoggedIn by mutableStateOf(false)
var apiUrl: String = "http://localhost:8080"
private var api: ApiClient = ApiClient()
@@ -21,9 +22,15 @@ class ClientState(player: Player) {
suspend fun login() : Player {
val p = this.api.login("http://localhost:8080")
println("Logged in as ${p.name} / ${p.id}")
this.hasLoggedIn = true
return p
}
suspend fun logout() {
this.hasLoggedIn = false
}
suspend fun updateMarket() {
println("Updating market")
this.player.lastMarket = this.api.market(this.player)
@@ -37,8 +44,7 @@ class ClientState(player: Player) {
}
fun isLoggedIn(): Boolean {
println("Testing if user is logged in - ${this.player.id.isNotEmpty()}")
return this.player.id.isNotEmpty()
return this.hasLoggedIn
}
fun refreshApi(url: String) {
@@ -48,7 +54,7 @@ class ClientState(player: Player) {
suspend fun travel(player: Player, destination: TravelRequest) {
val result = this.api.travel("http://localhost:8080", player.id, destination)
this.api.travel("http://localhost:8080", player.id, destination)
}
@@ -1,180 +0,0 @@
package dev.geekback.dopewars.desktop
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Text
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import dev.geekback.dopewars.shared.Drug
import dev.geekback.dopewars.shared.DrugInfo
import dev.geekback.dopewars.shared.Location
import dev.geekback.dopewars.shared.Locations
import dev.geekback.dopewars.shared.Market
import dev.geekback.dopewars.shared.Player
import dev.geekback.dopewars.shared.api.MarketRequest
import dev.geekback.dopewars.shared.api.MarketResult
import dev.geekback.dopewars.shared.api.PlayerNameUpdateRequest
import dev.geekback.dopewars.shared.api.PlayerNameUpdateResponse
import dev.geekback.dopewars.shared.api.TradeAction
import dev.geekback.dopewars.shared.api.TradeRequest
import dev.geekback.dopewars.shared.api.TravelRequest
import dev.geekback.dopewars.shared.api.TravelResult
import dev.geekback.dopewars.ui.DopeWarsUi
import kotlinx.coroutines.launch
fun main() = application {
Window(
onCloseRequest = ::exitApplication,
title = "DopeWars Dev Client"
) {
DemoApp()
}
}
data class ApiData(
var marketRequest: MarketRequest,
var marketResult: MarketResult,
var playerNameUpdateRequest: PlayerNameUpdateRequest,
var playerNameUpadteResponse: PlayerNameUpdateResponse,
var tradeAction: TradeAction,
var tradeRequest: TradeRequest,
var travelRequest: TravelRequest,
var travelResult: TravelResult
)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DemoApp() {
val drawerState = rememberDrawerState(DrawerValue.Closed)
val scope = rememberCoroutineScope()
var currentScreen by remember { mutableStateOf(Screens.HOME) }
var drug by remember { mutableStateOf(DrugInfo(drug = Drug.WEED, minValue = 100.0f, maxValue = 1000.0f)) }
var player by remember { mutableStateOf(Player(id = "____", name = "TBD")) }
var location by remember { mutableStateOf(Location(name = "LocationDefault"))}
var market by remember { mutableStateOf(Market(
location = Location(name = "MarketDefault"),
prices = mapOf()
))}
val ui = DopeWarsUi.new()
val travelResultLocation = Locations.random();
var api by remember { mutableStateOf(ApiData(
marketRequest = MarketRequest(
location = Location(name = "MarketRequestLocation")
),
marketResult = MarketResult(
market = Market(location = Location(name = "MarketResultLocation"),
prices = emptyMap()
)
),
playerNameUpdateRequest = PlayerNameUpdateRequest("newPlayerName"),
playerNameUpadteResponse = PlayerNameUpdateResponse(
updatedPlayer = Player(id = "____", name = "updatedPlayerName")
),
tradeAction = TradeAction.BUY,
tradeRequest = TradeRequest(
action = TradeAction.BUY,
drug = Drug.random(),
quantity = 10
),
travelRequest = TravelRequest(
destination = Locations.random()
),
travelResult = TravelResult(
location = travelResultLocation,
day = 1,
market = Market(location = travelResultLocation, mapOf())
)
)) }
MaterialTheme {
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet {
Button(onClick = {
currentScreen = Screens.HOME
scope.launch { drawerState.close() }
}) { Text("Home")}
Button( onClick = {
currentScreen = Screens.DRUG
scope.launch { drawerState.close() }
}) { Text("Drug") }
Button( onClick = {
currentScreen = Screens.PLAYER
scope.launch { drawerState.close() }
}) { Text("Player") }
Button(onClick = {
currentScreen = Screens.LOCATION
scope.launch { drawerState.close() }
}) { Text("Location") }
Button( onClick = {
currentScreen = Screens.API
scope.launch { drawerState.close() }
}) { Text("API") }
}
}
) {
// Main content
when (currentScreen) {
Screens.HOME -> {
Column {
ui.player.view(player)
// ui.drug.view(drug)
}
}
Screens.DRUG -> {
// ui.drug.form(drug) {
// drug = it
// }
}
Screens.PLAYER -> {
ui.player.form(player) {
player = it
}
}
Screens.LOCATION -> {
ui.location.form(location) {
location = it
}
}
Screens.MARKET -> {
ui.market.form(market) {
market = it
}
}
Screens.API -> {
Column {
ui.api.marketRequest.form(api.marketRequest) { api.marketRequest = it }
ui.api.marketResponse.form(api.marketResult) { api.marketResult = it }
ui.api.playerNameUpdateRequest.form(api.playerNameUpdateRequest) { api.playerNameUpdateRequest = it }
ui.api.playerNameUpdateResponse.form(api.playerNameUpadteResponse) { api.playerNameUpadteResponse = it }
ui.api.travelRequest.form(api.travelRequest) { api.travelRequest = it }
ui.api.travelResponse.form(api.travelResult) { api.travelResult = it}
ui.api.tradeRequest.form(api.tradeRequest) { api.tradeRequest = it}
}
}
}
}
}
}
@@ -1,131 +0,0 @@
package dev.geekback.dopewars.desktop
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import dev.geekback.dopewars.shared.Location
import dev.geekback.dopewars.shared.Player
import dev.geekback.dopewars.ui.AppTheme
fun main() {
val state = ClientState(Player(id = "", name = "TestClient"))
// var drug = Drug("TestDrug", minPrice = 10.0f, maxPrice = 100.0f)
var player = Player(id = "default", name = "defaultName")
var location = Location(name = "DefaultLocation")
application {
var currentScreen = remember { Screens.DRUG }
val scope = rememberCoroutineScope()
// state.updateApiUrl("http://localhost:8080")
// val scaffoldState = rememberScaffoldState(drawerState = DrawerState(DrawerValue.Closed))
// var menuVisible by remember { mutableStateOf(false) }
Window(onCloseRequest = ::exitApplication, title = "DopeWars") {
/*
Scaffold(
scaffoldState = scaffoldState,
drawerContent = {
Button(onClick = {
currentScreen = Screens.DRUG
scope.launch {
scaffoldState.drawerState.close()
}
}) { Text("Drug")}
Button(onClick = {
currentScreen = Screens.PLAYER
scope.launch {
scaffoldState.drawerState.close()
}
}) { Text("Player") }
},
topBar = {
TopAppBar(
title = {
Text("Title")
},
navigationIcon = {
IconButton(onClick = {
scope.launch {
scaffoldState.drawerState.open()
}
}) {
Icon(imageVector = MaterialIcons.Filled.Menu , contentDescription = "Menu")
}
})
}
) {
*/
AppTheme {
Text("Test123")
Column(modifier = Modifier.fillMaxSize()) {
/*
when (currentScreen) {
Screens.DRUG -> {
drugForm(drug) {
drug = it
}
drugView(drug)
}
Screens.PLAYER -> {
playerView(player)
}
Screens.LOCATION -> {
locationView(location)
locationForm(location) {
location = it
}
}
Screens.INVENTORY -> {
inventoryView(emptyList())
}
Screens.MARKET -> {
marketView(Market(Location("Unknown Location"), emptyMap()))
}
}
*/
}
}
}
/*
println("Session ID = ${state.player.id}")
Column {
Text("DopeWars Desktop Client")
configPanel(config = state, onUrlChanged = {
state.updateApiUrl(it)
})
PlayerForm(
initialState = state.player,
onRequestLogin = {
scope.launch {
state.login()
}
},
onRequestPlayerRename = { newName ->
scope.launch {
state.renamePlayer(newName)
}
},
onGetMarket = {
scope.launch {
state.updateMarket()
}
},
onRefresh = {
scope.launch {
state.refreshPlayer()
}
}
)
}
*/
}
}
@@ -10,6 +10,7 @@ import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.Text
import androidx.compose.material3.Icon
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -21,6 +22,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import compose.icons.FeatherIcons
import compose.icons.feathericons.LogIn
import dev.geekback.dopewars.ui.AppTheme
import dev.geekback.dopewars.ui.PlayerUi
import dev.geekback.dopewars.ui.TravelUi
@@ -51,31 +54,45 @@ fun App() {
ModalDrawerSheet {
if (state.isLoggedIn()) {
// Display the current player state
PlayerUi().view(state.player)
PlayerUi().view(state.player) {}
} else {
NavigationDrawerItem(
label = {
Row {
/*
Icon(
modifier = Modifier.padding(16.dp),
imageVector = ,
contentDescription = "Login Icon"
)
*/
Text(
modifier = Modifier.padding(16.dp),
text = "Login"
)
if (state.isLoggedIn()) {
NavigationDrawerItem(
label = {
Row {
Text("Logout")
}
},
selected = false,
onClick = {
scope.launch {
state.logout()
}
}
},
selected = false,
onClick = {
scope.launch {
state.player = state.login()
)
} else {
NavigationDrawerItem(
label = {
Row {
Icon(
modifier = Modifier.padding(16.dp),
imageVector = FeatherIcons.LogIn,
contentDescription = "Login Icon"
)
Text(
modifier = Modifier.padding(16.dp),
text = "Login"
)
}
},
selected = false,
onClick = {
scope.launch {
state.player = state.login()
}
}
}
)
)
}
}
}
}
@@ -91,13 +108,12 @@ fun App() {
}
}
@Composable
fun mainWindow(state: ClientState) {
val scope = rememberCoroutineScope();
val scope = rememberCoroutineScope()
var showingTravelWizard by remember { mutableStateOf(false) }
PlayerUi().view(state.player)
PlayerUi().view(state.player) {}
Row {
Button(onClick = {
println("Transactions")
@@ -1,10 +0,0 @@
package dev.geekback.dopewars.desktop
enum class Screens {
HOME,
DRUG,
PLAYER,
LOCATION,
MARKET,
API
}