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

[FC] Removes mavericks from repositories and more panes. #8154

Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
1 change: 1 addition & 0 deletions financial-connections/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<ID>MaximumLineLength:com.stripe.android.financialconnections.presentation.FinancialConnectionsUrls.kt:41</ID>
<ID>MaximumLineLength:com.stripe.android.financialconnections.presentation.FinancialConnectionsUrls.kt:46</ID>
<ID>MaximumLineLength:com.stripe.android.financialconnections.presentation.FinancialConnectionsUrls.kt:9</ID>
<ID>NestedBlockDepth:AccountPickerViewModel.kt$AccountPickerViewModel$fun onAccountClicked(account: PartnerAccount)</ID>
<ID>NestedBlockDepth:InstitutionPickerScreen.kt$private fun LazyListScope.searchResults( isInputEmpty: Boolean, payload: Payload, selectedInstitutionId: String?, onInstitutionSelected: (FinancialConnectionsInstitution, Boolean) -> Unit, institutions: Async&lt;InstitutionResponse>, onManualEntryClick: () -> Unit, onSearchMoreClick: () -> Unit )</ID>
<ID>SwallowedException:PollAttachPaymentAccount.kt$PollAttachPaymentAccount$e: StripeException</ID>
<ID>SwallowedException:PollAuthorizationSessionAccounts.kt$PollAuthorizationSessionAccounts$e: StripeException</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope
import com.stripe.android.financialconnections.core.Async.Fail
import com.stripe.android.financialconnections.core.Async.Loading
import com.stripe.android.financialconnections.core.Async.Success
import com.stripe.android.financialconnections.core.Async.Uninitialized
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
Expand All @@ -23,31 +24,28 @@ internal abstract class FinancialConnectionsViewModel<S>(
val stateFlow: StateFlow<S> = _stateFlow.asStateFlow()

protected open fun <T : Any?> (suspend () -> T).execute(
onSuccess: (T) -> Unit = {},
onFail: (Throwable) -> Unit = {},
reducer: S.(Async<T>) -> S
retainValue: KProperty1<S, Async<T>>? = null,
reducer: S.(Async<T>) -> S,
): Job {
return viewModelScope.launch {
setState { reducer(Loading) }
setState { reducer(Loading(value = retainValue?.get(this)?.invoke())) }
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took this from mavericks: This allows us to optionally keep the current Async value if moving to a Loading state from Success (so that we can keep the screen content while loading)

val result = runCatching { this@execute() }
// update state.
result.fold(
onSuccess = { data ->
setState { reducer(Success(data)) }
onSuccess(data)
},
onFailure = { throwable ->
setState { reducer(Fail(throwable)) }
onFail(throwable)
}
)
}
}

protected open fun <T> onAsync(
prop: KProperty1<S, Async<T>>,
onSuccess: (T) -> Unit = {},
onFail: (Throwable) -> Unit = {}
onSuccess: suspend (T) -> Unit = {},
onFail: suspend (Throwable) -> Unit = {}
) {
viewModelScope.launch {
stateFlow.map { prop.get(it) }
Expand All @@ -56,8 +54,8 @@ internal abstract class FinancialConnectionsViewModel<S>(
when (async) {
is Success -> onSuccess(async())
is Fail -> onFail(async.error)
Loading -> Unit
Async.Uninitialized -> Unit
is Loading -> Unit
Uninitialized -> Unit
}
}
}
Expand All @@ -70,7 +68,7 @@ internal sealed class Async<out T>(
private val value: T?
) {
data object Uninitialized : Async<Nothing>(value = null)
data object Loading : Async<Nothing>(value = null)
data class Loading<T>(val value: T? = null) : Async<T>(value = value)
data class Success<out T>(private val value: T) : Async<T>(value = value) {
override operator fun invoke(): T = value
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,19 +153,11 @@ internal interface FinancialConnectionsSheetNativeModule {

@Singleton
@Provides
fun providesSaveToLinkWithStripeSucceededRepository(
@IOContext workContext: CoroutineContext
): SuccessContentRepository = SuccessContentRepositoryImpl(
CoroutineScope(SupervisorJob() + workContext)
)
fun providesSaveToLinkWithStripeSucceededRepository(): SuccessContentRepository = SuccessContentRepositoryImpl()

@Singleton
@Provides
fun providesFinancialConnectionsErrorRepository(
@IOContext workContext: CoroutineContext
) = FinancialConnectionsErrorRepository(
CoroutineScope(SupervisorJob() + workContext)
)
fun providesFinancialConnectionsErrorRepository() = FinancialConnectionsErrorRepository()

@Singleton
@Provides
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.stripe.android.financialconnections.features.accountpicker

import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.stripe.android.core.exception.APIException
import com.stripe.android.financialconnections.core.Async.Fail
import com.stripe.android.financialconnections.core.Async.Loading
import com.stripe.android.financialconnections.core.Async.Success
import com.stripe.android.financialconnections.exception.AccountNoneEligibleForPaymentMethodError
import com.stripe.android.financialconnections.features.accountpicker.AccountPickerState.SelectionMode
import com.stripe.android.financialconnections.features.common.MerchantDataAccessModel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
Expand All @@ -31,14 +32,13 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.compose.collectAsState
import com.airbnb.mvrx.compose.mavericksViewModel
import com.stripe.android.financialconnections.R
import com.stripe.android.financialconnections.core.Async
import com.stripe.android.financialconnections.core.Async.Fail
import com.stripe.android.financialconnections.core.Async.Loading
import com.stripe.android.financialconnections.core.Async.Success
import com.stripe.android.financialconnections.core.Async.Uninitialized
import com.stripe.android.financialconnections.core.paneViewModel
import com.stripe.android.financialconnections.exception.AccountLoadError
import com.stripe.android.financialconnections.exception.AccountNoneEligibleForPaymentMethodError
import com.stripe.android.financialconnections.features.accountpicker.AccountPickerClickableText.DATA
Expand Down Expand Up @@ -68,9 +68,9 @@ import kotlinx.coroutines.launch

@Composable
internal fun AccountPickerScreen() {
val viewModel: AccountPickerViewModel = mavericksViewModel()
val viewModel: AccountPickerViewModel = paneViewModel { AccountPickerViewModel.factory(it) }
val parentViewModel = parentViewModel()
val state: State<AccountPickerState> = viewModel.collectAsState()
val state: State<AccountPickerState> = viewModel.stateFlow.collectAsState()
BackHandler(true) {}

val bottomSheetState = rememberModalBottomSheetState(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package com.stripe.android.financialconnections.features.accountpicker

import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.MavericksViewModel
import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.stripe.android.core.Logger
import com.stripe.android.financialconnections.FinancialConnections
import com.stripe.android.financialconnections.analytics.FinancialConnectionsAnalyticsEvent.AccountSelected
Expand All @@ -19,6 +16,11 @@ import com.stripe.android.financialconnections.analytics.FinancialConnectionsAna
import com.stripe.android.financialconnections.analytics.FinancialConnectionsAnalyticsTracker
import com.stripe.android.financialconnections.analytics.FinancialConnectionsEvent.Name
import com.stripe.android.financialconnections.analytics.logError
import com.stripe.android.financialconnections.core.Async
import com.stripe.android.financialconnections.core.Async.Loading
import com.stripe.android.financialconnections.core.Async.Uninitialized
import com.stripe.android.financialconnections.core.FinancialConnectionsViewModel
import com.stripe.android.financialconnections.di.FinancialConnectionsSheetNativeComponent
import com.stripe.android.financialconnections.domain.GetOrFetchSync
import com.stripe.android.financialconnections.domain.PollAuthorizationSessionAccounts
import com.stripe.android.financialconnections.domain.SelectAccounts
Expand All @@ -35,7 +37,6 @@ import com.stripe.android.financialconnections.navigation.Destination.ManualEntr
import com.stripe.android.financialconnections.navigation.Destination.Reset
import com.stripe.android.financialconnections.navigation.NavigationManager
import com.stripe.android.financialconnections.navigation.destination
import com.stripe.android.financialconnections.ui.FinancialConnectionsSheetNativeActivity
import com.stripe.android.financialconnections.ui.HandleClickableUrl
import com.stripe.android.financialconnections.utils.measureTimeMillis
import kotlinx.coroutines.launch
Expand All @@ -51,7 +52,7 @@ internal class AccountPickerViewModel @Inject constructor(
private val handleClickableUrl: HandleClickableUrl,
private val logger: Logger,
private val pollAuthorizationSessionAccounts: PollAuthorizationSessionAccounts
) : MavericksViewModel<AccountPickerState>(initialState) {
) : FinancialConnectionsViewModel<AccountPickerState>(initialState) {

init {
logErrors()
Expand All @@ -61,7 +62,7 @@ internal class AccountPickerViewModel @Inject constructor(

private fun loadAccounts() {
suspend {
val state = awaitState()
val state = stateFlow.value
val sync = getOrFetchSync()
val dataAccessNotice = sync.text?.consent?.dataAccessNotice
val manifest = sync.manifest
Expand Down Expand Up @@ -180,7 +181,7 @@ internal class AccountPickerViewModel @Inject constructor(
)
}

fun onAccountClicked(account: PartnerAccount) = withState { state ->
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about adding a withState method to our base ViewModel?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fun onAccountClicked(account: PartnerAccount) = stateFlow.value.let { state ->
state.payload()?.let { payload ->
val selectedIds = state.selectedIds
val newSelectedIds = when (payload.selectionMode) {
Expand Down Expand Up @@ -236,7 +237,7 @@ internal class AccountPickerViewModel @Inject constructor(
eventTracker.track(ClickLinkAccounts(PANE))
}
FinancialConnections.emitEvent(name = Name.ACCOUNTS_SELECTED)
withState { state ->
stateFlow.value.let { state ->
state.payload()?.let {
submitAccounts(
selectedIds = state.selectedIds,
Expand Down Expand Up @@ -308,21 +309,18 @@ internal class AccountPickerViewModel @Inject constructor(
setState { copy(viewEffect = null) }
}

companion object :
MavericksViewModelFactory<AccountPickerViewModel, AccountPickerState> {

override fun create(
viewModelContext: ViewModelContext,
state: AccountPickerState
): AccountPickerViewModel {
return viewModelContext.activity<FinancialConnectionsSheetNativeActivity>()
.viewModel
.activityRetainedComponent
.accountPickerBuilder
.initialState(state)
.build()
.viewModel
}
companion object {

fun factory(parentComponent: FinancialConnectionsSheetNativeComponent): ViewModelProvider.Factory =
viewModelFactory {
initializer {
parentComponent
.accountPickerBuilder
.initialState(AccountPickerState())
.build()
.viewModel
}
}

private val PANE = Pane.ACCOUNT_PICKER
}
Expand All @@ -334,7 +332,7 @@ internal data class AccountPickerState(
val selectAccounts: Async<PartnerAccountsList> = Uninitialized,
val selectedIds: Set<String> = emptySet(),
val viewEffect: ViewEffect? = null
) : MavericksState {
) {

val submitLoading: Boolean
get() = payload is Loading || selectAccounts is Loading
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ package com.stripe.android.financialconnections.features.bankauthrepair

import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import com.airbnb.mvrx.compose.collectAsState
import com.airbnb.mvrx.compose.mavericksViewModel
import androidx.compose.runtime.collectAsState
import com.stripe.android.financialconnections.core.paneViewModel
import com.stripe.android.financialconnections.features.common.SharedPartnerAuth
import com.stripe.android.financialconnections.features.partnerauth.SharedPartnerAuthState

@Composable
internal fun BankAuthRepairScreen() {
// step view model
val viewModel: BankAuthRepairViewModel = mavericksViewModel()
val state: State<SharedPartnerAuthState> = viewModel.collectAsState()
val viewModel: BankAuthRepairViewModel = paneViewModel { BankAuthRepairViewModel.factory(it) }
val state: State<SharedPartnerAuthState> = viewModel.stateFlow.collectAsState()

SharedPartnerAuth(
state = state.value,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,30 @@
package com.stripe.android.financialconnections.features.bankauthrepair

import com.airbnb.mvrx.MavericksViewModel
import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.stripe.android.financialconnections.core.FinancialConnectionsViewModel
import com.stripe.android.financialconnections.di.FinancialConnectionsSheetNativeComponent
import com.stripe.android.financialconnections.features.partnerauth.SharedPartnerAuthState
import com.stripe.android.financialconnections.model.FinancialConnectionsSessionManifest.Pane
import com.stripe.android.financialconnections.ui.FinancialConnectionsSheetNativeActivity
import javax.inject.Inject

internal class BankAuthRepairViewModel @Inject constructor(
initialState: SharedPartnerAuthState
) : MavericksViewModel<SharedPartnerAuthState>(initialState) {
) : FinancialConnectionsViewModel<SharedPartnerAuthState>(initialState) {

internal companion object :
MavericksViewModelFactory<BankAuthRepairViewModel, SharedPartnerAuthState> {
internal companion object {

override fun initialState(viewModelContext: ViewModelContext) =
SharedPartnerAuthState(pane = PANE)

override fun create(
viewModelContext: ViewModelContext,
state: SharedPartnerAuthState
): BankAuthRepairViewModel {
return viewModelContext.activity<FinancialConnectionsSheetNativeActivity>()
.viewModel
.activityRetainedComponent
.bankAuthRepairSubcomponent
.initialState(state)
.build()
.viewModel
}
fun factory(parentComponent: FinancialConnectionsSheetNativeComponent): ViewModelProvider.Factory =
viewModelFactory {
initializer {
parentComponent
.bankAuthRepairSubcomponent
.initialState(SharedPartnerAuthState(pane = PANE))
.build()
.viewModel
}
}

val PANE = Pane.BANK_AUTH_REPAIR
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.stripe.android.financialconnections.R
import com.stripe.android.financialconnections.core.Async
import com.stripe.android.financialconnections.core.Async.Fail
import com.stripe.android.financialconnections.core.Async.Loading
import com.stripe.android.financialconnections.core.Async.Success
import com.stripe.android.financialconnections.core.Async.Uninitialized
import com.stripe.android.financialconnections.core.collectAsState
import com.stripe.android.financialconnections.features.partnerauth.PartnerAuthPreviewParameterProvider
import com.stripe.android.financialconnections.features.partnerauth.SharedPartnerAuthState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ private fun ConsentContent(
onCloseFromErrorClick: (Throwable) -> Unit
) {
when (val result = state.consent) {
Uninitialized, Loading -> ConsentLoadingContent()
Uninitialized,
is Loading -> ConsentLoadingContent()

is Success -> LoadedContent(
payload = result(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal class InstitutionPickerPreviewParameterProvider :
private fun initialLoading() = InstitutionPreviewState(
state = InstitutionPickerState(
previewText = null,
payload = Loading,
payload = Loading(),
searchInstitutions = Uninitialized,
),
initialScroll = 0
Expand All @@ -47,7 +47,7 @@ internal class InstitutionPickerPreviewParameterProvider :
state = InstitutionPickerState(
previewText = "Some query",
payload = Success(payload()),
searchInstitutions = Loading,
searchInstitutions = Loading(),
),
initialScroll = 0
)
Expand Down Expand Up @@ -122,7 +122,7 @@ internal class InstitutionPickerPreviewParameterProvider :
payload = Success(payload()),
searchInstitutions = Success(institutionResponse(FEW_INSTITUTIONS)),
selectedInstitutionId = "2",
createSessionForInstitution = Loading,
createSessionForInstitution = Loading(),
),
initialScroll = 0
)
Expand Down
Loading
Loading