Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2026-01-24 19:44:36 +01:00
padre ebc81e2bf0
commit 8dc1ea12d9
Se han modificado 2 ficheros con 41 adiciones y 15 borrados

Ver fichero

@@ -269,13 +269,13 @@ private fun formatCount(count: Int): String {
* Parse HTML content and create AnnotatedString with clickable links
*/
@Composable
private fun parseHtmlContent(html: String, linkColor: Color) = buildAnnotatedString {
fun parseHtmlContent(html: String, linkColor: Color) = buildAnnotatedString {
// Convert HTML to plain text but extract links
val plainText = Html.fromHtml(html, Html.FROM_HTML_MODE_COMPACT).toString()
// Regex patterns for URLs and hashtags
val urlPattern = Regex("""https?://[^\s<>"{}|\\^`\[\]]+""")
val hashtagPattern = Regex("""(^|\s)#(\w+)""")
val hashtagPattern = Regex("""#(\w+)""") // Match # and word, simpler pattern
val allMatches = mutableListOf<Pair<IntRange, String>>()
@@ -286,11 +286,10 @@ private fun parseHtmlContent(html: String, linkColor: Color) = buildAnnotatedStr
// Find all hashtags
hashtagPattern.findAll(plainText).forEach { match ->
val hashtagRange = match.groups[2]?.range
val hashtagValue = match.groups[2]?.value
if (hashtagRange != null && hashtagValue != null) {
// Don't add # prefix as it's already in the text
allMatches.add(hashtagRange to hashtagValue)
val fullHashtagRange = match.range // Include the # in the range
val hashtagValue = match.groups[1]?.value // Just the word without #
if (hashtagValue != null) {
allMatches.add(fullHashtagRange to hashtagValue)
}
}
@@ -314,7 +313,12 @@ private fun parseHtmlContent(html: String, linkColor: Color) = buildAnnotatedStr
) {
val tag = if (value.startsWith("http")) "URL" else "HASHTAG"
pushStringAnnotation(tag = tag, annotation = value)
append(if (value.startsWith("http")) plainText.substring(range) else "#${value}")
// For URLs, use the full text from range; for hashtags, add # prefix to the value
if (value.startsWith("http")) {
append(plainText.substring(range))
} else {
append("#$value")
}
pop()
}

Ver fichero

@@ -1,10 +1,13 @@
package com.manalejandro.myactivitypub.ui.screens
import android.content.Intent
import android.net.Uri
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.*
@@ -15,12 +18,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import com.manalejandro.myactivitypub.data.models.Status
import com.manalejandro.myactivitypub.data.repository.MastodonRepository
import com.manalejandro.myactivitypub.ui.components.parseHtmlContent
import com.manalejandro.myactivitypub.ui.viewmodel.StatusDetailViewModel
import java.text.SimpleDateFormat
import java.util.*
@@ -113,6 +118,7 @@ fun StatusDetailScreen(
onReplyClick = onReplyClick,
onBoostClick = { viewModel.toggleBoost() },
onFavoriteClick = { viewModel.toggleFavorite() },
onHashtagClick = onHashtagClick,
isAuthenticated = isAuthenticated
)
@@ -169,6 +175,7 @@ private fun DetailedStatusCard(
onReplyClick: (Status) -> Unit,
onBoostClick: (Status) -> Unit,
onFavoriteClick: (Status) -> Unit,
onHashtagClick: (String) -> Unit,
isAuthenticated: Boolean
) {
var selectedMedia by remember { mutableStateOf<com.manalejandro.myactivitypub.data.models.MediaAttachment?>(null) }
@@ -224,13 +231,28 @@ private fun DetailedStatusCard(
Spacer(modifier = Modifier.height(16.dp))
// Content
Text(
text = android.text.Html.fromHtml(
status.content,
android.text.Html.FROM_HTML_MODE_COMPACT
).toString(),
style = MaterialTheme.typography.bodyLarge
// Content with clickable links and hashtags
val context = LocalContext.current
val annotatedContent = parseHtmlContent(status.content, MaterialTheme.colorScheme.primary)
@Suppress("DEPRECATION")
ClickableText(
text = annotatedContent,
style = MaterialTheme.typography.bodyLarge.copy(color = MaterialTheme.colorScheme.onSurface),
onClick = { offset ->
// Check for URL
annotatedContent.getStringAnnotations(tag = "URL", start = offset, end = offset)
.firstOrNull()?.let { annotation ->
// Open URL in browser
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(annotation.item))
context.startActivity(intent)
}
// Check for hashtag
annotatedContent.getStringAnnotations(tag = "HASHTAG", start = offset, end = offset)
.firstOrNull()?.let { annotation ->
onHashtagClick(annotation.item)
}
}
)
// Media attachments