Quickstart
A quick dive into getting started with Lore
A quick dive into getting started with Lore
In this step we'll add an opacity effect to tweets to reflect when they're being created, updated or deleted.
You can view the finished code for this step by checking out the
optimistic.5
branch of the completed project.
While it's great that we can now show tweets in the Feed immediately, there's currently no visual cue to distinguish between an optimistic tweet and a real tweet, and we're also exposing functionality like the "edit" and "delete" actions that can't be performed until the tweet has a real
To improve the experience, we're going to visually fade the tweet when it's being created, updated or deleted, and we're not going to show the edit
or delete
links until we have confirmation that the tweet actually exists.
Update the render()
method of the Tweet
component to look like this:
// src/components/Tweet.js
import PayloadStates from '../constants/PayloadStates';
...
render() {
const { tweet, user } = this.props;
const timestamp = moment(tweet.data.createdAt).fromNow().split(' ago')[0];
const isOptimistic = (
tweet.state === PayloadStates.CREATING ||
tweet.state === PayloadStates.UPDATING ||
tweet.state === PayloadStates.DELETING
);
return (
<li className={"list-group-item tweet" + (isOptimistic ? " transition" : "")}>
<div className="image-container">
<img
className="img-circle avatar"
src={user.data.avatar} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
{user.data.nickname}
</h4>
<h4 className="list-group-item-heading timestamp">
{'- ' + timestamp}
</h4>
<p className="list-group-item-text text">
{tweet.data.text}
</p>
<IsOwner tweet={tweet}>
<div className="tweet-actions">
<EditLink tweet={tweet} />
<DeleteLink tweet={tweet} />
</div>
</IsOwner>
</div>
</li>
);
}
In the code above, we're examining the state
of the tweet, and if it's in the process of being created, updated, or deleted, then we're going to apply the transition
class, which will fade the tweet and the hide the actions.
If everything went well, your application should now look like this when tweets are being created, updated, or deleted:
Below is a list of files modified during this step.
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import moment from 'moment';
import { connect } from 'lore-hook-connect';
import PayloadStates from '../constants/PayloadStates';
import EditLink from './EditLink';
import DeleteLink from './DeleteLink';
import IsOwner from './IsOwner';
export default connect(function(getState, props) {
const { tweet } = props;
return {
user: getState('user.byId', {
id: tweet.data.user
})
};
})(
createReactClass({
displayName: 'Tweet',
propTypes: {
tweet: PropTypes.object.isRequired,
user: PropTypes.object.isRequired
},
render() {
const { tweet, user } = this.props;
const timestamp = moment(tweet.data.createdAt).fromNow().split(' ago')[0];
const isOptimistic = (
tweet.state === PayloadStates.CREATING ||
tweet.state === PayloadStates.UPDATING ||
tweet.state === PayloadStates.DELETING
);
return (
<li className={"list-group-item tweet" + (isOptimistic ? " transition" : "")}>
<div className="image-container">
<img
className="img-circle avatar"
src={user.data.avatar} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
{user.data.nickname}
</h4>
<h4 className="list-group-item-heading timestamp">
{'- ' + timestamp}
</h4>
<p className="list-group-item-text text">
{tweet.data.text}
</p>
<IsOwner tweet={tweet}>
<div className="tweet-actions">
<EditLink tweet={tweet} />
<DeleteLink tweet={tweet} />
</div>
</IsOwner>
</div>
</li>
);
}
})
);
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { connect } from 'lore-hook-connect';
import PayloadStates from '../constants/PayloadStates';
import EditLink from './EditLink';
import DeleteLink from './DeleteLink';
import IsOwner from './IsOwner';
class Tweet extends React.Component {
render() {
const { tweet, user } = this.props;
const timestamp = moment(tweet.data.createdAt).fromNow().split(' ago')[0];
const isOptimistic = (
tweet.state === PayloadStates.CREATING ||
tweet.state === PayloadStates.UPDATING ||
tweet.state === PayloadStates.DELETING
);
return (
<li className={"list-group-item tweet" + (isOptimistic ? " transition" : "")}>
<div className="image-container">
<img
className="img-circle avatar"
src={user.data.avatar} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
{user.data.nickname}
</h4>
<h4 className="list-group-item-heading timestamp">
{'- ' + timestamp}
</h4>
<p className="list-group-item-text text">
{tweet.data.text}
</p>
<IsOwner tweet={tweet}>
<div className="tweet-actions">
<EditLink tweet={tweet} />
<DeleteLink tweet={tweet} />
</div>
</IsOwner>
</div>
</li>
);
}
}
Tweet.propTypes = {
tweet: PropTypes.object.isRequired,
user: PropTypes.object.isRequired
};
export default connect(function(getState, props) {
const tweet = props.tweet;
return {
user: getState('user.byId', {
id: tweet.data.user
})
};
})(Tweet);
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { connect } from 'lore-hook-connect';
import PayloadStates from '../constants/PayloadStates';
import EditLink from './EditLink';
import DeleteLink from './DeleteLink';
import IsOwner from './IsOwner';
@connect(function(getState, props) {
const tweet = props.tweet;
return {
user: getState('user.byId', {
id: tweet.data.user
})
};
})
class Tweet extends React.Component {
static propTypes = {
tweet: PropTypes.object.isRequired,
user: PropTypes.object.isRequired
};
render() {
const { tweet, user } = this.props;
const timestamp = moment(tweet.data.createdAt).fromNow().split(' ago')[0];
const isOptimistic = (
tweet.state === PayloadStates.CREATING ||
tweet.state === PayloadStates.UPDATING ||
tweet.state === PayloadStates.DELETING
);
return (
<li className={"list-group-item tweet" + (isOptimistic ? " transition" : "")}>
<div className="image-container">
<img
className="img-circle avatar"
src={user.data.avatar} />
</div>
<div className="content-container">
<h4 className="list-group-item-heading title">
{user.data.nickname}
</h4>
<h4 className="list-group-item-heading timestamp">
{'- ' + timestamp}
</h4>
<p className="list-group-item-text text">
{tweet.data.text}
</p>
<IsOwner tweet={tweet}>
<div className="tweet-actions">
<EditLink tweet={tweet} />
<DeleteLink tweet={tweet} />
</div>
</IsOwner>
</div>
</li>
);
}
}
export default Tweet;
In the next section we'll hide tweets that have been deleted.