From 2bcae523ac6061768366d4b0cafd074cea01926b Mon Sep 17 00:00:00 2001 From: BOHEUS <56270748+BOHEUS@users.noreply.github.com> Date: Fri, 30 Jan 2026 15:56:22 +0000 Subject: [PATCH] Added option to prevent sending requests to twenty-icons (#16723) Solution for #15891 https://github.com/user-attachments/assets/82ce5c4b-0a78-45a8-8196-413473887820 --------- Co-authored-by: Charles Bochet --- packages/twenty-front/nyc.config.cjs | 2 +- .../timeline-activities/components/EventRow.tsx | 7 +++++++ .../modules/client-config/hooks/useClientConfig.ts | 8 ++++++++ .../states/allowRequestsToTwentyIcons.ts | 6 ++++++ .../src/modules/client-config/types/ClientConfig.ts | 1 + .../components/CommandMenuContextRecordsChip.tsx | 6 ++++++ .../components/CommandMenuRecordInfo.tsx | 5 +++++ .../hooks/useCommandMenuContextChips.tsx | 6 ++++++ .../command-menu/utils/getRecordContextText.ts | 7 ++++++- .../src/modules/favorites/hooks/useFavorites.ts | 6 +++++- .../modules/favorites/hooks/useFavoritesMetadata.ts | 6 +++++- .../modules/favorites/hooks/useWorkspaceFavorites.ts | 6 +++++- .../components/PreComputedChipGeneratorsProvider.tsx | 12 +++++++++--- ...eGetObjectRecordIdentifierByNameSingular.test.tsx | 2 +- .../useMapToObjectRecordIdentifier.test.tsx | 1 + .../useGetObjectRecordIdentifierByNameSingular.ts | 5 ++++- .../hooks/useMapToObjectRecordIdentifier.ts | 8 +++++++- .../modules/object-metadata/utils/getAvatarUrl.ts | 6 +++++- .../utils/getObjectRecordIdentifier.ts | 3 +++ .../components/ObjectFilterDropdownRecordSelect.tsx | 7 +++++++ .../record-show/components/SummaryCard.tsx | 5 +++++ .../selectors/recordStoreIdentifierSelector.ts | 9 ++++++++- .../selectors/recordStoreIdentifiersSelector.ts | 9 ++++++++- .../select/hooks/useRecordsForSelect.ts | 3 +++ .../object-record/utils/getRecordChipGenerators.ts | 2 ++ .../useComputeRecordRelationFilterLabelValue.tsx | 7 +++++++ .../twenty-front/src/testing/mock-data/config.ts | 1 + packages/twenty-server/.env.example | 1 + .../client-config/client-config.controller.spec.ts | 1 + .../client-config/client-config.entity.ts | 3 +++ .../client-config/services/client-config.service.ts | 3 +++ .../search/__tests__/search.service.spec.ts | 4 ++-- .../core-modules/search/services/search.service.ts | 7 ++++++- .../core-modules/twenty-config/config-variables.ts | 8 ++++++++ 34 files changed, 156 insertions(+), 17 deletions(-) create mode 100644 packages/twenty-front/src/modules/client-config/states/allowRequestsToTwentyIcons.ts diff --git a/packages/twenty-front/nyc.config.cjs b/packages/twenty-front/nyc.config.cjs index d2885c31d9..edcd5ad86c 100644 --- a/packages/twenty-front/nyc.config.cjs +++ b/packages/twenty-front/nyc.config.cjs @@ -25,7 +25,7 @@ const pagesCoverage = { }; const performanceCoverage = { - branches: 35, + branches: 33, statements: 60, lines: 60, functions: 45, diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/components/EventRow.tsx b/packages/twenty-front/src/modules/activities/timeline-activities/components/EventRow.tsx index 426d9b57c7..78e001d5ff 100644 --- a/packages/twenty-front/src/modules/activities/timeline-activities/components/EventRow.tsx +++ b/packages/twenty-front/src/modules/activities/timeline-activities/components/EventRow.tsx @@ -16,6 +16,7 @@ import { recordStoreFamilyState } from '@/object-record/record-store/states/reco import { dateLocaleState } from '~/localization/states/dateLocaleState'; import { beautifyPastDateRelativeToNow } from '~/utils/date-utils'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; +import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; const StyledTimelineItemContainer = styled.div` color: ${({ theme }) => theme.font.color.primary}; @@ -92,6 +93,11 @@ export const EventRow = ({ mainObjectMetadataItem, }: EventRowProps) => { const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); + + const allowRequestsToTwentyIcons = useRecoilValue( + allowRequestsToTwentyIconsState, + ); + const { localeCatalog } = useRecoilValue(dateLocaleState); const { recordId } = useContext(TimelineActivityContext); @@ -120,6 +126,7 @@ export const EventRow = ({ const labelIdentifier = getObjectRecordIdentifier({ objectMetadataItem: mainObjectMetadataItem, record: recordFromStore, + allowRequestsToTwentyIcons, }); const authorFullName = getTimelineActivityAuthorFullName( diff --git a/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts b/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts index 3f0273c00e..d19753dcc6 100644 --- a/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts +++ b/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts @@ -29,6 +29,7 @@ import { useCallback } from 'react'; import { useRecoilState, useSetRecoilState } from 'recoil'; import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState'; import { getClientConfig } from '@/client-config/utils/getClientConfig'; +import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; type UseClientConfigResult = { data: { clientConfig: ClientConfig } | undefined; @@ -110,6 +111,11 @@ export const useClientConfig = (): UseClientConfigResult => { const setIsEmailingDomainsEnabled = useSetRecoilState( isEmailingDomainsEnabledState, ); + + const setAllowRequestsToTwentyIcons = useSetRecoilState( + allowRequestsToTwentyIconsState, + ); + const setIsCloudflareIntegrationEnabled = useSetRecoilState( isCloudflareIntegrationEnabledState, ); @@ -188,6 +194,7 @@ export const useClientConfig = (): UseClientConfigResult => { setCalendarBookingPageId(clientConfig?.calendarBookingPageId ?? null); setIsImapSmtpCaldavEnabled(clientConfig?.isImapSmtpCaldavEnabled); setIsEmailingDomainsEnabled(clientConfig?.isEmailingDomainsEnabled); + setAllowRequestsToTwentyIcons(clientConfig?.allowRequestsToTwentyIcons); setIsCloudflareIntegrationEnabled( clientConfig?.isCloudflareIntegrationEnabled, ); @@ -230,6 +237,7 @@ export const useClientConfig = (): UseClientConfigResult => { setMicrosoftMessagingEnabled, setSentryConfig, setSupportChat, + setAllowRequestsToTwentyIcons, ]); return { diff --git a/packages/twenty-front/src/modules/client-config/states/allowRequestsToTwentyIcons.ts b/packages/twenty-front/src/modules/client-config/states/allowRequestsToTwentyIcons.ts new file mode 100644 index 0000000000..2dcf7ca507 --- /dev/null +++ b/packages/twenty-front/src/modules/client-config/states/allowRequestsToTwentyIcons.ts @@ -0,0 +1,6 @@ +import { createState } from 'twenty-ui/utilities'; + +export const allowRequestsToTwentyIconsState = createState({ + key: 'allowRequestsToTwentyIcons', + defaultValue: true, +}); diff --git a/packages/twenty-front/src/modules/client-config/types/ClientConfig.ts b/packages/twenty-front/src/modules/client-config/types/ClientConfig.ts index 6c676ad297..8c8c71de53 100644 --- a/packages/twenty-front/src/modules/client-config/types/ClientConfig.ts +++ b/packages/twenty-front/src/modules/client-config/types/ClientConfig.ts @@ -38,4 +38,5 @@ export type ClientConfig = { signInPrefilled: boolean; support: Support; isTwoFactorAuthenticationEnabled: boolean; + allowRequestsToTwentyIcons: boolean; }; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextRecordsChip.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextRecordsChip.tsx index 9287870367..c681f98ec8 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextRecordsChip.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuContextRecordsChip.tsx @@ -3,6 +3,8 @@ import { CommandMenuContextRecordChipAvatars } from '@/command-menu/components/C import { getSelectedRecordsContextText } from '@/command-menu/utils/getRecordContextText'; import { useFindManyRecordsSelectedInContextStore } from '@/context-store/hooks/useFindManyRecordsSelectedInContextStore'; import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById'; +import { useRecoilValue } from 'recoil'; +import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; export const CommandMenuContextRecordsChip = ({ objectMetadataItemId, @@ -14,6 +16,9 @@ export const CommandMenuContextRecordsChip = ({ const { objectMetadataItem } = useObjectMetadataItemById({ objectId: objectMetadataItemId, }); + const allowRequestsToTwentyIcons = useRecoilValue( + allowRequestsToTwentyIconsState, + ); const { records, loading, totalCount } = useFindManyRecordsSelectedInContextStore({ @@ -39,6 +44,7 @@ export const CommandMenuContextRecordsChip = ({ objectMetadataItem, records, totalCount, + allowRequestsToTwentyIcons, )} Icons={Avatars} /> diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuRecordInfo.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuRecordInfo.tsx index 8056f5e266..fcf35e7eee 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuRecordInfo.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuRecordInfo.tsx @@ -19,6 +19,7 @@ import { FieldMetadataType } from '~/generated-metadata/graphql'; import { dateLocaleState } from '~/localization/states/dateLocaleState'; import { beautifyPastDateRelativeToNow } from '~/utils/date-utils'; import { CommandMenuPageInfoLayout } from './CommandMenuPageInfoLayout'; +import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; export const CommandMenuRecordInfo = ({ commandMenuPageInstanceId, @@ -29,6 +30,9 @@ export const CommandMenuRecordInfo = ({ viewableRecordNameSingularComponentState, commandMenuPageInstanceId, ); + const allowRequestsToTwentyIcons = useRecoilValue( + allowRequestsToTwentyIconsState, + ); const viewableRecordId = useRecoilComponentValue( viewableRecordIdComponentState, @@ -50,6 +54,7 @@ export const CommandMenuRecordInfo = ({ const recordIdentifier = useRecoilValue( recordStoreIdentifierFamilySelector({ recordId: objectRecordId, + allowRequestsToTwentyIcons, }), ); diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuContextChips.tsx b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuContextChips.tsx index eca4b5ebbe..f0597f598e 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuContextChips.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuContextChips.tsx @@ -11,6 +11,7 @@ import styled from '@emotion/styled'; import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; +import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; const StyledIconWrapper = styled.div` background: ${({ theme }) => theme.background.primary}; @@ -25,6 +26,10 @@ export const useCommandMenuContextChips = () => { commandMenuNavigationStackState, ); + const allowRequestsToTwentyIcons = useRecoilValue( + allowRequestsToTwentyIconsState, + ); + const objectMetadataItems = useRecoilValue(objectMetadataItemsState); const { navigateCommandMenuHistory } = useCommandMenuHistory(); @@ -44,6 +49,7 @@ export const useCommandMenuContextChips = () => { const recordIdentifiers = useRecoilValue( recordStoreIdentifiersFamilySelector({ recordIds: allRecordIds, + allowRequestsToTwentyIcons, }), ); const records = useRecoilValue( diff --git a/packages/twenty-front/src/modules/command-menu/utils/getRecordContextText.ts b/packages/twenty-front/src/modules/command-menu/utils/getRecordContextText.ts index 51e47fd0a1..5ca312e7f7 100644 --- a/packages/twenty-front/src/modules/command-menu/utils/getRecordContextText.ts +++ b/packages/twenty-front/src/modules/command-menu/utils/getRecordContextText.ts @@ -6,8 +6,13 @@ export const getSelectedRecordsContextText = ( objectMetadataItem: ObjectMetadataItem, records: ObjectRecord[], totalCount: number, + allowRequestsToTwentyIcons: boolean, ) => { return totalCount === 1 - ? getObjectRecordIdentifier({ objectMetadataItem, record: records[0] }).name + ? getObjectRecordIdentifier({ + objectMetadataItem, + record: records[0], + allowRequestsToTwentyIcons, + }).name : `${totalCount} ${objectMetadataItem.labelPlural}`; }; diff --git a/packages/twenty-front/src/modules/favorites/hooks/useFavorites.ts b/packages/twenty-front/src/modules/favorites/hooks/useFavorites.ts index e295397672..734032d07e 100644 --- a/packages/twenty-front/src/modules/favorites/hooks/useFavorites.ts +++ b/packages/twenty-front/src/modules/favorites/hooks/useFavorites.ts @@ -8,6 +8,7 @@ import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData'; +import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; export const useFavorites = () => { const { favorites } = usePrefetchedFavoritesData(); @@ -19,8 +20,11 @@ export const useFavorites = () => { useObjectMetadataItem({ objectNameSingular: CoreObjectNameSingular.Favorite, }); + const allowRequestsToTwentyIcons = useRecoilValue( + allowRequestsToTwentyIconsState, + ); const getObjectRecordIdentifierByNameSingular = - useGetObjectRecordIdentifierByNameSingular(); + useGetObjectRecordIdentifierByNameSingular(allowRequestsToTwentyIcons); const favoriteRelationFieldMetadataItems = useMemo( () => diff --git a/packages/twenty-front/src/modules/favorites/hooks/useFavoritesMetadata.ts b/packages/twenty-front/src/modules/favorites/hooks/useFavoritesMetadata.ts index 2ef9ce1ce3..4ccdb8cc26 100644 --- a/packages/twenty-front/src/modules/favorites/hooks/useFavoritesMetadata.ts +++ b/packages/twenty-front/src/modules/favorites/hooks/useFavoritesMetadata.ts @@ -4,11 +4,15 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useRecoilValue } from 'recoil'; import { FieldMetadataType } from '~/generated-metadata/graphql'; +import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; export const useFavoritesMetadata = () => { const objectMetadataItems = useRecoilValue(objectMetadataItemsState); + const allowRequestsToTwentyIcons = useRecoilValue( + allowRequestsToTwentyIconsState, + ); const getObjectRecordIdentifierByNameSingular = - useGetObjectRecordIdentifierByNameSingular(); + useGetObjectRecordIdentifierByNameSingular(allowRequestsToTwentyIcons); const { objectMetadataItem: favoriteObjectMetadataItem } = useObjectMetadataItem({ diff --git a/packages/twenty-front/src/modules/favorites/hooks/useWorkspaceFavorites.ts b/packages/twenty-front/src/modules/favorites/hooks/useWorkspaceFavorites.ts index 5f74df692e..407209b253 100644 --- a/packages/twenty-front/src/modules/favorites/hooks/useWorkspaceFavorites.ts +++ b/packages/twenty-front/src/modules/favorites/hooks/useWorkspaceFavorites.ts @@ -10,6 +10,7 @@ import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { usePrefetchedFavoritesData } from './usePrefetchedFavoritesData'; +import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; export const useWorkspaceFavorites = () => { const { workspaceFavorites } = usePrefetchedFavoritesData(); @@ -19,8 +20,11 @@ export const useWorkspaceFavorites = () => { useObjectMetadataItem({ objectNameSingular: CoreObjectNameSingular.Favorite, }); + const allowRequestsToTwentyIcons = useRecoilValue( + allowRequestsToTwentyIconsState, + ); const getObjectRecordIdentifierByNameSingular = - useGetObjectRecordIdentifierByNameSingular(); + useGetObjectRecordIdentifierByNameSingular(allowRequestsToTwentyIcons); const favoriteRelationFieldMetadataItems = useMemo( () => diff --git a/packages/twenty-front/src/modules/object-metadata/components/PreComputedChipGeneratorsProvider.tsx b/packages/twenty-front/src/modules/object-metadata/components/PreComputedChipGeneratorsProvider.tsx index 6a03a1bbda..e6403db11a 100644 --- a/packages/twenty-front/src/modules/object-metadata/components/PreComputedChipGeneratorsProvider.tsx +++ b/packages/twenty-front/src/modules/object-metadata/components/PreComputedChipGeneratorsProvider.tsx @@ -4,16 +4,22 @@ import { useRecoilValue } from 'recoil'; import { PreComputedChipGeneratorsContext } from '@/object-metadata/contexts/PreComputedChipGeneratorsContext'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { getRecordChipGenerators } from '@/object-record/utils/getRecordChipGenerators'; +import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; export const PreComputedChipGeneratorsProvider = ({ children, }: React.PropsWithChildren) => { const objectMetadataItems = useRecoilValue(objectMetadataItemsState); - + const allowRequestsToTwentyIcons = useRecoilValue( + allowRequestsToTwentyIconsState, + ); const { chipGeneratorPerObjectPerField, identifierChipGeneratorPerObject } = useMemo(() => { - return getRecordChipGenerators(objectMetadataItems); - }, [objectMetadataItems]); + return getRecordChipGenerators( + objectMetadataItems, + allowRequestsToTwentyIcons, + ); + }, [allowRequestsToTwentyIcons, objectMetadataItems]); return ( <> diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useGetObjectRecordIdentifierByNameSingular.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useGetObjectRecordIdentifierByNameSingular.test.tsx index eb8c5a940b..415568c172 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useGetObjectRecordIdentifierByNameSingular.test.tsx +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useGetObjectRecordIdentifierByNameSingular.test.tsx @@ -19,7 +19,7 @@ describe('useGetObjectRecordIdentifierByNameSingular', () => { setMetadataItems(generatedMockObjectMetadataItems); - return useGetObjectRecordIdentifierByNameSingular()( + return useGetObjectRecordIdentifierByNameSingular(true)( record, objectNameSingular, ); diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapToObjectRecordIdentifier.test.tsx b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapToObjectRecordIdentifier.test.tsx index 8792fb3129..46e5249f2f 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapToObjectRecordIdentifier.test.tsx +++ b/packages/twenty-front/src/modules/object-metadata/hooks/__tests__/useMapToObjectRecordIdentifier.test.tsx @@ -13,6 +13,7 @@ describe('useMapToObjectRecordIdentifier', () => { () => { const { mapToObjectRecordIdentifier } = useMapToObjectRecordIdentifier({ objectNameSingular: 'person', + allowRequestsToTwentyIcons: true, }); return mapToObjectRecordIdentifier({ diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useGetObjectRecordIdentifierByNameSingular.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useGetObjectRecordIdentifierByNameSingular.ts index f015904eb0..b6a5aa665c 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/useGetObjectRecordIdentifierByNameSingular.ts +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useGetObjectRecordIdentifierByNameSingular.ts @@ -4,7 +4,9 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier'; import { type ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier'; -export const useGetObjectRecordIdentifierByNameSingular = () => { +export const useGetObjectRecordIdentifierByNameSingular = ( + allowRequestsToTwentyIcons: boolean, +) => { const objectMetadataItems = useRecoilValue(objectMetadataItemsState); return (record: any, objectNameSingular: string): ObjectRecordIdentifier => { @@ -21,6 +23,7 @@ export const useGetObjectRecordIdentifierByNameSingular = () => { return getObjectRecordIdentifier({ objectMetadataItem, record, + allowRequestsToTwentyIcons, }); }; }; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useMapToObjectRecordIdentifier.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useMapToObjectRecordIdentifier.ts index 7b61f158c5..4e76db255b 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/useMapToObjectRecordIdentifier.ts +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useMapToObjectRecordIdentifier.ts @@ -4,15 +4,21 @@ import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; export const useMapToObjectRecordIdentifier = ({ objectNameSingular, + allowRequestsToTwentyIcons, }: { objectNameSingular: string; + allowRequestsToTwentyIcons: boolean; }) => { const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, }); const mapToObjectRecordIdentifier = (record: ObjectRecord) => { - return getObjectRecordIdentifier({ objectMetadataItem, record }); + return getObjectRecordIdentifier({ + objectMetadataItem, + record, + allowRequestsToTwentyIcons, + }); }; return { mapToObjectRecordIdentifier }; diff --git a/packages/twenty-front/src/modules/object-metadata/utils/getAvatarUrl.ts b/packages/twenty-front/src/modules/object-metadata/utils/getAvatarUrl.ts index 5fba682186..6b3ffc4f52 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/getAvatarUrl.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/getAvatarUrl.ts @@ -15,12 +15,16 @@ export const getAvatarUrl = ( objectNameSingular: string, record: ObjectRecord, imageIdentifierFieldMetadataItem: FieldMetadataItem | undefined, + allowRequestsToTwentyIcons?: boolean | undefined, ) => { if (objectNameSingular === CoreObjectNameSingular.WorkspaceMember) { return record.avatarUrl ?? undefined; } - if (objectNameSingular === CoreObjectNameSingular.Company) { + if ( + objectNameSingular === CoreObjectNameSingular.Company && + allowRequestsToTwentyIcons === true + ) { return getLogoUrlFromDomainName( getCompanyDomainName(record as Company) ?? '', ); diff --git a/packages/twenty-front/src/modules/object-metadata/utils/getObjectRecordIdentifier.ts b/packages/twenty-front/src/modules/object-metadata/utils/getObjectRecordIdentifier.ts index e37b87539a..29411b9b84 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/getObjectRecordIdentifier.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/getObjectRecordIdentifier.ts @@ -11,6 +11,7 @@ import { getLinkToShowPage } from './getLinkToShowPage'; export const getObjectRecordIdentifier = ({ objectMetadataItem, record, + allowRequestsToTwentyIcons, }: { objectMetadataItem: Pick< ObjectMetadataItem, @@ -20,6 +21,7 @@ export const getObjectRecordIdentifier = ({ | 'imageIdentifierFieldMetadataId' >; record: ObjectRecord; + allowRequestsToTwentyIcons: boolean; }): ObjectRecordIdentifier => { const labelIdentifierFieldMetadataItem = getLabelIdentifierFieldMetadataItem(objectMetadataItem); @@ -40,6 +42,7 @@ export const getObjectRecordIdentifier = ({ objectMetadataItem.nameSingular, record, imageIdentifierFieldMetadata, + allowRequestsToTwentyIcons, ); const linkToShowPage = getLinkToShowPage( diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx index 550795c895..2b8316e70b 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx @@ -20,6 +20,8 @@ import { jsonRelationFilterValueSchema, } from 'twenty-shared/utils'; import { IconUserCircle } from 'twenty-ui/display'; +import { useRecoilValue } from 'recoil'; +import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; export const EMPTY_FILTER_VALUE: string = JSON.stringify({ isCurrentWorkspaceMemberSelected: false, @@ -41,6 +43,10 @@ export const ObjectFilterDropdownRecordSelect = ({ fieldMetadataItemUsedInDropdownComponentSelector, ); + const allowRequestsToTwentyIcons = useRecoilValue( + allowRequestsToTwentyIconsState, + ); + const { objectFilterDropdownFilterValue } = useObjectFilterDropdownFilterValue(); @@ -120,6 +126,7 @@ export const ObjectFilterDropdownRecordSelect = ({ selectedIds: selectedRecordIds, objectNameSingular, limit: 10, + allowRequestsToTwentyIcons, }); const currentWorkspaceMemberSelectableItem: SelectableItem = { diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/SummaryCard.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/SummaryCard.tsx index bf095f91f5..3a14f815fa 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/components/SummaryCard.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/components/SummaryCard.tsx @@ -14,6 +14,7 @@ import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { FieldMetadataType } from '~/generated-metadata/graphql'; +import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; type SummaryCardProps = { objectNameSingular: string; @@ -37,6 +38,9 @@ export const SummaryCard = ({ fieldName: 'createdAt', }), ); + const allowRequestsToTwentyIcons = useRecoilValue( + allowRequestsToTwentyIconsState, + ); const { onUploadPicture, useUpdateOneObjectRecordMutation } = useRecordShowContainerActions({ @@ -49,6 +53,7 @@ export const SummaryCard = ({ const recordIdentifier = useRecoilValue( recordStoreIdentifierFamilySelector({ recordId: objectRecordId, + allowRequestsToTwentyIcons, }), ); diff --git a/packages/twenty-front/src/modules/object-record/record-store/states/selectors/recordStoreIdentifierSelector.ts b/packages/twenty-front/src/modules/object-record/record-store/states/selectors/recordStoreIdentifierSelector.ts index 93ecf69487..0c28fa6ea0 100644 --- a/packages/twenty-front/src/modules/object-record/record-store/states/selectors/recordStoreIdentifierSelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-store/states/selectors/recordStoreIdentifierSelector.ts @@ -8,7 +8,13 @@ import { uncapitalize } from 'twenty-shared/utils'; export const recordStoreIdentifierFamilySelector = selectorFamily({ key: 'recordStoreIdentifierFamilySelector', get: - ({ recordId }: { recordId: string }) => + ({ + recordId, + allowRequestsToTwentyIcons, + }: { + recordId: string; + allowRequestsToTwentyIcons: boolean; + }) => ({ get }) => { const recordFromStore = get(recordStoreFamilyState(recordId)); const objectNameSingular = uncapitalize( @@ -28,6 +34,7 @@ export const recordStoreIdentifierFamilySelector = selectorFamily({ return getObjectRecordIdentifier({ objectMetadataItem: objectMetadataItem, record: recordFromStore, + allowRequestsToTwentyIcons, }); }, }); diff --git a/packages/twenty-front/src/modules/object-record/record-store/states/selectors/recordStoreIdentifiersSelector.ts b/packages/twenty-front/src/modules/object-record/record-store/states/selectors/recordStoreIdentifiersSelector.ts index 438362b140..5fcf840034 100644 --- a/packages/twenty-front/src/modules/object-record/record-store/states/selectors/recordStoreIdentifiersSelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-store/states/selectors/recordStoreIdentifiersSelector.ts @@ -8,7 +8,13 @@ import { isDefined, uncapitalize } from 'twenty-shared/utils'; export const recordStoreIdentifiersFamilySelector = selectorFamily({ key: 'recordStoreIdentifiersFamilySelector', get: - ({ recordIds }: { recordIds: string[] }) => + ({ + recordIds, + allowRequestsToTwentyIcons, + }: { + recordIds: string[]; + allowRequestsToTwentyIcons: boolean; + }) => ({ get }) => { const objectMetadataItems = get(objectMetadataItemsState); @@ -30,6 +36,7 @@ export const recordStoreIdentifiersFamilySelector = selectorFamily({ return getObjectRecordIdentifier({ objectMetadataItem: objectMetadataItem, record: recordFromStore, + allowRequestsToTwentyIcons, }); }) .filter(isDefined); diff --git a/packages/twenty-front/src/modules/object-record/select/hooks/useRecordsForSelect.ts b/packages/twenty-front/src/modules/object-record/select/hooks/useRecordsForSelect.ts index ef8dbaa408..5a2f56d6bc 100644 --- a/packages/twenty-front/src/modules/object-record/select/hooks/useRecordsForSelect.ts +++ b/packages/twenty-front/src/modules/object-record/select/hooks/useRecordsForSelect.ts @@ -18,6 +18,7 @@ export const useRecordsForSelect = ({ limit, excludedRecordIds = [], objectNameSingular, + allowRequestsToTwentyIcons, }: { searchFilterText: string; sortOrder?: OrderBy; @@ -25,9 +26,11 @@ export const useRecordsForSelect = ({ limit?: number; excludedRecordIds?: string[]; objectNameSingular: string; + allowRequestsToTwentyIcons: boolean; }) => { const { mapToObjectRecordIdentifier } = useMapToObjectRecordIdentifier({ objectNameSingular, + allowRequestsToTwentyIcons, }); const filters = [ diff --git a/packages/twenty-front/src/modules/object-record/utils/getRecordChipGenerators.ts b/packages/twenty-front/src/modules/object-record/utils/getRecordChipGenerators.ts index f91f71d8fd..1184013180 100644 --- a/packages/twenty-front/src/modules/object-record/utils/getRecordChipGenerators.ts +++ b/packages/twenty-front/src/modules/object-record/utils/getRecordChipGenerators.ts @@ -16,6 +16,7 @@ import { FieldMetadataType } from '~/generated-metadata/graphql'; export const getRecordChipGenerators = ( objectMetadataItems: ObjectMetadataItem[], + allowRequestsToTwentyIcons?: boolean, ) => { const chipGeneratorPerObjectPerField: ChipGeneratorPerObjectNameSingularPerFieldName = {}; @@ -92,6 +93,7 @@ export const getRecordChipGenerators = ( objectMetadataItemToUse.nameSingular, record, imageIdentifierFieldMetadataToUse, + allowRequestsToTwentyIcons, ), avatarType, isLabelIdentifier, diff --git a/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx b/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx index 3c642e08ff..e4a79829b0 100644 --- a/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx +++ b/packages/twenty-front/src/modules/views/hooks/useComputeRecordRelationFilterLabelValue.tsx @@ -12,6 +12,8 @@ import { isDefined, jsonRelationFilterValueSchema, } from 'twenty-shared/utils'; +import { useRecoilValue } from 'recoil'; +import { allowRequestsToTwentyIconsState } from '@/client-config/states/allowRequestsToTwentyIcons'; type ObjectFilterDropdownRecordSelectProps = { recordFilter: RecordFilter; @@ -21,6 +23,10 @@ type ObjectFilterDropdownRecordSelectProps = { export const useComputeRecordRelationFilterLabelValue = ({ recordFilter, }: ObjectFilterDropdownRecordSelectProps) => { + const allowRequestsToTwentyIcons = useRecoilValue( + allowRequestsToTwentyIconsState, + ); + const { objectMetadataItems } = useObjectMetadataItems(); const { getRecordFilterChipLabelValue } = useGetRecordFilterChipLabelValue(); @@ -69,6 +75,7 @@ export const useComputeRecordRelationFilterLabelValue = ({ selectedIds: selectedRecordIds, objectNameSingular: relationObjectMetadataNameSingular, limit: 10, + allowRequestsToTwentyIcons, }); if (loading) { diff --git a/packages/twenty-front/src/testing/mock-data/config.ts b/packages/twenty-front/src/testing/mock-data/config.ts index ad6b2763d0..5fc1c46fbf 100644 --- a/packages/twenty-front/src/testing/mock-data/config.ts +++ b/packages/twenty-front/src/testing/mock-data/config.ts @@ -56,5 +56,6 @@ export const mockedClientConfig: ClientConfig = { isImapSmtpCaldavEnabled: false, isTwoFactorAuthenticationEnabled: false, isEmailingDomainsEnabled: false, + allowRequestsToTwentyIcons: true, isCloudflareIntegrationEnabled: false, }; diff --git a/packages/twenty-server/.env.example b/packages/twenty-server/.env.example index 4dd179de61..345c658285 100644 --- a/packages/twenty-server/.env.example +++ b/packages/twenty-server/.env.example @@ -83,3 +83,4 @@ FRONTEND_URL=http://localhost:3001 # ANALYTICS_ENABLED= # CLICKHOUSE_URL=http://default:clickhousePassword@localhost:8123/twenty # HTTP_TOOL_SAFE_MODE_ENABLED=true +# ALLOW_REQUESTS_TO_TWENTY_ICONS=true diff --git a/packages/twenty-server/src/engine/core-modules/client-config/client-config.controller.spec.ts b/packages/twenty-server/src/engine/core-modules/client-config/client-config.controller.spec.ts index 8695d96391..b6989cf995 100644 --- a/packages/twenty-server/src/engine/core-modules/client-config/client-config.controller.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/client-config/client-config.controller.spec.ts @@ -98,6 +98,7 @@ describe('ClientConfigController', () => { isImapSmtpCaldavEnabled: false, calendarBookingPageId: undefined, isTwoFactorAuthenticationEnabled: false, + allowRequestsToTwentyIcons: true, isCloudflareIntegrationEnabled: false, }; diff --git a/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts b/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts index d71d6f514f..39ab0114dd 100644 --- a/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts @@ -195,6 +195,9 @@ export class ClientConfig { @Field(() => Boolean) isImapSmtpCaldavEnabled: boolean; + @Field(() => Boolean) + allowRequestsToTwentyIcons: boolean; + @Field(() => String, { nullable: true }) calendarBookingPageId?: string; diff --git a/packages/twenty-server/src/engine/core-modules/client-config/services/client-config.service.ts b/packages/twenty-server/src/engine/core-modules/client-config/services/client-config.service.ts index 54a9976e62..01c9e3decf 100644 --- a/packages/twenty-server/src/engine/core-modules/client-config/services/client-config.service.ts +++ b/packages/twenty-server/src/engine/core-modules/client-config/services/client-config.service.ts @@ -195,6 +195,9 @@ export class ClientConfigService { isImapSmtpCaldavEnabled: this.twentyConfigService.get( 'IS_IMAP_SMTP_CALDAV_ENABLED', ), + allowRequestsToTwentyIcons: this.twentyConfigService.get( + 'ALLOW_REQUESTS_TO_TWENTY_ICONS', + ), calendarBookingPageId: isNonEmptyString(calendarBookingPageId) ? calendarBookingPageId : undefined, diff --git a/packages/twenty-server/src/engine/core-modules/search/__tests__/search.service.spec.ts b/packages/twenty-server/src/engine/core-modules/search/__tests__/search.service.spec.ts index e52d36423a..4ecfeb719c 100644 --- a/packages/twenty-server/src/engine/core-modules/search/__tests__/search.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/search/__tests__/search.service.spec.ts @@ -7,8 +7,8 @@ import { } from 'src/engine/core-modules/__mocks__/mockFlatObjectMetadatas'; import { FileService } from 'src/engine/core-modules/file/services/file.service'; import { SearchService } from 'src/engine/core-modules/search/services/search.service'; +import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; -import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; describe('SearchService', () => { let service: SearchService; @@ -18,8 +18,8 @@ describe('SearchService', () => { providers: [ SearchService, { provide: GlobalWorkspaceOrmManager, useValue: {} }, - { provide: WorkspaceCacheStorageService, useValue: {} }, { provide: FileService, useValue: {} }, + { provide: TwentyConfigService, useValue: { get: () => false } }, ], }).compile(); diff --git a/packages/twenty-server/src/engine/core-modules/search/services/search.service.ts b/packages/twenty-server/src/engine/core-modules/search/services/search.service.ts index 2163073304..5fe51669d9 100644 --- a/packages/twenty-server/src/engine/core-modules/search/services/search.service.ts +++ b/packages/twenty-server/src/engine/core-modules/search/services/search.service.ts @@ -33,6 +33,7 @@ import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-me import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config'; +import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; type LastRanks = { tsRankCD: number; tsRank: number }; @@ -48,6 +49,7 @@ export class SearchService { constructor( private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager, private readonly fileService: FileService, + private readonly twentyConfigService: TwentyConfigService, ) {} async getAllRecordsWithObjectMetadataItems({ @@ -383,7 +385,10 @@ export class SearchService { flatFieldMetadataMaps, ); - if (flatObjectMetadata.nameSingular === 'company') { + if ( + flatObjectMetadata.nameSingular === 'company' && + this.twentyConfigService.get('ALLOW_REQUESTS_TO_TWENTY_ICONS') + ) { return getLogoUrlFromDomainName(record.domainNamePrimaryLinkUrl) || ''; } diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts index 292c71b0c3..dd11813dd7 100644 --- a/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/config-variables.ts @@ -161,6 +161,14 @@ export class ConfigVariables { }) IS_IMAP_SMTP_CALDAV_ENABLED = true; + @ConfigVariablesMetadata({ + group: ConfigVariablesGroup.OTHER, + description: + "Enable or disable requests to twenty-icons to get companies' icons", + type: ConfigVariableType.BOOLEAN, + }) + ALLOW_REQUESTS_TO_TWENTY_ICONS = true; + @ConfigVariablesMetadata({ group: ConfigVariablesGroup.MICROSOFT_AUTH, description: 'Enable or disable Microsoft authentication',