From 829d41a70ae721e03e5a152d954cde4228207b7c Mon Sep 17 00:00:00 2001 From: csasq Date: Thu, 29 Aug 2024 18:05:59 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=B0=D0=BD=D1=8B=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20=D1=81=20API=20=D1=81=D1=86=D0=B5=D0=BD=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cit_is_bot/ui/dialogs/ScenarioDialog.kt | 84 ++++++++++++++----- .../cit_is_bot/ui/screens/ScenariosScreen.kt | 58 +++++++++++-- 2 files changed, 113 insertions(+), 29 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/ru/csasq/cit_is_bot/ui/dialogs/ScenarioDialog.kt b/composeApp/src/commonMain/kotlin/ru/csasq/cit_is_bot/ui/dialogs/ScenarioDialog.kt index ad14623..cd2cde2 100644 --- a/composeApp/src/commonMain/kotlin/ru/csasq/cit_is_bot/ui/dialogs/ScenarioDialog.kt +++ b/composeApp/src/commonMain/kotlin/ru/csasq/cit_is_bot/ui/dialogs/ScenarioDialog.kt @@ -57,6 +57,8 @@ import cit_is_bot.composeapp.generated.resources.baseline_title_24 import cit_is_bot.composeapp.generated.resources.outline_adjust_24 import cit_is_bot.composeapp.generated.resources.outline_time_auto_24 import io.ktor.client.HttpClient +import io.ktor.client.request.delete +import io.ktor.client.request.post import io.ktor.client.request.put import io.ktor.client.request.setBody import io.ktor.http.ContentType @@ -87,7 +89,7 @@ enum class MessageType( fun ScenarioDialog( scenarioDialogState: MutableState, client: HttpClient, - onSuccess: () -> Unit, + onSuccess: (message: String) -> Unit, properties: DialogProperties = DialogProperties( usePlatformDefaultWidth = false, ), @@ -155,24 +157,43 @@ fun ScenarioDialog( scenarioDialogState.value?.name = nameState.value!! coroutine.launch { progressIndicatorState.value = true - val url = URLBuilder( - protocol = URLProtocol.HTTPS, - host = "cit.csasq.ru", - pathSegments = listOf( - "api", - "scripts", - scenarioDialogState.value!!.id.toString(), - ) - ) - val response = client.put(url.build()) { - contentType(ContentType.Application.Json) - setBody(scenarioDialogState.value) - } - progressIndicatorState.value = false - when (response.status) { - HttpStatusCode.Created -> { - onSuccess() - scenarioDialogState.value = null + when(scenarioDialogState.value!!.id) { + null -> { + val url = URLBuilder( + protocol = URLProtocol.HTTPS, + host = "cit.csasq.ru", + pathSegments = listOf( + "api", + "scripts", + ), + ) + client.post(url.build()) { + contentType(ContentType.Application.Json) + setBody(scenarioDialogState.value) + } + } + else -> { + val url = URLBuilder( + protocol = URLProtocol.HTTPS, + host = "cit.csasq.ru", + pathSegments = listOf( + "api", + "scripts", + scenarioDialogState.value!!.id.toString(), + ), + ) + client.put(url.build()) { + contentType(ContentType.Application.Json) + setBody(scenarioDialogState.value) + } + } + }.also { response -> + progressIndicatorState.value = false + when (response.status) { + HttpStatusCode.Created -> { + onSuccess("Сценарий сохранен") + scenarioDialogState.value = null + } } } } @@ -209,7 +230,30 @@ fun ScenarioDialog( ) }, onClick = { - dropdownMenuState.value = false + coroutine.launch { + dropdownMenuState.value = false + progressIndicatorState.value = true + val url = URLBuilder( + protocol = URLProtocol.HTTPS, + host = "cit.csasq.ru", + pathSegments = listOf( + "api", + "scripts", + scenarioDialogState.value!!.id.toString(), + ) + ) + val response = client.delete(url.build()) { + contentType(ContentType.Application.Json) + setBody(scenarioDialogState.value) + } + progressIndicatorState.value = false + when (response.status) { + HttpStatusCode.NoContent -> { + onSuccess("Сценарий удален") + scenarioDialogState.value = null + } + } + } }, ) } diff --git a/composeApp/src/commonMain/kotlin/ru/csasq/cit_is_bot/ui/screens/ScenariosScreen.kt b/composeApp/src/commonMain/kotlin/ru/csasq/cit_is_bot/ui/screens/ScenariosScreen.kt index 550a56f..9ad2b8c 100644 --- a/composeApp/src/commonMain/kotlin/ru/csasq/cit_is_bot/ui/screens/ScenariosScreen.kt +++ b/composeApp/src/commonMain/kotlin/ru/csasq/cit_is_bot/ui/screens/ScenariosScreen.kt @@ -27,11 +27,18 @@ import cit_is_bot.composeapp.generated.resources.baseline_add_24 import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.plugins.websocket.WebSockets +import io.ktor.client.plugins.websocket.receiveDeserialized +import io.ktor.client.plugins.websocket.webSocket import io.ktor.client.request.get import io.ktor.http.HttpStatusCode +import io.ktor.http.URLBuilder +import io.ktor.http.URLProtocol +import io.ktor.serialization.kotlinx.KotlinxWebsocketSerializationConverter import io.ktor.serialization.kotlinx.json.json import kotlinx.coroutines.launch import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json import org.jetbrains.compose.resources.painterResource import ru.csasq.cit_is_bot.ui.components.SwitchButton import ru.csasq.cit_is_bot.ui.dialogs.ScenarioDialog @@ -45,6 +52,12 @@ data class Scenario( var isEnabled: Boolean, ) +@Serializable +data class WebSocketAction( + val action: String, + val target: Scenario, +) + @Composable fun ScenariosScreen( navController: NavController, @@ -53,6 +66,8 @@ fun ScenariosScreen( snackbarHostState: SnackbarHostState, floatingActionButtonState: MutableState<(@Composable () -> Unit)?>, ) { + val webSocketCoroutineScope = rememberCoroutineScope() + val httpRequestCoroutineScope = rememberCoroutineScope() val snackbarCoroutineScope = rememberCoroutineScope() val verticalScrollState = rememberScrollState() val floatingActionButtonExpandedState = remember { @@ -71,24 +86,49 @@ fun ScenariosScreen( mutableStateListOf() } val client = HttpClient { + install(WebSockets) { + contentConverter = KotlinxWebsocketSerializationConverter(Json) + } install(ContentNegotiation) { json() } -// install(WebSockets) { -// contentConverter = KotlinxWebsocketSerializationConverter(Json) -// } } LaunchedEffect(false) { - client.launch { + webSocketCoroutineScope.launch { + val url = URLBuilder( + protocol = URLProtocol.WSS, + host = "cit.csasq.ru", + pathSegments = listOf( + "ws", + ), + ) + client.webSocket(url.buildString()) { + while (true) { + val webSocketAction = receiveDeserialized() + val scenarioIndex = scenarioList.indexOfFirst { scenario -> + scenario.id == webSocketAction.target.id + } + when (webSocketAction.action) { + "insert" -> { + scenarioList.add(webSocketAction.target) + } + "update" -> { + scenarioList[scenarioIndex] = webSocketAction.target + } + "delete" -> { + scenarioList.removeAt(scenarioIndex) + } + } + } + } + } + httpRequestCoroutineScope.launch { progressIndicatorState.value = true val response = client.get("https://cit.csasq.ru/api/scripts") progressIndicatorState.value = false when (response.status) { HttpStatusCode.OK -> scenarioList.addAll(response.body()) } -// client.webSocket("wss://cit.csasq.ru/ws/scripts") { -// scenarioList.addAll(receiveDeserialized>()) -// } } } Column( @@ -155,10 +195,10 @@ fun ScenariosScreen( ScenarioDialog( scenarioDialogState = scenarioDialogState, client = client, - onSuccess = { + onSuccess = { message -> snackbarCoroutineScope.launch { snackbarHostState.showSnackbar( - message = "Сценарий сохранен", + message = message, duration = SnackbarDuration.Short, ) }