import invariant from "invariant"; import { observer } from "mobx-react"; import { PlusIcon } from "outline-icons"; import * as React from "react"; import { useTranslation, Trans } from "react-i18next"; import { toast } from "sonner"; import styled from "styled-components"; import { CollectionPermission } from "@shared/types"; import Group from "~/models/Group"; import User from "~/models/User"; import Button from "~/components/Button"; import Divider from "~/components/Divider"; import Flex from "~/components/Flex"; import InputSelectPermission from "~/components/InputSelectPermission"; import Labeled from "~/components/Labeled"; import Modal from "~/components/Modal"; import PaginatedList from "~/components/PaginatedList"; import Switch from "~/components/Switch"; import Text from "~/components/Text"; import useBoolean from "~/hooks/useBoolean"; import useCurrentUser from "~/hooks/useCurrentUser"; import useStores from "~/hooks/useStores"; import AddGroupsToCollection from "./AddGroupsToCollection"; import AddPeopleToCollection from "./AddPeopleToCollection"; import CollectionGroupMemberListItem from "./components/CollectionGroupMemberListItem"; import MemberListItem from "./components/MemberListItem"; type Props = { collectionId: string; }; function CollectionPermissions({ collectionId }: Props) { const { t } = useTranslation(); const user = useCurrentUser(); const { collections, memberships, collectionGroupMemberships, users, groups, auth, } = useStores(); const collection = collections.get(collectionId); invariant(collection, "Collection not found"); const [addGroupModalOpen, handleAddGroupModalOpen, handleAddGroupModalClose] = useBoolean(); const [ addMemberModalOpen, handleAddMemberModalOpen, handleAddMemberModalClose, ] = useBoolean(); const handleRemoveUser = React.useCallback( async (user) => { try { await memberships.delete({ collectionId: collection.id, userId: user.id, }); toast.success( t(`{{ userName }} was removed from the collection`, { userName: user.name, }) ); } catch (err) { toast.error(t("Could not remove user")); } }, [memberships, collection, t] ); const handleUpdateUser = React.useCallback( async (user, permission) => { try { await memberships.create({ collectionId: collection.id, userId: user.id, permission, }); toast.success( t(`{{ userName }} permissions were updated`, { userName: user.name, }) ); } catch (err) { toast.error(t("Could not update user")); } }, [memberships, collection, t] ); const handleRemoveGroup = React.useCallback( async (group) => { try { await collectionGroupMemberships.delete({ collectionId: collection.id, groupId: group.id, }); toast.success( t(`The {{ groupName }} group was removed from the collection`, { groupName: group.name, }) ); } catch (err) { toast.error(t("Could not remove group")); } }, [collectionGroupMemberships, collection, t] ); const handleUpdateGroup = React.useCallback( async (group, permission) => { try { await collectionGroupMemberships.create({ collectionId: collection.id, groupId: group.id, permission, }); toast.success( t(`{{ groupName }} permissions were updated`, { groupName: group.name, }) ); } catch (err) { toast.error(t("Could not update user")); } }, [collectionGroupMemberships, collection, t] ); const handleChangePermission = React.useCallback( async (permission: CollectionPermission) => { try { await collection.save({ permission, }); toast.success(t("Default access permissions were updated")); } catch (err) { toast.error(t("Could not update permissions")); } }, [collection, t] ); const fetchOptions = React.useMemo( () => ({ id: collection.id, }), [collection.id] ); const handleSharingChange = React.useCallback( async (ev: React.ChangeEvent) => { try { await collection.save({ sharing: ev.target.checked, }); toast.success(t("Public document sharing permissions were updated")); } catch (err) { toast.error(t("Could not update public document sharing")); } }, [collection, t] ); const collectionName = collection.name; const collectionGroups = groups.inCollection(collection.id); const collectionUsers = users.inCollection(collection.id); const isEmpty = !collectionGroups.length && !collectionUsers.length; const sharing = collection.sharing; const teamSharingEnabled = !!auth.team && auth.team.sharing; return ( {collection.isPrivate && ( , }} /> )} {collection.permission === CollectionPermission.ReadWrite && ( , }} /> )} {collection.permission === CollectionPermission.Read && ( , }} /> )} When enabled, documents can be shared publicly on the internet. ) : ( Public sharing is currently disabled in the workspace security settings. ) } /> {isEmpty && ( Add additional access for individual members and groups )} ( handleRemoveGroup(group)} onUpdate={(permission) => handleUpdateGroup(group, permission)} /> )} /> {collectionGroups.length ? : null} ( handleRemoveUser(item)} onUpdate={(permission) => handleUpdateUser(item, permission)} /> )} /> ); } const Empty = styled(Text)` margin-top: 8px; `; const PermissionExplainer = styled(Text)` margin-top: -8px; margin-bottom: 24px; `; const Actions = styled(Flex)` margin-bottom: 12px; `; export default observer(CollectionPermissions);