From 705c0893e25cae5570d929962b37e64459af7347 Mon Sep 17 00:00:00 2001 From: Dieguitux Date: Mon, 13 Jan 2025 09:56:32 +0100 Subject: [PATCH] fix: avoid concurrency issue while posts are being marked as read before opening detail (#262) * update PostListScreen * update CommunityDetailScreen * update FilteredContentsScreen * update MultiCommunityScreen * update UserDetailScreen * update ProfileLoggedScreen * update InboxChatScreen * update InboxMentionsScreen * update InboxRepliesScreen --- .../unit/chat/InboxChatScreen.kt | 2 +- .../unit/chat/InboxChatViewModel.kt | 20 +++-- .../CommunityDetailMviModel.kt | 8 +- .../communitydetail/CommunityDetailScreen.kt | 10 +-- .../CommunityDetailViewModel.kt | 43 ++++++----- .../FilteredContentsMviModel.kt | 10 ++- .../FilteredContentsScreen.kt | 33 ++++++--- .../FilteredContentsViewModel.kt | 17 ++++- .../unit/mentions/InboxMentionsMviModel.kt | 12 +++ .../unit/mentions/InboxMentionsScreen.kt | 26 +++---- .../unit/mentions/InboxMentionsViewModel.kt | 73 ++++++++++++------- .../detail/MultiCommunityMviModel.kt | 8 +- .../detail/MultiCommunityScreen.kt | 16 ++-- .../detail/MultiCommunityViewModel.kt | 47 ++++++------ .../unit/myaccount/ProfileLoggedMviModel.kt | 12 ++- .../unit/myaccount/ProfileLoggedScreen.kt | 36 +++++++-- .../unit/myaccount/ProfileLoggedViewModel.kt | 15 +++- .../unit/postlist/PostListMviModel.kt | 8 +- .../unit/postlist/PostListScreen.kt | 15 ++-- .../unit/postlist/PostListViewModel.kt | 47 ++++++------ .../unit/replies/InboxRepliesMviModel.kt | 12 +++ .../unit/replies/InboxRepliesScreen.kt | 25 ++++--- .../unit/replies/InboxRepliesViewModel.kt | 67 ++++++++++------- .../unit/userdetail/UserDetailMviModel.kt | 10 ++- .../unit/userdetail/UserDetailScreen.kt | 30 ++++++-- .../unit/userdetail/UserDetailViewModel.kt | 17 ++++- 26 files changed, 406 insertions(+), 213 deletions(-) diff --git a/unit/chat/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/chat/InboxChatScreen.kt b/unit/chat/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/chat/InboxChatScreen.kt index aa6c12ff8..2b0d83f6d 100644 --- a/unit/chat/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/chat/InboxChatScreen.kt +++ b/unit/chat/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/chat/InboxChatScreen.kt @@ -277,7 +277,7 @@ class InboxChatScreen( items( items = uiState.messages, key = { - it.id.toString() + (it.updateDate ?: it.publishDate) + it.read + it.id.toString() + (it.updateDate ?: it.publishDate) }, ) { message -> val isMyMessage = message.creator?.id == uiState.currentUserId diff --git a/unit/chat/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/chat/InboxChatViewModel.kt b/unit/chat/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/chat/InboxChatViewModel.kt index d7135217c..987987cfb 100644 --- a/unit/chat/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/chat/InboxChatViewModel.kt +++ b/unit/chat/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/chat/InboxChatViewModel.kt @@ -166,21 +166,19 @@ class InboxChatViewModel( } } - private fun markAsRead( + private suspend fun markAsRead( read: Boolean, messageId: Long, ) { val auth = identityRepository.authToken.value - screenModelScope.launch { - val newMessage = - messageRepository.markAsRead( - read = read, - messageId = messageId, - auth = auth, - ) - if (newMessage != null) { - handleMessageUpdate(newMessage) - } + val newMessage = + messageRepository.markAsRead( + read = read, + messageId = messageId, + auth = auth, + ) + if (newMessage != null) { + handleMessageUpdate(newMessage) } } diff --git a/unit/communitydetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/communitydetail/CommunityDetailMviModel.kt b/unit/communitydetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/communitydetail/CommunityDetailMviModel.kt index 5c8ff485b..656769516 100644 --- a/unit/communitydetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/communitydetail/CommunityDetailMviModel.kt +++ b/unit/communitydetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/communitydetail/CommunityDetailMviModel.kt @@ -102,7 +102,9 @@ interface CommunityDetailMviModel : val value: Boolean, ) : Intent - data object WillOpenDetail : Intent + data class WillOpenDetail( + val id: Long, + ) : Intent data object UnhideCommunity : Intent @@ -175,5 +177,9 @@ interface CommunityDetailMviModel : ) : Effect data object Back : Effect + + data class OpenDetail( + val post: PostModel, + ) : Effect } } diff --git a/unit/communitydetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/communitydetail/CommunityDetailScreen.kt b/unit/communitydetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/communitydetail/CommunityDetailScreen.kt index ae7b7ff1f..745778b80 100644 --- a/unit/communitydetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/communitydetail/CommunityDetailScreen.kt +++ b/unit/communitydetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/communitydetail/CommunityDetailScreen.kt @@ -246,6 +246,8 @@ class CommunityDetailScreen( } CommunityDetailMviModel.Effect.Back -> navigationCoordinator.popScreen() + is CommunityDetailMviModel.Effect.OpenDetail -> + detailOpener.openPostDetail(effect.post) } }.launchIn(this) } @@ -822,7 +824,7 @@ class CommunityDetailScreen( items( items = uiState.posts, key = { - it.id.toString() + (it.updateDate ?: it.publishDate) + it.read + it.id.toString() + (it.updateDate ?: it.publishDate) }, ) { post -> LaunchedEffect(post.id) { @@ -977,11 +979,10 @@ class CommunityDetailScreen( meTagColor = uiState.meTagColor, onClick = { model.reduce( - CommunityDetailMviModel.Intent.MarkAsRead( + CommunityDetailMviModel.Intent.WillOpenDetail( post.id, ), ) - model.reduce(CommunityDetailMviModel.Intent.WillOpenDetail) detailOpener.openPostDetail( post = post, otherInstance = otherInstanceName, @@ -1027,11 +1028,10 @@ class CommunityDetailScreen( onReply = { model.reduce( - CommunityDetailMviModel.Intent.MarkAsRead( + CommunityDetailMviModel.Intent.WillOpenDetail( post.id, ), ) - model.reduce(CommunityDetailMviModel.Intent.WillOpenDetail) detailOpener.openPostDetail(post) }.takeIf { uiState.isLogged && !isOnOtherInstance }, onOpenImage = { url -> diff --git a/unit/communitydetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/communitydetail/CommunityDetailViewModel.kt b/unit/communitydetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/communitydetail/CommunityDetailViewModel.kt index cd48b6c39..224c15265 100644 --- a/unit/communitydetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/communitydetail/CommunityDetailViewModel.kt +++ b/unit/communitydetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/communitydetail/CommunityDetailViewModel.kt @@ -299,8 +299,9 @@ class CommunityDetailViewModel( CommunityDetailMviModel.Intent.Block -> blockCommunity() CommunityDetailMviModel.Intent.BlockInstance -> blockInstance() - is CommunityDetailMviModel.Intent.MarkAsRead -> { - markAsRead(uiState.value.posts.first { it.id == intent.id }) + is CommunityDetailMviModel.Intent.MarkAsRead -> + screenModelScope.launch { + markAsRead(uiState.value.posts.first { it.id == intent.id }) } CommunityDetailMviModel.Intent.ClearRead -> clearRead() @@ -367,9 +368,15 @@ class CommunityDetailViewModel( is CommunityDetailMviModel.Intent.SetSearch -> updateSearchText(intent.value) - CommunityDetailMviModel.Intent.WillOpenDetail -> { - val state = postPaginationManager.extractState() - postNavigationManager.push(state) + is CommunityDetailMviModel.Intent.WillOpenDetail -> + screenModelScope.launch { + uiState.value.posts + .firstOrNull { it.id == intent.id } + ?.also { post -> + markAsRead(post) + val state = postPaginationManager.extractState() + postNavigationManager.push(state) + } } CommunityDetailMviModel.Intent.UnhideCommunity -> { @@ -552,24 +559,22 @@ class CommunityDetailViewModel( } } - private fun markAsRead(post: PostModel) { + private suspend fun markAsRead(post: PostModel) { if (post.read) { return } val newPost = post.copy(read = true) - screenModelScope.launch { - try { - val auth = identityRepository.authToken.value.orEmpty() - postRepository.setRead( - read = true, - postId = post.id, - auth = auth, - ) - handlePostUpdate(newPost) - } catch (e: Throwable) { - e.printStackTrace() - handlePostUpdate(post) - } + try { + val auth = identityRepository.authToken.value.orEmpty() + postRepository.setRead( + read = true, + postId = post.id, + auth = auth, + ) + handlePostUpdate(newPost) + } catch (e: Throwable) { + e.printStackTrace() + handlePostUpdate(post) } } diff --git a/unit/filteredcontents/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/filteredcontents/FilteredContentsMviModel.kt b/unit/filteredcontents/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/filteredcontents/FilteredContentsMviModel.kt index ab5ded9fd..6b1484e7f 100644 --- a/unit/filteredcontents/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/filteredcontents/FilteredContentsMviModel.kt +++ b/unit/filteredcontents/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/filteredcontents/FilteredContentsMviModel.kt @@ -100,7 +100,10 @@ interface FilteredContentsMviModel : val commentId: Long, ) : Intent - data object WillOpenDetail : Intent + data class WillOpenDetail( + val postId: Long, + val commentId: Long? = null, + ) : Intent } data class State( @@ -136,5 +139,10 @@ interface FilteredContentsMviModel : sealed interface Effect { data object BackToTop : Effect + + data class OpenDetail( + val postId: Long, + val commentId: Long? = null, + ) : Effect } } diff --git a/unit/filteredcontents/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/filteredcontents/FilteredContentsScreen.kt b/unit/filteredcontents/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/filteredcontents/FilteredContentsScreen.kt index cc7034c3e..1f176edfc 100644 --- a/unit/filteredcontents/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/filteredcontents/FilteredContentsScreen.kt +++ b/unit/filteredcontents/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/filteredcontents/FilteredContentsScreen.kt @@ -154,6 +154,13 @@ class FilteredContentsScreen( topAppBarState.contentOffset = 0f } } + + is FilteredContentsMviModel.Effect.OpenDetail -> + detailOpener.openPostDetail( + post = PostModel(id = effect.postId), + highlightCommentId = effect.commentId, + isMod = true, + ) } }.launchIn(this) } @@ -369,7 +376,7 @@ class FilteredContentsScreen( items( items = uiState.posts, key = { - it.id.toString() + (it.updateDate ?: it.publishDate) + it.read + it.id.toString() + (it.updateDate ?: it.publishDate) }, ) { post -> @@ -482,8 +489,11 @@ class FilteredContentsScreen( botTagColor = uiState.botTagColor, meTagColor = uiState.meTagColor, onClick = { - model.reduce(FilteredContentsMviModel.Intent.WillOpenDetail) - detailOpener.openPostDetail(post) + model.reduce( + FilteredContentsMviModel.Intent.WillOpenDetail( + postId = post.id, + ), + ) }, onOpenCommunity = { community, instance -> detailOpener.openCommunityDetail( @@ -512,8 +522,11 @@ class FilteredContentsScreen( ) }, onReply = { - model.reduce(FilteredContentsMviModel.Intent.WillOpenDetail) - detailOpener.openPostDetail(post) + model.reduce( + FilteredContentsMviModel.Intent.WillOpenDetail( + postId = post.id, + ), + ) }, onOpenImage = { url -> navigationCoordinator.pushScreen( @@ -820,11 +833,11 @@ class FilteredContentsScreen( detailOpener.openUserDetail(user, instance) }, onOpen = { - model.reduce(FilteredContentsMviModel.Intent.WillOpenDetail) - detailOpener.openPostDetail( - post = PostModel(id = comment.postId), - highlightCommentId = comment.id, - isMod = true, + model.reduce( + FilteredContentsMviModel.Intent.WillOpenDetail( + postId = comment.postId, + commentId = comment.id, + ), ) }, onUpVote = { diff --git a/unit/filteredcontents/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/filteredcontents/FilteredContentsViewModel.kt b/unit/filteredcontents/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/filteredcontents/FilteredContentsViewModel.kt index 91d5fad2c..b9bb481b4 100644 --- a/unit/filteredcontents/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/filteredcontents/FilteredContentsViewModel.kt +++ b/unit/filteredcontents/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/filteredcontents/FilteredContentsViewModel.kt @@ -212,10 +212,19 @@ class FilteredContentsViewModel( distinguish(comment) } - FilteredContentsMviModel.Intent.WillOpenDetail -> { - val state = postPaginationManager.extractState() - postNavigationManager.push(state) - } + is FilteredContentsMviModel.Intent.WillOpenDetail -> + screenModelScope.launch { + if (intent.commentId == null) { + val state = postPaginationManager.extractState() + postNavigationManager.push(state) + } + emitEffect( + FilteredContentsMviModel.Effect.OpenDetail( + postId = intent.postId, + commentId = intent.commentId, + ), + ) + } } } diff --git a/unit/mentions/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/mentions/InboxMentionsMviModel.kt b/unit/mentions/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/mentions/InboxMentionsMviModel.kt index cd40a941b..f479f7689 100644 --- a/unit/mentions/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/mentions/InboxMentionsMviModel.kt +++ b/unit/mentions/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/mentions/InboxMentionsMviModel.kt @@ -7,6 +7,7 @@ import com.livefast.eattrash.raccoonforlemmy.core.appearance.data.VoteFormat import com.livefast.eattrash.raccoonforlemmy.core.architecture.MviModel import com.livefast.eattrash.raccoonforlemmy.core.persistence.data.ActionOnSwipe import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.PersonMentionModel +import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.PostModel @Stable interface InboxMentionsMviModel : @@ -31,6 +32,12 @@ interface InboxMentionsMviModel : data class DownVoteComment( val id: Long, ) : Intent + + data class WillOpenDetail( + val id: Long, + val post: PostModel, + val commentId: Long, + ) : Intent } data class UiState( @@ -58,5 +65,10 @@ interface InboxMentionsMviModel : ) : Effect data object BackToTop : Effect + + data class OpenDetail( + val post: PostModel, + val commentId: Long, + ) : Effect } } diff --git a/unit/mentions/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/mentions/InboxMentionsScreen.kt b/unit/mentions/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/mentions/InboxMentionsScreen.kt index ab0a6cdf4..75f2386de 100644 --- a/unit/mentions/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/mentions/InboxMentionsScreen.kt +++ b/unit/mentions/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/mentions/InboxMentionsScreen.kt @@ -99,6 +99,12 @@ class InboxMentionsScreen : Tab { lazyListState.scrollToItem(0) } } + + is InboxMentionsMviModel.Effect.OpenDetail -> + detailOpener.openPostDetail( + post = effect.post, + highlightCommentId = effect.commentId, + ) } }.launchIn(this) } @@ -138,7 +144,7 @@ class InboxMentionsScreen : Tab { } items( items = uiState.mentions, - key = { it.id.toString() + it.read + uiState.unreadOnly }, + key = { it.id.toString() + uiState.unreadOnly }, ) { mention -> @Composable fun List.toSwipeActions(): List = @@ -233,18 +239,12 @@ class InboxMentionsScreen : Tab { downVoteEnabled = uiState.downVoteEnabled, previewMaxLines = uiState.previewMaxLines, onClick = { post -> - if (!mention.read) { - model.reduce( - InboxMentionsMviModel.Intent.MarkAsRead( - read = true, - id = mention.id, - ), - ) - } - detailOpener.openPostDetail( - post = post, - highlightCommentId = mention.comment.id, - otherInstance = "", + model.reduce( + InboxMentionsMviModel.Intent.WillOpenDetail( + id = mention.id, + post = post, + commentId = mention.comment.id, + ), ) }, onOpenCreator = { user, instance -> diff --git a/unit/mentions/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/mentions/InboxMentionsViewModel.kt b/unit/mentions/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/mentions/InboxMentionsViewModel.kt index bc5a365b1..ba991a786 100644 --- a/unit/mentions/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/mentions/InboxMentionsViewModel.kt +++ b/unit/mentions/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/mentions/InboxMentionsViewModel.kt @@ -101,13 +101,14 @@ class InboxMentionsViewModel( emitEffect(InboxMentionsMviModel.Effect.BackToTop) } - is InboxMentionsMviModel.Intent.MarkAsRead -> { - val mention = uiState.value.mentions.first { it.id == intent.id } - markAsRead( - read = intent.read, - mention = mention, - ) - } + is InboxMentionsMviModel.Intent.MarkAsRead -> + screenModelScope.launch { + val mention = uiState.value.mentions.first { it.id == intent.id } + markAsRead( + read = intent.read, + mention = mention, + ) + } InboxMentionsMviModel.Intent.HapticIndication -> hapticFeedback.vibrate() is InboxMentionsMviModel.Intent.DownVoteComment -> { @@ -119,6 +120,24 @@ class InboxMentionsViewModel( val mention = uiState.value.mentions.first { it.id == intent.id } toggleUpVoteComment(mention) } + + is InboxMentionsMviModel.Intent.WillOpenDetail -> + screenModelScope.launch { + uiState.value.mentions.firstOrNull { it.id == intent.id }?.also { mention -> + if (!mention.read) { + markAsRead( + mention = mention, + read = true, + ) + } + emitEffect( + InboxMentionsMviModel.Effect.OpenDetail( + post = intent.post, + commentId = intent.commentId, + ), + ) + } + } } } @@ -202,33 +221,31 @@ class InboxMentionsViewModel( } } - private fun markAsRead( + private suspend fun markAsRead( read: Boolean, mention: PersonMentionModel, ) { val auth = identityRepository.authToken.value - screenModelScope.launch { - userRepository.setMentionRead( - read = read, - mentionId = mention.id, - auth = auth, - ) - val currentState = uiState.value - if (read && currentState.unreadOnly) { - updateState { - it.copy( - mentions = - currentState.mentions.filter { m -> - m.id != mention.id - }, - ) - } - } else { - val newMention = mention.copy(read = read) - handleItemUpdate(newMention) + userRepository.setMentionRead( + read = read, + mentionId = mention.id, + auth = auth, + ) + val currentState = uiState.value + if (read && currentState.unreadOnly) { + updateState { + it.copy( + mentions = + currentState.mentions.filter { m -> + m.id != mention.id + }, + ) } - updateUnreadItems() + } else { + val newMention = mention.copy(read = read) + handleItemUpdate(newMention) } + updateUnreadItems() } private fun toggleUpVoteComment(mention: PersonMentionModel) { diff --git a/unit/multicommunity/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/multicommunity/detail/MultiCommunityMviModel.kt b/unit/multicommunity/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/multicommunity/detail/MultiCommunityMviModel.kt index 96eae3dfa..78653f359 100644 --- a/unit/multicommunity/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/multicommunity/detail/MultiCommunityMviModel.kt +++ b/unit/multicommunity/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/multicommunity/detail/MultiCommunityMviModel.kt @@ -48,7 +48,9 @@ interface MultiCommunityMviModel : val url: String, ) : Intent - data object WillOpenDetail : Intent + data class WillOpenDetail( + val id: Long, + ) : Intent } data class UiState( @@ -83,5 +85,9 @@ interface MultiCommunityMviModel : sealed interface Effect { data object BackToTop : Effect + + data class OpenDetail( + val post: PostModel, + ) : Effect } } diff --git a/unit/multicommunity/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/multicommunity/detail/MultiCommunityScreen.kt b/unit/multicommunity/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/multicommunity/detail/MultiCommunityScreen.kt index d1c9a3173..4f8220523 100644 --- a/unit/multicommunity/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/multicommunity/detail/MultiCommunityScreen.kt +++ b/unit/multicommunity/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/multicommunity/detail/MultiCommunityScreen.kt @@ -138,6 +138,9 @@ class MultiCommunityScreen( topAppBarState.contentOffset = 0f } } + + is MultiCommunityMviModel.Effect.OpenDetail -> + detailOpener.openPostDetail(post = effect.post) } }.launchIn(this) } @@ -282,7 +285,7 @@ class MultiCommunityScreen( items( items = uiState.posts, key = { - it.id.toString() + (it.updateDate ?: it.publishDate) + it.read + it.id.toString() + (it.updateDate ?: it.publishDate) }, ) { post -> LaunchedEffect(post.id) { @@ -404,9 +407,9 @@ class MultiCommunityScreen( botTagColor = uiState.botTagColor, meTagColor = uiState.meTagColor, onClick = { - model.reduce(MultiCommunityMviModel.Intent.MarkAsRead(post.id)) - model.reduce(MultiCommunityMviModel.Intent.WillOpenDetail) - detailOpener.openPostDetail(post) + model.reduce( + MultiCommunityMviModel.Intent.WillOpenDetail(post.id), + ) }, onDoubleClick = { @@ -439,8 +442,9 @@ class MultiCommunityScreen( ) }, onReply = { - model.reduce(MultiCommunityMviModel.Intent.WillOpenDetail) - detailOpener.openPostDetail(post) + model.reduce( + MultiCommunityMviModel.Intent.WillOpenDetail(post.id), + ) }, onOpenImage = { url -> model.reduce(MultiCommunityMviModel.Intent.MarkAsRead(post.id)) diff --git a/unit/multicommunity/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/multicommunity/detail/MultiCommunityViewModel.kt b/unit/multicommunity/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/multicommunity/detail/MultiCommunityViewModel.kt index 81be5df95..ff71cc363 100644 --- a/unit/multicommunity/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/multicommunity/detail/MultiCommunityViewModel.kt +++ b/unit/multicommunity/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/multicommunity/detail/MultiCommunityViewModel.kt @@ -191,19 +191,26 @@ class MultiCommunityViewModel( MultiCommunityMviModel.Intent.ClearRead -> clearRead() is MultiCommunityMviModel.Intent.MarkAsRead -> - markAsRead( - post = uiState.value.posts.first { it.id == intent.id }, - ) + screenModelScope.launch { + markAsRead( + post = uiState.value.posts.first { it.id == intent.id }, + ) + } is MultiCommunityMviModel.Intent.Hide -> hide( post = uiState.value.posts.first { it.id == intent.id }, ) - MultiCommunityMviModel.Intent.WillOpenDetail -> { - val state = postPaginationManager.extractState() - postNavigationManager.push(state) - } + is MultiCommunityMviModel.Intent.WillOpenDetail -> + screenModelScope.launch { + uiState.value.posts.firstOrNull { it.id == intent.id }?.also { post -> + markAsRead(post) + val state = postPaginationManager.extractState() + postNavigationManager.push(state) + emitEffect(MultiCommunityMviModel.Effect.OpenDetail(post)) + } + } } } @@ -314,24 +321,22 @@ class MultiCommunityViewModel( } } - private fun markAsRead(post: PostModel) { + private suspend fun markAsRead(post: PostModel) { if (post.read) { return } val newPost = post.copy(read = true) - screenModelScope.launch { - try { - val auth = identityRepository.authToken.value.orEmpty() - postRepository.setRead( - read = true, - postId = post.id, - auth = auth, - ) - handlePostUpdate(newPost) - } catch (e: Throwable) { - e.printStackTrace() - handlePostUpdate(post) - } + try { + val auth = identityRepository.authToken.value.orEmpty() + postRepository.setRead( + read = true, + postId = post.id, + auth = auth, + ) + handlePostUpdate(newPost) + } catch (e: Throwable) { + e.printStackTrace() + handlePostUpdate(post) } } diff --git a/unit/myaccount/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/myaccount/ProfileLoggedMviModel.kt b/unit/myaccount/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/myaccount/ProfileLoggedMviModel.kt index ea9fea9b6..b19f03440 100644 --- a/unit/myaccount/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/myaccount/ProfileLoggedMviModel.kt +++ b/unit/myaccount/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/myaccount/ProfileLoggedMviModel.kt @@ -63,7 +63,10 @@ interface ProfileLoggedMviModel : val feedback: Boolean = false, ) : Intent - data object WillOpenDetail : Intent + data class WillOpenDetail( + val postId: Long, + val commentId: Long? = null, + ) : Intent data class RestorePost( val id: Long, @@ -96,5 +99,10 @@ interface ProfileLoggedMviModel : val isModerator: Boolean = false, ) - sealed interface Effect + sealed interface Effect { + data class OpenDetail( + val postId: Long, + val commentId: Long? = null, + ) : Effect + } } diff --git a/unit/myaccount/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/myaccount/ProfileLoggedScreen.kt b/unit/myaccount/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/myaccount/ProfileLoggedScreen.kt index 813886418..eeb70c057 100644 --- a/unit/myaccount/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/myaccount/ProfileLoggedScreen.kt +++ b/unit/myaccount/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/myaccount/ProfileLoggedScreen.kt @@ -123,6 +123,18 @@ object ProfileLoggedScreen : Tab { model.reduce(ProfileLoggedMviModel.Intent.Refresh) }.launchIn(this) } + LaunchedEffect(model) { + model.effects + .onEach { effect -> + when (effect) { + is ProfileLoggedMviModel.Effect.OpenDetail -> + detailOpener.openPostDetail( + post = PostModel(id = effect.postId), + highlightCommentId = effect.commentId, + ) + } + }.launchIn(this) + } if (uiState.initial) { ProgressHud() @@ -277,7 +289,7 @@ object ProfileLoggedScreen : Tab { items( items = uiState.posts, key = { - it.id.toString() + (it.updateDate ?: it.publishDate) + it.read + it.id.toString() + (it.updateDate ?: it.publishDate) }, ) { post -> PostCard( @@ -295,12 +307,18 @@ object ProfileLoggedScreen : Tab { blurNsfw = false, downVoteEnabled = uiState.downVoteEnabled, onClick = { - model.reduce(ProfileLoggedMviModel.Intent.WillOpenDetail) - detailOpener.openPostDetail(post) + model.reduce( + ProfileLoggedMviModel.Intent.WillOpenDetail( + postId = post.id, + ), + ) }, onReply = { - model.reduce(ProfileLoggedMviModel.Intent.WillOpenDetail) - detailOpener.openPostDetail(post) + model.reduce( + ProfileLoggedMviModel.Intent.WillOpenDetail( + postId = post.id, + ), + ) }, onOpenCommunity = { community, instance -> detailOpener.openCommunityDetail(community, instance) @@ -494,9 +512,11 @@ object ProfileLoggedScreen : Tab { detailOpener.openCommunityDetail(community, instance) }, onClick = { - detailOpener.openPostDetail( - post = PostModel(id = comment.postId), - highlightCommentId = comment.id, + model.reduce( + ProfileLoggedMviModel.Intent.WillOpenDetail( + postId = comment.postId, + commentId = comment.id, + ), ) }, onReply = { diff --git a/unit/myaccount/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/myaccount/ProfileLoggedViewModel.kt b/unit/myaccount/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/myaccount/ProfileLoggedViewModel.kt index 4f022d87b..a838c800c 100644 --- a/unit/myaccount/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/myaccount/ProfileLoggedViewModel.kt +++ b/unit/myaccount/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/myaccount/ProfileLoggedViewModel.kt @@ -217,9 +217,18 @@ class ProfileLoggedViewModel( } } - ProfileLoggedMviModel.Intent.WillOpenDetail -> { - val state = postPaginationManager.extractState() - postNavigationManager.push(state) + is ProfileLoggedMviModel.Intent.WillOpenDetail -> + screenModelScope.launch { + if (intent.commentId == null) { + val state = postPaginationManager.extractState() + postNavigationManager.push(state) + } + emitEffect( + ProfileLoggedMviModel.Effect.OpenDetail( + postId = intent.postId, + commentId = intent.commentId, + ), + ) } is ProfileLoggedMviModel.Intent.RestorePost -> { diff --git a/unit/postlist/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/postlist/PostListMviModel.kt b/unit/postlist/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/postlist/PostListMviModel.kt index a14bfc3f0..d7e45ee60 100644 --- a/unit/postlist/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/postlist/PostListMviModel.kt +++ b/unit/postlist/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/postlist/PostListMviModel.kt @@ -66,7 +66,9 @@ interface PostListMviModel : data object PauseZombieMode : Intent - data object WillOpenDetail : Intent + data class WillOpenDetail( + val id: Long, + ) : Intent } data class UiState( @@ -107,5 +109,9 @@ interface PostListMviModel : data class ZombieModeTick( val index: Int, ) : Effect + + data class OpenDetail( + val post: PostModel, + ) : Effect } } diff --git a/unit/postlist/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/postlist/PostListScreen.kt b/unit/postlist/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/postlist/PostListScreen.kt index a5c5d84a2..f8d0d93e6 100644 --- a/unit/postlist/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/postlist/PostListScreen.kt +++ b/unit/postlist/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/postlist/PostListScreen.kt @@ -188,6 +188,9 @@ class PostListScreen : Screen { } } } + + is PostListMviModel.Effect.OpenDetail -> + detailOpener.openPostDetail(effect.post) } }.launchIn(this) } @@ -361,7 +364,7 @@ class PostListScreen : Screen { key = { it.id.toString() + ( it.updateDate ?: it.publishDate - ) + it.read + uiState.isLogged + ) + uiState.isLogged }, ) { post -> LaunchedEffect(post.id) { @@ -504,9 +507,7 @@ class PostListScreen : Screen { botTagColor = uiState.botTagColor, meTagColor = uiState.meTagColor, onClick = { - model.reduce(PostListMviModel.Intent.MarkAsRead(post.id)) - model.reduce(PostListMviModel.Intent.WillOpenDetail) - detailOpener.openPostDetail(post) + model.reduce(PostListMviModel.Intent.WillOpenDetail(post.id)) }, onDoubleClick = { @@ -549,9 +550,9 @@ class PostListScreen : Screen { }, onReply = { if (uiState.isLogged) { - model.reduce(PostListMviModel.Intent.MarkAsRead(post.id)) - model.reduce(PostListMviModel.Intent.WillOpenDetail) - detailOpener.openPostDetail(post) + model.reduce( + PostListMviModel.Intent.WillOpenDetail(post.id), + ) } }, onOpenImage = { url -> diff --git a/unit/postlist/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/postlist/PostListViewModel.kt b/unit/postlist/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/postlist/PostListViewModel.kt index b8a028bcd..a42e5b4cf 100644 --- a/unit/postlist/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/postlist/PostListViewModel.kt +++ b/unit/postlist/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/postlist/PostListViewModel.kt @@ -268,11 +268,12 @@ class PostListViewModel( shareHelper.share(intent.url) } - is PostListMviModel.Intent.MarkAsRead -> { - uiState.value.posts.firstOrNull { it.id == intent.id }?.also { post -> - markAsRead(post = post) + is PostListMviModel.Intent.MarkAsRead -> + screenModelScope.launch { + uiState.value.posts.firstOrNull { it.id == intent.id }?.also { post -> + markAsRead(post = post) + } } - } PostListMviModel.Intent.ClearRead -> clearRead() is PostListMviModel.Intent.Hide -> { @@ -298,9 +299,15 @@ class PostListViewModel( } } - PostListMviModel.Intent.WillOpenDetail -> { - val state = postPaginationManager.extractState() - postNavigationManager.push(state) + is PostListMviModel.Intent.WillOpenDetail -> { + screenModelScope.launch { + uiState.value.posts.firstOrNull { it.id == intent.id }?.also { post -> + markAsRead(post) + val state = postPaginationManager.extractState() + postNavigationManager.push(state) + emitEffect(PostListMviModel.Effect.OpenDetail(post)) + } + } } } } @@ -427,24 +434,22 @@ class PostListViewModel( } } - private fun markAsRead(post: PostModel) { + private suspend fun markAsRead(post: PostModel) { if (post.read) { return } val newPost = post.copy(read = true) - screenModelScope.launch { - try { - val auth = identityRepository.authToken.value.orEmpty() - postRepository.setRead( - read = true, - postId = post.id, - auth = auth, - ) - handlePostUpdate(newPost) - } catch (e: Throwable) { - e.printStackTrace() - handlePostUpdate(post) - } + try { + val auth = identityRepository.authToken.value.orEmpty() + postRepository.setRead( + read = true, + postId = post.id, + auth = auth, + ) + handlePostUpdate(newPost) + } catch (e: Throwable) { + e.printStackTrace() + handlePostUpdate(post) } } diff --git a/unit/replies/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/replies/InboxRepliesMviModel.kt b/unit/replies/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/replies/InboxRepliesMviModel.kt index c6b23f5cc..f50943dbd 100644 --- a/unit/replies/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/replies/InboxRepliesMviModel.kt +++ b/unit/replies/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/replies/InboxRepliesMviModel.kt @@ -7,6 +7,7 @@ import com.livefast.eattrash.raccoonforlemmy.core.appearance.data.VoteFormat import com.livefast.eattrash.raccoonforlemmy.core.architecture.MviModel import com.livefast.eattrash.raccoonforlemmy.core.persistence.data.ActionOnSwipe import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.PersonMentionModel +import com.livefast.eattrash.raccoonforlemmy.domain.lemmy.data.PostModel @Stable interface InboxRepliesMviModel : @@ -31,6 +32,12 @@ interface InboxRepliesMviModel : data class DownVoteComment( val id: Long, ) : Intent + + data class WillOpenDetail( + val id: Long, + val post: PostModel, + val commentId: Long, + ) : Intent } data class UiState( @@ -58,5 +65,10 @@ interface InboxRepliesMviModel : ) : Effect data object BackToTop : Effect + + data class OpenDetail( + val post: PostModel, + val commentId: Long, + ) : Effect } } diff --git a/unit/replies/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/replies/InboxRepliesScreen.kt b/unit/replies/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/replies/InboxRepliesScreen.kt index 677393fd8..35c43e3b3 100644 --- a/unit/replies/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/replies/InboxRepliesScreen.kt +++ b/unit/replies/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/replies/InboxRepliesScreen.kt @@ -99,6 +99,12 @@ class InboxRepliesScreen : Tab { lazyListState.scrollToItem(0) } } + + is InboxRepliesMviModel.Effect.OpenDetail -> + detailOpener.openPostDetail( + post = effect.post, + highlightCommentId = effect.commentId, + ) } }.launchIn(this) } @@ -138,7 +144,7 @@ class InboxRepliesScreen : Tab { } items( items = uiState.replies, - key = { it.id.toString() + it.read + uiState.unreadOnly }, + key = { it.id.toString() + uiState.unreadOnly }, ) { reply -> @Composable @@ -234,17 +240,12 @@ class InboxRepliesScreen : Tab { downVoteEnabled = uiState.downVoteEnabled, previewMaxLines = uiState.previewMaxLines, onClick = { post -> - if (!reply.read) { - model.reduce( - InboxRepliesMviModel.Intent.MarkAsRead( - read = true, - id = reply.id, - ), - ) - } - detailOpener.openPostDetail( - post = post, - highlightCommentId = reply.comment.id, + model.reduce( + InboxRepliesMviModel.Intent.WillOpenDetail( + id = reply.id, + post = post, + commentId = reply.comment.id, + ), ) }, onOpenCreator = { user, instance -> diff --git a/unit/replies/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/replies/InboxRepliesViewModel.kt b/unit/replies/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/replies/InboxRepliesViewModel.kt index c2bcdf5e9..06afa2233 100644 --- a/unit/replies/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/replies/InboxRepliesViewModel.kt +++ b/unit/replies/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/replies/InboxRepliesViewModel.kt @@ -101,10 +101,11 @@ class InboxRepliesViewModel( emitEffect(InboxRepliesMviModel.Effect.BackToTop) } - is InboxRepliesMviModel.Intent.MarkAsRead -> { - val reply = uiState.value.replies.first { it.id == intent.id } - markAsRead(read = intent.read, reply = reply) - } + is InboxRepliesMviModel.Intent.MarkAsRead -> + screenModelScope.launch { + val reply = uiState.value.replies.first { it.id == intent.id } + markAsRead(read = intent.read, reply = reply) + } InboxRepliesMviModel.Intent.HapticIndication -> hapticFeedback.vibrate() is InboxRepliesMviModel.Intent.DownVoteComment -> { @@ -116,6 +117,24 @@ class InboxRepliesViewModel( val reply = uiState.value.replies.first { it.id == intent.id } toggleUpVoteComment(reply) } + + is InboxRepliesMviModel.Intent.WillOpenDetail -> + screenModelScope.launch { + uiState.value.replies.firstOrNull { it.id == intent.id }?.also { reply -> + if (!reply.read) { + markAsRead( + reply = reply, + read = true, + ) + } + emitEffect( + InboxRepliesMviModel.Effect.OpenDetail( + post = intent.post, + commentId = intent.commentId, + ), + ) + } + } } } @@ -199,33 +218,31 @@ class InboxRepliesViewModel( } } - private fun markAsRead( + private suspend fun markAsRead( read: Boolean, reply: PersonMentionModel, ) { val auth = identityRepository.authToken.value - screenModelScope.launch { - userRepository.setReplyRead( - read = read, - replyId = reply.id, - auth = auth, - ) - val currentState = uiState.value - if (read && currentState.unreadOnly) { - updateState { - it.copy( - replies = - currentState.replies.filter { r -> - r.id != reply.id - }, - ) - } - } else { - val newItem = reply.copy(read = read) - handleItemUpdate(newItem) + userRepository.setReplyRead( + read = read, + replyId = reply.id, + auth = auth, + ) + val currentState = uiState.value + if (read && currentState.unreadOnly) { + updateState { + it.copy( + replies = + currentState.replies.filter { r -> + r.id != reply.id + }, + ) } - updateUnreadItems() + } else { + val newItem = reply.copy(read = read) + handleItemUpdate(newItem) } + updateUnreadItems() } private fun toggleUpVoteComment(mention: PersonMentionModel) { diff --git a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailMviModel.kt b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailMviModel.kt index a3c00a01c..9a1697a10 100644 --- a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailMviModel.kt +++ b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailMviModel.kt @@ -66,7 +66,10 @@ interface UserDetailMviModel : data object BlockInstance : Intent - data object WillOpenDetail : Intent + data class WillOpenDetail( + val postId: Long, + val commentId: Long? = null, + ) : Intent data class UpdateTags( val ids: List, @@ -122,5 +125,10 @@ interface UserDetailMviModel : ) : Effect data object BackToTop : Effect + + data class OpenDetail( + val postId: Long, + val commentId: Long? = null, + ) : Effect } } diff --git a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailScreen.kt b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailScreen.kt index 792ef6dad..cb63d423a 100644 --- a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailScreen.kt +++ b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailScreen.kt @@ -197,6 +197,12 @@ class UserDetailScreen( } } } + + is UserDetailMviModel.Effect.OpenDetail -> + detailOpener.openPostDetail( + post = PostModel(id = effect.postId), + highlightCommentId = effect.commentId, + ) } }.launchIn(this) } @@ -546,7 +552,7 @@ class UserDetailScreen( items( items = uiState.posts, key = { - it.id.toString() + (it.updateDate ?: it.publishDate) + it.read + it.id.toString() + (it.updateDate ?: it.publishDate) }, ) { post -> @@ -665,8 +671,11 @@ class UserDetailScreen( actionButtonsActive = uiState.isLogged, downVoteEnabled = uiState.downVoteEnabled, onClick = { - model.reduce(UserDetailMviModel.Intent.WillOpenDetail) - detailOpener.openPostDetail(post) + model.reduce( + UserDetailMviModel.Intent.WillOpenDetail( + postId = post.id, + ), + ) }, onDoubleClick = { @@ -709,8 +718,11 @@ class UserDetailScreen( }, onReply = { - model.reduce(UserDetailMviModel.Intent.WillOpenDetail) - detailOpener.openPostDetail(post) + model.reduce( + UserDetailMviModel.Intent.WillOpenDetail( + postId = post.id, + ), + ) }.takeIf { uiState.isLogged && !isOnOtherInstance }, onOpenImage = { url -> navigationCoordinator.pushScreen( @@ -969,9 +981,11 @@ class UserDetailScreen( downVoteEnabled = uiState.downVoteEnabled, actionButtonsActive = uiState.isLogged, onClick = { - detailOpener.openPostDetail( - post = PostModel(id = comment.postId), - highlightCommentId = comment.id, + model.reduce( + UserDetailMviModel.Intent.WillOpenDetail( + postId = comment.postId, + commentId = comment.id, + ), ) }, onImageClick = { url -> diff --git a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailViewModel.kt b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailViewModel.kt index de20ce0cd..018b4e1e5 100644 --- a/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailViewModel.kt +++ b/unit/userdetail/src/commonMain/kotlin/com/livefast/eattrash/raccoonforlemmy/unit/userdetail/UserDetailViewModel.kt @@ -308,10 +308,19 @@ class UserDetailViewModel( UserDetailMviModel.Intent.Block -> blockUser() UserDetailMviModel.Intent.BlockInstance -> blockInstance() - UserDetailMviModel.Intent.WillOpenDetail -> { - val state = postPaginationManager.extractState() - postNavigationManager.push(state) - } + is UserDetailMviModel.Intent.WillOpenDetail -> + screenModelScope.launch { + if (intent.commentId == null) { + val state = postPaginationManager.extractState() + postNavigationManager.push(state) + } + emitEffect( + UserDetailMviModel.Effect.OpenDetail( + postId = intent.postId, + commentId = intent.commentId, + ), + ) + } is UserDetailMviModel.Intent.AddUserTag -> addUserTag(name = intent.name, color = intent.color)