Share document link that opens full editor (#4134)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
@@ -46,7 +46,7 @@ const Contents = styled.div<{ $shrink?: boolean; $width?: number }>`
|
||||
border-radius: 6px;
|
||||
padding: ${(props) => (props.$shrink ? "6px 0" : "12px 24px")};
|
||||
max-height: 50vh;
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
box-shadow: ${(props) => props.theme.menuShadow};
|
||||
width: ${(props) => props.$width}px;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { RouteComponentProps, useLocation } from "react-router-dom";
|
||||
import { RouteComponentProps, useLocation, Redirect } from "react-router-dom";
|
||||
import styled, { useTheme } from "styled-components";
|
||||
import { setCookie } from "tiny-cookie";
|
||||
import DocumentModel from "~/models/Document";
|
||||
@@ -12,6 +12,7 @@ import ErrorOffline from "~/scenes/ErrorOffline";
|
||||
import Layout from "~/components/Layout";
|
||||
import Sidebar from "~/components/Sidebar/Shared";
|
||||
import Text from "~/components/Text";
|
||||
import usePolicy from "~/hooks/usePolicy";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import { NavigationNode } from "~/types";
|
||||
import { AuthorizationError, OfflineError } from "~/utils/errors";
|
||||
@@ -78,12 +79,17 @@ function SharedDocumentScene(props: Props) {
|
||||
const { ui } = useStores();
|
||||
const theme = useTheme();
|
||||
const location = useLocation();
|
||||
const searchParams = React.useMemo(
|
||||
() => new URLSearchParams(location.search),
|
||||
[location.search]
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
const [response, setResponse] = React.useState<Response>();
|
||||
const [error, setError] = React.useState<Error | null | undefined>();
|
||||
const { documents } = useStores();
|
||||
const { shareId, documentSlug } = props.match.params;
|
||||
const documentId = useDocumentId(documentSlug, response);
|
||||
const can = usePolicy(response?.document.id ?? "");
|
||||
|
||||
// ensure the wider page color always matches the theme
|
||||
React.useEffect(() => {
|
||||
@@ -140,6 +146,10 @@ function SharedDocumentScene(props: Props) {
|
||||
return <Loading location={props.location} />;
|
||||
}
|
||||
|
||||
if (response && searchParams.get("edit") === "true" && can.update) {
|
||||
return <Redirect to={response.document.url} />;
|
||||
}
|
||||
|
||||
const sidebar = response.sharedTree ? (
|
||||
<Sidebar rootNode={response.sharedTree} shareId={shareId} />
|
||||
) : undefined;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import invariant from "invariant";
|
||||
import { observer } from "mobx-react";
|
||||
import { GlobeIcon, PadlockIcon } from "outline-icons";
|
||||
import { ExpandedIcon, GlobeIcon, PadlockIcon } from "outline-icons";
|
||||
import * as React from "react";
|
||||
import { useTranslation, Trans } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
@@ -10,7 +10,6 @@ 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";
|
||||
@@ -42,6 +41,8 @@ function SharePopover({
|
||||
const { shares } = useStores();
|
||||
const { showToast } = useToasts();
|
||||
const [isCopied, setIsCopied] = React.useState(false);
|
||||
const [expandedOptions, setExpandedOptions] = React.useState(false);
|
||||
const [isEditMode, setIsEditMode] = React.useState(false);
|
||||
const timeout = React.useRef<ReturnType<typeof setTimeout>>();
|
||||
const buttonRef = React.useRef<HTMLButtonElement>(null);
|
||||
const can = usePolicy(share ? share.id : "");
|
||||
@@ -56,6 +57,11 @@ function SharePopover({
|
||||
((share && share.published) ||
|
||||
(sharedParent && sharedParent.published && !document.isDraft));
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!visible && expandedOptions) {
|
||||
setExpandedOptions(false);
|
||||
}
|
||||
}, [visible]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
useKeyDown("Escape", onRequestClose);
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -116,9 +122,10 @@ function SharePopover({
|
||||
|
||||
const userLocale = useUserLocale();
|
||||
const locale = userLocale ? dateLocale(userLocale) : undefined;
|
||||
const shareUrl = team.sharing
|
||||
? share?.url ?? ""
|
||||
: `${team.url}${document.url}`;
|
||||
let shareUrl = team.sharing ? share?.url ?? "" : `${team.url}${document.url}`;
|
||||
if (isEditMode) {
|
||||
shareUrl += "?edit=true";
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -127,8 +134,8 @@ function SharePopover({
|
||||
<GlobeIcon size={28} color="currentColor" />
|
||||
) : (
|
||||
<PadlockIcon size={28} color="currentColor" />
|
||||
)}{" "}
|
||||
{t("Share this document")}
|
||||
)}
|
||||
<span>{t("Share this document")}</span>
|
||||
</Heading>
|
||||
|
||||
{sharedParent && !document.isDraft && (
|
||||
@@ -196,19 +203,52 @@ function SharePopover({
|
||||
{share.includeChildDocuments
|
||||
? t("Nested documents are publicly available")
|
||||
: t("Nested documents are not shared")}
|
||||
.
|
||||
</SwitchText>
|
||||
</SwitchLabel>
|
||||
</SwitchWrapper>
|
||||
)}
|
||||
<Flex>
|
||||
<InputLink
|
||||
type="text"
|
||||
label={t("Link")}
|
||||
placeholder={`${t("Loading")}…`}
|
||||
value={shareUrl}
|
||||
labelHidden
|
||||
readOnly
|
||||
|
||||
{expandedOptions && (
|
||||
<>
|
||||
<Separator />
|
||||
<SwitchWrapper>
|
||||
<Switch
|
||||
id="enableEditMode"
|
||||
label={t("Automatically redirect to the editor")}
|
||||
onChange={({ currentTarget: { checked } }) =>
|
||||
setIsEditMode(checked)
|
||||
}
|
||||
checked={isEditMode}
|
||||
disabled={!share}
|
||||
/>
|
||||
<SwitchLabel>
|
||||
<SwitchText>
|
||||
{isEditMode
|
||||
? t(
|
||||
"Users with edit permission will be redirected to the main app"
|
||||
)
|
||||
: t("All users see the same publicly shared view")}
|
||||
.
|
||||
</SwitchText>
|
||||
</SwitchLabel>
|
||||
</SwitchWrapper>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Flex justify="space-between" style={{ marginBottom: 8 }}>
|
||||
{expandedOptions ? (
|
||||
<span />
|
||||
) : (
|
||||
<MoreOptionsButton
|
||||
icon={<ExpandedIcon />}
|
||||
onClick={() => setExpandedOptions(true)}
|
||||
neutral
|
||||
borderOnHover
|
||||
>
|
||||
{t("More options")}
|
||||
</MoreOptionsButton>
|
||||
)}
|
||||
<CopyToClipboard text={shareUrl} onCopy={handleCopied}>
|
||||
<Button
|
||||
type="submit"
|
||||
@@ -228,6 +268,9 @@ const Heading = styled.h2`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 12px;
|
||||
gap: 8px;
|
||||
|
||||
/* accounts for icon padding */
|
||||
margin-left: -4px;
|
||||
`;
|
||||
|
||||
@@ -235,9 +278,17 @@ const SwitchWrapper = styled.div`
|
||||
margin: 20px 0;
|
||||
`;
|
||||
|
||||
const InputLink = styled(Input)`
|
||||
flex-grow: 1;
|
||||
margin-right: 8px;
|
||||
const MoreOptionsButton = styled(Button)`
|
||||
background: none;
|
||||
font-size: 14px;
|
||||
color: ${(props) => props.theme.textSecondary};
|
||||
margin-left: -8px;
|
||||
`;
|
||||
|
||||
const Separator = styled.div`
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background-color: ${(props) => props.theme.divider};
|
||||
`;
|
||||
|
||||
const SwitchLabel = styled(Flex)`
|
||||
|
||||
@@ -449,6 +449,10 @@
|
||||
"Share nested documents": "Share nested documents",
|
||||
"Nested documents are publicly available": "Nested documents are publicly available",
|
||||
"Nested documents are not shared": "Nested documents are not shared",
|
||||
"Automatically redirect to the editor": "Automatically redirect to the editor",
|
||||
"Users with edit permission will be redirected to the main app": "Users with edit permission will be redirected to the main app",
|
||||
"All users see the same publicly shared view": "All users see the same publicly shared view",
|
||||
"More options": "More options",
|
||||
"{{ teamName }} is using Outline to share documents, please login to continue.": "{{ teamName }} is using Outline to share documents, please login to continue.",
|
||||
"Are you sure you want to delete the <em>{{ documentTitle }}</em> template?": "Are you sure you want to delete the <em>{{ documentTitle }}</em> template?",
|
||||
"Are you sure about that? Deleting the <em>{{ documentTitle }}</em> document will delete all of its history</em>.": "Are you sure about that? Deleting the <em>{{ documentTitle }}</em> document will delete all of its history</em>.",
|
||||
|
||||
Reference in New Issue
Block a user