Collection notification filtering (#39198)
This commit is contained in:
parent
faa5944d46
commit
4101f567c5
@ -36,9 +36,18 @@ function notificationTypeForFilter(type: NotificationType) {
|
||||
}
|
||||
|
||||
function notificationTypeForQuickFilter(type: NotificationType) {
|
||||
if (type === 'quoted_update') return 'update';
|
||||
else if (type === 'quote') return 'mention';
|
||||
else return type;
|
||||
switch (type) {
|
||||
case 'quoted_update':
|
||||
return 'update';
|
||||
case 'quote':
|
||||
return 'mention';
|
||||
case 'collection_update':
|
||||
return 'collection';
|
||||
case 'added_to_collection':
|
||||
return 'collection';
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
function excludeAllTypesExcept(filter: string) {
|
||||
|
||||
@ -86,6 +86,9 @@ export function setupBrowserNotifications() {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(NotificationPermission) => void} callback
|
||||
*/
|
||||
export function requestBrowserPermission(callback = noOp) {
|
||||
return dispatch => {
|
||||
requestNotificationPermission((permission) => {
|
||||
|
||||
@ -12,6 +12,7 @@ import ClearColumnButton from './clear_column_button';
|
||||
import GrantPermissionButton from './grant_permission_button';
|
||||
import { PolicyControls } from './policy_controls';
|
||||
import SettingToggle from './setting_toggle';
|
||||
import { ColumnSettingsGroup } from './column_settings_group';
|
||||
|
||||
class ColumnSettings extends PureComponent {
|
||||
static propTypes = {
|
||||
@ -187,6 +188,11 @@ class ColumnSettings extends PureComponent {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<ColumnSettingsGroup
|
||||
type="collections"
|
||||
label={<FormattedMessage id='notifications.column_settings.collections' defaultMessage='Collections:' />}
|
||||
/>
|
||||
|
||||
{((this.props.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && (
|
||||
<section role='group' aria-labelledby='notifications-admin-sign-up'>
|
||||
<h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.sign_up' defaultMessage='New sign-ups:' /></h3>
|
||||
|
||||
@ -0,0 +1,162 @@
|
||||
import { useCallback } from 'react';
|
||||
import type { FC, ReactNode } from 'react';
|
||||
|
||||
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||
|
||||
import { showAlert } from '@/mastodon/actions/alerts';
|
||||
import { requestBrowserPermission } from '@/mastodon/actions/notifications';
|
||||
import { changeAlerts } from '@/mastodon/actions/push_notifications';
|
||||
import { changeSetting } from '@/mastodon/actions/settings';
|
||||
import {
|
||||
createAppSelector,
|
||||
useAppDispatch,
|
||||
useAppSelector,
|
||||
} from '@/mastodon/store';
|
||||
|
||||
import SettingToggle from './setting_toggle';
|
||||
|
||||
const selectNotificationSettings = createAppSelector(
|
||||
[
|
||||
(state) =>
|
||||
(state.settings as Immutable.Map<string, unknown>).get(
|
||||
'notifications',
|
||||
) as Immutable.Map<string, Immutable.Map<string, unknown> | boolean>,
|
||||
(state) => state.notifications.get('browserPermission') as string,
|
||||
(state) => state.push_notifications,
|
||||
],
|
||||
(settings, browserPermission, pushSettings) => ({
|
||||
settings,
|
||||
browserPermission: browserPermission !== 'denied',
|
||||
pushSettings,
|
||||
showPushSettings:
|
||||
pushSettings.get('supported') && pushSettings.get('enabled'),
|
||||
}),
|
||||
);
|
||||
|
||||
const messages = defineMessages({
|
||||
permissionDenied: {
|
||||
id: 'notifications.permission_denied_alert',
|
||||
defaultMessage:
|
||||
"Desktop notifications can't be enabled, as browser permission has been denied before",
|
||||
},
|
||||
});
|
||||
|
||||
type SettingsType = 'alerts' | 'shows' | 'sounds';
|
||||
|
||||
export const ColumnSettingsGroup: FC<{ label: ReactNode; type: string }> = ({
|
||||
label,
|
||||
type,
|
||||
}) => {
|
||||
const { settings, browserPermission, pushSettings, showPushSettings } =
|
||||
useAppSelector(selectNotificationSettings);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const handleChange = useCallback(
|
||||
(path: [SettingsType, string], checked: boolean) => {
|
||||
if (
|
||||
path[0] === 'alerts' &&
|
||||
checked &&
|
||||
typeof window.Notification !== 'undefined' &&
|
||||
Notification.permission !== 'granted'
|
||||
) {
|
||||
dispatch(
|
||||
requestBrowserPermission((permission) => {
|
||||
if (permission === 'granted') {
|
||||
dispatch(changeSetting(['notifications', ...path], checked));
|
||||
} else {
|
||||
dispatch(showAlert({ message: messages.permissionDenied }));
|
||||
}
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
dispatch(changeSetting(['notifications', ...path], checked));
|
||||
}
|
||||
},
|
||||
[dispatch],
|
||||
);
|
||||
const handlePushChange = useCallback(
|
||||
(path: string[], checked: boolean) => {
|
||||
if (
|
||||
checked &&
|
||||
typeof window.Notification !== 'undefined' &&
|
||||
Notification.permission !== 'granted'
|
||||
) {
|
||||
dispatch(
|
||||
requestBrowserPermission((permission: NotificationPermission) => {
|
||||
if (permission === 'granted') {
|
||||
dispatch(changeAlerts(path, checked));
|
||||
} else {
|
||||
dispatch(showAlert({ message: messages.permissionDenied }));
|
||||
}
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
dispatch(changeAlerts(path, checked));
|
||||
}
|
||||
},
|
||||
[dispatch],
|
||||
);
|
||||
|
||||
return (
|
||||
<section role='group' aria-labelledby={`notifications-${type}`}>
|
||||
<h3 id={`notifications-${type}`}>{label}</h3>
|
||||
|
||||
<div className='column-settings__row'>
|
||||
<SettingToggle
|
||||
disabled={!browserPermission}
|
||||
prefix='notifications_desktop'
|
||||
settings={settings}
|
||||
settingPath={['alerts', type]}
|
||||
onChange={handleChange}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='notifications.column_settings.alert'
|
||||
defaultMessage='Desktop notifications'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
{showPushSettings && (
|
||||
<SettingToggle
|
||||
prefix='notifications_push'
|
||||
settings={pushSettings}
|
||||
settingPath={['alerts', type]}
|
||||
onChange={handlePushChange}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='notifications.column_settings.push'
|
||||
defaultMessage='Push notifications'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SettingToggle
|
||||
prefix='notifications'
|
||||
settings={settings}
|
||||
settingPath={['shows', type]}
|
||||
onChange={handleChange}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='notifications.column_settings.show'
|
||||
defaultMessage='Show in column'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<SettingToggle
|
||||
prefix='notifications'
|
||||
settings={settings}
|
||||
settingPath={['sounds', type]}
|
||||
onChange={handleChange}
|
||||
label={
|
||||
<FormattedMessage
|
||||
id='notifications.column_settings.sound'
|
||||
defaultMessage='Play sound'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@ -3,6 +3,7 @@ import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import CollectionsIcon from '@/material-icons/400-24px/category.svg?react';
|
||||
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
||||
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
|
||||
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
|
||||
@ -26,6 +27,10 @@ const tooltips = defineMessages({
|
||||
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' },
|
||||
polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' },
|
||||
follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
|
||||
collections: {
|
||||
id: 'notifications.filter.collections',
|
||||
defaultMessage: 'Collections',
|
||||
},
|
||||
statuses: {
|
||||
id: 'notifications.filter.statuses',
|
||||
defaultMessage: 'Updates from people you follow',
|
||||
@ -124,6 +129,14 @@ export const FilterBar: React.FC = () => {
|
||||
>
|
||||
<Icon id='user-plus' icon={PersonAddIcon} />
|
||||
</BarButton>
|
||||
<BarButton
|
||||
selectedFilter={selectedFilter}
|
||||
type='collection'
|
||||
key='collection'
|
||||
title={intl.formatMessage(tooltips.collections)}
|
||||
>
|
||||
<Icon id='collections' icon={CollectionsIcon} />
|
||||
</BarButton>
|
||||
</div>
|
||||
);
|
||||
else
|
||||
|
||||
@ -1000,6 +1000,7 @@
|
||||
"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.collections": "Collections:",
|
||||
"notifications.column_settings.favourite": "Favorites:",
|
||||
"notifications.column_settings.filter_bar.advanced": "Display all categories",
|
||||
"notifications.column_settings.filter_bar.category": "Quick filter bar",
|
||||
@ -1019,6 +1020,7 @@
|
||||
"notifications.column_settings.update": "Edits:",
|
||||
"notifications.filter.all": "All",
|
||||
"notifications.filter.boosts": "Boosts",
|
||||
"notifications.filter.collections": "Collections",
|
||||
"notifications.filter.favourites": "Favorites",
|
||||
"notifications.filter.follows": "Follows",
|
||||
"notifications.filter.mentions": "Mentions",
|
||||
|
||||
@ -41,6 +41,7 @@ const initialState = ImmutableMap({
|
||||
poll: false,
|
||||
status: false,
|
||||
update: false,
|
||||
collections: false,
|
||||
'admin.sign_up': false,
|
||||
'admin.report': false,
|
||||
}),
|
||||
@ -65,6 +66,7 @@ const initialState = ImmutableMap({
|
||||
poll: true,
|
||||
status: true,
|
||||
update: true,
|
||||
collections: true,
|
||||
'admin.sign_up': true,
|
||||
'admin.report': true,
|
||||
}),
|
||||
@ -79,6 +81,7 @@ const initialState = ImmutableMap({
|
||||
poll: true,
|
||||
status: true,
|
||||
update: true,
|
||||
collections: true,
|
||||
'admin.sign_up': true,
|
||||
'admin.report': true,
|
||||
}),
|
||||
|
||||
@ -29,7 +29,10 @@ const filterNotificationsByAllowedTypes = (
|
||||
(item) =>
|
||||
item.type === 'gap' ||
|
||||
allowedType === item.type ||
|
||||
(allowedType === 'mention' && item.type === 'quote'),
|
||||
(allowedType === 'mention' && item.type === 'quote') ||
|
||||
(allowedType === 'collection' &&
|
||||
(item.type === 'collection_update' ||
|
||||
item.type === 'added_to_collection')),
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -17,10 +17,19 @@ export const selectSettingsNotificationsShows = createSelector(
|
||||
|
||||
export const selectSettingsNotificationsExcludedTypes = createSelector(
|
||||
[selectSettingsNotificationsShows],
|
||||
(shows) =>
|
||||
Object.entries(shows)
|
||||
.filter(([_type, enabled]) => !enabled)
|
||||
.map(([type, _enabled]) => type),
|
||||
(shows) => {
|
||||
const excludedTypes: string[] = [];
|
||||
for (const key in shows) {
|
||||
if (!shows[key]) {
|
||||
if (key === 'collections') {
|
||||
excludedTypes.push('collection_update', 'added_to_collection');
|
||||
} else {
|
||||
excludedTypes.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return excludedTypes;
|
||||
},
|
||||
);
|
||||
|
||||
export const selectSettingsNotificationsQuickFilterShow = (state: RootState) =>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user