support for unknown message content types (#395)

* android: parse/serialize unknown chat items

* ios: more resilient decoding of MsgContent

* core: preserve JSON of unknown message content type in MCUknown, so it can be parsed once it is supported by the client
This commit is contained in:
Evgeny Poberezkin
2022-03-03 08:32:25 +00:00
committed by GitHub
parent b10b3a3434
commit c47a7d78fe
5 changed files with 72 additions and 29 deletions

View File

@@ -45,6 +45,7 @@ android {
freeCompilerArgs += "-opt-in=androidx.compose.material.ExperimentalMaterialApi"
freeCompilerArgs += "-opt-in=com.google.accompanist.insets.ExperimentalAnimatedInsets"
freeCompilerArgs += "-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi"
freeCompilerArgs += "-opt-in=kotlinx.serialization.InternalSerializationApi"
}
externalNativeBuild {
cmake {

View File

@@ -10,8 +10,12 @@ import androidx.compose.ui.text.style.TextDecoration
import chat.simplex.app.ui.theme.SecretColor
import chat.simplex.app.ui.theme.SimplexBlue
import kotlinx.datetime.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.*
import kotlinx.serialization.builtins.IntArraySerializer
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.*
import kotlinx.serialization.modules.SerializersModule
class ChatModel(val controller: ChatController) {
var currentUser = mutableStateOf<User?>(null)
@@ -587,14 +591,57 @@ sealed class CIContent {
}
}
@Serializable
@Serializable(with = MsgContentSerializer::class)
sealed class MsgContent {
abstract val text: String
abstract val cmdString: String
@Serializable @SerialName("text")
class MCText(override val text: String): MsgContent() {
override val cmdString get() = "text $text"
class MCText(override val text: String): MsgContent()
class MCUnknown(val type: String? = null, override val text: String, val json: JsonElement): MsgContent()
val cmdString: String get() = when (this) {
is MCText -> "text $text"
is MCUnknown -> "json $json"
}
}
object MsgContentSerializer : KSerializer<MsgContent> {
override val descriptor: SerialDescriptor = buildSerialDescriptor("MsgContent", PolymorphicKind.SEALED) {
element("MCText", buildClassSerialDescriptor("MCText") {
element<String>("text")
})
element("MCUnknown", buildClassSerialDescriptor("MCUnknown"))
}
override fun deserialize(decoder: Decoder): MsgContent {
require(decoder is JsonDecoder)
val json = decoder.decodeJsonElement()
return if (json is JsonObject) {
if ("type" in json) {
val t = json["type"]?.jsonPrimitive?.content ?: ""
val text = json["text"]?.jsonPrimitive?.content ?: "unknown message format"
when (t) {
"text" -> MsgContent.MCText(text)
else -> MsgContent.MCUnknown(t, text, json)
}
} else {
MsgContent.MCUnknown(text = "invalid message format", json = json)
}
} else {
MsgContent.MCUnknown(text = "invalid message format", json = json)
}
}
override fun serialize(encoder: Encoder, value: MsgContent) {
require(encoder is JsonEncoder)
val json = when (value) {
is MsgContent.MCText ->
buildJsonObject {
put("type", "text")
put("text", value.text)
}
is MsgContent.MCUnknown -> value.json
}
encoder.encodeJsonElement(json)
}
}

View File

@@ -625,15 +625,14 @@ struct RcvFileTransfer: Decodable {
enum MsgContent {
case text(String)
// TODO include original JSON, possibly using https://github.com/zoul/generic-json-swift
case unknown(type: String, text: String)
case invalid(error: String)
var text: String {
get {
switch self {
case let .text(text): return text
case let .unknown(_, text): return text
case .invalid: return "invalid"
}
}
}
@@ -655,8 +654,8 @@ enum MsgContent {
extension MsgContent: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(String.self, forKey: CodingKeys.type)
switch type {
case "text":
@@ -667,7 +666,7 @@ extension MsgContent: Decodable {
self = .unknown(type: type, text: text ?? "unknown message format")
}
} catch {
self = .invalid(error: String(describing: error))
self = .unknown(type: "unknown", text: "invalid message format")
}
}
}