Skip to content

Commit f2c0278

Browse files
committed
Support streamlined consent pane
1 parent 270b2cf commit f2c0278

File tree

17 files changed

+445
-23
lines changed

17 files changed

+445
-23
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="16dp"
3+
android:height="16dp"
4+
android:viewportWidth="16"
5+
android:viewportHeight="16">
6+
<path
7+
android:pathData="M8.75,11.25C9.054,11.022 9.25,10.659 9.25,10.25C9.25,9.56 8.69,9 8,9C7.31,9 6.75,9.56 6.75,10.25C6.75,10.659 6.946,11.022 7.25,11.25V12.25C7.25,12.664 7.586,13 8,13C8.414,13 8.75,12.664 8.75,12.25V11.25Z"
8+
android:fillColor="#474E5A" />
9+
<path
10+
android:pathData="M3.5,4V6H2.5C1.948,6 1.5,6.448 1.5,7V13C1.5,14.657 2.843,16 4.5,16H11.5C13.157,16 14.5,14.657 14.5,13V7C14.5,6.448 14.052,6 13.5,6H12.5V4C12.5,1.791 10.709,0 8.5,0H7.5C5.291,0 3.5,1.791 3.5,4ZM11,6V4C11,2.619 9.881,1.5 8.5,1.5H7.5C6.119,1.5 5,2.619 5,4V6H11ZM3,13V7.5H13V13C13,13.828 12.328,14.5 11.5,14.5H4.5C3.672,14.5 3,13.828 3,13Z"
11+
android:fillColor="#474E5A"
12+
android:fillType="evenOdd" />
13+
</vector>

financial-connections/src/main/java/com/stripe/android/financialconnections/di/FinancialConnectionsSheetNativeComponent.kt

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.stripe.android.financialconnections.features.networkingsavetolinkveri
2121
import com.stripe.android.financialconnections.features.notice.NoticeSheetViewModel
2222
import com.stripe.android.financialconnections.features.partnerauth.PartnerAuthViewModel
2323
import com.stripe.android.financialconnections.features.reset.ResetViewModel
24+
import com.stripe.android.financialconnections.features.streamlinedconsent.StreamlinedConsentViewModel
2425
import com.stripe.android.financialconnections.features.success.SuccessViewModel
2526
import com.stripe.android.financialconnections.model.SynchronizeSessionResponse
2627
import com.stripe.android.financialconnections.presentation.FinancialConnectionsSheetNativeState
@@ -44,6 +45,7 @@ internal interface FinancialConnectionsSheetNativeComponent {
4445
val viewModel: FinancialConnectionsSheetNativeViewModel
4546

4647
val consentViewModelFactory: ConsentViewModel.Factory
48+
val streamlinedConsentViewModelFactory: StreamlinedConsentViewModel.Factory
4749
val institutionPickerViewModelFactory: InstitutionPickerViewModel.Factory
4850
val accountPickerViewModelFactory: AccountPickerViewModel.Factory
4951
val manualEntryViewModelFactory: ManualEntryViewModel.Factory

financial-connections/src/main/java/com/stripe/android/financialconnections/features/common/ShapedIcon.kt

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.stripe.android.financialconnections.features.common
22

3+
import androidx.compose.foundation.Image
34
import androidx.compose.foundation.background
45
import androidx.compose.foundation.layout.Box
56
import androidx.compose.foundation.layout.size
@@ -10,6 +11,7 @@ import androidx.compose.ui.Alignment
1011
import androidx.compose.ui.Modifier
1112
import androidx.compose.ui.draw.clip
1213
import androidx.compose.ui.graphics.Shape
14+
import androidx.compose.ui.graphics.painter.ColorPainter
1315
import androidx.compose.ui.graphics.painter.Painter
1416
import androidx.compose.ui.layout.ContentScale
1517
import androidx.compose.ui.res.painterResource
@@ -90,6 +92,13 @@ internal fun ShapedIcon(
9092
)
9193
}
9294
},
95+
loadingContent = {
96+
Image(
97+
modifier = modifier,
98+
contentDescription = contentDescription,
99+
painter = ColorPainter(color = colors.background),
100+
)
101+
},
93102
contentScale = ContentScale.Crop
94103
)
95104
}

financial-connections/src/main/java/com/stripe/android/financialconnections/features/generic/GenericScreen.kt

+42-7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import FinancialConnectionsGenericInfoScreen.Footer
77
import FinancialConnectionsGenericInfoScreen.Header
88
import Size
99
import android.util.Log
10+
import androidx.compose.foundation.background
1011
import androidx.compose.foundation.layout.Arrangement
1112
import androidx.compose.foundation.layout.Box
1213
import androidx.compose.foundation.layout.Column
@@ -22,17 +23,21 @@ import androidx.compose.material.Text
2223
import androidx.compose.runtime.Composable
2324
import androidx.compose.runtime.remember
2425
import androidx.compose.ui.Modifier
26+
import androidx.compose.ui.draw.clip
27+
import androidx.compose.ui.draw.shadow
2528
import androidx.compose.ui.text.style.TextAlign
2629
import androidx.compose.ui.tooling.preview.Preview
2730
import androidx.compose.ui.tooling.preview.PreviewParameter
2831
import androidx.compose.ui.unit.dp
2932
import com.stripe.android.financialconnections.features.common.IconSize
33+
import com.stripe.android.financialconnections.features.common.ListItem
3034
import com.stripe.android.financialconnections.features.common.ShapedIcon
3135
import com.stripe.android.financialconnections.ui.FinancialConnectionsPreview
3236
import com.stripe.android.financialconnections.ui.LocalImageLoader
3337
import com.stripe.android.financialconnections.ui.TextResource
3438
import com.stripe.android.financialconnections.ui.components.AnnotatedText
3539
import com.stripe.android.financialconnections.ui.components.FinancialConnectionsButton
40+
import com.stripe.android.financialconnections.ui.sdui.BulletUI
3641
import com.stripe.android.financialconnections.ui.sdui.fromHtml
3742
import com.stripe.android.financialconnections.ui.theme.FinancialConnectionsTheme.colors
3843
import com.stripe.android.financialconnections.ui.theme.FinancialConnectionsTheme.typography
@@ -133,6 +138,23 @@ internal fun GenericBody(
133138
modifier = Modifier.padding(horizontal = 24.dp),
134139
)
135140
}
141+
is Body.Entry.Bullets -> {
142+
val bullets = remember(entry.bullets) {
143+
entry.bullets.map(BulletUI::from)
144+
}
145+
146+
Column(
147+
modifier = Modifier.padding(horizontal = 24.dp),
148+
) {
149+
bullets.forEach { bullet ->
150+
ListItem(
151+
bullet = bullet,
152+
onClickableTextClick = onClickableTextClick
153+
)
154+
Spacer(modifier = Modifier.size(24.dp))
155+
}
156+
}
157+
}
136158
else -> {
137159
Log.e("GenericBody", "Unsupported entry type: $entry")
138160
}
@@ -148,16 +170,29 @@ internal fun GenericHeader(
148170
onClickableTextClick: (String) -> Unit,
149171
modifier: Modifier = Modifier,
150172
) {
151-
val isBrandIcon: Boolean =
152-
remember(payload.icon?.default) { payload.icon?.default?.contains("BrandIcon") == true }
173+
val isBrandIcon: Boolean = remember(payload.icon?.default) {
174+
payload.icon?.default?.contains("BrandIcon") == true
175+
}
176+
153177
Column(
154178
modifier = modifier.fillMaxWidth(),
155179
verticalArrangement = Arrangement.spacedBy(20.dp),
156180
) {
181+
val iconShape = if (isBrandIcon) RoundedCornerShape(12.dp) else CircleShape
182+
183+
val iconModifier = if (isBrandIcon) {
184+
Modifier
185+
.shadow(8.dp, iconShape)
186+
.clip(iconShape)
187+
.background(color = colors.backgroundSecondary, shape = iconShape)
188+
} else {
189+
Modifier
190+
}
191+
157192
payload.icon?.default?.let { iconUrl ->
158193
ShapedIcon(
159-
modifier = Modifier.align(payload.alignment.toComposeAlignment()),
160-
backgroundShape = if (isBrandIcon) RoundedCornerShape(12.dp) else CircleShape,
194+
modifier = iconModifier.align(payload.alignment.toComposeAlignment()),
195+
backgroundShape = iconShape,
161196
flushed = isBrandIcon,
162197
url = iconUrl,
163198
contentDescription = null,
@@ -210,7 +245,7 @@ internal fun GenericFooter(
210245
onClickableTextClick = onClickableTextClick,
211246
defaultStyle = typography.labelSmall.copy(
212247
color = colors.textDefault,
213-
textAlign = TextAlign.Start,
248+
textAlign = TextAlign.Center,
214249
),
215250
)
216251
}
@@ -234,11 +269,11 @@ internal fun GenericFooter(
234269
payload.belowCta?.let { belowCta ->
235270
AnnotatedText(
236271
modifier = Modifier.fillMaxWidth(),
237-
text = TextResource.Text(fromHtml(belowCta.label)),
272+
text = TextResource.Text(fromHtml(belowCta)),
238273
onClickableTextClick = onClickableTextClick,
239274
defaultStyle = typography.labelSmall.copy(
240275
color = colors.textDefault,
241-
textAlign = TextAlign.Start,
276+
textAlign = TextAlign.Center,
242277
),
243278
)
244279
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.stripe.android.financialconnections.features.streamlinedconsent
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.getValue
5+
import com.stripe.android.financialconnections.features.common.FullScreenGenericLoading
6+
import com.stripe.android.financialconnections.features.common.UnclassifiedErrorContent
7+
import com.stripe.android.financialconnections.features.generic.GenericScreen
8+
import com.stripe.android.financialconnections.presentation.Async.Fail
9+
import com.stripe.android.financialconnections.presentation.Async.Loading
10+
import com.stripe.android.financialconnections.presentation.Async.Success
11+
import com.stripe.android.financialconnections.presentation.Async.Uninitialized
12+
import com.stripe.android.financialconnections.presentation.paneViewModel
13+
import com.stripe.android.financialconnections.presentation.parentViewModel
14+
import com.stripe.android.uicore.utils.collectAsState
15+
16+
@Composable
17+
fun StreamlinedConsent() {
18+
val viewModel: StreamlinedConsentViewModel = paneViewModel(StreamlinedConsentViewModel::factory)
19+
val parentViewModel = parentViewModel()
20+
21+
val state by viewModel.stateFlow.collectAsState()
22+
23+
StreamlinedConsentContent(
24+
state = state,
25+
onPrimaryButtonClick = viewModel::onContinueClick,
26+
onSecondaryButtonClick = {
27+
// There is no secondary button
28+
},
29+
onClickableTextClick = viewModel::onClickableTextClick,
30+
onCloseFromErrorClick = parentViewModel::onCloseFromErrorClick,
31+
)
32+
}
33+
34+
@Composable
35+
private fun StreamlinedConsentContent(
36+
state: StreamlinedConsentState,
37+
onPrimaryButtonClick: () -> Unit,
38+
onSecondaryButtonClick: () -> Unit,
39+
onClickableTextClick: (String) -> Unit,
40+
onCloseFromErrorClick: (Throwable) -> Unit,
41+
) {
42+
when (val result = state.payload) {
43+
is Uninitialized,
44+
is Loading -> {
45+
FullScreenGenericLoading()
46+
}
47+
is Success -> {
48+
GenericScreen(
49+
state = result().genericScreenState,
50+
onPrimaryButtonClick = onPrimaryButtonClick,
51+
onSecondaryButtonClick = onSecondaryButtonClick,
52+
onClickableTextClick = onClickableTextClick,
53+
)
54+
}
55+
is Fail -> {
56+
UnclassifiedErrorContent(
57+
onCtaClick = { onCloseFromErrorClick(result.error) },
58+
)
59+
}
60+
}
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.stripe.android.financialconnections.features.streamlinedconsent
2+
3+
import com.stripe.android.financialconnections.features.generic.GenericScreenState
4+
import com.stripe.android.financialconnections.model.FinancialConnectionsSessionManifest
5+
import com.stripe.android.financialconnections.model.StreamlinedConsentPane
6+
import com.stripe.android.financialconnections.presentation.Async
7+
8+
internal data class StreamlinedConsentState(
9+
val payload: Async<Payload> = Async.Uninitialized,
10+
val acceptConsent: Async<FinancialConnectionsSessionManifest> = Async.Uninitialized,
11+
val viewEffect: ViewEffect? = null,
12+
) {
13+
14+
data class Payload(
15+
val streamlinedConsent: StreamlinedConsentPane,
16+
) {
17+
18+
val genericScreenState: GenericScreenState
19+
get() = GenericScreenState(streamlinedConsent.screen, inModal = false)
20+
}
21+
22+
sealed interface ViewEffect {
23+
data class OpenUrl(
24+
val url: String,
25+
val id: Long,
26+
) : ViewEffect
27+
}
28+
}

0 commit comments

Comments
 (0)