diff --git a/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt index f271927e1..a16fa182c 100644 --- a/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt +++ b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt @@ -214,7 +214,11 @@ class SignInViewModel( interactor.loginSocial(token, authType) }.onFailure { error -> logger.e { "Social login error: $error" } - onUnknownError() + if (error is EdxError.InvalidGrantException) { + onInvalidGrantError(authType) + } else { + onUnknownError() + } }.onSuccess { logger.d { "Social login (${authType.methodName}) success" } _uiState.update { it.copy(loginSuccess = true) } @@ -224,6 +228,32 @@ class SignInViewModel( } } + private fun onInvalidGrantError(authType: AuthType? = null, message: (() -> String)? = null) { + message?.let { + logger.e { it() } + } + val providerName = authType?.methodName ?: "Social" + val platformName = resourceManager.getString(CoreRes.string.app_name) + + _uiMessage.value = UIMessage.SnackBarMessage( + resourceManager.getString( + CoreRes.string.core_error_no_linked_account_error, + providerName, + platformName + ) + ) + _uiState.update { it.copy(showProgress = false) } + } + + private fun onSignInCancelled() { + _uiMessage.value = UIMessage.SnackBarMessage( + resourceManager.getString( + CoreRes.string.core_sign_in_canceled + ) + ) + _uiState.update { it.copy(showProgress = false) } + } + private fun onUnknownError(message: (() -> String)? = null) { message?.let { logger.e { it() } @@ -247,7 +277,7 @@ class SignInViewModel( } else { _uiState.update { it.copy(showProgress = false) } } - } ?: onUnknownError() + } ?: onSignInCancelled() } fun openLink(fragmentManager: FragmentManager, links: Map, link: String) { diff --git a/auth/src/main/java/org/openedx/auth/presentation/signin/compose/SignInView.kt b/auth/src/main/java/org/openedx/auth/presentation/signin/compose/SignInView.kt index 439ff2af5..c49292893 100644 --- a/auth/src/main/java/org/openedx/auth/presentation/signin/compose/SignInView.kt +++ b/auth/src/main/java/org/openedx/auth/presentation/signin/compose/SignInView.kt @@ -24,10 +24,8 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.CircularProgressIndicator -import androidx.compose.material.Divider import androidx.compose.material.MaterialTheme import androidx.compose.material.OutlinedTextField -import androidx.compose.material.Scaffold import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.material.TextFieldDefaults @@ -56,7 +54,6 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp @@ -65,10 +62,12 @@ import org.openedx.auth.R import org.openedx.auth.presentation.signin.AuthEvent import org.openedx.auth.presentation.signin.SignInUIState import org.openedx.auth.presentation.ui.LoginTextField +import org.openedx.auth.presentation.ui.OrDivider import org.openedx.auth.presentation.ui.PasswordVisibilityIcon import org.openedx.auth.presentation.ui.SocialAuthView import org.openedx.core.extension.TextConverter import org.openedx.core.ui.BackBtn +import org.openedx.core.ui.CustomScaffold import org.openedx.core.ui.HandleUIMessage import org.openedx.core.ui.HyperlinkText import org.openedx.core.ui.OpenEdXButton @@ -96,7 +95,7 @@ internal fun LoginScreen( val scaffoldState = rememberScaffoldState() val scrollState = rememberScrollState() - Scaffold( + CustomScaffold( scaffoldState = scaffoldState, modifier = Modifier .semantics { @@ -493,40 +492,3 @@ private fun SignInScreenTabletPreview() { ) } } - -@Composable -fun OrDivider( - modifier: Modifier = Modifier, - text: String, - lineColor: Color = MaterialTheme.appColors.textPrimary, - textStyle: TextStyle = MaterialTheme.appTypography.labelLarge, - textColor: Color = MaterialTheme.appColors.textPrimary, - lineThickness: Dp = 0.8.dp, - padding: PaddingValues = PaddingValues(top = 24.dp) -) { - Row( - modifier = modifier - .fillMaxWidth() - .padding(padding), - verticalAlignment = Alignment.CenterVertically - ) { - Divider( - modifier = Modifier - .weight(1f) - .height(lineThickness), - color = lineColor - ) - Text( - text = text, - modifier = Modifier.padding(horizontal = 8.dp), - style = textStyle, - color = textColor - ) - Divider( - modifier = Modifier - .weight(1f) - .height(lineThickness), - color = lineColor - ) - } -} diff --git a/auth/src/main/java/org/openedx/auth/presentation/signup/compose/SignUpView.kt b/auth/src/main/java/org/openedx/auth/presentation/signup/compose/SignUpView.kt index 930f3345a..c727c5e18 100644 --- a/auth/src/main/java/org/openedx/auth/presentation/signup/compose/SignUpView.kt +++ b/auth/src/main/java/org/openedx/auth/presentation/signup/compose/SignUpView.kt @@ -25,7 +25,6 @@ import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.MaterialTheme import androidx.compose.material.ModalBottomSheetLayout import androidx.compose.material.ModalBottomSheetValue -import androidx.compose.material.Scaffold import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.material.rememberModalBottomSheetState @@ -63,15 +62,16 @@ import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import org.openedx.auth.R import org.openedx.auth.data.model.AuthType -import org.openedx.auth.presentation.signin.compose.OrDivider import org.openedx.auth.presentation.signup.SignUpUIState import org.openedx.auth.presentation.ui.ExpandableText import org.openedx.auth.presentation.ui.OptionalFields +import org.openedx.auth.presentation.ui.OrDivider import org.openedx.auth.presentation.ui.RequiredFields import org.openedx.auth.presentation.ui.SocialAuthView import org.openedx.core.domain.model.RegistrationField import org.openedx.core.domain.model.RegistrationFieldType import org.openedx.core.ui.BackBtn +import org.openedx.core.ui.CustomScaffold import org.openedx.core.ui.HandleUIMessage import org.openedx.core.ui.OpenEdXButton import org.openedx.core.ui.SheetContent @@ -165,7 +165,7 @@ internal fun SignUpView( } } - Scaffold( + CustomScaffold( scaffoldState = scaffoldState, modifier = Modifier .semantics { @@ -348,6 +348,22 @@ internal fun SignUpView( ) } } + if (uiState.isSocialAuthEnabled && uiState.socialAuth == null) { + SocialAuthView( + modifier = buttonWidth, + isGoogleAuthEnabled = uiState.isGoogleAuthEnabled, + isFacebookAuthEnabled = uiState.isFacebookAuthEnabled, + isMicrosoftAuthEnabled = uiState.isMicrosoftAuthEnabled, + isSignIn = false, + ) { + keyboardController?.hide() + onRegisterClick(it) + } + OrDivider(text=stringResource( + id = R.string.auth_normal_or_sso), + padding = PaddingValues(top = 0.dp) + ) + } RequiredFields( fields = uiState.requiredFields, showErrorMap = showErrorMap, @@ -448,22 +464,6 @@ internal fun SignUpView( } ) } - if (uiState.isSocialAuthEnabled && uiState.socialAuth == null) { - OrDivider(text=stringResource( - id = R.string.auth_normal_or_sso), - padding = PaddingValues(top = 0.dp) - ) - SocialAuthView( - modifier = buttonWidth, - isGoogleAuthEnabled = uiState.isGoogleAuthEnabled, - isFacebookAuthEnabled = uiState.isFacebookAuthEnabled, - isMicrosoftAuthEnabled = uiState.isMicrosoftAuthEnabled, - isSignIn = false, - ) { - keyboardController?.hide() - onRegisterClick(it) - } - } Spacer(Modifier.height(70.dp)) } } diff --git a/auth/src/main/java/org/openedx/auth/presentation/ui/AuthUI.kt b/auth/src/main/java/org/openedx/auth/presentation/ui/AuthUI.kt index 489da85b0..d7586c76b 100644 --- a/auth/src/main/java/org/openedx/auth/presentation/ui/AuthUI.kt +++ b/auth/src/main/java/org/openedx/auth/presentation/ui/AuthUI.kt @@ -8,6 +8,7 @@ import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -16,6 +17,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.Divider import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme @@ -34,15 +36,18 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.SnapshotStateMap +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.focus.FocusManager import androidx.compose.ui.focus.focusTarget import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation @@ -50,6 +55,7 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import org.openedx.auth.R import org.openedx.core.domain.model.RegistrationField @@ -570,6 +576,43 @@ internal fun PasswordVisibilityIcon( } } +@Composable +fun OrDivider( + modifier: Modifier = Modifier, + text: String, + lineColor: Color = MaterialTheme.appColors.textPrimary, + textStyle: TextStyle = MaterialTheme.appTypography.labelLarge, + textColor: Color = MaterialTheme.appColors.textPrimary, + lineThickness: Dp = 0.8.dp, + padding: PaddingValues = PaddingValues(top = 24.dp) +) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(padding), + verticalAlignment = Alignment.CenterVertically + ) { + Divider( + modifier = Modifier + .weight(1f) + .height(lineThickness), + color = lineColor + ) + Text( + text = text, + modifier = Modifier.padding(horizontal = 8.dp), + style = textStyle, + color = textColor + ) + Divider( + modifier = Modifier + .weight(1f) + .height(lineThickness), + color = lineColor + ) + } +} + @Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable @@ -654,6 +697,24 @@ private fun SheetContentPreview() { } } +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +fun OrDividerPreview() { + OpenEdXTheme { + Column( + modifier = Modifier + .fillMaxWidth() + .background(MaterialTheme.appColors.background) + .padding(16.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + OrDivider(text = "OR") + } + } +} + private val option = RegistrationField.Option("def", "Bachelor", "Android") private val field = RegistrationField( diff --git a/core/src/main/java/org/openedx/core/ui/ComposeCommon.kt b/core/src/main/java/org/openedx/core/ui/ComposeCommon.kt index 91c8a9f18..e5f4ba934 100644 --- a/core/src/main/java/org/openedx/core/ui/ComposeCommon.kt +++ b/core/src/main/java/org/openedx/core/ui/ComposeCommon.kt @@ -49,6 +49,8 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.OutlinedButton import androidx.compose.material.OutlinedTextField import androidx.compose.material.ScaffoldState +import androidx.compose.material.Snackbar +import androidx.compose.material.SnackbarHost import androidx.compose.material.Text import androidx.compose.material.TextFieldDefaults import androidx.compose.material.icons.Icons @@ -1405,6 +1407,30 @@ private fun RoundTab( } } +@Composable +fun CustomScaffold( + scaffoldState: ScaffoldState, + modifier: Modifier = Modifier, + backgroundColor: Color = MaterialTheme.appColors.background, + content: @Composable (PaddingValues) -> Unit +) { + androidx.compose.material.Scaffold( + modifier = modifier.fillMaxSize(), + scaffoldState = scaffoldState, + backgroundColor = backgroundColor, + snackbarHost = { hostState -> + SnackbarHost(hostState = hostState) { data -> + Snackbar( + snackbarData = data, + backgroundColor = MaterialTheme.appColors.error, + contentColor = Color.White, + ) + } + }, + content = content + ) +} + @Preview @Composable private fun StaticSearchBarPreview() { diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index c8d529afa..a82cd7da5 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -6,6 +6,10 @@ Invalid credentials Slow or no internet connection Something went wrong + + This %1$s account is not linked with any %2$s account. Please register. + + The user canceled the sign-in flow. Try again Privacy Policy Cookie policy diff --git a/default_config/dev/config.yaml b/default_config/dev/config.yaml index 537df7857..ea2800f44 100644 --- a/default_config/dev/config.yaml +++ b/default_config/dev/config.yaml @@ -76,7 +76,7 @@ WHATS_NEW_ENABLED: false #feature flag enable Social Login buttons SOCIAL_AUTH_ENABLED: false #Enables the pre login courses discovery experience -PRE_LOGIN_EXPERIENCE_ENABLED: false +PRE_LOGIN_EXPERIENCE_ENABLED: true #feature flag to enable registration from app REGISTRATION_ENABLED: true #feature flag to do the authentication flow in the browser to log in diff --git a/default_config/prod/config.yaml b/default_config/prod/config.yaml index 537df7857..ea2800f44 100644 --- a/default_config/prod/config.yaml +++ b/default_config/prod/config.yaml @@ -76,7 +76,7 @@ WHATS_NEW_ENABLED: false #feature flag enable Social Login buttons SOCIAL_AUTH_ENABLED: false #Enables the pre login courses discovery experience -PRE_LOGIN_EXPERIENCE_ENABLED: false +PRE_LOGIN_EXPERIENCE_ENABLED: true #feature flag to enable registration from app REGISTRATION_ENABLED: true #feature flag to do the authentication flow in the browser to log in diff --git a/default_config/stage/config.yaml b/default_config/stage/config.yaml index 537df7857..ea2800f44 100644 --- a/default_config/stage/config.yaml +++ b/default_config/stage/config.yaml @@ -76,7 +76,7 @@ WHATS_NEW_ENABLED: false #feature flag enable Social Login buttons SOCIAL_AUTH_ENABLED: false #Enables the pre login courses discovery experience -PRE_LOGIN_EXPERIENCE_ENABLED: false +PRE_LOGIN_EXPERIENCE_ENABLED: true #feature flag to enable registration from app REGISTRATION_ENABLED: true #feature flag to do the authentication flow in the browser to log in