更新动态详情页
- 动态详情页UI更新 - 动态详情页新增删除功能 - 新增下拉菜单组件 - 消息列表UI更新 - 搜索页UI更新 - 图片查看器移除状态栏黑色遮罩 - 更新API,新增删除动态接口 - 更新MomentService,新增删除动态方法 - 更新Moment类,新增删除动态方法 - 更新NewPostViewModel,发布动态后刷新动态列表 - 更新MomentViewModel,
This commit is contained in:
@@ -148,6 +148,11 @@ interface MomentService {
|
|||||||
* @param id 动态ID
|
* @param id 动态ID
|
||||||
*/
|
*/
|
||||||
suspend fun unfavoriteMoment(id: Int)
|
suspend fun unfavoriteMoment(id: Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除动态
|
||||||
|
*/
|
||||||
|
suspend fun deleteMoment(id: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import okhttp3.MultipartBody
|
|||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.DELETE
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.Multipart
|
import retrofit2.http.Multipart
|
||||||
import retrofit2.http.PATCH
|
import retrofit2.http.PATCH
|
||||||
@@ -251,4 +252,9 @@ interface RiderProAPI {
|
|||||||
@POST("register/google")
|
@POST("register/google")
|
||||||
suspend fun registerWithGoogle(@Body body: GoogleRegisterRequestBody): Response<AuthResult>
|
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)
|
momentBackend.unfavoriteMoment(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun deleteMoment(id: Int) {
|
||||||
|
momentBackend.deleteMoment(id)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MomentBackend {
|
class MomentBackend {
|
||||||
@@ -195,6 +199,10 @@ class MomentBackend {
|
|||||||
ApiClient.api.unfavoritePost(id)
|
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 animatedVisibilityScope = LocalAnimatedContentScope.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
systemUiController.setStatusBarColor(Color.Black)
|
|
||||||
systemUiController.setNavigationBarColor(Color.Black)
|
|
||||||
}
|
}
|
||||||
StatusBarMaskLayout(
|
StatusBarMaskLayout(
|
||||||
modifier = Modifier.background(Color.Black),
|
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.NavigationRoute
|
||||||
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
|
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
|
||||||
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||||
|
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
||||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -80,11 +81,7 @@ fun NotificationsScreen() {
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
Spacer(
|
StatusBarSpacer()
|
||||||
modifier = Modifier.padding(
|
|
||||||
bottom = navigationBarPaddings
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Box(modifier = Modifier
|
Box(modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
@@ -92,17 +89,6 @@ fun NotificationsScreen() {
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxSize(),
|
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(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|||||||
@@ -90,19 +90,13 @@ fun MomentsList() {
|
|||||||
var dataFlow = model.momentsFlow
|
var dataFlow = model.momentsFlow
|
||||||
var moments = dataFlow.collectAsLazyPagingItems()
|
var moments = dataFlow.collectAsLazyPagingItems()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
var refreshing by remember { mutableStateOf(false) }
|
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
|
||||||
val state = rememberPullRefreshState(refreshing, onRefresh = {
|
model.refreshPager(
|
||||||
model.refreshPager()
|
pullRefresh = true
|
||||||
|
)
|
||||||
})
|
})
|
||||||
val navigationBarPaddings =
|
val navigationBarPaddings =
|
||||||
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
|
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()
|
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
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.PagingConfig
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import androidx.paging.cachedIn
|
import androidx.paging.cachedIn
|
||||||
|
import androidx.paging.filter
|
||||||
import androidx.paging.map
|
import androidx.paging.map
|
||||||
import com.aiosman.riderpro.data.AccountService
|
import com.aiosman.riderpro.data.AccountService
|
||||||
import com.aiosman.riderpro.entity.MomentPagingSource
|
import com.aiosman.riderpro.entity.MomentPagingSource
|
||||||
@@ -28,17 +29,25 @@ object MomentViewModel : ViewModel() {
|
|||||||
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
||||||
val momentsFlow = _momentsFlow.asStateFlow()
|
val momentsFlow = _momentsFlow.asStateFlow()
|
||||||
val accountService: AccountService = AccountServiceImpl()
|
val accountService: AccountService = AccountServiceImpl()
|
||||||
var existsException = mutableStateOf(false)
|
var existsMoment = mutableStateOf(false)
|
||||||
|
var refreshing by mutableStateOf(false)
|
||||||
init {
|
init {
|
||||||
refreshPager()
|
refreshPager()
|
||||||
}
|
}
|
||||||
fun refreshPager() {
|
|
||||||
|
fun refreshPager(pullRefresh: Boolean = false) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
if (pullRefresh) {
|
||||||
|
refreshing = true
|
||||||
|
}
|
||||||
val profile = accountService.getMyAccountProfile()
|
val profile = accountService.getMyAccountProfile()
|
||||||
// 检查是否有动态
|
// 检查是否有动态
|
||||||
val existMoments = momentService.getMoments(timelineId = profile.id, pageNumber = 1)
|
val existMoments = momentService.getMoments(timelineId = profile.id, pageNumber = 1)
|
||||||
if (existMoments.list.isEmpty()) {
|
if (existMoments.list.isEmpty()) {
|
||||||
existsException.value = true
|
existsMoment.value = true
|
||||||
|
}
|
||||||
|
if (pullRefresh) {
|
||||||
|
refreshing = false
|
||||||
}
|
}
|
||||||
Pager(
|
Pager(
|
||||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||||
@@ -84,6 +93,7 @@ object MomentViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
_momentsFlow.value = updatedPagingData
|
_momentsFlow.value = updatedPagingData
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun onAddComment(id: Int) {
|
suspend fun onAddComment(id: Int) {
|
||||||
val currentPagingData = _momentsFlow.value
|
val currentPagingData = _momentsFlow.value
|
||||||
updateCommentCount(id)
|
updateCommentCount(id)
|
||||||
@@ -118,10 +128,12 @@ object MomentViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
_momentsFlow.value = updatedPagingData
|
_momentsFlow.value = updatedPagingData
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun favoriteMoment(id: Int) {
|
suspend fun favoriteMoment(id: Int) {
|
||||||
momentService.favoriteMoment(id)
|
momentService.favoriteMoment(id)
|
||||||
updateFavoriteCount(id)
|
updateFavoriteCount(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateUnfavoriteCount(id: Int) {
|
fun updateUnfavoriteCount(id: Int) {
|
||||||
val currentPagingData = _momentsFlow.value
|
val currentPagingData = _momentsFlow.value
|
||||||
val updatedPagingData = currentPagingData.map { momentItem ->
|
val updatedPagingData = currentPagingData.map { momentItem ->
|
||||||
@@ -133,8 +145,17 @@ object MomentViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
_momentsFlow.value = updatedPagingData
|
_momentsFlow.value = updatedPagingData
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun unfavoriteMoment(id: Int) {
|
suspend fun unfavoriteMoment(id: Int) {
|
||||||
momentService.unfavoriteMoment(id)
|
momentService.unfavoriteMoment(id)
|
||||||
updateUnfavoriteCount(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) {
|
if (model.showResult) {
|
||||||
Spacer(modifier = Modifier.padding(8.dp))
|
|
||||||
TabRow(
|
TabRow(
|
||||||
selectedTabIndex = selectedTabIndex.value,
|
selectedTabIndex = selectedTabIndex.value,
|
||||||
backgroundColor = Color.White,
|
backgroundColor = Color.White,
|
||||||
@@ -236,15 +235,22 @@ fun MomentResultTab() {
|
|||||||
var dataFlow = model.momentsFlow
|
var dataFlow = model.momentsFlow
|
||||||
var moments = dataFlow.collectAsLazyPagingItems()
|
var moments = dataFlow.collectAsLazyPagingItems()
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color(0xFFF0F2F5))
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
items(moments.itemCount) { idx ->
|
items(moments.itemCount) { idx ->
|
||||||
val momentItem = moments[idx] ?: return@items
|
val momentItem = moments[idx] ?: return@items
|
||||||
Spacer(modifier = Modifier.padding(8.dp))
|
Box(
|
||||||
MomentCard(momentEntity = momentItem, hideAction = true)
|
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.entity.MomentServiceImpl
|
||||||
import com.aiosman.riderpro.data.UploadImage
|
import com.aiosman.riderpro.data.UploadImage
|
||||||
import com.aiosman.riderpro.entity.MomentEntity
|
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.index.tabs.profile.MyProfileViewModel
|
||||||
import com.aiosman.riderpro.ui.modification.Modification
|
import com.aiosman.riderpro.ui.modification.Modification
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -78,6 +79,7 @@ object NewPostViewModel : ViewModel() {
|
|||||||
momentService.createMoment(textContent, 1, uploadImageList, relPostId)
|
momentService.createMoment(textContent, 1, uploadImageList, relPostId)
|
||||||
// 刷新个人动态
|
// 刷新个人动态
|
||||||
MyProfileViewModel.loadProfile()
|
MyProfileViewModel.loadProfile()
|
||||||
|
MomentViewModel.refreshPager()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun init() {
|
suspend fun init() {
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package com.aiosman.riderpro.ui.post
|
package com.aiosman.riderpro.ui.post
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||||
import androidx.compose.animation.animateContentSize
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
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.pager.rememberPagerState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
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.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -52,6 +45,7 @@ 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
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
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.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
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.LazyPagingItems
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import androidx.paging.map
|
|
||||||
import com.aiosman.riderpro.AppState
|
import com.aiosman.riderpro.AppState
|
||||||
import com.aiosman.riderpro.LocalAnimatedContentScope
|
import com.aiosman.riderpro.LocalAnimatedContentScope
|
||||||
import com.aiosman.riderpro.LocalNavController
|
import com.aiosman.riderpro.LocalNavController
|
||||||
import com.aiosman.riderpro.LocalSharedTransitionScope
|
import com.aiosman.riderpro.LocalSharedTransitionScope
|
||||||
import com.aiosman.riderpro.R
|
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.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.MomentEntity
|
||||||
import com.aiosman.riderpro.entity.MomentImageEntity
|
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.NavigationRoute
|
||||||
import com.aiosman.riderpro.ui.composables.AnimatedFavouriteIcon
|
import com.aiosman.riderpro.ui.composables.AnimatedFavouriteIcon
|
||||||
import com.aiosman.riderpro.ui.composables.AnimatedLikeIcon
|
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.BottomNavigationPlaceholder
|
||||||
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||||
import com.aiosman.riderpro.ui.composables.EditCommentBottomModal
|
import com.aiosman.riderpro.ui.composables.EditCommentBottomModal
|
||||||
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
||||||
import com.aiosman.riderpro.ui.imageviewer.ImageViewerViewModel
|
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.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
|
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
|
@Composable
|
||||||
fun PostScreen(
|
fun PostScreen(
|
||||||
id: String,
|
id: String,
|
||||||
) {
|
) {
|
||||||
val viewModel = PostViewModel
|
val viewModel = PostViewModel
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
val commentsPagging = viewModel.commentsFlow.collectAsLazyPagingItems()
|
val commentsPagging = viewModel.commentsFlow.collectAsLazyPagingItems()
|
||||||
var showCollapseContent by remember { mutableStateOf(false) }
|
val showMenuModal by remember { mutableStateOf(false) }
|
||||||
val scrollState = rememberLazyListState()
|
val navController = LocalNavController.current
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.initData()
|
viewModel.initData()
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
BottomNavigationBar(
|
PostBottomBar(
|
||||||
onLikeClick = {
|
onLikeClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
if (viewModel.moment?.liked == true) {
|
if (viewModel.moment?.liked == true) {
|
||||||
@@ -338,6 +142,11 @@ fun PostScreen(
|
|||||||
viewModel.followUser()
|
viewModel.followUser()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onDeleteClick = {
|
||||||
|
viewModel.deleteMoment {
|
||||||
|
navController.popBackStack()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
@@ -352,13 +161,11 @@ fun PostScreen(
|
|||||||
.aspectRatio(383f / 527f)
|
.aspectRatio(383f / 527f)
|
||||||
) {
|
) {
|
||||||
PostImageView(
|
PostImageView(
|
||||||
id,
|
|
||||||
viewModel.moment?.images ?: emptyList()
|
viewModel.moment?.images ?: emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
PostDetails(
|
PostDetails(
|
||||||
id,
|
|
||||||
viewModel.moment
|
viewModel.moment
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
@@ -417,16 +224,40 @@ fun PostScreen(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun Header(
|
fun Header(
|
||||||
avatar: String?,
|
avatar: String?,
|
||||||
nickname: String?,
|
nickname: String?,
|
||||||
userId: Int?,
|
userId: Int?,
|
||||||
isFollowing: Boolean,
|
isFollowing: Boolean,
|
||||||
onFollowClick: () -> Unit
|
onFollowClick: () -> Unit,
|
||||||
|
onDeleteClick: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val context = LocalContext.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(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.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)
|
@OptIn(ExperimentalFoundationApi::class, ExperimentalSharedTransitionApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PostImageView(
|
fun PostImageView(
|
||||||
postId: String,
|
|
||||||
images: List<MomentImageEntity>,
|
images: List<MomentImageEntity>,
|
||||||
) {
|
) {
|
||||||
val pagerState = rememberPagerState(pageCount = { images.size })
|
val pagerState = rememberPagerState(pageCount = { images.size })
|
||||||
@@ -579,7 +419,6 @@ fun PostImageView(
|
|||||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PostDetails(
|
fun PostDetails(
|
||||||
postId: String,
|
|
||||||
momentEntity: MomentEntity?
|
momentEntity: MomentEntity?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -665,8 +504,8 @@ fun CommentItem(commentEntity: CommentEntity, onLike: () -> Unit = {}) {
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
) {
|
) {
|
||||||
Text(text = commentEntity.name, fontWeight = FontWeight.Bold, fontSize = 14.sp)
|
Text(text = commentEntity.name, fontWeight = FontWeight.W600, fontSize = 14.sp)
|
||||||
Text(text = commentEntity.comment, fontSize = 12.sp)
|
Text(text = commentEntity.comment, fontSize = 14.sp)
|
||||||
Text(
|
Text(
|
||||||
text = commentEntity.date.timeAgo(context),
|
text = commentEntity.date.timeAgo(context),
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
@@ -680,9 +519,9 @@ fun CommentItem(commentEntity: CommentEntity, onLike: () -> Unit = {}) {
|
|||||||
AnimatedLikeIcon(
|
AnimatedLikeIcon(
|
||||||
liked = commentEntity.liked,
|
liked = commentEntity.liked,
|
||||||
onClick = onLike,
|
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))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
@@ -698,7 +537,7 @@ fun CommentItem(commentEntity: CommentEntity, onLike: () -> Unit = {}) {
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun BottomNavigationBar(
|
fun PostBottomBar(
|
||||||
onCreateComment: (String) -> Unit = {},
|
onCreateComment: (String) -> Unit = {},
|
||||||
onLikeClick: () -> Unit = {},
|
onLikeClick: () -> Unit = {},
|
||||||
onFavoriteClick: () -> 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