fix: Add ability to permanently delete documents in trash (#2192)
* Align false conditions before true * Update documents.delete endpoint for permanent delete * Add permanent delete to events table and integrate with socket.io * Add permanent delete to document menu * Update parentDocumentId of direct child to null * Add translation * Add test for permanent delete * Add space * Update app/scenes/DocumentPermanentDelete.js Co-authored-by: Tom Moor <tom.moor@gmail.com> * Update app/stores/DocumentsStore.js Co-authored-by: Tom Moor <tom.moor@gmail.com> * Update server/commands/documentPermanentDeleter.js Co-authored-by: Tom Moor <tom.moor@gmail.com> * Update app/scenes/DocumentPermanentDelete.js Co-authored-by: Tom Moor <tom.moor@gmail.com> * Change socket room from team to collection * Add translation * Create log func for commands * Move tests from utils to permanentDeleter command * Add additional tests * Set redirect to trash * Return promise from beforeEach * Add undeleted documents validation * Include deleteAt attribute in db query * Update server/commands/documentPermanentDeleter.js Co-authored-by: Tom Moor <tom.moor@gmail.com> * tweak language Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
@@ -250,6 +250,10 @@ class SocketProvider extends React.Component<Props> {
|
||||
documents.starredIds.set(event.documentId, false);
|
||||
});
|
||||
|
||||
this.socket.on("documents.permanent_delete", (event) => {
|
||||
documents.remove(event.documentId);
|
||||
});
|
||||
|
||||
// received when a user is given access to a collection
|
||||
// if the user is us then we go ahead and load the collection from API.
|
||||
this.socket.on("collections.add_user", (event) => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import styled from "styled-components";
|
||||
import Document from "models/Document";
|
||||
import DocumentDelete from "scenes/DocumentDelete";
|
||||
import DocumentMove from "scenes/DocumentMove";
|
||||
import DocumentPermanentDelete from "scenes/DocumentPermanentDelete";
|
||||
import DocumentTemplatize from "scenes/DocumentTemplatize";
|
||||
import CollectionIcon from "components/CollectionIcon";
|
||||
import ContextMenu from "components/ContextMenu";
|
||||
@@ -61,6 +62,10 @@ function DocumentMenu({
|
||||
const { t } = useTranslation();
|
||||
const [renderModals, setRenderModals] = React.useState(false);
|
||||
const [showDeleteModal, setShowDeleteModal] = React.useState(false);
|
||||
const [
|
||||
showPermanentDeleteModal,
|
||||
setShowPermanentDeleteModal,
|
||||
] = React.useState(false);
|
||||
const [showMoveModal, setShowMoveModal] = React.useState(false);
|
||||
const [showTemplateModal, setShowTemplateModal] = React.useState(false);
|
||||
const file = React.useRef<?HTMLInputElement>();
|
||||
@@ -327,6 +332,11 @@ function DocumentMenu({
|
||||
onClick: () => setShowDeleteModal(true),
|
||||
visible: !!can.delete,
|
||||
},
|
||||
{
|
||||
title: `${t("Permanently delete")}…`,
|
||||
onClick: () => setShowPermanentDeleteModal(true),
|
||||
visible: can.permanentDelete,
|
||||
},
|
||||
{
|
||||
title: `${t("Move")}…`,
|
||||
onClick: () => setShowMoveModal(true),
|
||||
@@ -357,40 +367,60 @@ function DocumentMenu({
|
||||
</ContextMenu>
|
||||
{renderModals && (
|
||||
<>
|
||||
<Modal
|
||||
title={t("Move {{ documentName }}", {
|
||||
documentName: document.noun,
|
||||
})}
|
||||
onRequestClose={() => setShowMoveModal(false)}
|
||||
isOpen={showMoveModal}
|
||||
>
|
||||
<DocumentMove
|
||||
document={document}
|
||||
{can.move && (
|
||||
<Modal
|
||||
title={t("Move {{ documentName }}", {
|
||||
documentName: document.noun,
|
||||
})}
|
||||
onRequestClose={() => setShowMoveModal(false)}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
title={t("Delete {{ documentName }}", {
|
||||
documentName: document.noun,
|
||||
})}
|
||||
onRequestClose={() => setShowDeleteModal(false)}
|
||||
isOpen={showDeleteModal}
|
||||
>
|
||||
<DocumentDelete
|
||||
document={document}
|
||||
onSubmit={() => setShowDeleteModal(false)}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
title={t("Create template")}
|
||||
onRequestClose={() => setShowTemplateModal(false)}
|
||||
isOpen={showTemplateModal}
|
||||
>
|
||||
<DocumentTemplatize
|
||||
document={document}
|
||||
onSubmit={() => setShowTemplateModal(false)}
|
||||
/>
|
||||
</Modal>
|
||||
isOpen={showMoveModal}
|
||||
>
|
||||
<DocumentMove
|
||||
document={document}
|
||||
onRequestClose={() => setShowMoveModal(false)}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
{can.delete && (
|
||||
<Modal
|
||||
title={t("Delete {{ documentName }}", {
|
||||
documentName: document.noun,
|
||||
})}
|
||||
onRequestClose={() => setShowDeleteModal(false)}
|
||||
isOpen={showDeleteModal}
|
||||
>
|
||||
<DocumentDelete
|
||||
document={document}
|
||||
onSubmit={() => setShowDeleteModal(false)}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
{can.permanentDelete && (
|
||||
<Modal
|
||||
title={t("Permanently delete {{ documentName }}", {
|
||||
documentName: document.noun,
|
||||
})}
|
||||
onRequestClose={() => setShowPermanentDeleteModal(false)}
|
||||
isOpen={showPermanentDeleteModal}
|
||||
>
|
||||
<DocumentPermanentDelete
|
||||
document={document}
|
||||
onSubmit={() => setShowPermanentDeleteModal(false)}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
{can.update && (
|
||||
<Modal
|
||||
title={t("Create template")}
|
||||
onRequestClose={() => setShowTemplateModal(false)}
|
||||
isOpen={showTemplateModal}
|
||||
>
|
||||
<DocumentTemplatize
|
||||
document={document}
|
||||
onSubmit={() => setShowTemplateModal(false)}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
60
app/scenes/DocumentPermanentDelete.js
Normal file
60
app/scenes/DocumentPermanentDelete.js
Normal file
@@ -0,0 +1,60 @@
|
||||
// @flow
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import Document from "models/Document.js";
|
||||
import Button from "components/Button";
|
||||
import Flex from "components/Flex";
|
||||
import HelpText from "components/HelpText";
|
||||
import useStores from "hooks/useStores";
|
||||
|
||||
type Props = {|
|
||||
document: Document,
|
||||
onSubmit: () => void,
|
||||
|};
|
||||
|
||||
function DocumentPermanentDelete({ document, onSubmit }: Props) {
|
||||
const [isDeleting, setIsDeleting] = React.useState(false);
|
||||
const { t } = useTranslation();
|
||||
const { ui, documents } = useStores();
|
||||
const { showToast } = ui;
|
||||
const history = useHistory();
|
||||
|
||||
const handleSubmit = React.useCallback(
|
||||
async (ev: SyntheticEvent<>) => {
|
||||
ev.preventDefault();
|
||||
try {
|
||||
setIsDeleting(true);
|
||||
await documents.delete(document, { permanent: true });
|
||||
showToast(t("Document permanently deleted"), { type: "success" });
|
||||
onSubmit();
|
||||
history.push("/trash");
|
||||
} catch (err) {
|
||||
showToast(err.message, { type: "error" });
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
},
|
||||
[document, onSubmit, showToast, t, history, documents]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex column>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<HelpText>
|
||||
<Trans
|
||||
defaults="Are you sure you want to permanently delete the <em>{{ documentTitle }}</em> document? This action is immediate and cannot be undone."
|
||||
values={{ documentTitle: document.titleWithDefault }}
|
||||
components={{ em: <strong /> }}
|
||||
/>
|
||||
</HelpText>
|
||||
<Button type="submit" danger>
|
||||
{isDeleting ? `${t("Deleting")}…` : t("I’m sure – Delete")}
|
||||
</Button>
|
||||
</form>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default observer(DocumentPermanentDelete);
|
||||
@@ -616,8 +616,8 @@ export default class DocumentsStore extends BaseStore<Document> {
|
||||
}
|
||||
|
||||
@action
|
||||
async delete(document: Document) {
|
||||
await super.delete(document);
|
||||
async delete(document: Document, options?: {| permanent: boolean |}) {
|
||||
await super.delete(document, options);
|
||||
|
||||
// check to see if we have any shares related to this document already
|
||||
// loaded in local state. If so we can go ahead and remove those too.
|
||||
|
||||
Reference in New Issue
Block a user