更新动态加载逻辑

This commit is contained in:
2024-12-01 09:40:13 +08:00
parent 6c19f83cfb
commit 79fccda1aa
25 changed files with 657 additions and 524 deletions

View File

@@ -116,6 +116,8 @@ dependencies {
implementation("androidx.core:core-splashscreen:1.0.1") // 添加 SplashScreen 依赖
// 添加 lifecycle-runtime-ktx 依赖
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation ("org.greenrobot:eventbus:3.3.1")
}

View File

@@ -17,6 +17,7 @@ import com.aiosman.ravenow.ui.follower.FollowerNoticeViewModel
import com.aiosman.ravenow.ui.follower.FollowingListViewModel
import com.aiosman.ravenow.ui.index.IndexViewModel
import com.aiosman.ravenow.ui.index.tabs.message.MessageListViewModel
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.MomentExploreViewModel
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline.TimelineMomentViewModel
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
import com.aiosman.ravenow.ui.index.tabs.search.DiscoverViewModel
@@ -131,6 +132,7 @@ object AppState {
fun ReloadAppState(context: Context) {
// 重置动态列表页面
TimelineMomentViewModel.ResetModel()
MomentExploreViewModel.ResetModel()
// 重置我的页面
MyProfileViewModel.ResetModel()
// 重置发现页面

View File

@@ -0,0 +1,60 @@
package com.aiosman.ravenow.entity
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import com.aiosman.ravenow.data.ListContainer
abstract class DataLoader<T,ET> {
var list: MutableList<T> = mutableListOf()
var page by mutableStateOf(1)
var total by mutableStateOf(0)
var pageSize by mutableStateOf(10)
var hasNext by mutableStateOf(true)
var onListChanged: ((List<T>) -> Unit)? = null
private var firstLoad = true
abstract suspend fun fetchData(
page: Int, pageSize: Int, extra: ET
): ListContainer<T>
suspend fun loadData(
extra: ET
) {
if (!firstLoad) {
return
}
firstLoad = false
val result = fetchData(page, pageSize, extra)
list = result.list.toMutableList()
this.page = page
this.total = result.total
this.pageSize = pageSize
this.hasNext = result.list.size == pageSize
onListChanged?.invoke(list)
}
suspend fun loadMore(extra: ET) {
if (firstLoad) {
return
}
if (!hasNext) {
return
}
val result = fetchData(page + 1, pageSize, extra)
list.addAll(result.list)
page += 1
hasNext = result.list.size == pageSize
onListChanged?.invoke(list)
}
fun clear() {
list.clear()
page = 1
total = 0
pageSize = 10
hasNext = true
firstLoad = true
}
}

View File

@@ -285,3 +285,68 @@ data class MomentEntity(
// 是否收藏
var isFavorite: Boolean = false
)
class MomentLoaderExtraArgs(
val explore: Boolean? = false,
val timelineId: Int? = null,
val authorId : Int? = null
)
class MomentLoader : DataLoader<MomentEntity,MomentLoaderExtraArgs>() {
override suspend fun fetchData(
page: Int,
pageSize: Int,
extra: MomentLoaderExtraArgs
): ListContainer<MomentEntity> {
val result = ApiClient.api.getPosts(
page = page,
pageSize = pageSize,
explore = if (extra.explore == true) "true" else "",
timelineId = extra.timelineId,
authorId = extra.authorId
)
val data = result.body()?.let {
ListContainer(
list = it.list.map { it.toMomentItem() },
total = it.total,
page = page,
pageSize = pageSize
)
}
if (data == null) {
throw ServiceException("Failed to get moments")
}
return data
}
fun updateMomentLike(id: Int,isLike:Boolean) {
this.list = this.list.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(likeCount = momentItem.likeCount + if (isLike) 1 else -1, liked = isLike)
} else {
momentItem
}
}.toMutableList()
onListChanged?.invoke(this.list)
}
fun updateFavoriteCount(id: Int,isFavorite:Boolean) {
this.list = this.list.map { momentItem ->
if (momentItem.id == id) {
momentItem.copy(favoriteCount = momentItem.favoriteCount + if (isFavorite) 1 else -1, isFavorite = isFavorite)
} else {
momentItem
}
}.toMutableList()
onListChanged?.invoke(this.list)
}
fun removeMoment(id: Int) {
this.list = this.list.filter { it.id != id }.toMutableList()
onListChanged?.invoke(this.list)
}
fun addMoment(moment: MomentEntity) {
this.list.add(0, moment)
onListChanged?.invoke(this.list)
}
}

View File

@@ -0,0 +1,7 @@
package com.aiosman.ravenow.event
import com.aiosman.ravenow.entity.MomentEntity
data class MomentAddEvent(
val moment:MomentEntity
)

View File

@@ -0,0 +1,6 @@
package com.aiosman.ravenow.event
data class MomentFavouriteChangeEvent(
val postId: Int,
val isFavourite: Boolean
)

View File

@@ -0,0 +1,7 @@
package com.aiosman.ravenow.event
data class MomentLikeChangeEvent(
val postId: Int,
val likeCount: Int?,
val isLike: Boolean
)

View File

@@ -0,0 +1,5 @@
package com.aiosman.ravenow.event
data class MomentRemoveEvent(
val postId: Int
)

View File

@@ -66,6 +66,7 @@ import com.aiosman.ravenow.ui.navigateToPost
@Composable
fun MomentCard(
modifier: Modifier = Modifier,
momentEntity: MomentEntity,
onLikeClick: () -> Unit = {},
onFavoriteClick: () -> Unit = {},
@@ -78,7 +79,7 @@ fun MomentCard(
var imageIndex by remember { mutableStateOf(0) }
val navController = LocalNavController.current
Column(
modifier = Modifier
modifier = modifier
.fillMaxWidth()
.background(AppColors.background)
) {

View File

@@ -0,0 +1,129 @@
package com.aiosman.ravenow.ui.index.tabs.moment
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.aiosman.ravenow.data.MomentService
import com.aiosman.ravenow.data.UserServiceImpl
import com.aiosman.ravenow.entity.MomentEntity
import com.aiosman.ravenow.entity.MomentLoader
import com.aiosman.ravenow.entity.MomentLoaderExtraArgs
import com.aiosman.ravenow.entity.MomentServiceImpl
import com.aiosman.ravenow.event.MomentAddEvent
import com.aiosman.ravenow.event.MomentFavouriteChangeEvent
import com.aiosman.ravenow.event.MomentLikeChangeEvent
import com.aiosman.ravenow.event.MomentRemoveEvent
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
open class BaseMomentModel :ViewModel(){
private val momentService: MomentService = MomentServiceImpl()
private val userService = UserServiceImpl()
val momentLoader = MomentLoader().apply {
onListChanged = {
moments = it
}
}
var refreshing by mutableStateOf(false)
var isFirstLoad = true
var moments by mutableStateOf<List<MomentEntity>>(listOf())
open fun extraArgs(): MomentLoaderExtraArgs {
return MomentLoaderExtraArgs()
}
fun refreshPager(pullRefresh: Boolean = false) {
if (!isFirstLoad && !pullRefresh) {
return
}
isFirstLoad = false
momentLoader.clear()
viewModelScope.launch {
momentLoader.loadData(extraArgs())
}
}
fun loadMore() {
viewModelScope.launch {
momentLoader.loadMore(extraArgs())
moments = momentLoader.list
}
}
@Subscribe
fun onMomentLikeChangeEvent(event: MomentLikeChangeEvent) {
momentLoader.updateMomentLike(event.postId, event.isLike)
}
suspend fun likeMoment(id: Int) {
momentService.likeMoment(id)
momentLoader.updateMomentLike(id, true)
}
suspend fun dislikeMoment(id: Int) {
momentService.dislikeMoment(id)
momentLoader.updateMomentLike(id, false)
}
suspend fun onAddComment(id: Int) {
// val currentPagingData = _momentsFlow.value
// updateCommentCount(id)
}
@Subscribe
fun onMomentFavoriteChangeEvent(event: MomentFavouriteChangeEvent) {
momentLoader.updateFavoriteCount(event.postId, event.isFavourite)
}
suspend fun favoriteMoment(id: Int) {
momentService.favoriteMoment(id)
momentLoader.updateFavoriteCount(id, true)
}
suspend fun unfavoriteMoment(id: Int) {
momentService.unfavoriteMoment(id)
momentLoader.updateFavoriteCount(id, false)
}
@Subscribe
fun onRemoveMomentEvent(event: MomentRemoveEvent) {
momentLoader.removeMoment(event.postId)
}
@Subscribe
fun onAddMomentEvent(event: MomentAddEvent) {
momentLoader.addMoment(event.moment)
}
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() {
momentLoader.clear()
isFirstLoad = true
}
override fun onCleared() {
super.onCleared()
EventBus.getDefault().unregister(this)
}
}

View File

@@ -4,16 +4,19 @@ 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.foundation.lazy.rememberLazyListState
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.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.paging.compose.collectAsLazyPagingItems
import com.aiosman.ravenow.ui.composables.MomentCard
import kotlinx.coroutines.launch
@@ -24,14 +27,30 @@ import kotlinx.coroutines.launch
@Composable
fun ExploreMomentsList() {
val model = MomentExploreViewModel
var dataFlow = model.momentsFlow
var moments = dataFlow.collectAsLazyPagingItems()
var moments = model.moments
val scope = rememberCoroutineScope()
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
model.refreshPager(
pullRefresh = true
)
})
val listState = rememberLazyListState()
// observe list scrolling
val reachedBottom by remember {
derivedStateOf {
val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull()
lastVisibleItem?.index != 0 && lastVisibleItem?.index == listState.layoutInfo.totalItemsCount - 2
}
}
// load more if scrolled to bottom
LaunchedEffect(reachedBottom) {
if (reachedBottom) {
model.loadMore()
}
}
LaunchedEffect(Unit) {
model.refreshPager()
}
@@ -43,9 +62,10 @@ fun ExploreMomentsList() {
Box(Modifier.pullRefresh(state)) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = listState
) {
items(
moments.itemCount,
moments.size,
key = { idx -> idx }
) { idx ->
val momentItem = moments[idx] ?: return@items
@@ -75,7 +95,7 @@ fun ExploreMomentsList() {
},
onFollowClick = {
model.followAction(momentItem)
},
}
)
}
}

View File

@@ -0,0 +1,17 @@
package com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre
import com.aiosman.ravenow.entity.MomentLoaderExtraArgs
import com.aiosman.ravenow.ui.index.tabs.moment.BaseMomentModel
import org.greenrobot.eventbus.EventBus
object MomentExploreViewModel : BaseMomentModel() {
init {
EventBus.getDefault().register(this)
}
override fun extraArgs(): MomentLoaderExtraArgs {
return MomentLoaderExtraArgs(explore = true)
}
}

View File

@@ -1,182 +0,0 @@
package com.aiosman.ravenow.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.ravenow.AppState
import com.aiosman.ravenow.data.MomentService
import com.aiosman.ravenow.data.UserServiceImpl
import com.aiosman.ravenow.entity.MomentEntity
import com.aiosman.ravenow.entity.MomentPagingSource
import com.aiosman.ravenow.entity.MomentRemoteDataSource
import com.aiosman.ravenow.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 userService = UserServiceImpl()
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),
// 如果没有动态,则显示热门动态
explore = 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 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

@@ -4,12 +4,16 @@ 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.foundation.lazy.rememberLazyListState
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.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -24,8 +28,7 @@ import kotlinx.coroutines.launch
@Composable
fun TimelineMomentsList() {
val model = TimelineMomentViewModel
var dataFlow = model.momentsFlow
var moments = dataFlow.collectAsLazyPagingItems()
var moments = model.moments
val scope = rememberCoroutineScope()
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
model.refreshPager(
@@ -35,6 +38,20 @@ fun TimelineMomentsList() {
LaunchedEffect(Unit) {
model.refreshPager()
}
val listState = rememberLazyListState()
// observe list scrolling
val reachedBottom by remember {
derivedStateOf {
val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull()
lastVisibleItem?.index != 0 && lastVisibleItem?.index == listState.layoutInfo.totalItemsCount - 2
}
}
LaunchedEffect(reachedBottom) {
if (reachedBottom) {
model.loadMore()
}
}
Column(
modifier = Modifier
.fillMaxSize()
@@ -42,12 +59,13 @@ fun TimelineMomentsList() {
Box(Modifier.pullRefresh(state)) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = listState
) {
items(
moments.itemCount,
key = { idx -> moments[idx]?.id ?: idx }
moments.size,
key = { idx -> moments[idx].id }
) { idx ->
val momentItem = moments[idx] ?: return@items
val momentItem = moments[idx]
MomentCard(momentEntity = momentItem,
onAddComment = {
scope.launch {
@@ -77,12 +95,6 @@ fun TimelineMomentsList() {
},
showFollowButton = false
)
// Box(
// modifier = Modifier
// .height(4.dp)
// .fillMaxWidth()
// .background(Color(0xFFF0F2F5))
// )
}
}
PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))

View File

@@ -1,209 +0,0 @@
package com.aiosman.ravenow.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.ravenow.AppState
import com.aiosman.ravenow.data.MomentService
import com.aiosman.ravenow.data.UserService
import com.aiosman.ravenow.data.UserServiceImpl
import com.aiosman.ravenow.entity.MomentEntity
import com.aiosman.ravenow.entity.MomentPagingSource
import com.aiosman.ravenow.entity.MomentRemoteDataSource
import com.aiosman.ravenow.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())
private val userService :UserService = UserServiceImpl()
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 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

@@ -0,0 +1,17 @@
package com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline
import com.aiosman.ravenow.AppState
import com.aiosman.ravenow.entity.MomentLoaderExtraArgs
import com.aiosman.ravenow.ui.index.tabs.moment.BaseMomentModel
import org.greenrobot.eventbus.EventBus
object TimelineMomentViewModel : BaseMomentModel() {
init {
EventBus.getDefault().register(this)
}
override fun extraArgs(): MomentLoaderExtraArgs {
return MomentLoaderExtraArgs(timelineId = AppState.UserId!!)
}
}

View File

@@ -8,11 +8,6 @@ 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 com.aiosman.ravenow.AppState
import com.aiosman.ravenow.AppStore
import com.aiosman.ravenow.Messaging
@@ -22,28 +17,40 @@ import com.aiosman.ravenow.data.MomentService
import com.aiosman.ravenow.data.UploadImage
import com.aiosman.ravenow.entity.AccountProfileEntity
import com.aiosman.ravenow.entity.MomentEntity
import com.aiosman.ravenow.entity.MomentPagingSource
import com.aiosman.ravenow.entity.MomentRemoteDataSource
import com.aiosman.ravenow.entity.MomentLoader
import com.aiosman.ravenow.entity.MomentLoaderExtraArgs
import com.aiosman.ravenow.entity.MomentServiceImpl
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import com.aiosman.ravenow.event.MomentAddEvent
import com.aiosman.ravenow.event.MomentFavouriteChangeEvent
import com.aiosman.ravenow.event.MomentLikeChangeEvent
import com.aiosman.ravenow.event.MomentRemoveEvent
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import java.io.File
object MyProfileViewModel : ViewModel() {
val accountService: AccountService = AccountServiceImpl()
val momentService: MomentService = MomentServiceImpl()
var profile by mutableStateOf<AccountProfileEntity?>(null)
private var _sharedFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
var sharedFlow = _sharedFlow.asStateFlow()
var moments by mutableStateOf<List<MomentEntity>>(emptyList())
val momentLoader: MomentLoader = MomentLoader().apply {
onListChanged = {
moments = it
}
}
var refreshing by mutableStateOf(false)
var firstLoad = true
init {
EventBus.getDefault().register(this)
}
suspend fun loadUserProfile() {
val profile = accountService.getMyAccountProfile()
MyProfileViewModel.profile = profile
}
fun loadProfile(pullRefresh: Boolean = false) {
if (!firstLoad && !pullRefresh) return
viewModelScope.launch {
@@ -55,18 +62,7 @@ object MyProfileViewModel : ViewModel() {
refreshing = false
profile?.let {
try {
// Collect shared flow
Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = {
MomentPagingSource(
MomentRemoteDataSource(momentService),
author = AppState.UserId
)
}
).flow.cachedIn(viewModelScope).collectLatest {
_sharedFlow.value = it
}
momentLoader.loadData(extra = MomentLoaderExtraArgs(authorId = it.id))
} catch (e: Exception) {
Log.e("MyProfileViewModel", "loadProfile: ", e)
}
@@ -75,6 +71,12 @@ object MyProfileViewModel : ViewModel() {
}
}
fun loadMoreMoment() {
viewModelScope.launch {
momentLoader.loadMore(extra = MomentLoaderExtraArgs(authorId = profile?.id))
}
}
fun logout(context: Context) {
viewModelScope.launch {
Messaging.unregisterDevice(context)
@@ -117,13 +119,30 @@ object MyProfileViewModel : ViewModel() {
}
}
fun deleteMoment(id: Int) {
val currentPagingData = _sharedFlow.value
val updatedPagingData = currentPagingData.filter { momentItem ->
momentItem.id != id
fun likeMoment(momentLMomentEntity: MomentEntity) {
viewModelScope.launch {
if (momentLMomentEntity.liked) {
momentService.dislikeMoment(momentLMomentEntity.id)
EventBus.getDefault().post(
MomentLikeChangeEvent(
momentLMomentEntity.id,
likeCount = momentLMomentEntity.likeCount - 1,
isLike = false
)
)
} else {
momentService.likeMoment(momentLMomentEntity.id)
EventBus.getDefault().post(
MomentLikeChangeEvent(
momentLMomentEntity.id,
likeCount = momentLMomentEntity.likeCount + 1,
isLike = true
)
)
}
_sharedFlow.value = updatedPagingData
}
}
val bio get() = profile?.bio ?: ""
val nickName get() = profile?.nickName ?: ""
@@ -131,7 +150,38 @@ object MyProfileViewModel : ViewModel() {
fun ResetModel() {
profile = null
_sharedFlow.value = PagingData.empty()
momentLoader.clear()
firstLoad = true
}
override fun onCleared() {
super.onCleared()
EventBus.getDefault().unregister(this)
}
@Subscribe
fun onMomentLikeChangeEvent(event: MomentLikeChangeEvent) {
momentLoader.updateMomentLike(event.postId, event.isLike)
}
@Subscribe
fun onMomentFavoriteChangeEvent(event: MomentFavouriteChangeEvent) {
momentLoader.updateFavoriteCount(event.postId, event.isFavourite)
}
@Subscribe
fun onRemoveMomentEvent(event: MomentRemoveEvent) {
momentLoader.removeMoment(event.postId)
}
@Subscribe
fun onAddMomentEvent(event: MomentAddEvent) {
momentLoader.addMoment(event.moment)
}
@Subscribe
fun onMomentDelete(event: MomentRemoveEvent) {
momentLoader.removeMoment(event.postId)
}
}

View File

@@ -23,8 +23,10 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
@@ -38,6 +40,8 @@ import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -58,8 +62,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import com.aiosman.ravenow.AppState
import com.aiosman.ravenow.ConstVars
import com.aiosman.ravenow.LocalAppTheme
@@ -88,9 +90,6 @@ import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import com.aiosman.ravenow.ui.post.NewPostViewModel
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import java.io.File
@@ -102,22 +101,23 @@ fun ProfileV3(
onLogout: () -> Unit = {},
onFollowClick: () -> Unit = {},
onChatClick: () -> Unit = {},
sharedFlow: SharedFlow<PagingData<MomentEntity>> = MutableStateFlow<PagingData<MomentEntity>>(
PagingData.empty()
).asStateFlow(),
isSelf: Boolean = true
moments: List<MomentEntity>,
isSelf: Boolean = true,
onLoadMore: () -> Unit = {},
onLike: (MomentEntity) -> Unit = {},
onComment: (MomentEntity) -> Unit = {},
) {
val model = MyProfileViewModel
val state = rememberCollapsingToolbarScaffoldState()
val pagerState = rememberPagerState(pageCount = { 2 })
var enabled by remember { mutableStateOf(true) }
val enabled by remember { mutableStateOf(true) }
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
var expanded by remember { mutableStateOf(false) }
var minibarExpanded by remember { mutableStateOf(false) }
val context = LocalContext.current
val scope = rememberCoroutineScope()
val navController = LocalNavController.current
var bannerHeight = 400
val bannerHeight = 400
val pickBannerImageLauncher = pickupAndCompressLauncher(
context,
scope,
@@ -126,15 +126,44 @@ fun ProfileV3(
) { uri, file ->
onUpdateBanner?.invoke(uri, file, context)
}
val moments = sharedFlow.collectAsLazyPagingItems()
val refreshState = rememberPullRefreshState(model.refreshing, onRefresh = {
model.loadProfile(pullRefresh = true)
})
var miniToolbarHeight by remember { mutableStateOf(0) }
val density = LocalDensity.current
var appTheme = LocalAppTheme.current
var AppColors = appTheme
var systemUiController = rememberSystemUiController()
val appTheme = LocalAppTheme.current
val AppColors = appTheme
val systemUiController = rememberSystemUiController()
val listState = rememberLazyListState()
// observe list scrolling
val reachedListBottom by remember {
derivedStateOf {
val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull()
lastVisibleItem?.index != 0 && lastVisibleItem?.index == listState.layoutInfo.totalItemsCount - 2
}
}
// load more if scrolled to bottom
LaunchedEffect(reachedListBottom) {
if (reachedListBottom) {
onLoadMore()
}
}
val gridState = rememberLazyStaggeredGridState()
val reachedGridBottom by remember {
derivedStateOf {
val lastVisibleItem = gridState.layoutInfo.visibleItemsInfo.lastOrNull()
lastVisibleItem?.index != 0 && lastVisibleItem?.index == gridState.layoutInfo.totalItemsCount - 2
}
}
LaunchedEffect(reachedGridBottom) {
if (reachedGridBottom) {
onLoadMore()
}
}
fun switchTheme(){
// delay
scope.launch {
@@ -499,7 +528,8 @@ fun ProfileV3(
8.dp
),
verticalItemSpacing = 8.dp,
contentPadding = PaddingValues(8.dp)
contentPadding = PaddingValues(8.dp),
state = gridState
) {
if (isSelf) {
items(1) {
@@ -541,7 +571,7 @@ fun ProfileV3(
}
}
}
items(moments.itemCount) { idx ->
items(moments.size) { idx ->
val moment = moments[idx] ?: return@items
GalleryItem(moment, idx)
}
@@ -553,18 +583,26 @@ fun ProfileV3(
1 ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.fillMaxSize(),
state = listState
) {
if (moments.itemCount == 0 && isSelf) {
if (moments.isEmpty() && isSelf) {
item {
EmptyMomentPostUnit()
}
}
item {
for (idx in 0 until moments.itemCount) {
val moment = moments[idx]
moment?.let {
MomentPostUnit(it)
for (element in moments) {
element.let {
MomentPostUnit(
it,
onLikeClick = {
onLike(it)
},
onCommentClick = {
onComment(it)
}
)
}
}
}

View File

@@ -3,12 +3,15 @@ package com.aiosman.ravenow.ui.index.tabs.profile
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.platform.LocalContext
import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.ui.navigateToPost
@Composable
fun ProfileWrap(
) {
val context = LocalContext.current
val navController = LocalNavController.current
LaunchedEffect(Unit) {
MyProfileViewModel.loadProfile()
}
@@ -20,6 +23,15 @@ fun ProfileWrap(
MyProfileViewModel.logout(context)
},
profile = MyProfileViewModel.profile,
sharedFlow = MyProfileViewModel.sharedFlow,
moments = MyProfileViewModel.moments,
onLoadMore = {
MyProfileViewModel.loadMoreMoment()
},
onLike = { moments ->
MyProfileViewModel.likeMoment(moments)
},
onComment = {
navController.navigateToPost(it.id)
}
)
}

View File

@@ -47,7 +47,9 @@ import com.aiosman.ravenow.R
import com.aiosman.ravenow.entity.MomentEntity
import com.aiosman.ravenow.exp.formatPostTime2
import com.aiosman.ravenow.ui.NavigationRoute
import com.aiosman.ravenow.ui.composables.AnimatedLikeIcon
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
import com.aiosman.ravenow.ui.composables.MomentOperateBtn
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import com.aiosman.ravenow.ui.navigateToPost
import com.aiosman.ravenow.ui.post.NewPostViewModel
@@ -130,20 +132,26 @@ fun ProfileEmptyMomentCard(
}
@Composable
fun MomentPostUnit(momentEntity: MomentEntity) {
fun MomentPostUnit(
momentEntity: MomentEntity,
onCommentClick: () -> Unit = {},
onLikeClick: () -> Unit = {}
) {
TimeGroup(momentEntity.time.formatPostTime2())
ProfileMomentCard(
momentEntity.momentTextContent,
momentEntity.images[0].thumbnail,
momentEntity.likeCount.toString(),
momentEntity.commentCount.toString(),
momentEntity = momentEntity
momentEntity = momentEntity,
onCommentClick = onCommentClick,
onLikeClick = onLikeClick
)
}
@Composable
fun TimeGroup(time: String = "2024.06.08 12:23") {
val AppColors = LocalAppTheme.current
val appColors = LocalAppTheme.current
Row(
modifier = Modifier
.fillMaxWidth()
@@ -162,7 +170,7 @@ fun TimeGroup(time: String = "2024.06.08 12:23") {
Text(
text = time,
fontSize = 16.sp,
color = AppColors.text,
color = appColors.text,
style = TextStyle(fontWeight = FontWeight.W600)
)
}
@@ -174,7 +182,9 @@ fun ProfileMomentCard(
imageUrl: String,
like: String,
comment: String,
momentEntity: MomentEntity
momentEntity: MomentEntity,
onCommentClick: () -> Unit = {},
onLikeClick: () -> Unit = {}
) {
var columnHeight by remember { mutableStateOf(0) }
val AppColors = LocalAppTheme.current
@@ -214,7 +224,13 @@ fun ProfileMomentCard(
MomentCardTopContent(content)
}
MomentCardPicture(imageUrl, momentEntity = momentEntity)
MomentCardOperation(like, comment)
MomentCardOperation(
like = like,
isLike = momentEntity.liked,
onLikeClick = onLikeClick,
comment = comment,
onCommentClick = onCommentClick
)
}
}
}
@@ -262,7 +278,13 @@ fun MomentCardPicture(imageUrl: String, momentEntity: MomentEntity) {
}
@Composable
fun MomentCardOperation(like: String, comment: String) {
fun MomentCardOperation(
like: String,
isLike: Boolean,
onLikeClick: () -> Unit,
comment: String,
onCommentClick: () -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
@@ -270,18 +292,26 @@ fun MomentCardOperation(like: String, comment: String) {
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
) {
MomentCardOperationItem(
drawable = R.drawable.rider_pro_like,
number = like,
modifier = Modifier.padding(end = 32.dp)
MomentOperateBtn(count = like) {
AnimatedLikeIcon(
liked = isLike,
onClick = onLikeClick,
)
MomentCardOperationItem(
drawable = R.drawable.rider_pro_moment_comment,
number = comment,
modifier = Modifier.padding(end = 32.dp)
}
MomentOperateBtn(count = comment) {
Image(
painter = painterResource(id = R.drawable.rider_pro_moment_comment),
contentDescription = "",
colorFilter = ColorFilter.tint(LocalAppTheme.current.text),
modifier = Modifier.noRippleClickable {
onCommentClick()
}
)
}
}
}
@Composable
fun MomentCardOperationItem(@DrawableRes drawable: Int, number: String, modifier: Modifier) {

View File

@@ -200,7 +200,7 @@ class CommentsViewModel(
replyUserId = replyUserId,
replyCommentId = replyCommentId
)
TimelineMomentViewModel.updateCommentCount(postId.toInt())
// TimelineMomentViewModel.updateCommentCount(postId.toInt())
// add to first
addedCommentList = listOf(comment) + addedCommentList
}

View File

@@ -14,6 +14,7 @@ import com.aiosman.ravenow.data.MomentService
import com.aiosman.ravenow.entity.MomentServiceImpl
import com.aiosman.ravenow.data.UploadImage
import com.aiosman.ravenow.entity.MomentEntity
import com.aiosman.ravenow.event.MomentAddEvent
import com.aiosman.ravenow.exp.rotate
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline.TimelineMomentViewModel
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
@@ -21,6 +22,7 @@ import com.aiosman.ravenow.ui.modification.Modification
import com.aiosman.ravenow.utils.FileUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
@@ -141,10 +143,10 @@ object NewPostViewModel : ViewModel() {
onUploadProgress(((index / imageList.size).toFloat())) // progressValue 是当前上传进度,例如 0.5 表示 50%
index += 1
}
momentService.createMoment(textContent, 1, uploadImageList, relPostId)
val result = momentService.createMoment(textContent, 1, uploadImageList, relPostId)
// 刷新个人动态
MyProfileViewModel.loadProfile(pullRefresh = true)
TimelineMomentViewModel.refreshPager()
EventBus.getDefault().post(MomentAddEvent(result))
}
suspend fun init() {

View File

@@ -13,9 +13,14 @@ import com.aiosman.ravenow.data.UserServiceImpl
import com.aiosman.ravenow.entity.AccountProfileEntity
import com.aiosman.ravenow.entity.MomentEntity
import com.aiosman.ravenow.entity.MomentServiceImpl
import com.aiosman.ravenow.event.MomentFavouriteChangeEvent
import com.aiosman.ravenow.event.MomentLikeChangeEvent
import com.aiosman.ravenow.event.MomentRemoveEvent
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline.TimelineMomentViewModel
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
class PostViewModel(
@@ -31,6 +36,9 @@ class PostViewModel(
var commentsViewModel: CommentsViewModel = CommentsViewModel(postId)
var isError by mutableStateOf(false)
var isFirstLoad by mutableStateOf(true)
init {
EventBus.getDefault().register(this)
}
fun reloadComment() {
commentsViewModel.reloadComment()
@@ -77,37 +85,80 @@ class PostViewModel(
moment = moment?.copy(commentCount = moment?.commentCount?.plus(1) ?: 0)
}
@Subscribe
fun onMomentLikeChangeEvent(event: MomentLikeChangeEvent) {
moment?.let {
if (event.postId == it.id) {
moment = it.copy(likeCount = event.likeCount ?: it.likeCount, liked = event.isLike)
}
}
}
suspend fun likeMoment() {
moment?.let {
service.likeMoment(it.id)
moment = moment?.copy(likeCount = moment?.likeCount?.plus(1) ?: 0, liked = true)
TimelineMomentViewModel.updateLikeCount(it.id)
EventBus.getDefault().post(
MomentLikeChangeEvent(
postId = it.id,
likeCount = it.likeCount + 1,
isLike = true
)
)
}
}
suspend fun dislikeMoment() {
moment?.let {
service.dislikeMoment(it.id)
moment = moment?.copy(likeCount = moment?.likeCount?.minus(1) ?: 0, liked = false)
// update home list
TimelineMomentViewModel.updateDislikeMomentById(it.id)
EventBus.getDefault().post(
MomentLikeChangeEvent(
postId = it.id,
likeCount = it.likeCount - 1,
isLike = false
)
)
}
}
@Subscribe
fun onMomentFavouriteChangeEvent(event: MomentFavouriteChangeEvent) {
moment?.let {
if (event.postId == it.id) {
val favouriteCount = if (event.isFavourite) {
it.favoriteCount + 1
} else {
it.favoriteCount - 1
}
moment = it.copy(
favoriteCount = favouriteCount,
isFavorite = event.isFavourite
)
}
}
}
suspend fun favoriteMoment() {
moment?.let {
service.favoriteMoment(it.id)
moment =
moment?.copy(favoriteCount = moment?.favoriteCount?.plus(1) ?: 0, isFavorite = true)
EventBus.getDefault().post(
MomentFavouriteChangeEvent(
postId = it.id,
isFavourite = true
)
)
}
}
suspend fun unfavoriteMoment() {
moment?.let {
service.unfavoriteMoment(it.id)
moment = moment?.copy(
favoriteCount = moment?.favoriteCount?.minus(1) ?: 0, isFavorite = false
EventBus.getDefault().post(
MomentFavouriteChangeEvent(
postId = it.id,
isFavourite = false
)
)
}
}
@@ -131,7 +182,6 @@ class PostViewModel(
commentsViewModel.deleteComment(commentId)
moment = moment?.copy(commentCount = moment?.commentCount?.minus(1) ?: 0)
moment?.let {
TimelineMomentViewModel.updateMomentCommentCount(it.id, -1)
}
}
@@ -160,8 +210,7 @@ class PostViewModel(
viewModelScope.launch {
moment?.let {
service.deleteMoment(it.id)
TimelineMomentViewModel.deleteMoment(it.id)
MyProfileViewModel.deleteMoment(it.id)
EventBus.getDefault().post(MomentRemoveEvent(it.id))
}
callback()
}
@@ -171,4 +220,8 @@ class PostViewModel(
commentsViewModel.loadMoreSubComments(commentId)
}
override fun onCleared() {
super.onCleared()
EventBus.getDefault().unregister(this)
}
}

View File

@@ -24,7 +24,7 @@ fun AccountProfileV2(id: String){
isSelf = true
}
ProfileV3(
sharedFlow = model.momentsFlow,
moments = model.moments,
profile = model.profile,
isSelf = isSelf,
onChatClick = {

View File

@@ -5,34 +5,29 @@ 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 com.aiosman.ravenow.data.AccountService
import com.aiosman.ravenow.data.AccountServiceImpl
import com.aiosman.ravenow.data.MomentService
import com.aiosman.ravenow.data.UserServiceImpl
import com.aiosman.ravenow.entity.AccountProfileEntity
import com.aiosman.ravenow.entity.MomentEntity
import com.aiosman.ravenow.entity.MomentPagingSource
import com.aiosman.ravenow.entity.MomentRemoteDataSource
import com.aiosman.ravenow.entity.MomentServiceImpl
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import com.aiosman.ravenow.entity.MomentLoader
import com.aiosman.ravenow.entity.MomentLoaderExtraArgs
import kotlinx.coroutines.launch
class AccountProfileViewModel : ViewModel() {
var profileId by mutableStateOf(0)
val accountService: AccountService = AccountServiceImpl()
val momentService: MomentService = MomentServiceImpl()
val userService = UserServiceImpl()
var profile by mutableStateOf<AccountProfileEntity?>(null)
private var _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
var momentsFlow = _momentsFlow.asStateFlow()
var refreshing by mutableStateOf(false)
var momentLoader = MomentLoader().apply {
onListChanged = {
moments = it
}
}
var moments by mutableStateOf<List<MomentEntity>>(listOf())
fun loadProfile(id: String, pullRefresh: Boolean = false) {
viewModelScope.launch {
if (pullRefresh) {
@@ -43,16 +38,12 @@ class AccountProfileViewModel : ViewModel() {
}
profile = userService.getUserProfile(id)
refreshing = false
Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = {
MomentPagingSource(
MomentRemoteDataSource(momentService),
author = profile!!.id
)
profile?.let {
try {
momentLoader.loadData(MomentLoaderExtraArgs(authorId = it.id))
} catch (e: Exception) {
e.printStackTrace()
}
).flow.cachedIn(viewModelScope).collectLatest {
_momentsFlow.value = it
}
}
}
@@ -72,8 +63,6 @@ class AccountProfileViewModel : ViewModel() {
}
}
val followerCount get() = profile?.followerCount ?: 0
val followingCount get() = profile?.followingCount ?: 0
val bio get() = profile?.bio ?: ""
val nickName get() = profile?.nickName ?: ""
val avatar get() = profile?.avatar