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

fixed: Image does not zoom where you click for desktop and web #8809

Merged
merged 9 commits into from
May 17, 2022

Conversation

metehanozyurt
Copy link
Contributor

@metehanozyurt metehanozyurt commented Apr 27, 2022

Details

For desktop and web versions big images can zoom clicked area problem solved.

Fixed Issues

$ #8119

Tests

  1. Open Expensify on desktop app or web
  2. Send horizontally and vertically big dimension image (for example : testFile3)
  3. Click image corners and center
  4. Notice that it is zooming clicked position.
  5. Send horizontally big dimension image (for example : testFile4)
  6. Click image corners and center
  7. Notice that it is zooming clicked position.
  8. Send vertically big dimension image (for example : testFile1)
  9. Click image corners and center
  10. Notice that it is zooming clicked position.
  11. Send horizontally and vertically small dimension image (for example : testFile2)
  12. Click image corners and center
  13. Notice that it is zooming image and fit screen keeping ratio
  14. Click any dimension image on ios and android native apps double top first then pinch
  15. Notice that image is zoom to center after that pan should work
  16. For mWeb pinch and double click not work. For ios pinch zooming modal only.

Test Files:
testFile1
testFile2
testFile3
testFile4

  • Verify that no errors appear in the JS console

PR Review Checklist

Contributor (PR Author) Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • iOS / native
    • Android / native
    • iOS / Safari
    • Android / Chrome
    • MacOS / Chrome
    • MacOS / Desktop
  • I verified there are no console errors (if there’s a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained “why” the code was doing something instead of only explaining “what” the code was doing.
    • I verified any copy / text shown in the product was added in all src/languages/* files
    • I verified any copy / text that was added to the app is correct English and approved by marketing by tagging the marketing team on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named “index.js”. All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • Any functional components have the displayName property
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose and it is
  • If a new CSS style is added I verified that:
    • A similar style doesn’t already exist
    • The style can’t be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.

PR Reviewer Checklist

  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I verified tests pass on all platforms & I tested again on:
    • iOS / native
    • Android / native
    • iOS / Safari
    • Android / Chrome
    • MacOS / Chrome
    • MacOS / Desktop
  • I verified there are no console errors (if there’s a console error not related to the PR, report it or open an issue for it to be fixed)
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick).
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained “why” the code was doing something instead of only explaining “what” the code was doing.
    • I verified any copy / text shown in the product was added in all src/languages/* files
    • I verified any copy / text that was added to the app is correct English and approved by marketing by tagging the marketing team on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named “index.js”. All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • Any functional components have the displayName property
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose and it is broken down into smaller components in order to separate concerns and functions
  • If a new CSS style is added I verified that:
    • A similar style doesn’t already exist
    • The style can’t be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.

QA Steps

  1. Open Expensify on desktop app or web
  2. Send horizontally and vertically big dimension image (for example : testFile3)
  3. Click image corners and center
  4. Notice that it is zooming clicked position.
  5. Send horizontally big dimension image (for example : testFile4)
  6. Click image corners and center
  7. Notice that it is zooming clicked position.
  8. Send vertically big dimension image (for example : testFile1)
  9. Click image corners and center
  10. Notice that it is zooming clicked position.
  11. Send horizontally and vertically small dimension image (for example : testFile2)
  12. Click image corners and center
  13. Notice that it is zooming image and fit screen keeping ratio
  14. Click any dimension image on ios and android native apps double top first then pinch
  15. Notice that image is zoom to center after that pan should work
  16. For mWeb pinch and double click not work. For ios pinch zooming modal only.

Test Files:
testFile1
testFile2
testFile3
testFile4

  • Verify that no errors appear in the JS console

Screenshots

Web

onContainerPress-2022-05-14-at-01.07.04_1.mp4

corner click video:

web-corners-click.mp4

Mobile Web

ios-safari.mp4
android-web.mp4

Desktop

Desktop-Screen-Recording-2022-05-14-at-10.13.32.mp4

corner click video:

desktop-corner-click_1.mp4

iOS

ios-native.mp4

Android

android-native.mp4

@metehanozyurt metehanozyurt requested a review from a team as a code owner April 27, 2022 22:51
@melvin-bot melvin-bot bot requested review from chiragsalian and mananjadhav and removed request for a team April 27, 2022 22:51
if (y > this.state.imageBottom) {
sy = this.state.imgHeight;
} else if (y > this.state.containerHeight / 2) {
// minus half of container size because we want to be center clicked position
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments start with Title case

@@ -98,17 +94,22 @@ class ImageView extends PureComponent {
* @param {SyntheticEvent} e
*/
onContainerPress(e) {
if (this.state.isZoomed && !this.state.isDragging) {
let sX;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename this to something more meaningful?

@@ -98,17 +94,22 @@ class ImageView extends PureComponent {
* @param {SyntheticEvent} e
*/
onContainerPress(e) {
if (this.state.isZoomed && !this.state.isDragging) {
let sX;
let sY;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one too

}

if (this.state.isZoomed && this.state.isDragging && this.state.isMouseDown) {
if (this.isZoomed && this.state.isDragging && this.state.isMouseDown) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We removed this from state because? and I can still see this.state.isZoomed used. Can you elaborate on why the class level will also required as well as the state?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to use it state.isZoomed to detect the change in rendering. I set the class level because when the state changes, the information of the clicked point is incorrect on containerPress . We hold state change , make calculations then set isZoomed state.
I know it looks bad. But if we let the state change, we were losing the clicked coordinates.


// return if image not loaded yet
if (imageHeight <= 0 || containerHeight <= 0) {
if (imageHeight <= 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need containerHeight check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned in the comment below (#8809 (comment)) , it can sometimes be called last. therefore it is still necessary for the calculations we use below.

imageRight: imgRight,
imageBottom: imgBottom,
});
zoomScale: containerHeight ? newZoomScale : prevState.zoomScale,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't understand the logic here, if containerHeight? In which case will it be 0 (or false)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little obsessed with this comment (#8119 (comment)) and i found the reason.
Usually setImageRegion calls first and we dont need to set zoomScale we just set the imgWidth and imgHeight. But sometimes the call order can be change and setImageRegion function calls last so we have last chance to set zoomScale. Here video and picture:

162569484-d35970dd-e961-4acf-b215-40ae5b329fe4.mov

WhatsApp Image 2022-04-09 at 14 41 10

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not sure how to reproduce but i don't mind keeping this. But let's add a comment explaining why its needed so that someone in future does not easily remove this by accident.


// White blank touch
if (x < this.state.imageLeft) {
// container size bigger than clicked position offset
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments are title case

@mananjadhav
Copy link
Collaborator

@metehanozyurt Ping me once you need me to take a look again.

Few more comments:

  1. Can we add some comment for the updated logic. I'll also try to get more 👀 on the removed code.
  2. Can you update the Tests section. I use this section to mention all the different tests that are performed. In your case, may be included small thumbnails, pinch works, etc.
  3. If you could add screenshots for the scaling at the corners of the image.

Once all comments are resolved, I'll take a look again and test it locally at my end.

@metehanozyurt
Copy link
Contributor Author

Thank you @mananjadhav comments.
I added comment on updated logic in my last commit. Fix my comments.
I have added detailed test steps. I hope I haven't added too much detail.
I also added videos of clicking the corners for the web and desktop versions.

I think I'm ready for you 😇. Can you please take a look, thank you.

If you'll excuse me, I have a question. The PR I prepared solves the problem mentioned in the issue (#8119). But there was no such support for mWeb before. For this reason, it is the principle of working as it was before. Is this a problem?

@mananjadhav
Copy link
Collaborator

I think I'm ready for you 😇. Can you please take a look, thank you.

Okay I'll go through this and get back.

But there was no such support for mWeb before. For this reason, it is the principle of working as it was before. Is this a problem

IMO, it shouldn't be a problem. @chiragsalian can comment once. But quick question, are you aware of why isn't it working? Lack of platform support? Incorrect code, etc.?

@metehanozyurt
Copy link
Contributor Author

Thank you. If it helps this code works for mWeb. I noticed it while testing for other platforms. When I took the code back and looked again, I observed the same situation. I guess it is not preferred for pinch effect but pinch not working too.

if (this.canUseTouchScreen) {
return (
<View
style={[styles.imageViewContainer, styles.overflowHidden]}
>
<Image
source={{uri: this.props.url}}
style={[
styles.w100,
styles.h100,
]}
resizeMode="contain"
onLoadStart={this.imageLoadingStart}
onLoadEnd={this.imageLoadingEnd}
/>
{this.state.isLoading && (
<FullscreenLoadingIndicator
style={[styles.opacity1, styles.bgTransparent]}
/>
)}
</View>
);
}

@chiragsalian
Copy link
Contributor

Yeah its not a problem me too if you don't focus on mWeb for this. If there is a simple fix for mWeb that would be cool but you don't need to focus on it and we can open up another issue for mWeb if we feel its worth it.

@chiragsalian
Copy link
Contributor

Any update here. @mananjadhav, i believe its waiting for a review?

@mananjadhav
Copy link
Collaborator

Will test tonight

this.scrollableRef.scrollLeft = sX * this.state.zoomScale;

// We divide the clicked positions by the zoomScale.
// We need pixel coordinates.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// We need pixel coordinates.
// Divide the clicked position by the zoomScale to get the pixel coordinates

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you accept this suggestion remember to remove the first line of the comment otherwise it will be a dupe.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also comments should generally explain "why" its needed not "what" it does. The "what" part is usually self explanatory from the code, because i see delta is being set by dividing offsets i.e., the clicked positions with zoomscale, having the comment explain the same is not valuable.

I would say to either remove it or rephrase it like so,

// Dividing clicked positions by the zoom scale to get coordinates so that once we zoom we will scroll to the clicked location.

@mananjadhav
Copy link
Collaborator

mananjadhav commented May 12, 2022

PR Reviewed. @chiragsalian Looks good to me. Thanks, @metehanozyurt for the patience. It was a big change and I needed to be sure that it doesn't break anything.

@metehanozyurt 1 minor comment and please merge the latest main branch

#### PR Reviewer Checklist

  • I verified the PR has a small number of commits behind main
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the testing environment is mentioned in the test steps
  • I verified testing steps cover success & fail scenarios (if applicable)
  • I checked that screenshots or videos are included for tests on all platforms
  • I verified tests pass on all platforms & I tested again on:
    • iOS / native
    • Android / native
    • iOS / Safari
    • Android / Chrome
    • MacOS / Chrome
    • MacOS / Desktop
  • I verified there are no console errors related to changes in this PR
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified comments were added when the code was not self explanatory
    • I verified any copy / text shown in the product was added in all src/languages/* files (if applicable)
    • I verified proper naming convention for platform-specific files was followed (if applicable)
    • I verified style guidelines were followed
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components are not impacted by changes in this PR (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified the UI performance was not affected (the performance is the same than main branch)
  • If a new component is created I verified that a similar component doesn't exist in the codebase

Copy link
Contributor

@chiragsalian chiragsalian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works well, left a few comments. Additionally in your test steps you mention,

14-) Click any dimension image on ios and android native apps notice that pinch and pan works. For Android need too double top image first then pinch works like on test video.
15-) for mWeb pinch and double click not work. For ios pinch zooming modal only.

For iOS native i also needed to double click to zoom into image, and it only can zoom to center and not a specific area. While i think thats fine i wasn't sure if you test steps mentioned the same for iOS or if the expectation was different. Can you make it more clear?

PR reviewer checklist
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I verified tests pass on all platforms & I tested again on:
    • iOS / native
    • Android / native
    • iOS / Safari
    • Android / Chrome
    • MacOS / Chrome
    • MacOS / Desktop
  • I verified there are no console errors (if there’s a console error not related to the PR, report it or open an issue for it to be fixed)
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick).
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained “why” the code was doing something instead of only explaining “what” the code was doing.
    • I verified any copy / text shown in the product was added in all src/languages/* files
    • I verified any copy / text that was added to the app is correct English and approved by marketing by tagging the marketing team on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named “index.js”. All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • Any functional components have the displayName property
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose and it is broken down into smaller components in order to separate concerns and functions
  • If a new CSS style is added I verified that:
    • A similar style doesn’t already exist
    • The style can’t be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.

this.scrollableRef.scrollLeft = sX * this.state.zoomScale;

// We divide the clicked positions by the zoomScale.
// We need pixel coordinates.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you accept this suggestion remember to remove the first line of the comment otherwise it will be a dupe.

this.scrollableRef.scrollLeft = sX * this.state.zoomScale;

// We divide the clicked positions by the zoomScale.
// We need pixel coordinates.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also comments should generally explain "why" its needed not "what" it does. The "what" part is usually self explanatory from the code, because i see delta is being set by dividing offsets i.e., the clicked positions with zoomscale, having the comment explain the same is not valuable.

I would say to either remove it or rephrase it like so,

// Dividing clicked positions by the zoom scale to get coordinates so that once we zoom we will scroll to the clicked location.

this.setState({isDragging: false, isMouseDown: false});
} else if (this.isZoomed) {
// We set isZoomed state then scroll image for calculating positions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why "for calculating positions" is mentioned here. I think the comment will be better read like so,

// We first zoom and once its done then we scroll to the location the user clicked

Thoughts?

isMouseDown: false,
}));
// We hold for set isZoomed state if true
// Because when set isZoomed state can't getting actual position user clicked
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't follow your comments here. Can you rephrase? I think you are trying to say onContainerPress calculates where to scroll so we zoom in over here and not here, is that correct?
If so how does this rephrase sound?

// We won't set isZoomed true in the block below and instead set it in onContainerPress as that method calculates the scroll positions after zooming.

// We hold for set isZoomed state if true
// Because when set isZoomed state can't getting actual position user clicked
this.isZoomed = !this.state.isZoomed;
if (this.isZoomed === false) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused why this logic is in onContainerPressOut and why this.isZoomed is needed.
I feel like this can be simplified if it lived in onContainerPress, then we wouldn't need this.isZoomed or even need to do multiple setStates.

So wouldn't this onContainerPress code work? (untested)

onContainerPress(e) {
    let scrollX;
    let scrollY;
    if (!this.state.isZoomed && !this.state.isDragging) {
        const {offsetX, offsetY} = e.nativeEvent;

        // We divide the clicked positions by the zoomScale.
        // We need pixel coordinates.
        const delta = this.getScrollOffset(offsetX / this.state.zoomScale, offsetY / this.state.zoomScale);
        scrollX = delta.offsetX;
        scrollY = delta.offsetY;
    }

    if (this.state.isZoomed && this.state.isDragging && this.state.isMouseDown) {
        this.setState({isDragging: false, isMouseDown: false});
    } else {
        // We first zoom and once its done if we are zooming in then we scroll to the location the user clicked.
        this.setState(prevState => ({
            isZoomed: !prevState.isZoomed,
            isMouseDown: false,
        }), () => {
            this.scrollableRef.scrollTop = scrollY;
            this.scrollableRef.scrollLeft = scrollX;
        });
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It worked perfectly. I wish I could think of that. Thank you so much. Here is the test video. I will use this code and re-shoot videos for web and desktop.

onContainerPress-2022-05-14-at-01.07.04_1.mp4

Thanks to this update, we can delete these lines.

this.onContainerPressOut = this.onContainerPressOut.bind(this);

onContainerPressOut() {
if (this.state.isDragging) {
return;
}
this.setState(prevState => ({
isZoomed: !prevState.isZoomed,
isMouseDown: false,
}));
}

onPressOut={this.onContainerPressOut}

imageRight: imgRight,
imageBottom: imgBottom,
});
zoomScale: containerHeight ? newZoomScale : prevState.zoomScale,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not sure how to reproduce but i don't mind keeping this. But let's add a comment explaining why its needed so that someone in future does not easily remove this by accident.

if (y < this.state.imageTop) {
sy = 0;
// Container size bigger than clicked position offset
if (x <= (this.state.containerWidth / 2)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove unnecessary brackets, it should be like so

x <= this.state.containerWidth / 2

@metehanozyurt
Copy link
Contributor Author

Thank you for your valuable comments and patience @mananjadhav and @chiragsalian. I will study harder to give better explanations.

You are right about this line. I've been paranoid. I took it back as it was an unnecessary condition.

zoomScale: containerHeight ? newZoomScale : prevState.zoomScale,

I shot new videos for desktop and web. It works as it should, after new changes.
I updated the test steps. I tried to be a little more understandable.

I hope you like it, thank you very much again 🙏 .

@mananjadhav
Copy link
Collaborator

Thanks for the changes @metehanozyurt. Appreciate your patience. I will test this again tonight.

Thanks, @chiragsalian for the suggestions.

Copy link
Collaborator

@mananjadhav mananjadhav left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks, @metehanozyurt for the changes.

@chiragsalian All yours.

🎀 👀 🎀 C+ reviewed

Copy link
Contributor

@chiragsalian chiragsalian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this looks much cleaner. Thank you for the changes @metehanozyurt.

PR Reviewer Checklist

  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I verified tests pass on all platforms & I tested again on:
    • iOS / native
    • Android / native
    • iOS / Safari
    • Android / Chrome
    • MacOS / Chrome
    • MacOS / Desktop
  • I verified there are no console errors (if there’s a console error not related to the PR, report it or open an issue for it to be fixed)
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick).
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained “why” the code was doing something instead of only explaining “what” the code was doing.
    • I verified any copy / text shown in the product was added in all src/languages/* files
    • I verified any copy / text that was added to the app is correct English and approved by marketing by tagging the marketing team on the original GH to get the correct copy.
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named “index.js”. All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.js or at the top of the file that uses the constant) are defined as such
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • Any functional components have the displayName property
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose and it is broken down into smaller components in order to separate concerns and functions
  • If a new CSS style is added I verified that:
    • A similar style doesn’t already exist
    • The style can’t be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG)
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.

@chiragsalian chiragsalian merged commit 9547655 into Expensify:main May 17, 2022
@OSBotify
Copy link
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@metehanozyurt
Copy link
Contributor Author

Thank you so much for helps and patience @chiragsalian , @mananjadhav 🙏 .

@OSBotify
Copy link
Contributor

🚀 Deployed to staging by @chiragsalian in version: 1.1.62-0 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

@OSBotify
Copy link
Contributor

🚀 Deployed to production by @AndrewGable in version: 1.1.62-0 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants