From 1d97a6c10b52fc66c57a8a78dbc367faa2b985a7 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Wed, 5 Jun 2024 06:33:39 -0400 Subject: [PATCH] chore: Remove old collection permissions UI (#6995) --- app/actions/definitions/collections.tsx | 30 +- .../AddGroupsToCollection.tsx | 132 ------- .../AddPeopleToCollection.tsx | 130 ------- .../CollectionGroupMemberListItem.tsx | 59 ---- .../components/MemberListItem.tsx | 92 ----- .../components/UserListItem.tsx | 49 --- app/scenes/CollectionPermissions/index.tsx | 334 ------------------ shared/i18n/locales/en_US/translation.json | 47 +-- 8 files changed, 20 insertions(+), 853 deletions(-) delete mode 100644 app/scenes/CollectionPermissions/AddGroupsToCollection.tsx delete mode 100644 app/scenes/CollectionPermissions/AddPeopleToCollection.tsx delete mode 100644 app/scenes/CollectionPermissions/components/CollectionGroupMemberListItem.tsx delete mode 100644 app/scenes/CollectionPermissions/components/MemberListItem.tsx delete mode 100644 app/scenes/CollectionPermissions/components/UserListItem.tsx delete mode 100644 app/scenes/CollectionPermissions/index.tsx diff --git a/app/actions/definitions/collections.tsx b/app/actions/definitions/collections.tsx index 2e567e14f..f995720a0 100644 --- a/app/actions/definitions/collections.tsx +++ b/app/actions/definitions/collections.tsx @@ -11,7 +11,6 @@ import { import * as React from "react"; import stores from "~/stores"; import Collection from "~/models/Collection"; -import CollectionPermissions from "~/scenes/CollectionPermissions"; import { CollectionEdit } from "~/components/Collection/CollectionEdit"; import { CollectionNew } from "~/components/Collection/CollectionNew"; import CollectionDeleteDialog from "~/components/CollectionDeleteDialog"; @@ -21,7 +20,6 @@ import { getHeaderExpandedKey } from "~/components/Sidebar/components/Header"; import { createAction } from "~/actions"; import { CollectionSection } from "~/actions/sections"; import { setPersistedState } from "~/hooks/usePersistedState"; -import { Feature, FeatureFlags } from "~/utils/FeatureFlags"; import history from "~/utils/history"; import { searchPath } from "~/utils/routeHelpers"; @@ -111,24 +109,16 @@ export const editCollectionPermissions = createAction({ return; } - if (FeatureFlags.isEnabled(Feature.newCollectionSharing)) { - stores.dialogs.openModal({ - title: t("Share this collection"), - content: ( - - ), - }); - } else { - stores.dialogs.openModal({ - title: t("Collection permissions"), - fullscreen: true, - content: , - }); - } + stores.dialogs.openModal({ + title: t("Share this collection"), + content: ( + + ), + }); }, }); diff --git a/app/scenes/CollectionPermissions/AddGroupsToCollection.tsx b/app/scenes/CollectionPermissions/AddGroupsToCollection.tsx deleted file mode 100644 index a2035ec48..000000000 --- a/app/scenes/CollectionPermissions/AddGroupsToCollection.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import debounce from "lodash/debounce"; -import { observer } from "mobx-react"; -import * as React from "react"; -import { useTranslation } from "react-i18next"; -import { toast } from "sonner"; -import styled from "styled-components"; -import Collection from "~/models/Collection"; -import Group from "~/models/Group"; -import GroupNew from "~/scenes/GroupNew"; -import Button from "~/components/Button"; -import ButtonLink from "~/components/ButtonLink"; -import Empty from "~/components/Empty"; -import Flex from "~/components/Flex"; -import GroupListItem from "~/components/GroupListItem"; -import Input from "~/components/Input"; -import Modal from "~/components/Modal"; -import PaginatedList from "~/components/PaginatedList"; -import Text from "~/components/Text"; -import useBoolean from "~/hooks/useBoolean"; -import useCurrentTeam from "~/hooks/useCurrentTeam"; -import useStores from "~/hooks/useStores"; - -type Props = { - collection: Collection; -}; - -function AddGroupsToCollection(props: Props) { - const { collection } = props; - - const [newGroupModalOpen, handleNewGroupModalOpen, handleNewGroupModalClose] = - useBoolean(false); - const [query, setQuery] = React.useState(""); - const team = useCurrentTeam(); - const { collectionGroupMemberships, groups, policies } = useStores(); - const { t } = useTranslation(); - const { fetchPage: fetchGroups } = groups; - const can = policies.abilities(team.id); - - const debouncedFetch = React.useMemo( - () => debounce((query) => fetchGroups({ query }), 250), - [fetchGroups] - ); - - const handleFilter = React.useCallback( - (ev: React.ChangeEvent) => { - const updatedQuery = ev.target.value; - setQuery(updatedQuery); - void debouncedFetch(updatedQuery); - }, - [debouncedFetch] - ); - - const handleAddGroup = async (group: Group) => { - try { - await collectionGroupMemberships.create({ - collectionId: collection.id, - groupId: group.id, - }); - toast.success( - t("{{ groupName }} was added to the collection", { - groupName: group.name, - }) - ); - } catch (err) { - toast.error(t("Could not add user")); - } - }; - - return ( - - {can.createGroup ? ( - - {t("Can’t find the group you’re looking for?")}{" "} - - {t("Create a group")} - - . - - ) : null} - - - {t("No groups matching your search")} - ) : ( - {t("No groups left to add")} - ) - } - items={groups.notInCollection(collection.id, query)} - fetch={query ? undefined : fetchGroups} - renderItem={(item: Group) => ( - ( - - - - )} - /> - )} - /> - {can.createGroup ? ( - - - - ) : null} - - ); -} - -const ButtonWrap = styled.div` - margin-left: 6px; -`; - -export default observer(AddGroupsToCollection); diff --git a/app/scenes/CollectionPermissions/AddPeopleToCollection.tsx b/app/scenes/CollectionPermissions/AddPeopleToCollection.tsx deleted file mode 100644 index 67f72fba9..000000000 --- a/app/scenes/CollectionPermissions/AddPeopleToCollection.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { observer } from "mobx-react"; -import * as React from "react"; -import { useTranslation } from "react-i18next"; -import { toast } from "sonner"; -import { CollectionPermission, UserRole } from "@shared/types"; -import Collection from "~/models/Collection"; -import User from "~/models/User"; -import Invite from "~/scenes/Invite"; -import Avatar from "~/components/Avatar"; -import { AvatarSize } from "~/components/Avatar/Avatar"; -import ButtonLink from "~/components/ButtonLink"; -import Empty from "~/components/Empty"; -import Flex from "~/components/Flex"; -import Input from "~/components/Input"; -import Modal from "~/components/Modal"; -import PaginatedList from "~/components/PaginatedList"; -import Text from "~/components/Text"; -import useBoolean from "~/hooks/useBoolean"; -import useCurrentTeam from "~/hooks/useCurrentTeam"; -import usePolicy from "~/hooks/usePolicy"; -import useStores from "~/hooks/useStores"; -import useThrottledCallback from "~/hooks/useThrottledCallback"; -import MemberListItem from "./components/MemberListItem"; - -type Props = { - collection: Collection; -}; - -function AddPeopleToCollection({ collection }: Props) { - const { memberships, users } = useStores(); - const team = useCurrentTeam(); - const { t } = useTranslation(); - const can = usePolicy(team); - - const [inviteModalOpen, setInviteModalOpen, setInviteModalClosed] = - useBoolean(); - const [query, setQuery] = React.useState(""); - - const debouncedFetch = useThrottledCallback( - (query) => - users.fetchPage({ - query, - }), - 250 - ); - - const handleFilter = (ev: React.ChangeEvent) => { - setQuery(ev.target.value); - void debouncedFetch(ev.target.value); - }; - - const handleAddUser = async (user: User) => { - try { - await memberships.create({ - permission: - user.role === UserRole.Viewer || user.role === UserRole.Guest - ? CollectionPermission.Read - : CollectionPermission.ReadWrite, - collectionId: collection.id, - userId: user.id, - }); - toast.success( - t("{{ userName }} was added to the collection", { - userName: user.name, - }), - { - icon: , - } - ); - } catch (err) { - toast.error(t("Could not add user")); - } - }; - - return ( - - - {t("Need to add someone who’s not on the team yet?")}{" "} - {can.inviteUser ? ( - - {t("Invite people to {{ teamName }}", { - teamName: team.name, - })} - - ) : ( - t("Ask an admin to invite them first") - )} - . - - - {t("No people matching your search")} - ) : ( - {t("No people left to add")} - ) - } - items={users.notInCollection(collection.id, query)} - fetch={query ? undefined : users.fetchPage} - renderItem={(item: User) => ( - handleAddUser(item)} - canEdit - /> - )} - /> - - - - - ); -} - -export default observer(AddPeopleToCollection); diff --git a/app/scenes/CollectionPermissions/components/CollectionGroupMemberListItem.tsx b/app/scenes/CollectionPermissions/components/CollectionGroupMemberListItem.tsx deleted file mode 100644 index 55c118ea4..000000000 --- a/app/scenes/CollectionPermissions/components/CollectionGroupMemberListItem.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import * as React from "react"; -import { useTranslation } from "react-i18next"; -import { CollectionPermission } from "@shared/types"; -import CollectionGroupMembership from "~/models/CollectionGroupMembership"; -import Group from "~/models/Group"; -import GroupListItem from "~/components/GroupListItem"; -import InputMemberPermissionSelect from "~/components/InputMemberPermissionSelect"; -import CollectionGroupMemberMenu from "~/menus/CollectionGroupMemberMenu"; - -type Props = { - group: Group; - collectionGroupMembership: CollectionGroupMembership | null | undefined; - onUpdate: (permission: CollectionPermission) => void; - onRemove: () => void; -}; - -const CollectionGroupMemberListItem = ({ - group, - collectionGroupMembership, - onUpdate, - onRemove, -}: Props) => { - const { t } = useTranslation(); - - return ( - ( - <> - - - - )} - /> - ); -}; - -export default CollectionGroupMemberListItem; diff --git a/app/scenes/CollectionPermissions/components/MemberListItem.tsx b/app/scenes/CollectionPermissions/components/MemberListItem.tsx deleted file mode 100644 index e453b2d30..000000000 --- a/app/scenes/CollectionPermissions/components/MemberListItem.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { observer } from "mobx-react"; -import * as React from "react"; -import { Trans, useTranslation } from "react-i18next"; -import { CollectionPermission } from "@shared/types"; -import Membership from "~/models/Membership"; -import User from "~/models/User"; -import UserMembership from "~/models/UserMembership"; -import Avatar from "~/components/Avatar"; -import Badge from "~/components/Badge"; -import Button from "~/components/Button"; -import Flex from "~/components/Flex"; -import InputMemberPermissionSelect from "~/components/InputMemberPermissionSelect"; -import ListItem from "~/components/List/Item"; -import Time from "~/components/Time"; -import MemberMenu from "~/menus/MemberMenu"; - -type Props = { - user: User; - membership?: Membership | UserMembership | undefined; - canEdit: boolean; - onAdd?: () => void; - onRemove?: () => void; - onUpdate?: (permission: CollectionPermission) => void; -}; - -const MemberListItem = ({ - user, - membership, - onRemove, - onUpdate, - onAdd, - canEdit, -}: Props) => { - const { t } = useTranslation(); - - return ( - - {user.lastActiveAt ? ( - - Active - ) : ( - t("Never signed in") - )} - {user.isInvited && {t("Invited")}} - {user.isAdmin && {t("Admin")}} - - } - image={} - actions={ - - {onUpdate && ( - - )} - {canEdit && ( - <> - {onRemove && } - {onAdd && ( - - )} - - )} - - } - /> - ); -}; - -export default observer(MemberListItem); diff --git a/app/scenes/CollectionPermissions/components/UserListItem.tsx b/app/scenes/CollectionPermissions/components/UserListItem.tsx deleted file mode 100644 index eecd5ae59..000000000 --- a/app/scenes/CollectionPermissions/components/UserListItem.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { observer } from "mobx-react"; -import { PlusIcon } from "outline-icons"; -import * as React from "react"; -import { Trans, useTranslation } from "react-i18next"; -import User from "~/models/User"; -import Avatar from "~/components/Avatar"; -import Badge from "~/components/Badge"; -import Button from "~/components/Button"; -import ListItem from "~/components/List/Item"; -import Time from "~/components/Time"; - -type Props = { - user: User; - canEdit: boolean; - onAdd: () => void; -}; - -const UserListItem = ({ user, onAdd, canEdit }: Props) => { - const { t } = useTranslation(); - - return ( - } - subtitle={ - <> - {user.lastActiveAt ? ( - - Active - ) : ( - t("Never signed in") - )} - {user.isInvited && {t("Invited")}} - {user.isAdmin && {t("Admin")}} - - } - actions={ - canEdit ? ( - - ) : undefined - } - /> - ); -}; - -export default observer(UserListItem); diff --git a/app/scenes/CollectionPermissions/index.tsx b/app/scenes/CollectionPermissions/index.tsx deleted file mode 100644 index d1d6c44db..000000000 --- a/app/scenes/CollectionPermissions/index.tsx +++ /dev/null @@ -1,334 +0,0 @@ -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); diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index f676cd0d0..c41920f97 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -518,43 +518,6 @@ "Recently published": "Recently published", "Least recently updated": "Least recently updated", "A–Z": "A–Z", - "{{ groupName }} was added to the collection": "{{ groupName }} was added to the collection", - "Could not add user": "Could not add user", - "Can’t find the group you’re looking for?": "Can’t find the group you’re looking for?", - "Create a group": "Create a group", - "Search by group name": "Search by group name", - "Search groups": "Search groups", - "No groups matching your search": "No groups matching your search", - "No groups left to add": "No groups left to add", - "Need to add someone who’s not on the team yet?": "Need to add someone who’s not on the team yet?", - "Invite people to {{ teamName }}": "Invite people to {{ teamName }}", - "Ask an admin to invite them first": "Ask an admin to invite them first", - "Search by name": "Search by name", - "Search people": "Search people", - "No people matching your search": "No people matching your search", - "No people left to add": "No people left to add", - "Active <1> ago": "Active <1> ago", - "Never signed in": "Never signed in", - "{{ userName }} was removed from the collection": "{{ userName }} was removed from the collection", - "{{ userName }} permissions were updated": "{{ userName }} permissions were updated", - "The {{ groupName }} group was removed from the collection": "The {{ groupName }} group was removed from the collection", - "Could not remove group": "Could not remove group", - "{{ groupName }} permissions were updated": "{{ groupName }} permissions were updated", - "Default access permissions were updated": "Default access permissions were updated", - "Could not update permissions": "Could not update permissions", - "Public document sharing permissions were updated": "Public document sharing permissions were updated", - "Could not update public document sharing": "Could not update public document sharing", - "The {{ collectionName }} collection is private. Workspace members have no access to it by default.": "The {{ collectionName }} collection is private. Workspace members have no access to it by default.", - "Workspace members can view and edit documents in the {{ collectionName }} collection by default.": "Workspace members can view and edit documents in the {{ collectionName }} collection by default.", - "Workspace members can view documents in the {{ collectionName }} collection by\n default.": "Workspace members can view documents in the {{ collectionName }} collection by\n default.", - "When enabled, documents can be shared publicly on the internet.": "When enabled, documents can be shared publicly on the internet.", - "Public sharing is currently disabled in the workspace security settings.": "Public sharing is currently disabled in the workspace security settings.", - "Additional access": "Additional access", - "Add groups": "Add groups", - "Add people": "Add people", - "Add additional access for individual members and groups": "Add additional access for individual members and groups", - "Add groups to {{ collectionName }}": "Add groups to {{ collectionName }}", - "Add people to {{ collectionName }}": "Add people to {{ collectionName }}", "Signing in": "Signing in", "You can safely close this window once the Outline desktop app has opened": "You can safely close this window once the Outline desktop app has opened", "Error creating comment": "Error creating comment", @@ -658,10 +621,19 @@ "Are you sure about that? Deleting the {{groupName}} group will cause its members to lose access to collections and documents that it is associated with.": "Are you sure about that? Deleting the {{groupName}} group will cause its members to lose access to collections and documents that it is associated with.", "You can edit the name of this group at any time, however doing so too often might confuse your team mates.": "You can edit the name of this group at any time, however doing so too often might confuse your team mates.", "{{userName}} was added to the group": "{{userName}} was added to the group", + "Could not add user": "Could not add user", "Add members below to give them access to the group. Need to add someone who’s not yet a member?": "Add members below to give them access to the group. Need to add someone who’s not yet a member?", "Invite them to {{teamName}}": "Invite them to {{teamName}}", + "Ask an admin to invite them first": "Ask an admin to invite them first", + "Search by name": "Search by name", + "Search people": "Search people", + "No people matching your search": "No people matching your search", + "No people left to add": "No people left to add", + "Active <1> ago": "Active <1> ago", + "Never signed in": "Never signed in", "{{userName}} was removed from the group": "{{userName}} was removed from the group", "Add and remove members to the {{groupName}} group. Members of the group will have access to any collections this group has been added to.": "Add and remove members to the {{groupName}} group. Members of the group will have access to any collections this group has been added to.", + "Add people": "Add people", "Listing members of the {{groupName}} group.": "Listing members of the {{groupName}} group.", "This group has no members.": "This group has no members.", "Add people to {{groupName}}": "Add people to {{groupName}}", @@ -876,6 +848,7 @@ "Groups can be used to organize and manage the people on your team.": "Groups can be used to organize and manage the people on your team.", "No groups have been created yet": "No groups have been created yet", "All": "All", + "Create a group": "Create a group", "Quickly transfer your existing documents, pages, and files from other tools and services into {{appName}}. You can also drag and drop any HTML, Markdown, and text documents directly into Collections in the app.": "Quickly transfer your existing documents, pages, and files from other tools and services into {{appName}}. You can also drag and drop any HTML, Markdown, and text documents directly into Collections in the app.", "Import a zip file of Markdown documents (exported from version 0.67.0 or earlier)": "Import a zip file of Markdown documents (exported from version 0.67.0 or earlier)", "Import data": "Import data",