From c898ef4dc464246b3550661c50d389dd2000a577 Mon Sep 17 00:00:00 2001 From: InfiniteStash <117855276+InfiniteStash@users.noreply.github.com> Date: Fri, 20 Dec 2024 19:57:52 +0100 Subject: [PATCH] Refactor auth hook (#865) --- frontend/src/Login.tsx | 8 ++--- frontend/src/Main.tsx | 4 +-- .../components/deleteButton/DeleteButton.tsx | 10 +++--- .../src/components/editCard/AddComment.tsx | 9 +++--- frontend/src/components/editCard/VoteBar.tsx | 14 +++------ frontend/src/components/form/NoteInput.tsx | 10 +++--- frontend/src/{AuthContext.tsx => context.tsx} | 4 ++- frontend/src/graphql/queries/index.ts | 11 +++---- frontend/src/hooks/index.ts | 1 + frontend/src/hooks/useAuth.tsx | 2 +- frontend/src/hooks/useCurrentUser.tsx | 29 +++++++++++++++++ .../src/pages/activateUser/ActivateUser.tsx | 8 ++--- frontend/src/pages/categories/Categories.tsx | 10 +++--- frontend/src/pages/categories/Category.tsx | 10 +++--- frontend/src/pages/edits/Edit.tsx | 11 +++---- .../pages/forgotPassword/ForgotPassword.tsx | 8 ++--- frontend/src/pages/performers/Performers.tsx | 11 +++---- .../performers/components/performerInfo.tsx | 9 +++--- frontend/src/pages/registerUser/Register.tsx | 8 ++--- .../src/pages/resetPassword/ResetPassword.tsx | 8 ++--- frontend/src/pages/scenes/Scene.tsx | 9 +++--- frontend/src/pages/scenes/Scenes.tsx | 11 +++---- frontend/src/pages/sites/Site.tsx | 10 +++--- frontend/src/pages/sites/Sites.tsx | 9 +++--- frontend/src/pages/studios/Studio.tsx | 9 +++--- frontend/src/pages/studios/Studios.tsx | 11 +++---- frontend/src/pages/tags/Tag.tsx | 10 +++--- frontend/src/pages/tags/Tags.tsx | 10 +++--- frontend/src/pages/users/User.tsx | 19 ++++++------ .../pages/users/UserConfirmChangeEmail.tsx | 2 +- frontend/src/pages/users/UserEditForm.tsx | 12 +++---- frontend/src/pages/users/UserEdits.tsx | 31 ++++++++++--------- frontend/src/pages/users/UserPassword.tsx | 8 ++--- .../pages/users/UserValidateChangeEmail.tsx | 2 +- frontend/src/pages/users/index.tsx | 3 +- frontend/src/utils/auth.ts | 11 ------- frontend/src/utils/index.ts | 1 - frontend/src/utils/user.ts | 13 ++++++-- 38 files changed, 189 insertions(+), 177 deletions(-) rename frontend/src/{AuthContext.tsx => context.tsx} (73%) create mode 100644 frontend/src/hooks/useCurrentUser.tsx delete mode 100644 frontend/src/utils/auth.ts diff --git a/frontend/src/Login.tsx b/frontend/src/Login.tsx index 2c9fdd594..a5eb89c28 100644 --- a/frontend/src/Login.tsx +++ b/frontend/src/Login.tsx @@ -1,4 +1,4 @@ -import { FC, useContext, useState } from "react"; +import { FC, useState } from "react"; import { Link, useLocation, useNavigate } from "react-router-dom"; import { useForm } from "react-hook-form"; import { Button, Col, Form, Row } from "react-bootstrap"; @@ -6,11 +6,11 @@ import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; import cx from "classnames"; -import AuthContext, { ContextType } from "src/AuthContext"; import { ROUTE_REGISTER, ROUTE_FORGOT_PASSWORD } from "src/constants/route"; import { getPlatformURL, getCredentialsSetting } from "src/utils/createClient"; import "./App.scss"; +import { useCurrentUser } from "./hooks"; const schema = yup.object({ username: yup.string().required("Username is required"), @@ -30,7 +30,7 @@ const Login: FC = () => { const [loginError, setLoginError] = useState(""); const msg = new URLSearchParams(location.search).get("msg"); const redirect = new URLSearchParams(location.search).get("redirect"); - const Auth = useContext(AuthContext); + const { isAuthenticated } = useCurrentUser(); const { register, handleSubmit, @@ -39,7 +39,7 @@ const Login: FC = () => { resolver: yupResolver(schema), }); - if (Auth.authenticated) navigate("/"); + if (isAuthenticated) navigate("/"); const onSubmit = async (formData: LoginFormData) => { setLoading(true); diff --git a/frontend/src/Main.tsx b/frontend/src/Main.tsx index b7d955019..3045a1e45 100644 --- a/frontend/src/Main.tsx +++ b/frontend/src/Main.tsx @@ -6,7 +6,7 @@ import { faBell as faBellOutlined } from "@fortawesome/free-regular-svg-icons"; import SearchField, { SearchType } from "src/components/searchField"; import { getPlatformURL, getCredentialsSetting } from "src/utils/createClient"; -import { isAdmin, canEdit, userHref, setCachedUser } from "src/utils"; +import { userHref, setCachedUser, canEdit, isAdmin } from "src/utils"; import { useAuth } from "src/hooks"; import { Icon } from "src/components/fragments"; import { useConfig, useUnreadNotificationsCount } from "src/graphql"; @@ -28,7 +28,7 @@ import { ROUTE_DRAFTS, ROUTE_NOTIFICATIONS, } from "src/constants/route"; -import AuthContext from "./AuthContext"; +import AuthContext from "./context"; interface Props { children?: React.ReactNode; diff --git a/frontend/src/components/deleteButton/DeleteButton.tsx b/frontend/src/components/deleteButton/DeleteButton.tsx index 136cbeac3..a2d33b572 100644 --- a/frontend/src/components/deleteButton/DeleteButton.tsx +++ b/frontend/src/components/deleteButton/DeleteButton.tsx @@ -1,10 +1,8 @@ -import { FC, useState, useContext } from "react"; +import { FC, useState } from "react"; import { Button } from "react-bootstrap"; -import AuthContext from "src/AuthContext"; -import { isAdmin } from "src/utils"; - import Modal from "src/components/modal"; +import { useCurrentUser } from "src/hooks"; interface DeleteButtonProps { message?: string; @@ -19,8 +17,8 @@ const DeleteButton: FC = ({ disabled = false, className, }) => { + const { isAdmin } = useCurrentUser(); const [showDelete, setShowDelete] = useState(false); - const auth = useContext(AuthContext); const toggleModal = () => setShowDelete(true); const handleDelete = (status: boolean): void => { @@ -40,7 +38,7 @@ const DeleteButton: FC = ({ return ( <> {deleteModal} - {isAdmin(auth.user) && ( + {isAdmin && ( diff --git a/frontend/src/pages/categories/Category.tsx b/frontend/src/pages/categories/Category.tsx index 661467071..98e9df873 100644 --- a/frontend/src/pages/categories/Category.tsx +++ b/frontend/src/pages/categories/Category.tsx @@ -1,13 +1,13 @@ -import { FC, useContext } from "react"; +import { FC } from "react"; import { Link, useNavigate } from "react-router-dom"; import { Button, Row } from "react-bootstrap"; import { useDeleteCategory, CategoryQuery } from "src/graphql"; -import AuthContext from "src/AuthContext"; -import { isAdmin, createHref } from "src/utils"; +import { createHref } from "src/utils"; import DeleteButton from "src/components/deleteButton"; import { TagList } from "src/components/list"; import { ROUTE_CATEGORIES, ROUTE_CATEGORY_EDIT } from "src/constants/route"; +import { useCurrentUser } from "src/hooks"; type Category = NonNullable; @@ -17,7 +17,7 @@ interface Props { const CategoryComponent: FC = ({ category }) => { const navigate = useNavigate(); - const auth = useContext(AuthContext); + const { isAdmin } = useCurrentUser(); const [deleteCategory, { loading: deleting }] = useDeleteCategory({ onCompleted: (result) => { @@ -43,7 +43,7 @@ const CategoryComponent: FC = ({ category }) => { {category.name}
- {isAdmin(auth.user) && ( + {isAdmin && ( <> { - const auth = useContext(AuthContext); + const { isAdmin, isSelf } = useCurrentUser(); const { id } = useParams(); const [showApply, setShowApply] = useState(false); const [showCancel, setShowCancel] = useState(false); @@ -78,7 +77,7 @@ const EditComponent: FC = () => { const mutating = cancelling || applying; - const buttons = (isAdmin(auth.user) || auth.user?.id === edit.user?.id) && + const buttons = (isAdmin || isSelf(edit.user?.id)) && edit.status === VoteStatusEnum.PENDING && (
{edit.updatable && ( @@ -96,7 +95,7 @@ const EditComponent: FC = () => { > Cancel Edit - {isAdmin(auth.user) && ( + {isAdmin && ( diff --git a/frontend/src/pages/performers/components/performerInfo.tsx b/frontend/src/pages/performers/components/performerInfo.tsx index 7b4309df3..3a050fd2b 100644 --- a/frontend/src/pages/performers/components/performerInfo.tsx +++ b/frontend/src/pages/performers/components/performerInfo.tsx @@ -1,4 +1,4 @@ -import { FC, useContext } from "react"; +import { FC } from "react"; import { Link } from "react-router-dom"; import { Button, Card, Col, Row, Table } from "react-bootstrap"; import { faCodeMerge } from "@fortawesome/free-solid-svg-icons"; @@ -9,9 +9,8 @@ import { usePerformer, } from "src/graphql"; -import AuthContext from "src/AuthContext"; +import { useCurrentUser } from "src/hooks"; import { - canEdit, getCountryByISO, formatBodyModifications, formatMeasurements, @@ -49,9 +48,9 @@ interface Props { } const Actions: FC = ({ performer }) => { - const auth = useContext(AuthContext); + const { isEditor } = useCurrentUser(); - if (!canEdit(auth?.user) || performer.deleted) return null; + if (!isEditor || performer.deleted) return null; return ( diff --git a/frontend/src/pages/registerUser/Register.tsx b/frontend/src/pages/registerUser/Register.tsx index 8aea98242..b12f4dc31 100644 --- a/frontend/src/pages/registerUser/Register.tsx +++ b/frontend/src/pages/registerUser/Register.tsx @@ -1,4 +1,4 @@ -import { FC, useContext, useState } from "react"; +import { FC, useState } from "react"; import { ApolloError } from "@apollo/client"; import { yupResolver } from "@hookform/resolvers/yup"; import { useForm } from "react-hook-form"; @@ -9,10 +9,10 @@ import cx from "classnames"; import { ErrorMessage, LoadingIndicator } from "src/components/fragments"; import Title from "src/components/title"; import { useNewUser, useConfig, ConfigQuery } from "src/graphql"; -import AuthContext, { ContextType } from "src/AuthContext"; import * as yup from "yup"; import { ROUTE_HOME, ROUTE_ACTIVATE, ROUTE_LOGIN } from "src/constants/route"; +import { useCurrentUser } from "src/hooks"; const schema = yup.object({ email: yup.string().email().required("Email is required"), @@ -31,7 +31,7 @@ interface Props { const Register: FC = ({ config }) => { const navigate = useNavigate(); const [awaitingActivation, setAwaitingActivation] = useState(false); - const Auth = useContext(AuthContext); + const { isAuthenticated } = useCurrentUser(); const [submitError, setSubmitError] = useState(); const inviteRequired = config.require_invite ?? true; @@ -46,7 +46,7 @@ const Register: FC = ({ config }) => { const [newUser] = useNewUser(); - if (Auth.authenticated) navigate(ROUTE_HOME); + if (isAuthenticated) navigate(ROUTE_HOME); const onSubmit = (formData: RegisterFormData) => { const userData = { diff --git a/frontend/src/pages/resetPassword/ResetPassword.tsx b/frontend/src/pages/resetPassword/ResetPassword.tsx index a4f0af383..54b65f412 100644 --- a/frontend/src/pages/resetPassword/ResetPassword.tsx +++ b/frontend/src/pages/resetPassword/ResetPassword.tsx @@ -1,9 +1,8 @@ -import { FC, useContext, useState } from "react"; +import { FC, useState } from "react"; import { yupResolver } from "@hookform/resolvers/yup"; import { useForm } from "react-hook-form"; import { isApolloError } from "@apollo/client"; import { useNavigate, useLocation } from "react-router-dom"; -import AuthContext, { ContextType } from "src/AuthContext"; import * as yup from "yup"; import cx from "classnames"; import { Button, Form, Row, Col } from "react-bootstrap"; @@ -11,6 +10,7 @@ import { Button, Form, Row, Col } from "react-bootstrap"; import { ErrorMessage } from "src/components/fragments"; import Title from "src/components/title"; import { useChangePassword } from "src/graphql"; +import { useCurrentUser } from "src/hooks"; import { ROUTE_HOME, ROUTE_LOGIN } from "src/constants/route"; const schema = yup.object({ @@ -46,8 +46,8 @@ function useQuery() { const ResetPassword: FC = () => { const navigate = useNavigate(); const query = useQuery(); - const Auth = useContext(AuthContext); const [submitError, setSubmitError] = useState(); + const { isAuthenticated } = useCurrentUser(); const { register, @@ -59,7 +59,7 @@ const ResetPassword: FC = () => { const [changePassword, { loading }] = useChangePassword(); - if (Auth.authenticated) navigate(ROUTE_HOME); + if (isAuthenticated) navigate(ROUTE_HOME); const key = query.get("key"); diff --git a/frontend/src/pages/scenes/Scene.tsx b/frontend/src/pages/scenes/Scene.tsx index 58ad5905d..06e755392 100644 --- a/frontend/src/pages/scenes/Scene.tsx +++ b/frontend/src/pages/scenes/Scene.tsx @@ -1,4 +1,4 @@ -import { FC, useContext } from "react"; +import { FC } from "react"; import { Link, useLocation, useNavigate } from "react-router-dom"; import { Button, Card, Tabs, Tab } from "react-bootstrap"; @@ -7,9 +7,8 @@ import { TargetTypeEnum, SceneFragment as Scene, } from "src/graphql"; -import AuthContext from "src/AuthContext"; +import { useCurrentUser } from "src/hooks"; import { - canEdit, tagHref, performerHref, studioHref, @@ -35,7 +34,7 @@ const SceneComponent: FC = ({ scene }) => { const navigate = useNavigate(); const location = useLocation(); const activeTab = location.hash?.slice(1) || DEFAULT_TAB; - const auth = useContext(AuthContext); + const { isEditor } = useCurrentUser(); const { data: editData } = usePendingEditsCount({ type: TargetTypeEnum.SCENE, @@ -79,7 +78,7 @@ const SceneComponent: FC = ({ scene }) => {
- {canEdit(auth.user) && !scene.deleted && ( + {isEditor && !scene.deleted && ( <> diff --git a/frontend/src/pages/scenes/Scenes.tsx b/frontend/src/pages/scenes/Scenes.tsx index 2555d16b2..0cc0aa666 100644 --- a/frontend/src/pages/scenes/Scenes.tsx +++ b/frontend/src/pages/scenes/Scenes.tsx @@ -1,16 +1,15 @@ -import { FC, useContext } from "react"; +import { FC } from "react"; import { Button } from "react-bootstrap"; import { Link } from "react-router-dom"; import { CriterionModifier, useConfig } from "src/graphql"; -import { canEdit, createHref } from "src/utils"; -import AuthContext from "src/AuthContext"; +import { createHref } from "src/utils"; import { SceneList } from "src/components/list"; -import { useQueryParams } from "src/hooks"; +import { useQueryParams, useCurrentUser } from "src/hooks"; import { ROUTE_SCENE_ADD } from "src/constants/route"; const Scenes: FC = () => { - const auth = useContext(AuthContext); + const { isEditor } = useCurrentUser(); const { data: configData } = useConfig(); const [{ fingerprint }] = useQueryParams({ fingerprint: { name: "fingerprint", type: "string" }, @@ -28,7 +27,7 @@ const Scenes: FC = () => { <>

Scenes

- {canEdit(auth.user) && !configData?.getConfig.require_scene_draft && ( + {isEditor && !configData?.getConfig.require_scene_draft && ( diff --git a/frontend/src/pages/sites/Site.tsx b/frontend/src/pages/sites/Site.tsx index 0a8dc3a79..e1be7155d 100644 --- a/frontend/src/pages/sites/Site.tsx +++ b/frontend/src/pages/sites/Site.tsx @@ -1,13 +1,13 @@ -import { FC, useContext } from "react"; +import { FC } from "react"; import { Link, useNavigate } from "react-router-dom"; import { Button } from "react-bootstrap"; import { useDeleteSite, SiteQuery } from "src/graphql"; -import AuthContext from "src/AuthContext"; -import { isAdmin, createHref } from "src/utils"; +import { createHref } from "src/utils"; import { SiteLink } from "src/components/fragments"; import DeleteButton from "src/components/deleteButton"; import { ROUTE_SITES, ROUTE_SITE_EDIT } from "src/constants/route"; +import { useCurrentUser } from "src/hooks"; type Site = NonNullable; @@ -17,7 +17,7 @@ interface Props { const SiteComponent: FC = ({ site }) => { const navigate = useNavigate(); - const auth = useContext(AuthContext); + const { isAdmin } = useCurrentUser(); const [deleteSite, { loading: deleting }] = useDeleteSite({ onCompleted: (result) => { @@ -42,7 +42,7 @@ const SiteComponent: FC = ({ site }) => {

- {isAdmin(auth.user) && ( + {isAdmin && (
diff --git a/frontend/src/pages/sites/Sites.tsx b/frontend/src/pages/sites/Sites.tsx index 027761b3b..35b795189 100644 --- a/frontend/src/pages/sites/Sites.tsx +++ b/frontend/src/pages/sites/Sites.tsx @@ -1,16 +1,15 @@ -import { FC, useContext } from "react"; +import { FC } from "react"; import { Link } from "react-router-dom"; import { Button, Card } from "react-bootstrap"; import { sortBy } from "lodash-es"; import { useSites } from "src/graphql"; import { LoadingIndicator, SiteLink } from "src/components/fragments"; -import { isAdmin } from "src/utils"; import { ROUTE_SITE_ADD } from "src/constants/route"; -import AuthContext from "src/AuthContext"; +import { useCurrentUser } from "src/hooks"; const SiteList: FC = () => { - const auth = useContext(AuthContext); + const { isAdmin } = useCurrentUser(); const { loading, data } = useSites(); const sites = sortBy(data?.querySites.sites ?? [], (s) => @@ -21,7 +20,7 @@ const SiteList: FC = () => { <>

Sites

- {isAdmin(auth.user) && ( + {isAdmin && ( diff --git a/frontend/src/pages/studios/Studio.tsx b/frontend/src/pages/studios/Studio.tsx index 29d8458d9..640fd23a2 100644 --- a/frontend/src/pages/studios/Studio.tsx +++ b/frontend/src/pages/studios/Studio.tsx @@ -1,4 +1,4 @@ -import { FC, useContext } from "react"; +import { FC } from "react"; import { Link, useLocation, useNavigate } from "react-router-dom"; import { Button, Tab, Tabs } from "react-bootstrap"; import { sortBy } from "lodash-es"; @@ -9,6 +9,7 @@ import { CriterionModifier, StudioFragment as Studio, } from "src/graphql"; +import { useCurrentUser } from "src/hooks"; import { EditList, SceneList, URLList } from "src/components/list"; import { StudioPerformers } from "./components"; @@ -16,13 +17,11 @@ import { getImage, createHref, studioHref, - canEdit, formatPendingEdits, getUrlBySite, } from "src/utils"; import { ROUTE_STUDIO_EDIT, ROUTE_STUDIO_DELETE } from "src/constants/route"; import { FavoriteStar } from "src/components/fragments"; -import AuthContext from "src/AuthContext"; const DEFAULT_TAB = "scenes"; @@ -31,7 +30,7 @@ interface Props { } const StudioComponent: FC = ({ studio }) => { - const auth = useContext(AuthContext); + const { isEditor } = useCurrentUser(); const navigate = useNavigate(); const location = useLocation(); const activeTab = location.hash?.slice(1) || DEFAULT_TAB; @@ -105,7 +104,7 @@ const StudioComponent: FC = ({ studio }) => {
)}
- {canEdit(auth.user) && !studio.deleted && ( + {isEditor && !studio.deleted && ( <> diff --git a/frontend/src/pages/studios/Studios.tsx b/frontend/src/pages/studios/Studios.tsx index 710cd4a98..f358208c7 100644 --- a/frontend/src/pages/studios/Studios.tsx +++ b/frontend/src/pages/studios/Studios.tsx @@ -1,20 +1,19 @@ -import { FC, useContext } from "react"; +import { FC } from "react"; import { Button, Card, Form } from "react-bootstrap"; import { Link } from "react-router-dom"; -import { studioHref, createHref, canEdit } from "src/utils"; +import { studioHref, createHref } from "src/utils"; import { ROUTE_STUDIO_ADD } from "src/constants/route"; import { debounce } from "lodash-es"; -import AuthContext from "src/AuthContext"; import { useStudios, SortDirectionEnum, StudioSortEnum } from "src/graphql"; -import { usePagination, useQueryParams } from "src/hooks"; +import { useCurrentUser, usePagination, useQueryParams } from "src/hooks"; import { List } from "src/components/list"; import { FavoriteStar } from "src/components/fragments"; const PER_PAGE = 40; const StudiosComponent: FC = () => { - const auth = useContext(AuthContext); + const { isEditor } = useCurrentUser(); const [params, setParams] = useQueryParams({ query: { name: "query", type: "string", default: "" }, favorite: { name: "favorite", type: "string", default: "false" }, @@ -73,7 +72,7 @@ const StudiosComponent: FC = () => { <>

Studios

- {canEdit(auth.user) && ( + {isEditor && ( diff --git a/frontend/src/pages/tags/Tag.tsx b/frontend/src/pages/tags/Tag.tsx index d23c36ec7..2018a9449 100644 --- a/frontend/src/pages/tags/Tag.tsx +++ b/frontend/src/pages/tags/Tag.tsx @@ -1,4 +1,4 @@ -import { FC, useContext } from "react"; +import { FC } from "react"; import { Link, useLocation, useNavigate } from "react-router-dom"; import { Button, Tab, Tabs } from "react-bootstrap"; @@ -9,16 +9,16 @@ import { TagFragment as Tag, } from "src/graphql"; -import AuthContext from "src/AuthContext"; import { Tooltip } from "src/components/fragments"; import { EditList, SceneList } from "src/components/list"; -import { canEdit, createHref, tagHref, formatPendingEdits } from "src/utils"; +import { createHref, tagHref, formatPendingEdits } from "src/utils"; import { ROUTE_TAG_EDIT, ROUTE_TAG_MERGE, ROUTE_TAG_DELETE, ROUTE_CATEGORY, } from "src/constants/route"; +import { useCurrentUser } from "src/hooks"; const DEFAULT_TAB = "scenes"; @@ -27,7 +27,7 @@ interface Props { } const TagComponent: FC = ({ tag }) => { - const auth = useContext(AuthContext); + const { isEditor } = useCurrentUser(); const navigate = useNavigate(); const location = useLocation(); const activeTab = location.hash?.slice(1) || DEFAULT_TAB; @@ -48,7 +48,7 @@ const TagComponent: FC = ({ tag }) => { Tag: {tag.deleted ? {tag.name} : {tag.name}} - {canEdit(auth.user) && !tag.deleted && ( + {isEditor && !tag.deleted && (
diff --git a/frontend/src/pages/tags/Tags.tsx b/frontend/src/pages/tags/Tags.tsx index de57d33e5..c75bfdb89 100644 --- a/frontend/src/pages/tags/Tags.tsx +++ b/frontend/src/pages/tags/Tags.tsx @@ -1,19 +1,19 @@ -import { FC, useContext } from "react"; +import { FC } from "react"; import { Button } from "react-bootstrap"; import { Link } from "react-router-dom"; import { TagList } from "src/components/list"; -import { canEdit, createHref } from "src/utils"; +import { createHref } from "src/utils"; import { ROUTE_TAG_ADD } from "src/constants/route"; -import AuthContext from "src/AuthContext"; +import { useCurrentUser } from "src/hooks"; const Tags: FC = () => { - const auth = useContext(AuthContext); + const { isEditor } = useCurrentUser(); return ( <>

Tags

- {canEdit(auth.user) && ( + {isEditor && ( diff --git a/frontend/src/pages/users/User.tsx b/frontend/src/pages/users/User.tsx index ef1974f8e..6eb15c280 100644 --- a/frontend/src/pages/users/User.tsx +++ b/frontend/src/pages/users/User.tsx @@ -1,4 +1,4 @@ -import { FC, useState, useContext } from "react"; +import { FC, useState } from "react"; import { Link } from "react-router-dom"; import { Button, Col, Form, InputGroup, Row, Table } from "react-bootstrap"; import { @@ -24,8 +24,7 @@ import { GenerateInviteCodeInput, useRequestChangeEmail, } from "src/graphql"; -import { useToast } from "src/hooks"; -import AuthContext from "src/AuthContext"; +import { useCurrentUser, useToast } from "src/hooks"; import { ROUTE_USER_EDIT, ROUTE_USER_PASSWORD, @@ -35,7 +34,7 @@ import { } from "src/constants/route"; import Modal from "src/components/modal"; import { Icon, Tooltip } from "src/components/fragments"; -import { isAdmin, isPrivateUser, createHref, formatDateTime } from "src/utils"; +import { isPrivateUser, createHref, formatDateTime } from "src/utils"; import { EditStatusTypes, VoteTypes } from "src/constants"; import { GenerateInviteKeyModal } from "./GenerateInviteKeyModal"; import { isApolloError } from "@apollo/client"; @@ -137,7 +136,7 @@ interface Props { } const UserComponent: FC = ({ user, refetch }) => { - const Auth = useContext(AuthContext); + const { isAdmin, isSelf } = useCurrentUser(); const { data: configData } = useConfig(); const [showDelete, setShowDelete] = useState(false); const [showRegenerateAPIKey, setShowRegenerateAPIKey] = useState(false); @@ -154,7 +153,7 @@ const UserComponent: FC = ({ user, refetch }) => { const [requestChangeEmail] = useRequestChangeEmail(); const showPrivate = isPrivateUser(user); - const isOwner = showPrivate && user.id === Auth.user?.id; + const isOwner = showPrivate && isSelf(user); const endpointURL = configData && `${configData.getConfig.host_url}/graphql`; @@ -175,7 +174,7 @@ const UserComponent: FC = ({ user, refetch }) => { const handleRegenerateAPIKey = (status: boolean): void => { if (status) { - const userID = user.id === Auth.user?.id ? null : user.id; + const userID = isSelf(user.id) ? null : user.id; regenerateAPIKey({ variables: { user_id: userID } }).then(() => { refetch(); }); @@ -319,7 +318,7 @@ const UserComponent: FC = ({ user, refetch }) => { Change Email )} - {isAdmin(Auth.user) && ( + {isAdmin && ( <> @@ -433,13 +432,13 @@ const UserComponent: FC = ({ user, refetch }) => { Invite Tokens - {isAdmin(Auth.user) && ( + {isAdmin && ( )} {user.invite_tokens ?? 0} - {isAdmin(Auth.user) && ( + {isAdmin && ( diff --git a/frontend/src/pages/users/UserConfirmChangeEmail.tsx b/frontend/src/pages/users/UserConfirmChangeEmail.tsx index 9a6b7e8d0..97647ff1b 100644 --- a/frontend/src/pages/users/UserConfirmChangeEmail.tsx +++ b/frontend/src/pages/users/UserConfirmChangeEmail.tsx @@ -3,7 +3,7 @@ import { isApolloError } from "@apollo/client"; import { useNavigate } from "react-router-dom"; import { Button, Form } from "react-bootstrap"; -import type { User } from "src/AuthContext"; +import type { User } from "src/context"; import { useQueryParams, useToast } from "src/hooks"; import { userHref } from "src/utils"; import { ErrorMessage } from "src/components/fragments"; diff --git a/frontend/src/pages/users/UserEditForm.tsx b/frontend/src/pages/users/UserEditForm.tsx index 4a5efd0ce..928862628 100644 --- a/frontend/src/pages/users/UserEditForm.tsx +++ b/frontend/src/pages/users/UserEditForm.tsx @@ -1,4 +1,4 @@ -import { FC, useContext } from "react"; +import { FC } from "react"; import { Button, Row, Form } from "react-bootstrap"; import { Link } from "react-router-dom"; import Select from "react-select"; @@ -8,8 +8,8 @@ import { yupResolver } from "@hookform/resolvers/yup"; import cx from "classnames"; import { RoleEnum, UserUpdateInput } from "src/graphql"; -import { isAdmin, userHref } from "src/utils"; -import AuthContext from "src/AuthContext"; +import { userHref } from "src/utils"; +import { useCurrentUser } from "src/hooks"; const schema = yup.object({ name: yup.string().optional(), @@ -39,7 +39,7 @@ const roles = Object.keys(RoleEnum).map((role) => ({ })); const UserForm: FC = ({ user, username, callback, error }) => { - const Auth = useContext(AuthContext); + const { isAdmin } = useCurrentUser(); const { register, control, @@ -62,7 +62,7 @@ const UserForm: FC = ({ user, username, callback, error }) => { return (
- {isAdmin(Auth.user) && ( + {isAdmin && ( Username = ({ user, username, callback, error }) => {
{errors?.email?.message}
- {isAdmin(Auth.user) && ( + {isAdmin && ( Roles diff --git a/frontend/src/pages/users/UserEdits.tsx b/frontend/src/pages/users/UserEdits.tsx index 788676114..1ddf24b34 100644 --- a/frontend/src/pages/users/UserEdits.tsx +++ b/frontend/src/pages/users/UserEdits.tsx @@ -5,27 +5,30 @@ import { ROUTE_USER } from "src/constants/route"; import { createHref } from "src/utils"; import { EditList } from "src/components/list"; import { VoteStatusEnum } from "src/graphql"; +import { useCurrentUser } from "src/hooks"; interface Props { user: { id: string; name: string; }; - isPrivateUser: boolean; } -const UserEditsComponent: FC = ({ user, isPrivateUser }) => ( - <> -

- Edits by {user.name} -

- - -); +const UserEditsComponent: FC = ({ user }) => { + const { isSelf } = useCurrentUser(); + return ( + <> +

+ Edits by {user.name} +

+ + + ); +}; export default UserEditsComponent; diff --git a/frontend/src/pages/users/UserPassword.tsx b/frontend/src/pages/users/UserPassword.tsx index 7eacb19ff..5f85ae17c 100644 --- a/frontend/src/pages/users/UserPassword.tsx +++ b/frontend/src/pages/users/UserPassword.tsx @@ -1,14 +1,14 @@ -import { FC, useContext, useState } from "react"; +import { FC, useState } from "react"; import { useNavigate } from "react-router-dom"; import { isApolloError } from "@apollo/client"; import { useChangePassword } from "src/graphql"; -import AuthContext from "src/AuthContext"; import { userHref } from "src/utils"; import UserPassword, { UserPasswordData } from "./UserPasswordForm"; +import { useCurrentUser } from "src/hooks"; const ChangePasswordComponent: FC = () => { - const Auth = useContext(AuthContext); + const { user } = useCurrentUser(); const [queryError, setQueryError] = useState(); const navigate = useNavigate(); const [changePassword] = useChangePassword(); @@ -19,7 +19,7 @@ const ChangePasswordComponent: FC = () => { new_password: formData.newPassword, }; changePassword({ variables: { userData } }) - .then(() => Auth.user && navigate(userHref(Auth.user))) + .then(() => user && navigate(userHref(user))) .catch( (error: unknown) => error instanceof Error && diff --git a/frontend/src/pages/users/UserValidateChangeEmail.tsx b/frontend/src/pages/users/UserValidateChangeEmail.tsx index 96a194b56..68fb3bd19 100644 --- a/frontend/src/pages/users/UserValidateChangeEmail.tsx +++ b/frontend/src/pages/users/UserValidateChangeEmail.tsx @@ -6,7 +6,7 @@ import * as yup from "yup"; import cx from "classnames"; import { Button, Form, Row, Col } from "react-bootstrap"; -import type { User } from "src/AuthContext"; +import type { User } from "src/context"; import { useQueryParams } from "src/hooks"; import { ErrorMessage } from "src/components/fragments"; import Title from "src/components/title"; diff --git a/frontend/src/pages/users/index.tsx b/frontend/src/pages/users/index.tsx index 5c37cb247..a20be6eb9 100644 --- a/frontend/src/pages/users/index.tsx +++ b/frontend/src/pages/users/index.tsx @@ -4,7 +4,6 @@ import { Route, Routes, useParams } from "react-router-dom"; import { useUser } from "src/graphql"; import Title from "src/components/title"; import { ErrorMessage, LoadingIndicator } from "src/components/fragments"; -import { isPrivateUser } from "src/utils"; import Users from "./Users"; import User from "./User"; @@ -52,7 +51,7 @@ const UserLoader: FC = () => { element={ <> - <UserEdits user={user} isPrivateUser={isPrivateUser(user)} /> + <UserEdits user={user} /> </> } /> diff --git a/frontend/src/utils/auth.ts b/frontend/src/utils/auth.ts deleted file mode 100644 index b185b0fd1..000000000 --- a/frontend/src/utils/auth.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { User } from "src/AuthContext"; -import { RoleEnum } from "src/graphql"; - -export const isAdmin = (user?: User) => - (user?.roles ?? []).includes(RoleEnum.ADMIN); - -export const canEdit = (user?: User) => - (user?.roles ?? []).includes(RoleEnum.EDIT) || isAdmin(user); - -export const canVote = (user?: User) => - (user?.roles ?? []).includes(RoleEnum.VOTE) || isAdmin(user); diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts index d07ec27c1..12a847f45 100644 --- a/frontend/src/utils/index.ts +++ b/frontend/src/utils/index.ts @@ -1,4 +1,3 @@ -export * from "./auth"; export * from "./country"; export * from "./date"; export * from "./edit"; diff --git a/frontend/src/utils/user.ts b/frontend/src/utils/user.ts index 0a4b20a9b..7ef2b14f1 100644 --- a/frontend/src/utils/user.ts +++ b/frontend/src/utils/user.ts @@ -1,5 +1,5 @@ -import { User } from "../AuthContext"; -import { UserQuery, PublicUserQuery } from "src/graphql"; +import type { User } from "../context"; +import { UserQuery, PublicUserQuery, RoleEnum } from "src/graphql"; type PrivateUser = NonNullable<UserQuery["findUser"]>; type PublicUser = NonNullable<PublicUserQuery["findUser"]>; @@ -17,3 +17,12 @@ export const setCachedUser = (user?: User | null) => { export const isPrivateUser = ( user: PublicUser | PrivateUser, ): user is PrivateUser => !!(user as PrivateUser).email; + +export const isAdmin = (user?: User) => + (user?.roles ?? []).includes(RoleEnum.ADMIN); + +export const canEdit = (user?: User) => + (user?.roles ?? []).includes(RoleEnum.EDIT) || isAdmin(user); + +export const canVote = (user?: User) => + (user?.roles ?? []).includes(RoleEnum.VOTE) || isAdmin(user);