Convert notifications policies frontend code to Typescript (#29868)
This commit is contained in:
		
					parent
					
						
							
								50db95b9a1
							
						
					
				
			
			
				commit
				
					
						d558dfd77d
					
				
			
		
					 12 changed files with 140 additions and 106 deletions
				
			
		
							
								
								
									
										16
									
								
								app/javascript/mastodon/actions/notification_policies.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/javascript/mastodon/actions/notification_policies.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| import { | ||||
|   apiGetNotificationPolicy, | ||||
|   apiUpdateNotificationsPolicy, | ||||
| } from 'mastodon/api/notification_policies'; | ||||
| import type { NotificationPolicy } from 'mastodon/models/notification_policy'; | ||||
| import { createDataLoadingThunk } from 'mastodon/store/typed_functions'; | ||||
| 
 | ||||
| export const fetchNotificationPolicy = createDataLoadingThunk( | ||||
|   'notificationPolicy/fetch', | ||||
|   () => apiGetNotificationPolicy(), | ||||
| ); | ||||
| 
 | ||||
| export const updateNotificationsPolicy = createDataLoadingThunk( | ||||
|   'notificationPolicy/update', | ||||
|   (policy: Partial<NotificationPolicy>) => apiUpdateNotificationsPolicy(policy), | ||||
| ); | ||||
|  | @ -44,10 +44,6 @@ export const NOTIFICATIONS_MARK_AS_READ = 'NOTIFICATIONS_MARK_AS_READ'; | |||
| export const NOTIFICATIONS_SET_BROWSER_SUPPORT    = 'NOTIFICATIONS_SET_BROWSER_SUPPORT'; | ||||
| export const NOTIFICATIONS_SET_BROWSER_PERMISSION = 'NOTIFICATIONS_SET_BROWSER_PERMISSION'; | ||||
| 
 | ||||
| export const NOTIFICATION_POLICY_FETCH_REQUEST = 'NOTIFICATION_POLICY_FETCH_REQUEST'; | ||||
| export const NOTIFICATION_POLICY_FETCH_SUCCESS = 'NOTIFICATION_POLICY_FETCH_SUCCESS'; | ||||
| export const NOTIFICATION_POLICY_FETCH_FAIL    = 'NOTIFICATION_POLICY_FETCH_FAIL'; | ||||
| 
 | ||||
| export const NOTIFICATION_REQUESTS_FETCH_REQUEST = 'NOTIFICATION_REQUESTS_FETCH_REQUEST'; | ||||
| export const NOTIFICATION_REQUESTS_FETCH_SUCCESS = 'NOTIFICATION_REQUESTS_FETCH_SUCCESS'; | ||||
| export const NOTIFICATION_REQUESTS_FETCH_FAIL    = 'NOTIFICATION_REQUESTS_FETCH_FAIL'; | ||||
|  | @ -346,40 +342,6 @@ export function setBrowserPermission (value) { | |||
|   }; | ||||
| } | ||||
| 
 | ||||
| export const fetchNotificationPolicy = () => (dispatch) => { | ||||
|   dispatch(fetchNotificationPolicyRequest()); | ||||
| 
 | ||||
|   api().get('/api/v1/notifications/policy').then(({ data }) => { | ||||
|     dispatch(fetchNotificationPolicySuccess(data)); | ||||
|   }).catch(err => { | ||||
|     dispatch(fetchNotificationPolicyFail(err)); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const fetchNotificationPolicyRequest = () => ({ | ||||
|   type: NOTIFICATION_POLICY_FETCH_REQUEST, | ||||
| }); | ||||
| 
 | ||||
| export const fetchNotificationPolicySuccess = policy => ({ | ||||
|   type: NOTIFICATION_POLICY_FETCH_SUCCESS, | ||||
|   policy, | ||||
| }); | ||||
| 
 | ||||
| export const fetchNotificationPolicyFail = error => ({ | ||||
|   type: NOTIFICATION_POLICY_FETCH_FAIL, | ||||
|   error, | ||||
| }); | ||||
| 
 | ||||
| export const updateNotificationsPolicy = params => (dispatch) => { | ||||
|   dispatch(fetchNotificationPolicyRequest()); | ||||
| 
 | ||||
|   api().put('/api/v1/notifications/policy', params).then(({ data }) => { | ||||
|     dispatch(fetchNotificationPolicySuccess(data)); | ||||
|   }).catch(err => { | ||||
|     dispatch(fetchNotificationPolicyFail(err)); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| export const fetchNotificationRequests = () => (dispatch, getState) => { | ||||
|   const params = {}; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										10
									
								
								app/javascript/mastodon/api/notification_policies.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/javascript/mastodon/api/notification_policies.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| import { apiRequest } from 'mastodon/api'; | ||||
| import type { NotificationPolicyJSON } from 'mastodon/api_types/notification_policies'; | ||||
| 
 | ||||
| export const apiGetNotificationPolicy = () => | ||||
|   apiRequest<NotificationPolicyJSON>('GET', '/v1/notifications/policy'); | ||||
| 
 | ||||
| export const apiUpdateNotificationsPolicy = ( | ||||
|   policy: Partial<NotificationPolicyJSON>, | ||||
| ) => | ||||
|   apiRequest<NotificationPolicyJSON>('PUT', '/v1/notifications/policy', policy); | ||||
							
								
								
									
										12
									
								
								app/javascript/mastodon/api_types/notification_policies.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								app/javascript/mastodon/api_types/notification_policies.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| // See app/serializers/rest/notification_policy_serializer.rb
 | ||||
| 
 | ||||
| export interface NotificationPolicyJSON { | ||||
|   filter_not_following: boolean; | ||||
|   filter_not_followers: boolean; | ||||
|   filter_new_accounts: boolean; | ||||
|   filter_private_mentions: boolean; | ||||
|   summary: { | ||||
|     pending_requests_count: number; | ||||
|     pending_notifications_count: number; | ||||
|   }; | ||||
| } | ||||
|  | @ -24,7 +24,7 @@ class ColumnSettings extends PureComponent { | |||
|     alertsEnabled: PropTypes.bool, | ||||
|     browserSupport: PropTypes.bool, | ||||
|     browserPermission: PropTypes.string, | ||||
|     notificationPolicy: ImmutablePropTypes.map, | ||||
|     notificationPolicy: PropTypes.object.isRequired, | ||||
|     onChangePolicy: PropTypes.func.isRequired, | ||||
|   }; | ||||
| 
 | ||||
|  | @ -82,22 +82,22 @@ class ColumnSettings extends PureComponent { | |||
|           <h3><FormattedMessage id='notifications.policy.title' defaultMessage='Filter out notifications from…' /></h3> | ||||
| 
 | ||||
|           <div className='column-settings__row'> | ||||
|             <CheckboxWithLabel checked={notificationPolicy.get('filter_not_following')} onChange={this.handleFilterNotFollowing}> | ||||
|             <CheckboxWithLabel checked={notificationPolicy.filter_not_following} onChange={this.handleFilterNotFollowing}> | ||||
|               <strong><FormattedMessage id='notifications.policy.filter_not_following_title' defaultMessage="People you don't follow" /></strong> | ||||
|               <span className='hint'><FormattedMessage id='notifications.policy.filter_not_following_hint' defaultMessage='Until you manually approve them' /></span> | ||||
|             </CheckboxWithLabel> | ||||
| 
 | ||||
|             <CheckboxWithLabel checked={notificationPolicy.get('filter_not_followers')} onChange={this.handleFilterNotFollowers}> | ||||
|             <CheckboxWithLabel checked={notificationPolicy.filter_not_followers} onChange={this.handleFilterNotFollowers}> | ||||
|               <strong><FormattedMessage id='notifications.policy.filter_not_followers_title' defaultMessage='People not following you' /></strong> | ||||
|               <span className='hint'><FormattedMessage id='notifications.policy.filter_not_followers_hint' defaultMessage='Including people who have been following you fewer than {days, plural, one {one day} other {# days}}' values={{ days: 3 }} /></span> | ||||
|             </CheckboxWithLabel> | ||||
| 
 | ||||
|             <CheckboxWithLabel checked={notificationPolicy.get('filter_new_accounts')} onChange={this.handleFilterNewAccounts}> | ||||
|             <CheckboxWithLabel checked={notificationPolicy.filter_new_accounts} onChange={this.handleFilterNewAccounts}> | ||||
|               <strong><FormattedMessage id='notifications.policy.filter_new_accounts_title' defaultMessage='New accounts' /></strong> | ||||
|               <span className='hint'><FormattedMessage id='notifications.policy.filter_new_accounts.hint' defaultMessage='Created within the past {days, plural, one {one day} other {# days}}' values={{ days: 30 }} /></span> | ||||
|             </CheckboxWithLabel> | ||||
| 
 | ||||
|             <CheckboxWithLabel checked={notificationPolicy.get('filter_private_mentions')} onChange={this.handleFilterPrivateMentions}> | ||||
|             <CheckboxWithLabel checked={notificationPolicy.filter_private_mentions} onChange={this.handleFilterPrivateMentions}> | ||||
|               <strong><FormattedMessage id='notifications.policy.filter_private_mentions_title' defaultMessage='Unsolicited private mentions' /></strong> | ||||
|               <span className='hint'><FormattedMessage id='notifications.policy.filter_private_mentions_hint' defaultMessage="Filtered unless it's in reply to your own mention or if you follow the sender" /></span> | ||||
|             </CheckboxWithLabel> | ||||
|  |  | |||
|  | @ -1,49 +0,0 @@ | |||
| import { useEffect } from 'react'; | ||||
| 
 | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| import { Link } from 'react-router-dom'; | ||||
| 
 | ||||
| import { useDispatch, useSelector } from 'react-redux'; | ||||
| 
 | ||||
| import InventoryIcon from '@/material-icons/400-24px/inventory_2.svg?react'; | ||||
| import { fetchNotificationPolicy } from 'mastodon/actions/notifications'; | ||||
| import { Icon } from 'mastodon/components/icon'; | ||||
| import { toCappedNumber } from 'mastodon/utils/numbers'; | ||||
| 
 | ||||
| export const FilteredNotificationsBanner = () => { | ||||
|   const dispatch = useDispatch(); | ||||
|   const policy = useSelector(state => state.get('notificationPolicy')); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     dispatch(fetchNotificationPolicy()); | ||||
| 
 | ||||
|     const interval = setInterval(() => { | ||||
|       dispatch(fetchNotificationPolicy()); | ||||
|     }, 120000); | ||||
| 
 | ||||
|     return () => { | ||||
|       clearInterval(interval); | ||||
|     }; | ||||
|   }, [dispatch]); | ||||
| 
 | ||||
|   if (policy === null || policy.getIn(['summary', 'pending_notifications_count']) === 0) { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <Link className='filtered-notifications-banner' to='/notifications/requests'> | ||||
|       <Icon icon={InventoryIcon} /> | ||||
| 
 | ||||
|       <div className='filtered-notifications-banner__text'> | ||||
|         <strong><FormattedMessage id='filtered_notifications_banner.title' defaultMessage='Filtered notifications' /></strong> | ||||
|         <span><FormattedMessage id='filtered_notifications_banner.pending_requests' defaultMessage='Notifications from {count, plural, =0 {no one} one {one person} other {# people}} you may know' values={{ count: policy.getIn(['summary', 'pending_requests_count']) }} /></span> | ||||
|       </div> | ||||
| 
 | ||||
|       <div className='filtered-notifications-banner__badge'> | ||||
|         <div className='filtered-notifications-banner__badge__badge'>{toCappedNumber(policy.getIn(['summary', 'pending_notifications_count']))}</div> | ||||
|         <FormattedMessage id='filtered_notifications_banner.mentions' defaultMessage='{count, plural, one {mention} other {mentions}}' values={{ count: policy.getIn(['summary', 'pending_notifications_count']) }} /> | ||||
|       </div> | ||||
|     </Link> | ||||
|   ); | ||||
| }; | ||||
|  | @ -0,0 +1,68 @@ | |||
| import { useEffect } from 'react'; | ||||
| 
 | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| import { Link } from 'react-router-dom'; | ||||
| 
 | ||||
| import InventoryIcon from '@/material-icons/400-24px/inventory_2.svg?react'; | ||||
| import { fetchNotificationPolicy } from 'mastodon/actions/notification_policies'; | ||||
| import { Icon } from 'mastodon/components/icon'; | ||||
| import { useAppSelector, useAppDispatch } from 'mastodon/store'; | ||||
| import { toCappedNumber } from 'mastodon/utils/numbers'; | ||||
| 
 | ||||
| export const FilteredNotificationsBanner: React.FC = () => { | ||||
|   const dispatch = useAppDispatch(); | ||||
|   const policy = useAppSelector((state) => state.notificationPolicy); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     void dispatch(fetchNotificationPolicy()); | ||||
| 
 | ||||
|     const interval = setInterval(() => { | ||||
|       void dispatch(fetchNotificationPolicy()); | ||||
|     }, 120000); | ||||
| 
 | ||||
|     return () => { | ||||
|       clearInterval(interval); | ||||
|     }; | ||||
|   }, [dispatch]); | ||||
| 
 | ||||
|   if (policy === null || policy.summary.pending_notifications_count === 0) { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <Link | ||||
|       className='filtered-notifications-banner' | ||||
|       to='/notifications/requests' | ||||
|     > | ||||
|       <Icon icon={InventoryIcon} id='filtered-notifications' /> | ||||
| 
 | ||||
|       <div className='filtered-notifications-banner__text'> | ||||
|         <strong> | ||||
|           <FormattedMessage | ||||
|             id='filtered_notifications_banner.title' | ||||
|             defaultMessage='Filtered notifications' | ||||
|           /> | ||||
|         </strong> | ||||
|         <span> | ||||
|           <FormattedMessage | ||||
|             id='filtered_notifications_banner.pending_requests' | ||||
|             defaultMessage='Notifications from {count, plural, =0 {no one} one {one person} other {# people}} you may know' | ||||
|             values={{ count: policy.summary.pending_requests_count }} | ||||
|           /> | ||||
|         </span> | ||||
|       </div> | ||||
| 
 | ||||
|       <div className='filtered-notifications-banner__badge'> | ||||
|         <div className='filtered-notifications-banner__badge__badge'> | ||||
|           {toCappedNumber(policy.summary.pending_notifications_count)} | ||||
|         </div> | ||||
|         <FormattedMessage | ||||
|           id='filtered_notifications_banner.mentions' | ||||
|           defaultMessage='{count, plural, one {mention} other {mentions}}' | ||||
|           values={{ count: policy.summary.pending_notifications_count }} | ||||
|         /> | ||||
|       </div> | ||||
|     </Link> | ||||
|   ); | ||||
| }; | ||||
|  | @ -4,7 +4,8 @@ import { connect } from 'react-redux'; | |||
| 
 | ||||
| import { showAlert } from '../../../actions/alerts'; | ||||
| import { openModal } from '../../../actions/modal'; | ||||
| import { setFilter, clearNotifications, requestBrowserPermission, updateNotificationsPolicy } from '../../../actions/notifications'; | ||||
| import { updateNotificationsPolicy } from '../../../actions/notification_policies'; | ||||
| import { setFilter, clearNotifications, requestBrowserPermission } from '../../../actions/notifications'; | ||||
| import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications'; | ||||
| import { changeSetting } from '../../../actions/settings'; | ||||
| import ColumnSettings from '../components/column_settings'; | ||||
|  | @ -15,13 +16,16 @@ const messages = defineMessages({ | |||
|   permissionDenied: { id: 'notifications.permission_denied_alert', defaultMessage: 'Desktop notifications can\'t be enabled, as browser permission has been denied before' }, | ||||
| }); | ||||
| 
 | ||||
| /** | ||||
|  * @param {import('mastodon/store').RootState} state | ||||
|  */ | ||||
| const mapStateToProps = state => ({ | ||||
|   settings: state.getIn(['settings', 'notifications']), | ||||
|   pushSettings: state.get('push_notifications'), | ||||
|   alertsEnabled: state.getIn(['settings', 'notifications', 'alerts']).includes(true), | ||||
|   browserSupport: state.getIn(['notifications', 'browserSupport']), | ||||
|   browserPermission: state.getIn(['notifications', 'browserPermission']), | ||||
|   notificationPolicy: state.get('notificationPolicy'), | ||||
|   notificationPolicy: state.notificationPolicy, | ||||
| }); | ||||
| 
 | ||||
| const mapDispatchToProps = (dispatch, { intl }) => ({ | ||||
|  |  | |||
							
								
								
									
										3
									
								
								app/javascript/mastodon/models/notification_policy.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/javascript/mastodon/models/notification_policy.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| import type { NotificationPolicyJSON } from 'mastodon/api_types/notification_policies'; | ||||
| 
 | ||||
| export type NotificationPolicy = NotificationPolicyJSON; // No changes from the API type
 | ||||
|  | @ -1,12 +0,0 @@ | |||
| import { fromJS } from 'immutable'; | ||||
| 
 | ||||
| import { NOTIFICATION_POLICY_FETCH_SUCCESS } from 'mastodon/actions/notifications'; | ||||
| 
 | ||||
| export const notificationPolicyReducer = (state = null, action) => { | ||||
|   switch(action.type) { | ||||
|   case NOTIFICATION_POLICY_FETCH_SUCCESS: | ||||
|     return fromJS(action.policy); | ||||
|   default: | ||||
|     return state; | ||||
|   } | ||||
| }; | ||||
							
								
								
									
										18
									
								
								app/javascript/mastodon/reducers/notification_policy.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								app/javascript/mastodon/reducers/notification_policy.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| import { createReducer, isAnyOf } from '@reduxjs/toolkit'; | ||||
| 
 | ||||
| import { | ||||
|   fetchNotificationPolicy, | ||||
|   updateNotificationsPolicy, | ||||
| } from 'mastodon/actions/notification_policies'; | ||||
| import type { NotificationPolicy } from 'mastodon/models/notification_policy'; | ||||
| 
 | ||||
| export const notificationPolicyReducer = | ||||
|   createReducer<NotificationPolicy | null>(null, (builder) => { | ||||
|     builder.addMatcher( | ||||
|       isAnyOf( | ||||
|         fetchNotificationPolicy.fulfilled, | ||||
|         updateNotificationsPolicy.fulfilled, | ||||
|       ), | ||||
|       (_state, action) => action.payload, | ||||
|     ); | ||||
|   }); | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue