diff --git a/app/soapbox/features/notifications/components/__tests__/notification.test.tsx b/app/soapbox/features/notifications/components/__tests__/notification.test.tsx index f11d43f09..14c43f50c 100644 --- a/app/soapbox/features/notifications/components/__tests__/notification.test.tsx +++ b/app/soapbox/features/notifications/components/__tests__/notification.test.tsx @@ -28,6 +28,31 @@ describe('', () => { expect(screen.getByTestId('notification')).toBeInTheDocument(); expect(screen.getByTestId('account')).toContainHTML('neko@rdrama.cc'); + expect(screen.getByTestId('message')).toHaveTextContent('Nekobit followed you'); + }); + + describe('grouped notifications', () => { + it('renders a grouped follow notification for more than 2', async() => { + const { notification, state } = normalize(require('soapbox/__fixtures__/notification-follow.json')); + const groupedNotification = { ...notification.toJS(), total_count: 5 }; + + render(, undefined, state); + + expect(screen.getByTestId('notification')).toBeInTheDocument(); + expect(screen.getByTestId('account')).toContainHTML('neko@rdrama.cc'); + expect(screen.getByTestId('message')).toHaveTextContent('Nekobit + 4 others followed you'); + }); + + it('renders a grouped follow notification for 1', async() => { + const { notification, state } = normalize(require('soapbox/__fixtures__/notification-follow.json')); + const groupedNotification = { ...notification.toJS(), total_count: 2 }; + + render(, undefined, state); + + expect(screen.getByTestId('notification')).toBeInTheDocument(); + expect(screen.getByTestId('account')).toContainHTML('neko@rdrama.cc'); + expect(screen.getByTestId('message')).toHaveTextContent('Nekobit + 1 other followed you'); + }); }); it('renders a favourite notification', async() => { diff --git a/app/soapbox/features/notifications/components/notification.tsx b/app/soapbox/features/notifications/components/notification.tsx index b3453dde1..1a9dad8eb 100644 --- a/app/soapbox/features/notifications/components/notification.tsx +++ b/app/soapbox/features/notifications/components/notification.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { HotKeys } from 'react-hotkeys'; -import { defineMessages, IntlShape, MessageDescriptor } from 'react-intl'; +import { defineMessages, FormattedMessage, IntlShape, MessageDescriptor } from 'react-intl'; import { useIntl } from 'react-intl'; import { useHistory } from 'react-router-dom'; @@ -102,13 +102,27 @@ const buildMessage = ( intl: IntlShape, type: NotificationType, account: Account, + totalCount: number | null, targetName: string, instanceTitle: string, ): React.ReactNode => { const link = buildLink(account); + const name = intl.formatMessage({ + id: 'notification.name', + defaultMessage: '{link}{others}', + }, { + link, + others: totalCount && totalCount > 0 ? ( + + ) : '', + }); return intl.formatMessage(messages[type], { - name: link, + name, targetName, instance: instanceTitle, }); @@ -268,7 +282,7 @@ const Notification: React.FC = (props) => { const targetName = notification.target && typeof notification.target === 'object' ? notification.target.acct : ''; - const message: React.ReactNode = type && account && typeof account === 'object' ? buildMessage(intl, type, account, targetName, instance.title) : null; + const message: React.ReactNode = type && account && typeof account === 'object' ? buildMessage(intl, type, account, notification.total_count, targetName, instance.title) : null; return ( @@ -300,6 +314,7 @@ const Notification: React.FC = (props) => { theme='muted' size='sm' truncate + data-testid='message' > {message} diff --git a/app/soapbox/normalizers/notification.ts b/app/soapbox/normalizers/notification.ts index cee69f7ce..2bac3f2b3 100644 --- a/app/soapbox/normalizers/notification.ts +++ b/app/soapbox/normalizers/notification.ts @@ -34,6 +34,7 @@ export const NotificationRecord = ImmutableRecord({ status: null as EmbeddedEntity, target: null as EmbeddedEntity, // move type: '' as NotificationType | '', + total_count: null as number | null, // grouped notifications }); export const normalizeNotification = (notification: Record) => { diff --git a/app/soapbox/reducers/__tests__/notifications-test.js b/app/soapbox/reducers/__tests__/notifications-test.js index aa7d6f714..7061c60bb 100644 --- a/app/soapbox/reducers/__tests__/notifications-test.js +++ b/app/soapbox/reducers/__tests__/notifications-test.js @@ -274,6 +274,7 @@ describe('notifications reducer', () => { status: '9vvNxoo5EFbbnfdXQu', emoji: '😢', chat_message: null, + total_count: null, })], ['10743', ImmutableMap({ id: '10743', @@ -284,6 +285,7 @@ describe('notifications reducer', () => { status: '9vvNxoo5EFbbnfdXQu', emoji: null, chat_message: null, + total_count: null, })], ['10741', ImmutableMap({ id: '10741', @@ -294,6 +296,7 @@ describe('notifications reducer', () => { status: '9vvNxoo5EFbbnfdXQu', emoji: null, chat_message: null, + total_count: null, })], ['10734', ImmutableMap({ id: '10734', @@ -339,6 +342,7 @@ describe('notifications reducer', () => { status: '9vvNxoo5EFbbnfdXQu', emoji: '😢', chat_message: null, + total_count: null, })], ['10743', ImmutableMap({ id: '10743', @@ -349,6 +353,7 @@ describe('notifications reducer', () => { status: '9vvNxoo5EFbbnfdXQu', emoji: null, chat_message: null, + total_count: null, })], ['10741', ImmutableMap({ id: '10741', @@ -359,6 +364,7 @@ describe('notifications reducer', () => { status: '9vvNxoo5EFbbnfdXQu', emoji: null, chat_message: null, + total_count: null, })], ]), unread: 1,