diff --git a/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/components/InboxMentionCard.kt b/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/components/InboxMentionCard.kt index 33d9de6b9..9d083c784 100644 --- a/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/components/InboxMentionCard.kt +++ b/core-commonui/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/commonui/components/InboxMentionCard.kt @@ -19,6 +19,7 @@ import com.github.diegoberaldin.racconforlemmy.core.utils.onClick import com.github.diegoberaldin.raccoonforlemmy.core.appearance.di.getThemeRepository import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.CornerSize import com.github.diegoberaldin.raccoonforlemmy.core.appearance.theme.Spacing +import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommunityModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PersonMentionModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PostModel @@ -30,6 +31,8 @@ fun InboxMentionCard( onOpenPost: (PostModel) -> Unit, onOpenCreator: (UserModel) -> Unit, onOpenCommunity: (CommunityModel) -> Unit, + onUpVote: ((CommentModel) -> Unit)? = null, + onDownVote: ((CommentModel) -> Unit)? = null, ) { val themeRepository = remember { getThemeRepository() } val fontScale by themeRepository.contentFontScale.collectAsState() @@ -66,10 +69,16 @@ fun InboxMentionCard( score = mention.score, upVoted = mention.myVote > 0, downVoted = mention.myVote < 0, + onOpenCommunity = onOpenCommunity, onOpenCreator = { user -> onOpenCreator(user) }, - onOpenCommunity = onOpenCommunity, + onUpVote = { + onUpVote?.invoke(mention.comment) + }, + onDownVote = { + onDownVote?.invoke(mention.comment) + }, ) } } diff --git a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/di/InboxModule.kt b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/di/InboxModule.kt index 0a26c4d96..04fe20012 100644 --- a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/di/InboxModule.kt +++ b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/di/InboxModule.kt @@ -1,6 +1,7 @@ package com.github.diegoberaldin.raccoonforlemmy.feature.inbox.di import com.github.diegoberaldin.raccoonforlemmy.core.architecture.DefaultMviModel +import com.github.diegoberaldin.raccoonforlemmy.core.commonui.di.commonUiModule import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.DefaultInboxCoordinator import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.InboxCoordinator import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.main.InboxMviModel @@ -31,6 +32,7 @@ val inboxTabModule = module { userRepository = get(), identityRepository = get(), siteRepository = get(), + commentRepository = get(), hapticFeedback = get(), coordinator = get(), notificationCenter = get(), @@ -42,6 +44,7 @@ val inboxTabModule = module { mvi = DefaultMviModel(InboxMentionsMviModel.UiState()), userRepository = get(), identityRepository = get(), + commentRepository = get(), hapticFeedback = get(), coordinator = get(), notificationCenter = get(), diff --git a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/mentions/InboxMentionsMviModel.kt b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/mentions/InboxMentionsMviModel.kt index fc6a674d9..5df384e29 100644 --- a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/mentions/InboxMentionsMviModel.kt +++ b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/mentions/InboxMentionsMviModel.kt @@ -1,7 +1,9 @@ package com.github.diegoberaldin.raccoonforlemmy.feature.inbox.mentions import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel +import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PersonMentionModel +import com.github.diegoberaldin.raccoonforlemmy.feature.inbox.replies.InboxRepliesMviModel interface InboxMentionsMviModel : MviModel { @@ -11,6 +13,8 @@ interface InboxMentionsMviModel : object LoadNextPage : Intent data class MarkAsRead(val read: Boolean, val mentionId: Int) : Intent object HapticIndication : Intent + data class UpVoteComment(val index: Int) : Intent + data class DownVoteComment(val index: Int) : Intent } data class UiState( diff --git a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/mentions/InboxMentionsScreen.kt b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/mentions/InboxMentionsScreen.kt index b3b66dbf1..09f2c6d54 100644 --- a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/mentions/InboxMentionsScreen.kt +++ b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/mentions/InboxMentionsScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.DismissDirection import androidx.compose.material.DismissValue @@ -70,7 +71,7 @@ class InboxMentionsScreen : Tab { modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(Spacing.xs), ) { - items(uiState.mentions) { mention -> + itemsIndexed(uiState.mentions) { idx, mention -> SwipeableCard( modifier = Modifier.fillMaxWidth(), enabled = uiState.swipeActionsEnabled, @@ -146,6 +147,12 @@ class InboxMentionsScreen : Tab { CommunityDetailScreen(community), ) }, + onUpVote = { + model.reduce(InboxMentionsMviModel.Intent.UpVoteComment(idx)) + }, + onDownVote = { + model.reduce(InboxMentionsMviModel.Intent.DownVoteComment(idx)) + }, ) }, ) diff --git a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/mentions/InboxMentionsViewModel.kt b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/mentions/InboxMentionsViewModel.kt index e854573ec..b93739968 100644 --- a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/mentions/InboxMentionsViewModel.kt +++ b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/mentions/InboxMentionsViewModel.kt @@ -9,6 +9,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationC 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 +import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PersonMentionModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommentRepository import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.UserRepository @@ -24,6 +25,7 @@ class InboxMentionsViewModel( private val mvi: DefaultMviModel, private val identityRepository: IdentityRepository, private val userRepository: UserRepository, + private val commentRepository: CommentRepository, private val hapticFeedback: HapticFeedback, private val coordinator: InboxCoordinator, private val notificationCenter: NotificationCenter, @@ -73,6 +75,15 @@ class InboxMentionsViewModel( } InboxMentionsMviModel.Intent.HapticIndication -> hapticFeedback.vibrate() + is InboxMentionsMviModel.Intent.DownVoteComment -> toggleDownVoteComment( + mention = mvi.uiState.value.mentions[intent.index], + feedback = true, + ) + + is InboxMentionsMviModel.Intent.UpVoteComment -> toggleUpVoteComment( + mention = mvi.uiState.value.mentions[intent.index], + feedback = true, + ) } } @@ -135,6 +146,99 @@ class InboxMentionsViewModel( } } + private fun toggleUpVoteComment( + mention: PersonMentionModel, + feedback: Boolean, + ) { + val newValue = mention.myVote <= 0 + if (feedback) { + hapticFeedback.vibrate() + } + val newComment = commentRepository.asUpVoted( + comment = mention.comment, + voted = newValue, + ) + mvi.updateState { + it.copy( + mentions = it.mentions.map { m -> + if (m.comment.id != mention.comment.id) { + m + } else { + m.copy(myVote = newComment.myVote) + } + }, + ) + } + mvi.scope?.launch(Dispatchers.IO) { + try { + val auth = identityRepository.authToken.value.orEmpty() + commentRepository.upVote( + auth = auth, + comment = mention.comment, + voted = newValue, + ) + } catch (e: Throwable) { + e.printStackTrace() + mvi.updateState { + it.copy( + mentions = it.mentions.map { m -> + if (m.comment.id != mention.comment.id) { + m + } else { + m.copy(myVote = mention.myVote) + } + }, + ) + } + } + } + } + + private fun toggleDownVoteComment( + mention: PersonMentionModel, + feedback: Boolean, + ) { + val newValue = mention.myVote >= 0 + if (feedback) { + hapticFeedback.vibrate() + } + val newComment = commentRepository.asDownVoted(mention.comment, newValue) + mvi.updateState { + it.copy( + mentions = it.mentions.map { m -> + if (m.comment.id != mention.comment.id) { + m + } else { + m.copy(myVote = newComment.myVote) + } + }, + ) + } + mvi.scope?.launch(Dispatchers.IO) { + try { + val auth = identityRepository.authToken.value.orEmpty() + commentRepository.downVote( + auth = auth, + comment = mention.comment, + downVoted = newValue, + ) + } catch (e: Throwable) { + e.printStackTrace() + mvi.updateState { + it.copy( + mentions = it.mentions.map { m -> + if (m.comment.id != mention.comment.id) { + m + } else { + m.copy(myVote = mention.myVote) + } + }, + ) + } + } + } + } + private fun handleLogout() { mvi.updateState { it.copy(mentions = emptyList()) } } diff --git a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/replies/InboxRepliesMviModel.kt b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/replies/InboxRepliesMviModel.kt index a6259f732..3d3667406 100644 --- a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/replies/InboxRepliesMviModel.kt +++ b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/replies/InboxRepliesMviModel.kt @@ -1,6 +1,7 @@ package com.github.diegoberaldin.raccoonforlemmy.feature.inbox.replies import com.github.diegoberaldin.raccoonforlemmy.core.architecture.MviModel +import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.CommentModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PersonMentionModel interface InboxRepliesMviModel : @@ -11,6 +12,8 @@ interface InboxRepliesMviModel : object LoadNextPage : Intent data class MarkAsRead(val read: Boolean, val mentionId: Int) : Intent object HapticIndication : Intent + data class UpVoteComment(val index: Int) : Intent + data class DownVoteComment(val index: Int) : Intent } data class UiState( diff --git a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/replies/InboxRepliesScreen.kt b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/replies/InboxRepliesScreen.kt index edd281abc..d7843867b 100644 --- a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/replies/InboxRepliesScreen.kt +++ b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/replies/InboxRepliesScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.DismissDirection import androidx.compose.material.DismissValue @@ -73,7 +74,7 @@ class InboxRepliesScreen : Tab { modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(Spacing.xs), ) { - items(uiState.replies) { mention -> + itemsIndexed(uiState.replies) { idx, mention -> val themeRepository = remember { getThemeRepository() } val fontScale by themeRepository.contentFontScale.collectAsState() CompositionLocalProvider( @@ -157,6 +158,12 @@ class InboxRepliesScreen : Tab { CommunityDetailScreen(community), ) }, + onUpVote = { + model.reduce(InboxRepliesMviModel.Intent.UpVoteComment(idx)) + }, + onDownVote = { + model.reduce(InboxRepliesMviModel.Intent.DownVoteComment(idx)) + }, ) }, ) diff --git a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/replies/InboxRepliesViewModel.kt b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/replies/InboxRepliesViewModel.kt index 94c91ee67..14782932e 100644 --- a/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/replies/InboxRepliesViewModel.kt +++ b/feature-inbox/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/feature/inbox/replies/InboxRepliesViewModel.kt @@ -9,6 +9,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.notifications.NotificationC 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 +import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.PersonMentionModel import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.SortType import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.CommentRepository import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository.SiteRepository @@ -26,6 +27,7 @@ class InboxRepliesViewModel( private val identityRepository: IdentityRepository, private val userRepository: UserRepository, private val siteRepository: SiteRepository, + private val commentRepository: CommentRepository, private val hapticFeedback: HapticFeedback, private val coordinator: InboxCoordinator, private val notificationCenter: NotificationCenter, @@ -75,7 +77,17 @@ class InboxRepliesViewModel( markAsRead(read = intent.read, replyId = intent.mentionId) } + InboxRepliesMviModel.Intent.HapticIndication -> hapticFeedback.vibrate() + is InboxRepliesMviModel.Intent.DownVoteComment -> toggleDownVoteComment( + mention = mvi.uiState.value.replies[intent.index], + feedback = true, + ) + + is InboxRepliesMviModel.Intent.UpVoteComment -> toggleUpVoteComment( + mention = mvi.uiState.value.replies[intent.index], + feedback = true, + ) } } @@ -143,6 +155,99 @@ class InboxRepliesViewModel( } } + private fun toggleUpVoteComment( + mention: PersonMentionModel, + feedback: Boolean, + ) { + val newValue = mention.myVote <= 0 + if (feedback) { + hapticFeedback.vibrate() + } + val newComment = commentRepository.asUpVoted( + comment = mention.comment, + voted = newValue, + ) + mvi.updateState { + it.copy( + replies = it.replies.map { m -> + if (m.comment.id != mention.comment.id) { + m + } else { + m.copy(myVote = newComment.myVote) + } + }, + ) + } + mvi.scope?.launch(Dispatchers.IO) { + try { + val auth = identityRepository.authToken.value.orEmpty() + commentRepository.upVote( + auth = auth, + comment = mention.comment, + voted = newValue, + ) + } catch (e: Throwable) { + e.printStackTrace() + mvi.updateState { + it.copy( + replies = it.replies.map { m -> + if (m.comment.id != mention.comment.id) { + m + } else { + m.copy(myVote = mention.myVote) + } + }, + ) + } + } + } + } + + private fun toggleDownVoteComment( + mention: PersonMentionModel, + feedback: Boolean, + ) { + val newValue = mention.myVote >= 0 + if (feedback) { + hapticFeedback.vibrate() + } + val newComment = commentRepository.asDownVoted(mention.comment, newValue) + mvi.updateState { + it.copy( + replies = it.replies.map { m -> + if (m.comment.id != mention.comment.id) { + m + } else { + m.copy(myVote = newComment.myVote) + } + }, + ) + } + mvi.scope?.launch(Dispatchers.IO) { + try { + val auth = identityRepository.authToken.value.orEmpty() + commentRepository.downVote( + auth = auth, + comment = mention.comment, + downVoted = newValue, + ) + } catch (e: Throwable) { + e.printStackTrace() + mvi.updateState { + it.copy( + replies = it.replies.map { m -> + if (m.comment.id != mention.comment.id) { + m + } else { + m.copy(myVote = mention.myVote) + } + }, + ) + } + } + } + } + private fun handleLogout() { mvi.updateState { it.copy(replies = emptyList()) } }