feat(friends): group chip-filter row over online/offline list + manager wiring

This commit is contained in:
simoleo89
2026-06-02 18:20:31 +02:00
parent 7913952f71
commit 860ec07dfd
2 changed files with 64 additions and 10 deletions
@@ -0,0 +1,38 @@
import { FriendCategoryData } from '@nitrots/nitro-renderer';
import { FC } from 'react';
import { LocalizeText, MessengerFriend, countFriendsByCategory } from '../../../../api';
import { Flex } from '../../../../common';
interface FriendsListGroupChipsViewProps
{
categories: FriendCategoryData[];
friends: MessengerFriend[];
selectedCategoryId: number;
setSelectedCategoryId: (id: number) => void;
onManageClick: () => void;
}
export const FriendsListGroupChipsView: FC<FriendsListGroupChipsViewProps> = props =>
{
const { categories = [], friends = [], selectedCategoryId = 0, setSelectedCategoryId = null, onManageClick = null } = props;
const counts = countFriendsByCategory(friends);
return (
<Flex alignItems="center" className="friends-group-chips px-2 py-1" gap={ 1 }>
<Flex alignItems="center" className="friends-group-chips-scroll" gap={ 1 }>
<div className={ `friends-group-chip${ (selectedCategoryId === 0) ? ' active' : '' }` } onClick={ () => setSelectedCategoryId(0) }>
{ LocalizeText('friendlist.friends') } ({ friends.length })
</div>
{ categories.map(category => (
<div key={ category.id } className={ `friends-group-chip${ (selectedCategoryId === category.id) ? ' active' : '' }` } onClick={ () => setSelectedCategoryId(category.id) }>
{ category.name } ({ counts.get(category.id) ?? 0 })
</div>
)) }
</Flex>
<div className="friends-group-chip friends-group-chip-manage ms-auto" title={ LocalizeText('friendlist.friends') } onClick={ onManageClick }>
{ '⚙' }
</div>
</Flex>
);
};
@@ -1,11 +1,13 @@
import { AddLinkEventTracker, ILinkEventTracker, RemoveFriendComposer, RemoveLinkEventTracker, SendRoomInviteComposer } from '@nitrots/nitro-renderer'; import { AddLinkEventTracker, ILinkEventTracker, RemoveFriendComposer, RemoveLinkEventTracker, SendRoomInviteComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { LocalizeText, MessengerFriend, SendMessageComposer } from '../../../../api'; import { LocalizeText, MessengerFriend, SendMessageComposer, filterFriendsByCategory } from '../../../../api';
import { Button, Flex, NitroCardAccordionSetView, NitroCardAccordionView, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common'; import { Button, Flex, NitroCardAccordionSetView, NitroCardAccordionView, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common';
import { useFriends } from '../../../../hooks'; import { useFriends } from '../../../../hooks';
import { FriendsCategoryManagerView } from './FriendsCategoryManagerView';
import { FriendsRemoveConfirmationView } from './FriendsListRemoveConfirmationView'; import { FriendsRemoveConfirmationView } from './FriendsListRemoveConfirmationView';
import { FriendsRoomInviteView } from './FriendsListRoomInviteView'; import { FriendsRoomInviteView } from './FriendsListRoomInviteView';
import { FriendsSearchView } from './FriendsListSearchView'; import { FriendsSearchView } from './FriendsListSearchView';
import { FriendsListGroupChipsView } from './FriendsListGroupChipsView';
import { FriendsListGroupView } from './friends-list-group/FriendsListGroupView'; import { FriendsListGroupView } from './friends-list-group/FriendsListGroupView';
import { FriendsListRequestView } from './friends-list-request/FriendsListRequestView'; import { FriendsListRequestView } from './friends-list-request/FriendsListRequestView';
@@ -15,7 +17,13 @@ export const FriendsListView: FC<{}> = props =>
const [ selectedFriendsIds, setSelectedFriendsIds ] = useState<number[]>([]); const [ selectedFriendsIds, setSelectedFriendsIds ] = useState<number[]>([]);
const [ showRoomInvite, setShowRoomInvite ] = useState<boolean>(false); const [ showRoomInvite, setShowRoomInvite ] = useState<boolean>(false);
const [ showRemoveFriendsConfirmation, setShowRemoveFriendsConfirmation ] = useState<boolean>(false); const [ showRemoveFriendsConfirmation, setShowRemoveFriendsConfirmation ] = useState<boolean>(false);
const { onlineFriends = [], offlineFriends = [], requests = [], requestFriend = null } = useFriends(); const [ selectedCategoryId, setSelectedCategoryId ] = useState<number>(0);
const [ showCategoryManager, setShowCategoryManager ] = useState<boolean>(false);
const { friends = [], onlineFriends = [], offlineFriends = [], requests = [], requestFriend = null, settings = null } = useFriends();
const categories = settings?.categories ?? [];
const filteredOnlineFriends = filterFriendsByCategory(onlineFriends, selectedCategoryId);
const filteredOfflineFriends = filterFriendsByCategory(offlineFriends, selectedCategoryId);
const removeFriendsText = useMemo(() => const removeFriendsText = useMemo(() =>
{ {
@@ -145,32 +153,38 @@ export const FriendsListView: FC<{}> = props =>
<NitroCardView className="nitro-friends" theme="primary-slim" uniqueKey="nitro-friends"> <NitroCardView className="nitro-friends" theme="primary-slim" uniqueKey="nitro-friends">
<NitroCardHeaderView headerText={ LocalizeText('friendlist.friends') } onCloseClick={ event => setIsVisible(false) } /> <NitroCardHeaderView headerText={ LocalizeText('friendlist.friends') } onCloseClick={ event => setIsVisible(false) } />
<NitroCardContentView className="text-black p-0" gap={ 1 } overflow="hidden"> <NitroCardContentView className="text-black p-0" gap={ 1 } overflow="hidden">
<FriendsListGroupChipsView
categories={ categories }
friends={ friends }
selectedCategoryId={ selectedCategoryId }
setSelectedCategoryId={ setSelectedCategoryId }
onManageClick={ () => setShowCategoryManager(true) } />
<NitroCardAccordionView fullHeight overflow="hidden"> <NitroCardAccordionView fullHeight overflow="hidden">
<NitroCardAccordionSetView className="friends-list-section" headerText={ LocalizeText('friendlist.friends') + ` (${ onlineFriends.length })` } isExpanded={ true }> <NitroCardAccordionSetView className="friends-list-section" headerText={ LocalizeText('friendlist.friends') + ` (${ filteredOnlineFriends.length })` } isExpanded={ true }>
<Flex className="friends-list-toolbar px-2 py-1" justifyContent="end"> <Flex className="friends-list-toolbar px-2 py-1" justifyContent="end">
<span className="friends-list-toolbar-link" onClick={ event => <span className="friends-list-toolbar-link" onClick={ event =>
{ {
event.stopPropagation(); toggleSelectFriends(onlineFriends.map(friend => friend.id)); event.stopPropagation(); toggleSelectFriends(filteredOnlineFriends.map(friend => friend.id));
} }> } }>
{ onlineFriends.length && onlineFriends.every(friend => (selectedFriendsIds.indexOf(friend.id) >= 0)) { filteredOnlineFriends.length && filteredOnlineFriends.every(friend => (selectedFriendsIds.indexOf(friend.id) >= 0))
? LocalizeText('friendlist.unselect_all') ? LocalizeText('friendlist.unselect_all')
: LocalizeText('friendlist.select_all') } : LocalizeText('friendlist.select_all') }
</span> </span>
</Flex> </Flex>
<FriendsListGroupView list={ onlineFriends } selectedFriendsIds={ selectedFriendsIds } selectFriend={ selectFriend } /> <FriendsListGroupView list={ filteredOnlineFriends } selectedFriendsIds={ selectedFriendsIds } selectFriend={ selectFriend } />
</NitroCardAccordionSetView> </NitroCardAccordionSetView>
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.friends.offlinecaption') + ` (${ offlineFriends.length })` }> <NitroCardAccordionSetView headerText={ LocalizeText('friendlist.friends.offlinecaption') + ` (${ filteredOfflineFriends.length })` }>
<Flex className="friends-list-toolbar px-2 py-1" justifyContent="end"> <Flex className="friends-list-toolbar px-2 py-1" justifyContent="end">
<span className="friends-list-toolbar-link" onClick={ event => <span className="friends-list-toolbar-link" onClick={ event =>
{ {
event.stopPropagation(); toggleSelectFriends(offlineFriends.map(friend => friend.id)); event.stopPropagation(); toggleSelectFriends(filteredOfflineFriends.map(friend => friend.id));
} }> } }>
{ offlineFriends.length && offlineFriends.every(friend => (selectedFriendsIds.indexOf(friend.id) >= 0)) { filteredOfflineFriends.length && filteredOfflineFriends.every(friend => (selectedFriendsIds.indexOf(friend.id) >= 0))
? LocalizeText('friendlist.unselect_all') ? LocalizeText('friendlist.unselect_all')
: LocalizeText('friendlist.select_all') } : LocalizeText('friendlist.select_all') }
</span> </span>
</Flex> </Flex>
<FriendsListGroupView list={ offlineFriends } selectedFriendsIds={ selectedFriendsIds } selectFriend={ selectFriend } /> <FriendsListGroupView list={ filteredOfflineFriends } selectedFriendsIds={ selectedFriendsIds } selectFriend={ selectFriend } />
</NitroCardAccordionSetView> </NitroCardAccordionSetView>
<FriendsListRequestView headerText={ LocalizeText('friendlist.tab.friendrequests') + ` (${ requests.length })` } isExpanded={ true } /> <FriendsListRequestView headerText={ LocalizeText('friendlist.tab.friendrequests') + ` (${ requests.length })` } isExpanded={ true } />
<FriendsSearchView headerText={ LocalizeText('people.search.title') } /> <FriendsSearchView headerText={ LocalizeText('people.search.title') } />
@@ -186,6 +200,8 @@ export const FriendsListView: FC<{}> = props =>
<FriendsRoomInviteView selectedFriendsIds={ selectedFriendsIds } sendRoomInvite={ sendRoomInvite } onCloseClick={ () => setShowRoomInvite(false) } /> } <FriendsRoomInviteView selectedFriendsIds={ selectedFriendsIds } sendRoomInvite={ sendRoomInvite } onCloseClick={ () => setShowRoomInvite(false) } /> }
{ showRemoveFriendsConfirmation && { showRemoveFriendsConfirmation &&
<FriendsRemoveConfirmationView removeFriendsText={ removeFriendsText } removeSelectedFriends={ removeSelectedFriends } selectedFriendsIds={ selectedFriendsIds } onCloseClick={ () => setShowRemoveFriendsConfirmation(false) } /> } <FriendsRemoveConfirmationView removeFriendsText={ removeFriendsText } removeSelectedFriends={ removeSelectedFriends } selectedFriendsIds={ selectedFriendsIds } onCloseClick={ () => setShowRemoveFriendsConfirmation(false) } /> }
{ showCategoryManager &&
<FriendsCategoryManagerView categories={ categories } onCloseClick={ () => setShowCategoryManager(false) } /> }
</> </>
); );
}; };