Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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
Disables the Button during onPress call in PressableWithFeedback #18122
Disables the Button during onPress call in PressableWithFeedback #18122
Changes from 2 commits
4471e8b
90d9b72
0c045be
fcee719
76500fc
cb06109
11a0929
ee9e4d2
ec2b5c2
e1640db
2d14e33
1bca5e6
690cf59
738cf17
bba4458
File filter
Filter by extension
Conversations
Jump to
There are no files selected for viewing
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.
This should be required
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.
does that mean all Buttons will need an accessibility label? & should adding all of them be in scope of this PR?
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.
Well since
accessibilityLabel
is required onGenericPressable
it would make sense to have this required onButton
too. I think it would be better to split this to two PRs:cc @roryabraham thoughts on this?
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.
Or given that I have already done the majority of the work for both the things above, I would prefer to move updating all usages of
Button
to add accessibilityLabels to a seperate PR.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 agree that
accessibilityLabel
should be required. I also agree that we should start by disablingPressableWithFeedback
on press in this PR, then create a separate PR to migrate fromPressable
toPressableWithFeedback
forButton
.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.
@roryabraham I have already done the migration part in this PR as we didn't get a reply for ~2 weeks, so what's the plan of action here?
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.
Why we are moving those from inner style to wrapper style?
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.
that's how the new component works, it'll make sense when you try it out.
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.
Can you please elaborate? Those styles were being passed to
<OpacityView />
why we are passing them to<Pressable />
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.
The style here is not correct. Previously we had
const activeAndHovered = !this.props.isDisabled && hovered;
where the active state was solely base on theisDisabled
prop. Now it's based on theisDisabled
state which is based on bothisDisabled
prop andisLoading
prop.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.
you're right for both the above comments, changes applied.
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.
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.
Why this change is required?
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.
It is required to apply styles based on the component states like isHovered, isDisabled, etc just like the lines below it. @robertKozik told me to add it so maybe he can add more context here if needed
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.
The idea inside
BaseGenericPressable
was to spread styling based on Pressable state into different props (HoverStyle, focusStyle etc.). So in place of:We would get:
But as
PressableWithFeedback
shifts all these state-aware styling intoopacityView
and passeswrapperStyle
as the style prop toGenericPressable
, I suggested this change in order to still have the possibility of state-aware styling inside the wrapperStyle prop.During our convo with @priyeshshah11 I came up with this change because I thought It could be used there.
All in all, even when there is no use here, this change is beneficial.
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.
Is this required? If the button is disabled how would the
onPress
even get called?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.
you're correct, I had added it as for some reason I was still able to click it while initial testing but couldn't reproduce it now so have removed it.
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.
nab, but if onPress is not an instance of Promise, does it still have to be inside
InteractionManager
?could you elaborate difference between the above code and the following:
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.
yes I would prefer for it to be inside
InteractionManager
to be sure it runs only once as on some android devices, there is still a possibility for it to capture multiple interactions before onPress's completionThere 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 am not sure about this when we have sync calls, but it's okay to keep what it is right now.
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.
The
props.disabled
that we are using inside theInteractionManager.runAfterInteractions
callback or the one after the promise is resolved (few lines below) is evaluated at the time we call onPress and that value is used later. Meaning props.disabled does not reflect the current state but the state that we evaluated when onPress was initially called.To better explain, here is an example:
This logic is copied from OptionRow but we missed that case in both PRs which lead to a regression #20983.
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.
Actions should not return a promise. Just revert this. The InteractionManager seems to be enough for our case, so no promise is actually needed.
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.
but didn't @roryabraham say so here ?
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.
This may create a pattern for further devs which I'm not sure if we want that.
But do we actually need a promise here?
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.
yes we do need it, as InteractionManager is not enough. And I think we were aware of this pattern change & accepted it before assigning/commencing work on this PR as per @roryabraham's comments in Slack.
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.
Yes, I understand that this is a new pattern. In general @s77rt is right that actions should not return a promise, but the spirit of that rule is that we should never be waiting for API requests to complete before doing something, because all our API interactions are completed optimistically.
What we need is a way to wait for the optimistic data to be written to Onyx and for subscribers to be updated before returning. But because that's async we need to use a Promise.
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.
Actually at this point (the following statement may change) we are waiting for onyx data update since the optimistic data use onyx
SET
method which is blocking (sync).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.
Yes, that's a very good point. I think this is maybe fine for now, but long-term we do need a better way to wait for just the optimistic data to be written to cache and for subscribers to be updated.
Onyx.set
is async:We could feasibly make a sync Onyx API on iOS and Android right now using JSI, but have no way to do so on web currently. We make
Onyx.set
async to make it more consistent withOnyx.merge
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.
Right, sorry for the confusion I was actually referring to another behaviour. See https://expensify.slack.com/archives/C01GTK53T8Q/p1682938682704609 but that's unrelated to the PR.
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.
@priyeshshah11, hi, very sorry for the interruption. We are a little confused about the
Promise
here. Do you have any description or links that can show us the actual effect of thisPromise
? If we remove it, what problems may occur? 🙂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.
@ntdiary If this is not a promise then we don't have a simple way of knowing when this action completed & when to re-enable the button.