mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-23 06:40:50 +01:00
Messenger Chat
This commit is contained in:
parent
b5850c1995
commit
7a6fe87aed
@ -19,6 +19,10 @@ $nitro-card-tabs-height: 33px;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
|
||||
.theme-primary {
|
||||
border: $border-width solid $border-color;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(lg) {
|
||||
|
||||
.draggable-window {
|
||||
|
@ -11,7 +11,7 @@ export const NitroCardView: FC<NitroCardViewProps> = props =>
|
||||
<NitroCardContextProvider value={ { theme, simple } }>
|
||||
<div className="nitro-card-responsive">
|
||||
<DraggableWindow { ...rest }>
|
||||
<div className={ 'nitro-card d-flex flex-column rounded border shadow overflow-hidden ' + className }>
|
||||
<div className={ `nitro-card d-flex flex-column rounded shadow overflow-hidden theme-${theme} ${className}` }>
|
||||
{ children }
|
||||
</div>
|
||||
</DraggableWindow>
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { FriendListFragmentEvent, FriendListUpdateEvent, FriendRequestsEvent, GetFriendRequestsComposer, MessengerInitEvent, NewConsoleMessageEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback } from 'react';
|
||||
import { GetSessionDataManager } from '../../api';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event';
|
||||
import { MessengerChatMessage } from './common/MessengerChatMessage';
|
||||
import { MessengerSettings } from './common/MessengerSettings';
|
||||
import { useFriendsContext } from './context/FriendsContext';
|
||||
import { FriendsActions } from './reducers/FriendsReducer';
|
||||
@ -68,17 +70,17 @@ export const FriendsMessageHandler: FC<{}> = props =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
const activeChat = activeChats.find(c => c.friendId === parser.senderId);
|
||||
let userId = parser.senderId;
|
||||
|
||||
if(activeChat)
|
||||
{
|
||||
if(userId === GetSessionDataManager().userId) userId = 0;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}, [ friendsState, dispatchFriendsState ]);
|
||||
dispatchFriendsState({
|
||||
type: FriendsActions.ADD_CHAT_MESSAGE,
|
||||
payload: {
|
||||
chatMessage: new MessengerChatMessage(MessengerChatMessage.MESSAGE, userId, parser.messageText, parser.secondsSinceSent, parser.extraData)
|
||||
}
|
||||
});
|
||||
}, [ dispatchFriendsState ]);
|
||||
|
||||
CreateMessageHook(MessengerInitEvent, onMessengerInitEvent);
|
||||
CreateMessageHook(FriendListFragmentEvent, onFriendsFragmentEvent);
|
||||
|
@ -3,3 +3,4 @@
|
||||
}
|
||||
|
||||
@import './views/friend-bar/FriendBarView';
|
||||
@import './views/messenger/FriendsMessengerView';
|
||||
|
@ -95,7 +95,7 @@ export const FriendsView: FC<{}> = props =>
|
||||
return (
|
||||
<FriendsContextProvider value={ { friendsState, dispatchFriendsState } }>
|
||||
<FriendsMessageHandler />
|
||||
{ isReady && createPortal(<FriendBarView />, document.getElementById('toolbar-friend-bar-container')) }
|
||||
{ isReady && createPortal(<FriendBarView onlineFriends={ onlineFriends } />, document.getElementById('toolbar-friend-bar-container')) }
|
||||
{ isListVisible && <FriendsListView onlineFriends={ onlineFriends } offlineFriends={ offlineFriends } friendRequests={ requests } onCloseClick={ () => setIsListVisible(false) } /> }
|
||||
<FriendsMessengerView />
|
||||
</FriendsContextProvider>
|
||||
|
@ -1,20 +1,24 @@
|
||||
import { MessengerChatMessage } from './MessengerChatMessage';
|
||||
import { MessengerChatMessageGroup } from './MessengerChatMessageGroup';
|
||||
export class MessengerChat
|
||||
{
|
||||
private _friendId: number;
|
||||
private _isRead: boolean;
|
||||
private _messages: MessengerChatMessage[];
|
||||
private _messageGroups: MessengerChatMessageGroup[];
|
||||
|
||||
constructor(friendId: number, isRead: boolean = true)
|
||||
{
|
||||
this._friendId = friendId;
|
||||
this._isRead = isRead;
|
||||
this._messages = [];
|
||||
this._messageGroups = [];
|
||||
}
|
||||
|
||||
public addMessage(type: number, senderId: number, message: string, sentAt: number, extraData?: string): void
|
||||
public addMessage(message: MessengerChatMessage): void
|
||||
{
|
||||
this._messages.push(new MessengerChatMessage(type, senderId, message, sentAt, extraData));
|
||||
if(!this.lastMessageGroup || this.lastMessageGroup.userId !== message.senderId) this._messageGroups.push(new MessengerChatMessageGroup(message.senderId));
|
||||
|
||||
this.lastMessageGroup.addMessage(message);
|
||||
this._isRead = false;
|
||||
}
|
||||
|
||||
public get friendId(): number
|
||||
@ -27,8 +31,13 @@ export class MessengerChat
|
||||
return this._isRead;
|
||||
}
|
||||
|
||||
public get messages(): MessengerChatMessage[]
|
||||
public get messageGroups(): MessengerChatMessageGroup[]
|
||||
{
|
||||
return this._messages;
|
||||
return this._messageGroups;
|
||||
}
|
||||
|
||||
public get lastMessageGroup(): MessengerChatMessageGroup
|
||||
{
|
||||
return this._messageGroups[this._messageGroups.length - 1];
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
export class MessengerChatMessage
|
||||
{
|
||||
public static MESSAGE: number = 0;
|
||||
public static ROOM_INVITE: number = 1;
|
||||
public static SYSTEM_NOTIFICATION: number = 2;
|
||||
|
||||
private _type: number;
|
||||
private _senderId: number;
|
||||
private _message: string;
|
||||
|
28
src/views/friends/common/MessengerChatMessageGroup.ts
Normal file
28
src/views/friends/common/MessengerChatMessageGroup.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { MessengerChatMessage } from './MessengerChatMessage';
|
||||
|
||||
export class MessengerChatMessageGroup
|
||||
{
|
||||
private _userId: number;
|
||||
private _messages: MessengerChatMessage[];
|
||||
|
||||
constructor(userId: number)
|
||||
{
|
||||
this._userId = userId;
|
||||
this._messages = [];
|
||||
}
|
||||
|
||||
public addMessage(message: MessengerChatMessage): void
|
||||
{
|
||||
this._messages.push(message);
|
||||
}
|
||||
|
||||
public get userId(): number
|
||||
{
|
||||
return this._userId;
|
||||
}
|
||||
|
||||
public get messages(): MessengerChatMessage[]
|
||||
{
|
||||
return this._messages;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import { FriendListUpdateParser, FriendParser, FriendRequestData } from '@nitrots/nitro-renderer';
|
||||
import { Reducer } from 'react';
|
||||
import { MessengerChat } from '../common/MessengerChat';
|
||||
import { MessengerChatMessage } from '../common/MessengerChatMessage';
|
||||
import { MessengerFriend } from '../common/MessengerFriend';
|
||||
import { MessengerRequest } from '../common/MessengerRequest';
|
||||
import { MessengerSettings } from '../common/MessengerSettings';
|
||||
@ -29,6 +30,8 @@ export interface IFriendsAction
|
||||
update?: FriendListUpdateParser;
|
||||
requests?: FriendRequestData[];
|
||||
chats?: MessengerChat[];
|
||||
chatMessage?: MessengerChatMessage;
|
||||
numberValue?: number;
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +43,7 @@ export class FriendsActions
|
||||
public static PROCESS_UPDATE: string = 'FA_PROCESS_UPDATE';
|
||||
public static PROCESS_REQUESTS: string = 'FA_PROCESS_REQUESTS';
|
||||
public static SET_ACTIVE_CHATS: string = 'FA_SET_ACTIVE_CHATS';
|
||||
public static ADD_CHAT_MESSAGE: string = 'FA_ADD_CHAT_MESSAGE';
|
||||
}
|
||||
|
||||
export const initialFriends: IFriendsState = {
|
||||
@ -143,6 +147,24 @@ export const FriendsReducer: Reducer<IFriendsState, IFriendsAction> = (state, ac
|
||||
|
||||
return { ...state, activeChats };
|
||||
}
|
||||
case FriendsActions.ADD_CHAT_MESSAGE: {
|
||||
const message = action.payload.chatMessage;
|
||||
const toFriendId = action.payload.numberValue;
|
||||
|
||||
const activeChats = Array.from(state.activeChats);
|
||||
|
||||
let activeChatIndex = activeChats.findIndex(c => c.friendId === toFriendId ? toFriendId : message.senderId);
|
||||
|
||||
if(activeChatIndex === -1)
|
||||
{
|
||||
activeChats.push(new MessengerChat(message.senderId, false));
|
||||
activeChatIndex = activeChats.length - 1;
|
||||
}
|
||||
|
||||
activeChats[activeChatIndex].addMessage(message);
|
||||
|
||||
return { ...state, activeChats };
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FollowFriendMessageComposer, MouseEventType, UserProfileComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../api';
|
||||
import { LocalizeText, OpenMessengerChat } from '../../../../api';
|
||||
import { SendMessageHook } from '../../../../hooks/messages';
|
||||
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
|
||||
import { FriendBarItemViewProps } from './FriendBarItemView.types';
|
||||
@ -16,6 +16,13 @@ export const FriendBarItemView: FC<FriendBarItemViewProps> = props =>
|
||||
SendMessageHook(new FollowFriendMessageComposer(friend.id));
|
||||
}, [ friend ]);
|
||||
|
||||
const openMessengerChat = useCallback(() =>
|
||||
{
|
||||
if(!friend) return;
|
||||
|
||||
OpenMessengerChat(friend.id);
|
||||
}, [ friend ]);
|
||||
|
||||
const openProfile = useCallback(() =>
|
||||
{
|
||||
SendMessageHook(new UserProfileComposer(friend.id));
|
||||
@ -59,7 +66,7 @@ export const FriendBarItemView: FC<FriendBarItemViewProps> = props =>
|
||||
<div className="text-truncate">{ friend.name }</div>
|
||||
{ isVisible &&
|
||||
<div className="d-flex justify-content-between">
|
||||
<i className="icon icon-fb-chat cursor-pointer" />
|
||||
<i onClick={ openMessengerChat } className="icon icon-fb-chat cursor-pointer" />
|
||||
{ friend.followingAllowed && <i onClick={ followFriend } className="icon icon-fb-visit cursor-pointer" /> }
|
||||
<i onClick={ openProfile } className="icon icon-fb-profile cursor-pointer" />
|
||||
</div> }
|
||||
|
@ -1,20 +1,14 @@
|
||||
import { FC, useMemo, useState } from 'react';
|
||||
import { useFriendsContext } from '../../context/FriendsContext';
|
||||
import { FriendBarItemView } from '../friend-bar-item/FriendBarItemView';
|
||||
import { FriendBarViewProps } from './FriendBarView.types';
|
||||
|
||||
export const FriendBarView: FC<FriendBarViewProps> = props =>
|
||||
{
|
||||
const { friendsState = null } = useFriendsContext();
|
||||
const { friends = null } = friendsState;
|
||||
const { onlineFriends = null } = props;
|
||||
|
||||
const [ indexOffset, setIndexOffset ] = useState(0);
|
||||
const [ maxDisplayCount, setMaxDisplayCount ] = useState(3);
|
||||
|
||||
const onlineFriends = useMemo(() =>
|
||||
{
|
||||
return friends.filter(friend => friend.online);
|
||||
}, [ friends ]);
|
||||
|
||||
const canDecreaseIndex = useMemo(() =>
|
||||
{
|
||||
if(indexOffset === 0) return false;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { MessengerFriend } from './../../common/MessengerFriend';
|
||||
export interface FriendBarViewProps
|
||||
{
|
||||
|
||||
onlineFriends: MessengerFriend[];
|
||||
}
|
||||
|
@ -1,71 +0,0 @@
|
||||
import { SetRelationshipStatusComposer } from '@nitrots/nitro-renderer';
|
||||
import { FollowFriendMessageComposer } from '@nitrots/nitro-renderer/src/nitro/communication/messages/outgoing/friendlist/FollowFriendMessageComposer';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { LocalizeText, OpenMessengerChat } from '../../../../api';
|
||||
import { SendMessageHook } from '../../../../hooks';
|
||||
import { UserProfileIconView } from '../../../shared/user-profile-icon/UserProfileIconView';
|
||||
import { MessengerFriend } from '../../common/MessengerFriend';
|
||||
import { FriendsListItemViewProps } from './FriendsListItemView.types';
|
||||
|
||||
export const FriendsListItemView: FC<FriendsListItemViewProps> = props =>
|
||||
{
|
||||
const { friend = null } = props;
|
||||
|
||||
const [ isExpanded, setIsExpanded ] = useState<boolean>(false);
|
||||
|
||||
const followFriend = useCallback(() =>
|
||||
{
|
||||
if(!friend) return;
|
||||
|
||||
SendMessageHook(new FollowFriendMessageComposer(friend.id));
|
||||
}, [ friend ]);
|
||||
|
||||
const openMessengerChat = useCallback(() =>
|
||||
{
|
||||
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 updateRelationship = useCallback((type: number) =>
|
||||
{
|
||||
if(type !== friend.relationshipStatus) SendMessageHook(new SetRelationshipStatusComposer(friend.id, type));
|
||||
|
||||
setIsExpanded(false);
|
||||
}, [ friend ]);
|
||||
|
||||
if(!friend) return null;
|
||||
|
||||
return (
|
||||
<div className="px-2 py-1 d-flex gap-1 align-items-center">
|
||||
<UserProfileIconView userId={ friend.id } />
|
||||
<div>{ friend.name }</div>
|
||||
<div className="ms-auto d-flex align-items-center gap-1">
|
||||
{ !isExpanded && <>
|
||||
{ friend.followingAllowed && <i className="icon icon-friendlist-follow cursor-pointer" onClick={ followFriend } title={ LocalizeText('friendlist.tip.follow') } /> }
|
||||
{ friend.online && <i className="icon icon-friendlist-chat cursor-pointer" onClick={ openMessengerChat } title={ LocalizeText('friendlist.tip.im') } /> }
|
||||
<i className={ 'icon cursor-pointer icon-relationship-' + getCurrentRelationshipName() } onClick={ () => setIsExpanded(true) } title={ LocalizeText('infostand.link.relationship') } />
|
||||
</> }
|
||||
{ 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) } />
|
||||
<i className="icon icon-relationship-bobba cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_BOBBA) } />
|
||||
<i className="icon icon-relationship-none cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_NONE) } />
|
||||
</> }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
import { MessengerFriend } from '../../common/MessengerFriend';
|
||||
|
||||
export interface FriendsListItemViewProps
|
||||
{
|
||||
friend: MessengerFriend;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { FollowFriendMessageComposer, SetRelationshipStatusComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { LocalizeText } from '../../../../api';
|
||||
import { LocalizeText, OpenMessengerChat } from '../../../../api';
|
||||
import { SendMessageHook } from '../../../../hooks';
|
||||
import { UserProfileIconView } from '../../../shared/user-profile-icon/UserProfileIconView';
|
||||
import { MessengerFriend } from '../../common/MessengerFriend';
|
||||
@ -19,6 +19,13 @@ export const FriendsGroupItemView: FC<FriendsGroupItemViewProps> = props =>
|
||||
SendMessageHook(new FollowFriendMessageComposer(friend.id));
|
||||
}, [ friend ]);
|
||||
|
||||
const openMessengerChat = useCallback(() =>
|
||||
{
|
||||
if(!friend) return;
|
||||
|
||||
OpenMessengerChat(friend.id);
|
||||
}, [ friend ]);
|
||||
|
||||
const getCurrentRelationshipName = useCallback(() =>
|
||||
{
|
||||
if(!friend) return 'none';
|
||||
@ -48,7 +55,7 @@ export const FriendsGroupItemView: FC<FriendsGroupItemViewProps> = props =>
|
||||
<div className="ms-auto d-flex align-items-center gap-1">
|
||||
{ !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" title={ LocalizeText('friendlist.tip.im') } /> }
|
||||
{ friend.online && <i className="icon icon-friendlist-chat cursor-pointer" onClick={ openMessengerChat } title={ LocalizeText('friendlist.tip.im') } /> }
|
||||
<i className={ 'icon cursor-pointer icon-relationship-' + getCurrentRelationshipName() } onClick={ () => setIsExpanded(true) } title={ LocalizeText('infostand.link.relationship') } />
|
||||
</> }
|
||||
{ isExpanded && <>
|
||||
|
71
src/views/friends/views/messenger/FriendsMessengerView.scss
Normal file
71
src/views/friends/views/messenger/FriendsMessengerView.scss
Normal file
@ -0,0 +1,71 @@
|
||||
.nitro-friends-messenger {
|
||||
width: 300px;
|
||||
|
||||
.friend-head {
|
||||
position: relative;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
overflow: hidden;
|
||||
|
||||
.avatar-image {
|
||||
position: absolute;
|
||||
margin-left: -27px;
|
||||
margin-top: -27px;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-title {
|
||||
margin-top: -21px;
|
||||
}
|
||||
|
||||
.chat-messages {
|
||||
height: 200px;
|
||||
min-height: 200px;
|
||||
overflow-y: auto;
|
||||
|
||||
.message-avatar {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
.avatar-image {
|
||||
position: absolute;
|
||||
margin-left: -22px;
|
||||
margin-top: -25px;
|
||||
}
|
||||
}
|
||||
|
||||
.messages-group-left {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
content: ' ';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-right: 8px solid rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
top: 10px;
|
||||
left: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
.messages-group-right {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
content: ' ';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 8px solid rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important;
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
top: 10px;
|
||||
right: -8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +1,25 @@
|
||||
import { ILinkEventTracker, NitroEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { AddEventLinkTracker, LocalizeText, RemoveLinkEventTracker } from '../../../../api';
|
||||
import { FollowFriendMessageComposer, ILinkEventTracker, NitroEvent, SendMessageComposer, UserProfileComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { AddEventLinkTracker, GetSessionDataManager, LocalizeText, RemoveLinkEventTracker } from '../../../../api';
|
||||
import { FriendsEvent } from '../../../../events/friends/FriendsEvent';
|
||||
import { useUiEvent } from '../../../../hooks';
|
||||
import { SendMessageHook, useUiEvent } from '../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
import { AvatarImageView } from '../../../shared/avatar-image/AvatarImageView';
|
||||
import { MessengerChat } from '../../common/MessengerChat';
|
||||
import { MessengerChatMessage } from '../../common/MessengerChatMessage';
|
||||
import { useFriendsContext } from '../../context/FriendsContext';
|
||||
import { FriendsActions } from '../../reducers/FriendsReducer';
|
||||
|
||||
export const FriendsMessengerView: FC<{}> = props =>
|
||||
{
|
||||
const { friendsState = null, dispatchFriendsState = null } = useFriendsContext();
|
||||
const { activeChats = [] } = friendsState;
|
||||
const { activeChats = [], friends = [] } = friendsState;
|
||||
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const [ activeChatIndex, setActiveChatIndex ] = useState(0);
|
||||
const [ selectedChatIndex, setSelectedChatIndex ] = useState(0);
|
||||
const [ message, setMessage ] = useState('');
|
||||
|
||||
const messagesBox = useRef<HTMLDivElement>();
|
||||
|
||||
const onNitroEvent = useCallback((event: NitroEvent) =>
|
||||
{
|
||||
@ -35,11 +40,11 @@ export const FriendsMessengerView: FC<{}> = props =>
|
||||
const linkReceived = useCallback((url: string) =>
|
||||
{
|
||||
const parts = url.split('/');
|
||||
console.log(parts);
|
||||
|
||||
if(parts.length < 3) return;
|
||||
|
||||
const friendId = parseInt(parts[2]);
|
||||
console.log(friendId);
|
||||
|
||||
let existingChatIndex = activeChats.findIndex(c => c.friendId === friendId);
|
||||
|
||||
if(existingChatIndex === -1)
|
||||
@ -57,10 +62,35 @@ export const FriendsMessengerView: FC<{}> = props =>
|
||||
existingChatIndex = clonedActiveChats.length - 1;
|
||||
}
|
||||
|
||||
setActiveChatIndex(existingChatIndex);
|
||||
setSelectedChatIndex(existingChatIndex);
|
||||
setIsVisible(true);
|
||||
}, [ activeChats, dispatchFriendsState ]);
|
||||
|
||||
const getFriendFigure = useCallback((id: number) =>
|
||||
{
|
||||
const friend = friends.find(f => f.id === id);
|
||||
|
||||
if(!friend) return null;
|
||||
|
||||
return friend.figure;
|
||||
}, [ friends ]);
|
||||
|
||||
const selectedChat = useMemo(() =>
|
||||
{
|
||||
return activeChats[selectedChatIndex];
|
||||
}, [ activeChats, selectedChatIndex ]);
|
||||
|
||||
const selectedChatFriend = useMemo(() =>
|
||||
{
|
||||
if(!selectedChat) return null;
|
||||
|
||||
const friend = friends.find(f => f.id === selectedChat.friendId);
|
||||
|
||||
if(!friend) return null;
|
||||
|
||||
return friend;
|
||||
}, [ friends, selectedChat ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const linkTracker: ILinkEventTracker = {
|
||||
@ -73,12 +103,99 @@ export const FriendsMessengerView: FC<{}> = props =>
|
||||
return () => RemoveLinkEventTracker(linkTracker);
|
||||
}, [ linkReceived ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!messagesBox || !messagesBox.current) return;
|
||||
|
||||
messagesBox.current.scrollTop = messagesBox.current.scrollHeight;
|
||||
|
||||
}, [ selectedChat ]);
|
||||
|
||||
const followFriend = useCallback(() =>
|
||||
{
|
||||
SendMessageHook(new FollowFriendMessageComposer(selectedChatFriend.id));
|
||||
}, [ selectedChatFriend ]);
|
||||
|
||||
const openProfile = useCallback(() =>
|
||||
{
|
||||
SendMessageHook(new UserProfileComposer(selectedChatFriend.id));
|
||||
}, [ selectedChatFriend ]);
|
||||
|
||||
const sendMessage = useCallback(() =>
|
||||
{
|
||||
if(message.length === 0) return;
|
||||
|
||||
SendMessageHook(new SendMessageComposer(selectedChat.friendId, message));
|
||||
|
||||
dispatchFriendsState({
|
||||
type: FriendsActions.ADD_CHAT_MESSAGE,
|
||||
payload: {
|
||||
chatMessage: new MessengerChatMessage(MessengerChatMessage.MESSAGE, 0, message, (new Date().getMilliseconds())),
|
||||
numberValue: selectedChat.friendId
|
||||
}
|
||||
});
|
||||
setMessage('');
|
||||
}, [ selectedChat, message, dispatchFriendsState ]);
|
||||
|
||||
const onKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) =>
|
||||
{
|
||||
if(event.key !== 'Enter') return;
|
||||
|
||||
sendMessage();
|
||||
}, [ sendMessage ]);
|
||||
|
||||
if(!isVisible) return null;
|
||||
|
||||
return (<NitroCardView className="nitro-friends-messenger" simple={ true }>
|
||||
<NitroCardHeaderView headerText={ LocalizeText('friendlist.friends') } onCloseClick={ () => {} } />
|
||||
<NitroCardContentView className="p-0">
|
||||
|
||||
<NitroCardHeaderView headerText={ LocalizeText('messenger.window.title', ['OPEN_CHAT_COUNT'], [activeChats.length.toString()]) } onCloseClick={ () => setIsVisible(false) } />
|
||||
<NitroCardContentView>
|
||||
<div className="d-flex gap-2 overflow-auto pb-1">
|
||||
{ activeChats && activeChats.map((chat, index) =>
|
||||
{
|
||||
return <div key={ index } className="friend-head bg-muted rounded flex-shrink-0 cursor-pointer" onClick={ () => setSelectedChatIndex(index) }>
|
||||
<AvatarImageView figure={ getFriendFigure(chat.friendId) } headOnly={true} direction={3} />
|
||||
</div>;
|
||||
}) }
|
||||
</div>
|
||||
<hr className="bg-dark mt-3 mb-2" />
|
||||
{ selectedChat && selectedChatFriend && <>
|
||||
<div className="text-black chat-title bg-light pe-2 position-absolute">{ LocalizeText('messenger.window.separator', ['FRIEND_NAME'], [ selectedChatFriend.name ]) }</div>
|
||||
<div className="d-flex gap-2 mb-2">
|
||||
<div className="btn-group">
|
||||
<button className="btn btn-sm btn-primary" onClick={ followFriend }>
|
||||
<i className="icon icon-friendlist-follow" />
|
||||
</button>
|
||||
<button className="btn btn-sm btn-primary" onClick={ openProfile }>
|
||||
<i className="icon icon-user-profile" />
|
||||
</button>
|
||||
</div>
|
||||
<button className="btn btn-sm btn-danger">{ LocalizeText('messenger.window.button.report') }</button>
|
||||
<button className="btn btn-sm btn-primary ms-auto"><i className="fas fa-times" /></button>
|
||||
</div>
|
||||
<div ref={ messagesBox } className="bg-muted p-2 rounded chat-messages mb-2 d-flex flex-column">
|
||||
{ selectedChat.messageGroups.map((group, groupIndex) =>
|
||||
{
|
||||
return <div key={ groupIndex } className={ 'd-flex gap-2 w-100 justify-content-' + (group.userId === 0 ? 'end' : 'start') }>
|
||||
{ group.userId !== 0 && <div className="message-avatar flex-shrink-0">
|
||||
<AvatarImageView figure={ selectedChatFriend.figure } direction={ 2 } />
|
||||
</div> }
|
||||
<div className={ 'bg-light text-black border-radius mb-2 rounded py-1 px-2 messages-group-' + (group.userId === 0 ? 'right' : 'left') }>
|
||||
{ group.messages.map((message, messageIndex) =>
|
||||
{
|
||||
return <div key={ messageIndex } className="text-break">{ message.message }</div>
|
||||
}) }
|
||||
</div>
|
||||
{ group.userId === 0 && <div className="message-avatar flex-shrink-0">
|
||||
<AvatarImageView figure={ GetSessionDataManager().figure } direction={ 4 } />
|
||||
</div> }
|
||||
</div>;
|
||||
}) }
|
||||
</div>
|
||||
<div className="d-flex gap-2">
|
||||
<input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('messenger.window.input.default', ['FRIEND_NAME'], [ selectedChatFriend.name ]) } value={ message } onChange={ (e) => setMessage(e.target.value) } onKeyDown={ onKeyDown } />
|
||||
<button className="btn btn-sm btn-success" onClick={ sendMessage }>{ LocalizeText('widgets.chatinput.say') }</button>
|
||||
</div>
|
||||
</> }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user