From 224b6d1334d90e0617e16dc43451d7867215c1f8 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Wed, 11 Dec 2024 15:42:53 +0100 Subject: [PATCH] Fix login issue (#9012) Co-authored-by: Weiko --- .../RecordActionMenuEntriesSetter.tsx | 12 +++- .../src/modules/auth/components/Logo.tsx | 5 +- .../src/modules/auth/hooks/useAuth.ts | 2 +- .../ObjectMetadataItemsLoadEffect.tsx | 62 ++++++------------- .../hooks/useLoadMockedObjectMetadataItems.ts | 24 +++++++ .../hooks/useRefreshObjectMetadataItem.ts | 44 +++++++++++++ .../ui/input/components/ImageInput.tsx | 5 +- .../hooks/__tests__/useShowAuthModal.test.tsx | 8 +-- .../ui/layout/hooks/useShowAuthModal.ts | 4 ++ packages/twenty-front/vite.config.ts | 3 + .../file/controllers/file.controller.ts | 1 + .../workspace/workspace.resolver.ts | 40 ++++++++---- 12 files changed, 145 insertions(+), 65 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-metadata/hooks/useLoadMockedObjectMetadataItems.ts create mode 100644 packages/twenty-front/src/modules/object-metadata/hooks/useRefreshObjectMetadataItem.ts diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter.tsx index 1d1bfdf5b..a8982d3ac 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter.tsx @@ -5,16 +5,26 @@ import { WorkflowRunRecordActionMenuEntrySetterEffect } from '@/action-menu/acti import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById'; +import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; +import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-ui'; export const RecordActionMenuEntriesSetter = () => { const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2( contextStoreCurrentObjectMetadataIdComponentState, ); + const objectMetadataItems = useRecoilValue(objectMetadataItemsState); - if (!isDefined(contextStoreCurrentObjectMetadataId)) { + const objectMetadataItem = objectMetadataItems.find( + (item) => item.id === contextStoreCurrentObjectMetadataId, + ); + + if ( + !isDefined(contextStoreCurrentObjectMetadataId) || + !isDefined(objectMetadataItem) + ) { return null; } diff --git a/packages/twenty-front/src/modules/auth/components/Logo.tsx b/packages/twenty-front/src/modules/auth/components/Logo.tsx index 1ecd75808..d9ca70201 100644 --- a/packages/twenty-front/src/modules/auth/components/Logo.tsx +++ b/packages/twenty-front/src/modules/auth/components/Logo.tsx @@ -1,5 +1,6 @@ import styled from '@emotion/styled'; -import { getImageAbsoluteURI, isDefined } from 'twenty-ui'; +import { isNonEmptyString } from '@sniptt/guards'; +import { getImageAbsoluteURI } from 'twenty-ui'; type LogoProps = { primaryLogo?: string | null; @@ -48,7 +49,7 @@ export const Logo = (props: LogoProps) => { const primaryLogoUrl = getImageAbsoluteURI( props.primaryLogo ?? defaultPrimaryLogoUrl, ); - const secondaryLogoUrl = isDefined(props.secondaryLogo) + const secondaryLogoUrl = isNonEmptyString(props.secondaryLogo) ? getImageAbsoluteURI(props.secondaryLogo) : null; diff --git a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts index aff5d35e2..3129976d4 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts @@ -42,9 +42,9 @@ import { getTimeFormatFromWorkspaceTimeFormat } from '@/localization/utils/getTi import { currentUserState } from '../states/currentUserState'; import { tokenPairState } from '../states/tokenPairState'; -import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain'; import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState'; import { useIsCurrentLocationOnAWorkspaceSubdomain } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspaceSubdomain'; +import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain'; import { useReadWorkspaceSubdomainFromCurrentLocation } from '@/domain-manager/hooks/useReadWorkspaceSubdomainFromCurrentLocation'; import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState'; diff --git a/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataItemsLoadEffect.tsx b/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataItemsLoadEffect.tsx index 4142a52f5..f078aa59b 100644 --- a/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataItemsLoadEffect.tsx +++ b/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataItemsLoadEffect.tsx @@ -1,59 +1,35 @@ import { useEffect } from 'react'; -import { useRecoilCallback, useRecoilValue } from 'recoil'; +import { useRecoilValue } from 'recoil'; -import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { currentUserState } from '@/auth/states/currentUserState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; -import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems'; -import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { useLoadMockedObjectMetadataItems } from '@/object-metadata/hooks/useLoadMockedObjectMetadataItems'; +import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem'; import { WorkspaceActivationStatus } from '~/generated/graphql'; -import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; -import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; export const ObjectMetadataItemsLoadEffect = () => { const currentUser = useRecoilValue(currentUserState); const currentWorkspace = useRecoilValue(currentWorkspaceState); - const isLoggedIn = useIsLogged(); - const { - objectMetadataItems: newObjectMetadataItems, - loading: isObjectMetadataLoading, - } = useFindManyObjectMetadataItems({ - skip: !isLoggedIn, - }); - - const updateObjectMetadataItems = useRecoilCallback( - ({ set, snapshot }) => - () => { - const toSetObjectMetadataItems = - isUndefinedOrNull(currentUser) || - currentWorkspace?.activationStatus !== - WorkspaceActivationStatus.Active - ? generatedMockObjectMetadataItems - : newObjectMetadataItems; - - if ( - !isObjectMetadataLoading && - !isDeeplyEqual( - snapshot.getLoadable(objectMetadataItemsState).getValue(), - toSetObjectMetadataItems, - ) - ) { - set(objectMetadataItemsState, toSetObjectMetadataItems); - } - }, - [ - currentUser, - currentWorkspace?.activationStatus, - isObjectMetadataLoading, - newObjectMetadataItems, - ], - ); + const { refreshObjectMetadataItems } = useRefreshObjectMetadataItems(); + const { loadMockedObjectMetadataItems } = useLoadMockedObjectMetadataItems(); useEffect(() => { - updateObjectMetadataItems(); - }, [updateObjectMetadataItems]); + if ( + isUndefinedOrNull(currentUser) || + currentWorkspace?.activationStatus !== WorkspaceActivationStatus.Active + ) { + loadMockedObjectMetadataItems(); + } else { + refreshObjectMetadataItems(); + } + }, [ + currentUser, + currentWorkspace?.activationStatus, + loadMockedObjectMetadataItems, + refreshObjectMetadataItems, + ]); return <>; }; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useLoadMockedObjectMetadataItems.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useLoadMockedObjectMetadataItems.ts new file mode 100644 index 000000000..e8e3fa71c --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useLoadMockedObjectMetadataItems.ts @@ -0,0 +1,24 @@ +import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { useRecoilCallback } from 'recoil'; +import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; +import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; + +export const useLoadMockedObjectMetadataItems = () => { + const loadMockedObjectMetadataItems = useRecoilCallback( + ({ set, snapshot }) => + () => { + if ( + !isDeeplyEqual( + snapshot.getLoadable(objectMetadataItemsState).getValue(), + generatedMockObjectMetadataItems, + ) + ) { + set(objectMetadataItemsState, generatedMockObjectMetadataItems); + } + }, + [], + ); + return { + loadMockedObjectMetadataItems, + }; +}; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useRefreshObjectMetadataItem.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useRefreshObjectMetadataItem.ts new file mode 100644 index 000000000..af68f985b --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useRefreshObjectMetadataItem.ts @@ -0,0 +1,44 @@ +import { FIND_MANY_OBJECT_METADATA_ITEMS } from '@/object-metadata/graphql/queries'; +import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient'; +import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { mapPaginatedObjectMetadataItemsToObjectMetadataItems } from '@/object-metadata/utils/mapPaginatedObjectMetadataItemsToObjectMetadataItems'; +import { useRecoilCallback } from 'recoil'; +import { ObjectMetadataItemsQuery } from '~/generated-metadata/graphql'; +import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; + +export const useRefreshObjectMetadataItems = () => { + const client = useApolloMetadataClient(); + + const refreshObjectMetadataItems = async () => { + const result = await client.query({ + query: FIND_MANY_OBJECT_METADATA_ITEMS, + variables: {}, + }); + + const objectMetadataItems = + mapPaginatedObjectMetadataItemsToObjectMetadataItems({ + pagedObjectMetadataItems: result.data, + }); + + replaceObjectMetadataItemIfDifferent(objectMetadataItems); + }; + + const replaceObjectMetadataItemIfDifferent = useRecoilCallback( + ({ set, snapshot }) => + (toSetObjectMetadataItems: ObjectMetadataItem[]) => { + if ( + !isDeeplyEqual( + snapshot.getLoadable(objectMetadataItemsState).getValue(), + toSetObjectMetadataItems, + ) + ) { + set(objectMetadataItemsState, toSetObjectMetadataItems); + } + }, + [], + ); + return { + refreshObjectMetadataItems, + }; +}; diff --git a/packages/twenty-front/src/modules/ui/input/components/ImageInput.tsx b/packages/twenty-front/src/modules/ui/input/components/ImageInput.tsx index 707029987..838238e44 100644 --- a/packages/twenty-front/src/modules/ui/input/components/ImageInput.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/ImageInput.tsx @@ -1,5 +1,6 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; +import { isNonEmptyString } from '@sniptt/guards'; import React from 'react'; import { Button, @@ -115,7 +116,9 @@ export const ImageInput = ({ hiddenFileInput.current?.click(); }; - const pictureURI = isDefined(picture) ? getImageAbsoluteURI(picture) : null; + const pictureURI = isNonEmptyString(picture) + ? getImageAbsoluteURI(picture) + : null; return ( diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx index 21fd9ea73..dc7d8477f 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx +++ b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx @@ -68,15 +68,15 @@ const testCases = [ { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: true }, { loc: AppPath.SignInUp, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: true }, { loc: AppPath.Invite, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.Invite, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: true }, diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts index 3ab686d65..f81f54536 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts +++ b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts @@ -21,6 +21,10 @@ export const useShowAuthModal = () => { ); return useMemo(() => { + if (isMatchingLocation(AppPath.SignInUp)) { + return true; + } + if (isMatchingLocation(AppPath.Verify)) { return false; } diff --git a/packages/twenty-front/vite.config.ts b/packages/twenty-front/vite.config.ts index 1cb1698d2..2e86ec59a 100644 --- a/packages/twenty-front/vite.config.ts +++ b/packages/twenty-front/vite.config.ts @@ -143,6 +143,9 @@ export default defineConfig(({ command, mode }) => { envPrefix: 'REACT_APP_', define: { + _env_: { + REACT_APP_SERVER_BASE_URL, + }, 'process.env': { REACT_APP_SERVER_BASE_URL, }, diff --git a/packages/twenty-server/src/engine/core-modules/file/controllers/file.controller.ts b/packages/twenty-server/src/engine/core-modules/file/controllers/file.controller.ts index 967df3119..cd95d43c7 100644 --- a/packages/twenty-server/src/engine/core-modules/file/controllers/file.controller.ts +++ b/packages/twenty-server/src/engine/core-modules/file/controllers/file.controller.ts @@ -27,6 +27,7 @@ export class FileController { @Req() req: Request, ) { const folderPath = checkFilePath(params[0]); + const filename = checkFilename(params['filename']); const workspaceId = (req as any)?.workspaceId; diff --git a/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts b/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts index b45336e36..dd6fdd397 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts @@ -12,35 +12,35 @@ import { FileUpload, GraphQLUpload } from 'graphql-upload'; import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface'; +import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service'; import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service'; +import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service'; import { FileService } from 'src/engine/core-modules/file/services/file.service'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; import { User } from 'src/engine/core-modules/user/user.entity'; import { ActivateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-input'; +import { ActivateWorkspaceOutput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-output'; +import { PublicWorkspaceDataOutput } from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output'; import { UpdateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/update-workspace-input'; +import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace'; +import { workspaceGraphqlApiExceptionHandler } from 'src/engine/core-modules/workspace/utils/workspaceGraphqlApiExceptionHandler'; +import { + WorkspaceException, + WorkspaceExceptionCode, +} from 'src/engine/core-modules/workspace/workspace.exception'; +import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate'; import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; +import { OriginHeader } from 'src/engine/decorators/auth/origin-header.decorator'; import { DemoEnvGuard } from 'src/engine/guards/demo.env.guard'; import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { assert } from 'src/utils/assert'; import { isDefined } from 'src/utils/is-defined'; import { streamToBuffer } from 'src/utils/stream-to-buffer'; -import { - WorkspaceException, - WorkspaceExceptionCode, -} from 'src/engine/core-modules/workspace/workspace.exception'; -import { PublicWorkspaceDataOutput } from 'src/engine/core-modules/workspace/dtos/public-workspace-data.output'; -import { ActivateWorkspaceOutput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-output'; -import { OriginHeader } from 'src/engine/decorators/auth/origin-header.decorator'; -import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate'; -import { LoginTokenService } from 'src/engine/core-modules/auth/token/services/login-token.service'; -import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service'; -import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/getAuthProvidersByWorkspace'; -import { workspaceGraphqlApiExceptionHandler } from 'src/engine/core-modules/workspace/utils/workspaceGraphqlApiExceptionHandler'; import { Workspace } from './workspace.entity'; @@ -193,9 +193,23 @@ export class WorkspaceResolver { ), ); + let workspaceLogoWithToken = ''; + + if (workspace.logo) { + try { + const workspaceLogoToken = await this.fileService.encodeFileToken({ + workspaceId: workspace.id, + }); + + workspaceLogoWithToken = `${workspace.logo}?token=${workspaceLogoToken}`; + } catch (e) { + workspaceLogoWithToken = workspace.logo; + } + } + return { id: workspace.id, - logo: workspace.logo, + logo: workspaceLogoWithToken, displayName: workspace.displayName, subdomain: workspace.subdomain, authProviders: getAuthProvidersByWorkspace(workspace),