Skip to content

Commit 4478798

Browse files
Merge pull request #13335 from woocommerce/issue/13321-magic-link-suspicious-emails
[Login] Show magic link screen for suspicious emails
2 parents c15c17d + d09a31e commit 4478798

File tree

11 files changed

+79
-80
lines changed

11 files changed

+79
-80
lines changed

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- [**] Enhanced WebView to dynamically scale receipt content, ensuring optimal fit across all device screen sizes. [https://github.com/woocommerce/woocommerce-android/pull/13266]
77
- [*] Fixes missing text in Blaze Campaigns card on larger display and font sizes [https://github.com/woocommerce/woocommerce-android/pull/13300]
88
- [*] 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]
9+
- [*] [Internal] Improved login flow for accounts using emails marked as suspicious [https://github.com/woocommerce/woocommerce-android/pull/13345]
910
- [Internal] [*] XMLRPC is not needed now for Jetpack Connection during an Account Mismatch flow [https://github.com/woocommerce/woocommerce-android/pull/13308]
1011
- [*] Removed the outdated feedback survey for shipping labels [https://github.com/woocommerce/woocommerce-android/pull/13319]
1112
- [*] Bulk update order status now shows improved result messages, including for partial success. [https://github.com/woocommerce/woocommerce-android/pull/13275]

WooCommerce/src/androidTest/kotlin/com/woocommerce/android/e2e/screens/login/MagicLinkScreen.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import com.woocommerce.android.R
44
import com.woocommerce.android.e2e.helpers.util.Screen
55

66
class MagicLinkScreen : Screen {
7-
constructor() : super(R.id.login_enter_password)
7+
constructor() : super(R.id.login_magic_link_fallback_button)
88

99
fun proceedWithPassword(): PasswordScreen {
10-
clickOn(R.id.login_enter_password)
10+
clickOn(R.id.login_magic_link_fallback_button)
1111
return PasswordScreen()
1212
}
1313
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/LoginActivity.kt

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ import org.wordpress.android.login.LoginMagicLinkRequestFragment
8585
import org.wordpress.android.login.LoginMode
8686
import org.wordpress.android.login.LoginSiteAddressFragment
8787
import org.wordpress.android.login.LoginUsernamePasswordFragment
88+
import org.wordpress.android.login.MagicLinkFallbackButton
8889
import org.wordpress.android.util.ToastUtils
8990
import javax.inject.Inject
9091
import kotlin.text.RegexOption.IGNORE_CASE
@@ -354,28 +355,33 @@ class LoginActivity :
354355
val forcePasswordLogin = BuildConfig.DEBUG && BuildConfig.FORCE_PASSWORD_LOGIN
355356

356357
if (authOptions.isPasswordless && !forcePasswordLogin) {
357-
showMagicLinkRequestScreen(email, verifyEmail, allowPassword = false, forceRequestAtStart = true)
358+
showMagicLinkRequestScreen(
359+
email = email,
360+
verifyEmail = verifyEmail,
361+
fallbackButton = MagicLinkFallbackButton.None,
362+
forceRequestAtStart = true
363+
)
358364
} else {
359365
showEmailPasswordScreen(email, verifyEmail)
360366
}
361367
} else {
362368
if (isMagicLinkEnabled) {
363-
showMagicLinkRequestScreen(email, verifyEmail, allowPassword = true, forceRequestAtStart = false)
369+
showMagicLinkRequestScreen(
370+
email = email,
371+
verifyEmail = verifyEmail,
372+
fallbackButton = MagicLinkFallbackButton.Password,
373+
forceRequestAtStart = false
374+
)
364375
} else {
365376
showEmailPasswordScreen(email, verifyEmail)
366377
}
367378
}
368379
}
369380

370-
private fun showEmailPasswordScreen(
371-
email: String?,
372-
verifyEmail: Boolean,
373-
password: String? = null
374-
) {
381+
private fun showEmailPasswordScreen(email: String?, verifyEmail: Boolean) {
375382
val wooLoginEmailPasswordFragment = WooLoginEmailPasswordFragment
376383
.newInstance(
377384
emailAddress = email,
378-
password = password,
379385
verifyMagicLinkEmail = verifyEmail
380386
)
381387
changeFragment(wooLoginEmailPasswordFragment, true, LoginEmailPasswordFragment.TAG)
@@ -384,7 +390,7 @@ class LoginActivity :
384390
private fun showMagicLinkRequestScreen(
385391
email: String?,
386392
verifyEmail: Boolean,
387-
allowPassword: Boolean,
393+
fallbackButton: MagicLinkFallbackButton,
388394
forceRequestAtStart: Boolean
389395
) {
390396
val scheme = WOOCOMMERCE
@@ -395,7 +401,7 @@ class LoginActivity :
395401
false,
396402
null,
397403
verifyEmail,
398-
allowPassword,
404+
fallbackButton,
399405
forceRequestAtStart
400406
)
401407
changeFragment(loginMagicLinkRequestFragment, true, LoginMagicLinkRequestFragment.TAG, false)
@@ -453,8 +459,8 @@ class LoginActivity :
453459
changeFragment(loginUsernamePasswordFragment, true, LoginUsernamePasswordFragment.TAG)
454460
}
455461

456-
override fun showMagicLinkSentScreen(email: String?, allowPassword: Boolean) {
457-
val loginMagicLinkSentFragment = LoginMagicLinkSentImprovedFragment.newInstance(email, allowPassword)
462+
override fun showMagicLinkSentScreen(email: String?, fallbackButton: MagicLinkFallbackButton) {
463+
val loginMagicLinkSentFragment = LoginMagicLinkSentImprovedFragment.newInstance(email, fallbackButton)
458464
changeFragment(loginMagicLinkSentFragment, true, LoginMagicLinkSentImprovedFragment.TAG, false)
459465
}
460466

@@ -904,9 +910,17 @@ class LoginActivity :
904910
TODO("Not yet implemented")
905911
}
906912

907-
override fun useMagicLinkInstead(email: String?, verifyEmail: Boolean) {
908-
showMagicLinkRequestScreen(email, verifyEmail, allowPassword = false, forceRequestAtStart = true)
909-
}
913+
override fun useMagicLinkInstead(
914+
email: String?,
915+
verifyEmail: Boolean,
916+
requestAtStart: Boolean,
917+
fallbackButton: MagicLinkFallbackButton
918+
) = showMagicLinkRequestScreen(
919+
email = email,
920+
verifyEmail = verifyEmail,
921+
fallbackButton = fallbackButton,
922+
forceRequestAtStart = requestAtStart
923+
)
910924

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

978992
siteUrl.isNotEmpty() && username.isNotEmpty() -> {

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/LoginAnalyticsTracker.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import org.wordpress.android.fluxc.store.AccountStore
1010
import org.wordpress.android.fluxc.store.SiteStore
1111
import org.wordpress.android.login.LoginAnalyticsListener
1212
import org.wordpress.android.login.LoginAnalyticsListener.CreatedAccountSource
13-
import java.util.HashMap
1413
import javax.inject.Singleton
1514

1615
@Singleton
@@ -272,6 +271,10 @@ class LoginAnalyticsTracker(
272271
unifiedLoginTracker.trackClick(Click.LOGIN_WITH_PASSWORD)
273272
}
274273

274+
override fun trackLoginWithWpComUsernamePasswordClick() {
275+
unifiedLoginTracker.trackClick(Click.LOGIN_WITH_WP_COM_USERNAME_PASSWORD)
276+
}
277+
275278
override fun trackShowHelpClick() {
276279
unifiedLoginTracker.trackClick(Click.SHOW_HELP)
277280
unifiedLoginTracker.track(step = Step.HELP)

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/LoginMagicLinkSentImprovedFragment.kt

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,46 @@ import android.view.View
99
import androidx.appcompat.app.AppCompatActivity
1010
import androidx.appcompat.widget.Toolbar
1111
import androidx.core.view.MenuProvider
12+
import androidx.core.view.isVisible
1213
import androidx.fragment.app.Fragment
1314
import com.woocommerce.android.R
1415
import com.woocommerce.android.databinding.FragmentLoginMagicLinkSentImprovedBinding
16+
import com.woocommerce.android.extensions.serializable
1517
import dagger.hilt.android.AndroidEntryPoint
1618
import org.wordpress.android.login.LoginAnalyticsListener
1719
import org.wordpress.android.login.LoginListener
20+
import org.wordpress.android.login.MagicLinkFallbackButton
1821
import javax.inject.Inject
1922

2023
@AndroidEntryPoint
2124
class LoginMagicLinkSentImprovedFragment : Fragment(R.layout.fragment_login_magic_link_sent_improved), MenuProvider {
2225
companion object {
2326
const val TAG = "login_magic_link_sent_fragment_tag"
2427
private const val ARG_EMAIL_ADDRESS = "ARG_EMAIL_ADDRESS"
25-
private const val ARG_ALLOW_PASSWORD = "ARG_ALLOW_PASSWORD"
28+
private const val ARG_FALLBACK_BUTTON: String = "ARG_FALLBACK_BUTTON"
2629

27-
fun newInstance(email: String?, allowPassword: Boolean = true): LoginMagicLinkSentImprovedFragment {
30+
fun newInstance(email: String?, fallbackButton: MagicLinkFallbackButton): LoginMagicLinkSentImprovedFragment {
2831
val fragment = LoginMagicLinkSentImprovedFragment()
2932
val args = Bundle()
3033
args.putString(ARG_EMAIL_ADDRESS, email)
31-
args.putBoolean(ARG_ALLOW_PASSWORD, allowPassword)
34+
args.putSerializable(ARG_FALLBACK_BUTTON, fallbackButton)
3235
fragment.arguments = args
3336
return fragment
3437
}
3538
}
3639

37-
private var mLoginListener: LoginListener? = null
38-
private var mEmail: String? = null
39-
private var mAllowPassword = false
40+
private var loginListener: LoginListener? = null
41+
private var email: String? = null
42+
private var fallbackButton: MagicLinkFallbackButton = MagicLinkFallbackButton.None
4043

4144
@Inject lateinit var mAnalyticsListener: LoginAnalyticsListener
4245

4346
override fun onCreate(savedInstanceState: Bundle?) {
4447
super.onCreate(savedInstanceState)
4548
val args = arguments
4649
if (args != null) {
47-
mEmail = args.getString(ARG_EMAIL_ADDRESS)
48-
mAllowPassword = args.getBoolean(ARG_ALLOW_PASSWORD)
50+
email = args.getString(ARG_EMAIL_ADDRESS)
51+
fallbackButton = args.serializable(ARG_FALLBACK_BUTTON) ?: MagicLinkFallbackButton.None
4952
}
5053
savedInstanceState?.let {
5154
mAnalyticsListener.trackLoginMagicLinkOpenEmailClientViewed()
@@ -55,7 +58,7 @@ class LoginMagicLinkSentImprovedFragment : Fragment(R.layout.fragment_login_magi
5558
override fun onAttach(context: Context) {
5659
super.onAttach(context)
5760
if (activity is LoginListener) {
58-
mLoginListener = activity as LoginListener
61+
loginListener = activity as LoginListener
5962
}
6063
}
6164

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

6972
requireActivity().addMenuProvider(this, viewLifecycleOwner)
7073

71-
binding.loginOpenEmailClient.setOnClickListener { mLoginListener?.openEmailClient(true) }
72-
with(binding.loginEnterPassword) {
73-
visibility = if (mAllowPassword) View.VISIBLE else View.GONE
74+
binding.loginOpenEmailClient.setOnClickListener { loginListener?.openEmailClient(true) }
75+
with(binding.loginMagicLinkFallbackButton) {
76+
text = if (fallbackButton == MagicLinkFallbackButton.Password) {
77+
getString(R.string.or_use_password_below_qr_code_scan_option)
78+
} else {
79+
getString(R.string.login_use_wpcom_username_instead)
80+
}
81+
isVisible = fallbackButton != MagicLinkFallbackButton.None
7482
setOnClickListener {
75-
mAnalyticsListener.trackLoginWithPasswordClick()
76-
mLoginListener?.usePasswordInstead(mEmail)
83+
when (fallbackButton) {
84+
MagicLinkFallbackButton.Password -> {
85+
mAnalyticsListener.trackLoginWithPasswordClick()
86+
loginListener?.usePasswordInstead(email)
87+
}
88+
MagicLinkFallbackButton.UsernameAndPassword -> {
89+
mAnalyticsListener.trackLoginWithWpComUsernamePasswordClick()
90+
loginListener?.loginViaWpcomUsernameInstead()
91+
}
92+
MagicLinkFallbackButton.None -> error("This button should not be visible")
93+
}
7794
}
7895
}
79-
binding.email.text = mEmail
96+
binding.email.text = email
8097
}
8198

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

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

104121
override fun onDetach() {
105122
super.onDetach()
106-
mLoginListener = null
123+
loginListener = null
107124
}
108125
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/MagicLinkInterceptActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class MagicLinkInterceptActivity : AppCompatActivity() {
6464
viewModel.fetchAccountInfo()
6565
}
6666

67-
findViewById<TextView>(R.id.login_enter_password).visibility = View.GONE
67+
findViewById<TextView>(R.id.login_magic_link_fallback_button).visibility = View.GONE
6868

6969
initializeViewModel()
7070
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/UnifiedLoginTracker.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ class UnifiedLoginTracker
191191
SUBMIT_2FA_CODE("submit_2fa_code"),
192192
REQUEST_MAGIC_LINK("request_magic_link"),
193193
LOGIN_WITH_PASSWORD("login_with_password"),
194+
LOGIN_WITH_WP_COM_USERNAME_PASSWORD("login_with_wp_com_username_password"),
194195
HELP_FINDING_SITE_ADDRESS("help_finding_site_address"),
195196
SELECT_EMAIL_FIELD("select_email_field"),
196197
PICK_EMAIL_FROM_HINT("pick_email_from_hint"),
Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,18 @@
11
package com.woocommerce.android.ui.login.overrides
22

3-
import android.content.Context
43
import android.os.Bundle
54
import android.view.ViewGroup
65
import android.widget.Button
76
import androidx.annotation.LayoutRes
87
import androidx.core.view.isVisible
9-
import com.bumptech.glide.Registry.MissingComponentException
108
import com.woocommerce.android.R
11-
import com.woocommerce.android.extensions.isNotNullOrEmpty
12-
import dagger.android.support.AndroidSupportInjection
139
import org.wordpress.android.login.LoginEmailPasswordFragment
14-
import org.wordpress.android.login.LoginListener
15-
import org.wordpress.android.login.widgets.WPLoginInputRow
1610

1711
class WooLoginEmailPasswordFragment : LoginEmailPasswordFragment() {
1812
companion object {
1913
@Suppress("LongParameterList")
2014
fun newInstance(
2115
emailAddress: String?,
22-
password: String? = null,
2316
idToken: String? = null,
2417
service: String? = null,
2518
isSocialLogin: Boolean = false,
@@ -28,7 +21,6 @@ class WooLoginEmailPasswordFragment : LoginEmailPasswordFragment() {
2821
val fragment = WooLoginEmailPasswordFragment()
2922
val args = Bundle()
3023
args.putString(ARG_EMAIL_ADDRESS, emailAddress)
31-
args.putString(ARG_PASSWORD, password)
3224
args.putString(ARG_SOCIAL_ID_TOKEN, idToken)
3325
args.putString(ARG_SOCIAL_SERVICE, service)
3426
args.putBoolean(ARG_SOCIAL_LOGIN, isSocialLogin)
@@ -39,48 +31,19 @@ class WooLoginEmailPasswordFragment : LoginEmailPasswordFragment() {
3931
}
4032
}
4133

42-
private var loginListener: LoginListener? = null
43-
private var email: String? = null
44-
private var isSocialLogin: Boolean = false
45-
46-
override fun onCreate(savedInstanceState: Bundle?) {
47-
super.onCreate(savedInstanceState)
48-
49-
email = requireArguments().getString(ARG_EMAIL_ADDRESS)
50-
isSocialLogin = requireArguments().getBoolean(ARG_SOCIAL_LOGIN)
51-
}
52-
53-
override fun onAttach(context: Context) {
54-
AndroidSupportInjection.inject(this)
55-
super.onAttach(context)
56-
loginListener = if (context is LoginListener) {
57-
context
58-
} else {
59-
throw MissingComponentException("$context must implement LoginListener")
60-
}
61-
}
62-
63-
override fun onDetach() {
64-
super.onDetach()
65-
loginListener = null
66-
}
67-
6834
@LayoutRes
6935
override fun getContentLayout(): Int = R.layout.fragment_login_email_password
7036

7137
override fun setupContent(rootView: ViewGroup) {
7238
super.setupContent(rootView)
7339

40+
// Replace the original magic link button with the new one in bottom section
41+
val originalMagicLinkButton = rootView.findViewById<Button>(R.id.login_get_email_link)
7442
rootView.findViewById<Button>(R.id.bottom_button_magic_link)?.apply {
7543
isVisible = true // this button was intentionally hidden until the password screen is shown
7644
setOnClickListener {
77-
loginListener?.useMagicLinkInstead(email, false)
45+
originalMagicLinkButton.performClick()
7846
}
7947
}
80-
81-
val prefilledPassword = requireArguments().getString(ARG_PASSWORD)
82-
if (prefilledPassword.isNotNullOrEmpty()) {
83-
rootView.findViewById<WPLoginInputRow>(R.id.login_password_row)
84-
}
8548
}
8649
}

WooCommerce/src/main/res/layout/fragment_login_magic_link_sent_improved.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
android:text="@string/open_mail" />
7979

8080
<com.google.android.material.button.MaterialButton
81-
android:id="@+id/login_enter_password"
81+
android:id="@+id/login_magic_link_fallback_button"
8282
style="@style/Widget.LoginFlow.Button.Tertiary"
8383
android:layout_width="match_parent"
8484
android:layout_height="wrap_content"

WooCommerce/src/main/res/values/strings.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2655,7 +2655,7 @@
26552655
<string name="enter_username_instead">Log in with your username.</string>
26562656
<string name="enter_verification_code">Almost there! Please enter the verification code for WordPress.com from your authenticator app.</string>
26572657
<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>
2658-
2658+
<string name="login_use_wpcom_username_instead">Use username and password instead</string>
26592659

26602660
<string name="requesting_otp">Requesting a verification code via SMS.</string>
26612661
<string name="requesting_sms_otp_success">SMS requested, please check your messages for the code.</string>

0 commit comments

Comments
 (0)