diff --git a/app/soapbox/features/notifications/components/notification.tsx b/app/soapbox/features/notifications/components/notification.tsx index b413517e6c..a6e7e9514e 100644 --- a/app/soapbox/features/notifications/components/notification.tsx +++ b/app/soapbox/features/notifications/components/notification.tsx @@ -9,9 +9,9 @@ import { HStack, Text, Emoji } from 'soapbox/components/ui'; import AccountContainer from 'soapbox/containers/account_container'; import StatusContainer from 'soapbox/containers/status_container'; import { useAppSelector } from 'soapbox/hooks'; +import { NotificationType, validType } from 'soapbox/utils/notification'; import type { ScrollPosition } from 'soapbox/components/status'; -import type { NotificationType } from 'soapbox/normalizers/notification'; import type { Account, Status, Notification as NotificationEntity } from 'soapbox/types/entities'; const notificationForScreenReader = (intl: IntlShape, message: string, timestamp: Date) => { @@ -48,7 +48,7 @@ const icons: Record = { user_approved: require('@tabler/icons/icons/user-plus.svg'), }; -const messages: Record = defineMessages({ +const messages: Record = defineMessages({ follow: { id: 'notification.follow', defaultMessage: '{name} followed you', @@ -221,7 +221,7 @@ const Notification: React.FC = (props) => { className='w-4 h-4 flex-none' /> ); - } else if (type) { + } else if (validType(type)) { return ( = (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, notification.total_count, targetName, instance.title) : null; + const message: React.ReactNode = validType(type) && account && typeof account === 'object' ? buildMessage(intl, type, account, notification.total_count, targetName, instance.title) : null; - const ariaLabel = messages[type] ? ( + const ariaLabel = validType(type) ? ( notificationForScreenReader( intl, intl.formatMessage(messages[type], { diff --git a/app/soapbox/normalizers/notification.ts b/app/soapbox/normalizers/notification.ts index 2bac3f2b3e..9b34278b6e 100644 --- a/app/soapbox/normalizers/notification.ts +++ b/app/soapbox/normalizers/notification.ts @@ -11,19 +11,6 @@ import { import type { Account, Status, EmbeddedEntity } from 'soapbox/types/entities'; -export type NotificationType = - 'follow' - | 'follow_request' - | 'mention' - | 'reblog' - | 'favourite' - | 'poll' - | 'status' - | 'move' - | 'pleroma:chat_mention' - | 'pleroma:emoji_reaction' - | 'user_approved'; - // https://docs.joinmastodon.org/entities/notification/ export const NotificationRecord = ImmutableRecord({ account: null as EmbeddedEntity, @@ -33,7 +20,7 @@ export const NotificationRecord = ImmutableRecord({ id: '', status: null as EmbeddedEntity, target: null as EmbeddedEntity, // move - type: '' as NotificationType | '', + type: '', total_count: null as number | null, // grouped notifications }); diff --git a/app/soapbox/utils/notification.ts b/app/soapbox/utils/notification.ts new file mode 100644 index 0000000000..635d16f293 --- /dev/null +++ b/app/soapbox/utils/notification.ts @@ -0,0 +1,25 @@ +/** Notification types known to Soapbox. */ +const NOTIFICATION_TYPES = [ + 'follow', + 'follow_request', + 'mention', + 'reblog', + 'favourite', + 'poll', + 'status', + 'move', + 'pleroma:chat_mention', + 'pleroma:emoji_reaction', + 'user_approved', +] as const; + +type NotificationType = typeof NOTIFICATION_TYPES[number]; + +/** Ensure the Notification is a valid, known type. */ +const validType = (type: string): type is NotificationType => NOTIFICATION_TYPES.includes(type as any); + +export { + NOTIFICATION_TYPES, + NotificationType, + validType, +};