feat: Implement moment follow/unfollow feature

This commit implements the follow/unfollow feature for moments in the Search, Timeline, and Explore tabs.

It includes:

- Adding a follow button to moment cards and implementing follow/unfollow logic in the respective ViewModels.
- Updating the UI to reflect the follow status changes in the moment list.
- Handling follow/unfollow API requests.
This commit is contained in:
2024-10-26 17:44:51 +08:00
parent fe2bd2f382
commit 01fb092e83
7 changed files with 129 additions and 13 deletions

View File

@@ -54,6 +54,7 @@ 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.AppState
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.entity.MomentEntity
@@ -70,17 +71,20 @@ fun MomentCard(
onLikeClick: () -> Unit = {},
onFavoriteClick: () -> Unit = {},
onAddComment: () -> Unit = {},
onFollowClick: () -> Unit = {},
hideAction: Boolean = false
) {
var imageIndex by remember { mutableStateOf(0) }
val navController = LocalNavController.current
Column(
modifier = Modifier.fillMaxWidth().background(AppColors.background)
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)
MomentTopRowGroup(momentEntity = momentEntity, onFollowClick = onFollowClick)
}
Column(
modifier = Modifier
@@ -163,9 +167,9 @@ fun ModificationListHeader() {
}
@Composable
fun MomentName(name: String) {
fun MomentName(name: String, modifier: Modifier = Modifier) {
Text(
modifier = Modifier,
modifier = modifier,
textAlign = TextAlign.Start,
text = name,
color = AppColors.text,
@@ -214,7 +218,10 @@ fun MomentPostTime(time: String) {
}
@Composable
fun MomentTopRowGroup(momentEntity: MomentEntity) {
fun MomentTopRowGroup(
momentEntity: MomentEntity,
onFollowClick: () -> Unit = {}
) {
val navController = LocalNavController.current
val context = LocalContext.current
Row(
@@ -239,7 +246,7 @@ fun MomentTopRowGroup(momentEntity: MomentEntity) {
)
Column(
modifier = Modifier
.defaultMinSize()
.weight(1f)
.padding(start = 12.dp, end = 12.dp)
) {
Row(
@@ -248,8 +255,13 @@ fun MomentTopRowGroup(momentEntity: MomentEntity) {
.height(22.dp),
verticalAlignment = Alignment.CenterVertically
) {
MomentName(momentEntity.nickname)
MomentName(
modifier = Modifier.weight(1f),
name = momentEntity.nickname
)
// MomentFollowBtn()
Spacer(modifier = Modifier.width(16.dp))
}
Row(
modifier = Modifier
@@ -262,6 +274,16 @@ fun MomentTopRowGroup(momentEntity: MomentEntity) {
MomentPostLocation(momentEntity.location)
}
}
Spacer(modifier = Modifier.width(16.dp))
if (AppState.UserId != momentEntity.authorId) {
FollowButton(
isFollowing = momentEntity.followStatus
) {
onFollowClick()
}
}
}
}

View File

@@ -72,7 +72,10 @@ fun ExploreMomentsList() {
model.favoriteMoment(momentItem.id)
}
}
}
},
onFollowClick = {
model.followAction(momentItem)
},
)
}
}

View File

@@ -11,7 +11,9 @@ import androidx.paging.PagingData
import androidx.paging.cachedIn
import androidx.paging.map
import com.aiosman.riderpro.AppState
import com.aiosman.riderpro.data.Moment
import com.aiosman.riderpro.data.MomentService
import com.aiosman.riderpro.data.UserServiceImpl
import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.entity.MomentPagingSource
import com.aiosman.riderpro.entity.MomentRemoteDataSource
@@ -24,6 +26,7 @@ import kotlinx.coroutines.launch
object MomentExploreViewModel : ViewModel() {
private val momentService: MomentService = MomentServiceImpl()
private val userService = UserServiceImpl()
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
val momentsFlow = _momentsFlow.asStateFlow()
var existsMoment = mutableStateOf(false)
@@ -147,6 +150,31 @@ object MomentExploreViewModel : ViewModel() {
momentService.unfavoriteMoment(id)
updateUnfavoriteCount(id)
}
fun updateFollowStatus(authorId:Int,isFollow:Boolean) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.authorId == authorId) {
momentItem.copy(followStatus = isFollow)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
fun followAction(moment: MomentEntity) {
viewModelScope.launch {
try {
if (moment.followStatus) {
userService.unFollowUser(moment.authorId.toString())
} else {
userService.followUser(moment.authorId.toString())
}
updateFollowStatus(moment.authorId, !moment.followStatus)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
fun ResetModel() {
_momentsFlow.value = PagingData.empty()

View File

@@ -71,6 +71,9 @@ fun TimelineMomentsList() {
model.favoriteMoment(momentItem.id)
}
}
},
onFollowClick = {
model.followAction(momentItem)
}
)
// Box(

View File

@@ -13,6 +13,8 @@ import androidx.paging.filter
import androidx.paging.map
import com.aiosman.riderpro.AppState
import com.aiosman.riderpro.data.MomentService
import com.aiosman.riderpro.data.UserService
import com.aiosman.riderpro.data.UserServiceImpl
import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.entity.MomentPagingSource
import com.aiosman.riderpro.entity.MomentRemoteDataSource
@@ -26,6 +28,7 @@ import kotlinx.coroutines.launch
object TimelineMomentViewModel : ViewModel() {
private val momentService: MomentService = MomentServiceImpl()
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
private val userService :UserService = UserServiceImpl()
val momentsFlow = _momentsFlow.asStateFlow()
var existsMoment = mutableStateOf(false)
var refreshing by mutableStateOf(false)
@@ -173,6 +176,32 @@ object TimelineMomentViewModel : ViewModel() {
_momentsFlow.value = updatedPagingData
}
fun updateFollowStatus(authorId:Int,isFollow:Boolean) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.authorId == authorId) {
momentItem.copy(followStatus = isFollow)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
fun followAction(moment: MomentEntity) {
viewModelScope.launch {
try {
if (moment.followStatus) {
userService.unFollowUser(moment.authorId.toString())
} else {
userService.followUser(moment.authorId.toString())
}
updateFollowStatus(moment.authorId, !moment.followStatus)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
fun ResetModel() {
_momentsFlow.value = PagingData.empty()
isFirstLoad = true

View File

@@ -265,7 +265,13 @@ fun MomentResultTab() {
.fillMaxWidth()
.background(Color.White)
) {
MomentCard(momentEntity = momentItem, hideAction = true)
MomentCard(
momentEntity = momentItem,
hideAction = true,
onFollowClick = {
model.momentFollowAction(momentItem)
}
)
}
// Spacer(modifier = Modifier.padding(16.dp))
}

View File

@@ -10,15 +10,14 @@ import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.cachedIn
import androidx.paging.map
import com.aiosman.riderpro.data.MomentService
import com.aiosman.riderpro.data.UserServiceImpl
import com.aiosman.riderpro.entity.AccountPagingSource
import com.aiosman.riderpro.entity.AccountProfileEntity
import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.entity.MomentPagingSource
import com.aiosman.riderpro.entity.MomentRemoteDataSource
import com.aiosman.riderpro.data.MomentService
import com.aiosman.riderpro.entity.MomentServiceImpl
import com.aiosman.riderpro.data.UserServiceImpl
import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
@@ -99,4 +98,30 @@ object SearchViewModel : ViewModel() {
_usersFlow.value = PagingData.empty()
showResult = false
}
fun updateMomentFollowStatus(authorId:Int,isFollow:Boolean) {
val currentPagingData = _momentsFlow.value
val updatedPagingData = currentPagingData.map { momentItem ->
if (momentItem.authorId == authorId) {
momentItem.copy(followStatus = isFollow)
} else {
momentItem
}
}
_momentsFlow.value = updatedPagingData
}
fun momentFollowAction(moment: MomentEntity) {
viewModelScope.launch {
try {
if (moment.followStatus) {
userService.unFollowUser(moment.authorId.toString())
} else {
userService.followUser(moment.authorId.toString())
}
updateMomentFollowStatus(moment.authorId, !moment.followStatus)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}