fix: post not updated in list when upvoted or downvoted from detail screen

This commit is contained in:
Diego Beraldin 2023-09-16 20:11:16 +02:00
parent c47bb2be0a
commit a7ca24bdea
11 changed files with 104 additions and 35 deletions

View File

@ -72,6 +72,7 @@ val commonUiModule = module {
postsRepository = get(),
hapticFeedback = get(),
keyStore = get(),
notificationCenter = get(),
)
}
factory {

View File

@ -5,6 +5,7 @@ import com.github.diegoberaldin.racconforlemmy.core.utils.HapticFeedback
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.preferences.KeyStoreKeys
import com.github.diegoberaldin.raccoonforlemmy.core.preferences.TemporaryKeyStore
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
@ -180,7 +181,9 @@ class PostDetailViewModel(
post = post,
voted = newValue,
)
notificationCenter.send(NotificationCenter.Event.PostUpdate(newPost))
notificationCenter.getObserver(NotificationCenterContractKeys.PostUpdate)?.also {
it.invoke(newPost)
}
} catch (e: Throwable) {
e.printStackTrace()
mvi.updateState { it.copy(post = post) }
@ -210,7 +213,9 @@ class PostDetailViewModel(
post = post,
downVoted = newValue,
)
notificationCenter.send(NotificationCenter.Event.PostUpdate(newPost))
notificationCenter.getObserver(NotificationCenterContractKeys.PostUpdate)?.also {
it.invoke(newPost)
}
} catch (e: Throwable) {
e.printStackTrace()
mvi.updateState { it.copy(post = post) }
@ -239,7 +244,9 @@ class PostDetailViewModel(
post = post,
saved = newValue,
)
notificationCenter.send(NotificationCenter.Event.PostUpdate(newPost))
notificationCenter.getObserver(NotificationCenterContractKeys.PostUpdate)?.also {
it.invoke(newPost)
}
} catch (e: Throwable) {
e.printStackTrace()
mvi.updateState { it.copy(post = post) }
@ -278,7 +285,6 @@ class PostDetailViewModel(
comment = comment,
voted = newValue,
)
notificationCenter.send(NotificationCenter.Event.CommentUpdate(newComment))
} catch (e: Throwable) {
e.printStackTrace()
mvi.updateState {
@ -324,7 +330,6 @@ class PostDetailViewModel(
comment = comment,
downVoted = newValue,
)
notificationCenter.send(NotificationCenter.Event.CommentUpdate(newComment))
} catch (e: Throwable) {
e.printStackTrace()
mvi.updateState {
@ -373,7 +378,6 @@ class PostDetailViewModel(
comment = comment,
saved = newValue,
)
notificationCenter.send(NotificationCenter.Event.CommentUpdate(newComment))
} catch (e: Throwable) {
e.printStackTrace()
mvi.updateState {

View File

@ -4,6 +4,8 @@ import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.racconforlemmy.core.utils.HapticFeedback
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.preferences.KeyStoreKeys
import com.github.diegoberaldin.raccoonforlemmy.core.preferences.TemporaryKeyStore
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
@ -24,13 +26,25 @@ class UserPostsViewModel(
private val postsRepository: PostsRepository,
private val hapticFeedback: HapticFeedback,
private val keyStore: TemporaryKeyStore,
private val notificationCenter: NotificationCenter,
) : ScreenModel,
MviModel<UserPostsMviModel.Intent, UserPostsMviModel.UiState, UserPostsMviModel.Effect> by mvi {
private var currentPage: Int = 1
fun finalize() {
notificationCenter.removeObserver(this::class.simpleName.orEmpty())
}
override fun onStarted() {
mvi.onStarted()
notificationCenter.addObserver({
(it as? PostModel)?.also { post ->
handlePostUpdate(post)
}
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.PostUpdate)
mvi.scope.launch(Dispatchers.IO) {
val user = userRepository.get(user.id)
if (user != null) {
@ -248,4 +262,18 @@ class UserPostsViewModel(
}
}
}
private fun handlePostUpdate(post: PostModel) {
mvi.updateState {
it.copy(
posts = it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
}
}
}

View File

@ -19,6 +19,8 @@ object DefaultNotificationCenter : NotificationCenter {
}
override fun addObserver(observer: (Any) -> Unit, key: String, contract: String) {
removeObserver(key)
val mapKey = key to contract
registry[mapKey] = observer
}

View File

@ -1,7 +1,5 @@
package com.github.diegoberaldin.raccoonforlemmy.core.notifications
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
import kotlinx.coroutines.flow.SharedFlow
/**
@ -13,8 +11,6 @@ interface NotificationCenter {
* Available event types.
*/
sealed interface Event {
data class PostUpdate(val post: PostModel) : Event
data class CommentUpdate(val comment: CommentModel) : Event
object Logout : Event
}

View File

@ -0,0 +1,5 @@
package com.github.diegoberaldin.raccoonforlemmy.core.notifications
object NotificationCenterContractKeys {
const val PostUpdate = "postUpdate"
}

View File

@ -16,6 +16,7 @@ interface PostListMviModel :
data class UpVotePost(val index: Int, val feedback: Boolean = false) : Intent
data class DownVotePost(val index: Int, val feedback: Boolean = false) : Intent
data class SavePost(val index: Int, val feedback: Boolean = false) : Intent
data class HandlePostUpdate(val post: PostModel) : Intent
object HapticIndication : Intent
}

View File

@ -61,6 +61,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.commonui.postdetail.PostDet
import com.github.diegoberaldin.raccoonforlemmy.core.commonui.userdetail.UserDetailScreen
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.di.getNotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.ListingType
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
import com.github.diegoberaldin.raccoonforlemmy.feature.home.di.getHomeScreenModel
import com.github.diegoberaldin.raccoonforlemmy.feature.home.ui.HomeTab

View File

@ -5,6 +5,7 @@ import com.github.diegoberaldin.racconforlemmy.core.utils.HapticFeedback
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.core.preferences.KeyStoreKeys
import com.github.diegoberaldin.raccoonforlemmy.core.preferences.TemporaryKeyStore
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.ApiConfigurationRepository
@ -35,6 +36,10 @@ class PostListViewModel(
private var currentPage: Int = 1
fun finalize() {
notificationCenter.removeObserver(this::class.simpleName.orEmpty())
}
override fun reduce(intent: PostListMviModel.Intent) {
when (intent) {
PostListMviModel.Intent.LoadNextPage -> loadNextPage()
@ -57,15 +62,13 @@ class PostListViewModel(
)
PostListMviModel.Intent.HapticIndication -> hapticFeedback.vibrate()
is PostListMviModel.Intent.HandlePostUpdate -> handlePostUpdate(intent.post)
}
}
override fun onDisposed() {
mvi.onDisposed()
}
override fun onStarted() {
mvi.onStarted()
val listingType = keyStore[KeyStoreKeys.DefaultListingType, 0].toListingType()
val sortType = keyStore[KeyStoreKeys.DefaultPostSortType, 0].toSortType()
mvi.updateState {
@ -77,33 +80,18 @@ class PostListViewModel(
)
}
notificationCenter.addObserver({
(it as? PostModel)?.also { post ->
handlePostUpdate(post)
}
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.PostUpdate)
mvi.scope.launch(Dispatchers.Main) {
identityRepository.authToken.map { !it.isNullOrEmpty() }.onEach { isLogged ->
mvi.updateState {
it.copy(isLogged = isLogged)
}
}.launchIn(this)
notificationCenter.events
.onEach { evt ->
when (evt) {
is NotificationCenter.Event.PostUpdate -> {
val newPost = evt.post
mvi.updateState {
it.copy(
posts = it.posts.map { p ->
if (p.id == newPost.id) {
newPost
} else {
p
}
},
)
}
}
else -> Unit
}
}.launchIn(this)
notificationCenter.events
.onEach { evt ->
when (evt) {
@ -328,4 +316,18 @@ class PostListViewModel(
}
}
}
private fun handlePostUpdate(post: PostModel) {
mvi.updateState {
it.copy(
posts = it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
}
}
}

View File

@ -3,7 +3,10 @@ package com.github.diegoberaldin.raccoonforlemmy.feature.profile.content.logged.
import cafe.adriel.voyager.core.model.ScreenModel
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel
import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenter
import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationCenterContractKeys
import com.github.diegoberaldin.raccoonforlemmy.domain.identity.repository.IdentityRepository
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.UserModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.PostsRepository
@ -18,14 +21,25 @@ class ProfilePostsViewModel(
private val savedOnly: Boolean = false,
private val identityRepository: IdentityRepository,
private val userRepository: UserRepository,
private val notificationCenter: NotificationCenter,
) : ScreenModel,
MviModel<ProfilePostsMviModel.Intent, ProfilePostsMviModel.UiState, ProfilePostsMviModel.Effect> by mvi {
private var currentPage: Int = 1
fun finalize() {
notificationCenter.removeObserver(this::class.simpleName.orEmpty())
}
override fun onStarted() {
mvi.onStarted()
notificationCenter.addObserver({
(it as? PostModel)?.also { post ->
handlePostUpdate(post)
}
}, this::class.simpleName.orEmpty(), NotificationCenterContractKeys.PostUpdate)
if (mvi.uiState.value.posts.isEmpty()) {
refresh()
}
@ -79,4 +93,18 @@ class ProfilePostsViewModel(
}
}
}
private fun handlePostUpdate(post: PostModel) {
mvi.updateState {
it.copy(
posts = it.posts.map { p ->
if (p.id == post.id) {
post
} else {
p
}
},
)
}
}
}

View File

@ -42,6 +42,7 @@ val profileTabModule = module {
savedOnly = params[1],
identityRepository = get(),
userRepository = get(),
notificationCenter = get(),
)
}
factory { params ->