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

Configurable hash #8596 #8603

Merged
merged 7 commits into from
Oct 16, 2019
Merged

Conversation

SebCorbin
Copy link
Contributor

@SebCorbin SebCorbin commented Aug 6, 2019

Refs #8596

Launch Checklist

  • briefly describe the changes in this PR
  • write tests for all new functionality
  • document any changes to public APIs
  • post benchmark scores
  • manually test the debug page
  • tagged @mapbox/studio and/or @mapbox/maps-design if this PR includes style spec changes
  • tagged @mapbox/gl-native if this PR includes shader changes or needs a native port

@SebCorbin SebCorbin changed the title Configurable hash (#8596) Configurable hash #8596 Aug 6, 2019
src/ui/map.js Outdated Show resolved Hide resolved
Copy link
Contributor

@asheemmamoowala asheemmamoowala left a comment

Choose a reason for hiding this comment

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

Thanks for this contribution @SebCorbin! I have just a few more changes 🙏

src/ui/map.js Outdated Show resolved Hide resolved
src/ui/hash.js Outdated Show resolved Hide resolved
test/unit/ui/hash.test.js Show resolved Hide resolved
@chloekraw
Copy link
Contributor

@SebCorbin thanks for this contribution! We're doing the branch cut for the next release tomorrow, so if you have any bandwidth to make the changes today, we can take another look and try to get it into v1.3

@SebCorbin
Copy link
Contributor Author

SebCorbin commented Aug 12, 2019

@SebCorbin thanks for this contribution! We're doing the branch cut for the next release tomorrow, so if you have any bandwidth to make the changes today, we can take another look and try to get it into v1.3

@chloekraw Sure, all changes have been made, except for the last of @asheemmamoowala's comments which I either did not understand or is not required.

test/unit/ui/hash.test.js Show resolved Hide resolved
@ryanhamley
Copy link
Contributor

I'm seeing a couple issues while testing this out.

Step 1: If I have a page with hash: true, I start out in the expected hash configuration, e.g. http://localhost:9966/debug/#12.5/38.888/-77.01866

Step 2: But if I change the map to hash: 'foo' and reload the page, I get http://localhost:9966/debug/#12.5/38.888/-77.01866&foo=12.5/38.888/-77.01866 with the new hash just appended to the old one.

Step 3: Now if I go back to hash: true and reload, I get a bunch of errors. Invalid LngLat object: (NaN, 38.888) followed by Cannot read property '0' of undefined.

Likewise, if after step 2, I delete the entire hash and reload the page, I get the correct hash http://localhost:9966/debug#foo=12.5/38.888/-77.01866. However, if I then set the map back to hash: true and reload, I get http://localhost:9966/debug#NaN/NaN/NaN and the same errors as seen in step 3.

@SebCorbin
Copy link
Contributor Author

SebCorbin commented Aug 15, 2019 via email

@asheemmamoowala
Copy link
Contributor

I'm seeing a couple issues while testing this out.

I would say this is fairly normal, with the current version if you put a wrongly formed

I agree with @SebCorbin , these issues seem related to work-in-progress code that is using the hash parameter, and not finished state of code.

  1. Now if I go back to hash: true and reload, I get a bunch of errors. Invalid LngLat object: (NaN, 38.888) followed by Cannot read property '0' of undefined.

As for the reload with another option value, we could at least drop the hash if the parsing fails, ... just ask and I’ll update the pull request to add the dropping mechanism.

@ryanhamley Is the map usable after the errors in Step 3? If not, then @SebCorbin it could we good to add the dropping mechanism.

@ryanhamley
Copy link
Contributor

Is the map usable after the errors in Step 3? If not, then @SebCorbin it could we good to add the dropping mechanism.

No, the map enters a broken state

@SebCorbin
Copy link
Contributor Author

@ryanhamley @chloekraw PR updated 👋

Copy link
Contributor

@ryanhamley ryanhamley left a comment

Choose a reason for hiding this comment

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

This is looking good. Thanks for sticking with it! I had a few suggestions but the overall functionality seems right to me.

src/ui/hash.js Outdated

constructor() {
constructor(hashName: ?string) {
this._hashName = hashName;
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably do some validation here. If I set hash: 'hello world' or hash: 'hello\nworld', the hash is appended twice so it makes sense to ensure the hash is what we expect.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you recommend, display a warning/error or sanitize the hashName?

Copy link
Contributor

Choose a reason for hiding this comment

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

this._hashName = hashName && encodeURIComponent(hashName) should be all that's needed.

Ensure the hashName is defined and if so, encode it. The problem is that in _getCurrentHash, we're reading the hash out of the URI (const hash = window.location.hash.replace('#', '');) so it's already encoded. Then the check part[0] === this._hashName fails because we're comparing a pure string against an encoded one, i.e. 'hello%20world' === 'hello world'. So encoding the hashName from the get-go gives us an apples-to-apples comparison.

src/ui/hash.js Outdated
}

if (bearing || pitch) hash += (`/${Math.round(bearing * 10) / 10}`);
if (pitch) hash += (`/${Math.round(pitch)}`);
return hash;

if (this._hashName && !mapFeedback) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: can we just move this block into the else statement on line 74 to avoid checking mapFeedback a second time?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is hardly possible, as we need bearing and pitch to be added to hash if needed. By the way, mapFeedback is not well documented so I don't understand if I need to take it into account when hashName is set.

Copy link
Contributor

Choose a reason for hiding this comment

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

It appears that you can remove the !mapFeedback part of this check entirely without causing any issues. I'm not entirely sure what the variable is for, but this is the only reference I can find for it in the code base and it looks like it's pretty much always true if there's a hash.

src/ui/hash.js Outdated Show resolved Hide resolved
src/ui/hash.js Show resolved Hide resolved
src/ui/hash.js Outdated
return part;
}).filter(a => a);
if (!found) {
parts.push(`${this._hashName || ''}=${hash}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need the || '' part here? If there's no hash name, then shouldn't we skip the = as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

test-flow fails without this, see https://circleci.com/gh/mapbox/mapbox-gl-js/50268

Cannot coerce `this._hashName` to string because null or undefined [1] should not be coerced.

   src/ui/hash.js:92:31
   92|                 parts.push(`${this._hashName}=${hash}`);
                                     ^^^^^^^^^^^^^^

Copy link
Contributor

Choose a reason for hiding this comment

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

We can get around this by assigning this._hashName to a variable at the top of the if statement and then changing the two references to this._hashName to the variable. That lets us get rid of the unnecessary and confusing || '' part of this while keeping Flow happy.

}

_getCurrentHash() {
const hash = window.location.hash.replace('#', '');
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you add a comment on this just to make it a little easier to grasp at a quick glance?

src/ui/map.js Outdated Show resolved Hide resolved
test/unit/ui/hash.test.js Outdated Show resolved Hide resolved
@SebCorbin
Copy link
Contributor Author

@ryanhamley weekly reminder for this PR :)

@ryanhamley
Copy link
Contributor

I promise I will review this in the next day or two @SebCorbin!

Copy link
Contributor

@ryanhamley ryanhamley left a comment

Choose a reason for hiding this comment

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

Hi @SebCorbin thanks so much for your patience. I spent some time going over the code today and added a few responses to some of your questions and comments. I think it looks pretty good overall.

Here's what I see as the TODO list:

  • sanitize the hash name input
  • remove the !mapFeedback part of the if statement on line 81
  • remove the || '' and fix the Flow error on line 92
  • add a short comment for line 101

So just a few really quick changes and we'll be ready to merge! If you can make them by Monday or Tuesday, we should be able to get this into our next release.

src/ui/hash.js Outdated

constructor() {
constructor(hashName: ?string) {
this._hashName = hashName;
Copy link
Contributor

Choose a reason for hiding this comment

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

this._hashName = hashName && encodeURIComponent(hashName) should be all that's needed.

Ensure the hashName is defined and if so, encode it. The problem is that in _getCurrentHash, we're reading the hash out of the URI (const hash = window.location.hash.replace('#', '');) so it's already encoded. Then the check part[0] === this._hashName fails because we're comparing a pure string against an encoded one, i.e. 'hello%20world' === 'hello world'. So encoding the hashName from the get-go gives us an apples-to-apples comparison.

src/ui/hash.js Outdated
}

if (bearing || pitch) hash += (`/${Math.round(bearing * 10) / 10}`);
if (pitch) hash += (`/${Math.round(pitch)}`);
return hash;

if (this._hashName && !mapFeedback) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It appears that you can remove the !mapFeedback part of this check entirely without causing any issues. I'm not entirely sure what the variable is for, but this is the only reference I can find for it in the code base and it looks like it's pretty much always true if there's a hash.

src/ui/hash.js Outdated
return part;
}).filter(a => a);
if (!found) {
parts.push(`${this._hashName || ''}=${hash}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

We can get around this by assigning this._hashName to a variable at the top of the if statement and then changing the two references to this._hashName to the variable. That lets us get rid of the unnecessary and confusing || '' part of this while keeping Flow happy.

src/ui/hash.js Show resolved Hide resolved
Copy link
Contributor

@ryanhamley ryanhamley left a comment

Choose a reason for hiding this comment

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

🎉 Thanks for your patience @SebCorbin This looks great!

@ryanhamley ryanhamley merged commit 6c7bd20 into mapbox:master Oct 16, 2019
@SebCorbin SebCorbin deleted the configurable-hash branch October 22, 2019 07:45
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.

5 participants