-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Configurable hash #8596 #8603
Conversation
9977545
to
d70a323
Compare
There was a problem hiding this 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 🙏
@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. |
I'm seeing a couple issues while testing this out. Step 1: If I have a page with Step 2: But if I change the map to Step 3: Now if I go back to Likewise, if after step 2, I delete the entire hash and reload the page, I get the correct hash |
@ryanhamley I would say this is fairly normal, with the current version if you put a wrongly formed hash, you would get a the same kind of error you get in Step 2 as there is currently no hash cleanup.
As for the reload with another option value, we could at least drop the hash if the parsing fails, but we can’t just assume what would be a previous option, just ask and I’ll update the pull request to add the dropping mechanism.
|
I agree with @SebCorbin , these issues seem related to work-in-progress code that is using the
@ryanhamley 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 |
@ryanhamley @chloekraw PR updated 👋 |
There was a problem hiding this 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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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}`); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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}`);
^^^^^^^^^^^^^^
There was a problem hiding this comment.
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('#', ''); |
There was a problem hiding this comment.
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?
276b677
to
b361f3a
Compare
@ryanhamley weekly reminder for this PR :) |
I promise I will review this in the next day or two @SebCorbin! |
There was a problem hiding this 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 theif
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; |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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}`); |
There was a problem hiding this comment.
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.
There was a problem hiding this 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!
Refs #8596
Launch Checklist
@mapbox/studio
and/or@mapbox/maps-design
if this PR includes style spec changes@mapbox/gl-native
if this PR includes shader changes or needs a native port