diff --git a/.changeset/large-planes-destroy.md b/.changeset/large-planes-destroy.md new file mode 100644 index 0000000000000..e2541e1df68f5 --- /dev/null +++ b/.changeset/large-planes-destroy.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes create channel modal not validating federated access permission diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateChannelModal.tsx b/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateChannelModal.tsx index e9fabaea5f6d6..bcb0e0272a3fa 100644 --- a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateChannelModal.tsx +++ b/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateChannelModal.tsx @@ -21,7 +21,14 @@ import { ModalFooterControllers, } from '@rocket.chat/fuselage'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useSetting, useTranslation, useEndpoint, useToastMessageDispatch, usePermissionWithScopedRoles } from '@rocket.chat/ui-contexts'; +import { + useSetting, + useTranslation, + useEndpoint, + useToastMessageDispatch, + usePermissionWithScopedRoles, + usePermission, +} from '@rocket.chat/ui-contexts'; import type { ComponentProps, ReactElement } from 'react'; import { useId, useEffect, useMemo } from 'react'; import { useForm, Controller } from 'react-hook-form'; @@ -50,13 +57,19 @@ type CreateChannelModalPayload = { federated: boolean; }; -const getFederationHintKey = (licenseModule: ReturnType, featureToggle: boolean): TranslationKey => { - if (licenseModule.isPending || !licenseModule.data) { +const getFederationHintKey = (federationModule: boolean, featureToggle: boolean, federationAccessPermission: boolean): TranslationKey => { + if (!federationModule) { return 'error-this-is-a-premium-feature'; } + if (!featureToggle) { return 'Federation_Matrix_Federated_Description_disabled'; } + + if (!federationAccessPermission) { + return 'error-not-authorized-federation'; + } + return 'Federation_Matrix_Federated_Description'; }; @@ -68,14 +81,17 @@ const CreateChannelModal = ({ teamId = '', onClose, reload }: CreateChannelModal const e2eEnabled = useSetting('E2E_Enable'); const namesValidation = useSetting('UTF8_Channel_Names_Validation'); const allowSpecialNames = useSetting('UI_Allow_room_names_with_special_chars'); - const federationEnabled = useIsFederationEnabled(); const e2eEnabledForPrivateByDefault = useSetting('E2E_Enabled_Default_PrivateRooms') && e2eEnabled; const getEncryptedHint = useEncryptedRoomDescription('channel'); const channelNameRegex = useMemo(() => new RegExp(`^${namesValidation}$`), [namesValidation]); - const federatedModule = useHasLicenseModule('federation'); - const canUseFederation = !federatedModule.isPending && federatedModule.data && federationEnabled; + + const federationEnabled = useIsFederationEnabled(); + const { data: federationModule = false } = useHasLicenseModule('federation'); + const federationAccessPermission = usePermission('access-federation'); + const canUseFederation = federationModule && federationEnabled && federationAccessPermission; + const federationFieldHint = getFederationHintKey(federationModule, federationEnabled, federationAccessPermission); const channelNameExists = useEndpoint('GET', '/v1/rooms.nameExists'); const createChannel = useEndpoint('POST', '/v1/channels.create'); @@ -303,7 +319,7 @@ const CreateChannelModal = ({ teamId = '', onClose, reload }: CreateChannelModal )} /> - {t(getFederationHintKey(federatedModule, federationEnabled))} + {t(federationFieldHint)} diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/testCreateChannelModal.tsx b/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/testCreateChannelModal.tsx index e2971bd6fa36b..ccaac78a99426 100644 --- a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/testCreateChannelModal.tsx +++ b/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/testCreateChannelModal.tsx @@ -3,177 +3,267 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import type CreateChannelModal2Component from './CreateChannelModal'; +import { createFakeLicenseInfo } from '../../../../tests/mocks/data'; import type CreateChannelModalComponent from '../../../sidebar/header/CreateChannel'; // eslint-disable-next-line @typescript-eslint/naming-convention export function testCreateChannelModal(CreateChannelModal: typeof CreateChannelModalComponent | typeof CreateChannelModal2Component) { describe('CreateChannelModal', () => { - it('should render with encryption option disabled and set to off when E2E_Enable=false and E2E_Enabled_Default_PrivateRooms=false', async () => { - render( null} />, { - wrapper: mockAppRoot().withSetting('E2E_Enable', false).withSetting('E2E_Enabled_Default_PrivateRooms', false).build(), + describe('Encryption', () => { + it('should render with encryption option disabled and set to off when E2E_Enable=false and E2E_Enabled_Default_PrivateRooms=false', async () => { + render( null} />, { + wrapper: mockAppRoot().withSetting('E2E_Enable', false).withSetting('E2E_Enabled_Default_PrivateRooms', false).build(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; + expect(encrypted).toBeInTheDocument(); + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeDisabled(); }); - await userEvent.click(screen.getByText('Advanced_settings')); + it('should render with encryption option enabled and set to off when E2E_Enable=true and E2E_Enabled_Default_PrivateRooms=false', async () => { + render( null} />, { + wrapper: mockAppRoot().withSetting('E2E_Enable', true).withSetting('E2E_Enabled_Default_PrivateRooms', false).build(), + }); - const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; - expect(encrypted).toBeInTheDocument(); - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeDisabled(); - }); + await userEvent.click(screen.getByText('Advanced_settings')); - it('should render with encryption option enabled and set to off when E2E_Enable=true and E2E_Enabled_Default_PrivateRooms=false', async () => { - render( null} />, { - wrapper: mockAppRoot().withSetting('E2E_Enable', true).withSetting('E2E_Enabled_Default_PrivateRooms', false).build(), + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; + expect(encrypted).toBeInTheDocument(); + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeEnabled(); }); - await userEvent.click(screen.getByText('Advanced_settings')); + it('should render with encryption option disabled and set to off when E2E_Enable=false and E2E_Enabled_Default_PrivateRooms=true', async () => { + render( null} />, { + wrapper: mockAppRoot().withSetting('E2E_Enable', false).withSetting('E2E_Enabled_Default_PrivateRooms', true).build(), + }); - const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; - expect(encrypted).toBeInTheDocument(); - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeEnabled(); - }); + await userEvent.click(screen.getByText('Advanced_settings')); - it('should render with encryption option disabled and set to off when E2E_Enable=false and E2E_Enabled_Default_PrivateRooms=true', async () => { - render( null} />, { - wrapper: mockAppRoot().withSetting('E2E_Enable', false).withSetting('E2E_Enabled_Default_PrivateRooms', true).build(), - }); + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; + expect(encrypted).toBeInTheDocument(); - await userEvent.click(screen.getByText('Advanced_settings')); + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeDisabled(); + }); - const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; - expect(encrypted).toBeInTheDocument(); + it('should render with encryption option enabled and set to on when E2E_Enable=true and E2E_Enabled_Default_PrivateRooms=True', async () => { + render( null} />, { + wrapper: mockAppRoot().withSetting('E2E_Enable', true).withSetting('E2E_Enabled_Default_PrivateRooms', true).build(), + }); - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeDisabled(); - }); + await userEvent.click(screen.getByText('Advanced_settings')); - it('should render with encryption option enabled and set to on when E2E_Enable=true and E2E_Enabled_Default_PrivateRooms=True', async () => { - render( null} />, { - wrapper: mockAppRoot().withSetting('E2E_Enable', true).withSetting('E2E_Enabled_Default_PrivateRooms', true).build(), + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; + expect(encrypted).toBeChecked(); + expect(encrypted).toBeEnabled(); }); - await userEvent.click(screen.getByText('Advanced_settings')); + it('when Private goes ON → OFF: forces Encrypted OFF and disables it (E2E_Enable=true, E2E_Enabled_Default_PrivateRooms=true)', async () => { + render( null} />, { + wrapper: mockAppRoot().withSetting('E2E_Enable', true).withSetting('E2E_Enabled_Default_PrivateRooms', true).build(), + }); - const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; - expect(encrypted).toBeChecked(); - expect(encrypted).toBeEnabled(); - }); + await userEvent.click(screen.getByText('Advanced_settings')); - it('when Private goes ON → OFF: forces Encrypted OFF and disables it (E2E_Enable=true, E2E_Enabled_Default_PrivateRooms=true)', async () => { - render( null} />, { - wrapper: mockAppRoot().withSetting('E2E_Enable', true).withSetting('E2E_Enabled_Default_PrivateRooms', true).build(), + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; + const priv = screen.getByLabelText('Private') as HTMLInputElement; + + // initial: private=true, encrypted ON and enabled + expect(priv).toBeChecked(); + expect(encrypted).toBeChecked(); + expect(encrypted).toBeEnabled(); + + // Private ON -> OFF: encrypted must become OFF and disabled + await userEvent.click(priv); + expect(priv).not.toBeChecked(); + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeDisabled(); }); - await userEvent.click(screen.getByText('Advanced_settings')); + it('when Private goes OFF → ON: keeps Encrypted OFF but re-enables it (E2E_Enable=true, E2E_Enabled_Default_PrivateRooms=true)', async () => { + render( null} />, { + wrapper: mockAppRoot().withSetting('E2E_Enable', true).withSetting('E2E_Enabled_Default_PrivateRooms', true).build(), + }); - const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; - const priv = screen.getByLabelText('Private') as HTMLInputElement; + await userEvent.click(screen.getByText('Advanced_settings')); - // initial: private=true, encrypted ON and enabled - expect(priv).toBeChecked(); - expect(encrypted).toBeChecked(); - expect(encrypted).toBeEnabled(); + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; + const priv = screen.getByLabelText('Private') as HTMLInputElement; - // Private ON -> OFF: encrypted must become OFF and disabled - await userEvent.click(priv); - expect(priv).not.toBeChecked(); - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeDisabled(); - }); + // turn private OFF to simulate user path from non-private + await userEvent.click(priv); + expect(priv).not.toBeChecked(); + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeDisabled(); - it('when Private goes OFF → ON: keeps Encrypted OFF but re-enables it (E2E_Enable=true, E2E_Enabled_Default_PrivateRooms=true)', async () => { - render( null} />, { - wrapper: mockAppRoot().withSetting('E2E_Enable', true).withSetting('E2E_Enabled_Default_PrivateRooms', true).build(), + // turn private back ON -> encrypted should remain OFF but become enabled + await userEvent.click(priv); + expect(priv).toBeChecked(); + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeEnabled(); }); - await userEvent.click(screen.getByText('Advanced_settings')); + it('private room: toggling Broadcast on/off does not change or disable Encrypted', async () => { + render( null} />, { + wrapper: mockAppRoot().withSetting('E2E_Enable', true).withSetting('E2E_Enabled_Default_PrivateRooms', true).build(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; + const broadcast = screen.getByLabelText('Broadcast') as HTMLInputElement; + const priv = screen.getByLabelText('Private') as HTMLInputElement; + + expect(priv).toBeChecked(); + expect(encrypted).toBeChecked(); + expect(encrypted).toBeEnabled(); + expect(broadcast).not.toBeChecked(); + + // Broadcast: OFF -> ON (Encrypted unchanged + enabled) + await userEvent.click(broadcast); + expect(broadcast).toBeChecked(); + expect(encrypted).toBeChecked(); + expect(encrypted).toBeEnabled(); + + // Broadcast: ON -> OFF (Encrypted unchanged + enabled) + await userEvent.click(broadcast); + expect(broadcast).not.toBeChecked(); + expect(encrypted).toBeChecked(); + expect(encrypted).toBeEnabled(); + + // User can still toggle Encrypted freely while Broadcast is OFF + await userEvent.click(encrypted); + expect(encrypted).not.toBeChecked(); + + // User can still toggle Encrypted freely while Broadcast is ON + await userEvent.click(broadcast); + expect(broadcast).toBeChecked(); + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeEnabled(); + }); - const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; - const priv = screen.getByLabelText('Private') as HTMLInputElement; + it('non-private room: Encrypted remains OFF and disabled regardless of Broadcast state', async () => { + render( null} />, { + wrapper: mockAppRoot().withSetting('E2E_Enable', true).withSetting('E2E_Enabled_Default_PrivateRooms', true).build(), + }); - // turn private OFF to simulate user path from non-private - await userEvent.click(priv); - expect(priv).not.toBeChecked(); - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeDisabled(); + await userEvent.click(screen.getByText('Advanced_settings')); - // turn private back ON -> encrypted should remain OFF but become enabled - await userEvent.click(priv); - expect(priv).toBeChecked(); - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeEnabled(); - }); + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; + const broadcast = screen.getByLabelText('Broadcast') as HTMLInputElement; + const priv = screen.getByLabelText('Private') as HTMLInputElement; - it('private room: toggling Broadcast on/off does not change or disable Encrypted', async () => { - render( null} />, { - wrapper: mockAppRoot().withSetting('E2E_Enable', true).withSetting('E2E_Enabled_Default_PrivateRooms', true).build(), - }); + // Switch to non-private + await userEvent.click(priv); + expect(priv).not.toBeChecked(); - await userEvent.click(screen.getByText('Advanced_settings')); - - const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; - const broadcast = screen.getByLabelText('Broadcast') as HTMLInputElement; - const priv = screen.getByLabelText('Private') as HTMLInputElement; - - expect(priv).toBeChecked(); - expect(encrypted).toBeChecked(); - expect(encrypted).toBeEnabled(); - expect(broadcast).not.toBeChecked(); - - // Broadcast: OFF -> ON (Encrypted unchanged + enabled) - await userEvent.click(broadcast); - expect(broadcast).toBeChecked(); - expect(encrypted).toBeChecked(); - expect(encrypted).toBeEnabled(); - - // Broadcast: ON -> OFF (Encrypted unchanged + enabled) - await userEvent.click(broadcast); - expect(broadcast).not.toBeChecked(); - expect(encrypted).toBeChecked(); - expect(encrypted).toBeEnabled(); - - // User can still toggle Encrypted freely while Broadcast is OFF - await userEvent.click(encrypted); - expect(encrypted).not.toBeChecked(); - - // User can still toggle Encrypted freely while Broadcast is ON - await userEvent.click(broadcast); - expect(broadcast).toBeChecked(); - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeEnabled(); - }); + // Encrypted must be OFF + disabled (non-private cannot be encrypted) + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeDisabled(); - it('non-private room: Encrypted remains OFF and disabled regardless of Broadcast state', async () => { - render( null} />, { - wrapper: mockAppRoot().withSetting('E2E_Enable', true).withSetting('E2E_Enabled_Default_PrivateRooms', true).build(), - }); + // Broadcast: OFF -> ON (Encrypted stays OFF + disabled) + await userEvent.click(broadcast); + expect(broadcast).toBeChecked(); + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeDisabled(); - await userEvent.click(screen.getByText('Advanced_settings')); - - const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; - const broadcast = screen.getByLabelText('Broadcast') as HTMLInputElement; - const priv = screen.getByLabelText('Private') as HTMLInputElement; + // Broadcast: ON -> OFF (Encrypted still OFF + disabled) + await userEvent.click(broadcast); + expect(broadcast).not.toBeChecked(); + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeDisabled(); + }); + }); - // Switch to non-private - await userEvent.click(priv); - expect(priv).not.toBeChecked(); + describe('Federation', () => { + it('should render with federated option disabled when user lacks license module', async () => { + render( null} />, { + wrapper: mockAppRoot().build(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + const federated = screen.getByLabelText('Federation_Matrix_Federated'); + expect(federated).toHaveAccessibleDescription('error-this-is-a-premium-feature'); + expect(federated).toBeInTheDocument(); + expect(federated).not.toBeChecked(); + expect(federated).toBeDisabled(); + }); - // Encrypted must be OFF + disabled (non-private cannot be encrypted) - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeDisabled(); + it('should render with federated option disabled if the feature is disabled for workspaces', async () => { + render( null} />, { + wrapper: mockAppRoot() + .withJohnDoe() + .withSetting('Federation_Matrix_enabled', false) + .withEndpoint( + 'GET', + '/v1/licenses.info', + jest.fn().mockImplementation(() => ({ + license: createFakeLicenseInfo({ activeModules: ['federation'] }), + })), + ) + .build(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + const federated = screen.getByLabelText('Federation_Matrix_Federated'); + + expect(federated).toBeInTheDocument(); + expect(federated).not.toBeChecked(); + expect(federated).toBeDisabled(); + expect(federated).toHaveAccessibleDescription('Federation_Matrix_Federated_Description_disabled'); + }); - // Broadcast: OFF -> ON (Encrypted stays OFF + disabled) - await userEvent.click(broadcast); - expect(broadcast).toBeChecked(); - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeDisabled(); + it('should render with federated option disabled when user lacks permission', async () => { + render( null} />, { + wrapper: mockAppRoot() + .withJohnDoe() + .withSetting('Federation_Matrix_enabled', true) + .withEndpoint( + 'GET', + '/v1/licenses.info', + jest.fn().mockImplementation(() => ({ + license: createFakeLicenseInfo({ activeModules: ['federation'] }), + })), + ) + .build(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + const federated = screen.getByLabelText('Federation_Matrix_Federated'); + + expect(federated).toBeInTheDocument(); + expect(federated).not.toBeChecked(); + expect(federated).toBeDisabled(); + expect(federated).toHaveAccessibleDescription('error-not-authorized-federation'); + }); - // Broadcast: ON -> OFF (Encrypted still OFF + disabled) - await userEvent.click(broadcast); - expect(broadcast).not.toBeChecked(); - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeDisabled(); + it('should render with federated option enabled when user has license module, permission and feature enabled', async () => { + render( null} />, { + wrapper: mockAppRoot() + .withJohnDoe() + .withSetting('Federation_Matrix_enabled', true) + .withPermission('access-federation') + .withEndpoint( + 'GET', + '/v1/licenses.info', + jest.fn().mockImplementation(() => ({ + license: createFakeLicenseInfo({ activeModules: ['federation'] }), + })), + ) + .build(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + const federated = screen.getByLabelText('Federation_Matrix_Federated'); + expect(federated).toBeInTheDocument(); + expect(federated).not.toBeChecked(); + expect(federated).not.toBeDisabled(); + expect(federated).toHaveAccessibleDescription('Federation_Matrix_Federated_Description'); + }); }); }); } diff --git a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx b/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx index 9671e4e83b527..f959c6be10620 100644 --- a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx +++ b/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx @@ -22,7 +22,14 @@ import { ModalFooterControllers, } from '@rocket.chat/fuselage'; import type { TranslationKey } from '@rocket.chat/ui-contexts'; -import { useSetting, useTranslation, useEndpoint, useToastMessageDispatch, usePermissionWithScopedRoles } from '@rocket.chat/ui-contexts'; +import { + useSetting, + useTranslation, + useEndpoint, + useToastMessageDispatch, + usePermissionWithScopedRoles, + usePermission, +} from '@rocket.chat/ui-contexts'; import type { ComponentProps, ReactElement } from 'react'; import { useId, useEffect, useMemo } from 'react'; import { useForm, Controller } from 'react-hook-form'; @@ -52,13 +59,19 @@ type CreateChannelModalPayload = { federated: boolean; }; -const getFederationHintKey = (licenseModule: ReturnType, featureToggle: boolean): TranslationKey => { - if (licenseModule.isPending || !licenseModule.data) { +const getFederationHintKey = (licenseModule: boolean, featureToggle: boolean, federationAccessPermission: boolean): TranslationKey => { + if (!licenseModule) { return 'error-this-is-a-premium-feature'; } + if (!featureToggle) { return 'Federation_Matrix_Federated_Description_disabled'; } + + if (!federationAccessPermission) { + return 'error-not-authorized-federation'; + } + return 'Federation_Matrix_Federated_Description'; }; @@ -71,15 +84,17 @@ const CreateChannelModal = ({ teamId = '', mainRoom, onClose, reload }: CreateCh const namesValidation = useSetting('UTF8_Channel_Names_Validation'); const allowSpecialNames = useSetting('UI_Allow_room_names_with_special_chars'); - const federationEnabled = useIsFederationEnabled(); - const e2eEnabledForPrivateByDefault = useSetting('E2E_Enabled_Default_PrivateRooms') && e2eEnabled; const getEncryptedHint = useEncryptedRoomDescription('channel'); const channelNameRegex = useMemo(() => new RegExp(`^${namesValidation}$`), [namesValidation]); - const federatedModule = useHasLicenseModule('federation'); - const canUseFederation = !federatedModule.isPending && federatedModule.data && federationEnabled; + + const federationEnabled = useIsFederationEnabled(); + const { data: federationModule = false } = useHasLicenseModule('federation'); + const federationAccessPermission = usePermission('access-federation'); + const canUseFederation = federationModule && federationEnabled && federationAccessPermission; + const federationFieldHint = getFederationHintKey(federationModule, federationEnabled, federationAccessPermission); const channelNameExists = useEndpoint('GET', '/v1/rooms.nameExists'); const createChannel = useEndpoint('POST', '/v1/channels.create'); @@ -304,7 +319,7 @@ const CreateChannelModal = ({ teamId = '', mainRoom, onClose, reload }: CreateCh )} /> - {t(getFederationHintKey(federatedModule, federationEnabled))} + {t(federationFieldHint)} diff --git a/apps/meteor/client/sidebar/header/CreateChannel/__snapshots__/CreateChannelModal.spec.tsx.snap b/apps/meteor/client/sidebar/header/CreateChannel/__snapshots__/CreateChannelModal.spec.tsx.snap index a2406adcb30b1..c9784408a1b7a 100644 --- a/apps/meteor/client/sidebar/header/CreateChannel/__snapshots__/CreateChannelModal.spec.tsx.snap +++ b/apps/meteor/client/sidebar/header/CreateChannel/__snapshots__/CreateChannelModal.spec.tsx.snap @@ -4,7 +4,7 @@ exports[`renders Default without crashing 1`] = `

Create_channel

@@ -57,7 +57,7 @@ exports[`renders Default without crashing 1`] = ` >