app: the stable-pseudonym switch was live at a level that pseudonymizes nothing
At `full` the anonymizer returns the document unchanged, so the salt has nothing to act on - but the switch was enabled and looked like it did something. A control that silently does nothing is the same class of fault as the preview button and the archived-level label: the screen implying more than is true. Shown disabled with the reason rather than hidden. The setting is still stored and applies the moment the level changes, so making it vanish would hide state that is still there; and a settings screen whose controls appear and disappear as you touch other controls is harder to trust, not easier. The label dims with the switch so "not active right now" reads at a glance. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
c19f382640
commit
57a5ef8796
@@ -16,6 +16,7 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Switch
|
||||
@@ -130,12 +131,21 @@ fun SettingsScreen(
|
||||
}
|
||||
Text(privacyExplanation(privacy), style = MaterialTheme.typography.bodySmall)
|
||||
|
||||
// At FULL nothing is pseudonymized, so a salt has nothing to act on. Shown
|
||||
// disabled rather than hidden: the setting is still stored and still applies the
|
||||
// moment the level changes, and a control that vanishes hides that fact.
|
||||
Toggle(
|
||||
label = "Stable pseudonyms across runs",
|
||||
detail = "Lets you compare uploaded runs over time (same SSID reads the same " +
|
||||
"each time). It also links your uploads together, so leave it off on a " +
|
||||
"server you don't run yourself.",
|
||||
checked = stableSalt,
|
||||
detail = if (privacy == PrivacyLevel.FULL) {
|
||||
"Not used at this level — nothing is pseudonymized, so there is nothing " +
|
||||
"to keep stable. Choose balanced or strict to use this."
|
||||
} else {
|
||||
"Lets you compare uploaded runs over time (same SSID reads the same " +
|
||||
"each time). It also links your uploads together, so leave it off on " +
|
||||
"a server you don't run yourself."
|
||||
},
|
||||
checked = stableSalt && privacy != PrivacyLevel.FULL,
|
||||
enabled = privacy != PrivacyLevel.FULL,
|
||||
) { stableSalt = it; settings.stableSalt = it }
|
||||
|
||||
TextButton(onClick = onPreviewUpload) { Text("Preview what an upload would send") }
|
||||
@@ -237,13 +247,22 @@ private fun privacyExplanation(level: PrivacyLevel): String = when (level) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Toggle(label: String, detail: String, checked: Boolean, onChange: (Boolean) -> Unit) {
|
||||
private fun Toggle(
|
||||
label: String,
|
||||
detail: String,
|
||||
checked: Boolean,
|
||||
enabled: Boolean = true,
|
||||
onChange: (Boolean) -> Unit,
|
||||
) {
|
||||
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.Top) {
|
||||
Column(Modifier.weight(1f)) {
|
||||
Text(label, style = MaterialTheme.typography.bodyMedium)
|
||||
Text(detail, style = MaterialTheme.typography.bodySmall)
|
||||
// Dimmed together with the switch, so "this does nothing right now" reads at a glance
|
||||
// instead of only on close inspection.
|
||||
val alpha = if (enabled) 1f else 0.5f
|
||||
Text(label, style = MaterialTheme.typography.bodyMedium, color = LocalContentColor.current.copy(alpha = alpha))
|
||||
Text(detail, style = MaterialTheme.typography.bodySmall, color = LocalContentColor.current.copy(alpha = alpha))
|
||||
}
|
||||
Switch(checked = checked, onCheckedChange = onChange)
|
||||
Switch(checked = checked, onCheckedChange = onChange, enabled = enabled)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user