fix(inbox): upvote and downvote from replies and mentions

This commit is contained in:
Diego Beraldin 2023-09-23 00:34:48 +02:00
parent 983a8b6094
commit 4c442a2c52
8 changed files with 245 additions and 3 deletions

View File

@ -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)
},
)
}
}

View File

@ -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(),

View File

@ -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<InboxMentionsMviModel.Intent, InboxMentionsMviModel.UiState, InboxMentionsMviModel.Effect> {
@ -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(

View File

@ -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))
},
)
},
)

View File

@ -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<InboxMentionsMviModel.Intent, InboxMentionsMviModel.UiState, InboxMentionsMviModel.Effect>,
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()) }
}

View File

@ -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(

View File

@ -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))
},
)
},
)

View File

@ -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()) }
}