yuken 4 лет назад
Родитель
Сommit
635f37831e

+ 0 - 71
packages/app/src/components/EmptyTrashModal.jsx

@@ -1,71 +0,0 @@
-import React, { useState } from 'react';
-import PropTypes from 'prop-types';
-
-import {
-  Modal, ModalHeader, ModalBody, ModalFooter,
-} from 'reactstrap';
-
-import { withTranslation } from 'react-i18next';
-import { withUnstatedContainers } from './UnstatedUtils';
-
-import SocketIoContainer from '~/client/services/SocketIoContainer';
-import AppContainer from '~/client/services/AppContainer';
-import ApiErrorMessageList from './PageManagement/ApiErrorMessageList';
-
-const EmptyTrashModal = (props) => {
-  const {
-    t, isOpen, onClose, appContainer, socketIoContainer,
-  } = props;
-
-  const [errs, setErrs] = useState(null);
-
-  async function emptyTrash() {
-    setErrs(null);
-
-    try {
-      await appContainer.apiv3Delete('/pages/empty-trash');
-      window.location.reload();
-    }
-    catch (err) {
-      setErrs(err);
-    }
-  }
-
-  function emptyButtonHandler() {
-    emptyTrash();
-  }
-
-  return (
-    <Modal isOpen={isOpen} toggle={onClose} className="grw-create-page">
-      <ModalHeader tag="h4" toggle={onClose} className="bg-danger text-light">
-        { t('modal_empty.empty_the_trash')}
-      </ModalHeader>
-      <ModalBody>
-        { t('modal_empty.notice')}
-      </ModalBody>
-      <ModalFooter>
-        <ApiErrorMessageList errs={errs} />
-        <button type="button" className="btn btn-danger" onClick={emptyButtonHandler}>
-          <i className="icon-trash mr-2" aria-hidden="true"></i> Empty
-        </button>
-      </ModalFooter>
-    </Modal>
-  );
-};
-
-/**
- * Wrapper component for using unstated
- */
-const EmptyTrashModalWrapper = withUnstatedContainers(EmptyTrashModal, [AppContainer, SocketIoContainer]);
-
-
-EmptyTrashModal.propTypes = {
-  t: PropTypes.func.isRequired, //  i18next
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-  socketIoContainer: PropTypes.instanceOf(SocketIoContainer),
-
-  isOpen: PropTypes.bool.isRequired,
-  onClose: PropTypes.func.isRequired,
-};
-
-export default withTranslation()(EmptyTrashModalWrapper);

+ 203 - 0
packages/app/src/components/EmptyTrashModal.tsx

@@ -0,0 +1,203 @@
+import React, {
+  useState, FC, useMemo, useCallback,
+} from 'react';
+
+import { useTranslation } from 'react-i18next';
+import {
+  Modal, ModalHeader, ModalBody, ModalFooter,
+} from 'reactstrap';
+
+import { apiPost } from '~/client/util/apiv1-client';
+import { apiv3Delete, apiv3Post } from '~/client/util/apiv3-client';
+import { HasObjectId } from '~/interfaces/has-object-id';
+import {
+  IDeleteSinglePageApiv1Result, IDeleteManyPageApiv3Result, IPageToDeleteWithMeta, IDataWithMeta, isIPageInfoForEntity, IPageInfoForEntity,
+} from '~/interfaces/page';
+import { usePageDeleteModal } from '~/stores/modal';
+import { useSWRxPageInfoForList } from '~/stores/page';
+import loggerFactory from '~/utils/logger';
+
+
+import ApiErrorMessageList from './PageManagement/ApiErrorMessageList';
+
+import { isTrashPage } from '^/../core/src/utils/page-path-utils';
+
+
+const logger = loggerFactory('growi:cli:PageDeleteModal');
+
+
+const deleteIconAndKey = {
+  completely: {
+    color: 'danger',
+    icon: 'fire',
+    translationKey: 'completely',
+  },
+  temporary: {
+    color: 'primary',
+    icon: 'trash',
+    translationKey: 'page',
+  },
+};
+
+const EmptyTrashModal: FC = () => {
+  const { t } = useTranslation();
+
+  const { data: emptyTrashModalData, close: closeEmptyTrashModal } = useEmptyTrashModal();
+
+  const isOpened = emptyTrashModalData?.isOpened ?? false;
+
+  const notOperatablePages: IPageToDeleteWithMeta[] = (deleteModalData?.pages ?? [])
+    .filter(p => !isIPageInfoForEntity(p.meta));
+  const notOperatablePageIds = notOperatablePages.map(p => p.data._id);
+
+  const { injectTo } = useSWRxPageInfoForList(notOperatablePageIds);
+
+  // inject IPageInfo to operate
+  let injectedPages: IDataWithMeta<HasObjectId & { path: string }, IPageInfoForEntity>[] | null = null;
+  if (emptyTrashModalData?.pages != null && notOperatablePageIds.length > 0) {
+    injectedPages = injectTo(emptyTrashModalData?.pages);
+  }
+
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  const [errs, setErrs] = useState<Error[] | null>(null);
+
+  async function emptyTrash() {
+    if (emptyTrashModalData == null || emptyTrashModalData.pages == null) {
+      return;
+    }
+
+    try {
+      await apiv3Delete('/pages/empty-trash');
+      if (onDeleted != null) {
+        onDeleted('', null, null);
+      }
+      closeEmptyTrashModal();
+    }
+    catch (err) {
+      setErrs([err]);
+    }
+  }
+
+  async function emptyTrashButtonHandler() {
+    await emptyTrash();
+  }
+
+  const renderPagePaths = () => {
+    const pages = injectedPages != null && injectedPages.length > 0 ? injectedPages : deleteModalData?.pages;
+
+    if (pages != null) {
+      return pages.map(page => (
+        <p key={page.data._id} className="mb-1">
+          <code>{ page.data.path }</code>
+          { !page.meta?.isDeletable && <span className="ml-3 text-danger"><strong>(CAN NOT TO DELETE)</strong></span> }
+        </p>
+      ));
+    }
+    return <></>;
+  };
+
+  return (
+    <Modal size="lg" isOpen={isOpened} toggle={closeEmptyTrashModal} data-testid="page-delete-modal" className="grw-create-page">
+      <ModalHeader tag="h4" toggle={closeEmptyTrashModal} className={`bg-${deleteIconAndKey[deleteMode].color} text-light`}>
+        <i className={`icon-fw icon-${deleteIconAndKey[deleteMode].icon}`}></i>
+        {t('ゴミ箱を空にする文言')}
+      </ModalHeader>
+      <ModalBody>
+        <div className="form-group grw-scrollable-modal-body pb-1">
+          <label>{ t('modal_delete.deleting_page') }:</label><br />
+          {/* Todo: change the way to show path on modal when too many pages are selected */}
+          {renderPagePaths()}
+        </div>
+        {t('ゴミ箱を空にする文言')}
+      </ModalBody>
+      <ModalFooter>
+        <ApiErrorMessageList errs={errs} />
+        <button
+          type="button"
+          className={`btn btn-${deleteIconAndKey[deleteMode].color}`}
+          onClick={emptyTrashButtonHandler}
+        >
+          <i className={`mr-1 icon-${deleteIconAndKey[deleteMode].icon}`} aria-hidden="true"></i>
+          {t('ゴミ箱を空にする文言')}
+        </button>
+      </ModalFooter>
+    </Modal>
+
+  );
+};
+
+export default EmptyTrashModal;
+
+
+// import React, { useState } from 'react';
+// import PropTypes from 'prop-types';
+
+// import {
+//   Modal, ModalHeader, ModalBody, ModalFooter,
+// } from 'reactstrap';
+
+// import { withTranslation } from 'react-i18next';
+// import { withUnstatedContainers } from './UnstatedUtils';
+
+// import SocketIoContainer from '~/client/services/SocketIoContainer';
+// import AppContainer from '~/client/services/AppContainer';
+// import ApiErrorMessageList from './PageManagement/ApiErrorMessageList';
+
+
+// const EmptyTrashModal = (props) => {
+//   const {
+//     t, isOpen, onClose, appContainer, socketIoContainer,
+//   } = props;
+
+//   const [errs, setErrs] = useState(null);
+
+//   async function emptyTrash() {
+//     setErrs(null);
+
+//     try {
+//       await appContainer.apiv3Delete('/pages/empty-trash');
+//       window.location.reload();
+//     }
+//     catch (err) {
+//       setErrs(err);
+//     }
+//   }
+
+//   function emptyButtonHandler() {
+//     emptyTrash();
+//   }
+
+//   return (
+//     <Modal isOpen={isOpen} toggle={onClose} className="grw-create-page">
+//       <ModalHeader tag="h4" toggle={onClose} className="bg-danger text-light">
+//         { t('modal_empty.empty_the_trash')}
+//       </ModalHeader>
+//       <ModalBody>
+//         { t('modal_empty.notice')}
+//       </ModalBody>
+//       <ModalFooter>
+//         <ApiErrorMessageList errs={errs} />
+//         <button type="button" className="btn btn-danger" onClick={emptyButtonHandler}>
+//           <i className="icon-trash mr-2" aria-hidden="true"></i> Empty
+//         </button>
+//       </ModalFooter>
+//     </Modal>
+//   );
+// };
+
+// /**
+//  * Wrapper component for using unstated
+//  */
+// const EmptyTrashModalWrapper = withUnstatedContainers(EmptyTrashModal, [AppContainer, SocketIoContainer]);
+
+
+// EmptyTrashModal.propTypes = {
+//   t: PropTypes.func.isRequired, //  i18next
+//   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+//   socketIoContainer: PropTypes.instanceOf(SocketIoContainer),
+
+//   isOpen: PropTypes.bool.isRequired,
+//   onClose: PropTypes.func.isRequired,
+// };
+
+// export default withTranslation()(EmptyTrashModalWrapper);