Add ability for admins to force grouped notifications in web UI (#31610)
This commit is contained in:
		@@ -2,6 +2,7 @@ import { debounce } from 'lodash';
 | 
			
		||||
 | 
			
		||||
import type { MarkerJSON } from 'mastodon/api_types/markers';
 | 
			
		||||
import { getAccessToken } from 'mastodon/initial_state';
 | 
			
		||||
import { selectUseGroupedNotifications } from 'mastodon/selectors/settings';
 | 
			
		||||
import type { AppDispatch, RootState } from 'mastodon/store';
 | 
			
		||||
import { createAppAsyncThunk } from 'mastodon/store/typed_functions';
 | 
			
		||||
 | 
			
		||||
@@ -75,13 +76,8 @@ interface MarkerParam {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getLastNotificationId(state: RootState): string | undefined {
 | 
			
		||||
  // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
 | 
			
		||||
  const enableBeta = state.settings.getIn(
 | 
			
		||||
    ['notifications', 'groupingBeta'],
 | 
			
		||||
    false,
 | 
			
		||||
  ) as boolean;
 | 
			
		||||
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
 | 
			
		||||
  return enableBeta
 | 
			
		||||
  return selectUseGroupedNotifications(state)
 | 
			
		||||
    ? state.notificationGroups.lastReadId
 | 
			
		||||
    : // @ts-expect-error state.notifications is not yet typed
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/no-unsafe-call
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import { selectUseGroupedNotifications } from 'mastodon/selectors/settings';
 | 
			
		||||
import { createAppAsyncThunk } from 'mastodon/store';
 | 
			
		||||
 | 
			
		||||
import { fetchNotifications } from './notification_groups';
 | 
			
		||||
@@ -6,13 +7,8 @@ import { expandNotifications } from './notifications';
 | 
			
		||||
export const initializeNotifications = createAppAsyncThunk(
 | 
			
		||||
  'notifications/initialize',
 | 
			
		||||
  (_, { dispatch, getState }) => {
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
 | 
			
		||||
    const enableBeta = getState().settings.getIn(
 | 
			
		||||
      ['notifications', 'groupingBeta'],
 | 
			
		||||
      false,
 | 
			
		||||
    ) as boolean;
 | 
			
		||||
 | 
			
		||||
    if (enableBeta) void dispatch(fetchNotifications());
 | 
			
		||||
    if (selectUseGroupedNotifications(getState()))
 | 
			
		||||
      void dispatch(fetchNotifications());
 | 
			
		||||
    else void dispatch(expandNotifications({}));
 | 
			
		||||
  },
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
// @ts-check
 | 
			
		||||
 | 
			
		||||
import { selectUseGroupedNotifications } from 'mastodon/selectors/settings';
 | 
			
		||||
 | 
			
		||||
import { getLocale } from '../locales';
 | 
			
		||||
import { connectStream } from '../stream';
 | 
			
		||||
 | 
			
		||||
@@ -103,7 +105,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
 | 
			
		||||
          const notificationJSON = JSON.parse(data.payload);
 | 
			
		||||
          dispatch(updateNotifications(notificationJSON, messages, locale));
 | 
			
		||||
          // TODO: remove this once the groups feature replaces the previous one
 | 
			
		||||
          if(getState().settings.getIn(['notifications', 'groupingBeta'], false)) {
 | 
			
		||||
          if(selectUseGroupedNotifications(getState())) {
 | 
			
		||||
            dispatch(processNewNotificationForGroups(notificationJSON));
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
@@ -112,7 +114,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
 | 
			
		||||
          const state = getState();
 | 
			
		||||
          if (state.notifications.top || !state.notifications.mounted)
 | 
			
		||||
            dispatch(expandNotifications({ forceLoad: true, maxId: undefined }));
 | 
			
		||||
          if(state.settings.getIn(['notifications', 'groupingBeta'], false)) {
 | 
			
		||||
          if (selectUseGroupedNotifications(state)) {
 | 
			
		||||
            dispatch(refreshStaleNotificationGroups());
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
@@ -145,7 +147,7 @@ async function refreshHomeTimelineAndNotification(dispatch, getState) {
 | 
			
		||||
  await dispatch(expandHomeTimeline({ maxId: undefined }));
 | 
			
		||||
 | 
			
		||||
  // TODO: remove this once the groups feature replaces the previous one
 | 
			
		||||
  if(getState().settings.getIn(['notifications', 'groupingBeta'], false)) {
 | 
			
		||||
  if(selectUseGroupedNotifications(getState())) {
 | 
			
		||||
    // TODO: polling for merged notifications
 | 
			
		||||
    try {
 | 
			
		||||
      await dispatch(pollRecentGroupNotifications());
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import { FormattedMessage } from 'react-intl';
 | 
			
		||||
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
			
		||||
 | 
			
		||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
 | 
			
		||||
import { forceGroupedNotifications } from 'mastodon/initial_state';
 | 
			
		||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'mastodon/permissions';
 | 
			
		||||
 | 
			
		||||
import ClearColumnButton from './clear_column_button';
 | 
			
		||||
@@ -67,15 +68,17 @@ class ColumnSettings extends PureComponent {
 | 
			
		||||
 | 
			
		||||
        <PolicyControls />
 | 
			
		||||
 | 
			
		||||
        <section role='group' aria-labelledby='notifications-beta'>
 | 
			
		||||
          <h3 id='notifications-beta'>
 | 
			
		||||
            <FormattedMessage id='notifications.column_settings.beta.category' defaultMessage='Experimental features' />
 | 
			
		||||
          </h3>
 | 
			
		||||
        {!forceGroupedNotifications && (
 | 
			
		||||
          <section role='group' aria-labelledby='notifications-beta'>
 | 
			
		||||
            <h3 id='notifications-beta'>
 | 
			
		||||
              <FormattedMessage id='notifications.column_settings.beta.category' defaultMessage='Experimental features' />
 | 
			
		||||
            </h3>
 | 
			
		||||
 | 
			
		||||
          <div className='column-settings__row'>
 | 
			
		||||
            <SettingToggle id='unread-notification-markers' prefix='notifications' settings={settings} settingPath={['groupingBeta']} onChange={onChange} label={groupingShowStr} />
 | 
			
		||||
          </div>
 | 
			
		||||
        </section>
 | 
			
		||||
            <div className='column-settings__row'>
 | 
			
		||||
              <SettingToggle id='unread-notification-markers' prefix='notifications' settings={settings} settingPath={['groupingBeta']} onChange={onChange} label={groupingShowStr} />
 | 
			
		||||
            </div>
 | 
			
		||||
          </section>
 | 
			
		||||
        )}
 | 
			
		||||
 | 
			
		||||
        <section role='group' aria-labelledby='notifications-unread-markers'>
 | 
			
		||||
          <h3 id='notifications-unread-markers'>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,10 @@
 | 
			
		||||
import Notifications from 'mastodon/features/notifications';
 | 
			
		||||
import Notifications_v2 from 'mastodon/features/notifications_v2';
 | 
			
		||||
import { selectUseGroupedNotifications } from 'mastodon/selectors/settings';
 | 
			
		||||
import { useAppSelector } from 'mastodon/store';
 | 
			
		||||
 | 
			
		||||
export const NotificationsWrapper = (props) => {
 | 
			
		||||
  const optedInGroupedNotifications = useAppSelector((state) => state.getIn(['settings', 'notifications', 'groupingBeta'], false));
 | 
			
		||||
  const optedInGroupedNotifications = useAppSelector(selectUseGroupedNotifications);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    optedInGroupedNotifications ? <Notifications_v2 {...props} /> : <Notifications {...props} />
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,7 @@ import { timelinePreview, trendsEnabled } from 'mastodon/initial_state';
 | 
			
		||||
import { transientSingleColumn } from 'mastodon/is_mobile';
 | 
			
		||||
import { canManageReports, canViewAdminDashboard } from 'mastodon/permissions';
 | 
			
		||||
import { selectUnreadNotificationGroupsCount } from 'mastodon/selectors/notifications';
 | 
			
		||||
import { selectUseGroupedNotifications } from 'mastodon/selectors/settings';
 | 
			
		||||
 | 
			
		||||
import ColumnLink from './column_link';
 | 
			
		||||
import DisabledAccountBanner from './disabled_account_banner';
 | 
			
		||||
@@ -64,7 +65,7 @@ const messages = defineMessages({
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const NotificationsLink = () => {
 | 
			
		||||
  const optedInGroupedNotifications = useSelector((state) => state.getIn(['settings', 'notifications', 'groupingBeta'], false));
 | 
			
		||||
  const optedInGroupedNotifications = useSelector(selectUseGroupedNotifications);
 | 
			
		||||
  const count = useSelector(state => state.getIn(['notifications', 'unread']));
 | 
			
		||||
  const intl = useIntl();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,7 @@
 | 
			
		||||
 * @property {boolean=} use_pending_items
 | 
			
		||||
 * @property {string} version
 | 
			
		||||
 * @property {string} sso_redirect
 | 
			
		||||
 * @property {boolean} force_grouped_notifications
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -118,6 +119,7 @@ export const criticalUpdatesPending = initialState?.critical_updates_pending;
 | 
			
		||||
// @ts-expect-error
 | 
			
		||||
export const statusPageUrl = getMeta('status_page_url');
 | 
			
		||||
export const sso_redirect = getMeta('sso_redirect');
 | 
			
		||||
export const forceGroupedNotifications = getMeta('force_grouped_notifications');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @returns {string | undefined}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import { forceGroupedNotifications } from 'mastodon/initial_state';
 | 
			
		||||
import type { RootState } from 'mastodon/store';
 | 
			
		||||
 | 
			
		||||
/* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */
 | 
			
		||||
@@ -25,6 +26,10 @@ export const selectSettingsNotificationsQuickFilterAdvanced = (
 | 
			
		||||
) =>
 | 
			
		||||
  state.settings.getIn(['notifications', 'quickFilter', 'advanced']) as boolean;
 | 
			
		||||
 | 
			
		||||
export const selectUseGroupedNotifications = (state: RootState) =>
 | 
			
		||||
  forceGroupedNotifications ||
 | 
			
		||||
  (state.settings.getIn(['notifications', 'groupingBeta']) as boolean);
 | 
			
		||||
 | 
			
		||||
export const selectSettingsNotificationsShowUnread = (state: RootState) =>
 | 
			
		||||
  state.settings.getIn(['notifications', 'showUnread']) as boolean;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,7 @@ class InitialStateSerializer < ActiveModel::Serializer
 | 
			
		||||
      trends_as_landing_page: Setting.trends_as_landing_page,
 | 
			
		||||
      trends_enabled: Setting.trends,
 | 
			
		||||
      version: instance_presenter.version,
 | 
			
		||||
      force_grouped_notifications: ENV['FORCE_GROUPED_NOTIFICATIONS'] == 'true',
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user