snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 01:21:02 +05:00
commit 30810eecd7
10161 changed files with 987110 additions and 0 deletions

View File

@@ -0,0 +1,283 @@
import { type ISubscription, type ILivechatInquiryRecord, type IRoom, isTeamRoom, isDirectMessageRoom } from '@rocket.chat/core-typings';
import { useStableCallback, useLocalStorage } from '@rocket.chat/fuselage-hooks';
import type { Keys as IconName } from '@rocket.chat/icons';
import { isTruthy } from '@rocket.chat/tools';
import type { SubscriptionWithRoom, TranslationKey } from '@rocket.chat/ui-contexts';
import { createContext, useCallback, useContext, useEffect, useMemo } from 'react';
import { useCollapsedGroups } from '../hooks/useCollapsedGroups';
export const sidePanelFiltersConfig: { [Key in AllGroupsKeys]: { title: TranslationKey; icon: IconName } } = {
all: {
title: 'All',
icon: 'inbox',
},
favorites: {
title: 'Favorites',
icon: 'star',
},
mentions: {
title: 'Mentions',
icon: 'at',
},
discussions: {
title: 'Discussions',
icon: 'balloons',
},
inProgress: {
title: 'In_progress',
icon: 'user-arrow-right',
},
queue: {
title: 'Queue',
icon: 'burger-arrow-left',
},
onHold: {
title: 'On_Hold',
icon: 'pause-unfilled',
},
teams: {
title: 'Teams',
icon: 'team',
},
channels: {
title: 'Channels',
icon: 'hashtag',
},
directMessages: {
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[] = ['unread', 'conversations', 'teams', 'channels', 'directMessages'];
export type SidePanelFiltersUnreadKeys = `${SidePanelFiltersKeys}_unread`;
export type SidePanelFilters = SidePanelFiltersKeys | SidePanelFiltersUnreadKeys;
export type SideBarFiltersKeys = 'teams' | 'channels' | 'directMessages' | 'conversations' | 'unread';
export type SideBarFiltersUnreadKeys = `${SideBarFiltersKeys}_unread`;
export type SideBarFilters = SidePanelFiltersKeys | SidePanelFiltersUnreadKeys;
export type AllGroupsKeys = SidePanelFiltersKeys | SideBarFiltersKeys;
export type AllGroupsKeysWithUnread = SidePanelFilters | SideBarFiltersKeys | SideBarFiltersUnreadKeys;
export type RecordTypeBySidebarKey<K extends AllGroupsKeysWithUnread> = K extends 'queue' ? ILivechatInquiryRecord : SubscriptionWithRoom;
// eslint-disable-next-line @typescript-eslint/naming-convention
export interface RoomsNavigationGroup extends Map<AllGroupsKeysWithUnread, Set<RecordTypeBySidebarKey<AllGroupsKeysWithUnread>>> {
get<K extends AllGroupsKeysWithUnread>(key: K): Set<RecordTypeBySidebarKey<K>> | undefined;
}
export type RoomsNavigationContextValue = {
groups: RoomsNavigationGroup;
currentFilter: AllGroupsKeysWithUnread;
setFilter: (filter: AllGroupsKeys, unread: boolean, parentRid?: IRoom['_id']) => void;
unreadGroupData: Map<AllGroupsKeys, GroupedUnreadInfoData>;
parentRid?: IRoom['_id'];
};
export type GroupedUnreadInfoData = {
userMentions: number;
groupMentions: number;
tunread: string[];
tunreadUser: string[];
unread: number;
};
export const RoomsNavigationContext = createContext<RoomsNavigationContextValue | undefined>(undefined);
export const useRoomsListContext = () => {
const contextValue = useContext(RoomsNavigationContext);
if (!contextValue) {
throw new Error('useRoomsListContext must be used within a RoomsNavigationContext');
}
return contextValue;
};
// Helper functions
export const splitFilter = (currentFilter: AllGroupsKeysWithUnread): [SidePanelFiltersKeys, boolean] => {
const [currentTab, unread] = currentFilter.split('_');
return [currentTab as SidePanelFiltersKeys, unread === 'unread'];
};
export const getFilterKey = (tab: AllGroupsKeys, unread: boolean): AllGroupsKeysWithUnread => {
return unread ? `${tab}_unread` : tab;
};
export const getEmptyUnreadInfo = (): GroupedUnreadInfoData => ({
userMentions: 0,
groupMentions: 0,
tunread: [],
tunreadUser: [],
unread: 0,
});
// Hooks
type RoomListGroup<T extends AllGroupsKeys> = {
group: T;
rooms: Array<T extends SideBarFiltersKeys ? SubscriptionWithRoom : ILivechatInquiryRecord>;
unreadInfo: GroupedUnreadInfoData;
};
export const useSideBarRoomsList = (): {
roomListGroups: RoomListGroup<SideBarFiltersKeys>[];
groupCounts: number[];
totalCount: number;
} & ReturnType<typeof useCollapsedGroups> => {
const { collapsedGroups, handleClick, handleKeyDown } = useCollapsedGroups();
const { groups, unreadGroupData } = useRoomsListContext();
const roomListGroups = collapsibleFilters
.map((group) => {
const roomSet = (groups as Map<SideBarFiltersKeys, Set<SubscriptionWithRoom>>).get(group);
const rooms = roomSet ? Array.from(roomSet) : [];
const unreadInfo = unreadGroupData.get(group) || getEmptyUnreadInfo();
if (!rooms.length) {
return undefined;
}
return { group, rooms, unreadInfo };
})
.filter(isTruthy);
const groupCounts = roomListGroups.map((group) => {
if (collapsedGroups.includes(group.group)) {
return 0;
}
return group.rooms.length;
});
return {
collapsedGroups,
handleClick,
handleKeyDown,
roomListGroups,
groupCounts,
totalCount: groupCounts.reduce((acc, count) => acc + count, 0),
};
};
export const isUnreadSubscription = (subscription: Partial<ISubscription>): boolean => {
if (subscription.hideUnreadStatus) {
return false;
}
return Boolean(
subscription.userMentions ||
subscription.groupMentions ||
subscription.tunread?.length ||
subscription.tunreadUser?.length ||
subscription.unread ||
subscription.alert,
);
};
export const useSidePanelQueueListTab = (): Array<ILivechatInquiryRecord> => {
return Array.from(useRoomsListContext().groups.get('queue') || []);
};
export const useSidePanelRoomsListTab = <K extends Exclude<AllGroupsKeys, 'queue'>>(tab: K): Array<RecordTypeBySidebarKey<K>> => {
const [, unread] = useSidePanelFilter();
const roomSet = useRoomsListContext().groups.get(tab);
const roomsList = useMemo(() => {
if (!roomSet) {
return [];
}
if (!unread) {
return Array.from(roomSet);
}
return Array.from(roomSet)
.reduce(
(result, room) => {
if (isUnreadSubscription(room)) {
result[0].push(room);
return result;
}
result[1].push(room);
return result;
},
[[], []] as [Array<RecordTypeBySidebarKey<K>>, Array<RecordTypeBySidebarKey<K>>],
)
.flat();
}, [roomSet, unread]);
return roomsList;
};
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] => {
const { setFilter, parentRid } = useRoomsListContext();
const [currentTab, unread] = useSidePanelFilter();
return [unread, useStableCallback(() => setFilter(currentTab, !unread, parentRid))];
};
export const useSwitchSidePanelTab = () => {
const { setFilter } = useRoomsListContext();
const [, unread] = useSidePanelFilter();
return (tab: AllGroupsKeys, { parentRid }: { parentRid?: IRoom['_id'] } = {}) => {
setFilter(tab, unread, parentRid);
};
};
export const useUnreadGroupData = (key: SidePanelFiltersKeys) => useRoomsListContext().unreadGroupData.get(key) || getEmptyUnreadInfo();
export const useIsRoomFilter = () => {
const [currentTab] = useSidePanelFilter();
return useMemo(() => collapsibleFilters.some((group) => currentTab === group), [currentTab]);
};
export const useRedirectToDefaultTab = (shouldRedirect: boolean) => {
const switchSidePanelTab = useSwitchSidePanelTab();
useEffect(() => {
if (shouldRedirect) {
switchSidePanelTab('all');
}
}, [shouldRedirect, switchSidePanelTab]);
};
export const useRedirectToFilter = () => {
const switchSidePanelTab = useSwitchSidePanelTab();
const handleRedirect = useCallback(
(room: SubscriptionWithRoom) => {
if (isTeamRoom(room)) {
switchSidePanelTab('teams', { parentRid: room.rid });
return;
}
if (isDirectMessageRoom(room)) {
switchSidePanelTab('directMessages', { parentRid: room.rid });
return;
}
switchSidePanelTab('channels', { parentRid: room.prid || room.rid });
},
[switchSidePanelTab],
);
return handleRedirect;
};