Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { ISubscription, ILivechatInquiryRecord, IRoom } from '@rocket.chat/core-typings';
import { useEffectEvent } from '@rocket.chat/fuselage-hooks';
import { type ISubscription, type ILivechatInquiryRecord, type IRoom, isTeamRoom, isDirectMessageRoom } from '@rocket.chat/core-typings';
import { useEffectEvent, useLocalStorage } from '@rocket.chat/fuselage-hooks';
import type { Keys as IconName } from '@rocket.chat/icons';
import type { SubscriptionWithRoom, TranslationKey } from '@rocket.chat/ui-contexts';
import { createContext, useContext, useEffect, useMemo } from 'react';
import { createContext, useCallback, useContext, useEffect, useMemo } from 'react';

import { isTruthy } from '../../../../lib/isTruthy';
import { useCollapsedGroups } from '../hooks/useCollapsedGroups';
Expand Down Expand Up @@ -101,7 +101,7 @@ export const useRoomsListContext = () => {
};

// Helper functions
const splitFilter = (currentFilter: AllGroupsKeysWithUnread): [SidePanelFiltersKeys, boolean] => {
export const splitFilter = (currentFilter: AllGroupsKeysWithUnread): [SidePanelFiltersKeys, boolean] => {
const [currentTab, unread] = currentFilter.split('_');
return [currentTab as SidePanelFiltersKeys, unread === 'unread'];
};
Expand Down Expand Up @@ -210,9 +210,9 @@ export const useSidePanelRoomsListTab = (tab: AllGroupsKeys) => {
return roomsList;
};

export const useSidePanelFilter = (): [AllGroupsKeys, boolean, AllGroupsKeysWithUnread] => {
const { currentFilter } = useRoomsListContext();
return [...splitFilter(currentFilter), currentFilter];
export const useSidePanelFilter = (): [AllGroupsKeys, boolean, AllGroupsKeysWithUnread, (filter: AllGroupsKeysWithUnread) => void] => {
const [currentFilter, setCurrentFilter] = useLocalStorage<AllGroupsKeysWithUnread>('sidePanelFilters', getFilterKey('all', false));
return [...splitFilter(currentFilter), currentFilter, setCurrentFilter];
};

export const useUnreadOnlyToggle = (): [boolean, () => void] => {
Expand Down Expand Up @@ -247,3 +247,27 @@ export const useRedirectToDefaultTab = (shouldRedirect: boolean) => {
}
}, [shouldRedirect, switchSidePanelTab]);
};

export const useRedirectToFilter = () => {
const switchSidePanelTab = useSwitchSidePanelTab();

const handleRedirect = useCallback(
(room: SubscriptionWithRoom | IRoom) => {
const roomId = 'rid' in room ? room.rid : room._id;
if (isTeamRoom(room)) {
switchSidePanelTab('teams', { parentRid: roomId });
return;
}

if (isDirectMessageRoom(room)) {
switchSidePanelTab('directMessages', { parentRid: roomId });
return;
}

Copy link

Copilot AI Aug 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback logic room.prid || roomId could be confusing. Consider adding a comment explaining when room.prid would be undefined and why roomId is the appropriate fallback.

Copilot uses AI. Check for mistakes.
switchSidePanelTab('channels', { parentRid: room.prid || roomId });
},
[switchSidePanelTab],
);

return handleRedirect;
};
23 changes: 0 additions & 23 deletions apps/meteor/client/views/navigation/hooks/useSidePanelFilters.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,26 @@ import {
isPublicRoom,
isTeamRoom,
} from '@rocket.chat/core-typings';
import type { ILivechatInquiryRecord } from '@rocket.chat/core-typings';
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
import type { ILivechatInquiryRecord, IRoom } from '@rocket.chat/core-typings';
import { useDebouncedValue, useEffectEvent } from '@rocket.chat/fuselage-hooks';
import type { SubscriptionWithRoom, TranslationKey } from '@rocket.chat/ui-contexts';
import { useSetting, useUserPreference, useUserSubscriptions } from '@rocket.chat/ui-contexts';
import { useSetting, useUserPreference, useUserSubscriptions, useLayout } from '@rocket.chat/ui-contexts';
import type { ReactNode } from 'react';
import { useMemo } from 'react';
import { useEffect, useMemo } from 'react';

import { useOmnichannelEnabled } from '../../../hooks/omnichannel/useOmnichannelEnabled';
import { useQueuedInquiries } from '../../../hooks/omnichannel/useQueuedInquiries';
import { useSortQueryOptions } from '../../../hooks/useSortQueryOptions';
import { RoomManager } from '../../../lib/RoomManager';
import { Rooms } from '../../../stores';
import type { GroupedUnreadInfoData, AllGroupsKeys, AllGroupsKeysWithUnread } from '../contexts/RoomsNavigationContext';
import { RoomsNavigationContext, getEmptyUnreadInfo, isUnreadSubscription } from '../contexts/RoomsNavigationContext';
import { useSidePanelFilters } from '../hooks/useSidePanelFilters';
import {
RoomsNavigationContext,
getEmptyUnreadInfo,
getFilterKey,
isUnreadSubscription,
useSidePanelFilter,
} from '../contexts/RoomsNavigationContext';
import { useSidePanelParentRid } from '../hooks/useSidePanelParentRid';

const query = { open: { $ne: false } };
Expand Down Expand Up @@ -156,11 +163,63 @@ const useRoomsGroups = (): [GroupMap, UnreadGroupDataMap] => {
};

const RoomsNavigationContextProvider = ({ children }: { children: ReactNode }) => {
const { currentFilter, setFilter } = useSidePanelFilters();
const { parentRid } = useSidePanelParentRid();
const {
sidePanel: { openSidePanel },
} = useLayout();
const { setParentRoom, parentRid } = useSidePanelParentRid();

const [currentFilter, unread, , setCurrentFilter] = useSidePanelFilter();

const setFilter = useEffectEvent((filter: AllGroupsKeys, unread: boolean, parentRid?: IRoom['_id']) => {
openSidePanel();
setCurrentFilter(getFilterKey(filter, unread));
setParentRoom(filter, parentRid);
});

const [groups, unreadGroupData] = useRoomsGroups();

const handleRoomOpened = useEffectEvent((rid: string) => {
const room = Rooms.use.getState().find((r) => r._id === rid);

if (!room) {
return;
}

if (isTeamRoom(room)) {
setFilter('teams', unread, rid);
return;
}

if (isDirectMessageRoom(room)) {
setFilter('directMessages', unread, rid);
return;
}

if (room.teamId && currentFilter === 'teams') {
const teamRid = Rooms.use.getState().find((r) => Boolean(r.teamId === room.teamId && r.teamMain))?._id;
Copy link

Copilot AI Aug 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code performs a linear search through all rooms on every room open event. Consider optimizing this by creating an index or using a more efficient lookup method, especially if the rooms collection is large.

Copilot uses AI. Check for mistakes.

/**
* if the room is the parent rid is still the same, don't change the filter
* the filter decision is going to be done by `useRedirectToFilter` when the item is clicked
**/
if (parentRid === teamRid) {
return;
}
setFilter('teams', unread, teamRid);
return;
}

if (room.prid) {
const parentRoom = Rooms.use.getState().find((r) => Boolean(r._id === room.prid));
setFilter(parentRoom?.teamMain ? 'teams' : 'channels', unread, parentRoom?._id);
return;
}

setFilter('channels', false, rid);
});

useEffect(() => RoomManager.on('opened', handleRoomOpened), [handleRoomOpened]);

const contextValue = useMemo(() => {
return {
currentFilter,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { isDirectMessageRoom, isOmnichannelRoom, isTeamRoom } from '@rocket.chat/core-typings';
import { isOmnichannelRoom } from '@rocket.chat/core-typings';
import { SidebarV2Action, SidebarV2Actions, SidebarV2ItemBadge, SidebarV2ItemIcon } from '@rocket.chat/fuselage';
import { useButtonPattern } from '@rocket.chat/fuselage-hooks';
import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts';
import type { TFunction } from 'i18next';
import type { AllHTMLAttributes } from 'react';
import { memo, useCallback, useMemo } from 'react';
import { memo, useMemo } from 'react';

import SidebarItem from './SidebarItem';
import { RoomIcon } from '../../../../components/RoomIcon';
import { roomCoordinator } from '../../../../lib/rooms/roomCoordinator';
import { useSwitchSidePanelTab, useRoomsListContext, useIsRoomFilter } from '../../contexts/RoomsNavigationContext';
import { useRoomsListContext, useIsRoomFilter, useRedirectToFilter } from '../../contexts/RoomsNavigationContext';
import { useUnreadDisplay } from '../hooks/useUnreadDisplay';

type RoomListRowProps = {
Expand Down Expand Up @@ -66,28 +66,14 @@ const SidebarItemWithData = ({ room, id, style, t, videoConfActions }: RoomListR
</>
);

const switchSidePanelTab = useSwitchSidePanelTab();
const { parentRid } = useRoomsListContext();

const isRoomFilter = useIsRoomFilter();

const selected = isRoomFilter && room.rid === parentRid;

const handleClick = useCallback(() => {
if (isTeamRoom(room)) {
switchSidePanelTab('teams', { parentRid: room.rid });
return;
}

if (isDirectMessageRoom(room)) {
switchSidePanelTab('directMessages', { parentRid: room.rid });
return;
}

switchSidePanelTab('channels', { parentRid: room.rid });
}, [room, switchSidePanelTab]);

const buttonProps = useButtonPattern(handleClick);
const redirectToFilter = useRedirectToFilter();
const buttonProps = useButtonPattern(() => redirectToFilter(room));

return (
<SidebarItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const sortByLmPipe = pipe<SubscriptionWithRoom>().sortByField('lm', -1);
* This helper function is used to ensure that the main room (main team room or parent's discussion room)
* is always at the top of the list.
*/
const getMainRoomAndSort = (records: SubscriptionWithRoom[], unreadOnly: boolean) => {
const mainRoom = records.find((record) => record.teamMain || (!record.prid && !record.teamId));
const getMainRoomAndSort = (records: SubscriptionWithRoom[], unreadOnly: boolean, teamId?: string) => {
const mainRoom = records.find((record) => (teamId ? record.teamMain : !record.prid));
const filteredRecords = records.filter((record) => mainRoom?.rid !== record.rid);
const sortedRecords = sortByLmPipe.apply(filteredRecords);
const rest = !unreadOnly
Expand Down Expand Up @@ -51,7 +51,7 @@ export const useChannelsChildrenList = (parentRid: string, unreadOnly: boolean,
return false;
});

return getMainRoomAndSort(records, unreadOnly);
return getMainRoomAndSort(records, unreadOnly, teamId);
}),
);
};
28 changes: 14 additions & 14 deletions apps/meteor/client/views/root/MainLayout/LayoutWithSidebarV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,20 @@ const LayoutWithSidebarV2 = ({ children }: { children: ReactNode }): ReactElemen
id='rocket-chat'
className={[embeddedLayout ? 'embedded-view' : undefined, 'menu-nav'].filter(Boolean).join(' ')}
>
<MainLayoutStyleTags />
{!removeSidenav && (
<FeaturePreview feature='secondarySidebar'>
<FeaturePreviewOn>
<RoomsNavigationProvider>
<NavigationRegion />
</RoomsNavigationProvider>
</FeaturePreviewOn>
<FeaturePreviewOff>
<Sidebar />
</FeaturePreviewOff>
</FeaturePreview>
)}
<MainContent>{children}</MainContent>
<FeaturePreview feature='secondarySidebar'>
<FeaturePreviewOn>
<RoomsNavigationProvider>
<MainLayoutStyleTags />
{!removeSidenav && <NavigationRegion />}
<MainContent>{children}</MainContent>
</RoomsNavigationProvider>
</FeaturePreviewOn>
<FeaturePreviewOff>
<MainLayoutStyleTags />
{!removeSidenav && <Sidebar />}
<MainContent>{children}</MainContent>
</FeaturePreviewOff>
</FeaturePreview>
</Box>
</>
);
Expand Down
Loading