Fix the remaining lint issues

Renaud Chaput 2024-06-12 15:51:06 +02:00
parent be36081f54
commit 8805b9d1bd
No known key found for this signature in database
GPG Key ID: BCFC859D49B46990
21 changed files with 253 additions and 77 deletions

View File

@ -17,9 +17,13 @@ export const fetchNotifications = createDataLoadingThunk(
fetchedAccounts.push(...notification.sample_accounts); fetchedAccounts.push(...notification.sample_accounts);
} }
// if (notification.type === 'admin.report') { if (notification.type === 'admin.report') {
// fetchedAccounts.push(...notification.report.target_account); fetchedAccounts.push(notification.report.target_account);
// } }
if (notification.type === 'moderation_warning') {
fetchedAccounts.push(notification.moderation_warning.target_account);
}
if ('status' in notification) { if ('status' in notification) {
fetchedStatuses.push(notification.status); fetchedStatuses.push(notification.status);

View File

@ -1,6 +1,9 @@
// See app/serializers/rest/notification_group_serializer.rb // See app/serializers/rest/notification_group_serializer.rb
import type { AccountWarningAction } from 'mastodon/models/notification_group';
import type { ApiAccountJSON } from './accounts'; import type { ApiAccountJSON } from './accounts';
import type { ApiReportJSON } from './reports';
import type { ApiStatusJSON } from './statuses'; import type { ApiStatusJSON } from './statuses';
// See app/model/notification.rb // See app/model/notification.rb
@ -26,7 +29,7 @@ export interface BaseNotificationGroupJSON {
notifications_count: number; notifications_count: number;
type: NotificationType; type: NotificationType;
sample_accounts: ApiAccountJSON[]; sample_accounts: ApiAccountJSON[];
latest_page_notification_at?: string; latest_page_notification_at: string; // FIXME: This will only be present if the notification group is returned in a paginated list, not requested directly
page_min_id?: string; page_min_id?: string;
page_max_id?: string; page_max_id?: string;
} }
@ -38,19 +41,39 @@ interface NotificationGroupWithStatusJSON extends BaseNotificationGroupJSON {
interface ReportNotificationGroupJSON extends BaseNotificationGroupJSON { interface ReportNotificationGroupJSON extends BaseNotificationGroupJSON {
type: 'admin.report'; type: 'admin.report';
report: unknown; report: ApiReportJSON;
}
export interface ApiAccountWarningJSON {
id: string;
action: AccountWarningAction;
text: string;
status_ids: string[];
created_at: string;
target_account: ApiAccountJSON;
appeal: unknown;
} }
interface ModerationWarningNotificationGroupJSON interface ModerationWarningNotificationGroupJSON
extends BaseNotificationGroupJSON { extends BaseNotificationGroupJSON {
type: 'moderation_warning'; type: 'moderation_warning';
moderation_warning: unknown; moderation_warning: ApiAccountWarningJSON;
}
export interface ApiAccountRelationshipSeveranceEventJSON {
id: string;
type: 'account_suspension' | 'domain_block' | 'user_domain_block';
purged: boolean;
target_name: string;
followers_count: number;
following_count: number;
created_at: string;
} }
interface AccountRelationshipSeveranceNotificationGroupJSON interface AccountRelationshipSeveranceNotificationGroupJSON
extends BaseNotificationGroupJSON { extends BaseNotificationGroupJSON {
type: 'severed_relationships'; type: 'severed_relationships';
account_relationship_severance_event: unknown; event: ApiAccountRelationshipSeveranceEventJSON;
} }
export type NotificationGroupJSON = export type NotificationGroupJSON =

View File

@ -0,0 +1,16 @@
import type { ApiAccountJSON } from './accounts';
export type ReportCategory = 'other' | 'spam' | 'legal' | 'violation';
export interface ApiReportJSON {
id: string;
action_taken: unknown;
action_taken_at: unknown;
category: ReportCategory;
comment: string;
forwarded: boolean;
created_at: string;
status_ids: string[];
rule_ids: string[];
target_account: ApiAccountJSON;
}

View File

@ -4,6 +4,7 @@ import classNames from 'classnames';
import GavelIcon from '@/material-icons/400-24px/gavel.svg?react'; import GavelIcon from '@/material-icons/400-24px/gavel.svg?react';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import type { AccountWarningAction } from 'mastodon/models/notification_group';
// This needs to be kept in sync with app/models/account_warning.rb // This needs to be kept in sync with app/models/account_warning.rb
const messages = defineMessages({ const messages = defineMessages({
@ -38,17 +39,10 @@ const messages = defineMessages({
}); });
interface Props { interface Props {
action: action: AccountWarningAction;
| 'none'
| 'disable'
| 'mark_statuses_as_sensitive'
| 'delete_statuses'
| 'sensitive'
| 'silence'
| 'suspend';
id: string; id: string;
hidden: boolean; hidden?: boolean;
unread: boolean; unread?: boolean;
} }
export const ModerationWarning: React.FC<Props> = ({ export const ModerationWarning: React.FC<Props> = ({
@ -70,7 +64,7 @@ export const ModerationWarning: React.FC<Props> = ({
'notification-group notification-group--link notification-group--moderation-warning focusable', 'notification-group notification-group--link notification-group--moderation-warning focusable',
{ 'notification-group--unread': unread }, { 'notification-group--unread': unread },
)} )}
tabIndex='0' tabIndex={0}
> >
<div className='notification-group__icon'> <div className='notification-group__icon'>
<Icon id='warning' icon={GavelIcon} /> <Icon id='warning' icon={GavelIcon} />

View File

@ -1,8 +1,10 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useHistory } from 'react-router-dom';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import type { List } from 'immutable'; import { useHistory } from 'react-router-dom';
import type { List as ImmutableList, RecordOf } 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';
@ -11,8 +13,11 @@ 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 type { Status } from 'mastodon/models/status';
import { useAppSelector } from 'mastodon/store'; import { useAppSelector } from 'mastodon/store';
import { EmbeddedStatusContent } from './embedded_status_content'; import { EmbeddedStatusContent } from './embedded_status_content';
export type Mention = RecordOf<{ url: string; acct: string }>;
export const EmbeddedStatus: React.FC<{ statusId: string }> = ({ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
statusId, statusId,
}) => { }) => {
@ -27,6 +32,8 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
); );
const handleClick = useCallback(() => { const handleClick = useCallback(() => {
if (!account) return;
history.push(`/@${account.acct}/${statusId}`); history.push(`/@${account.acct}/${statusId}`);
}, [statusId, account, history]); }, [statusId, account, history]);
@ -38,9 +45,9 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
const contentHtml = status.get('contentHtml') as string; const contentHtml = status.get('contentHtml') as string;
const poll = status.get('poll'); const poll = status.get('poll');
const language = status.get('language') as string; const language = status.get('language') as string;
const mentions = status.get('mentions'); const mentions = status.get('mentions') as ImmutableList<Mention>;
const mediaAttachmentsSize = ( const mediaAttachmentsSize = (
status.get('media_attachments') as List<unknown> status.get('media_attachments') as ImmutableList<unknown>
).size; ).size;
return ( return (
@ -62,7 +69,7 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
<div className='notification-group__embedded-status__attachments reply-indicator__attachments'> <div className='notification-group__embedded-status__attachments reply-indicator__attachments'>
{!!poll && ( {!!poll && (
<> <>
<Icon icon={BarChart4BarsIcon} /> <Icon icon={BarChart4BarsIcon} id='bar-chart-4-bars' />
<FormattedMessage <FormattedMessage
id='reply_indicator.poll' id='reply_indicator.poll'
defaultMessage='Poll' defaultMessage='Poll'
@ -71,7 +78,7 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
)} )}
{mediaAttachmentsSize > 0 && ( {mediaAttachmentsSize > 0 && (
<> <>
<Icon icon={PhotoLibraryIcon} /> <Icon icon={PhotoLibraryIcon} id='photo-library' />
<FormattedMessage <FormattedMessage
id='reply_indicator.attachments' id='reply_indicator.attachments'
defaultMessage='{count, plural, one {# attachment} other {# attachments}}' defaultMessage='{count, plural, one {# attachment} other {# attachments}}'

View File

@ -1,14 +1,29 @@
import { useCallback, useRef } from 'react'; import { useCallback, useRef } from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
const handleMentionClick = (history: unknown, mention: unknown, e: Event) => { import type { List } from 'immutable';
import type { History } from 'history';
import type { Mention } from './embedded_status';
const handleMentionClick = (
history: History,
mention: Mention,
e: MouseEvent,
) => {
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
e.preventDefault(); e.preventDefault();
history.push(`/@${mention.get('acct')}`); history.push(`/@${mention.get('acct')}`);
} }
}; };
const handleHashtagClick = (history: unknown, hashtag: string, e: Event) => { const handleHashtagClick = (
history: History,
hashtag: string,
e: MouseEvent,
) => {
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
e.preventDefault(); e.preventDefault();
history.push(`/tags/${hashtag.replace(/^#/, '')}`); history.push(`/tags/${hashtag.replace(/^#/, '')}`);
@ -17,22 +32,22 @@ const handleHashtagClick = (history: unknown, hashtag: string, e: Event) => {
export const EmbeddedStatusContent: React.FC<{ export const EmbeddedStatusContent: React.FC<{
content: string; content: string;
mentions: unknown; mentions: List<Mention>;
language: string; language: string;
onClick?: unknown; onClick?: () => void;
className?: string; className?: string;
}> = ({ content, mentions, language, onClick, className }) => { }> = ({ content, mentions, language, onClick, className }) => {
const clickCoordinatesRef = useRef(); const clickCoordinatesRef = useRef<[number, number] | null>();
const history = useHistory(); const history = useHistory();
const handleMouseDown = useCallback( const handleMouseDown = useCallback<React.MouseEventHandler<HTMLDivElement>>(
({ clientX, clientY }) => { ({ clientX, clientY }) => {
clickCoordinatesRef.current = [clientX, clientY]; clickCoordinatesRef.current = [clientX, clientY];
}, },
[clickCoordinatesRef], [clickCoordinatesRef],
); );
const handleMouseUp = useCallback( const handleMouseUp = useCallback<React.MouseEventHandler<HTMLDivElement>>(
({ clientX, clientY, target, button }) => { ({ clientX, clientY, target, button }) => {
const [startX, startY] = clickCoordinatesRef.current ?? [0, 0]; const [startX, startY] = clickCoordinatesRef.current ?? [0, 0];
const [deltaX, deltaY] = [ const [deltaX, deltaY] = [
@ -40,7 +55,7 @@ export const EmbeddedStatusContent: React.FC<{
Math.abs(clientY - startY), Math.abs(clientY - startY),
]; ];
let element = target; let element: HTMLDivElement | null = target as HTMLDivElement;
while (element) { while (element) {
if ( if (
@ -51,7 +66,7 @@ export const EmbeddedStatusContent: React.FC<{
return; return;
} }
element = element.parentNode; element = element.parentNode as HTMLDivElement | null;
} }
if (deltaX + deltaY < 5 && button === 0 && onClick) { if (deltaX + deltaY < 5 && button === 0 && onClick) {
@ -63,29 +78,39 @@ export const EmbeddedStatusContent: React.FC<{
[clickCoordinatesRef, onClick], [clickCoordinatesRef, onClick],
); );
const handleMouseEnter = useCallback(({ currentTarget }) => { const handleMouseEnter = useCallback<React.MouseEventHandler<HTMLDivElement>>(
const emojis = currentTarget.querySelectorAll('.custom-emoji'); ({ currentTarget }) => {
const emojis =
currentTarget.querySelectorAll<HTMLImageElement>('.custom-emoji');
for (const emoji of emojis) { for (const emoji of emojis) {
emoji.src = emoji.getAttribute('data-original'); const newSrc = emoji.getAttribute('data-original');
if (newSrc) emoji.src = newSrc;
} }
}, []); },
[],
);
const handleMouseLeave = useCallback(({ currentTarget }) => { const handleMouseLeave = useCallback<React.MouseEventHandler<HTMLDivElement>>(
const emojis = currentTarget.querySelectorAll('.custom-emoji'); ({ currentTarget }) => {
const emojis =
currentTarget.querySelectorAll<HTMLImageElement>('.custom-emoji');
for (const emoji of emojis) { for (const emoji of emojis) {
emoji.src = emoji.getAttribute('data-static'); const newSrc = emoji.getAttribute('data-static');
if (newSrc) emoji.src = newSrc;
} }
}, []); },
[],
);
const handleContentRef = useCallback( const handleContentRef = useCallback(
(node) => { (node: HTMLDivElement | null) => {
if (!node) { if (!node) {
return; return;
} }
const links = node.querySelectorAll('a'); const links = node.querySelectorAll<HTMLAnchorElement>('a');
for (const link of links) { for (const link of links) {
if (link.classList.contains('status-link')) { if (link.classList.contains('status-link')) {
@ -105,12 +130,8 @@ export const EmbeddedStatusContent: React.FC<{
link.setAttribute('title', `@${mention.get('acct')}`); link.setAttribute('title', `@${mention.get('acct')}`);
link.setAttribute('href', `/@${mention.get('acct')}`); link.setAttribute('href', `/@${mention.get('acct')}`);
} else if ( } else if (
link.textContent[0] === '#' || link.textContent?.[0] === '#' ||
(link.previousSibling && link.previousSibling?.textContent?.endsWith('#')
link.previousSibling.textContent &&
link.previousSibling.textContent[
link.previousSibling.textContent.length - 1
] === '#')
) { ) {
link.addEventListener( link.addEventListener(
'click', 'click',
@ -130,7 +151,7 @@ export const EmbeddedStatusContent: React.FC<{
return ( return (
<div <div
role='button' role='button'
tabIndex='0' tabIndex={0}
className={className} className={className}
ref={handleContentRef} ref={handleContentRef}
lang={language} lang={language}

View File

@ -30,15 +30,18 @@ const messages = defineMessages({
export const NotificationAdminReport: React.FC<{ export const NotificationAdminReport: React.FC<{
notification: NotificationGroupAdminReport; notification: NotificationGroupAdminReport;
unread: boolean; unread?: boolean;
}> = ({ notification, notification: { report }, unread }) => { }> = ({ notification, notification: { report }, unread }) => {
const intl = useIntl(); const intl = useIntl();
const targetAccount = useAppSelector((state) => const targetAccount = useAppSelector((state) =>
state.getIn(['accounts', report.target_account.id]), state.accounts.get(report.targetAccountId),
); );
const account = useAppSelector((state) => const account = useAppSelector((state) =>
state.getIn(['accounts', notification.sampleAccountsIds[0]]), state.accounts.get(notification.sampleAccountsIds[0] ?? '0'),
); );
if (!account || !targetAccount) return null;
const values = { const values = {
name: ( name: (
<bdi <bdi

View File

@ -21,6 +21,7 @@ export const NotificationAdminSignUp: React.FC<{
<NotificationGroupWithStatus <NotificationGroupWithStatus
type='admin-sign-up' type='admin-sign-up'
icon={PersonAddIcon} icon={PersonAddIcon}
iconId='person-add'
accountIds={notification.sampleAccountsIds} accountIds={notification.sampleAccountsIds}
timestamp={notification.latest_page_notification_at} timestamp={notification.latest_page_notification_at}
count={notification.notifications_count} count={notification.notifications_count}

View File

@ -21,6 +21,7 @@ export const NotificationFavourite: React.FC<{
<NotificationGroupWithStatus <NotificationGroupWithStatus
type='favourite' type='favourite'
icon={StarIcon} icon={StarIcon}
iconId='star'
accountIds={notification.sampleAccountsIds} accountIds={notification.sampleAccountsIds}
statusId={notification.statusId} statusId={notification.statusId}
timestamp={notification.latest_page_notification_at} timestamp={notification.latest_page_notification_at}

View File

@ -21,6 +21,7 @@ export const NotificationFollow: React.FC<{
<NotificationGroupWithStatus <NotificationGroupWithStatus
type='follow' type='follow'
icon={PersonAddIcon} icon={PersonAddIcon}
iconId='person-add'
accountIds={notification.sampleAccountsIds} accountIds={notification.sampleAccountsIds}
timestamp={notification.latest_page_notification_at} timestamp={notification.latest_page_notification_at}
count={notification.notifications_count} count={notification.notifications_count}

View File

@ -21,6 +21,7 @@ export const NotificationFollowRequest: React.FC<{
<NotificationGroupWithStatus <NotificationGroupWithStatus
type='follow-request' type='follow-request'
icon={PersonAddIcon} icon={PersonAddIcon}
iconId='person-add'
accountIds={notification.sampleAccountsIds} accountIds={notification.sampleAccountsIds}
timestamp={notification.latest_page_notification_at} timestamp={notification.latest_page_notification_at}
count={notification.notifications_count} count={notification.notifications_count}

View File

@ -21,8 +21,8 @@ import { NotificationUpdate } from './notification_update';
export const NotificationGroup: React.FC<{ export const NotificationGroup: React.FC<{
notificationGroupId: NotificationGroupModel['group_key']; notificationGroupId: NotificationGroupModel['group_key'];
unread: boolean; unread: boolean;
onMoveUp: unknown; onMoveUp: (groupId: string) => void;
onMoveDown: unknown; onMoveDown: (groupId: string) => void;
}> = ({ notificationGroupId, unread, onMoveUp, onMoveDown }) => { }> = ({ notificationGroupId, unread, onMoveUp, onMoveDown }) => {
const notificationGroup = useAppSelector((state) => const notificationGroup = useAppSelector((state) =>
state.notificationsGroups.groups.find( state.notificationsGroups.groups.find(

View File

@ -16,6 +16,7 @@ export type LabelRenderer = (
export const NotificationGroupWithStatus: React.FC<{ export const NotificationGroupWithStatus: React.FC<{
icon: IconProp; icon: IconProp;
iconId: string;
statusId?: string; statusId?: string;
count: number; count: number;
accountIds: string[]; accountIds: string[];
@ -25,6 +26,7 @@ export const NotificationGroupWithStatus: React.FC<{
unread: boolean; unread: boolean;
}> = ({ }> = ({
icon, icon,
iconId,
timestamp, timestamp,
accountIds, accountIds,
count, count,
@ -48,10 +50,10 @@ export const NotificationGroupWithStatus: React.FC<{
`notification-group focusable notification-group--${type}`, `notification-group focusable notification-group--${type}`,
{ 'notification-group--unread': unread }, { 'notification-group--unread': unread },
)} )}
tabIndex='0' tabIndex={0}
> >
<div className='notification-group__icon'> <div className='notification-group__icon'>
<Icon icon={icon} /> <Icon icon={icon} id={iconId} />
</div> </div>
<div className='notification-group__main'> <div className='notification-group__main'>

View File

@ -21,6 +21,7 @@ export const NotificationMention: React.FC<{
<NotificationWithStatus <NotificationWithStatus
type='mention' type='mention'
icon={ReplyIcon} icon={ReplyIcon}
iconId='reply'
accountIds={notification.sampleAccountsIds} accountIds={notification.sampleAccountsIds}
count={notification.notifications_count} count={notification.notifications_count}
statusId={notification.statusId} statusId={notification.statusId}

View File

@ -4,6 +4,10 @@ import type { NotificationGroupModerationWarning } from 'mastodon/models/notific
export const NotificationModerationWarning: React.FC<{ export const NotificationModerationWarning: React.FC<{
notification: NotificationGroupModerationWarning; notification: NotificationGroupModerationWarning;
unread: boolean; unread: boolean;
}> = ({ notification: { event }, unread }) => ( }> = ({ notification: { moderationWarning }, unread }) => (
<ModerationWarning action={event.action} id={event.id} unread={unread} /> <ModerationWarning
action={moderationWarning.action}
id={moderationWarning.id}
unread={unread}
/>
); );

View File

@ -27,6 +27,7 @@ export const NotificationPoll: React.FC<{
<NotificationWithStatus <NotificationWithStatus
type='poll' type='poll'
icon={BarChart4BarsIcon} icon={BarChart4BarsIcon}
iconId='bar-chart-4-bars'
accountIds={notification.sampleAccountsIds} accountIds={notification.sampleAccountsIds}
count={notification.notifications_count} count={notification.notifications_count}
statusId={notification.statusId} statusId={notification.statusId}

View File

@ -21,6 +21,7 @@ export const NotificationReblog: React.FC<{
<NotificationGroupWithStatus <NotificationGroupWithStatus
type='reblog' type='reblog'
icon={RepeatIcon} icon={RepeatIcon}
iconId='repeat'
accountIds={notification.sampleAccountsIds} accountIds={notification.sampleAccountsIds}
statusId={notification.statusId} statusId={notification.statusId}
timestamp={notification.latest_page_notification_at} timestamp={notification.latest_page_notification_at}

View File

@ -21,6 +21,7 @@ export const NotificationStatus: React.FC<{
<NotificationWithStatus <NotificationWithStatus
type='status' type='status'
icon={NotificationsActiveIcon} icon={NotificationsActiveIcon}
iconId='notifications-active'
accountIds={notification.sampleAccountsIds} accountIds={notification.sampleAccountsIds}
count={notification.notifications_count} count={notification.notifications_count}
statusId={notification.statusId} statusId={notification.statusId}

View File

@ -21,6 +21,7 @@ export const NotificationUpdate: React.FC<{
<NotificationWithStatus <NotificationWithStatus
type='update' type='update'
icon={EditIcon} icon={EditIcon}
iconId='edit'
accountIds={notification.sampleAccountsIds} accountIds={notification.sampleAccountsIds}
count={notification.notifications_count} count={notification.notifications_count}
statusId={notification.statusId} statusId={notification.statusId}

View File

@ -1,8 +1,9 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import classNames from 'classnames';
import type { IconProp } from 'mastodon/components/icon'; import type { IconProp } from 'mastodon/components/icon';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import classNames from 'classnames';
import Status from 'mastodon/containers/status_container'; import Status from 'mastodon/containers/status_container';
import { NamesList } from './names_list'; import { NamesList } from './names_list';
@ -11,12 +12,22 @@ import type { LabelRenderer } from './notification_group_with_status';
export const NotificationWithStatus: React.FC<{ export const NotificationWithStatus: React.FC<{
type: string; type: string;
icon: IconProp; icon: IconProp;
iconId: string;
accountIds: string[]; accountIds: string[];
statusId: string; statusId: string;
count: number; count: number;
labelRenderer: LabelRenderer; labelRenderer: LabelRenderer;
unread: boolean; unread: boolean;
}> = ({ icon, accountIds, statusId, count, labelRenderer, type, unread }) => { }> = ({
icon,
iconId,
accountIds,
statusId,
count,
labelRenderer,
type,
unread,
}) => {
const label = useMemo( const label = useMemo(
() => () =>
labelRenderer({ labelRenderer({
@ -32,11 +43,11 @@ export const NotificationWithStatus: React.FC<{
`notification-ungrouped focusable notification-ungrouped--${type}`, `notification-ungrouped focusable notification-ungrouped--${type}`,
{ 'notification-ungrouped--unread': unread }, { 'notification-ungrouped--unread': unread },
)} )}
tabIndex='0' tabIndex={0}
> >
<div className='notification-ungrouped__header'> <div className='notification-ungrouped__header'>
<div className='notification-ungrouped__header__icon'> <div className='notification-ungrouped__header__icon'>
<Icon icon={icon} /> <Icon icon={icon} id={iconId} />
</div> </div>
{label} {label}
</div> </div>

View File

@ -1,9 +1,12 @@
import type { import type {
ApiAccountRelationshipSeveranceEventJSON,
ApiAccountWarningJSON,
BaseNotificationGroupJSON, BaseNotificationGroupJSON,
NotificationGroupJSON, NotificationGroupJSON,
NotificationType, NotificationType,
NotificationWithStatusType, NotificationWithStatusType,
} from 'mastodon/api_types/notifications'; } from 'mastodon/api_types/notifications';
import type { ApiReportJSON } from 'mastodon/api_types/reports';
interface BaseNotificationGroup interface BaseNotificationGroup
extends Omit<BaseNotificationGroupJSON, 'sample_accounts'> { extends Omit<BaseNotificationGroupJSON, 'sample_accounts'> {
@ -32,12 +35,39 @@ export type NotificationGroupFollow = BaseNotification<'follow'>;
export type NotificationGroupFollowRequest = BaseNotification<'follow_request'>; export type NotificationGroupFollowRequest = BaseNotification<'follow_request'>;
export type NotificationGroupAdminSignUp = BaseNotification<'admin.sign_up'>; export type NotificationGroupAdminSignUp = BaseNotification<'admin.sign_up'>;
// TODO: those two will need special types export type AccountWarningAction =
export type NotificationGroupModerationWarning = | 'none'
BaseNotification<'moderation_warning'>; | 'disable'
export type NotificationGroupAdminReport = BaseNotification<'admin.report'>; | 'mark_statuses_as_sensitive'
export type NotificationGroupSeveredRelationships = | 'delete_statuses'
BaseNotification<'severed_relationships'>; | 'sensitive'
| 'silence'
| 'suspend';
export interface AccountWarning
extends Omit<ApiAccountWarningJSON, 'target_account'> {
targetAccountId: string;
}
export interface NotificationGroupModerationWarning
extends BaseNotification<'moderation_warning'> {
moderationWarning: AccountWarning;
}
type AccountRelationshipSeveranceEvent =
ApiAccountRelationshipSeveranceEventJSON;
export interface NotificationGroupSeveredRelationships
extends BaseNotification<'severed_relationships'> {
event: AccountRelationshipSeveranceEvent;
}
interface Report extends Omit<ApiReportJSON, 'target_account'> {
targetAccountId: string;
}
export interface NotificationGroupAdminReport
extends BaseNotification<'admin.report'> {
report: Report;
}
export type NotificationGroup = export type NotificationGroup =
| NotificationGroupFavourite | NotificationGroupFavourite
@ -53,6 +83,30 @@ export type NotificationGroup =
| NotificationGroupAdminSignUp | NotificationGroupAdminSignUp
| NotificationGroupAdminReport; | NotificationGroupAdminReport;
function createReportFromJSON(reportJSON: ApiReportJSON): Report {
const { target_account, ...report } = reportJSON;
return {
targetAccountId: target_account.id,
...report,
};
}
function createAccountWarningFromJSON(
warningJSON: ApiAccountWarningJSON,
): AccountWarning {
const { target_account, ...warning } = warningJSON;
return {
targetAccountId: target_account.id,
...warning,
};
}
function createAccountRelationshipSeveranceEventFromJSON(
eventJson: ApiAccountRelationshipSeveranceEventJSON,
): AccountRelationshipSeveranceEvent {
return eventJson;
}
export function createNotificationGroupFromJSON( export function createNotificationGroupFromJSON(
groupJson: NotificationGroupJSON, groupJson: NotificationGroupJSON,
): NotificationGroup { ): NotificationGroup {
@ -68,8 +122,36 @@ export function createNotificationGroupFromJSON(
}; };
} }
if ('report' in group) {
const { report, ...groupWithoutTargetAccount } = group;
return { return {
report: createReportFromJSON(report),
sampleAccountsIds, sampleAccountsIds,
...group, ...groupWithoutTargetAccount,
}; };
}
switch (group.type) {
case 'severed_relationships':
return {
...group,
event: createAccountRelationshipSeveranceEventFromJSON(group.event),
sampleAccountsIds,
};
case 'moderation_warning': {
const { moderation_warning, ...groupWithoutModerationWarning } = group;
return {
...groupWithoutModerationWarning,
moderationWarning: createAccountWarningFromJSON(moderation_warning),
sampleAccountsIds,
};
}
// This is commented out because all group types are covered in the previous statement and have their returns
// default:
// return {
// sampleAccountsIds,
// ...group,
// };
}
} }