Skip to content

Commit 50e7521

Browse files
authored
Merge pull request #5566 from element-hq/feature/bma/securityAndPrivacy
Enable access to security and privacy
2 parents ae04389 + 05963fb commit 50e7521

11 files changed

+91
-36
lines changed

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ class RoomDetailsPresenter(
164164
val securityAndPrivacyPermissions = room.securityAndPrivacyPermissionsAsState(syncUpdateFlow.value)
165165
val canShowSecurityAndPrivacy by remember {
166166
derivedStateOf {
167-
isKnockRequestsEnabled && roomType is RoomDetailsType.Room && securityAndPrivacyPermissions.value.hasAny
167+
roomType is RoomDetailsType.Room && securityAndPrivacyPermissions.value.hasAny
168168
}
169169
}
170170

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenter.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import io.element.android.libraries.architecture.AsyncData
2727
import io.element.android.libraries.architecture.Presenter
2828
import io.element.android.libraries.architecture.runCatchingUpdatingState
2929
import io.element.android.libraries.architecture.runUpdatingState
30+
import io.element.android.libraries.featureflag.api.FeatureFlagService
31+
import io.element.android.libraries.featureflag.api.FeatureFlags
3032
import io.element.android.libraries.matrix.api.MatrixClient
3133
import io.element.android.libraries.matrix.api.core.RoomAlias
3234
import io.element.android.libraries.matrix.api.room.JoinedRoom
@@ -45,6 +47,7 @@ class SecurityAndPrivacyPresenter(
4547
@Assisted private val navigator: SecurityAndPrivacyNavigator,
4648
private val matrixClient: MatrixClient,
4749
private val room: JoinedRoom,
50+
private val featureFlagService: FeatureFlagService,
4851
) : Presenter<SecurityAndPrivacyState> {
4952
@AssistedFactory
5053
interface Factory {
@@ -55,6 +58,9 @@ class SecurityAndPrivacyPresenter(
5558
override fun present(): SecurityAndPrivacyState {
5659
val coroutineScope = rememberCoroutineScope()
5760

61+
val isKnockEnabled by remember {
62+
featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock)
63+
}.collectAsState(false)
5864
val saveAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
5965
val homeserverName = remember { matrixClient.userIdServerName() }
6066
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
@@ -149,6 +155,7 @@ class SecurityAndPrivacyPresenter(
149155
editedSettings = editedSettings,
150156
homeserverName = homeserverName,
151157
showEnableEncryptionConfirmation = showEnableEncryptionConfirmation,
158+
isKnockEnabled = isKnockEnabled,
152159
saveAction = saveAction.value,
153160
permissions = permissions,
154161
eventSink = ::handleEvents

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyState.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ data class SecurityAndPrivacyState(
1919
val editedSettings: SecurityAndPrivacySettings,
2020
val homeserverName: String,
2121
val showEnableEncryptionConfirmation: Boolean,
22+
val isKnockEnabled: Boolean,
2223
val saveAction: AsyncAction<Unit>,
2324
private val permissions: SecurityAndPrivacyPermissions,
2425
val eventSink: (SecurityAndPrivacyEvents) -> Unit

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyStateProvider.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ open class SecurityAndPrivacyStateProvider : PreviewParameterProvider<SecurityAn
3030
aSecurityAndPrivacyState(
3131
savedSettings = aSecurityAndPrivacySettings(
3232
roomAccess = SecurityAndPrivacyRoomAccess.SpaceMember
33-
)
33+
),
34+
isKnockEnabled = false,
3435
),
3536
aSecurityAndPrivacyState(
3637
editedSettings = aSecurityAndPrivacySettings(
@@ -54,6 +55,12 @@ open class SecurityAndPrivacyStateProvider : PreviewParameterProvider<SecurityAn
5455
aSecurityAndPrivacyState(
5556
saveAction = AsyncAction.Loading
5657
),
58+
aSecurityAndPrivacyState(
59+
savedSettings = aSecurityAndPrivacySettings(
60+
roomAccess = SecurityAndPrivacyRoomAccess.AskToJoin
61+
),
62+
isKnockEnabled = false,
63+
),
5764
)
5865
}
5966

@@ -83,13 +90,15 @@ fun aSecurityAndPrivacyState(
8390
canChangeEncryption = true,
8491
canChangeRoomVisibility = true
8592
),
93+
isKnockEnabled: Boolean = true,
8694
eventSink: (SecurityAndPrivacyEvents) -> Unit = {}
8795
) = SecurityAndPrivacyState(
8896
editedSettings = editedSettings,
8997
savedSettings = savedSettings,
9098
homeserverName = homeserverName,
9199
showEnableEncryptionConfirmation = showEncryptionConfirmation,
92100
saveAction = saveAction,
101+
isKnockEnabled = isKnockEnabled,
93102
permissions = permissions,
94103
eventSink = eventSink
95104
)

features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyView.kt

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ fun SecurityAndPrivacyView(
8181
modifier = Modifier.padding(top = 24.dp),
8282
edited = state.editedSettings.roomAccess,
8383
saved = state.savedSettings.roomAccess,
84+
isKnockEnabled = state.isKnockEnabled,
8485
onSelectOption = { state.eventSink(SecurityAndPrivacyEvents.ChangeRoomAccess(it)) },
8586
)
8687
}
@@ -176,6 +177,7 @@ private fun SecurityAndPrivacySection(
176177
private fun RoomAccessSection(
177178
edited: SecurityAndPrivacyRoomAccess,
178179
saved: SecurityAndPrivacyRoomAccess,
180+
isKnockEnabled: Boolean,
179181
onSelectOption: (SecurityAndPrivacyRoomAccess) -> Unit,
180182
modifier: Modifier = Modifier,
181183
) {
@@ -189,12 +191,18 @@ private fun RoomAccessSection(
189191
trailingContent = ListItemContent.RadioButton(selected = edited == SecurityAndPrivacyRoomAccess.InviteOnly),
190192
onClick = { onSelectOption(SecurityAndPrivacyRoomAccess.InviteOnly) },
191193
)
192-
ListItem(
193-
headlineContent = { Text(text = stringResource(R.string.screen_security_and_privacy_ask_to_join_option_title)) },
194-
supportingContent = { Text(text = stringResource(R.string.screen_security_and_privacy_ask_to_join_option_description)) },
195-
trailingContent = ListItemContent.RadioButton(selected = edited == SecurityAndPrivacyRoomAccess.AskToJoin),
196-
onClick = { onSelectOption(SecurityAndPrivacyRoomAccess.AskToJoin) },
197-
)
194+
// Show Ask to join option in two cases:
195+
// - the Knock FF is enabled
196+
// - AskToJoin is the current saved value
197+
if (saved == SecurityAndPrivacyRoomAccess.AskToJoin || isKnockEnabled) {
198+
ListItem(
199+
headlineContent = { Text(text = stringResource(R.string.screen_security_and_privacy_ask_to_join_option_title)) },
200+
supportingContent = { Text(text = stringResource(R.string.screen_security_and_privacy_ask_to_join_option_description)) },
201+
trailingContent = ListItemContent.RadioButton(selected = edited == SecurityAndPrivacyRoomAccess.AskToJoin),
202+
onClick = { onSelectOption(SecurityAndPrivacyRoomAccess.AskToJoin) },
203+
enabled = isKnockEnabled,
204+
)
205+
}
198206
ListItem(
199207
headlineContent = { Text(text = stringResource(R.string.screen_security_and_privacy_room_access_anyone_option_title)) },
200208
supportingContent = { Text(text = stringResource(R.string.screen_security_and_privacy_room_access_anyone_option_description)) },

features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -719,10 +719,6 @@ class RoomDetailsPresenterTest {
719719
val presenter = createRoomDetailsPresenter(room = room, featureFlagService = featureFlagService)
720720
presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) {
721721
skipItems(1)
722-
with(awaitItem()) {
723-
assertThat(canShowSecurityAndPrivacy).isFalse()
724-
}
725-
featureFlagService.setFeatureEnabled(FeatureFlags.Knock, true)
726722
with(awaitItem()) {
727723
assertThat(canShowSecurityAndPrivacy).isTrue()
728724
}

features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenterTest.kt

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ package io.element.android.features.roomdetails.impl.securityandprivacy
1010
import com.google.common.truth.Truth.assertThat
1111
import io.element.android.libraries.architecture.AsyncAction
1212
import io.element.android.libraries.architecture.AsyncData
13+
import io.element.android.libraries.featureflag.api.FeatureFlagService
14+
import io.element.android.libraries.featureflag.api.FeatureFlags
15+
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
1316
import io.element.android.libraries.matrix.api.room.history.RoomHistoryVisibility
1417
import io.element.android.libraries.matrix.api.room.join.JoinRule
1518
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
@@ -38,6 +41,7 @@ class SecurityAndPrivacyPresenterTest {
3841
assertThat(showRoomVisibilitySections).isFalse()
3942
assertThat(showHistoryVisibilitySection).isFalse()
4043
assertThat(showEncryptionSection).isFalse()
44+
assertThat(isKnockEnabled).isFalse()
4145
}
4246
with(awaitItem()) {
4347
assertThat(editedSettings).isEqualTo(savedSettings)
@@ -48,6 +52,7 @@ class SecurityAndPrivacyPresenterTest {
4852
assertThat(showRoomVisibilitySections).isFalse()
4953
assertThat(showHistoryVisibilitySection).isTrue()
5054
assertThat(showEncryptionSection).isTrue()
55+
assertThat(isKnockEnabled).isFalse()
5156
}
5257
}
5358
}
@@ -56,14 +61,14 @@ class SecurityAndPrivacyPresenterTest {
5661
fun `present - room info change updates saved and edited settings`() = runTest {
5762
val room = FakeJoinedRoom(
5863
baseRoom = FakeBaseRoom(
59-
canSendStateResult = { _, _ -> Result.success(true) },
60-
initialRoomInfo = aRoomInfo(
61-
joinRule = JoinRule.Public,
62-
historyVisibility = RoomHistoryVisibility.WorldReadable,
63-
canonicalAlias = A_ROOM_ALIAS,
64+
canSendStateResult = { _, _ -> Result.success(true) },
65+
initialRoomInfo = aRoomInfo(
66+
joinRule = JoinRule.Public,
67+
historyVisibility = RoomHistoryVisibility.WorldReadable,
68+
canonicalAlias = A_ROOM_ALIAS,
69+
)
6470
)
6571
)
66-
)
6772
val presenter = createSecurityAndPrivacyPresenter(room = room)
6873
presenter.test {
6974
skipItems(1)
@@ -163,10 +168,10 @@ class SecurityAndPrivacyPresenterTest {
163168
fun `present - room visibility loading and change`() = runTest {
164169
val room = FakeJoinedRoom(
165170
baseRoom = FakeBaseRoom(
166-
canSendStateResult = { _, _ -> Result.success(true) },
167-
getRoomVisibilityResult = { Result.success(RoomVisibility.Private) },
168-
initialRoomInfo = aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared)
169-
)
171+
canSendStateResult = { _, _ -> Result.success(true) },
172+
getRoomVisibilityResult = { Result.success(RoomVisibility.Private) },
173+
initialRoomInfo = aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared)
174+
)
170175
)
171176
val presenter = createSecurityAndPrivacyPresenter(room = room)
172177
presenter.test {
@@ -212,10 +217,10 @@ class SecurityAndPrivacyPresenterTest {
212217
val updateRoomHistoryVisibilityLambda = lambdaRecorder<RoomHistoryVisibility, Result<Unit>> { Result.success(Unit) }
213218
val room = FakeJoinedRoom(
214219
baseRoom = FakeBaseRoom(
215-
canSendStateResult = { _, _ -> Result.success(true) },
216-
getRoomVisibilityResult = { Result.success(RoomVisibility.Private) },
217-
initialRoomInfo = aRoomInfo(joinRule = JoinRule.Invite, historyVisibility = RoomHistoryVisibility.Shared)
218-
),
220+
canSendStateResult = { _, _ -> Result.success(true) },
221+
getRoomVisibilityResult = { Result.success(RoomVisibility.Private) },
222+
initialRoomInfo = aRoomInfo(joinRule = JoinRule.Invite, historyVisibility = RoomHistoryVisibility.Shared)
223+
),
219224
enableEncryptionResult = enableEncryptionLambda,
220225
updateJoinRuleResult = updateJoinRuleLambda,
221226
updateRoomVisibilityResult = updateRoomVisibilityLambda,
@@ -279,10 +284,10 @@ class SecurityAndPrivacyPresenterTest {
279284
val updateRoomHistoryVisibilityLambda = lambdaRecorder<RoomHistoryVisibility, Result<Unit>> { Result.success(Unit) }
280285
val room = FakeJoinedRoom(
281286
baseRoom = FakeBaseRoom(
282-
canSendStateResult = { _, _ -> Result.success(true) },
283-
getRoomVisibilityResult = { Result.success(RoomVisibility.Private) },
284-
initialRoomInfo = aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared, joinRule = JoinRule.Private)
285-
),
287+
canSendStateResult = { _, _ -> Result.success(true) },
288+
getRoomVisibilityResult = { Result.success(RoomVisibility.Private) },
289+
initialRoomInfo = aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared, joinRule = JoinRule.Private)
290+
),
286291
enableEncryptionResult = enableEncryptionLambda,
287292
updateJoinRuleResult = updateJoinRuleLambda,
288293
updateRoomVisibilityResult = updateRoomVisibilityLambda,
@@ -323,7 +328,8 @@ class SecurityAndPrivacyPresenterTest {
323328
)
324329
// Saved settings are updated 2 times to match the edited settings
325330
skipItems(3)
326-
with(awaitItem()) {
331+
val state = awaitItem()
332+
with(state) {
327333
assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java)
328334
assertThat(savedSettings.isVisibleInRoomDirectory).isNotEqualTo(editedSettings.isVisibleInRoomDirectory)
329335
assertThat(canBeSaved).isTrue()
@@ -332,6 +338,26 @@ class SecurityAndPrivacyPresenterTest {
332338
assert(updateJoinRuleLambda).isCalledOnce()
333339
assert(updateRoomVisibilityLambda).isCalledOnce()
334340
assert(updateRoomHistoryVisibilityLambda).isCalledOnce()
341+
// Clear error
342+
state.eventSink(SecurityAndPrivacyEvents.DismissSaveError)
343+
with(awaitItem()) {
344+
assertThat(saveAction).isEqualTo(AsyncAction.Uninitialized)
345+
}
346+
}
347+
}
348+
349+
@Test
350+
fun `present - isKnockEnabled is true if the Knock feature flag is enabled`() = runTest {
351+
val presenter = createSecurityAndPrivacyPresenter(
352+
featureFlagService = FakeFeatureFlagService(
353+
initialState = mapOf(
354+
FeatureFlags.Knock.key to true,
355+
)
356+
)
357+
)
358+
presenter.test {
359+
assertThat(awaitItem().isKnockEnabled).isFalse()
360+
assertThat(awaitItem().isKnockEnabled).isTrue()
335361
}
336362
}
337363

@@ -345,13 +371,15 @@ class SecurityAndPrivacyPresenterTest {
345371
),
346372
),
347373
navigator: SecurityAndPrivacyNavigator = FakeSecurityAndPrivacyNavigator(),
374+
featureFlagService: FeatureFlagService = FakeFeatureFlagService(),
348375
): SecurityAndPrivacyPresenter {
349376
return SecurityAndPrivacyPresenter(
350377
room = room,
351378
matrixClient = FakeMatrixClient(
352379
userIdServerNameLambda = { serverName },
353380
),
354-
navigator = navigator
381+
navigator = navigator,
382+
featureFlagService = featureFlagService,
355383
)
356384
}
357385
}
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading

0 commit comments

Comments
 (0)