diff --git a/app/assets/javascripts/components/actions/accounts.jsx b/app/assets/javascripts/components/actions/accounts.jsx index 803911c6c7..2245621996 100644 --- a/app/assets/javascripts/components/actions/accounts.jsx +++ b/app/assets/javascripts/components/actions/accounts.jsx @@ -40,6 +40,10 @@ export const FOLLOWING_FETCH_REQUEST = 'FOLLOWING_FETCH_REQUEST'; export const FOLLOWING_FETCH_SUCCESS = 'FOLLOWING_FETCH_SUCCESS'; export const FOLLOWING_FETCH_FAIL = 'FOLLOWING_FETCH_FAIL'; +export const RELATIONSHIPS_FETCH_REQUEST = 'RELATIONSHIPS_FETCH_REQUEST'; +export const RELATIONSHIPS_FETCH_SUCCESS = 'RELATIONSHIPS_FETCH_SUCCESS'; +export const RELATIONSHIPS_FETCH_FAIL = 'RELATIONSHIPS_FETCH_FAIL'; + export function setAccountSelf(account) { return { type: ACCOUNT_SET_SELF, @@ -304,6 +308,7 @@ export function fetchFollowers(id) { api(getState).get(`/api/v1/accounts/${id}/followers`).then(response => { dispatch(fetchFollowersSuccess(id, response.data)); + dispatch(fetchRelationships(response.data.map(item => item.id))); }).catch(error => { dispatch(fetchFollowersFail(id, error)); }); @@ -339,6 +344,7 @@ export function fetchFollowing(id) { api(getState).get(`/api/v1/accounts/${id}/following`).then(response => { dispatch(fetchFollowingSuccess(id, response.data)); + dispatch(fetchRelationships(response.data.map(item => item.id))); }).catch(error => { dispatch(fetchFollowingFail(id, error)); }); @@ -367,3 +373,36 @@ export function fetchFollowingFail(id, error) { error: error }; }; + +export function fetchRelationships(account_ids) { + return (dispatch, getState) => { + dispatch(fetchRelationshipsRequest(account_ids)); + + api(getState).get(`/api/v1/accounts/relationships?${account_ids.map(id => `id[]=${id}`).join('&')}`).then(response => { + dispatch(fetchRelationshipsSuccess(response.data)); + }).catch(error => { + dispatch(fetchRelationshipsFail(error)); + }); + }; +}; + +export function fetchRelationshipsRequest(ids) { + return { + type: RELATIONSHIPS_FETCH_REQUEST, + ids: ids + }; +}; + +export function fetchRelationshipsSuccess(relationships) { + return { + type: RELATIONSHIPS_FETCH_SUCCESS, + relationships: relationships + }; +}; + +export function fetchRelationshipsFail(error) { + return { + type: RELATIONSHIPS_FETCH_FAIL, + error: error + }; +}; diff --git a/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx b/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx index aebe362306..c46b825347 100644 --- a/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx +++ b/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx @@ -109,7 +109,7 @@ const SuggestionsBox = React.createClass({
{displayName} - {account.get('acct')} + @{account.get('acct')} ) })} diff --git a/app/assets/javascripts/components/features/followers/components/account.jsx b/app/assets/javascripts/components/features/followers/components/account.jsx index 1aa3ce511b..27f34c4773 100644 --- a/app/assets/javascripts/components/features/followers/components/account.jsx +++ b/app/assets/javascripts/components/features/followers/components/account.jsx @@ -1,62 +1,82 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; import Avatar from '../../../components/avatar'; +import DisplayName from '../../../components/display_name'; import { Link } from 'react-router'; +import IconButton from '../../../components/icon_button'; const outerStyle = { - padding: '10px' + padding: '10px', + borderBottom: '1px solid #363c4b' }; -const displayNameStyle = { +const itemStyle = { + flex: '1 1 auto', display: 'block', - fontWeight: '500', + color: '#9baec8', overflow: 'hidden', - textOverflow: 'ellipsis', - color: '#fff' + textDecoration: 'none', + fontSize: '14px' }; -const acctStyle = { - display: 'block', - overflow: 'hidden', - textOverflow: 'ellipsis' +const noteStyle = { + paddingTop: '5px', + fontSize: '12px', + color: '#616b86' }; -const itemStyle = { - display: 'block', - color: '#9baec8', - overflow: 'hidden', - textDecoration: 'none' +const buttonsStyle = { + padding: '10px' }; const Account = React.createClass({ propTypes: { account: ImmutablePropTypes.map.isRequired, - me: React.PropTypes.number.isRequired + me: React.PropTypes.number.isRequired, + onFollow: React.PropTypes.func.isRequired, + withNote: React.PropTypes.bool }, mixins: [PureRenderMixin], + handleFollow () { + this.props.onFollow(this.props.account); + }, + render () { - const { account } = this.props; + const { account, me } = this.props; if (!account) { return
; } - let displayName = account.get('display_name'); + let note, buttons; - if (displayName.length === 0) { - displayName = account.get('username'); + if (account.get('note').length > 0) { + note =
{account.get('note')}
; + } + + if (account.get('id') !== me) { + buttons = ( +
+ +
+ ); } return (
- -
- {displayName} - {account.get('acct')} - +
+ +
+ + + + {buttons} +
+ + {note}
); } diff --git a/app/assets/javascripts/components/features/followers/containers/account_container.jsx b/app/assets/javascripts/components/features/followers/containers/account_container.jsx index ee6b6dcfdf..988d60adbb 100644 --- a/app/assets/javascripts/components/features/followers/containers/account_container.jsx +++ b/app/assets/javascripts/components/features/followers/containers/account_container.jsx @@ -1,6 +1,10 @@ import { connect } from 'react-redux'; import { makeGetAccount } from '../../../selectors'; import Account from '../components/account'; +import { + followAccount, + unfollowAccount +} from '../../../actions/accounts'; const makeMapStateToProps = () => { const getAccount = makeGetAccount(); @@ -14,7 +18,13 @@ const makeMapStateToProps = () => { }; const mapDispatchToProps = (dispatch) => ({ - // + onFollow (account) { + if (account.getIn(['relationship', 'following'])) { + dispatch(unfollowAccount(account.get('id'))); + } else { + dispatch(followAccount(account.get('id'))); + } + } }); export default connect(makeMapStateToProps, mapDispatchToProps)(Account); diff --git a/app/assets/javascripts/components/reducers/timelines.jsx b/app/assets/javascripts/components/reducers/timelines.jsx index 59a1fbaa7b..4bf97c18d3 100644 --- a/app/assets/javascripts/components/reducers/timelines.jsx +++ b/app/assets/javascripts/components/reducers/timelines.jsx @@ -20,7 +20,8 @@ import { ACCOUNT_TIMELINE_FETCH_SUCCESS, ACCOUNT_TIMELINE_EXPAND_SUCCESS, FOLLOWERS_FETCH_SUCCESS, - FOLLOWING_FETCH_SUCCESS + FOLLOWING_FETCH_SUCCESS, + RELATIONSHIPS_FETCH_SUCCESS } from '../actions/accounts'; import { STATUS_FETCH_SUCCESS, @@ -184,6 +185,14 @@ function normalizeRelationship(state, relationship) { return state.setIn(['relationships', relationship.get('id')], relationship); }; +function normalizeRelationships(state, relationships) { + relationships.forEach(relationship => { + state = normalizeRelationship(state, relationship); + }); + + return state; +}; + function setSelf(state, account) { state = normalizeAccount(state, account); return state.set('me', account.get('id')); @@ -252,6 +261,8 @@ export default function timelines(state = initialState, action) { case FOLLOWERS_FETCH_SUCCESS: case FOLLOWING_FETCH_SUCCESS: return normalizeAccounts(state, Immutable.fromJS(action.accounts)); + case RELATIONSHIPS_FETCH_SUCCESS: + return normalizeRelationships(state, Immutable.fromJS(action.relationships)); default: return state; } diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss index 0fd026fee0..2b1c1194d9 100644 --- a/app/assets/stylesheets/components.scss +++ b/app/assets/stylesheets/components.scss @@ -117,17 +117,17 @@ } } -.status__display-name, .status__relative-time, .detailed-status__display-name, .detailed-status__datetime { +.status__display-name, .status__relative-time, .detailed-status__display-name, .detailed-status__datetime, .account__display-name { text-decoration: none; } -.status__display-name { +.status__display-name, .account__display-name { strong { color: #fff; } } -.status__display-name, .reply-indicator__display-name, .detailed-status__display-name { +.status__display-name, .reply-indicator__display-name, .detailed-status__display-name, .account__display-name { &:hover { strong { text-decoration: underline; @@ -135,6 +135,12 @@ } } +.account__display-name { + strong { + display: block; + } +} + .detailed-status__display-name { color: #d9e1e8; line-height: 24px;