Move report modal state to useState

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak
2024-09-17 23:03:31 +02:00
parent c7dba3334f
commit 0c7d57ce40
14 changed files with 124 additions and 239 deletions

View File

@ -2,7 +2,6 @@ import React, { Suspense, lazy } from 'react';
import { cancelReplyCompose } from 'pl-fe/actions/compose';
import { cancelEventCompose } from 'pl-fe/actions/events';
import { cancelReport } from 'pl-fe/actions/reports';
import Base from 'pl-fe/components/modal-root';
import { useAppDispatch } from 'pl-fe/hooks';
import { useModalsStore } from 'pl-fe/stores';
@ -73,9 +72,6 @@ const ModalRoot: React.FC = () => {
case 'COMPOSE_EVENT':
dispatch(cancelEventCompose());
break;
case 'REPORT':
dispatch(cancelReport());
break;
default:
break;
}

View File

@ -1,23 +1,22 @@
import noop from 'lodash/noop';
import React, { Suspense } from 'react';
import { toggleStatusReport } from 'pl-fe/actions/reports';
import StatusContent from 'pl-fe/components/status-content';
import { Stack, Toggle } from 'pl-fe/components/ui';
import { MediaGallery, Video, Audio } from 'pl-fe/features/ui/util/async-components';
import { useAppDispatch, useAppSelector } from 'pl-fe/hooks';
import { useAppSelector } from 'pl-fe/hooks';
interface IStatusCheckBox {
id: string;
disabled?: boolean;
toggleStatusReport: (value: boolean) => void;
checked: boolean;
}
const StatusCheckBox: React.FC<IStatusCheckBox> = ({ id, disabled }) => {
const dispatch = useAppDispatch();
const StatusCheckBox: React.FC<IStatusCheckBox> = ({ id, disabled, checked, toggleStatusReport }) => {
const status = useAppSelector((state) => state.statuses.get(id));
const checked = useAppSelector((state) => state.reports.new.status_ids.includes(id));
const onToggle: React.ChangeEventHandler<HTMLInputElement> = (e) => dispatch(toggleStatusReport(id, e.target.checked));
const onToggle: React.ChangeEventHandler<HTMLInputElement> = (e) => toggleStatusReport(e.target.checked);
if (!status || status.reblog_id) {
return null;

View File

@ -64,19 +64,25 @@ const SelectedStatus = ({ statusId }: { statusId: string }) => {
);
};
const ReportModal = ({ onClose }: BaseModalProps) => {
interface ReportModalProps {
accountId: string;
entityType: ReportableEntities;
statusIds: Array<string>;
}
const ReportModal: React.FC<BaseModalProps & ReportModalProps> = ({ onClose, accountId, entityType, statusIds }) => {
const dispatch = useAppDispatch();
const intl = useIntl();
const accountId = useAppSelector((state) => state.reports.new.account_id);
const { account } = useAccount(accountId || undefined);
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 [block, setBlock] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const { rules } = useInstance();
const ruleIds = useAppSelector((state) => state.reports.new.rule_ids);
const selectedStatusIds = useAppSelector((state) => state.reports.new.status_ids);
const [ruleIds, setRuleIds] = useState<Array<string>>([]);
const [selectedStatusIds, setSelectedStatusIds] = useState(statusIds);
const [comment, setComment] = useState('');
const [forward, setForward] = useState(false);
const shouldRequireRule = rules.length > 0;
@ -86,17 +92,25 @@ const ReportModal = ({ onClose }: BaseModalProps) => {
const [currentStep, setCurrentStep] = useState<Steps>(Steps.ONE);
const handleSubmit = () => {
dispatch(submitReport())
.then(() => setCurrentStep(Steps.THREE))
.catch((error) => dispatch(submitReportFail(error)));
setIsSubmitting(true);
if (isBlocked && account) {
dispatch(submitReport(accountId, selectedStatusIds, [...ruleIds], comment, forward))
.then(() => {
setIsSubmitting(false);
setCurrentStep(Steps.THREE);
})
.catch((error) => {
setIsSubmitting(false);
dispatch(submitReportFail(error));
});
if (block && account) {
dispatch(blockAccount(account.id));
}
};
const renderSelectedStatuses = useCallback(() => {
switch (selectedStatusIds.size) {
switch (selectedStatusIds.length) {
case 0:
return (
<div className='flex w-full items-center justify-center rounded-lg bg-gray-100 p-4 dark:bg-gray-800'>
@ -104,9 +118,9 @@ const ReportModal = ({ onClose }: BaseModalProps) => {
</div>
);
default:
return <SelectedStatus statusId={selectedStatusIds.first()} />;
return <SelectedStatus statusId={selectedStatusIds[0]} />;
}
}, [selectedStatusIds.size]);
}, [selectedStatusIds.length]);
const cancelText = useMemo(() => {
switch (currentStep) {
@ -174,8 +188,8 @@ const ReportModal = ({ onClose }: BaseModalProps) => {
return false;
}
return isSubmitting || (shouldRequireRule && ruleIds.isEmpty()) || (isReportingStatus && selectedStatusIds.size === 0);
}, [currentStep, isSubmitting, shouldRequireRule, ruleIds, selectedStatusIds.size, isReportingStatus]);
return isSubmitting || (shouldRequireRule && ruleIds.length === 0) || (isReportingStatus && selectedStatusIds.length === 0);
}, [currentStep, isSubmitting, shouldRequireRule, ruleIds.length, selectedStatusIds.length, isReportingStatus]);
const calculateProgress = useCallback(() => {
switch (currentStep) {
@ -219,11 +233,24 @@ const ReportModal = ({ onClose }: BaseModalProps) => {
{(currentStep !== Steps.THREE && !isReportingAccount) && renderSelectedEntity()}
{StepToRender && (
<StepToRender account={account} />
<StepToRender
account={account}
selectedStatusIds={selectedStatusIds}
setSelectedStatusIds={setSelectedStatusIds}
block={block}
setBlock={setBlock}
forward={forward}
setForward={setForward}
comment={comment}
setComment={setComment}
ruleIds={ruleIds}
setRuleIds={setRuleIds}
isSubmitting={isSubmitting}
/>
)}
</Stack>
</Modal>
);
};
export { ReportModal as default };
export { ReportModal as default, type ReportModalProps };

View File

@ -2,10 +2,9 @@ import { OrderedSet } from 'immutable';
import React, { useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { changeReportBlock, changeReportForward } from 'pl-fe/actions/reports';
import { Button, FormGroup, HStack, Stack, Text, Toggle } from 'pl-fe/components/ui';
import StatusCheckBox from 'pl-fe/features/ui/components/modals/report-modal/components/status-check-box';
import { useAppDispatch, useAppSelector, useFeatures } from 'pl-fe/hooks';
import { useAppSelector, useFeatures } from 'pl-fe/hooks';
import { getDomain } from 'pl-fe/utils/accounts';
import type { Account } from 'pl-fe/normalizers';
@ -20,27 +19,49 @@ const messages = defineMessages({
interface IOtherActionsStep {
account: Pick<Account, 'id' | 'acct' | 'local' | 'url'>;
selectedStatusIds: string[];
setSelectedStatusIds: (value: string[]) => void;
block: boolean;
setBlock: (value: boolean) => void;
forward: boolean;
setForward: (value: boolean) => void;
isSubmitting: boolean;
}
const OtherActionsStep = ({ account }: IOtherActionsStep) => {
const dispatch = useAppDispatch();
const OtherActionsStep = ({
account,
selectedStatusIds,
setSelectedStatusIds,
block,
setBlock,
forward,
setForward,
isSubmitting,
}: IOtherActionsStep) => {
const features = useFeatures();
const intl = useIntl();
const statusIds = useAppSelector((state) => OrderedSet(state.timelines.get(`account:${account.id}:with_replies`)!.items).union(state.reports.new.status_ids) as OrderedSet<string>);
const isBlocked = useAppSelector((state) => state.reports.new.block);
const isForward = useAppSelector((state) => state.reports.new.forward);
const statusIds = useAppSelector((state) => OrderedSet(state.timelines.get(`account:${account.id}:with_replies`)!.items).union(selectedStatusIds) as OrderedSet<string>);
const isBlocked = block;
const isForward = forward;
const canForward = !account.local && features.federating;
const isSubmitting = useAppSelector((state) => state.reports.new.isSubmitting);
const [showAdditionalStatuses, setShowAdditionalStatuses] = useState<boolean>(false);
const handleBlockChange = (event: React.ChangeEvent<HTMLInputElement>) => {
dispatch(changeReportBlock(event.target.checked));
setBlock(event.target.checked);
};
const handleForwardChange = (event: React.ChangeEvent<HTMLInputElement>) => {
dispatch(changeReportForward(event.target.checked));
setForward(event.target.checked);
};
const toggleStatusReport = (statusId: string) => (value: boolean) => {
let newStatusIds = selectedStatusIds;
if (value && !selectedStatusIds.includes(statusId)) newStatusIds = [...selectedStatusIds, statusId];
if (!value) newStatusIds = selectedStatusIds.filter(id => id !== statusId);
setSelectedStatusIds(newStatusIds);
};
return (
@ -54,7 +75,14 @@ const OtherActionsStep = ({ account }: IOtherActionsStep) => {
{showAdditionalStatuses ? (
<Stack space={2}>
<div className='divide-y divide-solid divide-gray-200 dark:divide-gray-800'>
{statusIds.map((statusId) => <StatusCheckBox id={statusId} key={statusId} />)}
{statusIds.map((statusId) => (
<StatusCheckBox
id={statusId}
key={statusId}
checked={selectedStatusIds.includes(statusId)}
toggleStatusReport={toggleStatusReport(statusId)}
/>
))}
</div>
<div>

View File

@ -2,9 +2,8 @@ import clsx from 'clsx';
import React, { useEffect, useRef, useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { changeReportComment, changeReportRule } from 'pl-fe/actions/reports';
import { FormGroup, Stack, Text, Textarea } from 'pl-fe/components/ui';
import { useAppDispatch, useAppSelector, useInstance } from 'pl-fe/hooks';
import { useInstance } from 'pl-fe/hooks';
import type { Account } from 'pl-fe/normalizers';
@ -15,12 +14,15 @@ const messages = defineMessages({
interface IReasonStep {
account?: Account;
comment: string;
setComment: (value: string) => void;
ruleIds: Array<string>;
setRuleIds: (value: Array<string>) => void;
}
const RULES_HEIGHT = 385;
const ReasonStep: React.FC<IReasonStep> = () => {
const dispatch = useAppDispatch();
const ReasonStep: React.FC<IReasonStep> = ({ comment, setComment, ruleIds, setRuleIds }) => {
const intl = useIntl();
const rulesListRef = useRef(null);
@ -28,13 +30,18 @@ const ReasonStep: React.FC<IReasonStep> = () => {
const [isNearBottom, setNearBottom] = useState<boolean>(false);
const [isNearTop, setNearTop] = useState<boolean>(true);
const comment = useAppSelector((state) => state.reports.new.comment);
const { rules } = useInstance();
const ruleIds = useAppSelector((state) => state.reports.new.rule_ids);
const shouldRequireRule = rules.length > 0;
const handleCommentChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
dispatch(changeReportComment(event.target.value));
setComment(event.target.value);
};
const handleRuleChange = (ruleId: string) => {
let newRuleIds;
if (ruleIds.includes(ruleId)) newRuleIds = ruleIds.filter(id => id !== ruleId);
else newRuleIds = [...ruleIds, ruleId];
setRuleIds(newRuleIds);
};
const handleRulesScrolling = () => {
@ -81,13 +88,13 @@ const ReasonStep: React.FC<IReasonStep> = () => {
ref={rulesListRef}
>
{rules.map((rule, idx) => {
const isSelected = ruleIds.includes(String(rule.id));
const isSelected = ruleIds.includes(rule.id);
return (
<button
key={idx}
data-testid={`rule-${rule.id}`}
onClick={() => dispatch(changeReportRule(rule.id))}
onClick={() => handleRuleChange(rule.id)}
className={clsx({
'relative border border-solid border-gray-200 dark:border-gray-800 hover:bg-gray-100 dark:hover:bg-primary-800/30 text-start w-full p-4 flex justify-between items-center cursor-pointer': true,
'rounded-tl-lg rounded-tr-lg': idx === 0,