Skip to content

Commit

Permalink
Merge branch 'develop' into chore/bump-storybok-fuselage-ui-kit-cloud…
Browse files Browse the repository at this point in the history
…-surface
  • Loading branch information
lucas-a-pelegrino authored Nov 13, 2024
2 parents df8e534 + 8e47f50 commit 038d4fa
Show file tree
Hide file tree
Showing 19 changed files with 479 additions and 290 deletions.
5 changes: 5 additions & 0 deletions .changeset/friendly-ravens-teach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': minor
---

adds unread badge to sidebar collapser
5 changes: 5 additions & 0 deletions .changeset/metal-avocados-serve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/livechat": patch
---

Fixes the 'Finish Chat' option in Livechat appearing before the conversation is started, which caused the action to fail.
2 changes: 0 additions & 2 deletions .kodiak.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,5 @@ include_coauthors=true
[merge.automerge_dependencies]
versions = ["minor", "patch"]
usernames = ["dependabot"]
[update]
ignored_usernames = ["dependabot"]
[approve]
auto_approve_usernames = ["dependabot"]
12 changes: 7 additions & 5 deletions apps/meteor/client/sidebarv2/RoomList/RoomList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, SidebarV2CollapseGroup } from '@rocket.chat/fuselage';
import { Box } from '@rocket.chat/fuselage';
import { useResizeObserver } from '@rocket.chat/fuselage-hooks';
import { useUserPreference, useUserId } from '@rocket.chat/ui-contexts';
import React, { useMemo } from 'react';
Expand All @@ -13,6 +13,7 @@ import { usePreventDefault } from '../hooks/usePreventDefault';
import { useRoomList } from '../hooks/useRoomList';
import { useShortcutOpenMenu } from '../hooks/useShortcutOpenMenu';
import { useTemplateByViewMode } from '../hooks/useTemplateByViewMode';
import RoomListCollapser from './RoomListCollapser';
import RoomListRow from './RoomListRow';
import RoomListRowWrapper from './RoomListRowWrapper';
import RoomListWrapper from './RoomListWrapper';
Expand All @@ -22,7 +23,7 @@ const RoomList = () => {
const isAnonymous = !useUserId();

const { collapsedGroups, handleClick, handleKeyDown } = useCollapsedGroups();
const { groupsCount, groupsList, roomList } = useRoomList({ collapsedGroups });
const { groupsCount, groupsList, roomList, groupedUnreadInfo } = useRoomList({ collapsedGroups });
const avatarTemplate = useAvatarTemplate();
const sideBarItemTemplate = useTemplateByViewMode();
const { ref } = useResizeObserver<HTMLElement>({ debounceDelay: 100 });
Expand Down Expand Up @@ -51,11 +52,12 @@ const RoomList = () => {
<GroupedVirtuoso
groupCounts={groupsCount}
groupContent={(index) => (
<SidebarV2CollapseGroup
title={t(groupsList[index])}
<RoomListCollapser
collapsedGroups={collapsedGroups}
onClick={() => handleClick(groupsList[index])}
onKeyDown={(e) => handleKeyDown(e, groupsList[index])}
expanded={!collapsedGroups.includes(groupsList[index])}
groupTitle={groupsList[index]}
unreadCount={groupedUnreadInfo[index]}
/>
)}
{...(roomList.length > 0 && {
Expand Down
37 changes: 37 additions & 0 deletions apps/meteor/client/sidebarv2/RoomList/RoomListCollapser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { ISubscription } from '@rocket.chat/core-typings';
import { Badge, SidebarV2CollapseGroup } from '@rocket.chat/fuselage';
import type { HTMLAttributes, KeyboardEvent, MouseEventHandler } from 'react';
import React from 'react';
import { useTranslation } from 'react-i18next';

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

type RoomListCollapserProps = {
groupTitle: string;
collapsedGroups: string[];
onClick: MouseEventHandler<HTMLElement>;
onKeyDown: (e: KeyboardEvent) => void;
unreadCount: Pick<ISubscription, 'userMentions' | 'groupMentions' | 'unread' | 'tunread' | 'tunreadUser' | 'tunreadGroup'>;
} & Omit<HTMLAttributes<HTMLElement>, 'onClick' | 'onKeyDown'>;
const RoomListCollapser = ({ groupTitle, unreadCount: unreadGroupCount, collapsedGroups, ...props }: RoomListCollapserProps) => {
const { t } = useTranslation();

const { unreadTitle, unreadVariant, showUnread, unreadCount } = useUnreadDisplay(unreadGroupCount);

return (
<SidebarV2CollapseGroup
title={t(groupTitle)}
expanded={!collapsedGroups.includes(groupTitle)}
badge={
showUnread ? (
<Badge variant={unreadVariant} title={unreadTitle} aria-label={unreadTitle} role='status'>
{unreadCount.total}
</Badge>
) : undefined
}
{...props}
/>
);
};

export default RoomListCollapser;
78 changes: 52 additions & 26 deletions apps/meteor/client/sidebarv2/hooks/useRoomList.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,53 @@ const user = createFakeUser({
type: 'user',
});

const unreadRooms = [
{ ...createFakeSubscription({ t: 'c', unread: 1 }), ...createFakeRoom({ t: 'c' }) },
{ ...createFakeSubscription({ t: 'c', unread: 1 }), ...createFakeRoom({ t: 'c' }) },
{ ...createFakeSubscription({ t: 'c', unread: 1 }), ...createFakeRoom({ t: 'c' }) },
{ ...createFakeSubscription({ t: 'c', unread: 1 }), ...createFakeRoom({ t: 'c' }) },
const emptyUnread = {
userMentions: 0,
groupMentions: 0,
unread: 0,
tunread: undefined,
tunreadUser: undefined,
tunreadGroup: undefined,
alert: false,
};

const unreadChannels = [
{ ...createFakeSubscription({ t: 'c', tunread: ['1'] }), ...createFakeRoom({ t: 'c' }) },
{ ...createFakeSubscription({ t: 'c', tunread: ['1'] }), ...createFakeRoom({ t: 'c' }) },
{ ...createFakeSubscription({ t: 'c', tunreadUser: ['1'] }), ...createFakeRoom({ t: 'c' }) },
{ ...createFakeSubscription({ t: 'c', tunreadUser: ['1'] }), ...createFakeRoom({ t: 'c' }) },
];

const favoriteRooms = [
{ ...createFakeSubscription({ t: 'c', f: true, unread: undefined }), ...createFakeRoom({ t: 'c' }) },
{ ...createFakeSubscription({ t: 'c', f: true, unread: undefined }), ...createFakeRoom({ t: 'c' }) },
{ ...createFakeSubscription({ t: 'c', f: true, unread: undefined }), ...createFakeRoom({ t: 'c' }) },
{ ...createFakeSubscription({ t: 'c', f: true, ...emptyUnread }), ...createFakeRoom({ t: 'c' }) },
{ ...createFakeSubscription({ t: 'c', f: true, ...emptyUnread }), ...createFakeRoom({ t: 'c' }) },
{ ...createFakeSubscription({ t: 'c', f: true, ...emptyUnread }), ...createFakeRoom({ t: 'c' }) },
];

const teams = [
{ ...createFakeSubscription({ unread: undefined }), ...createFakeRoom({ teamMain: true }) },
{ ...createFakeSubscription({ unread: undefined }), ...createFakeRoom({ teamMain: true }) },
{ ...createFakeSubscription({ unread: undefined }), ...createFakeRoom({ teamMain: true }) },
{ ...createFakeSubscription({ unread: undefined }), ...createFakeRoom({ teamMain: true }) },
{ ...createFakeSubscription({ unread: undefined }), ...createFakeRoom({ teamMain: true }) },
{ ...createFakeSubscription({ ...emptyUnread }), ...createFakeRoom({ teamMain: true }) },
{ ...createFakeSubscription({ ...emptyUnread }), ...createFakeRoom({ teamMain: true }) },
{ ...createFakeSubscription({ ...emptyUnread }), ...createFakeRoom({ teamMain: true }) },
{ ...createFakeSubscription({ ...emptyUnread }), ...createFakeRoom({ teamMain: true }) },
{ ...createFakeSubscription({ ...emptyUnread }), ...createFakeRoom({ teamMain: true }) },
];

const discussionRooms = [
{ ...createFakeSubscription({ unread: undefined }), ...createFakeRoom({ prid: '123' }) },
{ ...createFakeSubscription({ unread: undefined }), ...createFakeRoom({ prid: '124' }) },
{ ...createFakeSubscription({ unread: undefined }), ...createFakeRoom({ prid: '125' }) },
{ ...createFakeSubscription({ unread: undefined }), ...createFakeRoom({ prid: '126' }) },
{ ...createFakeSubscription({ unread: undefined }), ...createFakeRoom({ prid: '127' }) },
{ ...createFakeSubscription({ ...emptyUnread }), ...createFakeRoom({ prid: '123' }) },
{ ...createFakeSubscription({ ...emptyUnread }), ...createFakeRoom({ prid: '124' }) },
{ ...createFakeSubscription({ ...emptyUnread }), ...createFakeRoom({ prid: '125' }) },
{ ...createFakeSubscription({ ...emptyUnread }), ...createFakeRoom({ prid: '126' }) },
{ ...createFakeSubscription({ ...emptyUnread }), ...createFakeRoom({ prid: '127' }) },
];

const directRooms = [
{ ...createFakeSubscription({ t: 'd', unread: undefined }), ...createFakeRoom({ t: 'd' }) },
{ ...createFakeSubscription({ t: 'd', unread: undefined }), ...createFakeRoom({ t: 'd' }) },
{ ...createFakeSubscription({ t: 'd', unread: undefined }), ...createFakeRoom({ t: 'd' }) },
{ ...createFakeSubscription({ t: 'd', unread: undefined }), ...createFakeRoom({ t: 'd' }) },
{ ...createFakeSubscription({ t: 'd', ...emptyUnread }), ...createFakeRoom({ t: 'd' }) },
{ ...createFakeSubscription({ t: 'd', ...emptyUnread }), ...createFakeRoom({ t: 'd' }) },
{ ...createFakeSubscription({ t: 'd', ...emptyUnread }), ...createFakeRoom({ t: 'd' }) },
{ ...createFakeSubscription({ t: 'd', ...emptyUnread }), ...createFakeRoom({ t: 'd' }) },
];

const fakeRooms = [...unreadRooms, ...favoriteRooms, ...teams, ...discussionRooms, ...directRooms];
const fakeRooms = [...unreadChannels, ...favoriteRooms, ...teams, ...discussionRooms, ...directRooms];

const emptyArr: any[] = [];

Expand Down Expand Up @@ -228,7 +238,7 @@ it('should return "Unread" group with the correct items if sidebarShowUnread is
});
const unreadIndex = result.current.groupsList.indexOf('Unread');
expect(result.current.groupsList).toContain('Unread');
expect(result.current.groupsCount[unreadIndex]).toEqual(unreadRooms.length);
expect(result.current.groupsCount[unreadIndex]).toEqual(unreadChannels.length);
});

it('should not include unread room in unread group if hideUnreadStatus is enabled', async () => {
Expand All @@ -246,6 +256,22 @@ it('should not include unread room in unread group if hideUnreadStatus is enable
const unreadIndex = result.current.groupsList.indexOf('Unread');
const roomListUnread = result.current.roomList.filter((room) => room.unread);

expect(result.current.groupsCount[unreadIndex]).toEqual(unreadRooms.length);
expect(roomListUnread.length).not.toEqual(unreadRooms.length);
expect(result.current.groupsCount[unreadIndex]).toEqual(unreadChannels.length);
expect(roomListUnread.length).not.toEqual(unreadChannels.length);
});

it('should accumulate unread data into `groupedUnreadInfo` when group is collapsed', async () => {
const { result } = renderHook(() => useRoomList({ collapsedGroups: ['Channels'] }), {
legacyRoot: true,
wrapper: getWrapperSettings({ sidebarGroupByType: true }).build(),
});

const channelsIndex = result.current.groupsList.indexOf('Channels');
const { groupMentions, unread, userMentions, tunread, tunreadUser } = result.current.groupedUnreadInfo[channelsIndex];

expect(groupMentions).toEqual(fakeRooms.reduce((acc, cv) => acc + cv.groupMentions, 0));
expect(unread).toEqual(fakeRooms.reduce((acc, cv) => acc + cv.unread, 0));
expect(userMentions).toEqual(fakeRooms.reduce((acc, cv) => acc + cv.userMentions, 0));
expect(tunread).toEqual(fakeRooms.reduce((acc, cv) => [...acc, ...(cv.tunread || [])], [] as string[]));
expect(tunreadUser).toEqual(fakeRooms.reduce((acc, cv) => [...acc, ...(cv.tunreadUser || [])], [] as string[]));
});
57 changes: 42 additions & 15 deletions apps/meteor/client/sidebarv2/hooks/useRoomList.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ILivechatInquiryRecord, IRoom, ISubscription } from '@rocket.chat/core-typings';
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import type { SubscriptionWithRoom, TranslationKey } from '@rocket.chat/ui-contexts';
import { useUserPreference, useUserSubscriptions, useSetting } from '@rocket.chat/ui-contexts';
import { useMemo } from 'react';

Expand All @@ -27,15 +27,16 @@ const order = [
'Conversations',
] as const;

export const useRoomList = ({
collapsedGroups,
}: {
collapsedGroups?: string[];
}): {
type useRoomListReturnType = {
roomList: Array<ISubscription & IRoom>;
groupsCount: number[];
groupsList: TranslationKey[];
} => {
groupedUnreadInfo: Pick<
SubscriptionWithRoom,
'userMentions' | 'groupMentions' | 'unread' | 'tunread' | 'tunreadUser' | 'tunreadGroup' | 'alert' | 'hideUnreadStatus'
>[];
};
export const useRoomList = ({ collapsedGroups }: { collapsedGroups?: string[] }): useRoomListReturnType => {
const showOmnichannel = useOmnichannelEnabled();
const sidebarGroupByType = useUserPreference('sidebarGroupByType');
const favoritesEnabled = useUserPreference('sidebarShowFavorites');
Expand All @@ -53,7 +54,7 @@ export const useRoomList = ({

const queue = inquiries.enabled ? inquiries.queue : emptyQueue;

const { groupsCount, groupsList, roomList } = useDebouncedValue(
const { groupsCount, groupsList, roomList, groupedUnreadInfo } = useDebouncedValue(
useMemo(() => {
const isCollapsed = (groupTitle: string) => collapsedGroups?.includes(groupTitle);

Expand Down Expand Up @@ -133,7 +134,7 @@ export const useRoomList = ({

!sidebarGroupByType && groups.set('Conversations', conversation);

const { groupsCount, groupsList, roomList } = sidebarOrder.reduce(
const { groupsCount, groupsList, roomList, groupedUnreadInfo } = sidebarOrder.reduce(
(acc, key) => {
const value = groups.get(key);

Expand All @@ -142,11 +143,39 @@ export const useRoomList = ({
}

acc.groupsList.push(key as TranslationKey);

const groupedUnreadInfoAcc = {
userMentions: 0,
groupMentions: 0,
tunread: [],
tunreadUser: [],
unread: 0,
};

if (isCollapsed(key)) {
const groupedUnreadInfo = [...value].reduce(
(counter, { userMentions, groupMentions, tunread, tunreadUser, unread, alert, hideUnreadStatus }) => {
if (hideUnreadStatus) {
return counter;
}

counter.userMentions += userMentions || 0;
counter.groupMentions += groupMentions || 0;
counter.tunread = [...counter.tunread, ...(tunread || [])];
counter.tunreadUser = [...counter.tunreadUser, ...(tunreadUser || [])];
counter.unread += unread || 0;
!unread && !tunread?.length && alert && (counter.unread += 1);
return counter;
},
groupedUnreadInfoAcc,
);

acc.groupedUnreadInfo.push(groupedUnreadInfo);
acc.groupsCount.push(0);
return acc;
}

acc.groupedUnreadInfo.push(groupedUnreadInfoAcc);
acc.groupsCount.push(value.size);
acc.roomList.push(...value);
return acc;
Expand All @@ -155,14 +184,11 @@ export const useRoomList = ({
groupsCount: [],
groupsList: [],
roomList: [],
} as {
groupsCount: number[];
groupsList: TranslationKey[];
roomList: Array<ISubscription & IRoom>;
},
groupedUnreadInfo: [],
} as useRoomListReturnType,
);

return { groupsCount, groupsList, roomList };
return { groupsCount, groupsList, roomList, groupedUnreadInfo };
}, [
rooms,
showOmnichannel,
Expand All @@ -183,5 +209,6 @@ export const useRoomList = ({
roomList,
groupsCount,
groupsList,
groupedUnreadInfo,
};
};
2 changes: 1 addition & 1 deletion apps/meteor/ee/server/services/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"bcrypt": "^5.1.1",
"body-parser": "^1.20.3",
"colorette": "^2.0.20",
"cookie": "^0.5.0",
"cookie": "^0.7.0",
"cookie-parser": "^1.4.7",
"ejson": "^2.2.3",
"eventemitter3": "^5.0.1",
Expand Down
4 changes: 2 additions & 2 deletions apps/meteor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@
"colorette": "^2.0.20",
"colors": "^1.4.0",
"connect": "^3.7.0",
"cookie": "^0.5.0",
"cookie": "^0.7.0",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"cron": "~1.8.2",
Expand Down Expand Up @@ -413,7 +413,7 @@
"react-error-boundary": "^3.1.4",
"react-hook-form": "~7.45.4",
"react-i18next": "~13.2.2",
"react-keyed-flatten-children": "^1.3.0",
"react-keyed-flatten-children": "^3.0.2",
"react-virtuoso": "^4.12.0",
"sanitize-html": "^2.13.1",
"semver": "^7.6.3",
Expand Down
Loading

0 comments on commit 038d4fa

Please sign in to comment.