mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 12:07:28 +00:00
Move the "Blocked users" item to the Moderation and safety screen.
The item is removed from the account section of the root settings screen and added at the bottom of the Moderation and safety screen. As before, it is only shown when at least one user is blocked, with the number of blocked users. ModerationAndSafetyPresenter now computes the number of blocked users, and ModerationAndSafetyNode exposes a Callback to navigate to the blocked users screen. Tests are moved accordingly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Benoit Marty
parent
c612c80b1d
commit
01681e9fff
+6
-5
@@ -193,10 +193,6 @@ class PreferencesFlowNode(
|
||||
backstack.push(NavTarget.UserProfile(matrixUser))
|
||||
}
|
||||
|
||||
override fun navigateToBlockedUsers() {
|
||||
backstack.push(NavTarget.BlockedUsers)
|
||||
}
|
||||
|
||||
override fun startSignOutFlow() {
|
||||
backstack.push(NavTarget.SignOut)
|
||||
}
|
||||
@@ -236,7 +232,12 @@ class PreferencesFlowNode(
|
||||
createNode<LabsNode>(buildContext, listOf(callback))
|
||||
}
|
||||
NavTarget.ModerationAndSafety -> {
|
||||
createNode<ModerationAndSafetyNode>(buildContext)
|
||||
val callback = object : ModerationAndSafetyNode.Callback {
|
||||
override fun navigateToBlockedUsers() {
|
||||
backstack.push(NavTarget.BlockedUsers)
|
||||
}
|
||||
}
|
||||
createNode<ModerationAndSafetyNode>(buildContext, listOf(callback))
|
||||
}
|
||||
NavTarget.LocationSettings -> {
|
||||
createNode<LocationSettingsNode>(buildContext)
|
||||
|
||||
-1
@@ -16,7 +16,6 @@ interface PreferencesAccountCallback {
|
||||
fun navigateToNotificationSettings()
|
||||
fun navigateToLinkNewDevice()
|
||||
fun navigateToUserProfile(matrixUser: MatrixUser)
|
||||
fun navigateToBlockedUsers()
|
||||
fun startSignOutFlow()
|
||||
fun startAccountDeactivationFlow()
|
||||
}
|
||||
|
||||
-9
@@ -27,8 +27,6 @@ import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.indicator.api.IndicatorService
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
@Inject
|
||||
class PreferencesAccountPresenter(
|
||||
@@ -67,12 +65,6 @@ class PreferencesAccountPresenter(
|
||||
canDeactivateAccount = matrixClient.canDeactivateAccount()
|
||||
}
|
||||
|
||||
val numberOfBlockedUsers by produceState(initialValue = 0) {
|
||||
matrixClient.ignoredUsersFlow
|
||||
.onEach { value = it.size }
|
||||
.launchIn(this)
|
||||
}
|
||||
|
||||
val directLogoutState = directLogoutPresenter.present()
|
||||
|
||||
val accountManagementUrl by produceState<String?>(initialValue = null) {
|
||||
@@ -91,7 +83,6 @@ class PreferencesAccountPresenter(
|
||||
canReportBug = canReportBug,
|
||||
showLinkNewDevice = showLinkNewDevice,
|
||||
canDeactivateAccount = canDeactivateAccount,
|
||||
numberOfBlockedUsers = numberOfBlockedUsers,
|
||||
directLogoutState = directLogoutState,
|
||||
snackbarMessage = snackbarMessage,
|
||||
)
|
||||
|
||||
+1
-4
@@ -19,9 +19,6 @@ data class PreferencesAccountState(
|
||||
val canReportBug: Boolean,
|
||||
val showLinkNewDevice: Boolean,
|
||||
val canDeactivateAccount: Boolean,
|
||||
val numberOfBlockedUsers: Int,
|
||||
val directLogoutState: DirectLogoutState,
|
||||
val snackbarMessage: SnackbarMessage?,
|
||||
) {
|
||||
val showBlockedUsersItem = numberOfBlockedUsers > 0
|
||||
}
|
||||
)
|
||||
|
||||
-4
@@ -26,7 +26,6 @@ open class PreferencesAccountStatePreviewParam : PreviewParameterProvider<Prefer
|
||||
canReportBug = true,
|
||||
showLinkNewDevice = true,
|
||||
canDeactivateAccount = false,
|
||||
numberOfBlockedUsers = 3,
|
||||
),
|
||||
aPreferencesAccountState(
|
||||
myUser = aMatrixUser(displayName = null),
|
||||
@@ -40,7 +39,6 @@ open class PreferencesAccountStatePreviewParam : PreviewParameterProvider<Prefer
|
||||
),
|
||||
aPreferencesAccountState(
|
||||
canReportBug = true,
|
||||
numberOfBlockedUsers = 3,
|
||||
snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete),
|
||||
),
|
||||
aPreferencesAccountState(
|
||||
@@ -58,7 +56,6 @@ fun aPreferencesAccountState(
|
||||
canReportBug: Boolean = false,
|
||||
showLinkNewDevice: Boolean = false,
|
||||
canDeactivateAccount: Boolean = false,
|
||||
numberOfBlockedUsers: Int = 0,
|
||||
directLogoutState: DirectLogoutState = aDirectLogoutState(),
|
||||
snackbarMessage: SnackbarMessage? = null,
|
||||
) = PreferencesAccountState(
|
||||
@@ -69,7 +66,6 @@ fun aPreferencesAccountState(
|
||||
canReportBug = canReportBug,
|
||||
showLinkNewDevice = showLinkNewDevice,
|
||||
canDeactivateAccount = canDeactivateAccount,
|
||||
numberOfBlockedUsers = numberOfBlockedUsers,
|
||||
directLogoutState = directLogoutState,
|
||||
snackbarMessage = snackbarMessage,
|
||||
)
|
||||
|
||||
-13
@@ -35,7 +35,6 @@ fun PreferencesAccountView(
|
||||
onOpenRageShake: () -> Unit,
|
||||
onModerationAndSafetyClick: () -> Unit,
|
||||
onOpenNotificationSettings: () -> Unit,
|
||||
onOpenBlockedUsers: () -> Unit,
|
||||
onSignOutClick: () -> Unit,
|
||||
onDeactivateClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -46,7 +45,6 @@ fun PreferencesAccountView(
|
||||
state = state,
|
||||
onManageAccountClick = onManageAccountClick,
|
||||
onLinkNewDeviceClick = onLinkNewDeviceClick,
|
||||
onOpenBlockedUsers = onOpenBlockedUsers,
|
||||
)
|
||||
OtherSettingsSection(
|
||||
state = state,
|
||||
@@ -69,7 +67,6 @@ private fun ManageAccountSection(
|
||||
state: PreferencesAccountState,
|
||||
onManageAccountClick: (url: String) -> Unit,
|
||||
onLinkNewDeviceClick: () -> Unit,
|
||||
onOpenBlockedUsers: () -> Unit,
|
||||
) {
|
||||
PreferenceCategory(
|
||||
title = stringResource(CommonStrings.common_account_settings),
|
||||
@@ -90,14 +87,6 @@ private fun ManageAccountSection(
|
||||
onClick = onLinkNewDeviceClick,
|
||||
)
|
||||
}
|
||||
if (state.showBlockedUsersItem) {
|
||||
ListItem(
|
||||
content = { Text(stringResource(id = CommonStrings.common_blocked_users)) },
|
||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())),
|
||||
onClick = onOpenBlockedUsers,
|
||||
trailingContent = ListItemContent.Text(state.numberOfBlockedUsers.toString()),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,9 +162,7 @@ internal fun PreferencesAccountViewPreview(@PreviewParameter(PreferencesAccountS
|
||||
onManageAccountClick = {},
|
||||
onLinkNewDeviceClick = {},
|
||||
onOpenNotificationSettings = {},
|
||||
onOpenBlockedUsers = {},
|
||||
onSignOutClick = {},
|
||||
onDeactivateClick = {},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -15,6 +15,7 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||
import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.libraries.architecture.callback
|
||||
import io.element.android.libraries.di.SessionScope
|
||||
|
||||
@ContributesNode(SessionScope::class)
|
||||
@@ -24,6 +25,12 @@ class ModerationAndSafetyNode(
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val presenter: ModerationAndSafetyPresenter,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
interface Callback : Plugin {
|
||||
fun navigateToBlockedUsers()
|
||||
}
|
||||
|
||||
private val callback: Callback = callback()
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val state = presenter.present()
|
||||
@@ -31,6 +38,7 @@ class ModerationAndSafetyNode(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
onBackClick = ::navigateUp,
|
||||
onOpenBlockedUsers = callback::navigateToBlockedUsers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -10,16 +10,21 @@ package io.element.android.features.preferences.impl.moderation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Inject
|
||||
class ModerationAndSafetyPresenter(
|
||||
private val matrixClient: MatrixClient,
|
||||
private val sessionPreferencesStore: SessionPreferencesStore,
|
||||
private val mediaPreviewConfigStateStore: MediaPreviewConfigStateStore,
|
||||
@SessionCoroutineScope
|
||||
@@ -33,6 +38,12 @@ class ModerationAndSafetyPresenter(
|
||||
|
||||
val mediaPreviewConfigState = mediaPreviewConfigStateStore.state()
|
||||
|
||||
val numberOfBlockedUsers by produceState(initialValue = 0) {
|
||||
matrixClient.ignoredUsersFlow
|
||||
.onEach { value = it.size }
|
||||
.launchIn(this)
|
||||
}
|
||||
|
||||
fun handleEvent(event: ModerationAndSafetyEvent) {
|
||||
when (event) {
|
||||
is ModerationAndSafetyEvent.SetSharePresenceEnabled -> sessionCoroutineScope.launch {
|
||||
@@ -46,6 +57,7 @@ class ModerationAndSafetyPresenter(
|
||||
return ModerationAndSafetyState(
|
||||
isSharePresenceEnabled = isSharePresenceEnabled,
|
||||
mediaPreviewConfigState = mediaPreviewConfigState,
|
||||
numberOfBlockedUsers = numberOfBlockedUsers,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
|
||||
+4
-1
@@ -10,5 +10,8 @@ package io.element.android.features.preferences.impl.moderation
|
||||
data class ModerationAndSafetyState(
|
||||
val isSharePresenceEnabled: Boolean,
|
||||
val mediaPreviewConfigState: MediaPreviewConfigState,
|
||||
val numberOfBlockedUsers: Int,
|
||||
val eventSink: (ModerationAndSafetyEvent) -> Unit
|
||||
)
|
||||
) {
|
||||
val showBlockedUsersItem = numberOfBlockedUsers > 0
|
||||
}
|
||||
|
||||
+3
@@ -20,6 +20,7 @@ open class ModerationAndSafetyStatePreviewParam : PreviewParameterProvider<Moder
|
||||
aModerationAndSafetyState(timelineMediaPreviewValue = MediaPreviewValue.Off),
|
||||
aModerationAndSafetyState(setHideInviteAvatarsAction = AsyncAction.Loading),
|
||||
aModerationAndSafetyState(setTimelineMediaPreviewAction = AsyncAction.Loading),
|
||||
aModerationAndSafetyState(numberOfBlockedUsers = 3),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,6 +30,7 @@ fun aModerationAndSafetyState(
|
||||
timelineMediaPreviewValue: MediaPreviewValue = MediaPreviewValue.On,
|
||||
setTimelineMediaPreviewAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
setHideInviteAvatarsAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
numberOfBlockedUsers: Int = 0,
|
||||
eventSink: (ModerationAndSafetyEvent) -> Unit = {},
|
||||
) = ModerationAndSafetyState(
|
||||
isSharePresenceEnabled = isSharePresenceEnabled,
|
||||
@@ -38,5 +40,6 @@ fun aModerationAndSafetyState(
|
||||
setTimelineMediaPreviewAction = setTimelineMediaPreviewAction,
|
||||
setHideInviteAvatarsAction = setHideInviteAvatarsAction
|
||||
),
|
||||
numberOfBlockedUsers = numberOfBlockedUsers,
|
||||
eventSink = eventSink
|
||||
)
|
||||
|
||||
+28
-1
@@ -14,6 +14,7 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.preferences.impl.R
|
||||
import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
@@ -24,6 +25,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewBlack
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight
|
||||
import io.element.android.libraries.designsystem.theme.components.IconSource
|
||||
import io.element.android.libraries.designsystem.theme.components.ListItem
|
||||
import io.element.android.libraries.designsystem.theme.components.ListSectionHeader
|
||||
import io.element.android.libraries.designsystem.theme.components.ListSupportingText
|
||||
@@ -43,6 +45,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
|
||||
fun ModerationAndSafetyView(
|
||||
state: ModerationAndSafetyState,
|
||||
onBackClick: () -> Unit,
|
||||
onOpenBlockedUsers: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val snackbarDispatcher = LocalSnackbarDispatcher.current
|
||||
@@ -63,6 +66,29 @@ fun ModerationAndSafetyView(
|
||||
PresenceSection(state)
|
||||
InvitesSection(state)
|
||||
MediaSection(state)
|
||||
if (state.showBlockedUsersItem) {
|
||||
BlockedUsersSection(
|
||||
numberOfBlockedUsers = state.numberOfBlockedUsers,
|
||||
onOpenBlockedUsers = onOpenBlockedUsers,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BlockedUsersSection(
|
||||
numberOfBlockedUsers: Int,
|
||||
onOpenBlockedUsers: () -> Unit,
|
||||
) {
|
||||
PreferenceCategory(
|
||||
title = stringResource(R.string.screen_moderation_and_safety_other_users_heading),
|
||||
) {
|
||||
ListItem(
|
||||
content = { Text(stringResource(id = CommonStrings.common_blocked_users)) },
|
||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Block())),
|
||||
onClick = onOpenBlockedUsers,
|
||||
trailingContent = ListItemContent.Text(numberOfBlockedUsers.toString()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +201,7 @@ internal fun ModerationAndSafetyViewBlackPreview(@PreviewParameter(ModerationAnd
|
||||
private fun ContentToPreview(state: ModerationAndSafetyState) {
|
||||
ModerationAndSafetyView(
|
||||
state = state,
|
||||
onBackClick = { }
|
||||
onBackClick = { },
|
||||
onOpenBlockedUsers = { },
|
||||
)
|
||||
}
|
||||
|
||||
-1
@@ -89,7 +89,6 @@ class PreferencesRootNode(
|
||||
onManageAccountClick = { onManageAccountClick(activity, it, isDark) },
|
||||
onOpenNotificationSettings = callback::navigateToNotificationSettings,
|
||||
onEditProfileClick = callback::navigateToUserProfile,
|
||||
onOpenBlockedUsers = callback::navigateToBlockedUsers,
|
||||
onSignOutClick = {
|
||||
if (state.preferencesAccountState.directLogoutState.canDoDirectSignOut) {
|
||||
state.preferencesAccountState.directLogoutState.eventSink(DirectLogoutEvent.Logout(ignoreSdkError = false))
|
||||
|
||||
-3
@@ -93,7 +93,6 @@ fun PreferencesRootView(
|
||||
onOpenRageShake: () -> Unit,
|
||||
onModerationAndSafetyClick: () -> Unit,
|
||||
onOpenNotificationSettings: () -> Unit,
|
||||
onOpenBlockedUsers: () -> Unit,
|
||||
onSignOutClick: () -> Unit,
|
||||
onDeactivateClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -140,7 +139,6 @@ fun PreferencesRootView(
|
||||
onOpenRageShake = onOpenRageShake,
|
||||
onModerationAndSafetyClick = onModerationAndSafetyClick,
|
||||
onOpenNotificationSettings = onOpenNotificationSettings,
|
||||
onOpenBlockedUsers = onOpenBlockedUsers,
|
||||
onSignOutClick = onSignOutClick,
|
||||
onDeactivateClick = onDeactivateClick,
|
||||
)
|
||||
@@ -441,7 +439,6 @@ private fun ContentToPreview(state: PreferencesRootState) {
|
||||
onOpenRageShake = {},
|
||||
onModerationAndSafetyClick = {},
|
||||
onOpenNotificationSettings = {},
|
||||
onOpenBlockedUsers = {},
|
||||
onSignOutClick = {},
|
||||
onDeactivateClick = {},
|
||||
)
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
<string name="screen_labs_header_title">"Feeling experimental?"</string>
|
||||
<string name="screen_labs_title">"Labs"</string>
|
||||
<string name="screen_moderation_and_safety_invites_heading">"Invites"</string>
|
||||
<string name="screen_moderation_and_safety_other_users_heading">"Other users"</string>
|
||||
<string name="screen_moderation_and_safety_share_presence_heading">"Your appearance"</string>
|
||||
<string name="screen_notification_settings_additional_settings_section_title">"Additional settings"</string>
|
||||
<string name="screen_notification_settings_call_ringtone_label">"Call ringtone"</string>
|
||||
|
||||
-21
@@ -22,8 +22,6 @@ import io.element.android.libraries.matrix.api.oauth.AccountManagementAction
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.test.AN_AVATAR_URL
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.A_USER_NAME
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
|
||||
@@ -32,8 +30,6 @@ import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
@@ -69,8 +65,6 @@ class PreferencesAccountPresenterTest {
|
||||
assertThat(initialState.showSecureBackupBadge).isFalse()
|
||||
assertThat(initialState.accountManagementUrl).isNull()
|
||||
assertThat(initialState.showLinkNewDevice).isFalse()
|
||||
assertThat(initialState.numberOfBlockedUsers).isEqualTo(0)
|
||||
assertThat(initialState.showBlockedUsersItem).isFalse()
|
||||
assertThat(initialState.directLogoutState).isEqualTo(aDirectLogoutState())
|
||||
assertThat(initialState.snackbarMessage).isNull()
|
||||
val loadedState = consumeItemsUntilPredicate { it.accountManagementUrl != null }.last()
|
||||
@@ -98,21 +92,6 @@ class PreferencesAccountPresenterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - number of blocked users`() = runTest {
|
||||
createPreferencesAccountPresenter(
|
||||
matrixClient = FakeMatrixClient(
|
||||
canDeactivateAccountResult = { true },
|
||||
accountManagementUrlResult = { Result.success("") },
|
||||
ignoredUsersFlow = MutableStateFlow(persistentListOf(A_USER_ID, A_USER_ID_2)),
|
||||
),
|
||||
).test {
|
||||
val state = consumeItemsUntilPredicate { it.numberOfBlockedUsers == 2 }.last()
|
||||
assertThat(state.showBlockedUsersItem).isTrue()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - secure backup badge`() = runTest {
|
||||
val indicatorService = FakeIndicatorService()
|
||||
|
||||
-19
@@ -62,23 +62,6 @@ class PreferencesAccountViewTest : RobolectricTest() {
|
||||
onNodeWithText(activity!!.getString(CommonStrings.common_link_new_device)).assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `click on Blocked users invokes the expected callback`() = runAndroidComposeUiTest {
|
||||
ensureCalledOnce { callback ->
|
||||
setView(
|
||||
aPreferencesAccountState(numberOfBlockedUsers = 1),
|
||||
onOpenBlockedUsers = callback,
|
||||
)
|
||||
clickOn(CommonStrings.common_blocked_users)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when numberOfBlockedUsers is 0, item is not shown`() = runAndroidComposeUiTest {
|
||||
setView(aPreferencesAccountState(numberOfBlockedUsers = 0))
|
||||
onNodeWithText(activity!!.getString(CommonStrings.common_blocked_users)).assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `click on Notification invokes the expected callback`() = runAndroidComposeUiTest {
|
||||
ensureCalledOnce { callback ->
|
||||
@@ -177,7 +160,6 @@ private fun AndroidComposeUiTest<ComponentActivity>.setView(
|
||||
onOpenRageShake: () -> Unit = EnsureNeverCalled(),
|
||||
onModerationAndSafetyClick: () -> Unit = EnsureNeverCalled(),
|
||||
onOpenNotificationSettings: () -> Unit = EnsureNeverCalled(),
|
||||
onOpenBlockedUsers: () -> Unit = EnsureNeverCalled(),
|
||||
onSignOutClick: () -> Unit = EnsureNeverCalled(),
|
||||
onDeactivateClick: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
@@ -190,7 +172,6 @@ private fun AndroidComposeUiTest<ComponentActivity>.setView(
|
||||
onOpenRageShake = onOpenRageShake,
|
||||
onModerationAndSafetyClick = onModerationAndSafetyClick,
|
||||
onOpenNotificationSettings = onOpenNotificationSettings,
|
||||
onOpenBlockedUsers = onOpenBlockedUsers,
|
||||
onSignOutClick = onSignOutClick,
|
||||
onDeactivateClick = onDeactivateClick,
|
||||
)
|
||||
|
||||
+23
@@ -10,10 +10,16 @@ package io.element.android.features.preferences.impl.moderation
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import io.element.android.tests.testutils.consumeItemsUntilPredicate
|
||||
import io.element.android.tests.testutils.test
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -31,10 +37,25 @@ class ModerationAndSafetyPresenterTest {
|
||||
assertThat(mediaPreviewConfigState.timelineMediaPreviewValue).isEqualTo(MediaPreviewValue.On)
|
||||
assertThat(mediaPreviewConfigState.setHideInviteAvatarsAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
assertThat(mediaPreviewConfigState.setTimelineMediaPreviewAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
assertThat(numberOfBlockedUsers).isEqualTo(0)
|
||||
assertThat(showBlockedUsersItem).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - number of blocked users`() = runTest {
|
||||
createModerationAndSafetyPresenter(
|
||||
matrixClient = FakeMatrixClient(
|
||||
ignoredUsersFlow = MutableStateFlow(persistentListOf(A_USER_ID, A_USER_ID_2)),
|
||||
),
|
||||
).test {
|
||||
val state = consumeItemsUntilPredicate { it.numberOfBlockedUsers == 2 }.last()
|
||||
assertThat(state.showBlockedUsersItem).isTrue()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - share presence off on`() = runTest {
|
||||
createModerationAndSafetyPresenter().test {
|
||||
@@ -121,9 +142,11 @@ class ModerationAndSafetyPresenterTest {
|
||||
}
|
||||
|
||||
private fun CoroutineScope.createModerationAndSafetyPresenter(
|
||||
matrixClient: FakeMatrixClient = FakeMatrixClient(),
|
||||
sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(),
|
||||
mediaPreviewConfigStateStore: MediaPreviewConfigStateStore = FakeMediaPreviewConfigStateStore(),
|
||||
) = ModerationAndSafetyPresenter(
|
||||
matrixClient = matrixClient,
|
||||
sessionPreferencesStore = sessionPreferencesStore,
|
||||
mediaPreviewConfigStateStore = mediaPreviewConfigStateStore,
|
||||
sessionCoroutineScope = this,
|
||||
|
||||
+36
@@ -12,10 +12,16 @@ package io.element.android.features.preferences.impl.moderation
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.AndroidComposeUiTest
|
||||
import androidx.compose.ui.test.ExperimentalTestApi
|
||||
import androidx.compose.ui.test.hasClickAction
|
||||
import androidx.compose.ui.test.hasText
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performScrollTo
|
||||
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
|
||||
import io.element.android.features.preferences.impl.R
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
import io.element.android.tests.testutils.clickOn
|
||||
@@ -138,16 +144,46 @@ class ModerationAndSafetyViewTest : RobolectricTest() {
|
||||
clickOn(R.string.screen_advanced_settings_show_media_timeline_always_hide)
|
||||
clickOn(R.string.screen_advanced_settings_show_media_timeline_private_rooms)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `click on Blocked users invokes the expected callback`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<ModerationAndSafetyEvent>(expectEvents = false)
|
||||
ensureCalledOnce { callback ->
|
||||
setModerationAndSafetyView(
|
||||
state = aModerationAndSafetyState(
|
||||
numberOfBlockedUsers = 1,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
onOpenBlockedUsers = callback,
|
||||
)
|
||||
val text = activity!!.getString(CommonStrings.common_blocked_users)
|
||||
onNode(hasText(text) and hasClickAction()).performScrollTo().performClick()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when numberOfBlockedUsers is 0, Blocked users item is not shown`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<ModerationAndSafetyEvent>(expectEvents = false)
|
||||
setModerationAndSafetyView(
|
||||
state = aModerationAndSafetyState(
|
||||
numberOfBlockedUsers = 0,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
onNodeWithText(activity!!.getString(CommonStrings.common_blocked_users)).assertDoesNotExist()
|
||||
}
|
||||
}
|
||||
|
||||
private fun AndroidComposeUiTest<ComponentActivity>.setModerationAndSafetyView(
|
||||
state: ModerationAndSafetyState,
|
||||
onBackClick: () -> Unit = EnsureNeverCalled(),
|
||||
onOpenBlockedUsers: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
setContent {
|
||||
ModerationAndSafetyView(
|
||||
state = state,
|
||||
onBackClick = onBackClick,
|
||||
onOpenBlockedUsers = onOpenBlockedUsers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
-2
@@ -409,7 +409,6 @@ private fun AndroidComposeUiTest<ComponentActivity>.setView(
|
||||
onOpenRageShake: () -> Unit = EnsureNeverCalled(),
|
||||
onModerationAndSafetyClick: () -> Unit = EnsureNeverCalled(),
|
||||
onOpenNotificationSettings: () -> Unit = EnsureNeverCalled(),
|
||||
onOpenBlockedUsers: () -> Unit = EnsureNeverCalled(),
|
||||
onSignOutClick: () -> Unit = EnsureNeverCalled(),
|
||||
onDeactivateClick: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
@@ -433,7 +432,6 @@ private fun AndroidComposeUiTest<ComponentActivity>.setView(
|
||||
onOpenRageShake = onOpenRageShake,
|
||||
onModerationAndSafetyClick = onModerationAndSafetyClick,
|
||||
onOpenNotificationSettings = onOpenNotificationSettings,
|
||||
onOpenBlockedUsers = onOpenBlockedUsers,
|
||||
onSignOutClick = onSignOutClick,
|
||||
onDeactivateClick = onDeactivateClick,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user