diff --git a/link/src/main/java/com/stripe/android/link/ui/LinkContent.kt b/link/src/main/java/com/stripe/android/link/ui/LinkContent.kt
index 70aa7d81108..6ac9815c958 100644
--- a/link/src/main/java/com/stripe/android/link/ui/LinkContent.kt
+++ b/link/src/main/java/com/stripe/android/link/ui/LinkContent.kt
@@ -37,6 +37,7 @@ import com.stripe.android.link.ui.signup.SignUpViewModel
import com.stripe.android.link.ui.verification.VerificationScreen
import com.stripe.android.link.ui.verification.VerificationViewModel
import com.stripe.android.link.ui.wallet.WalletScreen
+import com.stripe.android.link.ui.wallet.WalletViewModel
import com.stripe.android.ui.core.CircularProgressIndicator
import kotlinx.coroutines.launch
@@ -155,7 +156,17 @@ private fun Screens(
}
composable(LinkScreen.Wallet.route) {
- WalletScreen()
+ val linkAccount = getLinkAccount()
+ ?: return@composable dismissWithResult(LinkActivityResult.Failed(NoLinkAccountFoundException()))
+ val viewModel: WalletViewModel = linkViewModel { parentComponent ->
+ WalletViewModel.factory(
+ parentComponent = parentComponent,
+ linkAccount = linkAccount,
+ navigate = { _, _ -> },
+ dismissWithResult = dismissWithResult
+ )
+ }
+ WalletScreen(viewModel)
}
composable(LinkScreen.CardEdit.route) {
diff --git a/link/src/main/java/com/stripe/android/link/ui/wallet/WalletScreen.kt b/link/src/main/java/com/stripe/android/link/ui/wallet/WalletScreen.kt
index 05acb8633f2..5de98bce0a7 100644
--- a/link/src/main/java/com/stripe/android/link/ui/wallet/WalletScreen.kt
+++ b/link/src/main/java/com/stripe/android/link/ui/wallet/WalletScreen.kt
@@ -48,6 +48,19 @@ internal fun WalletScreen(
) {
val state by viewModel.uiState.collectAsState()
+ WalletBody(
+ state = state,
+ onItemSelected = viewModel::onItemSelected,
+ onExpandedChanged = viewModel::setExpanded
+ )
+}
+
+@Composable
+internal fun WalletBody(
+ state: WalletUiState,
+ onItemSelected: (ConsumerPaymentDetails.PaymentDetails) -> Unit,
+ onExpandedChanged: (Boolean) -> Unit,
+) {
if (state.paymentDetailsList.isEmpty()) {
Box(
modifier = Modifier
@@ -56,6 +69,7 @@ internal fun WalletScreen(
) {
CircularProgressIndicator()
}
+ return
}
val focusManager = LocalFocusManager.current
@@ -79,32 +93,26 @@ internal fun WalletScreen(
if (state.isExpanded || selectedItem == null) {
ExpandedPaymentDetails(
uiState = state,
- onItemSelected = viewModel::onItemSelected,
+ onItemSelected = onItemSelected,
onMenuButtonClick = {},
onAddNewPaymentMethodClick = {},
- onCollapse = {}
+ onCollapse = {
+ onExpandedChanged(false)
+ }
)
} else {
CollapsedPaymentDetails(
selectedPaymentMethod = selectedItem,
enabled = !state.primaryButtonState.isBlocking,
- onClick = {}
+ onClick = {
+ onExpandedChanged(true)
+ }
)
}
}
AnimatedVisibility(state.showBankAccountTerms) {
- Html(
- html = stringResource(R.string.stripe_wallet_bank_account_terms).replaceHyperlinks(),
- color = MaterialTheme.colors.onSecondary,
- style = MaterialTheme.typography.caption,
- modifier = Modifier
- .fillMaxWidth()
- .padding(top = 12.dp),
- urlSpanStyle = SpanStyle(
- color = MaterialTheme.colors.primary
- )
- )
+ BankAccountTerms()
}
}
}
@@ -182,32 +190,10 @@ private fun ExpandedPaymentDetails(
)
) {
item {
- Row(
- modifier = Modifier
- .height(44.dp)
- .clickable(enabled = isEnabled, onClick = onCollapse),
- verticalAlignment = Alignment.CenterVertically
- ) {
- Text(
- text = stringResource(id = R.string.stripe_wallet_expanded_title),
- modifier = Modifier
- .padding(start = HorizontalPadding, top = 20.dp),
- color = MaterialTheme.colors.onPrimary,
- style = MaterialTheme.typography.button
- )
- Spacer(modifier = Modifier.weight(1f))
- Icon(
- painter = painterResource(id = R.drawable.stripe_link_chevron),
- contentDescription = stringResource(id = R.string.stripe_wallet_expand_accessibility),
- modifier = Modifier
- .padding(top = 20.dp, end = 22.dp)
- .rotate(180f)
- .semantics {
- testTag = "ChevronIcon"
- },
- tint = MaterialTheme.colors.onPrimary
- )
- }
+ ExpandedRowHeader(
+ isEnabled = isEnabled,
+ onCollapse = onCollapse
+ )
}
items(
@@ -231,31 +217,92 @@ private fun ExpandedPaymentDetails(
}
item {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .height(60.dp)
- .clickable(enabled = isEnabled, onClick = onAddNewPaymentMethodClick),
- verticalAlignment = Alignment.CenterVertically
- ) {
- Icon(
- painter = painterResource(id = R.drawable.stripe_link_add_green),
- contentDescription = null,
- modifier = Modifier.padding(start = HorizontalPadding, end = 12.dp),
- tint = Color.Unspecified
- )
- Text(
- text = stringResource(id = R.string.stripe_add_payment_method),
- modifier = Modifier.padding(end = HorizontalPadding),
- color = MaterialTheme.linkColors.actionLabel,
- style = MaterialTheme.typography.button
- )
- }
+ AddPaymentMethodRow(
+ isEnabled = isEnabled,
+ onAddNewPaymentMethodClick = onAddNewPaymentMethodClick
+ )
}
}
}
+@Composable
+private fun ExpandedRowHeader(
+ isEnabled: Boolean,
+ onCollapse: () -> Unit,
+) {
+ Row(
+ modifier = Modifier
+ .height(44.dp)
+ .clickable(enabled = isEnabled, onClick = onCollapse),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = stringResource(id = R.string.stripe_wallet_expanded_title),
+ modifier = Modifier
+ .padding(start = HorizontalPadding, top = 20.dp),
+ color = MaterialTheme.colors.onPrimary,
+ style = MaterialTheme.typography.button
+ )
+ Spacer(modifier = Modifier.weight(1f))
+ Icon(
+ painter = painterResource(id = R.drawable.stripe_link_chevron),
+ contentDescription = stringResource(id = R.string.stripe_wallet_expand_accessibility),
+ modifier = Modifier
+ .padding(top = 20.dp, end = 22.dp)
+ .rotate(CHEVRON_ICON_ROTATION)
+ .semantics {
+ testTag = "ChevronIcon"
+ },
+ tint = MaterialTheme.colors.onPrimary
+ )
+ }
+}
+
+@Composable
+private fun AddPaymentMethodRow(
+ isEnabled: Boolean,
+ onAddNewPaymentMethodClick: () -> Unit,
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(60.dp)
+ .clickable(enabled = isEnabled, onClick = onAddNewPaymentMethodClick),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.stripe_link_add_green),
+ contentDescription = null,
+ modifier = Modifier.padding(start = HorizontalPadding, end = 12.dp),
+ tint = Color.Unspecified
+ )
+ Text(
+ text = stringResource(id = R.string.stripe_add_payment_method),
+ modifier = Modifier.padding(end = HorizontalPadding),
+ color = MaterialTheme.linkColors.actionLabel,
+ style = MaterialTheme.typography.button
+ )
+ }
+}
+
+@Composable
+private fun BankAccountTerms() {
+ Html(
+ html = stringResource(R.string.stripe_wallet_bank_account_terms).replaceHyperlinks(),
+ color = MaterialTheme.colors.onSecondary,
+ style = MaterialTheme.typography.caption,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = 12.dp),
+ urlSpanStyle = SpanStyle(
+ color = MaterialTheme.colors.primary
+ )
+ )
+}
+
private fun String.replaceHyperlinks() = this.replace(
"",
""
).replace("", "")
+
+private const val CHEVRON_ICON_ROTATION = 180f
diff --git a/link/src/main/java/com/stripe/android/link/ui/wallet/WalletUiState.kt b/link/src/main/java/com/stripe/android/link/ui/wallet/WalletUiState.kt
index e101c92b58d..25a1149462f 100644
--- a/link/src/main/java/com/stripe/android/link/ui/wallet/WalletUiState.kt
+++ b/link/src/main/java/com/stripe/android/link/ui/wallet/WalletUiState.kt
@@ -6,11 +6,10 @@ import com.stripe.android.model.ConsumerPaymentDetails
@Immutable
internal data class WalletUiState(
- val supportedTypes: Set,
val paymentDetailsList: List,
val selectedItem: ConsumerPaymentDetails.PaymentDetails?,
val isProcessing: Boolean,
- val isExpanded: Boolean = false
+ val isExpanded: Boolean
) {
val showBankAccountTerms = selectedItem is ConsumerPaymentDetails.BankAccount
diff --git a/link/src/main/java/com/stripe/android/link/ui/wallet/WalletViewModel.kt b/link/src/main/java/com/stripe/android/link/ui/wallet/WalletViewModel.kt
index e3082913b5f..84f74c68de4 100644
--- a/link/src/main/java/com/stripe/android/link/ui/wallet/WalletViewModel.kt
+++ b/link/src/main/java/com/stripe/android/link/ui/wallet/WalletViewModel.kt
@@ -32,10 +32,10 @@ internal class WalletViewModel @Inject constructor(
private val _uiState = MutableStateFlow(
value = WalletUiState(
- supportedTypes = stripeIntent.supportedPaymentMethodTypes(linkAccount),
paymentDetailsList = emptyList(),
selectedItem = null,
- isProcessing = false
+ isProcessing = false,
+ isExpanded = false
)
)
@@ -52,7 +52,7 @@ internal class WalletViewModel @Inject constructor(
viewModelScope.launch {
linkAccountManager.listPaymentDetails(
- paymentMethodTypes = _uiState.value.supportedTypes
+ paymentMethodTypes = stripeIntent.supportedPaymentMethodTypes(linkAccount)
).fold(
onSuccess = { response ->
_uiState.update {
@@ -82,6 +82,12 @@ internal class WalletViewModel @Inject constructor(
}
}
+ fun setExpanded(expanded: Boolean) {
+ _uiState.update {
+ it.copy(isExpanded = expanded)
+ }
+ }
+
companion object {
fun factory(
parentComponent: NativeLinkComponent,
diff --git a/link/src/test/java/com/stripe/android/link/TestFactory.kt b/link/src/test/java/com/stripe/android/link/TestFactory.kt
index 85e10f6a959..9cd4a0602dd 100644
--- a/link/src/test/java/com/stripe/android/link/TestFactory.kt
+++ b/link/src/test/java/com/stripe/android/link/TestFactory.kt
@@ -82,6 +82,19 @@ internal object TestFactory {
)
)
+ private val CONSUMER_PAYMENT_DETAILS_BANK_ACCOUNT = ConsumerPaymentDetails.BankAccount(
+ id = "pm_124",
+ last4 = "4242",
+ isDefault = false,
+ bankName = "Stripe Test Bank",
+ bankIconCode = null
+ )
+
+ private val CONSUMER_PAYMENT_DETAILS_PASSTHROUGH = ConsumerPaymentDetails.Passthrough(
+ id = "pm_125",
+ last4 = "4242",
+ )
+
val LINK_NEW_PAYMENT_DETAILS = LinkPaymentDetails.New(
paymentDetails = CONSUMER_PAYMENT_DETAILS_CARD,
paymentMethodCreateParams = PAYMENT_METHOD_CREATE_PARAMS,
@@ -92,7 +105,9 @@ internal object TestFactory {
val CONSUMER_PAYMENT_DETAILS: ConsumerPaymentDetails = ConsumerPaymentDetails(
paymentDetails = listOf(
- CONSUMER_PAYMENT_DETAILS_CARD
+ CONSUMER_PAYMENT_DETAILS_CARD,
+ CONSUMER_PAYMENT_DETAILS_BANK_ACCOUNT,
+ CONSUMER_PAYMENT_DETAILS_PASSTHROUGH,
)
)
diff --git a/link/src/test/java/com/stripe/android/link/ui/wallet/WalletScreenScreenshotTest.kt b/link/src/test/java/com/stripe/android/link/ui/wallet/WalletScreenScreenshotTest.kt
new file mode 100644
index 00000000000..e6470146153
--- /dev/null
+++ b/link/src/test/java/com/stripe/android/link/ui/wallet/WalletScreenScreenshotTest.kt
@@ -0,0 +1,77 @@
+package com.stripe.android.link.ui.wallet
+
+import com.stripe.android.link.TestFactory
+import com.stripe.android.link.theme.DefaultLinkTheme
+import com.stripe.android.model.ConsumerPaymentDetails
+import com.stripe.android.screenshottesting.PaparazziRule
+import org.junit.Rule
+import org.junit.Test
+
+internal class WalletScreenScreenshotTest {
+ @get:Rule
+ val paparazziRule = PaparazziRule()
+
+ @Test
+ fun testEmptyState() {
+ snapshot(
+ state = WalletUiState(
+ paymentDetailsList = emptyList(),
+ selectedItem = null,
+ isProcessing = false,
+ isExpanded = false
+ )
+ )
+ }
+
+ @Test
+ fun testCollapsedState() {
+ snapshot(
+ state = WalletUiState(
+ paymentDetailsList = TestFactory.CONSUMER_PAYMENT_DETAILS.paymentDetails,
+ selectedItem = TestFactory.CONSUMER_PAYMENT_DETAILS.paymentDetails.firstOrNull(),
+ isProcessing = false,
+ isExpanded = false
+ )
+ )
+ }
+
+ @Test
+ fun testExpandedState() {
+ snapshot(
+ state = WalletUiState(
+ paymentDetailsList = TestFactory.CONSUMER_PAYMENT_DETAILS.paymentDetails,
+ selectedItem = TestFactory.CONSUMER_PAYMENT_DETAILS.paymentDetails.firstOrNull(),
+ isProcessing = false,
+ isExpanded = true
+ )
+ )
+ }
+
+ @Test
+ fun testBankAccountSelectedState() {
+ snapshot(
+ state = WalletUiState(
+ paymentDetailsList = TestFactory.CONSUMER_PAYMENT_DETAILS.paymentDetails,
+ selectedItem = TestFactory.CONSUMER_PAYMENT_DETAILS.paymentDetails.firstOrNull {
+ it is ConsumerPaymentDetails.BankAccount
+ },
+ isProcessing = false,
+ isExpanded = true
+ )
+ )
+ }
+
+ private fun snapshot(
+ state: WalletUiState
+ ) {
+ paparazziRule.snapshot {
+ DefaultLinkTheme {
+ WalletBody(
+ state = state,
+ onItemSelected = {},
+ onExpandedChanged = {}
+ )
+ }
+ }
+ }
+}
diff --git a/link/src/test/java/com/stripe/android/link/ui/wallet/WalletViewModelTest.kt b/link/src/test/java/com/stripe/android/link/ui/wallet/WalletViewModelTest.kt
index 4ac37082c0f..b2853e272b4 100644
--- a/link/src/test/java/com/stripe/android/link/ui/wallet/WalletViewModelTest.kt
+++ b/link/src/test/java/com/stripe/android/link/ui/wallet/WalletViewModelTest.kt
@@ -54,10 +54,10 @@ class WalletViewModelTest {
assertThat(viewModel.uiState.value).isEqualTo(
WalletUiState(
- supportedTypes = TestFactory.LINK_CONFIGURATION.stripeIntent.paymentMethodTypes.toSet(),
paymentDetailsList = TestFactory.CONSUMER_PAYMENT_DETAILS.paymentDetails,
selectedItem = TestFactory.CONSUMER_PAYMENT_DETAILS.paymentDetails.firstOrNull(),
- isProcessing = false
+ isProcessing = false,
+ isExpanded = false
)
)
assertThat(viewModel.uiState.value.primaryButtonState).isEqualTo(PrimaryButtonState.Disabled)
diff --git a/link/src/test/snapshots/images/com.stripe.android.link.ui.wallet_WalletScreenScreenshotTest_testBankAccountSelectedState[].png b/link/src/test/snapshots/images/com.stripe.android.link.ui.wallet_WalletScreenScreenshotTest_testBankAccountSelectedState[].png
new file mode 100644
index 00000000000..991097a8a9f
Binary files /dev/null and b/link/src/test/snapshots/images/com.stripe.android.link.ui.wallet_WalletScreenScreenshotTest_testBankAccountSelectedState[].png differ
diff --git a/link/src/test/snapshots/images/com.stripe.android.link.ui.wallet_WalletScreenScreenshotTest_testCollapsedState[].png b/link/src/test/snapshots/images/com.stripe.android.link.ui.wallet_WalletScreenScreenshotTest_testCollapsedState[].png
new file mode 100644
index 00000000000..b71c72a355d
Binary files /dev/null and b/link/src/test/snapshots/images/com.stripe.android.link.ui.wallet_WalletScreenScreenshotTest_testCollapsedState[].png differ
diff --git a/link/src/test/snapshots/images/com.stripe.android.link.ui.wallet_WalletScreenScreenshotTest_testEmptyState[].png b/link/src/test/snapshots/images/com.stripe.android.link.ui.wallet_WalletScreenScreenshotTest_testEmptyState[].png
new file mode 100644
index 00000000000..cd69858e104
Binary files /dev/null and b/link/src/test/snapshots/images/com.stripe.android.link.ui.wallet_WalletScreenScreenshotTest_testEmptyState[].png differ
diff --git a/link/src/test/snapshots/images/com.stripe.android.link.ui.wallet_WalletScreenScreenshotTest_testExpandedState[].png b/link/src/test/snapshots/images/com.stripe.android.link.ui.wallet_WalletScreenScreenshotTest_testExpandedState[].png
new file mode 100644
index 00000000000..aa088c73d1a
Binary files /dev/null and b/link/src/test/snapshots/images/com.stripe.android.link.ui.wallet_WalletScreenScreenshotTest_testExpandedState[].png differ