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

Framework: Remove sites-list from ProfileLinksAddWordPress #14758

Merged
merged 8 commits into from
Jun 30, 2017

Conversation

tyxla
Copy link
Member

@tyxla tyxla commented Jun 5, 2017

This PR removes the sites-list usage from the ProfileLinksAddWordPress component. It also ES6ifies the component and introduces a straightforward getPublicSites selector that we need in order to allow only public sites to be added as profile links.

To test:

  • Checkout this branch
  • Go to http://calypso.localhost:3000/me
  • In the "Profile Links" section, click the "Add" button and from the dropdown menu click "Add WordPress Site"
  • Verify the profile link addition works properly and there are no regressions.
  • Verify the tests of the new selector pass:
npm run test-client client/state/selectors/test/get-public-sites.js

@tyxla tyxla added Framework [Feature] User & Account Settings (/me) Settings and tools for managing your WordPress.com user account. Sites [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. [Type] Janitorial labels Jun 5, 2017
@tyxla tyxla self-assigned this Jun 5, 2017
@tyxla tyxla requested a review from ockham June 5, 2017 11:57
@matticbot
Copy link
Contributor

@tyxla tyxla requested review from mcsf, ehg, mtias and gwwar June 5, 2017 11:57
@matticbot matticbot added the [Size] XL Probably needs to be broken down into multiple smaller issues label Jun 5, 2017
return (
<form>
<p>
{
this.translate(
translate(
'You have no public sites on WordPress.com yet, but if you like you ' +
Copy link

Choose a reason for hiding this comment

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

Hi! I've found a possible matching string that has already been translated 21 times:
translate( 'You have no public sites on WordPress.com yet, but if you like you can create one now. You may also add self-hosted WordPress sites here after installing {{jetpackLink}}Jetpack{{/jetpackLink}} on them.' ) ES Score: 18

Help me improve these suggestions: react with 👎 if the suggestion doesn't make any sense, or with 👍 if it's a particularly good one (even if not implemented).

Copy link
Member Author

Choose a reason for hiding this comment

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

But I'm not changing the string, @a8ci18n?

>
{ this.translate( 'Add Site', 'Add Sites', { count: checkedCount } ) }
{ translate( 'Add Site', 'Add Sites', { count: checkedCount } ) }
Copy link

Choose a reason for hiding this comment

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

😞 85 existing translations will be lost with this change.

Copy link
Member Author

Choose a reason for hiding this comment

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

But I'm not changing the string, @a8ci18n?

* @param {Object} state Global state tree
* @return {Array} Site objects
*/
export default function getPublicSites( state ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like this is probably worth memoizing with createSelector

Copy link
Member

Choose a reason for hiding this comment

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

Please. :)

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, done in dc84197.


export default connect(
( state ) => ( {
sites: keyBy( getSites( state ), 'ID' ),
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think we can cut down data needs here? I believe @mcsf ran into a similar issue in #14026

Copy link
Member

Choose a reason for hiding this comment

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

Agreed. There might be a way.

this.props.sites seems to only be used to obtain name and URL in preparation of the call to this.props.userProfileLinks.addProfileLinks. In that case, I would define an action creator:

// pseudo-code-ish
const addProfileLinks = ( inputs ) => ( dispatch, getState ) => {
  const links = inputs
    .filter( isSiteInput )
    .map( toSiteId )
    .map( getSite( getState(), _ ) )
    .map( pick( _, [ 'name', 'URL' ] ) );

  if ( links.length ) userProfileLinks.addProfileLinks( links );
};

If not, at the very least you should cache this selection, since keyBy could potentially process a large number of sites and, more importantly, will always return a new reference.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've gone with your suggestion @mcsf, using the inline action-y approach in f79b512.

title: site.name,
value: site.URL
} );
}
Copy link
Member

Choose a reason for hiding this comment

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

[obviously besides the scope of this PR] May I just say that I hate that we still have these data libraries like UserProfileLinks? :/ It took me a while to realize why we were duplicating site state into links, that this is actually an action "dispatch" in disguise.

Copy link
Member Author

@tyxla tyxla Jun 27, 2017

Choose a reason for hiding this comment

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

Yeah, I agree completely. It's a very obsolete pattern that we should consider refactoring in a future PR.

@@ -157,84 +157,94 @@ export default React.createClass( {
<Site
site={ site }
indicator={ false }
onSelect={ this.onSelect.bind( this, inputName ) } />
onSelect={ this.onSelect( inputName ) } />
Copy link
Member

Choose a reason for hiding this comment

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

Note that this is still an instance of function binding within render. No cheating! 😄

The core problem is that we want to assign/bind (not necessarily in the Function#bind sense) a particular site/input to an event handler (onSelect). There are several ways to deal with it. One, which might make the most sense here, is to refactor the <li> node into a component — e.g.:

class AddableSite extends PureComponent {
  inputName = `site-${ site.ID }`
  onSelect = this.props.onSelect.bind( null, inputName )
  render() {
    return <li><Site site={ site } onSelect={ this.onSelect }  /></li>;
  }
}

// …

renderAddableSites() {
  return this.props.publicSites.map( ( site ) =>
    this.props.userProfileLinks.isSiteInProfileLinks( site )
      ? null
      : <AddableSite site={ site } />;  

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point there, addressed in 8613691.


export default connect(
( state ) => ( {
sites: keyBy( getSites( state ), 'ID' ),
Copy link
Member

Choose a reason for hiding this comment

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

Agreed. There might be a way.

this.props.sites seems to only be used to obtain name and URL in preparation of the call to this.props.userProfileLinks.addProfileLinks. In that case, I would define an action creator:

// pseudo-code-ish
const addProfileLinks = ( inputs ) => ( dispatch, getState ) => {
  const links = inputs
    .filter( isSiteInput )
    .map( toSiteId )
    .map( getSite( getState(), _ ) )
    .map( pick( _, [ 'name', 'URL' ] ) );

  if ( links.length ) userProfileLinks.addProfileLinks( links );
};

If not, at the very least you should cache this selection, since keyBy could potentially process a large number of sites and, more importantly, will always return a new reference.

* @param {Object} state Global state tree
* @return {Array} Site objects
*/
export default function getPublicSites( state ) {
Copy link
Member

Choose a reason for hiding this comment

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

Please. :)

@tyxla tyxla added [Status] Needs Author Reply and removed [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. labels Jun 6, 2017
@tyxla tyxla force-pushed the remove/sites-list-profile-links branch from 67b36f1 to 901bdac Compare June 27, 2017 11:43
@tyxla tyxla added [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. and removed [Status] Needs Author Reply labels Jun 27, 2017
@tyxla
Copy link
Member Author

tyxla commented Jun 27, 2017

@mcsf @gwwar thanks for the great feedback! I've addressed it all, feel free to give it another shot. Thanks!

checked: false,
};

inputName = `site-${ this.props.site.ID }`;
Copy link
Contributor

@gwwar gwwar Jun 28, 2017

Choose a reason for hiding this comment

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

Does this update properly outside of render? I feel like this won't be correct if we pass another site/onSelect to this component

Copy link
Contributor

Choose a reason for hiding this comment

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

Does this work?

onSelect = ( event ) => {
    this.props.onSelect( event, `site-${ this.props.site.ID }` );
};

Copy link
Member Author

Choose a reason for hiding this comment

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

That shouldn't be a problem, because we're mounting one ProfileLinksAddWordPressSite instance for each site anyway. But it's a nice improvement, so I've addressed in f65e2af.

Copy link
Contributor

@gwwar gwwar left a comment

Choose a reason for hiding this comment

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

Other than the note I left, things look good 👍

@tyxla
Copy link
Member Author

tyxla commented Jun 29, 2017

Thanks! I've addressed your recommendation @gwwar, do you have any other tips before we 🚢 ?

@gwwar
Copy link
Contributor

gwwar commented Jun 29, 2017

:shipit:

@tyxla tyxla added [Status] Ready to Merge and removed [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. labels Jun 30, 2017
@tyxla tyxla force-pushed the remove/sites-list-profile-links branch from f65e2af to debc1c5 Compare June 30, 2017 07:41
@tyxla tyxla merged commit dbd79f9 into master Jun 30, 2017
@tyxla tyxla deleted the remove/sites-list-profile-links branch June 30, 2017 08:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] User & Account Settings (/me) Settings and tools for managing your WordPress.com user account. Framework Sites [Size] XL Probably needs to be broken down into multiple smaller issues [Type] Janitorial
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants