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 2s
284 lines
8.2 KiB
TypeScript
284 lines
8.2 KiB
TypeScript
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;
|
|
};
|