Skip to content
Snippets Groups Projects
Commit 20f581f7 authored by Eugen Rochko's avatar Eugen Rochko
Browse files

Display follow suggestions

parent e21a3fe0
No related branches found
No related tags found
No related merge requests found
......@@ -6,10 +6,32 @@ export const SUGGESTIONS_FETCH_FAIL = 'SUGGESTIONS_FETCH_FAIL';
export function fetchSuggestions() {
return (dispatch, getState) => {
dispatch(fetchSuggestionsRequest());
api(getState).get('/api/v1/accounts/suggestions').then(response => {
console.log(response.data);
dispatch(fetchSuggestionsSuccess(response.data));
}).catch(error => {
console.error(error);
dispatch(fetchSuggestionsFail(error));
});
};
};
export function fetchSuggestionsRequest() {
return {
type: SUGGESTIONS_FETCH_REQUEST
};
};
export function fetchSuggestionsSuccess(suggestions) {
return {
type: SUGGESTIONS_FETCH_SUCCESS,
suggestions: suggestions
};
};
export function fetchSuggestionsFail(error) {
return {
type: SUGGESTIONS_FETCH_FAIL,
error: error
};
};
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';
const outerStyle = {
marginBottom: '10px',
borderTop: '1px solid #616b86'
};
const headerStyle = {
fontSize: '14px',
fontWeight: '500',
display: 'block',
padding: '10px',
color: '#9baec8',
background: '#454b5e',
width: '120px',
marginTop: '-18px'
};
const itemStyle = {
display: 'block',
padding: '10px',
color: '#9baec8',
overflow: 'hidden',
textDecoration: 'none'
};
const displayNameStyle = {
display: 'block',
fontWeight: '500'
};
const acctStyle = {
display: 'block'
};
const SuggestionsBox = React.createClass({
propTypes: {
accounts: ImmutablePropTypes.list.isRequired
},
mixins: [PureRenderMixin],
render () {
const accounts = this.props.accounts.take(2);
return (
<div style={outerStyle}>
<strong style={headerStyle}>Who to follow</strong>
{accounts.map(account => {
let displayName = account.get('display_name');
if (displayName.length === 0) {
displayName = account.get('username');
}
return (
<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>
);
}
});
export default SuggestionsBox;
import { connect } from 'react-redux';
import { getSuggestions } from '../../../selectors';
import SuggestionsBox from '../components/suggestions_box';
const mapStateToProps = (state) => ({
accounts: getSuggestions(state)
});
export default connect(mapStateToProps)(SuggestionsBox);
......@@ -4,11 +4,22 @@ import FollowFormContainer from '../ui/containers/follow_form_container';
import UploadFormContainer from '../ui/containers/upload_form_container';
import NavigationContainer from '../ui/containers/navigation_container';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import SuggestionsContainer from './containers/suggestions_container';
import { fetchSuggestions } from '../../actions/suggestions';
import { connect } from 'react-redux';
const Compose = React.createClass({
propTypes: {
dispatch: React.PropTypes.func.isRequired
},
mixins: [PureRenderMixin],
componentDidMount () {
this.props.dispatch(fetchSuggestions());
},
render () {
return (
<Drawer>
......@@ -18,6 +29,7 @@ const Compose = React.createClass({
<UploadFormContainer />
</div>
<SuggestionsContainer />
<FollowFormContainer />
</Drawer>
);
......@@ -25,4 +37,4 @@ const Compose = React.createClass({
});
export default Compose;
export default connect()(Compose);
......@@ -25,6 +25,7 @@ import {
STATUS_DELETE_SUCCESS
} from '../actions/statuses';
import { FOLLOW_SUBMIT_SUCCESS } from '../actions/follow';
import { SUGGESTIONS_FETCH_SUCCESS } from '../actions/suggestions';
import Immutable from 'immutable';
const initialState = Immutable.Map({
......@@ -37,7 +38,8 @@ const initialState = Immutable.Map({
me: null,
ancestors: Immutable.Map(),
descendants: Immutable.Map(),
relationships: Immutable.Map()
relationships: Immutable.Map(),
suggestions: Immutable.List([])
});
function normalizeStatus(state, status) {
......@@ -189,6 +191,14 @@ function normalizeContext(state, status, ancestors, descendants) {
});
};
function normalizeSuggestions(state, accounts) {
accounts.forEach(account => {
state = state.setIn(['accounts', account.get('id')], account);
});
return state.set('suggestions', accounts.map(account => account.get('id')));
};
export default function timelines(state = initialState, action) {
switch(action.type) {
case TIMELINE_REFRESH_SUCCESS:
......@@ -221,6 +231,8 @@ export default function timelines(state = initialState, action) {
return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses));
case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
return appendNormalizedAccountTimeline(state, action.id, Immutable.fromJS(action.statuses));
case SUGGESTIONS_FETCH_SUCCESS:
return normalizeSuggestions(state, Immutable.fromJS(action.suggestions));
default:
return state;
}
......
......@@ -79,3 +79,9 @@ export const getNotifications = createSelector([getNotificationsBase], (base) =>
return arr;
});
const getSuggestionsBase = (state) => state.getIn(['timelines', 'suggestions']);
export const getSuggestions = createSelector([getSuggestionsBase, getAccounts], (base, accounts) => {
return base.map(accountId => accounts.get(accountId));
});
......@@ -35,7 +35,7 @@ class Follow < ApplicationRecord
b = neo.create_unique_node('account_index', 'Account', target_account_id.to_s, account_id: target_account_id)
neo.create_unique_relationship('follow_index', 'Follow', id.to_s, 'follows', a, b)
rescue Neography::NeographyError => e
rescue Neography::NeographyError, Excon::Error::Socket => e
Rails.logger.error e
end
......@@ -43,7 +43,7 @@ class Follow < ApplicationRecord
neo = Neography::Rest.new
rel = neo.get_relationship_index('follow_index', 'Follow', id.to_s)
neo.delete_relationship(rel)
rescue Neography::NeographyError => e
rescue Neography::NeographyError, Excon::Error::Socket => e
Rails.logger.error e
end
end
......@@ -3,5 +3,8 @@ class FollowSuggestion
neo = Neography::Rest.new
account_ids = neo.execute_query('START a=node:account_index(Account={id}) MATCH (a)-[:follows]->(b)-[:follows]->(c) WHERE a <> c AND NOT (a)-[:follows]->(c) RETURN DISTINCT c.account_id', id: for_account_id)
Account.where(id: account_ids['data'].first) unless account_ids.empty?
rescue Neography::NeographyError, Excon::Error::Socket => e
Rails.logger.error e
[]
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment