diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateChannelModal.spec.tsx b/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateChannelModal.spec.tsx deleted file mode 100644 index 81603cdeeca54..0000000000000 --- a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateChannelModal.spec.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { testCreateChannelModal } from './testCreateChannelModal'; -import CreateChannelModalComponent from '../../../sidebar/header/CreateChannel'; - -jest.mock('../../../lib/utils/goToRoomById', () => ({ - goToRoomById: jest.fn(), -})); - -testCreateChannelModal(CreateChannelModalComponent); diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/testCreateChannelModal.tsx b/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/testCreateChannelModal.tsx deleted file mode 100644 index ccaac78a99426..0000000000000 --- a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/testCreateChannelModal.tsx +++ /dev/null @@ -1,269 +0,0 @@ -import { mockAppRoot } from '@rocket.chat/mock-providers'; -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', () => { - 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(); - }); - - 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(), - }); - - await userEvent.click(screen.getByText('Advanced_settings')); - - const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; - expect(encrypted).toBeInTheDocument(); - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeEnabled(); - }); - - 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(), - }); - - await userEvent.click(screen.getByText('Advanced_settings')); - - const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; - expect(encrypted).toBeInTheDocument(); - - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeDisabled(); - }); - - 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(), - }); - - await userEvent.click(screen.getByText('Advanced_settings')); - - const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; - expect(encrypted).toBeChecked(); - expect(encrypted).toBeEnabled(); - }); - - 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(), - }); - - await userEvent.click(screen.getByText('Advanced_settings')); - - 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(); - }); - - 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(), - }); - - await userEvent.click(screen.getByText('Advanced_settings')); - - const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; - const priv = screen.getByLabelText('Private') as HTMLInputElement; - - // 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(); - - // 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(); - }); - - 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(); - }); - - 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(), - }); - - 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; - - // Switch to non-private - await userEvent.click(priv); - expect(priv).not.toBeChecked(); - - // Encrypted must be OFF + disabled (non-private cannot be encrypted) - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeDisabled(); - - // Broadcast: OFF -> ON (Encrypted stays OFF + disabled) - await userEvent.click(broadcast); - expect(broadcast).toBeChecked(); - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeDisabled(); - - // Broadcast: ON -> OFF (Encrypted still OFF + disabled) - await userEvent.click(broadcast); - expect(broadcast).not.toBeChecked(); - expect(encrypted).not.toBeChecked(); - expect(encrypted).toBeDisabled(); - }); - }); - - 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(); - }); - - 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'); - }); - - 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'); - }); - - 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/components/CreateDiscussion/CreateDiscussion.tsx b/apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx index 438efbcd3e957..7aae6736909b5 100644 --- a/apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx +++ b/apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx @@ -27,10 +27,10 @@ import type { ReactElement } from 'react'; import { useForm, Controller } from 'react-hook-form'; import { goToRoomById } from '../../lib/utils/goToRoomById'; +import { useEncryptedRoomDescription } from '../../navbar/NavBarPagesGroup/actions/useEncryptedRoomDescription'; import RoomAutoComplete from '../RoomAutoComplete'; import UserAutoCompleteMultiple from '../UserAutoCompleteMultiple'; import DefaultParentRoomField from './DefaultParentRoomField'; -import { useEncryptedRoomDescription } from '../../NavBarV2/NavBarPagesGroup/actions/useEncryptedRoomDescription'; type CreateDiscussionFormValues = { name: string; diff --git a/apps/meteor/client/components/SidebarTogglerV2/SidebarToggler.tsx b/apps/meteor/client/components/SidebarToggler/SidebarToggler.tsx similarity index 100% rename from apps/meteor/client/components/SidebarTogglerV2/SidebarToggler.tsx rename to apps/meteor/client/components/SidebarToggler/SidebarToggler.tsx diff --git a/apps/meteor/client/components/SidebarTogglerV2/SidebarTogglerBadge.tsx b/apps/meteor/client/components/SidebarToggler/SidebarTogglerBadge.tsx similarity index 100% rename from apps/meteor/client/components/SidebarTogglerV2/SidebarTogglerBadge.tsx rename to apps/meteor/client/components/SidebarToggler/SidebarTogglerBadge.tsx diff --git a/apps/meteor/client/components/SidebarTogglerV2/SidebarTogglerButton.stories.tsx b/apps/meteor/client/components/SidebarToggler/SidebarTogglerButton.stories.tsx similarity index 100% rename from apps/meteor/client/components/SidebarTogglerV2/SidebarTogglerButton.stories.tsx rename to apps/meteor/client/components/SidebarToggler/SidebarTogglerButton.stories.tsx diff --git a/apps/meteor/client/components/SidebarTogglerV2/SidebarTogglerButton.tsx b/apps/meteor/client/components/SidebarToggler/SidebarTogglerButton.tsx similarity index 100% rename from apps/meteor/client/components/SidebarTogglerV2/SidebarTogglerButton.tsx rename to apps/meteor/client/components/SidebarToggler/SidebarTogglerButton.tsx diff --git a/apps/meteor/client/components/SidebarToggler/index.ts b/apps/meteor/client/components/SidebarToggler/index.ts new file mode 100644 index 0000000000000..c0319dc75c38b --- /dev/null +++ b/apps/meteor/client/components/SidebarToggler/index.ts @@ -0,0 +1 @@ +export { default } from './SidebarToggler'; diff --git a/apps/meteor/client/components/SidebarTogglerV2/index.ts b/apps/meteor/client/components/SidebarTogglerV2/index.ts deleted file mode 100644 index 4698327ba2938..0000000000000 --- a/apps/meteor/client/components/SidebarTogglerV2/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as SidebarTogglerV2 } from './SidebarToggler'; diff --git a/apps/meteor/client/definitions/global.d.ts b/apps/meteor/client/definitions/global.d.ts index b19bfe2db3b06..3cefc38cc8362 100644 --- a/apps/meteor/client/definitions/global.d.ts +++ b/apps/meteor/client/definitions/global.d.ts @@ -26,6 +26,7 @@ declare global { mozAudioContext?: AudioContext; /** @deprecated use `window.AudioContext` */ webkitAudioContext?: AudioContext; + opera?: string; } interface Navigator { @@ -57,6 +58,9 @@ declare global { onSuccess?: (stream: MediaStream) => void, onError?: (error: any) => void, ) => void; + userAgentData?: { + mobile: boolean; + }; } interface RTCPeerConnection { diff --git a/apps/meteor/client/NavBarV2/NavBar.tsx b/apps/meteor/client/navbar/NavBar.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBar.tsx rename to apps/meteor/client/navbar/NavBar.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarControls/NavBarControlsMenu.tsx b/apps/meteor/client/navbar/NavBarControls/NavBarControlsMenu.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarControls/NavBarControlsMenu.tsx rename to apps/meteor/client/navbar/NavBarControls/NavBarControlsMenu.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarControls/NavBarControlsSection.tsx b/apps/meteor/client/navbar/NavBarControls/NavBarControlsSection.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarControls/NavBarControlsSection.tsx rename to apps/meteor/client/navbar/NavBarControls/NavBarControlsSection.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarControls/NavBarControlsWithData.tsx b/apps/meteor/client/navbar/NavBarControls/NavBarControlsWithData.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarControls/NavBarControlsWithData.tsx rename to apps/meteor/client/navbar/NavBarControls/NavBarControlsWithData.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarNavigation.tsx b/apps/meteor/client/navbar/NavBarNavigation.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarNavigation.tsx rename to apps/meteor/client/navbar/NavBarNavigation.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/NavBarItemOmnichannelContact.tsx b/apps/meteor/client/navbar/NavBarOmnichannelGroup/NavBarItemOmnichannelContact.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/NavBarItemOmnichannelContact.tsx rename to apps/meteor/client/navbar/NavBarOmnichannelGroup/NavBarItemOmnichannelContact.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/NavBarItemOmnichannelLivechatToggle.tsx b/apps/meteor/client/navbar/NavBarOmnichannelGroup/NavBarItemOmnichannelLivechatToggle.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/NavBarItemOmnichannelLivechatToggle.tsx rename to apps/meteor/client/navbar/NavBarOmnichannelGroup/NavBarItemOmnichannelLivechatToggle.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/NavBarItemOmnichannelQueue.tsx b/apps/meteor/client/navbar/NavBarOmnichannelGroup/NavBarItemOmnichannelQueue.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/NavBarItemOmnichannelQueue.tsx rename to apps/meteor/client/navbar/NavBarOmnichannelGroup/NavBarItemOmnichannelQueue.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/NavBarOmnichannelGroup.tsx b/apps/meteor/client/navbar/NavBarOmnichannelGroup/NavBarOmnichannelGroup.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/NavBarOmnichannelGroup.tsx rename to apps/meteor/client/navbar/NavBarOmnichannelGroup/NavBarOmnichannelGroup.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/hooks/useOmnichannelContactAction.ts b/apps/meteor/client/navbar/NavBarOmnichannelGroup/hooks/useOmnichannelContactAction.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/hooks/useOmnichannelContactAction.ts rename to apps/meteor/client/navbar/NavBarOmnichannelGroup/hooks/useOmnichannelContactAction.ts diff --git a/apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/hooks/useOmnichannelLivechatToggle.ts b/apps/meteor/client/navbar/NavBarOmnichannelGroup/hooks/useOmnichannelLivechatToggle.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/hooks/useOmnichannelLivechatToggle.ts rename to apps/meteor/client/navbar/NavBarOmnichannelGroup/hooks/useOmnichannelLivechatToggle.ts diff --git a/apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/hooks/useOmnichannelQueueAction.ts b/apps/meteor/client/navbar/NavBarOmnichannelGroup/hooks/useOmnichannelQueueAction.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/hooks/useOmnichannelQueueAction.ts rename to apps/meteor/client/navbar/NavBarOmnichannelGroup/hooks/useOmnichannelQueueAction.ts diff --git a/apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/index.ts b/apps/meteor/client/navbar/NavBarOmnichannelGroup/index.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarOmnichannelGroup/index.ts rename to apps/meteor/client/navbar/NavBarOmnichannelGroup/index.ts diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarItemCreateNew.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemCreateNew.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarItemCreateNew.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemCreateNew.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarItemDirectoryPage.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemDirectoryPage.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarItemDirectoryPage.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemDirectoryPage.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarItemHomePage.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemHomePage.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarItemHomePage.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemHomePage.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarItemMarketPlaceMenu.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemMarketPlaceMenu.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarItemMarketPlaceMenu.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemMarketPlaceMenu.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarItemSort.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemSort.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarItemSort.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/NavBarItemSort.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarPagesGroup.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/NavBarPagesGroup.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarPagesGroup.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/NavBarPagesGroup.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarPagesStackMenu.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/NavBarPagesStackMenu.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarPagesStackMenu.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/NavBarPagesStackMenu.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateTeamModal.spec.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/actions/CreateChannelModal.spec.tsx similarity index 55% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateTeamModal.spec.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/actions/CreateChannelModal.spec.tsx index 8e7c4a3a5bad5..c64e282aabc3c 100644 --- a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateTeamModal.spec.tsx +++ b/apps/meteor/client/navbar/NavBarPagesGroup/actions/CreateChannelModal.spec.tsx @@ -2,57 +2,49 @@ import { mockAppRoot } from '@rocket.chat/mock-providers'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import CreateTeamModal from './CreateTeamModal'; -import CreateTeamModalOld from '../../../sidebar/header/CreateTeam'; +import CreateChannelModal from './CreateChannelModal'; +import { createFakeLicenseInfo } from '../../../../tests/mocks/data'; jest.mock('../../../lib/utils/goToRoomById', () => ({ goToRoomById: jest.fn(), })); -type CreateTeamModalComponentType = typeof CreateTeamModal | typeof CreateTeamModalOld; -// eslint-disable-next-line @typescript-eslint/naming-convention - -describe.each([ - ['CreateTeamModal', CreateTeamModalOld], - ['CreateTeamModal in NavbarV2', CreateTeamModal], -] as const)( - '%s', - // eslint-disable-next-line @typescript-eslint/naming-convention - (_name: string, CreateTeamModalComponent: CreateTeamModalComponentType) => { +describe('CreateChannelModal', () => { + 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} />, { + 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('Teams_New_Encrypted_Label') as HTMLInputElement; + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; expect(encrypted).toBeInTheDocument(); expect(encrypted).not.toBeChecked(); expect(encrypted).toBeDisabled(); }); it('should render with encryption option enabled and set to off when E2E_Enable=true and E2E_Enabled_Default_PrivateRooms=false', async () => { - render( null} />, { + render( null} />, { wrapper: mockAppRoot().withSetting('E2E_Enable', true).withSetting('E2E_Enabled_Default_PrivateRooms', false).build(), }); await userEvent.click(screen.getByText('Advanced_settings')); - const encrypted = screen.getByLabelText('Teams_New_Encrypted_Label') as HTMLInputElement; + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; expect(encrypted).toBeInTheDocument(); expect(encrypted).not.toBeChecked(); expect(encrypted).toBeEnabled(); }); it('should render with encryption option disabled and set to off when E2E_Enable=false and E2E_Enabled_Default_PrivateRooms=true', async () => { - render( null} />, { + render( null} />, { wrapper: mockAppRoot().withSetting('E2E_Enable', false).withSetting('E2E_Enabled_Default_PrivateRooms', true).build(), }); await userEvent.click(screen.getByText('Advanced_settings')); - const encrypted = screen.getByLabelText('Teams_New_Encrypted_Label') as HTMLInputElement; + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; expect(encrypted).toBeInTheDocument(); expect(encrypted).not.toBeChecked(); @@ -60,26 +52,26 @@ describe.each([ }); it('should render with encryption option enabled and set to on when E2E_Enable=true and E2E_Enabled_Default_PrivateRooms=True', async () => { - render( null} />, { + 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('Teams_New_Encrypted_Label') as HTMLInputElement; + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; expect(encrypted).toBeChecked(); expect(encrypted).toBeEnabled(); }); it('when Private goes ON → OFF: forces Encrypted OFF and disables it (E2E_Enable=true, E2E_Enabled_Default_PrivateRooms=true)', async () => { - render( null} />, { + 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('Teams_New_Encrypted_Label') as HTMLInputElement; - const priv = screen.getByLabelText('Teams_New_Private_Label') as HTMLInputElement; + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; + const priv = screen.getByLabelText('Private') as HTMLInputElement; // initial: private=true, encrypted ON and enabled expect(priv).toBeChecked(); @@ -94,14 +86,14 @@ describe.each([ }); it('when Private goes OFF → ON: keeps Encrypted OFF but re-enables it (E2E_Enable=true, E2E_Enabled_Default_PrivateRooms=true)', async () => { - render( null} />, { + 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('Teams_New_Encrypted_Label') as HTMLInputElement; - const priv = screen.getByLabelText('Teams_New_Private_Label') as HTMLInputElement; + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; + const priv = screen.getByLabelText('Private') as HTMLInputElement; // turn private OFF to simulate user path from non-private await userEvent.click(priv); @@ -116,16 +108,16 @@ describe.each([ expect(encrypted).toBeEnabled(); }); - it('private team: toggling Broadcast on/off does not change or disable Encrypted', async () => { - render( null} />, { + 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('Teams_New_Encrypted_Label') as HTMLInputElement; - const broadcast = screen.getByLabelText('Teams_New_Broadcast_Label') as HTMLInputElement; - const priv = screen.getByLabelText('Teams_New_Private_Label') as HTMLInputElement; + 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(); @@ -155,16 +147,16 @@ describe.each([ expect(encrypted).toBeEnabled(); }); - it('non-private team: Encrypted remains OFF and disabled regardless of Broadcast state', async () => { - render( null} />, { + 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(), }); await userEvent.click(screen.getByText('Advanced_settings')); - const encrypted = screen.getByLabelText('Teams_New_Encrypted_Label') as HTMLInputElement; - const broadcast = screen.getByLabelText('Teams_New_Broadcast_Label') as HTMLInputElement; - const priv = screen.getByLabelText('Teams_New_Private_Label') as HTMLInputElement; + const encrypted = screen.getByLabelText('Encrypted') as HTMLInputElement; + const broadcast = screen.getByLabelText('Broadcast') as HTMLInputElement; + const priv = screen.getByLabelText('Private') as HTMLInputElement; // Switch to non-private await userEvent.click(priv); @@ -186,73 +178,92 @@ describe.each([ expect(encrypted).not.toBeChecked(); expect(encrypted).toBeDisabled(); }); + }); - it('should disable and turn on ReadOnly toggle when Broadcast is ON and no set-readonly permission', async () => { - render( null} />, { + 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 broadcast = screen.getByLabelText('Teams_New_Broadcast_Label') as HTMLInputElement; - const readOnly = screen.getByLabelText('Teams_New_Read_only_Label') as HTMLInputElement; - - expect(readOnly).not.toBeChecked(); - - // Broadcast: OFF -> ON (ReadOnly stays ON + disabled) - await userEvent.click(broadcast); - expect(broadcast).toBeChecked(); - expect(readOnly).toBeChecked(); - expect(readOnly).toBeDisabled(); + 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(); }); - it('should disable and turn on ReadOnly toggle when Broadcast is ON with set-readonly permission', async () => { - render( null} />, { - wrapper: mockAppRoot().withPermission('set-readonly').build(), + 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'); - const broadcast = screen.getByLabelText('Teams_New_Broadcast_Label') as HTMLInputElement; - const readOnly = screen.getByLabelText('Teams_New_Read_only_Label') as HTMLInputElement; - - expect(readOnly).not.toBeChecked(); - - // Broadcast: OFF -> ON (ReadOnly stays ON + disabled) - await userEvent.click(broadcast); - expect(broadcast).toBeChecked(); - expect(readOnly).toBeChecked(); - expect(readOnly).toBeDisabled(); + expect(federated).toBeInTheDocument(); + expect(federated).not.toBeChecked(); + expect(federated).toBeDisabled(); + expect(federated).toHaveAccessibleDescription('Federation_Matrix_Federated_Description_disabled'); }); - it('should disable and turn off ReadOnly toggle when Broadcast is OFF with no set-readonly permission', async () => { - render( null} />, { - wrapper: mockAppRoot().build(), + 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'); - const broadcast = screen.getByLabelText('Teams_New_Broadcast_Label') as HTMLInputElement; - const readOnly = screen.getByLabelText('Teams_New_Read_only_Label') as HTMLInputElement; - - expect(broadcast).not.toBeChecked(); - expect(readOnly).not.toBeChecked(); - expect(readOnly).toBeDisabled(); + expect(federated).toBeInTheDocument(); + expect(federated).not.toBeChecked(); + expect(federated).toBeDisabled(); + expect(federated).toHaveAccessibleDescription('error-not-authorized-federation'); }); - it('should enable ReadOnly toggle when Broadcast is OFF with set-readonly permission', async () => { - render( null} />, { - wrapper: mockAppRoot().withPermission('set-readonly').build(), + 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 broadcast = screen.getByLabelText('Teams_New_Broadcast_Label') as HTMLInputElement; - const readOnly = screen.getByLabelText('Teams_New_Read_only_Label') as HTMLInputElement; - - expect(broadcast).not.toBeChecked(); - expect(readOnly).not.toBeChecked(); - expect(readOnly).toBeEnabled(); + 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/NavBarV2/NavBarPagesGroup/actions/CreateChannelModal.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/actions/CreateChannelModal.tsx similarity index 98% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateChannelModal.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/actions/CreateChannelModal.tsx index 8b5e6e99cfaa6..bc23b781437b7 100644 --- a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateChannelModal.tsx +++ b/apps/meteor/client/navbar/NavBarPagesGroup/actions/CreateChannelModal.tsx @@ -1,3 +1,4 @@ +import type { IRoom } from '@rocket.chat/core-typings'; import { Box, Modal, @@ -42,6 +43,7 @@ import { goToRoomById } from '../../../lib/utils/goToRoomById'; type CreateChannelModalProps = { teamId?: string; + mainRoom?: IRoom; onClose: () => void; reload?: () => void; }; @@ -75,7 +77,7 @@ const getFederationHintKey = (federationModule: boolean, featureToggle: boolean, const hasExternalMembers = (members: string[]): boolean => members.some((member) => member.startsWith('@')); -const CreateChannelModal = ({ teamId = '', onClose, reload }: CreateChannelModalProps) => { +const CreateChannelModal = ({ teamId = '', mainRoom, onClose, reload }: CreateChannelModalProps) => { const t = useTranslation(); const canSetReadOnly = usePermissionWithScopedRoles('set-readonly', ['owner']); const e2eEnabled = useSetting('E2E_Enable'); @@ -99,7 +101,7 @@ const CreateChannelModal = ({ teamId = '', onClose, reload }: CreateChannelModal const dispatchToastMessage = useToastMessageDispatch(); - const canOnlyCreateOneType = useCreateChannelTypePermission(); + const canOnlyCreateOneType = useCreateChannelTypePermission(mainRoom?._id); const { register, diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateDirectMessage.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/actions/CreateDirectMessage.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateDirectMessage.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/actions/CreateDirectMessage.tsx diff --git a/apps/meteor/client/navbar/NavBarPagesGroup/actions/CreateTeamModal.spec.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/actions/CreateTeamModal.spec.tsx new file mode 100644 index 0000000000000..ecdf1f22591d0 --- /dev/null +++ b/apps/meteor/client/navbar/NavBarPagesGroup/actions/CreateTeamModal.spec.tsx @@ -0,0 +1,247 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import CreateTeamModal from './CreateTeamModal'; + +jest.mock('../../../lib/utils/goToRoomById', () => ({ + goToRoomById: jest.fn(), +})); + +describe('CreateTeamModal', () => { + 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('Teams_New_Encrypted_Label') as HTMLInputElement; + expect(encrypted).toBeInTheDocument(); + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeDisabled(); + }); + + 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(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + + const encrypted = screen.getByLabelText('Teams_New_Encrypted_Label') as HTMLInputElement; + expect(encrypted).toBeInTheDocument(); + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeEnabled(); + }); + + 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(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + + const encrypted = screen.getByLabelText('Teams_New_Encrypted_Label') as HTMLInputElement; + expect(encrypted).toBeInTheDocument(); + + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeDisabled(); + }); + + 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(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + + const encrypted = screen.getByLabelText('Teams_New_Encrypted_Label') as HTMLInputElement; + expect(encrypted).toBeChecked(); + expect(encrypted).toBeEnabled(); + }); + + 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(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + + const encrypted = screen.getByLabelText('Teams_New_Encrypted_Label') as HTMLInputElement; + const priv = screen.getByLabelText('Teams_New_Private_Label') 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(); + }); + + 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(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + + const encrypted = screen.getByLabelText('Teams_New_Encrypted_Label') as HTMLInputElement; + const priv = screen.getByLabelText('Teams_New_Private_Label') as HTMLInputElement; + + // 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(); + + // 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(); + }); + + it('private team: 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('Teams_New_Encrypted_Label') as HTMLInputElement; + const broadcast = screen.getByLabelText('Teams_New_Broadcast_Label') as HTMLInputElement; + const priv = screen.getByLabelText('Teams_New_Private_Label') 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(); + }); + + it('non-private team: 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(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + + const encrypted = screen.getByLabelText('Teams_New_Encrypted_Label') as HTMLInputElement; + const broadcast = screen.getByLabelText('Teams_New_Broadcast_Label') as HTMLInputElement; + const priv = screen.getByLabelText('Teams_New_Private_Label') as HTMLInputElement; + + // Switch to non-private + await userEvent.click(priv); + expect(priv).not.toBeChecked(); + + // Encrypted must be OFF + disabled (non-private cannot be encrypted) + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeDisabled(); + + // Broadcast: OFF -> ON (Encrypted stays OFF + disabled) + await userEvent.click(broadcast); + expect(broadcast).toBeChecked(); + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeDisabled(); + + // Broadcast: ON -> OFF (Encrypted still OFF + disabled) + await userEvent.click(broadcast); + expect(broadcast).not.toBeChecked(); + expect(encrypted).not.toBeChecked(); + expect(encrypted).toBeDisabled(); + }); + + it('should disable and turn on ReadOnly toggle when Broadcast is ON and no set-readonly permission', async () => { + render( null} />, { + wrapper: mockAppRoot().build(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + + const broadcast = screen.getByLabelText('Teams_New_Broadcast_Label') as HTMLInputElement; + const readOnly = screen.getByLabelText('Teams_New_Read_only_Label') as HTMLInputElement; + + expect(readOnly).not.toBeChecked(); + + // Broadcast: OFF -> ON (ReadOnly stays ON + disabled) + await userEvent.click(broadcast); + expect(broadcast).toBeChecked(); + expect(readOnly).toBeChecked(); + expect(readOnly).toBeDisabled(); + }); + + it('should disable and turn on ReadOnly toggle when Broadcast is ON with set-readonly permission', async () => { + render( null} />, { + wrapper: mockAppRoot().withPermission('set-readonly').build(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + + const broadcast = screen.getByLabelText('Teams_New_Broadcast_Label') as HTMLInputElement; + const readOnly = screen.getByLabelText('Teams_New_Read_only_Label') as HTMLInputElement; + + expect(readOnly).not.toBeChecked(); + + // Broadcast: OFF -> ON (ReadOnly stays ON + disabled) + await userEvent.click(broadcast); + expect(broadcast).toBeChecked(); + expect(readOnly).toBeChecked(); + expect(readOnly).toBeDisabled(); + }); + + it('should disable and turn off ReadOnly toggle when Broadcast is OFF with no set-readonly permission', async () => { + render( null} />, { + wrapper: mockAppRoot().build(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + + const broadcast = screen.getByLabelText('Teams_New_Broadcast_Label') as HTMLInputElement; + const readOnly = screen.getByLabelText('Teams_New_Read_only_Label') as HTMLInputElement; + + expect(broadcast).not.toBeChecked(); + expect(readOnly).not.toBeChecked(); + expect(readOnly).toBeDisabled(); + }); + + it('should enable ReadOnly toggle when Broadcast is OFF with set-readonly permission', async () => { + render( null} />, { + wrapper: mockAppRoot().withPermission('set-readonly').build(), + }); + + await userEvent.click(screen.getByText('Advanced_settings')); + + const broadcast = screen.getByLabelText('Teams_New_Broadcast_Label') as HTMLInputElement; + const readOnly = screen.getByLabelText('Teams_New_Read_only_Label') as HTMLInputElement; + + expect(broadcast).not.toBeChecked(); + expect(readOnly).not.toBeChecked(); + expect(readOnly).toBeEnabled(); + }); +}); diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateTeamModal.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/actions/CreateTeamModal.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/CreateTeamModal.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/actions/CreateTeamModal.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/useEncryptedRoomDescription.spec.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/actions/useEncryptedRoomDescription.spec.tsx similarity index 90% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/useEncryptedRoomDescription.spec.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/actions/useEncryptedRoomDescription.spec.tsx index 28f288d76797f..ee22dcea795dc 100644 --- a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/useEncryptedRoomDescription.spec.tsx +++ b/apps/meteor/client/navbar/NavBarPagesGroup/actions/useEncryptedRoomDescription.spec.tsx @@ -2,23 +2,17 @@ import { mockAppRoot } from '@rocket.chat/mock-providers'; import { renderHook } from '@testing-library/react'; import { useEncryptedRoomDescription } from './useEncryptedRoomDescription'; -import { useEncryptedRoomDescription as useEncryptedRoomDescriptionOld } from '../../../sidebar/header/hooks/useEncryptedRoomDescription'; - -type Hook = typeof useEncryptedRoomDescription | typeof useEncryptedRoomDescriptionOld; const wrapper = mockAppRoot(); -describe.each([ - ['useEncryptedRoomDescription in NavBarV2', useEncryptedRoomDescription], - ['useEncryptedRoomDescription', useEncryptedRoomDescriptionOld], -] as const)('%s', (_name, useEncryptedRoomDescriptionHook: Hook) => { +describe('useEncryptedRoomDescription', () => { beforeEach(() => { jest.clearAllMocks(); }); describe.each(['channel', 'team'] as const)('roomType=%s', (roomType) => { it('returns "Not_available_for_this_workspace" when E2E is disabled', () => { - const { result } = renderHook(() => useEncryptedRoomDescriptionHook(roomType), { + const { result } = renderHook(() => useEncryptedRoomDescription(roomType), { wrapper: wrapper.withSetting('E2E_Enable', false).build(), }); const describe = result.current; @@ -27,7 +21,7 @@ describe.each([ }); it('returns "Encrypted_not_available" when room is not private and E2E is enabled', () => { - const { result } = renderHook(() => useEncryptedRoomDescriptionHook(roomType), { + const { result } = renderHook(() => useEncryptedRoomDescription(roomType), { wrapper: wrapper.withSetting('E2E_Enable', true).build(), }); const describe = result.current; @@ -36,7 +30,7 @@ describe.each([ }); it('returns "Encrypted_messages" when private and encrypted are true and E2E is enabled', () => { - const { result } = renderHook(() => useEncryptedRoomDescriptionHook(roomType), { + const { result } = renderHook(() => useEncryptedRoomDescription(roomType), { wrapper: wrapper.withSetting('E2E_Enable', true).build(), }); const describe = result.current; @@ -45,7 +39,7 @@ describe.each([ }); it('returns "Encrypted_messages_false" when private and encrypted are false and E2E is enabled', () => { - const { result } = renderHook(() => useEncryptedRoomDescriptionHook(roomType), { + const { result } = renderHook(() => useEncryptedRoomDescription(roomType), { wrapper: wrapper.withSetting('E2E_Enable', true).build(), }); const describe = result.current; diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/useEncryptedRoomDescription.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/actions/useEncryptedRoomDescription.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/actions/useEncryptedRoomDescription.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/actions/useEncryptedRoomDescription.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useCreateNewItems.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useCreateNewItems.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useCreateNewItems.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useCreateNewItems.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useCreateNewMenu.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useCreateNewMenu.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useCreateNewMenu.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useCreateNewMenu.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useCreateRoomModal.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useCreateRoomModal.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useCreateRoomModal.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useCreateRoomModal.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useGroupingListItems.spec.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useGroupingListItems.spec.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useGroupingListItems.spec.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useGroupingListItems.spec.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useGroupingListItems.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useGroupingListItems.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useGroupingListItems.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useGroupingListItems.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useGrouppingListItems.spec.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useGrouppingListItems.spec.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useGrouppingListItems.spec.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useGrouppingListItems.spec.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useMarketPlaceMenu.spec.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useMarketPlaceMenu.spec.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useMarketPlaceMenu.spec.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useMarketPlaceMenu.spec.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useMarketPlaceMenu.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useMarketPlaceMenu.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useMarketPlaceMenu.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useMarketPlaceMenu.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useMatrixFederationItems.ts b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useMatrixFederationItems.ts similarity index 89% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useMatrixFederationItems.ts rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useMatrixFederationItems.ts index 2a90219e03fd7..f0851180ef6fd 100644 --- a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useMatrixFederationItems.ts +++ b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useMatrixFederationItems.ts @@ -2,7 +2,7 @@ import type { GenericMenuItemProps } from '@rocket.chat/ui-client'; import { useTranslation } from 'react-i18next'; import { useCreateRoomModal } from './useCreateRoomModal'; -import MatrixFederationSearch from '../../../sidebarv2/header/MatrixFederationSearch'; +import MatrixFederationSearch from '../../../sidebar/header/MatrixFederationSearch'; export const useMatrixFederationItems = ({ isMatrixEnabled, diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useSortMenu.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useSortMenu.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useSortMenu.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useSortMenu.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useSortModeItems.spec.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useSortModeItems.spec.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useSortModeItems.spec.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useSortModeItems.spec.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useSortModeItems.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useSortModeItems.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useSortModeItems.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useSortModeItems.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useViewModeItems.spec.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useViewModeItems.spec.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useViewModeItems.spec.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useViewModeItems.spec.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useViewModeItems.tsx b/apps/meteor/client/navbar/NavBarPagesGroup/hooks/useViewModeItems.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useViewModeItems.tsx rename to apps/meteor/client/navbar/NavBarPagesGroup/hooks/useViewModeItems.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarPagesGroup/index.ts b/apps/meteor/client/navbar/NavBarPagesGroup/index.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarPagesGroup/index.ts rename to apps/meteor/client/navbar/NavBarPagesGroup/index.ts diff --git a/apps/meteor/client/NavBarV2/NavBarPagesSection.tsx b/apps/meteor/client/navbar/NavBarPagesSection.tsx similarity index 82% rename from apps/meteor/client/NavBarV2/NavBarPagesSection.tsx rename to apps/meteor/client/navbar/NavBarPagesSection.tsx index 0d8f88386382b..6e2906ca97e7c 100644 --- a/apps/meteor/client/NavBarV2/NavBarPagesSection.tsx +++ b/apps/meteor/client/navbar/NavBarPagesSection.tsx @@ -2,7 +2,7 @@ import { NavBarGroup, NavBarSection } from '@rocket.chat/fuselage'; import { useLayout } from '@rocket.chat/ui-contexts'; import NavBarPagesGroup from './NavBarPagesGroup'; -import { SidebarTogglerV2 } from '../components/SidebarTogglerV2'; +import SidebarToggler from '../components/SidebarToggler'; const NavBarPagesSection = () => { const { sidebar } = useLayout(); @@ -12,7 +12,7 @@ const NavBarPagesSection = () => { {sidebar.shouldToggle && ( <> - + )} diff --git a/apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearch.tsx b/apps/meteor/client/navbar/NavBarSearch/NavBarSearch.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearch.tsx rename to apps/meteor/client/navbar/NavBarSearch/NavBarSearch.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearchItem.tsx b/apps/meteor/client/navbar/NavBarSearch/NavBarSearchItem.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearchItem.tsx rename to apps/meteor/client/navbar/NavBarSearch/NavBarSearchItem.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearchItemWithData.tsx b/apps/meteor/client/navbar/NavBarSearch/NavBarSearchItemWithData.tsx similarity index 90% rename from apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearchItemWithData.tsx rename to apps/meteor/client/navbar/NavBarSearch/NavBarSearchItemWithData.tsx index 99c4e952476ba..af0d0e43c3b35 100644 --- a/apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearchItemWithData.tsx +++ b/apps/meteor/client/navbar/NavBarSearch/NavBarSearchItemWithData.tsx @@ -6,8 +6,8 @@ import { useTranslation } from 'react-i18next'; import NavBarSearchItem from './NavBarSearchItem'; import { RoomIcon } from '../../components/RoomIcon'; import { roomCoordinator } from '../../lib/rooms/roomCoordinator'; -import SidebarItemBadges from '../../sidebarv2/badges/SidebarItemBadges'; -import { useUnreadDisplay } from '../../sidebarv2/hooks/useUnreadDisplay'; +import SidebarItemBadges from '../../sidebar/badges/SidebarItemBadges'; +import { useUnreadDisplay } from '../../sidebar/hooks/useUnreadDisplay'; type NavBarSearchItemWithDataProps = { room: SubscriptionWithRoom; diff --git a/apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearchListbox.tsx b/apps/meteor/client/navbar/NavBarSearch/NavBarSearchListbox.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearchListbox.tsx rename to apps/meteor/client/navbar/NavBarSearch/NavBarSearchListbox.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearchNoResults.tsx b/apps/meteor/client/navbar/NavBarSearch/NavBarSearchNoResults.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearchNoResults.tsx rename to apps/meteor/client/navbar/NavBarSearch/NavBarSearchNoResults.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearchRow.tsx b/apps/meteor/client/navbar/NavBarSearch/NavBarSearchRow.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearchRow.tsx rename to apps/meteor/client/navbar/NavBarSearch/NavBarSearchRow.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearchUserRow.tsx b/apps/meteor/client/navbar/NavBarSearch/NavBarSearchUserRow.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSearch/NavBarSearchUserRow.tsx rename to apps/meteor/client/navbar/NavBarSearch/NavBarSearchUserRow.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSearch/getShortcutLabel.ts b/apps/meteor/client/navbar/NavBarSearch/getShortcutLabel.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSearch/getShortcutLabel.ts rename to apps/meteor/client/navbar/NavBarSearch/getShortcutLabel.ts diff --git a/apps/meteor/client/NavBarV2/NavBarSearch/hooks/useSearchClick.ts b/apps/meteor/client/navbar/NavBarSearch/hooks/useSearchClick.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSearch/hooks/useSearchClick.ts rename to apps/meteor/client/navbar/NavBarSearch/hooks/useSearchClick.ts diff --git a/apps/meteor/client/NavBarV2/NavBarSearch/hooks/useSearchFocus.ts b/apps/meteor/client/navbar/NavBarSearch/hooks/useSearchFocus.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSearch/hooks/useSearchFocus.ts rename to apps/meteor/client/navbar/NavBarSearch/hooks/useSearchFocus.ts diff --git a/apps/meteor/client/NavBarV2/NavBarSearch/hooks/useSearchItems.ts b/apps/meteor/client/navbar/NavBarSearch/hooks/useSearchItems.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSearch/hooks/useSearchItems.ts rename to apps/meteor/client/navbar/NavBarSearch/hooks/useSearchItems.ts diff --git a/apps/meteor/client/NavBarV2/NavBarSearch/hooks/useSearchNavigation.ts b/apps/meteor/client/navbar/NavBarSearch/hooks/useSearchNavigation.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSearch/hooks/useSearchNavigation.ts rename to apps/meteor/client/navbar/NavBarSearch/hooks/useSearchNavigation.ts diff --git a/apps/meteor/client/NavBarV2/NavBarSearch/index.ts b/apps/meteor/client/navbar/NavBarSearch/index.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSearch/index.ts rename to apps/meteor/client/navbar/NavBarSearch/index.ts diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemAdministrationMenu.spec.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/NavBarItemAdministrationMenu.spec.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemAdministrationMenu.spec.tsx rename to apps/meteor/client/navbar/NavBarSettingsToolbar/NavBarItemAdministrationMenu.spec.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemAdministrationMenu.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/NavBarItemAdministrationMenu.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemAdministrationMenu.tsx rename to apps/meteor/client/navbar/NavBarSettingsToolbar/NavBarItemAdministrationMenu.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemLoginPage.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/NavBarItemLoginPage.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/NavBarItemLoginPage.tsx rename to apps/meteor/client/navbar/NavBarSettingsToolbar/NavBarItemLoginPage.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/EditStatusModal.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/EditStatusModal.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/EditStatusModal.tsx rename to apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/EditStatusModal.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/UserMenu.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/UserMenu.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/UserMenu.tsx rename to apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/UserMenu.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/UserMenuButton.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/UserMenuButton.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/UserMenuButton.tsx rename to apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/UserMenuButton.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/UserMenuHeader.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/UserMenuHeader.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/UserMenuHeader.tsx rename to apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/UserMenuHeader.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/hooks/useAccountItems.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useAccountItems.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/hooks/useAccountItems.tsx rename to apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useAccountItems.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/hooks/useCustomStatusModalHandler.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useCustomStatusModalHandler.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/hooks/useCustomStatusModalHandler.tsx rename to apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useCustomStatusModalHandler.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/hooks/useStatusItems.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useStatusItems.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/hooks/useStatusItems.tsx rename to apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useStatusItems.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/hooks/useUserMenu.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useUserMenu.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/hooks/useUserMenu.tsx rename to apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/hooks/useUserMenu.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/index.ts b/apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/index.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/UserMenu/index.ts rename to apps/meteor/client/navbar/NavBarSettingsToolbar/UserMenu/index.ts diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/hooks/useAdministrationMenu.spec.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/hooks/useAdministrationMenu.spec.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/hooks/useAdministrationMenu.spec.tsx rename to apps/meteor/client/navbar/NavBarSettingsToolbar/hooks/useAdministrationMenu.spec.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/hooks/useAdministrationMenu.tsx b/apps/meteor/client/navbar/NavBarSettingsToolbar/hooks/useAdministrationMenu.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/hooks/useAdministrationMenu.tsx rename to apps/meteor/client/navbar/NavBarSettingsToolbar/hooks/useAdministrationMenu.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/hooks/useAuditMenu.spec.ts b/apps/meteor/client/navbar/NavBarSettingsToolbar/hooks/useAuditMenu.spec.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/hooks/useAuditMenu.spec.ts rename to apps/meteor/client/navbar/NavBarSettingsToolbar/hooks/useAuditMenu.spec.ts diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/hooks/useAuditMenu.ts b/apps/meteor/client/navbar/NavBarSettingsToolbar/hooks/useAuditMenu.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/hooks/useAuditMenu.ts rename to apps/meteor/client/navbar/NavBarSettingsToolbar/hooks/useAuditMenu.ts diff --git a/apps/meteor/client/NavBarV2/NavBarSettingsToolbar/index.ts b/apps/meteor/client/navbar/NavBarSettingsToolbar/index.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarSettingsToolbar/index.ts rename to apps/meteor/client/navbar/NavBarSettingsToolbar/index.ts diff --git a/apps/meteor/client/NavBarV2/NavBarVoipGroup/NavBarVoipGroup.tsx b/apps/meteor/client/navbar/NavBarVoipGroup/NavBarVoipGroup.tsx similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarVoipGroup/NavBarVoipGroup.tsx rename to apps/meteor/client/navbar/NavBarVoipGroup/NavBarVoipGroup.tsx diff --git a/apps/meteor/client/NavBarV2/NavBarVoipGroup/index.ts b/apps/meteor/client/navbar/NavBarVoipGroup/index.ts similarity index 100% rename from apps/meteor/client/NavBarV2/NavBarVoipGroup/index.ts rename to apps/meteor/client/navbar/NavBarVoipGroup/index.ts diff --git a/apps/meteor/client/NavBarV2/index.ts b/apps/meteor/client/navbar/index.ts similarity index 100% rename from apps/meteor/client/NavBarV2/index.ts rename to apps/meteor/client/navbar/index.ts diff --git a/apps/meteor/client/sidebar/Item/Condensed.tsx b/apps/meteor/client/sidebar/Item/Condensed.tsx index f0e576d4f908a..3c737cc30e7c9 100644 --- a/apps/meteor/client/sidebar/Item/Condensed.tsx +++ b/apps/meteor/client/sidebar/Item/Condensed.tsx @@ -1,52 +1,41 @@ -import { IconButton, Sidebar } from '@rocket.chat/fuselage'; -import type { Keys as IconName } from '@rocket.chat/icons'; -import type { ReactElement } from 'react'; +import { IconButton, SidebarV2Item, SidebarV2ItemAvatarWrapper, SidebarV2ItemMenu, SidebarV2ItemTitle } from '@rocket.chat/fuselage'; +import type { HTMLAttributes, ReactNode } from 'react'; import { memo, useState } from 'react'; type CondensedProps = { - title: ReactElement | string; - titleIcon?: ReactElement; - avatar: ReactElement | boolean; - icon?: IconName; - actions?: ReactElement; + title: ReactNode; + titleIcon?: ReactNode; + avatar: ReactNode; + icon?: ReactNode; + actions?: ReactNode; href?: string; unread?: boolean; - menu?: () => ReactElement; + menu?: () => ReactNode; menuOptions?: any; selected?: boolean; - badges?: ReactElement; + badges?: ReactNode; clickable?: boolean; -}; +} & Omit, 'is'>; -const Condensed = ({ icon, title = '', avatar, actions, href, unread, menu, badges, ...props }: CondensedProps) => { +const Condensed = ({ icon, title, avatar, actions, unread, menu, badges, ...props }: CondensedProps) => { const [menuVisibility, setMenuVisibility] = useState(!!window.DISABLE_ANIMATION); const handleFocus = () => setMenuVisibility(true); const handlePointerEnter = () => setMenuVisibility(true); return ( - - {avatar && {avatar}} - - - {icon} - - {title} - - - {badges && {badges}} - {menu && ( - - {menuVisibility ? menu() : } - - )} - - {actions && ( - - {actions} - + + {avatar && {avatar}} + {icon} + {title} + {badges} + {actions} + {menu && ( + + {menuVisibility ? menu() : } + )} - + ); }; diff --git a/apps/meteor/client/sidebar/Item/Extended.stories.tsx b/apps/meteor/client/sidebar/Item/Extended.stories.tsx index 0ccd5e7a59e0b..2f8e13679e941 100644 --- a/apps/meteor/client/sidebar/Item/Extended.stories.tsx +++ b/apps/meteor/client/sidebar/Item/Extended.stories.tsx @@ -20,14 +20,7 @@ export default { const Template: StoryFn = (args) => ( - - John Doe - - 15:38 - - } + title='John Doe' subtitle={ diff --git a/apps/meteor/client/sidebar/Item/Extended.tsx b/apps/meteor/client/sidebar/Item/Extended.tsx index f8ec241a3dafb..ce9faea597849 100644 --- a/apps/meteor/client/sidebar/Item/Extended.tsx +++ b/apps/meteor/client/sidebar/Item/Extended.tsx @@ -1,13 +1,22 @@ -import { Sidebar, IconButton } from '@rocket.chat/fuselage'; -import type { Keys as IconName } from '@rocket.chat/icons'; -import type { ReactNode } from 'react'; +import { + SidebarV2Item, + SidebarV2ItemAvatarWrapper, + SidebarV2ItemCol, + SidebarV2ItemRow, + SidebarV2ItemTitle, + SidebarV2ItemTimestamp, + SidebarV2ItemContent, + SidebarV2ItemMenu, + IconButton, +} from '@rocket.chat/fuselage'; +import type { HTMLAttributes, ReactNode } from 'react'; import { memo, useState } from 'react'; import { useShortTimeAgo } from '../../hooks/useTimeAgo'; type ExtendedProps = { - icon?: IconName; - title?: ReactNode; + icon?: ReactNode; + title: ReactNode; avatar?: ReactNode; actions?: ReactNode; href?: string; @@ -20,11 +29,11 @@ type ExtendedProps = { menuOptions?: any; titleIcon?: ReactNode; threadUnread?: boolean; -}; +} & Omit, 'is'>; const Extended = ({ icon, - title = '', + title, avatar, actions, href, @@ -46,44 +55,26 @@ const Extended = ({ const handlePointerEnter = () => setMenuVisibility(true); return ( - - {avatar && {avatar}} - - - - {icon} - - {title} - - {time && {formatDate(time)}} - - - - - {subtitle} - {badges} - {menu && ( - - {menuVisibility ? menu() : } - - )} - - - - {actions && ( - - {actions} - - )} - + + {avatar && {avatar}} + + + {icon} + {title} + {time && {formatDate(time)}} + + + {subtitle} + {badges} + {actions} + {menu && ( + + {menuVisibility ? menu() : } + + )} + + + ); }; diff --git a/apps/meteor/client/sidebar/Item/Medium.tsx b/apps/meteor/client/sidebar/Item/Medium.tsx index 6c266c8636ffa..f26d64b983891 100644 --- a/apps/meteor/client/sidebar/Item/Medium.tsx +++ b/apps/meteor/client/sidebar/Item/Medium.tsx @@ -1,12 +1,12 @@ -import { Sidebar, IconButton } from '@rocket.chat/fuselage'; -import type { ReactNode } from 'react'; +import { IconButton, SidebarV2Item, SidebarV2ItemAvatarWrapper, SidebarV2ItemMenu, SidebarV2ItemTitle } from '@rocket.chat/fuselage'; +import type { HTMLAttributes, ReactNode } from 'react'; import { memo, useState } from 'react'; type MediumProps = { title: ReactNode; titleIcon?: ReactNode; avatar: ReactNode; - icon?: string; + icon?: ReactNode; actions?: ReactNode; href?: string; unread?: boolean; @@ -14,37 +14,27 @@ type MediumProps = { badges?: ReactNode; selected?: boolean; menuOptions?: any; -}; +} & Omit, 'is'>; -const Medium = ({ icon, title = '', avatar, actions, href, badges, unread, menu, ...props }: MediumProps) => { +const Medium = ({ icon, title, avatar, actions, badges, unread, menu, ...props }: MediumProps) => { const [menuVisibility, setMenuVisibility] = useState(!!window.DISABLE_ANIMATION); const handleFocus = () => setMenuVisibility(true); const handlePointerEnter = () => setMenuVisibility(true); return ( - - {avatar && {avatar}} - - - {icon} - - {title} - - - {badges && {badges}} - {menu && ( - - {menuVisibility ? menu() : } - - )} - - {actions && ( - - {actions} - + + {avatar} + {icon} + {title} + {badges} + {actions} + {menu && ( + + {menuVisibility ? menu() : } + )} - + ); }; diff --git a/apps/meteor/client/sidebar/RoomList/RoomList.tsx b/apps/meteor/client/sidebar/RoomList/RoomList.tsx index 1a080e96de9fd..2403621675507 100644 --- a/apps/meteor/client/sidebar/RoomList/RoomList.tsx +++ b/apps/meteor/client/sidebar/RoomList/RoomList.tsx @@ -1,33 +1,32 @@ -import { css } from '@rocket.chat/css-in-js'; import { Box } from '@rocket.chat/fuselage'; import { useResizeObserver } from '@rocket.chat/fuselage-hooks'; import { VirtualizedScrollbars } from '@rocket.chat/ui-client'; -import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts'; import { useUserPreference, useUserId } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { Virtuoso } from 'react-virtuoso'; +import { GroupedVirtuoso } from 'react-virtuoso'; +import RoomListCollapser from './RoomListCollapser'; import RoomListRow from './RoomListRow'; import RoomListRowWrapper from './RoomListRowWrapper'; import RoomListWrapper from './RoomListWrapper'; import { useOpenedRoom } from '../../lib/RoomManager'; import { useAvatarTemplate } from '../hooks/useAvatarTemplate'; +import { useCollapsedGroups } from '../hooks/useCollapsedGroups'; import { usePreventDefault } from '../hooks/usePreventDefault'; import { useRoomList } from '../hooks/useRoomList'; import { useShortcutOpenMenu } from '../hooks/useShortcutOpenMenu'; import { useTemplateByViewMode } from '../hooks/useTemplateByViewMode'; -const computeItemKey = (index: number, room: SubscriptionWithRoom): SubscriptionWithRoom['_id'] | number => room._id || index; - -const RoomList = (): ReactElement => { +const RoomList = () => { const { t } = useTranslation(); const isAnonymous = !useUserId(); - const roomsList = useRoomList(); + + const { collapsedGroups, handleClick, handleKeyDown } = useCollapsedGroups(); + const { groupsCount, groupsList, roomList, groupedUnreadInfo } = useRoomList({ collapsedGroups }); const avatarTemplate = useAvatarTemplate(); const sideBarItemTemplate = useTemplateByViewMode(); - const { ref } = useResizeObserver({ debounceDelay: 100 }); + const { ref } = useResizeObserver({ debounceDelay: 100 }); const openedRoom = useOpenedRoom() ?? ''; const sidebarViewMode = useUserPreference<'extended' | 'medium' | 'condensed'>('sidebarViewMode') || 'extended'; @@ -36,7 +35,7 @@ const RoomList = (): ReactElement => { () => ({ extended, t, - SideBarItemTemplate: sideBarItemTemplate, + SidebarItemTemplate: sideBarItemTemplate, AvatarTemplate: avatarTemplate, openedRoom, sidebarViewMode, @@ -48,92 +47,26 @@ const RoomList = (): ReactElement => { usePreventDefault(ref); useShortcutOpenMenu(ref); - const roomsListStyle = css` - position: relative; - - display: flex; - - overflow-x: hidden; - overflow-y: hidden; - - flex: 1 1 auto; - - height: 100%; - - &--embedded { - margin-top: 2rem; - } - - &__list:not(:last-child) { - margin-bottom: 22px; - } - - &__type { - display: flex; - - flex-direction: row; - - padding: 0 var(--sidebar-default-padding) 1rem var(--sidebar-default-padding); - - color: var(--rooms-list-title-color); - - font-size: var(--rooms-list-title-text-size); - align-items: center; - justify-content: space-between; - - &-text--livechat { - flex: 1; - } - } - - &__empty-room { - padding: 0 var(--sidebar-default-padding); - - color: var(--rooms-list-empty-text-color); - - font-size: var(--rooms-list-empty-text-size); - } - - &__toolbar-search { - position: absolute; - z-index: 10; - left: 0; - - overflow-y: scroll; - - height: 100%; - - background-color: var(--sidebar-background); - - padding-block-start: 12px; - } - - @media (max-width: 400px) { - padding: 0 calc(var(--sidebar-small-default-padding) - 4px); - - &__type, - &__empty-room { - padding: 0 calc(var(--sidebar-small-default-padding) - 4px) 0.5rem calc(var(--sidebar-small-default-padding) - 4px); - } - } - `; - return ( - - - - } - /> - - + + + ( + handleClick(groupsList[index])} + onKeyDown={(e) => handleKeyDown(e, groupsList[index])} + groupTitle={groupsList[index]} + unreadCount={groupedUnreadInfo[index]} + /> + )} + {...(roomList.length > 0 && { + itemContent: (index) => roomList[index] && , + })} + components={{ Item: RoomListRowWrapper, List: RoomListWrapper }} + /> + ); }; diff --git a/apps/meteor/client/sidebarv2/RoomList/RoomListCollapser.tsx b/apps/meteor/client/sidebar/RoomList/RoomListCollapser.tsx similarity index 100% rename from apps/meteor/client/sidebarv2/RoomList/RoomListCollapser.tsx rename to apps/meteor/client/sidebar/RoomList/RoomListCollapser.tsx diff --git a/apps/meteor/client/sidebar/RoomList/RoomListRow.tsx b/apps/meteor/client/sidebar/RoomList/RoomListRow.tsx index fdee764b7c272..832699a092ecc 100644 --- a/apps/meteor/client/sidebar/RoomList/RoomListRow.tsx +++ b/apps/meteor/client/sidebar/RoomList/RoomListRow.tsx @@ -1,26 +1,27 @@ -import { SidebarSection } from '@rocket.chat/fuselage'; import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts'; import { useVideoConfAcceptCall, useVideoConfRejectIncomingCall, useVideoConfIncomingCalls } from '@rocket.chat/ui-video-conf'; import type { TFunction } from 'i18next'; -import type { ReactElement } from 'react'; import { memo, useMemo } from 'react'; -import SideBarItemTemplateWithData from './SideBarItemTemplateWithData'; +import SidebarItemTemplateWithData from './SidebarItemTemplateWithData'; import type { useAvatarTemplate } from '../hooks/useAvatarTemplate'; import type { useTemplateByViewMode } from '../hooks/useTemplateByViewMode'; type RoomListRowProps = { - extended: boolean; - t: TFunction; - SideBarItemTemplate: ReturnType; - AvatarTemplate: ReturnType; - openedRoom: string; - sidebarViewMode: 'extended' | 'condensed' | 'medium'; - isAnonymous: boolean; + data: { + extended: boolean; + t: TFunction; + SidebarItemTemplate: ReturnType; + AvatarTemplate: ReturnType; + openedRoom: string; + sidebarViewMode: 'extended' | 'condensed' | 'medium'; + isAnonymous: boolean; + }; + item: SubscriptionWithRoom; }; -const RoomListRow = ({ data, item }: { data: RoomListRowProps; item: SubscriptionWithRoom }): ReactElement => { - const { extended, t, SideBarItemTemplate, AvatarTemplate, openedRoom, sidebarViewMode } = data; +const RoomListRow = ({ data, item }: RoomListRowProps) => { + const { extended, t, SidebarItemTemplate, AvatarTemplate, openedRoom, sidebarViewMode } = data; const acceptCall = useVideoConfAcceptCall(); const rejectCall = useVideoConfRejectIncomingCall(); @@ -36,22 +37,14 @@ const RoomListRow = ({ data, item }: { data: RoomListRowProps; item: Subscriptio [acceptCall, rejectCall, currentCall], ); - if (typeof item === 'string') { - return ( - - {t(item)} - - ); - } - return ( - diff --git a/apps/meteor/client/sidebar/RoomList/RoomListRowWrapper.tsx b/apps/meteor/client/sidebar/RoomList/RoomListRowWrapper.tsx index ab7b4d8570c1d..9a01fbacffdef 100644 --- a/apps/meteor/client/sidebar/RoomList/RoomListRowWrapper.tsx +++ b/apps/meteor/client/sidebar/RoomList/RoomListRowWrapper.tsx @@ -1,8 +1,11 @@ -import type { HTMLAttributes, Ref } from 'react'; +import { SidebarV2ListItem } from '@rocket.chat/fuselage'; +import type { ForwardedRef, HTMLAttributes } from 'react'; import { forwardRef } from 'react'; -const RoomListRoomWrapper = forwardRef(function RoomListRoomWrapper(props: HTMLAttributes, ref: Ref) { - return
; +type RoomListRoomWrapperProps = HTMLAttributes; + +const RoomListRoomWrapper = forwardRef(function RoomListRoomWrapper(props: RoomListRoomWrapperProps, ref: ForwardedRef) { + return ; }); export default RoomListRoomWrapper; diff --git a/apps/meteor/client/sidebar/RoomList/RoomListWrapper.tsx b/apps/meteor/client/sidebar/RoomList/RoomListWrapper.tsx index 9eb2fbc4cc0ee..64dc40e0d8792 100644 --- a/apps/meteor/client/sidebar/RoomList/RoomListWrapper.tsx +++ b/apps/meteor/client/sidebar/RoomList/RoomListWrapper.tsx @@ -1,11 +1,13 @@ import { useMergedRefs } from '@rocket.chat/fuselage-hooks'; -import type { HTMLAttributes, Ref } from 'react'; +import type { ForwardedRef, HTMLAttributes } from 'react'; import { forwardRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useSidebarListNavigation } from './useSidebarListNavigation'; -const RoomListWrapper = forwardRef(function RoomListWrapper(props: HTMLAttributes, ref: Ref) { +type RoomListWrapperProps = HTMLAttributes; + +const RoomListWrapper = forwardRef(function RoomListWrapper(props: RoomListWrapperProps, ref: ForwardedRef) { const { t } = useTranslation(); const { sidebarListRef } = useSidebarListNavigation(); const mergedRefs = useMergedRefs(ref, sidebarListRef); diff --git a/apps/meteor/client/sidebar/RoomList/SideBarItemTemplateWithData.tsx b/apps/meteor/client/sidebar/RoomList/SideBarItemTemplateWithData.tsx deleted file mode 100644 index ec0ba9c8b1575..0000000000000 --- a/apps/meteor/client/sidebar/RoomList/SideBarItemTemplateWithData.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import type { IMessage } from '@rocket.chat/core-typings'; -import { isDirectMessageRoom, isMultipleDirectMessageRoom, isOmnichannelRoom, isVideoConfMessage } from '@rocket.chat/core-typings'; -import { Sidebar, SidebarItemAction, SidebarItemActions } from '@rocket.chat/fuselage'; -import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts'; -import { useLayout } from '@rocket.chat/ui-contexts'; -import DOMPurify from 'dompurify'; -import type { TFunction } from 'i18next'; -import type { AllHTMLAttributes, ComponentType, ReactElement, ReactNode } from 'react'; -import { memo, useMemo } from 'react'; - -import { normalizeSidebarMessage } from './normalizeSidebarMessage'; -import { RoomIcon } from '../../components/RoomIcon'; -import { roomCoordinator } from '../../lib/rooms/roomCoordinator'; -import { isIOsDevice } from '../../lib/utils/isIOsDevice'; -import { useOmnichannelPriorities } from '../../views/omnichannel/hooks/useOmnichannelPriorities'; -import RoomMenu from '../RoomMenu'; -import SidebarItemBadges from '../badges/SidebarItemBadges'; -import type { useAvatarTemplate } from '../hooks/useAvatarTemplate'; -import { useUnreadDisplay } from '../hooks/useUnreadDisplay'; - -const getMessage = (room: SubscriptionWithRoom, lastMessage: IMessage | undefined, t: TFunction): string | undefined => { - if (!lastMessage) { - return t('No_messages_yet'); - } - if (isVideoConfMessage(lastMessage)) { - return t('Call_started'); - } - if (!lastMessage.u) { - return normalizeSidebarMessage(lastMessage, t); - } - if (lastMessage.u?.username === room.u?.username) { - return `${t('You')}: ${normalizeSidebarMessage(lastMessage, t)}`; - } - if (isDirectMessageRoom(room) && !isMultipleDirectMessageRoom(room)) { - return normalizeSidebarMessage(lastMessage, t); - } - return `${lastMessage.u.name || lastMessage.u.username}: ${normalizeSidebarMessage(lastMessage, t)}`; -}; - -type RoomListRowProps = { - extended: boolean; - t: TFunction; - SideBarItemTemplate: ComponentType< - { - icon: ReactNode; - title: ReactNode; - avatar: ReactNode; - actions: unknown; - href: string; - time?: Date; - menu?: () => ReactNode; - menuOptions?: unknown; - subtitle?: ReactNode; - titleIcon?: string; - badges?: ReactNode; - threadUnread?: boolean; - unread?: boolean; - selected?: boolean; - is?: string; - } & AllHTMLAttributes - >; - AvatarTemplate: ReturnType; - openedRoom?: string; - // sidebarViewMode: 'extended'; - isAnonymous?: boolean; - - room: SubscriptionWithRoom; - id?: string; - /* @deprecated */ - style?: AllHTMLAttributes['style']; - - selected?: boolean; - - sidebarViewMode?: unknown; - videoConfActions?: { - [action: string]: () => void; - }; -}; - -function SideBarItemTemplateWithData({ - room, - id, - selected, - style, - extended, - SideBarItemTemplate, - AvatarTemplate, - t, - isAnonymous, - videoConfActions, -}: RoomListRowProps): ReactElement { - const { sidebar } = useLayout(); - - const href = roomCoordinator.getRouteLink(room.t, room) || ''; - const title = roomCoordinator.getRoomName(room.t, room) || ''; - - const { lastMessage, unread = 0, alert, rid, t: type, cl } = room; - - const { unreadCount, unreadTitle, showUnread, highlightUnread: highlighted } = useUnreadDisplay(room); - - const icon = ( - // TODO: Remove icon='at' - - - - ); - - const actions = useMemo( - () => - videoConfActions && ( - - - - - ), - [videoConfActions], - ); - - const isQueued = isOmnichannelRoom(room) && room.status === 'queued'; - const { enabled: isPriorityEnabled } = useOmnichannelPriorities(); - - const message = extended && getMessage(room, lastMessage, t); - const subtitle = message ? ( - - ) : null; - - return ( - { - !selected && sidebar.toggle(); - }} - aria-label={showUnread ? t('__unreadTitle__from__roomTitle__', { unreadTitle, roomTitle: title }) : title} - title={title} - time={lastMessage?.ts} - subtitle={subtitle} - icon={icon} - style={style} - badges={} - avatar={AvatarTemplate && } - actions={actions} - menu={ - !isIOsDevice && !isAnonymous && (!isQueued || (isQueued && isPriorityEnabled)) - ? (): ReactElement => ( - 0} - rid={rid} - unread={!!unread} - roomOpen={selected} - type={type} - cl={cl} - name={title} - hideDefaultOptions={isQueued} - /> - ) - : undefined - } - /> - ); -} - -function safeDateNotEqualCheck(a: Date | string | undefined, b: Date | string | undefined): boolean { - if (!a || !b) { - return a !== b; - } - return new Date(a).toISOString() !== new Date(b).toISOString(); -} - -const keys: (keyof RoomListRowProps)[] = [ - 'id', - 'style', - 'extended', - 'selected', - 'SideBarItemTemplate', - 'AvatarTemplate', - 't', - 'sidebarViewMode', - 'videoConfActions', -]; - -// eslint-disable-next-line react/no-multi-comp -export default memo(SideBarItemTemplateWithData, (prevProps, nextProps) => { - if (keys.some((key) => prevProps[key] !== nextProps[key])) { - return false; - } - - if (prevProps.room === nextProps.room) { - return true; - } - - if (prevProps.room._id !== nextProps.room._id) { - return false; - } - if (prevProps.room._updatedAt?.toISOString() !== nextProps.room._updatedAt?.toISOString()) { - return false; - } - if (safeDateNotEqualCheck(prevProps.room.lastMessage?._updatedAt, nextProps.room.lastMessage?._updatedAt)) { - return false; - } - if (prevProps.room.alert !== nextProps.room.alert) { - return false; - } - if (isOmnichannelRoom(prevProps.room) && isOmnichannelRoom(nextProps.room) && prevProps.room?.v?.status !== nextProps.room?.v?.status) { - return false; - } - if (prevProps.room.teamMain !== nextProps.room.teamMain) { - return false; - } - - if ( - isOmnichannelRoom(prevProps.room) && - isOmnichannelRoom(nextProps.room) && - prevProps.room.priorityWeight !== nextProps.room.priorityWeight - ) { - return false; - } - - return true; -}); diff --git a/apps/meteor/client/sidebarv2/RoomList/SidebarItem.tsx b/apps/meteor/client/sidebar/RoomList/SidebarItem.tsx similarity index 100% rename from apps/meteor/client/sidebarv2/RoomList/SidebarItem.tsx rename to apps/meteor/client/sidebar/RoomList/SidebarItem.tsx diff --git a/apps/meteor/client/sidebarv2/RoomList/SidebarItemTemplateWithData.tsx b/apps/meteor/client/sidebar/RoomList/SidebarItemTemplateWithData.tsx similarity index 100% rename from apps/meteor/client/sidebarv2/RoomList/SidebarItemTemplateWithData.tsx rename to apps/meteor/client/sidebar/RoomList/SidebarItemTemplateWithData.tsx diff --git a/apps/meteor/client/sidebarv2/RoomList/SidebarItemWithData.tsx b/apps/meteor/client/sidebar/RoomList/SidebarItemWithData.tsx similarity index 100% rename from apps/meteor/client/sidebarv2/RoomList/SidebarItemWithData.tsx rename to apps/meteor/client/sidebar/RoomList/SidebarItemWithData.tsx diff --git a/apps/meteor/client/sidebar/RoomList/useSidebarListNavigation.ts b/apps/meteor/client/sidebar/RoomList/useSidebarListNavigation.ts index baad06280ca9e..3f6ee5062a26d 100644 --- a/apps/meteor/client/sidebar/RoomList/useSidebarListNavigation.ts +++ b/apps/meteor/client/sidebar/RoomList/useSidebarListNavigation.ts @@ -1,12 +1,13 @@ import { useCallback } from 'react'; import { useFocusManager } from 'react-aria'; -const isListItem = (node: EventTarget) => (node as HTMLElement).classList.contains('rcx-sidebar-item'); -const isListItemMenu = (node: EventTarget) => (node as HTMLElement).classList.contains('rcx-sidebar-item__menu'); +const isListItem = (node: EventTarget) => (node as HTMLElement).classList.contains('rcx-sidebar-v2-item'); +const isCollapseGroup = (node: EventTarget) => (node as HTMLElement).classList.contains('rcx-sidebar-v2-collapse-group__bar'); +const isListItemMenu = (node: EventTarget) => (node as HTMLElement).classList.contains('rcx-sidebar-v2-item__menu'); /** * Custom hook to provide the sidebar navigation by keyboard. - * @param ref - A ref to the message list DOM element. + * @returns ref - A ref to the message list DOM element. */ export const useSidebarListNavigation = () => { const sidebarListFocusManager = useFocusManager(); @@ -24,7 +25,7 @@ export const useSidebarListNavigation = () => { return; } - if (!isListItem(e.target)) { + if (!isListItem(e.target) && !isCollapseGroup(e.target)) { return; } @@ -34,26 +35,29 @@ export const useSidebarListNavigation = () => { if (e.shiftKey) { sidebarListFocusManager?.focusPrevious({ - accept: (node) => !isListItem(node) && !isListItemMenu(node), + accept: (node) => !isListItem(node) && !isListItemMenu(node) && !isCollapseGroup(node), }); } else if (isListItemMenu(e.target)) { sidebarListFocusManager?.focusNext({ - accept: (node) => !isListItem(node) && !isListItemMenu(node), + accept: (node) => !isListItem(node) && !isListItemMenu(node) && !isCollapseGroup(node), }); } else { sidebarListFocusManager?.focusNext({ - accept: (node) => !isListItem(node), + accept: (node) => !isListItem(node) && !isCollapseGroup(node), }); } } if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { + e.preventDefault(); + e.stopPropagation(); + if (e.key === 'ArrowUp') { - sidebarListFocusManager?.focusPrevious({ accept: (node) => isListItem(node) }); + sidebarListFocusManager?.focusPrevious({ accept: (node) => isListItem(node) || isCollapseGroup(node) }); } if (e.key === 'ArrowDown') { - sidebarListFocusManager?.focusNext({ accept: (node) => isListItem(node) }); + sidebarListFocusManager?.focusNext({ accept: (node) => isListItem(node) || isCollapseGroup(node) }); } lastItemFocused = document.activeElement as HTMLElement; diff --git a/apps/meteor/client/sidebar/RoomMenu.tsx b/apps/meteor/client/sidebar/RoomMenu.tsx index ea0bacccfef40..c6f2561ca9081 100644 --- a/apps/meteor/client/sidebar/RoomMenu.tsx +++ b/apps/meteor/client/sidebar/RoomMenu.tsx @@ -1,7 +1,6 @@ import type { RoomType } from '@rocket.chat/core-typings'; import { GenericMenu } from '@rocket.chat/ui-client'; -import { useLayout, useTranslation } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; +import { useTranslation } from '@rocket.chat/ui-contexts'; import { memo } from 'react'; import { useRoomMenuActions } from '../hooks/useRoomMenuActions'; @@ -18,33 +17,13 @@ type RoomMenuProps = { hideDefaultOptions: boolean; }; -const RoomMenu = ({ - rid, - unread, - threadUnread, - alert, - roomOpen, - type, - cl, - name = '', - hideDefaultOptions = false, -}: RoomMenuProps): ReactElement | null => { +const RoomMenu = ({ rid, unread, threadUnread, alert, roomOpen, type, cl, name = '', hideDefaultOptions = false }: RoomMenuProps) => { const t = useTranslation(); - const { sidebar } = useLayout(); + const isUnread = alert || unread || threadUnread; const sections = useRoomMenuActions({ rid, type, name, isUnread, cl, roomOpen, hideDefaultOptions }); - return ( - - ); + return ; }; export default memo(RoomMenu); diff --git a/apps/meteor/client/sidebar/Sidebar.tsx b/apps/meteor/client/sidebar/Sidebar.tsx index dd725329dc027..f6dd40024684f 100644 --- a/apps/meteor/client/sidebar/Sidebar.tsx +++ b/apps/meteor/client/sidebar/Sidebar.tsx @@ -1,49 +1,28 @@ -import { css } from '@rocket.chat/css-in-js'; -import { Box } from '@rocket.chat/fuselage'; -import { useLayout, useUserPreference } from '@rocket.chat/ui-contexts'; +import { SidebarV2 } from '@rocket.chat/fuselage'; +import { useUserPreference } from '@rocket.chat/ui-contexts'; import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; import SidebarRoomList from './RoomList'; import SidebarFooter from './footer'; -import SidebarHeader from './header'; import BannerSection from './sections/BannerSection'; -import OmnichannelSection from './sections/OmnichannelSection'; -import { useOmnichannelEnabled } from '../views/omnichannel/hooks/useOmnichannelEnabled'; -// TODO unit test airgappedbanner const Sidebar = () => { - const showOmnichannel = useOmnichannelEnabled(); - + const { t } = useTranslation(); const sidebarViewMode = useUserPreference('sidebarViewMode'); const sidebarHideAvatar = !useUserPreference('sidebarDisplayAvatar'); - const { sidebar } = useLayout(); - - const sidebarLink = css` - a { - text-decoration: none; - } - `; return ( - - - {showOmnichannel && } - + ); }; diff --git a/apps/meteor/client/sidebarv2/SidebarPortal.tsx b/apps/meteor/client/sidebar/SidebarPortal.tsx similarity index 100% rename from apps/meteor/client/sidebarv2/SidebarPortal.tsx rename to apps/meteor/client/sidebar/SidebarPortal.tsx diff --git a/apps/meteor/client/sidebar/SidebarRegion.tsx b/apps/meteor/client/sidebar/SidebarRegion.tsx index 9a09cabfa7be1..1541c900d7a5d 100644 --- a/apps/meteor/client/sidebar/SidebarRegion.tsx +++ b/apps/meteor/client/sidebar/SidebarRegion.tsx @@ -7,7 +7,7 @@ import { FocusScope } from 'react-aria'; import Sidebar from './Sidebar'; const SidebarRegion = () => { - const { isMobile, sidebar } = useLayout(); + const { sidebar } = useLayout(); const sidebarMobileClass = css` position: absolute; @@ -93,14 +93,17 @@ const SidebarRegion = () => { - {isMobile && ( - sidebar.toggle()}> + {sidebar.shouldToggle && ( + sidebar.toggle()} /> )} ); diff --git a/apps/meteor/client/sidebar/badges/SidebarItemBadges.spec.tsx b/apps/meteor/client/sidebar/badges/SidebarItemBadges.spec.tsx index 285c5ecdbc72d..9866febc2dcaa 100644 --- a/apps/meteor/client/sidebar/badges/SidebarItemBadges.spec.tsx +++ b/apps/meteor/client/sidebar/badges/SidebarItemBadges.spec.tsx @@ -69,7 +69,9 @@ describe('SidebarItemBadges', () => { }); it('should not render InvitationBadge when subscription does not have status INVITED', () => { - render(, { wrapper: appRoot }); + render(, { + wrapper: appRoot, + }); expect(screen.queryByRole('status', { name: /Invited/ })).not.toBeInTheDocument(); }); diff --git a/apps/meteor/client/sidebar/badges/SidebarItemBadges.tsx b/apps/meteor/client/sidebar/badges/SidebarItemBadges.tsx index de8264eb4a422..aedfd3610ea9c 100644 --- a/apps/meteor/client/sidebar/badges/SidebarItemBadges.tsx +++ b/apps/meteor/client/sidebar/badges/SidebarItemBadges.tsx @@ -1,5 +1,4 @@ -import { isOmnichannelRoom, isInviteSubscription } from '@rocket.chat/core-typings'; -import { Margins } from '@rocket.chat/fuselage'; +import { isInviteSubscription, isOmnichannelRoom } from '@rocket.chat/core-typings'; import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts'; import UnreadBadge from './UnreadBadge'; @@ -16,11 +15,11 @@ const SidebarItemBadges = ({ room, roomTitle }: SidebarItemBadgesProps) => { const { unreadCount, unreadTitle, unreadVariant, showUnread } = useUnreadDisplay(room); return ( - + <> {showUnread && } {isOmnichannelRoom(room) && } {isInviteSubscription(room) && } - + ); }; diff --git a/apps/meteor/client/sidebar/badges/UnreadBadge.tsx b/apps/meteor/client/sidebar/badges/UnreadBadge.tsx index 3d7a1a8465f11..d3abe4f8aaca6 100644 --- a/apps/meteor/client/sidebar/badges/UnreadBadge.tsx +++ b/apps/meteor/client/sidebar/badges/UnreadBadge.tsx @@ -1,4 +1,4 @@ -import { Badge } from '@rocket.chat/fuselage'; +import { SidebarV2ItemBadge } from '@rocket.chat/fuselage'; import { useTranslation } from 'react-i18next'; type UnreadBadgeProps = { @@ -12,15 +12,14 @@ const UnreadBadge = ({ title, variant, total, roomTitle }: UnreadBadgeProps) => const { t } = useTranslation(); return ( - {total} - + ); }; diff --git a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.spec.tsx b/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.spec.tsx deleted file mode 100644 index 198b53d8d0498..0000000000000 --- a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.spec.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { mockAppRoot } from '@rocket.chat/mock-providers'; -import { composeStories } from '@storybook/react'; -import { render } from '@testing-library/react'; -import { axe } from 'jest-axe'; - -import CreateChannelModal from './CreateChannelModal'; -import * as stories from './CreateChannelModal.stories'; -import { testCreateChannelModal } from '../../../NavBarV2/NavBarPagesGroup/actions/testCreateChannelModal'; - -jest.mock('../../../lib/utils/goToRoomById', () => ({ - goToRoomById: jest.fn(), -})); - -testCreateChannelModal(CreateChannelModal); - -const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]); - -test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => { - const { baseElement } = render(, { wrapper: mockAppRoot().build() }); - expect(baseElement).toMatchSnapshot(); -}); - -test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => { - const { container } = render(, { wrapper: mockAppRoot().build() }); - - const results = await axe(container); - expect(results).toHaveNoViolations(); -}); diff --git a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.stories.tsx b/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.stories.tsx deleted file mode 100644 index 3d15c97d5d53d..0000000000000 --- a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.stories.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import type { Meta, StoryFn } from '@storybook/react'; - -import CreateChanelModal from './CreateChannelModal'; -import CreateChannelModalV2 from '../../../NavBarV2/NavBarPagesGroup/actions/CreateChannelModal'; - -export default { - component: CreateChanelModal, -} satisfies Meta; - -export const Default: StoryFn = (args) => ; -export const DefaultVersion2: StoryFn = (args) => ; diff --git a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx b/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx deleted file mode 100644 index 9ab6fe1aafcb4..0000000000000 --- a/apps/meteor/client/sidebar/header/CreateChannel/CreateChannelModal.tsx +++ /dev/null @@ -1,402 +0,0 @@ -import type { IRoom } from '@rocket.chat/core-typings'; -import { - Box, - Modal, - Button, - TextInput, - Icon, - Field, - ToggleSwitch, - FieldGroup, - FieldLabel, - FieldRow, - FieldError, - FieldHint, - Accordion, - AccordionItem, - ModalHeader, - ModalTitle, - ModalClose, - ModalContent, - ModalFooter, - ModalFooterControllers, -} from '@rocket.chat/fuselage'; -import type { TranslationKey } 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'; - -import UserAutoCompleteMultiple from '../../../components/UserAutoCompleteMultiple'; -import { useCreateChannelTypePermission } from '../../../hooks/useCreateChannelTypePermission'; -import { useHasLicenseModule } from '../../../hooks/useHasLicenseModule'; -import { useIsFederationEnabled } from '../../../hooks/useIsFederationEnabled'; -import { goToRoomById } from '../../../lib/utils/goToRoomById'; -import { useEncryptedRoomDescription } from '../hooks/useEncryptedRoomDescription'; - -type CreateChannelModalProps = { - teamId?: string; - mainRoom?: IRoom; - onClose: () => void; - reload?: () => void; -}; - -type CreateChannelModalPayload = { - name: string; - isPrivate: boolean; - topic?: string; - members: string[]; - readOnly: boolean; - encrypted: boolean; - broadcast: boolean; - federated: boolean; -}; - -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'; -}; - -const CreateChannelModal = ({ teamId = '', mainRoom, onClose, reload }: CreateChannelModalProps): ReactElement => { - const t = useTranslation(); - const canSetReadOnly = usePermissionWithScopedRoles('set-readonly', ['owner']); - const e2eEnabled = useSetting('E2E_Enable'); - const namesValidation = useSetting('UTF8_Channel_Names_Validation'); - const allowSpecialNames = useSetting('UI_Allow_room_names_with_special_chars'); - - const e2eEnabledForPrivateByDefault = useSetting('E2E_Enabled_Default_PrivateRooms') && e2eEnabled; - - const getEncryptedHint = useEncryptedRoomDescription('channel'); - - const channelNameRegex = useMemo(() => new RegExp(`^${namesValidation}$`), [namesValidation]); - - 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'); - const createPrivateChannel = useEndpoint('POST', '/v1/groups.create'); - - const dispatchToastMessage = useToastMessageDispatch(); - - const canOnlyCreateOneType = useCreateChannelTypePermission(mainRoom?._id); - - const { - register, - formState: { errors }, - handleSubmit, - control, - setValue, - watch, - } = useForm({ - mode: 'onBlur', - defaultValues: { - members: [], - name: '', - topic: '', - isPrivate: canOnlyCreateOneType ? canOnlyCreateOneType === 'p' : true, - readOnly: false, - encrypted: (e2eEnabledForPrivateByDefault as boolean) ?? false, - broadcast: false, - federated: false, - }, - }); - - const { isPrivate, broadcast, readOnly, federated, encrypted } = watch(); - - useEffect(() => { - if (federated) { - // if room is federated, it cannot be encrypted or broadcast or readOnly - setValue('encrypted', false); - setValue('broadcast', false); - setValue('readOnly', false); - } - }, [federated, setValue]); - - useEffect(() => { - if (!isPrivate) { - setValue('encrypted', false); - } - }, [isPrivate, setValue]); - - useEffect(() => { - setValue('readOnly', broadcast); - }, [broadcast, setValue]); - - const validateChannelName = async (name: string): Promise => { - if (!name) { - return; - } - - if (!allowSpecialNames && !channelNameRegex.test(name)) { - return t('Name_cannot_have_special_characters'); - } - - const { exists } = await channelNameExists({ roomName: name }); - if (exists) { - return t('Channel_already_exist', name); - } - }; - - const handleCreateChannel = async ({ name, members, readOnly, topic, broadcast, encrypted, federated }: CreateChannelModalPayload) => { - let roomData; - const params = { - name, - members, - readOnly, - extraData: { - topic, - broadcast, - encrypted, - ...(federated && { federated }), - ...(teamId && { teamId }), - }, - }; - - try { - if (isPrivate) { - roomData = await createPrivateChannel(params); - !teamId && goToRoomById(roomData.group._id); - } else { - roomData = await createChannel(params); - !teamId && goToRoomById(roomData.channel._id); - } - - dispatchToastMessage({ type: 'success', message: t('Room_has_been_created') }); - reload?.(); - } catch (error) { - dispatchToastMessage({ type: 'error', message: error }); - } finally { - onClose(); - } - }; - - const e2eDisabled = useMemo(() => !isPrivate || Boolean(!e2eEnabled) || federated, [e2eEnabled, federated, isPrivate]); - - const createChannelFormId = useId(); - const nameId = useId(); - const topicId = useId(); - const privateId = useId(); - const federatedId = useId(); - const readOnlyId = useId(); - const encryptedId = useId(); - const broadcastId = useId(); - const addMembersId = useId(); - - return ( - ) => ( - - )} - > - - {t('Create_channel')} - - - - - - - {t('Name')} - - - validateChannelName(value), - })} - error={errors.name?.message} - addon={} - aria-invalid={errors.name ? 'true' : 'false'} - aria-describedby={`${nameId}-error ${nameId}-hint`} - aria-required='true' - /> - - {errors.name && ( - - {errors.name.message} - - )} - {!allowSpecialNames && {t('No_spaces_or_special_characters')}} - - - {t('Topic')} - - - - {t('Displayed_next_to_name')} - - - {t('Members')} - ( - - )} - /> - {errors.members && ( - - {errors.members.message} - - )} - - - - {t('Private')} - ( - - )} - /> - - - {isPrivate ? t('People_can_only_join_by_being_invited') : t('Anyone_can_access')} - - - - - - - - {t('Security_and_permissions')} - - - - {t('Federation_Matrix_Federated')} - ( - - )} - /> - - {t(federationFieldHint)} - - - - {t('Encrypted')} - ( - - )} - /> - - {getEncryptedHint({ isPrivate, encrypted })} - - - - {t('Read_only')} - ( - - )} - /> - - - {readOnly ? t('Read_only_field_hint_enabled', { roomType: 'channel' }) : t('Anyone_can_send_new_messages')} - - - - - {t('Broadcast')} - ( - - )} - /> - - {broadcast && {t('Broadcast_hint_enabled', { roomType: 'channel' })}} - - - - - - - - - - - - - ); -}; - -export default CreateChannelModal; 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 deleted file mode 100644 index 56ecb40563c6a..0000000000000 --- a/apps/meteor/client/sidebar/header/CreateChannel/__snapshots__/CreateChannelModal.spec.tsx.snap +++ /dev/null @@ -1,862 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`renders Default without crashing 1`] = ` - -
- -
-
-
-

- Create_channel -

- -
-
-
-
-
-
- - - - - - No_spaces_or_special_characters - -
-
- - - - - - Displayed_next_to_name - -
-
- -
-
-
- -
-
-
- -
-
-
-
- - -