diff --git a/app/javascript/mastodon/features/notifications_v2/components/avatar_group.tsx b/app/javascript/mastodon/features/notifications_v2/components/avatar_group.tsx index a32962c475..788215bf8d 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/avatar_group.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/avatar_group.tsx @@ -3,8 +3,10 @@ import { Link } from 'react-router-dom'; import { Avatar } from 'mastodon/components/avatar'; import { useAppSelector } from 'mastodon/store'; -const AvatarWrapper = ({ accountId }) => { - const account = useAppSelector(state => state.getIn(['accounts', accountId])); +const AvatarWrapper: React.FC<{ accountId: string }> = ({ accountId }) => { + const account = useAppSelector((state) => state.accounts.get(accountId)); + + if (!account) return null; return ( @@ -13,8 +15,12 @@ const AvatarWrapper = ({ accountId }) => { ); }; -export const AvatarGroup = ({ accountIds }) => ( +export const AvatarGroup: React.FC<{ accountIds: string[] }> = ({ + accountIds, +}) => (
- {accountIds.map(accountId => )} + {accountIds.map((accountId) => ( + + ))}
); diff --git a/app/javascript/mastodon/features/notifications_v2/components/embedded_status.tsx b/app/javascript/mastodon/features/notifications_v2/components/embedded_status.tsx index 8f80959de1..c7e8172980 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/embedded_status.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/embedded_status.tsx @@ -1,21 +1,37 @@ import { FormattedMessage } from 'react-intl'; +import type { List } from 'immutable'; + import BarChart4BarsIcon from '@/material-icons/400-24px/bar_chart_4_bars.svg?react'; import PhotoLibraryIcon from '@/material-icons/400-24px/photo_library.svg?react'; import { Avatar } from 'mastodon/components/avatar'; import { DisplayName } from 'mastodon/components/display_name'; import { Icon } from 'mastodon/components/icon'; +import type { Status } from 'mastodon/models/status'; import { useAppSelector } from 'mastodon/store'; -export const EmbeddedStatus = ({ statusId }) => { - const status = useAppSelector(state => state.getIn(['statuses', statusId])); - const account = useAppSelector(state => state.getIn(['accounts', status?.get('account')])); +export const EmbeddedStatus: React.FC<{ statusId: string }> = ({ + statusId, +}) => { + const status = useAppSelector( + (state) => state.statuses.get(statusId) as Status | undefined, + ); + const account = useAppSelector((state) => + state.accounts.get(status?.get('account') as string), + ); if (!status) { return null; } - const content = { __html: status.get('contentHtml') }; + // Assign status attributes to variables with a forced type, as status is not yet properly typed + const contentHtml = status.get('contentHtml') as string; + const poll = status.get('poll'); + const mediaAttachmentsSize = ( + status.get('media_attachments') as List + ).size; + + const content = { __html: contentHtml }; return (
@@ -24,12 +40,32 @@ export const EmbeddedStatus = ({ statusId }) => {
-
+
- {(status.get('poll') || status.get('media_attachments').size > 0) && ( + {(poll || mediaAttachmentsSize > 0) && (
- {status.get('poll') && <>} - {status.get('media_attachments').size > 0 && <>} + {!!poll && ( + <> + + + + )} + {mediaAttachmentsSize > 0 && ( + <> + + + + )}
)}
diff --git a/app/javascript/mastodon/features/notifications_v2/components/names_list.tsx b/app/javascript/mastodon/features/notifications_v2/components/names_list.tsx index 14a20dd307..c49e99f759 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/names_list.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/names_list.tsx @@ -4,10 +4,22 @@ import { Link } from 'react-router-dom'; import { useAppSelector } from 'mastodon/store'; -export const NamesList = ({ accountIds, total }) => { - const lastAccountId = accountIds[0]; - const account = useAppSelector(state => state.getIn(['accounts', lastAccountId])); - const displayedName = ; +export const NamesList: React.FC<{ accountIds: string[]; total: number }> = ({ + accountIds, + total, +}) => { + const lastAccountId = accountIds[0] ?? '0'; + const account = useAppSelector((state) => state.accounts.get(lastAccountId)); + + if (!account) return null; + + const displayedName = ( + + + + ); if (total === 1) { return displayedName; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_admin_report.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_admin_report.tsx index 68b6d1768d..bca0da0f59 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_admin_report.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_admin_report.tsx @@ -8,44 +8,101 @@ import { useAppSelector } from 'mastodon/store'; import { NamesList } from './names_list'; - - - // This needs to be kept in sync with app/models/report.rb const messages = defineMessages({ - other: { id: 'report_notification.categories.other', defaultMessage: 'Other' }, + other: { + id: 'report_notification.categories.other', + defaultMessage: 'Other', + }, spam: { id: 'report_notification.categories.spam', defaultMessage: 'Spam' }, - legal: { id: 'report_notification.categories.legal', defaultMessage: 'Legal' }, - violation: { id: 'report_notification.categories.violation', defaultMessage: 'Rule violation' }, + legal: { + id: 'report_notification.categories.legal', + defaultMessage: 'Legal', + }, + violation: { + id: 'report_notification.categories.violation', + defaultMessage: 'Rule violation', + }, }); export const NotificationAdminReport: React.FC<{ notification: NotificationGroupAdminReport; }> = ({ notification, notification: { report } }) => { const intl = useIntl(); - const targetAccount = useAppSelector(state => state.getIn(['accounts', report.target_account.id])); - const account = useAppSelector(state => state.getIn(['accounts', notification.sampleAccountsIds[0]])); - const values = { name: , target: , category: intl.formatMessage(messages[report.category]), count: report.status_ids.length }; + const targetAccount = useAppSelector((state) => + state.getIn(['accounts', report.target_account.id]), + ); + const account = useAppSelector((state) => + state.getIn(['accounts', notification.sampleAccountsIds[0]]), + ); + const values = { + name: ( + + ), + target: ( + + ), + category: intl.formatMessage(messages[report.category]), + count: report.status_ids.length, + }; let message; if (report.status_ids.length > 0) { if (report.category === 'other') { - message = ; + message = ( + + ); } else { - message = ; + message = ( + + ); } } else { if (report.category === 'other') { - message = ; + message = ( + + ); } else { - message = ; + message = ( + + ); } } return ( - -
+
+
+ +
@@ -55,7 +112,11 @@ export const NotificationAdminReport: React.FC<{
- {report.comment.length > 0 &&
“{report.comment}”
} + {report.comment.length > 0 && ( +
+ “{report.comment}” +
+ )}
); diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_admin_sign_up.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_admin_sign_up.tsx index 7a01c12c22..58bfe3ee52 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_admin_sign_up.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_admin_sign_up.tsx @@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl'; import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react'; import type { NotificationGroupAdminSignUp } from 'mastodon/models/notification_group'; +import type { LabelRenderer } from './notification_group_with_status'; import { NotificationGroupWithStatus } from './notification_group_with_status'; -const labelRenderer = values => - ; +const labelRenderer: LabelRenderer = (values) => ( + +); export const NotificationAdminSignUp: React.FC<{ notification: NotificationGroupAdminSignUp; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_favourite.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_favourite.tsx index 8341646b30..924a9c3a6c 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_favourite.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_favourite.tsx @@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl'; import StarIcon from '@/material-icons/400-24px/star-fill.svg?react'; import type { NotificationGroupFavourite } from 'mastodon/models/notification_group'; +import type { LabelRenderer } from './notification_group_with_status'; import { NotificationGroupWithStatus } from './notification_group_with_status'; -const labelRenderer = values => - ; +const labelRenderer: LabelRenderer = (values) => ( + +); export const NotificationFavourite: React.FC<{ notification: NotificationGroupFavourite; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_follow.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_follow.tsx index b7c74b8ae2..057b53be92 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_follow.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_follow.tsx @@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl'; import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react'; import type { NotificationGroupFollow } from 'mastodon/models/notification_group'; +import type { LabelRenderer } from './notification_group_with_status'; import { NotificationGroupWithStatus } from './notification_group_with_status'; -const labelRenderer = values => - ; +const labelRenderer: LabelRenderer = (values) => ( + +); export const NotificationFollow: React.FC<{ notification: NotificationGroupFollow; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_follow_request.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_follow_request.tsx index 98aa53f198..5c08e0535b 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_follow_request.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_follow_request.tsx @@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl'; import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react'; import type { NotificationGroupFollowRequest } from 'mastodon/models/notification_group'; +import type { LabelRenderer } from './notification_group_with_status'; import { NotificationGroupWithStatus } from './notification_group_with_status'; -const labelRenderer = values => - ; +const labelRenderer: LabelRenderer = (values) => ( + +); export const NotificationFollowRequest: React.FC<{ notification: NotificationGroupFollowRequest; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx index fbcae2e920..2ceb5b6aec 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_group_with_status.tsx @@ -2,14 +2,27 @@ import { useMemo } from 'react'; import { FormattedMessage } from 'react-intl'; -import { Icon } from 'mastodon/components/icon'; +import type { IconProp } from 'mastodon/components/icon'; +import { Icon } from 'mastodon/components/icon'; import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; import { AvatarGroup } from './avatar_group'; import { EmbeddedStatus } from './embedded_status'; import { NamesList } from './names_list'; -export const NotificationGroupWithStatus = ({ +export type LabelRenderer = ( + values: Record, +) => JSX.Element; + +export const NotificationGroupWithStatus: React.FC<{ + icon: IconProp; + statusId?: string; + count: number; + accountIds: string[]; + timestamp: string; + labelRenderer: LabelRenderer; + type: string; +}> = ({ icon, timestamp, accountIds, @@ -18,12 +31,22 @@ export const NotificationGroupWithStatus = ({ labelRenderer, type, }) => { - const label = useMemo(() => - labelRenderer({ name: }), [labelRenderer, accountIds, count]); + const label = useMemo( + () => + labelRenderer({ + name: , + }), + [labelRenderer, accountIds, count], + ); return ( -
-
+
+
+ +
diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_mention.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_mention.tsx index b862a297a4..c9553fa057 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_mention.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_mention.tsx @@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl'; import ReplyIcon from '@/material-icons/400-24px/reply-fill.svg?react'; import type { NotificationGroupMention } from 'mastodon/models/notification_group'; +import type { LabelRenderer } from './notification_group_with_status'; import { NotificationWithStatus } from './notification_with_status'; -const labelRenderer = values => - ; +const labelRenderer: LabelRenderer = (values) => ( + +); export const NotificationMention: React.FC<{ notification: NotificationGroupMention; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_poll.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_poll.tsx index 708b081a6c..ed5701b86b 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_poll.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_poll.tsx @@ -5,8 +5,12 @@ import type { NotificationGroupPoll } from 'mastodon/models/notification_group'; import { NotificationWithStatus } from './notification_with_status'; -const labelRenderer = values => - ; +const labelRenderer = () => ( + +); export const NotificationPoll: React.FC<{ notification: NotificationGroupPoll; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_reblog.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_reblog.tsx index d0de0590ff..bff33b4d19 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_reblog.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_reblog.tsx @@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl'; import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react'; import type { NotificationGroupReblog } from 'mastodon/models/notification_group'; +import type { LabelRenderer } from './notification_group_with_status'; import { NotificationGroupWithStatus } from './notification_group_with_status'; -const labelRenderer = values => - ; +const labelRenderer: LabelRenderer = (values) => ( + +); export const NotificationReblog: React.FC<{ notification: NotificationGroupReblog; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_status.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_status.tsx index c0e7c2c1e9..4f15ab0939 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_status.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_status.tsx @@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl'; import NotificationsActiveIcon from '@/material-icons/400-24px/notifications_active-fill.svg?react'; import type { NotificationGroupStatus } from 'mastodon/models/notification_group'; +import type { LabelRenderer } from './notification_group_with_status'; import { NotificationWithStatus } from './notification_with_status'; -const labelRenderer = values => - ; +const labelRenderer: LabelRenderer = (values) => ( + +); export const NotificationStatus: React.FC<{ notification: NotificationGroupStatus; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_update.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_update.tsx index cfb05685ce..1f0e005581 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_update.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_update.tsx @@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl'; import EditIcon from '@/material-icons/400-24px/edit.svg?react'; import type { NotificationGroupUpdate } from 'mastodon/models/notification_group'; +import type { LabelRenderer } from './notification_group_with_status'; import { NotificationWithStatus } from './notification_with_status'; -const labelRenderer = values => - ; +const labelRenderer: LabelRenderer = (values) => ( + +); export const NotificationUpdate: React.FC<{ notification: NotificationGroupUpdate; diff --git a/app/javascript/mastodon/features/notifications_v2/components/notification_with_status.tsx b/app/javascript/mastodon/features/notifications_v2/components/notification_with_status.tsx index 138e436fb3..8a246467f7 100644 --- a/app/javascript/mastodon/features/notifications_v2/components/notification_with_status.tsx +++ b/app/javascript/mastodon/features/notifications_v2/components/notification_with_status.tsx @@ -1,35 +1,42 @@ import { useMemo } from 'react'; -import { FormattedMessage } from 'react-intl'; - -import { Icon } from 'mastodon/components/icon'; +import type { IconProp } from 'mastodon/components/icon'; +import { Icon } from 'mastodon/components/icon'; import Status from 'mastodon/containers/status_container'; import { NamesList } from './names_list'; +import type { LabelRenderer } from './notification_group_with_status'; - -export const NotificationWithStatus = ({ - icon, - accountIds, - statusId, - count, - labelRenderer, - type, -}) => { - const label = useMemo(() => labelRenderer({ name: }), [labelRenderer, accountIds, count]); +export const NotificationWithStatus: React.FC<{ + type: string; + icon: IconProp; + accountIds: string[]; + statusId: string; + count: number; + labelRenderer: LabelRenderer; +}> = ({ icon, accountIds, statusId, count, labelRenderer, type }) => { + const label = useMemo( + () => + labelRenderer({ + name: , + }), + [labelRenderer, accountIds, count], + ); return ( -
+
-
+
+ +
{label}
- + {/* @ts-expect-error -- is not yet typed */} +
); };