From cfbd0c4c02951b2a552fcf583d52287d35b2ba22 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Aug 2024 12:16:38 +0200 Subject: [PATCH] Ensure success logout url is opened in all cases. --- .../lockscreen/impl/unlock/PinUnlockNode.kt | 5 +++ .../lockscreen/impl/unlock/PinUnlockView.kt | 21 +++++++++++-- .../impl/unlock/activity/PinUnlockActivity.kt | 7 ++++- features/logout/api/build.gradle.kts | 1 + .../android/features/logout/api/util/Util.kt | 31 +++++++++++++++++++ .../features/logout/impl/LogoutNode.kt | 10 +----- .../impl/root/PreferencesRootNode.kt | 9 +----- 7 files changed, 64 insertions(+), 20 deletions(-) create mode 100644 features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt index f357869375..bb0c86d5a2 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt @@ -16,9 +16,11 @@ package io.element.android.features.lockscreen.impl.unlock +import android.app.Activity import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -26,6 +28,7 @@ import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -47,6 +50,7 @@ class PinUnlockNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { val state = presenter.present() + val activity = LocalContext.current as Activity LaunchedEffect(state.isUnlocked) { if (state.isUnlocked) { onUnlock() @@ -57,6 +61,7 @@ class PinUnlockNode @AssistedInject constructor( // UnlockNode is only used for in-app unlock, so we can safely set isInAppUnlock to true. // It's set to false in PinUnlockActivity. isInAppUnlock = true, + onSuccessLogout = { onSuccessLogout(activity, it) }, modifier = modifier ) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt index 89d313df9b..494135e6c2 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockView.kt @@ -39,7 +39,9 @@ import androidx.compose.material.icons.filled.Lock import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester @@ -75,6 +77,7 @@ import io.element.android.libraries.ui.strings.CommonStrings fun PinUnlockView( state: PinUnlockState, isInAppUnlock: Boolean, + onSuccessLogout: (logoutUrlResult: String?) -> Unit, modifier: Modifier = Modifier, ) { OnLifecycleEvent { _, event -> @@ -92,9 +95,21 @@ fun PinUnlockView( onDismiss = { state.eventSink(PinUnlockEvents.ClearSignOutPrompt) }, ) } - if (state.signOutAction == AsyncAction.Loading) { - ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content)) + when (state.signOutAction) { + AsyncAction.Loading -> { + ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content)) + } + is AsyncAction.Success -> { + val latestOnSuccessLogout by rememberUpdatedState(onSuccessLogout) + LaunchedEffect(state) { + latestOnSuccessLogout(state.signOutAction.data) + } + } + AsyncAction.Confirming, + is AsyncAction.Failure, + AsyncAction.Uninitialized -> Unit } + if (state.showBiometricUnlockError) { ErrorDialog( content = state.biometricUnlockErrorMessage ?: "", @@ -364,6 +379,7 @@ internal fun PinUnlockViewInAppPreview(@PreviewParameter(PinUnlockStateProvider: PinUnlockView( state = state, isInAppUnlock = true, + onSuccessLogout = {}, ) } } @@ -375,6 +391,7 @@ internal fun PinUnlockViewPreview(@PreviewParameter(PinUnlockStateProvider::clas PinUnlockView( state = state, isInAppUnlock = false, + onSuccessLogout = {}, ) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt index 7b7b16790f..cbb05909a1 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt @@ -29,6 +29,7 @@ import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.lockscreen.impl.unlock.PinUnlockPresenter import io.element.android.features.lockscreen.impl.unlock.PinUnlockView import io.element.android.features.lockscreen.impl.unlock.di.PinUnlockBindings +import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.architecture.bindings import io.element.android.libraries.designsystem.theme.ElementThemeApp import io.element.android.libraries.preferences.api.store.AppPreferencesStore @@ -53,7 +54,11 @@ class PinUnlockActivity : AppCompatActivity() { setContent { ElementThemeApp(appPreferencesStore) { val state = presenter.present() - PinUnlockView(state = state, isInAppUnlock = false) + PinUnlockView( + state = state, + isInAppUnlock = false, + onSuccessLogout = { onSuccessLogout(this, it) }, + ) } } lifecycleScope.launch { diff --git a/features/logout/api/build.gradle.kts b/features/logout/api/build.gradle.kts index 85532f5617..655ab29ee5 100644 --- a/features/logout/api/build.gradle.kts +++ b/features/logout/api/build.gradle.kts @@ -22,6 +22,7 @@ android { } dependencies { + implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt new file mode 100644 index 0000000000..6b59c2d0ef --- /dev/null +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/util/Util.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.logout.api.util + +import android.app.Activity +import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab +import timber.log.Timber + +fun onSuccessLogout( + activity: Activity, + url: String?, +) { + Timber.d("Success logout with result url: $url") + url?.let { + activity.openUrlInChromeCustomTab(null, false, it) + } +} diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt index 4f5bab10b9..c4f1fd9e71 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt @@ -28,9 +28,8 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.logout.api.LogoutEntryPoint -import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab +import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.di.SessionScope -import timber.log.Timber @ContributesNode(SessionScope::class) class LogoutNode @AssistedInject constructor( @@ -42,13 +41,6 @@ class LogoutNode @AssistedInject constructor( plugins().forEach { it.onChangeRecoveryKeyClick() } } - private fun onSuccessLogout(activity: Activity, url: String?) { - Timber.d("Success logout with result url: $url") - url?.let { - activity.openUrlInChromeCustomTab(null, false, it) - } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt index 106f07ecc2..16642029e4 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt @@ -30,10 +30,10 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.compound.theme.ElementTheme import io.element.android.features.logout.api.direct.DirectLogoutEvents import io.element.android.features.logout.api.direct.DirectLogoutView +import io.element.android.features.logout.api.util.onSuccessLogout import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.user.MatrixUser -import timber.log.Timber @ContributesNode(SessionScope::class) class PreferencesRootNode @AssistedInject constructor( @@ -94,13 +94,6 @@ class PreferencesRootNode @AssistedInject constructor( } } - private fun onSuccessLogout(activity: Activity, url: String?) { - Timber.d("Success (direct) logout with result url: $url") - url?.let { - activity.openUrlInChromeCustomTab(null, false, it) - } - } - private fun onOpenNotificationSettings() { plugins().forEach { it.onOpenNotificationSettings() } }