|
|
@@ -1,79 +1,95 @@
|
|
|
-/* eslint-disable react/prop-types */
|
|
|
-import React, { useCallback } from 'react';
|
|
|
+import React, {
|
|
|
+ useCallback, useMemo, useState,
|
|
|
+} from 'react';
|
|
|
|
|
|
-import { HasObjectId, IAttachment } from '@growi/core';
|
|
|
+import type { IUser } from '@growi/core';
|
|
|
import { UserPicture } from '@growi/ui/dist/components/User/UserPicture';
|
|
|
import {
|
|
|
- Button,
|
|
|
- Modal, ModalHeader, ModalBody, ModalFooter,
|
|
|
+ Button, Modal, ModalHeader, ModalBody, ModalFooter,
|
|
|
} from 'reactstrap';
|
|
|
|
|
|
+import { toastSuccess, toastError } from '~/client/util/toastr';
|
|
|
+import { useDeleteAttachmentModal } from '~/stores/modal';
|
|
|
+import loggerFactory from '~/utils/logger';
|
|
|
+
|
|
|
import { Username } from '../User/Username';
|
|
|
|
|
|
import styles from './DeleteAttachmentModal.module.scss';
|
|
|
|
|
|
+const logger = loggerFactory('growi:attachmentDelete');
|
|
|
+
|
|
|
+const iconByFormat = (format: string): string => {
|
|
|
+ return format.match(/image\/.+/i) ? 'icon-picture' : 'icon-doc';
|
|
|
+};
|
|
|
|
|
|
-function iconNameByFormat(format: string): string {
|
|
|
- if (format.match(/image\/.+/i)) {
|
|
|
- return 'icon-picture';
|
|
|
- }
|
|
|
+export const DeleteAttachmentModal: React.FC = () => {
|
|
|
+ const [deleting, setDeleting] = useState<boolean>(false);
|
|
|
+ const [deleteError, setDeleteError] = useState<string>('');
|
|
|
|
|
|
- return 'icon-doc';
|
|
|
-}
|
|
|
+ const { data: deleteAttachmentModal, close: closeDeleteAttachmentModal } = useDeleteAttachmentModal();
|
|
|
+ const isOpen = deleteAttachmentModal?.isOpened;
|
|
|
+ const attachment = deleteAttachmentModal?.attachment;
|
|
|
+ const remove = deleteAttachmentModal?.remove;
|
|
|
|
|
|
+ const toggleHandler = useCallback(() => {
|
|
|
+ closeDeleteAttachmentModal();
|
|
|
+ setDeleting(false);
|
|
|
+ setDeleteError('');
|
|
|
+ }, [closeDeleteAttachmentModal]);
|
|
|
|
|
|
-type Props = {
|
|
|
- isOpen: boolean,
|
|
|
- toggle: () => void,
|
|
|
- attachmentToDelete: IAttachment & HasObjectId | null,
|
|
|
- deleting: boolean,
|
|
|
- deleteError: string,
|
|
|
- onAttachmentDeleteClickedConfirm?: (attachment: IAttachment & HasObjectId) => Promise<void>,
|
|
|
-}
|
|
|
+ const onClickDeleteButtonHandler = useCallback(async() => {
|
|
|
+ if (remove == null || attachment == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
-export const DeleteAttachmentModal = (props: Props): JSX.Element => {
|
|
|
+ setDeleting(true);
|
|
|
|
|
|
- const {
|
|
|
- isOpen, toggle,
|
|
|
- attachmentToDelete, deleting, deleteError,
|
|
|
- onAttachmentDeleteClickedConfirm,
|
|
|
- } = props;
|
|
|
+ try {
|
|
|
+ await remove({ attachment_id: attachment._id });
|
|
|
+ setDeleting(false);
|
|
|
+ closeDeleteAttachmentModal();
|
|
|
+ toastSuccess(`Delete ${attachment.originalName}`);
|
|
|
+ }
|
|
|
+ catch (err) {
|
|
|
+ setDeleting(false);
|
|
|
+ setDeleteError('Something went wrong.');
|
|
|
+ toastError(err);
|
|
|
+ logger.error(err);
|
|
|
+ }
|
|
|
+ }, [attachment, closeDeleteAttachmentModal, remove]);
|
|
|
|
|
|
- const onDeleteConfirm = useCallback(() => {
|
|
|
- if (attachmentToDelete == null || onAttachmentDeleteClickedConfirm == null) {
|
|
|
+ const attachmentFileFormat = useMemo(() => {
|
|
|
+ if (attachment == null) {
|
|
|
return;
|
|
|
}
|
|
|
- onAttachmentDeleteClickedConfirm(attachmentToDelete);
|
|
|
- }, [attachmentToDelete, onAttachmentDeleteClickedConfirm]);
|
|
|
|
|
|
- const renderByFileFormat = useCallback((attachment) => {
|
|
|
const content = (attachment.fileFormat.match(/image\/.+/i))
|
|
|
// eslint-disable-next-line @next/next/no-img-element
|
|
|
? <img src={attachment.filePathProxied} alt="deleting image" />
|
|
|
: '';
|
|
|
|
|
|
-
|
|
|
return (
|
|
|
<div className="attachment-delete-image">
|
|
|
<p>
|
|
|
- <i className={iconNameByFormat(attachment.fileFormat)}></i> {attachment.originalName}
|
|
|
+ <i className={iconByFormat(attachment.fileFormat)}></i> {attachment.originalName}
|
|
|
</p>
|
|
|
<p>
|
|
|
- uploaded by <UserPicture user={attachment.creator} size="sm"></UserPicture> <Username user={attachment.creator}></Username>
|
|
|
+ uploaded by <UserPicture user={attachment.creator} size="sm"></UserPicture> <Username user={attachment.creator as IUser}></Username>
|
|
|
</p>
|
|
|
{content}
|
|
|
</div>
|
|
|
);
|
|
|
- }, []);
|
|
|
-
|
|
|
- let deletingIndicator = <></>;
|
|
|
- if (deleting) {
|
|
|
- deletingIndicator = <div className="speeding-wheel-sm"></div>;
|
|
|
- }
|
|
|
- if (deleteError) {
|
|
|
- deletingIndicator = <span>{deleteError}</span>;
|
|
|
- }
|
|
|
+ }, [attachment]);
|
|
|
|
|
|
+ const deletingIndicator = useMemo(() => {
|
|
|
+ if (deleting) {
|
|
|
+ return <div className="speeding-wheel-sm"></div>;
|
|
|
+ }
|
|
|
+ if (deleteError) {
|
|
|
+ return <span>{deleteError}</span>;
|
|
|
+ }
|
|
|
+ return <></>;
|
|
|
+ }, [deleting, deleteError]);
|
|
|
|
|
|
return (
|
|
|
<Modal
|
|
|
@@ -83,11 +99,11 @@ export const DeleteAttachmentModal = (props: Props): JSX.Element => {
|
|
|
aria-labelledby="contained-modal-title-lg"
|
|
|
fade={false}
|
|
|
>
|
|
|
- <ModalHeader tag="h4" toggle={toggle} className="bg-danger text-light">
|
|
|
+ <ModalHeader tag="h4" toggle={toggleHandler} className="bg-danger text-light">
|
|
|
<span id="contained-modal-title-lg">Delete attachment?</span>
|
|
|
</ModalHeader>
|
|
|
<ModalBody>
|
|
|
- {renderByFileFormat(attachmentToDelete)}
|
|
|
+ {attachmentFileFormat}
|
|
|
</ModalBody>
|
|
|
<ModalFooter>
|
|
|
<div className="mr-3 d-inline-block">
|
|
|
@@ -95,12 +111,11 @@ export const DeleteAttachmentModal = (props: Props): JSX.Element => {
|
|
|
</div>
|
|
|
<Button
|
|
|
color="danger"
|
|
|
- onClick={onDeleteConfirm}
|
|
|
+ onClick={onClickDeleteButtonHandler}
|
|
|
disabled={deleting}
|
|
|
- >Delete!
|
|
|
+ >Delete
|
|
|
</Button>
|
|
|
</ModalFooter>
|
|
|
</Modal>
|
|
|
);
|
|
|
-
|
|
|
};
|