-
Notifications
You must be signed in to change notification settings - Fork 0
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
[FX-722] Add support for derivedCardInfo.usState
to ElementState
for ForagePANEditText
#133
Conversation
Current dependencies on/for this PR:
This stack of pull requests is managed by Graphite. |
ElementState previously only cared about values that were common to both PAN end PIN. We have reached a point where the states will need to diverage. This commit makes ElementState generic and introduces the `details` field. The Details field will serve as an entity that can evovle indpendently for PIN and PAN according to their respective needs. There are still a few outstanding questions after this commit: - [ ] Are we OK with the name `details` field? - [ ] In a world where we're planning to support more than just EBT cards, should we nest `usState` under a prefix like `derivedEbtInfo` since other cards that Forage will support in the future may not care about `usState`? For example, I wonder if a pattern like `details.derievedEbtInfo` could make sense?
14bb82c
to
df8523d
Compare
Had to do a bit of type casting in the unit tests to get around the `Nothing?` type of PinDetails giving the compiler ambiguous overloading but that wasn't too big an issue. It's worth noting that making ElementState generic did break the tests (i.e. it was not reverse compatibile) because the tests explicitly referenced the types `ElementState<PanDetails>` and `StatefulElementListner<PanDetails>` callback. Referencing these types in this way is not necessary and it's like that GoPuff did not do this in which case it would not even be a breaking change for them.
df8523d
to
726ee0b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome sauce! I left some questions to clarify some things, but I liked your use of generics to make the design flexible for PIN elements.
What are some examples where you expect that we'll have to support additional PIN input details in the future?
|
||
data class ElementState( | ||
data class ElementState<InputDetails>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@devinmorgan Will this be a breaking change for the client? My guess is no, but just want to confirm.
i.e. will the client have to update their code to conform to the new interfaces?
// client code
// before:
ForageElement
// after (breaking change):
ForageElement<ClientInsertsDetailsTypeHere>
Also, should we explicitly declare public
classes as public
or is it fine to omit? Not sure what the kotlin linting rules prefer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fear for the flakiness of the QA tests so I am taking a screenshot to capture that the CI tests passed for this PR at one point (see below).
Will this be a breaking change for the client?
It depends. As you can see the CI tests against this PR is that I did not change the Sample App in this PR. The significance of them passing is that this PR is reverse compatible (for the with the Sample App at least). The reason this change is reverse compatible with the Sample App is because the Sample App never explicitly refers to any of the types that this PR changes (e.g. ElementState
, AbstractElement
, etc). If the Sample App had referenced these types then this would have been a breaking change.
This same point applies to GoPuff. If they currently reference the types that this PR does change instead of letting the compiler infer the types, then this PR would be a breaking change. However, even if GoPuff does reference the types changed, it would only require minor updates in their code (just updating the types)
isFocused = false, | ||
isBlurred = true, | ||
isEmpty = true, | ||
isValid = true, | ||
isComplete = false, | ||
validationError = null | ||
validationError = null, | ||
details = PanDetails(null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you feel about making derivedCardInfo
non-nullable, but rather making the usState
field inside nullable. I would prefer if someone can always reference .derivedCardInfo
without having to null check it all the time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
excellent point! Just updated the code. Nice catch team!
ElementState<PanDetails>.details.derivedCardInfo.usState
ElementState<PanDetails>.details.derivedCardInfo.usState
ElementState<PanDetails>.details.derivedCardInfo.usState
ElementState<PanDetails>.details.derivedCardInfo.usState
derivedCardInfo.usState
to ElementState
for ForagePANEditText
isFocused = false, | ||
isBlurred = true, | ||
isEmpty = true, | ||
isValid = true, | ||
isComplete = false, | ||
validationError = null | ||
validationError = null, | ||
details = PanDetails(DerivedCardInfo()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered using inheritance instead of generics:
class PanElementState: ElementState {
derivedCardInfo: DerivedCardInfo
}
class PinElementState: ElementState {}
- less nesting (
state.derivedCardInfo
instead ofstate.details.derivedCardInfo
) details
for PIN elements will likely always be null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glad you brought this up Danilo! A couple of things:
- we were hoping to be able to use inheritance initially. That would be the ideal because PIN and PAN state's could easily evolve based on their own needs and sure enough we'd also eliminate additional nesting as you're pointing out. The problem is that
data class
es in kotlin don't seem to support sub-classing. Definitely annoying. - Another option was to convert
data class ElementState
-->class ElementState
as means of making inheritance possible. When I tried that with the sample app, it didn't break anything (i.e. appeared reverse compatible). However,data class
es automatically generate some extra utility functions and given the time crunch on Friday, I didn't spend much time considering whether GoPuff is actually using any of these utility methods. If we suspect they're not using these utility methods, then it would be save to changedata class ElementState
-->class ElementState
, and use inheritance there.
If we're open to shipping the Android tomorrow instead of today (assume we discuss at standup, changes take the afternoon, can release tomorrow morning), then we'd certainly have time to discuss this tradeoff and move forward with whatever design we prefer.
`class` let alone an `interface`. However, Forage X had a conversation around this where we said we would be OK with risk of changing the type of `ElementState` to something other than `data class`. This opened the door for us to make `ElementState` an interface, which is decoupled from the actual Data Transfer Objects used to ferry the state of the ForageElement. The rest of the changes in this commit follow from making `ElementState` an interface. One upside of this approach is that we get rid of `PinDetails`, which was annoying
824c190
to
fb6c221
Compare
Update: The latest changes on this PR now satisfies these design goals:
I initially tried to pursue inheritance on |
What
Extend
ForagePANEditText
to exposeusState
on thegetState()
and thesetOnChangeEventListener
callback. This was a feature requested by GoPuff.We add support for
usState
by makingElementState
by introducing adetails
field that is generic based onForagePINEditText
orForagePANEditText
.Why
The intention of this PR was to maintain that
getState()
andsetOnChangeEventListener
remain the only two places that developers need to look / be aware to find out information about the state of aForageElement
. The hope is to keep the cognitive weight of the SDK as minimal as possible compared to adding additional computed properties on either theForagePANEditText
or theForagePINEditText
Test Plan
usState
acronym get's displayed.Demo
The CI tests passing are the demo for this case. The next PR updates the Sample App and will actually show a visual demo.
How
This PR can be released as is. It's not essential that we update the Sample App at the same time as releasing this code.