Fix pass at fixing lint issues

Renaud Chaput 2024-06-12 11:35:17 +02:00
parent 9999fdc9fb
commit 7c58b58ad1
No known key found for this signature in database
GPG Key ID: BCFC859D49B46990
15 changed files with 273 additions and 76 deletions

View File

@ -3,8 +3,10 @@ import { Link } from 'react-router-dom';
import { Avatar } from 'mastodon/components/avatar'; import { Avatar } from 'mastodon/components/avatar';
import { useAppSelector } from 'mastodon/store'; import { useAppSelector } from 'mastodon/store';
const AvatarWrapper = ({ accountId }) => { const AvatarWrapper: React.FC<{ accountId: string }> = ({ accountId }) => {
const account = useAppSelector(state => state.getIn(['accounts', accountId])); const account = useAppSelector((state) => state.accounts.get(accountId));
if (!account) return null;
return ( return (
<Link to={`/@${account.get('acct')}`} title={`@${account.get('acct')}`}> <Link to={`/@${account.get('acct')}`} title={`@${account.get('acct')}`}>
@ -13,8 +15,12 @@ const AvatarWrapper = ({ accountId }) => {
); );
}; };
export const AvatarGroup = ({ accountIds }) => ( export const AvatarGroup: React.FC<{ accountIds: string[] }> = ({
accountIds,
}) => (
<div className='notification-group__avatar-group'> <div className='notification-group__avatar-group'>
{accountIds.map(accountId => <AvatarWrapper key={accountId} accountId={accountId} />)} {accountIds.map((accountId) => (
<AvatarWrapper key={accountId} accountId={accountId} />
))}
</div> </div>
); );

View File

@ -1,21 +1,37 @@
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import type { List } from 'immutable';
import BarChart4BarsIcon from '@/material-icons/400-24px/bar_chart_4_bars.svg?react'; 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 PhotoLibraryIcon from '@/material-icons/400-24px/photo_library.svg?react';
import { Avatar } from 'mastodon/components/avatar'; import { Avatar } from 'mastodon/components/avatar';
import { DisplayName } from 'mastodon/components/display_name'; import { DisplayName } from 'mastodon/components/display_name';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import type { Status } from 'mastodon/models/status';
import { useAppSelector } from 'mastodon/store'; import { useAppSelector } from 'mastodon/store';
export const EmbeddedStatus = ({ statusId }) => { export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
const status = useAppSelector(state => state.getIn(['statuses', statusId])); statusId,
const account = useAppSelector(state => state.getIn(['accounts', status?.get('account')])); }) => {
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) { if (!status) {
return null; 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<unknown>
).size;
const content = { __html: contentHtml };
return ( return (
<div className='notification-group__embedded-status'> <div className='notification-group__embedded-status'>
@ -24,12 +40,32 @@ export const EmbeddedStatus = ({ statusId }) => {
<DisplayName account={account} /> <DisplayName account={account} />
</div> </div>
<div className='notification-group__embedded-status__content reply-indicator__content translate' dangerouslySetInnerHTML={content} /> <div
className='notification-group__embedded-status__content reply-indicator__content translate'
dangerouslySetInnerHTML={content}
/>
{(status.get('poll') || status.get('media_attachments').size > 0) && ( {(poll || mediaAttachmentsSize > 0) && (
<div className='notification-group__embedded-status__attachments reply-indicator__attachments'> <div className='notification-group__embedded-status__attachments reply-indicator__attachments'>
{status.get('poll') && <><Icon icon={BarChart4BarsIcon} /><FormattedMessage id='reply_indicator.poll' defaultMessage='Poll' /></>} {!!poll && (
{status.get('media_attachments').size > 0 && <><Icon icon={PhotoLibraryIcon} /><FormattedMessage id='reply_indicator.attachments' defaultMessage='{count, plural, one {# attachment} other {# attachments}}' values={{ count: status.get('media_attachments').size }} /></>} <>
<Icon icon={BarChart4BarsIcon} />
<FormattedMessage
id='reply_indicator.poll'
defaultMessage='Poll'
/>
</>
)}
{mediaAttachmentsSize > 0 && (
<>
<Icon icon={PhotoLibraryIcon} />
<FormattedMessage
id='reply_indicator.attachments'
defaultMessage='{count, plural, one {# attachment} other {# attachments}}'
values={{ count: mediaAttachmentsSize }}
/>
</>
)}
</div> </div>
)} )}
</div> </div>

View File

@ -4,10 +4,22 @@ import { Link } from 'react-router-dom';
import { useAppSelector } from 'mastodon/store'; import { useAppSelector } from 'mastodon/store';
export const NamesList = ({ accountIds, total }) => { export const NamesList: React.FC<{ accountIds: string[]; total: number }> = ({
const lastAccountId = accountIds[0]; accountIds,
const account = useAppSelector(state => state.getIn(['accounts', lastAccountId])); total,
const displayedName = <Link to={`/@${account.get('acct')}`} title={`@${account.get('acct')}`}><bdi dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></Link>; }) => {
const lastAccountId = accountIds[0] ?? '0';
const account = useAppSelector((state) => state.accounts.get(lastAccountId));
if (!account) return null;
const displayedName = (
<Link to={`/@${account.get('acct')}`} title={`@${account.get('acct')}`}>
<bdi
dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }}
/>
</Link>
);
if (total === 1) { if (total === 1) {
return displayedName; return displayedName;

View File

@ -8,44 +8,101 @@ import { useAppSelector } from 'mastodon/store';
import { NamesList } from './names_list'; import { NamesList } from './names_list';
// This needs to be kept in sync with app/models/report.rb // This needs to be kept in sync with app/models/report.rb
const messages = defineMessages({ 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' }, spam: { id: 'report_notification.categories.spam', defaultMessage: 'Spam' },
legal: { id: 'report_notification.categories.legal', defaultMessage: 'Legal' }, legal: {
violation: { id: 'report_notification.categories.violation', defaultMessage: 'Rule violation' }, id: 'report_notification.categories.legal',
defaultMessage: 'Legal',
},
violation: {
id: 'report_notification.categories.violation',
defaultMessage: 'Rule violation',
},
}); });
export const NotificationAdminReport: React.FC<{ export const NotificationAdminReport: React.FC<{
notification: NotificationGroupAdminReport; notification: NotificationGroupAdminReport;
}> = ({ notification, notification: { report } }) => { }> = ({ notification, notification: { report } }) => {
const intl = useIntl(); const intl = useIntl();
const targetAccount = useAppSelector(state => state.getIn(['accounts', report.target_account.id])); const targetAccount = useAppSelector((state) =>
const account = useAppSelector(state => state.getIn(['accounts', notification.sampleAccountsIds[0]])); state.getIn(['accounts', report.target_account.id]),
const values = { name: <bdi dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} />, target: <bdi dangerouslySetInnerHTML={{ __html: targetAccount.get('display_name_html') }} />, category: intl.formatMessage(messages[report.category]), count: report.status_ids.length }; );
const account = useAppSelector((state) =>
state.getIn(['accounts', notification.sampleAccountsIds[0]]),
);
const values = {
name: (
<bdi
dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }}
/>
),
target: (
<bdi
dangerouslySetInnerHTML={{
__html: targetAccount.get('display_name_html'),
}}
/>
),
category: intl.formatMessage(messages[report.category]),
count: report.status_ids.length,
};
let message; let message;
if (report.status_ids.length > 0) { if (report.status_ids.length > 0) {
if (report.category === 'other') { if (report.category === 'other') {
message = <FormattedMessage id='notification.admin.report_account_other' defaultMessage='{name} reported {count, plural, one {one post} other {# posts}} from {target}' values={values} />; message = (
<FormattedMessage
id='notification.admin.report_account_other'
defaultMessage='{name} reported {count, plural, one {one post} other {# posts}} from {target}'
values={values}
/>
);
} else { } else {
message = <FormattedMessage id='notification.admin.report_account' defaultMessage='{name} reported {count, plural, one {one post} other {# posts}} from {target} for {category}' values={values} />; message = (
<FormattedMessage
id='notification.admin.report_account'
defaultMessage='{name} reported {count, plural, one {one post} other {# posts}} from {target} for {category}'
values={values}
/>
);
} }
} else { } else {
if (report.category === 'other') { if (report.category === 'other') {
message = <FormattedMessage id='notification.admin.report_statuses_other' defaultMessage='{name} reported {target}' values={values} />; message = (
<FormattedMessage
id='notification.admin.report_statuses_other'
defaultMessage='{name} reported {target}'
values={values}
/>
);
} else { } else {
message = <FormattedMessage id='notification.admin.report_statuses' defaultMessage='{name} reported {target} for {category}' values={values} />; message = (
<FormattedMessage
id='notification.admin.report_statuses'
defaultMessage='{name} reported {target} for {category}'
values={values}
/>
);
} }
} }
return ( return (
<a href={`/admin/reports/${report.id}`} target='_blank' rel='noopener noreferrer' className='notification-group notification-group--link notification-group--admin-report focusable' tabIndex='0'> <a
<div className='notification-group__icon'><Icon id='flag' icon={FlagIcon} /></div> href={`/admin/reports/${report.id}`}
target='_blank'
rel='noopener noreferrer'
className='notification-group notification-group--link notification-group--admin-report focusable'
tabIndex={0}
>
<div className='notification-group__icon'>
<Icon id='flag' icon={FlagIcon} />
</div>
<div className='notification-group__main'> <div className='notification-group__main'>
<div className='notification-group__main__header'> <div className='notification-group__main__header'>
@ -55,7 +112,11 @@ export const NotificationAdminReport: React.FC<{
</div> </div>
</div> </div>
{report.comment.length > 0 && <div className='notification-group__embedded-status__content'>{report.comment}</div>} {report.comment.length > 0 && (
<div className='notification-group__embedded-status__content'>
{report.comment}
</div>
)}
</div> </div>
</a> </a>
); );

View File

@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl';
import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react'; import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
import type { NotificationGroupAdminSignUp } from 'mastodon/models/notification_group'; import type { NotificationGroupAdminSignUp } from 'mastodon/models/notification_group';
import type { LabelRenderer } from './notification_group_with_status';
import { NotificationGroupWithStatus } from './notification_group_with_status'; import { NotificationGroupWithStatus } from './notification_group_with_status';
const labelRenderer = values => const labelRenderer: LabelRenderer = (values) => (
<FormattedMessage id='notification.admin.sign_up' defaultMessage='{name} signed up' values={values} />; <FormattedMessage
id='notification.admin.sign_up'
defaultMessage='{name} signed up'
values={values}
/>
);
export const NotificationAdminSignUp: React.FC<{ export const NotificationAdminSignUp: React.FC<{
notification: NotificationGroupAdminSignUp; notification: NotificationGroupAdminSignUp;

View File

@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl';
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react'; import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import type { NotificationGroupFavourite } from 'mastodon/models/notification_group'; import type { NotificationGroupFavourite } from 'mastodon/models/notification_group';
import type { LabelRenderer } from './notification_group_with_status';
import { NotificationGroupWithStatus } from './notification_group_with_status'; import { NotificationGroupWithStatus } from './notification_group_with_status';
const labelRenderer = values => const labelRenderer: LabelRenderer = (values) => (
<FormattedMessage id='notification.favourite' defaultMessage='{name} favorited your status' values={values} />; <FormattedMessage
id='notification.favourite'
defaultMessage='{name} favorited your status'
values={values}
/>
);
export const NotificationFavourite: React.FC<{ export const NotificationFavourite: React.FC<{
notification: NotificationGroupFavourite; notification: NotificationGroupFavourite;

View File

@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl';
import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react'; import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
import type { NotificationGroupFollow } from 'mastodon/models/notification_group'; import type { NotificationGroupFollow } from 'mastodon/models/notification_group';
import type { LabelRenderer } from './notification_group_with_status';
import { NotificationGroupWithStatus } from './notification_group_with_status'; import { NotificationGroupWithStatus } from './notification_group_with_status';
const labelRenderer = values => const labelRenderer: LabelRenderer = (values) => (
<FormattedMessage id='notification.follow' defaultMessage='{name} followed you' values={values} />; <FormattedMessage
id='notification.follow'
defaultMessage='{name} followed you'
values={values}
/>
);
export const NotificationFollow: React.FC<{ export const NotificationFollow: React.FC<{
notification: NotificationGroupFollow; notification: NotificationGroupFollow;

View File

@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl';
import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react'; import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
import type { NotificationGroupFollowRequest } from 'mastodon/models/notification_group'; import type { NotificationGroupFollowRequest } from 'mastodon/models/notification_group';
import type { LabelRenderer } from './notification_group_with_status';
import { NotificationGroupWithStatus } from './notification_group_with_status'; import { NotificationGroupWithStatus } from './notification_group_with_status';
const labelRenderer = values => const labelRenderer: LabelRenderer = (values) => (
<FormattedMessage id='notification.follow_request' defaultMessage='{name} has requested to follow you' values={values} />; <FormattedMessage
id='notification.follow_request'
defaultMessage='{name} has requested to follow you'
values={values}
/>
);
export const NotificationFollowRequest: React.FC<{ export const NotificationFollowRequest: React.FC<{
notification: NotificationGroupFollowRequest; notification: NotificationGroupFollowRequest;

View File

@ -2,6 +2,7 @@ import { useMemo } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import type { IconProp } from 'mastodon/components/icon';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
@ -9,7 +10,19 @@ import { AvatarGroup } from './avatar_group';
import { EmbeddedStatus } from './embedded_status'; import { EmbeddedStatus } from './embedded_status';
import { NamesList } from './names_list'; import { NamesList } from './names_list';
export const NotificationGroupWithStatus = ({ export type LabelRenderer = (
values: Record<string, React.ReactNode>,
) => JSX.Element;
export const NotificationGroupWithStatus: React.FC<{
icon: IconProp;
statusId?: string;
count: number;
accountIds: string[];
timestamp: string;
labelRenderer: LabelRenderer;
type: string;
}> = ({
icon, icon,
timestamp, timestamp,
accountIds, accountIds,
@ -18,12 +31,22 @@ export const NotificationGroupWithStatus = ({
labelRenderer, labelRenderer,
type, type,
}) => { }) => {
const label = useMemo(() => const label = useMemo(
labelRenderer({ name: <NamesList accountIds={accountIds} total={count} /> }), [labelRenderer, accountIds, count]); () =>
labelRenderer({
name: <NamesList accountIds={accountIds} total={count} />,
}),
[labelRenderer, accountIds, count],
);
return ( return (
<div className={`notification-group focusable notification-group--${type}`} tabIndex='0'> <div
<div className='notification-group__icon'><Icon icon={icon} /></div> className={`notification-group focusable notification-group--${type}`}
tabIndex='0'
>
<div className='notification-group__icon'>
<Icon icon={icon} />
</div>
<div className='notification-group__main'> <div className='notification-group__main'>
<div className='notification-group__main__header'> <div className='notification-group__main__header'>

View File

@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl';
import ReplyIcon from '@/material-icons/400-24px/reply-fill.svg?react'; import ReplyIcon from '@/material-icons/400-24px/reply-fill.svg?react';
import type { NotificationGroupMention } from 'mastodon/models/notification_group'; import type { NotificationGroupMention } from 'mastodon/models/notification_group';
import type { LabelRenderer } from './notification_group_with_status';
import { NotificationWithStatus } from './notification_with_status'; import { NotificationWithStatus } from './notification_with_status';
const labelRenderer = values => const labelRenderer: LabelRenderer = (values) => (
<FormattedMessage id='notification.mention' defaultMessage='{name} mentioned you' values={values} />; <FormattedMessage
id='notification.mention'
defaultMessage='{name} mentioned you'
values={values}
/>
);
export const NotificationMention: React.FC<{ export const NotificationMention: React.FC<{
notification: NotificationGroupMention; notification: NotificationGroupMention;

View File

@ -5,8 +5,12 @@ import type { NotificationGroupPoll } from 'mastodon/models/notification_group';
import { NotificationWithStatus } from './notification_with_status'; import { NotificationWithStatus } from './notification_with_status';
const labelRenderer = values => const labelRenderer = () => (
<FormattedMessage id='notification.poll' defaultMessage='A poll you have voted in has ended' />; <FormattedMessage
id='notification.poll'
defaultMessage='A poll you have voted in has ended'
/>
);
export const NotificationPoll: React.FC<{ export const NotificationPoll: React.FC<{
notification: NotificationGroupPoll; notification: NotificationGroupPoll;

View File

@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react'; import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';
import type { NotificationGroupReblog } from 'mastodon/models/notification_group'; import type { NotificationGroupReblog } from 'mastodon/models/notification_group';
import type { LabelRenderer } from './notification_group_with_status';
import { NotificationGroupWithStatus } from './notification_group_with_status'; import { NotificationGroupWithStatus } from './notification_group_with_status';
const labelRenderer = values => const labelRenderer: LabelRenderer = (values) => (
<FormattedMessage id='notification.reblog' defaultMessage='{name} boosted your status' values={values} />; <FormattedMessage
id='notification.reblog'
defaultMessage='{name} boosted your status'
values={values}
/>
);
export const NotificationReblog: React.FC<{ export const NotificationReblog: React.FC<{
notification: NotificationGroupReblog; notification: NotificationGroupReblog;

View File

@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl';
import NotificationsActiveIcon from '@/material-icons/400-24px/notifications_active-fill.svg?react'; import NotificationsActiveIcon from '@/material-icons/400-24px/notifications_active-fill.svg?react';
import type { NotificationGroupStatus } from 'mastodon/models/notification_group'; import type { NotificationGroupStatus } from 'mastodon/models/notification_group';
import type { LabelRenderer } from './notification_group_with_status';
import { NotificationWithStatus } from './notification_with_status'; import { NotificationWithStatus } from './notification_with_status';
const labelRenderer = values => const labelRenderer: LabelRenderer = (values) => (
<FormattedMessage id='notification.status' defaultMessage='{name} just posted' values={values} />; <FormattedMessage
id='notification.status'
defaultMessage='{name} just posted'
values={values}
/>
);
export const NotificationStatus: React.FC<{ export const NotificationStatus: React.FC<{
notification: NotificationGroupStatus; notification: NotificationGroupStatus;

View File

@ -3,10 +3,16 @@ import { FormattedMessage } from 'react-intl';
import EditIcon from '@/material-icons/400-24px/edit.svg?react'; import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import type { NotificationGroupUpdate } from 'mastodon/models/notification_group'; import type { NotificationGroupUpdate } from 'mastodon/models/notification_group';
import type { LabelRenderer } from './notification_group_with_status';
import { NotificationWithStatus } from './notification_with_status'; import { NotificationWithStatus } from './notification_with_status';
const labelRenderer = values => const labelRenderer: LabelRenderer = (values) => (
<FormattedMessage id='notification.update' defaultMessage='{name} edited a post' values={values} />; <FormattedMessage
id='notification.update'
defaultMessage='{name} edited a post'
values={values}
/>
);
export const NotificationUpdate: React.FC<{ export const NotificationUpdate: React.FC<{
notification: NotificationGroupUpdate; notification: NotificationGroupUpdate;

View File

@ -1,35 +1,42 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { FormattedMessage } from 'react-intl'; import type { IconProp } from 'mastodon/components/icon';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import Status from 'mastodon/containers/status_container'; import Status from 'mastodon/containers/status_container';
import { NamesList } from './names_list'; import { NamesList } from './names_list';
import type { LabelRenderer } from './notification_group_with_status';
export const NotificationWithStatus: React.FC<{
export const NotificationWithStatus = ({ type: string;
icon, icon: IconProp;
accountIds, accountIds: string[];
statusId, statusId: string;
count, count: number;
labelRenderer, labelRenderer: LabelRenderer;
type, }> = ({ icon, accountIds, statusId, count, labelRenderer, type }) => {
}) => { const label = useMemo(
const label = useMemo(() => labelRenderer({ name: <NamesList accountIds={accountIds} total={count} /> }), [labelRenderer, accountIds, count]); () =>
labelRenderer({
name: <NamesList accountIds={accountIds} total={count} />,
}),
[labelRenderer, accountIds, count],
);
return ( return (
<div className={`notification-ungrouped focusable notification-ungrouped--${type}`} tabIndex='0'> <div
className={`notification-ungrouped focusable notification-ungrouped--${type}`}
tabIndex='0'
>
<div className='notification-ungrouped__header'> <div className='notification-ungrouped__header'>
<div className='notification-ungrouped__header__icon'><Icon icon={icon} /></div> <div className='notification-ungrouped__header__icon'>
<Icon icon={icon} />
</div>
{label} {label}
</div> </div>
<Status {/* @ts-expect-error -- <Status> is not yet typed */}
id={statusId} <Status id={statusId} contextType='notifications' withDismiss />
contextType='notifications'
withDismiss
/>
</div> </div>
); );
}; };