From b4046c5957f16437910cdfe1ab45ee818118e7d7 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Fri, 31 Mar 2017 19:59:54 +0200
Subject: Rework search

---
 .../components/features/compose/index.jsx          | 64 +++++++++++++++++++---
 1 file changed, 56 insertions(+), 8 deletions(-)

(limited to 'app/assets/javascripts/components/features/compose/index.jsx')

diff --git a/app/assets/javascripts/components/features/compose/index.jsx b/app/assets/javascripts/components/features/compose/index.jsx
index 15e2c5809..d21e7a9bc 100644
--- a/app/assets/javascripts/components/features/compose/index.jsx
+++ b/app/assets/javascripts/components/features/compose/index.jsx
@@ -1,17 +1,34 @@
-import Drawer from './components/drawer';
 import ComposeFormContainer from './containers/compose_form_container';
 import UploadFormContainer from './containers/upload_form_container';
 import NavigationContainer from './containers/navigation_container';
 import PureRenderMixin from 'react-addons-pure-render-mixin';
-import SearchContainer from './containers/search_container';
 import { connect } from 'react-redux';
 import { mountCompose, unmountCompose } from '../../actions/compose';
+import { Link } from 'react-router';
+import { injectIntl, defineMessages } from 'react-intl';
+import SearchContainer from './containers/search_container';
+import { Motion, spring } from 'react-motion';
+import SearchResultsContainer from './containers/search_results_container';
+
+const messages = defineMessages({
+  start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
+  public: { id: 'navigation_bar.public_timeline', defaultMessage: 'Whole Known Network' },
+  community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
+  preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
+  logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }
+});
+
+const mapStateToProps = state => ({
+  showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden'])
+});
 
 const Compose = React.createClass({
 
   propTypes: {
     dispatch: React.PropTypes.func.isRequired,
-    withHeader: React.PropTypes.bool
+    withHeader: React.PropTypes.bool,
+    showSearch: React.PropTypes.bool,
+    intl: React.PropTypes.object.isRequired
   },
 
   mixins: [PureRenderMixin],
@@ -25,15 +42,46 @@ const Compose = React.createClass({
   },
 
   render () {
+    const { withHeader, showSearch, intl } = this.props;
+
+    let header = '';
+
+    if (withHeader) {
+      header = (
+        <div className='drawer__header'>
+          <Link title={intl.formatMessage(messages.start)} className='drawer__tab' to='/getting-started'><i className='fa fa-fw fa-asterisk' /></Link>
+          <Link title={intl.formatMessage(messages.community)} className='drawer__tab' to='/timelines/public/local'><i className='fa fa-fw fa-users' /></Link>
+          <Link title={intl.formatMessage(messages.public)} className='drawer__tab' to='/timelines/public'><i className='fa fa-fw fa-globe' /></Link>
+          <a title={intl.formatMessage(messages.preferences)} className='drawer__tab' href='/settings/preferences'><i className='fa fa-fw fa-cog' /></a>
+          <a title={intl.formatMessage(messages.logout)} className='drawer__tab' href='/auth/sign_out' data-method='delete'><i className='fa fa-fw fa-sign-out' /></a>
+        </div>
+      );
+    }
+
     return (
-      <Drawer withHeader={this.props.withHeader}>
+      <div className='drawer'>
+        {header}
+
         <SearchContainer />
-        <NavigationContainer />
-        <ComposeFormContainer />
-      </Drawer>
+
+        <div className='drawer__pager'>
+          <div className='drawer__inner'>
+            <NavigationContainer />
+            <ComposeFormContainer />
+          </div>
+
+          <Motion defaultStyle={{ x: -300 }} style={{ x: spring(showSearch ? 0 : -300, { stiffness: 210, damping: 20 }) }}>
+            {({ x }) =>
+              <div className='drawer__inner darker' style={{ transform: `translateX(${x}px)` }}>
+                <SearchResultsContainer />
+              </div>
+            }
+          </Motion>
+        </div>
+      </div>
     );
   }
 
 });
 
-export default connect()(Compose);
+export default connect(mapStateToProps)(injectIntl(Compose));
-- 
cgit 


From d93d6f5124d7e120ad1542a65b72792e31f86b22 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Fri, 31 Mar 2017 22:44:12 +0200
Subject: Fix reworked search

---
 .../javascripts/components/actions/search.jsx      |  4 ++++
 .../features/compose/components/search.jsx         |  5 +++--
 .../compose/containers/search_container.jsx        |  3 ++-
 .../components/features/compose/index.jsx          |  4 ++--
 .../components/features/getting_started/index.jsx  |  2 --
 .../components/reducers/relationships.jsx          | 22 +++++++++++-----------
 app/assets/stylesheets/components.scss             |  9 ++++++---
 app/services/search_service.rb                     |  4 ++--
 8 files changed, 30 insertions(+), 23 deletions(-)

(limited to 'app/assets/javascripts/components/features/compose/index.jsx')

diff --git a/app/assets/javascripts/components/actions/search.jsx b/app/assets/javascripts/components/actions/search.jsx
index 9d28ed11e..df3ae0db1 100644
--- a/app/assets/javascripts/components/actions/search.jsx
+++ b/app/assets/javascripts/components/actions/search.jsx
@@ -25,6 +25,10 @@ export function submitSearch() {
   return (dispatch, getState) => {
     const value = getState().getIn(['search', 'value']);
 
+    if (value.length === 0) {
+      return;
+    }
+
     dispatch(fetchSearchRequest());
 
     api(getState).get('/api/v1/search', {
diff --git a/app/assets/javascripts/components/features/compose/components/search.jsx b/app/assets/javascripts/components/features/compose/components/search.jsx
index 8e86f053e..936e003f2 100644
--- a/app/assets/javascripts/components/features/compose/components/search.jsx
+++ b/app/assets/javascripts/components/features/compose/components/search.jsx
@@ -10,6 +10,7 @@ const Search = React.createClass({
 
   propTypes: {
     value: React.PropTypes.string.isRequired,
+    submitted: React.PropTypes.bool,
     onChange: React.PropTypes.func.isRequired,
     onSubmit: React.PropTypes.func.isRequired,
     onClear: React.PropTypes.func.isRequired,
@@ -40,8 +41,8 @@ const Search = React.createClass({
   },
 
   render () {
-    const { intl, value } = this.props;
-    const hasValue = value.length > 0;
+    const { intl, value, submitted } = this.props;
+    const hasValue = value.length > 0 || submitted;
 
     return (
       <div className='search'>
diff --git a/app/assets/javascripts/components/features/compose/containers/search_container.jsx b/app/assets/javascripts/components/features/compose/containers/search_container.jsx
index 96709215a..906c0c28c 100644
--- a/app/assets/javascripts/components/features/compose/containers/search_container.jsx
+++ b/app/assets/javascripts/components/features/compose/containers/search_container.jsx
@@ -8,7 +8,8 @@ import {
 import Search from '../components/search';
 
 const mapStateToProps = state => ({
-  value: state.getIn(['search', 'value'])
+  value: state.getIn(['search', 'value']),
+  submitted: state.getIn(['search', 'submitted'])
 });
 
 const mapDispatchToProps = dispatch => ({
diff --git a/app/assets/javascripts/components/features/compose/index.jsx b/app/assets/javascripts/components/features/compose/index.jsx
index d21e7a9bc..d4df259dc 100644
--- a/app/assets/javascripts/components/features/compose/index.jsx
+++ b/app/assets/javascripts/components/features/compose/index.jsx
@@ -70,9 +70,9 @@ const Compose = React.createClass({
             <ComposeFormContainer />
           </div>
 
-          <Motion defaultStyle={{ x: -300 }} style={{ x: spring(showSearch ? 0 : -300, { stiffness: 210, damping: 20 }) }}>
+          <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
             {({ x }) =>
-              <div className='drawer__inner darker' style={{ transform: `translateX(${x}px)` }}>
+              <div className='drawer__inner darker' style={{ transform: `translateX(${x}%)` }}>
                 <SearchResultsContainer />
               </div>
             }
diff --git a/app/assets/javascripts/components/features/getting_started/index.jsx b/app/assets/javascripts/components/features/getting_started/index.jsx
index 6f9e988ba..8253ad017 100644
--- a/app/assets/javascripts/components/features/getting_started/index.jsx
+++ b/app/assets/javascripts/components/features/getting_started/index.jsx
@@ -43,8 +43,6 @@ const GettingStarted = ({ intl, me }) => {
 
       <div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}>
         <div className='static-content getting-started'>
-          <p><FormattedMessage id='getting_started.about_addressing' defaultMessage='You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the form at the top of the sidebar.' /></p>
-          <p><FormattedMessage id='getting_started.about_shortcuts' defaultMessage='If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.' /></p>
           <p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on github at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/mastodon/blob/master/docs/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p>
         </div>
       </div>
diff --git a/app/assets/javascripts/components/reducers/relationships.jsx b/app/assets/javascripts/components/reducers/relationships.jsx
index 591f8034b..c65c48b43 100644
--- a/app/assets/javascripts/components/reducers/relationships.jsx
+++ b/app/assets/javascripts/components/reducers/relationships.jsx
@@ -23,16 +23,16 @@ const initialState = Immutable.Map();
 
 export default function relationships(state = initialState, action) {
   switch(action.type) {
-    case ACCOUNT_FOLLOW_SUCCESS:
-    case ACCOUNT_UNFOLLOW_SUCCESS:
-    case ACCOUNT_BLOCK_SUCCESS:
-    case ACCOUNT_UNBLOCK_SUCCESS:
-    case ACCOUNT_MUTE_SUCCESS:
-    case ACCOUNT_UNMUTE_SUCCESS:
-      return normalizeRelationship(state, action.relationship);
-    case RELATIONSHIPS_FETCH_SUCCESS:
-      return normalizeRelationships(state, action.relationships);
-    default:
-      return state;
+  case ACCOUNT_FOLLOW_SUCCESS:
+  case ACCOUNT_UNFOLLOW_SUCCESS:
+  case ACCOUNT_BLOCK_SUCCESS:
+  case ACCOUNT_UNBLOCK_SUCCESS:
+  case ACCOUNT_MUTE_SUCCESS:
+  case ACCOUNT_UNMUTE_SUCCESS:
+    return normalizeRelationship(state, action.relationship);
+  case RELATIONSHIPS_FETCH_SUCCESS:
+    return normalizeRelationships(state, action.relationships);
+  default:
+    return state;
   }
 };
diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss
index 9c138e495..d7589d9b0 100644
--- a/app/assets/stylesheets/components.scss
+++ b/app/assets/stylesheets/components.scss
@@ -1120,9 +1120,8 @@ a.status__content__spoiler-link {
   box-sizing: border-box;
   overflow-y: auto;
   padding-bottom: 235px;
-  background: image-url('mastodon-getting-started.png') no-repeat bottom left;
-  height: auto;
-  min-height: 100%;
+  background: image-url('mastodon-getting-started.png') no-repeat 0 100% local;
+  height: 100%;
 
   p {
     color: $color2;
@@ -1793,6 +1792,10 @@ button.active i.fa-retweet {
     &.active {
       transform: translateZ(0) rotate(90deg);
     }
+
+    &:hover {
+      color: $color5;
+    }
   }
 }
 
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
index 159c03713..e9745010b 100644
--- a/app/services/search_service.rb
+++ b/app/services/search_service.rb
@@ -2,10 +2,10 @@
 
 class SearchService < BaseService
   def call(query, limit, resolve = false, account = nil)
-    return if query.blank?
-
     results = { accounts: [], hashtags: [], statuses: [] }
 
+    return results if query.blank?
+
     if query =~ /\Ahttps?:\/\//
       resource = FetchRemoteResourceService.new.call(query)
 
-- 
cgit 


From e809caa0e1633cede15b2578d1582d9878eae291 Mon Sep 17 00:00:00 2001
From: Eugen Rochko <eugen@zeonfederated.com>
Date: Sun, 2 Apr 2017 15:46:31 +0200
Subject: Fix feed regeneration bug

---
 app/assets/javascripts/components/features/compose/index.jsx | 2 +-
 app/services/fan_out_on_write_service.rb                     | 6 +++++-
 app/services/precompute_feed_service.rb                      | 8 ++++----
 3 files changed, 10 insertions(+), 6 deletions(-)

(limited to 'app/assets/javascripts/components/features/compose/index.jsx')

diff --git a/app/assets/javascripts/components/features/compose/index.jsx b/app/assets/javascripts/components/features/compose/index.jsx
index d4df259dc..9421de3ff 100644
--- a/app/assets/javascripts/components/features/compose/index.jsx
+++ b/app/assets/javascripts/components/features/compose/index.jsx
@@ -72,7 +72,7 @@ const Compose = React.createClass({
 
           <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
             {({ x }) =>
-              <div className='drawer__inner darker' style={{ transform: `translateX(${x}%)` }}>
+              <div className='drawer__inner darker' style={{ transform: `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
                 <SearchResultsContainer />
               </div>
             }
diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb
index 0cacfd7cd..402b84b2f 100644
--- a/app/services/fan_out_on_write_service.rb
+++ b/app/services/fan_out_on_write_service.rb
@@ -6,7 +6,11 @@ class FanOutOnWriteService < BaseService
   def call(status)
     deliver_to_self(status) if status.account.local?
 
-    status.direct_visibility? ? deliver_to_mentioned_followers(status) : deliver_to_followers(status)
+    if status.direct_visibility?
+      deliver_to_mentioned_followers(status)
+    else
+      deliver_to_followers(status)
+    end
 
     return if status.account.silenced? || !status.public_visibility? || status.reblog?
 
diff --git a/app/services/precompute_feed_service.rb b/app/services/precompute_feed_service.rb
index 54d11b631..984eb8e86 100644
--- a/app/services/precompute_feed_service.rb
+++ b/app/services/precompute_feed_service.rb
@@ -4,10 +4,10 @@ class PrecomputeFeedService < BaseService
   # Fill up a user's home/mentions feed from DB and return a subset
   # @param [Symbol] type :home or :mentions
   # @param [Account] account
-  def call(type, account)
-    Status.send("as_#{type}_timeline", account).limit(FeedManager::MAX_ITEMS).each do |status|
-      next if FeedManager.instance.filter?(type, status, account)
-      redis.zadd(FeedManager.instance.key(type, account.id), status.id, status.reblog? ? status.reblog_of_id : status.id)
+  def call(_, account)
+    Status.as_home_timeline(account).limit(FeedManager::MAX_ITEMS).each do |status|
+      next if (status.direct_visibility? && !status.permitted?(account)) || FeedManager.instance.filter?(:home, status, account)
+      redis.zadd(FeedManager.instance.key(:home, account.id), status.id, status.reblog? ? status.reblog_of_id : status.id)
     end
   end
 
-- 
cgit 

