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
18 changes: 18 additions & 0 deletions apps/meteor/client/NavBarV2/NavBarPagesGroup/NavBarItemSort.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { SidebarV2Action } from '@rocket.chat/fuselage';
import { GenericMenu } from '@rocket.chat/ui-client';
import type { HTMLAttributes } from 'react';
import { useTranslation } from 'react-i18next';

import { useSortMenu } from './hooks/useSortMenu';

type NavBarItemSortProps = Omit<HTMLAttributes<HTMLElement>, 'is'>;

const NavBarItemSort = (props: NavBarItemSortProps) => {
const { t } = useTranslation();

const sections = useSortMenu();

return <GenericMenu icon='sort' sections={sections} title={t('Display')} selectionMode='multiple' is={SidebarV2Action} {...props} />;
};

export default NavBarItemSort;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import NavBarItemCreateNew from './NavBarItemCreateNew';
import NavBarItemDirectoryPage from './NavBarItemDirectoryPage';
import NavBarItemHomePage from './NavBarItemHomePage';
import NavBarItemMarketPlaceMenu from './NavBarItemMarketPlaceMenu';
import NavBarItemSort from './NavBarItemSort';
import NavBarPagesStackMenu from './NavBarPagesStackMenu';

const NavBarPagesGroup = () => {
Expand All @@ -26,6 +27,7 @@ const NavBarPagesGroup = () => {
</>
)}
{showMarketplace && !isMobile && <NavBarItemMarketPlaceMenu />}
{!isMobile && <NavBarItemSort />}
<NavBarItemCreateNew />
</NavBarGroup>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { CheckBox } from '@rocket.chat/fuselage';
import type { GenericMenuItemProps } from '@rocket.chat/ui-client';
import { useEndpoint, useUserPreference } from '@rocket.chat/ui-contexts';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';

export const useGroupingListItems = (): GenericMenuItemProps[] => {
const { t } = useTranslation();

const sidebarGroupByType = useUserPreference<boolean>('sidebarGroupByType');
const sidebarShowUnread = useUserPreference<boolean>('sidebarShowUnread');

const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences');

const useHandleChange = (key: 'sidebarGroupByType' | 'sidebarShowUnread', value: boolean): (() => void) =>
useCallback(() => saveUserPreferences({ data: { [key]: value } }), [key, value]);

const handleChangeGroupByType = useHandleChange('sidebarGroupByType', !sidebarGroupByType);
const handleChangeShowUnread = useHandleChange('sidebarShowUnread', !sidebarShowUnread);

return [
{
id: 'unread',
content: t('Unread'),
icon: 'flag',
addon: <CheckBox onChange={handleChangeShowUnread} checked={sidebarShowUnread} />,
},
{
id: 'types',
content: t('Types'),
icon: 'group-by-type',
addon: <CheckBox onChange={handleChangeGroupByType} checked={sidebarGroupByType} />,
},
];
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { renderHook } from '@testing-library/react';

import { useGroupingListItems } from './useGroupingListItems';

it('should render groupingList items', async () => {
const { result } = renderHook(() => useGroupingListItems());

expect(result.current[0]).toEqual(
expect.objectContaining({
id: 'unread',
}),
);

expect(result.current[1]).toEqual(
expect.objectContaining({
id: 'types',
}),
);
});
18 changes: 18 additions & 0 deletions apps/meteor/client/NavBarV2/NavBarPagesGroup/hooks/useSortMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useTranslation } from 'react-i18next';

import { useGroupingListItems } from './useGroupingListItems';
import { useSortModeItems } from './useSortModeItems';

export const useSortMenu = () => {
const { t } = useTranslation();

const sortModeItems = useSortModeItems();
const groupingListItems = useGroupingListItems();

const sections = [
{ title: t('Sort_By'), items: sortModeItems },
{ title: t('Group_by'), items: groupingListItems },
];

return sections;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { renderHook } from '@testing-library/react';

import { useSortModeItems } from './useSortModeItems';

it('should render sortMode items', async () => {
const { result } = renderHook(() => useSortModeItems());

expect(result.current[0]).toEqual(
expect.objectContaining({
id: 'activity',
}),
);

expect(result.current[1]).toEqual(
expect.objectContaining({
id: 'name',
}),
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { RadioButton } from '@rocket.chat/fuselage';
import type { GenericMenuItemProps } from '@rocket.chat/ui-client';
import { useEndpoint, useUserPreference } from '@rocket.chat/ui-contexts';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';

import {
OmnichannelSortingDisclaimer,
useOmnichannelSortingDisclaimer,
} from '../../../components/Omnichannel/OmnichannelSortingDisclaimer';

export const useSortModeItems = (): GenericMenuItemProps[] => {
const { t } = useTranslation();

const saveUserPreferences = useEndpoint('POST', '/v1/users.setPreferences');
const sidebarSortBy = useUserPreference<'activity' | 'alphabetical'>('sidebarSortby', 'activity');
const isOmnichannelEnabled = useOmnichannelSortingDisclaimer();

const useHandleChange = (value: 'alphabetical' | 'activity'): (() => void) =>
useCallback(() => saveUserPreferences({ data: { sidebarSortby: value } }), [value]);

const setToAlphabetical = useHandleChange('alphabetical');
const setToActivity = useHandleChange('activity');

return [
{
id: 'activity',
content: t('Activity'),
icon: 'clock',
addon: <RadioButton onChange={setToActivity} checked={sidebarSortBy === 'activity'} />,
description: sidebarSortBy === 'activity' && isOmnichannelEnabled && <OmnichannelSortingDisclaimer />,
},
{
id: 'name',
content: t('Name'),
icon: 'sort-az',
addon: <RadioButton onChange={setToAlphabetical} checked={sidebarSortBy === 'alphabetical'} />,
description: sidebarSortBy === 'alphabetical' && isOmnichannelEnabled && <OmnichannelSortingDisclaimer />,
},
];
};
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,23 @@ export const sidePanelFiltersConfig: { [Key in AllGroupsKeys]: { title: Translat
title: 'Direct_Messages',
icon: 'at',
},
unread: {
title: 'Unread',
icon: 'flag',
},
conversations: {
title: 'Conversations',
icon: 'chat',
},
};

export type SidePanelFiltersKeys = 'all' | 'mentions' | 'favorites' | 'discussions' | 'inProgress' | 'queue' | 'onHold';

export const collapsibleFilters: SideBarFiltersKeys[] = ['teams', 'channels', 'directMessages'];
export const collapsibleFilters: SideBarFiltersKeys[] = ['unread', 'conversations', 'teams', 'channels', 'directMessages'];
export type SidePanelFiltersUnreadKeys = `${SidePanelFiltersKeys}_unread`;
export type SidePanelFilters = SidePanelFiltersKeys | SidePanelFiltersUnreadKeys;

export type SideBarFiltersKeys = 'teams' | 'channels' | 'directMessages';
export type SideBarFiltersKeys = 'teams' | 'channels' | 'directMessages' | 'conversations' | 'unread';
export type SideBarFiltersUnreadKeys = `${SideBarFiltersKeys}_unread`;
export type SideBarFilters = SidePanelFiltersKeys | SidePanelFiltersUnreadKeys;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { mockAppRoot } from '@rocket.chat/mock-providers';
import { renderHook } from '@testing-library/react';

import { useSortQueryOptions } from './useSortQueryOptions';

it("should return query option to sort by last message when user preference is 'activity'", () => {
const { result } = renderHook(() => useSortQueryOptions(), {
wrapper: mockAppRoot().withUserPreference('sidebarSortby', 'activity').build(),
});
expect(result.current.sort).toHaveProperty('lm', -1);
});

it("should return query option to sort by name when user preference is 'name'", () => {
const { result } = renderHook(() => useSortQueryOptions(), {
wrapper: mockAppRoot().withUserPreference('sidebarSortby', 'name').build(),
});
expect(result.current.sort).toHaveProperty('lowerCaseName', 1);
});

it("should return query option to sort by fname when user preference is 'name' and showRealName is true", () => {
const { result } = renderHook(() => useSortQueryOptions(), {
wrapper: mockAppRoot().withUserPreference('sidebarSortby', 'name').withSetting('UI_Use_Real_Name', true).build(),
});
expect(result.current.sort).toHaveProperty('lowerCaseFName', 1);
});
32 changes: 32 additions & 0 deletions apps/meteor/client/views/navigation/hooks/useSortQueryOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useUserPreference, useSetting } from '@rocket.chat/ui-contexts';
import { useMemo } from 'react';

export const useSortQueryOptions = (): {
sort:
| {
lm?: -1 | 1 | undefined;
}
| {
lowerCaseFName: -1 | 1;
lm?: -1 | 1 | undefined;
}
| {
lowerCaseName: -1 | 1;
lm?: -1 | 1 | undefined;
};
} => {
const sortBy = useUserPreference('sidebarSortby');
const showRealName = useSetting('UI_Use_Real_Name');

return useMemo(
() => ({
sort: {
...(sortBy === 'activity' && { lm: -1 }),
...(sortBy !== 'activity' && {
...(showRealName ? { lowerCaseFName: 1 } : { lowerCaseName: 1 }),
}),
},
}),
[sortBy, showRealName],
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import type { GroupedUnreadInfoData, AllGroupsKeys, AllGroupsKeysWithUnread } fr
import { RoomsNavigationContext, getEmptyUnreadInfo } from '../contexts/RoomsNavigationContext';
import { useSidePanelFilters } from '../hooks/useSidePanelFilters';
import { useSidePanelParentRid } from '../hooks/useSidePanelParentRid';
import { useSortQueryOptions } from '../hooks/useSortQueryOptions';

const query = { open: { $ne: false } };
const sortOptions = { sort: { lm: -1 } } as const;

const emptyQueue: ILivechatInquiryRecord[] = [];

Expand Down Expand Up @@ -65,9 +65,12 @@ type UnreadGroupDataMap = Map<AllGroupsKeys, GroupedUnreadInfoData>;
const useRoomsGroups = (): [GroupMap, UnreadGroupDataMap] => {
const showOmnichannel = useOmnichannelEnabled();
const favoritesEnabled = useUserPreference('sidebarShowFavorites');
const sidebarShowUnread = useUserPreference('sidebarShowUnread');
const sidebarGroupByType = useUserPreference('sidebarGroupByType');
const isDiscussionEnabled = useSetting('Discussion_enabled');
const options = useSortQueryOptions();

const rooms = useUserSubscriptions(query, sortOptions);
const rooms = useUserSubscriptions(query, options);

const inquiries = useQueuedInquiries();
const queue = inquiries.enabled ? inquiries.queue : emptyQueue;
Expand Down Expand Up @@ -104,6 +107,14 @@ const useRoomsGroups = (): [GroupMap, UnreadGroupDataMap] => {
return;
}

if (sidebarShowUnread && isUnread(room)) {
setGroupRoom('unread', room);
}

if (!sidebarGroupByType) {
setGroupRoom('conversations', room);
}

if (hasMention(room)) {
setGroupRoom('mentions', room);
}
Expand All @@ -112,15 +123,23 @@ const useRoomsGroups = (): [GroupMap, UnreadGroupDataMap] => {
setGroupRoom('favorites', room);
}

if (isTeamRoom(room)) {
if (sidebarGroupByType && isTeamRoom(room)) {
if (sidebarShowUnread && isUnread(room)) {
return;
}

setGroupRoom('teams', room);
}

if (isDiscussionEnabled && isDiscussion(room)) {
setGroupRoom('discussions', room);
}

if ((isPrivateRoom(room) || isPublicRoom(room)) && !isDiscussion(room) && !isTeamRoom(room)) {
if (sidebarGroupByType && (isPrivateRoom(room) || isPublicRoom(room)) && !isDiscussion(room) && !isTeamRoom(room)) {
if (sidebarShowUnread && isUnread(room)) {
return;
}

setGroupRoom('channels', room);
}

Expand All @@ -132,15 +151,19 @@ const useRoomsGroups = (): [GroupMap, UnreadGroupDataMap] => {
return setGroupRoom('inProgress', room);
}

if (isDirectMessageRoom(room)) {
if (sidebarGroupByType && isDirectMessageRoom(room)) {
if (sidebarShowUnread && isUnread(room)) {
return;
}

setGroupRoom('directMessages', room);
}

setGroupRoom('all', room);
});

return [groups, unreadGroupData];
}, [rooms, showOmnichannel, queue, favoritesEnabled, isDiscussionEnabled]),
}, [showOmnichannel, queue, rooms, sidebarShowUnread, sidebarGroupByType, favoritesEnabled, isDiscussionEnabled]),
50,
);
};
Expand Down
Loading