fix: Do not show suspended users in picker
This commit is contained in:
158
app/components/Sharing/OtherAccess.tsx
Normal file
158
app/components/Sharing/OtherAccess.tsx
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
import { observer } from "mobx-react";
|
||||||
|
import { MoreIcon, QuestionMarkIcon, UserIcon } from "outline-icons";
|
||||||
|
import * as React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useTheme } from "styled-components";
|
||||||
|
import { CollectionPermission } from "@shared/types";
|
||||||
|
import type Collection from "~/models/Collection";
|
||||||
|
import type Document from "~/models/Document";
|
||||||
|
import Flex from "~/components/Flex";
|
||||||
|
import Text from "~/components/Text";
|
||||||
|
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||||
|
import useRequest from "~/hooks/useRequest";
|
||||||
|
import useStores from "~/hooks/useStores";
|
||||||
|
import Avatar from "../Avatar";
|
||||||
|
import { AvatarSize } from "../Avatar/Avatar";
|
||||||
|
import CollectionIcon from "../Icons/CollectionIcon";
|
||||||
|
import Squircle from "../Squircle";
|
||||||
|
import Tooltip from "../Tooltip";
|
||||||
|
import { StyledListItem } from "./MemberListItem";
|
||||||
|
|
||||||
|
export const OtherAccess = observer(
|
||||||
|
({
|
||||||
|
document,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
document: Document;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const theme = useTheme();
|
||||||
|
const collection = document.collection;
|
||||||
|
const usersInCollection = useUsersInCollection(collection);
|
||||||
|
const user = useCurrentUser();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{collection ? (
|
||||||
|
<>
|
||||||
|
{collection.permission ? (
|
||||||
|
<StyledListItem
|
||||||
|
image={
|
||||||
|
<Squircle color={theme.accent} size={AvatarSize.Medium}>
|
||||||
|
<UserIcon color={theme.accentText} size={16} />
|
||||||
|
</Squircle>
|
||||||
|
}
|
||||||
|
title={t("All members")}
|
||||||
|
subtitle={t("Everyone in the workspace")}
|
||||||
|
actions={
|
||||||
|
<AccessTooltip>
|
||||||
|
{collection?.permission === CollectionPermission.ReadWrite
|
||||||
|
? t("Can edit")
|
||||||
|
: t("Can view")}
|
||||||
|
</AccessTooltip>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : usersInCollection ? (
|
||||||
|
<StyledListItem
|
||||||
|
image={
|
||||||
|
<Squircle color={collection.color} size={AvatarSize.Medium}>
|
||||||
|
<CollectionIcon
|
||||||
|
collection={collection}
|
||||||
|
color={theme.white}
|
||||||
|
size={16}
|
||||||
|
/>
|
||||||
|
</Squircle>
|
||||||
|
}
|
||||||
|
title={collection.name}
|
||||||
|
subtitle={t("Everyone in the collection")}
|
||||||
|
actions={<AccessTooltip>{t("Can view")}</AccessTooltip>}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<StyledListItem
|
||||||
|
image={<Avatar model={user} showBorder={false} />}
|
||||||
|
title={user.name}
|
||||||
|
subtitle={t("You have full access")}
|
||||||
|
actions={<AccessTooltip>{t("Can edit")}</AccessTooltip>}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
) : document.isDraft ? (
|
||||||
|
<>
|
||||||
|
<StyledListItem
|
||||||
|
image={<Avatar model={document.createdBy} showBorder={false} />}
|
||||||
|
title={document.createdBy.name}
|
||||||
|
actions={
|
||||||
|
<AccessTooltip tooltip={t("Created the document")}>
|
||||||
|
{t("Can edit")}
|
||||||
|
</AccessTooltip>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{children}
|
||||||
|
<StyledListItem
|
||||||
|
image={
|
||||||
|
<Squircle color={theme.accent} size={AvatarSize.Medium}>
|
||||||
|
<MoreIcon color={theme.accentText} size={16} />
|
||||||
|
</Squircle>
|
||||||
|
}
|
||||||
|
title={t("Other people")}
|
||||||
|
subtitle={t("Other workspace members may have access")}
|
||||||
|
actions={
|
||||||
|
<AccessTooltip
|
||||||
|
tooltip={t(
|
||||||
|
"This document may be shared with more workspace members through a parent document or collection you do not have access to"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const AccessTooltip = ({
|
||||||
|
children,
|
||||||
|
tooltip,
|
||||||
|
}: {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
tooltip?: string;
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex align="center" gap={2}>
|
||||||
|
<Text type="secondary" size="small">
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
<Tooltip tooltip={tooltip ?? t("Access inherited from collection")}>
|
||||||
|
<QuestionMarkIcon size={18} />
|
||||||
|
</Tooltip>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function useUsersInCollection(collection?: Collection) {
|
||||||
|
const { users, memberships } = useStores();
|
||||||
|
const { request } = useRequest(() =>
|
||||||
|
memberships.fetchPage({ limit: 1, id: collection!.id })
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (collection && !collection.permission) {
|
||||||
|
void request();
|
||||||
|
}
|
||||||
|
}, [collection]);
|
||||||
|
|
||||||
|
return collection
|
||||||
|
? collection.permission
|
||||||
|
? true
|
||||||
|
: users.inCollection(collection.id).length > 1
|
||||||
|
: false;
|
||||||
|
}
|
||||||
@@ -1,49 +1,33 @@
|
|||||||
import { AnimatePresence, m } from "framer-motion";
|
import { AnimatePresence, m } from "framer-motion";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import {
|
import { BackIcon, LinkIcon } from "outline-icons";
|
||||||
BackIcon,
|
|
||||||
LinkIcon,
|
|
||||||
MoreIcon,
|
|
||||||
QuestionMarkIcon,
|
|
||||||
UserIcon,
|
|
||||||
} from "outline-icons";
|
|
||||||
import { darken } from "polished";
|
import { darken } from "polished";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import styled, { useTheme } from "styled-components";
|
import styled from "styled-components";
|
||||||
import { s } from "@shared/styles";
|
import { s } from "@shared/styles";
|
||||||
import { CollectionPermission } from "@shared/types";
|
|
||||||
import Collection from "~/models/Collection";
|
|
||||||
import Document from "~/models/Document";
|
import Document from "~/models/Document";
|
||||||
import Share from "~/models/Share";
|
import Share from "~/models/Share";
|
||||||
import User from "~/models/User";
|
import User from "~/models/User";
|
||||||
import CopyToClipboard from "~/components/CopyToClipboard";
|
import CopyToClipboard from "~/components/CopyToClipboard";
|
||||||
import Flex from "~/components/Flex";
|
import Flex from "~/components/Flex";
|
||||||
import Text from "~/components/Text";
|
|
||||||
import useBoolean from "~/hooks/useBoolean";
|
import useBoolean from "~/hooks/useBoolean";
|
||||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
|
||||||
import useKeyDown from "~/hooks/useKeyDown";
|
import useKeyDown from "~/hooks/useKeyDown";
|
||||||
import useMobile from "~/hooks/useMobile";
|
import useMobile from "~/hooks/useMobile";
|
||||||
import usePolicy from "~/hooks/usePolicy";
|
import usePolicy from "~/hooks/usePolicy";
|
||||||
import useRequest from "~/hooks/useRequest";
|
|
||||||
import useStores from "~/hooks/useStores";
|
import useStores from "~/hooks/useStores";
|
||||||
import useThrottledCallback from "~/hooks/useThrottledCallback";
|
|
||||||
import { hover } from "~/styles";
|
import { hover } from "~/styles";
|
||||||
import { documentPath, urlify } from "~/utils/routeHelpers";
|
import { documentPath, urlify } from "~/utils/routeHelpers";
|
||||||
import Avatar from "../Avatar";
|
|
||||||
import { AvatarSize } from "../Avatar/Avatar";
|
|
||||||
import ButtonSmall from "../ButtonSmall";
|
import ButtonSmall from "../ButtonSmall";
|
||||||
import Empty from "../Empty";
|
|
||||||
import CollectionIcon from "../Icons/CollectionIcon";
|
|
||||||
import Input, { NativeInput } from "../Input";
|
import Input, { NativeInput } from "../Input";
|
||||||
import NudeButton from "../NudeButton";
|
import NudeButton from "../NudeButton";
|
||||||
import Squircle from "../Squircle";
|
|
||||||
import Tooltip from "../Tooltip";
|
import Tooltip from "../Tooltip";
|
||||||
import DocumentMembersList from "./DocumentMemberList";
|
import DocumentMembersList from "./DocumentMemberList";
|
||||||
import { InviteIcon, StyledListItem } from "./MemberListItem";
|
import { OtherAccess } from "./OtherAccess";
|
||||||
import PublicAccess from "./PublicAccess";
|
import PublicAccess from "./PublicAccess";
|
||||||
|
import { UserSuggestions } from "./UserSuggestions";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
/** The document to share. */
|
/** The document to share. */
|
||||||
@@ -81,25 +65,6 @@ const presence = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function useUsersInCollection(collection?: Collection) {
|
|
||||||
const { users, memberships } = useStores();
|
|
||||||
const { request } = useRequest(() =>
|
|
||||||
memberships.fetchPage({ limit: 1, id: collection!.id })
|
|
||||||
);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (collection && !collection.permission) {
|
|
||||||
void request();
|
|
||||||
}
|
|
||||||
}, [collection]);
|
|
||||||
|
|
||||||
return collection
|
|
||||||
? collection.permission
|
|
||||||
? true
|
|
||||||
: users.inCollection(collection.id).length > 1
|
|
||||||
: false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SharePopover({
|
function SharePopover({
|
||||||
document,
|
document,
|
||||||
share,
|
share,
|
||||||
@@ -274,17 +239,21 @@ function SharePopover({
|
|||||||
|
|
||||||
{picker && (
|
{picker && (
|
||||||
<div>
|
<div>
|
||||||
<Picker document={document} query={query} onInvite={handleInvite} />
|
<UserSuggestions
|
||||||
|
document={document}
|
||||||
|
query={query}
|
||||||
|
onInvite={handleInvite}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div style={{ display: picker ? "none" : "block" }}>
|
<div style={{ display: picker ? "none" : "block" }}>
|
||||||
<DocumentOtherAccessList document={document}>
|
<OtherAccess document={document}>
|
||||||
<DocumentMembersList
|
<DocumentMembersList
|
||||||
document={document}
|
document={document}
|
||||||
invitedInSession={invitedInSession}
|
invitedInSession={invitedInSession}
|
||||||
/>
|
/>
|
||||||
</DocumentOtherAccessList>
|
</OtherAccess>
|
||||||
|
|
||||||
{team.sharing && can.share && !collectionSharingDisabled && (
|
{team.sharing && can.share && !collectionSharingDisabled && (
|
||||||
<>
|
<>
|
||||||
@@ -302,184 +271,6 @@ function SharePopover({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Picker = observer(
|
|
||||||
({
|
|
||||||
document,
|
|
||||||
query,
|
|
||||||
onInvite,
|
|
||||||
}: {
|
|
||||||
document: Document;
|
|
||||||
query: string;
|
|
||||||
onInvite: (user: User) => Promise<void>;
|
|
||||||
}) => {
|
|
||||||
const { users } = useStores();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const user = useCurrentUser();
|
|
||||||
|
|
||||||
const fetchUsersByQuery = useThrottledCallback(
|
|
||||||
(query) => users.fetchPage({ query }),
|
|
||||||
250
|
|
||||||
);
|
|
||||||
|
|
||||||
const suggestions = React.useMemo(
|
|
||||||
() =>
|
|
||||||
users
|
|
||||||
.notInDocument(document.id, query)
|
|
||||||
.filter((u) => u.id !== user.id && !user.isSuspended),
|
|
||||||
[users, users.orderedData, document.id, document.members, user.id, query]
|
|
||||||
);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (query) {
|
|
||||||
void fetchUsersByQuery(query);
|
|
||||||
}
|
|
||||||
}, [query, fetchUsersByQuery]);
|
|
||||||
|
|
||||||
return suggestions.length ? (
|
|
||||||
<>
|
|
||||||
{suggestions.map((suggestion) => (
|
|
||||||
<StyledListItem
|
|
||||||
key={suggestion.id}
|
|
||||||
onClick={() => onInvite(suggestion)}
|
|
||||||
title={suggestion.name}
|
|
||||||
subtitle={suggestion.isViewer ? t("Viewer") : t("Editor")}
|
|
||||||
image={
|
|
||||||
<Avatar
|
|
||||||
model={suggestion}
|
|
||||||
size={AvatarSize.Medium}
|
|
||||||
showBorder={false}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
actions={<InviteIcon />}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Empty style={{ marginTop: 22 }}>{t("No matches")}</Empty>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const DocumentOtherAccessList = observer(
|
|
||||||
({
|
|
||||||
document,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
document: Document;
|
|
||||||
children: React.ReactNode;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const theme = useTheme();
|
|
||||||
const collection = document.collection;
|
|
||||||
const usersInCollection = useUsersInCollection(collection);
|
|
||||||
const user = useCurrentUser();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{collection ? (
|
|
||||||
<>
|
|
||||||
{collection.permission ? (
|
|
||||||
<StyledListItem
|
|
||||||
image={
|
|
||||||
<Squircle color={theme.accent} size={AvatarSize.Medium}>
|
|
||||||
<UserIcon color={theme.accentText} size={16} />
|
|
||||||
</Squircle>
|
|
||||||
}
|
|
||||||
title={t("All members")}
|
|
||||||
subtitle={t("Everyone in the workspace")}
|
|
||||||
actions={
|
|
||||||
<AccessTooltip>
|
|
||||||
{collection?.permission === CollectionPermission.ReadWrite
|
|
||||||
? t("Can edit")
|
|
||||||
: t("Can view")}
|
|
||||||
</AccessTooltip>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : usersInCollection ? (
|
|
||||||
<StyledListItem
|
|
||||||
image={
|
|
||||||
<Squircle color={collection.color} size={AvatarSize.Medium}>
|
|
||||||
<CollectionIcon
|
|
||||||
collection={collection}
|
|
||||||
color={theme.white}
|
|
||||||
size={16}
|
|
||||||
/>
|
|
||||||
</Squircle>
|
|
||||||
}
|
|
||||||
title={collection.name}
|
|
||||||
subtitle={t("Everyone in the collection")}
|
|
||||||
actions={<AccessTooltip>{t("Can view")}</AccessTooltip>}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<StyledListItem
|
|
||||||
image={<Avatar model={user} showBorder={false} />}
|
|
||||||
title={user.name}
|
|
||||||
subtitle={t("You have full access")}
|
|
||||||
actions={<AccessTooltip>{t("Can edit")}</AccessTooltip>}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
) : document.isDraft ? (
|
|
||||||
<>
|
|
||||||
<StyledListItem
|
|
||||||
image={<Avatar model={document.createdBy} showBorder={false} />}
|
|
||||||
title={document.createdBy.name}
|
|
||||||
actions={
|
|
||||||
<AccessTooltip tooltip={t("Created the document")}>
|
|
||||||
{t("Can edit")}
|
|
||||||
</AccessTooltip>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{children}
|
|
||||||
<StyledListItem
|
|
||||||
image={
|
|
||||||
<Squircle color={theme.accent} size={AvatarSize.Medium}>
|
|
||||||
<MoreIcon color={theme.accentText} size={16} />
|
|
||||||
</Squircle>
|
|
||||||
}
|
|
||||||
title={t("Other people")}
|
|
||||||
subtitle={t("Other workspace members may have access")}
|
|
||||||
actions={
|
|
||||||
<AccessTooltip
|
|
||||||
tooltip={t(
|
|
||||||
"This document may be shared with more workspace members through a parent document or collection you do not have access to"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const AccessTooltip = ({
|
|
||||||
children,
|
|
||||||
tooltip,
|
|
||||||
}: {
|
|
||||||
children?: React.ReactNode;
|
|
||||||
tooltip?: string;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex align="center" gap={2}>
|
|
||||||
<Text type="secondary" size="small">
|
|
||||||
{children}
|
|
||||||
</Text>
|
|
||||||
<Tooltip tooltip={tooltip ?? t("Access inherited from collection")}>
|
|
||||||
<QuestionMarkIcon size={18} />
|
|
||||||
</Tooltip>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Temp until Button/NudeButton styles are normalized
|
// TODO: Temp until Button/NudeButton styles are normalized
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
${NudeButton}:${hover},
|
${NudeButton}:${hover},
|
||||||
|
|||||||
70
app/components/Sharing/UserSuggestions.tsx
Normal file
70
app/components/Sharing/UserSuggestions.tsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { observer } from "mobx-react";
|
||||||
|
import * as React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import Document from "~/models/Document";
|
||||||
|
import User from "~/models/User";
|
||||||
|
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||||
|
import useStores from "~/hooks/useStores";
|
||||||
|
import useThrottledCallback from "~/hooks/useThrottledCallback";
|
||||||
|
import Avatar from "../Avatar";
|
||||||
|
import { AvatarSize } from "../Avatar/Avatar";
|
||||||
|
import Empty from "../Empty";
|
||||||
|
import { InviteIcon, StyledListItem } from "./MemberListItem";
|
||||||
|
|
||||||
|
export const UserSuggestions = observer(
|
||||||
|
({
|
||||||
|
document,
|
||||||
|
query,
|
||||||
|
onInvite,
|
||||||
|
}: {
|
||||||
|
document: Document;
|
||||||
|
query: string;
|
||||||
|
onInvite: (user: User) => Promise<void>;
|
||||||
|
}) => {
|
||||||
|
const { users } = useStores();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const user = useCurrentUser();
|
||||||
|
|
||||||
|
const fetchUsersByQuery = useThrottledCallback(
|
||||||
|
(query) => users.fetchPage({ query }),
|
||||||
|
250
|
||||||
|
);
|
||||||
|
|
||||||
|
const suggestions = React.useMemo(
|
||||||
|
() =>
|
||||||
|
users
|
||||||
|
.notInDocument(document.id, query)
|
||||||
|
.filter((u) => u.id !== user.id && !u.isSuspended),
|
||||||
|
[users, users.orderedData, document.id, document.members, user.id, query]
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (query) {
|
||||||
|
void fetchUsersByQuery(query);
|
||||||
|
}
|
||||||
|
}, [query, fetchUsersByQuery]);
|
||||||
|
|
||||||
|
return suggestions.length ? (
|
||||||
|
<>
|
||||||
|
{suggestions.map((suggestion) => (
|
||||||
|
<StyledListItem
|
||||||
|
key={suggestion.id}
|
||||||
|
onClick={() => onInvite(suggestion)}
|
||||||
|
title={suggestion.name}
|
||||||
|
subtitle={suggestion.isViewer ? t("Viewer") : t("Editor")}
|
||||||
|
image={
|
||||||
|
<Avatar
|
||||||
|
model={suggestion}
|
||||||
|
size={AvatarSize.Medium}
|
||||||
|
showBorder={false}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
actions={<InviteIcon />}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Empty style={{ marginTop: 22 }}>{t("No matches")}</Empty>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -257,6 +257,16 @@
|
|||||||
"Suspended": "Suspended",
|
"Suspended": "Suspended",
|
||||||
"Invited": "Invited",
|
"Invited": "Invited",
|
||||||
"Leave": "Leave",
|
"Leave": "Leave",
|
||||||
|
"All members": "All members",
|
||||||
|
"Everyone in the workspace": "Everyone in the workspace",
|
||||||
|
"Can view": "Can view",
|
||||||
|
"Everyone in the collection": "Everyone in the collection",
|
||||||
|
"You have full access": "You have full access",
|
||||||
|
"Created the document": "Created the document",
|
||||||
|
"Other people": "Other people",
|
||||||
|
"Other workspace members may have access": "Other workspace members may have access",
|
||||||
|
"This document may be shared with more workspace members through a parent document or collection you do not have access to": "This document may be shared with more workspace members through a parent document or collection you do not have access to",
|
||||||
|
"Access inherited from collection": "Access inherited from collection",
|
||||||
"Only lowercase letters, digits and dashes allowed": "Only lowercase letters, digits and dashes allowed",
|
"Only lowercase letters, digits and dashes allowed": "Only lowercase letters, digits and dashes allowed",
|
||||||
"Sorry, this link has already been used": "Sorry, this link has already been used",
|
"Sorry, this link has already been used": "Sorry, this link has already been used",
|
||||||
"Public link copied to clipboard": "Public link copied to clipboard",
|
"Public link copied to clipboard": "Public link copied to clipboard",
|
||||||
@@ -270,16 +280,6 @@
|
|||||||
"Done": "Done",
|
"Done": "Done",
|
||||||
"Invite by name": "Invite by name",
|
"Invite by name": "Invite by name",
|
||||||
"No matches": "No matches",
|
"No matches": "No matches",
|
||||||
"All members": "All members",
|
|
||||||
"Everyone in the workspace": "Everyone in the workspace",
|
|
||||||
"Can view": "Can view",
|
|
||||||
"Everyone in the collection": "Everyone in the collection",
|
|
||||||
"You have full access": "You have full access",
|
|
||||||
"Created the document": "Created the document",
|
|
||||||
"Other people": "Other people",
|
|
||||||
"Other workspace members may have access": "Other workspace members may have access",
|
|
||||||
"This document may be shared with more workspace members through a parent document or collection you do not have access to": "This document may be shared with more workspace members through a parent document or collection you do not have access to",
|
|
||||||
"Access inherited from collection": "Access inherited from collection",
|
|
||||||
"Logo": "Logo",
|
"Logo": "Logo",
|
||||||
"Move document": "Move document",
|
"Move document": "Move document",
|
||||||
"New doc": "New doc",
|
"New doc": "New doc",
|
||||||
|
|||||||
Reference in New Issue
Block a user