[Glitch] Add ability for admins to force grouped notifications in web UI

Port c73868cd78 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
shrike
Claire 2024-08-27 16:55:51 +02:00
parent e15fad27bc
commit 435ff8e550
9 changed files with 52 additions and 38 deletions

View File

@ -2,6 +2,7 @@ import { debounce } from 'lodash';
import type { MarkerJSON } from 'flavours/glitch/api_types/markers'; import type { MarkerJSON } from 'flavours/glitch/api_types/markers';
import { getAccessToken } from 'flavours/glitch/initial_state'; import { getAccessToken } from 'flavours/glitch/initial_state';
import { selectUseGroupedNotifications } from 'flavours/glitch/selectors/settings';
import type { AppDispatch, RootState } from 'flavours/glitch/store'; import type { AppDispatch, RootState } from 'flavours/glitch/store';
import { createAppAsyncThunk } from 'flavours/glitch/store/typed_functions'; import { createAppAsyncThunk } from 'flavours/glitch/store/typed_functions';
@ -75,13 +76,8 @@ interface MarkerParam {
} }
function getLastNotificationId(state: RootState): string | undefined { 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-return
return enableBeta return selectUseGroupedNotifications(state)
? state.notificationGroups.lastReadId ? state.notificationGroups.lastReadId
: // @ts-expect-error state.notifications is not yet typed : // @ts-expect-error state.notifications is not yet typed
// eslint-disable-next-line @typescript-eslint/no-unsafe-call // eslint-disable-next-line @typescript-eslint/no-unsafe-call

View File

@ -1,3 +1,4 @@
import { selectUseGroupedNotifications } from 'flavours/glitch/selectors/settings';
import { createAppAsyncThunk } from 'flavours/glitch/store'; import { createAppAsyncThunk } from 'flavours/glitch/store';
import { fetchNotifications } from './notification_groups'; import { fetchNotifications } from './notification_groups';
@ -6,13 +7,8 @@ import { expandNotifications } from './notifications';
export const initializeNotifications = createAppAsyncThunk( export const initializeNotifications = createAppAsyncThunk(
'notifications/initialize', 'notifications/initialize',
(_, { dispatch, getState }) => { (_, { dispatch, getState }) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access if (selectUseGroupedNotifications(getState()))
const enableBeta = getState().settings.getIn( void dispatch(fetchNotifications());
['notifications', 'groupingBeta'],
false,
) as boolean;
if (enableBeta) void dispatch(fetchNotifications());
else void dispatch(expandNotifications({})); else void dispatch(expandNotifications({}));
}, },
); );

View File

@ -1,5 +1,7 @@
// @ts-check // @ts-check
import { selectUseGroupedNotifications } from 'flavours/glitch/selectors/settings';
import { getLocale } from '../locales'; import { getLocale } from '../locales';
import { connectStream } from '../stream'; import { connectStream } from '../stream';
@ -103,7 +105,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
const notificationJSON = JSON.parse(data.payload); const notificationJSON = JSON.parse(data.payload);
dispatch(updateNotifications(notificationJSON, messages, locale)); dispatch(updateNotifications(notificationJSON, messages, locale));
// TODO: remove this once the groups feature replaces the previous one // TODO: remove this once the groups feature replaces the previous one
if(getState().settings.getIn(['notifications', 'groupingBeta'], false)) { if(selectUseGroupedNotifications(getState())) {
dispatch(processNewNotificationForGroups(notificationJSON)); dispatch(processNewNotificationForGroups(notificationJSON));
} }
break; break;
@ -112,7 +114,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
const state = getState(); const state = getState();
if (state.notifications.top || !state.notifications.mounted) if (state.notifications.top || !state.notifications.mounted)
dispatch(expandNotifications({ forceLoad: true, maxId: undefined })); dispatch(expandNotifications({ forceLoad: true, maxId: undefined }));
if(state.settings.getIn(['notifications', 'groupingBeta'], false)) { if (selectUseGroupedNotifications(state)) {
dispatch(refreshStaleNotificationGroups()); dispatch(refreshStaleNotificationGroups());
} }
break; break;
@ -145,7 +147,7 @@ async function refreshHomeTimelineAndNotification(dispatch, getState) {
await dispatch(expandHomeTimeline({ maxId: undefined })); await dispatch(expandHomeTimeline({ maxId: undefined }));
// TODO: remove this once the groups feature replaces the previous one // 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 // TODO: polling for merged notifications
try { try {
await dispatch(pollRecentGroupNotifications()); await dispatch(pollRecentGroupNotifications());

View File

@ -6,6 +6,7 @@ import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { forceGroupedNotifications } from 'flavours/glitch/initial_state';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'flavours/glitch/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'flavours/glitch/permissions';
import ClearColumnButton from './clear_column_button'; import ClearColumnButton from './clear_column_button';
@ -78,6 +79,7 @@ class ColumnSettings extends PureComponent {
</div> </div>
</section> </section>
{!forceGroupedNotifications && (
<section role='group' aria-labelledby='notifications-beta'> <section role='group' aria-labelledby='notifications-beta'>
<h3 id='notifications-beta'> <h3 id='notifications-beta'>
<FormattedMessage id='notifications.column_settings.beta.category' defaultMessage='Experimental features' /> <FormattedMessage id='notifications.column_settings.beta.category' defaultMessage='Experimental features' />
@ -87,6 +89,7 @@ class ColumnSettings extends PureComponent {
<SettingToggle id='unread-notification-markers' prefix='notifications' settings={settings} settingPath={['groupingBeta']} onChange={onChange} label={groupingShowStr} /> <SettingToggle id='unread-notification-markers' prefix='notifications' settings={settings} settingPath={['groupingBeta']} onChange={onChange} label={groupingShowStr} />
</div> </div>
</section> </section>
)}
<section role='group' aria-labelledby='notifications-unread-markers'> <section role='group' aria-labelledby='notifications-unread-markers'>
<h3 id='notifications-unread-markers'> <h3 id='notifications-unread-markers'>

View File

@ -1,9 +1,10 @@
import Notifications from 'flavours/glitch/features/notifications'; import Notifications from 'flavours/glitch/features/notifications';
import Notifications_v2 from 'flavours/glitch/features/notifications_v2'; import Notifications_v2 from 'flavours/glitch/features/notifications_v2';
import { selectUseGroupedNotifications } from 'flavours/glitch/selectors/settings';
import { useAppSelector } from 'flavours/glitch/store'; import { useAppSelector } from 'flavours/glitch/store';
export const NotificationsWrapper = (props) => { export const NotificationsWrapper = (props) => {
const optedInGroupedNotifications = useAppSelector((state) => state.getIn(['settings', 'notifications', 'groupingBeta'], false)); const optedInGroupedNotifications = useAppSelector(selectUseGroupedNotifications);
return ( return (
optedInGroupedNotifications ? <Notifications_v2 {...props} /> : <Notifications {...props} /> optedInGroupedNotifications ? <Notifications_v2 {...props} /> : <Notifications {...props} />

View File

@ -24,7 +24,8 @@ export const BoostModal: React.FC<{
status: Status; status: Status;
onClose: () => void; onClose: () => void;
onReblog: (status: Status, privacy: StatusVisibility) => void; onReblog: (status: Status, privacy: StatusVisibility) => void;
}> = ({ status, onReblog, onClose }) => { missingMediaDescription?: boolean;
}> = ({ status, onReblog, onClose, missingMediaDescription }) => {
const intl = useIntl(); const intl = useIntl();
const defaultPrivacy = useAppSelector( const defaultPrivacy = useAppSelector(
@ -80,6 +81,12 @@ export const BoostModal: React.FC<{
)} )}
</h1> </h1>
<div> <div>
{missingMediaDescription ? (
<FormattedMessage
id='boost_modal.missing_description'
defaultMessage='This toot contains some media without description'
/>
) : (
<FormattedMessage <FormattedMessage
id='boost_modal.combo' id='boost_modal.combo'
defaultMessage='You can press {combo} to skip this next time' defaultMessage='You can press {combo} to skip this next time'
@ -91,6 +98,7 @@ export const BoostModal: React.FC<{
), ),
}} }}
/> />
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -36,6 +36,7 @@ import { timelinePreview, trendsEnabled } from 'flavours/glitch/initial_state';
import { transientSingleColumn } from 'flavours/glitch/is_mobile'; import { transientSingleColumn } from 'flavours/glitch/is_mobile';
import { canManageReports, canViewAdminDashboard } from 'flavours/glitch/permissions'; import { canManageReports, canViewAdminDashboard } from 'flavours/glitch/permissions';
import { selectUnreadNotificationGroupsCount } from 'flavours/glitch/selectors/notifications'; import { selectUnreadNotificationGroupsCount } from 'flavours/glitch/selectors/notifications';
import { selectUseGroupedNotifications } from 'flavours/glitch/selectors/settings';
import { preferencesLink } from 'flavours/glitch/utils/backend_links'; import { preferencesLink } from 'flavours/glitch/utils/backend_links';
import ColumnLink from './column_link'; import ColumnLink from './column_link';
@ -65,7 +66,7 @@ const messages = defineMessages({
}); });
const NotificationsLink = () => { const NotificationsLink = () => {
const optedInGroupedNotifications = useSelector((state) => state.getIn(['settings', 'notifications', 'groupingBeta'], false)); const optedInGroupedNotifications = useSelector(selectUseGroupedNotifications);
const count = useSelector(state => state.getIn(['local_settings', 'notifications', 'tab_badge']) ? state.getIn(['notifications', 'unread']) : 0); const count = useSelector(state => state.getIn(['local_settings', 'notifications', 'tab_badge']) ? state.getIn(['notifications', 'unread']) : 0);
const intl = useIntl(); const intl = useIntl();

View File

@ -46,6 +46,7 @@
* @property {boolean=} use_pending_items * @property {boolean=} use_pending_items
* @property {string} version * @property {string} version
* @property {string} sso_redirect * @property {string} sso_redirect
* @property {boolean} force_grouped_notifications
* @property {string} status_page_url * @property {string} status_page_url
* @property {boolean} system_emoji_font * @property {boolean} system_emoji_font
* @property {string} default_content_type * @property {string} default_content_type
@ -137,6 +138,7 @@ export const languages = initialState?.languages;
export const criticalUpdatesPending = initialState?.critical_updates_pending; export const criticalUpdatesPending = initialState?.critical_updates_pending;
export const statusPageUrl = getMeta('status_page_url'); export const statusPageUrl = getMeta('status_page_url');
export const sso_redirect = getMeta('sso_redirect'); export const sso_redirect = getMeta('sso_redirect');
export const forceGroupedNotifications = getMeta('force_grouped_notifications');
// Glitch-soc-specific settings // Glitch-soc-specific settings
export const maxFeedHashtags = (initialState && initialState.max_feed_hashtags) || 4; export const maxFeedHashtags = (initialState && initialState.max_feed_hashtags) || 4;

View File

@ -1,3 +1,4 @@
import { forceGroupedNotifications } from 'flavours/glitch/initial_state';
import type { RootState } from 'flavours/glitch/store'; import type { RootState } from 'flavours/glitch/store';
/* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */ /* 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; 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) => export const selectSettingsNotificationsShowUnread = (state: RootState) =>
state.settings.getIn(['notifications', 'showUnread']) as boolean; state.settings.getIn(['notifications', 'showUnread']) as boolean;