From 1d6b70e1f0c682260f2b600b41650ea110ed3bbe Mon Sep 17 00:00:00 2001 From: Diego Beraldin Date: Sun, 9 Jun 2024 21:47:19 +0200 Subject: [PATCH] feat: manage local user vote display mode (#960) --- .../core/api/dto/LocalUserView.kt | 1 + .../core/api/dto/LocalUserVoteDisplayMode.kt | 13 +++++ .../core/api/dto/SaveUserSettingsForm.kt | 3 ++ .../identity/usecase/DefaultLoginUseCase.kt | 17 +++++- .../domain/lemmy/data/AccountSettingsModel.kt | 3 ++ .../lemmy/repository/DefaultSiteRepository.kt | 11 +--- .../domain/lemmy/repository/utils/Mappings.kt | 18 +++++++ .../AccountSettingsMviModel.kt | 11 +++- .../accountsettings/AccountSettingsScreen.kt | 53 +++++++++++++++++-- .../AccountSettingsViewModel.kt | 40 +++++++++++++- 10 files changed, 152 insertions(+), 18 deletions(-) create mode 100644 core/api/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/api/dto/LocalUserVoteDisplayMode.kt diff --git a/core/api/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/api/dto/LocalUserView.kt b/core/api/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/api/dto/LocalUserView.kt index 3b14ecb06..38fbfde9d 100644 --- a/core/api/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/api/dto/LocalUserView.kt +++ b/core/api/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/api/dto/LocalUserView.kt @@ -8,4 +8,5 @@ data class LocalUserView( @SerialName("local_user") val localUser: LocalUser? = null, @SerialName("person") val person: Person, @SerialName("counts") val counts: PersonAggregates, + @SerialName("local_user_vote_display_mode") val localUserVoteDisplayMode: LocalUserVoteDisplayMode? = null, ) diff --git a/core/api/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/api/dto/LocalUserVoteDisplayMode.kt b/core/api/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/api/dto/LocalUserVoteDisplayMode.kt new file mode 100644 index 000000000..743fe9193 --- /dev/null +++ b/core/api/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/api/dto/LocalUserVoteDisplayMode.kt @@ -0,0 +1,13 @@ +package com.github.diegoberaldin.raccoonforlemmy.core.api.dto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class LocalUserVoteDisplayMode( + @SerialName("downvotes") val downvotes: Boolean, + @SerialName("local_user_id") val localUserId: Long, + @SerialName("score") val score: Boolean, + @SerialName("upvote_percentage") val upvotePercentage: Boolean, + @SerialName("upvotes") val upvotes: Boolean, +) diff --git a/core/api/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/api/dto/SaveUserSettingsForm.kt b/core/api/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/api/dto/SaveUserSettingsForm.kt index e6cc8a06f..92ad799b0 100644 --- a/core/api/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/api/dto/SaveUserSettingsForm.kt +++ b/core/api/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/core/api/dto/SaveUserSettingsForm.kt @@ -31,5 +31,8 @@ data class SaveUserSettingsForm( @SerialName("show_nsfw") val showNsfw: Boolean? = null, @SerialName("show_read_posts") val showReadPosts: Boolean? = null, @SerialName("show_scores") val showScores: Boolean? = null, + @SerialName("show_upvotes") val showUpvotes: Boolean? = null, + @SerialName("show_downvotes") val showDownvotes: Boolean? = null, + @SerialName("show_upvote_percentage") val showUpvotePercentage: Boolean? = null, @SerialName("theme") val theme: String? = null, ) diff --git a/domain/identity/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/identity/usecase/DefaultLoginUseCase.kt b/domain/identity/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/identity/usecase/DefaultLoginUseCase.kt index a581e5956..dc5cb2143 100644 --- a/domain/identity/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/identity/usecase/DefaultLoginUseCase.kt +++ b/domain/identity/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/identity/usecase/DefaultLoginUseCase.kt @@ -1,5 +1,6 @@ package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase +import com.github.diegoberaldin.raccoonforlemmy.core.appearance.data.VoteFormat import com.github.diegoberaldin.raccoonforlemmy.core.persistence.data.AccountModel import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.AccountRepository import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.CommunityPreferredLanguageRepository @@ -59,13 +60,27 @@ internal class DefaultLoginUseCase( val accountId = if (existing == null) { // new account with a copy of the anonymous settings - // (except a couple of fields from the Lemmy accounts) + // (except a couple of fields from the Lemmy account) val newAccountId = accountRepository.createAccount(account) val anonymousSettings = settingsRepository.getSettings(null) .copy( showScores = accountSettings?.showScores ?: true, includeNsfw = accountSettings?.showNsfw ?: false, + voteFormat = + accountSettings?.let { settings -> + val showPercentage = settings.showUpVotePercentage == true + val separate = + settings.showUpVotes == true && settings.showDownVotes == true && settings.showScores == false + val hidden = + settings.showUpVotes == false && settings.showDownVotes == false && settings.showScores == false + when { + showPercentage -> VoteFormat.Percentage + separate -> VoteFormat.Separated + hidden -> VoteFormat.Hidden + else -> VoteFormat.Aggregated + } + } ?: VoteFormat.Aggregated, ) settingsRepository.createSettings( settings = anonymousSettings, diff --git a/domain/lemmy/data/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/lemmy/data/AccountSettingsModel.kt b/domain/lemmy/data/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/lemmy/data/AccountSettingsModel.kt index 510cd2b7b..6bae9caab 100644 --- a/domain/lemmy/data/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/lemmy/data/AccountSettingsModel.kt +++ b/domain/lemmy/data/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/lemmy/data/AccountSettingsModel.kt @@ -15,4 +15,7 @@ data class AccountSettingsModel( val showScores: Boolean? = null, val defaultListingType: ListingType? = null, val defaultSortType: SortType? = null, + val showUpVotes: Boolean? = null, + val showDownVotes: Boolean? = null, + val showUpVotePercentage: Boolean? = null, ) diff --git a/domain/lemmy/repository/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/lemmy/repository/DefaultSiteRepository.kt b/domain/lemmy/repository/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/lemmy/repository/DefaultSiteRepository.kt index e0cf91606..d25fc5fd0 100644 --- a/domain/lemmy/repository/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/lemmy/repository/DefaultSiteRepository.kt +++ b/domain/lemmy/repository/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/lemmy/repository/DefaultSiteRepository.kt @@ -133,16 +133,7 @@ internal class DefaultSiteRepository( auth = auth, authHeader = auth.toAuthHeader(), ) - response.myUser?.localUserView?.run { - localUser?.toModel()?.copy( - avatar = person.avatar, - banner = person.banner, - bio = person.bio, - bot = person.botAccount ?: false, - displayName = person.displayName, - matrixUserId = person.matrixUserId, - ) - } + response.myUser?.localUserView?.toModel() }.getOrNull() } diff --git a/domain/lemmy/repository/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/lemmy/repository/utils/Mappings.kt b/domain/lemmy/repository/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/lemmy/repository/utils/Mappings.kt index 7fe79c156..76115fa86 100644 --- a/domain/lemmy/repository/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/lemmy/repository/utils/Mappings.kt +++ b/domain/lemmy/repository/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/domain/lemmy/repository/utils/Mappings.kt @@ -19,6 +19,7 @@ import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListingType.Local import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListingType.ModeratorView import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListingType.Subscribed import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.LocalUser +import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.LocalUserView import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ModAddCommunityView import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ModAddView import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ModBanFromCommunityView @@ -595,6 +596,9 @@ internal fun AccountSettingsModel.toDto() = showBotAccounts = showBotAccounts, showNsfw = showNsfw, showScores = showScores, + showUpvotes = showUpVotes, + showDownvotes = showDownVotes, + showUpvotePercentage = showUpVotePercentage, showReadPosts = showReadPosts, ) @@ -603,3 +607,17 @@ internal fun Instance.toModel() = id = id, domain = domain, ) + +internal fun LocalUserView.toModel() = + localUser?.toModel()?.copy( + avatar = person.avatar, + banner = person.banner, + bio = person.bio, + bot = person.botAccount ?: false, + displayName = person.displayName, + matrixUserId = person.matrixUserId, + showUpVotes = localUserVoteDisplayMode?.upvotes, + showDownVotes = localUserVoteDisplayMode?.downvotes, + showScores = localUserVoteDisplayMode?.score, + showUpVotePercentage = localUserVoteDisplayMode?.upvotePercentage, + ) diff --git a/unit/accountsettings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/accountsettings/AccountSettingsMviModel.kt b/unit/accountsettings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/accountsettings/AccountSettingsMviModel.kt index d087c6389..83f3eb5ce 100644 --- a/unit/accountsettings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/accountsettings/AccountSettingsMviModel.kt +++ b/unit/accountsettings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/accountsettings/AccountSettingsMviModel.kt @@ -31,6 +31,12 @@ interface AccountSettingsMviModel : data class ChangeShowScores(val value: Boolean) : Intent + data class ChangeShowUpVotes(val value: Boolean) : Intent + + data class ChangeShowDownVotes(val value: Boolean) : Intent + + data class ChangeShowUpVotePercentage(val value: Boolean) : Intent + data class AvatarSelected(val value: ByteArray) : Intent { override fun equals(other: Any?): Boolean { if (this === other) return true @@ -78,10 +84,13 @@ interface AccountSettingsMviModel : val showBotAccounts: Boolean = false, val showReadPosts: Boolean = false, val showNsfw: Boolean = false, - val showScores: Boolean = true, val defaultListingType: ListingType = ListingType.All, val availableSortTypes: List = emptyList(), val defaultSortType: SortType = SortType.Active, + val showScores: Boolean = true, + val showUpVotes: Boolean = false, + val showDownVotes: Boolean = false, + val showUpVotePercentage: Boolean = false, ) sealed interface Effect { diff --git a/unit/accountsettings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/accountsettings/AccountSettingsScreen.kt b/unit/accountsettings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/accountsettings/AccountSettingsScreen.kt index 3acec7659..e1eaa9b97 100644 --- a/unit/accountsettings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/accountsettings/AccountSettingsScreen.kt +++ b/unit/accountsettings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/accountsettings/AccountSettingsScreen.kt @@ -9,8 +9,10 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState @@ -22,6 +24,7 @@ import androidx.compose.material.icons.automirrored.filled.Article import androidx.compose.material.icons.filled.AccountCircle import androidx.compose.material.icons.filled.Notifications import androidx.compose.material.icons.filled.Sync +import androidx.compose.material.icons.filled.ThumbsUpDown import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api @@ -211,6 +214,7 @@ class AccountSettingsScreen : Screen { Modifier .padding( top = padding.calculateTopPadding(), + bottom = Spacing.m, ) .then( if (settings.hideNavigationBarWhileScrolling) { @@ -368,6 +372,21 @@ class AccountSettingsScreen : Screen { }, ) + // show read posts + SettingsSwitchRow( + title = LocalStrings.current.settingsWebShowRead, + value = uiState.showReadPosts, + onValueChanged = + rememberCallbackArgs { value -> + model.reduce(AccountSettingsMviModel.Intent.ChangeShowReadPosts(value)) + }, + ) + + SettingsHeader( + icon = Icons.Default.ThumbsUpDown, + title = LocalStrings.current.settingsVoteFormat, + ) + // show scores SettingsSwitchRow( title = LocalStrings.current.settingsShowScores, @@ -378,13 +397,37 @@ class AccountSettingsScreen : Screen { }, ) - // show read posts + // show positive votes SettingsSwitchRow( - title = LocalStrings.current.settingsWebShowRead, - value = uiState.showReadPosts, + title = LocalStrings.current.actionUpvote, + value = uiState.showUpVotes, onValueChanged = rememberCallbackArgs { value -> - model.reduce(AccountSettingsMviModel.Intent.ChangeShowReadPosts(value)) + model.reduce(AccountSettingsMviModel.Intent.ChangeShowUpVotes(value)) + }, + ) + + // show negative votes + SettingsSwitchRow( + title = LocalStrings.current.actionDownvote, + value = uiState.showDownVotes, + onValueChanged = + rememberCallbackArgs { value -> + model.reduce(AccountSettingsMviModel.Intent.ChangeShowDownVotes(value)) + }, + ) + + // show vote percentage + SettingsSwitchRow( + title = LocalStrings.current.settingsVoteFormatPercentage, + value = uiState.showUpVotePercentage, + onValueChanged = + rememberCallbackArgs { value -> + model.reduce( + AccountSettingsMviModel.Intent.ChangeShowUpVotePercentage( + value, + ), + ) }, ) @@ -406,6 +449,8 @@ class AccountSettingsScreen : Screen { ) }, ) + + Spacer(modifier = Modifier.height(Spacing.m)) } Box( diff --git a/unit/accountsettings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/accountsettings/AccountSettingsViewModel.kt b/unit/accountsettings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/accountsettings/AccountSettingsViewModel.kt index 138b11bf1..af7453e61 100644 --- a/unit/accountsettings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/accountsettings/AccountSettingsViewModel.kt +++ b/unit/accountsettings/src/commonMain/kotlin/com/github/diegoberaldin/raccoonforlemmy/unit/accountsettings/AccountSettingsViewModel.kt @@ -153,6 +153,36 @@ class AccountSettingsViewModel( } } + is AccountSettingsMviModel.Intent.ChangeShowDownVotes -> + screenModelScope.launch { + updateState { + it.copy( + showDownVotes = intent.value, + hasUnsavedChanges = true, + ) + } + } + + is AccountSettingsMviModel.Intent.ChangeShowUpVotePercentage -> + screenModelScope.launch { + updateState { + it.copy( + showUpVotePercentage = intent.value, + hasUnsavedChanges = true, + ) + } + } + + is AccountSettingsMviModel.Intent.ChangeShowUpVotes -> + screenModelScope.launch { + updateState { + it.copy( + showUpVotes = intent.value, + hasUnsavedChanges = true, + ) + } + } + is AccountSettingsMviModel.Intent.ChangeShowReadPosts -> { screenModelScope.launch { updateState { @@ -194,9 +224,12 @@ class AccountSettingsViewModel( showBotAccounts = accountSettings?.showBotAccounts ?: false, showReadPosts = accountSettings?.showReadPosts ?: false, showNsfw = accountSettings?.showNsfw ?: false, - showScores = accountSettings?.showScores ?: true, defaultListingType = accountSettings?.defaultListingType ?: ListingType.All, defaultSortType = accountSettings?.defaultSortType ?: SortType.Active, + showScores = accountSettings?.showScores ?: true, + showUpVotes = accountSettings?.showUpVotes ?: false, + showDownVotes = accountSettings?.showDownVotes ?: false, + showUpVotePercentage = accountSettings?.showUpVotePercentage ?: false, ) } } @@ -257,8 +290,11 @@ class AccountSettingsViewModel( sendNotificationsToEmail = currentState.sendNotificationsToEmail, showBotAccounts = currentState.showBotAccounts, showNsfw = currentState.showNsfw, - showScores = currentState.showScores, showReadPosts = currentState.showReadPosts, + showScores = currentState.showScores, + showUpVotes = currentState.showUpVotes, + showDownVotes = currentState.showDownVotes, + showUpVotePercentage = currentState.showUpVotePercentage, ) ?: return screenModelScope.launch(Dispatchers.IO) { updateState { it.copy(loading = true) }