feat(security&privacy) : manage encryption settings

This commit is contained in:
ganfra 2025-01-22 17:55:37 +01:00
parent 8a90770072
commit 968db70e94
5 changed files with 55 additions and 15 deletions

View file

@ -11,7 +11,9 @@ sealed interface SecurityAndPrivacyEvents {
data object EditRoomAddress : SecurityAndPrivacyEvents data object EditRoomAddress : SecurityAndPrivacyEvents
data object Save : SecurityAndPrivacyEvents data object Save : SecurityAndPrivacyEvents
data class ChangeRoomAccess(val roomAccess: SecurityAndPrivacyRoomAccess) : SecurityAndPrivacyEvents data class ChangeRoomAccess(val roomAccess: SecurityAndPrivacyRoomAccess) : SecurityAndPrivacyEvents
data object EnableEncryption: SecurityAndPrivacyEvents data object ToggleEncryptionState: SecurityAndPrivacyEvents
data object CancelEnableEncryption : SecurityAndPrivacyEvents
data object ConfirmEnableEncryption: SecurityAndPrivacyEvents
data class ChangeHistoryVisibility(val historyVisibility: SecurityAndPrivacyHistoryVisibility) : SecurityAndPrivacyEvents data class ChangeHistoryVisibility(val historyVisibility: SecurityAndPrivacyHistoryVisibility) : SecurityAndPrivacyEvents
data class ChangeRoomVisibility(val isVisibleInRoomDirectory: Boolean) : SecurityAndPrivacyEvents data class ChangeRoomVisibility(val isVisibleInRoomDirectory: Boolean) : SecurityAndPrivacyEvents
} }

View file

@ -71,6 +71,9 @@ class SecurityAndPrivacyPresenter @AssistedInject constructor(
var currentIsEncrypted by remember(savedSettings.isEncrypted) { var currentIsEncrypted by remember(savedSettings.isEncrypted) {
mutableStateOf(savedSettings.isEncrypted) mutableStateOf(savedSettings.isEncrypted)
} }
var showEncryptionConfirmation by remember { mutableStateOf(false) }
val currentSettings = SecurityAndPrivacySettings( val currentSettings = SecurityAndPrivacySettings(
roomAccess = currentRoomAccess, roomAccess = currentRoomAccess,
isEncrypted = currentIsEncrypted, isEncrypted = currentIsEncrypted,
@ -86,8 +89,12 @@ class SecurityAndPrivacyPresenter @AssistedInject constructor(
is SecurityAndPrivacyEvents.ChangeRoomAccess -> { is SecurityAndPrivacyEvents.ChangeRoomAccess -> {
currentRoomAccess = event.roomAccess currentRoomAccess = event.roomAccess
} }
is SecurityAndPrivacyEvents.EnableEncryption -> { is SecurityAndPrivacyEvents.ToggleEncryptionState -> {
currentIsEncrypted = true if(currentSettings.isEncrypted) {
currentIsEncrypted = false
} else {
showEncryptionConfirmation = true
}
} }
is SecurityAndPrivacyEvents.ChangeHistoryVisibility -> { is SecurityAndPrivacyEvents.ChangeHistoryVisibility -> {
currentHistoryVisibility = Optional.of(event.historyVisibility) currentHistoryVisibility = Optional.of(event.historyVisibility)
@ -96,12 +103,20 @@ class SecurityAndPrivacyPresenter @AssistedInject constructor(
currentVisibleInRoomDirectory = Optional.of(AsyncData.Success(event.isVisibleInRoomDirectory)) currentVisibleInRoomDirectory = Optional.of(AsyncData.Success(event.isVisibleInRoomDirectory))
} }
SecurityAndPrivacyEvents.EditRoomAddress -> navigator.openEditRoomAddress() SecurityAndPrivacyEvents.EditRoomAddress -> navigator.openEditRoomAddress()
SecurityAndPrivacyEvents.CancelEnableEncryption -> {
showEncryptionConfirmation = false
}
SecurityAndPrivacyEvents.ConfirmEnableEncryption -> {
showEncryptionConfirmation = false
currentIsEncrypted = true
}
} }
} }
return SecurityAndPrivacyState( return SecurityAndPrivacyState(
savedSettings = savedSettings, savedSettings = savedSettings,
currentSettings = currentSettings, currentSettings = currentSettings,
homeserverName = homeserverName, homeserverName = homeserverName,
showEncryptionConfirmation = showEncryptionConfirmation,
eventSink = ::handleEvents eventSink = ::handleEvents
) )
} }

View file

@ -15,6 +15,7 @@ data class SecurityAndPrivacyState(
val savedSettings: SecurityAndPrivacySettings, val savedSettings: SecurityAndPrivacySettings,
val currentSettings: SecurityAndPrivacySettings, val currentSettings: SecurityAndPrivacySettings,
val homeserverName: String, val homeserverName: String,
val showEncryptionConfirmation: Boolean,
val eventSink: (SecurityAndPrivacyEvents) -> Unit val eventSink: (SecurityAndPrivacyEvents) -> Unit
) { ) {

View file

@ -41,6 +41,9 @@ open class SecurityAndPrivacyStateProvider : PreviewParameterProvider<SecurityAn
isVisibleInRoomDirectory = Optional.of(AsyncData.Success(true)) isVisibleInRoomDirectory = Optional.of(AsyncData.Success(true))
) )
), ),
aSecurityAndPrivacyState(
showEncryptionConfirmation = true
),
) )
} }
@ -62,10 +65,12 @@ fun aSecurityAndPrivacyState(
currentSettings: SecurityAndPrivacySettings = aSecurityAndPrivacySettings(), currentSettings: SecurityAndPrivacySettings = aSecurityAndPrivacySettings(),
savedSettings: SecurityAndPrivacySettings = currentSettings, savedSettings: SecurityAndPrivacySettings = currentSettings,
homeserverName: String = "myserver.xyz", homeserverName: String = "myserver.xyz",
showEncryptionConfirmation: Boolean = false,
eventSink: (SecurityAndPrivacyEvents) -> Unit = {} eventSink: (SecurityAndPrivacyEvents) -> Unit = {}
) = SecurityAndPrivacyState( ) = SecurityAndPrivacyState(
currentSettings = currentSettings, currentSettings = currentSettings,
savedSettings = savedSettings, savedSettings = savedSettings,
homeserverName = homeserverName, homeserverName = homeserverName,
showEncryptionConfirmation = showEncryptionConfirmation,
eventSink = eventSink eventSink = eventSink
) )

View file

@ -33,6 +33,7 @@ import io.element.android.features.roomdetails.impl.R
import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage
import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.ElementPreviewLight
@ -69,10 +70,10 @@ fun SecurityAndPrivacyView(
) { padding -> ) { padding ->
Column( Column(
modifier = Modifier modifier = Modifier
.padding(padding) .padding(padding)
.imePadding() .imePadding()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.consumeWindowInsets(padding), .consumeWindowInsets(padding),
verticalArrangement = Arrangement.spacedBy(32.dp), verticalArrangement = Arrangement.spacedBy(32.dp),
) { ) {
RoomAccessSection( RoomAccessSection(
@ -93,9 +94,12 @@ fun SecurityAndPrivacyView(
) )
} }
EncryptionSection( EncryptionSection(
isEncryptionEnabled = state.currentSettings.isEncrypted, isRoomEncrypted = state.currentSettings.isEncrypted,
isSectionEnabled = !state.savedSettings.isEncrypted, isSectionEnabled = !state.savedSettings.isEncrypted,
onEnableEncryption = { state.eventSink(SecurityAndPrivacyEvents.EnableEncryption) }, onToggleEncryption = { state.eventSink(SecurityAndPrivacyEvents.ToggleEncryptionState) },
showConfirmation = state.showEncryptionConfirmation,
onDismissConfirmation = { state.eventSink(SecurityAndPrivacyEvents.CancelEnableEncryption) },
onConfirmEncryption = { state.eventSink(SecurityAndPrivacyEvents.ConfirmEnableEncryption) },
) )
if (state.showRoomHistoryVisibilitySection) { if (state.showRoomHistoryVisibilitySection) {
RoomHistorySection( RoomHistorySection(
@ -246,8 +250,8 @@ private fun RoomAddressSection(
ListItemContent.Custom { ListItemContent.Custom {
CircularProgressIndicator( CircularProgressIndicator(
modifier = Modifier modifier = Modifier
.progressSemantics() .progressSemantics()
.size(20.dp), .size(20.dp),
strokeWidth = 2.dp strokeWidth = 2.dp
) )
} }
@ -272,9 +276,12 @@ private fun RoomAddressSection(
@Composable @Composable
private fun EncryptionSection( private fun EncryptionSection(
isEncryptionEnabled: Boolean, isRoomEncrypted: Boolean,
isSectionEnabled: Boolean, isSectionEnabled: Boolean,
onEnableEncryption: () -> Unit, showConfirmation: Boolean,
onToggleEncryption: () -> Unit,
onConfirmEncryption: () -> Unit,
onDismissConfirmation: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
SecurityAndPrivacySection( SecurityAndPrivacySection(
@ -285,10 +292,20 @@ private fun EncryptionSection(
headlineContent = { Text(text = stringResource(CommonStrings.screen_security_and_privacy_encryption_toggle_title)) }, headlineContent = { Text(text = stringResource(CommonStrings.screen_security_and_privacy_encryption_toggle_title)) },
supportingContent = { Text(text = stringResource(CommonStrings.screen_security_and_privacy_encryption_section_footer)) }, supportingContent = { Text(text = stringResource(CommonStrings.screen_security_and_privacy_encryption_section_footer)) },
trailingContent = ListItemContent.Switch( trailingContent = ListItemContent.Switch(
checked = isEncryptionEnabled, checked = isRoomEncrypted,
enabled = isSectionEnabled, enabled = isSectionEnabled,
onChange = { onEnableEncryption() } onChange = { onToggleEncryption() },
), ),
onClick = onToggleEncryption,
)
}
if (showConfirmation) {
ConfirmationDialog(
title = stringResource(CommonStrings.screen_security_and_privacy_enable_encryption_alert_title),
content = stringResource(CommonStrings.screen_security_and_privacy_enable_encryption_alert_description),
submitText = stringResource(CommonStrings.screen_security_and_privacy_enable_encryption_alert_confirm_button_title),
onSubmitClick = onConfirmEncryption,
onDismiss = onDismissConfirmation,
) )
} }
} }