Add notifications for new sign-ups (#16953)
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -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> | ||||
|     ); | ||||
|   } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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, | ||||
|     }), | ||||
|   }), | ||||
|  | ||||
|   | ||||
| @@ -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'] || '', | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -61,6 +61,7 @@ ignore_unused: | ||||
|   - 'admin.action_logs.actions.*' | ||||
|   - 'statuses.attached.*' | ||||
|   - 'move_handler.carry_{mutes,blocks}_over_text' | ||||
|   - 'notification_mailer.*' | ||||
|  | ||||
| ignore_inconsistent_interpolations: | ||||
|   - '*.one' | ||||
|   | ||||
| @@ -1176,6 +1176,9 @@ en: | ||||
|     carry_mutes_over_text: This user moved from %{acct}, which you had muted. | ||||
|     copy_account_note_text: 'This user moved from %{acct}, here were your previous notes about them:' | ||||
|   notification_mailer: | ||||
|     admin: | ||||
|       sign_up: | ||||
|         subject: "%{name} signed up" | ||||
|     digest: | ||||
|       action: View all notifications | ||||
|       body: Here is a brief summary of the messages you missed since your last visit on %{since} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user