chore: Quick refactor to usePolicy hook (#3161)

This commit is contained in:
Tom Moor
2022-02-23 21:33:18 -08:00
committed by GitHub
parent 4c95674ef0
commit a3b8e7a65e
29 changed files with 90 additions and 64 deletions

View File

@ -10,6 +10,7 @@ import Editor from "~/components/Editor";
import LoadingIndicator from "~/components/LoadingIndicator";
import NudeButton from "~/components/NudeButton";
import useDebouncedCallback from "~/hooks/useDebouncedCallback";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import useToasts from "~/hooks/useToasts";
@ -18,13 +19,13 @@ type Props = {
};
function CollectionDescription({ collection }: Props) {
const { collections, policies } = useStores();
const { collections } = useStores();
const { showToast } = useToasts();
const { t } = useTranslation();
const [isExpanded, setExpanded] = React.useState(false);
const [isEditing, setEditing] = React.useState(false);
const [isDirty, setDirty] = React.useState(false);
const can = policies.abilities(collection.id);
const can = usePolicy(collection.id);
const handleStartEditing = React.useCallback(() => {
setEditing(true);

View File

@ -17,7 +17,7 @@ import Tooltip from "~/components/Tooltip";
import useBoolean from "~/hooks/useBoolean";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import useCurrentUser from "~/hooks/useCurrentUser";
import useStores from "~/hooks/useStores";
import usePolicy from "~/hooks/usePolicy";
import DocumentMenu from "~/menus/DocumentMenu";
import { hover } from "~/styles";
import { newDocumentPath } from "~/utils/routeHelpers";
@ -46,7 +46,6 @@ function DocumentListItem(
ref: React.RefObject<HTMLAnchorElement>
) {
const { t } = useTranslation();
const { policies } = useStores();
const currentUser = useCurrentUser();
const currentTeam = useCurrentTeam();
const [menuOpen, handleMenuOpen, handleMenuClose] = useBoolean();
@ -67,8 +66,8 @@ function DocumentListItem(
!!document.title.toLowerCase().includes(highlight.toLowerCase());
const canStar =
!document.isDraft && !document.isArchived && !document.isTemplate;
const can = policies.abilities(currentTeam.id);
const canCollection = policies.abilities(document.collectionId);
const can = usePolicy(currentTeam.id);
const canCollection = usePolicy(document.collectionId);
return (
<DocumentLink

View File

@ -15,7 +15,7 @@ import Event from "~/models/Event";
import Avatar from "~/components/Avatar";
import Item, { Actions } from "~/components/List/Item";
import Time from "~/components/Time";
import useStores from "~/hooks/useStores";
import usePolicy from "~/hooks/usePolicy";
import RevisionMenu from "~/menus/RevisionMenu";
import { documentHistoryUrl } from "~/utils/routeHelpers";
@ -27,9 +27,8 @@ type Props = {
const EventListItem = ({ event, latest, document }: Props) => {
const { t } = useTranslation();
const { policies } = useStores();
const location = useLocation();
const can = policies.abilities(document.id);
const can = usePolicy(document.id);
const opts = {
userName: event.actor.name,
};

View File

@ -13,6 +13,7 @@ import Text from "~/components/Text";
import { inviteUser } from "~/actions/definitions/users";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import useCurrentUser from "~/hooks/useCurrentUser";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import AccountMenu from "~/menus/AccountMenu";
import OrganizationMenu from "~/menus/OrganizationMenu";
@ -36,12 +37,13 @@ import TrashLink from "./components/TrashLink";
function AppSidebar() {
const { t } = useTranslation();
const { ui, policies, documents } = useStores();
const { ui, documents } = useStores();
const team = useCurrentTeam();
const user = useCurrentUser();
const { query } = useKBar();
const location = useLocation();
const history = useHistory();
const can = usePolicy(team.id);
React.useEffect(() => {
documents.fetchDrafts();
@ -56,7 +58,6 @@ function AppSidebar() {
}),
[dndArea]
);
const can = policies.abilities(team.id);
const handleSearch = React.useCallback(() => {
const isSearching = location.pathname.startsWith(searchPath());

View File

@ -23,7 +23,7 @@ import SlackIcon from "~/components/SlackIcon";
import ZapierIcon from "~/components/ZapierIcon";
import env from "~/env";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import useStores from "~/hooks/useStores";
import usePolicy from "~/hooks/usePolicy";
import Sidebar from "./Sidebar";
import Header from "./components/Header";
import Section from "./components/Section";
@ -37,8 +37,7 @@ function SettingsSidebar() {
const { t } = useTranslation();
const history = useHistory();
const team = useCurrentTeam();
const { policies } = useStores();
const can = policies.abilities(team.id);
const can = usePolicy(team.id);
const returnToApp = React.useCallback(() => {
history.push("/home");

View File

@ -12,6 +12,7 @@ import DocumentReparent from "~/scenes/DocumentReparent";
import CollectionIcon from "~/components/CollectionIcon";
import Modal from "~/components/Modal";
import useBoolean from "~/hooks/useBoolean";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import CollectionMenu from "~/menus/CollectionMenu";
import CollectionSortMenu from "~/menus/CollectionSortMenu";
@ -65,7 +66,7 @@ function CollectionLink({
setIsEditing(isEditing);
}, []);
const { ui, documents, policies, collections } = useStores();
const { ui, documents, collections } = useStores();
const [expanded, setExpanded] = React.useState(
collection.id === ui.activeCollectionId
);
@ -78,7 +79,7 @@ function CollectionLink({
}, [expanded]);
const manualSort = collection.sort.field === "index";
const can = policies.abilities(collection.id);
const can = usePolicy(collection.id);
const belowCollectionIndex = belowCollection ? belowCollection.index : null;
// Drop to re-parent document
@ -112,7 +113,7 @@ function CollectionLink({
}
},
canDrop: () => {
return policies.abilities(collection.id).update;
return can.update;
},
collect: (monitor) => ({
isOver: !!monitor.isOver({

View File

@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next";
import styled, { css } from "styled-components";
import LoadingIndicator from "~/components/LoadingIndicator";
import useImportDocument from "~/hooks/useImportDocument";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import useToasts from "~/hooks/useToasts";
@ -19,7 +20,7 @@ type Props = {
function DropToImport({ disabled, children, collectionId, documentId }: Props) {
const { t } = useTranslation();
const { documents, policies } = useStores();
const { documents } = useStores();
const { showToast } = useToasts();
const { handleFiles, isImporting } = useImportDocument(
collectionId,
@ -28,7 +29,7 @@ function DropToImport({ disabled, children, collectionId, documentId }: Props) {
const targetId = collectionId || documentId;
invariant(targetId, "Must provide either collectionId or documentId");
const can = policies.abilities(targetId);
const can = usePolicy(targetId);
const handleRejection = React.useCallback(() => {
showToast(
t("Document not supported try Markdown, Plain text, HTML, or Word"),

12
app/hooks/usePolicy.ts Normal file
View File

@ -0,0 +1,12 @@
import useStores from "./useStores";
/**
* Quick access to retrieve the abilities of a policy for a given entity
*
* @param entityId The entity id
* @returns The available abilities
*/
export default function usePolicy(entityId: string) {
const { policies } = useStores();
return policies.abilities(entityId);
}

View File

@ -23,6 +23,7 @@ import OverflowMenuButton from "~/components/ContextMenu/OverflowMenuButton";
import Template from "~/components/ContextMenu/Template";
import Modal from "~/components/Modal";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import useToasts from "~/hooks/useToasts";
import { MenuItem } from "~/types";
@ -51,7 +52,7 @@ function CollectionMenu({
});
const [renderModals, setRenderModals] = React.useState(false);
const team = useCurrentTeam();
const { documents, policies } = useStores();
const { documents } = useStores();
const { showToast } = useToasts();
const { t } = useTranslation();
const history = useHistory();
@ -123,8 +124,8 @@ function CollectionMenu({
[history, showToast, collection.id, documents]
);
const can = policies.abilities(collection.id);
const canUserInTeam = policies.abilities(team.id);
const can = usePolicy(collection.id);
const canUserInTeam = usePolicy(team.id);
const items: MenuItem[] = React.useMemo(
() => [
{

View File

@ -46,6 +46,7 @@ import {
} from "~/actions/definitions/documents";
import useActionContext from "~/hooks/useActionContext";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import useToasts from "~/hooks/useToasts";
import { MenuItem } from "~/types";
@ -177,7 +178,7 @@ function DocumentMenu({
);
const collection = collections.get(document.collectionId);
const can = policies.abilities(document.id);
const can = usePolicy(document.id);
const canViewHistory = can.read && !can.restore;
const restoreItems = React.useMemo(
() => [

View File

@ -10,7 +10,7 @@ import ContextMenu from "~/components/ContextMenu";
import OverflowMenuButton from "~/components/ContextMenu/OverflowMenuButton";
import Template from "~/components/ContextMenu/Template";
import Modal from "~/components/Modal";
import useStores from "~/hooks/useStores";
import usePolicy from "~/hooks/usePolicy";
type Props = {
group: Group;
@ -19,13 +19,12 @@ type Props = {
function GroupMenu({ group, onMembers }: Props) {
const { t } = useTranslation();
const { policies } = useStores();
const menu = useMenuState({
modal: true,
});
const [editModalOpen, setEditModalOpen] = React.useState(false);
const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);
const can = policies.abilities(group.id);
const can = usePolicy(group.id);
return (
<>

View File

@ -10,6 +10,7 @@ import ContextMenu from "~/components/ContextMenu";
import Header from "~/components/ContextMenu/Header";
import Template from "~/components/ContextMenu/Template";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import { MenuItem } from "~/types";
import { newDocumentPath } from "~/utils/routeHelpers";
@ -21,7 +22,7 @@ function NewDocumentMenu() {
const { t } = useTranslation();
const team = useCurrentTeam();
const { collections, policies } = useStores();
const can = policies.abilities(team.id);
const can = usePolicy(team.id);
const items = React.useMemo(
() =>
collections.orderedData.reduce<MenuItem[]>((filtered, collection) => {

View File

@ -10,6 +10,7 @@ import ContextMenu from "~/components/ContextMenu";
import Header from "~/components/ContextMenu/Header";
import Template from "~/components/ContextMenu/Template";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import { MenuItem } from "~/types";
import { newDocumentPath } from "~/utils/routeHelpers";
@ -21,7 +22,7 @@ function NewTemplateMenu() {
const { t } = useTranslation();
const team = useCurrentTeam();
const { collections, policies } = useStores();
const can = policies.abilities(team.id);
const can = usePolicy(team.id);
const items = React.useMemo(
() =>

View File

@ -9,6 +9,7 @@ import ContextMenu from "~/components/ContextMenu";
import MenuItem from "~/components/ContextMenu/MenuItem";
import OverflowMenuButton from "~/components/ContextMenu/OverflowMenuButton";
import CopyToClipboard from "~/components/CopyToClipboard";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import useToasts from "~/hooks/useToasts";
@ -20,11 +21,11 @@ function ShareMenu({ share }: Props) {
const menu = useMenuState({
modal: true,
});
const { shares, policies } = useStores();
const { shares } = useStores();
const { showToast } = useToasts();
const { t } = useTranslation();
const history = useHistory();
const can = policies.abilities(share.id);
const can = usePolicy(share.id);
const handleGoToDocument = React.useCallback(
(ev: React.SyntheticEvent) => {

View File

@ -6,6 +6,7 @@ import User from "~/models/User";
import ContextMenu from "~/components/ContextMenu";
import OverflowMenuButton from "~/components/ContextMenu/OverflowMenuButton";
import Template from "~/components/ContextMenu/Template";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
type Props = {
@ -13,12 +14,12 @@ type Props = {
};
function UserMenu({ user }: Props) {
const { users, policies } = useStores();
const { users } = useStores();
const { t } = useTranslation();
const menu = useMenuState({
modal: true,
});
const can = policies.abilities(user.id);
const can = usePolicy(user.id);
const handlePromote = React.useCallback(
(ev: React.SyntheticEvent) => {

View File

@ -26,6 +26,7 @@ import Tabs from "~/components/Tabs";
import Tooltip from "~/components/Tooltip";
import { editCollection } from "~/actions/definitions/collections";
import useCommandBarActions from "~/hooks/useCommandBarActions";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import { collectionUrl, updateCollectionUrl } from "~/utils/routeHelpers";
import Actions from "./Collection/Actions";
@ -37,14 +38,14 @@ function CollectionScene() {
const history = useHistory();
const match = useRouteMatch();
const { t } = useTranslation();
const { documents, pins, policies, collections, ui } = useStores();
const { documents, pins, collections, ui } = useStores();
const [isFetching, setFetching] = React.useState(false);
const [error, setError] = React.useState<Error | undefined>();
const id = params.id || "";
const collection: Collection | null | undefined =
collections.getByUrl(id) || collections.get(id);
const can = policies.abilities(collection?.id || "");
const can = usePolicy(collection?.id || "");
React.useEffect(() => {
if (collection) {

View File

@ -8,7 +8,7 @@ import { Action, Separator } from "~/components/Actions";
import Button from "~/components/Button";
import InputSearchPage from "~/components/InputSearchPage";
import Tooltip from "~/components/Tooltip";
import useStores from "~/hooks/useStores";
import usePolicy from "~/hooks/usePolicy";
import CollectionMenu from "~/menus/CollectionMenu";
import { newDocumentPath } from "~/utils/routeHelpers";
@ -18,8 +18,7 @@ type Props = {
function Actions({ collection }: Props) {
const { t } = useTranslation();
const { policies } = useStores();
const can = policies.abilities(collection.id);
const can = usePolicy(collection.id);
return (
<>

View File

@ -11,7 +11,7 @@ import Flex from "~/components/Flex";
import Modal from "~/components/Modal";
import Text from "~/components/Text";
import useBoolean from "~/hooks/useBoolean";
import useStores from "~/hooks/useStores";
import usePolicy from "~/hooks/usePolicy";
import { newDocumentPath } from "~/utils/routeHelpers";
type Props = {
@ -19,9 +19,8 @@ type Props = {
};
function EmptyCollection({ collection }: Props) {
const { policies } = useStores();
const { t } = useTranslation();
const can = policies.abilities(collection.id);
const can = usePolicy(collection.id);
const collectionName = collection ? collection.name : "";
const [

View File

@ -7,7 +7,7 @@ import { light } from "@shared/theme";
import Document from "~/models/Document";
import ContentEditable from "~/components/ContentEditable";
import Star, { AnimatedStar } from "~/components/Star";
import useStores from "~/hooks/useStores";
import usePolicy from "~/hooks/usePolicy";
import { isModKey } from "~/utils/keyboard";
type Props = {
@ -43,8 +43,7 @@ const EditableTitle = React.forwardRef(
}: Props,
ref: React.RefObject<HTMLSpanElement>
) => {
const { policies } = useStores();
const can = policies.abilities(document.id);
const can = usePolicy(document.id);
const normalizedTitle =
!value && readOnly ? document.titleWithDefault : value;

View File

@ -21,6 +21,7 @@ import DocumentBreadcrumb from "~/components/DocumentBreadcrumb";
import Header from "~/components/Header";
import Tooltip from "~/components/Tooltip";
import useMobile from "~/hooks/useMobile";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import DocumentMenu from "~/menus/DocumentMenu";
import NewChildDocumentMenu from "~/menus/NewChildDocumentMenu";
@ -73,7 +74,7 @@ function DocumentHeader({
headings,
}: Props) {
const { t } = useTranslation();
const { ui, policies, auth } = useStores();
const { ui, auth } = useStores();
const { resolvedTheme } = ui;
const { team } = auth;
const isMobile = useMobile();
@ -97,7 +98,7 @@ function DocumentHeader({
}, [onSave]);
const { isDeleted, isTemplate } = document;
const can = policies.abilities(document.id);
const can = usePolicy(document.id);
const canToggleEmbeds = team?.documentEmbeds;
const canEdit = can.update && !isEditing;
const toc = (

View File

@ -15,6 +15,7 @@ import Notice from "~/components/Notice";
import Switch from "~/components/Switch";
import Text from "~/components/Text";
import useKeyDown from "~/hooks/useKeyDown";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import useToasts from "~/hooks/useToasts";
@ -34,13 +35,13 @@ function SharePopover({
visible,
}: Props) {
const { t } = useTranslation();
const { policies, shares, auth } = useStores();
const { shares, auth } = useStores();
const { showToast } = useToasts();
const [isCopied, setIsCopied] = React.useState(false);
const timeout = React.useRef<ReturnType<typeof setTimeout>>();
const buttonRef = React.useRef<HTMLButtonElement>(null);
const can = policies.abilities(share ? share.id : "");
const documentAbilities = policies.abilities(document.id);
const can = usePolicy(share ? share.id : "");
const documentAbilities = usePolicy(document.id);
const canPublish =
can.update &&
!document.isTemplate &&

View File

@ -11,6 +11,7 @@ import Modal from "~/components/Modal";
import PaginatedList from "~/components/PaginatedList";
import Subheading from "~/components/Subheading";
import Text from "~/components/Text";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import useToasts from "~/hooks/useToasts";
import AddPeopleToGroup from "./AddPeopleToGroup";
@ -22,10 +23,10 @@ type Props = {
function GroupMembers({ group }: Props) {
const [addModalOpen, setAddModalOpen] = React.useState(false);
const { users, groupMemberships, policies } = useStores();
const { users, groupMemberships } = useStores();
const { showToast } = useToasts();
const { t } = useTranslation();
const can = policies.abilities(group.id);
const can = usePolicy(group.id);
const handleAddModal = (state: boolean) => {
setAddModalOpen(state);

View File

@ -15,11 +15,12 @@ import Tab from "~/components/Tab";
import Tabs from "~/components/Tabs";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import useCurrentUser from "~/hooks/useCurrentUser";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import NewDocumentMenu from "~/menus/NewDocumentMenu";
function Home() {
const { documents, pins, policies, ui } = useStores();
const { documents, pins, ui } = useStores();
const team = useCurrentTeam();
const user = useCurrentUser();
const userId = user?.id;
@ -29,7 +30,7 @@ function Home() {
pins.fetchPage();
}, [pins]);
const canManageTeam = policies.abilities(team.id).manage;
const canManageTeam = usePolicy(team.id).manage;
return (
<Scene

View File

@ -15,6 +15,7 @@ import Text from "~/components/Text";
import Tooltip from "~/components/Tooltip";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import useCurrentUser from "~/hooks/useCurrentUser";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import useToasts from "~/hooks/useToasts";
@ -50,13 +51,13 @@ function Invite({ onSubmit }: Props) {
role: "member",
},
]);
const { users, policies } = useStores();
const { users } = useStores();
const { showToast } = useToasts();
const user = useCurrentUser();
const team = useCurrentTeam();
const { t } = useTranslation();
const predictedDomain = user.email.split("@")[1];
const can = policies.abilities(team.id);
const can = usePolicy(team.id);
const handleSubmit = React.useCallback(
async (ev: React.SyntheticEvent) => {

View File

@ -15,14 +15,15 @@ import Subheading from "~/components/Subheading";
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 GroupMenu from "~/menus/GroupMenu";
function Groups() {
const { t } = useTranslation();
const { policies, groups } = useStores();
const { groups } = useStores();
const team = useCurrentTeam();
const can = policies.abilities(team.id);
const can = usePolicy(team.id);
const [
newGroupModalOpen,
handleNewGroupModalOpen,

View File

@ -19,6 +19,7 @@ import Scene from "~/components/Scene";
import Text from "~/components/Text";
import useBoolean from "~/hooks/useBoolean";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import usePolicy from "~/hooks/usePolicy";
import useQuery from "~/hooks/useQuery";
import useStores from "~/hooks/useStores";
import PeopleTable from "./components/PeopleTable";
@ -34,14 +35,14 @@ function Members() {
handleInviteModalClose,
] = useBoolean();
const team = useCurrentTeam();
const { users, policies } = useStores();
const { users } = useStores();
const { t } = useTranslation();
const params = useQuery();
const [isLoading, setIsLoading] = React.useState(false);
const [data, setData] = React.useState<User[]>([]);
const [totalPages, setTotalPages] = React.useState(0);
const [userIds, setUserIds] = React.useState<string[]>([]);
const can = policies.abilities(team.id);
const can = usePolicy(team.id);
const query = params.get("query") || "";
const filter = params.get("filter") || "";
const sort = params.get("sort") || "name";

View File

@ -10,15 +10,16 @@ import Scene from "~/components/Scene";
import Subheading from "~/components/Subheading";
import Text from "~/components/Text";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import ShareListItem from "./components/ShareListItem";
function Shares() {
const team = useCurrentTeam();
const { t } = useTranslation();
const { shares, auth, policies } = useStores();
const { shares, auth } = useStores();
const canShareDocuments = auth.team && auth.team.sharing;
const can = policies.abilities(team.id);
const can = usePolicy(team.id);
return (
<Scene title={t("Share Links")} icon={<LinkIcon color="currentColor" />}>

View File

@ -13,15 +13,16 @@ import Subheading from "~/components/Subheading";
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 TokenListItem from "./components/TokenListItem";
function Tokens() {
const team = useCurrentTeam();
const { t } = useTranslation();
const { apiKeys, policies } = useStores();
const { apiKeys } = useStores();
const [newModalOpen, handleNewModalOpen, handleNewModalClose] = useBoolean();
const can = policies.abilities(team.id);
const can = usePolicy(team.id);
return (
<Scene

View File

@ -11,16 +11,17 @@ import Scene from "~/components/Scene";
import Tab from "~/components/Tab";
import Tabs from "~/components/Tabs";
import useCurrentTeam from "~/hooks/useCurrentTeam";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import NewTemplateMenu from "~/menus/NewTemplateMenu";
function Templates(props: RouteComponentProps<{ sort: string }>) {
const { documents, policies } = useStores();
const { documents } = useStores();
const { t } = useTranslation();
const team = useCurrentTeam();
const { fetchTemplates, templates, templatesAlphabetical } = documents;
const { sort } = props.match.params;
const can = policies.abilities(team.id);
const can = usePolicy(team.id);
return (
<Scene