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

[FX-722] Add support for derivedCardInfo.usState to ElementState for ForagePANEditText #133

Merged
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.joinforage.forage.android

import com.joinforage.forage.android.core.EnvConfig
import com.joinforage.forage.android.core.element.state.ElementState
import com.joinforage.forage.android.core.telemetry.CustomerPerceivedResponseMonitor
import com.joinforage.forage.android.core.telemetry.EventOutcome
import com.joinforage.forage.android.core.telemetry.Log
Expand All @@ -23,7 +24,7 @@ import java.util.UUID
*/
class ForageSDK : ForageSDKInterface {

private fun _getForageConfigOrThrow(element: AbstractForageElement): ForageConfig {
private fun <T : ElementState> _getForageConfigOrThrow(element: AbstractForageElement<T>): ForageConfig {
val context = element.getForageConfig()
return context ?: throw ForageConfigNotSetException(
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
package com.joinforage.forage.android.core.element

import com.joinforage.forage.android.core.element.state.ElementState

internal typealias SimpleElementListener = () -> Unit
internal typealias StatefulElementListener = (state: ElementState) -> Unit
internal typealias StatefulElementListener<T> = (state: T) -> Unit
Original file line number Diff line number Diff line change
@@ -1,20 +1,65 @@
package com.joinforage.forage.android.core.element.state

import com.joinforage.forage.android.core.element.ElementValidationError
import com.joinforage.forage.android.model.USState

data class ElementState(
val isFocused: Boolean,
val isBlurred: Boolean,
val isEmpty: Boolean,
val isValid: Boolean,
val isComplete: Boolean,
interface ElementState {
val isFocused: Boolean
val isBlurred: Boolean
val isEmpty: Boolean
val isValid: Boolean
val isComplete: Boolean
val validationError: ElementValidationError?
)
internal val INITIAL_ELEMENT_STATE = ElementState(
}

interface PinElementState : ElementState

data class PinElementStateDto(
override val isFocused: Boolean,
override val isBlurred: Boolean,
override val isEmpty: Boolean,
override val isValid: Boolean,
override val isComplete: Boolean,
override val validationError: ElementValidationError?
) : PinElementState

internal val INITIAL_PIN_ELEMENT_STATE = PinElementStateDto(
isFocused = false,
isBlurred = true,
isEmpty = true,
isValid = true,
isComplete = false,
validationError = null
)

interface DerivedCardInfo {
val usState: USState?
}

data class DerivedCardInfoDto(
override val usState: USState? = null
) : DerivedCardInfo

interface PanElementState : ElementState {
val derivedCardInfo: DerivedCardInfo // the interface not the DTO
}

data class PanElementStateDto(
override val isFocused: Boolean,
override val isBlurred: Boolean,
override val isEmpty: Boolean,
override val isValid: Boolean,
override val isComplete: Boolean,
override val validationError: ElementValidationError?,
override val derivedCardInfo: DerivedCardInfoDto
) : PanElementState

internal val INITIAL_PAN_ELEMENT_STATE = PanElementStateDto(
isFocused = false,
isBlurred = true,
isEmpty = true,
isValid = true,
isComplete = false,
validationError = null,
derivedCardInfo = DerivedCardInfoDto()
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,37 @@ import com.joinforage.forage.android.core.element.ElementValidationError
import com.joinforage.forage.android.core.element.SimpleElementListener
import com.joinforage.forage.android.core.element.StatefulElementListener

internal abstract class ElementStateManager(
private var isFocused: Boolean,
private var isBlurred: Boolean,
internal abstract class ElementStateManager<T : ElementState>(
// private because only this class should house focus / blur logic
private var _isFocused: Boolean,
private var _isBlurred: Boolean,

// internal because subclasses will define the logic for these
internal var isEmpty: Boolean,
internal var isValid: Boolean,
internal var isComplete: Boolean,
internal var validationError: ElementValidationError?
) {
private var onFocusEventListener: SimpleElementListener? = null
private var onBlurEventListener: SimpleElementListener? = null
internal var onChangeEventListener: StatefulElementListener? = null
internal var onChangeEventListener: StatefulElementListener<T>? = null

internal val isFocused
get() = _isFocused

internal constructor(state: ElementState) : this(
isFocused = state.isFocused,
isBlurred = state.isBlurred,
internal val isBlurred
get() = _isBlurred

internal constructor(state: T) : this(
_isFocused = state.isFocused,
_isBlurred = state.isBlurred,
isEmpty = state.isEmpty,
isValid = state.isValid,
isComplete = state.isComplete,
validationError = state.validationError
)

fun getState(): ElementState {
return ElementState(
isFocused = isFocused,
isBlurred = isBlurred,
isEmpty = isEmpty,
isValid = isValid,
isComplete = isComplete,
validationError = validationError
)
}
internal abstract fun getState(): T

fun setOnFocusEventListener(l: SimpleElementListener) {
onFocusEventListener = l
Expand All @@ -44,13 +44,13 @@ internal abstract class ElementStateManager(
onBlurEventListener = l
}

fun setOnChangeEventListener(l: StatefulElementListener) {
fun setOnChangeEventListener(l: StatefulElementListener<T>) {
onChangeEventListener = l
}

fun changeFocus(hasFocus: Boolean) {
isFocused = hasFocus
isBlurred = !hasFocus
_isFocused = hasFocus
_isBlurred = !hasFocus
if (hasFocus) {
onFocusEventListener?.invoke()
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.joinforage.forage.android.core.element.TooLongEbtPanError
import com.joinforage.forage.android.model.hasInvalidStateIIN
import com.joinforage.forage.android.model.isCorrectLength
import com.joinforage.forage.android.model.missingStateIIN
import com.joinforage.forage.android.model.queryForStateIIN
import com.joinforage.forage.android.model.tooLongForStateIIN
import com.joinforage.forage.android.model.tooShortForStateIIN

Expand Down Expand Up @@ -76,7 +77,11 @@ internal class BalanceCheckErrorCard : WhitelistedCards("5", 14)
internal class NonProdValidEbtCard : WhitelistedCards("9", 4)
internal class EmptyEbtCashBalanceCard : WhitelistedCards("654321")

internal class PanElementStateManager(state: ElementState, private val validators: Array<PanValidator>) : ElementStateManager(state) {
internal class PanElementStateManager(
state: PanElementState,
private val validators: Array<PanValidator>
) : ElementStateManager<PanElementState>(state) {
private var derivedCardInfo = DerivedCardInfoDto()

private fun checkIsValid(cardNumber: String): Boolean {
return validators.any { it.checkIfValid(cardNumber) }
Expand All @@ -89,6 +94,21 @@ internal class PanElementStateManager(state: ElementState, private val validator
.map { it.checkForValidationError(cardNumber) }
.firstOrNull { it != null }
}
private fun getDerivedCardInfo(cardNumber: String): DerivedCardInfoDto {
return DerivedCardInfoDto(queryForStateIIN(cardNumber)?.publicEnum)
}

override fun getState(): PanElementState {
return PanElementStateDto(
isFocused = this.isFocused,
isBlurred = this.isBlurred,
isEmpty = this.isEmpty,
isValid = this.isValid,
isComplete = this.isComplete,
validationError = this.validationError,
derivedCardInfo = this.derivedCardInfo
)
}

fun handleChangeEvent(rawInput: String) {
// because the input may be formatted, we need to
Expand All @@ -97,10 +117,13 @@ internal class PanElementStateManager(state: ElementState, private val validator

// check to see if any of the validators believe the
// card to be valid
this.isValid = checkIsValid(newCardNumber)
this.isComplete = checkIfComplete(newCardNumber)
this.validationError = checkForValidationError(newCardNumber)
this.isEmpty = newCardNumber.isEmpty()
isEmpty = newCardNumber.isEmpty()
isValid = checkIsValid(newCardNumber)
isComplete = checkIfComplete(newCardNumber)
validationError = checkForValidationError(newCardNumber)

// update state details based on newCardNumber
derivedCardInfo = getDerivedCardInfo(newCardNumber)

// invoke the registered listener with the updated state
onChangeEventListener?.invoke(getState())
Expand All @@ -109,14 +132,14 @@ internal class PanElementStateManager(state: ElementState, private val validator
companion object {
fun forEmptyInput(): PanElementStateManager {
return PanElementStateManager(
INITIAL_ELEMENT_STATE,
INITIAL_PAN_ELEMENT_STATE,
arrayOf(StrictEbtValidator())
)
}

fun NON_PROD_forEmptyInput(): PanElementStateManager {
return PanElementStateManager(
INITIAL_ELEMENT_STATE,
INITIAL_PAN_ELEMENT_STATE,
arrayOf(
StrictEbtValidator(),
PaymentCaptureErrorCard(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@ package com.joinforage.forage.android.core.element.state
import com.joinforage.forage.android.core.element.IncompleteEbtPinError
import com.joinforage.forage.android.core.element.WrongEbtPinError

internal class PinElementStateManager(state: ElementState) : ElementStateManager(state) {
internal class PinElementStateManager(state: PinElementState) : ElementStateManager<PinElementState>(state) {

override fun getState(): PinElementState {
return PinElementStateDto(
isFocused = this.isFocused,
isBlurred = this.isBlurred,
isEmpty = this.isEmpty,
isValid = this.isValid,
isComplete = this.isComplete,
validationError = this.validationError
)
}

// this function is used for after the submit event
// happens and we learn that the PIN was not correct
Expand Down Expand Up @@ -34,7 +45,7 @@ internal class PinElementStateManager(state: ElementState) : ElementStateManager

companion object {
fun forEmptyInput(): PinElementStateManager {
return PinElementStateManager(INITIAL_ELEMENT_STATE)
return PinElementStateManager(INITIAL_PIN_ELEMENT_STATE)
}
}
}
Loading