2017-09-10 05:12:59 +00:00
|
|
|
|
// @flow
|
2021-01-14 06:00:25 +00:00
|
|
|
|
import { observer } from "mobx-react";
|
2021-08-13 21:21:25 +00:00
|
|
|
|
import {
|
|
|
|
|
NewDocumentIcon,
|
|
|
|
|
EditIcon,
|
|
|
|
|
TrashIcon,
|
|
|
|
|
ImportIcon,
|
|
|
|
|
ExportIcon,
|
|
|
|
|
PadlockIcon,
|
|
|
|
|
} from "outline-icons";
|
2020-08-09 05:53:59 +00:00
|
|
|
|
import * as React from "react";
|
2021-01-14 06:00:25 +00:00
|
|
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
|
import { useHistory } from "react-router-dom";
|
|
|
|
|
import { useMenuState, MenuButton } from "reakit/Menu";
|
2021-01-23 17:47:02 +00:00
|
|
|
|
import { VisuallyHidden } from "reakit/VisuallyHidden";
|
2020-08-09 05:53:59 +00:00
|
|
|
|
import Collection from "models/Collection";
|
2020-08-12 17:49:15 +00:00
|
|
|
|
import CollectionDelete from "scenes/CollectionDelete";
|
|
|
|
|
import CollectionEdit from "scenes/CollectionEdit";
|
|
|
|
|
import CollectionExport from "scenes/CollectionExport";
|
2021-03-31 04:02:08 +00:00
|
|
|
|
import CollectionPermissions from "scenes/CollectionPermissions";
|
2021-01-14 06:00:25 +00:00
|
|
|
|
import ContextMenu from "components/ContextMenu";
|
|
|
|
|
import OverflowMenuButton from "components/ContextMenu/OverflowMenuButton";
|
2021-08-13 21:21:25 +00:00
|
|
|
|
import Template from "components/ContextMenu/Template";
|
2020-08-09 05:53:59 +00:00
|
|
|
|
import Modal from "components/Modal";
|
2021-08-28 21:27:07 +00:00
|
|
|
|
import useCurrentTeam from "hooks/useCurrentTeam";
|
2021-01-14 06:00:25 +00:00
|
|
|
|
import useStores from "hooks/useStores";
|
2021-07-20 09:06:10 +00:00
|
|
|
|
import useToasts from "hooks/useToasts";
|
2020-06-20 20:59:15 +00:00
|
|
|
|
import getDataTransferFiles from "utils/getDataTransferFiles";
|
2021-10-24 19:30:27 +00:00
|
|
|
|
import { newDocumentPath } from "utils/routeHelpers";
|
2017-09-10 05:12:59 +00:00
|
|
|
|
|
2021-01-14 06:00:25 +00:00
|
|
|
|
type Props = {|
|
2018-01-30 06:31:49 +00:00
|
|
|
|
collection: Collection,
|
2021-01-14 06:00:25 +00:00
|
|
|
|
placement?: string,
|
|
|
|
|
modal?: boolean,
|
|
|
|
|
label?: (any) => React.Node,
|
2019-07-13 17:15:38 +00:00
|
|
|
|
onOpen?: () => void,
|
|
|
|
|
onClose?: () => void,
|
2021-01-14 06:00:25 +00:00
|
|
|
|
|};
|
|
|
|
|
|
|
|
|
|
function CollectionMenu({
|
|
|
|
|
collection,
|
|
|
|
|
label,
|
|
|
|
|
modal = true,
|
|
|
|
|
placement,
|
|
|
|
|
onOpen,
|
|
|
|
|
onClose,
|
|
|
|
|
}: Props) {
|
|
|
|
|
const menu = useMenuState({ modal, placement });
|
|
|
|
|
const [renderModals, setRenderModals] = React.useState(false);
|
2021-08-28 21:27:07 +00:00
|
|
|
|
const team = useCurrentTeam();
|
2021-07-20 09:06:10 +00:00
|
|
|
|
const { documents, policies } = useStores();
|
|
|
|
|
const { showToast } = useToasts();
|
2021-01-14 06:00:25 +00:00
|
|
|
|
const { t } = useTranslation();
|
|
|
|
|
const history = useHistory();
|
|
|
|
|
|
|
|
|
|
const file = React.useRef<?HTMLInputElement>();
|
2021-03-31 04:02:08 +00:00
|
|
|
|
const [
|
|
|
|
|
showCollectionPermissions,
|
|
|
|
|
setShowCollectionPermissions,
|
|
|
|
|
] = React.useState(false);
|
2021-01-14 06:00:25 +00:00
|
|
|
|
const [showCollectionEdit, setShowCollectionEdit] = React.useState(false);
|
|
|
|
|
const [showCollectionDelete, setShowCollectionDelete] = React.useState(false);
|
|
|
|
|
const [showCollectionExport, setShowCollectionExport] = React.useState(false);
|
|
|
|
|
|
|
|
|
|
const handleOpen = React.useCallback(() => {
|
|
|
|
|
setRenderModals(true);
|
|
|
|
|
if (onOpen) {
|
|
|
|
|
onOpen();
|
2018-12-05 06:24:30 +00:00
|
|
|
|
}
|
2021-01-14 06:00:25 +00:00
|
|
|
|
}, [onOpen]);
|
|
|
|
|
|
|
|
|
|
const handleNewDocument = React.useCallback(
|
|
|
|
|
(ev: SyntheticEvent<>) => {
|
|
|
|
|
ev.preventDefault();
|
2021-10-24 19:30:27 +00:00
|
|
|
|
history.push(newDocumentPath(collection.id));
|
2021-01-14 06:00:25 +00:00
|
|
|
|
},
|
|
|
|
|
[history, collection.id]
|
|
|
|
|
);
|
|
|
|
|
|
2021-02-10 04:13:09 +00:00
|
|
|
|
const stopPropagation = React.useCallback((ev: SyntheticEvent<>) => {
|
|
|
|
|
ev.stopPropagation();
|
|
|
|
|
}, []);
|
|
|
|
|
|
2021-01-14 06:00:25 +00:00
|
|
|
|
const handleImportDocument = React.useCallback(
|
|
|
|
|
(ev: SyntheticEvent<>) => {
|
|
|
|
|
ev.preventDefault();
|
|
|
|
|
ev.stopPropagation();
|
|
|
|
|
|
|
|
|
|
// simulate a click on the file upload input element
|
|
|
|
|
if (file.current) {
|
|
|
|
|
file.current.click();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[file]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const handleFilePicked = React.useCallback(
|
|
|
|
|
async (ev: SyntheticEvent<>) => {
|
|
|
|
|
const files = getDataTransferFiles(ev);
|
|
|
|
|
|
2021-03-17 05:09:19 +00:00
|
|
|
|
// Because this is the onChange handler it's possible for the change to be
|
|
|
|
|
// from previously selecting a file to not selecting a file – aka empty
|
|
|
|
|
if (!files.length) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-14 06:00:25 +00:00
|
|
|
|
try {
|
|
|
|
|
const file = files[0];
|
2021-02-08 00:13:44 +00:00
|
|
|
|
const document = await documents.import(file, null, collection.id, {
|
|
|
|
|
publish: true,
|
|
|
|
|
});
|
2021-01-14 06:00:25 +00:00
|
|
|
|
history.push(document.url);
|
|
|
|
|
} catch (err) {
|
2021-07-20 09:06:10 +00:00
|
|
|
|
showToast(err.message, {
|
2021-01-14 06:00:25 +00:00
|
|
|
|
type: "error",
|
|
|
|
|
});
|
2021-02-08 00:13:44 +00:00
|
|
|
|
|
|
|
|
|
throw err;
|
2021-01-14 06:00:25 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
2021-07-20 09:06:10 +00:00
|
|
|
|
[history, showToast, collection.id, documents]
|
2021-01-14 06:00:25 +00:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const can = policies.abilities(collection.id);
|
2021-08-28 21:27:07 +00:00
|
|
|
|
const canUserInTeam = policies.abilities(team.id);
|
|
|
|
|
|
2021-07-07 02:02:31 +00:00
|
|
|
|
const items = React.useMemo(
|
2021-08-13 21:21:25 +00:00
|
|
|
|
() => [
|
|
|
|
|
{
|
|
|
|
|
title: t("New document"),
|
|
|
|
|
visible: can.update,
|
|
|
|
|
onClick: handleNewDocument,
|
|
|
|
|
icon: <NewDocumentIcon />,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: t("Import document"),
|
|
|
|
|
visible: can.update,
|
|
|
|
|
onClick: handleImportDocument,
|
|
|
|
|
icon: <ImportIcon />,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: "separator",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: `${t("Edit")}…`,
|
|
|
|
|
visible: can.update,
|
|
|
|
|
onClick: () => setShowCollectionEdit(true),
|
|
|
|
|
icon: <EditIcon />,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: `${t("Permissions")}…`,
|
|
|
|
|
visible: can.update,
|
|
|
|
|
onClick: () => setShowCollectionPermissions(true),
|
|
|
|
|
icon: <PadlockIcon />,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: `${t("Export")}…`,
|
2021-08-28 21:27:07 +00:00
|
|
|
|
visible: !!(collection && canUserInTeam.export),
|
2021-08-13 21:21:25 +00:00
|
|
|
|
onClick: () => setShowCollectionExport(true),
|
|
|
|
|
icon: <ExportIcon />,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: "separator",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: `${t("Delete")}…`,
|
|
|
|
|
visible: !!(collection && can.delete),
|
|
|
|
|
onClick: () => setShowCollectionDelete(true),
|
|
|
|
|
icon: <TrashIcon />,
|
|
|
|
|
},
|
|
|
|
|
],
|
2021-08-28 21:27:07 +00:00
|
|
|
|
[
|
|
|
|
|
t,
|
|
|
|
|
can.update,
|
|
|
|
|
can.delete,
|
|
|
|
|
handleNewDocument,
|
|
|
|
|
handleImportDocument,
|
|
|
|
|
collection,
|
|
|
|
|
canUserInTeam.export,
|
|
|
|
|
]
|
2021-07-07 02:02:31 +00:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!items.length) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2021-01-14 06:00:25 +00:00
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<VisuallyHidden>
|
|
|
|
|
<input
|
|
|
|
|
type="file"
|
|
|
|
|
ref={file}
|
|
|
|
|
onChange={handleFilePicked}
|
2021-02-10 04:13:09 +00:00
|
|
|
|
onClick={stopPropagation}
|
2021-01-14 06:00:25 +00:00
|
|
|
|
accept={documents.importFileTypes.join(", ")}
|
|
|
|
|
tabIndex="-1"
|
|
|
|
|
/>
|
|
|
|
|
</VisuallyHidden>
|
|
|
|
|
{label ? (
|
|
|
|
|
<MenuButton {...menu}>{label}</MenuButton>
|
|
|
|
|
) : (
|
2021-01-23 03:31:30 +00:00
|
|
|
|
<OverflowMenuButton aria-label={t("Show menu")} {...menu} />
|
2021-01-14 06:00:25 +00:00
|
|
|
|
)}
|
|
|
|
|
<ContextMenu
|
|
|
|
|
{...menu}
|
|
|
|
|
onOpen={handleOpen}
|
|
|
|
|
onClose={onClose}
|
|
|
|
|
aria-label={t("Collection")}
|
|
|
|
|
>
|
2021-07-07 02:02:31 +00:00
|
|
|
|
<Template {...menu} items={items} />
|
2021-01-14 06:00:25 +00:00
|
|
|
|
</ContextMenu>
|
|
|
|
|
{renderModals && (
|
|
|
|
|
<>
|
|
|
|
|
<Modal
|
2021-03-31 04:02:08 +00:00
|
|
|
|
title={t("Collection permissions")}
|
|
|
|
|
onRequestClose={() => setShowCollectionPermissions(false)}
|
|
|
|
|
isOpen={showCollectionPermissions}
|
2021-01-14 06:00:25 +00:00
|
|
|
|
>
|
2021-03-31 04:02:08 +00:00
|
|
|
|
<CollectionPermissions collection={collection} />
|
2021-01-14 06:00:25 +00:00
|
|
|
|
</Modal>
|
|
|
|
|
<Modal
|
|
|
|
|
title={t("Edit collection")}
|
|
|
|
|
isOpen={showCollectionEdit}
|
|
|
|
|
onRequestClose={() => setShowCollectionEdit(false)}
|
|
|
|
|
>
|
|
|
|
|
<CollectionEdit
|
|
|
|
|
onSubmit={() => setShowCollectionEdit(false)}
|
2021-10-24 19:30:27 +00:00
|
|
|
|
collectionId={collection.id}
|
2021-01-14 06:00:25 +00:00
|
|
|
|
/>
|
|
|
|
|
</Modal>
|
|
|
|
|
<Modal
|
|
|
|
|
title={t("Delete collection")}
|
|
|
|
|
isOpen={showCollectionDelete}
|
|
|
|
|
onRequestClose={() => setShowCollectionDelete(false)}
|
|
|
|
|
>
|
|
|
|
|
<CollectionDelete
|
|
|
|
|
onSubmit={() => setShowCollectionDelete(false)}
|
|
|
|
|
collection={collection}
|
|
|
|
|
/>
|
|
|
|
|
</Modal>
|
|
|
|
|
<Modal
|
|
|
|
|
title={t("Export collection")}
|
|
|
|
|
isOpen={showCollectionExport}
|
|
|
|
|
onRequestClose={() => setShowCollectionExport(false)}
|
|
|
|
|
>
|
|
|
|
|
<CollectionExport
|
|
|
|
|
onSubmit={() => setShowCollectionExport(false)}
|
|
|
|
|
collection={collection}
|
|
|
|
|
/>
|
|
|
|
|
</Modal>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
);
|
2017-09-10 05:12:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-14 06:00:25 +00:00
|
|
|
|
export default observer(CollectionMenu);
|