about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2016-10-16 17:04:13 +0200
committerEugen Rochko <eugen@zeonfederated.com>2016-10-16 17:04:13 +0200
commit447033038549495318f7c218941829e845aabb61 (patch)
treeae3b3ca628946a5cc64d92a933dac6e66976bb5a
parentf9c9fef1575af3b97cbb300acae60272affc7730 (diff)
Backfill follow suggestions with fallback when not enough results. Cycling
through suggestions in UI
-rw-r--r--app/assets/javascripts/components/features/compose/components/suggestions_box.jsx49
-rw-r--r--app/models/follow_suggestion.rb39
2 files changed, 65 insertions, 23 deletions
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 50229eb69..d7eeee729 100644
--- a/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx
+++ b/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx
@@ -6,7 +6,8 @@ import { Link }           from 'react-router';
 
 const outerStyle = {
   marginBottom: '10px',
-  borderTop: '1px solid #616b86'
+  borderTop: '1px solid #616b86',
+  position: 'relative'
 };
 
 const headerStyle = {
@@ -41,26 +42,64 @@ const acctStyle = {
   textOverflow: 'ellipsis'
 };
 
+const nextStyle = {
+  fontWeight: '400',
+  color: '#2b90d9'
+};
+
 const SuggestionsBox = React.createClass({
 
   propTypes: {
-    accounts: ImmutablePropTypes.list.isRequired
+    accounts: ImmutablePropTypes.list.isRequired,
+    perWindow: React.PropTypes.number
+  },
+
+  getInitialState () {
+    return {
+      index: 0
+    };
+  },
+
+  getDefaultProps () {
+    return {
+      perWindow: 2
+    };
   },
 
   mixins: [PureRenderMixin],
 
+  handleNextClick (e) {
+    e.preventDefault();
+
+    let newIndex = this.state.index + 1;
+
+    if (this.props.accounts.skip(this.props.perWindow * newIndex).size === 0) {
+      newIndex = 0;
+    }
+
+    this.setState({ index: newIndex });
+  },
+
   render () {
-    const accounts = this.props.accounts.take(3);
+    const { accounts, perWindow } = this.props;
 
     if (accounts.size === 0) {
       return <div />;
     }
 
+    let nextLink = '';
+
+    if (accounts.size > perWindow) {
+      nextLink = <a href='#' style={nextStyle} onClick={this.handleNextClick}>Next</a>;
+    }
+
     return (
       <div style={outerStyle}>
-        <strong style={headerStyle}>Who to follow</strong>
+        <strong style={headerStyle}>
+          Who to follow {nextLink}
+        </strong>
 
-        {accounts.map(account => {
+        {accounts.skip(perWindow * this.state.index).take(perWindow).map(account => {
           let displayName = account.get('display_name');
 
           if (displayName.length === 0) {
diff --git a/app/models/follow_suggestion.rb b/app/models/follow_suggestion.rb
index 719270318..25d28f5ac 100644
--- a/app/models/follow_suggestion.rb
+++ b/app/models/follow_suggestion.rb
@@ -14,9 +14,17 @@ END
 
     results = neo.execute_query(query, id: for_account_id, limit: limit)
 
-    return fallback(for_account_id, limit) if results.empty? || results['data'].empty?
+    if results.empty?
+      results = fallback(for_account_id, limit)
+    elsif results['data'].size < limit
+      results['data'] = (results['data'] + fallback(for_account_id, limit - results['data'].size)['data']).uniq
+    end
 
-    map_to_accounts(for_account_id, results)
+    account_ids  = results['data'].map(&:first)
+    blocked_ids  = Block.where(account_id: for_account_id).pluck(:target_account_id)
+    accounts_map = Account.where(id: account_ids - blocked_ids).map { |a| [a.id, a] }.to_h
+
+    account_ids.map { |id| accounts_map[id] }.compact
   rescue Neography::NeographyError, Excon::Error::Socket => e
     Rails.logger.error e
     return []
@@ -25,23 +33,18 @@ END
   private
 
   def self.fallback(for_account_id, limit)
-    neo     = Neography::Rest.new
-    query   = 'MATCH (a) WHERE a.account_id <> {id} RETURN a.account_id ORDER BY a.nodeRank DESC LIMIT {limit}'
-    results = neo.execute_query(query, id: for_account_id, limit: limit)
-
-    map_to_accounts(for_account_id, results)
-  rescue Neography::NeographyError, Excon::Error::Socket => e
-    Rails.logger.error e
-    return []
-  end
-
-  def self.map_to_accounts(for_account_id, results)
-    return [] if results.empty? || results['data'].empty?
+    neo = Neography::Rest.new
 
-    account_ids  = results['data'].map(&:first)
-    blocked_ids  = Block.where(account_id: for_account_id).pluck(:target_account_id)
-    accounts_map = Account.where(id: account_ids - blocked_ids).map { |a| [a.id, a] }.to_h
+    query = <<END
+START a=node:account_index(Account={id})
+MATCH (b)
+WHERE a <> b
+AND NOT (a)-[:follows]->(b)
+RETURN b.account_id
+ORDER BY b.nodeRank DESC
+LIMIT {limit}
+END
 
-    account_ids.map { |id| accounts_map[id] }.compact
+    neo.execute_query(query, id: for_account_id, limit: limit)
   end
 end