更新动态详情页
- 动态详情页UI更新 - 动态详情页新增删除功能 - 新增下拉菜单组件 - 消息列表UI更新 - 搜索页UI更新 - 图片查看器移除状态栏黑色遮罩 - 更新API,新增删除动态接口 - 更新MomentService,新增删除动态方法 - 更新Moment类,新增删除动态方法 - 更新NewPostViewModel,发布动态后刷新动态列表 - 更新MomentViewModel,
This commit is contained in:
@@ -148,6 +148,11 @@ interface MomentService {
|
||||
* @param id 动态ID
|
||||
*/
|
||||
suspend fun unfavoriteMoment(id: Int)
|
||||
|
||||
/**
|
||||
* 删除动态
|
||||
*/
|
||||
suspend fun deleteMoment(id: Int)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.PATCH
|
||||
@@ -251,4 +252,9 @@ interface RiderProAPI {
|
||||
@POST("register/google")
|
||||
suspend fun registerWithGoogle(@Body body: GoogleRegisterRequestBody): Response<AuthResult>
|
||||
|
||||
@DELETE("post/{id}")
|
||||
suspend fun deletePost(
|
||||
@Path("id") id: Int
|
||||
): Response<Unit>
|
||||
|
||||
}
|
||||
@@ -123,6 +123,10 @@ class MomentServiceImpl() : MomentService {
|
||||
momentBackend.unfavoriteMoment(id)
|
||||
}
|
||||
|
||||
override suspend fun deleteMoment(id: Int) {
|
||||
momentBackend.deleteMoment(id)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MomentBackend {
|
||||
@@ -195,6 +199,10 @@ class MomentBackend {
|
||||
ApiClient.api.unfavoritePost(id)
|
||||
}
|
||||
|
||||
suspend fun deleteMoment(id: Int) {
|
||||
ApiClient.api.deletePost(id)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.aiosman.riderpro.ui.composables
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
data class MenuItem(
|
||||
val title: String,
|
||||
val icon: Int,
|
||||
val action: () -> Unit
|
||||
|
||||
)
|
||||
@Composable
|
||||
fun DropdownMenu(
|
||||
expanded: Boolean = false,
|
||||
menuItems: List<MenuItem> = emptyList(),
|
||||
width: Int? = null,
|
||||
onDismissRequest: () -> Unit = {},
|
||||
) {
|
||||
MaterialTheme(
|
||||
shapes = MaterialTheme.shapes.copy(
|
||||
extraSmall = RoundedCornerShape(
|
||||
16.dp
|
||||
)
|
||||
)
|
||||
) {
|
||||
androidx.compose.material3.DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = onDismissRequest,
|
||||
modifier = Modifier
|
||||
.apply {
|
||||
width?.let { width(width.dp) }
|
||||
}
|
||||
.background(Color.White)
|
||||
) {
|
||||
for (item in menuItems) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 14.dp, horizontal = 24.dp)
|
||||
.noRippleClickable {
|
||||
item.action()
|
||||
}) {
|
||||
Row {
|
||||
Text(
|
||||
item.title,
|
||||
fontWeight = FontWeight.W500
|
||||
)
|
||||
if (width != null) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
}else{
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
}
|
||||
Icon(
|
||||
painter = painterResource(id = item.icon),
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,7 @@ fun ImageViewer() {
|
||||
val animatedVisibilityScope = LocalAnimatedContentScope.current
|
||||
val context = LocalContext.current
|
||||
LaunchedEffect(Unit) {
|
||||
systemUiController.setStatusBarColor(Color.Black)
|
||||
systemUiController.setNavigationBarColor(Color.Black)
|
||||
|
||||
}
|
||||
StatusBarMaskLayout(
|
||||
modifier = Modifier.background(Color.Black),
|
||||
|
||||
@@ -51,6 +51,7 @@ import com.aiosman.riderpro.exp.timeAgo
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
|
||||
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -80,11 +81,7 @@ fun NotificationsScreen() {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier.padding(
|
||||
bottom = navigationBarPaddings
|
||||
)
|
||||
)
|
||||
StatusBarSpacer()
|
||||
Box(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
@@ -92,17 +89,6 @@ fun NotificationsScreen() {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 24.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_message_title),
|
||||
contentDescription = "Back",
|
||||
modifier = Modifier.width(120.dp)
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
@@ -90,19 +90,13 @@ fun MomentsList() {
|
||||
var dataFlow = model.momentsFlow
|
||||
var moments = dataFlow.collectAsLazyPagingItems()
|
||||
val scope = rememberCoroutineScope()
|
||||
var refreshing by remember { mutableStateOf(false) }
|
||||
val state = rememberPullRefreshState(refreshing, onRefresh = {
|
||||
model.refreshPager()
|
||||
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
|
||||
model.refreshPager(
|
||||
pullRefresh = true
|
||||
)
|
||||
})
|
||||
val navigationBarPaddings =
|
||||
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
|
||||
LaunchedEffect(moments.loadState) {
|
||||
if (moments.loadState.refresh is LoadState.Loading) {
|
||||
refreshing = true
|
||||
} else {
|
||||
refreshing = false
|
||||
}
|
||||
}
|
||||
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -154,7 +148,7 @@ fun MomentsList() {
|
||||
)
|
||||
}
|
||||
}
|
||||
PullRefreshIndicator(refreshing, state, Modifier.align(Alignment.TopCenter))
|
||||
PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -388,32 +382,6 @@ fun PostImageView(
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
// Indicator container
|
||||
// if (images.size > 1) {
|
||||
// Row(
|
||||
// modifier = Modifier
|
||||
// .padding(8.dp)
|
||||
// .fillMaxWidth(),
|
||||
// horizontalArrangement = Arrangement.Center
|
||||
// ) {
|
||||
// images.forEachIndexed { index, _ ->
|
||||
// Box(
|
||||
// modifier = Modifier
|
||||
// .size(8.dp)
|
||||
// .clip(CircleShape)
|
||||
// .background(
|
||||
// if (pagerState.currentPage == index) Color.Red else Color.Gray.copy(
|
||||
// alpha = 0.5f
|
||||
// )
|
||||
// )
|
||||
// .padding(4.dp)
|
||||
// )
|
||||
// Spacer(modifier = Modifier.width(8.dp))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ 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.data.AccountService
|
||||
import com.aiosman.riderpro.entity.MomentPagingSource
|
||||
@@ -28,17 +29,25 @@ object MomentViewModel : ViewModel() {
|
||||
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
||||
val momentsFlow = _momentsFlow.asStateFlow()
|
||||
val accountService: AccountService = AccountServiceImpl()
|
||||
var existsException = mutableStateOf(false)
|
||||
var existsMoment = mutableStateOf(false)
|
||||
var refreshing by mutableStateOf(false)
|
||||
init {
|
||||
refreshPager()
|
||||
}
|
||||
fun refreshPager() {
|
||||
|
||||
fun refreshPager(pullRefresh: Boolean = false) {
|
||||
viewModelScope.launch {
|
||||
if (pullRefresh) {
|
||||
refreshing = true
|
||||
}
|
||||
val profile = accountService.getMyAccountProfile()
|
||||
// 检查是否有动态
|
||||
val existMoments = momentService.getMoments(timelineId = profile.id, pageNumber = 1)
|
||||
if (existMoments.list.isEmpty()) {
|
||||
existsException.value = true
|
||||
existsMoment.value = true
|
||||
}
|
||||
if (pullRefresh) {
|
||||
refreshing = false
|
||||
}
|
||||
Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
@@ -84,6 +93,7 @@ object MomentViewModel : ViewModel() {
|
||||
}
|
||||
_momentsFlow.value = updatedPagingData
|
||||
}
|
||||
|
||||
suspend fun onAddComment(id: Int) {
|
||||
val currentPagingData = _momentsFlow.value
|
||||
updateCommentCount(id)
|
||||
@@ -118,10 +128,12 @@ object MomentViewModel : ViewModel() {
|
||||
}
|
||||
_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 ->
|
||||
@@ -133,8 +145,17 @@ object MomentViewModel : ViewModel() {
|
||||
}
|
||||
_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
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,6 @@ fun SearchScreen() {
|
||||
}
|
||||
|
||||
if (model.showResult) {
|
||||
Spacer(modifier = Modifier.padding(8.dp))
|
||||
TabRow(
|
||||
selectedTabIndex = selectedTabIndex.value,
|
||||
backgroundColor = Color.White,
|
||||
@@ -236,16 +235,23 @@ fun MomentResultTab() {
|
||||
var dataFlow = model.momentsFlow
|
||||
var moments = dataFlow.collectAsLazyPagingItems()
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFF0F2F5))
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
items(moments.itemCount) { idx ->
|
||||
val momentItem = moments[idx] ?: return@items
|
||||
Spacer(modifier = Modifier.padding(8.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth().background(Color.White)
|
||||
) {
|
||||
MomentCard(momentEntity = momentItem, hideAction = true)
|
||||
}
|
||||
Spacer(modifier = Modifier.padding(16.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.aiosman.riderpro.data.MomentService
|
||||
import com.aiosman.riderpro.entity.MomentServiceImpl
|
||||
import com.aiosman.riderpro.data.UploadImage
|
||||
import com.aiosman.riderpro.entity.MomentEntity
|
||||
import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.MyProfileViewModel
|
||||
import com.aiosman.riderpro.ui.modification.Modification
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -78,6 +79,7 @@ object NewPostViewModel : ViewModel() {
|
||||
momentService.createMoment(textContent, 1, uploadImageList, relPostId)
|
||||
// 刷新个人动态
|
||||
MyProfileViewModel.loadProfile()
|
||||
MomentViewModel.refreshPager()
|
||||
}
|
||||
|
||||
suspend fun init() {
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package com.aiosman.riderpro.ui.post
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
@@ -30,12 +28,7 @@ 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.Edit
|
||||
import androidx.compose.material.icons.filled.Favorite
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
@@ -52,6 +45,7 @@ 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.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -62,235 +56,45 @@ import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
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.compose.LazyPagingItems
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.map
|
||||
import com.aiosman.riderpro.AppState
|
||||
import com.aiosman.riderpro.LocalAnimatedContentScope
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.LocalSharedTransitionScope
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.entity.AccountProfileEntity
|
||||
import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.entity.CommentEntity
|
||||
import com.aiosman.riderpro.entity.CommentPagingSource
|
||||
import com.aiosman.riderpro.data.CommentRemoteDataSource
|
||||
import com.aiosman.riderpro.data.CommentService
|
||||
import com.aiosman.riderpro.data.CommentServiceImpl
|
||||
import com.aiosman.riderpro.data.MomentService
|
||||
import com.aiosman.riderpro.data.AccountServiceImpl
|
||||
import com.aiosman.riderpro.entity.MomentServiceImpl
|
||||
import com.aiosman.riderpro.data.UserServiceImpl
|
||||
import com.aiosman.riderpro.data.UserService
|
||||
import com.aiosman.riderpro.exp.formatPostTime
|
||||
import com.aiosman.riderpro.exp.timeAgo
|
||||
import com.aiosman.riderpro.entity.MomentEntity
|
||||
import com.aiosman.riderpro.entity.MomentImageEntity
|
||||
import com.aiosman.riderpro.exp.formatPostTime
|
||||
import com.aiosman.riderpro.exp.timeAgo
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.composables.AnimatedFavouriteIcon
|
||||
import com.aiosman.riderpro.ui.composables.AnimatedLikeIcon
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
|
||||
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.riderpro.ui.composables.EditCommentBottomModal
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
||||
import com.aiosman.riderpro.ui.imageviewer.ImageViewerViewModel
|
||||
import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object PostViewModel : ViewModel() {
|
||||
var service: MomentService = MomentServiceImpl()
|
||||
var commentService: CommentService = CommentServiceImpl()
|
||||
var userService: UserService = UserServiceImpl()
|
||||
private var _commentsFlow = MutableStateFlow<PagingData<CommentEntity>>(PagingData.empty())
|
||||
val commentsFlow = _commentsFlow.asStateFlow()
|
||||
var postId: String = ""
|
||||
|
||||
// 预加载的 moment
|
||||
|
||||
var accountProfileEntity by mutableStateOf<AccountProfileEntity?>(null)
|
||||
var moment by mutableStateOf<MomentEntity?>(null)
|
||||
var accountService: AccountService = AccountServiceImpl()
|
||||
|
||||
/**
|
||||
* 预加载,在跳转到 PostScreen 之前设置好内容
|
||||
*/
|
||||
fun preTransit(momentEntity: MomentEntity?) {
|
||||
this.postId = momentEntity?.id.toString()
|
||||
this.moment = momentEntity
|
||||
viewModelScope.launch {
|
||||
Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
pagingSourceFactory = {
|
||||
CommentPagingSource(
|
||||
CommentRemoteDataSource(commentService),
|
||||
postId = postId.toInt()
|
||||
)
|
||||
}
|
||||
).flow.cachedIn(viewModelScope).collectLatest {
|
||||
_commentsFlow.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun reloadComment() {
|
||||
viewModelScope.launch {
|
||||
Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
pagingSourceFactory = {
|
||||
CommentPagingSource(
|
||||
CommentRemoteDataSource(commentService),
|
||||
postId = postId.toInt()
|
||||
)
|
||||
}
|
||||
).flow.cachedIn(viewModelScope).collectLatest {
|
||||
_commentsFlow.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun initData() {
|
||||
moment = service.getMomentById(postId.toInt())
|
||||
// moment?.let {
|
||||
// accountProfileEntity = userService.getUserProfile(it.authorId.toString())
|
||||
// }
|
||||
}
|
||||
|
||||
suspend fun likeComment(commentId: Int) {
|
||||
commentService.likeComment(commentId)
|
||||
val currentPagingData = commentsFlow.value
|
||||
val updatedPagingData = currentPagingData.map { comment ->
|
||||
if (comment.id == commentId) {
|
||||
comment.copy(liked = !comment.liked, likes = comment.likes + 1)
|
||||
} else {
|
||||
comment
|
||||
}
|
||||
}
|
||||
_commentsFlow.value = updatedPagingData
|
||||
}
|
||||
|
||||
suspend fun unlikeComment(commentId: Int) {
|
||||
commentService.dislikeComment(commentId)
|
||||
val currentPagingData = commentsFlow.value
|
||||
val updatedPagingData = currentPagingData.map { comment ->
|
||||
if (comment.id == commentId) {
|
||||
comment.copy(liked = !comment.liked, likes = comment.likes - 1)
|
||||
} else {
|
||||
comment
|
||||
}
|
||||
}
|
||||
_commentsFlow.value = updatedPagingData
|
||||
}
|
||||
|
||||
suspend fun createComment(content: String) {
|
||||
commentService.createComment(postId.toInt(), content)
|
||||
this.moment = service.getMomentById(postId.toInt())
|
||||
MomentViewModel.updateCommentCount(postId.toInt())
|
||||
reloadComment()
|
||||
}
|
||||
|
||||
suspend fun likeMoment() {
|
||||
moment?.let {
|
||||
service.likeMoment(it.id)
|
||||
moment = moment?.copy(likeCount = moment?.likeCount?.plus(1) ?: 0, liked = true)
|
||||
MomentViewModel.updateLikeCount(it.id)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun dislikeMoment() {
|
||||
moment?.let {
|
||||
service.dislikeMoment(it.id)
|
||||
moment = moment?.copy(likeCount = moment?.likeCount?.minus(1) ?: 0, liked = false)
|
||||
// update home list
|
||||
MomentViewModel.updateDislikeMomentById(it.id)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun favoriteMoment() {
|
||||
moment?.let {
|
||||
service.favoriteMoment(it.id)
|
||||
moment =
|
||||
moment?.copy(favoriteCount = moment?.favoriteCount?.plus(1) ?: 0, isFavorite = true)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun unfavoriteMoment() {
|
||||
moment?.let {
|
||||
service.unfavoriteMoment(it.id)
|
||||
moment = moment?.copy(
|
||||
favoriteCount = moment?.favoriteCount?.minus(1) ?: 0,
|
||||
isFavorite = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun followUser() {
|
||||
accountProfileEntity?.let {
|
||||
userService.followUser(it.id.toString())
|
||||
accountProfileEntity = accountProfileEntity?.copy(isFollowing = true)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun unfollowUser() {
|
||||
accountProfileEntity?.let {
|
||||
userService.unFollowUser(it.id.toString())
|
||||
accountProfileEntity = accountProfileEntity?.copy(isFollowing = false)
|
||||
}
|
||||
}
|
||||
|
||||
var avatar: String? = null
|
||||
get() {
|
||||
accountProfileEntity?.avatar?.let {
|
||||
return it
|
||||
}
|
||||
moment?.avatar?.let {
|
||||
return it
|
||||
}
|
||||
return field
|
||||
}
|
||||
var nickname: String? = null
|
||||
get() {
|
||||
accountProfileEntity?.nickName?.let {
|
||||
return it
|
||||
}
|
||||
moment?.nickname?.let {
|
||||
return it
|
||||
}
|
||||
return field
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PostScreen(
|
||||
id: String,
|
||||
) {
|
||||
val viewModel = PostViewModel
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val commentsPagging = viewModel.commentsFlow.collectAsLazyPagingItems()
|
||||
var showCollapseContent by remember { mutableStateOf(false) }
|
||||
val scrollState = rememberLazyListState()
|
||||
val showMenuModal by remember { mutableStateOf(false) }
|
||||
val navController = LocalNavController.current
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.initData()
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
bottomBar = {
|
||||
BottomNavigationBar(
|
||||
PostBottomBar(
|
||||
onLikeClick = {
|
||||
scope.launch {
|
||||
if (viewModel.moment?.liked == true) {
|
||||
@@ -338,6 +142,11 @@ fun PostScreen(
|
||||
viewModel.followUser()
|
||||
}
|
||||
}
|
||||
},
|
||||
onDeleteClick = {
|
||||
viewModel.deleteMoment {
|
||||
navController.popBackStack()
|
||||
}
|
||||
}
|
||||
)
|
||||
LazyColumn(
|
||||
@@ -352,13 +161,11 @@ fun PostScreen(
|
||||
.aspectRatio(383f / 527f)
|
||||
) {
|
||||
PostImageView(
|
||||
id,
|
||||
viewModel.moment?.images ?: emptyList()
|
||||
)
|
||||
|
||||
}
|
||||
PostDetails(
|
||||
id,
|
||||
viewModel.moment
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
@@ -417,16 +224,40 @@ fun PostScreen(
|
||||
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun Header(
|
||||
avatar: String?,
|
||||
nickname: String?,
|
||||
userId: Int?,
|
||||
isFollowing: Boolean,
|
||||
onFollowClick: () -> Unit
|
||||
onFollowClick: () -> Unit,
|
||||
onDeleteClick: () -> Unit = {}
|
||||
) {
|
||||
val navController = LocalNavController.current
|
||||
val context = LocalContext.current
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
if (expanded) {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = {
|
||||
expanded = false
|
||||
},
|
||||
containerColor = Color.White,
|
||||
sheetState = rememberModalBottomSheetState(
|
||||
skipPartiallyExpanded = true
|
||||
),
|
||||
dragHandle = {},
|
||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||
windowInsets = WindowInsets(0)
|
||||
) {
|
||||
PostMenuModal(
|
||||
onDeleteClick = {
|
||||
onDeleteClick()
|
||||
expanded = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -498,8 +329,18 @@ fun Header(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Box {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.height(20.dp)
|
||||
.noRippleClickable {
|
||||
expanded = true
|
||||
},
|
||||
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
|
||||
contentDescription = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,7 +348,6 @@ fun Header(
|
||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalSharedTransitionApi::class)
|
||||
@Composable
|
||||
fun PostImageView(
|
||||
postId: String,
|
||||
images: List<MomentImageEntity>,
|
||||
) {
|
||||
val pagerState = rememberPagerState(pageCount = { images.size })
|
||||
@@ -579,7 +419,6 @@ fun PostImageView(
|
||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||
@Composable
|
||||
fun PostDetails(
|
||||
postId: String,
|
||||
momentEntity: MomentEntity?
|
||||
) {
|
||||
|
||||
@@ -665,8 +504,8 @@ fun CommentItem(commentEntity: CommentEntity, onLike: () -> Unit = {}) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text(text = commentEntity.name, fontWeight = FontWeight.Bold, fontSize = 14.sp)
|
||||
Text(text = commentEntity.comment, fontSize = 12.sp)
|
||||
Text(text = commentEntity.name, fontWeight = FontWeight.W600, fontSize = 14.sp)
|
||||
Text(text = commentEntity.comment, fontSize = 14.sp)
|
||||
Text(
|
||||
text = commentEntity.date.timeAgo(context),
|
||||
fontSize = 12.sp,
|
||||
@@ -680,9 +519,9 @@ fun CommentItem(commentEntity: CommentEntity, onLike: () -> Unit = {}) {
|
||||
AnimatedLikeIcon(
|
||||
liked = commentEntity.liked,
|
||||
onClick = onLike,
|
||||
modifier = Modifier.size(16.dp)
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
Text(text = commentEntity.likes.toString(), fontSize = 11.sp)
|
||||
Text(text = commentEntity.likes.toString(), fontSize = 12.sp)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@@ -698,7 +537,7 @@ fun CommentItem(commentEntity: CommentEntity, onLike: () -> Unit = {}) {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun BottomNavigationBar(
|
||||
fun PostBottomBar(
|
||||
onCreateComment: (String) -> Unit = {},
|
||||
onLikeClick: () -> Unit = {},
|
||||
onFavoriteClick: () -> Unit = {},
|
||||
@@ -790,3 +629,50 @@ fun BottomNavigationBar(
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PostMenuModal(
|
||||
onDeleteClick: () -> Unit = {}
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 24.dp, horizontal = 24.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.background(Color(0xFFE5E5E5))
|
||||
.padding(8.dp)
|
||||
.noRippleClickable {
|
||||
onDeleteClick()
|
||||
}
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_delete),
|
||||
contentDescription = "Delete",
|
||||
modifier = Modifier.size(24.dp),
|
||||
colorFilter = ColorFilter.tint(Color.Black)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = "Delete",
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user