feat: collapse sidebar groups (#33592)

feat/sidebar-collapser^2
Júlia Jaeger Foresti 1 year ago committed by GitHub
parent bd64aedba8
commit 9274cf4586
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 14
      .changeset/khaki-boxes-suffer.md
  2. 46
      apps/meteor/client/sidebarv2/RoomList/RoomList.tsx
  3. 19
      apps/meteor/client/sidebarv2/hooks/useCollapsedGroups.ts
  4. 134
      apps/meteor/client/sidebarv2/hooks/useRoomList.ts
  5. 2
      apps/meteor/package.json
  6. 2
      apps/uikit-playground/package.json
  7. 2
      ee/packages/ui-theming/package.json
  8. 2
      packages/fuselage-ui-kit/package.json
  9. 2
      packages/gazzodown/package.json
  10. 2
      packages/ui-avatar/package.json
  11. 2
      packages/ui-client/package.json
  12. 2
      packages/ui-composer/package.json
  13. 2
      packages/ui-video-conf/package.json
  14. 2
      packages/ui-voip/package.json
  15. 28
      yarn.lock

@ -0,0 +1,14 @@
---
'@rocket.chat/fuselage-ui-kit': minor
'@rocket.chat/ui-theming': minor
'@rocket.chat/ui-video-conf': minor
'@rocket.chat/uikit-playground': minor
'@rocket.chat/ui-composer': minor
'@rocket.chat/gazzodown': minor
'@rocket.chat/ui-avatar': minor
'@rocket.chat/ui-client': minor
'@rocket.chat/ui-voip': minor
'@rocket.chat/meteor': minor
---
Adds ability to collapse/expand sidebar groups

@ -1,8 +1,6 @@
/* eslint-disable react/no-multi-comp */
import type { ISubscription, IRoom } from '@rocket.chat/core-typings';
import { Box, SidebarV2GroupTitle } from '@rocket.chat/fuselage';
import { Box, SidebarV2CollapseGroup } from '@rocket.chat/fuselage';
import { useResizeObserver } from '@rocket.chat/fuselage-hooks';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import { useUserPreference, useUserId } from '@rocket.chat/ui-contexts';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@ -11,6 +9,7 @@ import { GroupedVirtuoso } from 'react-virtuoso';
import { VirtuosoScrollbars } from '../../components/CustomScrollbars';
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';
@ -19,30 +18,12 @@ import RoomListRow from './RoomListRow';
import RoomListRowWrapper from './RoomListRowWrapper';
import RoomListWrapper from './RoomListWrapper';
const getRoomsByGroup = (rooms: (ISubscription & IRoom)[]) => {
const groupCounts = rooms
.reduce((acc, item, index) => {
if (typeof item === 'string') {
acc.push(index);
}
return acc;
}, [] as number[])
.map((item, index, arr) => (arr[index + 1] ? arr[index + 1] : rooms.length) - item - 1);
const groupList = rooms.filter((item) => typeof item === 'string') as unknown as TranslationKey[];
const roomList = rooms.filter((item) => typeof item !== 'string');
return {
groupCounts,
groupList,
roomList,
};
};
const RoomList = () => {
const { t } = useTranslation();
const isAnonymous = !useUserId();
const roomsList = useRoomList();
const { collapsedGroups, handleCollapsedGroups } = useCollapsedGroups();
const { groupsCount, groupsList, roomList } = useRoomList({ collapsedGroups });
const avatarTemplate = useAvatarTemplate();
const sideBarItemTemplate = useTemplateByViewMode();
const { ref } = useResizeObserver<HTMLElement>({ debounceDelay: 100 });
@ -66,14 +47,21 @@ const RoomList = () => {
usePreventDefault(ref);
useShortcutOpenMenu(ref);
const { groupCounts, groupList, roomList } = getRoomsByGroup(roomsList);
return (
<Box position='relative' display='flex' overflow='hidden' height='full' flexGrow={1} flexShrink={1} flexBasis='auto' ref={ref}>
<GroupedVirtuoso
groupCounts={groupCounts}
groupContent={(index) => <SidebarV2GroupTitle title={t(groupList[index])} />}
itemContent={(index) => <RoomListRow data={itemData} item={roomList[index]} />}
groupCounts={groupsCount}
groupContent={(index) => (
<SidebarV2CollapseGroup
title={t(groupsList[index])}
onClick={() => handleCollapsedGroups(groupsList[index])}
onKeyDown={() => handleCollapsedGroups(groupsList[index])}
expanded={!collapsedGroups.includes(groupsList[index])}
/>
)}
{...(roomList.length > 0 && {
itemContent: (index) => roomList[index] && <RoomListRow data={itemData} item={roomList[index]} />,
})}
components={{ Item: RoomListRowWrapper, List: RoomListWrapper, Scroller: VirtuosoScrollbars }}
/>
</Box>

@ -0,0 +1,19 @@
import { useLocalStorage } from '@rocket.chat/fuselage-hooks';
import { useCallback } from 'react';
export const useCollapsedGroups = () => {
const [collapsedGroups, setCollapsedGroups] = useLocalStorage<string[]>('sidebarGroups', []);
const handleCollapsedGroups = useCallback(
(group: string) => {
if (collapsedGroups.includes(group)) {
setCollapsedGroups(collapsedGroups.filter((item) => item !== group));
} else {
setCollapsedGroups([...collapsedGroups, group]);
}
},
[collapsedGroups, setCollapsedGroups],
);
return { collapsedGroups, handleCollapsedGroups };
};

@ -1,7 +1,8 @@
import type { ILivechatInquiryRecord, IRoom, ISubscription } from '@rocket.chat/core-typings';
import { useDebouncedState } from '@rocket.chat/fuselage-hooks';
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import { useUserPreference, useUserSubscriptions, useSetting } from '@rocket.chat/ui-contexts';
import { useEffect } from 'react';
import { useMemo } from 'react';
import { useVideoConfIncomingCalls } from '../../contexts/VideoConfContext';
import { useOmnichannelEnabled } from '../../hooks/omnichannel/useOmnichannelEnabled';
@ -12,19 +13,7 @@ const query = { open: { $ne: false } };
const emptyQueue: ILivechatInquiryRecord[] = [];
const order: (
| 'Incoming_Calls'
| 'Incoming_Livechats'
| 'Open_Livechats'
| 'On_Hold_Chats'
| 'Unread'
| 'Favorites'
| 'Teams'
| 'Discussions'
| 'Channels'
| 'Direct_Messages'
| 'Conversations'
)[] = [
const order = [
'Incoming_Calls',
'Incoming_Livechats',
'Open_Livechats',
@ -36,11 +25,17 @@ const order: (
'Channels',
'Direct_Messages',
'Conversations',
];
export const useRoomList = (): Array<ISubscription & IRoom> => {
const [roomList, setRoomList] = useDebouncedState<(ISubscription & IRoom)[]>([], 150);
] as const;
export const useRoomList = ({
collapsedGroups,
}: {
collapsedGroups?: string[];
}): {
roomList: Array<ISubscription & IRoom>;
groupsCount: number[];
groupsList: TranslationKey[];
} => {
const showOmnichannel = useOmnichannelEnabled();
const sidebarGroupByType = useUserPreference('sidebarGroupByType');
const favoritesEnabled = useUserPreference('sidebarShowFavorites');
@ -56,13 +51,12 @@ export const useRoomList = (): Array<ISubscription & IRoom> => {
const incomingCalls = useVideoConfIncomingCalls();
let queue = emptyQueue;
if (inquiries.enabled) {
queue = inquiries.queue;
}
const queue = inquiries.enabled ? inquiries.queue : emptyQueue;
const { groupsCount, groupsList, roomList } = useDebouncedValue(
useMemo(() => {
const isCollapsed = (groupTitle: string) => collapsedGroups?.includes(groupTitle);
useEffect(() => {
setRoomList(() => {
const incomingCall = new Set();
const favorite = new Set();
const team = new Set();
@ -83,7 +77,7 @@ export const useRoomList = (): Array<ISubscription & IRoom> => {
return incomingCall.add(room);
}
if (sidebarShowUnread && (room.alert || room.unread) && !room.hideUnreadStatus) {
if (sidebarShowUnread && (room.alert || room.unread)) {
return unread.add(room);
}
@ -118,42 +112,76 @@ export const useRoomList = (): Array<ISubscription & IRoom> => {
conversation.add(room);
});
const groups = new Map();
const groups = new Map<string, Set<any>>();
incomingCall.size && groups.set('Incoming_Calls', incomingCall);
showOmnichannel && inquiries.enabled && queue.length && groups.set('Incoming_Livechats', queue);
showOmnichannel && inquiries.enabled && queue.length && groups.set('Incoming_Livechats', new Set(queue));
showOmnichannel && omnichannel.size && groups.set('Open_Livechats', omnichannel);
showOmnichannel && onHold.size && groups.set('On_Hold_Chats', onHold);
sidebarShowUnread && unread.size && groups.set('Unread', unread);
favoritesEnabled && favorite.size && groups.set('Favorites', favorite);
sidebarGroupByType && team.size && groups.set('Teams', team);
sidebarGroupByType && isDiscussionEnabled && discussion.size && groups.set('Discussions', discussion);
sidebarGroupByType && channels.size && groups.set('Channels', channels);
sidebarGroupByType && direct.size && groups.set('Direct_Messages', direct);
!sidebarGroupByType && groups.set('Conversations', conversation);
return sidebarOrder
.map((key) => {
const group = groups.get(key);
if (!group) {
return [];
const { groupsCount, groupsList, roomList } = sidebarOrder.reduce(
(acc, key) => {
const value = groups.get(key);
if (!value) {
return acc;
}
acc.groupsList.push(key as TranslationKey);
if (isCollapsed(key)) {
acc.groupsCount.push(0);
return acc;
}
return [key, ...group];
})
.flat();
});
}, [
rooms,
showOmnichannel,
incomingCalls,
inquiries.enabled,
queue,
sidebarShowUnread,
favoritesEnabled,
sidebarGroupByType,
setRoomList,
isDiscussionEnabled,
sidebarOrder,
]);
return roomList;
acc.groupsCount.push(value.size);
acc.roomList.push(...value);
return acc;
},
{
groupsCount: [],
groupsList: [],
roomList: [],
} as {
groupsCount: number[];
groupsList: TranslationKey[];
roomList: Array<ISubscription & IRoom>;
},
);
return { groupsCount, groupsList, roomList };
}, [
rooms,
showOmnichannel,
inquiries.enabled,
queue,
sidebarShowUnread,
favoritesEnabled,
sidebarGroupByType,
isDiscussionEnabled,
sidebarOrder,
collapsedGroups,
incomingCalls,
]),
50,
);
return {
roomList,
groupsCount,
groupsList,
};
};

@ -244,7 +244,7 @@
"@rocket.chat/forked-matrix-appservice-bridge": "^4.0.2",
"@rocket.chat/forked-matrix-bot-sdk": "^0.6.0-beta.3",
"@rocket.chat/freeswitch": "workspace:^",
"@rocket.chat/fuselage": "^0.59.1",
"@rocket.chat/fuselage": "^0.59.3",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/fuselage-polyfills": "~0.31.25",
"@rocket.chat/fuselage-toastbar": "^0.33.0",

@ -16,7 +16,7 @@
"@lezer/highlight": "^1.1.6",
"@rocket.chat/core-typings": "workspace:^",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/fuselage": "^0.59.1",
"@rocket.chat/fuselage": "^0.59.3",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/fuselage-polyfills": "~0.31.25",
"@rocket.chat/fuselage-toastbar": "^0.33.0",

@ -4,7 +4,7 @@
"private": true,
"devDependencies": {
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/fuselage": "^0.59.1",
"@rocket.chat/fuselage": "^0.59.3",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/icons": "~0.38.0",
"@rocket.chat/ui-contexts": "workspace:~",

@ -52,7 +52,7 @@
"@rocket.chat/apps-engine": "workspace:^",
"@rocket.chat/core-typings": "workspace:^",
"@rocket.chat/eslint-config": "workspace:^",
"@rocket.chat/fuselage": "^0.59.1",
"@rocket.chat/fuselage": "^0.59.3",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/fuselage-polyfills": "~0.31.25",
"@rocket.chat/icons": "~0.38.0",

@ -30,7 +30,7 @@
"@babel/core": "~7.25.8",
"@rocket.chat/core-typings": "workspace:^",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/fuselage": "^0.59.1",
"@rocket.chat/fuselage": "^0.59.3",
"@rocket.chat/fuselage-tokens": "^0.33.1",
"@rocket.chat/jest-presets": "workspace:~",
"@rocket.chat/message-parser": "workspace:^",

@ -4,7 +4,7 @@
"private": true,
"devDependencies": {
"@babel/core": "~7.25.8",
"@rocket.chat/fuselage": "^0.59.1",
"@rocket.chat/fuselage": "^0.59.3",
"@rocket.chat/ui-contexts": "workspace:^",
"@types/react": "~17.0.80",
"@types/react-dom": "~17.0.25",

@ -21,7 +21,7 @@
"@babel/core": "~7.25.8",
"@react-aria/toolbar": "^3.0.0-beta.1",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/fuselage": "^0.59.1",
"@rocket.chat/fuselage": "^0.59.3",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/icons": "~0.38.0",
"@rocket.chat/jest-presets": "workspace:~",

@ -21,7 +21,7 @@
"@babel/core": "~7.25.8",
"@react-aria/toolbar": "^3.0.0-beta.1",
"@rocket.chat/eslint-config": "workspace:^",
"@rocket.chat/fuselage": "^0.59.1",
"@rocket.chat/fuselage": "^0.59.3",
"@rocket.chat/icons": "~0.38.0",
"@storybook/addon-actions": "^8.3.5",
"@storybook/addon-docs": "^8.3.5",

@ -24,7 +24,7 @@
"@babel/core": "~7.25.8",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/eslint-config": "workspace:^",
"@rocket.chat/fuselage": "^0.59.1",
"@rocket.chat/fuselage": "^0.59.3",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/icons": "~0.38.0",
"@rocket.chat/jest-presets": "workspace:~",

@ -28,7 +28,7 @@
"@faker-js/faker": "~8.0.2",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/eslint-config": "workspace:^",
"@rocket.chat/fuselage": "^0.59.1",
"@rocket.chat/fuselage": "^0.59.3",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/icons": "~0.38.0",
"@rocket.chat/jest-presets": "workspace:~",

@ -8092,7 +8092,7 @@ __metadata:
"@rocket.chat/apps-engine": "workspace:^"
"@rocket.chat/core-typings": "workspace:^"
"@rocket.chat/eslint-config": "workspace:^"
"@rocket.chat/fuselage": "npm:^0.59.1"
"@rocket.chat/fuselage": "npm:^0.59.3"
"@rocket.chat/fuselage-hooks": "npm:^0.33.1"
"@rocket.chat/fuselage-polyfills": "npm:~0.31.25"
"@rocket.chat/gazzodown": "workspace:^"
@ -8151,9 +8151,9 @@ __metadata:
languageName: unknown
linkType: soft
"@rocket.chat/fuselage@npm:^0.59.1":
version: 0.59.1
resolution: "@rocket.chat/fuselage@npm:0.59.1"
"@rocket.chat/fuselage@npm:^0.59.3":
version: 0.59.3
resolution: "@rocket.chat/fuselage@npm:0.59.3"
dependencies:
"@rocket.chat/css-in-js": "npm:^0.31.25"
"@rocket.chat/css-supports": "npm:^0.31.25"
@ -8171,7 +8171,7 @@ __metadata:
react: ^17.0.2
react-dom: ^17.0.2
react-virtuoso: 1.2.4
checksum: 10/8451ad891a2731d310581e9dae96cee197d34db940c3933d5d875e9710d66e643b19164472af4727470e84bf35442f292d0d9dc11421b34aecdb0ecc83be1ccf
checksum: 10/b3677f4b7b8fc161757ff93d3619ed599ff2474b9243c8955863acbeb9b41a539029e4598be471b03e0be0219e985ddb9d15f42e306bcb060288c35f9752c5af
languageName: node
linkType: hard
@ -8182,7 +8182,7 @@ __metadata:
"@babel/core": "npm:~7.25.8"
"@rocket.chat/core-typings": "workspace:^"
"@rocket.chat/css-in-js": "npm:~0.31.25"
"@rocket.chat/fuselage": "npm:^0.59.1"
"@rocket.chat/fuselage": "npm:^0.59.3"
"@rocket.chat/fuselage-tokens": "npm:^0.33.1"
"@rocket.chat/jest-presets": "workspace:~"
"@rocket.chat/message-parser": "workspace:^"
@ -8553,7 +8553,7 @@ __metadata:
"@rocket.chat/forked-matrix-appservice-bridge": "npm:^4.0.2"
"@rocket.chat/forked-matrix-bot-sdk": "npm:^0.6.0-beta.3"
"@rocket.chat/freeswitch": "workspace:^"
"@rocket.chat/fuselage": "npm:^0.59.1"
"@rocket.chat/fuselage": "npm:^0.59.3"
"@rocket.chat/fuselage-hooks": "npm:^0.33.1"
"@rocket.chat/fuselage-polyfills": "npm:~0.31.25"
"@rocket.chat/fuselage-toastbar": "npm:^0.33.0"
@ -9445,7 +9445,7 @@ __metadata:
resolution: "@rocket.chat/ui-avatar@workspace:packages/ui-avatar"
dependencies:
"@babel/core": "npm:~7.25.8"
"@rocket.chat/fuselage": "npm:^0.59.1"
"@rocket.chat/fuselage": "npm:^0.59.3"
"@rocket.chat/ui-contexts": "workspace:^"
"@types/react": "npm:~17.0.80"
"@types/react-dom": "npm:~17.0.25"
@ -9470,7 +9470,7 @@ __metadata:
"@babel/core": "npm:~7.25.8"
"@react-aria/toolbar": "npm:^3.0.0-beta.1"
"@rocket.chat/css-in-js": "npm:~0.31.25"
"@rocket.chat/fuselage": "npm:^0.59.1"
"@rocket.chat/fuselage": "npm:^0.59.3"
"@rocket.chat/fuselage-hooks": "npm:^0.33.1"
"@rocket.chat/icons": "npm:~0.38.0"
"@rocket.chat/jest-presets": "workspace:~"
@ -9522,7 +9522,7 @@ __metadata:
"@babel/core": "npm:~7.25.8"
"@react-aria/toolbar": "npm:^3.0.0-beta.1"
"@rocket.chat/eslint-config": "workspace:^"
"@rocket.chat/fuselage": "npm:^0.59.1"
"@rocket.chat/fuselage": "npm:^0.59.3"
"@rocket.chat/icons": "npm:~0.38.0"
"@storybook/addon-actions": "npm:^8.3.5"
"@storybook/addon-docs": "npm:^8.3.5"
@ -9616,7 +9616,7 @@ __metadata:
resolution: "@rocket.chat/ui-theming@workspace:ee/packages/ui-theming"
dependencies:
"@rocket.chat/css-in-js": "npm:~0.31.25"
"@rocket.chat/fuselage": "npm:^0.59.1"
"@rocket.chat/fuselage": "npm:^0.59.3"
"@rocket.chat/fuselage-hooks": "npm:^0.33.1"
"@rocket.chat/icons": "npm:~0.38.0"
"@rocket.chat/ui-contexts": "workspace:~"
@ -9646,7 +9646,7 @@ __metadata:
"@rocket.chat/css-in-js": "npm:~0.31.25"
"@rocket.chat/emitter": "npm:~0.31.25"
"@rocket.chat/eslint-config": "workspace:^"
"@rocket.chat/fuselage": "npm:^0.59.1"
"@rocket.chat/fuselage": "npm:^0.59.3"
"@rocket.chat/fuselage-hooks": "npm:^0.33.1"
"@rocket.chat/icons": "npm:~0.38.0"
"@rocket.chat/jest-presets": "workspace:~"
@ -9695,7 +9695,7 @@ __metadata:
"@rocket.chat/css-in-js": "npm:~0.31.25"
"@rocket.chat/emitter": "npm:~0.31.25"
"@rocket.chat/eslint-config": "workspace:^"
"@rocket.chat/fuselage": "npm:^0.59.1"
"@rocket.chat/fuselage": "npm:^0.59.3"
"@rocket.chat/fuselage-hooks": "npm:^0.33.1"
"@rocket.chat/icons": "npm:~0.38.0"
"@rocket.chat/jest-presets": "workspace:~"
@ -9752,7 +9752,7 @@ __metadata:
"@lezer/highlight": "npm:^1.1.6"
"@rocket.chat/core-typings": "workspace:^"
"@rocket.chat/css-in-js": "npm:~0.31.25"
"@rocket.chat/fuselage": "npm:^0.59.1"
"@rocket.chat/fuselage": "npm:^0.59.3"
"@rocket.chat/fuselage-hooks": "npm:^0.33.1"
"@rocket.chat/fuselage-polyfills": "npm:~0.31.25"
"@rocket.chat/fuselage-toastbar": "npm:^0.33.0"

Loading…
Cancel
Save