feat: manage local user vote display mode (#960)

This commit is contained in:
Diego Beraldin 2024-06-09 21:47:19 +02:00 committed by GitHub
parent e10daafe59
commit 1d6b70e1f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 152 additions and 18 deletions

View File

@ -8,4 +8,5 @@ data class LocalUserView(
@SerialName("local_user") val localUser: LocalUser? = null, @SerialName("local_user") val localUser: LocalUser? = null,
@SerialName("person") val person: Person, @SerialName("person") val person: Person,
@SerialName("counts") val counts: PersonAggregates, @SerialName("counts") val counts: PersonAggregates,
@SerialName("local_user_vote_display_mode") val localUserVoteDisplayMode: LocalUserVoteDisplayMode? = null,
) )

View File

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

View File

@ -31,5 +31,8 @@ data class SaveUserSettingsForm(
@SerialName("show_nsfw") val showNsfw: Boolean? = null, @SerialName("show_nsfw") val showNsfw: Boolean? = null,
@SerialName("show_read_posts") val showReadPosts: Boolean? = null, @SerialName("show_read_posts") val showReadPosts: Boolean? = null,
@SerialName("show_scores") val showScores: 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, @SerialName("theme") val theme: String? = null,
) )

View File

@ -1,5 +1,6 @@
package com.github.diegoberaldin.raccoonforlemmy.domain.identity.usecase 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.data.AccountModel
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.AccountRepository import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.AccountRepository
import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.CommunityPreferredLanguageRepository import com.github.diegoberaldin.raccoonforlemmy.core.persistence.repository.CommunityPreferredLanguageRepository
@ -59,13 +60,27 @@ internal class DefaultLoginUseCase(
val accountId = val accountId =
if (existing == null) { if (existing == null) {
// new account with a copy of the anonymous settings // 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 newAccountId = accountRepository.createAccount(account)
val anonymousSettings = val anonymousSettings =
settingsRepository.getSettings(null) settingsRepository.getSettings(null)
.copy( .copy(
showScores = accountSettings?.showScores ?: true, showScores = accountSettings?.showScores ?: true,
includeNsfw = accountSettings?.showNsfw ?: false, 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( settingsRepository.createSettings(
settings = anonymousSettings, settings = anonymousSettings,

View File

@ -15,4 +15,7 @@ data class AccountSettingsModel(
val showScores: Boolean? = null, val showScores: Boolean? = null,
val defaultListingType: ListingType? = null, val defaultListingType: ListingType? = null,
val defaultSortType: SortType? = null, val defaultSortType: SortType? = null,
val showUpVotes: Boolean? = null,
val showDownVotes: Boolean? = null,
val showUpVotePercentage: Boolean? = null,
) )

View File

@ -133,16 +133,7 @@ internal class DefaultSiteRepository(
auth = auth, auth = auth,
authHeader = auth.toAuthHeader(), authHeader = auth.toAuthHeader(),
) )
response.myUser?.localUserView?.run { response.myUser?.localUserView?.toModel()
localUser?.toModel()?.copy(
avatar = person.avatar,
banner = person.banner,
bio = person.bio,
bot = person.botAccount ?: false,
displayName = person.displayName,
matrixUserId = person.matrixUserId,
)
}
}.getOrNull() }.getOrNull()
} }

View File

@ -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.ModeratorView
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ListingType.Subscribed 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.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.ModAddCommunityView
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ModAddView import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ModAddView
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ModBanFromCommunityView import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.ModBanFromCommunityView
@ -595,6 +596,9 @@ internal fun AccountSettingsModel.toDto() =
showBotAccounts = showBotAccounts, showBotAccounts = showBotAccounts,
showNsfw = showNsfw, showNsfw = showNsfw,
showScores = showScores, showScores = showScores,
showUpvotes = showUpVotes,
showDownvotes = showDownVotes,
showUpvotePercentage = showUpVotePercentage,
showReadPosts = showReadPosts, showReadPosts = showReadPosts,
) )
@ -603,3 +607,17 @@ internal fun Instance.toModel() =
id = id, id = id,
domain = domain, 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,
)

View File

@ -31,6 +31,12 @@ interface AccountSettingsMviModel :
data class ChangeShowScores(val value: Boolean) : Intent 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 { data class AvatarSelected(val value: ByteArray) : Intent {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
@ -78,10 +84,13 @@ interface AccountSettingsMviModel :
val showBotAccounts: Boolean = false, val showBotAccounts: Boolean = false,
val showReadPosts: Boolean = false, val showReadPosts: Boolean = false,
val showNsfw: Boolean = false, val showNsfw: Boolean = false,
val showScores: Boolean = true,
val defaultListingType: ListingType = ListingType.All, val defaultListingType: ListingType = ListingType.All,
val availableSortTypes: List<SortType> = emptyList(), val availableSortTypes: List<SortType> = emptyList(),
val defaultSortType: SortType = SortType.Active, val defaultSortType: SortType = SortType.Active,
val showScores: Boolean = true,
val showUpVotes: Boolean = false,
val showDownVotes: Boolean = false,
val showUpVotePercentage: Boolean = false,
) )
sealed interface Effect { sealed interface Effect {

View File

@ -9,8 +9,10 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState 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.AccountCircle
import androidx.compose.material.icons.filled.Notifications import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.Sync import androidx.compose.material.icons.filled.Sync
import androidx.compose.material.icons.filled.ThumbsUpDown
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
@ -211,6 +214,7 @@ class AccountSettingsScreen : Screen {
Modifier Modifier
.padding( .padding(
top = padding.calculateTopPadding(), top = padding.calculateTopPadding(),
bottom = Spacing.m,
) )
.then( .then(
if (settings.hideNavigationBarWhileScrolling) { 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 // show scores
SettingsSwitchRow( SettingsSwitchRow(
title = LocalStrings.current.settingsShowScores, title = LocalStrings.current.settingsShowScores,
@ -378,13 +397,37 @@ class AccountSettingsScreen : Screen {
}, },
) )
// show read posts // show positive votes
SettingsSwitchRow( SettingsSwitchRow(
title = LocalStrings.current.settingsWebShowRead, title = LocalStrings.current.actionUpvote,
value = uiState.showReadPosts, value = uiState.showUpVotes,
onValueChanged = onValueChanged =
rememberCallbackArgs { value -> 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( Box(

View File

@ -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 -> { is AccountSettingsMviModel.Intent.ChangeShowReadPosts -> {
screenModelScope.launch { screenModelScope.launch {
updateState { updateState {
@ -194,9 +224,12 @@ class AccountSettingsViewModel(
showBotAccounts = accountSettings?.showBotAccounts ?: false, showBotAccounts = accountSettings?.showBotAccounts ?: false,
showReadPosts = accountSettings?.showReadPosts ?: false, showReadPosts = accountSettings?.showReadPosts ?: false,
showNsfw = accountSettings?.showNsfw ?: false, showNsfw = accountSettings?.showNsfw ?: false,
showScores = accountSettings?.showScores ?: true,
defaultListingType = accountSettings?.defaultListingType ?: ListingType.All, defaultListingType = accountSettings?.defaultListingType ?: ListingType.All,
defaultSortType = accountSettings?.defaultSortType ?: SortType.Active, 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, sendNotificationsToEmail = currentState.sendNotificationsToEmail,
showBotAccounts = currentState.showBotAccounts, showBotAccounts = currentState.showBotAccounts,
showNsfw = currentState.showNsfw, showNsfw = currentState.showNsfw,
showScores = currentState.showScores,
showReadPosts = currentState.showReadPosts, showReadPosts = currentState.showReadPosts,
showScores = currentState.showScores,
showUpVotes = currentState.showUpVotes,
showDownVotes = currentState.showDownVotes,
showUpVotePercentage = currentState.showUpVotePercentage,
) ?: return ) ?: return
screenModelScope.launch(Dispatchers.IO) { screenModelScope.launch(Dispatchers.IO) {
updateState { it.copy(loading = true) } updateState { it.copy(loading = true) }