Add confirmation dialog when dragging

This commit is contained in:
Tom Moor
2024-09-29 09:31:00 -04:00
parent 7b9ec4c43a
commit ba7c446f59
6 changed files with 148 additions and 128 deletions
+62
View File
@@ -0,0 +1,62 @@
import { observer } from "mobx-react";
import * as React from "react";
import { Trans, useTranslation } from "react-i18next";
import { CollectionPermission, NavigationNode } from "@shared/types";
import type Collection from "~/models/Collection";
import ConfirmationDialog from "~/components/ConfirmationDialog";
import useStores from "~/hooks/useStores";
type Props = {
/** The navigation node to move, must represent a document. */
item: NavigationNode;
/** The collection to move the document to. */
collection: Collection;
/** The parent document to move the document under. */
parentDocumentId?: string | null;
/** The index to move the document to. */
index?: number | null;
};
function ConfirmMoveDialog({ collection, item, ...rest }: Props) {
const { documents, dialogs, collections } = useStores();
const { t } = useTranslation();
const prevCollection = collections.get(item.collectionId!);
const accessMapping = {
[CollectionPermission.ReadWrite]: t("view and edit access"),
[CollectionPermission.Read]: t("view only access"),
null: t("no access"),
};
const handleSubmit = async () => {
await documents.move({
documentId: item.id,
collectionId: collection.id,
...rest,
});
dialogs.closeAllModals();
};
return (
<ConfirmationDialog
onSubmit={handleSubmit}
submitText={t("Move document")}
savingText={`${t("Moving")}`}
>
<Trans
defaults="Moving the document <em>{{ title }}</em> to the {{ newCollectionName }} collection will change permission for all workspace members from <em>{{ prevPermission }}</em> to <em>{{ newPermission }}</em>."
values={{
title: item.title,
prevCollectionName: prevCollection?.name,
newCollectionName: collection.name,
prevPermission: accessMapping[prevCollection?.permission || "null"],
newPermission: accessMapping[collection.permission || "null"],
}}
components={{
em: <strong />,
}}
/>
</ConfirmationDialog>
);
}
export default observer(ConfirmMoveDialog);
@@ -8,7 +8,7 @@ import { useHistory } from "react-router-dom";
import { CollectionValidation } from "@shared/validations";
import Collection from "~/models/Collection";
import Document from "~/models/Document";
import DocumentReparent from "~/scenes/DocumentReparent";
import ConfirmMoveDialog from "~/components/ConfirmMoveDialog";
import Fade from "~/components/Fade";
import CollectionIcon from "~/components/Icons/CollectionIcon";
import NudeButton from "~/components/NudeButton";
@@ -78,20 +78,12 @@ const CollectionLink: React.FC<Props> = ({
if (
prevCollection &&
prevCollection.permission === null &&
prevCollection.permission !== collection.permission &&
!document?.isDraft
) {
dialogs.openModal({
title: t("Move document"),
content: (
<DocumentReparent
item={item}
collection={collection}
onSubmit={dialogs.closeAllModals}
onCancel={dialogs.closeAllModals}
/>
),
title: t("Change permissions?"),
content: <ConfirmMoveDialog item={item} collection={collection} />,
});
} else {
await documents.move({ documentId: id, collectionId: collection.id });
@@ -6,6 +6,7 @@ import { toast } from "sonner";
import styled from "styled-components";
import Collection from "~/models/Collection";
import Document from "~/models/Document";
import ConfirmMoveDialog from "~/components/ConfirmMoveDialog";
import DocumentsLoader from "~/components/DocumentsLoader";
import { ResizingHeightContainer } from "~/components/ResizingHeightContainer";
import Text from "~/components/Text";
@@ -35,7 +36,7 @@ function CollectionLinkChildren({
}: Props) {
const can = usePolicy(collection);
const manualSort = collection.sort.field === "index";
const { documents } = useStores();
const { documents, dialogs, collections } = useStores();
const { t } = useTranslation();
const childDocuments = useCollectionDocuments(collection, documents.active);
@@ -55,11 +56,26 @@ function CollectionLinkChildren({
if (!collection) {
return;
}
void documents.move({
documentId: item.id,
collectionId: collection.id,
index: 0,
});
const prevCollection = collections.get(item.collectionId);
if (
prevCollection &&
prevCollection.permission !== collection.permission
) {
dialogs.openModal({
title: t("Change permissions?"),
content: (
<ConfirmMoveDialog item={item} collection={collection} index={0} />
),
});
} else {
void documents.move({
documentId: item.id,
collectionId: collection.id,
index: 0,
});
}
},
collect: (monitor) => ({
isOverReorder: !!monitor.isOver(),
@@ -12,6 +12,7 @@ import Document from "~/models/Document";
import GroupMembership from "~/models/GroupMembership";
import Star from "~/models/Star";
import UserMembership from "~/models/UserMembership";
import ConfirmMoveDialog from "~/components/ConfirmMoveDialog";
import Icon from "~/components/Icon";
import useCurrentUser from "~/hooks/useCurrentUser";
import useStores from "~/hooks/useStores";
@@ -172,7 +173,8 @@ export function useDropToReparentDocument(
setExpanded: () => void,
parentRef: React.RefObject<HTMLDivElement>
) {
const { documents, policies } = useStores();
const { t } = useTranslation();
const { documents, collections, dialogs, policies } = useStores();
const hasChildDocuments = !!node?.children.length;
const document = node ? documents.get(node.id) : undefined;
const pathToNode = React.useMemo(
@@ -192,10 +194,11 @@ export function useDropToReparentDocument(
}
};
parentRef.current?.addEventListener("dragleave", resetHoverExpanding);
const element = parentRef.current;
element?.addEventListener("dragleave", resetHoverExpanding);
return () => {
parentRef.current?.removeEventListener("dragleave", resetHoverExpanding);
element?.removeEventListener("dragleave", resetHoverExpanding);
};
}, [parentRef]);
@@ -209,10 +212,32 @@ export function useDropToReparentDocument(
if (monitor.didDrop() || !node) {
return;
}
await documents.move({
documentId: item.id,
parentDocumentId: node.id,
});
const collection = documents.get(node.id)?.collection;
const prevCollection = collections.get(item.collectionId);
if (
collection &&
prevCollection &&
prevCollection.permission !== collection.permission
) {
dialogs.openModal({
title: t("Change permissions?"),
content: (
<ConfirmMoveDialog
item={item}
collection={collection}
parentDocumentId={node.id}
/>
),
});
} else {
await documents.move({
documentId: item.id,
parentDocumentId: node.id,
});
}
setExpanded();
},
canDrop: (item, monitor) =>
@@ -270,7 +295,7 @@ export function useDropToReorderDocument(
}
) {
const { t } = useTranslation();
const { documents, policies } = useStores();
const { documents, collections, dialogs, policies } = useStores();
return useDrop<
DragObject,
@@ -304,8 +329,28 @@ export function useDropToReorderDocument(
}
const params = getMoveParams(item);
if (params) {
void documents.move(params);
const prevCollection = collections.get(item.collectionId);
if (
collection &&
prevCollection &&
prevCollection.permission !== collection.permission
) {
dialogs.openModal({
title: t("Change permissions?"),
content: (
<ConfirmMoveDialog
item={item}
collection={collection}
{...params}
/>
),
});
} else {
void documents.move(params);
}
}
},
collect: (monitor) => ({
-96
View File
@@ -1,96 +0,0 @@
import { observer } from "mobx-react";
import { useState } from "react";
import * as React from "react";
import { Trans, useTranslation } from "react-i18next";
import { toast } from "sonner";
import { CollectionPermission, NavigationNode } from "@shared/types";
import Collection from "~/models/Collection";
import Button from "~/components/Button";
import Flex from "~/components/Flex";
import Text from "~/components/Text";
import useStores from "~/hooks/useStores";
type Props = {
item:
| {
active: boolean | null | undefined;
children: Array<NavigationNode>;
collectionId: string;
depth: number;
id: string;
title: string;
url: string;
}
| {
id: string;
collectionId: string;
title: string;
};
collection: Collection;
onCancel: () => void;
onSubmit: () => void;
};
function DocumentReparent({ collection, item, onSubmit, onCancel }: Props) {
const [isSaving, setIsSaving] = useState(false);
const { documents, collections } = useStores();
const { t } = useTranslation();
const prevCollection = collections.get(item.collectionId);
const accessMapping = {
[CollectionPermission.ReadWrite]: t("view and edit access"),
[CollectionPermission.Read]: t("view only access"),
null: t("no access"),
};
const handleSubmit = React.useCallback(
async (ev: React.SyntheticEvent) => {
ev.preventDefault();
setIsSaving(true);
try {
await documents.move({
documentId: item.id,
collectionId: collection.id,
});
toast.message(t("Document moved"));
onSubmit();
} catch (err) {
toast.error(err.message);
} finally {
setIsSaving(false);
}
},
[documents, item.id, collection.id, t, onSubmit]
);
return (
<Flex column>
<form onSubmit={handleSubmit}>
<Text as="p" type="secondary">
<Trans
defaults="Heads up moving the document <em>{{ title }}</em> to the <em>{{ newCollectionName }}</em> collection will grant all members of the workspace <em>{{ newPermission }}</em>, they currently have {{ prevPermission }}."
values={{
title: item.title,
prevCollectionName: prevCollection?.name,
newCollectionName: collection.name,
prevPermission:
accessMapping[prevCollection?.permission || "null"],
newPermission: accessMapping[collection.permission || "null"],
}}
components={{
em: <strong />,
}}
/>
</Text>
<Button type="submit">
{isSaving ? `${t("Moving")}` : t("Move document")}
</Button>{" "}
<Button type="button" onClick={onCancel} neutral>
{t("Cancel")}
</Button>
</form>
</Flex>
);
}
export default observer(DocumentReparent);
+7 -6
View File
@@ -162,6 +162,12 @@
"Are you sure you want to permanently delete this entire comment thread?": "Are you sure you want to permanently delete this entire comment thread?",
"Are you sure you want to permanently delete this comment?": "Are you sure you want to permanently delete this comment?",
"Confirm": "Confirm",
"view and edit access": "view and edit access",
"view only access": "view only access",
"no access": "no access",
"Move document": "Move document",
"Moving": "Moving",
"Moving the document <em>{{ title }}</em> to the {{ newCollectionName }} collection will change permission for all workspace members from <em>{{ prevPermission }}</em> to <em>{{ newPermission }}</em>.": "Moving the document <em>{{ title }}</em> to the {{ newCollectionName }} collection will change permission for all workspace members from <em>{{ prevPermission }}</em> to <em>{{ newPermission }}</em>.",
"Document is too large": "Document is too large",
"This document has reached the maximum size and can no longer be edited": "This document has reached the maximum size and can no longer be edited",
"Authentication failed": "Authentication failed",
@@ -336,7 +342,7 @@
"{{ count }} groups added to the document": "{{ count }} groups added to the document",
"{{ count }} groups added to the document_plural": "{{ count }} groups added to the document",
"Logo": "Logo",
"Move document": "Move document",
"Change permissions?": "Change permissions?",
"New doc": "New doc",
"You can't reorder documents in an alphabetically sorted collection": "You can't reorder documents in an alphabetically sorted collection",
"Empty": "Empty",
@@ -645,11 +651,6 @@
"Document published": "Document published",
"Couldnt publish the document, try again?": "Couldnt publish the document, try again?",
"Publish in <em>{{ location }}</em>": "Publish in <em>{{ location }}</em>",
"view and edit access": "view and edit access",
"view only access": "view only access",
"no access": "no access",
"Heads up moving the document <em>{{ title }}</em> to the <em>{{ newCollectionName }}</em> collection will grant all members of the workspace <em>{{ newPermission }}</em>, they currently have {{ prevPermission }}.": "Heads up moving the document <em>{{ title }}</em> to the <em>{{ newCollectionName }}</em> collection will grant all members of the workspace <em>{{ newPermission }}</em>, they currently have {{ prevPermission }}.",
"Moving": "Moving",
"Search documents": "Search documents",
"No documents found for your filters.": "No documents found for your filters.",
"Youve not got any drafts at the moment.": "Youve not got any drafts at the moment.",