mirror of
https://github.com/fromchat-messenger/app.git
synced 2026-09-22 19:15:05 +03:00
Fix the payload format
Signed-off-by: denis0001-dev <denis0001.dev@ya.ru>
This commit is contained in:
@@ -9,7 +9,8 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/Theme.FromChat">
|
||||
android:theme="@style/Theme.FromChat"
|
||||
android:usesCleartextTraffic="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
||||
@@ -3,7 +3,8 @@ package ru.fromchat
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.datetime.format.char
|
||||
|
||||
const val API_HOST = "fromchat.ru"
|
||||
const val API_HOST = "http://10.0.2.2:8301"
|
||||
const val WS_API_HOST = "ws://10.0.2.2:8301"
|
||||
|
||||
val DATETIME_FORMAT = LocalDateTime.Format {
|
||||
day()
|
||||
|
||||
@@ -16,10 +16,11 @@ import io.ktor.http.ContentType
|
||||
import io.ktor.http.contentType
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import kotlinx.serialization.json.Json
|
||||
import ru.fromchat.API_HOST
|
||||
import ru.fromchat.utils.failOnError
|
||||
|
||||
object ApiClient {
|
||||
private val json = Json {
|
||||
val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
isLenient = true
|
||||
encodeDefaults = true
|
||||
@@ -53,7 +54,7 @@ object ApiClient {
|
||||
|
||||
suspend fun login(request: LoginRequest) =
|
||||
http
|
||||
.post("https://fromchat.ru/api/login") {
|
||||
.post("$API_HOST/api/login") {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(request.also { Log.d("ApiClient", "Login request: $it") })
|
||||
}
|
||||
@@ -66,7 +67,7 @@ object ApiClient {
|
||||
|
||||
suspend fun register(request: RegisterRequest) =
|
||||
http
|
||||
.post("https://fromchat.ru/api/register") {
|
||||
.post("$API_HOST/api/register") {
|
||||
contentType(ContentType.Application.Json)
|
||||
setBody(request)
|
||||
}
|
||||
@@ -74,7 +75,7 @@ object ApiClient {
|
||||
|
||||
suspend fun getMessages() =
|
||||
http
|
||||
.get("https://fromchat.ru/api/get_messages") {
|
||||
.get("$API_HOST/api/get_messages") {
|
||||
contentType(ContentType.Application.Json)
|
||||
}
|
||||
.failOnError()
|
||||
@@ -82,7 +83,7 @@ object ApiClient {
|
||||
|
||||
suspend fun send(message: String) =
|
||||
http
|
||||
.post("https://fromchat.ru/api/send_message") {
|
||||
.post("$API_HOST/api/send_message") {
|
||||
contentType(ContentType.Application.Json)
|
||||
bearerAuth(token!!)
|
||||
setBody(SendMessageRequest(message))
|
||||
|
||||
@@ -61,7 +61,8 @@ data class Message(
|
||||
|
||||
@Serializable
|
||||
data class SendMessageRequest(
|
||||
val content: String
|
||||
val content: String,
|
||||
val reply_to_id: Int? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
||||
@@ -3,9 +3,8 @@ package ru.fromchat.api
|
||||
import android.util.Log
|
||||
import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession
|
||||
import io.ktor.client.plugins.websocket.webSocket
|
||||
import io.ktor.client.request.url
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.URLProtocol
|
||||
import io.ktor.http.encodedPath
|
||||
import io.ktor.websocket.Frame
|
||||
import io.ktor.websocket.readText
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -20,7 +19,7 @@ import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import kotlinx.serialization.json.Json
|
||||
import ru.fromchat.API_HOST
|
||||
import ru.fromchat.WS_API_HOST
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
object WebSocketManager {
|
||||
@@ -55,16 +54,12 @@ object WebSocketManager {
|
||||
try {
|
||||
ApiClient.http.webSocket(
|
||||
method = HttpMethod.Get,
|
||||
host = API_HOST,
|
||||
request = {
|
||||
url {
|
||||
protocol = URLProtocol.WSS
|
||||
host = "fromchat.ru"
|
||||
encodedPath = "/api/chat/ws"
|
||||
}
|
||||
url("$WS_API_HOST/api/chat/ws")
|
||||
}
|
||||
) {
|
||||
session = this
|
||||
connecting = false
|
||||
|
||||
Log.d("WebSocketManager", "WebSocket connected")
|
||||
for (frame in incoming) {
|
||||
@@ -82,17 +77,31 @@ object WebSocketManager {
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Log.w("WebSocketManager", "An error occurred:", e)
|
||||
connecting = false
|
||||
session = null
|
||||
delay(3000)
|
||||
} finally {
|
||||
Log.w("WebSocketManager", "WebSocket disconnected")
|
||||
session = null
|
||||
connecting = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun send(message: WebSocketMessage) {
|
||||
session?.send(Frame.Text(json.encodeToString(message)))
|
||||
val session = session
|
||||
if (session != null) {
|
||||
try {
|
||||
session.send(Frame.Text(json.encodeToString(message)))
|
||||
} catch (e: Exception) {
|
||||
Log.e("WebSocketManager", "Failed to send message", e)
|
||||
throw e
|
||||
}
|
||||
} else {
|
||||
Log.w("WebSocketManager", "Cannot send message: no active session")
|
||||
throw IllegalStateException("No active WebSocket session")
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
@@ -101,12 +110,21 @@ object WebSocketManager {
|
||||
var handler: ((WebSocketMessage) -> Unit)? = null
|
||||
|
||||
return try {
|
||||
// Check if we have a valid session before sending
|
||||
if (session == null) {
|
||||
Log.w("WebSocketManager", "No active WebSocket session")
|
||||
return null
|
||||
}
|
||||
|
||||
send(message)
|
||||
withTimeout(timeoutMs) {
|
||||
suspendCoroutine { continuation ->
|
||||
handler = {
|
||||
continuation.resumeWith(Result.success(it))
|
||||
removeGlobalMessageHandler(handler!!)
|
||||
handler = { response ->
|
||||
// Only process responses that match our request type
|
||||
if (response.type == message.type) {
|
||||
continuation.resumeWith(Result.success(response))
|
||||
removeGlobalMessageHandler(handler!!)
|
||||
}
|
||||
}
|
||||
addGlobalMessageHandler(handler)
|
||||
}
|
||||
@@ -114,6 +132,9 @@ object WebSocketManager {
|
||||
} catch (_: TimeoutCancellationException) {
|
||||
Log.w("WebSocketManager", "Request timed out")
|
||||
null
|
||||
} catch (e: Exception) {
|
||||
Log.e("WebSocketManager", "Request failed", e)
|
||||
null
|
||||
} finally {
|
||||
handler?.let { removeGlobalMessageHandler(it) }
|
||||
}
|
||||
|
||||
@@ -67,7 +67,6 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.format
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
import kotlinx.serialization.json.encodeToJsonElement
|
||||
import ru.fromchat.DATETIME_FORMAT
|
||||
@@ -75,7 +74,6 @@ import ru.fromchat.R
|
||||
import ru.fromchat.api.ApiClient
|
||||
import ru.fromchat.api.Message
|
||||
import ru.fromchat.api.SendMessageRequest
|
||||
import ru.fromchat.api.SendMessageResponse
|
||||
import ru.fromchat.api.WebSocketCredentials
|
||||
import ru.fromchat.api.WebSocketManager
|
||||
import ru.fromchat.api.WebSocketMessage
|
||||
@@ -116,8 +114,12 @@ fun PublicChatScreen() {
|
||||
scope.launch {
|
||||
if (msg.type == "newMessage") {
|
||||
try {
|
||||
messages += Json.decodeFromJsonElement<Message>(msg.data!!)
|
||||
scrollDown()
|
||||
val newMessage = ApiClient.json.decodeFromJsonElement<Message>(msg.data!!)
|
||||
// Check if message already exists to prevent duplicates
|
||||
if (messages.none { it.id == newMessage.id }) {
|
||||
messages += newMessage
|
||||
scrollDown()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("PublicChatScreen", "Failed to process incoming message:", e)
|
||||
}
|
||||
@@ -188,29 +190,36 @@ fun PublicChatScreen() {
|
||||
IconButton(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
runCatching {
|
||||
Json.decodeFromJsonElement<SendMessageResponse>(
|
||||
WebSocketManager.request(
|
||||
WebSocketMessage(
|
||||
type = "sendMessage",
|
||||
credentials = WebSocketCredentials(
|
||||
scheme = "Bearer",
|
||||
credentials = ApiClient.token!!
|
||||
),
|
||||
data = Json.encodeToJsonElement(
|
||||
SendMessageRequest(
|
||||
content = "${message.text}"
|
||||
)
|
||||
val messageText = "${message.text.trim()}"
|
||||
if (messageText.isEmpty()) return@launch
|
||||
|
||||
try {
|
||||
val response = WebSocketManager.request(
|
||||
WebSocketMessage(
|
||||
type = "sendMessage",
|
||||
credentials = WebSocketCredentials(
|
||||
scheme = "Bearer",
|
||||
credentials = ApiClient.token!!
|
||||
),
|
||||
data = ApiClient.json.encodeToJsonElement(
|
||||
SendMessageRequest(
|
||||
content = messageText
|
||||
)
|
||||
)
|
||||
)!!.data!!
|
||||
)
|
||||
)
|
||||
}.getOrNull()?.let {
|
||||
messages += it.message
|
||||
scrollDown()
|
||||
|
||||
if (response != null && response.error == null) {
|
||||
message.setTextAndPlaceCursorAtEnd("")
|
||||
Log.d("PublicChatScreen", "Message sent successfully")
|
||||
// Message will be added to the list via WebSocket "newMessage" event
|
||||
} else {
|
||||
Log.e("PublicChatScreen", "WebSocket error: ${response?.error}")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("PublicChatScreen", "Failed to send message", e)
|
||||
// Could show a toast or error message to user here
|
||||
}
|
||||
|
||||
message.setTextAndPlaceCursorAtEnd("")
|
||||
}
|
||||
}
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user