Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update account item UI to support NME #8729

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.stripe.android.financialconnections.features.common

import FinancialConnectionsGenericInfoScreen
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.M
import android.view.HapticFeedbackConstants.CONTEXT_CLICK
Expand All @@ -14,7 +15,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
Expand All @@ -28,10 +28,13 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.os.ConfigurationCompat.getLocales
import com.stripe.android.financialconnections.features.common.AccountSelectionState.Disabled
import com.stripe.android.financialconnections.features.common.AccountSelectionState.Enabled
import com.stripe.android.financialconnections.model.FinancialConnectionsAccount
import com.stripe.android.financialconnections.model.FinancialConnectionsInstitution
import com.stripe.android.financialconnections.model.Image
Expand Down Expand Up @@ -63,8 +66,8 @@ internal fun AccountItem(
networkedAccount: NetworkedAccount? = null,
) {
val view = LocalView.current
// networked account's allowSelection takes precedence over the account's.
val selectable = networkedAccount?.allowSelection ?: account.allowSelection
val viewState = remember(account, networkedAccount) { getVisibilityState(account, networkedAccount) }

val shape = remember { RoundedCornerShape(12.dp) }
Box(
modifier = Modifier
Expand All @@ -78,11 +81,11 @@ internal fun AccountItem(
},
shape = shape
)
.clickableSingle(enabled = selectable) {
.clickableSingle(enabled = viewState != Disabled) {
if (SDK_INT >= M) view.performHapticFeedback(CONTEXT_CLICK)
onAccountClicked(account)
}
.alpha(if (selectable) 1f else ContentAlpha.disabled)
.alpha(viewState.alpha)
.padding(16.dp)
) {
Row(
Expand All @@ -104,7 +107,7 @@ internal fun AccountItem(
color = colors.textDefault,
style = typography.labelLargeEmphasized
)
AccountSubtitle(selectable, account, networkedAccount)
AccountSubtitle(viewState, account, networkedAccount)
}
Icon(
modifier = Modifier
Expand All @@ -118,18 +121,39 @@ internal fun AccountItem(
}
}

private fun getVisibilityState(
account: PartnerAccount,
networkedAccount: NetworkedAccount?
): AccountSelectionState = when {
// networked account's allowSelection takes precedence over the account's.
networkedAccount?.allowSelection ?: account.allowSelection -> Enabled
// Even if the account looks "not selectable", when clicking we'd display the "drawer on selection" if available.
networkedAccount?.drawerOnSelection != null -> AccountSelectionState.VisuallyDisabled
else -> Disabled
}

@Composable
private fun AccountSubtitle(
selectable: Boolean,
accountSelectionState: AccountSelectionState,
account: PartnerAccount,
networkedAccount: NetworkedAccount?,
) {
val subtitle = getSubtitle(allowSelection = selectable, account = account, networkedAccount = networkedAccount)
val subtitle = getSubtitle(
accountSelectionState = accountSelectionState,
account = account,
networkedAccount = networkedAccount
)
Row(
verticalAlignment = Alignment.CenterVertically
) {
MiddleEllipsisText(
text = subtitle ?: account.redactedAccountNumbers,
// underline there's a subtitle and the account is clickable (even if visually disabled)
textDecoration = if (accountSelectionState != Disabled && subtitle != null) {
TextDecoration.Underline
} else {
null
},
color = colors.textSubdued,
style = typography.labelMedium
)
Expand All @@ -156,12 +180,13 @@ private fun AccountSubtitle(

@Composable
private fun getSubtitle(
allowSelection: Boolean,
accountSelectionState: AccountSelectionState,
account: PartnerAccount,
networkedAccount: NetworkedAccount?,
): String? = when {
networkedAccount?.caption != null -> networkedAccount.caption
allowSelection.not() && account.allowSelectionMessage?.isNotBlank() == true -> account.allowSelectionMessage
accountSelectionState != Enabled && account.allowSelectionMessage?.isNotBlank() == true ->
account.allowSelectionMessage
else -> null
}

Expand All @@ -180,6 +205,18 @@ private fun PartnerAccount.getFormattedBalance(): String? {
}
}

private enum class AccountSelectionState(val alpha: Float) {
Enabled(alpha = 1f),
Disabled(alpha = VISUALLY_DISABLED_ALPHA),
VisuallyDisabled(alpha = VISUALLY_DISABLED_ALPHA)
}

/**
* Using a more visible alpha (instead of ContentAlpha.disabled)
* since disabled accounts are still clickable (they can show a drawer on selection)
*/
private const val VISUALLY_DISABLED_ALPHA = 0.6f

@Composable
@Preview
internal fun AccountItemPreview() {
Expand All @@ -194,7 +231,35 @@ internal fun AccountItemPreview() {
onAccountClicked = { },
account = PartnerAccount(
id = "id",
name = "Regular Checking (Unselected)",
name = "Regular Checking",
allowSelectionMessage = "allowSelectionMessage",
institution = FinancialConnectionsInstitution(
id = "id",
name = "Bank of America",
featured = false,
mobileHandoffCapable = false,
icon = Image(default = "www.image.com")
),
authorization = "",
currency = "USD",
category = FinancialConnectionsAccount.Category.CASH,
subcategory = FinancialConnectionsAccount.Subcategory.CHECKING,
supportedPaymentMethodTypes = emptyList(),
balanceAmount = 100,
),
networkedAccount = NetworkedAccount(
id = "id",
caption = "With some caption",
allowSelection = true,
icon = Image(default = "www.image.com")
)
)
AccountItem(
selected = false,
onAccountClicked = { },
account = PartnerAccount(
id = "id",
name = "Regular Checking",
allowSelectionMessage = "allowSelectionMessage",
institution = FinancialConnectionsInstitution(
id = "id",
Expand Down Expand Up @@ -294,6 +359,37 @@ internal fun AccountItemPreview() {
icon = Image(default = "www.image.com")
)
)
AccountItem(
selected = false,
onAccountClicked = { },
account = PartnerAccount(
id = "id",
name = "Manually entered (Disabled)",
_allowSelection = false,
allowSelectionMessage = "Visually disabled but clickable",
institution = FinancialConnectionsInstitution(
id = "id",
name = "Bank of America",
featured = false,
mobileHandoffCapable = false,
icon = Image(default = "www.image.com")
),
authorization = "",
currency = "USD",
category = FinancialConnectionsAccount.Category.CASH,
subcategory = FinancialConnectionsAccount.Subcategory.CHECKING,
supportedPaymentMethodTypes = emptyList(),
balanceAmount = 100,
),
networkedAccount = NetworkedAccount(
id = "id",
allowSelection = false,
icon = Image(default = "www.image.com"),
drawerOnSelection = FinancialConnectionsGenericInfoScreen(
id = "id",
)
)
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,15 @@ internal class LinkAccountPickerViewModel @AssistedInject constructor(
fun onAccountClick(partnerAccount: PartnerAccount) {
logAccountClick(partnerAccount)

val accounts = requireNotNull(stateFlow.value.payload()?.accounts)
val drawerOnSelection = accounts.firstOrNull { it.first.id == partnerAccount.id }?.second?.drawerOnSelection

if (drawerOnSelection != null) {
// TODO@carlosmuvi handle drawer display.
logger.debug("Drawer on selection: $drawerOnSelection")
return
}

val updateRequired = createUpdateRequiredContent(
partnerAccount = partnerAccount,
partnerToCoreAuths = stateFlow.value.payload()?.partnerToCoreAuths,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,6 @@ internal data class FinancialConnectionsGenericInfoScreen(
val id: String,
val label: String,
val icon: ImageResponse? = null,
val action: String? = null,
@SerialName("test_id")
val testId: String? = null
) : Parcelable
}

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading