Fix notifications re-rendering spuriously in web UI (#31879)
This commit is contained in:
parent
7d53ca56d2
commit
f2a92c2d22
5 changed files with 30 additions and 25 deletions
|
@ -1,5 +1,7 @@
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
|
import AlternateEmailIcon from '@/material-icons/400-24px/alternate_email.svg?react';
|
||||||
import ReplyIcon from '@/material-icons/400-24px/reply-fill.svg?react';
|
import ReplyIcon from '@/material-icons/400-24px/reply-fill.svg?react';
|
||||||
import { me } from 'mastodon/initial_state';
|
import { me } from 'mastodon/initial_state';
|
||||||
|
@ -47,7 +49,7 @@ export const NotificationMention: React.FC<{
|
||||||
status.get('visibility') === 'direct',
|
status.get('visibility') === 'direct',
|
||||||
status.get('in_reply_to_account_id') === me,
|
status.get('in_reply_to_account_id') === me,
|
||||||
] as const;
|
] as const;
|
||||||
});
|
}, isEqual);
|
||||||
|
|
||||||
let labelRenderer = mentionLabelRenderer;
|
let labelRenderer = mentionLabelRenderer;
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
import { useDebouncedCallback } from 'use-debounce';
|
import { useDebouncedCallback } from 'use-debounce';
|
||||||
|
|
||||||
import DoneAllIcon from '@/material-icons/400-24px/done_all.svg?react';
|
import DoneAllIcon from '@/material-icons/400-24px/done_all.svg?react';
|
||||||
|
@ -62,7 +63,7 @@ export const Notifications: React.FC<{
|
||||||
multiColumn?: boolean;
|
multiColumn?: boolean;
|
||||||
}> = ({ columnId, multiColumn }) => {
|
}> = ({ columnId, multiColumn }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const notifications = useAppSelector(selectNotificationGroups);
|
const notifications = useAppSelector(selectNotificationGroups, isEqual);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const isLoading = useAppSelector((s) => s.notificationGroups.isLoading);
|
const isLoading = useAppSelector((s) => s.notificationGroups.isLoading);
|
||||||
const hasMore = notifications.at(-1)?.type === 'gap';
|
const hasMore = notifications.at(-1)?.type === 'gap';
|
||||||
|
|
|
@ -3,10 +3,12 @@ import { connect } from 'react-redux';
|
||||||
import { openModal, closeModal } from '../../../actions/modal';
|
import { openModal, closeModal } from '../../../actions/modal';
|
||||||
import ModalRoot from '../components/modal_root';
|
import ModalRoot from '../components/modal_root';
|
||||||
|
|
||||||
|
const defaultProps = {};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
ignoreFocus: state.getIn(['modal', 'ignoreFocus']),
|
ignoreFocus: state.getIn(['modal', 'ignoreFocus']),
|
||||||
type: state.getIn(['modal', 'stack', 0, 'modalType'], null),
|
type: state.getIn(['modal', 'stack', 0, 'modalType'], null),
|
||||||
props: state.getIn(['modal', 'stack', 0, 'modalProps'], {}),
|
props: state.getIn(['modal', 'stack', 0, 'modalProps'], defaultProps),
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
|
|
@ -4,24 +4,11 @@ import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { NotificationStack } from 'react-notification';
|
import { NotificationStack } from 'react-notification';
|
||||||
|
|
||||||
import { dismissAlert } from '../../../actions/alerts';
|
import { dismissAlert } from 'mastodon/actions/alerts';
|
||||||
import { getAlerts } from '../../../selectors';
|
import { getAlerts } from 'mastodon/selectors';
|
||||||
|
|
||||||
const formatIfNeeded = (intl, message, values) => {
|
|
||||||
if (typeof message === 'object') {
|
|
||||||
return intl.formatMessage(message, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
return message;
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapStateToProps = (state, { intl }) => ({
|
const mapStateToProps = (state, { intl }) => ({
|
||||||
notifications: getAlerts(state).map(alert => ({
|
notifications: getAlerts(state, { intl }),
|
||||||
...alert,
|
|
||||||
action: formatIfNeeded(intl, alert.action, alert.values),
|
|
||||||
title: formatIfNeeded(intl, alert.title, alert.values),
|
|
||||||
message: formatIfNeeded(intl, alert.message, alert.values),
|
|
||||||
})),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
|
|
@ -7,14 +7,16 @@ import { me } from '../initial_state';
|
||||||
|
|
||||||
export { makeGetAccount } from "./accounts";
|
export { makeGetAccount } from "./accounts";
|
||||||
|
|
||||||
const getFilters = (state, { contextType }) => {
|
const getFilters = createSelector([state => state.get('filters'), (_, { contextType }) => contextType], (filters, contextType) => {
|
||||||
if (!contextType) return null;
|
if (!contextType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const serverSideType = toServerSideType(contextType);
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
const serverSideType = toServerSideType(contextType);
|
||||||
|
|
||||||
return state.get('filters').filter((filter) => filter.get('context').includes(serverSideType) && (filter.get('expires_at') === null || filter.get('expires_at') > now));
|
return filters.filter(filter => filter.get('context').includes(serverSideType) && (filter.get('expires_at') === null || filter.get('expires_at') > now));
|
||||||
};
|
});
|
||||||
|
|
||||||
export const makeGetStatus = () => {
|
export const makeGetStatus = () => {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
|
@ -73,10 +75,21 @@ const ALERT_DEFAULTS = {
|
||||||
style: false,
|
style: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAlerts = createSelector(state => state.get('alerts'), alerts =>
|
const formatIfNeeded = (intl, message, values) => {
|
||||||
|
if (typeof message === 'object') {
|
||||||
|
return intl.formatMessage(message, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAlerts = createSelector([state => state.get('alerts'), (_, { intl }) => intl], (alerts, intl) =>
|
||||||
alerts.map(item => ({
|
alerts.map(item => ({
|
||||||
...ALERT_DEFAULTS,
|
...ALERT_DEFAULTS,
|
||||||
...item,
|
...item,
|
||||||
|
action: formatIfNeeded(intl, item.action, item.values),
|
||||||
|
title: formatIfNeeded(intl, item.title, item.values),
|
||||||
|
message: formatIfNeeded(intl, item.message, item.values),
|
||||||
})).toArray());
|
})).toArray());
|
||||||
|
|
||||||
export const makeGetNotification = () => createSelector([
|
export const makeGetNotification = () => createSelector([
|
||||||
|
|
Loading…
Reference in a new issue