Skip to content

Commit

Permalink
Merge pull request #13335 from woocommerce/issue/13321-magic-link-sus…
Browse files Browse the repository at this point in the history
…picious-emails

[Login] Show magic link screen for suspicious emails
  • Loading branch information
hichamboushaba authored Jan 21, 2025
2 parents c15c17d + d09a31e commit 4478798
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 80 deletions.
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [**] Enhanced WebView to dynamically scale receipt content, ensuring optimal fit across all device screen sizes. [https://github.com/woocommerce/woocommerce-android/pull/13266]
- [*] Fixes missing text in Blaze Campaigns card on larger display and font sizes [https://github.com/woocommerce/woocommerce-android/pull/13300]
- [*] Puerto Rico is now available in the list of countries that are supported by in-person payments [https://github.com/woocommerce/woocommerce-android/pull/13200]
- [*] [Internal] Improved login flow for accounts using emails marked as suspicious [https://github.com/woocommerce/woocommerce-android/pull/13345]
- [Internal] [*] XMLRPC is not needed now for Jetpack Connection during an Account Mismatch flow [https://github.com/woocommerce/woocommerce-android/pull/13308]
- [*] Removed the outdated feedback survey for shipping labels [https://github.com/woocommerce/woocommerce-android/pull/13319]
- [*] Bulk update order status now shows improved result messages, including for partial success. [https://github.com/woocommerce/woocommerce-android/pull/13275]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import com.woocommerce.android.R
import com.woocommerce.android.e2e.helpers.util.Screen

class MagicLinkScreen : Screen {
constructor() : super(R.id.login_enter_password)
constructor() : super(R.id.login_magic_link_fallback_button)

fun proceedWithPassword(): PasswordScreen {
clickOn(R.id.login_enter_password)
clickOn(R.id.login_magic_link_fallback_button)
return PasswordScreen()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import org.wordpress.android.login.LoginMagicLinkRequestFragment
import org.wordpress.android.login.LoginMode
import org.wordpress.android.login.LoginSiteAddressFragment
import org.wordpress.android.login.LoginUsernamePasswordFragment
import org.wordpress.android.login.MagicLinkFallbackButton
import org.wordpress.android.util.ToastUtils
import javax.inject.Inject
import kotlin.text.RegexOption.IGNORE_CASE
Expand Down Expand Up @@ -354,28 +355,33 @@ class LoginActivity :
val forcePasswordLogin = BuildConfig.DEBUG && BuildConfig.FORCE_PASSWORD_LOGIN

if (authOptions.isPasswordless && !forcePasswordLogin) {
showMagicLinkRequestScreen(email, verifyEmail, allowPassword = false, forceRequestAtStart = true)
showMagicLinkRequestScreen(
email = email,
verifyEmail = verifyEmail,
fallbackButton = MagicLinkFallbackButton.None,
forceRequestAtStart = true
)
} else {
showEmailPasswordScreen(email, verifyEmail)
}
} else {
if (isMagicLinkEnabled) {
showMagicLinkRequestScreen(email, verifyEmail, allowPassword = true, forceRequestAtStart = false)
showMagicLinkRequestScreen(
email = email,
verifyEmail = verifyEmail,
fallbackButton = MagicLinkFallbackButton.Password,
forceRequestAtStart = false
)
} else {
showEmailPasswordScreen(email, verifyEmail)
}
}
}

private fun showEmailPasswordScreen(
email: String?,
verifyEmail: Boolean,
password: String? = null
) {
private fun showEmailPasswordScreen(email: String?, verifyEmail: Boolean) {
val wooLoginEmailPasswordFragment = WooLoginEmailPasswordFragment
.newInstance(
emailAddress = email,
password = password,
verifyMagicLinkEmail = verifyEmail
)
changeFragment(wooLoginEmailPasswordFragment, true, LoginEmailPasswordFragment.TAG)
Expand All @@ -384,7 +390,7 @@ class LoginActivity :
private fun showMagicLinkRequestScreen(
email: String?,
verifyEmail: Boolean,
allowPassword: Boolean,
fallbackButton: MagicLinkFallbackButton,
forceRequestAtStart: Boolean
) {
val scheme = WOOCOMMERCE
Expand All @@ -395,7 +401,7 @@ class LoginActivity :
false,
null,
verifyEmail,
allowPassword,
fallbackButton,
forceRequestAtStart
)
changeFragment(loginMagicLinkRequestFragment, true, LoginMagicLinkRequestFragment.TAG, false)
Expand Down Expand Up @@ -453,8 +459,8 @@ class LoginActivity :
changeFragment(loginUsernamePasswordFragment, true, LoginUsernamePasswordFragment.TAG)
}

override fun showMagicLinkSentScreen(email: String?, allowPassword: Boolean) {
val loginMagicLinkSentFragment = LoginMagicLinkSentImprovedFragment.newInstance(email, allowPassword)
override fun showMagicLinkSentScreen(email: String?, fallbackButton: MagicLinkFallbackButton) {
val loginMagicLinkSentFragment = LoginMagicLinkSentImprovedFragment.newInstance(email, fallbackButton)
changeFragment(loginMagicLinkSentFragment, true, LoginMagicLinkSentImprovedFragment.TAG, false)
}

Expand Down Expand Up @@ -904,9 +910,17 @@ class LoginActivity :
TODO("Not yet implemented")
}

override fun useMagicLinkInstead(email: String?, verifyEmail: Boolean) {
showMagicLinkRequestScreen(email, verifyEmail, allowPassword = false, forceRequestAtStart = true)
}
override fun useMagicLinkInstead(
email: String?,
verifyEmail: Boolean,
requestAtStart: Boolean,
fallbackButton: MagicLinkFallbackButton
) = showMagicLinkRequestScreen(
email = email,
verifyEmail = verifyEmail,
fallbackButton = fallbackButton,
forceRequestAtStart = requestAtStart
)

/**
* Allows for special handling of errors that come up during the login by address: check site address.
Expand Down Expand Up @@ -972,7 +986,7 @@ class LoginActivity :
stat = AnalyticsEvent.LOGIN_APP_LOGIN_LINK_SUCCESS,
properties = mapOf(KEY_FLOW to VALUE_WP_COM)
)
showEmailPasswordScreen(email = wpComEmail, verifyEmail = false, password = null)
showEmailPasswordScreen(email = wpComEmail, verifyEmail = false)
}

siteUrl.isNotEmpty() && username.isNotEmpty() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import org.wordpress.android.fluxc.store.AccountStore
import org.wordpress.android.fluxc.store.SiteStore
import org.wordpress.android.login.LoginAnalyticsListener
import org.wordpress.android.login.LoginAnalyticsListener.CreatedAccountSource
import java.util.HashMap
import javax.inject.Singleton

@Singleton
Expand Down Expand Up @@ -272,6 +271,10 @@ class LoginAnalyticsTracker(
unifiedLoginTracker.trackClick(Click.LOGIN_WITH_PASSWORD)
}

override fun trackLoginWithWpComUsernamePasswordClick() {
unifiedLoginTracker.trackClick(Click.LOGIN_WITH_WP_COM_USERNAME_PASSWORD)
}

override fun trackShowHelpClick() {
unifiedLoginTracker.trackClick(Click.SHOW_HELP)
unifiedLoginTracker.track(step = Step.HELP)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,46 @@ import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.view.MenuProvider
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import com.woocommerce.android.R
import com.woocommerce.android.databinding.FragmentLoginMagicLinkSentImprovedBinding
import com.woocommerce.android.extensions.serializable
import dagger.hilt.android.AndroidEntryPoint
import org.wordpress.android.login.LoginAnalyticsListener
import org.wordpress.android.login.LoginListener
import org.wordpress.android.login.MagicLinkFallbackButton
import javax.inject.Inject

@AndroidEntryPoint
class LoginMagicLinkSentImprovedFragment : Fragment(R.layout.fragment_login_magic_link_sent_improved), MenuProvider {
companion object {
const val TAG = "login_magic_link_sent_fragment_tag"
private const val ARG_EMAIL_ADDRESS = "ARG_EMAIL_ADDRESS"
private const val ARG_ALLOW_PASSWORD = "ARG_ALLOW_PASSWORD"
private const val ARG_FALLBACK_BUTTON: String = "ARG_FALLBACK_BUTTON"

fun newInstance(email: String?, allowPassword: Boolean = true): LoginMagicLinkSentImprovedFragment {
fun newInstance(email: String?, fallbackButton: MagicLinkFallbackButton): LoginMagicLinkSentImprovedFragment {
val fragment = LoginMagicLinkSentImprovedFragment()
val args = Bundle()
args.putString(ARG_EMAIL_ADDRESS, email)
args.putBoolean(ARG_ALLOW_PASSWORD, allowPassword)
args.putSerializable(ARG_FALLBACK_BUTTON, fallbackButton)
fragment.arguments = args
return fragment
}
}

private var mLoginListener: LoginListener? = null
private var mEmail: String? = null
private var mAllowPassword = false
private var loginListener: LoginListener? = null
private var email: String? = null
private var fallbackButton: MagicLinkFallbackButton = MagicLinkFallbackButton.None

@Inject lateinit var mAnalyticsListener: LoginAnalyticsListener

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = arguments
if (args != null) {
mEmail = args.getString(ARG_EMAIL_ADDRESS)
mAllowPassword = args.getBoolean(ARG_ALLOW_PASSWORD)
email = args.getString(ARG_EMAIL_ADDRESS)
fallbackButton = args.serializable(ARG_FALLBACK_BUTTON) ?: MagicLinkFallbackButton.None
}
savedInstanceState?.let {
mAnalyticsListener.trackLoginMagicLinkOpenEmailClientViewed()
Expand All @@ -55,7 +58,7 @@ class LoginMagicLinkSentImprovedFragment : Fragment(R.layout.fragment_login_magi
override fun onAttach(context: Context) {
super.onAttach(context)
if (activity is LoginListener) {
mLoginListener = activity as LoginListener
loginListener = activity as LoginListener
}
}

Expand All @@ -68,15 +71,29 @@ class LoginMagicLinkSentImprovedFragment : Fragment(R.layout.fragment_login_magi

requireActivity().addMenuProvider(this, viewLifecycleOwner)

binding.loginOpenEmailClient.setOnClickListener { mLoginListener?.openEmailClient(true) }
with(binding.loginEnterPassword) {
visibility = if (mAllowPassword) View.VISIBLE else View.GONE
binding.loginOpenEmailClient.setOnClickListener { loginListener?.openEmailClient(true) }
with(binding.loginMagicLinkFallbackButton) {
text = if (fallbackButton == MagicLinkFallbackButton.Password) {
getString(R.string.or_use_password_below_qr_code_scan_option)
} else {
getString(R.string.login_use_wpcom_username_instead)
}
isVisible = fallbackButton != MagicLinkFallbackButton.None
setOnClickListener {
mAnalyticsListener.trackLoginWithPasswordClick()
mLoginListener?.usePasswordInstead(mEmail)
when (fallbackButton) {
MagicLinkFallbackButton.Password -> {
mAnalyticsListener.trackLoginWithPasswordClick()
loginListener?.usePasswordInstead(email)
}
MagicLinkFallbackButton.UsernameAndPassword -> {
mAnalyticsListener.trackLoginWithWpComUsernamePasswordClick()
loginListener?.loginViaWpcomUsernameInstead()
}
MagicLinkFallbackButton.None -> error("This button should not be visible")
}
}
}
binding.email.text = mEmail
binding.email.text = email
}

override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
Expand All @@ -88,7 +105,7 @@ class LoginMagicLinkSentImprovedFragment : Fragment(R.layout.fragment_login_magi
return when (menuItem.itemId) {
org.wordpress.android.login.R.id.help -> {
mAnalyticsListener.trackShowHelpClick()
mLoginListener?.helpMagicLinkSent(mEmail)
loginListener?.helpMagicLinkSent(email)
true
}

Expand All @@ -103,6 +120,6 @@ class LoginMagicLinkSentImprovedFragment : Fragment(R.layout.fragment_login_magi

override fun onDetach() {
super.onDetach()
mLoginListener = null
loginListener = null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class MagicLinkInterceptActivity : AppCompatActivity() {
viewModel.fetchAccountInfo()
}

findViewById<TextView>(R.id.login_enter_password).visibility = View.GONE
findViewById<TextView>(R.id.login_magic_link_fallback_button).visibility = View.GONE

initializeViewModel()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ class UnifiedLoginTracker
SUBMIT_2FA_CODE("submit_2fa_code"),
REQUEST_MAGIC_LINK("request_magic_link"),
LOGIN_WITH_PASSWORD("login_with_password"),
LOGIN_WITH_WP_COM_USERNAME_PASSWORD("login_with_wp_com_username_password"),
HELP_FINDING_SITE_ADDRESS("help_finding_site_address"),
SELECT_EMAIL_FIELD("select_email_field"),
PICK_EMAIL_FROM_HINT("pick_email_from_hint"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
package com.woocommerce.android.ui.login.overrides

import android.content.Context
import android.os.Bundle
import android.view.ViewGroup
import android.widget.Button
import androidx.annotation.LayoutRes
import androidx.core.view.isVisible
import com.bumptech.glide.Registry.MissingComponentException
import com.woocommerce.android.R
import com.woocommerce.android.extensions.isNotNullOrEmpty
import dagger.android.support.AndroidSupportInjection
import org.wordpress.android.login.LoginEmailPasswordFragment
import org.wordpress.android.login.LoginListener
import org.wordpress.android.login.widgets.WPLoginInputRow

class WooLoginEmailPasswordFragment : LoginEmailPasswordFragment() {
companion object {
@Suppress("LongParameterList")
fun newInstance(
emailAddress: String?,
password: String? = null,
idToken: String? = null,
service: String? = null,
isSocialLogin: Boolean = false,
Expand All @@ -28,7 +21,6 @@ class WooLoginEmailPasswordFragment : LoginEmailPasswordFragment() {
val fragment = WooLoginEmailPasswordFragment()
val args = Bundle()
args.putString(ARG_EMAIL_ADDRESS, emailAddress)
args.putString(ARG_PASSWORD, password)
args.putString(ARG_SOCIAL_ID_TOKEN, idToken)
args.putString(ARG_SOCIAL_SERVICE, service)
args.putBoolean(ARG_SOCIAL_LOGIN, isSocialLogin)
Expand All @@ -39,48 +31,19 @@ class WooLoginEmailPasswordFragment : LoginEmailPasswordFragment() {
}
}

private var loginListener: LoginListener? = null
private var email: String? = null
private var isSocialLogin: Boolean = false

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

email = requireArguments().getString(ARG_EMAIL_ADDRESS)
isSocialLogin = requireArguments().getBoolean(ARG_SOCIAL_LOGIN)
}

override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
loginListener = if (context is LoginListener) {
context
} else {
throw MissingComponentException("$context must implement LoginListener")
}
}

override fun onDetach() {
super.onDetach()
loginListener = null
}

@LayoutRes
override fun getContentLayout(): Int = R.layout.fragment_login_email_password

override fun setupContent(rootView: ViewGroup) {
super.setupContent(rootView)

// Replace the original magic link button with the new one in bottom section
val originalMagicLinkButton = rootView.findViewById<Button>(R.id.login_get_email_link)
rootView.findViewById<Button>(R.id.bottom_button_magic_link)?.apply {
isVisible = true // this button was intentionally hidden until the password screen is shown
setOnClickListener {
loginListener?.useMagicLinkInstead(email, false)
originalMagicLinkButton.performClick()
}
}

val prefilledPassword = requireArguments().getString(ARG_PASSWORD)
if (prefilledPassword.isNotNullOrEmpty()) {
rootView.findViewById<WPLoginInputRow>(R.id.login_password_row)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
android:text="@string/open_mail" />

<com.google.android.material.button.MaterialButton
android:id="@+id/login_enter_password"
android:id="@+id/login_magic_link_fallback_button"
style="@style/Widget.LoginFlow.Button.Tertiary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
Expand Down
2 changes: 1 addition & 1 deletion WooCommerce/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2655,7 +2655,7 @@
<string name="enter_username_instead">Log in with your username.</string>
<string name="enter_verification_code">Almost there! Please enter the verification code for WordPress.com from your authenticator app.</string>
<string name="enter_verification_code_sms">We sent a text message to the phone number ending in %s. Please enter the verification code in the SMS.</string>

<string name="login_use_wpcom_username_instead">Use username and password instead</string>

<string name="requesting_otp">Requesting a verification code via SMS.</string>
<string name="requesting_sms_otp_success">SMS requested, please check your messages for the code.</string>
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ tinder-statemachine = '0.2.0'
wiremock = '2.26.3'
wordpress-aztec = 'v2.1.4'
wordpress-fluxc = 'trunk-0264533ce612f74e1ae6fcbaefeb69b252163774'
wordpress-login = '1.19.0'
wordpress-login = 'trunk-706a58b33ff8f1a2858ae64e51204426ec12fe7b'
wordpress-libaddressinput = '0.0.2'
wordpress-mediapicker = '0.3.1'
wordpress-utils = '3.15.0'
Expand Down

0 comments on commit 4478798

Please sign in to comment.