mirror of
https://github.com/mastodon/mastodon.git
synced 2024-12-22 00:58:18 +00:00
Convert notifications policies frontend code to Typescript (#29868)
This commit is contained in:
parent
50db95b9a1
commit
d558dfd77d
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_SUPPORT = 'NOTIFICATIONS_SET_BROWSER_SUPPORT';
|
||||||
export const NOTIFICATIONS_SET_BROWSER_PERMISSION = 'NOTIFICATIONS_SET_BROWSER_PERMISSION';
|
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_REQUEST = 'NOTIFICATION_REQUESTS_FETCH_REQUEST';
|
||||||
export const NOTIFICATION_REQUESTS_FETCH_SUCCESS = 'NOTIFICATION_REQUESTS_FETCH_SUCCESS';
|
export const NOTIFICATION_REQUESTS_FETCH_SUCCESS = 'NOTIFICATION_REQUESTS_FETCH_SUCCESS';
|
||||||
export const NOTIFICATION_REQUESTS_FETCH_FAIL = 'NOTIFICATION_REQUESTS_FETCH_FAIL';
|
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) => {
|
export const fetchNotificationRequests = () => (dispatch, getState) => {
|
||||||
const params = {};
|
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,
|
alertsEnabled: PropTypes.bool,
|
||||||
browserSupport: PropTypes.bool,
|
browserSupport: PropTypes.bool,
|
||||||
browserPermission: PropTypes.string,
|
browserPermission: PropTypes.string,
|
||||||
notificationPolicy: ImmutablePropTypes.map,
|
notificationPolicy: PropTypes.object.isRequired,
|
||||||
onChangePolicy: PropTypes.func.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>
|
<h3><FormattedMessage id='notifications.policy.title' defaultMessage='Filter out notifications from…' /></h3>
|
||||||
|
|
||||||
<div className='column-settings__row'>
|
<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>
|
<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>
|
<span className='hint'><FormattedMessage id='notifications.policy.filter_not_following_hint' defaultMessage='Until you manually approve them' /></span>
|
||||||
</CheckboxWithLabel>
|
</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>
|
<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>
|
<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>
|
||||||
|
|
||||||
<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>
|
<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>
|
<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>
|
||||||
|
|
||||||
<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>
|
<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>
|
<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>
|
</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 { showAlert } from '../../../actions/alerts';
|
||||||
import { openModal } from '../../../actions/modal';
|
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 { changeAlerts as changePushNotifications } from '../../../actions/push_notifications';
|
||||||
import { changeSetting } from '../../../actions/settings';
|
import { changeSetting } from '../../../actions/settings';
|
||||||
import ColumnSettings from '../components/column_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' },
|
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 => ({
|
const mapStateToProps = state => ({
|
||||||
settings: state.getIn(['settings', 'notifications']),
|
settings: state.getIn(['settings', 'notifications']),
|
||||||
pushSettings: state.get('push_notifications'),
|
pushSettings: state.get('push_notifications'),
|
||||||
alertsEnabled: state.getIn(['settings', 'notifications', 'alerts']).includes(true),
|
alertsEnabled: state.getIn(['settings', 'notifications', 'alerts']).includes(true),
|
||||||
browserSupport: state.getIn(['notifications', 'browserSupport']),
|
browserSupport: state.getIn(['notifications', 'browserSupport']),
|
||||||
browserPermission: state.getIn(['notifications', 'browserPermission']),
|
browserPermission: state.getIn(['notifications', 'browserPermission']),
|
||||||
notificationPolicy: state.get('notificationPolicy'),
|
notificationPolicy: state.notificationPolicy,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch, { intl }) => ({
|
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,
|
||||||
|
);
|
||||||
|
});
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class REST::NotificationPolicySerializer < ActiveModel::Serializer
|
class REST::NotificationPolicySerializer < ActiveModel::Serializer
|
||||||
|
# Please update `app/javascript/mastodon/api_types/notification_policies.ts` when making changes to the attributes
|
||||||
|
|
||||||
attributes :filter_not_following,
|
attributes :filter_not_following,
|
||||||
:filter_not_followers,
|
:filter_not_followers,
|
||||||
:filter_new_accounts,
|
:filter_new_accounts,
|
||||||
|
|
Loading…
Reference in a new issue