feat: Compose 动态生成贴子内容
This commit is contained in:
parent
ac5998d840
commit
ba837abd86
|
|
@ -1,10 +1,27 @@
|
||||||
package com.huanchengfly.tieba.post.api
|
package com.huanchengfly.tieba.post.api
|
||||||
|
|
||||||
import android.os.Build
|
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
|
||||||
import com.huanchengfly.tieba.post.App.ScreenInfo
|
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.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.EmoticonManager
|
||||||
|
import com.huanchengfly.tieba.post.utils.EmoticonUtil.emoticonString
|
||||||
|
import com.huanchengfly.tieba.post.utils.ImageUtil
|
||||||
|
|
||||||
|
|
||||||
private val defaultUserAgent: String =
|
private val defaultUserAgent: String =
|
||||||
|
|
@ -67,4 +84,153 @@ fun ThreadInfo.updateAgreeStatus(
|
||||||
copy(
|
copy(
|
||||||
agreeNum = if (hasAgree == 1) agreeNum + 1 else agreeNum - 1
|
agreeNum = if (hasAgree == 1) agreeNum + 1 else agreeNum - 1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val PbContent.picUrl: String
|
||||||
|
get() =
|
||||||
|
ImageUtil.getUrl(
|
||||||
|
App.INSTANCE,
|
||||||
|
true,
|
||||||
|
originSrc,
|
||||||
|
bigCdnSrc,
|
||||||
|
bigSrc,
|
||||||
|
dynamic_,
|
||||||
|
cdnSrc,
|
||||||
|
cdnSrcActive,
|
||||||
|
src
|
||||||
|
)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTextApi::class)
|
||||||
|
val List<PbContent>.renders: List<PbContentRender>
|
||||||
|
get() {
|
||||||
|
val renders = mutableListOf<PbContentRender>()
|
||||||
|
|
||||||
|
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<PbContentRender>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue