about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2022-02-23 16:45:22 +0100
committerGitHub <noreply@github.com>2022-02-23 16:45:22 +0100
commit7b816eb5aeeed61fa1e3a7c95cbb5bb7978f7fa5 (patch)
tree5888b4ec8bbb68d88c527ec8dada549f4a6c9b77 /app
parent3e12abc1fe9b32f69eaf2a8d1edf8bad9622eb4a (diff)
Add notifications for new sign-ups (#16953)
Diffstat (limited to 'app')
-rw-r--r--app/controllers/api/web/push_subscriptions_controller.rb23
-rw-r--r--app/javascript/mastodon/actions/notifications.js3
-rw-r--r--app/javascript/mastodon/features/notifications/components/column_settings.js16
-rw-r--r--app/javascript/mastodon/features/notifications/components/notification.js25
-rw-r--r--app/javascript/mastodon/reducers/settings.js3
-rw-r--r--app/javascript/mastodon/service_worker/web_push_locales.js1
-rw-r--r--app/models/notification.rb14
-rw-r--r--app/services/bootstrap_timeline_service.rb7
-rw-r--r--app/services/notify_service.rb42
9 files changed, 69 insertions, 65 deletions
diff --git a/app/controllers/api/web/push_subscriptions_controller.rb b/app/controllers/api/web/push_subscriptions_controller.rb
index db2512e5f..5167928e9 100644
--- a/app/controllers/api/web/push_subscriptions_controller.rb
+++ b/app/controllers/api/web/push_subscriptions_controller.rb
@@ -17,17 +17,7 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
 
     data = {
       policy: 'all',
-
-      alerts: {
-        follow: alerts_enabled,
-        follow_request: alerts_enabled,
-        favourite: alerts_enabled,
-        reblog: alerts_enabled,
-        mention: alerts_enabled,
-        poll: alerts_enabled,
-        status: alerts_enabled,
-        update: alerts_enabled,
-      },
+      alerts: Notification::TYPES.index_with { alerts_enabled },
     }
 
     data.deep_merge!(data_params) if params[:data]
@@ -62,15 +52,6 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
   end
 
   def data_params
-    @data_params ||= params.require(:data).permit(:policy, alerts: [
-      :follow,
-      :follow_request,
-      :favourite,
-      :reblog,
-      :mention,
-      :poll,
-      :status,
-      :update,
-    ])
+    @data_params ||= params.require(:data).permit(:policy, alerts: Notification::TYPES)
   end
 end
diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js
index 9370811e0..00e8d74d7 100644
--- a/app/javascript/mastodon/actions/notifications.js
+++ b/app/javascript/mastodon/actions/notifications.js
@@ -45,7 +45,7 @@ defineMessages({
 });
 
 const fetchRelatedRelationships = (dispatch, notifications) => {
-  const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id);
+  const accountIds = notifications.filter(item => ['follow', 'follow_request', 'admin.sign_up'].indexOf(item.type) !== -1).map(item => item.account.id);
 
   if (accountIds.length > 0) {
     dispatch(fetchRelationships(accountIds));
@@ -132,6 +132,7 @@ const excludeTypesFromFilter = filter => {
     'poll',
     'status',
     'update',
+    'admin.sign_up',
   ]);
 
   return allTypes.filterNot(item => item === filter).toJS();
diff --git a/app/javascript/mastodon/features/notifications/components/column_settings.js b/app/javascript/mastodon/features/notifications/components/column_settings.js
index ada8b6e4a..84db04430 100644
--- a/app/javascript/mastodon/features/notifications/components/column_settings.js
+++ b/app/javascript/mastodon/features/notifications/components/column_settings.js
@@ -5,6 +5,7 @@ import { FormattedMessage } from 'react-intl';
 import ClearColumnButton from './clear_column_button';
 import GrantPermissionButton from './grant_permission_button';
 import SettingToggle from './setting_toggle';
+import { isStaff } from 'mastodon/initial_state';
 
 export default class ColumnSettings extends React.PureComponent {
 
@@ -155,7 +156,7 @@ export default class ColumnSettings extends React.PureComponent {
         </div>
 
         <div role='group' aria-labelledby='notifications-update'>
-          <span id='notifications-status' className='column-settings__section'><FormattedMessage id='notifications.column_settings.update' defaultMessage='Edits:' /></span>
+          <span id='notifications-update' className='column-settings__section'><FormattedMessage id='notifications.column_settings.update' defaultMessage='Edits:' /></span>
 
           <div className='column-settings__row'>
             <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'update']} onChange={onChange} label={alertStr} />
@@ -164,6 +165,19 @@ export default class ColumnSettings extends React.PureComponent {
             <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'update']} onChange={onChange} label={soundStr} />
           </div>
         </div>
+
+        {isStaff && (
+          <div role='group' aria-labelledby='notifications-admin-sign-up'>
+            <span id='notifications-status' className='column-settings__section'><FormattedMessage id='notifications.column_settings.admin.sign_up' defaultMessage='New sign-ups:' /></span>
+
+            <div className='column-settings__row'>
+              <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'admin.sign_up']} onChange={onChange} label={alertStr} />
+              {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'admin.sign_up']} onChange={this.onPushChange} label={pushStr} />}
+              <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'admin.sign_up']} onChange={onChange} label={showStr} />
+              <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'admin.sign_up']} onChange={onChange} label={soundStr} />
+            </div>
+          </div>
+        )}
       </div>
     );
   }
diff --git a/app/javascript/mastodon/features/notifications/components/notification.js b/app/javascript/mastodon/features/notifications/components/notification.js
index cd471852b..9198e9c9d 100644
--- a/app/javascript/mastodon/features/notifications/components/notification.js
+++ b/app/javascript/mastodon/features/notifications/components/notification.js
@@ -20,6 +20,7 @@ const messages = defineMessages({
   reblog: { id: 'notification.reblog', defaultMessage: '{name} boosted your status' },
   status: { id: 'notification.status', defaultMessage: '{name} just posted' },
   update: { id: 'notification.update', defaultMessage: '{name} edited a post' },
+  adminSignUp: { id: 'notification.admin.sign_up', defaultMessage: '{name} signed up' },
 });
 
 const notificationForScreenReader = (intl, message, timestamp) => {
@@ -344,6 +345,28 @@ class Notification extends ImmutablePureComponent {
     );
   }
 
+  renderAdminSignUp (notification, account, link) {
+    const { intl, unread } = this.props;
+
+    return (
+      <HotKeys handlers={this.getHandlers()}>
+        <div className={classNames('notification notification-admin-sign-up focusable', { unread })} tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.adminSignUp, { name: account.get('acct') }), notification.get('created_at'))}>
+          <div className='notification__message'>
+            <div className='notification__favourite-icon-wrapper'>
+              <Icon id='user-plus' fixedWidth />
+            </div>
+
+            <span title={notification.get('created_at')}>
+              <FormattedMessage id='notification.admin.sign_up' defaultMessage='{name} signed up' values={{ name: link }} />
+            </span>
+          </div>
+
+          <AccountContainer id={account.get('id')} hidden={this.props.hidden} />
+        </div>
+      </HotKeys>
+    );
+  }
+
   render () {
     const { notification } = this.props;
     const account          = notification.get('account');
@@ -367,6 +390,8 @@ class Notification extends ImmutablePureComponent {
       return this.renderUpdate(notification, link);
     case 'poll':
       return this.renderPoll(notification, account);
+    case 'admin.sign_up':
+      return this.renderAdminSignUp(notification, account, link);
     }
 
     return null;
diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js
index 5146abe98..39639f3dc 100644
--- a/app/javascript/mastodon/reducers/settings.js
+++ b/app/javascript/mastodon/reducers/settings.js
@@ -37,6 +37,7 @@ const initialState = ImmutableMap({
       poll: false,
       status: false,
       update: false,
+      'admin.sign_up': false,
     }),
 
     quickFilter: ImmutableMap({
@@ -57,6 +58,7 @@ const initialState = ImmutableMap({
       poll: true,
       status: true,
       update: true,
+      'admin.sign_up': true,
     }),
 
     sounds: ImmutableMap({
@@ -68,6 +70,7 @@ const initialState = ImmutableMap({
       poll: true,
       status: true,
       update: true,
+      'admin.sign_up': true,
     }),
   }),
 
diff --git a/app/javascript/mastodon/service_worker/web_push_locales.js b/app/javascript/mastodon/service_worker/web_push_locales.js
index 807a1bcb9..7d713cd37 100644
--- a/app/javascript/mastodon/service_worker/web_push_locales.js
+++ b/app/javascript/mastodon/service_worker/web_push_locales.js
@@ -22,6 +22,7 @@ filenames.forEach(filename => {
     'notification.poll': full['notification.poll'] || '',
     'notification.status': full['notification.status'] || '',
     'notification.update': full['notification.update'] || '',
+    'notification.admin.sign_up': full['notification.admin.sign_up'] || '',
 
     'status.show_more': full['status.show_more'] || '',
     'status.reblog': full['status.reblog'] || '',
diff --git a/app/models/notification.rb b/app/models/notification.rb
index c14eb8a7e..9bf296386 100644
--- a/app/models/notification.rb
+++ b/app/models/notification.rb
@@ -36,6 +36,7 @@ class Notification < ApplicationRecord
     favourite
     poll
     update
+    admin.sign_up
   ).freeze
 
   TARGET_STATUS_INCLUDES_BY_TYPE = {
@@ -63,13 +64,10 @@ class Notification < ApplicationRecord
   scope :without_suspended, -> { joins(:from_account).merge(Account.without_suspended) }
 
   scope :browserable, ->(exclude_types = [], account_id = nil) {
-    types = TYPES - exclude_types.map(&:to_sym)
-
-    if account_id.nil?
-      where(type: types)
-    else
-      where(type: types, from_account_id: account_id)
-    end
+    scope = all
+    scope = where(from_account_id: account_id) if account_id.present?
+    scope = scope.where(type: TYPES - exclude_types.map(&:to_sym)) unless exclude_types.empty?
+    scope
   }
 
   def type
@@ -142,6 +140,8 @@ class Notification < ApplicationRecord
       self.from_account_id = activity&.account_id
     when 'Mention'
       self.from_account_id = activity&.status&.account_id
+    when 'Account'
+      self.from_account_id = activity&.id
     end
   end
 end
diff --git a/app/services/bootstrap_timeline_service.rb b/app/services/bootstrap_timeline_service.rb
index e1a1b98c3..70e7cc570 100644
--- a/app/services/bootstrap_timeline_service.rb
+++ b/app/services/bootstrap_timeline_service.rb
@@ -5,6 +5,7 @@ class BootstrapTimelineService < BaseService
     @source_account = source_account
 
     autofollow_inviter!
+    notify_staff!
   end
 
   private
@@ -14,4 +15,10 @@ class BootstrapTimelineService < BaseService
 
     FollowService.new.call(@source_account, @source_account.user.invite.user.account)
   end
+
+  def notify_staff!
+    User.staff.includes(:account).find_each do |user|
+      NotifyService.new.call(user.account, :'admin.new_user', @source_account)
+    end
+  end
 end
diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb
index ab80a7e4b..b1f9fd755 100644
--- a/app/services/notify_service.rb
+++ b/app/services/notify_service.rb
@@ -22,34 +22,6 @@ class NotifyService < BaseService
     FeedManager.instance.filter?(:mentions, @notification.mention.status, @recipient)
   end
 
-  def blocked_status?
-    false
-  end
-
-  def blocked_favourite?
-    false
-  end
-
-  def blocked_follow?
-    false
-  end
-
-  def blocked_reblog?
-    false
-  end
-
-  def blocked_follow_request?
-    false
-  end
-
-  def blocked_poll?
-    false
-  end
-
-  def blocked_update?
-    false
-  end
-
   def following_sender?
     return @following_sender if defined?(@following_sender)
     @following_sender = @recipient.following?(@notification.from_account) || @recipient.requested?(@notification.from_account)
@@ -149,15 +121,15 @@ class NotifyService < BaseService
 
     return blocked if message? && from_staff?
 
-    blocked ||= domain_blocking?                                 # Skip for domain blocked accounts
-    blocked ||= @recipient.blocking?(@notification.from_account) # Skip for blocked accounts
+    blocked ||= domain_blocking?
+    blocked ||= @recipient.blocking?(@notification.from_account)
     blocked ||= @recipient.muting_notifications?(@notification.from_account)
-    blocked ||= hellbanned?                                      # Hellban
-    blocked ||= optional_non_follower?                           # Options
-    blocked ||= optional_non_following?                          # Options
-    blocked ||= optional_non_following_and_direct?               # Options
+    blocked ||= hellbanned?
+    blocked ||= optional_non_follower?
+    blocked ||= optional_non_following?
+    blocked ||= optional_non_following_and_direct?
     blocked ||= conversation_muted?
-    blocked ||= send("blocked_#{@notification.type}?")           # Type-dependent filters
+    blocked ||= blocked_mention? if @notification.type == :mention
     blocked
   end