Skip to content

Commit 76f1515

Browse files
start migration to compose in webapp
1 parent 1c43769 commit 76f1515

5 files changed

Lines changed: 145 additions & 99 deletions

File tree

WebApp/build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ buildscript {
1111
plugins {
1212
id "org.jetbrains.kotlin.multiplatform"
1313
id "org.jetbrains.kotlin.plugin.serialization"
14+
15+
id "org.jetbrains.kotlin.plugin.compose" version "$kotlin_version"
16+
id "org.jetbrains.compose" version "$compose_version"
1417
}
1518

1619
apply plugin: 'application'
@@ -27,12 +30,14 @@ kotlin {
2730
dependencies {
2831
implementation kotlin('stdlib')
2932
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version"
33+
implementation compose.runtime
3034
}
3135
}
3236

3337
jsMain {
3438
dependencies {
3539
implementation "dev.inmo:tgbotapi.webapps:$telegram_bot_api_version"
40+
implementation compose.web.core
3641
}
3742
}
3843

@@ -41,6 +46,7 @@ kotlin {
4146
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
4247
implementation "dev.inmo:micro_utils.ktor.server:$micro_utils_version"
4348
implementation "io.ktor:ktor-server-cio:$ktor_version"
49+
implementation compose.desktop.currentOs
4450
}
4551
}
4652
}

WebApp/src/jsMain/kotlin/main.kt

Lines changed: 132 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import androidx.compose.runtime.*
12
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
23
import dev.inmo.tgbotapi.types.webAppQueryIdField
34
import dev.inmo.tgbotapi.webapps.*
@@ -18,6 +19,10 @@ import kotlinx.dom.appendElement
1819
import kotlinx.dom.appendText
1920
import kotlinx.dom.clear
2021
import kotlinx.serialization.json.Json
22+
import org.jetbrains.compose.web.dom.Button
23+
import org.jetbrains.compose.web.dom.P
24+
import org.jetbrains.compose.web.dom.Text
25+
import org.jetbrains.compose.web.renderComposable
2126
import org.w3c.dom.*
2227
import kotlin.random.Random
2328
import kotlin.random.nextUBytes
@@ -33,117 +38,145 @@ fun main() {
3338
val client = HttpClient()
3439
val baseUrl = window.location.origin.removeSuffix("/")
3540

36-
window.onload = {
37-
val scope = CoroutineScope(Dispatchers.Default)
38-
runCatching {
39-
40-
scope.launchSafelyWithoutExceptions {
41-
val response = client.post("$baseUrl/check") {
42-
setBody(
43-
Json.encodeToString(
44-
WebAppDataWrapper.serializer(),
45-
WebAppDataWrapper(webApp.initData, webApp.initDataUnsafe.hash)
46-
)
41+
renderComposable("root") {
42+
val scope = rememberCoroutineScope()
43+
val isSafeState = remember { mutableStateOf<Boolean?>(null) }
44+
val logsState = remember { mutableStateListOf<String>() }
45+
LaunchedEffect(baseUrl) {
46+
val response = client.post("$baseUrl/check") {
47+
setBody(
48+
Json.encodeToString(
49+
WebAppDataWrapper.serializer(),
50+
WebAppDataWrapper(webApp.initData, webApp.initDataUnsafe.hash)
4751
)
48-
}
49-
val dataIsSafe = response.bodyAsText().toBoolean()
50-
51-
document.body ?.log(
52-
if (dataIsSafe) {
53-
"Data is safe"
54-
} else {
55-
"Data is unsafe"
56-
}
57-
)
58-
59-
document.body ?.log(
60-
webApp.initDataUnsafe.chat.toString()
6152
)
6253
}
54+
val dataIsSafe = response.bodyAsText().toBoolean()
6355

64-
document.body ?.appendElement("button") {
65-
addEventListener("click", {
66-
scope.launchSafelyWithoutExceptions {
67-
handleResult({ "Clicked" }) {
68-
client.post("${window.location.origin.removeSuffix("/")}/inline") {
69-
parameter(webAppQueryIdField, it)
70-
setBody(TextContent("Clicked", ContentType.Text.Plain))
71-
document.body ?.log(url.build().toString())
72-
}.coroutineContext.job.join()
73-
}
74-
}
75-
})
76-
appendText("Answer in chat button")
77-
} ?: window.alert("Unable to load body")
78-
79-
document.body ?.appendElement("p", {})
80-
document.body ?.appendText("Allow to write in private messages: ${webApp.initDataUnsafe.user ?.allowsWriteToPM ?: "User unavailable"}")
81-
82-
document.body ?.appendElement("p", {})
83-
document.body ?.appendText("Alerts:")
56+
document.body ?.log(
57+
if (dataIsSafe) {
58+
"Data is safe"
59+
} else {
60+
"Data is unsafe"
61+
}
62+
)
8463

85-
document.body ?.appendElement("button") {
86-
addEventListener("click", {
87-
webApp.showPopup(
88-
PopupParams(
89-
"It is sample title of default button",
90-
"It is sample message of default button",
91-
DefaultPopupButton("default", "Default button"),
92-
OkPopupButton("ok"),
93-
DestructivePopupButton("destructive", "Destructive button")
94-
)
95-
) {
96-
document.body ?.log(
97-
when (it) {
98-
"default" -> "You have clicked default button in popup"
99-
"ok" -> "You have clicked ok button in popup"
100-
"destructive" -> "You have clicked destructive button in popup"
101-
else -> "I can't imagine where you take button with id $it"
102-
}
103-
)
104-
}
105-
})
106-
appendText("Popup")
107-
} ?: window.alert("Unable to load body")
64+
document.body ?.log(
65+
webApp.initDataUnsafe.chat.toString()
66+
)
67+
}
10868

109-
document.body ?.appendElement("button") {
110-
addEventListener("click", {
111-
webApp.showAlert(
112-
"This is alert message"
113-
) {
114-
document.body ?.log(
115-
"You have closed alert"
116-
)
69+
Text(
70+
when (isSafeState.value) {
71+
null -> "Checking safe state..."
72+
true -> "Data is safe"
73+
false -> "Data is unsafe"
74+
}
75+
)
76+
Text(webApp.initDataUnsafe.chat.toString())
77+
78+
Button({
79+
onClick {
80+
scope.launchSafelyWithoutExceptions {
81+
handleResult({ "Clicked" }) {
82+
client.post("${window.location.origin.removeSuffix("/")}/inline") {
83+
parameter(webAppQueryIdField, it)
84+
setBody(TextContent("Clicked", ContentType.Text.Plain))
85+
logsState.add(url.build().toString())
86+
}.coroutineContext.job.join()
11787
}
118-
})
119-
appendText("Alert")
120-
} ?: window.alert("Unable to load body")
121-
122-
document.body ?.appendElement("p", {})
123-
124-
document.body ?.appendElement("button") {
125-
addEventListener("click", { webApp.requestWriteAccess() })
126-
appendText("Request write access without callback")
127-
} ?: window.alert("Unable to load body")
88+
}
89+
}
90+
}) {
91+
Text("Answer in chat button")
92+
}
12893

129-
document.body ?.appendElement("button") {
130-
addEventListener("click", { webApp.requestWriteAccess { document.body ?.log("Write access request result: $it") } })
131-
appendText("Request write access with callback")
132-
} ?: window.alert("Unable to load body")
94+
P()
95+
Text("Allow to write in private messages: ${webApp.initDataUnsafe.user ?.allowsWriteToPM ?: "User unavailable"}")
96+
97+
P()
98+
Text("Alerts:")
99+
Button({
100+
onClick {
101+
webApp.showPopup(
102+
PopupParams(
103+
"It is sample title of default button",
104+
"It is sample message of default button",
105+
DefaultPopupButton("default", "Default button"),
106+
OkPopupButton("ok"),
107+
DestructivePopupButton("destructive", "Destructive button")
108+
)
109+
) {
110+
logsState.add(
111+
when (it) {
112+
"default" -> "You have clicked default button in popup"
113+
"ok" -> "You have clicked ok button in popup"
114+
"destructive" -> "You have clicked destructive button in popup"
115+
else -> "I can't imagine where you take button with id $it"
116+
}
117+
)
118+
}
119+
}
120+
}) {
121+
Text("Popup")
122+
}
123+
Button({
124+
onClick {
125+
webApp.showAlert(
126+
"This is alert message"
127+
) {
128+
logsState.add(
129+
"You have closed alert"
130+
)
131+
}
132+
}
133+
}) {
134+
Text("Alert")
135+
}
133136

134-
document.body ?.appendElement("p", {})
137+
P()
138+
Button({
139+
onClick {
140+
webApp.requestWriteAccess()
141+
}
142+
}) {
143+
Text("Request write access without callback")
144+
}
145+
Button({
146+
onClick {
147+
webApp.requestWriteAccess {
148+
logsState.add("Write access request result: $it")
149+
}
150+
}
151+
}) {
152+
Text("Request write access with callback")
153+
}
135154

136-
document.body ?.appendElement("button") {
137-
addEventListener("click", { webApp.requestContact() })
138-
appendText("Request contact without callback")
139-
} ?: window.alert("Unable to load body")
155+
P()
156+
Button({
157+
onClick {
158+
webApp.requestContact()
159+
}
160+
}) {
161+
Text("Request contact without callback")
162+
}
163+
Button({
164+
onClick {
165+
webApp.requestContact { logsState.add("Contact request result: $it") }
166+
}
167+
}) {
168+
Text("Request contact with callback")
169+
}
170+
P()
140171

141-
document.body ?.appendElement("button") {
142-
addEventListener("click", { webApp.requestContact { document.body ?.log("Contact request result: $it") } })
143-
appendText("Request contact with callback")
144-
} ?: window.alert("Unable to load body")
172+
logsState.forEach {
173+
P { Text(it) }
174+
}
175+
}
145176

146-
document.body ?.appendElement("p", {})
177+
window.onload = {
178+
val scope = CoroutineScope(Dispatchers.Default)
179+
runCatching {
147180

148181
document.body ?.appendElement("button") {
149182
addEventListener("click", {

WebApp/src/jsMain/resources/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<title>Web App Example</title>
1111
</head>
1212
<body>
13+
<div id="root"></div>
1314
<script type="application/javascript" src="https://telegram.org/js/telegram-web-app.js"></script>
1415
<script type="application/javascript" src="WebApp.js"></script>
1516
</body>

build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,8 @@ allprojects {
2929
maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
3030
}
3131
}
32+
33+
// Fix of https://youtrack.jetbrains.com/issue/KTOR-7912/Module-not-found-errors-when-executing-browserProductionWebpack-task-since-3.0.2
34+
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin.class) {
35+
rootProject.kotlinYarn.resolution("ws", "8.18.0")
36+
}

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ telegram_bot_api_version=22.0.0
1010
micro_utils_version=0.23.2
1111
serialization_version=1.7.3
1212
ktor_version=3.0.2
13+
compose_version=1.7.1

0 commit comments

Comments
 (0)