Update friends

This commit is contained in:
Bill 2021-11-18 03:40:51 -05:00
parent 91ff5d4c66
commit 2c04e7e075
13 changed files with 264 additions and 74 deletions

View File

@ -1,6 +1,95 @@
.nitro-friends-spritesheet {
background: url('../../assets/images/friends/friends-spritesheet.png') transparent no-repeat;
&.icon-friendbar-visit {
width: 21px; height: 21px;
background-position: -38px -5px;
}
&.icon-heart {
width: 16px; height: 14px;
background-position: -5px -67px;
}
&.icon-new-message {
width: 14px; height: 14px;
background-position: -96px -53px;
}
&.icon-none {
width: 16px; height: 14px;
background-position: -31px -67px;
}
&.icon-profile {
width: 21px; height: 21px;
background-position: -5px -36px;
}
&.icon-profile-sm {
width: 13px; height: 11px;
background-position: -51px -91px;
&:hover {
width: 13px; height: 11px;
background-position: -74px -91px;
}
}
&.icon-smile {
width: 16px; height: 14px;
background-position: -57px -67px;
}
&.icon-warning {
width: 23px; height: 21px;
background-position: -5px -5px;
}
&.icon-accept {
width: 13px; height: 14px;
background-position: -5px -91px;
}
&.icon-add {
width: 16px; height: 15px;
background-position: -69px -31px;
}
&.icon-bobba {
width: 16px; height: 14px;
background-position: -96px -5px;
}
&.icon-chat {
width: 17px; height: 16px;
background-position: -69px -5px;
}
&.icon-deny {
width: 13px; height: 14px;
background-position: -28px -91px;
}
&.icon-follow {
width: 16px; height: 14px;
background-position: -96px -29px;
}
&.icon-friendbar-chat {
width: 20px; height: 21px;
background-position: -36px -36px;
}
}
.nitro-friends { .nitro-friends {
width: $friends-list-width; width: $friends-list-width;
height: $friends-list-height; height: $friends-list-height;
.search-input {
border: 0;
border-bottom: 1px solid rgba($black, 0.2);
}
} }
@import "./views/friend-bar/FriendBarView"; @import "./views/friend-bar/FriendBarView";

View File

@ -344,7 +344,7 @@ export const FriendsView: FC<{}> = props =>
}, [ requests ]); }, [ requests ]);
return ( return (
<FriendsContextProvider value={ { friends, requests, settings, acceptFriend, declineFriend } }> <FriendsContextProvider value={ { friends, requests, settings, canRequestFriend, requestFriend, acceptFriend, declineFriend } }>
{ isReady && { isReady &&
createPortal(<FriendBarView onlineFriends={ onlineFriends } />, document.getElementById('toolbar-friend-bar-container')) } createPortal(<FriendBarView onlineFriends={ onlineFriends } />, document.getElementById('toolbar-friend-bar-container')) }
{ isVisible && { isVisible &&

View File

@ -5,6 +5,8 @@ const FriendsContext = createContext<IFriendsContext>({
friends: null, friends: null,
requests: null, requests: null,
settings: null, settings: null,
canRequestFriend: null,
requestFriend: null,
acceptFriend: null, acceptFriend: null,
declineFriend: null declineFriend: null
}); });

View File

@ -8,6 +8,8 @@ export interface IFriendsContext
friends: MessengerFriend[]; friends: MessengerFriend[];
requests: MessengerRequest[]; requests: MessengerRequest[];
settings: MessengerSettings; settings: MessengerSettings;
canRequestFriend: (userId: number) => boolean;
requestFriend: (userId: number, userName: string) => void;
acceptFriend: (userId: number) => void; acceptFriend: (userId: number) => void;
declineFriend: (userId: number, declineAll?: boolean) => void; declineFriend: (userId: number, declineAll?: boolean) => void;
} }

View File

@ -2,6 +2,7 @@ import { FollowFriendMessageComposer, MouseEventType } from '@nitrots/nitro-rend
import { FC, useCallback, useEffect, useRef, useState } from 'react'; import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { GetUserProfile, LocalizeText, OpenMessengerChat } from '../../../../api'; import { GetUserProfile, LocalizeText, OpenMessengerChat } from '../../../../api';
import { SendMessageHook } from '../../../../hooks/messages'; import { SendMessageHook } from '../../../../hooks/messages';
import { NitroLayoutBase } from '../../../../layout/base';
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView'; import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
import { FriendBarItemViewProps } from './FriendBarItemView.types'; import { FriendBarItemViewProps } from './FriendBarItemView.types';
@ -63,9 +64,10 @@ export const FriendBarItemView: FC<FriendBarItemViewProps> = props =>
<div className="text-truncate">{ friend.name }</div> <div className="text-truncate">{ friend.name }</div>
{ isVisible && { isVisible &&
<div className="d-flex justify-content-between"> <div className="d-flex justify-content-between">
<i onClick={ openMessengerChat } className="icon icon-fb-chat cursor-pointer" /> <NitroLayoutBase className="nitro-friends-spritesheet icon-friendbar-chat cursor-pointer" onClick={ openMessengerChat } />
{ friend.followingAllowed && <i onClick={ followFriend } className="icon icon-fb-visit cursor-pointer" /> } { friend.followingAllowed &&
<i onClick={ event => GetUserProfile(friend.id) } className="icon icon-fb-profile cursor-pointer" /> <NitroLayoutBase className="nitro-friends-spritesheet icon-friendbar-visit cursor-pointer" onClick={ followFriend } /> }
<NitroLayoutBase className="nitro-friends-spritesheet icon-profile cursor-pointer" onClick={ event => GetUserProfile(friend.id) } />
</div> } </div> }
</div> </div>
); );

View File

@ -1,4 +1,6 @@
import { FC, useMemo, useState } from 'react'; import { FC, useMemo, useState } from 'react';
import { NitroLayoutButton, NitroLayoutFlex } from '../../../../layout';
import { NitroLayoutBase } from '../../../../layout/base';
import { FriendBarItemView } from '../friend-bar-item/FriendBarItemView'; import { FriendBarItemView } from '../friend-bar-item/FriendBarItemView';
import { FriendBarViewProps } from './FriendBarView.types'; import { FriendBarViewProps } from './FriendBarView.types';
@ -24,10 +26,10 @@ export const FriendBarView: FC<FriendBarViewProps> = props =>
}, [ maxDisplayCount, indexOffset, onlineFriends ]); }, [ maxDisplayCount, indexOffset, onlineFriends ]);
return ( return (
<div className="d-flex friend-bar align-items-center"> <NitroLayoutFlex className="friend-bar align-items-center">
<button type="button" className="btn btn-sm btn-black align-self-center friend-bar-button" disabled={ !canDecreaseIndex } onClick={ event => setIndexOffset(indexOffset - 1) }> <NitroLayoutButton className="friend-bar-button" variant="black" size="sm" disabled={ !canDecreaseIndex } onClick={ event => setIndexOffset(indexOffset - 1) }>
<i className="fas fa-chevron-left" /> <NitroLayoutBase className="fas fa-chevron-left" />
</button> </NitroLayoutButton>
{ Array.from(Array(maxDisplayCount), (e, i) => { Array.from(Array(maxDisplayCount), (e, i) =>
{ {
return <FriendBarItemView key={ i } friend={ (onlineFriends[ indexOffset + i ] || null) } />; return <FriendBarItemView key={ i } friend={ (onlineFriends[ indexOffset + i ] || null) } />;
@ -35,6 +37,6 @@ export const FriendBarView: FC<FriendBarViewProps> = props =>
<button type="button" className="btn btn-sm btn-black align-self-center friend-bar-button" disabled={ !canIncreaseIndex } onClick={ event => setIndexOffset(indexOffset + 1) }> <button type="button" className="btn btn-sm btn-black align-self-center friend-bar-button" disabled={ !canIncreaseIndex } onClick={ event => setIndexOffset(indexOffset + 1) }>
<i className="fas fa-chevron-right" /> <i className="fas fa-chevron-right" />
</button> </button>
</div> </NitroLayoutFlex>
); );
} }

View File

@ -2,7 +2,8 @@ import { FollowFriendMessageComposer, SetRelationshipStatusComposer } from '@nit
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { LocalizeText, OpenMessengerChat } from '../../../../api'; import { LocalizeText, OpenMessengerChat } from '../../../../api';
import { SendMessageHook } from '../../../../hooks'; import { SendMessageHook } from '../../../../hooks';
import { UserProfileIconView } from '../../../shared/user-profile-icon/UserProfileIconView'; import { NitroLayoutFlex, UserProfileIconView } from '../../../../layout';
import { NitroLayoutBase } from '../../../../layout/base';
import { MessengerFriend } from '../../common/MessengerFriend'; import { MessengerFriend } from '../../common/MessengerFriend';
import { FriendsGroupItemViewProps } from './FriendsGroupItemView.types'; import { FriendsGroupItemViewProps } from './FriendsGroupItemView.types';
@ -52,19 +53,23 @@ export const FriendsGroupItemView: FC<FriendsGroupItemViewProps> = props =>
<div className="px-2 py-1 d-flex gap-1 align-items-center"> <div className="px-2 py-1 d-flex gap-1 align-items-center">
<UserProfileIconView userId={ friend.id } /> <UserProfileIconView userId={ friend.id } />
<div>{ friend.name }</div> <div>{ friend.name }</div>
<div className="ms-auto d-flex align-items-center gap-1"> <NitroLayoutFlex className="ms-auto align-items-center" gap={ 1 }>
{ !isExpanded && <> { !isExpanded &&
{ friend.followingAllowed && <i onClick={ followFriend } className="icon icon-friendlist-follow cursor-pointer" title={ LocalizeText('friendlist.tip.follow') } /> } <>
{ friend.online && <i className="icon icon-friendlist-chat cursor-pointer" onClick={ openMessengerChat } title={ LocalizeText('friendlist.tip.im') } /> } { friend.followingAllowed &&
<i className={ 'icon cursor-pointer icon-relationship-' + getCurrentRelationshipName() } onClick={ () => setIsExpanded(true) } title={ LocalizeText('infostand.link.relationship') } /> <NitroLayoutBase onClick={ followFriend } className="nitro-friends-spritesheet icon-follow cursor-pointer" title={ LocalizeText('friendlist.tip.follow') } /> }
{ friend.online &&
<NitroLayoutBase className="nitro-friends-spritesheet icon-chat cursor-pointer" onClick={ openMessengerChat } title={ LocalizeText('friendlist.tip.im') } /> }
<NitroLayoutBase className={ `nitro-friends-spritesheet icon-${ getCurrentRelationshipName() } cursor-pointer` }onClick={ event => setIsExpanded(true) } title={ LocalizeText('infostand.link.relationship') } />
</> } </> }
{ isExpanded && <> { isExpanded &&
<i className="icon icon-relationship-heart cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_HEART) } /> <>
<i className="icon icon-relationship-smile cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_SMILE) } /> <NitroLayoutBase className="nitro-friends-spritesheet icon-heart cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_HEART) } />
<i className="icon icon-relationship-bobba cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_BOBBA) } /> <NitroLayoutBase className="nitro-friends-spritesheet icon-smile cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_SMILE) } />
<i className="icon icon-relationship-none cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_NONE) } /> <NitroLayoutBase className="nitro-friends-spritesheet icon-bobba cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_BOBBA) } />
<NitroLayoutBase className="nitro-friends-spritesheet icon-none cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_NONE) } />
</> } </> }
</div> </NitroLayoutFlex>
</div> </div>
); );
} }

View File

@ -3,6 +3,7 @@ import { LocalizeText } from '../../../../api';
import { NitroCardAccordionSetView, NitroCardAccordionView, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../layout'; import { NitroCardAccordionSetView, NitroCardAccordionView, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../layout';
import { FriendsGroupView } from '../friends-group/FriendsGroupView'; import { FriendsGroupView } from '../friends-group/FriendsGroupView';
import { FriendsRequestView } from '../friends-request/FriendsRequestView'; import { FriendsRequestView } from '../friends-request/FriendsRequestView';
import { FriendsSearchView } from '../friends-search/FriendsSearchView';
import { FriendsListViewProps } from './FriendsListView.types'; import { FriendsListViewProps } from './FriendsListView.types';
const MODE_FRIENDS: number = 0; const MODE_FRIENDS: number = 0;
@ -15,7 +16,7 @@ export const FriendsListView: FC<FriendsListViewProps> = props =>
const [ mode, setMode ] = useState<number>(0); const [ mode, setMode ] = useState<number>(0);
return ( return (
<NitroCardView className="nitro-friends"> <NitroCardView className="nitro-friends" uniqueKey="nitro-friends">
<NitroCardHeaderView headerText={ LocalizeText('friendlist.friends') } onCloseClick={ onCloseClick } /> <NitroCardHeaderView headerText={ LocalizeText('friendlist.friends') } onCloseClick={ onCloseClick } />
<NitroCardTabsView> <NitroCardTabsView>
<NitroCardTabsItemView isActive={ (mode === MODE_FRIENDS) } count={ friendRequests.length } onClick={ event => setMode(MODE_FRIENDS) }> <NitroCardTabsItemView isActive={ (mode === MODE_FRIENDS) } count={ friendRequests.length } onClick={ event => setMode(MODE_FRIENDS) }>
@ -34,13 +35,10 @@ export const FriendsListView: FC<FriendsListViewProps> = props =>
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.friends.offlinecaption') + ` (${offlineFriends.length})` }> <NitroCardAccordionSetView headerText={ LocalizeText('friendlist.friends.offlinecaption') + ` (${offlineFriends.length})` }>
<FriendsGroupView list={ offlineFriends } /> <FriendsGroupView list={ offlineFriends } />
</NitroCardAccordionSetView> </NitroCardAccordionSetView>
<FriendsRequestView requests={ friendRequests } /> <FriendsRequestView requests={ friendRequests } />
</NitroCardAccordionView> } </NitroCardAccordionView> }
{ (mode === MODE_SEARCH) && { (mode === MODE_SEARCH) &&
<> <FriendsSearchView /> }
</> }
</NitroCardContentView> </NitroCardContentView>
</NitroCardView> </NitroCardView>
); );

View File

@ -1,6 +1,6 @@
import { FC } from 'react'; import { FC } from 'react';
import { NitroCardAccordionItemView } from '../../../../layout'; import { NitroCardAccordionItemView, NitroLayoutFlex, UserProfileIconView } from '../../../../layout';
import { UserProfileIconView } from '../../../shared/user-profile-icon/UserProfileIconView'; import { NitroLayoutBase } from '../../../../layout/base';
import { useFriendsContext } from '../../context/FriendsContext'; import { useFriendsContext } from '../../context/FriendsContext';
import { FriendsRequestItemViewProps } from './FriendsRequestItemView.types'; import { FriendsRequestItemViewProps } from './FriendsRequestItemView.types';
@ -14,11 +14,11 @@ export const FriendsRequestItemView: FC<FriendsRequestItemViewProps> = props =>
return ( return (
<NitroCardAccordionItemView> <NitroCardAccordionItemView>
<UserProfileIconView userId={ request.id } /> <UserProfileIconView userId={ request.id } />
<div>{ request.name }</div> <NitroLayoutBase>{ request.name }</NitroLayoutBase>
<div className="ms-auto d-flex align-items-center gap-1"> <NitroLayoutFlex className="ms-auto align-items-center" gap={ 1 }>
<i className="icon icon-accept cursor-pointer" onClick={ event => acceptFriend(request.requesterUserId) } /> <NitroLayoutBase className="nitro-friends-spritesheet icon-accept cursor-pointer" onClick={ event => acceptFriend(request.requesterUserId) } />
<i className="icon icon-deny cursor-pointer" onClick={ event => declineFriend(request.requesterUserId) } /> <NitroLayoutBase className="nitro-friends-spritesheet icon-deny cursor-pointer" onClick={ event => declineFriend(request.requesterUserId) } />
</div> </NitroLayoutFlex>
</NitroCardAccordionItemView> </NitroCardAccordionItemView>
); );
}; };

View File

@ -0,0 +1,80 @@
import { HabboSearchComposer, HabboSearchResultData, HabboSearchResultEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react';
import { LocalizeText, OpenMessengerChat } from '../../../../api';
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../hooks';
import { NitroCardAccordionItemView, NitroCardAccordionSetView, NitroCardAccordionView, NitroLayoutFlex, UserProfileIconView } from '../../../../layout';
import { NitroLayoutBase } from '../../../../layout/base';
import { useFriendsContext } from '../../context/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);
});
}, []);
CreateMessageHook(HabboSearchResultEvent, onHabboSearchResultEvent);
useEffect(() =>
{
if(!searchValue || !searchValue.length) return;
const timeout = setTimeout(() =>
{
if(!searchValue || !searchValue.length) return;
SendMessageHook(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 } />
<NitroLayoutBase>{ result.avatarName }</NitroLayoutBase>
<NitroLayoutFlex className="ms-auto align-items-center" gap={ 1 }>
{ result.isAvatarOnline &&
<NitroLayoutBase className="nitro-friends-spritesheet icon-chat cursor-pointer" onClick={ event => OpenMessengerChat(result.avatarId) } title={ LocalizeText('friendlist.tip.im') } /> }
</NitroLayoutFlex>
</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 } />
<NitroLayoutBase>{ result.avatarName }</NitroLayoutBase>
<NitroLayoutFlex className="ms-auto align-items-center" gap={ 1 }>
{ canRequestFriend(result.avatarId) &&
<NitroLayoutBase className="nitro-friends-spritesheet icon-add cursor-pointer" onClick={ event => requestFriend(result.avatarId, result.avatarName) } title={ LocalizeText('friendlist.tip.addfriend') } /> }
</NitroLayoutFlex>
</NitroCardAccordionItemView>
);
}) }
</NitroCardAccordionSetView>
</NitroCardAccordionView>
</>
);
}

View File

@ -1,5 +1,7 @@
import { FC } from 'react'; import { FC } from 'react';
import { GetSessionDataManager } from '../../../../api'; import { GetSessionDataManager } from '../../../../api';
import { NitroLayoutFlex } from '../../../../layout';
import { NitroLayoutBase } from '../../../../layout/base';
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView'; import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
import { MessengerThreadChat } from '../../common/MessengerThreadChat'; import { MessengerThreadChat } from '../../common/MessengerThreadChat';
import { FriendsMessengerThreadGroupProps } from './FriendsMessengerThreadGroup.types'; import { FriendsMessengerThreadGroupProps } from './FriendsMessengerThreadGroup.types';
@ -17,13 +19,13 @@ export const FriendsMessengerThreadGroup: FC<FriendsMessengerThreadGroupProps> =
{ group.chats.map((chat, index) => { group.chats.map((chat, index) =>
{ {
return ( return (
<div key={ index } className="text-break"> <NitroLayoutBase key={ index } className="text-break">
{ chat.type === MessengerThreadChat.SECURITY_NOTIFICATION && { chat.type === MessengerThreadChat.SECURITY_NOTIFICATION &&
<div className="bg-light rounded mb-2 d-flex gap-2 px-2 py-1 small text-muted align-items-center"> <NitroLayoutBase className="bg-light rounded mb-2 d-flex gap-2 px-2 py-1 small text-muted align-items-center">
<i className="icon icon-friendlist-warning flex-shrink-0" /> <NitroLayoutBase className="nitro-friends-spritesheet icon-warning flex-shrink-0" />
<div>{ chat.message }</div> <NitroLayoutBase>{ chat.message }</NitroLayoutBase>
</div> } </NitroLayoutBase> }
</div> </NitroLayoutBase>
); );
}) } }) }
</div> </div>
@ -31,18 +33,18 @@ export const FriendsMessengerThreadGroup: FC<FriendsMessengerThreadGroupProps> =
} }
return ( return (
<div className={ 'd-flex gap-2 w-100 justify-content-' + (group.userId === 0 ? 'end' : 'start') }> <NitroLayoutFlex className={ 'w-100 justify-content-' + (group.userId === 0 ? 'end' : 'start') } gap={ 2 }>
{ (group.userId > 0) && { (group.userId > 0) &&
<div className="message-avatar flex-shrink-0"> <NitroLayoutBase className="message-avatar flex-shrink-0">
<AvatarImageView figure={ thread.participant.figure } direction={ 2 } /> <AvatarImageView figure={ thread.participant.figure } direction={ 2 } />
</div> } </NitroLayoutBase> }
<div className={ 'bg-light text-black border-radius mb-2 rounded py-1 px-2 messages-group-' + (group.userId === 0 ? 'right' : 'left') }> <NitroLayoutBase className={ 'bg-light text-black border-radius mb-2 rounded py-1 px-2 messages-group-' + (group.userId === 0 ? 'right' : 'left') }>
{ group.chats.map((chat, index) => <div key={ index } className="text-break">{ chat.message }</div>) } { group.chats.map((chat, index) => <NitroLayoutBase key={ index } className="text-break">{ chat.message }</NitroLayoutBase>) }
</div> </NitroLayoutBase>
{ (group.userId === 0) && { (group.userId === 0) &&
<div className="message-avatar flex-shrink-0"> <NitroLayoutBase className="message-avatar flex-shrink-0">
<AvatarImageView figure={ GetSessionDataManager().figure } direction={ 4 } /> <AvatarImageView figure={ GetSessionDataManager().figure } direction={ 4 } />
</div> } </NitroLayoutBase> }
</div> </NitroLayoutFlex>
); );
} }

View File

@ -3,7 +3,8 @@ import { FC, KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState }
import { AddEventLinkTracker, GetUserProfile, LocalizeText, RemoveLinkEventTracker } from '../../../../api'; import { AddEventLinkTracker, GetUserProfile, LocalizeText, RemoveLinkEventTracker } from '../../../../api';
import { FriendsMessengerIconEvent } from '../../../../events'; import { FriendsMessengerIconEvent } from '../../../../events';
import { BatchUpdates, CreateMessageHook, dispatchUiEvent, SendMessageHook } from '../../../../hooks'; import { BatchUpdates, CreateMessageHook, dispatchUiEvent, SendMessageHook } from '../../../../hooks';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutFlex } from '../../../../layout'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutButton, NitroLayoutButtonGroup, NitroLayoutFlex, NitroLayoutFlexColumn } from '../../../../layout';
import { NitroLayoutBase } from '../../../../layout/base';
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView'; import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
import { MessengerThread } from '../../common/MessengerThread'; import { MessengerThread } from '../../common/MessengerThread';
import { MessengerThreadChat } from '../../common/MessengerThreadChat'; import { MessengerThreadChat } from '../../common/MessengerThreadChat';
@ -232,7 +233,7 @@ export const FriendsMessengerView: FC<{}> = props =>
if(!isVisible) return null; if(!isVisible) return null;
return ( return (
<NitroCardView className="nitro-friends-messenger" simple={ true }> <NitroCardView className="nitro-friends-messenger" uniqueKey="nitro-friends-messenger" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('messenger.window.title', [ 'OPEN_CHAT_COUNT' ], [ visibleThreads.length.toString() ]) } onCloseClick={ event => setIsVisible(false) } /> <NitroCardHeaderView headerText={ LocalizeText('messenger.window.title', [ 'OPEN_CHAT_COUNT' ], [ visibleThreads.length.toString() ]) } onCloseClick={ event => setIsVisible(false) } />
<NitroCardContentView> <NitroCardContentView>
<NitroLayoutFlex gap={ 2 } overflow="auto"> <NitroLayoutFlex gap={ 2 } overflow="auto">
@ -241,8 +242,9 @@ export const FriendsMessengerView: FC<{}> = props =>
const messageThreadIndex = messageThreads.indexOf(thread); const messageThreadIndex = messageThreads.indexOf(thread);
return ( return (
<div key={ index } className="friend-head rounded flex-shrink-0 cursor-pointer bg-muted" onClick={ event => setActiveThreadIndex(messageThreadIndex) }> <div key={ index } className="position-relative friend-head rounded flex-shrink-0 cursor-pointer bg-muted" onClick={ event => setActiveThreadIndex(messageThreadIndex) }>
{ thread.unread && <i className="icon icon-friendlist-new-message" /> } { thread.unread &&
<NitroLayoutBase className="position-absolute nitro-friends-spritesheet icon-new-message top-1 end-1 z-index-1" /> }
<AvatarImageView figure={ thread.participant.figure } headOnly={ true } direction={ 3 } /> <AvatarImageView figure={ thread.participant.figure } headOnly={ true } direction={ 3 } />
</div> </div>
); );
@ -250,32 +252,38 @@ export const FriendsMessengerView: FC<{}> = props =>
</NitroLayoutFlex> </NitroLayoutFlex>
<NitroLayoutFlex className="align-items-center my-1" position="relative"> <NitroLayoutFlex className="align-items-center my-1" position="relative">
{ (activeThreadIndex >= 0) && { (activeThreadIndex >= 0) &&
<div className="text-black bg-light pe-2 flex-none"> <NitroLayoutBase className="text-black bg-light pe-2 flex-none">
{ LocalizeText('messenger.window.separator', [ 'FRIEND_NAME' ], [ messageThreads[activeThreadIndex].participant.name ]) } { LocalizeText('messenger.window.separator', [ 'FRIEND_NAME' ], [ messageThreads[activeThreadIndex].participant.name ]) }
</div> } </NitroLayoutBase> }
<hr className="bg-dark m-0 w-100" /> <hr className="bg-dark m-0 w-100" />
</NitroLayoutFlex> </NitroLayoutFlex>
{ (activeThreadIndex >= 0) && { (activeThreadIndex >= 0) &&
<> <>
<div className="d-flex gap-2 mb-2"> <NitroLayoutFlex className="mb-2" gap={ 2 }>
<div className="btn-group"> <NitroLayoutButtonGroup>
<button className="d-flex justify-content-center align-items-center btn btn-sm btn-primary" onClick={ followFriend }> <NitroLayoutButton variant="primary" size="sm" onClick={ followFriend }>
<i className="icon icon-friendlist-follow" /> <NitroLayoutBase className="nitro-friends-spritesheet icon-follow" />
</button> </NitroLayoutButton>
<button className="d-flex justify-content-center align-items-center btn btn-sm btn-primary" onClick={ openProfile }> <NitroLayoutButton variant="primary" size="sm" onClick={ openProfile }>
<i className="icon icon-user-profile" /> <NitroLayoutBase className="nitro-friends-spritesheet icon-profile-sm" />
</button> </NitroLayoutButton>
</div> </NitroLayoutButtonGroup>
<button className="btn btn-sm btn-danger">{ LocalizeText('messenger.window.button.report') }</button> <NitroLayoutButton variant="danger" size="sm" onClick={ openProfile }>
<button className="btn btn-sm btn-primary ms-auto" onClick={ event => closeThread(activeThreadIndex) }><i className="fas fa-times" /></button> { LocalizeText('messenger.window.button.report') }
</div> </NitroLayoutButton>
<div ref={ messagesBox } className="bg-muted p-2 rounded chat-messages mb-2 d-flex flex-column"> <NitroLayoutButton className="ms-auto" variant="primary" size="sm" onClick={ event => closeThread(activeThreadIndex) }>
<NitroLayoutBase className="fas fa-times" />
</NitroLayoutButton>
</NitroLayoutFlex>
<NitroLayoutFlexColumn innerRef={ messagesBox } className="bg-muted p-2 rounded chat-messages mb-2">
<FriendsMessengerThreadView thread={ messageThreads[activeThreadIndex] } /> <FriendsMessengerThreadView thread={ messageThreads[activeThreadIndex] } />
</div> </NitroLayoutFlexColumn>
<div className="d-flex gap-2"> <NitroLayoutFlex gap={ 2 }>
<input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('messenger.window.input.default', [ 'FRIEND_NAME' ], [ messageThreads[activeThreadIndex].participant.name ]) } value={ messageText } onChange={ event => setMessageText(event.target.value) } onKeyDown={ onKeyDown } /> <input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('messenger.window.input.default', [ 'FRIEND_NAME' ], [ messageThreads[activeThreadIndex].participant.name ]) } value={ messageText } onChange={ event => setMessageText(event.target.value) } onKeyDown={ onKeyDown } />
<button className="btn btn-sm btn-success" onClick={ sendMessage }>{ LocalizeText('widgets.chatinput.say') }</button> <NitroLayoutButton variant="success" size="sm" onClick={ sendMessage }>
</div> { LocalizeText('widgets.chatinput.say') }
</NitroLayoutButton>
</NitroLayoutFlex>
</> } </> }
</NitroCardContentView> </NitroCardContentView>
</NitroCardView> </NitroCardView>

View File

@ -1,7 +1,7 @@
import { AcceptFriendMessageComposer, DeclineFriendMessageComposer } from '@nitrots/nitro-renderer'; import { AcceptFriendMessageComposer, DeclineFriendMessageComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react'; import { FC, useCallback } from 'react';
import { SendMessageHook } from '../../../../hooks/messages/message-event'; import { SendMessageHook } from '../../../../hooks';
import { UserProfileIconView } from '../../../shared/user-profile-icon/UserProfileIconView'; import { UserProfileIconView } from '../../../../layout';
import { FriendsRequestItemViewProps } from './FriendsRequestItemView.types'; import { FriendsRequestItemViewProps } from './FriendsRequestItemView.types';
export const FriendsRequestItemView: FC<FriendsRequestItemViewProps> = props => export const FriendsRequestItemView: FC<FriendsRequestItemViewProps> = props =>