mirror of
https://github.com/billsonnn/nitro-react.git
synced 2025-01-18 21:36:27 +01:00
Update friends list
This commit is contained in:
parent
3a491fedde
commit
6474a88000
@ -32,7 +32,7 @@ export const NitroCardHeaderView: FC<NitroCardHeaderViewProps> = props =>
|
||||
<Column center classNames={ getClassNames } { ...rest }>
|
||||
<Flex fullWidth center position="relative">
|
||||
<span className="nitro-card-header-text">{ headerText }</span>
|
||||
<Base position="absolute" className="end-0 nitro-card-header-close" onMouseDownCapture={ onMouseDown } onClick={ onCloseClick }>
|
||||
<Base position="absolute" className="top-1 end-0 nitro-card-header-close" onMouseDownCapture={ onMouseDown } onClick={ onCloseClick }>
|
||||
<FontAwesomeIcon icon="times" />
|
||||
</Base>
|
||||
</Flex>
|
||||
|
@ -61,8 +61,8 @@ $nitro-card-tabs-height: 33px;
|
||||
|
||||
.nitro-card-header {
|
||||
position: relative;
|
||||
min-height: 27px;
|
||||
max-height: 27px;
|
||||
min-height: 28px;
|
||||
max-height: 28px;
|
||||
background: repeating-linear-gradient($tertiary, $tertiary 50%, $quaternary 50%, $quaternary 100%);
|
||||
border-bottom: 2px solid darken($quaternary, 5);
|
||||
box-shadow: 0 2px white;
|
||||
@ -87,7 +87,7 @@ $nitro-card-tabs-height: 33px;
|
||||
|
||||
.nitro-card-header-close {
|
||||
cursor: pointer;
|
||||
padding: 0px 2px;
|
||||
padding: 0px 1px;
|
||||
line-height: 1;
|
||||
@include font-size($h7-font-size);
|
||||
border-radius: $border-radius;
|
||||
|
@ -1,93 +0,0 @@
|
||||
import { FollowFriendMessageComposer, SetRelationshipStatusComposer } from '@nitrots/nitro-renderer';
|
||||
import classNames from 'classnames';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { LocalizeText, OpenMessengerChat, SendMessageComposer } from '../../../../api';
|
||||
import { Base, Flex, FlexProps, UserProfileIconView } from '../../../../common';
|
||||
import { MessengerFriend } from '../../common/MessengerFriend';
|
||||
|
||||
interface FriendsGroupItemViewProps extends FlexProps
|
||||
{
|
||||
friend: MessengerFriend;
|
||||
selected?: boolean;
|
||||
selectFriend: () => void;
|
||||
}
|
||||
|
||||
export const FriendsGroupItemView: FC<FriendsGroupItemViewProps> = props =>
|
||||
{
|
||||
const { friend = null, selected = false, selectFriend = null, children = null, ...rest } = props;
|
||||
|
||||
const [ isExpanded, setIsExpanded ] = useState<boolean>(false);
|
||||
|
||||
const followFriend = useCallback(() =>
|
||||
{
|
||||
if(!friend) return;
|
||||
|
||||
SendMessageComposer(new FollowFriendMessageComposer(friend.id));
|
||||
}, [ friend ]);
|
||||
|
||||
const openMessengerChat = useCallback((e) =>
|
||||
{
|
||||
e.stopPropagation();
|
||||
|
||||
if(!friend) return;
|
||||
|
||||
OpenMessengerChat(friend.id);
|
||||
}, [ friend ]);
|
||||
|
||||
const getCurrentRelationshipName = useCallback(() =>
|
||||
{
|
||||
if(!friend) return 'none';
|
||||
|
||||
switch(friend.relationshipStatus)
|
||||
{
|
||||
case MessengerFriend.RELATIONSHIP_HEART: return 'heart';
|
||||
case MessengerFriend.RELATIONSHIP_SMILE: return 'smile';
|
||||
case MessengerFriend.RELATIONSHIP_BOBBA: return 'bobba';
|
||||
default: return 'none';
|
||||
}
|
||||
}, [ friend ]);
|
||||
|
||||
const initUpdateRelationship = useCallback((e) =>
|
||||
{
|
||||
e.stopPropagation();
|
||||
setIsExpanded(true);
|
||||
}, []);
|
||||
|
||||
const updateRelationship = useCallback((e, type: number) =>
|
||||
{
|
||||
e.stopPropagation();
|
||||
|
||||
if(type !== friend.relationshipStatus) SendMessageComposer(new SetRelationshipStatusComposer(friend.id, type));
|
||||
|
||||
setIsExpanded(false);
|
||||
}, [ friend ]);
|
||||
|
||||
if(!friend) return null;
|
||||
|
||||
return (
|
||||
<Flex className={ 'px-2 py-1 align-items-center' + classNames({ ' bg-primary text-white': selected }) } gap={ 1 } { ...rest } onClick={ selectFriend }>
|
||||
<div onClick={ (e) => e.stopPropagation() }>
|
||||
<UserProfileIconView userId={ friend.id } />
|
||||
</div>
|
||||
<div>{ friend.name }</div>
|
||||
<Flex className="ms-auto align-items-center" gap={ 1 }>
|
||||
{ !isExpanded &&
|
||||
<>
|
||||
{ friend.followingAllowed &&
|
||||
<Base onClick={ followFriend } className="nitro-friends-spritesheet icon-follow cursor-pointer" title={ LocalizeText('friendlist.tip.follow') } /> }
|
||||
{ friend.online &&
|
||||
<Base className="nitro-friends-spritesheet icon-chat cursor-pointer" onClick={ openMessengerChat } title={ LocalizeText('friendlist.tip.im') } /> }
|
||||
<Base className={ `nitro-friends-spritesheet icon-${ getCurrentRelationshipName() } cursor-pointer` } onClick={ initUpdateRelationship } title={ LocalizeText('infostand.link.relationship') } />
|
||||
</> }
|
||||
{ isExpanded &&
|
||||
<>
|
||||
<Base className="nitro-friends-spritesheet icon-heart cursor-pointer" onClick={ (e) => updateRelationship(e, MessengerFriend.RELATIONSHIP_HEART) } />
|
||||
<Base className="nitro-friends-spritesheet icon-smile cursor-pointer" onClick={ (e) => updateRelationship(e, MessengerFriend.RELATIONSHIP_SMILE) } />
|
||||
<Base className="nitro-friends-spritesheet icon-bobba cursor-pointer" onClick={ (e) => updateRelationship(e, MessengerFriend.RELATIONSHIP_BOBBA) } />
|
||||
<Base className="nitro-friends-spritesheet icon-none cursor-pointer" onClick={ (e) => updateRelationship(e, MessengerFriend.RELATIONSHIP_NONE) } />
|
||||
</> }
|
||||
</Flex>
|
||||
{ children }
|
||||
</Flex>
|
||||
);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
import { FC } from 'react';
|
||||
import { MessengerFriend } from '../../common/MessengerFriend';
|
||||
import { FriendsGroupItemView } from './FriendsGroupItemView';
|
||||
|
||||
interface FriendsGroupViewProps
|
||||
{
|
||||
list: MessengerFriend[];
|
||||
selectedFriendsIds: number[];
|
||||
selectFriend: (userId: number) => void;
|
||||
}
|
||||
|
||||
export const FriendsGroupView: FC<FriendsGroupViewProps> = props =>
|
||||
{
|
||||
const { list = null, selectedFriendsIds = null, selectFriend = null } = props;
|
||||
|
||||
if(!list) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{ selectedFriendsIds && list && list.map((item, index) =>
|
||||
{
|
||||
return <FriendsGroupItemView key={ index } friend={ item } selected={ selectedFriendsIds.includes(item.id) } selectFriend={ () => selectFriend(item.id) } />;
|
||||
}) }
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
import { RemoveFriendComposer, SendRoomInviteComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useMemo, useState } from 'react';
|
||||
import { LocalizeText, SendMessageComposer } from '../../../../api';
|
||||
import { NitroCardAccordionSetView, NitroCardAccordionView, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../common';
|
||||
import { Button, Flex, NitroCardAccordionSetView, NitroCardAccordionView, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common';
|
||||
import { MessengerFriend } from '../../common/MessengerFriend';
|
||||
import { MessengerRequest } from '../../common/MessengerRequest';
|
||||
import { FriendsGroupView } from '../friends-group/FriendsGroupView';
|
||||
import { FriendsRemoveConfirmationView } from '../friends-remove-confirmation/FriendsRemoveConfirmationView';
|
||||
import { FriendsRequestView } from '../friends-request/FriendsRequestView';
|
||||
import { FriendsRoomInviteView } from '../friends-room-invite/FriendsRoomInviteView';
|
||||
import { FriendsSearchView } from '../friends-search/FriendsSearchView';
|
||||
import { FriendsListGroupView } from './friends-list-group/FriendsListGroupView';
|
||||
import { FriendsListRequestView } from './friends-list-request/FriendsListRequestView';
|
||||
import { FriendsRemoveConfirmationView } from './FriendsRemoveConfirmationView';
|
||||
import { FriendsRoomInviteView } from './FriendsRoomInviteView';
|
||||
import { FriendsSearchView } from './FriendsSearchView';
|
||||
|
||||
interface FriendsListViewProps
|
||||
{
|
||||
@ -18,15 +18,11 @@ interface FriendsListViewProps
|
||||
friendRequests: MessengerRequest[];
|
||||
}
|
||||
|
||||
const MODE_FRIENDS: number = 0;
|
||||
const MODE_SEARCH: number = 1;
|
||||
|
||||
export const FriendsListView: FC<FriendsListViewProps> = props =>
|
||||
{
|
||||
const { onlineFriends = [], offlineFriends = [], friendRequests = [], onCloseClick = null } = props;
|
||||
|
||||
const [ selectedFriendsIds, setSelectedFriendsIds ] = useState<number[]>([]);
|
||||
const [ mode, setMode ] = useState<number>(0);
|
||||
|
||||
const [ showRoomInvite, setShowRoomInvite ] = useState<boolean>(false);
|
||||
const [ showRemoveFriendsConfirmation, setShowRemoveFriendsConfirmation ] = useState<boolean>(false);
|
||||
@ -89,42 +85,30 @@ export const FriendsListView: FC<FriendsListViewProps> = props =>
|
||||
|
||||
return (
|
||||
<>
|
||||
<NitroCardView className="nitro-friends" uniqueKey="nitro-friends">
|
||||
<NitroCardView className="nitro-friends" uniqueKey="nitro-friends" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('friendlist.friends') } onCloseClick={ onCloseClick } />
|
||||
<NitroCardTabsView>
|
||||
<NitroCardTabsItemView isActive={ (mode === MODE_FRIENDS) } count={ friendRequests.length } onClick={ event => setMode(MODE_FRIENDS) }>
|
||||
{ LocalizeText('friendlist.friends') }
|
||||
</NitroCardTabsItemView>
|
||||
<NitroCardTabsItemView isActive={ (mode === MODE_SEARCH) } onClick={ event => setMode(MODE_SEARCH) }>
|
||||
{ LocalizeText('generic.search') }
|
||||
</NitroCardTabsItemView>
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView className="p-0 text-black">
|
||||
{ (mode === MODE_FRIENDS) &&
|
||||
<>
|
||||
<NitroCardAccordionView className="overflow-y-auto">
|
||||
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.friends') + ` (${onlineFriends.length})` } isExpanded={ true }>
|
||||
<FriendsGroupView list={ onlineFriends } selectedFriendsIds={ selectedFriendsIds } selectFriend={ selectFriend } />
|
||||
</NitroCardAccordionSetView>
|
||||
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.friends.offlinecaption') + ` (${offlineFriends.length})` }>
|
||||
<FriendsGroupView list={ offlineFriends } selectedFriendsIds={ selectedFriendsIds } selectFriend={ selectFriend } />
|
||||
</NitroCardAccordionSetView>
|
||||
<FriendsRequestView requests={ friendRequests } />
|
||||
</NitroCardAccordionView>
|
||||
{ selectedFriendsIds && selectedFriendsIds.length > 0 && <div className="d-flex gap-2 p-2">
|
||||
<button className="btn btn-primary w-100" onClick={ () => setShowRoomInvite(true) }>Invite</button>
|
||||
<button className="btn btn-danger w-100" onClick={ () => setShowRemoveFriendsConfirmation(true) }>Delete</button>
|
||||
</div> }
|
||||
</>
|
||||
}
|
||||
{ (mode === MODE_SEARCH) &&
|
||||
<FriendsSearchView /> }
|
||||
<NitroCardContentView overflow="hidden" gap={ 1 } className="text-black p-0">
|
||||
<NitroCardAccordionView fullHeight overflow="hidden">
|
||||
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.friends') + ` (${onlineFriends.length})` } isExpanded={ true }>
|
||||
<FriendsListGroupView list={ onlineFriends } selectedFriendsIds={ selectedFriendsIds } selectFriend={ selectFriend } />
|
||||
</NitroCardAccordionSetView>
|
||||
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.friends.offlinecaption') + ` (${offlineFriends.length})` }>
|
||||
<FriendsListGroupView list={ offlineFriends } selectedFriendsIds={ selectedFriendsIds } selectFriend={ selectFriend } />
|
||||
</NitroCardAccordionSetView>
|
||||
<FriendsListRequestView headerText={ LocalizeText('friendlist.tab.friendrequests') + ` (${ friendRequests.length })` } isExpanded={ true } requests={ friendRequests } />
|
||||
<FriendsSearchView headerText={ LocalizeText('people.search.title') } />
|
||||
</NitroCardAccordionView>
|
||||
{ selectedFriendsIds && selectedFriendsIds.length > 0 &&
|
||||
<Flex gap={ 1 } className="p-1">
|
||||
<Button fullWidth onClick={ () => setShowRoomInvite(true) }>{ LocalizeText('friendlist.tip.invite') }</Button>
|
||||
<Button fullWidth variant="danger" onClick={ event => setShowRemoveFriendsConfirmation(true) }>{ LocalizeText('generic.delete') }</Button>
|
||||
</Flex> }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
{ showRoomInvite &&
|
||||
<FriendsRoomInviteView selectedFriendsIds={ selectedFriendsIds } onCloseClick={ () => setShowRoomInvite(false) } sendRoomInvite={ sendRoomInvite } /> }
|
||||
<FriendsRoomInviteView selectedFriendsIds={ selectedFriendsIds } onCloseClick={ () => setShowRoomInvite(false) } sendRoomInvite={ sendRoomInvite } /> }
|
||||
{ showRemoveFriendsConfirmation &&
|
||||
<FriendsRemoveConfirmationView selectedFriendsIds={ selectedFriendsIds } removeFriendsText={ removeFriendsText } onCloseClick={ () => setShowRemoveFriendsConfirmation(false) } removeSelectedFriends={ removeSelectedFriends } /> }
|
||||
<FriendsRemoveConfirmationView selectedFriendsIds={ selectedFriendsIds } removeFriendsText={ removeFriendsText } onCloseClick={ () => setShowRemoveFriendsConfirmation(false) } removeSelectedFriends={ removeSelectedFriends } /> }
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FC } from 'react';
|
||||
import { LocalizeText } from '../../../../api';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common';
|
||||
import { Button, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common';
|
||||
|
||||
interface FriendsRemoveConfirmationViewProps
|
||||
{
|
||||
@ -15,14 +15,14 @@ export const FriendsRemoveConfirmationView: FC<FriendsRemoveConfirmationViewProp
|
||||
const { selectedFriendsIds = null, removeFriendsText = null, removeSelectedFriends = null, onCloseClick = null } = props;
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-friends-remove-confirmation" uniqueKey="nitro-friends-remove-confirmation" theme="primary-slim">
|
||||
<NitroCardView className="nitro-friends-remove-confirmation" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('friendlist.removefriendconfirm.title') } onCloseClick={ onCloseClick } />
|
||||
<NitroCardContentView className="text-black d-flex flex-column gap-3">
|
||||
<NitroCardContentView className="text-black">
|
||||
<div>{ removeFriendsText }</div>
|
||||
<div className="d-flex gap-2">
|
||||
<button className="btn btn-danger w-100" disabled={ selectedFriendsIds.length === 0 } onClick={ removeSelectedFriends }>{ LocalizeText('generic.ok') }</button>
|
||||
<button className="btn btn-primary w-100" onClick={ onCloseClick }>{ LocalizeText('generic.cancel') }</button>
|
||||
</div>
|
||||
<Flex gap={ 1 }>
|
||||
<Button fullWidth variant="danger" disabled={ (selectedFriendsIds.length === 0) } onClick={ removeSelectedFriends }>{ LocalizeText('generic.ok') }</Button>
|
||||
<Button fullWidth onClick={ onCloseClick }>{ LocalizeText('generic.cancel') }</Button>
|
||||
</Flex>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
@ -0,0 +1,32 @@
|
||||
import { FC, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../api';
|
||||
import { Button, Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
|
||||
|
||||
interface FriendsRoomInviteViewProps
|
||||
{
|
||||
selectedFriendsIds: number[];
|
||||
onCloseClick: () => void;
|
||||
sendRoomInvite: (message: string) => void;
|
||||
}
|
||||
|
||||
export const FriendsRoomInviteView: FC<FriendsRoomInviteViewProps> = props =>
|
||||
{
|
||||
const { selectedFriendsIds = null, onCloseClick = null, sendRoomInvite = null } = props;
|
||||
|
||||
const [ roomInviteMessage, setRoomInviteMessage ] = useState<string>('');
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-friends-room-invite" uniqueKey="nitro-friends-room-invite" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('friendlist.invite.title') } onCloseClick={ onCloseClick } />
|
||||
<NitroCardContentView className="text-black">
|
||||
{ LocalizeText('friendlist.invite.summary', [ 'count' ], [ selectedFriendsIds.length.toString() ]) }
|
||||
<textarea className="form-control" value={ roomInviteMessage } onChange={ event => setRoomInviteMessage(event.target.value) }></textarea>
|
||||
<Text center className="bg-muted rounded p-1">{ LocalizeText('friendlist.invite.note') }</Text>
|
||||
<Flex gap={ 1 }>
|
||||
<Button fullWidth variant="success" disabled={ ((roomInviteMessage.length === 0) || (selectedFriendsIds.length === 0)) } onClick={ () => sendRoomInvite(roomInviteMessage) }>{ LocalizeText('friendlist.invite.send') }</Button>
|
||||
<Button fullWidth onClick={ onCloseClick }>{ LocalizeText('generic.cancel') }</Button>
|
||||
</Flex>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
};
|
141
src/views/friends/views/friends-list/FriendsSearchView.tsx
Normal file
141
src/views/friends/views/friends-list/FriendsSearchView.tsx
Normal file
@ -0,0 +1,141 @@
|
||||
import { HabboSearchComposer, HabboSearchResultData, HabboSearchResultEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { LocalizeText, OpenMessengerChat, SendMessageComposer } from '../../../../api';
|
||||
import { Base, Column, Flex, NitroCardAccordionItemView, NitroCardAccordionSetView, NitroCardAccordionSetViewProps, Text, UserProfileIconView } from '../../../../common';
|
||||
import { BatchUpdates, UseMessageEventHook } from '../../../../hooks';
|
||||
import { useFriendsContext } from '../../FriendsContext';
|
||||
|
||||
interface FriendsSearchViewProps extends NitroCardAccordionSetViewProps
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
export const FriendsSearchView: FC<FriendsSearchViewProps> = props =>
|
||||
{
|
||||
const { ...rest } = props;
|
||||
const [ searchValue, setSearchValue ] = useState('');
|
||||
const [ friendResults, setFriendResults ] = useState<HabboSearchResultData[]>(null);
|
||||
const [ otherResults, setOtherResults ] = useState<HabboSearchResultData[]>(null);
|
||||
const { canRequestFriend = null, requestFriend = null } = useFriendsContext();
|
||||
|
||||
const onHabboSearchResultEvent = useCallback((event: HabboSearchResultEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
BatchUpdates(() =>
|
||||
{
|
||||
setFriendResults(parser.friends);
|
||||
setOtherResults(parser.others);
|
||||
});
|
||||
}, []);
|
||||
|
||||
UseMessageEventHook(HabboSearchResultEvent, onHabboSearchResultEvent);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!searchValue || !searchValue.length) return;
|
||||
|
||||
const timeout = setTimeout(() =>
|
||||
{
|
||||
if(!searchValue || !searchValue.length) return;
|
||||
|
||||
SendMessageComposer(new HabboSearchComposer(searchValue));
|
||||
}, 500);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [ searchValue ]);
|
||||
|
||||
return (
|
||||
<NitroCardAccordionSetView { ...rest }>
|
||||
<input type="text" className="search-input form-control form-control-sm w-100 rounded-0" placeholder={ LocalizeText('generic.search') } value={ searchValue } onChange={ event => setSearchValue(event.target.value) } />
|
||||
<Column>
|
||||
{ friendResults &&
|
||||
<>
|
||||
{ (friendResults.length === 0) &&
|
||||
<Text bold small className="px-2 py-1">{ LocalizeText('friendlist.search.nofriendsfound') }</Text> }
|
||||
{ (friendResults.length > 0) &&
|
||||
<Column gap={ 0 }>
|
||||
<Text bold small className="px-2 py-1">{ LocalizeText('friendlist.search.friendscaption', [ 'cnt' ], [ friendResults.length.toString() ]) }</Text>
|
||||
<hr className="mx-2 mt-0 mb-1 text-black" />
|
||||
<Column gap={ 0 }>
|
||||
{ friendResults.map(result =>
|
||||
{
|
||||
return (
|
||||
<NitroCardAccordionItemView key={ result.avatarId } justifyContent="between" className="px-2 py-1">
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<UserProfileIconView userId={ result.avatarId } />
|
||||
<div>{ result.avatarName }</div>
|
||||
</Flex>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
{ result.isAvatarOnline &&
|
||||
<Base className="nitro-friends-spritesheet icon-chat cursor-pointer" onClick={ event => OpenMessengerChat(result.avatarId) } title={ LocalizeText('friendlist.tip.im') } /> }
|
||||
</Flex>
|
||||
</NitroCardAccordionItemView>
|
||||
)
|
||||
}) }
|
||||
</Column>
|
||||
</Column> }
|
||||
</> }
|
||||
{ otherResults &&
|
||||
<>
|
||||
{ (otherResults.length === 0) &&
|
||||
<Text bold small className="px-2 py-1">{ LocalizeText('friendlist.search.noothersfound') }</Text> }
|
||||
{ (otherResults.length > 0) &&
|
||||
<Column gap={ 0 }>
|
||||
<Text bold small className="px-2 py-1">{ LocalizeText('friendlist.search.otherscaption', [ 'cnt' ], [ otherResults.length.toString() ]) }</Text>
|
||||
<hr className="mx-2 mt-0 mb-1 text-black" />
|
||||
<Column gap={ 0 }>
|
||||
{ otherResults.map(result =>
|
||||
{
|
||||
return (
|
||||
<NitroCardAccordionItemView key={ result.avatarId } justifyContent="between" className="px-2 py-1">
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<UserProfileIconView userId={ result.avatarId } />
|
||||
<div>{ result.avatarName }</div>
|
||||
</Flex>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
{ canRequestFriend(result.avatarId) &&
|
||||
<Base className="nitro-friends-spritesheet icon-add cursor-pointer" onClick={ event => requestFriend(result.avatarId, result.avatarName) } title={ LocalizeText('friendlist.tip.addfriend') } /> }
|
||||
</Flex>
|
||||
</NitroCardAccordionItemView>
|
||||
)
|
||||
}) }
|
||||
</Column>
|
||||
</Column> }
|
||||
</> }
|
||||
</Column>
|
||||
{/* <NitroCardAccordionView>
|
||||
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.search.friendscaption', [ 'cnt' ], [ friendResults.length.toString() ]) } isExpanded={ true }>
|
||||
{ (friendResults.length > 0) && friendResults.map(result =>
|
||||
{
|
||||
return (
|
||||
<NitroCardAccordionItemView key={ result.avatarId }>
|
||||
<UserProfileIconView userId={ result.avatarId } />
|
||||
<Flex>{ result.avatarName }</Flex>
|
||||
<Flex className="ms-auto align-items-center" gap={ 1 }>
|
||||
{ result.isAvatarOnline &&
|
||||
<Base className="nitro-friends-spritesheet icon-chat cursor-pointer" onClick={ event => OpenMessengerChat(result.avatarId) } title={ LocalizeText('friendlist.tip.im') } /> }
|
||||
</Flex>
|
||||
</NitroCardAccordionItemView>
|
||||
);
|
||||
}) }
|
||||
</NitroCardAccordionSetView>
|
||||
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.search.otherscaption', [ 'cnt' ], [ otherResults.length.toString() ]) } isExpanded={ true }>
|
||||
{ (otherResults.length > 0) && otherResults.map(result =>
|
||||
{
|
||||
return (
|
||||
<NitroCardAccordionItemView key={ result.avatarId }>
|
||||
<UserProfileIconView userId={ result.avatarId } />
|
||||
<Base>{ result.avatarName }</Base>
|
||||
<Flex className="ms-auto align-items-center" gap={ 1 }>
|
||||
{ canRequestFriend(result.avatarId) &&
|
||||
<Base className="nitro-friends-spritesheet icon-add cursor-pointer" onClick={ event => requestFriend(result.avatarId, result.avatarName) } title={ LocalizeText('friendlist.tip.addfriend') } /> }
|
||||
</Flex>
|
||||
</NitroCardAccordionItemView>
|
||||
);
|
||||
}) }
|
||||
</NitroCardAccordionSetView>
|
||||
</NitroCardAccordionView> */}
|
||||
</NitroCardAccordionSetView>
|
||||
);
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
import { FollowFriendMessageComposer, SetRelationshipStatusComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, MouseEvent, useState } from 'react';
|
||||
import { LocalizeText, OpenMessengerChat, SendMessageComposer } from '../../../../../api';
|
||||
import { Base, Flex, NitroCardAccordionItemView, NitroCardAccordionItemViewProps, UserProfileIconView } from '../../../../../common';
|
||||
import { MessengerFriend } from '../../../common/MessengerFriend';
|
||||
|
||||
interface FriendsListGroupItemViewProps extends NitroCardAccordionItemViewProps
|
||||
{
|
||||
friend: MessengerFriend;
|
||||
selected?: boolean;
|
||||
selectFriend: () => void;
|
||||
}
|
||||
|
||||
export const FriendsListGroupItemView: FC<FriendsListGroupItemViewProps> = props =>
|
||||
{
|
||||
const { friend = null, selected = false, selectFriend = null, children = null, ...rest } = props;
|
||||
const [ isRelationshipOpen, setIsRelationshipOpen ] = useState<boolean>(false);
|
||||
|
||||
const followFriend = (event: MouseEvent<HTMLDivElement>) =>
|
||||
{
|
||||
event.stopPropagation();
|
||||
|
||||
SendMessageComposer(new FollowFriendMessageComposer(friend.id));
|
||||
}
|
||||
|
||||
const openMessengerChat = (event: MouseEvent<HTMLDivElement>) =>
|
||||
{
|
||||
event.stopPropagation();
|
||||
|
||||
OpenMessengerChat(friend.id);
|
||||
}
|
||||
|
||||
const openRelationship = (event: MouseEvent<HTMLDivElement>) =>
|
||||
{
|
||||
event.stopPropagation();
|
||||
|
||||
setIsRelationshipOpen(true);
|
||||
}
|
||||
|
||||
const updateRelationship = (event: MouseEvent<HTMLDivElement>, type: number) =>
|
||||
{
|
||||
event.stopPropagation();
|
||||
|
||||
if(type !== friend.relationshipStatus) SendMessageComposer(new SetRelationshipStatusComposer(friend.id, type));
|
||||
|
||||
setIsRelationshipOpen(false);
|
||||
}
|
||||
|
||||
const getCurrentRelationshipName = () =>
|
||||
{
|
||||
if(!friend) return 'none';
|
||||
|
||||
switch(friend.relationshipStatus)
|
||||
{
|
||||
case MessengerFriend.RELATIONSHIP_HEART: return 'heart';
|
||||
case MessengerFriend.RELATIONSHIP_SMILE: return 'smile';
|
||||
case MessengerFriend.RELATIONSHIP_BOBBA: return 'bobba';
|
||||
default: return 'none';
|
||||
}
|
||||
}
|
||||
|
||||
if(!friend) return null;
|
||||
|
||||
return (
|
||||
<NitroCardAccordionItemView justifyContent="between" className={ `px-2 py-1 ${ selected && 'bg-primary text-white' }` } onClick={ selectFriend } { ...rest }>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<Base onClick={ event => event.stopPropagation() }>
|
||||
<UserProfileIconView userId={ friend.id } />
|
||||
</Base>
|
||||
<div>{ friend.name }</div>
|
||||
</Flex>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
{ !isRelationshipOpen &&
|
||||
<>
|
||||
{ friend.followingAllowed &&
|
||||
<Base pointer onClick={ followFriend } className="nitro-friends-spritesheet icon-follow" title={ LocalizeText('friendlist.tip.follow') } /> }
|
||||
{ friend.online &&
|
||||
<Base pointer className="nitro-friends-spritesheet icon-chat" onClick={ openMessengerChat } title={ LocalizeText('friendlist.tip.im') } /> }
|
||||
{ (friend.id > 0) &&
|
||||
<Base className={ `nitro-friends-spritesheet icon-${ getCurrentRelationshipName() } cursor-pointer` } onClick={ openRelationship } title={ LocalizeText('infostand.link.relationship') } /> }
|
||||
</> }
|
||||
{ isRelationshipOpen &&
|
||||
<>
|
||||
<Base pointer className="nitro-friends-spritesheet icon-heart" onClick={ event => updateRelationship(event, MessengerFriend.RELATIONSHIP_HEART) } />
|
||||
<Base pointer className="nitro-friends-spritesheet icon-smile" onClick={ event => updateRelationship(event, MessengerFriend.RELATIONSHIP_SMILE) } />
|
||||
<Base pointer className="nitro-friends-spritesheet icon-bobba" onClick={ event => updateRelationship(event, MessengerFriend.RELATIONSHIP_BOBBA) } />
|
||||
<Base pointer className="nitro-friends-spritesheet icon-none" onClick={ event => updateRelationship(event, MessengerFriend.RELATIONSHIP_NONE) } />
|
||||
</> }
|
||||
</Flex>
|
||||
{ children }
|
||||
</NitroCardAccordionItemView>
|
||||
);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import { FC } from 'react';
|
||||
import { MessengerFriend } from '../../../common/MessengerFriend';
|
||||
import { FriendsListGroupItemView } from './FriendsListGroupItemView';
|
||||
|
||||
interface FriendsListGroupViewProps
|
||||
{
|
||||
list: MessengerFriend[];
|
||||
selectedFriendsIds: number[];
|
||||
selectFriend: (userId: number) => void;
|
||||
}
|
||||
|
||||
export const FriendsListGroupView: FC<FriendsListGroupViewProps> = props =>
|
||||
{
|
||||
const { list = null, selectedFriendsIds = null, selectFriend = null } = props;
|
||||
|
||||
if(!list || !list.length) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{ list.map((item, index) => <FriendsListGroupItemView key={ index } friend={ item } selected={ selectedFriendsIds && selectedFriendsIds.includes(item.id) } selectFriend={ () => selectFriend(item.id) } />) }
|
||||
</>
|
||||
|
||||
);
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
import { FC } from 'react';
|
||||
import { Base, Flex, NitroCardAccordionItemView, NitroCardAccordionItemViewProps, UserProfileIconView } from '../../../../../common';
|
||||
import { MessengerRequest } from '../../../common/MessengerRequest';
|
||||
import { useFriendsContext } from '../../../FriendsContext';
|
||||
|
||||
interface FriendsListRequestItemViewProps extends NitroCardAccordionItemViewProps
|
||||
{
|
||||
request: MessengerRequest;
|
||||
}
|
||||
|
||||
export const FriendsListRequestItemView: FC<FriendsListRequestItemViewProps> = props =>
|
||||
{
|
||||
const { request = null, children = null, ...rest } = props;
|
||||
const { acceptFriend = null, declineFriend = null } = useFriendsContext();
|
||||
|
||||
if(!request) return null;
|
||||
|
||||
return (
|
||||
<NitroCardAccordionItemView justifyContent="between" className="px-2 py-1" { ...rest }>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<UserProfileIconView userId={ request.id } />
|
||||
<div>{ request.name }</div>
|
||||
</Flex>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<Base className="nitro-friends-spritesheet icon-accept cursor-pointer" onClick={ event => acceptFriend(request.requesterUserId) } />
|
||||
<Base className="nitro-friends-spritesheet icon-deny cursor-pointer" onClick={ event => declineFriend(request.requesterUserId) } />
|
||||
</Flex>
|
||||
{ children }
|
||||
</NitroCardAccordionItemView>
|
||||
);
|
||||
};
|
@ -0,0 +1,35 @@
|
||||
import { FC } from 'react';
|
||||
import { LocalizeText } from '../../../../../api';
|
||||
import { Button, Column, Flex, NitroCardAccordionSetView, NitroCardAccordionSetViewProps } from '../../../../../common';
|
||||
import { MessengerRequest } from '../../../common/MessengerRequest';
|
||||
import { useFriendsContext } from '../../../FriendsContext';
|
||||
import { FriendsListRequestItemView } from './FriendsListRequestItemView';
|
||||
|
||||
interface FriendsListRequestViewProps extends NitroCardAccordionSetViewProps
|
||||
{
|
||||
requests: MessengerRequest[];
|
||||
}
|
||||
|
||||
export const FriendsListRequestView: FC<FriendsListRequestViewProps> = props =>
|
||||
{
|
||||
const { requests = [], children = null, ...rest } = props;
|
||||
const { declineFriend = null } = useFriendsContext();
|
||||
|
||||
if(!requests.length) return null;
|
||||
|
||||
return (
|
||||
<NitroCardAccordionSetView { ...rest }>
|
||||
<Column fullHeight justifyContent="between" gap={ 1 }>
|
||||
<Column gap={ 0 }>
|
||||
{ requests.map((request, index) => <FriendsListRequestItemView key={ index } request={ request } />) }
|
||||
</Column>
|
||||
<Flex justifyContent="center" className="px-2 py-1">
|
||||
<Button onClick={ event => declineFriend(-1, true) }>
|
||||
{ LocalizeText('friendlist.requests.dismissall') }
|
||||
</Button>
|
||||
</Flex>
|
||||
</Column>
|
||||
{ children }
|
||||
</NitroCardAccordionSetView>
|
||||
);
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import { FC } from 'react';
|
||||
import { Base, Flex, NitroCardAccordionItemView, UserProfileIconView } from '../../../../common';
|
||||
import { MessengerRequest } from '../../common/MessengerRequest';
|
||||
import { useFriendsContext } from '../../FriendsContext';
|
||||
|
||||
interface FriendsRequestItemViewProps
|
||||
{
|
||||
request: MessengerRequest;
|
||||
}
|
||||
|
||||
export const FriendsRequestItemView: FC<FriendsRequestItemViewProps> = props =>
|
||||
{
|
||||
const { request = null } = props;
|
||||
const { acceptFriend = null, declineFriend = null } = useFriendsContext();
|
||||
|
||||
if(!request) return null;
|
||||
|
||||
return (
|
||||
<NitroCardAccordionItemView>
|
||||
<UserProfileIconView userId={ request.id } />
|
||||
<Base>{ request.name }</Base>
|
||||
<Flex className="ms-auto align-items-center" gap={ 1 }>
|
||||
<Base className="nitro-friends-spritesheet icon-accept cursor-pointer" onClick={ event => acceptFriend(request.requesterUserId) } />
|
||||
<Base className="nitro-friends-spritesheet icon-deny cursor-pointer" onClick={ event => declineFriend(request.requesterUserId) } />
|
||||
</Flex>
|
||||
</NitroCardAccordionItemView>
|
||||
);
|
||||
};
|
@ -1,33 +0,0 @@
|
||||
import { FC } from 'react';
|
||||
import { LocalizeText } from '../../../../api';
|
||||
import { Button, Flex, NitroCardAccordionSetView } from '../../../../common';
|
||||
import { MessengerRequest } from '../../common/MessengerRequest';
|
||||
import { useFriendsContext } from '../../FriendsContext';
|
||||
import { FriendsRequestItemView } from './FriendsRequestItemView';
|
||||
|
||||
interface FriendsRequestViewProps
|
||||
{
|
||||
requests: MessengerRequest[];
|
||||
}
|
||||
|
||||
export const FriendsRequestView: FC<FriendsRequestViewProps> = props =>
|
||||
{
|
||||
const { requests = [] } = props;
|
||||
const { declineFriend = null } = useFriendsContext();
|
||||
|
||||
if(!requests.length) return null;
|
||||
|
||||
return (
|
||||
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.tab.friendrequests') + ` (${ requests.length })` } isExpanded={ true }>
|
||||
{ requests.map((request, index) =>
|
||||
{
|
||||
return <FriendsRequestItemView key={ index } request={ request } />
|
||||
}) }
|
||||
<Flex className="justify-content-center p-1">
|
||||
<Button onClick={ event => declineFriend(-1, true) }>
|
||||
{ LocalizeText('friendlist.requests.dismissall') }
|
||||
</Button>
|
||||
</Flex>
|
||||
</NitroCardAccordionSetView>
|
||||
);
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
import { FC, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../api';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common';
|
||||
|
||||
interface FriendsRoomInviteViewProps
|
||||
{
|
||||
selectedFriendsIds: number[];
|
||||
onCloseClick: () => void;
|
||||
sendRoomInvite: (message: string) => void;
|
||||
}
|
||||
|
||||
export const FriendsRoomInviteView: FC<FriendsRoomInviteViewProps> = props =>
|
||||
{
|
||||
const { selectedFriendsIds = null, onCloseClick = null, sendRoomInvite = null } = props;
|
||||
|
||||
const [ roomInviteMessage, setRoomInviteMessage ] = useState<string>('');
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-friends-room-invite" uniqueKey="nitro-friends-room-invite" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('friendlist.invite.title') } onCloseClick={ onCloseClick } />
|
||||
<NitroCardContentView className="text-black d-flex flex-column gap-2">
|
||||
{ LocalizeText('friendlist.invite.summary', ['count'], [selectedFriendsIds.length.toString()]) }
|
||||
<textarea className="form-control" value={roomInviteMessage} onChange={e => setRoomInviteMessage(e.target.value)}></textarea>
|
||||
<div className="bg-muted rounded text-center p-2">{ LocalizeText('friendlist.invite.note') }</div>
|
||||
<div className="d-flex gap-2">
|
||||
<button className="btn btn-success w-100" disabled={ roomInviteMessage.length === 0 || selectedFriendsIds.length === 0 } onClick={ () => sendRoomInvite(roomInviteMessage) }>{ LocalizeText('friendlist.invite.send') }</button>
|
||||
<button className="btn btn-primary w-100" onClick={ onCloseClick }>{ LocalizeText('generic.cancel') }</button>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
};
|
@ -1,79 +0,0 @@
|
||||
import { HabboSearchComposer, HabboSearchResultData, HabboSearchResultEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { LocalizeText, OpenMessengerChat, SendMessageComposer } from '../../../../api';
|
||||
import { Base, Flex, NitroCardAccordionItemView, NitroCardAccordionSetView, NitroCardAccordionView, UserProfileIconView } from '../../../../common';
|
||||
import { BatchUpdates, UseMessageEventHook } from '../../../../hooks';
|
||||
import { useFriendsContext } from '../../FriendsContext';
|
||||
|
||||
export const FriendsSearchView: FC<{}> = props =>
|
||||
{
|
||||
const [ searchValue, setSearchValue ] = useState('');
|
||||
const [ friendResults, setFriendResults ] = useState<HabboSearchResultData[]>([]);
|
||||
const [ otherResults, setOtherResults ] = useState<HabboSearchResultData[]>([]);
|
||||
const { canRequestFriend = null, requestFriend = null } = useFriendsContext();
|
||||
|
||||
const onHabboSearchResultEvent = useCallback((event: HabboSearchResultEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
BatchUpdates(() =>
|
||||
{
|
||||
setFriendResults(parser.friends);
|
||||
setOtherResults(parser.others);
|
||||
});
|
||||
}, []);
|
||||
|
||||
UseMessageEventHook(HabboSearchResultEvent, onHabboSearchResultEvent);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!searchValue || !searchValue.length) return;
|
||||
|
||||
const timeout = setTimeout(() =>
|
||||
{
|
||||
if(!searchValue || !searchValue.length) return;
|
||||
|
||||
SendMessageComposer(new HabboSearchComposer(searchValue));
|
||||
}, 500);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
}, [ searchValue ]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<input type="text" className="search-input form-control form-control-sm w-100 rounded-0" placeholder={ LocalizeText('generic.search') } value={ searchValue } onChange={ event => setSearchValue(event.target.value) } />
|
||||
<NitroCardAccordionView>
|
||||
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.search.friendscaption', [ 'cnt' ], [ friendResults.length.toString() ]) } isExpanded={ true }>
|
||||
{ (friendResults.length > 0) && friendResults.map(result =>
|
||||
{
|
||||
return (
|
||||
<NitroCardAccordionItemView key={ result.avatarId }>
|
||||
<UserProfileIconView userId={ result.avatarId } />
|
||||
<Flex>{ result.avatarName }</Flex>
|
||||
<Flex className="ms-auto align-items-center" gap={ 1 }>
|
||||
{ result.isAvatarOnline &&
|
||||
<Base className="nitro-friends-spritesheet icon-chat cursor-pointer" onClick={ event => OpenMessengerChat(result.avatarId) } title={ LocalizeText('friendlist.tip.im') } /> }
|
||||
</Flex>
|
||||
</NitroCardAccordionItemView>
|
||||
);
|
||||
}) }
|
||||
</NitroCardAccordionSetView>
|
||||
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.search.otherscaption', [ 'cnt' ], [ otherResults.length.toString() ]) } isExpanded={ true }>
|
||||
{ (otherResults.length > 0) && otherResults.map(result =>
|
||||
{
|
||||
return (
|
||||
<NitroCardAccordionItemView key={ result.avatarId }>
|
||||
<UserProfileIconView userId={ result.avatarId } />
|
||||
<Base>{ result.avatarName }</Base>
|
||||
<Flex className="ms-auto align-items-center" gap={ 1 }>
|
||||
{ canRequestFriend(result.avatarId) &&
|
||||
<Base className="nitro-friends-spritesheet icon-add cursor-pointer" onClick={ event => requestFriend(result.avatarId, result.avatarName) } title={ LocalizeText('friendlist.tip.addfriend') } /> }
|
||||
</Flex>
|
||||
</NitroCardAccordionItemView>
|
||||
);
|
||||
}) }
|
||||
</NitroCardAccordionSetView>
|
||||
</NitroCardAccordionView>
|
||||
</>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user