feat(dm): WebSocket 계약 모델을 추가한다

This commit is contained in:
2026-06-18 17:41:31 +09:00
parent 0e03a1a14a
commit e76562067f
2 changed files with 283 additions and 0 deletions

View File

@@ -0,0 +1,115 @@
package kr.co.vividnext.sodalive.v2.main.chat.dm.data
import androidx.annotation.Keep
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonSyntaxException
import com.google.gson.annotations.SerializedName
@Keep
data class DmChatSocketEnvelope(
@SerializedName("type") val type: String,
@SerializedName("payload") val payload: JsonObject?
)
@Keep
data class DmChatSocketRoomPayload(
@SerializedName("roomId") val roomId: Long
)
@Keep
data class DmChatSocketSendTextPayload(
@SerializedName("roomId") val roomId: Long,
@SerializedName("requestId") val requestId: String,
@SerializedName("textMessage") val textMessage: String
)
@Keep
data class DmChatSocketMessagePayload(
@SerializedName("message") val message: DmChatMessageResponse
)
@Keep
data class DmChatSocketSendAckPayload(
@SerializedName("requestId") val requestId: String,
@SerializedName("message") val message: DmChatMessageResponse
)
@Keep
data class DmChatSocketErrorPayload(
@SerializedName("requestId") val requestId: String?,
@SerializedName("code") val code: String?,
@SerializedName("message") val message: String?
)
enum class DmChatSocketClientType(val value: String) {
JOIN_ROOM("JOIN_ROOM"),
LEAVE_ROOM("LEAVE_ROOM"),
SEND_TEXT("SEND_TEXT"),
PING("PING")
}
sealed class DmChatSocketEvent {
data object Joined : DmChatSocketEvent()
data class Message(val message: DmChatMessageResponse) : DmChatSocketEvent()
data class SendAck(
val requestId: String,
val message: DmChatMessageResponse
) : DmChatSocketEvent()
data class Error(
val requestId: String?,
val code: String?,
val message: String?
) : DmChatSocketEvent()
data object Pong : DmChatSocketEvent()
}
class DmChatSocketParser(private val gson: Gson) {
fun parse(text: String): DmChatSocketEvent? = try {
val envelope = gson.fromJson(text, DmChatSocketEnvelope::class.java)
when (envelope.type) {
TYPE_JOINED -> DmChatSocketEvent.Joined
TYPE_MESSAGE -> parseMessage(envelope.payload)
TYPE_SEND_ACK -> parseSendAck(envelope.payload)
TYPE_ERROR -> parseError(envelope.payload)
TYPE_PONG -> DmChatSocketEvent.Pong
else -> null
}
} catch (e: JsonSyntaxException) {
null
} catch (e: IllegalStateException) {
null
} catch (e: NullPointerException) {
null
}
private fun parseMessage(payload: JsonObject?): DmChatSocketEvent.Message? {
val messagePayload = gson.fromJson(payload, DmChatSocketMessagePayload::class.java) ?: return null
return DmChatSocketEvent.Message(messagePayload.message)
}
private fun parseSendAck(payload: JsonObject?): DmChatSocketEvent.SendAck? {
val ackPayload = gson.fromJson(payload, DmChatSocketSendAckPayload::class.java) ?: return null
return DmChatSocketEvent.SendAck(
requestId = ackPayload.requestId,
message = ackPayload.message
)
}
private fun parseError(payload: JsonObject?): DmChatSocketEvent.Error? {
val errorPayload = gson.fromJson(payload, DmChatSocketErrorPayload::class.java) ?: return null
return DmChatSocketEvent.Error(
requestId = errorPayload.requestId,
code = errorPayload.code,
message = errorPayload.message
)
}
private companion object {
const val TYPE_JOINED = "JOINED"
const val TYPE_MESSAGE = "MESSAGE"
const val TYPE_SEND_ACK = "SEND_ACK"
const val TYPE_ERROR = "ERROR"
const val TYPE_PONG = "PONG"
}
}