import { formatDistanceToNow } from "date-fns"; import invariant from "invariant"; import { observer } from "mobx-react"; import { GlobeIcon, PadlockIcon } from "outline-icons"; import * as React from "react"; import { useTranslation, Trans } from "react-i18next"; import styled from "styled-components"; import Document from "~/models/Document"; import Share from "~/models/Share"; import Button from "~/components/Button"; import CopyToClipboard from "~/components/CopyToClipboard"; import Flex from "~/components/Flex"; import Input from "~/components/Input"; import Notice from "~/components/Notice"; import Switch from "~/components/Switch"; import Text from "~/components/Text"; import useCurrentTeam from "~/hooks/useCurrentTeam"; import useKeyDown from "~/hooks/useKeyDown"; import usePolicy from "~/hooks/usePolicy"; import useStores from "~/hooks/useStores"; import useToasts from "~/hooks/useToasts"; import useUserLocale from "~/hooks/useUserLocale"; import { dateLocale } from "~/utils/i18n"; type Props = { document: Document; share: Share | null | undefined; sharedParent: Share | null | undefined; onRequestClose: () => void; visible: boolean; }; function SharePopover({ document, share, sharedParent, onRequestClose, visible, }: Props) { const team = useCurrentTeam(); const { t } = useTranslation(); const { shares } = useStores(); const { showToast } = useToasts(); const [isCopied, setIsCopied] = React.useState(false); const timeout = React.useRef>(); const buttonRef = React.useRef(null); const can = usePolicy(share ? share.id : ""); const documentAbilities = usePolicy(document); const canPublish = can.update && !document.isTemplate && team.sharing && documentAbilities.share; const isPubliclyShared = team.sharing && ((share && share.published) || (sharedParent && sharedParent.published && !document.isDraft)); useKeyDown("Escape", onRequestClose); React.useEffect(() => { if (visible && team.sharing) { document.share(); buttonRef.current?.focus(); } return () => (timeout.current ? clearTimeout(timeout.current) : undefined); }, [document, visible, team.sharing]); const handlePublishedChange = React.useCallback( async (event) => { const share = shares.getByDocumentId(document.id); invariant(share, "Share must exist"); try { await share.save({ published: event.currentTarget.checked, }); } catch (err) { showToast(err.message, { type: "error", }); } }, [document.id, shares, showToast] ); const handleChildDocumentsChange = React.useCallback( async (event) => { const share = shares.getByDocumentId(document.id); invariant(share, "Share must exist"); try { await share.save({ includeChildDocuments: event.currentTarget.checked, }); } catch (err) { showToast(err.message, { type: "error", }); } }, [document.id, shares, showToast] ); const handleCopied = React.useCallback(() => { setIsCopied(true); timeout.current = setTimeout(() => { setIsCopied(false); onRequestClose(); showToast(t("Share link copied"), { type: "info", }); }, 250); }, [t, onRequestClose, showToast]); const userLocale = useUserLocale(); const locale = userLocale ? dateLocale(userLocale) : undefined; const shareUrl = team.sharing ? share?.url ?? "" : `${team.url}${document.url}`; return ( <> {isPubliclyShared ? ( ) : ( )}{" "} {t("Share this document")} {sharedParent && !document.isDraft && ( , }} /> )} {canPublish ? ( {share?.published ? t("Anyone with the link can view this document") : t("Only team members with permission can view")} {share?.lastAccessedAt && ( <> .{" "} {t("The shared link was last accessed {{ timeAgo }}.", { timeAgo: formatDistanceToNow( Date.parse(share?.lastAccessedAt), { addSuffix: true, locale, } ), })} )} ) : ( {t("Only team members with permission can view")} )} {canPublish && share?.published && !document.isDraft && ( {share.includeChildDocuments ? t("Nested documents are publicly available") : t("Nested documents are not shared")} )} ); } const Heading = styled.h2` display: flex; align-items: center; margin-top: 12px; margin-left: -4px; `; const SwitchWrapper = styled.div` margin: 20px 0; `; const InputLink = styled(Input)` flex-grow: 1; margin-right: 8px; `; const SwitchLabel = styled(Flex)` svg { flex-shrink: 0; } `; const SwitchText = styled(Text)` margin: 0; font-size: 15px; `; export default observer(SharePopover);