Closed kavimuru closed 1 year ago
Triggered auto assignment to @CortneyOfstad (Bug
), see https://stackoverflow.com/c/expensify/questions/14418 for more details.
Platforms
in OP are ✅)@CortneyOfstad Whoops! This issue is 2 days overdue. Let's get this updated quick!
Was OoO — tackling this now!
Job added to Upwork: https://www.upwork.com/jobs/~0175f90dca3be1912f
Current assignee @CortneyOfstad is eligible for the External assigner, not assigning anyone new.
Triggered auto assignment to Contributor Plus for review of internal employee PR - @parasharrajat (Internal
)
Triggered auto assignment to Contributor-plus team member for initial proposal review - @rushatgabhane (External
)
Triggered auto assignment to @MariaHCD (External
), see https://stackoverflow.com/c/expensify/questions/7972 for more details.
The tooltip position is initialized only once during the first hover and the issue occurs because of the single initialization of the position of the tooltip so even if the target element position gets changed but tooltip position will not changed.
I have tried to implement the same scenario using the same component and I am attaching the before and after the fix
before: https://www.loom.com/share/f40dfbbdf9f94c07bee3e3c73285de34
after: https://www.loom.com/share/fa19295843f74158a8c4164546455202
After returning to a setting from a profile, the tooltip loses position on the profile image when you hover the mouse for the first time.
There 2 events that are causing this issue.
TooltipSense
component.Tooltip
is delayed (500 ms - atm) when TooltipSense
is active it is happening imidiate, those the positions of tooltip is passed imidiatly and the tooltip is shown in the wrong placeI propose 2 things:
TooltipSense
component to have method called deactivateImidiate
which will reset active state on back button click.Tooltip
to move the delay
parameter as depency on active state before the execution of getWrapperPosition
which desides on the tooltip position
this will effectly will take the correct position and conserve the TooltipSense behavior.Moving the delay to Hoverable
component but at this point i belive it is over complecated without breaking the TooltipSense
behavior.
Looks like something related to react-navigation
may have been mentioned in this issue discussion.
As a reminder, please make sure that all proposals are not workarounds and that any and all attempt to fix the issue holistically have been made before proceeding with a solution. Proposals to change our DeprecatedCustomActions.js
files should not be accepted.
Feel free to drop a note in #expensify-open-source with any questions.
📣 @Litande! 📣
Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork. Please follow these steps:
Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>
⚠️ Missing/invalid email or upwork profile link. Please make sure you add both your Expensify email and Upwork profile link in the format specified.
Contributor details Your Expensify account email: mihaelchas@gmail.com Upwork Profile Link: https://www.upwork.com/freelancers/michaelchasovin
⚠️ Missing/invalid email or upwork profile link. Please make sure you add both your Expensify email and Upwork profile link in the format specified.
Can someone help me out with Melvin here?) both are valid
@Litande you need not specify the scheme in the upwork URL, just upwork.com/freelancers/michaelchasovin
will do, I believe! 😄
📣 @Prince-Mendiratta! 📣
Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork. Please follow these steps:
Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>
@Prince-Mendiratta thanks, didn't notice )
Contributor details Your Expensify account email: mihaelchas@gmail.com Upwork Profile Link: upwork.com/freelancers/michaelchasovin
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
I have reported this issue, should i share my email and upwork as well.
📣 @ayazhussain79! 📣
Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork. Please follow these steps:
Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>
Contributor details Expensify account email: hussainayaz79@gmail.com
Upwork Profile Link: https://www.upwork.com/fl/~018a62c1e70a3fab40
Proposal
Problem. The position of the tooltip appears in the wrong location.
What is the root cause of that problem? The tooltip position is initialized only once during the first hover and the issue occurs because of the single initialization of the position of the tooltip so even if the target element position gets changed but tooltip position will not changed.
What changes do you think we should make in order to solve the problem? We have to re-render that part after UI will get into its original position
Contributor details Your Expensify account email: bhattramkrishna98@gmail.com Upwork Profile Link: https://www.upwork.com/freelancers/~0121390686e5dc8b39
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
All the proposals at their current stage do not have sufficient information to proceed. Please update your proposals to propose how will you solve this issue with technical details. The root cause should also be explained with technical details.
cc: @Ramkrishna1998 @Litande
📣 @parasharrajat! 📣
Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork. Please follow these steps:
Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>
@parasharrajat I have implemented the same scenario using the same component in my local machine using your code and component and I am attaching the before and after the fix videos. can we connect for further explanation?
before: https://www.loom.com/share/f40dfbbdf9f94c07bee3e3c73285de34
after: https://www.loom.com/share/fa19295843f74158a8c4164546455202
📣 @Ramkrishna1998! 📣
Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork. Please follow these steps:
Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>
Tooltips are just not synchronized with the "tooltiped" element after they are shown.
In this case, tooltip is first shown when the avatar is animating (although the animation is short and it's a temporary position), but stays in the incorrect position.
Another example where the problem is obvious is when the user scrolls the chat, an by chance has a cursor in the line of the avatar:
https://user-images.githubusercontent.com/2590174/225157987-55a12ab3-c7c0-41e8-8db5-01920baa213d.mov
I'm arguing that these are two aspects of the same problem.
The tooltip position is sampled and stored into a fixed
CSS position, relative to the viewports's top left corner.
Conceptually, I'd like to propose just keeping the tooltip over the "tooltiped" element.
Technically, the solution can be expressed using a few helper callback-oriented operators:
/**
* Start syncing the tooltip's position with wrapper's position
*
* @param {function(): void} onStarted a callback called once bounds are established for the first ime
* @returns {Handle} a handle to the syncing process
*/
startSyncingPosition({
onStarted,
}) {
/**
* @param {{x: number, y: number, width: number, height: number}} position
* @returns {void}
*/
const updatePosition = ({
x, y, width, height,
}) => {
this.setState({
wrapperWidth: width,
wrapperHeight: height,
xOffset: x,
yOffset: y,
});
};
/**
* Function that processes each new bounds, updating the tooltip position and ensuring that onStarted is called
* when we got first bounds
*
* @param {{x: number, y: number, width: number, height: number}} position
* @returns {void}
*/
const processWrapperBounds = CallbackUtils.merge(
updatePosition,
CallbackUtils.calledOnce((_) => {
onStarted();
}),
);
/**
* Handle called at each animation frame, starting the position getting async task
*
* @type {{
* call: function(): void,
* cancel: function(): void,
* }}
*/
const processAnimationFrameHandle = PromiseUtils.transformAsync(processWrapperBounds, this.getWrapperBounds);
/**
* Handle to the animation loop that drives the whole syncing process
*
* @type {Handle}
*/
const animationHandle = AnimationUtils.requestAnimationFrames(processAnimationFrameHandle.call);
return HandleUtils.mergeHandles(
processAnimationFrameHandle,
animationHandle,
);
}
I tested this code and didn't observe any performance issues, neither visually nor in the Performance Timeline. Aside that, it's worth noting that:
Tooltip
is a pure component, meaning that "updating" position to the same position, which is the typical case, is optimized out by React and does not trigger a re-render.throttle
operator between animationFrames()
and asyncMap
, and limit the sampling to, let's say, 15 or 30 times per second (instead of typical ~60 FPS animation frame frequency). But this doesn't seem to be necessary.animationFrames()
otherwise. This would limit the number of reactions processed in JavaScript to bare minimum, but as the reactions seem to be very fast and in some way are often a noop, this also doesn't seem be necessary right now.This solves the tooltip positioning in the general case.
The tooltip slightly readjusts while the animation is finishing:
https://user-images.githubusercontent.com/2590174/227300180-dee76064-b233-4399-b784-98b22a515733.mp4
It's so subtle that best observed in slow motion:
https://user-images.githubusercontent.com/2590174/227300434-038c741e-acee-40ff-af12-cdda77cb1f23.mp4
When scrolling the chat, it looks like this:
https://user-images.githubusercontent.com/2590174/227303773-ff751fd1-ebe8-43d6-8797-cb494b1bae12.mp4
To sum up, we have a solution that:
📣 @cubuspl42! 📣
Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork. Please follow these steps:
Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>
Contributor details Your Expensify account email: cubuspl42.1@gmail.com Upwork Profile Link: https://www.upwork.com/freelancers/~012b13cc109598f579
✅ Contributor details stored successfully. Thank you for contributing to Expensify!
@Ramkrishna1998 Please read the contribution guidelines if you are new to the project. You will find them on the ReadMe page. You will also find a ton of useful info to propose a better solution.
@parasharrajat should i udate the proposal inline? or add new one ?
two c+ assigned to this issue? @parasharrajat
unassigned myself
The position of the tooltip apear in the wrong location.
ModalStackNavigator
is used in this instance and while in transition interactions on web aren't disabled,
those hovering on it, while in transition causes a trigger to show Tooltip
which uses Hoverable
onMouseEnter events with X and Y params that aren't in their final position.
We can change the logic to such that the initialization of the tooltip is throttled while the cordinates are changing, with a small delay to wait for fixed cordinates.
I am proposing to update Hoverable
, in there onMouseMove
to add an event which will also apply IsHoverable(true)
here
https://github.com/Expensify/App/blob/9d04b46456253955c96981db9ef38245953baf18/src/components/Hoverable/index.js#L46-L55
we should update the logic contain a throttle mechanic for acutally settings is hoverable to true so it will happen upon stationary set of mouse after, for example for 50 miliseconds to show the tooltip, this
here is a video of the result https://www.loom.com/share/b4f1fb67e7204659a9f280b044100611 in here the throttle is 300 but i tested with 50 and looks also good.
Where trying check why overlay isn't used in web display for stack navigation component in android and ios there is such overlay (transparent) which blocks interactions, but i don't think filling this for web would be an easy task.
@parasharrajat updated https://github.com/Expensify/App/issues/15229#issuecomment-1440067841
Also do by some change know if there is anyone to help with joining slack send the email 3 already )
Thanks.
@Litande
Also do by some chance know if there is anyone to help with joining slack send the email 3 already )
Sometimes, people are busy so they miss these. cc: @CortneyOfstad
@Litande Do you mind sharing more on how will you throttle & where? I don't think Hoverable
is a good target for that. Hoverable
is independent of navigation so it should remain as it is.
We should either create a wrapper component, HOC, or make use of props that makes sense to Hoverable
and does not depend on any external factors.
Oh, so you are just going to throttle for a specific time irrespective of the navigation and all. I don't think that is a good approach. It will affect all the tooltips.
@parasharrajat Would you mind commenting on my proposal? Don't you think that changing the way tooltips are layed out could solve the problem at its core? We wouldn't have to apply the new strategy to all tooltips at once, to minimize a risk of breakage.
@parasharrajat ofcourse,
inside setIsHoverable
we can add a mechanic similar to the folowing
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
this.timeout = setTimeout(() => {
//// SET TIMEOUT
}, 50)
ofcourse adding mousemove will trigger that event while you are moving you mouse above it also.
this in fact an effect delay of hoverable exection while the mouse is moving, this is ofcourse the general idean and not how a potential fix will look like.
Also regarding the location, I am not proposing fixing the navigation this what i wrote in the optional, where we are looking into implementing the logic of cover the element with other transparent element (which blocks integrations), and also this will be only partial fix to this location if any.
Hoverable is the target of the fix/imporvement, in such a way that while the user is moving his mouse the execution of hoverable will be delayed/throttled, and will actually take effect if the user stopped on the target element and those hoverable should apply.
As for implementation in web delay effect over tooltip is a common usage take for example the "member" label on your comment hear in github, it has also a minor delay, scroll over it nothing apear scroll into it and stop and a tooltip apears.
@cubuspl42 Sorry missed it.
Embed the tooltip in the DOM tree close to the avatar, but with the size of zero, with appropriate Z-index Reactively update the fixed tooltip position during the animation
Out of these, 2 are very expensive. It might also show a moving tooltip which is not good.
For 1, I didn't understand. What do you mean by size of zero
. I think initially tooltip was developed like this only but there were some overlapping issues. You have tested it before proposing.
@Litande Thanks for the explanation. I think static delay for this problem is not a good idea. We don't need this delay everywhere.
As for implementation in web delay effect over tooltip is a common usage take for example the "member" label on your comment hear in github, it has also a minor delay, scroll over it nothing apear scroll into it and stop and a tooltip apears.
This is already present on our Tooltip. They are not immediately shown to the users.
@parasharrajat Hi i went over the layers of Hoverable and Tooltip and didn't find delay mechanics, also check the behavior and seems to only have small animation sequence, can you please check it out?
Regrading evertwgere, i am proposing for it be parametrized so it can be adjusted depending on the implementation, we can set paramater "delay" with default as undefined in which case there won't be any.
i have also went over where it is implemented, and it exists in 5 components mainly tooltips, and report user namings.
@parasharrajat After some experimenting, I realized that approach no. 1 doesn't integrate well with scrollables. So it's not an acceptable solution. Sorry if I made it sound confusing. I'm talking about an approach where we lay out an "anchor", having no size (size of zero; being empty) in the place we want the tooltip to point to. Then we position the tooltip absolutely to that anchor (with an extra transform, like [{translateX: '-50%'}, {translateY: '-50%'}]
and a high z-order. It's a simple and neat solution, but such a tooltip cannot "escape" scrollables. Which makes this approach not applicable here.
I don't know why the moving tooltip is a bad thing... If what is under the tooltip moves, I'd assume that the tooltip should move too. For example, currently in Expensify when you scroll the chat while you hover an avatar, the glitch we're talking about (or a similar one) also appears. Of course, it's another topic wether the tooltip is not displayed too soon.
When you said "expensive", you mean expensive performance-wise, right?
The tooltip position is misplaced when hover the profile image while navigation is transitioning.
The current implementation of the tooltip uses a portal to put it at the body level. When we hover a component, it will calculate the current position of the component and the position of the tooltip based on it. However, because the transition is happening, the component position is not in a final position, yet we are calculating the tooltip position.
At first, I thought we can use InteractionManager.runAfterInteractions
, but web does not have a full support for it yet. The alternative I could think of is
runAfterNavigation
which will execute the passed callback after the transitioning is done. We will wrap the showTooltip
code with runAfterNavigation
.Here is the pseudocode of runAfterNavigation
runAfterNavigation (action):
if isNavigating then:
pendingAction push action
return
invoke action
If the transition is happening, we push the action to the pendingAction
array, else we execute it immediately. We will execute all pendingAction
later inside setIsNavigating
when the value passed is false. The isNavigatingValue
will be true when the transition starts and false when it ends.
setIsNavigating
inside navigate
except when it's a drawer route, because drawer route never calls transition end event (only stack navigator calls the transition event ref).But with this 2nd change, this will prevent multiple navigation when we press quickly over several components that call navigate
. Currently, we only prevent it when navigating back only.
@MariaHCD Any updates with this? Just checking to see if we're close to selecting a proposal. Thanks!
Catching up here. @parasharrajat let me know what your thoughts are so far and how I can help!
If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!
Action Performed:
Expected Result:
The tooltip should not lose the position even if you hover the image quickly.
Actual Result:
If you quickly hover over the image after returning from the profile, the tooltip loses position.
Workaround:
unknown
Platforms:
Which of our officially supported platforms is this issue occurring on?
Version Number: 1.2.73-1 Reproducible in staging?: y Reproducible in production?: y If this was caught during regression testing, add the test name, ID and link from TestRail: Email or phone of affected tester (no customers): Logs: https://stackoverflow.com/c/expensify/questions/4856 Notes/Photos/Videos:
https://user-images.githubusercontent.com/43996225/219512628-3d4e28ad-dabe-46d0-9be3-64bd37826d8a.mp4
https://user-images.githubusercontent.com/43996225/219512642-13a4168c-f829-4e2e-935e-cf2fce52f0ef.mp4
Expensify/Expensify Issue URL: Issue reported by: @ayazhussain79 Slack conversation: https://expensify.slack.com/archives/C049HHMV9SM/p1676563140766109
View all open jobs on GitHub
Upwork Automation - Do Not Edit