Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
9 changes: 8 additions & 1 deletion core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,8 @@ public final class com/adyen/checkout/core/components/CheckoutController {
}

public abstract interface class com/adyen/checkout/core/components/CheckoutControllerInterface {
public abstract fun submit (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun submit ()V
public abstract fun validate ()Z
}

public final class com/adyen/checkout/core/components/CheckoutControllerKt {
Expand Down Expand Up @@ -2031,6 +2032,12 @@ public final class com/adyen/checkout/core/components/internal/data/model/Status
public final class com/adyen/checkout/core/components/internal/data/provider/DefaultSdkDataProvider$Companion {
}

public abstract interface class com/adyen/checkout/core/components/internal/ui/NewPaymentComponent {
public abstract fun getState ()Lcom/adyen/checkout/core/components/paymentmethod/PaymentComponentState;
public abstract fun setLoading (Z)V
public abstract fun validate ()Z
}

public final class com/adyen/checkout/core/components/internal/ui/state/model/RequirementPolicy$Hidden : com/adyen/checkout/core/components/internal/ui/state/model/RequirementPolicy {
public static final field $stable I
public static final field INSTANCE Lcom/adyen/checkout/core/components/internal/ui/state/model/RequirementPolicy$Hidden;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,33 @@
import com.adyen.checkout.core.common.CheckoutContext
import com.adyen.checkout.core.components.data.model.paymentmethod.PaymentMethodsApiResponse
import com.adyen.checkout.core.components.internal.CheckoutControllerState
import com.adyen.checkout.core.components.paymentmethod.PaymentComponentState
import com.adyen.checkout.core.components.internal.ui.NewPaymentComponent
import com.adyen.checkout.core.error.CheckoutError
import com.adyen.checkout.core.error.CheckoutException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

// TODO - rename later
interface CheckoutControllerInterface {
suspend fun submit()
fun validate(): Boolean
fun submit()
}

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
class NewCheckoutController(
private val target: CheckoutTarget,
private val context: CheckoutContext,
@Suppress("unused")
private val callbacks: CheckoutCallbacks,
private val coroutineScope: CoroutineScope,
) : CheckoutControllerInterface {

private val _state = MutableStateFlow(createInitialState())
internal val state: StateFlow<CheckoutControllerState> = _state.asStateFlow()

private var componentStateFlow: StateFlow<PaymentComponentState<*>>? = null
private var paymentComponent: NewPaymentComponent? = null

private fun createInitialState(): CheckoutControllerState {
return when (target) {
Expand Down Expand Up @@ -68,16 +71,34 @@
}
}

fun registerComponentState(flow: StateFlow<PaymentComponentState<*>>) {
componentStateFlow = flow
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun attach(component: NewPaymentComponent) {
paymentComponent = component
}

// TODO - Ensure state is valid, handle state being null, add validate function and support sessions
override suspend fun submit() {
if (_state.value is CheckoutControllerState.PaymentMethod) {
componentStateFlow?.value?.let {
callbacks.beforeSubmit?.beforeSubmit(it)
callbacks.onSubmit?.onSubmit(it)
override fun validate(): Boolean {
return paymentComponent?.validate() ?: false
}

// TODO - Support sessions

Check warning on line 83 in core/src/main/java/com/adyen/checkout/core/components/NewCheckoutController.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=Adyen_adyen-android&issues=AZz3S_uWo-aJzDrw2K8t&open=AZz3S_uWo-aJzDrw2K8t&pullRequest=2635
override fun submit() {
val component = paymentComponent

if (component == null) {
val error = CheckoutError(
code = CheckoutError.ErrorCode.UNKNOWN,
message = "Submit should not be called when there is no component attached",
)
callbacks.onError?.onError(error)
return
}

if (validate()) {
coroutineScope.launch {
val componentState = component.getState()
component.setLoading(true)
callbacks.onSubmit?.onSubmit(componentState)
component.setLoading(false)
Comment thread
OscarSpruit marked this conversation as resolved.
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package com.adyen.checkout.core.components.internal.ui
import androidx.annotation.RestrictTo
import com.adyen.checkout.core.components.internal.BasePaymentComponentState
import com.adyen.checkout.core.components.internal.PaymentComponentEvent
import com.adyen.checkout.core.components.paymentmethod.PaymentComponentState

// TODO - Some components might not be composable,
// Move ComposableComponent to PaymentMethod specific component later
Expand All @@ -25,3 +26,12 @@ interface PaymentComponent<T : BasePaymentComponentState> :

fun onCleared()
}

interface NewPaymentComponent {

fun validate(): Boolean

fun setLoading(isLoading: Boolean)

fun getState(): PaymentComponentState<*>
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
Expand Down Expand Up @@ -118,12 +119,14 @@ private fun Component(
)
}

val coroutineScope = rememberCoroutineScope()
CheckoutPaymentFlow(
controller = remember(selectedPaymentMethod) {
NewCheckoutController(
target = CheckoutTarget.PaymentMethod(selectedPaymentMethod.type),
context = uiState.checkoutContext,
callbacks = uiState.checkoutCallbacks,
coroutineScope = coroutineScope,
)
},
theme = theme,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,18 @@

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.adyen.checkout.core.components.NewCheckoutController
import com.adyen.checkout.core.components.internal.ui.NewPaymentComponent
import com.adyen.checkout.core.components.internal.ui.state.ComponentStateFlow
import com.adyen.checkout.core.components.internal.ui.state.viewState
import com.adyen.checkout.core.components.paymentmethod.PaymentComponentState
import com.adyen.checkout.mbway.internal.ui.state.MBWayComponentStateFactory
import com.adyen.checkout.mbway.internal.ui.state.MBWayComponentStateReducer
import com.adyen.checkout.mbway.internal.ui.state.MBWayComponentStateValidator
import com.adyen.checkout.mbway.internal.ui.state.MBWayIntent
import com.adyen.checkout.mbway.internal.ui.state.MBWayViewStateProducer
import com.adyen.checkout.mbway.internal.ui.state.toPaymentComponentState
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

internal class MBWayViewModel(
private val controller: NewCheckoutController,
// TODO - Add constructor params
// private val componentParams: ComponentParams,
// private val analyticsManager: AnalyticsManager,
Expand All @@ -34,7 +30,7 @@
componentStateFactory: MBWayComponentStateFactory,
componentStateReducer: MBWayComponentStateReducer,
viewStateProducer: MBWayViewStateProducer,
) : ViewModel() {
) : ViewModel(), NewPaymentComponent {

private val componentState = ComponentStateFlow(
initialState = componentStateFactory.createInitialState(),
Expand All @@ -46,33 +42,28 @@
val viewState = componentState.viewState(viewStateProducer, viewModelScope)

init {
val paymentComponentStateFlow = componentState
.map { it.toPaymentComponentState(amount = null, sdkData = "") }
.stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
initialValue = componentState.value.toPaymentComponentState(amount = null, sdkData = ""),
)

controller.registerComponentState(paymentComponentStateFlow)

initializeAnalytics()
}

private fun initializeAnalytics() {
// analyticsManager.initialize(this, viewModelScope)
}

fun submit() {
if (componentStateValidator.isValid(componentState.value)) {
viewModelScope.launch {
componentState.handleIntent(MBWayIntent.UpdateLoading(true))
controller.submit()
componentState.handleIntent(MBWayIntent.UpdateLoading(false))
}
} else {
onIntent(MBWayIntent.HighlightValidationErrors)
}
override fun validate(): Boolean {
onIntent(MBWayIntent.HighlightValidationErrors)
return componentStateValidator.isValid(componentState.value)
}

override fun setLoading(isLoading: Boolean) {
componentState.handleIntent(MBWayIntent.UpdateLoading(isLoading))
}

override fun getState(): PaymentComponentState<*> {
// TODO - Use actual amount and sdkData

Check warning on line 62 in mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/MBWayViewModel.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=Adyen_adyen-android&issues=AZz3S_xxo-aJzDrw2K8u&open=AZz3S_xxo-aJzDrw2K8u&pullRequest=2635
return componentState.value.toPaymentComponentState(
amount = null,
sdkData = null,
Comment thread
OscarSpruit marked this conversation as resolved.
)
}

fun onIntent(intent: MBWayIntent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,20 @@ internal fun MBWayComponent(
publicKey = "",
)
MBWayViewModel(
controller = controller,
componentStateFactory = MBWayComponentStateFactory(componentParams),
componentStateReducer = MBWayComponentStateReducer(),
componentStateValidator = MBWayComponentStateValidator(),
viewStateProducer = MBWayViewStateProducer(),
)
).also {
controller.attach(it)
}
}
val viewState by viewModel.viewState.collectAsStateWithLifecycle()

MBWayContent(
viewState = viewState,
onIntent = viewModel::onIntent,
onSubmitClick = viewModel::submit,
onSubmitClick = controller::submit,
onCountryCodePickerClick = { TODO() },
modifier = modifier,
)
Expand Down
Loading