diff options
author | Eugen Rochko <eugen@zeonfederated.com> | 2016-10-28 20:05:44 +0200 |
---|---|---|
committer | Eugen Rochko <eugen@zeonfederated.com> | 2016-10-28 20:05:44 +0200 |
commit | ac4f53a3a2488c4af789df862d9e68d5327bebb1 (patch) | |
tree | 1c7f3da1dccd1eab021e0b34927a36b50634344c | |
parent | 1c84d505c8cb926710d059725c5a2d966dd4736b (diff) |
Improved how user lists look, added follow button to them
6 files changed, 116 insertions, 30 deletions
diff --git a/app/assets/javascripts/components/actions/accounts.jsx b/app/assets/javascripts/components/actions/accounts.jsx index 803911c6c..224562199 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 aebe36230..c46b82534 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({ <Link key={account.get('id')} style={itemStyle} to={`/accounts/${account.get('id')}`}> <div style={{ float: 'left', marginRight: '10px' }}><Avatar src={account.get('avatar')} size={36} /></div> <strong style={displayNameStyle}>{displayName}</strong> - <span style={acctStyle}>{account.get('acct')}</span> + <span style={acctStyle}>@{account.get('acct')}</span> </Link> ) })} diff --git a/app/assets/javascripts/components/features/followers/components/account.jsx b/app/assets/javascripts/components/features/followers/components/account.jsx index 1aa3ce511..27f34c477 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 <div />; } - let displayName = account.get('display_name'); + let note, buttons; - if (displayName.length === 0) { - displayName = account.get('username'); + if (account.get('note').length > 0) { + note = <div style={noteStyle}>{account.get('note')}</div>; + } + + if (account.get('id') !== me) { + buttons = ( + <div style={buttonsStyle}> + <IconButton icon='user-plus' title='Follow' onClick={this.handleFollow} active={account.getIn(['relationship', 'following'])} /> + </div> + ); } return ( <div style={outerStyle}> - <Link key={account.get('id')} style={itemStyle} to={`/accounts/${account.get('id')}`}> - <div style={{ float: 'left', marginRight: '10px' }}><Avatar src={account.get('avatar')} size={36} /></div> - <strong style={displayNameStyle}>{displayName}</strong> - <span style={acctStyle}>{account.get('acct')}</span> - </Link> + <div style={{ display: 'flex' }}> + <Link key={account.get('id')} style={itemStyle} className='account__display-name' to={`/accounts/${account.get('id')}`}> + <div style={{ float: 'left', marginRight: '10px' }}><Avatar src={account.get('avatar')} size={36} /></div> + <DisplayName account={account} /> + </Link> + + {buttons} + </div> + + {note} </div> ); } 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 ee6b6dcfd..988d60adb 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 59a1fbaa7..4bf97c18d 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 0fd026fee..2b1c1194d 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; |