mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-03 06:55:41 +00:00
Merge pull request #7788 from element-hq/feature/bma/removeAccount
Improve the sign out screen
This commit is contained in:
@@ -53,7 +53,6 @@ dependencies {
|
||||
implementation(projects.features.announcement.api)
|
||||
implementation(projects.features.invite.api)
|
||||
implementation(projects.features.networkmonitor.api)
|
||||
implementation(projects.features.logout.api)
|
||||
implementation(projects.features.leaveroom.api)
|
||||
implementation(projects.features.rageshake.api)
|
||||
implementation(projects.services.analytics.api)
|
||||
@@ -67,7 +66,6 @@ dependencies {
|
||||
testCommonDependencies(libs, true)
|
||||
testImplementation(projects.features.announcement.test)
|
||||
testImplementation(projects.features.invite.test)
|
||||
testImplementation(projects.features.logout.test)
|
||||
testImplementation(projects.features.networkmonitor.test)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.featureflag.test)
|
||||
|
||||
-3
@@ -40,7 +40,6 @@ import io.element.android.features.invite.api.InviteData
|
||||
import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteView
|
||||
import io.element.android.features.invite.api.declineandblock.DeclineInviteAndBlockEntryPoint
|
||||
import io.element.android.features.leaveroom.api.LeaveRoomRenderer
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutView
|
||||
import io.element.android.features.reportroom.api.ReportRoomEntryPoint
|
||||
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesEntryPoint
|
||||
import io.element.android.features.rolesandpermissions.api.ChangeRoomMemberRolesListType
|
||||
@@ -83,7 +82,6 @@ class HomeFlowNode(
|
||||
private val inviteFriendsUseCase: InviteFriendsUseCase,
|
||||
private val analyticsService: AnalyticsService,
|
||||
private val acceptDeclineInviteView: AcceptDeclineInviteView,
|
||||
private val directLogoutView: DirectLogoutView,
|
||||
private val reportRoomEntryPoint: ReportRoomEntryPoint,
|
||||
private val declineInviteAndBlockUserEntryPoint: DeclineInviteAndBlockEntryPoint,
|
||||
private val changeRoomMemberRolesEntryPoint: ChangeRoomMemberRolesEntryPoint,
|
||||
@@ -248,7 +246,6 @@ class HomeFlowNode(
|
||||
)
|
||||
}
|
||||
)
|
||||
directLogoutView.Render(state.directLogoutState)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-4
@@ -21,7 +21,6 @@ import androidx.compose.runtime.setValue
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.features.home.impl.roomlist.RoomListState
|
||||
import io.element.android.features.home.impl.spaces.HomeSpacesState
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
@@ -42,7 +41,6 @@ class HomePresenter(
|
||||
private val indicatorService: IndicatorService,
|
||||
private val roomListPresenter: Presenter<RoomListState>,
|
||||
private val homeSpacesPresenter: Presenter<HomeSpacesState>,
|
||||
private val logoutPresenter: Presenter<DirectLogoutState>,
|
||||
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
|
||||
private val sessionStore: SessionStore,
|
||||
) : Presenter<HomeState> {
|
||||
@@ -75,7 +73,6 @@ class HomePresenter(
|
||||
}
|
||||
// Avatar indicator
|
||||
val showAvatarIndicator by indicatorService.showRoomListTopBarIndicator()
|
||||
val directLogoutState = logoutPresenter.present()
|
||||
|
||||
fun handleEvent(event: HomeEvent) {
|
||||
when (event) {
|
||||
@@ -98,7 +95,6 @@ class HomePresenter(
|
||||
homeSpacesState = homeSpacesState,
|
||||
snackbarMessage = snackbarMessage,
|
||||
canReportBug = canReportBug,
|
||||
directLogoutState = directLogoutState,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ package io.element.android.features.home.impl
|
||||
import io.element.android.features.home.impl.roomlist.RoomListState
|
||||
import io.element.android.features.home.impl.spacefilters.SpaceFiltersState
|
||||
import io.element.android.features.home.impl.spaces.HomeSpacesState
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -29,7 +28,6 @@ data class HomeState(
|
||||
val homeSpacesState: HomeSpacesState,
|
||||
val snackbarMessage: SnackbarMessage?,
|
||||
val canReportBug: Boolean,
|
||||
val directLogoutState: DirectLogoutState,
|
||||
val eventSink: (HomeEvent) -> Unit,
|
||||
) {
|
||||
val isBackHandlerEnabled = currentHomeNavigationBarItem != HomeNavigationBarItem.Chats || roomListState.spaceFiltersState is SpaceFiltersState.Selected
|
||||
|
||||
-4
@@ -16,8 +16,6 @@ import io.element.android.features.home.impl.roomlist.aRoomsContentState
|
||||
import io.element.android.features.home.impl.roomlist.generateRoomListRoomSummaryList
|
||||
import io.element.android.features.home.impl.spaces.HomeSpacesState
|
||||
import io.element.android.features.home.impl.spaces.aHomeSpacesState
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.logout.api.direct.aDirectLogoutState
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
@@ -63,7 +61,6 @@ internal fun aHomeState(
|
||||
roomListState: RoomListState = aRoomListState(),
|
||||
homeSpacesState: HomeSpacesState = aHomeSpacesState(),
|
||||
canReportBug: Boolean = true,
|
||||
directLogoutState: DirectLogoutState = aDirectLogoutState(),
|
||||
eventSink: (HomeEvent) -> Unit = {}
|
||||
) = HomeState(
|
||||
currentUserAndNeighbors = currentUserAndNeighbors.toImmutableList(),
|
||||
@@ -71,7 +68,6 @@ internal fun aHomeState(
|
||||
hasNetworkConnection = hasNetworkConnection,
|
||||
snackbarMessage = snackbarMessage,
|
||||
canReportBug = canReportBug,
|
||||
directLogoutState = directLogoutState,
|
||||
currentHomeNavigationBarItem = currentHomeNavigationBarItem,
|
||||
roomListState = roomListState,
|
||||
homeSpacesState = homeSpacesState,
|
||||
|
||||
-1
@@ -36,7 +36,6 @@ class DefaultHomeEntryPointTest : RobolectricTest() {
|
||||
inviteFriendsUseCase = { lambdaError() },
|
||||
analyticsService = FakeAnalyticsService(),
|
||||
acceptDeclineInviteView = { _, _, _, _ -> lambdaError() },
|
||||
directLogoutView = { _ -> lambdaError() },
|
||||
reportRoomEntryPoint = { _, _, _ -> lambdaError() },
|
||||
declineInviteAndBlockUserEntryPoint = FakeDeclineInviteAndBlockEntryPoint(),
|
||||
changeRoomMemberRolesEntryPoint = { _, _, _, _ -> lambdaError() },
|
||||
|
||||
-2
@@ -12,7 +12,6 @@ import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.home.impl.roomlist.aRoomListState
|
||||
import io.element.android.features.home.impl.spaces.HomeSpacesState
|
||||
import io.element.android.features.home.impl.spaces.aHomeSpacesState
|
||||
import io.element.android.features.logout.api.direct.aDirectLogoutState
|
||||
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
@@ -162,7 +161,6 @@ internal fun createHomePresenter(
|
||||
indicatorService = indicatorService,
|
||||
roomListPresenter = { aRoomListState() },
|
||||
homeSpacesPresenter = homeSpacesPresenter,
|
||||
logoutPresenter = { aDirectLogoutState() },
|
||||
rageshakeFeatureAvailability = rageshakeFeatureAvailability,
|
||||
sessionStore = sessionStore,
|
||||
)
|
||||
|
||||
-1
@@ -11,7 +11,6 @@ package io.element.android.features.logout.api.direct
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
|
||||
data class DirectLogoutState(
|
||||
val canDoDirectSignOut: Boolean,
|
||||
val logoutAction: AsyncAction<Unit>,
|
||||
val eventSink: (DirectLogoutEvent) -> Unit,
|
||||
)
|
||||
|
||||
-2
@@ -23,11 +23,9 @@ open class DirectLogoutStatePreviewParam : PreviewParameterProvider<DirectLogout
|
||||
}
|
||||
|
||||
fun aDirectLogoutState(
|
||||
canDoDirectSignOut: Boolean = true,
|
||||
logoutAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
eventSink: (DirectLogoutEvent) -> Unit = {},
|
||||
) = DirectLogoutState(
|
||||
canDoDirectSignOut = canDoDirectSignOut,
|
||||
logoutAction = logoutAction,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
|
||||
@@ -31,6 +31,7 @@ dependencies {
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.featureflag.api)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.matrixui)
|
||||
implementation(projects.libraries.designsystem)
|
||||
implementation(projects.libraries.testtags)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
|
||||
+2
@@ -41,6 +41,7 @@ class LogoutPresenter(
|
||||
@Composable
|
||||
override fun present(): LogoutState {
|
||||
val localCoroutineScope = rememberCoroutineScope()
|
||||
val currentUser by matrixClient.userProfile.collectAsState()
|
||||
val logoutAction: MutableState<AsyncAction<Unit>> = remember {
|
||||
mutableStateOf(AsyncAction.Uninitialized)
|
||||
}
|
||||
@@ -90,6 +91,7 @@ class LogoutPresenter(
|
||||
}
|
||||
|
||||
return LogoutState(
|
||||
currentUser = currentUser,
|
||||
isLastDevice = isLastDevice,
|
||||
backupState = backupState,
|
||||
doesBackupExistOnServer = doesBackupExistOnServerAction.value.dataOrNull().orTrue(),
|
||||
|
||||
+2
@@ -12,8 +12,10 @@ import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
||||
data class LogoutState(
|
||||
val currentUser: MatrixUser,
|
||||
val isLastDevice: Boolean,
|
||||
val backupState: BackupState,
|
||||
val doesBackupExistOnServer: Boolean,
|
||||
|
||||
+5
@@ -14,6 +14,8 @@ import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.encryption.SteadyStateException
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||
|
||||
open class LogoutStatePreviewParam : PreviewParameterProvider<LogoutState> {
|
||||
override val values: Sequence<LogoutState>
|
||||
@@ -39,10 +41,12 @@ open class LogoutStatePreviewParam : PreviewParameterProvider<LogoutState> {
|
||||
backupUploadState = BackupUploadState.Waiting,
|
||||
waitingForALongTime = true,
|
||||
),
|
||||
aLogoutState(currentUser = aMatrixUser(id = "@alice:server.org", displayName = null)),
|
||||
)
|
||||
}
|
||||
|
||||
fun aLogoutState(
|
||||
currentUser: MatrixUser = aMatrixUser(),
|
||||
isLastDevice: Boolean = false,
|
||||
backupState: BackupState = BackupState.ENABLED,
|
||||
doesBackupExistOnServer: Boolean = true,
|
||||
@@ -52,6 +56,7 @@ fun aLogoutState(
|
||||
logoutAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
eventSink: (LogoutEvent) -> Unit = {},
|
||||
) = LogoutState(
|
||||
currentUser = currentUser,
|
||||
isLastDevice = isLastDevice,
|
||||
backupState = backupState,
|
||||
doesBackupExistOnServer = doesBackupExistOnServer,
|
||||
|
||||
+30
-3
@@ -11,7 +11,9 @@ package io.element.android.features.logout.impl
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -32,15 +34,22 @@ import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.LinearProgressIndicator
|
||||
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.components.TextButton
|
||||
import io.element.android.libraries.designsystem.theme.progressIndicatorTrackColor
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.encryption.SteadyStateException
|
||||
import io.element.android.libraries.matrix.ui.components.accountinfo.AccountInfoCard
|
||||
import io.element.android.libraries.matrix.ui.components.accountinfo.AccountInfoCardMode
|
||||
import io.element.android.libraries.matrix.ui.components.accountinfo.AccountInfoCardState
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
import io.element.android.libraries.testtags.testTag
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
/**
|
||||
* Ref: https://www.figma.com/design/kEAcfun9iSpszeUDvdKZ6b/ER-351--Multi-account-in-EX?node-id=442-34581
|
||||
*/
|
||||
@Composable
|
||||
fun LogoutView(
|
||||
state: LogoutState,
|
||||
@@ -54,15 +63,17 @@ fun LogoutView(
|
||||
onBackClick = onBackClick,
|
||||
title = title(state),
|
||||
subTitle = subtitle(state),
|
||||
iconStyle = BigIcon.Style.Default(CompoundIcons.KeySolid()),
|
||||
iconStyle = BigIcon.Style.Default(CompoundIcons.Mobile()),
|
||||
modifier = modifier,
|
||||
isScrollable = true,
|
||||
buttons = {
|
||||
Buttons(
|
||||
state = state,
|
||||
onChangeRecoveryKeyClick = onChangeRecoveryKeyClick,
|
||||
onLogoutClick = {
|
||||
eventSink(LogoutEvent.Logout(ignoreSdkError = false))
|
||||
}
|
||||
},
|
||||
onCancelClick = onBackClick,
|
||||
)
|
||||
},
|
||||
) {
|
||||
@@ -71,6 +82,8 @@ fun LogoutView(
|
||||
|
||||
LogoutActionDialog(
|
||||
state.logoutAction,
|
||||
// Do not show the progress dialog, the progress is rendered in the button.
|
||||
showProgressDialog = false,
|
||||
onConfirmClick = {
|
||||
eventSink(LogoutEvent.Logout(ignoreSdkError = false))
|
||||
},
|
||||
@@ -116,6 +129,7 @@ private fun ColumnScope.Buttons(
|
||||
state: LogoutState,
|
||||
onLogoutClick: () -> Unit,
|
||||
onChangeRecoveryKeyClick: () -> Unit,
|
||||
onCancelClick: () -> Unit,
|
||||
) {
|
||||
val logoutAction = state.logoutAction
|
||||
if (state.isLastDevice) {
|
||||
@@ -139,6 +153,12 @@ private fun ColumnScope.Buttons(
|
||||
.testTag(TestTags.signOut),
|
||||
onClick = onLogoutClick,
|
||||
)
|
||||
TextButton(
|
||||
text = stringResource(id = CommonStrings.action_cancel),
|
||||
enabled = logoutAction !is AsyncAction.Loading,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = onCancelClick,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -149,9 +169,16 @@ private fun Content(
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 60.dp, start = 20.dp, end = 20.dp),
|
||||
.padding(top = 8.dp, start = 16.dp, end = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
AccountInfoCard(
|
||||
AccountInfoCardState(
|
||||
matrixUser = state.currentUser,
|
||||
mode = AccountInfoCardMode.Simple,
|
||||
)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
when (state.backupUploadState) {
|
||||
is BackupUploadState.Uploading -> {
|
||||
LinearProgressIndicator(
|
||||
|
||||
+1
@@ -27,6 +27,7 @@ class DefaultDirectLogoutView : DirectLogoutView {
|
||||
val eventSink = state.eventSink
|
||||
LogoutActionDialog(
|
||||
state.logoutAction,
|
||||
showProgressDialog = true,
|
||||
onConfirmClick = {
|
||||
eventSink(DirectLogoutEvent.Logout(ignoreSdkError = false))
|
||||
},
|
||||
|
||||
-15
@@ -10,28 +10,22 @@ package io.element.android.features.logout.impl.direct
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutEvent
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.logout.impl.tools.isBackingUp
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.architecture.runCatchingUpdatingState
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Inject
|
||||
class DirectLogoutPresenter(
|
||||
private val matrixClient: MatrixClient,
|
||||
private val encryptionService: EncryptionService,
|
||||
) : Presenter<DirectLogoutState> {
|
||||
@Composable
|
||||
override fun present(): DirectLogoutState {
|
||||
@@ -41,13 +35,6 @@ class DirectLogoutPresenter(
|
||||
mutableStateOf(AsyncAction.Uninitialized)
|
||||
}
|
||||
|
||||
val backupUploadState: BackupUploadState by remember {
|
||||
encryptionService.waitForBackupUploadSteadyState()
|
||||
}
|
||||
.collectAsState(initial = BackupUploadState.Unknown)
|
||||
|
||||
val isLastDevice by encryptionService.isLastDevice.collectAsState()
|
||||
|
||||
fun handleEvent(event: DirectLogoutEvent) {
|
||||
when (event) {
|
||||
is DirectLogoutEvent.Logout -> {
|
||||
@@ -64,8 +51,6 @@ class DirectLogoutPresenter(
|
||||
}
|
||||
|
||||
return DirectLogoutState(
|
||||
canDoDirectSignOut = !isLastDevice &&
|
||||
!backupUploadState.isBackingUp(),
|
||||
logoutAction = logoutAction.value,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
|
||||
+6
-2
@@ -19,6 +19,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
|
||||
@Composable
|
||||
fun LogoutActionDialog(
|
||||
state: AsyncAction<Unit>,
|
||||
showProgressDialog: Boolean,
|
||||
onConfirmClick: () -> Unit,
|
||||
onForceLogoutClick: () -> Unit,
|
||||
onDismissDialog: () -> Unit,
|
||||
@@ -31,8 +32,11 @@ fun LogoutActionDialog(
|
||||
onSubmitClick = onConfirmClick,
|
||||
onDismiss = onDismissDialog
|
||||
)
|
||||
is AsyncAction.Loading ->
|
||||
ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content))
|
||||
is AsyncAction.Loading -> {
|
||||
if (showProgressDialog) {
|
||||
ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content))
|
||||
}
|
||||
}
|
||||
is AsyncAction.Failure ->
|
||||
RetryDialog(
|
||||
title = stringResource(id = CommonStrings.dialog_title_error),
|
||||
|
||||
+10
-1
@@ -20,7 +20,9 @@ import io.element.android.libraries.matrix.api.encryption.BackupState
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||
import io.element.android.libraries.workmanager.api.WorkManagerRequestType
|
||||
@@ -39,11 +41,18 @@ class LogoutPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - initial state`() = runTest {
|
||||
val presenter = createLogoutPresenter()
|
||||
val presenter = createLogoutPresenter(
|
||||
matrixClient = FakeMatrixClient(
|
||||
sessionId = A_SESSION_ID,
|
||||
userDisplayName = "Alice",
|
||||
userAvatarUrl = "mxc://avatar",
|
||||
),
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.currentUser).isEqualTo(MatrixUser(A_SESSION_ID, "Alice", "mxc://avatar"))
|
||||
assertThat(initialState.isLastDevice).isFalse()
|
||||
assertThat(initialState.backupState).isEqualTo(BackupState.UNKNOWN)
|
||||
assertThat(initialState.doesBackupExistOnServer).isTrue()
|
||||
|
||||
+32
-1
@@ -13,8 +13,11 @@ package io.element.android.features.logout.impl
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.test.AndroidComposeUiTest
|
||||
import androidx.compose.ui.test.ExperimentalTestApi
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
@@ -25,8 +28,22 @@ import io.element.android.tests.testutils.pressBack
|
||||
import io.element.android.tests.testutils.pressTag
|
||||
import io.element.android.tests.testutils.robolectric.RobolectricTest
|
||||
import org.junit.Test
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
class LogoutViewTest : RobolectricTest() {
|
||||
@Test
|
||||
@Config(qualifiers = "h1024dp")
|
||||
fun `the current user is displayed`() = runAndroidComposeUiTest {
|
||||
setLogoutView(
|
||||
aLogoutState(
|
||||
currentUser = aMatrixUser(id = "@alice:server.org", displayName = "Alice"),
|
||||
eventSink = EventsRecorder(expectEvents = false),
|
||||
),
|
||||
)
|
||||
onNodeWithText("Alice").assertIsDisplayed()
|
||||
onNodeWithText("@alice:server.org").assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on logout sends a LogoutEvent`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<LogoutEvent>()
|
||||
@@ -88,10 +105,24 @@ class LogoutViewTest : RobolectricTest() {
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
)
|
||||
clickOn(CommonStrings.action_cancel)
|
||||
clickOn(CommonStrings.action_cancel, inDialog = true)
|
||||
eventsRecorder.assertSingle(LogoutEvent.CloseDialogs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on cancel invoke back callback`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<LogoutEvent>(expectEvents = false)
|
||||
ensureCalledOnce { callback ->
|
||||
setLogoutView(
|
||||
aLogoutState(
|
||||
eventSink = eventsRecorder
|
||||
),
|
||||
onBackClick = callback,
|
||||
)
|
||||
clickOn(CommonStrings.action_cancel)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `last session setting button invoke onChangeRecoveryKeyClicked`() = runAndroidComposeUiTest {
|
||||
val eventsRecorder = EventsRecorder<LogoutEvent>(expectEvents = false)
|
||||
|
||||
-44
@@ -16,13 +16,9 @@ import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutEvent
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.test.AN_EXCEPTION
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -38,44 +34,6 @@ class DirectLogoutPresenterTest {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.canDoDirectSignOut).isTrue()
|
||||
assertThat(initialState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial state - last session`() = runTest {
|
||||
val presenter = createDirectLogoutPresenter(
|
||||
encryptionService = FakeEncryptionService().apply {
|
||||
emitIsLastDevice(true)
|
||||
}
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.canDoDirectSignOut).isFalse()
|
||||
assertThat(initialState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial state - backing up`() = runTest {
|
||||
val encryptionService = FakeEncryptionService()
|
||||
encryptionService.givenWaitForBackupUploadSteadyStateFlow(
|
||||
flow {
|
||||
emit(BackupUploadState.Waiting)
|
||||
}
|
||||
)
|
||||
val presenter = createDirectLogoutPresenter(
|
||||
encryptionService = encryptionService
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
val initialState = awaitFirstItem()
|
||||
assertThat(initialState.canDoDirectSignOut).isFalse()
|
||||
assertThat(initialState.logoutAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
}
|
||||
@@ -180,9 +138,7 @@ class DirectLogoutPresenterTest {
|
||||
|
||||
private fun createDirectLogoutPresenter(
|
||||
matrixClient: MatrixClient = FakeMatrixClient(),
|
||||
encryptionService: EncryptionService = FakeEncryptionService(),
|
||||
): DirectLogoutPresenter = DirectLogoutPresenter(
|
||||
matrixClient = matrixClient,
|
||||
encryptionService = encryptionService,
|
||||
)
|
||||
}
|
||||
|
||||
-5
@@ -17,7 +17,6 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.features.enterprise.api.SessionEnterpriseService
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
@@ -34,7 +33,6 @@ class PreferencesAccountPresenter(
|
||||
private val sessionVerificationService: SessionVerificationService,
|
||||
private val snackbarDispatcher: SnackbarDispatcher,
|
||||
private val indicatorService: IndicatorService,
|
||||
private val directLogoutPresenter: Presenter<DirectLogoutState>,
|
||||
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
private val sessionEnterpriseService: SessionEnterpriseService,
|
||||
@@ -65,8 +63,6 @@ class PreferencesAccountPresenter(
|
||||
canDeactivateAccount = matrixClient.canDeactivateAccount()
|
||||
}
|
||||
|
||||
val directLogoutState = directLogoutPresenter.present()
|
||||
|
||||
val accountManagementUrl by produceState<String?>(initialValue = null) {
|
||||
value = matrixClient.getAccountManagementUrl(null)
|
||||
.getOrNull()
|
||||
@@ -83,7 +79,6 @@ class PreferencesAccountPresenter(
|
||||
canReportBug = canReportBug,
|
||||
showLinkNewDevice = showLinkNewDevice,
|
||||
canDeactivateAccount = canDeactivateAccount,
|
||||
directLogoutState = directLogoutState,
|
||||
snackbarMessage = snackbarMessage,
|
||||
)
|
||||
}
|
||||
|
||||
-2
@@ -7,7 +7,6 @@
|
||||
|
||||
package io.element.android.features.preferences.impl.account
|
||||
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
||||
@@ -19,6 +18,5 @@ data class PreferencesAccountState(
|
||||
val canReportBug: Boolean,
|
||||
val showLinkNewDevice: Boolean,
|
||||
val canDeactivateAccount: Boolean,
|
||||
val directLogoutState: DirectLogoutState,
|
||||
val snackbarMessage: SnackbarMessage?,
|
||||
)
|
||||
|
||||
-4
@@ -8,8 +8,6 @@
|
||||
package io.element.android.features.preferences.impl.account
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
import io.element.android.features.logout.api.direct.aDirectLogoutState
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||
@@ -56,7 +54,6 @@ fun aPreferencesAccountState(
|
||||
canReportBug: Boolean = false,
|
||||
showLinkNewDevice: Boolean = false,
|
||||
canDeactivateAccount: Boolean = false,
|
||||
directLogoutState: DirectLogoutState = aDirectLogoutState(),
|
||||
snackbarMessage: SnackbarMessage? = null,
|
||||
) = PreferencesAccountState(
|
||||
myUser = myUser,
|
||||
@@ -66,6 +63,5 @@ fun aPreferencesAccountState(
|
||||
canReportBug = canReportBug,
|
||||
showLinkNewDevice = showLinkNewDevice,
|
||||
canDeactivateAccount = canDeactivateAccount,
|
||||
directLogoutState = directLogoutState,
|
||||
snackbarMessage = snackbarMessage,
|
||||
)
|
||||
|
||||
+1
-12
@@ -19,8 +19,6 @@ import dev.zacsweers.metro.Assisted
|
||||
import dev.zacsweers.metro.AssistedInject
|
||||
import io.element.android.annotations.ContributesNode
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutEvent
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutView
|
||||
import io.element.android.features.preferences.impl.account.PreferencesAccountCallback
|
||||
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
|
||||
import io.element.android.libraries.architecture.callback
|
||||
@@ -34,7 +32,6 @@ class PreferencesRootNode(
|
||||
@Assisted plugins: List<Plugin>,
|
||||
private val presenter: PreferencesRootPresenter,
|
||||
private val emojiPickerRenderer: EmojiPickerRenderer,
|
||||
private val directLogoutView: DirectLogoutView,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
interface Callback : PreferencesAccountCallback, Plugin {
|
||||
fun navigateToAddAccount()
|
||||
@@ -89,16 +86,8 @@ class PreferencesRootNode(
|
||||
onManageAccountClick = { onManageAccountClick(activity, it, isDark) },
|
||||
onOpenNotificationSettings = callback::navigateToNotificationSettings,
|
||||
onEditProfileClick = callback::navigateToUserProfile,
|
||||
onSignOutClick = {
|
||||
if (state.preferencesAccountState.directLogoutState.canDoDirectSignOut) {
|
||||
state.preferencesAccountState.directLogoutState.eventSink(DirectLogoutEvent.Logout(ignoreSdkError = false))
|
||||
} else {
|
||||
callback.startSignOutFlow()
|
||||
}
|
||||
},
|
||||
onSignOutClick = callback::startSignOutFlow,
|
||||
onDeactivateClick = callback::startAccountDeactivationFlow
|
||||
)
|
||||
|
||||
directLogoutView.Render(state = state.preferencesAccountState.directLogoutState)
|
||||
}
|
||||
}
|
||||
|
||||
-3
@@ -10,7 +10,6 @@ package io.element.android.features.preferences.impl.account
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.enterprise.api.SessionEnterpriseService
|
||||
import io.element.android.features.enterprise.test.FakeSessionEnterpriseService
|
||||
import io.element.android.features.logout.api.direct.aDirectLogoutState
|
||||
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
@@ -65,7 +64,6 @@ class PreferencesAccountPresenterTest {
|
||||
assertThat(initialState.showSecureBackupBadge).isFalse()
|
||||
assertThat(initialState.accountManagementUrl).isNull()
|
||||
assertThat(initialState.showLinkNewDevice).isFalse()
|
||||
assertThat(initialState.directLogoutState).isEqualTo(aDirectLogoutState())
|
||||
assertThat(initialState.snackbarMessage).isNull()
|
||||
val loadedState = consumeItemsUntilPredicate { it.accountManagementUrl != null }.last()
|
||||
assertThat(loadedState.showSecureBackup).isFalse()
|
||||
@@ -169,7 +167,6 @@ class PreferencesAccountPresenterTest {
|
||||
sessionVerificationService = sessionVerificationService,
|
||||
snackbarDispatcher = SnackbarDispatcher(),
|
||||
indicatorService = indicatorService,
|
||||
directLogoutPresenter = { aDirectLogoutState() },
|
||||
rageshakeFeatureAvailability = rageshakeFeatureAvailability,
|
||||
featureFlagService = featureFlagService,
|
||||
sessionEnterpriseService = sessionEnterpriseService,
|
||||
|
||||
Reference in New Issue
Block a user