fix: generic-client startup window now prompts for server URL instead of hardcoding localhost:8080
- Add connection UI with server URL and entry path inputs - Show loading/error states during connection attempt - Add Disconnect button to main window to return to connection screen - Display connected URL in top bar
This commit is contained in:
parent
d4f7e39834
commit
b7cffab3f9
@ -1,25 +1,9 @@
|
|||||||
package com.bricks.test.generic
|
package com.bricks.test.generic
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
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.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.IntSize
|
import androidx.compose.ui.unit.IntSize
|
||||||
@ -53,34 +37,91 @@ fun main() = application {
|
|||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val windowState = remember { WindowState(width = 1280.dp, height = 800.dp) }
|
val windowState = remember { WindowState(width = 1280.dp, height = 800.dp) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
var serverUrl by remember { mutableStateOf(System.getProperty("bricks.baseUrl", "")) }
|
||||||
context.baseUrl = System.getProperty("bricks.baseUrl", "http://localhost:8080")
|
var entryPath by remember { mutableStateOf(System.getProperty("bricks.entry", "/")) }
|
||||||
DevLogStore.log(
|
var isConnected by remember { mutableStateOf(false) }
|
||||||
level = DevLogLevel.INFO,
|
var isLoading by remember { mutableStateOf(false) }
|
||||||
message = "bricks-mp starting",
|
var connectionError by remember { mutableStateOf<String?>(null) }
|
||||||
details = "baseUrl=${context.baseUrl}",
|
|
||||||
source = DevLogSource.APP
|
if (!isConnected) {
|
||||||
|
Window(
|
||||||
|
onCloseRequest = { exitApplication() },
|
||||||
|
title = "bricks-mp Connect",
|
||||||
|
state = WindowState(width = 500.dp, height = 300.dp)
|
||||||
|
) {
|
||||||
|
MaterialTheme {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize().padding(24.dp),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Text("Connect to Bricks Server", style = MaterialTheme.typography.headlineSmall, modifier = Modifier.padding(bottom = 16.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = serverUrl,
|
||||||
|
onValueChange = { serverUrl = it },
|
||||||
|
label = { Text("Server URL (e.g., http://127.0.0.1:8080)") },
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp)
|
||||||
)
|
)
|
||||||
runCatching { http.fetchUi(System.getProperty("bricks.entry", "/")) }
|
OutlinedTextField(
|
||||||
.onSuccess {
|
value = entryPath,
|
||||||
context.setCurrentWidget(it)
|
onValueChange = { entryPath = it },
|
||||||
DevLogStore.log(
|
label = { Text("Entry Path (default: /)") },
|
||||||
level = DevLogLevel.INFO,
|
modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp)
|
||||||
message = "Entry UI loaded successfully",
|
|
||||||
source = DevLogSource.APP
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
.onFailure {
|
connectionError?.let { err ->
|
||||||
DevLogStore.log(
|
Text(err, color = MaterialTheme.colorScheme.error, modifier = Modifier.padding(bottom = 8.dp))
|
||||||
level = DevLogLevel.ERROR,
|
|
||||||
message = "Failed to load entry UI: ${it.message}",
|
|
||||||
source = DevLogSource.APP,
|
|
||||||
stackTrace = it.stackTraceToString()
|
|
||||||
)
|
|
||||||
println("[Bricks] Failed to load entry UI: ${it.message}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
isLoading = true
|
||||||
|
connectionError = null
|
||||||
|
scope.launch {
|
||||||
|
try {
|
||||||
|
context.baseUrl = serverUrl
|
||||||
|
DevLogStore.log(
|
||||||
|
level = DevLogLevel.INFO,
|
||||||
|
message = "Connecting to server",
|
||||||
|
details = "baseUrl=$serverUrl",
|
||||||
|
source = DevLogSource.APP
|
||||||
|
)
|
||||||
|
val widget = http.fetchUi(entryPath)
|
||||||
|
context.setCurrentWidget(widget)
|
||||||
|
isConnected = true
|
||||||
|
DevLogStore.log(
|
||||||
|
level = DevLogLevel.INFO,
|
||||||
|
message = "Connected and UI loaded successfully",
|
||||||
|
source = DevLogSource.APP
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
connectionError = "Connection failed: ${e.message}"
|
||||||
|
DevLogStore.log(
|
||||||
|
level = DevLogLevel.ERROR,
|
||||||
|
message = "Connection failed: ${e.message}",
|
||||||
|
source = DevLogSource.APP,
|
||||||
|
stackTrace = e.stackTraceToString()
|
||||||
|
)
|
||||||
|
} finally {
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled = !isLoading && serverUrl.isNotBlank(),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
if (isLoading) {
|
||||||
|
CircularProgressIndicator(modifier = Modifier.size(20.dp), color = MaterialTheme.colorScheme.onPrimary)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
}
|
||||||
|
Text(if (isLoading) "Connecting..." else "Connect")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Main window after connection
|
||||||
Window(
|
Window(
|
||||||
onCloseRequest = {
|
onCloseRequest = {
|
||||||
http.close()
|
http.close()
|
||||||
@ -147,7 +188,7 @@ fun main() = application {
|
|||||||
message = "User triggered reload",
|
message = "User triggered reload",
|
||||||
source = DevLogSource.APP
|
source = DevLogSource.APP
|
||||||
)
|
)
|
||||||
runCatching { http.fetchUi(System.getProperty("bricks.entry", "/")) }
|
runCatching { http.fetchUi(entryPath) }
|
||||||
.onSuccess { context.setCurrentWidget(it) }
|
.onSuccess { context.setCurrentWidget(it) }
|
||||||
.onFailure {
|
.onFailure {
|
||||||
message = Triple("Error", it.message ?: "Load failed", true)
|
message = Triple("Error", it.message ?: "Load failed", true)
|
||||||
@ -158,7 +199,12 @@ fun main() = application {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
onDisconnect = {
|
||||||
|
isConnected = false
|
||||||
|
context.setCurrentWidget(null)
|
||||||
|
},
|
||||||
|
serverUrl = serverUrl
|
||||||
)
|
)
|
||||||
|
|
||||||
// DevPanel - only rendered when dev mode is enabled
|
// DevPanel - only rendered when dev mode is enabled
|
||||||
@ -193,6 +239,7 @@ fun main() = application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(androidx.compose.material3.ExperimentalMaterial3Api::class)
|
@OptIn(androidx.compose.material3.ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@ -201,12 +248,14 @@ private fun BricksHostScreen(
|
|||||||
actionDispatcher: ActionDispatcher,
|
actionDispatcher: ActionDispatcher,
|
||||||
devModeEnabled: Boolean,
|
devModeEnabled: Boolean,
|
||||||
onDevModeToggle: () -> Unit,
|
onDevModeToggle: () -> Unit,
|
||||||
onReload: () -> Unit
|
onReload: () -> Unit,
|
||||||
|
onDisconnect: () -> Unit,
|
||||||
|
serverUrl: String
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text("bricks-mp") },
|
title = { Text("bricks-mp ($serverUrl)") },
|
||||||
actions = {
|
actions = {
|
||||||
Button(
|
Button(
|
||||||
onClick = onDevModeToggle,
|
onClick = onDevModeToggle,
|
||||||
@ -214,9 +263,12 @@ private fun BricksHostScreen(
|
|||||||
) {
|
) {
|
||||||
Text(if (devModeEnabled) "Dev ON" else "Dev")
|
Text(if (devModeEnabled) "Dev ON" else "Dev")
|
||||||
}
|
}
|
||||||
Button(onClick = onReload, modifier = Modifier.padding(end = 8.dp)) {
|
Button(onClick = onReload, modifier = Modifier.padding(end = 4.dp)) {
|
||||||
Text("Reload")
|
Text("Reload")
|
||||||
}
|
}
|
||||||
|
Button(onClick = onDisconnect) {
|
||||||
|
Text("Disconnect")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user