Change adjacent notifications about the same status to display as one
Fix #7698
This commit is contained in:
parent
aefeb65656
commit
7e31cce96d
4 changed files with 34 additions and 5 deletions
|
@ -17,6 +17,8 @@ const notificationForScreenReader = (intl, message, timestamp) => {
|
||||||
return output.join(', ');
|
return output.join(', ');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const appendDisplayNames = (base, accounts) => <span>{base} <abbr title={accounts.map(a => `@${a.get('acct')}`).join(', ')}><FormattedMessage id='notification.and_n_others' defaultMessage='and {count, plural, one {# other} other {# others}}' values={{ count: accounts.size }} /></abbr></span>;
|
||||||
|
|
||||||
export default @injectIntl
|
export default @injectIntl
|
||||||
class Notification extends ImmutablePureComponent {
|
class Notification extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
@ -144,6 +146,10 @@ class Notification extends ImmutablePureComponent {
|
||||||
renderFavourite (notification, link) {
|
renderFavourite (notification, link) {
|
||||||
const { intl } = this.props;
|
const { intl } = this.props;
|
||||||
|
|
||||||
|
if (notification.get('collapsed_accounts')) {
|
||||||
|
link = appendDisplayNames(link, notification.get('collapsed_accounts'));
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={this.getHandlers()}>
|
<HotKeys handlers={this.getHandlers()}>
|
||||||
<div className='notification notification-favourite focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.favourite', defaultMessage: '{name} favourited your status' }, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
<div className='notification notification-favourite focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.favourite', defaultMessage: '{name} favourited your status' }, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
||||||
|
@ -176,6 +182,10 @@ class Notification extends ImmutablePureComponent {
|
||||||
renderReblog (notification, link) {
|
renderReblog (notification, link) {
|
||||||
const { intl } = this.props;
|
const { intl } = this.props;
|
||||||
|
|
||||||
|
if (notification.get('collapsed_accounts')) {
|
||||||
|
link = appendDisplayNames(link, notification.get('collapsed_accounts'));
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={this.getHandlers()}>
|
<HotKeys handlers={this.getHandlers()}>
|
||||||
<div className='notification notification-reblog focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.reblog', defaultMessage: '{name} boosted your status' }, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
<div className='notification notification-reblog focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.reblog', defaultMessage: '{name} boosted your status' }, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
||||||
|
|
|
@ -21,6 +21,7 @@ const makeMapStateToProps = () => {
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
const notification = getNotification(state, props.notification, props.accountId);
|
const notification = getNotification(state, props.notification, props.accountId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
notification: notification,
|
notification: notification,
|
||||||
status: notification.get('status') ? getStatus(state, { id: notification.get('status') }) : null,
|
status: notification.get('status') ? getStatus(state, { id: notification.get('status') }) : null,
|
||||||
|
|
|
@ -20,6 +20,22 @@ const messages = defineMessages({
|
||||||
title: { id: 'column.notifications', defaultMessage: 'Notifications' },
|
title: { id: 'column.notifications', defaultMessage: 'Notifications' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const collapsibleNotifications = (a, b) => {
|
||||||
|
if (!['reblog', 'favourite'].includes(a.get('type'))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.get('type') === b.get('type') && a.get('status') === b.get('status');
|
||||||
|
};
|
||||||
|
|
||||||
|
const reduceNotifications = (list, notification) => {
|
||||||
|
if (!list.isEmpty() && collapsibleNotifications(list.last(), notification)) {
|
||||||
|
return list.update(list.size - 1, item => item.update('collapsed_account_ids', ImmutableList(), collapsed_account_ids => collapsed_account_ids.push(notification.get('account'))));
|
||||||
|
} else {
|
||||||
|
return list.push(notification);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getNotifications = createSelector([
|
const getNotifications = createSelector([
|
||||||
state => state.getIn(['settings', 'notifications', 'quickFilter', 'show']),
|
state => state.getIn(['settings', 'notifications', 'quickFilter', 'show']),
|
||||||
state => state.getIn(['settings', 'notifications', 'quickFilter', 'active']),
|
state => state.getIn(['settings', 'notifications', 'quickFilter', 'active']),
|
||||||
|
@ -27,12 +43,13 @@ const getNotifications = createSelector([
|
||||||
state => state.getIn(['notifications', 'items']),
|
state => state.getIn(['notifications', 'items']),
|
||||||
], (showFilterBar, allowedType, excludedTypes, notifications) => {
|
], (showFilterBar, allowedType, excludedTypes, notifications) => {
|
||||||
if (!showFilterBar || allowedType === 'all') {
|
if (!showFilterBar || allowedType === 'all') {
|
||||||
// used if user changed the notification settings after loading the notifications from the server
|
// Used if user changed the notification settings after loading the notifications from the server
|
||||||
// otherwise a list of notifications will come pre-filtered from the backend
|
// otherwise a list of notifications will come pre-filtered from the backend
|
||||||
// we need to turn it off for FilterBar in order not to block ourselves from seeing a specific category
|
// we need to turn it off for FilterBar in order not to block ourselves from seeing a specific category
|
||||||
return notifications.filterNot(item => item !== null && excludedTypes.includes(item.get('type')));
|
return notifications.filterNot(item => item !== null && excludedTypes.includes(item.get('type'))).reduce(reduceNotifications, ImmutableList());
|
||||||
}
|
}
|
||||||
return notifications.filter(item => item !== null && allowedType === item.get('type'));
|
|
||||||
|
return notifications.filter(item => item !== null && allowedType === item.get('type')).reduce(reduceNotifications, ImmutableList());
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
|
|
|
@ -144,8 +144,9 @@ export const makeGetNotification = () => {
|
||||||
return createSelector([
|
return createSelector([
|
||||||
(_, base) => base,
|
(_, base) => base,
|
||||||
(state, _, accountId) => state.getIn(['accounts', accountId]),
|
(state, _, accountId) => state.getIn(['accounts', accountId]),
|
||||||
], (base, account) => {
|
(state, base) => base.get('collapsed_account_ids') && base.get('collapsed_account_ids').map(id => state.getIn(['accounts', id])),
|
||||||
return base.set('account', account);
|
], (base, account, collapsedAccounts) => {
|
||||||
|
return base.set('account', account).set('collapsed_accounts', collapsedAccounts);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue