pref: 优化动态列表性能
This commit is contained in:
parent
2ef051f7fa
commit
db88b51b14
|
|
@ -2,6 +2,8 @@ package com.huanchengfly.tieba.post.arch
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class StableHolder<T>(val item: T) {
|
class StableHolder<T>(val item: T) {
|
||||||
|
|
@ -53,8 +55,8 @@ class ImmutableHolder<T>(val item: T) {
|
||||||
return wrapImmutable(getter(item))
|
return wrapImmutable(getter(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <R> getImmutableList(getter: T.() -> List<R>): List<ImmutableHolder<R>> {
|
fun <R> getImmutableList(getter: T.() -> List<R>): ImmutableList<ImmutableHolder<R>> {
|
||||||
return getter(item).map { wrapImmutable(it) }
|
return getter(item).map { wrapImmutable(it) }.toImmutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
|
|
@ -68,4 +70,5 @@ fun <T> wrapStable(item: T): StableHolder<T> = StableHolder(item)
|
||||||
|
|
||||||
fun <T> wrapImmutable(item: T): ImmutableHolder<T> = ImmutableHolder(item)
|
fun <T> wrapImmutable(item: T): ImmutableHolder<T> = ImmutableHolder(item)
|
||||||
|
|
||||||
fun <T> List<T>.wrapImmutable(): List<ImmutableHolder<T>> = map { wrapImmutable(it) }
|
fun <T> List<T>.wrapImmutable(): ImmutableList<ImmutableHolder<T>> =
|
||||||
|
map { wrapImmutable(it) }.toImmutableList()
|
||||||
|
|
@ -57,6 +57,8 @@ import com.huanchengfly.tieba.post.arch.collectPartialAsState
|
||||||
import com.huanchengfly.tieba.post.arch.onEvent
|
import com.huanchengfly.tieba.post.arch.onEvent
|
||||||
import com.huanchengfly.tieba.post.arch.pageViewModel
|
import com.huanchengfly.tieba.post.arch.pageViewModel
|
||||||
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
|
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
|
||||||
|
import com.huanchengfly.tieba.post.ui.page.LocalNavigator
|
||||||
|
import com.huanchengfly.tieba.post.ui.page.destinations.ForumPageDestination
|
||||||
import com.huanchengfly.tieba.post.ui.page.main.MainUiEvent
|
import com.huanchengfly.tieba.post.ui.page.main.MainUiEvent
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.FeedCard
|
import com.huanchengfly.tieba.post.ui.widgets.compose.FeedCard
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
|
import com.huanchengfly.tieba.post.ui.widgets.compose.LazyLoad
|
||||||
|
|
@ -76,6 +78,7 @@ fun PersonalizedPage(
|
||||||
viewModel.initialized = true
|
viewModel.initialized = true
|
||||||
}
|
}
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val navigator = LocalNavigator.current
|
||||||
val isRefreshing by viewModel.uiState.collectPartialAsState(
|
val isRefreshing by viewModel.uiState.collectPartialAsState(
|
||||||
prop1 = PersonalizedUiState::isRefreshing,
|
prop1 = PersonalizedUiState::isRefreshing,
|
||||||
initial = false
|
initial = false
|
||||||
|
|
@ -172,6 +175,9 @@ fun PersonalizedPage(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onRefresh = { viewModel.send(PersonalizedUiIntent.Refresh) },
|
onRefresh = { viewModel.send(PersonalizedUiIntent.Refresh) },
|
||||||
|
onOpenForum = {
|
||||||
|
navigator.navigate(ForumPageDestination(it))
|
||||||
|
},
|
||||||
state = lazyStaggeredGridState
|
state = lazyStaggeredGridState
|
||||||
)
|
)
|
||||||
LaunchedEffect(data.firstOrNull()?.get { id }) {
|
LaunchedEffect(data.firstOrNull()?.get { id }) {
|
||||||
|
|
@ -223,6 +229,7 @@ private fun FeedList(
|
||||||
onAgree: (ThreadInfo) -> Unit,
|
onAgree: (ThreadInfo) -> Unit,
|
||||||
onDislike: (ThreadInfo, Long, List<ImmutableHolder<DislikeReason>>) -> Unit,
|
onDislike: (ThreadInfo, Long, List<ImmutableHolder<DislikeReason>>) -> Unit,
|
||||||
onRefresh: () -> Unit,
|
onRefresh: () -> Unit,
|
||||||
|
onOpenForum: (forumName: String) -> Unit = {},
|
||||||
state: LazyStaggeredGridState,
|
state: LazyStaggeredGridState,
|
||||||
) {
|
) {
|
||||||
val data = dataProvider()
|
val data = dataProvider()
|
||||||
|
|
@ -259,6 +266,9 @@ private fun FeedList(
|
||||||
onAgree = {
|
onAgree = {
|
||||||
onAgree(item.get())
|
onAgree(item.get())
|
||||||
},
|
},
|
||||||
|
onClickForum = {
|
||||||
|
onOpenForum(item.get { forumInfo?.name ?: "" })
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
Dislike(
|
Dislike(
|
||||||
personalized = threadPersonalizedData[index],
|
personalized = threadPersonalizedData[index],
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.huanchengfly.tieba.post.ui.utils
|
package com.huanchengfly.tieba.post.ui.utils
|
||||||
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
|
import com.huanchengfly.tieba.post.api.models.protos.Media
|
||||||
import com.huanchengfly.tieba.post.api.models.protos.Post
|
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.arch.ImmutableHolder
|
import com.huanchengfly.tieba.post.arch.ImmutableHolder
|
||||||
|
|
@ -54,19 +55,35 @@ fun getPhotoViewData(
|
||||||
threadInfo: ThreadInfo,
|
threadInfo: ThreadInfo,
|
||||||
index: Int
|
index: Int
|
||||||
): PhotoViewData {
|
): PhotoViewData {
|
||||||
val media = threadInfo.media[index]
|
return getPhotoViewData(
|
||||||
|
medias = threadInfo.media,
|
||||||
|
forumId = threadInfo.forumId,
|
||||||
|
forumName = threadInfo.forumName,
|
||||||
|
threadId = threadInfo.threadId,
|
||||||
|
index = index
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPhotoViewData(
|
||||||
|
medias: List<Media>,
|
||||||
|
forumId: Long,
|
||||||
|
forumName: String,
|
||||||
|
threadId: Long,
|
||||||
|
index: Int
|
||||||
|
): PhotoViewData {
|
||||||
|
val media = medias[index]
|
||||||
return PhotoViewData(
|
return PhotoViewData(
|
||||||
data_ = LoadPicPageData(
|
data_ = LoadPicPageData(
|
||||||
forumId = threadInfo.forumId,
|
forumId = forumId,
|
||||||
forumName = threadInfo.forumName,
|
forumName = forumName,
|
||||||
threadId = threadInfo.threadId,
|
threadId = threadId,
|
||||||
postId = media.postId,
|
postId = media.postId,
|
||||||
seeLz = false,
|
seeLz = false,
|
||||||
objType = "index",
|
objType = "index",
|
||||||
picId = ImageUtil.getPicId(media.originPic),
|
picId = ImageUtil.getPicId(media.originPic),
|
||||||
picIndex = index + 1
|
picIndex = index + 1
|
||||||
),
|
),
|
||||||
picItems = threadInfo.media.mapIndexed { mediaIndex, mediaItem ->
|
picItems = medias.mapIndexed { mediaIndex, mediaItem ->
|
||||||
PicItem(
|
PicItem(
|
||||||
picId = ImageUtil.getPicId(mediaItem.originPic),
|
picId = ImageUtil.getPicId(mediaItem.originPic),
|
||||||
picIndex = mediaIndex + 1,
|
picIndex = mediaIndex + 1,
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import androidx.compose.material.icons.rounded.SwapCalls
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
|
@ -62,8 +63,6 @@ import com.huanchengfly.tieba.post.api.models.protos.User
|
||||||
import com.huanchengfly.tieba.post.arch.ImmutableHolder
|
import com.huanchengfly.tieba.post.arch.ImmutableHolder
|
||||||
import com.huanchengfly.tieba.post.arch.wrapImmutable
|
import com.huanchengfly.tieba.post.arch.wrapImmutable
|
||||||
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
|
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
|
||||||
import com.huanchengfly.tieba.post.ui.page.LocalNavigator
|
|
||||||
import com.huanchengfly.tieba.post.ui.page.destinations.ForumPageDestination
|
|
||||||
import com.huanchengfly.tieba.post.ui.utils.getImmutablePhotoViewData
|
import com.huanchengfly.tieba.post.ui.utils.getImmutablePhotoViewData
|
||||||
import com.huanchengfly.tieba.post.ui.widgets.VideoPlayerStandard
|
import com.huanchengfly.tieba.post.ui.widgets.VideoPlayerStandard
|
||||||
import com.huanchengfly.tieba.post.utils.DateTimeUtils
|
import com.huanchengfly.tieba.post.utils.DateTimeUtils
|
||||||
|
|
@ -306,20 +305,187 @@ private fun ForumInfoChip(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ThreadMedia(
|
||||||
|
item: ImmutableHolder<ThreadInfo>
|
||||||
|
) {
|
||||||
|
val isVideo = remember(item) {
|
||||||
|
item.isNotNull { videoInfo }
|
||||||
|
}
|
||||||
|
val medias = remember(item) {
|
||||||
|
item.getImmutableList { media }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVideo) {
|
||||||
|
val videoInfo = item.getImmutable { videoInfo!! }
|
||||||
|
VideoPlayer(
|
||||||
|
videoUrl = videoInfo.get { videoUrl },
|
||||||
|
thumbnailUrl = videoInfo.get { thumbnailUrl },
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.aspectRatio(
|
||||||
|
max(
|
||||||
|
videoInfo
|
||||||
|
.get { thumbnailWidth }
|
||||||
|
.toFloat() / videoInfo.get { thumbnailHeight },
|
||||||
|
16f / 9
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.clip(RoundedCornerShape(8.dp))
|
||||||
|
)
|
||||||
|
} else if (medias.isNotEmpty()) {
|
||||||
|
Box {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.aspectRatio(if (medias.size == 1) 2f else 3f)
|
||||||
|
.clip(RoundedCornerShape(8.dp)),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
medias.subList(0, min(medias.size, 3))
|
||||||
|
.forEachIndexed { index, media ->
|
||||||
|
val photoViewData = remember(item, index) {
|
||||||
|
getImmutablePhotoViewData(item.get(), index)
|
||||||
|
}
|
||||||
|
NetworkImage(
|
||||||
|
imageUri = media.url,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
photoViewData = photoViewData,
|
||||||
|
contentScale = ContentScale.Crop
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (medias.size > 3) {
|
||||||
|
Badge(
|
||||||
|
icon = Icons.Rounded.PhotoSizeSelectActual,
|
||||||
|
text = "${medias.size}",
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.BottomEnd)
|
||||||
|
.padding(8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ThreadForumInfo(
|
||||||
|
item: ImmutableHolder<ThreadInfo>,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
val hasForumInfo = remember(item) { item.isNotNull { forumInfo } }
|
||||||
|
if (hasForumInfo) {
|
||||||
|
val forumInfo = remember(item) { item.getImmutable { forumInfo!! } }
|
||||||
|
if (forumInfo.get { name }.isNotBlank()) {
|
||||||
|
ForumInfoChip(
|
||||||
|
imageUriProvider = { StringUtil.getAvatarUrl(forumInfo.get { avatar }) },
|
||||||
|
nameProvider = { forumInfo.get { name } },
|
||||||
|
onClick = onClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ThreadCommentBtn(
|
||||||
|
commentNum: Int,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
ActionBtn(
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = ImageVector.vectorResource(id = R.drawable.ic_comment_new),
|
||||||
|
contentDescription = stringResource(id = R.string.desc_comment),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
text = if (commentNum == 0)
|
||||||
|
stringResource(id = R.string.title_reply)
|
||||||
|
else "$commentNum"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
modifier = modifier,
|
||||||
|
onClick = onClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ThreadAgreeBtn(
|
||||||
|
hasAgree: Boolean,
|
||||||
|
agreeNum: Int,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val contentColor =
|
||||||
|
if (hasAgree) ExtendedTheme.colors.accent else ExtendedTheme.colors.textSecondary
|
||||||
|
val animatedColor by animateColorAsState(contentColor, label = "agreeBtnContentColor")
|
||||||
|
|
||||||
|
ActionBtn(
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = if (hasAgree) Icons.Rounded.Favorite else Icons.Rounded.FavoriteBorder,
|
||||||
|
contentDescription = stringResource(id = R.string.desc_like),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
text = if (agreeNum == 0)
|
||||||
|
stringResource(id = R.string.title_agree)
|
||||||
|
else "$agreeNum"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
modifier = modifier,
|
||||||
|
color = animatedColor,
|
||||||
|
onClick = onClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ThreadShareBtn(
|
||||||
|
shareNum: Long,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
ActionBtn(
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.SwapCalls,
|
||||||
|
contentDescription = stringResource(id = R.string.desc_share),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
text = if (shareNum == 0L)
|
||||||
|
stringResource(id = R.string.title_share)
|
||||||
|
else "$shareNum"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
modifier = modifier,
|
||||||
|
onClick = onClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FeedCard(
|
fun FeedCard(
|
||||||
item: ImmutableHolder<ThreadInfo>,
|
item: ImmutableHolder<ThreadInfo>,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onAgree: () -> Unit,
|
onAgree: () -> Unit,
|
||||||
|
onClickForum: () -> Unit = {},
|
||||||
dislikeAction: @Composable () -> Unit = {},
|
dislikeAction: @Composable () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
Card(
|
Card(
|
||||||
header = {
|
header = {
|
||||||
if (item.isNotNull { author }) {
|
val hasAuthor = remember(item) { item.isNotNull { author } }
|
||||||
val author = item.getImmutable { author!! }
|
if (hasAuthor) {
|
||||||
|
val author = remember(item) { item.getImmutable { author!! } }
|
||||||
|
val time = remember(item) { item.get { lastTimeInt } }
|
||||||
DefaultUserHeader(
|
DefaultUserHeader(
|
||||||
user = author,
|
user = author,
|
||||||
time = item.get { lastTimeInt }) { dislikeAction() }
|
time = time
|
||||||
|
) { dislikeAction() }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
|
|
@ -332,130 +498,29 @@ fun FeedCard(
|
||||||
isGood = item.get { isGood == 1 }
|
isGood = item.get { isGood == 1 }
|
||||||
)
|
)
|
||||||
|
|
||||||
if (item.isNotNull { videoInfo }) {
|
ThreadMedia(item = item)
|
||||||
val videoInfo = item.getImmutable { videoInfo!! }
|
|
||||||
VideoPlayer(
|
|
||||||
videoUrl = videoInfo.get { videoUrl },
|
|
||||||
thumbnailUrl = videoInfo.get { thumbnailUrl },
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.aspectRatio(
|
|
||||||
max(
|
|
||||||
videoInfo
|
|
||||||
.get { thumbnailWidth }
|
|
||||||
.toFloat() / videoInfo.get { thumbnailHeight },
|
|
||||||
16f / 9
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.clip(RoundedCornerShape(8.dp))
|
|
||||||
)
|
|
||||||
} else if (item.getImmutableList { media }.isNotEmpty()) {
|
|
||||||
val media = item.getImmutableList { media }
|
|
||||||
Box {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.aspectRatio(if (media.size == 1) 2f else 3f)
|
|
||||||
.clip(RoundedCornerShape(8.dp)),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
|
||||||
) {
|
|
||||||
media.subList(0, min(media.size, 3))
|
|
||||||
.forEachIndexed { index, media ->
|
|
||||||
NetworkImage(
|
|
||||||
imageUri = media.url,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
photoViewData = getImmutablePhotoViewData(item.get(), index),
|
|
||||||
contentScale = ContentScale.Crop
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (media.size > 3) {
|
|
||||||
Badge(
|
|
||||||
icon = Icons.Rounded.PhotoSizeSelectActual,
|
|
||||||
text = "${media.size}",
|
|
||||||
modifier = Modifier
|
|
||||||
.align(Alignment.BottomEnd)
|
|
||||||
.padding(8.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.isNotNull { forumInfo }) {
|
ThreadForumInfo(item = item, onClick = onClickForum)
|
||||||
val navigator = LocalNavigator.current
|
|
||||||
val forumInfo = item.getImmutable { forumInfo!! }
|
|
||||||
ForumInfoChip(
|
|
||||||
imageUriProvider = { StringUtil.getAvatarUrl(forumInfo.get { avatar }) },
|
|
||||||
nameProvider = { forumInfo.get { name } },
|
|
||||||
onClick = {
|
|
||||||
navigator.navigate(ForumPageDestination(forumInfo.get { name }))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
action = {
|
action = {
|
||||||
Row(modifier = Modifier.fillMaxWidth()) {
|
Row(modifier = Modifier.fillMaxWidth()) {
|
||||||
ActionBtn(
|
ThreadCommentBtn(
|
||||||
icon = {
|
commentNum = item.get { commentNum },
|
||||||
Icon(
|
onClick = onClick,
|
||||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_comment_new),
|
modifier = Modifier.weight(1f)
|
||||||
contentDescription = stringResource(id = R.string.desc_comment)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
val replyNum = item.get { replyNum }
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = if (replyNum == 0)
|
|
||||||
stringResource(id = R.string.title_reply)
|
|
||||||
else "$replyNum"
|
|
||||||
)
|
|
||||||
},
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
color = ExtendedTheme.colors.textSecondary,
|
|
||||||
onClick = {},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val hasAgree = item.get { agree?.hasAgree == 1 }
|
ThreadAgreeBtn(
|
||||||
ActionBtn(
|
hasAgree = item.get { agree?.hasAgree == 1 },
|
||||||
icon = {
|
agreeNum = item.get { agreeNum },
|
||||||
Icon(
|
onClick = onAgree,
|
||||||
imageVector = if (hasAgree) Icons.Rounded.Favorite else Icons.Rounded.FavoriteBorder,
|
modifier = Modifier.weight(1f)
|
||||||
contentDescription = stringResource(id = R.string.desc_like)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
val agreeNum = item.get { agreeNum }
|
|
||||||
Text(
|
|
||||||
text = if (agreeNum == 0)
|
|
||||||
stringResource(id = R.string.title_agree)
|
|
||||||
else "$agreeNum"
|
|
||||||
)
|
|
||||||
},
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
color = if (hasAgree) ExtendedTheme.colors.accent else ExtendedTheme.colors.textSecondary,
|
|
||||||
onClick = onAgree
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ActionBtn(
|
ThreadShareBtn(
|
||||||
icon = {
|
shareNum = item.get { shareNum },
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Rounded.SwapCalls,
|
|
||||||
contentDescription = stringResource(id = R.string.desc_share)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
val shareNum = item.get { shareNum }
|
|
||||||
Text(
|
|
||||||
text = if (shareNum == 0L)
|
|
||||||
stringResource(id = R.string.title_share)
|
|
||||||
else shareNum.toString()
|
|
||||||
)
|
|
||||||
},
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
color = ExtendedTheme.colors.textSecondary,
|
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -486,35 +551,6 @@ private fun ActionBtnPlaceholder(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun ActionBtn(
|
|
||||||
icon: ImageVector,
|
|
||||||
contentDescription: String?,
|
|
||||||
text: String,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
color: Color = LocalContentColor.current,
|
|
||||||
onClick: (() -> Unit)? = null,
|
|
||||||
) {
|
|
||||||
val animatedColor by animateColorAsState(targetValue = color)
|
|
||||||
val clickableModifier = if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier
|
|
||||||
Row(
|
|
||||||
modifier = clickableModifier
|
|
||||||
.padding(vertical = 16.dp)
|
|
||||||
.then(modifier),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.Center,
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = icon,
|
|
||||||
contentDescription = contentDescription,
|
|
||||||
modifier = Modifier.size(18.dp),
|
|
||||||
tint = animatedColor,
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
|
||||||
Text(text = text, style = MaterialTheme.typography.caption, color = animatedColor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ActionBtn(
|
private fun ActionBtn(
|
||||||
icon: @Composable () -> Unit,
|
icon: @Composable () -> Unit,
|
||||||
|
|
@ -523,7 +559,6 @@ private fun ActionBtn(
|
||||||
color: Color = LocalContentColor.current,
|
color: Color = LocalContentColor.current,
|
||||||
onClick: (() -> Unit)? = null,
|
onClick: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
val animatedColor by animateColorAsState(targetValue = color)
|
|
||||||
val clickableModifier = if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier
|
val clickableModifier = if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier
|
||||||
Row(
|
Row(
|
||||||
modifier = clickableModifier
|
modifier = clickableModifier
|
||||||
|
|
@ -532,12 +567,12 @@ private fun ActionBtn(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
) {
|
) {
|
||||||
Box(modifier = Modifier.size(18.dp)) {
|
ProvideContentColor(color = color) {
|
||||||
icon()
|
Box(modifier = Modifier.size(18.dp)) {
|
||||||
}
|
icon()
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
}
|
||||||
ProvideTextStyle(value = MaterialTheme.typography.caption) {
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
ProvideContentColor(color = animatedColor) {
|
ProvideTextStyle(value = MaterialTheme.typography.caption) {
|
||||||
text()
|
text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -548,8 +583,8 @@ private fun ActionBtn(
|
||||||
fun VideoPlayer(
|
fun VideoPlayer(
|
||||||
videoUrl: String,
|
videoUrl: String,
|
||||||
thumbnailUrl: String,
|
thumbnailUrl: String,
|
||||||
title: String = "",
|
modifier: Modifier = Modifier,
|
||||||
modifier: Modifier = Modifier
|
title: String = ""
|
||||||
) {
|
) {
|
||||||
AndroidView(
|
AndroidView(
|
||||||
factory = { context ->
|
factory = { context ->
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue