blob: 47b4463f0b9673773c6b0da5b7794f6d169c0e4e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { memo, FC } from 'react';
import { Button, Dropdown } from 'react-bootstrap';
import { Link, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Modal } from '@/components';
import { useReportModal, useToast } from '@/hooks';
import { useCaptchaPlugin } from '@/utils/pluginKit';
import { QuestionOperationReq } from '@/common/interface';
import Share from '../Share';
import {
deleteQuestion,
deleteAnswer,
editCheck,
reopenQuestion,
questionOperation,
unDeleteAnswer,
unDeleteQuestion,
} from '@/services';
import { tryNormalLogged } from '@/utils/guard';
import { floppyNavigation } from '@/utils';
import { toastStore } from '@/stores';
interface IProps {
type: 'answer' | 'question';
qid: string;
aid?: string;
title: string;
hasAnswer?: boolean;
isAccepted: boolean;
callback: (type: string) => void;
memberActions;
}
const Index: FC<IProps> = ({
type,
qid,
aid = '',
title,
isAccepted = false,
hasAnswer = false,
memberActions = [],
callback,
}) => {
const { t } = useTranslation('translation', { keyPrefix: 'delete' });
const toast = useToast();
const navigate = useNavigate();
const reportModal = useReportModal();
const dCaptcha = useCaptchaPlugin('delete');
const refreshQuestion = () => {
callback?.('default');
};
const closeModal = useReportModal(refreshQuestion);
const editUrl =
type === 'answer' ? `/posts/${qid}/${aid}/edit` : `/posts/${qid}/edit`;
const handleReport = () => {
reportModal.onShow({
type,
id: type === 'answer' ? aid : qid,
action: 'flag',
});
};
const handleClose = () => {
closeModal.onShow({
type,
id: qid,
action: 'close',
});
};
const submitDeleteQuestion = () => {
const req = {
id: qid,
captcha_code: undefined,
captcha_id: undefined,
};
dCaptcha?.resolveCaptchaReq(req);
deleteQuestion(req)
.then(async () => {
await dCaptcha?.close();
toast.onShow({
msg: t('post_deleted', { keyPrefix: 'messages' }),
variant: 'success',
});
callback?.('delete_question');
})
.catch((ex) => {
if (ex.isError) {
dCaptcha?.handleCaptchaError(ex.list);
}
});
};
const submitDeleteAnswer = () => {
const req = {
id: aid,
captcha_code: undefined,
captcha_id: undefined,
};
dCaptcha?.resolveCaptchaReq(req);
deleteAnswer(req)
.then(async () => {
await dCaptcha?.close();
// refresh page
toast.onShow({
msg: t('tip_answer_deleted'),
variant: 'success',
});
callback?.('delete_answer');
})
.catch((ex) => {
if (ex.isError) {
dCaptcha?.handleCaptchaError(ex.list);
}
});
};
const handleDelete = () => {
if (type === 'question') {
Modal.confirm({
title: t('title'),
content: hasAnswer ? t('question') : t('other'),
cancelBtnVariant: 'link',
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
if (!dCaptcha) {
submitDeleteQuestion();
return;
}
dCaptcha.check(() => {
submitDeleteQuestion();
});
},
});
}
if (type === 'answer' && aid) {
Modal.confirm({
title: t('title'),
content: isAccepted ? t('answer_accepted') : t('other'),
cancelBtnVariant: 'link',
confirmBtnVariant: 'danger',
confirmText: t('delete', { keyPrefix: 'btns' }),
onConfirm: () => {
if (!dCaptcha) {
submitDeleteAnswer();
return;
}
dCaptcha.check(() => {
submitDeleteAnswer();
});
},
});
}
};
const handleUndelete = () => {
Modal.confirm({
title: t('undelete_title'),
content: t('undelete_desc'),
cancelBtnVariant: 'link',
confirmBtnVariant: 'danger',
confirmText: t('undelete', { keyPrefix: 'btns' }),
onConfirm: () => {
if (type === 'question') {
unDeleteQuestion(qid).then(() => {
callback?.('default');
});
}
if (type === 'answer') {
unDeleteAnswer(aid).then(() => {
callback?.('all');
});
}
},
});
};
const handleEdit = (evt, targetUrl) => {
if (!floppyNavigation.shouldProcessLinkClick(evt)) {
return;
}
evt.preventDefault();
let checkObjectId = qid;
if (type === 'answer') {
checkObjectId = aid;
}
editCheck(checkObjectId).then(() => {
navigate(targetUrl);
});
};
const handleReopen = () => {
Modal.confirm({
title: t('title', { keyPrefix: 'question_detail.reopen' }),
content: t('content', { keyPrefix: 'question_detail.reopen' }),
cancelBtnVariant: 'link',
confirmText: t('confirm_btn', { keyPrefix: 'question_detail.reopen' }),
onConfirm: () => {
reopenQuestion({
question_id: qid,
}).then(() => {
toast.onShow({
msg: t('post_reopen', { keyPrefix: 'messages' }),
variant: 'success',
});
refreshQuestion();
});
},
});
};
const handleCommon = async (params) => {
await questionOperation(params);
let msg = '';
if (params.operation === 'pin') {
msg = t('post_pin', { keyPrefix: 'messages' });
}
if (params.operation === 'unpin') {
msg = t('post_unpin', { keyPrefix: 'messages' });
}
if (params.operation === 'hide') {
msg = t('post_hide_list', { keyPrefix: 'messages' });
}
if (params.operation === 'show') {
msg = t('post_show_list', { keyPrefix: 'messages' });
}
toastStore.getState().show({
msg,
variant: 'success',
});
setTimeout(() => {
refreshQuestion();
}, 100);
};
const handlOtherActions = (action) => {
const params: QuestionOperationReq = {
id: qid,
operation: action,
};
if (action === 'pin') {
Modal.confirm({
title: t('title', { keyPrefix: 'question_detail.pin' }),
content: t('content', { keyPrefix: 'question_detail.pin' }),
cancelBtnVariant: 'link',
confirmText: t('confirm_btn', { keyPrefix: 'question_detail.pin' }),
onConfirm: () => {
handleCommon(params);
},
});
} else {
handleCommon(params);
}
};
const handleAction = (action) => {
if (!tryNormalLogged(true)) {
return;
}
if (action === 'delete') {
handleDelete();
}
if (action === 'undelete') {
handleUndelete();
}
if (action === 'report') {
handleReport();
}
if (action === 'close') {
handleClose();
}
if (action === 'reopen') {
handleReopen();
}
if (
action === 'pin' ||
action === 'unpin' ||
action === 'hide' ||
action === 'show'
) {
handlOtherActions(action);
}
};
const firstAction =
memberActions?.filter(
(v) =>
v.action === 'report' ||
v.action === 'edit' ||
v.action === 'delete' ||
v.action === 'undelete',
) || [];
const secondAction =
memberActions?.filter(
(v) =>
v.action === 'close' ||
v.action === 'reopen' ||
v.action === 'pin' ||
v.action === 'unpin' ||
v.action === 'hide' ||
v.action === 'show',
) || [];
return (
<div className="d-flex align-items-center">
<Share type={type} qid={qid} aid={aid} title={title} />
{firstAction?.map((item) => {
if (item.action === 'edit') {
return (
<Link
key={item.action}
to={editUrl}
className="link-secondary p-0 small ms-3"
onClick={(evt) => handleEdit(evt, editUrl)}
style={{ lineHeight: '23px' }}>
{item.name}
</Link>
);
}
return (
<Button
key={item.action}
variant="link"
size="sm"
className="link-secondary p-0 ms-3"
onClick={() => handleAction(item.action)}>
{item.name}
</Button>
);
})}
{secondAction.length > 0 && (
<Dropdown className="ms-3 d-flex">
<Dropdown.Toggle
variant="link"
size="sm"
className="link-secondary p-0 no-toggle">
{t('action', { keyPrefix: 'question_detail' })}
</Dropdown.Toggle>
<Dropdown.Menu>
{secondAction.map((item) => {
return (
<Dropdown.Item
key={item.action}
onClick={() => handleAction(item.action)}>
{item.name}
</Dropdown.Item>
);
})}
</Dropdown.Menu>
</Dropdown>
)}
</div>
);
};
export default memo(Index);