Makes the new translation settings work

This commit is contained in:
Vitor Pamplona
2026-03-13 09:24:12 -04:00
parent 3e0b8a5f8f
commit 4bf7f982ba
6 changed files with 172 additions and 84 deletions
@@ -497,6 +497,16 @@ class Account(
sendNewAppSpecificData() sendNewAppSpecificData()
} }
suspend fun addDontTranslateFrom(languageCode: String) {
settings.addDontTranslateFrom(languageCode)
sendNewAppSpecificData()
}
suspend fun removeDontTranslateFrom(languageCode: String) {
settings.removeDontTranslateFrom(languageCode)
sendNewAppSpecificData()
}
suspend fun updateTranslateTo(languageCode: Locale) { suspend fun updateTranslateTo(languageCode: Locale) {
if (settings.updateTranslateTo(languageCode)) { if (settings.updateTranslateTo(languageCode)) {
sendNewAppSpecificData() sendNewAppSpecificData()
@@ -322,7 +322,19 @@ class AccountSettings(
saveAccountSettings() saveAccountSettings()
} }
fun translateToContains(languageCode: Locale) = syncedSettings.languages.translateTo.contains(languageCode.language) fun addDontTranslateFrom(languageCode: String) {
syncedSettings.languages.addDontTranslateFrom(languageCode)
saveAccountSettings()
}
fun removeDontTranslateFrom(languageCode: String) {
syncedSettings.languages.removeDontTranslateFrom(languageCode)
saveAccountSettings()
}
fun translateToContains(languageCode: Locale) =
syncedSettings.languages.translateTo.value
.contains(languageCode.language)
fun updateTranslateTo(languageCode: Locale): Boolean { fun updateTranslateTo(languageCode: Locale): Boolean {
if (syncedSettings.languages.updateTranslateTo(languageCode)) { if (syncedSettings.languages.updateTranslateTo(languageCode)) {
@@ -45,9 +45,9 @@ class AccountSyncedSettings(
) )
val languages = val languages =
AccountLanguagePreferences( AccountLanguagePreferences(
internalSettings.languages.dontTranslateFrom, MutableStateFlow(internalSettings.languages.dontTranslateFrom),
internalSettings.languages.languagePreferences, MutableStateFlow(internalSettings.languages.languagePreferences),
internalSettings.languages.translateTo, MutableStateFlow(internalSettings.languages.translateTo),
) )
val security = val security =
AccountSecurityPreferences( AccountSecurityPreferences(
@@ -66,9 +66,9 @@ class AccountSyncedSettings(
), ),
languages = languages =
AccountLanguagePreferencesInternal( AccountLanguagePreferencesInternal(
languages.dontTranslateFrom, languages.dontTranslateFrom.value,
languages.languagePreferences, languages.languagePreferences.value,
languages.translateTo, languages.translateTo.value,
), ),
security = security =
AccountSecurityPreferencesInternal( AccountSecurityPreferencesInternal(
@@ -98,16 +98,16 @@ class AccountSyncedSettings(
zaps.defaultZapType.tryEmit(syncedSettingsInternal.zaps.defaultZapType) zaps.defaultZapType.tryEmit(syncedSettingsInternal.zaps.defaultZapType)
} }
if (languages.dontTranslateFrom != syncedSettingsInternal.languages.dontTranslateFrom) { if (languages.dontTranslateFrom.value != syncedSettingsInternal.languages.dontTranslateFrom) {
languages.dontTranslateFrom = syncedSettingsInternal.languages.dontTranslateFrom languages.dontTranslateFrom.value = syncedSettingsInternal.languages.dontTranslateFrom
} }
if (languages.languagePreferences != syncedSettingsInternal.languages.languagePreferences) { if (languages.languagePreferences.value != syncedSettingsInternal.languages.languagePreferences) {
languages.languagePreferences = syncedSettingsInternal.languages.languagePreferences languages.languagePreferences.value = syncedSettingsInternal.languages.languagePreferences
} }
if (languages.translateTo != syncedSettingsInternal.languages.translateTo) { if (languages.translateTo.value != syncedSettingsInternal.languages.translateTo) {
languages.translateTo = syncedSettingsInternal.languages.translateTo languages.translateTo.value = syncedSettingsInternal.languages.translateTo
} }
if (security.showSensitiveContent.value != syncedSettingsInternal.security.showSensitiveContent) { if (security.showSensitiveContent.value != syncedSettingsInternal.security.showSensitiveContent) {
@@ -123,7 +123,7 @@ class AccountSyncedSettings(
} }
} }
fun dontTranslateFromFilteredBySpokenLanguages(): Set<String> = languages.dontTranslateFrom - getLanguagesSpokenByUser() fun dontTranslateFromFilteredBySpokenLanguages(): Set<String> = languages.dontTranslateFrom.value - getLanguagesSpokenByUser()
} }
@Stable @Stable
@@ -140,27 +140,36 @@ class AccountZapPreferences(
@Stable @Stable
class AccountLanguagePreferences( class AccountLanguagePreferences(
var dontTranslateFrom: Set<String>, var dontTranslateFrom: MutableStateFlow<Set<String>>,
var languagePreferences: Map<String, String>, var languagePreferences: MutableStateFlow<Map<String, String>>,
var translateTo: String, var translateTo: MutableStateFlow<String>,
) { ) {
// --- // ---
// language services // language services
// --- // ---
fun toggleDontTranslateFrom(languageCode: String) { fun toggleDontTranslateFrom(languageCode: String) {
dontTranslateFrom = dontTranslateFrom.update {
if (!dontTranslateFrom.contains(languageCode)) { if (it.contains(languageCode)) {
dontTranslateFrom.plus(languageCode) it - languageCode
} else { } else {
dontTranslateFrom.minus(languageCode) it + languageCode
} }
}
} }
fun translateToContains(languageCode: Locale) = translateTo.contains(languageCode.language) fun addDontTranslateFrom(languageCode: String) {
dontTranslateFrom.update { it + languageCode }
}
fun removeDontTranslateFrom(languageCode: String) {
dontTranslateFrom.update { it - languageCode }
}
fun translateToContains(languageCode: Locale) = translateTo.value.contains(languageCode.language)
fun updateTranslateTo(languageCode: Locale): Boolean { fun updateTranslateTo(languageCode: Locale): Boolean {
if (translateTo != languageCode.language) { if (translateTo.value != languageCode.language) {
translateTo = languageCode.language translateTo.tryEmit(languageCode.language)
return true return true
} }
return false return false
@@ -172,13 +181,15 @@ class AccountLanguagePreferences(
preference: String, preference: String,
) { ) {
val key = "$source,$target" val key = "$source,$target"
if (key !in languagePreferences) { languagePreferences.update {
languagePreferences = languagePreferences + Pair(key, preference) if (key !in it) {
} else { it + Pair(key, preference)
if (languagePreferences.get(key) == preference) {
languagePreferences = languagePreferences.minus(key)
} else { } else {
languagePreferences = languagePreferences + Pair(key, preference) if (it.get(key) == preference) {
it.minus(key)
} else {
it + Pair(key, preference)
}
} }
} }
} }
@@ -186,7 +197,7 @@ class AccountLanguagePreferences(
fun preferenceBetween( fun preferenceBetween(
source: String, source: String,
target: String, target: String,
): String? = languagePreferences["$source,$target"] ): String? = languagePreferences.value["$source,$target"]
} }
@Stable @Stable
@@ -953,11 +953,9 @@ class AccountViewModel(
fun markDonatedInThisVersion() = account.markDonatedInThisVersion() fun markDonatedInThisVersion() = account.markDonatedInThisVersion()
fun dontTranslateFrom() = account.settings.syncedSettings.languages.dontTranslateFrom fun dontTranslateFrom() = account.settings.syncedSettings.languages.dontTranslateFrom.value
fun dontTranslateFromFilteredBySpokenLanguages() = account.settings.syncedSettings.dontTranslateFromFilteredBySpokenLanguages() fun translateTo() = account.settings.syncedSettings.languages.translateTo.value
fun translateTo() = account.settings.syncedSettings.languages.translateTo
fun defaultZapType() = account.settings.syncedSettings.zaps.defaultZapType.value fun defaultZapType() = account.settings.syncedSettings.zaps.defaultZapType.value
@@ -1007,6 +1005,10 @@ class AccountViewModel(
fun toggleDontTranslateFrom(languageCode: String) = launchSigner { account.toggleDontTranslateFrom(languageCode) } fun toggleDontTranslateFrom(languageCode: String) = launchSigner { account.toggleDontTranslateFrom(languageCode) }
fun addDontTranslateFrom(languageCode: String) = launchSigner { account.addDontTranslateFrom(languageCode) }
fun removeDontTranslateFrom(languageCode: String) = launchSigner { account.removeDontTranslateFrom(languageCode) }
fun updateTranslateTo(languageCode: Locale) = launchSigner { account.updateTranslateTo(languageCode) } fun updateTranslateTo(languageCode: Locale) = launchSigner { account.updateTranslateTo(languageCode) }
fun prefer( fun prefer(
@@ -422,3 +422,33 @@ fun SettingsRow(
} }
} }
} }
@Composable
fun SettingsRow(
name: Int,
description: Int,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(5.dp),
) {
Column(
modifier = Modifier.weight(2.0f),
verticalArrangement = Arrangement.spacedBy(3.dp),
) {
Text(
text = stringRes(name),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
text = stringRes(description),
style = MaterialTheme.typography.bodySmall,
color = Color.Gray,
maxLines = 3,
overflow = TextOverflow.Ellipsis,
)
}
}
}
@@ -20,6 +20,7 @@
*/ */
package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings
import android.R.attr.targetName
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -40,7 +41,6 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
@@ -67,6 +67,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
@@ -79,6 +80,7 @@ import com.vitorpamplona.amethyst.ui.theme.Size10dp
import com.vitorpamplona.amethyst.ui.theme.Size20dp import com.vitorpamplona.amethyst.ui.theme.Size20dp
import com.vitorpamplona.amethyst.ui.theme.SpacedBy10dp import com.vitorpamplona.amethyst.ui.theme.SpacedBy10dp
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonRow import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonRow
import okio.`-DeprecatedOkio`.source
import java.util.Locale as JavaLocale import java.util.Locale as JavaLocale
@Preview(device = "spec:width=2160px,height=2340px,dpi=440") @Preview(device = "spec:width=2160px,height=2340px,dpi=440")
@@ -193,7 +195,8 @@ private fun SearchableLanguageList(
@Composable @Composable
fun TranslateToSetting(accountViewModel: AccountViewModel) { fun TranslateToSetting(accountViewModel: AccountViewModel) {
val currentTranslateTo = accountViewModel.translateTo() val currentTranslateTo by accountViewModel.account.settings.syncedSettings.languages.translateTo
.collectAsStateWithLifecycle()
val allLanguages = remember { getAllLanguagesSorted() } val allLanguages = remember { getAllLanguagesSorted() }
var showPicker by remember { mutableStateOf(false) } var showPicker by remember { mutableStateOf(false) }
@@ -235,7 +238,8 @@ fun TranslateToSetting(accountViewModel: AccountViewModel) {
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
@Composable @Composable
fun DontTranslateFromSetting(accountViewModel: AccountViewModel) { fun DontTranslateFromSetting(accountViewModel: AccountViewModel) {
val selectedLanguages = accountViewModel.dontTranslateFrom().toSet() val selectedLanguages by accountViewModel.account.settings.syncedSettings.languages.dontTranslateFrom
.collectAsStateWithLifecycle()
var showAddPicker by remember { mutableStateOf(false) } var showAddPicker by remember { mutableStateOf(false) }
val allLanguages = remember { getAllLanguagesSorted() } val allLanguages = remember { getAllLanguagesSorted() }
@@ -248,7 +252,7 @@ fun DontTranslateFromSetting(accountViewModel: AccountViewModel) {
SettingsRow( SettingsRow(
name = R.string.dont_translate_from, name = R.string.dont_translate_from,
description = R.string.dont_translate_from_description, description = R.string.dont_translate_from_description,
) {} )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
@@ -260,7 +264,7 @@ fun DontTranslateFromSetting(accountViewModel: AccountViewModel) {
selectedLanguages.forEach { languageCode -> selectedLanguages.forEach { languageCode ->
InputChip( InputChip(
selected = true, selected = true,
onClick = { accountViewModel.toggleDontTranslateFrom(languageCode) }, onClick = { accountViewModel.removeDontTranslateFrom(languageCode) },
label = { Text(JavaLocale(languageCode).displayName) }, label = { Text(JavaLocale(languageCode).displayName) },
trailingIcon = { trailingIcon = {
Icon( Icon(
@@ -292,7 +296,7 @@ fun DontTranslateFromSetting(accountViewModel: AccountViewModel) {
SearchableLanguageList( SearchableLanguageList(
languages = availableToAdd, languages = availableToAdd,
onSelect = { locale -> onSelect = { locale ->
accountViewModel.toggleDontTranslateFrom(locale.language) accountViewModel.addDontTranslateFrom(locale.language)
showAddPicker = false showAddPicker = false
}, },
) )
@@ -302,14 +306,16 @@ fun DontTranslateFromSetting(accountViewModel: AccountViewModel) {
@Composable @Composable
fun LanguagePreferencesSetting(accountViewModel: AccountViewModel) { fun LanguagePreferencesSetting(accountViewModel: AccountViewModel) {
val languagePreferences = accountViewModel.account.settings.syncedSettings.languages.languagePreferences val languagePreferences by
accountViewModel.account.settings.syncedSettings.languages.languagePreferences
.collectAsStateWithLifecycle()
var showAddPair by remember { mutableStateOf(false) } var showAddPair by remember { mutableStateOf(false) }
Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp)) { Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp)) {
SettingsRow( SettingsRow(
name = R.string.language_preferences, name = R.string.language_preferences,
description = R.string.language_preferences_description, description = R.string.language_preferences_description,
) {} )
if (languagePreferences.isEmpty() && !showAddPair) { if (languagePreferences.isEmpty() && !showAddPair) {
Text( Text(
@@ -332,20 +338,20 @@ fun LanguagePreferencesSetting(accountViewModel: AccountViewModel) {
} }
} }
TextButton( if (!showAddPair) {
onClick = { showAddPair = !showAddPair }, TextButton(
modifier = Modifier.align(Alignment.CenterHorizontally), onClick = { showAddPair = !showAddPair },
) { modifier = Modifier.align(Alignment.CenterHorizontally),
Icon( ) {
imageVector = Icons.Default.Add, Icon(
contentDescription = null, imageVector = Icons.Default.Add,
modifier = Modifier.size(18.dp), contentDescription = null,
) modifier = Modifier.size(18.dp),
Spacer(modifier = Modifier.width(4.dp)) )
Text(stringRes(R.string.add_language_pair)) Spacer(modifier = Modifier.width(4.dp))
} Text(stringRes(R.string.add_language_pair))
}
if (showAddPair) { } else {
AddLanguagePairCard( AddLanguagePairCard(
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
onDismiss = { showAddPair = false }, onDismiss = { showAddPair = false },
@@ -448,7 +454,8 @@ private fun AddLanguagePairCard(
val allLanguages = remember { getAllLanguagesSorted() } val allLanguages = remember { getAllLanguagesSorted() }
var selectedSource by remember { mutableStateOf<JavaLocale?>(null) } var selectedSource by remember { mutableStateOf<JavaLocale?>(null) }
var selectedTarget by remember { mutableStateOf<JavaLocale?>(null) } var selectedTarget by remember { mutableStateOf<JavaLocale?>(null) }
var pickingSource by remember { mutableStateOf(true) } var selectedPreference by remember { mutableStateOf<JavaLocale?>(null) }
var pickingSource by remember { mutableStateOf(false) }
var pickingTarget by remember { mutableStateOf(false) } var pickingTarget by remember { mutableStateOf(false) }
OutlinedCard(modifier = Modifier.fillMaxWidth()) { OutlinedCard(modifier = Modifier.fillMaxWidth()) {
@@ -557,41 +564,57 @@ private fun AddLanguagePairCard(
) )
} }
val selectedSource = selectedSource
val selectedTarget = selectedTarget
if (selectedSource != null && selectedTarget != null) { if (selectedSource != null && selectedTarget != null) {
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier =
horizontalArrangement = Arrangement.End, Modifier
.fillMaxWidth()
.clickable {
accountViewModel.prefer(selectedSource.language, selectedTarget.language, selectedSource.language)
onDismiss()
}.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
TextButton(onClick = onDismiss) { RadioButton(
Text(stringRes(R.string.go_back)) selected = selectedPreference == selectedSource,
}
Spacer(modifier = Modifier.width(8.dp))
TextButton(
onClick = { onClick = {
val src = selectedSource!!.language accountViewModel.prefer(selectedSource.language, selectedTarget.language, selectedSource.language)
val tgt = selectedTarget!!.language
accountViewModel.prefer(src, tgt, tgt)
onDismiss() onDismiss()
}, },
) { )
Icon( Spacer(modifier = Modifier.width(4.dp))
imageVector = Icons.Default.Check, Text(
contentDescription = null, text = stringRes(R.string.show_first, selectedSource.displayName),
modifier = Modifier.size(18.dp), style = MaterialTheme.typography.bodyMedium,
) )
Spacer(modifier = Modifier.width(4.dp)) }
Text(
stringRes( Row(
R.string.show_first, modifier =
selectedTarget!!.displayName, Modifier
), .fillMaxWidth()
) .clickable {
} accountViewModel.prefer(selectedSource.language, selectedTarget.language, selectedTarget.language)
onDismiss()
}.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
RadioButton(
selected = selectedPreference == selectedTarget,
onClick = {
accountViewModel.prefer(selectedSource.language, selectedTarget.language, selectedTarget.language)
onDismiss()
},
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = stringRes(R.string.show_first, selectedTarget.displayName),
style = MaterialTheme.typography.bodyMedium,
)
} }
} }
} }