Skip to content

Commit f726bfa

Browse files
committed
Fix issue with "Change email" behavior in Link
After selecting "Change email" and closing the flow, we now show the signup screen on re-open instead of the verification screen.
1 parent 3d60d7e commit f726bfa

File tree

4 files changed

+79
-28
lines changed

4 files changed

+79
-28
lines changed

paymentsheet/src/main/java/com/stripe/android/link/LinkActivityViewModel.kt

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import com.stripe.android.link.injection.NativeLinkComponent
2424
import com.stripe.android.link.model.AccountStatus
2525
import com.stripe.android.link.model.LinkAccount
2626
import com.stripe.android.link.ui.LinkAppBarState
27-
import com.stripe.android.link.ui.signup.SignUpViewModel
27+
import com.stripe.android.link.ui.signup.SignUpViewModel.Companion.DID_SELECT_TO_CHANGE_EMAIL
2828
import com.stripe.android.link.utils.LINK_DEFAULT_ANIMATION_DELAY_MILLIS
2929
import com.stripe.android.paymentelement.confirmation.ConfirmationHandler
3030
import com.stripe.android.paymentsheet.analytics.EventReporter
@@ -61,6 +61,9 @@ internal class LinkActivityViewModel @Inject constructor(
6161
private val _linkScreenState = MutableStateFlow<ScreenState>(ScreenState.Loading)
6262
val linkScreenState: StateFlow<ScreenState> = _linkScreenState
6363

64+
private val didChangeEmail: Boolean
65+
get() = savedStateHandle.get<Boolean>(DID_SELECT_TO_CHANGE_EMAIL) == true
66+
6467
val linkAccount: LinkAccount?
6568
get() = linkAccountManager.linkAccount.value
6669

@@ -138,7 +141,11 @@ internal class LinkActivityViewModel @Inject constructor(
138141
if (!navController.popBackStack()) {
139142
dismissWithResult?.invoke(
140143
LinkActivityResult.Canceled(
141-
linkAccountUpdate = linkAccountManager.linkAccountUpdate
144+
linkAccountUpdate = if (didChangeEmail) {
145+
LinkAccountUpdate.Value(null)
146+
} else {
147+
LinkAccountUpdate.Value(linkAccount)
148+
},
142149
)
143150
)
144151
}
@@ -172,14 +179,18 @@ internal class LinkActivityViewModel @Inject constructor(
172179
if (navController?.popBackStack() == false) {
173180
dismissWithResult?.invoke(
174181
LinkActivityResult.Canceled(
175-
linkAccountUpdate = linkAccountManager.linkAccountUpdate
182+
linkAccountUpdate = if (didChangeEmail) {
183+
LinkAccountUpdate.Value(null)
184+
} else {
185+
LinkAccountUpdate.Value(linkAccount)
186+
},
176187
)
177188
)
178189
}
179190
}
180191

181192
fun changeEmail() {
182-
savedStateHandle[SignUpViewModel.USE_LINK_CONFIGURATION_CUSTOMER_INFO] = false
193+
savedStateHandle[DID_SELECT_TO_CHANGE_EMAIL] = true
183194
navigate(LinkScreen.SignUp, clearStack = true)
184195
}
185196

@@ -285,7 +296,7 @@ internal class LinkActivityViewModel @Inject constructor(
285296

286297
DaggerNativeLinkComponent
287298
.builder()
288-
.configuration(args.configuration)
299+
.configuration(args.configurationWithUpdatedCustomerInfo)
289300
.publishableKeyProvider { args.publishableKey }
290301
.stripeAccountIdProvider { args.stripeAccountId }
291302
.paymentElementCallbackIdentifier(args.paymentElementCallbackIdentifier)
@@ -308,3 +319,16 @@ internal sealed interface ScreenState {
308319
}
309320

310321
internal class NoArgsException : IllegalArgumentException("NativeLinkArgs not found")
322+
323+
private val NativeLinkArgs.configurationWithUpdatedCustomerInfo: LinkConfiguration
324+
// The user might have logged out on a previous Link session. We set the email to null
325+
// to avoid us defaulting back to the account that they previously logged out of.
326+
get() {
327+
val clearCustomerDetails = linkAccount == null
328+
val effectiveCustomerInfo = if (clearCustomerDetails) {
329+
LinkConfiguration.CustomerInfo(null, null, null, null)
330+
} else {
331+
configuration.customerInfo
332+
}
333+
return configuration.copy(customerInfo = effectiveCustomerInfo)
334+
}

paymentsheet/src/main/java/com/stripe/android/link/ui/signup/SignUpViewModel.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,12 @@ internal class SignUpViewModel @Inject constructor(
4646
private val navigateAndClearStack: (LinkScreen) -> Unit,
4747
private val moveToWeb: () -> Unit
4848
) : ViewModel() {
49-
private val useLinkConfigurationCustomerInfo =
50-
savedStateHandle.get<Boolean>(USE_LINK_CONFIGURATION_CUSTOMER_INFO) ?: true
51-
private val customerInfo = configuration.customerInfo.takeIf { useLinkConfigurationCustomerInfo }
49+
50+
private val didSelectToChangeEmail: Boolean
51+
get() = savedStateHandle.get<Boolean>(DID_SELECT_TO_CHANGE_EMAIL) == true
52+
53+
private val customerInfo: LinkConfiguration.CustomerInfo?
54+
get() = configuration.customerInfo.takeUnless { didSelectToChangeEmail }
5255

5356
val emailController = EmailConfig.createController(
5457
initialValue = customerInfo?.email
@@ -240,7 +243,7 @@ internal class SignUpViewModel @Inject constructor(
240243
companion object {
241244
// How long to wait before triggering a call to lookup the email
242245
internal val LOOKUP_DEBOUNCE = 1.seconds
243-
internal const val USE_LINK_CONFIGURATION_CUSTOMER_INFO = "use_link_configuration_customer_info"
246+
internal const val DID_SELECT_TO_CHANGE_EMAIL = "did_select_to_change_email"
244247

245248
fun factory(
246249
parentComponent: NativeLinkComponent,

paymentsheet/src/test/java/com/stripe/android/link/LinkActivityViewModelTest.kt

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,33 @@ internal class LinkActivityViewModelTest {
194194
}
195195

196196
@Test
197-
fun `initializer creates ViewModel when args are valid`() {
198-
val mockArgs = NativeLinkArgs(
199-
configuration = mock(),
197+
fun `initializer creates ViewModel when args are valid and a Link account is passed`() {
198+
val configuration = TestFactory.LINK_CONFIGURATION
199+
val args = NativeLinkArgs(
200+
configuration = configuration,
201+
publishableKey = "",
202+
stripeAccountId = null,
203+
startWithVerificationDialog = false,
204+
linkAccount = TestFactory.LINK_ACCOUNT,
205+
paymentElementCallbackIdentifier = "LinkNativeTestIdentifier",
206+
)
207+
val savedStateHandle = SavedStateHandle()
208+
val factory = LinkActivityViewModel.factory(savedStateHandle)
209+
savedStateHandle[LinkActivity.EXTRA_ARGS] = args
210+
211+
val viewModel = factory.create(LinkActivityViewModel::class.java, creationExtras())
212+
assertThat(viewModel.activityRetainedComponent.configuration).isEqualTo(configuration)
213+
}
214+
215+
@Test
216+
fun `initializer creates ViewModel when args are valid and no Link account is passed`() {
217+
val configuration = TestFactory.LINK_CONFIGURATION
218+
val expectedConfiguration = configuration.copy(
219+
customerInfo = LinkConfiguration.CustomerInfo(null, null, null, null)
220+
)
221+
222+
val args = NativeLinkArgs(
223+
configuration = configuration,
200224
publishableKey = "",
201225
stripeAccountId = null,
202226
startWithVerificationDialog = false,
@@ -205,10 +229,10 @@ internal class LinkActivityViewModelTest {
205229
)
206230
val savedStateHandle = SavedStateHandle()
207231
val factory = LinkActivityViewModel.factory(savedStateHandle)
208-
savedStateHandle[LinkActivity.EXTRA_ARGS] = mockArgs
232+
savedStateHandle[LinkActivity.EXTRA_ARGS] = args
209233

210234
val viewModel = factory.create(LinkActivityViewModel::class.java, creationExtras())
211-
assertThat(viewModel.activityRetainedComponent.configuration).isEqualTo(mockArgs.configuration)
235+
assertThat(viewModel.activityRetainedComponent.configuration).isEqualTo(expectedConfiguration)
212236
}
213237

214238
@Test
@@ -668,7 +692,7 @@ internal class LinkActivityViewModelTest {
668692

669693
viewModel.changeEmail()
670694

671-
assertThat(savedStateHandle.get<Boolean>(SignUpViewModel.USE_LINK_CONFIGURATION_CUSTOMER_INFO)).isFalse()
695+
assertThat(savedStateHandle.get<Boolean>(SignUpViewModel.DID_SELECT_TO_CHANGE_EMAIL)).isTrue()
672696
assertNavigation(
673697
navController = navController,
674698
screen = LinkScreen.SignUp,

paymentsheet/src/test/java/com/stripe/android/link/ui/signup/SignUpViewModelTest.kt

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,10 @@ internal class SignUpViewModelTest {
9292
}
9393

9494
@Test
95-
fun `When USE_LINK_CONFIGURATION_CUSTOMER_INFO is false, controllers should not be prefilled`() =
95+
fun `When DID_SELECT_TO_CHANGE_EMAIL is true, controllers should not be prefilled`() =
9696
runTest(dispatcher) {
97-
testUseLinkConfigurationCustomerInfo(
98-
useLinkConfigurationCustomerInfo = false,
97+
testDidChangeEmail(
98+
didChangeEmail = true,
9999
expectedSignUpState = SignUpState.InputtingPrimaryField,
100100
expectedEmail = "",
101101
expectedPhoneNumber = "",
@@ -104,9 +104,9 @@ internal class SignUpViewModelTest {
104104
}
105105

106106
@Test
107-
fun `When USE_LINK_CONFIGURATION_CUSTOMER_INFO is true, controllers should be prefilled`() = runTest(dispatcher) {
108-
testUseLinkConfigurationCustomerInfo(
109-
useLinkConfigurationCustomerInfo = true,
107+
fun `When DID_SELECT_TO_CHANGE_EMAIL is false, controllers should be prefilled`() = runTest(dispatcher) {
108+
testDidChangeEmail(
109+
didChangeEmail = false,
110110
expectedSignUpState = SignUpState.InputtingRemainingFields,
111111
expectedEmail = CUSTOMER_EMAIL,
112112
expectedPhoneNumber = TestFactory.CUSTOMER_PHONE,
@@ -115,10 +115,10 @@ internal class SignUpViewModelTest {
115115
}
116116

117117
@Test
118-
fun `When USE_LINK_CONFIGURATION_CUSTOMER_INFO is not set, controllers should be prefilled`() =
118+
fun `When DID_SELECT_TO_CHANGE_EMAIL is not set, controllers should be prefilled`() =
119119
runTest(dispatcher) {
120-
testUseLinkConfigurationCustomerInfo(
121-
useLinkConfigurationCustomerInfo = null,
120+
testDidChangeEmail(
121+
didChangeEmail = null,
122122
expectedSignUpState = SignUpState.InputtingRemainingFields,
123123
expectedEmail = CUSTOMER_EMAIL,
124124
expectedPhoneNumber = TestFactory.CUSTOMER_PHONE,
@@ -561,17 +561,17 @@ internal class SignUpViewModelTest {
561561
onSignUpClick()
562562
}
563563

564-
private fun testUseLinkConfigurationCustomerInfo(
565-
useLinkConfigurationCustomerInfo: Boolean?,
564+
private fun testDidChangeEmail(
565+
didChangeEmail: Boolean?,
566566
expectedSignUpState: SignUpState = SignUpState.InputtingRemainingFields,
567567
expectedEmail: String = CUSTOMER_EMAIL,
568568
expectedPhoneNumber: String = TestFactory.CUSTOMER_PHONE,
569569
expectedName: String = TestFactory.CUSTOMER_NAME
570570
) {
571571
val savedStateHandle = SavedStateHandle()
572572
.apply {
573-
useLinkConfigurationCustomerInfo?.let {
574-
set(SignUpViewModel.USE_LINK_CONFIGURATION_CUSTOMER_INFO, it)
573+
didChangeEmail?.let {
574+
set(SignUpViewModel.DID_SELECT_TO_CHANGE_EMAIL, it)
575575
}
576576
}
577577
val viewModel = createViewModel(

0 commit comments

Comments
 (0)