Configure with i18n

api-accept
Justin 2 years ago
parent 86fb9bf704
commit 2607a55380

@ -6,7 +6,10 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { blockAccount } from 'soapbox/actions/accounts'; import { blockAccount } from 'soapbox/actions/accounts';
import { submitReport, cancelReport, submitReportSuccess, submitReportFail } from 'soapbox/actions/reports'; import { submitReport, cancelReport, submitReportSuccess, submitReportFail } from 'soapbox/actions/reports';
import { expandAccountTimeline } from 'soapbox/actions/timelines'; import { expandAccountTimeline } from 'soapbox/actions/timelines';
import { Modal, ProgressBar, Stack } from 'soapbox/components/ui'; import AttachmentThumbs from 'soapbox/components/attachment_thumbs';
import StatusContent from 'soapbox/components/status_content';
import { Modal, ProgressBar, Stack, Text } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account_container';
import { useAccount, useAppDispatch, useAppSelector } from 'soapbox/hooks'; import { useAccount, useAppDispatch, useAppSelector } from 'soapbox/hooks';
import ConfirmationStep from './steps/confirmation-step'; import ConfirmationStep from './steps/confirmation-step';
@ -14,6 +17,9 @@ import OtherActionsStep from './steps/other-actions-step';
import ReasonStep from './steps/reason-step'; import ReasonStep from './steps/reason-step';
const messages = defineMessages({ const messages = defineMessages({
blankslate: { id: 'report.reason.blankslate', defaultMessage: 'You have removed all statuses from being selected.' },
done: { id: 'report.done', defaultMessage: 'Done' },
next: { id: 'report.next', defaultMessage: 'Next' },
close: { id: 'lightbox.close', defaultMessage: 'Close' }, close: { id: 'lightbox.close', defaultMessage: 'Close' },
placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' }, placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' },
submit: { id: 'report.submit', defaultMessage: 'Submit' }, submit: { id: 'report.submit', defaultMessage: 'Submit' },
@ -31,6 +37,39 @@ const reportSteps = {
THREE: ConfirmationStep, THREE: ConfirmationStep,
}; };
const SelectedStatus = ({ statusId }: { statusId: string }) => {
const status = useAppSelector((state) => state.statuses.get(statusId));
if (!status) {
return null;
}
return (
<Stack space={2} className='p-4 rounded-lg bg-gray-100 dark:bg-slate-700'>
<AccountContainer
id={status.get('account') as any}
showProfileHoverCard={false}
timestamp={status.get('created_at')}
hideActions
/>
<StatusContent
status={status}
expanded
collapsable
/>
{status.get('media_attachments').size > 0 && (
<AttachmentThumbs
compact
media={status.get('media_attachments')}
sensitive={status.get('sensitive')}
/>
)}
</Stack>
);
};
interface IReportModal { interface IReportModal {
onClose: () => void onClose: () => void
} }
@ -84,14 +123,27 @@ const ReportModal = ({ onClose }: IReportModal) => {
} }
}; };
const renderSelectedStatuses = useCallback(() => {
switch (selectedStatusIds.size) {
case 0:
return (
<div className='bg-gray-100 dark:bg-slate-700 p-4 rounded-lg flex items-center justify-center w-full'>
<Text theme='muted'>{intl.formatMessage(messages.blankslate)}</Text>
</div>
);
default:
return <SelectedStatus statusId={selectedStatusIds.first()} />;
}
}, [selectedStatusIds.size]);
const confirmationText = useMemo(() => { const confirmationText = useMemo(() => {
switch (currentStep) { switch (currentStep) {
case Steps.TWO: case Steps.TWO:
return intl.formatMessage(messages.submit); return intl.formatMessage(messages.submit);
case Steps.THREE: case Steps.THREE:
return 'Done'; return intl.formatMessage(messages.done);
default: default:
return 'Next'; return intl.formatMessage(messages.next);
} }
}, [currentStep]); }, [currentStep]);
@ -141,6 +193,8 @@ const ReportModal = ({ onClose }: IReportModal) => {
<Stack space={4}> <Stack space={4}>
<ProgressBar progress={calculateProgress()} /> <ProgressBar progress={calculateProgress()} />
{currentStep !== Steps.THREE && renderSelectedStatuses()}
<StepToRender account={account} /> <StepToRender account={account} />
</Stack> </Stack>
</Modal> </Modal>

@ -1,23 +1,52 @@
import React from 'react'; import React from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
import { Stack, Text } from 'soapbox/components/ui'; import { Stack, Text } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks';
import type { ReducerAccount } from 'soapbox/reducers/accounts'; import type { ReducerAccount } from 'soapbox/reducers/accounts';
const messages = defineMessages({
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.' },
});
interface IOtherActionsStep { interface IOtherActionsStep {
account: ReducerAccount account: ReducerAccount
} }
const termsOfServiceText = (<FormattedMessage
id='shared.tos'
defaultMessage='Terms of Service'
/>);
const renderTermsOfServiceLink = (href: string) => (
<a
href={href}
target='_blank'
className='hover:underline text-primary-600 dark:text-primary-400 hover:text-primary-800 dark:hover:text-primary-500'
>
{termsOfServiceText}
</a>
);
const ConfirmationStep = ({ account }: IOtherActionsStep) => { const ConfirmationStep = ({ account }: IOtherActionsStep) => {
const intl = useIntl();
const links = useAppSelector((state) => getSoapboxConfig(state).get('links') as any);
return ( return (
<Stack space={1}> <Stack space={1}>
<Text weight='semibold' tag='h1' size='xl'> <Text weight='semibold' tag='h1' size='xl'>
Thanks for submitting your report. {intl.formatMessage(messages.title)}
</Text> </Text>
<Text> <Text>
If we find that this account is violating the TRUTH Terms of Service we {intl.formatMessage(messages.content, {
will take further action on the matter. link: links.get('termsOfService') ?
renderTermsOfServiceLink(links.get('termsOfService')) :
termsOfServiceText,
})}
</Text> </Text>
</Stack> </Stack>
); );

@ -1,53 +1,25 @@
import { OrderedSet, Set as ImmutableSet } from 'immutable'; import { OrderedSet } from 'immutable';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { FormattedMessage } from 'react-intl'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import Toggle from 'react-toggle'; import Toggle from 'react-toggle';
import { changeReportBlock, changeReportForward } from 'soapbox/actions/reports'; import { changeReportBlock, changeReportForward } from 'soapbox/actions/reports';
import { fetchRules } from 'soapbox/actions/rules'; import { fetchRules } from 'soapbox/actions/rules';
import AttachmentThumbs from 'soapbox/components/attachment_thumbs';
import StatusContent from 'soapbox/components/status_content';
import { Button, FormGroup, HStack, Stack, Text } from 'soapbox/components/ui'; import { Button, FormGroup, HStack, Stack, Text } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account_container';
import StatusCheckBox from 'soapbox/features/report/containers/status_check_box_container'; import StatusCheckBox from 'soapbox/features/report/containers/status_check_box_container';
import { useAppSelector, useFeatures } from 'soapbox/hooks'; import { useAppSelector, useFeatures } from 'soapbox/hooks';
import { isRemote, getDomain } from 'soapbox/utils/accounts'; import { isRemote, getDomain } from 'soapbox/utils/accounts';
import type { ReducerAccount } from 'soapbox/reducers/accounts'; import type { ReducerAccount } from 'soapbox/reducers/accounts';
const SelectedStatus = ({ statusId }: { statusId: string }) => { const messages = defineMessages({
const status = useAppSelector((state) => state.statuses.get(statusId)); addAdditionalStatuses: { id: 'report.otherActions.addAdditionl', defaultMessage: 'Would you like to add additional statuses to this report?' },
addMore: { id: 'report.otherActions.addMore', defaultMessage: 'Add more' },
if (!status) { furtherActions: { id: 'report.otherActions.furtherActions', defaultMessage: 'Further actions:' },
return null; hideAdditonalStatuses: { id: 'report.otherActions.hideAdditional', defaultMessage: 'Hide additional statuses' },
} otherStatuses: { id: 'report.otherActions.otherStatuses', defaultMessage: 'Include other statuses?' },
});
return (
<Stack space={2} className='p-4 rounded-lg bg-gray-100 dark:bg-slate-700'>
<AccountContainer
id={status.get('account') as any}
showProfileHoverCard={false}
timestamp={status.get('created_at')}
hideActions
/>
<StatusContent
status={status}
expanded
collapsable
/>
{status.get('media_attachments').size > 0 && (
<AttachmentThumbs
compact
media={status.get('media_attachments')}
sensitive={status.get('sensitive')}
/>
)}
</Stack>
);
};
interface IOtherActionsStep { interface IOtherActionsStep {
account: ReducerAccount account: ReducerAccount
@ -56,8 +28,8 @@ interface IOtherActionsStep {
const OtherActionsStep = ({ account }: IOtherActionsStep) => { const OtherActionsStep = ({ account }: IOtherActionsStep) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const features = useFeatures(); const features = useFeatures();
const intl = useIntl();
const selectedStatusIds = useAppSelector((state) => state.reports.getIn(['new', 'status_ids']) as ImmutableSet<string>);
const statusIds = useAppSelector((state) => OrderedSet(state.timelines.getIn([`account:${account.id}:with_replies`, 'items'])).union(state.reports.getIn(['new', 'status_ids']) as Iterable<unknown>) as OrderedSet<string>); const statusIds = useAppSelector((state) => OrderedSet(state.timelines.getIn([`account:${account.id}:with_replies`, 'items'])).union(state.reports.getIn(['new', 'status_ids']) as Iterable<unknown>) as OrderedSet<string>);
const isBlocked = useAppSelector((state) => state.reports.getIn(['new', 'block']) as boolean); const isBlocked = useAppSelector((state) => state.reports.getIn(['new', 'block']) as boolean);
const isForward = useAppSelector((state) => state.reports.getIn(['reports', 'forward']) as boolean); const isForward = useAppSelector((state) => state.reports.getIn(['reports', 'forward']) as boolean);
@ -66,19 +38,6 @@ const OtherActionsStep = ({ account }: IOtherActionsStep) => {
const [showAdditionalStatuses, setShowAdditionalStatuses] = useState<boolean>(false); const [showAdditionalStatuses, setShowAdditionalStatuses] = useState<boolean>(false);
const renderSelectedStatuses = useCallback(() => {
switch (selectedStatusIds.size) {
case 0:
return (
<div className='bg-gray-100 dark:bg-slate-700 p-4 rounded-lg flex items-center justify-center w-full'>
<Text theme='muted'>You have removed all statuses from being selected.</Text>
</div>
);
default:
return <SelectedStatus statusId={selectedStatusIds.first()} />;
}
}, [selectedStatusIds.size]);
const handleBlockChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleBlockChange = (event: React.ChangeEvent<HTMLInputElement>) => {
dispatch(changeReportBlock(event.target.checked)); dispatch(changeReportBlock(event.target.checked));
}; };
@ -93,15 +52,13 @@ const OtherActionsStep = ({ account }: IOtherActionsStep) => {
return ( return (
<Stack space={4}> <Stack space={4}>
{renderSelectedStatuses()}
{features.reportMultipleStatuses && ( {features.reportMultipleStatuses && (
<Stack space={2}> <Stack space={2}>
<Text tag='h1' size='xl' weight='semibold'>Include other statuses?</Text> <Text tag='h1' size='xl' weight='semibold'>
{intl.formatMessage(messages.otherStatuses)}
</Text>
<FormGroup <FormGroup labelText={intl.formatMessage(messages.addAdditionalStatuses)}>
labelText='Would you like to add additional statuses to this report?'
>
{showAdditionalStatuses ? ( {showAdditionalStatuses ? (
<Stack space={2}> <Stack space={2}>
<div className='bg-gray-100 rounded-lg p-4'> <div className='bg-gray-100 rounded-lg p-4'>
@ -115,7 +72,7 @@ const OtherActionsStep = ({ account }: IOtherActionsStep) => {
size='sm' size='sm'
onClick={() => setShowAdditionalStatuses(false)} onClick={() => setShowAdditionalStatuses(false)}
> >
Hide additional statuses {intl.formatMessage(messages.hideAdditonalStatuses)}
</Button> </Button>
</div> </div>
</Stack> </Stack>
@ -126,7 +83,7 @@ const OtherActionsStep = ({ account }: IOtherActionsStep) => {
size='sm' size='sm'
onClick={() => setShowAdditionalStatuses(true)} onClick={() => setShowAdditionalStatuses(true)}
> >
Add more {intl.formatMessage(messages.addMore)}
</Button> </Button>
)} )}
</FormGroup> </FormGroup>
@ -134,7 +91,9 @@ const OtherActionsStep = ({ account }: IOtherActionsStep) => {
)} )}
<Stack space={2}> <Stack space={2}>
<Text tag='h1' size='xl' weight='semibold'>Further actions:</Text> <Text tag='h1' size='xl' weight='semibold'>
{intl.formatMessage(messages.furtherActions)}
</Text>
<FormGroup <FormGroup
labelText={<FormattedMessage id='report.block_hint' defaultMessage='Do you also want to block this account?' />} labelText={<FormattedMessage id='report.block_hint' defaultMessage='Do you also want to block this account?' />}

@ -1,56 +1,20 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { Set as ImmutableSet } from 'immutable'; import React, { useEffect, useRef, useState } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { changeReportComment, changeReportRule } from 'soapbox/actions/reports'; import { changeReportComment, changeReportRule } from 'soapbox/actions/reports';
import { fetchRules } from 'soapbox/actions/rules'; import { fetchRules } from 'soapbox/actions/rules';
import AttachmentThumbs from 'soapbox/components/attachment_thumbs'; import { FormGroup, Stack, Text, Textarea } from 'soapbox/components/ui';
import StatusContent from 'soapbox/components/status_content';
import { FormGroup, HStack, Stack, Text, Textarea } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account_container';
import { useAppSelector } from 'soapbox/hooks'; import { useAppSelector } from 'soapbox/hooks';
import type { ReducerAccount } from 'soapbox/reducers/accounts'; import type { ReducerAccount } from 'soapbox/reducers/accounts';
const messages = defineMessages({ const messages = defineMessages({
placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' }, placeholder: { id: 'report.placeholder', defaultMessage: 'Additional comments' },
reasonForReporting: { id: 'report.reason.title', defaultMessage: 'Reason for reporting' },
}); });
const SelectedStatus = ({ statusId }: { statusId: string }) => {
const status = useAppSelector((state) => state.statuses.get(statusId));
if (!status) {
return null;
}
return (
<Stack space={2} className='p-4 rounded-lg bg-gray-100 dark:bg-slate-700'>
<AccountContainer
id={status.get('account') as any}
showProfileHoverCard={false}
timestamp={status.get('created_at')}
hideActions
/>
<StatusContent
status={status}
expanded
collapsable
/>
{status.get('media_attachments').size > 0 && (
<AttachmentThumbs
compact
media={status.get('media_attachments')}
sensitive={status.get('sensitive')}
/>
)}
</Stack>
);
};
interface IReasonStep { interface IReasonStep {
account: ReducerAccount account: ReducerAccount
} }
@ -64,25 +28,11 @@ const ReasonStep = (_props: IReasonStep) => {
const [isNearBottom, setNearBottom] = useState<boolean>(false); const [isNearBottom, setNearBottom] = useState<boolean>(false);
const [isNearTop, setNearTop] = useState<boolean>(true); const [isNearTop, setNearTop] = useState<boolean>(true);
const selectedStatusIds = useAppSelector((state) => state.reports.getIn(['new', 'status_ids']) as ImmutableSet<string>);
const comment = useAppSelector((state) => state.reports.getIn(['new', 'comment']) as string); const comment = useAppSelector((state) => state.reports.getIn(['new', 'comment']) as string);
const rules = useAppSelector((state) => state.rules.items); const rules = useAppSelector((state) => state.rules.items);
const ruleId = useAppSelector((state) => state.reports.getIn(['new', 'rule_id']) as boolean); const ruleId = useAppSelector((state) => state.reports.getIn(['new', 'rule_id']) as boolean);
const shouldRequireRule = rules.length > 0; const shouldRequireRule = rules.length > 0;
const renderSelectedStatuses = useCallback(() => {
switch (selectedStatusIds.size) {
case 0:
return (
<div className='bg-gray-100 dark:bg-slate-700 p-4 rounded-lg flex items-center justify-center w-full'>
<Text theme='muted'>You have removed all statuses from being selected.</Text>
</div>
);
default:
return <SelectedStatus statusId={selectedStatusIds.first()} />;
}
}, [selectedStatusIds.size]);
const handleCommentChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => { const handleCommentChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
dispatch(changeReportComment(event.target.value)); dispatch(changeReportComment(event.target.value));
}; };
@ -111,11 +61,11 @@ const ReasonStep = (_props: IReasonStep) => {
return ( return (
<Stack space={4}> <Stack space={4}>
{renderSelectedStatuses()}
{shouldRequireRule && ( {shouldRequireRule && (
<Stack space={2}> <Stack space={2}>
<Text size='xl' weight='semibold' tag='h1'>Reason for reporting</Text> <Text size='xl' weight='semibold' tag='h1'>
{intl.formatMessage(messages.reasonForReporting)}
</Text>
<div className='relative'> <div className='relative'>
<div <div
@ -138,16 +88,17 @@ const ReasonStep = (_props: IReasonStep) => {
'bg-gray-50 dark:bg-slate-900': isSelected, 'bg-gray-50 dark:bg-slate-900': isSelected,
})} })}
> >
<div className='mr-3 flex flex-col'> <Stack className='mr-3'>
<Text <Text
tag='span'
size='sm' size='sm'
weight='medium' weight='medium'
theme={isSelected ? 'primary' : 'default'} theme={isSelected ? 'primary' : 'default'}
> >
{rule.text} {rule.text}
</Text> </Text>
<Text theme='muted' size='sm'>{rule.subtext}</Text> <Text tag='span' theme='muted' size='sm'>{rule.subtext}</Text>
</div> </Stack>
<input <input
name='reason' name='reason'

@ -920,6 +920,7 @@
"settings.save.success": "Your preferences have been saved!", "settings.save.success": "Your preferences have been saved!",
"settings.security": "Security", "settings.security": "Security",
"settings.settings": "Settings", "settings.settings": "Settings",
"shared.tos": "Terms of Service",
"signup_panel.subtitle": "Sign up now to discuss.", "signup_panel.subtitle": "Sign up now to discuss.",
"signup_panel.title": "New to {site_title}?", "signup_panel.title": "New to {site_title}?",
"snackbar.view": "View", "snackbar.view": "View",

Loading…
Cancel
Save