Follow/unfollow button outside of dropdown, also make favs/reblogs update indicator instantly and
then adjust to failure later if the request fails
This commit is contained in:
parent
f6d196255c
commit
90b3ff2518
5 changed files with 48 additions and 15 deletions
|
@ -7,7 +7,14 @@ const Button = React.createClass({
|
||||||
onClick: React.PropTypes.func,
|
onClick: React.PropTypes.func,
|
||||||
disabled: React.PropTypes.bool,
|
disabled: React.PropTypes.bool,
|
||||||
block: React.PropTypes.bool,
|
block: React.PropTypes.bool,
|
||||||
secondary: React.PropTypes.bool
|
secondary: React.PropTypes.bool,
|
||||||
|
size: React.PropTypes.number,
|
||||||
|
},
|
||||||
|
|
||||||
|
getDefaultProps () {
|
||||||
|
return {
|
||||||
|
size: 36
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [PureRenderMixin],
|
mixins: [PureRenderMixin],
|
||||||
|
@ -32,16 +39,16 @@ const Button = React.createClass({
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
letterSpacing: '0',
|
letterSpacing: '0',
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
padding: '0 16px',
|
padding: `0 ${this.props.size / 2.25}px`,
|
||||||
height: '36px',
|
height: `${this.props.size}px`,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
lineHeight: '36px',
|
lineHeight: `${this.props.size}px`,
|
||||||
borderRadius: '4px',
|
borderRadius: '4px',
|
||||||
textDecoration: 'none'
|
textDecoration: 'none'
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button className={`button ${this.props.secondary ? 'button-secondary' : ''}`} disabled={this.props.disabled} onClick={this.handleClick} style={style}>
|
<button className={`button ${this.props.secondary ? 'button-secondary' : ''}`} disabled={this.props.disabled} onClick={this.handleClick} style={{ ...style, ...this.props.style }}>
|
||||||
{this.props.text || this.props.children}
|
{this.props.text || this.props.children}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
|
@ -58,10 +58,8 @@ const ActionBar = React.createClass({
|
||||||
} else if (account.getIn(['relationship', 'blocking'])) {
|
} else if (account.getIn(['relationship', 'blocking'])) {
|
||||||
menu.push({ text: intl.formatMessage(messages.unblock), action: this.props.onBlock });
|
menu.push({ text: intl.formatMessage(messages.unblock), action: this.props.onBlock });
|
||||||
} else if (account.getIn(['relationship', 'following'])) {
|
} else if (account.getIn(['relationship', 'following'])) {
|
||||||
menu.push({ text: intl.formatMessage(messages.unfollow), action: this.props.onFollow });
|
|
||||||
menu.push({ text: intl.formatMessage(messages.block), action: this.props.onBlock });
|
menu.push({ text: intl.formatMessage(messages.block), action: this.props.onBlock });
|
||||||
} else {
|
} else {
|
||||||
menu.push({ text: intl.formatMessage(messages.follow), action: this.props.onFollow });
|
|
||||||
menu.push({ text: intl.formatMessage(messages.block), action: this.props.onBlock });
|
menu.push({ text: intl.formatMessage(messages.block), action: this.props.onBlock });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,22 +2,30 @@ import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import emojify from '../../../emoji';
|
import emojify from '../../../emoji';
|
||||||
import escapeTextContentForBrowser from 'react/lib/escapeTextContentForBrowser';
|
import escapeTextContentForBrowser from 'react/lib/escapeTextContentForBrowser';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import IconButton from '../../../components/icon_button';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
||||||
|
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
||||||
|
});
|
||||||
|
|
||||||
const Header = React.createClass({
|
const Header = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
account: ImmutablePropTypes.map.isRequired,
|
account: ImmutablePropTypes.map.isRequired,
|
||||||
me: React.PropTypes.number.isRequired
|
me: React.PropTypes.number.isRequired,
|
||||||
|
onFollow: React.PropTypes.func.isRequired
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [PureRenderMixin],
|
mixins: [PureRenderMixin],
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, me } = this.props;
|
const { account, me, intl } = this.props;
|
||||||
|
|
||||||
let displayName = account.get('display_name');
|
let displayName = account.get('display_name');
|
||||||
let info = '';
|
let info = '';
|
||||||
|
let actionBtn = '';
|
||||||
|
|
||||||
if (displayName.length === 0) {
|
if (displayName.length === 0) {
|
||||||
displayName = account.get('username');
|
displayName = account.get('username');
|
||||||
|
@ -27,6 +35,14 @@ const Header = React.createClass({
|
||||||
info = <span style={{ position: 'absolute', top: '10px', right: '10px', opacity: '0.7', display: 'inline-block', verticalAlign: 'top', background: 'rgba(0, 0, 0, 0.4)', color: '#fff', textTransform: 'uppercase', fontSize: '11px', fontWeight: '500', padding: '4px', borderRadius: '4px' }}><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>
|
info = <span style={{ position: 'absolute', top: '10px', right: '10px', opacity: '0.7', display: 'inline-block', verticalAlign: 'top', background: 'rgba(0, 0, 0, 0.4)', color: '#fff', textTransform: 'uppercase', fontSize: '11px', fontWeight: '500', padding: '4px', borderRadius: '4px' }}><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (me !== account.get('id')) {
|
||||||
|
actionBtn = (
|
||||||
|
<div style={{ position: 'absolute', top: '10px', left: '20px' }}>
|
||||||
|
<IconButton size={26} icon={account.getIn(['relationship', 'following']) ? 'user-times' : 'user-plus'} active={account.getIn(['relationship', 'following'])} title={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const content = { __html: emojify(account.get('note')) };
|
const content = { __html: emojify(account.get('note')) };
|
||||||
const displayNameHTML = { __html: emojify(escapeTextContentForBrowser(displayName)) };
|
const displayNameHTML = { __html: emojify(escapeTextContentForBrowser(displayName)) };
|
||||||
|
|
||||||
|
@ -45,6 +61,7 @@ const Header = React.createClass({
|
||||||
<div style={{ color: '#616b86', fontSize: '14px' }} className='account__header__content' dangerouslySetInnerHTML={content} />
|
<div style={{ color: '#616b86', fontSize: '14px' }} className='account__header__content' dangerouslySetInnerHTML={content} />
|
||||||
|
|
||||||
{info}
|
{info}
|
||||||
|
{actionBtn}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -52,4 +69,4 @@ const Header = React.createClass({
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Header;
|
export default injectIntl(Header);
|
||||||
|
|
|
@ -87,9 +87,8 @@ const Account = React.createClass({
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column>
|
||||||
<ColumnBackButton />
|
<ColumnBackButton />
|
||||||
<Header account={account} me={me} />
|
<Header account={account} me={me} onFollow={this.handleFollow} />
|
||||||
|
<ActionBar account={account} me={me} onBlock={this.handleBlock} onMention={this.handleMention} />
|
||||||
<ActionBar account={account} me={me} onFollow={this.handleFollow} onBlock={this.handleBlock} onMention={this.handleMention} />
|
|
||||||
|
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</Column>
|
</Column>
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import {
|
import {
|
||||||
|
REBLOG_REQUEST,
|
||||||
REBLOG_SUCCESS,
|
REBLOG_SUCCESS,
|
||||||
|
REBLOG_FAIL,
|
||||||
UNREBLOG_SUCCESS,
|
UNREBLOG_SUCCESS,
|
||||||
|
FAVOURITE_REQUEST,
|
||||||
FAVOURITE_SUCCESS,
|
FAVOURITE_SUCCESS,
|
||||||
|
FAVOURITE_FAIL,
|
||||||
UNFAVOURITE_SUCCESS
|
UNFAVOURITE_SUCCESS
|
||||||
} from '../actions/interactions';
|
} from '../actions/interactions';
|
||||||
import {
|
import {
|
||||||
|
@ -82,6 +86,14 @@ export default function statuses(state = initialState, action) {
|
||||||
case FAVOURITE_SUCCESS:
|
case FAVOURITE_SUCCESS:
|
||||||
case UNFAVOURITE_SUCCESS:
|
case UNFAVOURITE_SUCCESS:
|
||||||
return normalizeStatus(state, action.response);
|
return normalizeStatus(state, action.response);
|
||||||
|
case FAVOURITE_REQUEST:
|
||||||
|
return state.setIn([action.status.get('id'), 'favourited'], true);
|
||||||
|
case FAVOURITE_FAIL:
|
||||||
|
return state.setIn([action.status.get('id'), 'favourited'], false);
|
||||||
|
case REBLOG_REQUEST:
|
||||||
|
return state.setIn([action.status.get('id'), 'reblogged'], true);
|
||||||
|
case REBLOG_FAIL:
|
||||||
|
return state.setIn([action.status.get('id'), 'reblogged'], false);
|
||||||
case TIMELINE_REFRESH_SUCCESS:
|
case TIMELINE_REFRESH_SUCCESS:
|
||||||
case TIMELINE_EXPAND_SUCCESS:
|
case TIMELINE_EXPAND_SUCCESS:
|
||||||
case ACCOUNT_TIMELINE_FETCH_SUCCESS:
|
case ACCOUNT_TIMELINE_FETCH_SUCCESS:
|
||||||
|
|
Loading…
Reference in a new issue