1 Commits

Author SHA1 Message Date
922d31b7df v1.0.5: Fix history viewer - ContentConverter JsonElement type bug
- ContentConverter.tipTapToHtml() had same JsonObject.asMap() bug as tiptapToMarkdown
- All as? String casts silently returned null, producing empty HTML
- Now uses Gson TypeToken<Map<String, Any?>> for proper deserialization
- Added contentToHtml() that handles String (HTML), Map (TipTap), and JSON string
- Fixed heading level cast (Gson deserializes numbers as Double, not Int)
- VersionViewerActivity handles all content types correctly
2026-07-27 17:39:48 -06:00
3 changed files with 55 additions and 16 deletions

View File

@@ -20,8 +20,8 @@ android {
applicationId = "com.docmost.app" applicationId = "com.docmost.app"
minSdk = 26 minSdk = 26
targetSdk = 34 targetSdk = 34
versionCode = 5 versionCode = 6
versionName = "1.0.4" versionName = "1.0.5"
} }
signingConfigs { signingConfigs {

View File

@@ -109,13 +109,26 @@ class VersionViewerActivity : AppCompatActivity() {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
val history = api.getPageHistoryInfo(pageId, historyId) val history = api.getPageHistoryInfo(pageId, historyId)
val htmlContent = if (history.content != null) { val htmlContent: String
val gson = Gson() if (history.content != null) {
val json = gson.toJson(history.content) val content = history.content
originalTipTapJson = json if (content is String && content.trimStart().startsWith("<")) {
ContentConverter.tipTapToHtml(json) htmlContent = content
originalTipTapJson = ""
} else if (content is Map<*, *>) {
val gson = Gson()
originalTipTapJson = gson.toJson(content)
htmlContent = ContentConverter.contentToHtml(content)
} else if (content is String) {
originalTipTapJson = content
htmlContent = ContentConverter.tipTapToHtml(content)
} else {
htmlContent = "<p>Sin contenido</p>"
originalTipTapJson = ""
}
} else { } else {
"<p>Sin contenido</p>" htmlContent = "<p>Sin contenido</p>"
originalTipTapJson = ""
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {

View File

@@ -1,16 +1,16 @@
package com.docmost.app.utils package com.docmost.app.utils
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.JsonObject import com.google.gson.reflect.TypeToken
object ContentConverter { object ContentConverter {
private val gson = Gson() private val gson = Gson()
fun tipTapJsonToHtml(tipTapJson: String): String { fun tipTapJsonToHtml(tipTapJson: String): String {
return try { return try {
val doc = gson.fromJson(tipTapJson, JsonObject::class.java).asMap() val type = object : TypeToken<Map<String, Any?>>() {}.type
@Suppress("UNCHECKED_CAST") val doc: Map<String, Any?> = gson.fromJson(tipTapJson, type)
nodeToHtml(doc as Map<String, Any?>) nodeToHtml(doc)
} catch (e: Exception) { } catch (e: Exception) {
tipTapJson tipTapJson
} }
@@ -18,14 +18,34 @@ object ContentConverter {
fun tipTapToHtml(tipTapJson: String): String { fun tipTapToHtml(tipTapJson: String): String {
return try { return try {
val doc = gson.fromJson(tipTapJson, JsonObject::class.java).asMap() val type = object : TypeToken<Map<String, Any?>>() {}.type
@Suppress("UNCHECKED_CAST") val doc: Map<String, Any?> = gson.fromJson(tipTapJson, type)
nodeToHtml(doc as Map<String, Any?>) nodeToHtml(doc)
} catch (e: Exception) { } catch (e: Exception) {
tipTapJson tipTapJson
} }
} }
fun contentToHtml(content: Any?): String {
if (content == null) return "<p>Sin contenido</p>"
if (content is String) {
return if (content.trimStart().startsWith("<")) {
content
} else {
tipTapToHtml(content)
}
}
if (content is Map<*, *>) {
@Suppress("UNCHECKED_CAST")
return try {
nodeToHtml(content as Map<String, Any?>)
} catch (e: Exception) {
"<p>Error converting content</p>"
}
}
return "<p>Sin contenido</p>"
}
private fun nodeToHtml(node: Map<String, Any?>): String { private fun nodeToHtml(node: Map<String, Any?>): String {
val type = node["type"] as? String val type = node["type"] as? String
@@ -74,7 +94,13 @@ object ContentConverter {
sb.append("</p>") sb.append("</p>")
} }
"heading" -> { "heading" -> {
val level = (node["attrs"] as? Map<*, *>)?.get("level") as? Int ?: 1 val levelRaw = (node["attrs"] as? Map<*, *>)?.get("level")
val level = when (levelRaw) {
is Int -> levelRaw
is Double -> levelRaw.toInt()
is Number -> levelRaw.toInt()
else -> 1
}
sb.append("<h$level>") sb.append("<h$level>")
content?.forEach { child -> content?.forEach { child ->
if (child is Map<*, *>) { if (child is Map<*, *>) {