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:
@@ -54,6 +54,7 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
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 com.aiosman.riderpro.AppColors
|
import com.aiosman.riderpro.AppColors
|
||||||
|
import com.aiosman.riderpro.AppState
|
||||||
import com.aiosman.riderpro.LocalNavController
|
import com.aiosman.riderpro.LocalNavController
|
||||||
import com.aiosman.riderpro.R
|
import com.aiosman.riderpro.R
|
||||||
import com.aiosman.riderpro.entity.MomentEntity
|
import com.aiosman.riderpro.entity.MomentEntity
|
||||||
@@ -70,17 +71,20 @@ fun MomentCard(
|
|||||||
onLikeClick: () -> Unit = {},
|
onLikeClick: () -> Unit = {},
|
||||||
onFavoriteClick: () -> Unit = {},
|
onFavoriteClick: () -> Unit = {},
|
||||||
onAddComment: () -> Unit = {},
|
onAddComment: () -> Unit = {},
|
||||||
|
onFollowClick: () -> Unit = {},
|
||||||
hideAction: Boolean = false
|
hideAction: Boolean = false
|
||||||
) {
|
) {
|
||||||
var imageIndex by remember { mutableStateOf(0) }
|
var imageIndex by remember { mutableStateOf(0) }
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth().background(AppColors.background)
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(AppColors.background)
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp)
|
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp)
|
||||||
) {
|
) {
|
||||||
MomentTopRowGroup(momentEntity = momentEntity)
|
MomentTopRowGroup(momentEntity = momentEntity, onFollowClick = onFollowClick)
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -163,9 +167,9 @@ fun ModificationListHeader() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MomentName(name: String) {
|
fun MomentName(name: String, modifier: Modifier = Modifier) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier,
|
modifier = modifier,
|
||||||
textAlign = TextAlign.Start,
|
textAlign = TextAlign.Start,
|
||||||
text = name,
|
text = name,
|
||||||
color = AppColors.text,
|
color = AppColors.text,
|
||||||
@@ -214,7 +218,10 @@ fun MomentPostTime(time: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MomentTopRowGroup(momentEntity: MomentEntity) {
|
fun MomentTopRowGroup(
|
||||||
|
momentEntity: MomentEntity,
|
||||||
|
onFollowClick: () -> Unit = {}
|
||||||
|
) {
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
Row(
|
Row(
|
||||||
@@ -239,7 +246,7 @@ fun MomentTopRowGroup(momentEntity: MomentEntity) {
|
|||||||
)
|
)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.defaultMinSize()
|
.weight(1f)
|
||||||
.padding(start = 12.dp, end = 12.dp)
|
.padding(start = 12.dp, end = 12.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
@@ -248,8 +255,13 @@ fun MomentTopRowGroup(momentEntity: MomentEntity) {
|
|||||||
.height(22.dp),
|
.height(22.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
MomentName(momentEntity.nickname)
|
MomentName(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
name = momentEntity.nickname
|
||||||
|
)
|
||||||
// MomentFollowBtn()
|
// MomentFollowBtn()
|
||||||
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -262,6 +274,16 @@ fun MomentTopRowGroup(momentEntity: MomentEntity) {
|
|||||||
MomentPostLocation(momentEntity.location)
|
MomentPostLocation(momentEntity.location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
if (AppState.UserId != momentEntity.authorId) {
|
||||||
|
FollowButton(
|
||||||
|
isFollowing = momentEntity.followStatus
|
||||||
|
) {
|
||||||
|
onFollowClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,10 @@ fun ExploreMomentsList() {
|
|||||||
model.favoriteMoment(momentItem.id)
|
model.favoriteMoment(momentItem.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
onFollowClick = {
|
||||||
|
model.followAction(momentItem)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ import androidx.paging.PagingData
|
|||||||
import androidx.paging.cachedIn
|
import androidx.paging.cachedIn
|
||||||
import androidx.paging.map
|
import androidx.paging.map
|
||||||
import com.aiosman.riderpro.AppState
|
import com.aiosman.riderpro.AppState
|
||||||
|
import com.aiosman.riderpro.data.Moment
|
||||||
import com.aiosman.riderpro.data.MomentService
|
import com.aiosman.riderpro.data.MomentService
|
||||||
|
import com.aiosman.riderpro.data.UserServiceImpl
|
||||||
import com.aiosman.riderpro.entity.MomentEntity
|
import com.aiosman.riderpro.entity.MomentEntity
|
||||||
import com.aiosman.riderpro.entity.MomentPagingSource
|
import com.aiosman.riderpro.entity.MomentPagingSource
|
||||||
import com.aiosman.riderpro.entity.MomentRemoteDataSource
|
import com.aiosman.riderpro.entity.MomentRemoteDataSource
|
||||||
@@ -24,6 +26,7 @@ import kotlinx.coroutines.launch
|
|||||||
|
|
||||||
object MomentExploreViewModel : ViewModel() {
|
object MomentExploreViewModel : ViewModel() {
|
||||||
private val momentService: MomentService = MomentServiceImpl()
|
private val momentService: MomentService = MomentServiceImpl()
|
||||||
|
private val userService = UserServiceImpl()
|
||||||
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
||||||
val momentsFlow = _momentsFlow.asStateFlow()
|
val momentsFlow = _momentsFlow.asStateFlow()
|
||||||
var existsMoment = mutableStateOf(false)
|
var existsMoment = mutableStateOf(false)
|
||||||
@@ -147,6 +150,31 @@ object MomentExploreViewModel : ViewModel() {
|
|||||||
momentService.unfavoriteMoment(id)
|
momentService.unfavoriteMoment(id)
|
||||||
updateUnfavoriteCount(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() {
|
fun ResetModel() {
|
||||||
_momentsFlow.value = PagingData.empty()
|
_momentsFlow.value = PagingData.empty()
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ fun TimelineMomentsList() {
|
|||||||
model.favoriteMoment(momentItem.id)
|
model.favoriteMoment(momentItem.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onFollowClick = {
|
||||||
|
model.followAction(momentItem)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// Box(
|
// Box(
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import androidx.paging.filter
|
|||||||
import androidx.paging.map
|
import androidx.paging.map
|
||||||
import com.aiosman.riderpro.AppState
|
import com.aiosman.riderpro.AppState
|
||||||
import com.aiosman.riderpro.data.MomentService
|
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.MomentEntity
|
||||||
import com.aiosman.riderpro.entity.MomentPagingSource
|
import com.aiosman.riderpro.entity.MomentPagingSource
|
||||||
import com.aiosman.riderpro.entity.MomentRemoteDataSource
|
import com.aiosman.riderpro.entity.MomentRemoteDataSource
|
||||||
@@ -26,6 +28,7 @@ import kotlinx.coroutines.launch
|
|||||||
object TimelineMomentViewModel : ViewModel() {
|
object TimelineMomentViewModel : ViewModel() {
|
||||||
private val momentService: MomentService = MomentServiceImpl()
|
private val momentService: MomentService = MomentServiceImpl()
|
||||||
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
private val _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
||||||
|
private val userService :UserService = UserServiceImpl()
|
||||||
val momentsFlow = _momentsFlow.asStateFlow()
|
val momentsFlow = _momentsFlow.asStateFlow()
|
||||||
var existsMoment = mutableStateOf(false)
|
var existsMoment = mutableStateOf(false)
|
||||||
var refreshing by mutableStateOf(false)
|
var refreshing by mutableStateOf(false)
|
||||||
@@ -173,6 +176,32 @@ object TimelineMomentViewModel : ViewModel() {
|
|||||||
_momentsFlow.value = updatedPagingData
|
_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() {
|
fun ResetModel() {
|
||||||
_momentsFlow.value = PagingData.empty()
|
_momentsFlow.value = PagingData.empty()
|
||||||
isFirstLoad = true
|
isFirstLoad = true
|
||||||
|
|||||||
@@ -265,7 +265,13 @@ fun MomentResultTab() {
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(Color.White)
|
.background(Color.White)
|
||||||
) {
|
) {
|
||||||
MomentCard(momentEntity = momentItem, hideAction = true)
|
MomentCard(
|
||||||
|
momentEntity = momentItem,
|
||||||
|
hideAction = true,
|
||||||
|
onFollowClick = {
|
||||||
|
model.momentFollowAction(momentItem)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// Spacer(modifier = Modifier.padding(16.dp))
|
// Spacer(modifier = Modifier.padding(16.dp))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,15 +10,14 @@ import androidx.paging.PagingConfig
|
|||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import androidx.paging.cachedIn
|
import androidx.paging.cachedIn
|
||||||
import androidx.paging.map
|
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.AccountPagingSource
|
||||||
import com.aiosman.riderpro.entity.AccountProfileEntity
|
import com.aiosman.riderpro.entity.AccountProfileEntity
|
||||||
|
import com.aiosman.riderpro.entity.MomentEntity
|
||||||
import com.aiosman.riderpro.entity.MomentPagingSource
|
import com.aiosman.riderpro.entity.MomentPagingSource
|
||||||
import com.aiosman.riderpro.entity.MomentRemoteDataSource
|
import com.aiosman.riderpro.entity.MomentRemoteDataSource
|
||||||
import com.aiosman.riderpro.data.MomentService
|
|
||||||
import com.aiosman.riderpro.entity.MomentServiceImpl
|
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.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
@@ -99,4 +98,30 @@ object SearchViewModel : ViewModel() {
|
|||||||
_usersFlow.value = PagingData.empty()
|
_usersFlow.value = PagingData.empty()
|
||||||
showResult = false
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user