From a0063d7ea322b73be3aa9ebe50f7560453fc562f Mon Sep 17 00:00:00 2001 From: StefanMoCoAt Date: Wed, 10 Sep 2025 21:24:59 +0200 Subject: [PATCH] Ping Frontend Desktop und WasmJs funktionieren --- client/build.gradle.kts | 12 ++ .../drawable/compose-multiplatform.xml | 86 +++++++----- client/src/commonMain/kotlin/at/mocode/App.kt | 132 ++++++++++++++++++ .../mocode/temp/pingservice/PingController.kt | 2 + 4 files changed, 194 insertions(+), 38 deletions(-) diff --git a/client/build.gradle.kts b/client/build.gradle.kts index 8288d45b..20da3102 100644 --- a/client/build.gradle.kts +++ b/client/build.gradle.kts @@ -3,6 +3,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl plugins { alias(libs.plugins.kotlin.multiplatform) + alias(libs.plugins.kotlin.serialization) alias(libs.plugins.compose.multiplatform) alias(libs.plugins.compose.compiler) } @@ -28,6 +29,13 @@ kotlin { implementation(compose.ui) implementation(compose.components.resources) implementation(compose.components.uiToolingPreview) + + // HTTP client dependencies for ping-service + implementation(libs.ktor.client.core) + implementation(libs.ktor.client.contentNegotiation) + implementation(libs.ktor.client.serialization.kotlinx.json) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.serialization.json) } commonTest.dependencies { implementation(libs.kotlin.test) @@ -35,6 +43,10 @@ kotlin { jvmMain.dependencies { implementation(compose.desktop.currentOs) implementation(libs.kotlinx.coroutines.swing) + implementation(libs.ktor.client.cio) + } + wasmJsMain.dependencies { + implementation(libs.ktor.client.js) } } } diff --git a/client/src/commonMain/composeResources/drawable/compose-multiplatform.xml b/client/src/commonMain/composeResources/drawable/compose-multiplatform.xml index 1ffc948c..704b517f 100644 --- a/client/src/commonMain/composeResources/drawable/compose-multiplatform.xml +++ b/client/src/commonMain/composeResources/drawable/compose-multiplatform.xml @@ -4,41 +4,51 @@ android:width="450dp" android:height="450dp" android:viewportWidth="64" - android:viewportHeight="64"> - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + android:viewportHeight="64" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://schemas.android.com/aapt "> + + + + + + + + + + + + + + + + + + + + diff --git a/client/src/commonMain/kotlin/at/mocode/App.kt b/client/src/commonMain/kotlin/at/mocode/App.kt index 33f5e7a8..79f81661 100644 --- a/client/src/commonMain/kotlin/at/mocode/App.kt +++ b/client/src/commonMain/kotlin/at/mocode/App.kt @@ -7,20 +7,66 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeContentPadding +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import org.jetbrains.compose.ui.tooling.preview.Preview +import io.ktor.client.* +import io.ktor.client.call.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.http.* +import io.ktor.serialization.kotlinx.json.* +import kotlinx.coroutines.launch +import kotlinx.serialization.Serializable + +@Serializable +data class PingResponse( + val status: String, + val timestamp: String? = null, + val message: String? = null +) + +sealed class PingState { + object Idle : PingState() + object Loading : PingState() + data class Success(val response: PingResponse) : PingState() + data class Error(val message: String) : PingState() +} @Composable @Preview fun App() { MaterialTheme { var showContent by remember { mutableStateOf(false) } + var pingState by remember { mutableStateOf(PingState.Idle) } + val coroutineScope = rememberCoroutineScope() + + // Create HTTP client + val httpClient = remember { + HttpClient { + install(ContentNegotiation) { + json() + } + } + } + + // Cleanup client on disposal + DisposableEffect(Unit) { + onDispose { + httpClient.close() + } + } + Column( modifier = Modifier .background(MaterialTheme.colorScheme.background) @@ -35,6 +81,7 @@ fun App() { modifier = Modifier.padding(top = 32.dp, bottom = 16.dp) ) + // Platform Info Button Button( onClick = { showContent = !showContent }, modifier = Modifier.padding(16.dp) @@ -42,6 +89,91 @@ fun App() { Text(if (showContent) "Platform-Info ausblenden" else "Platform-Info anzeigen") } + // Ping Backend Button + Button( + onClick = { + coroutineScope.launch { + pingState = PingState.Loading + try { + val response: PingResponse = httpClient.get("http://localhost:8082/ping").body() + pingState = PingState.Success(response) + } catch (e: Exception) { + pingState = PingState.Error(e.message ?: "Unknown error occurred") + } + } + }, + enabled = pingState !is PingState.Loading, + modifier = Modifier.padding(8.dp) + ) { + if (pingState is PingState.Loading) { + CircularProgressIndicator( + modifier = Modifier.height(16.dp), + color = MaterialTheme.colorScheme.onPrimary + ) + Spacer(modifier = Modifier.height(8.dp)) + } + Text("Ping Backend") + } + + // Ping Status Display + when (val state = pingState) { + is PingState.Success -> { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Column( + modifier = Modifier.padding(16.dp) + ) { + Text( + text = "✅ Ping erfolgreich!", + style = MaterialTheme.typography.titleMedium, + color = Color(0xFF4CAF50) + ) + Text( + text = "Status: ${state.response.status}", + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(top = 4.dp) + ) + state.response.timestamp?.let { + Text( + text = "Zeit: $it", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(top = 2.dp) + ) + } + } + } + } + is PingState.Error -> { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Column( + modifier = Modifier.padding(16.dp) + ) { + Text( + text = "❌ Ping fehlgeschlagen", + style = MaterialTheme.typography.titleMedium, + color = Color(0xFFF44336) + ) + Text( + text = "Fehler: ${state.message}", + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(top = 4.dp) + ) + } + } + } + else -> { + // Idle or Loading state - no additional display needed + } + } + AnimatedVisibility(showContent) { val greeting = remember { Greeting().greet() } Column( diff --git a/temp/ping-service/src/main/kotlin/at/mocode/temp/pingservice/PingController.kt b/temp/ping-service/src/main/kotlin/at/mocode/temp/pingservice/PingController.kt index 548f29e9..35cff732 100644 --- a/temp/ping-service/src/main/kotlin/at/mocode/temp/pingservice/PingController.kt +++ b/temp/ping-service/src/main/kotlin/at/mocode/temp/pingservice/PingController.kt @@ -1,10 +1,12 @@ package at.mocode.temp.pingservice +import org.springframework.web.bind.annotation.CrossOrigin import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @RestController +@CrossOrigin(origins = ["http://localhost:8080"]) class PingController( private val pingService: PingServiceCircuitBreaker ) {