Quickstart

A quick dive into getting started with Lore

Step 1: Hide Edit Link

In this step we're going to wrap our EditLink 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.1 branch of the completed project.

The Authorization Decorator

If you look inside src/decorators you'll find one called UserIsAuthorized that looks like this:

import { AuthorizationGenerator } from 'lore-auth';

export default AuthorizationGenerator({
  displayName: 'UserIsAuthorized',

  isAuthorized() {
    return true;
  }
});
import { AuthorizationGenerator } from 'lore-auth';

export default AuthorizationGenerator({
  displayName: 'UserIsAuthorized',

  isAuthorized() {
    return true;
  }
});
import { AuthorizationGenerator } from 'lore-auth';

export default AuthorizationGenerator({
  displayName: 'UserIsAuthorized',

  isAuthorized() {
    return true;
  }
});

This decorator is designed to wrap a component, and will only render that component if the isAuthorized() method returns true.

We're going to use this decorator to hide the edit link from any users who were not the author of the tweet.

Create the UserCanEditTweet Decorator

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

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

export default AuthorizationGenerator({
  displayName: 'UserCanEditTweet',

  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;
  }
});

In the code above we're declaring that the decorator expects to receive a tweet from props, and will need the user from context.

Then in the isAuthorized() method, we're checking whether the current user was the author of the tweet.

Wrap the Edit Link

To use this decorator, open the EditLink component and decorate the component just like you would when using connect.

// src/components/EditLink.js
import UserCanEditTweet from '../decorators/UserCanEditTweet';

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

class EditLink extends React.Component {
  ...
}

export default UserCanEditTweet(EditLink);
// src/components/EditLink.js
import UserCanEditTweet from '../decorators/UserCanEditTweet';

@UserCanEditTweet
class EditLink extends React.Component {
  ...
}

With that change in place, refresh the page, and the edit 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/UserCanEditTweet.js

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

export default AuthorizationGenerator({
  displayName: 'UserCanEditTweet',

  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/EditLink.js

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

export default UserCanEditTweet(createReactClass({
  displayName: 'EditLink',

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

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

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

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

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

class EditLink 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.update(tweet, {
        blueprint: 'optimistic',
        request: function(data) {
          return lore.actions.tweet.update(tweet, data).payload;
        }
      });
    });
  }

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

}

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

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

@UserCanEditTweet
class EditLink 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.update(tweet, {
        blueprint: 'optimistic',
        request: function(data) {
          return lore.actions.tweet.update(tweet, data).payload;
        }
      });
    });
  }

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

}

export default EditLink;

Next Steps

Next we're going to hide the delete link.