about summary refs log tree commit diff
diff options
context:
space:
mode:
authormultiple creatures <dev@multiple-creature.party>2019-12-21 12:08:49 -0600
committermultiple creatures <dev@multiple-creature.party>2019-12-21 12:08:49 -0600
commitb8f7ccf22729b77fbb5a44ecda01de5329d07adc (patch)
treea756895666b8a6513c34a7c1286aea98c204016f
parent82f98a770b088a3ffb2c165fd22ccb0adadd57a7 (diff)
add option to set mutes that apply only to timelines
-rw-r--r--app/controllers/api/v1/accounts_controller.rb2
-rw-r--r--app/javascript/flavours/glitch/actions/accounts.js4
-rw-r--r--app/javascript/flavours/glitch/actions/mutes.js7
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/mute_modal.js27
-rw-r--r--app/javascript/flavours/glitch/reducers/mutes.js5
-rw-r--r--app/models/concerns/account_interactions.rb11
-rw-r--r--app/models/mute.rb1
-rw-r--r--app/serializers/rest/mute_serializer.rb6
-rw-r--r--app/services/mute_service.rb4
-rw-r--r--db/migrate/20191221164844_add_timelines_only_to_mute.rb5
-rw-r--r--db/schema.rb3
11 files changed, 57 insertions, 18 deletions
diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb
index b0c62778e..e452bd0f5 100644
--- a/app/controllers/api/v1/accounts_controller.rb
+++ b/app/controllers/api/v1/accounts_controller.rb
@@ -42,7 +42,7 @@ class Api::V1::AccountsController < Api::BaseController
   end
 
   def mute
-    MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications))
+    MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), timelines_only: truthy_param?(:timelines_only))
     render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
   end
 
diff --git a/app/javascript/flavours/glitch/actions/accounts.js b/app/javascript/flavours/glitch/actions/accounts.js
index b659e4ff3..05badb9a7 100644
--- a/app/javascript/flavours/glitch/actions/accounts.js
+++ b/app/javascript/flavours/glitch/actions/accounts.js
@@ -264,11 +264,11 @@ export function unblockAccountFail(error) {
 };
 
 
-export function muteAccount(id, notifications) {
+export function muteAccount(id, notifications, timelinesOnly) {
   return (dispatch, getState) => {
     dispatch(muteAccountRequest(id));
 
-    api(getState).post(`/api/v1/accounts/${id}/mute`, { notifications }).then(response => {
+    api(getState).post(`/api/v1/accounts/${id}/mute`, { notifications, timelinesOnly }).then(response => {
       // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
       dispatch(muteAccountSuccess(response.data, getState().get('statuses')));
     }).catch(error => {
diff --git a/app/javascript/flavours/glitch/actions/mutes.js b/app/javascript/flavours/glitch/actions/mutes.js
index 927fc7415..645261627 100644
--- a/app/javascript/flavours/glitch/actions/mutes.js
+++ b/app/javascript/flavours/glitch/actions/mutes.js
@@ -13,6 +13,7 @@ export const MUTES_EXPAND_FAIL    = 'MUTES_EXPAND_FAIL';
 
 export const MUTES_INIT_MODAL = 'MUTES_INIT_MODAL';
 export const MUTES_TOGGLE_HIDE_NOTIFICATIONS = 'MUTES_TOGGLE_HIDE_NOTIFICATIONS';
+export const MUTES_TOGGLE_TIMELINES_ONLY = 'MUTES_TOGGLE_TIMELINES_ONLY';
 
 export function fetchMutes() {
   return (dispatch, getState) => {
@@ -104,3 +105,9 @@ export function toggleHideNotifications() {
     dispatch({ type: MUTES_TOGGLE_HIDE_NOTIFICATIONS });
   };
 }
+
+export function toggleTimelinesOnly() {
+  return dispatch => {
+    dispatch({ type: MUTES_TOGGLE_TIMELINES_ONLY });
+  };
+}
diff --git a/app/javascript/flavours/glitch/features/ui/components/mute_modal.js b/app/javascript/flavours/glitch/features/ui/components/mute_modal.js
index 0202b1ab1..460373dd9 100644
--- a/app/javascript/flavours/glitch/features/ui/components/mute_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/mute_modal.js
@@ -7,6 +7,7 @@ import Button from 'flavours/glitch/components/button';
 import { closeModal } from 'flavours/glitch/actions/modal';
 import { muteAccount } from 'flavours/glitch/actions/accounts';
 import { toggleHideNotifications } from 'flavours/glitch/actions/mutes';
+import { toggleTimelinesOnly } from 'flavours/glitch/actions/mutes';
 
 
 const mapStateToProps = state => {
@@ -14,13 +15,14 @@ const mapStateToProps = state => {
     isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
     account: state.getIn(['mutes', 'new', 'account']),
     notifications: state.getIn(['mutes', 'new', 'notifications']),
+    timelinesOnly: state.getIn(['mutes', 'new', 'timelines_only']),
   };
 };
 
 const mapDispatchToProps = dispatch => {
   return {
-    onConfirm(account, notifications) {
-      dispatch(muteAccount(account.get('id'), notifications));
+    onConfirm(account, notifications, timelinesOnly) {
+      dispatch(muteAccount(account.get('id'), notifications, timelinesOnly));
     },
 
     onClose() {
@@ -30,6 +32,10 @@ const mapDispatchToProps = dispatch => {
     onToggleNotifications() {
       dispatch(toggleHideNotifications());
     },
+
+    onToggleTimelinesOnly() {
+      dispatch(toggleTimelinesOnly());
+    },
   };
 };
 
@@ -41,9 +47,11 @@ export default class MuteModal extends React.PureComponent {
     isSubmitting: PropTypes.bool.isRequired,
     account: PropTypes.object.isRequired,
     notifications: PropTypes.bool.isRequired,
+    timelinesOnly: PropTypes.bool.isRequired,
     onClose: PropTypes.func.isRequired,
     onConfirm: PropTypes.func.isRequired,
     onToggleNotifications: PropTypes.func.isRequired,
+    onTimelinesOnly: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
   };
 
@@ -53,7 +61,7 @@ export default class MuteModal extends React.PureComponent {
 
   handleClick = () => {
     this.props.onClose();
-    this.props.onConfirm(this.props.account, this.props.notifications);
+    this.props.onConfirm(this.props.account, this.props.notifications, this.props.timelinesOnly);
   }
 
   handleCancel = () => {
@@ -68,8 +76,12 @@ export default class MuteModal extends React.PureComponent {
     this.props.onToggleNotifications();
   }
 
+  toggleTimelinesOnly = () => {
+    this.props.onToggleTimelinesOnly();
+  }
+
   render () {
-    const { account, notifications } = this.props;
+    const { account, notifications, timelinesOnly } = this.props;
 
     return (
       <div className='modal-root__modal mute-modal'>
@@ -88,6 +100,13 @@ export default class MuteModal extends React.PureComponent {
               <Toggle id='mute-modal__hide-notifications-checkbox' checked={notifications} onChange={this.toggleNotifications} />
             </label>
           </div>
+          <div>
+            <label htmlFor='mute-modal__timelines-only-checkbox'>
+              <FormattedMessage id='mute_modal.timelines_only' defaultMessage='Hide from timelines only?' />
+              {' '}
+              <Toggle id='mute-modal__timelines-only-checkbox' checked={timelinesOnly} onChange={this.toggleTimelinesOnly} />
+            </label>
+          </div>
         </div>
 
         <div className='mute-modal__action-bar'>
diff --git a/app/javascript/flavours/glitch/reducers/mutes.js b/app/javascript/flavours/glitch/reducers/mutes.js
index 8f52a7704..d66909188 100644
--- a/app/javascript/flavours/glitch/reducers/mutes.js
+++ b/app/javascript/flavours/glitch/reducers/mutes.js
@@ -3,6 +3,7 @@ import Immutable from 'immutable';
 import {
   MUTES_INIT_MODAL,
   MUTES_TOGGLE_HIDE_NOTIFICATIONS,
+  MUTES_TOGGLE_TIMELINES_ONLY,
 } from 'flavours/glitch/actions/mutes';
 
 const initialState = Immutable.Map({
@@ -10,6 +11,7 @@ const initialState = Immutable.Map({
     isSubmitting: false,
     account: null,
     notifications: true,
+    timelinesOnly: false,
   }),
 });
 
@@ -20,9 +22,12 @@ export default function mutes(state = initialState, action) {
       state.setIn(['new', 'isSubmitting'], false);
       state.setIn(['new', 'account'], action.account);
       state.setIn(['new', 'notifications'], true);
+      state.setIn(['new', 'timelinesOnly'], false);
     });
   case MUTES_TOGGLE_HIDE_NOTIFICATIONS:
     return state.updateIn(['new', 'notifications'], (old) => !old);
+  case MUTES_TOGGLE_TIMELINES_ONLY:
+    return state.updateIn(['new', 'timelines_only'], (old) => !old);
   default:
     return state;
   }
diff --git a/app/models/concerns/account_interactions.rb b/app/models/concerns/account_interactions.rb
index f27d39483..88678910e 100644
--- a/app/models/concerns/account_interactions.rb
+++ b/app/models/concerns/account_interactions.rb
@@ -25,7 +25,7 @@ module AccountInteractions
     end
 
     def muting_map(target_account_ids, account_id)
-      Mute.where(target_account_id: target_account_ids, account_id: account_id).each_with_object({}) do |mute, mapping|
+      Mute.where(target_account_id: target_account_ids, account_id: account_id, timelines_only: false).each_with_object({}) do |mute, mapping|
         mapping[mute.target_account_id] = {
           notifications: mute.hide_notifications?,
         }
@@ -104,14 +104,15 @@ module AccountInteractions
                        .find_or_create_by!(target_account: other_account)
   end
 
-  def mute!(other_account, notifications: nil)
+  def mute!(other_account, notifications: nil, timelines_only: nil)
     notifications = true if notifications.nil?
-    mute = mute_relationships.create_with(hide_notifications: notifications).find_or_create_by!(target_account: other_account)
+    timelines_only = false if timelines_only.nil?
+    mute = mute_relationships.create_with(hide_notifications: notifications, timelines_only: timelines_only).find_or_create_by!(target_account: other_account)
     remove_potential_friendship(other_account)
 
     # When toggling a mute between hiding and allowing notifications, the mute will already exist, so the find_or_create_by! call will return the existing Mute without updating the hide_notifications attribute. Therefore, we check that hide_notifications? is what we want and set it if it isn't.
     if mute.hide_notifications? != notifications
-      mute.update!(hide_notifications: notifications)
+      mute.update!(hide_notifications: notifications, timelines_only: timelines_only)
     end
 
     mute
@@ -163,7 +164,7 @@ module AccountInteractions
   end
 
   def muting?(other_account)
-    mute_relationships.where(target_account: other_account).exists?
+    mute_relationships.where(target_account: other_account, timelines_only: false).exists?
   end
 
   def muting_conversation?(conversation)
diff --git a/app/models/mute.rb b/app/models/mute.rb
index 639120f7d..11f833d8e 100644
--- a/app/models/mute.rb
+++ b/app/models/mute.rb
@@ -9,6 +9,7 @@
 #  hide_notifications :boolean          default(TRUE), not null
 #  account_id         :bigint(8)        not null
 #  target_account_id  :bigint(8)        not null
+#  timelines_only     :boolean          default(FALSE), not null
 #
 
 class Mute < ApplicationRecord
diff --git a/app/serializers/rest/mute_serializer.rb b/app/serializers/rest/mute_serializer.rb
index 043a2f059..db33f8574 100644
--- a/app/serializers/rest/mute_serializer.rb
+++ b/app/serializers/rest/mute_serializer.rb
@@ -2,8 +2,8 @@
 
 class REST::MuteSerializer < ActiveModel::Serializer
   include RoutingHelper
-  
-  attributes :id, :account, :target_account, :created_at, :hide_notifications
+
+  attributes :id, :account, :target_account, :created_at, :hide_notifications, :timelines_only
 
   def account
     REST::AccountSerializer.new(object.account)
@@ -12,4 +12,4 @@ class REST::MuteSerializer < ActiveModel::Serializer
   def target_account
     REST::AccountSerializer.new(object.target_account)
   end
-end
\ No newline at end of file
+end
diff --git a/app/services/mute_service.rb b/app/services/mute_service.rb
index 676804cb9..1a3f4981f 100644
--- a/app/services/mute_service.rb
+++ b/app/services/mute_service.rb
@@ -1,10 +1,10 @@
 # frozen_string_literal: true
 
 class MuteService < BaseService
-  def call(account, target_account, notifications: nil)
+  def call(account, target_account, notifications: nil, timelines_only: nil)
     return if account.id == target_account.id
 
-    mute = account.mute!(target_account, notifications: notifications)
+    mute = account.mute!(target_account, notifications: notifications, timelines_only: timelines_only)
 
     if mute.hide_notifications?
       BlockWorker.perform_async(account.id, target_account.id)
diff --git a/db/migrate/20191221164844_add_timelines_only_to_mute.rb b/db/migrate/20191221164844_add_timelines_only_to_mute.rb
new file mode 100644
index 000000000..0bfb7bad6
--- /dev/null
+++ b/db/migrate/20191221164844_add_timelines_only_to_mute.rb
@@ -0,0 +1,5 @@
+class AddTimelinesOnlyToMute < ActiveRecord::Migration[5.2]
+  def change
+    add_column :mutes, :timelines_only, :boolean, default: false, null: false
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 81fc11630..f6f1ba209 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 2019_12_20_020441) do
+ActiveRecord::Schema.define(version: 2019_12_21_164844) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "pg_trgm"
@@ -443,6 +443,7 @@ ActiveRecord::Schema.define(version: 2019_12_20_020441) do
     t.boolean "hide_notifications", default: true, null: false
     t.bigint "account_id", null: false
     t.bigint "target_account_id", null: false
+    t.boolean "timelines_only", default: false, null: false
     t.index ["account_id", "target_account_id"], name: "index_mutes_on_account_id_and_target_account_id", unique: true
     t.index ["target_account_id"], name: "index_mutes_on_target_account_id"
   end