about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/components/actions/accounts.jsx74
-rw-r--r--app/assets/javascripts/components/components/status_action_bar.jsx2
-rw-r--r--app/assets/javascripts/components/features/account/components/action_bar.jsx38
-rw-r--r--app/assets/javascripts/components/features/account/index.jsx12
-rw-r--r--app/assets/javascripts/components/reducers/timelines.jsx4
-rw-r--r--app/assets/stylesheets/components.scss20
-rw-r--r--app/services/favourite_service.rb2
-rw-r--r--app/services/follow_service.rb2
-rw-r--r--app/services/process_feed_service.rb4
-rw-r--r--app/services/process_interaction_service.rb4
-rw-r--r--app/services/process_mentions_service.rb2
-rw-r--r--app/services/reblog_service.rb2
12 files changed, 134 insertions, 32 deletions
diff --git a/app/assets/javascripts/components/actions/accounts.jsx b/app/assets/javascripts/components/actions/accounts.jsx
index c4aa2d80c..eeec405d1 100644
--- a/app/assets/javascripts/components/actions/accounts.jsx
+++ b/app/assets/javascripts/components/actions/accounts.jsx
@@ -15,6 +15,14 @@ export const ACCOUNT_UNFOLLOW_REQUEST = 'ACCOUNT_UNFOLLOW_REQUEST';
 export const ACCOUNT_UNFOLLOW_SUCCESS = 'ACCOUNT_UNFOLLOW_SUCCESS';
 export const ACCOUNT_UNFOLLOW_FAIL    = 'ACCOUNT_UNFOLLOW_FAIL';
 
+export const ACCOUNT_BLOCK_REQUEST = 'ACCOUNT_BLOCK_REQUEST';
+export const ACCOUNT_BLOCK_SUCCESS = 'ACCOUNT_BLOCK_SUCCESS';
+export const ACCOUNT_BLOCK_FAIL    = 'ACCOUNT_BLOCK_FAIL';
+
+export const ACCOUNT_UNBLOCK_REQUEST = 'ACCOUNT_UNBLOCK_REQUEST';
+export const ACCOUNT_UNBLOCK_SUCCESS = 'ACCOUNT_UNBLOCK_SUCCESS';
+export const ACCOUNT_UNBLOCK_FAIL    = 'ACCOUNT_UNBLOCK_FAIL';
+
 export const ACCOUNT_TIMELINE_FETCH_REQUEST = 'ACCOUNT_TIMELINE_FETCH_REQUEST';
 export const ACCOUNT_TIMELINE_FETCH_SUCCESS = 'ACCOUNT_TIMELINE_FETCH_SUCCESS';
 export const ACCOUNT_TIMELINE_FETCH_FAIL    = 'ACCOUNT_TIMELINE_FETCH_FAIL';
@@ -204,3 +212,69 @@ export function expandAccountTimelineFail(id, error) {
     error: error
   };
 };
+
+export function blockAccount(id) {
+  return (dispatch, getState) => {
+    dispatch(blockAccountRequest(id));
+
+    api(getState).post(`/api/v1/accounts/${id}/block`).then(response => {
+      dispatch(blockAccountSuccess(response.data));
+    }).catch(error => {
+      dispatch(blockAccountFail(id, error));
+    });
+  };
+};
+
+export function unblockAccount(id) {
+  return (dispatch, getState) => {
+    dispatch(unblockAccountRequest(id));
+
+    api(getState).post(`/api/v1/accounts/${id}/unblock`).then(response => {
+      dispatch(unblockAccountSuccess(response.data));
+    }).catch(error => {
+      dispatch(unblockAccountFail(id, error));
+    });
+  };
+};
+
+export function blockAccountRequest(id) {
+  return {
+    type: ACCOUNT_BLOCK_REQUEST,
+    id: id
+  };
+};
+
+export function blockAccountSuccess(relationship) {
+  return {
+    type: ACCOUNT_BLOCK_SUCCESS,
+    relationship: relationship
+  };
+};
+
+export function blockAccountFail(error) {
+  return {
+    type: ACCOUNT_BLOCK_FAIL,
+    error: error
+  };
+};
+
+export function unblockAccountRequest(id) {
+  return {
+    type: ACCOUNT_UNBLOCK_REQUEST,
+    id: id
+  };
+};
+
+export function unblockAccountSuccess(relationship) {
+  return {
+    type: ACCOUNT_UNBLOCK_SUCCESS,
+    relationship: relationship
+  };
+};
+
+export function unblockAccountFail(error) {
+  return {
+    type: ACCOUNT_UNBLOCK_FAIL,
+    error: error
+  };
+};
diff --git a/app/assets/javascripts/components/components/status_action_bar.jsx b/app/assets/javascripts/components/components/status_action_bar.jsx
index 76f0ac5f1..3e826d68a 100644
--- a/app/assets/javascripts/components/components/status_action_bar.jsx
+++ b/app/assets/javascripts/components/components/status_action_bar.jsx
@@ -41,6 +41,8 @@ const StatusActionBar = React.createClass({
           <li><a href='#' onClick={this.handleDeleteClick}>Delete</a></li>
         </ul>
       );
+    } else {
+      menu = <ul />;
     }
 
     return (
diff --git a/app/assets/javascripts/components/features/account/components/action_bar.jsx b/app/assets/javascripts/components/features/account/components/action_bar.jsx
index 309471dd2..aadf168b2 100644
--- a/app/assets/javascripts/components/features/account/components/action_bar.jsx
+++ b/app/assets/javascripts/components/features/account/components/action_bar.jsx
@@ -7,7 +7,8 @@ const ActionBar = React.createClass({
   propTypes: {
     account: ImmutablePropTypes.map.isRequired,
     me: React.PropTypes.number.isRequired,
-    onFollow: React.PropTypes.func.isRequired
+    onFollow: React.PropTypes.func.isRequired,
+    onBlock: React.PropTypes.func.isRequired
   },
 
   mixins: [PureRenderMixin],
@@ -16,25 +17,46 @@ const ActionBar = React.createClass({
     const { account, me } = this.props;
 
     let infoText     = '';
+    let follow       = '';
     let buttonText   = '';
+    let block        = '';
+    let disabled     = false;
 
     if (account.get('id') === me) {
       buttonText = 'This is you!';
+      disabled   = true;
     } else {
-      if (account.getIn(['relationship', 'following'])) {
-        buttonText = 'Unfollow';
+      let blockText = '';
+
+      if (account.getIn(['relationship', 'blocking'])) {
+        buttonText = 'Blocked';
+        disabled   = true;
+        blockText  = 'Unblock';
       } else {
-        buttonText = 'Follow';
-      }
+        if (account.getIn(['relationship', 'following'])) {
+          buttonText = 'Unfollow';
+        } else {
+          buttonText = 'Follow';
+        }
+
+        if (account.getIn(['relationship', 'followed_by'])) {
+          infoText = 'Follows you!';
+        }
 
-      if (account.getIn(['relationship', 'followed_by'])) {
-        infoText = 'Follows you!';
+        blockText = 'Block';
       }
+
+      block = <Button text={blockText} onClick={this.props.onBlock} />;
+    }
+
+    if (!account.getIn(['relationship', 'blocking'])) {
+      follow = <Button text={buttonText} onClick={this.props.onFollow} disabled={disabled} />;
     }
 
     return (
       <div style={{ borderTop: '1px solid #363c4b', borderBottom: '1px solid #363c4b', padding: '10px', lineHeight: '36px', overflow: 'hidden', flex: '0 0 auto' }}>
-        <Button text={buttonText} onClick={this.props.onFollow} disabled={account.get('id') === me} /> <span style={{ color: '#616b86', fontWeight: '500', textTransform: 'uppercase', float: 'right', display: 'block' }}>{infoText}</span>
+        {follow} {block}
+        <span style={{ color: '#616b86', fontWeight: '500', textTransform: 'uppercase', float: 'right', display: 'block' }}>{infoText}</span>
       </div>
     );
   },
diff --git a/app/assets/javascripts/components/features/account/index.jsx b/app/assets/javascripts/components/features/account/index.jsx
index 40c06c545..c2389af2e 100644
--- a/app/assets/javascripts/components/features/account/index.jsx
+++ b/app/assets/javascripts/components/features/account/index.jsx
@@ -5,6 +5,8 @@ import {
   fetchAccount,
   followAccount,
   unfollowAccount,
+  blockAccount,
+  unblockAccount,
   fetchAccountTimeline,
   expandAccountTimeline
 }                            from '../../actions/accounts';
@@ -66,6 +68,14 @@ const Account = React.createClass({
     }
   },
 
+  handleBlock () {
+    if (this.props.account.getIn(['relationship', 'blocking'])) {
+      this.props.dispatch(unblockAccount(this.props.account.get('id')));
+    } else {
+      this.props.dispatch(blockAccount(this.props.account.get('id')));
+    }
+  },
+
   handleReply (status) {
     this.props.dispatch(replyCompose(status));
   },
@@ -104,7 +114,7 @@ const Account = React.createClass({
     return (
       <div style={{ display: 'flex', flexDirection: 'column', 'flex': '0 0 auto', height: '100%' }}>
         <Header account={account} />
-        <ActionBar account={account} me={me} onFollow={this.handleFollow} onUnfollow={this.handleUnfollow} />
+        <ActionBar account={account} me={me} onFollow={this.handleFollow} onBlock={this.handleBlock} />
         <StatusList statuses={statuses} me={me} onScrollToBottom={this.handleScrollToBottom} onReply={this.handleReply} onReblog={this.handleReblog} onFavourite={this.handleFavourite} />
       </div>
     );
diff --git a/app/assets/javascripts/components/reducers/timelines.jsx b/app/assets/javascripts/components/reducers/timelines.jsx
index ec42b7825..392683150 100644
--- a/app/assets/javascripts/components/reducers/timelines.jsx
+++ b/app/assets/javascripts/components/reducers/timelines.jsx
@@ -15,6 +15,8 @@ import {
   ACCOUNT_FETCH_SUCCESS,
   ACCOUNT_FOLLOW_SUCCESS,
   ACCOUNT_UNFOLLOW_SUCCESS,
+  ACCOUNT_BLOCK_SUCCESS,
+  ACCOUNT_UNBLOCK_SUCCESS,
   ACCOUNT_TIMELINE_FETCH_SUCCESS,
   ACCOUNT_TIMELINE_EXPAND_SUCCESS
 }                                from '../actions/accounts';
@@ -231,6 +233,8 @@ export default function timelines(state = initialState, action) {
       return normalizeAccount(state, Immutable.fromJS(action.account), Immutable.fromJS(action.relationship));
     case ACCOUNT_FOLLOW_SUCCESS:
     case ACCOUNT_UNFOLLOW_SUCCESS:
+    case ACCOUNT_UNBLOCK_SUCCESS:
+    case ACCOUNT_BLOCK_SUCCESS:
       return normalizeRelationship(state, Immutable.fromJS(action.relationship));
     case STATUS_FETCH_SUCCESS:
       return normalizeContext(state, Immutable.fromJS(action.status), Immutable.fromJS(action.context.ancestors), Immutable.fromJS(action.context.descendants));
diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss
index 7c111e04a..155517362 100644
--- a/app/assets/stylesheets/components.scss
+++ b/app/assets/stylesheets/components.scss
@@ -170,7 +170,6 @@
 .dropdown--active .dropdown__content {
   display: block;
   z-index: 9999;
-  box-shadow: 0 0 15px rgba(0, 0, 0, 0.4);
 
   &:before {
     content: "";
@@ -187,20 +186,11 @@
 
   ul {
     list-style: none;
-  }
-
-  li {
-    &:first-child a {
-      border-radius: 4px 4px 0 0;
-    }
-
-    &:last-child a {
-      border-radius: 0 0 4px 4px;
-    }
-
-    &:first-child:last-child a {
-      border-radius: 4px;
-    }
+    background: #d9e1e8;
+    padding: 4px 0;
+    border-radius: 4px;
+    box-shadow: 0 0 15px rgba(0, 0, 0, 0.4);
+    min-width: 100px;
   }
 
   a {
diff --git a/app/services/favourite_service.rb b/app/services/favourite_service.rb
index 3d0bc718c..98f08d32b 100644
--- a/app/services/favourite_service.rb
+++ b/app/services/favourite_service.rb
@@ -8,7 +8,7 @@ class FavouriteService < BaseService
     account.ping!(account_url(account, format: 'atom'), [Rails.configuration.x.hub_url])
 
     if status.local?
-      NotificationMailer.favourite(status, account).deliver_later
+      NotificationMailer.favourite(status, account).deliver_later unless status.account.blocking?(account)
     else
       NotificationWorker.perform_async(favourite.stream_entry.id, status.account_id)
     end
diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb
index 6dd85dd7d..f44d53398 100644
--- a/app/services/follow_service.rb
+++ b/app/services/follow_service.rb
@@ -10,7 +10,7 @@ class FollowService < BaseService
     follow = source_account.follow!(target_account)
 
     if target_account.local?
-      NotificationMailer.follow(target_account, source_account).deliver_later
+      NotificationMailer.follow(target_account, source_account).deliver_later unless target_account.blocking?(source_account)
     else
       subscribe_service.call(target_account)
       NotificationWorker.perform_async(follow.stream_entry.id, target_account.id)
diff --git a/app/services/process_feed_service.rb b/app/services/process_feed_service.rb
index 1ea79835b..9585b15dd 100644
--- a/app/services/process_feed_service.rb
+++ b/app/services/process_feed_service.rb
@@ -69,7 +69,7 @@ class ProcessFeedService < BaseService
 
         unless mentioned_account.nil?
           mentioned_account.mentions.where(status: status).first_or_create(status: status)
-          NotificationMailer.mention(mentioned_account, status).deliver_later
+          NotificationMailer.mention(mentioned_account, status).deliver_later unless mentioned_account.blocking?(status.account)
         end
       else
         # What to do about remote user?
@@ -114,7 +114,7 @@ class ProcessFeedService < BaseService
 
     if !status.reblog.nil?
       status.save!
-      NotificationMailer.reblog(status.reblog, status.account).deliver_later if status.reblog.local?
+      NotificationMailer.reblog(status.reblog, status.account).deliver_later if status.reblog.local? && !status.reblog.account.blocking?(status.account)
     end
   end
 
diff --git a/app/services/process_interaction_service.rb b/app/services/process_interaction_service.rb
index 96dae30da..31c7c46a5 100644
--- a/app/services/process_interaction_service.rb
+++ b/app/services/process_interaction_service.rb
@@ -58,7 +58,7 @@ class ProcessInteractionService < BaseService
 
   def follow!(account, target_account)
     account.follow!(target_account)
-    NotificationMailer.follow(target_account, account).deliver_later
+    NotificationMailer.follow(target_account, account).deliver_later unless target_account.blocking?(account)
   end
 
   def unfollow!(account, target_account)
@@ -78,7 +78,7 @@ class ProcessInteractionService < BaseService
   def favourite!(xml, from_account)
     current_status = status(xml)
     current_status.favourites.where(account: from_account).first_or_create!(account: from_account)
-    NotificationMailer.favourite(current_status, from_account).deliver_later
+    NotificationMailer.favourite(current_status, from_account).deliver_later unless current_status.account.blocking?(from_account)
   end
 
   def add_post!(body, account)
diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb
index 2d09fc2cd..8baa03d07 100644
--- a/app/services/process_mentions_service.rb
+++ b/app/services/process_mentions_service.rb
@@ -27,7 +27,7 @@ class ProcessMentionsService < BaseService
       mentioned_account = mention.account
 
       if mentioned_account.local?
-        NotificationMailer.mention(mentioned_account, status).deliver_later
+        NotificationMailer.mention(mentioned_account, status).deliver_later unless mentioned_account.blocking?(status.account)
       else
         NotificationWorker.perform_async(status.stream_entry.id, mentioned_account.id)
       end
diff --git a/app/services/reblog_service.rb b/app/services/reblog_service.rb
index f149477c7..56c59cb18 100644
--- a/app/services/reblog_service.rb
+++ b/app/services/reblog_service.rb
@@ -9,7 +9,7 @@ class ReblogService < BaseService
     account.ping!(account_url(account, format: 'atom'), [Rails.configuration.x.hub_url])
 
     if reblogged_status.local?
-      NotificationMailer.reblog(reblogged_status, account).deliver_later
+      NotificationMailer.reblog(reblogged_status, account).deliver_later unless reblogged_status.account.blocking?(account)
     else
       NotificationWorker.perform_async(reblog.stream_entry.id, reblogged_status.account_id)
     end