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 e71bd045..0337e009 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'; @@ -26,7 +25,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 390c568d..b83de58e 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 getPermissionCatalog: PermissionCatalog getNotifications: [Notification] diff --git a/packages/web/src/components/DeleteAccountDialog/index.ee.jsx b/packages/web/src/components/DeleteAccountDialog/index.ee.jsx index c4d3d08c..df095907 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,19 @@ import useCurrentUser from 'hooks/useCurrentUser'; function DeleteAccountDialog(props) { const [deleteCurrentUser] = useMutation(DELETE_CURRENT_USER); const formatMessage = useFormatMessage(); - const currentUser = useCurrentUser(); + const { data } = useCurrentUser(); + const currentUser = data?.data; + const authentication = useAuthentication(); const navigate = useNavigate(); + const handleConfirm = React.useCallback(async () => { await deleteCurrentUser(); authentication.updateToken(''); await apolloClient.clearStore(); navigate(URLS.LOGIN); }, [deleteCurrentUser, currentUser]); + return ( { const theme = useTheme(); - const currentUser = useCurrentUser(); + const { data } = useCurrentUser(); + const currentUser = data?.data; 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; @@ -24,6 +30,7 @@ const Chatwoot = ({ ready }) => { email: currentUser.email, name: currentUser.fullName, }); + if (!matchSmallScreens) { window.$chatwoot.toggleBubbleVisibility('show'); } @@ -40,6 +47,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..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 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,10 +31,10 @@ export default function UpgradeFreeTrial() { const handleCheckout = React.useCallback(() => { window.Paddle.Checkout?.open({ product: selectedPlan.productId, - email: currentUser.email, + email: currentUser?.email, passthrough: JSON.stringify({ - id: currentUser.id, - email: currentUser.email, + id: currentUser?.id, + email: currentUser?.email, }), }); }, [selectedPlan, currentUser]); 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/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..9a7ab8b3 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,33 @@ 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 } = useCurrentUser(); + const currentUser = data?.data; 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 +63,13 @@ function ProfileSettings() { optimisticResponse: { updateCurrentUser: { __typename: 'User', - id: currentUser.id, + id: currentUser?.id, fullName, email, }, }, }); + enqueueSnackbar(formatMessage('profileSettings.updatedProfile'), { variant: 'success', SnackbarProps: { @@ -68,6 +77,7 @@ function ProfileSettings() { }, }); }; + return (