Quickstart

A quick dive into getting started with Lore

Step 2: Hide Delete Link

In this step we're going to wrap our DeleteLink with a decorator that will only display it for the user who created that tweet.

You can view the finished code for this step by checking out the authorization.2 branch of the completed project.

Create the UserCanDeleteTweet Decorator

Create a copy of the UserIsAuthorized decorator once again and rename it to UserCanDeleteTweet. Then update the code to look like this:

import PropTypes from 'prop-types';
import { AuthorizationGenerator } from 'lore-auth';

export default AuthorizationGenerator({
  displayName: 'UserCanDeleteTweet',

  propTypes: {
    tweet: PropTypes.object.isRequired
  },

  contextTypes: {
    user: PropTypes.object.isRequired
  },

  isAuthorized() {
    const { tweet } = this.props;
    const { user } = this.context;

    return tweet.data.user === user.id;
  }
});

Wrap the Delete Link

Next open the DeleteLink component and decorate the component just like you would when using connect.

// src/components/DeleteLink.js
import UserCanDeleteTweet from '../decorators/UserCanDeleteTweet';

export default UserCanDeleteTweet(createReactClass({
  ...
}));
// src/components/DeleteLink.js
import UserCanDeleteTweet from '../decorators/UserCanDeleteTweet';

class DeleteLink extends React.Component {
  ...
}

export default UserCanDeleteTweet(DeleteLink);
// src/components/DeleteLink.js
import UserCanDeleteTweet from '../decorators/UserCanDeleteTweet';

@UserCanDeleteTweet
class DeleteLink extends React.Component {
  ...
}

With that change in place, refresh the page, and the delete links should disappear from any tweets that were not created by Ayla.

Visual Check-in

If everything went well, your application should now look like this.

Code Changes

Below is a list of files modified during this step.

src/decorators/UserCanDeleteTweet.js

import PropTypes from 'prop-types';
import { AuthorizationGenerator } from 'lore-auth';

export default AuthorizationGenerator({
  displayName: 'UserCanDeleteTweet',

  propTypes: {
    tweet: PropTypes.object.isRequired
  },

  contextTypes: {
    user: PropTypes.object.isRequired
  },

  isAuthorized() {
    const { tweet } = this.props;
    const { user } = this.context;

    return tweet.data.user === user.id;
  }
});

src/components/DeleteLink.js

import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import UserCanDeleteTweet from '../decorators/UserCanDeleteTweet';

export default UserCanDeleteTweet(createReactClass({
  displayName: 'DeleteLink',

  propTypes: {
    tweet: PropTypes.object.isRequired
  },

  onClick() {
    const { tweet } = this.props;

    lore.dialog.show(function() {
      return lore.dialogs.tweet.destroy(tweet, {
        blueprint: 'optimistic',
        request: function(data) {
          return lore.actions.tweet.destroy(tweet).payload;
        }
      });
    });
  },

  render() {
    return (
      <a className="link" onClick={this.onClick}>
        delete
      </a>
    );
  }

}));
import React from 'react';
import PropTypes from 'prop-types';
import UserCanDeleteTweet from '../decorators/UserCanDeleteTweet';

class DeleteLink extends React.Component {

  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    const { tweet } = this.props;

    lore.dialog.show(function() {
      return lore.dialogs.tweet.destroy(tweet, {
        blueprint: 'optimistic',
        request: function(data) {
          return lore.actions.tweet.destroy(tweet).payload;
        }
      });
    });
  }

  render() {
    return (
      <a className="link" onClick={this.onClick}>
        delete
      </a>
    );
  }

}

DeleteLink.propTypes = {
  tweet: PropTypes.object.isRequired
};

export default UserCanDeleteTweet(DeleteLink);
import React from 'react';
import PropTypes from 'prop-types';
import UserCanDeleteTweet from '../decorators/UserCanDeleteTweet';

@UserCanDeleteTweet
class DeleteLink extends React.Component {

  static propTypes = {
    tweet: PropTypes.object.isRequired
  };

  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    const { tweet } = this.props;

    lore.dialog.show(function() {
      return lore.dialogs.tweet.destroy(tweet, {
        blueprint: 'optimistic',
        request: function(data) {
          return lore.actions.tweet.destroy(tweet).payload;
        }
      });
    });
  }

  render() {
    return (
      <a className="link" onClick={this.onClick}>
        delete
      </a>
    );
  }

}

export default DeleteLink;

Next Steps

In the next section we're going to look at an alternative approach to hiding components that doesn't use decorators.