Compare commits

..
2 Commits
Author SHA1 Message Date
Amirmahdi dd5cbaa3bc feat: clean text 2026-05-27 20:42:39 +03:30
Amirmahdi e65c699bcb feat: clean text 2026-05-27 20:42:16 +03:30
2 changed files with 218 additions and 134 deletions
@@ -535,23 +535,94 @@ class DrugDetailParser {
* Converts block elements to newlines for better readability. * Converts block elements to newlines for better readability.
*/ */
private fun cleanText(element: Element): String { private fun cleanText(element: Element): String {
// Clone to avoid affecting the original
val clone = element.clone() val clone = element.clone()
val output = StringBuilder()
// Replace block-level elements with newlines for structure // Process all nodes in order (both elements and text nodes)
clone.select("p, div, h2, h3, h4, li, br").before("\n") processNode(clone, output)
// Get the text and clean it up return output.toString()
var text = clone.text() .replace(Regex("\\n{3,}"), "\n\n") // Collapse 3+ newlines to 2
.replace(Regex("\\n\\s*\\n+"), "\n\n") // Collapse multiple newlines
.replace(Regex("[ \\t]+"), " ") // Collapse spaces/tabs
.trim() .trim()
// Ensure lists look decent
if (element.tagName() == "li") {
text = "$text"
} }
return text /**
* Recursively process nodes to build formatted text
*/
private fun processNode(node: org.jsoup.nodes.Node, output: StringBuilder) {
when (node) {
is Element -> {
when (node.tagName()) {
"h2" -> {
val text = node.text().trim()
if (text.isNotEmpty()) {
output.append("\n\n📌 $text\n\n")
output.append("".repeat(minOf(30, text.length))).append("\n\n")
}
}
"h3" -> {
val text = node.text().trim()
if (text.isNotEmpty()) {
output.append("\n\n🔹 $text\n\n")
}
}
"h4" -> {
val text = node.text().trim()
if (text.isNotEmpty()) {
output.append("\n$text\n")
}
}
"p" -> {
val text = node.text().trim()
if (text.isNotEmpty()) {
output.append(text).append("\n\n")
}
}
"ul", "ol" -> {
for (li in node.select("li")) {
val liText = li.text().trim()
if (liText.isNotEmpty()) {
output.append("$liText\n")
}
}
output.append("\n")
}
"li" -> {
// Handled by ul/ol processing, but if standalone:
val text = node.text().trim()
if (text.isNotEmpty()) {
output.append("$text\n")
}
}
"br" -> {
output.append("\n")
}
"strong", "b" -> {
val text = node.text().trim()
if (text.isNotEmpty()) {
output.append("**$text**")
}
}
"div", "section", "article" -> {
// Process children of container elements
for (child in node.childNodes()) {
processNode(child, output)
}
}
else -> {
// For other elements, just process their children
for (child in node.childNodes()) {
processNode(child, output)
}
}
}
}
is org.jsoup.nodes.TextNode -> {
val text = node.text().trim()
if (text.isNotEmpty()) {
output.append(text)
}
}
}
} }
} }
@@ -25,12 +25,14 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SecondaryTabRow
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow import androidx.compose.material3.TabRow
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
@@ -39,8 +41,10 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -70,6 +74,7 @@ fun DrugDetailScreen(
viewModel.loadDrugDetail(detailUrl) viewModel.loadDrugDetail(detailUrl)
} }
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
@@ -93,6 +98,7 @@ fun DrugDetailScreen(
} }
} }
} }
else -> { else -> {
Text("جزئیات دارو", fontSize = 16.sp) Text("جزئیات دارو", fontSize = 16.sp)
} }
@@ -148,7 +154,10 @@ fun DrugDetailScreen(
ManufacturerInfoCard( ManufacturerInfoCard(
manufacturer = drugDetail.manufacturer!!, manufacturer = drugDetail.manufacturer!!,
genericInfo = drugDetail.genericInfo, genericInfo = drugDetail.genericInfo,
modifier = Modifier.padding(horizontal = dime.md, vertical = dime.sm) modifier = Modifier.padding(
horizontal = dime.md,
vertical = dime.sm
)
) )
} }
@@ -186,12 +195,15 @@ fun DrugDetailScreen(
0 -> { 0 -> {
item { GeneralInfoTabContent(drugDetail) } item { GeneralInfoTabContent(drugDetail) }
} }
1 -> { 1 -> {
item { SpecializedInfoTabContent(drugDetail) } item { SpecializedInfoTabContent(drugDetail) }
} }
2 -> { 2 -> {
item { DosageFormsTabContent(drugDetail) } item { DosageFormsTabContent(drugDetail) }
} }
3 -> { 3 -> {
item { BrandNamesTabContent(drugDetail) } item { BrandNamesTabContent(drugDetail) }
} }
@@ -214,6 +226,7 @@ fun DrugDetailScreen(
} }
} }
} }
}
} }
@Composable @Composable