mirror of https://github.com/mastodon/mastodon.git
Fix the remaining lint issues
parent
be36081f54
commit
8805b9d1bd
|
@ -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);
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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} />
|
||||||
|
|
|
@ -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}}'
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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'>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
|
// };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue