Skip to content

Commit 0156743

Browse files
Feat: discussion content style (#437)
* feat: replaced text with WebView to support rich formatting * feat: html renderer * fix: mapping error * fix: changes according code review * fix: changes according code review
1 parent d659f71 commit 0156743

File tree

17 files changed

+323
-305
lines changed

17 files changed

+323
-305
lines changed
Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package org.openedx.core.extension
22

3-
import android.os.Parcelable
43
import android.util.Patterns
5-
import kotlinx.parcelize.Parcelize
64
import org.jsoup.Jsoup
75
import org.jsoup.nodes.Document
86
import org.jsoup.select.Elements
@@ -36,84 +34,10 @@ object TextConverter : KoinComponent {
3634
return LinkedText(text, linksMap.toMap())
3735
}
3836

39-
fun textToLinkedImageText(html: String): LinkedImageText {
40-
val doc: Document =
41-
Jsoup.parse(html)
42-
val links: Elements = doc.select("a[href]")
43-
var text = doc.text()
44-
val headers = getHeaders(doc)
45-
val linksMap = mutableMapOf<String, String>()
46-
for (link in links) {
47-
if (isLinkValid(link.attr("href"))) {
48-
val linkText = if (link.hasText()) link.text() else link.attr("href")
49-
linksMap[linkText] = link.attr("href")
50-
} else {
51-
val resultLink =
52-
if (link.attr("href").isNotEmpty() && link.attr("href")[0] == '/') {
53-
link.attr("href").substring(1)
54-
} else {
55-
link.attr("href")
56-
}
57-
if (resultLink.isNotEmpty() && isLinkValid(config.getApiHostURL() + resultLink)) {
58-
linksMap[link.text()] = config.getApiHostURL() + resultLink
59-
}
60-
}
61-
}
62-
text = setSpacesForHeaders(text, headers)
63-
return LinkedImageText(
64-
text,
65-
linksMap.toMap(),
66-
getImageLinks(doc),
67-
headers
68-
)
69-
}
70-
7137
fun isLinkValid(link: String) = Patterns.WEB_URL.matcher(link.lowercase()).matches()
72-
73-
@Suppress("MagicNumber")
74-
private fun getHeaders(document: Document): List<String> {
75-
val headersList = mutableListOf<String>()
76-
for (index in 1..6) {
77-
if (document.select("h$index").hasText()) {
78-
headersList.add(document.select("h$index").text())
79-
}
80-
}
81-
return headersList.toList()
82-
}
83-
84-
private fun setSpacesForHeaders(text: String, headers: List<String>): String {
85-
var result = text
86-
headers.forEach {
87-
val startIndex = text.indexOf(it)
88-
val endIndex = startIndex + it.length + 1
89-
result = text.replaceRange(startIndex, endIndex, it + "\n")
90-
}
91-
return result
92-
}
93-
94-
private fun getImageLinks(document: Document): Map<String, String> {
95-
val imageLinks = mutableMapOf<String, String>()
96-
val elements = document.getElementsByTag("img")
97-
for (element in elements) {
98-
if (element.hasAttr("alt")) {
99-
imageLinks[element.attr("alt")] = element.attr("src")
100-
} else {
101-
imageLinks[element.attr("src")] = element.attr("src")
102-
}
103-
}
104-
return imageLinks.toMap()
105-
}
10638
}
10739

10840
data class LinkedText(
10941
val text: String,
11042
val links: Map<String, String>
11143
)
112-
113-
@Parcelize
114-
data class LinkedImageText(
115-
val text: String,
116-
val links: Map<String, String>,
117-
val imageLinks: Map<String, String>,
118-
val headers: List<String>
119-
) : Parcelable

core/src/main/java/org/openedx/core/ui/ComposeCommon.kt

Lines changed: 0 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.openedx.core.ui
22

3-
import android.os.Build
4-
import android.os.Build.VERSION.SDK_INT
53
import androidx.compose.foundation.BorderStroke
64
import androidx.compose.foundation.ExperimentalFoundationApi
75
import androidx.compose.foundation.background
@@ -19,7 +17,6 @@ import androidx.compose.foundation.layout.Spacer
1917
import androidx.compose.foundation.layout.fillMaxSize
2018
import androidx.compose.foundation.layout.fillMaxWidth
2119
import androidx.compose.foundation.layout.height
22-
import androidx.compose.foundation.layout.heightIn
2320
import androidx.compose.foundation.layout.padding
2421
import androidx.compose.foundation.layout.size
2522
import androidx.compose.foundation.layout.width
@@ -81,7 +78,6 @@ import androidx.compose.ui.graphics.painter.Painter
8178
import androidx.compose.ui.graphics.vector.ImageVector
8279
import androidx.compose.ui.graphics.vector.rememberVectorPainter
8380
import androidx.compose.ui.input.pointer.pointerInput
84-
import androidx.compose.ui.layout.ContentScale
8581
import androidx.compose.ui.platform.LocalContext
8682
import androidx.compose.ui.platform.LocalFocusManager
8783
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
@@ -106,15 +102,10 @@ import androidx.compose.ui.unit.Dp
106102
import androidx.compose.ui.unit.TextUnit
107103
import androidx.compose.ui.unit.dp
108104
import androidx.compose.ui.zIndex
109-
import coil.ImageLoader
110-
import coil.compose.AsyncImage
111-
import coil.decode.GifDecoder
112-
import coil.decode.ImageDecoderDecoder
113105
import kotlinx.coroutines.launch
114106
import org.openedx.core.NoContentScreenType
115107
import org.openedx.core.R
116108
import org.openedx.core.domain.model.RegistrationField
117-
import org.openedx.core.extension.LinkedImageText
118109
import org.openedx.core.presentation.global.ErrorType
119110
import org.openedx.core.ui.theme.OpenEdXTheme
120111
import org.openedx.core.ui.theme.appColors
@@ -542,131 +533,6 @@ fun HyperlinkText(
542533
)
543534
}
544535

545-
@Composable
546-
fun HyperlinkImageText(
547-
modifier: Modifier = Modifier,
548-
title: String = "",
549-
imageText: LinkedImageText,
550-
textStyle: TextStyle = TextStyle.Default,
551-
linkTextColor: Color = MaterialTheme.appColors.primary,
552-
linkTextFontWeight: FontWeight = FontWeight.Normal,
553-
linkTextDecoration: TextDecoration = TextDecoration.None,
554-
fontSize: TextUnit = TextUnit.Unspecified,
555-
) {
556-
val fullText = imageText.text
557-
val hyperLinks = imageText.links
558-
val annotatedString = buildAnnotatedString {
559-
if (title.isNotEmpty()) {
560-
append(title)
561-
append("\n\n")
562-
}
563-
append(fullText)
564-
addStyle(
565-
style = SpanStyle(
566-
color = MaterialTheme.appColors.textPrimary,
567-
fontSize = fontSize
568-
),
569-
start = 0,
570-
end = this.length
571-
)
572-
573-
for ((key, value) in hyperLinks) {
574-
val startIndex = this.toString().indexOf(key)
575-
if (startIndex == -1) continue
576-
val endIndex = startIndex + key.length
577-
addStyle(
578-
style = SpanStyle(
579-
color = linkTextColor,
580-
fontSize = fontSize,
581-
fontWeight = linkTextFontWeight,
582-
textDecoration = linkTextDecoration
583-
),
584-
start = startIndex,
585-
end = endIndex
586-
)
587-
addStringAnnotation(
588-
tag = "URL",
589-
annotation = value,
590-
start = startIndex,
591-
end = endIndex
592-
)
593-
}
594-
if (title.isNotEmpty()) {
595-
addStyle(
596-
style = SpanStyle(
597-
color = MaterialTheme.appColors.textPrimary,
598-
fontSize = MaterialTheme.appTypography.titleLarge.fontSize,
599-
fontWeight = MaterialTheme.appTypography.titleLarge.fontWeight
600-
),
601-
start = 0,
602-
end = title.length
603-
)
604-
}
605-
for (item in imageText.headers) {
606-
val startIndex = this.toString().indexOf(item)
607-
if (startIndex == -1) continue
608-
val endIndex = startIndex + item.length
609-
addStyle(
610-
style = SpanStyle(
611-
color = MaterialTheme.appColors.textPrimary,
612-
fontSize = MaterialTheme.appTypography.titleLarge.fontSize,
613-
fontWeight = MaterialTheme.appTypography.titleLarge.fontWeight
614-
),
615-
start = startIndex,
616-
end = endIndex
617-
)
618-
}
619-
addStyle(
620-
style = SpanStyle(
621-
fontSize = fontSize
622-
),
623-
start = 0,
624-
end = this.length
625-
)
626-
}
627-
628-
val uriHandler = LocalUriHandler.current
629-
val context = LocalContext.current
630-
val imageLoader = ImageLoader.Builder(context)
631-
.components {
632-
if (SDK_INT >= Build.VERSION_CODES.P) {
633-
add(ImageDecoderDecoder.Factory())
634-
} else {
635-
add(GifDecoder.Factory())
636-
}
637-
}
638-
.build()
639-
640-
Column(Modifier.fillMaxWidth()) {
641-
BasicText(
642-
text = annotatedString,
643-
modifier = modifier.pointerInput(Unit) {
644-
detectTapGestures { offset ->
645-
val position = offset.x.toInt()
646-
annotatedString.getStringAnnotations("URL", position, position)
647-
.firstOrNull()?.let { stringAnnotation ->
648-
uriHandler.openUri(stringAnnotation.item)
649-
}
650-
}
651-
},
652-
style = textStyle
653-
)
654-
imageText.imageLinks.values.forEach {
655-
Spacer(Modifier.height(8.dp))
656-
AsyncImage(
657-
modifier = Modifier
658-
.fillMaxWidth()
659-
.heightIn(0.dp, 360.dp),
660-
contentScale = ContentScale.Fit,
661-
model = it,
662-
contentDescription = null,
663-
imageLoader = imageLoader
664-
)
665-
}
666-
Spacer(Modifier.height(16.dp))
667-
}
668-
}
669-
670536
@Composable
671537
fun SheetContent(
672538
searchValue: TextFieldValue,

0 commit comments

Comments
 (0)