首页新增推荐

This commit is contained in:
2024-10-25 22:01:58 +08:00
parent 5e65f217bb
commit d3518b1a82
14 changed files with 1084 additions and 697 deletions

View File

@@ -16,6 +16,7 @@ import com.aiosman.riderpro.ui.follower.FollowingListViewModel
import com.aiosman.riderpro.ui.index.IndexViewModel
import com.aiosman.riderpro.ui.index.tabs.message.MessageListViewModel
import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel
import com.aiosman.riderpro.ui.index.tabs.moment.tabs.timeline.TimelineMomentViewModel
import com.aiosman.riderpro.ui.index.tabs.profile.MyProfileViewModel
import com.aiosman.riderpro.ui.index.tabs.search.DiscoverViewModel
import com.aiosman.riderpro.ui.index.tabs.search.SearchViewModel
@@ -117,7 +118,7 @@ object AppState {
fun ReloadAppState(context: Context) {
// 重置动态列表页面
MomentViewModel.ResetModel()
TimelineMomentViewModel.ResetModel()
// 重置我的页面
MyProfileViewModel.ResetModel()
// 重置发现页面

View File

@@ -0,0 +1,507 @@
package com.aiosman.riderpro.ui.composables
import androidx.annotation.DrawableRes
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Build
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.AppColors
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.entity.MomentImageEntity
import com.aiosman.riderpro.exp.timeAgo
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.comment.CommentModalContent
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import com.aiosman.riderpro.ui.navigateToPost
@Composable
fun MomentCard(
momentEntity: MomentEntity,
onLikeClick: () -> Unit = {},
onFavoriteClick: () -> Unit = {},
onAddComment: () -> Unit = {},
hideAction: Boolean = false
) {
var imageIndex by remember { mutableStateOf(0) }
val navController = LocalNavController.current
Column(
modifier = Modifier.fillMaxWidth().background(AppColors.background)
) {
Box(
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp)
) {
MomentTopRowGroup(momentEntity = momentEntity)
}
Column(
modifier = Modifier
.fillMaxWidth()
.noRippleClickable {
navController.navigateToPost(
momentEntity.id,
highlightCommentId = 0,
initImagePagerIndex = imageIndex
)
}
) {
MomentContentGroup(
momentEntity = momentEntity,
onPageChange = { index -> imageIndex = index }
)
}
if (!hideAction) {
MomentBottomOperateRowGroup(
momentEntity = momentEntity,
onLikeClick = onLikeClick,
onAddComment = onAddComment,
onFavoriteClick = onFavoriteClick,
imageIndex = imageIndex,
onCommentClick = {
navController.navigateToPost(
momentEntity.id,
highlightCommentId = 0,
initImagePagerIndex = imageIndex
)
}
)
}
}
}
@Composable
fun ModificationListHeader() {
val navController = LocalNavController.current
Box(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.background(Color(0xFFF8F8F8))
.padding(4.dp)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
navController.navigate("ModificationList")
}
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Box(
modifier = Modifier
.background(Color(0xFFEB4869))
.padding(8.dp)
) {
Icon(
Icons.Filled.Build,
contentDescription = "Modification Icon",
tint = Color.White, // Assuming the icon should be white
modifier = Modifier.size(12.dp)
)
}
Spacer(modifier = Modifier.width(12.dp))
Text(
text = "Modification List",
color = Color(0xFF333333),
fontSize = 14.sp,
textAlign = TextAlign.Left
)
}
}
}
}
@Composable
fun MomentName(name: String) {
Text(
modifier = Modifier,
textAlign = TextAlign.Start,
text = name,
color = AppColors.text,
fontSize = 16.sp, style = TextStyle(fontWeight = FontWeight.Bold)
)
}
@Composable
fun MomentFollowBtn() {
Box(
modifier = Modifier
.size(width = 53.dp, height = 18.dp)
.padding(start = 8.dp),
contentAlignment = Alignment.Center
) {
Image(
modifier = Modifier
.fillMaxSize(),
painter = painterResource(id = R.drawable.follow_bg),
contentDescription = ""
)
Text(
text = "Follow",
color = Color.White,
fontSize = 12.sp
)
}
}
@Composable
fun MomentPostLocation(location: String) {
Text(
text = location,
color = AppColors.secondaryText,
fontSize = 12.sp
)
}
@Composable
fun MomentPostTime(time: String) {
Text(
modifier = Modifier,
text = time, color = AppColors.text,
fontSize = 12.sp
)
}
@Composable
fun MomentTopRowGroup(momentEntity: MomentEntity) {
val navController = LocalNavController.current
val context = LocalContext.current
Row(
modifier = Modifier
) {
CustomAsyncImage(
context,
momentEntity.avatar,
contentDescription = "",
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(40.dp))
.noRippleClickable {
navController.navigate(
NavigationRoute.AccountProfile.route.replace(
"{id}",
momentEntity.authorId.toString()
)
)
},
contentScale = ContentScale.Crop
)
Column(
modifier = Modifier
.defaultMinSize()
.padding(start = 12.dp, end = 12.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(22.dp),
verticalAlignment = Alignment.CenterVertically
) {
MomentName(momentEntity.nickname)
// MomentFollowBtn()
}
Row(
modifier = Modifier
.fillMaxWidth()
.height(21.dp),
verticalAlignment = Alignment.CenterVertically
) {
MomentPostTime(momentEntity.time.timeAgo(context))
Spacer(modifier = Modifier.width(8.dp))
MomentPostLocation(momentEntity.location)
}
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PostImageView(
images: List<MomentImageEntity>,
onPageChange: (Int) -> Unit = {}
) {
val pagerState = rememberPagerState(pageCount = { images.size })
LaunchedEffect(pagerState.currentPage) {
onPageChange(pagerState.currentPage)
}
val context = LocalContext.current
Column(
modifier = Modifier.fillMaxWidth()
) {
HorizontalPager(
state = pagerState,
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f),
) { page ->
val image = images[page]
CustomAsyncImage(
context,
image.thumbnail,
contentDescription = "Image",
blurHash = image.blurHash,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxSize()
)
}
}
}
@Composable
fun MomentContentGroup(
momentEntity: MomentEntity,
onPageChange: (Int) -> Unit = {}
) {
if (momentEntity.momentTextContent.isNotEmpty()) {
Text(
text = momentEntity.momentTextContent,
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, bottom = 8.dp),
fontSize = 16.sp,
color = AppColors.text
)
}
if (momentEntity.relMoment != null) {
RelPostCard(
momentEntity = momentEntity.relMoment!!,
modifier = Modifier.background(Color(0xFFF8F8F8))
)
} else {
Box(
modifier = Modifier.fillMaxWidth()
) {
PostImageView(
images = momentEntity.images,
onPageChange = onPageChange
)
}
}
}
@Composable
fun MomentOperateBtn(@DrawableRes icon: Int, count: String) {
Row(verticalAlignment = Alignment.CenterVertically) {
Image(
modifier = Modifier
.size(width = 24.dp, height = 24.dp),
painter = painterResource(id = icon),
contentDescription = "",
colorFilter = ColorFilter.tint(AppColors.text)
)
Text(
text = count,
modifier = Modifier.padding(start = 7.dp),
fontSize = 12.sp,
color = AppColors.text
)
}
}
@Composable
fun MomentOperateBtn(count: String, content: @Composable () -> Unit) {
Row(
modifier = Modifier,
verticalAlignment = Alignment.CenterVertically
) {
content()
AnimatedCounter(
count = count.toInt(),
fontSize = 14,
modifier = Modifier
.padding(start = 7.dp)
.width(24.dp)
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MomentBottomOperateRowGroup(
onLikeClick: () -> Unit = {},
onAddComment: () -> Unit = {},
onCommentClick: () -> Unit = {},
onFavoriteClick: () -> Unit = {},
momentEntity: MomentEntity,
imageIndex: Int = 0
) {
var showCommentModal by remember { mutableStateOf(false) }
if (showCommentModal) {
ModalBottomSheet(
onDismissRequest = { showCommentModal = false },
containerColor = Color.White,
sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
),
windowInsets = WindowInsets(0),
dragHandle = {
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.clip(CircleShape)
) {
}
}
) {
CommentModalContent(
postId = momentEntity.id,
commentCount = momentEntity.commentCount,
onCommentAdded = {
onAddComment()
}
)
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.padding(start = 16.dp, end = 0.dp)
) {
Row(
modifier = Modifier.fillMaxSize()
) {
Box(
modifier = Modifier.fillMaxHeight(),
contentAlignment = Alignment.Center
) {
MomentOperateBtn(count = momentEntity.likeCount.toString()) {
AnimatedLikeIcon(
modifier = Modifier.size(24.dp),
liked = momentEntity.liked
) {
onLikeClick()
}
}
}
Spacer(modifier = Modifier.width(4.dp))
Box(
modifier = Modifier
.fillMaxHeight()
.noRippleClickable {
onCommentClick()
},
contentAlignment = Alignment.Center
) {
MomentOperateBtn(
icon = R.drawable.rider_pro_comment,
count = momentEntity.commentCount.toString()
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.noRippleClickable {
onFavoriteClick()
},
contentAlignment = Alignment.CenterEnd
) {
MomentOperateBtn(count = momentEntity.favoriteCount.toString()) {
AnimatedFavouriteIcon(
modifier = Modifier.size(24.dp),
isFavourite = momentEntity.isFavorite
) {
onFavoriteClick()
}
}
}
}
if (momentEntity.images.size > 1) {
Row(
modifier = Modifier.fillMaxSize(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
momentEntity.images.forEachIndexed { index, _ ->
Box(
modifier = Modifier
.size(4.dp)
.clip(CircleShape)
.background(
if (imageIndex == index) Color.Red else Color.Gray.copy(
alpha = 0.5f
)
)
.padding(1.dp)
)
Spacer(modifier = Modifier.width(8.dp))
}
}
}
}
}
@Composable
fun MomentListLoading() {
CircularProgressIndicator(
modifier =
Modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally),
color = Color.Red
)
}

View File

@@ -9,9 +9,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.ui.index.tabs.moment.MomentTopRowGroup
@Composable
fun RelPostCard(

View File

@@ -1,13 +1,7 @@
package com.aiosman.riderpro.ui.index.tabs.moment
import androidx.annotation.DrawableRes
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -15,590 +9,124 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Build
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.paging.compose.collectAsLazyPagingItems
import com.aiosman.riderpro.AppColors
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.entity.MomentImageEntity
import com.aiosman.riderpro.exp.timeAgo
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.comment.CommentModalContent
import com.aiosman.riderpro.ui.composables.AnimatedCounter
import com.aiosman.riderpro.ui.composables.AnimatedFavouriteIcon
import com.aiosman.riderpro.ui.composables.AnimatedLikeIcon
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
import com.aiosman.riderpro.ui.composables.RelPostCard
import com.aiosman.riderpro.ui.index.tabs.moment.tabs.expolre.ExploreMomentsList
import com.aiosman.riderpro.ui.index.tabs.moment.tabs.timeline.TimelineMomentsList
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import com.aiosman.riderpro.ui.navigateToPost
import com.aiosman.riderpro.ui.post.NewPostViewModel
import kotlinx.coroutines.launch
/**
* 动态列表
*/
@OptIn(ExperimentalMaterialApi::class)
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
@Composable
fun MomentsList() {
val model = MomentViewModel
var dataFlow = model.momentsFlow
var moments = dataFlow.collectAsLazyPagingItems()
val scope = rememberCoroutineScope()
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
model.refreshPager(
pullRefresh = true
)
})
val navigationBarPaddings =
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
LaunchedEffect(Unit) {
model.refreshPager()
}
var pagerState = rememberPagerState { 2 }
var scope = rememberCoroutineScope()
Column(
modifier = Modifier
.fillMaxSize()
.padding(
top = statusBarPaddingValues.calculateTopPadding(),
bottom = navigationBarPaddings
)
) {
Box(Modifier.pullRefresh(state)) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
) {
items(
moments.itemCount,
key = { idx -> moments[idx]?.id ?: idx }
) { idx ->
val momentItem = moments[idx] ?: return@items
MomentCard(momentEntity = momentItem,
onAddComment = {
scope.launch {
model.onAddComment(momentItem.id)
}
},
onLikeClick = {
scope.launch {
if (momentItem.liked) {
model.dislikeMoment(momentItem.id)
} else {
model.likeMoment(momentItem.id)
}
}
},
onFavoriteClick = {
scope.launch {
if (momentItem.isFavorite) {
model.unfavoriteMoment(momentItem.id)
} else {
model.favoriteMoment(momentItem.id)
}
}
}
)
// Box(
// modifier = Modifier
// .height(4.dp)
// .fillMaxWidth()
// .background(Color(0xFFF0F2F5))
// )
}
}
PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))
}
}
}
@Composable
fun MomentCard(
momentEntity: MomentEntity,
onLikeClick: () -> Unit = {},
onFavoriteClick: () -> Unit = {},
onAddComment: () -> Unit = {},
hideAction: Boolean = false
) {
var imageIndex by remember { mutableStateOf(0) }
val navController = LocalNavController.current
Column(
modifier = Modifier.fillMaxWidth().background(AppColors.background)
) {
Box(
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp)
) {
MomentTopRowGroup(momentEntity = momentEntity)
}
Column(
modifier = Modifier
.fillMaxWidth()
.noRippleClickable {
navController.navigateToPost(
momentEntity.id,
highlightCommentId = 0,
initImagePagerIndex = imageIndex
)
}
) {
MomentContentGroup(
momentEntity = momentEntity,
onPageChange = { index -> imageIndex = index }
)
}
if (!hideAction) {
MomentBottomOperateRowGroup(
momentEntity = momentEntity,
onLikeClick = onLikeClick,
onAddComment = onAddComment,
onFavoriteClick = onFavoriteClick,
imageIndex = imageIndex,
onCommentClick = {
navController.navigateToPost(
momentEntity.id,
highlightCommentId = 0,
initImagePagerIndex = imageIndex
)
}
)
}
}
}
@Composable
fun ModificationListHeader() {
val navController = LocalNavController.current
Box(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.background(Color(0xFFF8F8F8))
.padding(4.dp)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
navController.navigate("ModificationList")
}
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Box(
modifier = Modifier
.background(Color(0xFFEB4869))
.padding(8.dp)
) {
Icon(
Icons.Filled.Build,
contentDescription = "Modification Icon",
tint = Color.White, // Assuming the icon should be white
modifier = Modifier.size(12.dp)
)
}
Spacer(modifier = Modifier.width(12.dp))
Text(
text = "Modification List",
color = Color(0xFF333333),
fontSize = 14.sp,
textAlign = TextAlign.Left
)
}
}
}
}
@Composable
fun MomentName(name: String) {
Text(
modifier = Modifier,
textAlign = TextAlign.Start,
text = name,
color = AppColors.text,
fontSize = 16.sp, style = TextStyle(fontWeight = FontWeight.Bold)
)
}
@Composable
fun MomentFollowBtn() {
Box(
modifier = Modifier
.size(width = 53.dp, height = 18.dp)
.padding(start = 8.dp),
contentAlignment = Alignment.Center
) {
Image(
modifier = Modifier
.fillMaxSize(),
painter = painterResource(id = R.drawable.follow_bg),
contentDescription = ""
)
Text(
text = "Follow",
color = Color.White,
fontSize = 12.sp
)
}
}
@Composable
fun MomentPostLocation(location: String) {
Text(
text = location,
color = AppColors.secondaryText,
fontSize = 12.sp
)
}
@Composable
fun MomentPostTime(time: String) {
Text(
modifier = Modifier,
text = time, color = AppColors.text,
fontSize = 12.sp
)
}
@Composable
fun MomentTopRowGroup(momentEntity: MomentEntity) {
val navController = LocalNavController.current
val context = LocalContext.current
Row(
modifier = Modifier
) {
CustomAsyncImage(
context,
momentEntity.avatar,
contentDescription = "",
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(40.dp))
.noRippleClickable {
navController.navigate(
NavigationRoute.AccountProfile.route.replace(
"{id}",
momentEntity.authorId.toString()
)
)
},
contentScale = ContentScale.Crop
)
Column(
modifier = Modifier
.defaultMinSize()
.padding(start = 12.dp, end = 12.dp)
),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(22.dp),
modifier = Modifier.fillMaxWidth().height(44.dp),
// center the tabs
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
MomentName(momentEntity.nickname)
// MomentFollowBtn()
}
Row(
modifier = Modifier
.fillMaxWidth()
.height(21.dp),
verticalAlignment = Alignment.CenterVertically
) {
MomentPostTime(momentEntity.time.timeAgo(context))
Spacer(modifier = Modifier.width(8.dp))
MomentPostLocation(momentEntity.location)
}
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PostImageView(
images: List<MomentImageEntity>,
onPageChange: (Int) -> Unit = {}
) {
val pagerState = rememberPagerState(pageCount = { images.size })
LaunchedEffect(pagerState.currentPage) {
onPageChange(pagerState.currentPage)
}
val context = LocalContext.current
Column(
modifier = Modifier.fillMaxWidth()
modifier = Modifier
.noRippleClickable {
scope.launch {
pagerState.animateScrollToPage(0)
}
},
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Worldwide", fontSize = 16.sp, color = AppColors.text,fontWeight = FontWeight.W600)
Spacer(modifier = Modifier.height(4.dp))
Box(
modifier = Modifier
.width(48.dp)
.height(4.dp)
.clip(RoundedCornerShape(16.dp))
.background(if (pagerState.currentPage == 0) AppColors.text else AppColors.background)
)
}
Spacer(modifier = Modifier.width(32.dp))
Column(
modifier = Modifier
.noRippleClickable {
scope.launch {
pagerState.animateScrollToPage(1)
}
},
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Following", fontSize = 16.sp, color = AppColors.text, fontWeight = FontWeight.W600)
Spacer(modifier = Modifier.height(4.dp))
Box(
modifier = Modifier
.width(48.dp)
.height(4.dp)
.clip(RoundedCornerShape(16.dp))
.background(if (pagerState.currentPage == 1) AppColors.text else AppColors.background)
)
}
}
HorizontalPager(
state = pagerState,
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f),
) { page ->
val image = images[page]
CustomAsyncImage(
context,
image.thumbnail,
contentDescription = "Image",
blurHash = image.blurHash,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxSize()
)
}
}
}
@Composable
fun MomentContentGroup(
momentEntity: MomentEntity,
onPageChange: (Int) -> Unit = {}
) {
if (momentEntity.momentTextContent.isNotEmpty()) {
Text(
text = momentEntity.momentTextContent,
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, bottom = 8.dp),
fontSize = 16.sp,
color = AppColors.text
)
}
if (momentEntity.relMoment != null) {
RelPostCard(
momentEntity = momentEntity.relMoment!!,
modifier = Modifier.background(Color(0xFFF8F8F8))
)
} else {
Box(
modifier = Modifier.fillMaxWidth()
.weight(1f)
) {
PostImageView(
images = momentEntity.images,
onPageChange = onPageChange
)
}
when (it) {
0 -> {
ExploreMomentsList()
}
1 -> {
TimelineMomentsList()
}
}
}
}
}
@Composable
fun MomentOperateBtn(@DrawableRes icon: Int, count: String) {
Row(verticalAlignment = Alignment.CenterVertically) {
Image(
modifier = Modifier
.size(width = 24.dp, height = 24.dp),
painter = painterResource(id = icon),
contentDescription = "",
colorFilter = ColorFilter.tint(AppColors.text)
)
Text(
text = count,
modifier = Modifier.padding(start = 7.dp),
fontSize = 12.sp,
color = AppColors.text
)
}
}
@Composable
fun MomentOperateBtn(count: String, content: @Composable () -> Unit) {
Row(
modifier = Modifier,
verticalAlignment = Alignment.CenterVertically
) {
content()
AnimatedCounter(
count = count.toInt(),
fontSize = 14,
modifier = Modifier
.padding(start = 7.dp)
.width(24.dp)
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MomentBottomOperateRowGroup(
onLikeClick: () -> Unit = {},
onAddComment: () -> Unit = {},
onCommentClick: () -> Unit = {},
onFavoriteClick: () -> Unit = {},
momentEntity: MomentEntity,
imageIndex: Int = 0
) {
var showCommentModal by remember { mutableStateOf(false) }
if (showCommentModal) {
ModalBottomSheet(
onDismissRequest = { showCommentModal = false },
containerColor = Color.White,
sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
),
windowInsets = WindowInsets(0),
dragHandle = {
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.clip(CircleShape)
) {
}
}
) {
CommentModalContent(
postId = momentEntity.id,
commentCount = momentEntity.commentCount,
onCommentAdded = {
onAddComment()
}
)
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.padding(start = 16.dp, end = 0.dp)
) {
Row(
modifier = Modifier.fillMaxSize()
) {
Box(
modifier = Modifier.fillMaxHeight(),
contentAlignment = Alignment.Center
) {
MomentOperateBtn(count = momentEntity.likeCount.toString()) {
AnimatedLikeIcon(
modifier = Modifier.size(24.dp),
liked = momentEntity.liked
) {
onLikeClick()
}
}
}
Spacer(modifier = Modifier.width(4.dp))
Box(
modifier = Modifier
.fillMaxHeight()
.noRippleClickable {
onCommentClick()
},
contentAlignment = Alignment.Center
) {
MomentOperateBtn(
icon = R.drawable.rider_pro_comment,
count = momentEntity.commentCount.toString()
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.noRippleClickable {
onFavoriteClick()
},
contentAlignment = Alignment.CenterEnd
) {
MomentOperateBtn(count = momentEntity.favoriteCount.toString()) {
AnimatedFavouriteIcon(
modifier = Modifier.size(24.dp),
isFavourite = momentEntity.isFavorite
) {
onFavoriteClick()
}
}
}
}
if (momentEntity.images.size > 1) {
Row(
modifier = Modifier.fillMaxSize(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
momentEntity.images.forEachIndexed { index, _ ->
Box(
modifier = Modifier
.size(4.dp)
.clip(CircleShape)
.background(
if (imageIndex == index) Color.Red else Color.Gray.copy(
alpha = 0.5f
)
)
.padding(1.dp)
)
Spacer(modifier = Modifier.width(8.dp))
}
}
}
}
}
@Composable
fun MomentListLoading() {
CircularProgressIndicator(
modifier =
Modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally),
color = Color.Red
)
}

View File

@@ -26,157 +26,5 @@ import kotlinx.coroutines.launch
object MomentViewModel : ViewModel() {
private val momentService: MomentService = MomentServiceImpl()
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
val momentsFlow = _momentsFlow.asStateFlow()
var existsMoment = mutableStateOf(false)
var refreshing by mutableStateOf(false)
var isFirstLoad = true
fun refreshPager(pullRefresh: Boolean = false) {
if (!isFirstLoad && !pullRefresh) {
return
}
isFirstLoad = false
viewModelScope.launch {
if (pullRefresh) {
refreshing = true
}
// 检查是否有动态
val existMoments =
momentService.getMoments(timelineId = AppState.UserId, pageNumber = 1)
if (existMoments.list.isEmpty()) {
existsMoment.value = true
}
if (pullRefresh) {
refreshing = false
}
Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = {
MomentPagingSource(
MomentRemoteDataSource(momentService),
// 如果没有动态,则显示热门动态
timelineId = if (existMoments.list.isEmpty()) null else AppState.UserId,
trend = if (existMoments.list.isEmpty()) true else null
)
}
).flow.cachedIn(viewModelScope).collectLatest {
_momentsFlow.value = it
}
}
}
fun updateLikeCount(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(likeCount = momentItem.likeCount + 1, liked = true)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun likeMoment(id: Int) {
momentService.likeMoment(id)
updateLikeCount(id)
}
fun updateCommentCount(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(commentCount = momentItem.commentCount + 1)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun onAddComment(id: Int) {
val currentPagingData = _momentsFlow.value
updateCommentCount(id)
}
fun updateDislikeMomentById(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(likeCount = momentItem.likeCount - 1, liked = false)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun dislikeMoment(id: Int) {
momentService.dislikeMoment(id)
updateDislikeMomentById(id)
}
fun updateFavoriteCount(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(favoriteCount = momentItem.favoriteCount + 1, isFavorite = true)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun favoriteMoment(id: Int) {
momentService.favoriteMoment(id)
updateFavoriteCount(id)
}
fun updateUnfavoriteCount(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(favoriteCount = momentItem.favoriteCount - 1, isFavorite = false)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun unfavoriteMoment(id: Int) {
momentService.unfavoriteMoment(id)
updateUnfavoriteCount(id)
}
fun deleteMoment(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.filter { momentItem ->
momentItem.id != id
}
_momentsFlow.value = updatedPagingData
}
/**
* 更新动态评论数
*/
fun updateMomentCommentCount(id: Int, delta: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(commentCount = momentItem.commentCount + delta)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
fun ResetModel() {
_momentsFlow.value = PagingData.empty()
isFirstLoad = true
}
}

View File

@@ -0,0 +1,82 @@
package com.aiosman.riderpro.ui.index.tabs.moment.tabs.expolre
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.paging.compose.collectAsLazyPagingItems
import com.aiosman.riderpro.ui.composables.MomentCard
import kotlinx.coroutines.launch
/**
* 动态列表
*/
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ExploreMomentsList() {
val model = MomentExploreViewModel
var dataFlow = model.momentsFlow
var moments = dataFlow.collectAsLazyPagingItems()
val scope = rememberCoroutineScope()
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
model.refreshPager(
pullRefresh = true
)
})
LaunchedEffect(Unit) {
model.refreshPager()
}
Column(
modifier = Modifier
.fillMaxSize()
) {
Box(Modifier.pullRefresh(state)) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
) {
items(
moments.itemCount,
key = { idx -> moments[idx]?.id ?: idx }
) { idx ->
val momentItem = moments[idx] ?: return@items
MomentCard(momentEntity = momentItem,
onAddComment = {
scope.launch {
model.onAddComment(momentItem.id)
}
},
onLikeClick = {
scope.launch {
if (momentItem.liked) {
model.dislikeMoment(momentItem.id)
} else {
model.likeMoment(momentItem.id)
}
}
},
onFavoriteClick = {
scope.launch {
if (momentItem.isFavorite) {
model.unfavoriteMoment(momentItem.id)
} else {
model.favoriteMoment(momentItem.id)
}
}
}
)
}
}
PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))
}
}
}

View File

@@ -0,0 +1,155 @@
package com.aiosman.riderpro.ui.index.tabs.moment.tabs.expolre
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.cachedIn
import androidx.paging.map
import com.aiosman.riderpro.AppState
import com.aiosman.riderpro.data.MomentService
import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.entity.MomentPagingSource
import com.aiosman.riderpro.entity.MomentRemoteDataSource
import com.aiosman.riderpro.entity.MomentServiceImpl
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
object MomentExploreViewModel : ViewModel() {
private val momentService: MomentService = MomentServiceImpl()
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
val momentsFlow = _momentsFlow.asStateFlow()
var existsMoment = mutableStateOf(false)
var refreshing by mutableStateOf(false)
var isFirstLoad = true
fun refreshPager(pullRefresh: Boolean = false) {
if (!isFirstLoad && !pullRefresh) {
return
}
isFirstLoad = false
viewModelScope.launch {
if (pullRefresh) {
refreshing = true
}
// 检查是否有动态
val existMoments =
momentService.getMoments(timelineId = AppState.UserId, pageNumber = 1)
if (existMoments.list.isEmpty()) {
existsMoment.value = true
}
if (pullRefresh) {
refreshing = false
}
Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = {
MomentPagingSource(
MomentRemoteDataSource(momentService),
// 如果没有动态,则显示热门动态
trend = true
)
}
).flow.cachedIn(viewModelScope).collectLatest {
_momentsFlow.value = it
}
}
}
fun updateLikeCount(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(likeCount = momentItem.likeCount + 1, liked = true)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun likeMoment(id: Int) {
momentService.likeMoment(id)
updateLikeCount(id)
}
fun updateCommentCount(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(commentCount = momentItem.commentCount + 1)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun onAddComment(id: Int) {
val currentPagingData = _momentsFlow.value
updateCommentCount(id)
}
fun updateDislikeMomentById(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(likeCount = momentItem.likeCount - 1, liked = false)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun dislikeMoment(id: Int) {
momentService.dislikeMoment(id)
updateDislikeMomentById(id)
}
fun updateFavoriteCount(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(favoriteCount = momentItem.favoriteCount + 1, isFavorite = true)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun favoriteMoment(id: Int) {
momentService.favoriteMoment(id)
updateFavoriteCount(id)
}
fun updateUnfavoriteCount(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(favoriteCount = momentItem.favoriteCount - 1, isFavorite = false)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun unfavoriteMoment(id: Int) {
momentService.unfavoriteMoment(id)
updateUnfavoriteCount(id)
}
fun ResetModel() {
_momentsFlow.value = PagingData.empty()
isFirstLoad = true
}
}

View File

@@ -0,0 +1,87 @@
package com.aiosman.riderpro.ui.index.tabs.moment.tabs.timeline
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.paging.compose.collectAsLazyPagingItems
import com.aiosman.riderpro.ui.composables.MomentCard
import kotlinx.coroutines.launch
/**
* 动态列表
*/
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun TimelineMomentsList() {
val model = TimelineMomentViewModel
var dataFlow = model.momentsFlow
var moments = dataFlow.collectAsLazyPagingItems()
val scope = rememberCoroutineScope()
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
model.refreshPager(
pullRefresh = true
)
})
LaunchedEffect(Unit) {
model.refreshPager()
}
Column(
modifier = Modifier
.fillMaxSize()
) {
Box(Modifier.pullRefresh(state)) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
) {
items(
moments.itemCount,
key = { idx -> moments[idx]?.id ?: idx }
) { idx ->
val momentItem = moments[idx] ?: return@items
MomentCard(momentEntity = momentItem,
onAddComment = {
scope.launch {
model.onAddComment(momentItem.id)
}
},
onLikeClick = {
scope.launch {
if (momentItem.liked) {
model.dislikeMoment(momentItem.id)
} else {
model.likeMoment(momentItem.id)
}
}
},
onFavoriteClick = {
scope.launch {
if (momentItem.isFavorite) {
model.unfavoriteMoment(momentItem.id)
} else {
model.favoriteMoment(momentItem.id)
}
}
}
)
// Box(
// modifier = Modifier
// .height(4.dp)
// .fillMaxWidth()
// .background(Color(0xFFF0F2F5))
// )
}
}
PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))
}
}
}

View File

@@ -0,0 +1,180 @@
package com.aiosman.riderpro.ui.index.tabs.moment.tabs.timeline
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.cachedIn
import androidx.paging.filter
import androidx.paging.map
import com.aiosman.riderpro.AppState
import com.aiosman.riderpro.data.MomentService
import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.entity.MomentPagingSource
import com.aiosman.riderpro.entity.MomentRemoteDataSource
import com.aiosman.riderpro.entity.MomentServiceImpl
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
object TimelineMomentViewModel : ViewModel() {
private val momentService: MomentService = MomentServiceImpl()
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
val momentsFlow = _momentsFlow.asStateFlow()
var existsMoment = mutableStateOf(false)
var refreshing by mutableStateOf(false)
var isFirstLoad = true
fun refreshPager(pullRefresh: Boolean = false) {
if (!isFirstLoad && !pullRefresh) {
return
}
isFirstLoad = false
viewModelScope.launch {
if (pullRefresh) {
refreshing = true
}
// 检查是否有动态
val existMoments =
momentService.getMoments(timelineId = AppState.UserId, pageNumber = 1)
if (existMoments.list.isEmpty()) {
existsMoment.value = true
}
if (pullRefresh) {
refreshing = false
}
Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = {
MomentPagingSource(
MomentRemoteDataSource(momentService),
// 如果没有动态,则显示热门动态
timelineId = if (existMoments.list.isEmpty()) null else AppState.UserId,
trend = if (existMoments.list.isEmpty()) true else null
)
}
).flow.cachedIn(viewModelScope).collectLatest {
_momentsFlow.value = it
}
}
}
fun updateLikeCount(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(likeCount = momentItem.likeCount + 1, liked = true)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun likeMoment(id: Int) {
momentService.likeMoment(id)
updateLikeCount(id)
}
fun updateCommentCount(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(commentCount = momentItem.commentCount + 1)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun onAddComment(id: Int) {
val currentPagingData = _momentsFlow.value
updateCommentCount(id)
}
fun updateDislikeMomentById(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(likeCount = momentItem.likeCount - 1, liked = false)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun dislikeMoment(id: Int) {
momentService.dislikeMoment(id)
updateDislikeMomentById(id)
}
fun updateFavoriteCount(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(favoriteCount = momentItem.favoriteCount + 1, isFavorite = true)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun favoriteMoment(id: Int) {
momentService.favoriteMoment(id)
updateFavoriteCount(id)
}
fun updateUnfavoriteCount(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(favoriteCount = momentItem.favoriteCount - 1, isFavorite = false)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
suspend fun unfavoriteMoment(id: Int) {
momentService.unfavoriteMoment(id)
updateUnfavoriteCount(id)
}
fun deleteMoment(id: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.filter { momentItem ->
momentItem.id != id
}
_momentsFlow.value = updatedPagingData
}
/**
* 更新动态评论数
*/
fun updateMomentCommentCount(id: Int, delta: Int) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(commentCount = momentItem.commentCount + delta)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
fun ResetModel() {
_momentsFlow.value = PagingData.empty()
isFirstLoad = true
}
}

View File

@@ -2,10 +2,8 @@ package com.aiosman.riderpro.ui.index.tabs.search
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
@@ -60,9 +58,8 @@ import com.aiosman.riderpro.AppState
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.entity.AccountProfileEntity
import com.aiosman.riderpro.ui.composables.ActionButton
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
import com.aiosman.riderpro.ui.index.tabs.moment.MomentCard
import com.aiosman.riderpro.ui.composables.MomentCard
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.launch

View File

@@ -33,7 +33,7 @@ import androidx.paging.LoadState
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.compose.collectAsLazyPagingItems
import com.aiosman.riderpro.ui.index.tabs.moment.MomentListLoading
import com.aiosman.riderpro.ui.composables.MomentListLoading
import com.aiosman.riderpro.R
import com.aiosman.riderpro.model.ChatNotificationData
import com.aiosman.riderpro.model.TestChatBackend

View File

@@ -17,6 +17,7 @@ import com.aiosman.riderpro.data.CommentServiceImpl
import com.aiosman.riderpro.entity.CommentEntity
import com.aiosman.riderpro.entity.CommentPagingSource
import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel
import com.aiosman.riderpro.ui.index.tabs.moment.tabs.timeline.TimelineMomentViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
@@ -200,7 +201,7 @@ class CommentsViewModel(
replyUserId = replyUserId,
replyCommentId = replyCommentId
)
MomentViewModel.updateCommentCount(postId.toInt())
TimelineMomentViewModel.updateCommentCount(postId.toInt())
// add to first
addedCommentList = listOf(comment) + addedCommentList
}

View File

@@ -16,6 +16,7 @@ import com.aiosman.riderpro.data.UploadImage
import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.exp.rotate
import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel
import com.aiosman.riderpro.ui.index.tabs.moment.tabs.timeline.TimelineMomentViewModel
import com.aiosman.riderpro.ui.index.tabs.profile.MyProfileViewModel
import com.aiosman.riderpro.ui.modification.Modification
import com.aiosman.riderpro.utils.FileUtil
@@ -160,7 +161,7 @@ object NewPostViewModel : ViewModel() {
momentService.createMoment(textContent, 1, uploadImageList, relPostId)
// 刷新个人动态
MyProfileViewModel.loadProfile(pullRefresh = true)
MomentViewModel.refreshPager()
TimelineMomentViewModel.refreshPager()
}

View File

@@ -14,6 +14,8 @@ import com.aiosman.riderpro.entity.AccountProfileEntity
import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.entity.MomentServiceImpl
import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel
import com.aiosman.riderpro.ui.index.tabs.moment.tabs.timeline.TimelineMomentViewModel
import com.aiosman.riderpro.ui.index.tabs.moment.tabs.timeline.TimelineMomentsList
import kotlinx.coroutines.launch
@@ -80,7 +82,7 @@ class PostViewModel(
moment?.let {
service.likeMoment(it.id)
moment = moment?.copy(likeCount = moment?.likeCount?.plus(1) ?: 0, liked = true)
MomentViewModel.updateLikeCount(it.id)
TimelineMomentViewModel.updateLikeCount(it.id)
}
}
@@ -89,7 +91,7 @@ class PostViewModel(
service.dislikeMoment(it.id)
moment = moment?.copy(likeCount = moment?.likeCount?.minus(1) ?: 0, liked = false)
// update home list
MomentViewModel.updateDislikeMomentById(it.id)
TimelineMomentViewModel.updateDislikeMomentById(it.id)
}
}
@@ -130,7 +132,7 @@ class PostViewModel(
commentsViewModel.deleteComment(commentId)
moment = moment?.copy(commentCount = moment?.commentCount?.minus(1) ?: 0)
moment?.let {
MomentViewModel.updateMomentCommentCount(it.id, -1)
TimelineMomentViewModel.updateMomentCommentCount(it.id, -1)
}
}
@@ -159,7 +161,7 @@ class PostViewModel(
viewModelScope.launch {
moment?.let {
service.deleteMoment(it.id)
MomentViewModel.deleteMoment(it.id)
TimelineMomentViewModel.deleteMoment(it.id)
}
callback()
}