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

[$250] IOS - Avatar doesn't get back to its original center position after zooming in and out #53471

Open
2 of 8 tasks
lanitochka17 opened this issue Dec 3, 2024 · 75 comments
Assignees
Labels
Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 External Added to denote the issue can be worked on by a contributor

Comments

@lanitochka17
Copy link

lanitochka17 commented Dec 3, 2024

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Version Number: 9.0.70-2
Reproducible in staging?: Y
Reproducible in production?: Y
If this was caught on HybridApp, is this reproducible on New Expensify Standalone?: Y
If this was caught during regression testing, add the test name, ID and link from TestRail: https://expensify.testrail.io/index.php?/tests/view/5291133
Email or phone of affected tester (no customers): nathanmulugetatesting+2272@gmail.com
Issue reported by: Applause - Internal Team

Action Performed:

  1. Open hybrid app
  2. Upload a custom avatar for your profile
  3. Open your self DM
  4. Open the avatar to show in full screen
  5. Pinch out to zoom and without releasing our finger pinch in to zoom out and position your fingers to the corner while zooming out and release your fingers

Expected Result:

The position of the avatar gets back to its original center position

Actual Result:

The avatar stays at the position it was released on and doesn't get back to the center

Workaround:

Unknown

Platforms:

Which of our officially supported platforms is this issue occurring on?

  • Android: Standalone
  • Android: HybridApp
  • Android: mWeb Chrome
  • iOS: Standalone
  • iOS: HybridApp
  • iOS: mWeb Safari
  • MacOS: Chrome / Safari
  • MacOS: Desktop

Screenshots/Videos

Add any screenshot/video evidence
Bug6683084_1733231204767.ScreenRecording_12-03-2024_15-58-36_1.1.mp4

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~021863989062279850488
  • Upwork Job ID: 1863989062279850488
  • Last Price Increase: 2025-02-03
  • Automatic offers:
    • dominictb | Contributor | 106011607
@lanitochka17 lanitochka17 added Daily KSv2 Bug Something is broken. Auto assigns a BugZero manager. labels Dec 3, 2024
Copy link

melvin-bot bot commented Dec 3, 2024

Triggered auto assignment to @greg-schroeder (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.

@greg-schroeder greg-schroeder added the External Added to denote the issue can be worked on by a contributor label Dec 3, 2024
@melvin-bot melvin-bot bot changed the title IOS - Avatar doesn't get back to its original center position after zooming in and out [$250] IOS - Avatar doesn't get back to its original center position after zooming in and out Dec 3, 2024
Copy link

melvin-bot bot commented Dec 3, 2024

Job added to Upwork: https://www.upwork.com/jobs/~021863989062279850488

@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label Dec 3, 2024
Copy link

melvin-bot bot commented Dec 3, 2024

Triggered auto assignment to Contributor-plus team member for initial proposal review - @abdulrahuman5196 (External)

@greg-schroeder greg-schroeder changed the title [$250] IOS - Avatar doesn't get back to its original center position after zooming in and out [$125] IOS - Avatar doesn't get back to its original center position after zooming in and out Dec 3, 2024
Copy link

melvin-bot bot commented Dec 3, 2024

Upwork job price has been updated to $125

@ahmedkhan28
Copy link

Hi! I came across your job posting on Upwork regarding the avatar centering issue in your iOS app. After analyzing the problem where the avatar doesn't return to center after zoom gestures, I believe this can be fixed effectively through iterative solutions. I am a full time full stack iOS developer, designer and game developer at MagicHabits. This can be fixed in max 1-2 days.

Based on my experience with iOS gesture handling and UI animations, I've identified 5 potential approaches that could solve this:

Spring Animation Reset

swiftCopyUIView.animate(withSpringDamping: 0.8) {
avatarImageView.center = view.center
}

Velocity-Based Repositioning

swiftCopylet velocity = gesture.velocity(in: view)
let newCenter = calculatePosition(from: velocity)
animateToPosition(newCenter)

Smart Edge Detection

swiftCopyif !safeBounds.contains(avatar.frame) {
returnToCenter(animated: true)
}

Progressive Center Force

swiftCopylet centerForce = 1 - currentScale
let newCenter = interpolate(current: position,
target: center,
force: centerForce)

Physics-Based Boundaries

swiftCopylet behavior = UIAttachmentBehavior(item: avatarImageView)
animator.addBehavior(behavior)
Most natural but complex implementation
Recommended Approach:

I suggest implementing Solution #1 (Spring Animation Reset) first, then enhancing it with elements of Solution #4 (Progressive Center Force). This combination would:

Provide immediate improvement in UX
Feel natural and iOS-native
Be quick to implement and test
Allow for easy tuning of behavior

Let me know if you like it so I can submit a proposal on upwork!

Copy link

melvin-bot bot commented Dec 4, 2024

📣 @ahmedkhan28! 📣
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:

  1. Make sure you've read and understood the contributing guidelines.
  2. Get the email address used to login to your Expensify account. If you don't already have an Expensify account, create one here. If you have multiple accounts (e.g. one for testing), please use your main account email.
  3. Get the link to your Upwork profile. It's necessary because we only pay via Upwork. You can access it by logging in, and then clicking on your name. It'll look like this. If you don't already have an account, sign up for one here.
  4. Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details.
    Screen Shot 2022-11-16 at 4 42 54 PM
    Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>

@ahmedkhan28
Copy link

Contributor details
Your Expensify account email: ahmedkhan850@gmail.com
Upwork Profile Link: www.upwork.com/agencies/1838604427293852717

Copy link

melvin-bot bot commented Dec 4, 2024

✅ Contributor details stored successfully. Thank you for contributing to Expensify!

@JoshIri360
Copy link
Contributor

JoshIri360 commented Dec 4, 2024

Edited by proposal-police: This proposal was edited at 2024-12-20 10:33:27 UTC.

Proposal

Please re-state the problem that we are trying to solve in this issue.

Avatar doesn't get back to its original center position after zooming in and out

What is the root cause of that problem?

The root cause of this problem is that recentering the avatar is only done if the zoom scale is less than the minimum zoom scale or more than the maximum zoom scale.

When zoomed past the minimum zoom, it recenters and adjusts the zoom:

overzoomed.mp4

When zoomed past the maximum zoom, it does the same (may be difficult to see here)

output.mp4

If it's not a maximum or minimum zoom, there is no recenter, this allows the user move the avatar in any direction without it recentering

Recording.at.2024-12-20.11.06.43.mp4

The line of code that helps the zoom recenter after minimum or maximum zoom can be found here:

// If the content was "overzoomed" or "underzoomed", we need to bounce back with an animation
if (pinchBounceTranslateX.get() !== 0 || pinchBounceTranslateY.get() !== 0) {
pinchBounceTranslateX.set(withSpring(0, SPRING_CONFIG));
pinchBounceTranslateY.set(withSpring(0, SPRING_CONFIG));
}

What changes do you think we should make in order to solve the problem?

We should modify the onEnd handler to reset the position to center when the gesture ends, if it's not overzoomed or underzoomed. This ensures the avatar maintains its expected centered position after any pinch interaction.

The logic should be added in the else block where we handle normal zoom levels:

zoom scale, we need to set the zoom scale to the maximum
        pinchScale.set(zoomRange.max);
        zoomScale.set(withSpring(zoomRange.max, SPRING_CONFIG, triggerScaleChangedEvent));
    } else {
+         // Ensure image is centered
+         offsetX.set(withSpring(0, SPRING_CONFIG));
+         offsetY.set(withSpring(0, SPRING_CONFIG));
    
        // Otherwise, we just update the pinch scale offset
        pinchScale.set(zoomScale.get());
        triggerScaleChangedEvent();
    }
});

What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?

N/A, It's a UI issue

What alternative solutions did you explore? (Optional)

N/A

Result

compressednormalxoom.mp4

Copy link

melvin-bot bot commented Dec 4, 2024

📣 @JoshIri360! 📣
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:

  1. Make sure you've read and understood the contributing guidelines.
  2. Get the email address used to login to your Expensify account. If you don't already have an Expensify account, create one here. If you have multiple accounts (e.g. one for testing), please use your main account email.
  3. Get the link to your Upwork profile. It's necessary because we only pay via Upwork. You can access it by logging in, and then clicking on your name. It'll look like this. If you don't already have an account, sign up for one here.
  4. Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details.
    Screen Shot 2022-11-16 at 4 42 54 PM
    Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>

@JoshIri360
Copy link
Contributor

Contributor details
Your Expensify account email: aidelojejoshua@gmail.com
Upwork Profile Link: https://www.upwork.com/freelancers/~018bc4a19179469daa

Copy link

melvin-bot bot commented Dec 4, 2024

✅ Contributor details stored successfully. Thank you for contributing to Expensify!

@ahmedkhan28
Copy link

Submitted the proposal just now, thank you!

@greg-schroeder
Copy link
Contributor

Next up is proposal review

@melvin-bot melvin-bot bot added the Overdue label Dec 6, 2024
Copy link

melvin-bot bot commented Dec 9, 2024

@greg-schroeder, @abdulrahuman5196 Huh... This is 4 days overdue. Who can take care of this?

@ahmedkhan28
Copy link

ahmedkhan28 commented Dec 9, 2024 via email

@abdulrahuman5196
Copy link
Contributor

Hi, will review today

@melvin-bot melvin-bot bot removed the Overdue label Dec 9, 2024
@greg-schroeder
Copy link
Contributor

Thanks @abdulrahuman5196

@abdulrahuman5196
Copy link
Contributor

@ahmedkhan28 / @JoshIri360 Kindly follow the proposal template for suggesting solutions as mentioned here https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#propose-a-solution-for-the-job

And on another not @JoshIri360 I tried your solution and its not fixing the issue. I am still able to repro the issue with your solution.

@abdulrahuman5196
Copy link
Contributor

No pending proposal to review or approve yet

Copy link

melvin-bot bot commented Jan 27, 2025

@greg-schroeder, @abdulrahuman5196 Huh... This is 4 days overdue. Who can take care of this?

Copy link

melvin-bot bot commented Jan 27, 2025

📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸

@greg-schroeder
Copy link
Contributor

bump @abdulrahuman5196 on proposal review!

@melvin-bot melvin-bot bot removed the Overdue label Jan 27, 2025
Copy link
Contributor

⚠️ @anboia Thanks for your proposal. Please update it to follow the proposal template, as proposals are only reviewed if they follow that format (note the mandatory sections).

@anboia
Copy link

anboia commented Jan 28, 2025

Proposal

Updated

Copy link
Contributor

⚠️ @anboia Thanks for your proposal. Please update it to follow the proposal template, as proposals are only reviewed if they follow that format (note the mandatory sections).

@anboia
Copy link

anboia commented Jan 28, 2025

@abdulrahuman5196 @greg-schroeder , I have updated the proposal, the issue mentioned in the comment #53471 (comment) is acctually a different issue, but also related to the pinch gesture.

@melvin-bot melvin-bot bot added the Overdue label Jan 30, 2025
@anboia
Copy link

anboia commented Jan 30, 2025

2. Is there any reliable way to reproduce the issue consistently?

To reproduce the issue:

  1. Zoom in the avatar picture.
  2. Move the image to a different position from the center.
  3. Zoom out, to the min zoom.
  4. move to another position.
  5. release.

the image will move back to the same position when the image transition from original scale, to a small scale ("underzoom")

does not need to " position your fingers to the corner" as described in the action performed.

@abdulrahuman5196 please let me know if you still need more detail on the RCA.

@anboia
Copy link

anboia commented Jan 30, 2025

@greg-schroeder my proposal covers 4 additional fixes on the MultiGestureCanvas. Would't be a reason to raise the bounty on this?

Copy link

melvin-bot bot commented Jan 31, 2025

@greg-schroeder, @abdulrahuman5196 Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!

@greg-schroeder
Copy link
Contributor

What do you think of the proposal @abdulrahuman5196?

@melvin-bot melvin-bot bot added Overdue and removed Overdue labels Jan 31, 2025
@abdulrahuman5196
Copy link
Contributor

Hi, @greg-schroeder I would be unavailable for couple of days. Could you kindly add the appropriate labels for another random C+ to take over? Unassigning myself to avoid delays.

@melvin-bot melvin-bot bot removed the Overdue label Feb 3, 2025
Copy link

melvin-bot bot commented Feb 3, 2025

📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸

@dominictb
Copy link
Contributor

@greg-schroeder I'm low on issues and can take this over.

@greg-schroeder
Copy link
Contributor

Nice, thanks @dominictb

@melvin-bot melvin-bot bot added Overdue and removed Help Wanted Apply this label when an issue is open to proposals by contributors Overdue labels Feb 5, 2025
Copy link

melvin-bot bot commented Feb 5, 2025

📣 @dominictb 🎉 An offer has been automatically sent to your Upwork account for the Contributor role 🎉 Thanks for contributing to the Expensify app!

Offer link
Upwork job
Please accept the offer and leave a comment on the Github issue letting us know when we can expect a PR to be ready for review 🧑‍💻
Keep in mind: Code of Conduct | Contributing 📖

@anboia
Copy link

anboia commented Feb 5, 2025

@greg-schroeder @dominictb how about my proposal? Wasn't that supposed to get a review?

@melvin-bot melvin-bot bot added the Overdue label Feb 6, 2025
@dominictb
Copy link
Contributor

Catching up with the ongoing progress. Will leave my review soon.

@melvin-bot melvin-bot bot removed the Overdue label Feb 6, 2025
@bernhardoj
Copy link
Contributor

Proposal

Please re-state the problem that we are trying to solve in this issue.

Avatar doesn't snap back to the edge of the screen after zoom in and out.

What is the root cause of that problem?

When we zoom out, there are 2 callbacks called. First is the pinch gesture onEnd callback (this is to add the pinch gesture translate to the offset, offset is the total accumulated translate of all gestures),

.onEnd(() => {
// Add pinch translation to total offset and reset gesture variables
offsetX.set((value) => value + pinchTranslateX.get());
offsetY.set((value) => value + pinchTranslateY.get());
pinchTranslateX.set(0);
pinchTranslateY.set(0);
currentPinchScale.set(1);

and the second one is the pan gesture onEnd callback (this also adds the pan gesture translate to the offset, but also does the snapping logic).

.onEnd(() => {
// Add pan translation to total offset and reset gesture variables
offsetX.set((value) => value + panTranslateX.get());
offsetY.set((value) => value + panTranslateY.get());
// Reset pan gesture variables
panTranslateX.set(0);
panTranslateY.set(0);
previousTouch.set(null);
// If we are swiping (in the pager), we don't want to return to boundaries
if (shouldDisableTransformationGestures.get()) {
return;
}
finishPanGesture();
});

Pinch gesture end callback is always called first before the pan gesture. The one that is responsible for snapping the image back to the edge of the screen (boundary) is the pan gesture end callback, specifically the finishPanGesture function.

} else {
// Animated back to the boundary
offsetX.set(withSpring(clampedOffset.x, SPRING_CONFIG));
}

However, the problem we currently face is that even though the pinch gesture end callback is called first, the offset shared value is updated first from the pan gesture end callback.

order of calls:

  1. pinch gesture end callback
  2. pan gesture end callback

order of shared value updates:

  1. pan gesture end callback
  2. pinch gesture end callback

If we log the offset value (specifically offsetX) inside finishPanGesture, we can see that the value is not updated yet. It's as if the offsetX update from the pinch gesture end callback is not immediately synchronized even though all the callbacks are supposed to run on the same thread, that is the UI thread.

And that's true, somehow the pinch gesture end callback runs on the JS thread. We can prove this by printing the _WORKLET value inside the onEnd callback.

console.log(_WORKLET)

This all happens after #52825 where we migrate the way we access and update the shared value to satisfy the react-compiler. Now the hook is successfully optimized by react-compiler, it broke the automatic workletized by the reanimated. We can prove this by disabling react-compiler for usePinchGesture by adding 'use no memo' directive inside the hook.

What changes do you think we should make in order to solve the problem?

We can either disable the react-compiler for usePinchGesture by adding 'use no memo'

OR

we can manually add 'worklet' directive inside the onTouchesDown callback and somehow the rest of the functions are workletized again.

.onTouchesDown((_evt, state) => {
// We don't want to activate pinch gesture when we are swiping in the pager
if (!shouldDisableTransformationGestures.get()) {

What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?

N/A

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 External Added to denote the issue can be worked on by a contributor
Projects
Status: Bugs and Follow Up Issues
Development

No branches or pull requests

8 participants