-
Notifications
You must be signed in to change notification settings - Fork 21
Add ability to share urls. #107
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
base: master
Are you sure you want to change the base?
Conversation
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 works, doesn't break anything, and adds great functionality. LGTM!
I have yet to test it, but it looks great! It's way simpler than my PR. 👍 |
@TimBroddin discovered that the existing implementation of state persistence At the same time, I'd like to ensure that loading initial state from param also takes in considering of existing data in local storage, which may already have endpoints data loaded, which will shave off a couple of seconds on repeat pageload. Will continue refining this PR. We're in no rush. |
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.
@yansern this is a great patch. I left a lot of comments, but the ones that I think are important are questions about deep coupling between app components. I think we can make a few adjustments and avoid some headaches down the line. thanks for being patient with me!
@@ -0,0 +1 @@ | |||
16.20.2 |
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.
was this inclusion intentional? the package.json
already specifies node@>=16.0.0
but this one is more specific, and doesn't reference npm
@@ -16,7 +16,7 @@ if ( wpcomConfig ) { | |||
id: 'WPCOM', | |||
baseUrl: 'https://public-api.wordpress.com/oauth2', | |||
userUrl: 'https://public-api.wordpress.com/rest/v1.1/me', | |||
redirectUrl: wpcomConfig.redirectUrl || wpcomConfig.redirect_uri, | |||
redirectUrl: window.location.href, |
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.
is this the proper change here? if someone uses the current location then it seems like it could lead to oauth failures, ass each oauth client application is setup for a specific redirect url. can you share some context on the change?
import reducer from './reducer'; | ||
import { boot } from './security/actions'; | ||
import { loadInitialState, persistState } from '../lib/redux/cache'; | ||
|
||
const store = createStore( | ||
reducer, | ||
loadInitialState( {}, reducer ), | ||
applyMiddleware( thunk ) | ||
loadInitialState( {}, getStateFromUrl(), reducer ), |
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 a nice setup for passing this information; I appreciate the thought that went into your design
}; | ||
|
||
// Discard empty params | ||
Object.keys( params ).forEach( key => ! params[ key ] && ( delete params[ key ] ) ); |
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.
there's a value in this terseness, but we don't often use the pattern of **
for control flow, and given how infrequently this project is maintained, it might be more valuable to choose something more familiar, including constructing the object positively instead of removing empty bits.
const urlParams = new URLSearchParams();
for ( const [ value, key ] of Object.entries( params ) ) {
if ( value ) {
urlParams.update( key, value );
}
}
also is the basic truthiness check valid here for all the parameters? it seems like we might end up needlessly encoding empty objects and omit false
values.
I wonder if it would help to use undefined
to include these when forming the params
object. url: '' === request.url ? encodeString( request.url ) : undefined
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: const [ value, key ] of Object.entries( params )
should be const [ key, value ] of Object.entries( params )
, or weird things start happening. Also .update -> .set
.
|
||
function decodeString( val ) { | ||
return decodeURIComponent( val ); | ||
} |
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.
is this offering much over directly calling decodeURIComponent()
? I find that the abstraction hasn't made the code clearer to me, and from the way it's used, it seems like it's only being used in the context of a query string where decodeURIComponent
is a fitting match.
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 seems that it's making a pair between encodeString
and decodeString
, it's clear that those do the inverse of each other. encodeObject
and decodeObject
is a complementary pair. Replacing one of those 4 with a direct call makes it more confusing imo.
return false; | ||
} | ||
|
||
const state = { request: { ...defaultRequestState }, ui: { ...defaultUiState } }; |
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 would be best to avoid such deep coupling outside of the react tree. I can easily imagine someone adding another top-level state property and not realizing this code needs it.
I'm not sure we even need to return the full state object here as it seems like we could treat this as state augmentation. we can pass this into the initial state tree and merge it with the defaults there. that way these two parts of the system can operate independently.
// Fail without breaking the app | ||
console.error( 'Could not parse state from url params.', e ); | ||
return false; | ||
} |
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's a choice to bail if any parameter doesn't decode, but would we want to try and pre-fill whatever parameters we can decode?
switch ( action.type ) { | ||
case REQUEST_TRIGGER: | ||
const url = getUrlFromState( state ); | ||
window.history.pushState( {}, document.title, url ); |
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.
MDN suggests that the middle parameter is unused, so passing a real value could be confusing since it won't have an impact in the browser. should we pass ''
here instead?
} | ||
isInitializingEndpoint = false; | ||
} | ||
break; |
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.
nice queueing of the initial request.
do we need to make allotments for logged-out views here or will this only run if someone is logged in?
version: null, | ||
}; | ||
|
||
const reducer = createReducer( defaultState, { |
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.
given that the reducer ultimately governs its own state and structure, I would feel better if this code merged the initial values from cache and from the URL, and then we could leave both of those systems decoupled from each other. this would require making this export something like makeReducer
that takes both of those and merges them.
export const makeReducer = ( { persistedState = null, urlState = null } ) => {
const initialState = Object.assign(
{},
defaultState,
persistedState,
);
if ( urlState ) {
initialState.request.bodyParams = urlState.bodyParams;
initialState.request.method = urlState.method;
…
}
return createReducer( initialState, { … } );
}
Update: I didn't realize there was another PR working on a similar feature here until I submitted this PR. #106
Update 2: I looked into the other PR and I would say the approach taken in this PR is more feature complete. This PR supports restoring the endpoint selected from suggestion dropdown, and the path values of those selected endpoint and also the query params from filled from the UI.
Description
This PR generates a shareable URL and pushes the browser history state when the "Run Endpoint" button is clicked, allowing the URL to be copied from the address bar and shared to colleagues.
In order to achieve this, I created a redux middleware called
router
that would encode and decode relevant states to and from URLs.Test Instructions
You can test this with the following urls and you can see the values prefilled:
Test entering the URLs signed-out, and then sign-in. After signing-in, you should be redirected back to the same url with all the params intact.
Additional notes
Screenshots