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
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
@ -53,34 +37,91 @@ fun main() = application {
|
||||
val scope = rememberCoroutineScope()
|
||||
val windowState = remember { WindowState(width = 1280.dp, height = 800.dp) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
context.baseUrl = System.getProperty("bricks.baseUrl", "http://localhost:8080")
|
||||
DevLogStore.log(
|
||||
level = DevLogLevel.INFO,
|
||||
message = "bricks-mp starting",
|
||||
details = "baseUrl=${context.baseUrl}",
|
||||
source = DevLogSource.APP
|
||||
var serverUrl by remember { mutableStateOf(System.getProperty("bricks.baseUrl", "")) }
|
||||
var entryPath by remember { mutableStateOf(System.getProperty("bricks.entry", "/")) }
|
||||
var isConnected by remember { mutableStateOf(false) }
|
||||
var isLoading by remember { mutableStateOf(false) }
|
||||
var connectionError by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
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", "/")) }
|
||||
.onSuccess {
|
||||
context.setCurrentWidget(it)
|
||||
DevLogStore.log(
|
||||
level = DevLogLevel.INFO,
|
||||
message = "Entry UI loaded successfully",
|
||||
source = DevLogSource.APP
|
||||
OutlinedTextField(
|
||||
value = entryPath,
|
||||
onValueChange = { entryPath = it },
|
||||
label = { Text("Entry Path (default: /)") },
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp)
|
||||
)
|
||||
}
|
||||
.onFailure {
|
||||
DevLogStore.log(
|
||||
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}")
|
||||
}
|
||||
|
||||
connectionError?.let { err ->
|
||||
Text(err, color = MaterialTheme.colorScheme.error, modifier = Modifier.padding(bottom = 8.dp))
|
||||
}
|
||||
|
||||
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(
|
||||
onCloseRequest = {
|
||||
http.close()
|
||||
@ -147,7 +188,7 @@ fun main() = application {
|
||||
message = "User triggered reload",
|
||||
source = DevLogSource.APP
|
||||
)
|
||||
runCatching { http.fetchUi(System.getProperty("bricks.entry", "/")) }
|
||||
runCatching { http.fetchUi(entryPath) }
|
||||
.onSuccess { context.setCurrentWidget(it) }
|
||||
.onFailure {
|
||||
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
|
||||
@ -192,6 +238,7 @@ fun main() = application {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(androidx.compose.material3.ExperimentalMaterial3Api::class)
|
||||
@ -201,12 +248,14 @@ private fun BricksHostScreen(
|
||||
actionDispatcher: ActionDispatcher,
|
||||
devModeEnabled: Boolean,
|
||||
onDevModeToggle: () -> Unit,
|
||||
onReload: () -> Unit
|
||||
onReload: () -> Unit,
|
||||
onDisconnect: () -> Unit,
|
||||
serverUrl: String
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("bricks-mp") },
|
||||
title = { Text("bricks-mp ($serverUrl)") },
|
||||
actions = {
|
||||
Button(
|
||||
onClick = onDevModeToggle,
|
||||
@ -214,9 +263,12 @@ private fun BricksHostScreen(
|
||||
) {
|
||||
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")
|
||||
}
|
||||
Button(onClick = onDisconnect) {
|
||||
Text("Disconnect")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user