diff --git a/app/soapbox/actions/reports.ts b/app/soapbox/actions/reports.ts index d6a24a8c85..f51ef1f0a9 100644 --- a/app/soapbox/actions/reports.ts +++ b/app/soapbox/actions/reports.ts @@ -4,7 +4,7 @@ import { openModal } from './modals'; import type { AxiosError } from 'axios'; import type { AppDispatch, RootState } from 'soapbox/store'; -import type { Account, ChatMessage, Status } from 'soapbox/types/entities'; +import type { Account, ChatMessage, Group, Status } from 'soapbox/types/entities'; const REPORT_INIT = 'REPORT_INIT'; const REPORT_CANCEL = 'REPORT_CANCEL'; @@ -20,19 +20,29 @@ const REPORT_BLOCK_CHANGE = 'REPORT_BLOCK_CHANGE'; const REPORT_RULE_CHANGE = 'REPORT_RULE_CHANGE'; +enum ReportableEntities { + ACCOUNT = 'ACCOUNT', + CHAT_MESSAGE = 'CHAT_MESSAGE', + GROUP = 'GROUP', + STATUS = 'STATUS' +} + type ReportedEntity = { status?: Status chatMessage?: ChatMessage + group?: Group } -const initReport = (account: Account, entities?: ReportedEntity) => (dispatch: AppDispatch) => { - const { status, chatMessage } = entities || {}; +const initReport = (entityType: ReportableEntities, account: Account, entities?: ReportedEntity) => (dispatch: AppDispatch) => { + const { status, chatMessage, group } = entities || {}; dispatch({ type: REPORT_INIT, + entityType, account, status, chatMessage, + group, }); return dispatch(openModal('REPORT')); @@ -56,7 +66,8 @@ const submitReport = () => return api(getState).post('/api/v1/reports', { account_id: reports.getIn(['new', 'account_id']), status_ids: reports.getIn(['new', 'status_ids']), - message_ids: [reports.getIn(['new', 'chat_message', 'id'])], + message_ids: [reports.getIn(['new', 'chat_message', 'id'])].filter(Boolean), + group_id: reports.getIn(['new', 'group', 'id']), rule_ids: reports.getIn(['new', 'rule_ids']), comment: reports.getIn(['new', 'comment']), forward: reports.getIn(['new', 'forward']), @@ -97,6 +108,7 @@ const changeReportRule = (ruleId: string) => ({ }); export { + ReportableEntities, REPORT_INIT, REPORT_CANCEL, REPORT_SUBMIT_REQUEST, diff --git a/app/soapbox/components/status-action-bar.tsx b/app/soapbox/components/status-action-bar.tsx index f36bb34e9c..479095e667 100644 --- a/app/soapbox/components/status-action-bar.tsx +++ b/app/soapbox/components/status-action-bar.tsx @@ -12,7 +12,7 @@ import { toggleBookmark, toggleFavourite, togglePin, toggleReblog } from 'soapbo import { openModal } from 'soapbox/actions/modals'; import { deleteStatusModal, toggleStatusSensitivityModal } from 'soapbox/actions/moderation'; import { initMuteModal } from 'soapbox/actions/mutes'; -import { initReport } from 'soapbox/actions/reports'; +import { initReport, ReportableEntities } from 'soapbox/actions/reports'; import { deleteStatus, editStatus, toggleMuteStatus } from 'soapbox/actions/statuses'; import DropdownMenu from 'soapbox/components/dropdown-menu'; import StatusActionButton from 'soapbox/components/status-action-button'; @@ -254,7 +254,7 @@ const StatusActionBar: React.FC = ({ secondary: intl.formatMessage(messages.blockAndReport), onSecondary: () => { dispatch(blockAccount(account.id)); - dispatch(initReport(account, { status })); + dispatch(initReport(ReportableEntities.STATUS, account, { status })); }, })); }; @@ -271,7 +271,7 @@ const StatusActionBar: React.FC = ({ }; const handleReport: React.EventHandler = (e) => { - dispatch(initReport(status.account as Account, { status })); + dispatch(initReport(ReportableEntities.STATUS, status.account as Account, { status })); }; const handleConversationMuteClick: React.EventHandler = (e) => { diff --git a/app/soapbox/components/ui/icon-button/icon-button.tsx b/app/soapbox/components/ui/icon-button/icon-button.tsx index 086b5a2c04..ad9d6a5176 100644 --- a/app/soapbox/components/ui/icon-button/icon-button.tsx +++ b/app/soapbox/components/ui/icon-button/icon-button.tsx @@ -14,7 +14,7 @@ interface IIconButton extends React.ButtonHTMLAttributes { /** Don't render a background behind the icon. */ transparent?: boolean /** Predefined styles to display for the button. */ - theme?: 'seamless' | 'outlined' + theme?: 'seamless' | 'outlined' | 'secondary' /** Override the data-testid */ 'data-testid'?: string } @@ -30,6 +30,7 @@ const IconButton = React.forwardRef((props: IIconButton, ref: React.ForwardedRef className={clsx('flex items-center space-x-2 rounded-full p-1 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:ring-offset-0', { 'bg-white dark:bg-transparent': !transparent, 'border border-solid bg-transparent border-gray-400 dark:border-gray-800 hover:border-primary-300 dark:hover:border-primary-700 focus:border-primary-500 text-gray-900 dark:text-gray-100 focus:ring-primary-500': theme === 'outlined', + 'border-transparent bg-primary-100 dark:bg-primary-800 hover:bg-primary-50 dark:hover:bg-primary-700 focus:bg-primary-100 dark:focus:bg-primary-800 text-primary-500 dark:text-primary-200': theme === 'secondary', 'opacity-50': filteredProps.disabled, }, className)} {...filteredProps} diff --git a/app/soapbox/features/account/components/header.tsx b/app/soapbox/features/account/components/header.tsx index 4b9e43f36e..de21618801 100644 --- a/app/soapbox/features/account/components/header.tsx +++ b/app/soapbox/features/account/components/header.tsx @@ -12,7 +12,7 @@ import { mentionCompose, directCompose } from 'soapbox/actions/compose'; import { blockDomain, unblockDomain } from 'soapbox/actions/domain-blocks'; import { openModal } from 'soapbox/actions/modals'; import { initMuteModal } from 'soapbox/actions/mutes'; -import { initReport } from 'soapbox/actions/reports'; +import { initReport, ReportableEntities } from 'soapbox/actions/reports'; import { setSearchAccount } from 'soapbox/actions/search'; import { getSettings } from 'soapbox/actions/settings'; import Badge from 'soapbox/components/badge'; @@ -136,7 +136,7 @@ const Header: React.FC = ({ account }) => { secondary: intl.formatMessage(messages.blockAndReport), onSecondary: () => { dispatch(blockAccount(account.id)); - dispatch(initReport(account)); + dispatch(initReport(ReportableEntities.ACCOUNT, account)); }, })); } @@ -171,7 +171,7 @@ const Header: React.FC = ({ account }) => { }; const onReport = () => { - dispatch(initReport(account)); + dispatch(initReport(ReportableEntities.ACCOUNT, account)); }; const onMute = () => { diff --git a/app/soapbox/features/chats/components/chat-message.tsx b/app/soapbox/features/chats/components/chat-message.tsx index 05602a9afd..edeb13d65a 100644 --- a/app/soapbox/features/chats/components/chat-message.tsx +++ b/app/soapbox/features/chats/components/chat-message.tsx @@ -6,7 +6,7 @@ import React, { useMemo, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { openModal } from 'soapbox/actions/modals'; -import { initReport } from 'soapbox/actions/reports'; +import { initReport, ReportableEntities } from 'soapbox/actions/reports'; import DropdownMenu from 'soapbox/components/dropdown-menu'; import { HStack, Icon, Stack, Text } from 'soapbox/components/ui'; import emojify from 'soapbox/features/emoji'; @@ -24,7 +24,7 @@ import ChatMessageReactionWrapper from './chat-message-reaction-wrapper/chat-mes import type { Menu as IMenu } from 'soapbox/components/dropdown-menu'; import type { IMediaGallery } from 'soapbox/components/media-gallery'; -import type { ChatMessage as ChatMessageEntity } from 'soapbox/types/entities'; +import type { Account, ChatMessage as ChatMessageEntity } from 'soapbox/types/entities'; const messages = defineMessages({ copy: { id: 'chats.actions.copy', defaultMessage: 'Copy' }, @@ -178,7 +178,7 @@ const ChatMessage = (props: IChatMessage) => { if (features.reportChats) { menu.push({ text: intl.formatMessage(messages.report), - action: () => dispatch(initReport(normalizeAccount(chat.account) as any, { chatMessage } as any)), + action: () => dispatch(initReport(ReportableEntities.CHAT_MESSAGE, normalizeAccount(chat.account) as Account, { chatMessage })), icon: require('@tabler/icons/flag.svg'), }); } diff --git a/app/soapbox/features/event/components/event-header.tsx b/app/soapbox/features/event/components/event-header.tsx index e18d342fec..8e8cf9ca3f 100644 --- a/app/soapbox/features/event/components/event-header.tsx +++ b/app/soapbox/features/event/components/event-header.tsx @@ -11,7 +11,7 @@ import { toggleBookmark, togglePin, toggleReblog } from 'soapbox/actions/interac import { openModal } from 'soapbox/actions/modals'; import { deleteStatusModal, toggleStatusSensitivityModal } from 'soapbox/actions/moderation'; import { initMuteModal } from 'soapbox/actions/mutes'; -import { initReport } from 'soapbox/actions/reports'; +import { initReport, ReportableEntities } from 'soapbox/actions/reports'; import { deleteStatus } from 'soapbox/actions/statuses'; import Icon from 'soapbox/components/icon'; import StillImage from 'soapbox/components/still-image'; @@ -176,13 +176,13 @@ const EventHeader: React.FC = ({ status }) => { secondary: intl.formatMessage(messages.blockAndReport), onSecondary: () => { dispatch(blockAccount(account.id)); - dispatch(initReport(account, { status })); + dispatch(initReport(ReportableEntities.STATUS, account, { status })); }, })); }; const handleReport = () => { - dispatch(initReport(account, { status })); + dispatch(initReport(ReportableEntities.STATUS, account, { status })); }; const handleModerate = () => { diff --git a/app/soapbox/features/group/components/group-header.tsx b/app/soapbox/features/group/components/group-header.tsx index 30732dbec2..cbeddfaea0 100644 --- a/app/soapbox/features/group/components/group-header.tsx +++ b/app/soapbox/features/group/components/group-header.tsx @@ -12,6 +12,7 @@ import { isDefaultHeader } from 'soapbox/utils/accounts'; import GroupActionButton from './group-action-button'; import GroupMemberCount from './group-member-count'; +import GroupOptionsButton from './group-options-button'; import GroupPrivacy from './group-privacy'; import GroupRelationship from './group-relationship'; @@ -140,7 +141,10 @@ const GroupHeader: React.FC = ({ group }) => { /> - + + + + ); diff --git a/app/soapbox/features/group/components/group-options-button.tsx b/app/soapbox/features/group/components/group-options-button.tsx new file mode 100644 index 0000000000..54ded91812 --- /dev/null +++ b/app/soapbox/features/group/components/group-options-button.tsx @@ -0,0 +1,46 @@ +import React, { useMemo } from 'react'; + +import { initReport, ReportableEntities } from 'soapbox/actions/reports'; +import DropdownMenu, { Menu } from 'soapbox/components/dropdown-menu'; +import { IconButton } from 'soapbox/components/ui'; +import { useAppDispatch, useOwnAccount } from 'soapbox/hooks'; +import { GroupRoles } from 'soapbox/schemas/group-member'; + +import type { Account, Group } from 'soapbox/types/entities'; + +interface IGroupActionButton { + group: Group +} + +const GroupOptionsButton = ({ group }: IGroupActionButton) => { + const dispatch = useAppDispatch(); + const account = useOwnAccount(); + + const isMember = group.relationship?.role === GroupRoles.USER; + const isBlocked = group.relationship?.blocked_by; + + const menu: Menu = useMemo(() => ([ + { + text: 'Report', + icon: require('@tabler/icons/flag.svg'), + action: () => dispatch(initReport(ReportableEntities.GROUP, account as Account, { group })), + }, + ]), []); + + if (isBlocked || !isMember || menu.length === 0) { + return null; + } + + return ( + + + + ); +}; + +export default GroupOptionsButton; \ No newline at end of file diff --git a/app/soapbox/features/ui/components/modals/report-modal/__tests__/report-modal.test.tsx b/app/soapbox/features/ui/components/modals/report-modal/__tests__/report-modal.test.tsx index 269f97146c..56816b10c1 100644 --- a/app/soapbox/features/ui/components/modals/report-modal/__tests__/report-modal.test.tsx +++ b/app/soapbox/features/ui/components/modals/report-modal/__tests__/report-modal.test.tsx @@ -2,6 +2,7 @@ import userEvent from '@testing-library/user-event'; import { Map as ImmutableMap, Record as ImmutableRecord, Set as ImmutableSet } from 'immutable'; import React from 'react'; +import { ReportableEntities } from 'soapbox/actions/reports'; import { __stub } from 'soapbox/api'; import { render, screen, waitFor } from '../../../../../../jest/test-helpers'; @@ -29,6 +30,7 @@ describe('', () => { account_id: '1', status_ids: ImmutableSet(['1']), rule_ids: ImmutableSet(), + entityType: ReportableEntities.STATUS, })(), })(), statuses: ImmutableMap({ diff --git a/app/soapbox/features/ui/components/modals/report-modal/report-modal.tsx b/app/soapbox/features/ui/components/modals/report-modal/report-modal.tsx index ed9c5c4115..04252424de 100644 --- a/app/soapbox/features/ui/components/modals/report-modal/report-modal.tsx +++ b/app/soapbox/features/ui/components/modals/report-modal/report-modal.tsx @@ -2,9 +2,10 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { blockAccount } from 'soapbox/actions/accounts'; -import { submitReport, submitReportSuccess, submitReportFail } from 'soapbox/actions/reports'; +import { submitReport, submitReportSuccess, submitReportFail, ReportableEntities } from 'soapbox/actions/reports'; import { expandAccountTimeline } from 'soapbox/actions/timelines'; import AttachmentThumbs from 'soapbox/components/attachment-thumbs'; +import GroupCard from 'soapbox/components/group-card'; import List, { ListItem } from 'soapbox/components/list'; import StatusContent from 'soapbox/components/status-content'; import { Avatar, HStack, Icon, Modal, ProgressBar, Stack, Text } from 'soapbox/components/ui'; @@ -24,6 +25,7 @@ const messages = defineMessages({ submit: { id: 'report.submit', defaultMessage: 'Submit' }, reportContext: { id: 'report.chatMessage.context', defaultMessage: 'When reporting a user’s message, the five messages before and five messages after the one selected will be passed along to our moderation team for context.' }, reportMessage: { id: 'report.chatMessage.title', defaultMessage: 'Report message' }, + reportGroup: { id: 'report.group.title', defaultMessage: 'Report Group' }, cancel: { id: 'common.cancel', defaultMessage: 'Cancel' }, previous: { id: 'report.previous', defaultMessage: 'Previous' }, }); @@ -35,9 +37,26 @@ enum Steps { } const reportSteps = { - ONE: ReasonStep, - TWO: OtherActionsStep, - THREE: ConfirmationStep, + [ReportableEntities.ACCOUNT]: { + ONE: ReasonStep, + TWO: OtherActionsStep, + THREE: ConfirmationStep, + }, + [ReportableEntities.CHAT_MESSAGE]: { + ONE: ReasonStep, + TWO: OtherActionsStep, + THREE: ConfirmationStep, + }, + [ReportableEntities.STATUS]: { + ONE: ReasonStep, + TWO: OtherActionsStep, + THREE: ConfirmationStep, + }, + [ReportableEntities.GROUP]: { + ONE: ReasonStep, + TWO: ConfirmationStep, + THREE: null, + }, }; const SelectedStatus = ({ statusId }: { statusId: string }) => { @@ -76,12 +95,6 @@ interface IReportModal { onClose: () => void } -enum ReportedEntities { - Account = 'Account', - Status = 'Status', - ChatMessage = 'ChatMessage' -} - const ReportModal = ({ onClose }: IReportModal) => { const dispatch = useAppDispatch(); const intl = useIntl(); @@ -89,27 +102,20 @@ const ReportModal = ({ onClose }: IReportModal) => { const accountId = useAppSelector((state) => state.reports.new.account_id); const account = useAccount(accountId as string); + const entityType = useAppSelector((state) => state.reports.new.entityType); const isBlocked = useAppSelector((state) => state.reports.new.block); const isSubmitting = useAppSelector((state) => state.reports.new.isSubmitting); const rules = useAppSelector((state) => state.rules.items); const ruleIds = useAppSelector((state) => state.reports.new.rule_ids); const selectedStatusIds = useAppSelector((state) => state.reports.new.status_ids); const selectedChatMessage = useAppSelector((state) => state.reports.new.chat_message); + const selectedGroup = useAppSelector((state) => state.reports.new.group); const shouldRequireRule = rules.length > 0; - const reportedEntity = useMemo(() => { - if (selectedStatusIds.size === 0 && !selectedChatMessage) { - return ReportedEntities.Account; - } else if (selectedChatMessage) { - return ReportedEntities.ChatMessage; - } else { - return ReportedEntities.Status; - } - }, []); - - const isReportingAccount = reportedEntity === ReportedEntities.Account; - const isReportingStatus = reportedEntity === ReportedEntities.Status; + const isReportingAccount = entityType === ReportableEntities.ACCOUNT; + const isReportingStatus = entityType === ReportableEntities.STATUS; + const isReportingGroup = entityType === ReportableEntities.GROUP; const [currentStep, setCurrentStep] = useState(Steps.ONE); @@ -160,22 +166,41 @@ const ReportModal = ({ onClose }: IReportModal) => { const confirmationText = useMemo(() => { switch (currentStep) { + case Steps.ONE: + if (isReportingGroup) { + return intl.formatMessage(messages.submit); + } else { + return intl.formatMessage(messages.next); + } case Steps.TWO: - return intl.formatMessage(messages.submit); + if (isReportingGroup) { + return intl.formatMessage(messages.done); + } else { + return intl.formatMessage(messages.submit); + } case Steps.THREE: return intl.formatMessage(messages.done); default: return intl.formatMessage(messages.next); } - }, [currentStep]); + }, [currentStep, isReportingGroup]); const handleNextStep = () => { switch (currentStep) { case Steps.ONE: - setCurrentStep(Steps.TWO); + if (isReportingGroup) { + handleSubmit(); + } else { + setCurrentStep(Steps.TWO); + } break; case Steps.TWO: - handleSubmit(); + if (isReportingGroup) { + dispatch(submitReportSuccess()); + onClose(); + } else { + handleSubmit(); + } break; case Steps.THREE: dispatch(submitReportSuccess()); @@ -212,19 +237,35 @@ const ReportModal = ({ onClose }: IReportModal) => { } }; + const renderSelectedGroup = () => { + if (selectedGroup) { + return ; + } + }; + const renderSelectedEntity = () => { - switch (reportedEntity) { - case ReportedEntities.Status: + switch (entityType) { + case ReportableEntities.STATUS: return renderSelectedStatuses(); - case ReportedEntities.ChatMessage: + case ReportableEntities.CHAT_MESSAGE: return renderSelectedChatMessage(); + case ReportableEntities.GROUP: + if (currentStep === Steps.TWO) { + return null; + } + + return renderSelectedGroup(); + default: + return null; } }; const renderTitle = () => { - switch (reportedEntity) { - case ReportedEntities.ChatMessage: + switch (entityType) { + case ReportableEntities.CHAT_MESSAGE: return intl.formatMessage(messages.reportMessage); + case ReportableEntities.GROUP: + return intl.formatMessage(messages.reportGroup); default: return @{account?.acct} }} />; } @@ -252,16 +293,16 @@ const ReportModal = ({ onClose }: IReportModal) => { }, [currentStep]); useEffect(() => { - if (account) { + if (account?.id) { dispatch(expandAccountTimeline(account.id, { withReplies: true, maxId: null })); } - }, [account]); + }, [account?.id]); if (!account) { return null; } - const StepToRender = reportSteps[currentStep]; + const StepToRender = reportSteps[entityType][currentStep]; return ( { {(currentStep !== Steps.THREE && !isReportingAccount) && renderSelectedEntity()} - + {StepToRender && ( + + )} ); diff --git a/app/soapbox/features/ui/components/modals/report-modal/steps/confirmation-step.tsx b/app/soapbox/features/ui/components/modals/report-modal/steps/confirmation-step.tsx index eebb3d717a..2cfe141367 100644 --- a/app/soapbox/features/ui/components/modals/report-modal/steps/confirmation-step.tsx +++ b/app/soapbox/features/ui/components/modals/report-modal/steps/confirmation-step.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; +import { ReportableEntities } from 'soapbox/actions/reports'; import { getSoapboxConfig } from 'soapbox/actions/soapbox'; import { Stack, Text } from 'soapbox/components/ui'; import { useAppSelector } from 'soapbox/hooks'; @@ -8,8 +9,10 @@ import { useAppSelector } from 'soapbox/hooks'; import type { ReducerAccount } from 'soapbox/reducers/accounts'; const messages = defineMessages({ + accountEntity: { id: 'report.confirmation.entity.account', defaultMessage: 'account' }, + groupEntity: { id: 'report.confirmation.entity.group', defaultMessage: 'group' }, title: { id: 'report.confirmation.title', defaultMessage: 'Thanks for submitting your report.' }, - content: { id: 'report.confirmation.content', defaultMessage: 'If we find that this account is violating the {link} we will take further action on the matter.' }, + content: { id: 'report.confirmation.content', defaultMessage: 'If we find that this {entity} is violating the {link} we will take further action on the matter.' }, }); interface IOtherActionsStep { @@ -34,6 +37,11 @@ const renderTermsOfServiceLink = (href: string) => ( const ConfirmationStep = ({ account }: IOtherActionsStep) => { const intl = useIntl(); const links = useAppSelector((state) => getSoapboxConfig(state).get('links') as any); + const entityType = useAppSelector((state) => state.reports.new.entityType); + + const entity = entityType === ReportableEntities.GROUP + ? intl.formatMessage(messages.groupEntity) + : intl.formatMessage(messages.accountEntity); return ( @@ -43,6 +51,7 @@ const ConfirmationStep = ({ account }: IOtherActionsStep) => { {intl.formatMessage(messages.content, { + entity, link: links.get('termsOfService') ? renderTermsOfServiceLink(links.get('termsOfService')) : termsOfServiceText, diff --git a/app/soapbox/features/ui/components/modals/report-modal/steps/reason-step.tsx b/app/soapbox/features/ui/components/modals/report-modal/steps/reason-step.tsx index af73d8485f..85a78f1453 100644 --- a/app/soapbox/features/ui/components/modals/report-modal/steps/reason-step.tsx +++ b/app/soapbox/features/ui/components/modals/report-modal/steps/reason-step.tsx @@ -1,8 +1,8 @@ import clsx from 'clsx'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { changeReportComment, changeReportRule } from 'soapbox/actions/reports'; +import { changeReportComment, changeReportRule, ReportableEntities } from 'soapbox/actions/reports'; import { fetchRules } from 'soapbox/actions/rules'; import { FormGroup, Stack, Text, Textarea } from 'soapbox/components/ui'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; @@ -29,14 +29,12 @@ const ReasonStep = (_props: IReasonStep) => { const [isNearBottom, setNearBottom] = useState(false); const [isNearTop, setNearTop] = useState(true); + const entityType = useAppSelector((state) => state.reports.new.entityType); const comment = useAppSelector((state) => state.reports.new.comment); const rules = useAppSelector((state) => state.rules.items); const ruleIds = useAppSelector((state) => state.reports.new.rule_ids); const shouldRequireRule = rules.length > 0; - const selectedStatusIds = useAppSelector((state) => state.reports.new.status_ids); - const isReportingAccount = useMemo(() => selectedStatusIds.size === 0, []); - const handleCommentChange = (event: React.ChangeEvent) => { dispatch(changeReportComment(event.target.value)); }; @@ -60,7 +58,23 @@ const ReasonStep = (_props: IReasonStep) => { }; const filterRuleType = (rule: any) => { - const ruleTypeToFilter = isReportingAccount ? 'account' : 'content'; + let ruleTypeToFilter = 'content'; + + switch (entityType) { + case ReportableEntities.ACCOUNT: + ruleTypeToFilter = 'account'; + break; + case ReportableEntities.STATUS: + case ReportableEntities.CHAT_MESSAGE: + ruleTypeToFilter = 'content'; + break; + case ReportableEntities.GROUP: + ruleTypeToFilter = 'group'; + break; + default: + ruleTypeToFilter = 'content'; + break; + } if (rule.rule_type) { return rule.rule_type === ruleTypeToFilter; diff --git a/app/soapbox/locales/en.json b/app/soapbox/locales/en.json index 2dafb643fc..1d4d12a313 100644 --- a/app/soapbox/locales/en.json +++ b/app/soapbox/locales/en.json @@ -1241,11 +1241,14 @@ "report.block_hint": "Do you also want to block this account?", "report.chatMessage.context": "When reporting a user’s message, the five messages before and five messages after the one selected will be passed along to our moderation team for context.", "report.chatMessage.title": "Report message", - "report.confirmation.content": "If we find that this account is violating the {link} we will take further action on the matter.", + "report.confirmation.content": "If we find that this {entity} is violating the {link} we will take further action on the matter.", + "report.confirmation.entity.account": "account", + "report.confirmation.entity.group": "group", "report.confirmation.title": "Thanks for submitting your report.", "report.done": "Done", "report.forward": "Forward to {target}", "report.forward_hint": "The account is from another server. Send a copy of the report there as well?", + "report.group.title": "Report Group", "report.next": "Next", "report.otherActions.addAdditional": "Would you like to add additional statuses to this report?", "report.otherActions.addMore": "Add more", diff --git a/app/soapbox/reducers/__tests__/reports.test.ts b/app/soapbox/reducers/__tests__/reports.test.ts index 9105c11be0..fd94e8062e 100644 --- a/app/soapbox/reducers/__tests__/reports.test.ts +++ b/app/soapbox/reducers/__tests__/reports.test.ts @@ -8,6 +8,8 @@ describe('reports reducer', () => { account_id: null, status_ids: [], chat_message: null, + group: null, + entityType: '', comment: '', forward: false, block: false, diff --git a/app/soapbox/reducers/reports.ts b/app/soapbox/reducers/reports.ts index 71d0325fce..5c355a266e 100644 --- a/app/soapbox/reducers/reports.ts +++ b/app/soapbox/reducers/reports.ts @@ -1,7 +1,5 @@ import { Record as ImmutableRecord, Set as ImmutableSet } from 'immutable'; -import { ChatMessage } from 'soapbox/types/entities'; - import { REPORT_INIT, REPORT_SUBMIT_REQUEST, @@ -13,15 +11,19 @@ import { REPORT_FORWARD_CHANGE, REPORT_BLOCK_CHANGE, REPORT_RULE_CHANGE, + ReportableEntities, } from '../actions/reports'; import type { AnyAction } from 'redux'; +import type { ChatMessage, Group } from 'soapbox/types/entities'; const NewReportRecord = ImmutableRecord({ isSubmitting: false, + entityType: '' as ReportableEntities, account_id: null as string | null, status_ids: ImmutableSet(), chat_message: null as null | ChatMessage, + group: null as null | Group, comment: '', forward: false, block: false, @@ -40,11 +42,16 @@ export default function reports(state: State = ReducerRecord(), action: AnyActio return state.withMutations(map => { map.setIn(['new', 'isSubmitting'], false); map.setIn(['new', 'account_id'], action.account.id); + map.setIn(['new', 'entityType'], action.entityType); if (action.chatMessage) { map.setIn(['new', 'chat_message'], action.chatMessage); } + if (action.group) { + map.setIn(['new', 'group'], action.group); + } + if (state.new.account_id !== action.account.id) { map.setIn(['new', 'status_ids'], action.status ? ImmutableSet([action.status.reblog?.id || action.status.id]) : ImmutableSet()); map.setIn(['new', 'comment'], '');