Files
Nitro-V3/src/components/navigator/views/room-settings/NavigatorRoomSettingsRightsTabView.tsx
T
simoleo89 c9df2d8765 style(navigator): unify room-settings tabs with section cards
Introduce a reusable NavigatorRoomSettingsSectionView card (rounded
bg-gray-100 panel with a bold-small title) and apply it across the
Access, VIP/Chat, Moderation and Rights tabs so every room-settings
screen matches the Base and Misc tab styling. Pure visual restyle —
handleChange wiring, events, composers and validations are unchanged.
2026-05-31 09:20:35 +02:00

179 lines
6.9 KiB
TypeScript

import { FlatControllerAddedEvent, FlatControllerRemovedEvent, FlatControllersEvent, RemoveAllRightsMessageComposer, RoomGiveRightsComposer, RoomTakeRightsComposer, RoomUsersWithRightsComposer } from '@nitrots/nitro-renderer';
import { FC, useEffect, useRef, useState } from 'react';
import { IRoomData, LocalizeText, SendMessageComposer } from '../../../../api';
import { Button, Column, Flex, Grid, Text, UserProfileIconView } from '../../../../common';
import { useFriends, useMessageEvent } from '../../../../hooks';
import { NavigatorRoomSettingsSectionView } from './NavigatorRoomSettingsSectionView';
interface NavigatorRoomSettingsTabViewProps
{
roomData: IRoomData;
handleChange: (field: string, value: string | number | boolean) => void;
}
const STAFF_CHAT_ID = -1;
const STAFF_CHAT_NAME = 'Staff Chat';
export const NavigatorRoomSettingsRightsTabView: FC<NavigatorRoomSettingsTabViewProps> = props =>
{
const { roomData = null } = props;
const [ usersWithRights, setUsersWithRights ] = useState<Map<number, string>>(new Map());
const { onlineFriends = [], offlineFriends = [] } = useFriends();
const pendingActionsRef = useRef<Set<string>>(new Set());
const guardedSend = (key: string, composer: any) =>
{
if(pendingActionsRef.current.has(key)) return;
pendingActionsRef.current.add(key);
SendMessageComposer(composer);
setTimeout(() => pendingActionsRef.current.delete(key), 2000);
};
const allFriendsRaw = [ ...onlineFriends, ...offlineFriends ];
const allFriends = allFriendsRaw.filter(friend =>
{
if(friend.id === STAFF_CHAT_ID) return false;
if(friend.name === STAFF_CHAT_NAME) return false;
if(friend.id <= 0) return false;
return true;
});
const filteredUsersWithRights = new Map(
Array.from(usersWithRights.entries()).filter(([ id, name ]) =>
{
if(id === STAFF_CHAT_ID) return false;
if(name === STAFF_CHAT_NAME) return false;
if(id <= 0) return false;
return true;
})
);
const friendsWithoutRights = allFriends.filter(friend => !filteredUsersWithRights.has(friend.id));
useMessageEvent<FlatControllersEvent>(FlatControllersEvent, event =>
{
const parser = event.getParser();
if(!roomData || (roomData.roomId !== parser.roomId)) return;
setUsersWithRights(parser.users);
});
useMessageEvent<FlatControllerAddedEvent>(FlatControllerAddedEvent, event =>
{
const parser = event.getParser();
if(!roomData || (roomData.roomId !== parser.roomId)) return;
setUsersWithRights(prevValue =>
{
const newValue = new Map(prevValue);
newValue.set(parser.data.userId, parser.data.userName);
return newValue;
});
});
useMessageEvent<FlatControllerRemovedEvent>(FlatControllerRemovedEvent, event =>
{
const parser = event.getParser();
if(!roomData || (roomData.roomId !== parser.roomId)) return;
setUsersWithRights(prevValue =>
{
const newValue = new Map(prevValue);
newValue.delete(parser.userId);
return newValue;
});
});
useEffect(() =>
{
if(!roomData) return;
SendMessageComposer(new RoomUsersWithRightsComposer(roomData.roomId));
}, [ roomData ]);
return (
<Grid>
<Column size={ 6 }>
<NavigatorRoomSettingsSectionView gap={ 1 } className="h-full"
title={ LocalizeText(
'navigator.flatctrls.userswithrights',
[ 'displayed', 'total' ],
[
filteredUsersWithRights.size.toString(),
filteredUsersWithRights.size.toString()
]
) }>
<Flex overflow="hidden" className="nitro-card-panel p-2 list-container">
<Column fullWidth overflow="auto" gap={ 1 }>
{ Array.from(filteredUsersWithRights.entries()).map(([ id, name ], index) =>
{
return (
<Flex key={ `${id}-${index}` } shrink alignItems="center" gap={ 1 } overflow="hidden">
<UserProfileIconView userId={ id } />
<Text
pointer
grow
onClick={ () => guardedSend(`take_${id}`, new RoomTakeRightsComposer(id)) }>
{ name }
</Text>
</Flex>
);
}) }
</Column>
</Flex>
<Button
variant="danger"
disabled={ !filteredUsersWithRights.size }
onClick={ () => roomData && guardedSend('removeAll', new RemoveAllRightsMessageComposer(roomData.roomId)) }>
{ LocalizeText('navigator.flatctrls.clear') }
</Button>
</NavigatorRoomSettingsSectionView>
</Column>
<Column size={ 6 }>
<NavigatorRoomSettingsSectionView gap={ 1 } className="h-full"
title={ LocalizeText(
'navigator.flatctrls.friends',
[ 'displayed', 'total' ],
[
friendsWithoutRights.length.toString(),
allFriends.length.toString()
]
) }>
<Flex overflow="hidden" className="nitro-card-panel p-2 list-container">
<Column fullWidth overflow="auto" gap={ 1 }>
{ friendsWithoutRights.map((friend, index) =>
{
return (
<Flex key={ `${friend.id}-${index}` } shrink alignItems="center" gap={ 1 } overflow="hidden">
<UserProfileIconView userId={ friend.id } />
<Text
pointer
grow
onClick={ () => guardedSend(`give_${friend.id}`, new RoomGiveRightsComposer(friend.id)) }>
{ friend.name }
</Text>
</Flex>
);
}) }
</Column>
</Flex>
</NavigatorRoomSettingsSectionView>
</Column>
</Grid>
);
};