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

Fix for Issue 4329: Updated dropzone provider to check hovered zones on dragover. #4604

Merged
merged 14 commits into from
Feb 1, 2018

Conversation

chriskmnds
Copy link
Contributor

@chriskmnds chriskmnds commented Jan 19, 2018

Description

This is a fix for issue 4329.

To my understanding, there is an issue with the top/bottom dropzones. Initially the dropzones were checked and activated on the dragenter and dragleave events - which fire once each, when we enter an area while dragging and when we leave that area. So when we are already dragging somewhere in the bottom part of the top dropzone and drag the item towards the top of it - the positioning is never updated (since nothing keeps track of the cursor position), and once we drag passed the dropzone then we are not within the zone bounds for the update logic to fire - hence we need to come back into the dropzone area to re-activate the top/first dropzone.

With dragover (the fix), we keep track of the cursor position and do our checks constantly + as this was written in a way that only the dropzones that need to be updated will change their state, then the overhead is not severe. This way we have the update logic fire when we drag from the bottom half to the top half of the first dropzone (and vice versa for the bottom one), hence the activation of the topmost and bottommost goes through as we are on our way to leave the dropzone area.

Another Approach

Another possibility to investigate for a different solution may be to pad the top part of the blocks list with an additional dropzone in order to have the dragenter handler fire when leaving the topmost dropzone (so we never really leave the dropzone area, even though we won't be hovering over one of the visible blocks). This may not require changes to the provider.

How Has This Been Tested?

Manually. Dragging an image from the desktop into a block and image gallery dropzone - confirming insert.

Screenshots (jpeg or gifs if applicable):

A gif has been added to the issue referenced above.

Types of changes

Updated handler to check on dragover instead of once on dragenter/leave. Reasoning: we need to tell when the cursor goes from the bottom to the top of the same block, otherwise we can't activate the top/bottom dropzones in the block list unless we drag something out and into the top/bottom dropzones respectively.

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code has proper inline documentation.

@@ -92,21 +83,29 @@ class DropZoneProvider extends Component {
// Computing state
const { onVerifyValidTransfer } = this.props;
const isValidDrag = ! onVerifyValidTransfer || onVerifyValidTransfer( detail.dataTransfer );

// Always true (onVerifyValidTransfer is not defined).
Copy link
Member

Choose a reason for hiding this comment

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

Can we remove it if it's always true?

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 can, although I can't tell what the original intention was to have this property in the first place. For the moment the property is not defined. Maybe @youknowriad can give us a bit more insight.

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 fine removing this, this is a leftover since this component has been adapted from Calypso

this.setState( {
isDraggingOverDocument,
hoveredDropZone,
position,
} );

event.preventDefault();
event.stopPropagation();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reminder to check again and probably remove this last line. No need to stop propagation here.

Copy link
Member

Choose a reason for hiding this comment

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

Any decision here?

@@ -162,14 +165,18 @@ class DropZoneProvider extends Component {
// This seemingly useless line has been shown to resolve a Safari issue
// where files dragged directly from the dock are not recognized
event.dataTransfer && event.dataTransfer.files.length; // eslint-disable-line no-unused-expressions

Copy link
Member

Choose a reason for hiding this comment

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

Do we need those empty lines?

Copy link
Contributor Author

@chriskmnds chriskmnds Jan 27, 2018

Choose a reason for hiding this comment

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

Yes 😛

Copy link
Contributor Author

@chriskmnds chriskmnds Jan 27, 2018

Choose a reason for hiding this comment

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

jk! In general I prefer to keep declarations (const/let/var/etc) and conditionals separate from other statements. That's just personal preference though - I am more than happy to remove them. Do we have any general approach/convention that I can adopt for such details?

@nb nb requested a review from youknowriad January 26, 2018 20:29
@nb
Copy link
Member

nb commented Jan 26, 2018

Good catch and works well for me. Added @youknowriad as a review to confirm whether we need isDraggingOverDocument and for another sanity check.

@chriskmnds
Copy link
Contributor Author

chriskmnds commented Jan 27, 2018

Thanks @nb! I also pinged @youknowriad in the comments to shed some light on that missing onVerifyValidTransfer property.

onDragOver( event ) {
event.preventDefault();
}

toggleDraggingOverDocument( event ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This function is called so many times when dragging right now, do you think we should throttle it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed. Great idea Riad. I am looking into this, currently trying something out with lodash. Sorry for the delayed response, I was just away since yesterday.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey Riad, I've finally added throttling at 300ms using lodash. It feels ok to me at this value, but please have a go and let me know what you think. Also, this is the best I could come up with code-wise to get the event.preventDefault call working. Tested it in Chrome/Firefox/Safari/IE11. :-)

Copy link
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

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

So this change can create performance issues when dragging files in theory but I'm not seeing the performance issues we had when we first tried this.

I think we optimized the blocks to avoid rerendering without changes which makes this strategy possible now.

cc @jasmussen for sanity check (performance wise) but in general, this is looking good.

@jasmussen
Copy link
Contributor

In my quick tests here, dragging and dropping both single and multiple images into a long document caused no performance issues for me and was simply a delight.

@chriskmnds
Copy link
Contributor Author

I think we optimized the blocks to avoid rerendering without changes which makes this strategy possible now.

Indeed. I noticed some smart checks in there to only update the dropzones that need to be updated. Last I remember it was I believe 2 state updates as we drag something over a block top to bottom. I will run a few more checks to be sure and clean up that onVerifyValidTransfer property.

- The top/bottom negative offsets of the block dropzones allow for the
  top/bottom borders shown when on hover to overlap, hence visualy appearing
  as one (or not changing). This was off by 1px, which this commit fixes.
@chriskmnds
Copy link
Contributor Author

chriskmnds commented Jan 29, 2018

@jasmussen when/if you get a chance, can you please take a look at my very last commit here? There is a brief description there - not sure if you noticed it, but if you look closer there is a 1px difference in overlap of the top/bottom blue borders of stacked dropzones. These are supposed to overlap exactly when you drag something from one block to the next. This seemingly simple change fixes it, but I can't determine what the cause was. Any ideas? shall we just call it fixed and not worry?

Thanks! 😄

@jasmussen
Copy link
Contributor

Sure thing, I'll take a look first thing tomorrow morning.

@chriskmnds
Copy link
Contributor Author

Thanks :-)

@jasmussen
Copy link
Contributor

Checked out your branch, tried to drag and drop some images in, tried to create some reusable blocks, tried dragging and dropping again. I can't seem to find any visual regressions in this change. If you see the same, then let's call it fixed for now, and we can revisit if any issues pop up.

@youknowriad
Copy link
Contributor

This comment #4604 (comment) doesn't seem resolved yet. Any thoughts about this?

@chriskmnds
Copy link
Contributor Author

Thanks @jasmussen ! I was just puzzled why I had to reduce the bottom offset by 1px to get the blue borders aligned when you drag across blocks -but yeah, it's such a minor issue, don't worry about it if it's looking fine. Thanks for checking 😄

@@ -17,6 +17,13 @@ class DropZoneProvider extends Component {
this.isWithinZoneBounds = this.isWithinZoneBounds.bind( this );
this.onDrop = this.onDrop.bind( this );

const throttledAction = throttle( this.toggleDraggingOverDocument, 500 );

this.dragOverListener = ( event ) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: I guess it's just a preference of mine but what do you think about something like this:

this.toggleDraggingOverDocument = throttle( this.toggleDraggingOverDocument.bind( this ), 500 );
this.dragOverListener = this.dragOverListener.bind( this );

and move the dragOverListener declaration as a regular method (not defined in the constructor).

Copy link
Contributor

Choose a reason for hiding this comment

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

Also wondering if 500 is not too high :) maybe 200?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, 200 is a lot better.

Minor: I guess it's just a preference of mine but what do you think about something like this:...

Definitely. That is a lot cleaner/better. Just updated now. :-)

Also, you'll notice I make the call to the throttled method as this.toggleDraggingOverDocument.call( this, event ); - it's not necessary - we can just this.toggleDraggingOverDocument( event ) - lodash uses the last this value on each call. I don't know why but I feel safer to have it this way instead? Should I just revert to the normal invocation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry this is going back/forth so many times... :-)

Copy link
Contributor

Choose a reason for hiding this comment

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

No worries, thanks for handling all these details :)

I'd be in favor of a regular this.toggleDraggingOverDocument( event )

I'll give it a last spin tomorrow morning and approve

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Awesome! Updated. Let me know if you spot anything else :-)

Copy link
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

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

Nice work here 👍 Thanks for addressing all the concerns

@youknowriad youknowriad merged commit 321f5c3 into WordPress:master Feb 1, 2018
@youknowriad
Copy link
Contributor

Hey @chriskmnds I'm seeing a small bug on master where there's a lingering placeholder at the end of the post when drag and dropping a file.

I wonder if it's related to this PR

@chriskmnds
Copy link
Contributor Author

chriskmnds commented Feb 1, 2018

Hey @youknowriad I notice that too. It doesn't look like it's related to this PR. I've checked the commit before it (d8e13bf) and I can still see the lingering placeholder. This is what you are referring to right:

bottom-placeholder

I sense this is related to the latest changes with the sibling inserter. I can look into this first thing tomorrow in any case. Shall I create a ticket for it? :-)

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