Add notifications for new reports (#18697)
This commit is contained in:
		
							parent
							
								
									602f291da9
								
							
						
					
					
						commit
						2936f42a14
					
				
					 18 changed files with 235 additions and 17 deletions
				
			
		|  | @ -91,6 +91,10 @@ export function updateNotifications(notification, intlMessages, intlLocale) { | |||
|         dispatch(importFetchedStatus(notification.status)); | ||||
|       } | ||||
| 
 | ||||
|       if (notification.report) { | ||||
|         dispatch(importFetchedAccount(notification.report.target_account)); | ||||
|       } | ||||
| 
 | ||||
|       dispatch({ | ||||
|         type: NOTIFICATIONS_UPDATE, | ||||
|         notification, | ||||
|  | @ -134,6 +138,7 @@ const excludeTypesFromFilter = filter => { | |||
|     'status', | ||||
|     'update', | ||||
|     'admin.sign_up', | ||||
|     'admin.report', | ||||
|   ]); | ||||
| 
 | ||||
|   return allTypes.filterNot(item => item === filter).toJS(); | ||||
|  | @ -179,6 +184,7 @@ export function expandNotifications({ maxId } = {}, done = noOp) { | |||
| 
 | ||||
|       dispatch(importFetchedAccounts(response.data.map(item => item.account))); | ||||
|       dispatch(importFetchedStatuses(response.data.map(item => item.status).filter(status => !!status))); | ||||
|       dispatch(importFetchedAccounts(response.data.filter(item => item.report).map(item => item.report.target_account))); | ||||
| 
 | ||||
|       dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore, isLoadingRecent, isLoadingRecent && preferPendingItems)); | ||||
|       fetchRelatedRelationships(dispatch, response.data); | ||||
|  |  | |||
|  | @ -132,8 +132,16 @@ export default class IconButton extends React.PureComponent { | |||
|     ); | ||||
| 
 | ||||
|     if (href) { | ||||
|       contents = ( | ||||
|         <a href={href} target='_blank' rel='noopener noreferrer'> | ||||
|       return ( | ||||
|         <a | ||||
|           href={href} | ||||
|           aria-label={title} | ||||
|           title={title} | ||||
|           target='_blank' | ||||
|           rel='noopener noreferrer' | ||||
|           className={classes} | ||||
|           style={style} | ||||
|         > | ||||
|           {contents} | ||||
|         </a> | ||||
|       ); | ||||
|  |  | |||
|  | @ -178,6 +178,19 @@ export default class ColumnSettings extends React.PureComponent { | |||
|             </div> | ||||
|           </div> | ||||
|         )} | ||||
| 
 | ||||
|         {isStaff && ( | ||||
|           <div role='group' aria-labelledby='notifications-admin-report'> | ||||
|             <span id='notifications-status' className='column-settings__section'><FormattedMessage id='notifications.column_settings.admin.report' defaultMessage='New reports:' /></span> | ||||
| 
 | ||||
|             <div className='column-settings__row'> | ||||
|               <SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'admin.report']} onChange={onChange} label={alertStr} /> | ||||
|               {showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'admin.report']} onChange={this.onPushChange} label={pushStr} />} | ||||
|               <SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'admin.report']} onChange={onChange} label={showStr} /> | ||||
|               <SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'admin.report']} onChange={onChange} label={soundStr} /> | ||||
|             </div> | ||||
|           </div> | ||||
|         )} | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; | |||
| import { me } from 'mastodon/initial_state'; | ||||
| import StatusContainer from 'mastodon/containers/status_container'; | ||||
| import AccountContainer from 'mastodon/containers/account_container'; | ||||
| import Report from './report'; | ||||
| import FollowRequestContainer from '../containers/follow_request_container'; | ||||
| import Icon from 'mastodon/components/icon'; | ||||
| import Permalink from 'mastodon/components/permalink'; | ||||
|  | @ -21,6 +22,7 @@ const messages = defineMessages({ | |||
|   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' }, | ||||
|   adminReport: { id: 'notification.admin.report', defaultMessage: '{name} reported {target}' }, | ||||
| }); | ||||
| 
 | ||||
| const notificationForScreenReader = (intl, message, timestamp) => { | ||||
|  | @ -367,6 +369,32 @@ class Notification extends ImmutablePureComponent { | |||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   renderAdminReport (notification, account, link) { | ||||
|     const { intl, unread, report } = this.props; | ||||
| 
 | ||||
|     const targetAccount = report.get('target_account'); | ||||
|     const targetDisplayNameHtml = { __html: targetAccount.get('display_name_html') }; | ||||
|     const targetLink = <bdi><Permalink className='notification__display-name' href={targetAccount.get('url')} title={targetAccount.get('acct')} to={`/@${targetAccount.get('acct')}`} dangerouslySetInnerHTML={targetDisplayNameHtml} /></bdi>; | ||||
| 
 | ||||
|     return ( | ||||
|       <HotKeys handlers={this.getHandlers()}> | ||||
|         <div className={classNames('notification notification-admin-report focusable', { unread })} tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.adminReport, { name: account.get('acct'), target: notification.getIn(['report', 'target_account', 'acct']) }), notification.get('created_at'))}> | ||||
|           <div className='notification__message'> | ||||
|             <div className='notification__favourite-icon-wrapper'> | ||||
|               <Icon id='flag' fixedWidth /> | ||||
|             </div> | ||||
| 
 | ||||
|             <span title={notification.get('created_at')}> | ||||
|               <FormattedMessage id='notification.admin.report' defaultMessage='{name} reported {target}' values={{ name: link, target: targetLink }} /> | ||||
|             </span> | ||||
|           </div> | ||||
| 
 | ||||
|           <Report account={account} report={notification.get('report')} hidden={this.props.hidden} /> | ||||
|         </div> | ||||
|       </HotKeys> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     const { notification } = this.props; | ||||
|     const account          = notification.get('account'); | ||||
|  | @ -392,6 +420,8 @@ class Notification extends ImmutablePureComponent { | |||
|       return this.renderPoll(notification, account); | ||||
|     case 'admin.sign_up': | ||||
|       return this.renderAdminSignUp(notification, account, link); | ||||
|     case 'admin.report': | ||||
|       return this.renderAdminReport(notification, account, link); | ||||
|     } | ||||
| 
 | ||||
|     return null; | ||||
|  |  | |||
|  | @ -0,0 +1,62 @@ | |||
| import React, { Fragment } from 'react'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import AvatarOverlay from 'mastodon/components/avatar_overlay'; | ||||
| import RelativeTimestamp from 'mastodon/components/relative_timestamp'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
|   openReport: { id: 'report_notification.open', defaultMessage: 'Open report' }, | ||||
|   other: { id: 'report_notification.categories.other', defaultMessage: 'Other' }, | ||||
|   spam: { id: 'report_notification.categories.spam', defaultMessage: 'Spam' }, | ||||
|   violation: { id: 'report_notification.categories.violation', defaultMessage: 'Rule violation' }, | ||||
| }); | ||||
| 
 | ||||
| export default @injectIntl | ||||
| class Report extends ImmutablePureComponent { | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     account: ImmutablePropTypes.map.isRequired, | ||||
|     report: ImmutablePropTypes.map.isRequired, | ||||
|     hidden: PropTypes.bool, | ||||
|     intl: PropTypes.object.isRequired, | ||||
|   }; | ||||
| 
 | ||||
|   render () { | ||||
|     const { intl, hidden, report, account } = this.props; | ||||
| 
 | ||||
|     if (!report) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     if (hidden) { | ||||
|       return ( | ||||
|         <Fragment> | ||||
|           {report.get('id')} | ||||
|         </Fragment> | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|       <div className='notification__report'> | ||||
|         <div className='notification__report__avatar'> | ||||
|           <AvatarOverlay account={report.get('target_account')} friend={account} /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div className='notification__report__details'> | ||||
|           <div> | ||||
|             <RelativeTimestamp timestamp={report.get('created_at')} short={false} /> · <FormattedMessage id='report_notification.attached_statuses' defaultMessage='{count, plural, one {{count} post} other {{count} posts}} attached' values={{ count: report.get('status_ids').size }} /> | ||||
|             <br /> | ||||
|             <strong>{intl.formatMessage(messages[report.get('category')])}</strong> | ||||
|           </div> | ||||
| 
 | ||||
|           <div className='notification__report__actions'> | ||||
|             <a href={`/admin/reports/${report.get('id')}`} className='button' target='_blank' rel='noopener noreferrer'>{intl.formatMessage(messages.openReport)}</a> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | @ -1,5 +1,5 @@ | |||
| import { connect } from 'react-redux'; | ||||
| import { makeGetNotification, makeGetStatus } from '../../../selectors'; | ||||
| import { makeGetNotification, makeGetStatus, makeGetReport } from '../../../selectors'; | ||||
| import Notification from '../components/notification'; | ||||
| import { initBoostModal } from '../../../actions/boosts'; | ||||
| import { mentionCompose } from '../../../actions/compose'; | ||||
|  | @ -18,12 +18,14 @@ import { boostModal } from '../../../initial_state'; | |||
| const makeMapStateToProps = () => { | ||||
|   const getNotification = makeGetNotification(); | ||||
|   const getStatus = makeGetStatus(); | ||||
|   const getReport = makeGetReport(); | ||||
| 
 | ||||
|   const mapStateToProps = (state, props) => { | ||||
|     const notification = getNotification(state, props.notification, props.accountId); | ||||
|     return { | ||||
|       notification: notification, | ||||
|       status: notification.get('status') ? getStatus(state, { id: notification.get('status') }) : null, | ||||
|       report: notification.get('report') ? getReport(state, notification.get('report'), notification.getIn(['report', 'target_account', 'id'])) : null, | ||||
|     }; | ||||
|   }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -2532,6 +2532,10 @@ | |||
|       { | ||||
|         "defaultMessage": "New sign-ups:", | ||||
|         "id": "notifications.column_settings.admin.sign_up" | ||||
|       }, | ||||
|       { | ||||
|         "defaultMessage": "New reports:", | ||||
|         "id": "notifications.column_settings.admin.report" | ||||
|       } | ||||
|     ], | ||||
|     "path": "app/javascript/mastodon/features/notifications/components/column_settings.json" | ||||
|  | @ -2625,6 +2629,10 @@ | |||
|         "defaultMessage": "{name} signed up", | ||||
|         "id": "notification.admin.sign_up" | ||||
|       }, | ||||
|       { | ||||
|         "defaultMessage": "{name} reported {target}", | ||||
|         "id": "notification.admin.report" | ||||
|       }, | ||||
|       { | ||||
|         "defaultMessage": "{name} has requested to follow you", | ||||
|         "id": "notification.follow_request" | ||||
|  | @ -2653,6 +2661,31 @@ | |||
|     ], | ||||
|     "path": "app/javascript/mastodon/features/notifications/components/notifications_permission_banner.json" | ||||
|   }, | ||||
|   { | ||||
|     "descriptors": [ | ||||
|       { | ||||
|         "defaultMessage": "Open report", | ||||
|         "id": "report_notification.open" | ||||
|       }, | ||||
|       { | ||||
|         "defaultMessage": "Other", | ||||
|         "id": "report_notification.categories.other" | ||||
|       }, | ||||
|       { | ||||
|         "defaultMessage": "Spam", | ||||
|         "id": "report_notification.categories.spam" | ||||
|       }, | ||||
|       { | ||||
|         "defaultMessage": "Rule violation", | ||||
|         "id": "report_notification.categories.violation" | ||||
|       }, | ||||
|       { | ||||
|         "defaultMessage": "{count, plural, one {{count} post} other {{count} posts}} attached", | ||||
|         "id": "report_notification.attached_statuses" | ||||
|       } | ||||
|     ], | ||||
|     "path": "app/javascript/mastodon/features/notifications/components/report.json" | ||||
|   }, | ||||
|   { | ||||
|     "descriptors": [ | ||||
|       { | ||||
|  |  | |||
|  | @ -314,6 +314,7 @@ | |||
|   "navigation_bar.preferences": "Preferences", | ||||
|   "navigation_bar.public_timeline": "Federated timeline", | ||||
|   "navigation_bar.security": "Security", | ||||
|   "notification.admin.report": "{name} reported {target}", | ||||
|   "notification.admin.sign_up": "{name} signed up", | ||||
|   "notification.favourite": "{name} favourited your post", | ||||
|   "notification.follow": "{name} followed you", | ||||
|  | @ -326,6 +327,7 @@ | |||
|   "notification.update": "{name} edited a post", | ||||
|   "notifications.clear": "Clear notifications", | ||||
|   "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?", | ||||
|   "notifications.column_settings.admin.report": "New reports:", | ||||
|   "notifications.column_settings.admin.sign_up": "New sign-ups:", | ||||
|   "notifications.column_settings.alert": "Desktop notifications", | ||||
|   "notifications.column_settings.favourite": "Favourites:", | ||||
|  | @ -431,6 +433,11 @@ | |||
|   "report.thanks.title_actionable": "Thanks for reporting, we'll look into this.", | ||||
|   "report.unfollow": "Unfollow @{name}", | ||||
|   "report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.", | ||||
|   "report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} posts}} attached", | ||||
|   "report_notification.categories.other": "Other", | ||||
|   "report_notification.categories.spam": "Spam", | ||||
|   "report_notification.categories.violation": "Rule violation", | ||||
|   "report_notification.open": "Open report", | ||||
|   "search.placeholder": "Search", | ||||
|   "search_popout.search_format": "Advanced search format", | ||||
|   "search_popout.tips.full_text": "Simple text returns posts you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.", | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ import { | |||
| } from '../actions/app'; | ||||
| import { DOMAIN_BLOCK_SUCCESS } from 'mastodon/actions/domain_blocks'; | ||||
| import { TIMELINE_DELETE, TIMELINE_DISCONNECT } from '../actions/timelines'; | ||||
| import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; | ||||
| import { fromJS, Map as ImmutableMap, List as ImmutableList } from 'immutable'; | ||||
| import compareId from '../compare_id'; | ||||
| 
 | ||||
| const initialState = ImmutableMap({ | ||||
|  | @ -52,6 +52,7 @@ const notificationToMap = notification => ImmutableMap({ | |||
|   account: notification.account.id, | ||||
|   created_at: notification.created_at, | ||||
|   status: notification.status ? notification.status.id : null, | ||||
|   report: notification.report ? fromJS(notification.report) : null, | ||||
| }); | ||||
| 
 | ||||
| const normalizeNotification = (state, notification, usePendingItems) => { | ||||
|  |  | |||
|  | @ -39,6 +39,7 @@ const initialState = ImmutableMap({ | |||
|       status: false, | ||||
|       update: false, | ||||
|       'admin.sign_up': false, | ||||
|       'admin.report': false, | ||||
|     }), | ||||
| 
 | ||||
|     quickFilter: ImmutableMap({ | ||||
|  | @ -60,6 +61,7 @@ const initialState = ImmutableMap({ | |||
|       status: true, | ||||
|       update: true, | ||||
|       'admin.sign_up': true, | ||||
|       'admin.report': true, | ||||
|     }), | ||||
| 
 | ||||
|     sounds: ImmutableMap({ | ||||
|  | @ -72,6 +74,7 @@ const initialState = ImmutableMap({ | |||
|       status: true, | ||||
|       update: true, | ||||
|       'admin.sign_up': true, | ||||
|       'admin.report': true, | ||||
|     }), | ||||
|   }), | ||||
| 
 | ||||
|  |  | |||
|  | @ -152,14 +152,15 @@ export const getAlerts = createSelector([getAlertsBase], (base) => { | |||
|   return arr; | ||||
| }); | ||||
| 
 | ||||
| export const makeGetNotification = () => { | ||||
|   return createSelector([ | ||||
|     (_, base)             => base, | ||||
|     (state, _, accountId) => state.getIn(['accounts', accountId]), | ||||
|   ], (base, account) => { | ||||
|     return base.set('account', account); | ||||
|   }); | ||||
| }; | ||||
| export const makeGetNotification = () => createSelector([ | ||||
|   (_, base)             => base, | ||||
|   (state, _, accountId) => state.getIn(['accounts', accountId]), | ||||
| ], (base, account) => base.set('account', account)); | ||||
| 
 | ||||
| export const makeGetReport = () => createSelector([ | ||||
|   (_, base) => base, | ||||
|   (state, _, targetAccountId) => state.getIn(['accounts', targetAccountId]), | ||||
| ], (base, targetAccount) => base.set('target_account', targetAccount)); | ||||
| 
 | ||||
| export const getAccountGallery = createSelector([ | ||||
|   (state, id) => state.getIn(['timelines', `account:${id}:media`, 'items'], ImmutableList()), | ||||
|  |  | |||
|  | @ -1355,6 +1355,8 @@ a .account__avatar { | |||
| .account__avatar-overlay { | ||||
|   @include avatar-size(48px); | ||||
| 
 | ||||
|   position: relative; | ||||
| 
 | ||||
|   &-base { | ||||
|     @include avatar-radius; | ||||
|     @include avatar-size(36px); | ||||
|  | @ -1620,6 +1622,33 @@ a.account__display-name { | |||
|   } | ||||
| } | ||||
| 
 | ||||
| .notification__report { | ||||
|   padding: 8px 10px; | ||||
|   padding-left: 68px; | ||||
|   position: relative; | ||||
|   border-bottom: 1px solid lighten($ui-base-color, 8%); | ||||
|   min-height: 54px; | ||||
| 
 | ||||
|   &__details { | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
|     align-items: center; | ||||
|     color: $darker-text-color; | ||||
|     font-size: 15px; | ||||
|     line-height: 22px; | ||||
| 
 | ||||
|     strong { | ||||
|       font-weight: 500; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   &__avatar { | ||||
|     position: absolute; | ||||
|     left: 10px; | ||||
|     top: 10px; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .notification__message { | ||||
|   margin: 0 10px 0 68px; | ||||
|   padding: 8px 0 0; | ||||
|  | @ -2360,6 +2389,16 @@ a.account__display-name { | |||
|       padding-top: 15px; | ||||
|     } | ||||
| 
 | ||||
|     .notification__report { | ||||
|       padding: 15px 15px 15px (48px + 15px * 2); | ||||
|       min-height: 48px + 2px; | ||||
| 
 | ||||
|       &__avatar { | ||||
|         left: 15px; | ||||
|         top: 17px; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     .status { | ||||
|       padding: 15px 15px 15px (48px + 15px * 2); | ||||
|       min-height: 48px + 2px; | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ class Notification < ApplicationRecord | |||
|     poll | ||||
|     update | ||||
|     admin.sign_up | ||||
|     admin.report | ||||
|   ).freeze | ||||
| 
 | ||||
|   TARGET_STATUS_INCLUDES_BY_TYPE = { | ||||
|  | @ -46,6 +47,7 @@ class Notification < ApplicationRecord | |||
|     favourite: [favourite: :status], | ||||
|     poll: [poll: :status], | ||||
|     update: :status, | ||||
|     'admin.report': [report: :target_account], | ||||
|   }.freeze | ||||
| 
 | ||||
|   belongs_to :account, optional: true | ||||
|  | @ -58,6 +60,7 @@ class Notification < ApplicationRecord | |||
|   belongs_to :follow_request, foreign_key: 'activity_id', optional: true | ||||
|   belongs_to :favourite,      foreign_key: 'activity_id', optional: true | ||||
|   belongs_to :poll,           foreign_key: 'activity_id', optional: true | ||||
|   belongs_to :report,         foreign_key: 'activity_id', optional: true | ||||
| 
 | ||||
|   validates :type, inclusion: { in: TYPES } | ||||
| 
 | ||||
|  | @ -146,7 +149,7 @@ class Notification < ApplicationRecord | |||
|     return unless new_record? | ||||
| 
 | ||||
|     case activity_type | ||||
|     when 'Status', 'Follow', 'Favourite', 'FollowRequest', 'Poll' | ||||
|     when 'Status', 'Follow', 'Favourite', 'FollowRequest', 'Poll', 'Report' | ||||
|       self.from_account_id = activity&.account_id | ||||
|     when 'Mention' | ||||
|       self.from_account_id = activity&.status&.account_id | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| class REST::Admin::ReportSerializer < ActiveModel::Serializer | ||||
|   attributes :id, :action_taken, :action_taken_at, :category, :comment, | ||||
|              :created_at, :updated_at | ||||
|              :forwarded, :created_at, :updated_at | ||||
| 
 | ||||
|   has_one :account, serializer: REST::Admin::AccountSerializer | ||||
|   has_one :target_account, serializer: REST::Admin::AccountSerializer | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ class REST::NotificationSerializer < ActiveModel::Serializer | |||
| 
 | ||||
|   belongs_to :from_account, key: :account, serializer: REST::AccountSerializer | ||||
|   belongs_to :target_status, key: :status, if: :status_type?, serializer: REST::StatusSerializer | ||||
|   belongs_to :report, if: :report_type?, serializer: REST::ReportSerializer | ||||
| 
 | ||||
|   def id | ||||
|     object.id.to_s | ||||
|  | @ -13,4 +14,8 @@ class REST::NotificationSerializer < ActiveModel::Serializer | |||
|   def status_type? | ||||
|     [:favourite, :reblog, :status, :mention, :poll, :update].include?(object.type) | ||||
|   end | ||||
| 
 | ||||
|   def report_type? | ||||
|     object.type == :'admin.report' | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -1,7 +1,10 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class REST::ReportSerializer < ActiveModel::Serializer | ||||
|   attributes :id, :action_taken | ||||
|   attributes :id, :action_taken, :action_taken_at, :category, :comment, | ||||
|              :forwarded, :created_at, :status_ids, :rule_ids | ||||
| 
 | ||||
|   has_one :target_account, serializer: REST::AccountSerializer | ||||
| 
 | ||||
|   def id | ||||
|     object.id.to_s | ||||
|  |  | |||
|  | @ -39,8 +39,8 @@ class ReportService < BaseService | |||
|     return if @report.unresolved_siblings? | ||||
| 
 | ||||
|     User.staff.includes(:account).each do |u| | ||||
|       next unless u.allows_report_emails? | ||||
|       AdminMailer.new_report(u.account, @report).deliver_later | ||||
|       LocalNotificationWorker.perform_async(u.account_id, @report.id, 'Report', 'admin.report') | ||||
|       AdminMailer.new_report(u.account, @report).deliver_later if u.allows_report_emails? | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -1251,6 +1251,8 @@ en: | |||
|     copy_account_note_text: 'This user moved from %{acct}, here were your previous notes about them:' | ||||
|   notification_mailer: | ||||
|     admin: | ||||
|       report: | ||||
|         subject: "%{name} submitted a report" | ||||
|       sign_up: | ||||
|         subject: "%{name} signed up" | ||||
|     digest: | ||||
|  |  | |||
		Reference in a new issue