From 1e868dc802c537716a96021288dcd8e0dfa9fd20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=B1dvan=20Akca?= Date: Wed, 13 Mar 2024 12:33:31 +0300 Subject: [PATCH 1/2] refactor: rewrite useCurrentUser with RQ --- .../DeleteAccountDialog/index.ee.jsx | 8 ++++++-- .../src/components/LiveChat/Chatwoot.ee.jsx | 19 ++++++++++++------ .../components/UpgradeFreeTrial/index.ee.jsx | 10 +++++----- packages/web/src/hooks/useCurrentUser.js | 20 +++++++++++++++---- .../web/src/hooks/useCurrentUserAbility.js | 6 ++++-- .../web/src/pages/ProfileSettings/index.jsx | 15 +++++++++++--- 6 files changed, 56 insertions(+), 22 deletions(-) diff --git a/packages/web/src/components/DeleteAccountDialog/index.ee.jsx b/packages/web/src/components/DeleteAccountDialog/index.ee.jsx index c4d3d08c..17fc568e 100644 --- a/packages/web/src/components/DeleteAccountDialog/index.ee.jsx +++ b/packages/web/src/components/DeleteAccountDialog/index.ee.jsx @@ -2,6 +2,7 @@ import PropTypes from 'prop-types'; import * as React from 'react'; import { useNavigate } from 'react-router-dom'; import { useMutation } from '@apollo/client'; + import * as URLS from 'config/urls'; import ConfirmationDialog from 'components/ConfirmationDialog'; import apolloClient from 'graphql/client'; @@ -13,15 +14,18 @@ import useCurrentUser from 'hooks/useCurrentUser'; function DeleteAccountDialog(props) { const [deleteCurrentUser] = useMutation(DELETE_CURRENT_USER); const formatMessage = useFormatMessage(); - const currentUser = useCurrentUser(); + const { data: currentUser } = useCurrentUser(); + const authentication = useAuthentication(); const navigate = useNavigate(); + const handleConfirm = React.useCallback(async () => { await deleteCurrentUser(); authentication.updateToken(''); await apolloClient.clearStore(); navigate(URLS.LOGIN); - }, [deleteCurrentUser, currentUser]); + }, [deleteCurrentUser, currentUser?.data]); + return ( { const theme = useTheme(); - const currentUser = useCurrentUser(); + const { data: currentUser } = useCurrentUser(); const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md')); + React.useEffect(function initiateChatwoot() { window.chatwootSDK.run({ websiteToken: 'EFyq5MTsvS7XwUrwSH36VskT', baseUrl: appConfig.chatwootBaseUrl, }); + return function removeChatwoot() { window.$chatwoot.reset(); window.$chatwoot.toggleBubbleVisibility('hide'); }; }, []); + React.useEffect( function initiateUser() { - if (!currentUser?.id || !ready) return; - window.$chatwoot.setUser(currentUser.id, { - email: currentUser.email, - name: currentUser.fullName, + if (!currentUser?.data?.id || !ready) return; + window.$chatwoot.setUser(currentUser.data?.id, { + email: currentUser?.data?.email, + name: currentUser?.data?.fullName, }); + if (!matchSmallScreens) { window.$chatwoot.toggleBubbleVisibility('show'); } }, - [currentUser, ready, matchSmallScreens], + [currentUser?.data, ready, matchSmallScreens], ); React.useLayoutEffect( function hideChatwoot() { @@ -40,6 +46,7 @@ const Chatwoot = ({ ready }) => { }, [matchSmallScreens], ); + return ; }; export default Chatwoot; diff --git a/packages/web/src/components/UpgradeFreeTrial/index.ee.jsx b/packages/web/src/components/UpgradeFreeTrial/index.ee.jsx index c5773274..161adacd 100644 --- a/packages/web/src/components/UpgradeFreeTrial/index.ee.jsx +++ b/packages/web/src/components/UpgradeFreeTrial/index.ee.jsx @@ -21,7 +21,7 @@ import usePaddle from 'hooks/usePaddle.ee'; export default function UpgradeFreeTrial() { const { data: plans, isLoading: isPaymentPlansLoading } = usePaymentPlans(); - const currentUser = useCurrentUser(); + const { data: currentUser } = useCurrentUser(); const { loaded: paddleLoaded } = usePaddle(); const [selectedIndex, setSelectedIndex] = React.useState(0); const selectedPlan = plans?.data?.[selectedIndex]; @@ -30,13 +30,13 @@ export default function UpgradeFreeTrial() { const handleCheckout = React.useCallback(() => { window.Paddle.Checkout?.open({ product: selectedPlan.productId, - email: currentUser.email, + email: currentUser?.data?.email, passthrough: JSON.stringify({ - id: currentUser.id, - email: currentUser.email, + id: currentUser?.data?.id, + email: currentUser?.data?.email, }), }); - }, [selectedPlan, currentUser]); + }, [selectedPlan, currentUser?.data]); if (isPaymentPlansLoading || !plans?.data?.length) return null; diff --git a/packages/web/src/hooks/useCurrentUser.js b/packages/web/src/hooks/useCurrentUser.js index 33d7b546..c5679715 100644 --- a/packages/web/src/hooks/useCurrentUser.js +++ b/packages/web/src/hooks/useCurrentUser.js @@ -1,6 +1,18 @@ -import { useQuery } from '@apollo/client'; -import { GET_CURRENT_USER } from 'graphql/queries/get-current-user'; +import { useQuery } from '@tanstack/react-query'; + +import api from 'helpers/api'; + export default function useCurrentUser() { - const { data } = useQuery(GET_CURRENT_USER); - return data?.getCurrentUser; + const query = useQuery({ + queryKey: ['currentUser'], + queryFn: async ({ signal }) => { + const { data } = await api.get(`/v1/users/me`, { + signal, + }); + + return data; + }, + }); + + return query; } diff --git a/packages/web/src/hooks/useCurrentUserAbility.js b/packages/web/src/hooks/useCurrentUserAbility.js index 7985a67c..169d06a5 100644 --- a/packages/web/src/hooks/useCurrentUserAbility.js +++ b/packages/web/src/hooks/useCurrentUserAbility.js @@ -1,6 +1,8 @@ import userAbility from 'helpers/userAbility'; import useCurrentUser from 'hooks/useCurrentUser'; + export default function useCurrentUserAbility() { - const currentUser = useCurrentUser(); - return userAbility(currentUser); + const { data: currentUser } = useCurrentUser(); + + return userAbility(currentUser?.data); } diff --git a/packages/web/src/pages/ProfileSettings/index.jsx b/packages/web/src/pages/ProfileSettings/index.jsx index 305d1584..01c23f63 100644 --- a/packages/web/src/pages/ProfileSettings/index.jsx +++ b/packages/web/src/pages/ProfileSettings/index.jsx @@ -9,6 +9,7 @@ import { styled } from '@mui/material/styles'; import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar'; import * as React from 'react'; import * as yup from 'yup'; + import Container from 'components/Container'; import DeleteAccountDialog from 'components/DeleteAccountDialog/index.ee'; import Form from 'components/Form'; @@ -17,6 +18,7 @@ import TextField from 'components/TextField'; import { UPDATE_CURRENT_USER } from 'graphql/mutations/update-current-user'; import useCurrentUser from 'hooks/useCurrentUser'; import useFormatMessage from 'hooks/useFormatMessage'; + const validationSchema = yup .object({ fullName: yup.string().required(), @@ -27,27 +29,32 @@ const validationSchema = yup .oneOf([yup.ref('password')], 'Passwords must match'), }) .required(); + const StyledForm = styled(Form)` display: flex; align-items: end; flex-direction: column; `; + function ProfileSettings() { const [showDeleteAccountConfirmation, setShowDeleteAccountConfirmation] = React.useState(false); const enqueueSnackbar = useEnqueueSnackbar(); - const currentUser = useCurrentUser(); + const { data: currentUser } = useCurrentUser(); const formatMessage = useFormatMessage(); const [updateCurrentUser] = useMutation(UPDATE_CURRENT_USER); + const handleProfileSettingsUpdate = async (data) => { const { fullName, password, email } = data; const mutationInput = { fullName, email, }; + if (password) { mutationInput.password = password; } + await updateCurrentUser({ variables: { input: mutationInput, @@ -55,12 +62,13 @@ function ProfileSettings() { optimisticResponse: { updateCurrentUser: { __typename: 'User', - id: currentUser.id, + id: currentUser?.data?.id, fullName, email, }, }, }); + enqueueSnackbar(formatMessage('profileSettings.updatedProfile'), { variant: 'success', SnackbarProps: { @@ -68,6 +76,7 @@ function ProfileSettings() { }, }); }; + return ( @@ -78,7 +87,7 @@ function ProfileSettings() { Date: Wed, 13 Mar 2024 12:36:11 +0300 Subject: [PATCH 2/2] refactor: remove get-current-user GQL query --- .../src/graphql/queries/get-current-user.js | 5 -- .../graphql/queries/get-current-user.test.js | 79 ------------------- .../backend/src/graphql/query-resolvers.js | 2 - packages/backend/src/graphql/schema.graphql | 1 - .../DeleteAccountDialog/index.ee.jsx | 5 +- .../src/components/LiveChat/Chatwoot.ee.jsx | 13 +-- .../components/UpgradeFreeTrial/index.ee.jsx | 11 +-- .../src/graphql/queries/get-current-user.js | 20 ----- .../web/src/pages/ProfileSettings/index.jsx | 7 +- 9 files changed, 20 insertions(+), 123 deletions(-) delete mode 100644 packages/backend/src/graphql/queries/get-current-user.js delete mode 100644 packages/backend/src/graphql/queries/get-current-user.test.js delete mode 100644 packages/web/src/graphql/queries/get-current-user.js diff --git a/packages/backend/src/graphql/queries/get-current-user.js b/packages/backend/src/graphql/queries/get-current-user.js deleted file mode 100644 index 6f725a28..00000000 --- a/packages/backend/src/graphql/queries/get-current-user.js +++ /dev/null @@ -1,5 +0,0 @@ -const getCurrentUser = async (_parent, _params, context) => { - return context.currentUser; -}; - -export default getCurrentUser; diff --git a/packages/backend/src/graphql/queries/get-current-user.test.js b/packages/backend/src/graphql/queries/get-current-user.test.js deleted file mode 100644 index d0897332..00000000 --- a/packages/backend/src/graphql/queries/get-current-user.test.js +++ /dev/null @@ -1,79 +0,0 @@ -import { describe, it, expect, beforeEach } from 'vitest'; -import request from 'supertest'; -import app from '../../app'; -import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id'; -import { createRole } from '../../../test/factories/role'; -import { createUser } from '../../../test/factories/user'; - -describe('graphQL getCurrentUser query', () => { - let role, currentUser, token, requestObject; - - beforeEach(async () => { - role = await createRole({ - key: 'sample', - name: 'sample', - }); - - currentUser = await createUser({ - roleId: role.id, - }); - - token = createAuthTokenByUserId(currentUser.id); - requestObject = request(app).post('/graphql').set('Authorization', token); - }); - - it('should return user data', async () => { - const query = ` - query { - getCurrentUser { - id - email - fullName - email - createdAt - updatedAt - role { - id - name - } - } - } - `; - - const response = await requestObject.send({ query }).expect(200); - - const expectedResponsePayload = { - data: { - getCurrentUser: { - createdAt: currentUser.createdAt.getTime().toString(), - email: currentUser.email, - fullName: currentUser.fullName, - id: currentUser.id, - role: { id: role.id, name: role.name }, - updatedAt: currentUser.updatedAt.getTime().toString(), - }, - }, - }; - - expect(response.body).toEqual(expectedResponsePayload); - }); - - it('should not return user password', async () => { - const query = ` - query { - getCurrentUser { - id - email - password - } - } - `; - - const response = await requestObject.send({ query }).expect(400); - - expect(response.body.errors).toBeDefined(); - expect(response.body.errors[0].message).toEqual( - 'Cannot query field "password" on type "User".' - ); - }); -}); diff --git a/packages/backend/src/graphql/query-resolvers.js b/packages/backend/src/graphql/query-resolvers.js index dc0eb649..0af2bbb9 100644 --- a/packages/backend/src/graphql/query-resolvers.js +++ b/packages/backend/src/graphql/query-resolvers.js @@ -4,7 +4,6 @@ import getAppAuthClients from './queries/get-app-auth-clients.ee.js'; import getBillingAndUsage from './queries/get-billing-and-usage.ee.js'; import getConfig from './queries/get-config.ee.js'; import getConnectedApps from './queries/get-connected-apps.js'; -import getCurrentUser from './queries/get-current-user.js'; import getDynamicData from './queries/get-dynamic-data.js'; import getDynamicFields from './queries/get-dynamic-fields.js'; import getFlow from './queries/get-flow.js'; @@ -32,7 +31,6 @@ const queryResolvers = { getBillingAndUsage, getConfig, getConnectedApps, - getCurrentUser, getDynamicData, getDynamicFields, getFlow, diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index 9ccd9088..2785827e 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -24,7 +24,6 @@ type Query { parameters: JSONObject ): [SubstepArgument] getBillingAndUsage: GetBillingAndUsage - getCurrentUser: User getConfig(keys: [String]): JSONObject getInvoices: [Invoice] getPermissionCatalog: PermissionCatalog diff --git a/packages/web/src/components/DeleteAccountDialog/index.ee.jsx b/packages/web/src/components/DeleteAccountDialog/index.ee.jsx index 17fc568e..df095907 100644 --- a/packages/web/src/components/DeleteAccountDialog/index.ee.jsx +++ b/packages/web/src/components/DeleteAccountDialog/index.ee.jsx @@ -14,7 +14,8 @@ import useCurrentUser from 'hooks/useCurrentUser'; function DeleteAccountDialog(props) { const [deleteCurrentUser] = useMutation(DELETE_CURRENT_USER); const formatMessage = useFormatMessage(); - const { data: currentUser } = useCurrentUser(); + const { data } = useCurrentUser(); + const currentUser = data?.data; const authentication = useAuthentication(); const navigate = useNavigate(); @@ -24,7 +25,7 @@ function DeleteAccountDialog(props) { authentication.updateToken(''); await apolloClient.clearStore(); navigate(URLS.LOGIN); - }, [deleteCurrentUser, currentUser?.data]); + }, [deleteCurrentUser, currentUser]); return ( { const theme = useTheme(); - const { data: currentUser } = useCurrentUser(); + const { data } = useCurrentUser(); + const currentUser = data?.data; const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md')); React.useEffect(function initiateChatwoot() { @@ -24,17 +25,17 @@ const Chatwoot = ({ ready }) => { React.useEffect( function initiateUser() { - if (!currentUser?.data?.id || !ready) return; - window.$chatwoot.setUser(currentUser.data?.id, { - email: currentUser?.data?.email, - name: currentUser?.data?.fullName, + if (!currentUser?.id || !ready) return; + window.$chatwoot.setUser(currentUser.id, { + email: currentUser.email, + name: currentUser.fullName, }); if (!matchSmallScreens) { window.$chatwoot.toggleBubbleVisibility('show'); } }, - [currentUser?.data, ready, matchSmallScreens], + [currentUser, ready, matchSmallScreens], ); React.useLayoutEffect( function hideChatwoot() { diff --git a/packages/web/src/components/UpgradeFreeTrial/index.ee.jsx b/packages/web/src/components/UpgradeFreeTrial/index.ee.jsx index 161adacd..98978cee 100644 --- a/packages/web/src/components/UpgradeFreeTrial/index.ee.jsx +++ b/packages/web/src/components/UpgradeFreeTrial/index.ee.jsx @@ -21,7 +21,8 @@ import usePaddle from 'hooks/usePaddle.ee'; export default function UpgradeFreeTrial() { const { data: plans, isLoading: isPaymentPlansLoading } = usePaymentPlans(); - const { data: currentUser } = useCurrentUser(); + const { data } = useCurrentUser(); + const currentUser = data?.data; const { loaded: paddleLoaded } = usePaddle(); const [selectedIndex, setSelectedIndex] = React.useState(0); const selectedPlan = plans?.data?.[selectedIndex]; @@ -30,13 +31,13 @@ export default function UpgradeFreeTrial() { const handleCheckout = React.useCallback(() => { window.Paddle.Checkout?.open({ product: selectedPlan.productId, - email: currentUser?.data?.email, + email: currentUser?.email, passthrough: JSON.stringify({ - id: currentUser?.data?.id, - email: currentUser?.data?.email, + id: currentUser?.id, + email: currentUser?.email, }), }); - }, [selectedPlan, currentUser?.data]); + }, [selectedPlan, currentUser]); if (isPaymentPlansLoading || !plans?.data?.length) return null; diff --git a/packages/web/src/graphql/queries/get-current-user.js b/packages/web/src/graphql/queries/get-current-user.js deleted file mode 100644 index 93d0ddcd..00000000 --- a/packages/web/src/graphql/queries/get-current-user.js +++ /dev/null @@ -1,20 +0,0 @@ -import { gql } from '@apollo/client'; -export const GET_CURRENT_USER = gql` - query GetCurrentUser { - getCurrentUser { - id - fullName - email - role { - id - isAdmin - } - permissions { - id - action - subject - conditions - } - } - } -`; diff --git a/packages/web/src/pages/ProfileSettings/index.jsx b/packages/web/src/pages/ProfileSettings/index.jsx index 01c23f63..9a7ab8b3 100644 --- a/packages/web/src/pages/ProfileSettings/index.jsx +++ b/packages/web/src/pages/ProfileSettings/index.jsx @@ -40,7 +40,8 @@ function ProfileSettings() { const [showDeleteAccountConfirmation, setShowDeleteAccountConfirmation] = React.useState(false); const enqueueSnackbar = useEnqueueSnackbar(); - const { data: currentUser } = useCurrentUser(); + const { data } = useCurrentUser(); + const currentUser = data?.data; const formatMessage = useFormatMessage(); const [updateCurrentUser] = useMutation(UPDATE_CURRENT_USER); @@ -62,7 +63,7 @@ function ProfileSettings() { optimisticResponse: { updateCurrentUser: { __typename: 'User', - id: currentUser?.data?.id, + id: currentUser?.id, fullName, email, }, @@ -87,7 +88,7 @@ function ProfileSettings() {