diff --git a/app/src/main/java/com/huanchengfly/tieba/post/api/Utils.kt b/app/src/main/java/com/huanchengfly/tieba/post/api/Utils.kt index 6764929e..f5d8210a 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/api/Utils.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/api/Utils.kt @@ -1,10 +1,27 @@ package com.huanchengfly.tieba.post.api import android.os.Build +import androidx.compose.foundation.text.appendInlineContent +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.ExperimentalTextApi +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.withAnnotation +import androidx.compose.ui.text.withStyle import com.huanchengfly.tieba.post.App import com.huanchengfly.tieba.post.App.ScreenInfo +import com.huanchengfly.tieba.post.R +import com.huanchengfly.tieba.post.api.models.protos.PbContent +import com.huanchengfly.tieba.post.api.models.protos.Post import com.huanchengfly.tieba.post.api.models.protos.ThreadInfo +import com.huanchengfly.tieba.post.ui.common.PbContentRender +import com.huanchengfly.tieba.post.ui.common.PicContentRender +import com.huanchengfly.tieba.post.ui.common.TextContentRender +import com.huanchengfly.tieba.post.ui.common.theme.utils.ThemeUtils +import com.huanchengfly.tieba.post.ui.utils.getPhotoViewData import com.huanchengfly.tieba.post.utils.EmoticonManager +import com.huanchengfly.tieba.post.utils.EmoticonUtil.emoticonString +import com.huanchengfly.tieba.post.utils.ImageUtil private val defaultUserAgent: String = @@ -67,4 +84,153 @@ fun ThreadInfo.updateAgreeStatus( copy( agreeNum = if (hasAgree == 1) agreeNum + 1 else agreeNum - 1 ) -} \ No newline at end of file +} + +private val PbContent.picUrl: String + get() = + ImageUtil.getUrl( + App.INSTANCE, + true, + originSrc, + bigCdnSrc, + bigSrc, + dynamic_, + cdnSrc, + cdnSrcActive, + src + ) + +@OptIn(ExperimentalTextApi::class) +val List.renders: List + get() { + val renders = mutableListOf() + + forEach { + when (it.type) { + 0, 9, 27 -> { + val lastRender = renders.lastOrNull() + if (lastRender is TextContentRender) { + lastRender.append(it.text) + } else + renders.add(TextContentRender(it.text)) + } + + 1 -> { + val text = buildAnnotatedString { + appendInlineContent("link_icon", alternateText = "🔗") + withAnnotation(tag = "url", annotation = it.link) { + withStyle( + SpanStyle( + color = Color( + ThemeUtils.getColorByAttr( + App.INSTANCE, + R.attr.colorPrimary + ) + ) + ) + ) { + append(it.text) + } + } + } + val lastRender = renders.lastOrNull() + if (lastRender is TextContentRender) { + lastRender.append(text) + } else + renders.add(TextContentRender(text)) + } + + 2 -> { + EmoticonManager.registerEmoticon( + it.text, + it.c + ) + val emoticonText = "#(${it.c})".emoticonString + val lastRender = renders.lastOrNull() + if (lastRender is TextContentRender) { + lastRender.append(emoticonText) + } else + renders.add(TextContentRender(emoticonText)) + } + + 3 -> { + val width = it.bsize.split(",")[0].toInt() + val height = it.bsize.split(",")[1].toInt() + renders.add( + PicContentRender( + picUrl = it.picUrl, + originUrl = it.originSrc, + showOriginBtn = it.showOriginalBtn == 1, + originSize = it.originSize, + picId = ImageUtil.getPicId(it.originSrc), + width = width, + height = height + ) + ) + } + + 4 -> { + val text = buildAnnotatedString { + appendInlineContent("user_icon", alternateText = "🧑") + withAnnotation(tag = "user", annotation = "${it.uid}") { + withStyle( + SpanStyle( + color = Color( + ThemeUtils.getColorByAttr( + App.INSTANCE, + R.attr.colorPrimary + ) + ) + ) + ) { + append(it.text) + } + } + } + val lastRender = renders.lastOrNull() + if (lastRender is TextContentRender) { + lastRender.append(text) + } else + renders.add(TextContentRender(text)) + } + + 20 -> { + val width = it.bsize.split(",")[0].toInt() + val height = it.bsize.split(",")[1].toInt() + renders.add( + PicContentRender( + picUrl = it.src, + originUrl = it.src, + showOriginBtn = it.showOriginalBtn == 1, + originSize = it.originSize, + picId = ImageUtil.getPicId(it.src), + width = width, + height = height + ) + ) + } + } + } + + return renders + } + +val Post.contentRenders: List + get() { + val renders = content.renders + + renders.forEach { + if (it is PicContentRender) { + it.photoViewData = getPhotoViewData( + this, + it.picId, + it.picUrl, + it.originUrl, + it.showOriginBtn, + it.originSize + ) + } + } + + return renders + } \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/common/PbContentRender.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/common/PbContentRender.kt new file mode 100644 index 00000000..e00e96f5 --- /dev/null +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/common/PbContentRender.kt @@ -0,0 +1,73 @@ +package com.huanchengfly.tieba.post.ui.common + +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +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.AnnotatedString +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.huanchengfly.tieba.post.arch.BaseComposeActivity.Companion.LocalWindowSizeClass +import com.huanchengfly.tieba.post.models.protos.PhotoViewData +import com.huanchengfly.tieba.post.ui.common.windowsizeclass.WindowWidthSizeClass +import com.huanchengfly.tieba.post.ui.widgets.compose.EmoticonText +import com.huanchengfly.tieba.post.ui.widgets.compose.NetworkImage +import com.huanchengfly.tieba.post.utils.appPreferences + +interface PbContentRender { + @Composable + fun Render() +} + +data class TextContentRender( + var text: AnnotatedString +) : PbContentRender { + constructor(text: String) : this(AnnotatedString(text)) + + fun append(text: String) { + append(AnnotatedString(text)) + } + + fun append(text: AnnotatedString) { + this.text += text + } + + @Composable + override fun Render() { + EmoticonText(text = text, style = MaterialTheme.typography.body1, fontSize = 15.sp) + } +} + +data class PicContentRender( + val picUrl: String, + val originUrl: String, + val showOriginBtn: Boolean, + val originSize: Int, + val width: Int, + val height: Int, + val picId: String, + var photoViewData: PhotoViewData? = null +) : PbContentRender { + @Composable + override fun Render() { + val widthFraction = + if (LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Compact) 1f else 0.5f + val context = LocalContext.current + + NetworkImage( + imageUri = picUrl, + contentDescription = null, + modifier = Modifier + .clip(RoundedCornerShape(context.appPreferences.radius.dp)) + .fillMaxWidth(widthFraction) + .aspectRatio(width * 1f / height), + photoViewData = photoViewData, + contentScale = ContentScale.Crop + ) + } +} \ No newline at end of file