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) {
|
function notificationTypeForQuickFilter(type: NotificationType) {
|
||||||
if (type === 'quoted_update') return 'update';
|
switch (type) {
|
||||||
else if (type === 'quote') return 'mention';
|
case 'quoted_update':
|
||||||
else return type;
|
return 'update';
|
||||||
|
case 'quote':
|
||||||
|
return 'mention';
|
||||||
|
case 'collection_update':
|
||||||
|
return 'collection';
|
||||||
|
case 'added_to_collection':
|
||||||
|
return 'collection';
|
||||||
|
default:
|
||||||
|
return type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function excludeAllTypesExcept(filter: string) {
|
function excludeAllTypesExcept(filter: string) {
|
||||||
|
|||||||
@ -86,6 +86,9 @@ export function setupBrowserNotifications() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {(NotificationPermission) => void} callback
|
||||||
|
*/
|
||||||
export function requestBrowserPermission(callback = noOp) {
|
export function requestBrowserPermission(callback = noOp) {
|
||||||
return dispatch => {
|
return dispatch => {
|
||||||
requestNotificationPermission((permission) => {
|
requestNotificationPermission((permission) => {
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import ClearColumnButton from './clear_column_button';
|
|||||||
import GrantPermissionButton from './grant_permission_button';
|
import GrantPermissionButton from './grant_permission_button';
|
||||||
import { PolicyControls } from './policy_controls';
|
import { PolicyControls } from './policy_controls';
|
||||||
import SettingToggle from './setting_toggle';
|
import SettingToggle from './setting_toggle';
|
||||||
|
import { ColumnSettingsGroup } from './column_settings_group';
|
||||||
|
|
||||||
class ColumnSettings extends PureComponent {
|
class ColumnSettings extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -187,6 +188,11 @@ class ColumnSettings extends PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<ColumnSettingsGroup
|
||||||
|
type="collections"
|
||||||
|
label={<FormattedMessage id='notifications.column_settings.collections' defaultMessage='Collections:' />}
|
||||||
|
/>
|
||||||
|
|
||||||
{((this.props.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && (
|
{((this.props.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && (
|
||||||
<section role='group' aria-labelledby='notifications-admin-sign-up'>
|
<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>
|
<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 { 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 HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
||||||
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
|
import InsertChartIcon from '@/material-icons/400-24px/insert_chart.svg?react';
|
||||||
import PersonAddIcon from '@/material-icons/400-24px/person_add.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' },
|
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' },
|
||||||
polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' },
|
polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' },
|
||||||
follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
|
follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
|
||||||
|
collections: {
|
||||||
|
id: 'notifications.filter.collections',
|
||||||
|
defaultMessage: 'Collections',
|
||||||
|
},
|
||||||
statuses: {
|
statuses: {
|
||||||
id: 'notifications.filter.statuses',
|
id: 'notifications.filter.statuses',
|
||||||
defaultMessage: 'Updates from people you follow',
|
defaultMessage: 'Updates from people you follow',
|
||||||
@ -124,6 +129,14 @@ export const FilterBar: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<Icon id='user-plus' icon={PersonAddIcon} />
|
<Icon id='user-plus' icon={PersonAddIcon} />
|
||||||
</BarButton>
|
</BarButton>
|
||||||
|
<BarButton
|
||||||
|
selectedFilter={selectedFilter}
|
||||||
|
type='collection'
|
||||||
|
key='collection'
|
||||||
|
title={intl.formatMessage(tooltips.collections)}
|
||||||
|
>
|
||||||
|
<Icon id='collections' icon={CollectionsIcon} />
|
||||||
|
</BarButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
|
|||||||
@ -1000,6 +1000,7 @@
|
|||||||
"notifications.column_settings.admin.report": "New reports:",
|
"notifications.column_settings.admin.report": "New reports:",
|
||||||
"notifications.column_settings.admin.sign_up": "New sign-ups:",
|
"notifications.column_settings.admin.sign_up": "New sign-ups:",
|
||||||
"notifications.column_settings.alert": "Desktop notifications",
|
"notifications.column_settings.alert": "Desktop notifications",
|
||||||
|
"notifications.column_settings.collections": "Collections:",
|
||||||
"notifications.column_settings.favourite": "Favorites:",
|
"notifications.column_settings.favourite": "Favorites:",
|
||||||
"notifications.column_settings.filter_bar.advanced": "Display all categories",
|
"notifications.column_settings.filter_bar.advanced": "Display all categories",
|
||||||
"notifications.column_settings.filter_bar.category": "Quick filter bar",
|
"notifications.column_settings.filter_bar.category": "Quick filter bar",
|
||||||
@ -1019,6 +1020,7 @@
|
|||||||
"notifications.column_settings.update": "Edits:",
|
"notifications.column_settings.update": "Edits:",
|
||||||
"notifications.filter.all": "All",
|
"notifications.filter.all": "All",
|
||||||
"notifications.filter.boosts": "Boosts",
|
"notifications.filter.boosts": "Boosts",
|
||||||
|
"notifications.filter.collections": "Collections",
|
||||||
"notifications.filter.favourites": "Favorites",
|
"notifications.filter.favourites": "Favorites",
|
||||||
"notifications.filter.follows": "Follows",
|
"notifications.filter.follows": "Follows",
|
||||||
"notifications.filter.mentions": "Mentions",
|
"notifications.filter.mentions": "Mentions",
|
||||||
|
|||||||
@ -41,6 +41,7 @@ const initialState = ImmutableMap({
|
|||||||
poll: false,
|
poll: false,
|
||||||
status: false,
|
status: false,
|
||||||
update: false,
|
update: false,
|
||||||
|
collections: false,
|
||||||
'admin.sign_up': false,
|
'admin.sign_up': false,
|
||||||
'admin.report': false,
|
'admin.report': false,
|
||||||
}),
|
}),
|
||||||
@ -65,6 +66,7 @@ const initialState = ImmutableMap({
|
|||||||
poll: true,
|
poll: true,
|
||||||
status: true,
|
status: true,
|
||||||
update: true,
|
update: true,
|
||||||
|
collections: true,
|
||||||
'admin.sign_up': true,
|
'admin.sign_up': true,
|
||||||
'admin.report': true,
|
'admin.report': true,
|
||||||
}),
|
}),
|
||||||
@ -79,6 +81,7 @@ const initialState = ImmutableMap({
|
|||||||
poll: true,
|
poll: true,
|
||||||
status: true,
|
status: true,
|
||||||
update: true,
|
update: true,
|
||||||
|
collections: true,
|
||||||
'admin.sign_up': true,
|
'admin.sign_up': true,
|
||||||
'admin.report': true,
|
'admin.report': true,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -29,7 +29,10 @@ const filterNotificationsByAllowedTypes = (
|
|||||||
(item) =>
|
(item) =>
|
||||||
item.type === 'gap' ||
|
item.type === 'gap' ||
|
||||||
allowedType === item.type ||
|
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(
|
export const selectSettingsNotificationsExcludedTypes = createSelector(
|
||||||
[selectSettingsNotificationsShows],
|
[selectSettingsNotificationsShows],
|
||||||
(shows) =>
|
(shows) => {
|
||||||
Object.entries(shows)
|
const excludedTypes: string[] = [];
|
||||||
.filter(([_type, enabled]) => !enabled)
|
for (const key in shows) {
|
||||||
.map(([type, _enabled]) => type),
|
if (!shows[key]) {
|
||||||
|
if (key === 'collections') {
|
||||||
|
excludedTypes.push('collection_update', 'added_to_collection');
|
||||||
|
} else {
|
||||||
|
excludedTypes.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return excludedTypes;
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectSettingsNotificationsQuickFilterShow = (state: RootState) =>
|
export const selectSettingsNotificationsQuickFilterShow = (state: RootState) =>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user