mirror of
https://github.com/billsonnn/nitro-react.git
synced 2025-01-18 21:36:27 +01:00
Merge branch '@experimental/freeflowchat' into 'dev'
@experimental/freeflowchat See merge request nitro/nitro-react!38
This commit is contained in:
commit
d3fd557ed3
@ -1,10 +0,0 @@
|
||||
export const DoElementsOverlap = (a: HTMLElement, b: HTMLElement) =>
|
||||
{
|
||||
const rectA = a.getBoundingClientRect();
|
||||
const rectB = b.getBoundingClientRect();
|
||||
|
||||
const ox = Math.abs(rectA.x - rectB.x) < (rectA.x < rectB.x ? rectB.width : rectA.width);
|
||||
const oy = Math.abs(rectA.y - rectB.y) < (rectA.y < rectB.y ? rectB.height : rectA.height);
|
||||
|
||||
return (ox && oy);
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
export * from './CloneObject';
|
||||
export * from './ColorUtils';
|
||||
export * from './DoElementsOverlap';
|
||||
export * from './LocalizeBadgeDescription';
|
||||
export * from './LocalizeBageName';
|
||||
export * from './LocalizeFormattedNumber';
|
||||
|
@ -28,9 +28,9 @@ export const NavigatorRoomSettingsVipChatTabView: FC<NavigatorRoomSettingsTabVie
|
||||
<option value="1">{LocalizeText('navigator.roomsettings.chat.mode.line.by.line')}</option>
|
||||
</select>
|
||||
<select className="form-select form-select-sm" value={roomSettingsData.chatBubbleWeight} onChange={event => handleChange('chat_weight', event.target.value)}>
|
||||
<option value="0">{LocalizeText('navigator.roomsettings.chat.bubbles.width.normal')}</option>
|
||||
<option value="1">{LocalizeText('navigator.roomsettings.chat.bubbles.width.thin')}</option>
|
||||
<option value="2">{LocalizeText('navigator.roomsettings.chat.bubbles.width.wide')}</option>
|
||||
<option value="1">{LocalizeText('navigator.roomsettings.chat.bubbles.width.normal')}</option>
|
||||
<option value="2">{LocalizeText('navigator.roomsettings.chat.bubbles.width.thin')}</option>
|
||||
<option value="0">{LocalizeText('navigator.roomsettings.chat.bubbles.width.wide')}</option>
|
||||
</select>
|
||||
<select className="form-select form-select-sm" value={roomSettingsData.chatBubbleSpeed} onChange={event => handleChange('bubble_speed', event.target.value)}>
|
||||
<option value="0">{LocalizeText('navigator.roomsettings.chat.speed.fast')}</option>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { FC, MouseEvent, useEffect, useRef, useState } from 'react';
|
||||
import { RoomChatSettings } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { ChatBubbleMessage } from './common/ChatBubbleMessage';
|
||||
|
||||
interface ChatWidgetMessageViewProps
|
||||
@ -6,15 +7,27 @@ interface ChatWidgetMessageViewProps
|
||||
chat: ChatBubbleMessage;
|
||||
makeRoom: (chat: ChatBubbleMessage) => void;
|
||||
onChatClicked: (chat: ChatBubbleMessage) => void;
|
||||
bubbleWidth?: number;
|
||||
}
|
||||
|
||||
export const ChatWidgetMessageView: FC<ChatWidgetMessageViewProps> = props =>
|
||||
{
|
||||
const { chat = null, makeRoom = null, onChatClicked = null } = props;
|
||||
const { chat = null, makeRoom = null, onChatClicked = null, bubbleWidth = RoomChatSettings.CHAT_BUBBLE_WIDTH_NORMAL } = props;
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const elementRef = useRef<HTMLDivElement>();
|
||||
|
||||
const onMouseDown = (event: MouseEvent<HTMLDivElement>) => onChatClicked(chat);
|
||||
const getBubbleWidth = useMemo(() =>
|
||||
{
|
||||
switch(bubbleWidth)
|
||||
{
|
||||
case RoomChatSettings.CHAT_BUBBLE_WIDTH_NORMAL:
|
||||
return 350;
|
||||
case RoomChatSettings.CHAT_BUBBLE_WIDTH_THIN:
|
||||
return 240;
|
||||
case RoomChatSettings.CHAT_BUBBLE_WIDTH_WIDE:
|
||||
return 2000;
|
||||
}
|
||||
}, [ bubbleWidth ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -57,17 +70,19 @@ export const ChatWidgetMessageView: FC<ChatWidgetMessageViewProps> = props =>
|
||||
useEffect(() => setIsVisible(chat.visible), [ chat.visible ]);
|
||||
|
||||
return (
|
||||
<div ref={ elementRef } className="bubble-container" style={ { visibility: (isVisible ? 'visible' : 'hidden') } } onClick={ onMouseDown }>
|
||||
{ (chat.styleId === 0) && <div className="user-container-bg" style={ { backgroundColor: chat.color } } /> }
|
||||
<div className={ 'chat-bubble bubble-' + chat.styleId + ' type-' + chat.type }>
|
||||
<div ref={ elementRef } className={ `bubble-container ${ isVisible ? 'visible' : 'invisible' }` } onClick={ event => onChatClicked(chat) }>
|
||||
{ (chat.styleId === 0) &&
|
||||
<div className="user-container-bg" style={ { backgroundColor: chat.color } } /> }
|
||||
<div className={ `chat-bubble bubble-${ chat.styleId } type-${ chat.type }` } style={ { maxWidth: getBubbleWidth } }>
|
||||
<div className="user-container">
|
||||
{ (chat.imageUrl && (chat.imageUrl !== '')) && <div className="user-image" style={ { backgroundImage: 'url(' + chat.imageUrl + ')' } } /> }
|
||||
{ chat.imageUrl && (chat.imageUrl.length > 0) &&
|
||||
<div className="user-image" style={ { backgroundImage: `url(${ chat.imageUrl })` } } /> }
|
||||
</div>
|
||||
<div className="chat-content">
|
||||
<b className="username mr-1" dangerouslySetInnerHTML={ { __html: `${ chat.username }: ` } } />
|
||||
<span className="message" dangerouslySetInnerHTML={{ __html: `${ chat.formattedText }` }} />
|
||||
</div>
|
||||
<div className="pointer"></div>
|
||||
<div className="pointer" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -5,7 +5,7 @@
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
height: 270px;
|
||||
min-height: 1px;
|
||||
z-index: $chat-zindex;
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { NitroPoint, RoomDragEvent } from '@nitrots/nitro-renderer';
|
||||
import { GetGuestRoomResultEvent, NitroPoint, RoomChatSettings, RoomChatSettingsEvent, RoomDragEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { RoomChatFormatter, RoomWidgetChatSelectAvatarMessage, RoomWidgetRoomObjectMessage, RoomWidgetUpdateChatEvent } from '../../../../api';
|
||||
import { UseEventDispatcherHook, UseRoomEngineEvent } from '../../../../hooks';
|
||||
import { GetConfiguration, RoomChatFormatter, RoomWidgetChatSelectAvatarMessage, RoomWidgetRoomObjectMessage, RoomWidgetUpdateChatEvent } from '../../../../api';
|
||||
import { UseEventDispatcherHook, UseMessageEventHook, UseRoomEngineEvent } from '../../../../hooks';
|
||||
import { useRoomContext } from '../../RoomContext';
|
||||
import { ChatWidgetMessageView } from './ChatWidgetMessageView';
|
||||
import { ChatBubbleMessage } from './common/ChatBubbleMessage';
|
||||
import { DoChatsOverlap } from './common/DoChatsOverlap';
|
||||
|
||||
export const ChatWidgetView: FC<{}> = props =>
|
||||
{
|
||||
const [chatSettings, setChatSettings] = useState<RoomChatSettings>(null);
|
||||
const [ chatMessages, setChatMessages ] = useState<ChatBubbleMessage[]>([]);
|
||||
const { roomSession = null, eventDispatcher = null, widgetHandler = null } = useRoomContext();
|
||||
const elementRef = useRef<HTMLDivElement>();
|
||||
@ -21,40 +23,66 @@ export const ChatWidgetView: FC<{}> = props =>
|
||||
if(newMessages.length !== chatMessages.length) setChatMessages(newMessages);
|
||||
}, [ chatMessages ]);
|
||||
|
||||
const moveChatUp = useCallback((chat: ChatBubbleMessage, amount: number) =>
|
||||
{
|
||||
chat.top -= amount;
|
||||
}, []);
|
||||
|
||||
const moveAllChatsUp = useCallback((amount: number) =>
|
||||
{
|
||||
chatMessages.forEach(chat => moveChatUp(chat, amount));
|
||||
chatMessages.forEach(chat => (chat.top -= amount));
|
||||
|
||||
removeHiddenChats();
|
||||
}, [ chatMessages, moveChatUp, removeHiddenChats ]);
|
||||
}, [ chatMessages, removeHiddenChats ]);
|
||||
|
||||
const checkOverlappingChats = useCallback((chat: ChatBubbleMessage, moved: number, tempChats: ChatBubbleMessage[]) =>
|
||||
{
|
||||
const totalChats = chatMessages.length;
|
||||
|
||||
if(!totalChats) return;
|
||||
|
||||
for(let i = (totalChats - 1); i >= 0; i--)
|
||||
{
|
||||
const collides = chatMessages[i];
|
||||
|
||||
if(!collides || (chat === collides) || (tempChats.indexOf(collides) >= 0) || ((collides.top - moved) >= (chat.top + chat.height))) continue;
|
||||
|
||||
if(DoChatsOverlap(chat, collides, -moved))
|
||||
{
|
||||
const amount = Math.abs((collides.top + collides.height) - chat.top);
|
||||
|
||||
tempChats.push(collides);
|
||||
|
||||
collides.top -= amount;
|
||||
|
||||
checkOverlappingChats(collides, amount, tempChats);
|
||||
}
|
||||
}
|
||||
}, [ chatMessages ]);
|
||||
|
||||
const makeRoom = useCallback((chat: ChatBubbleMessage) =>
|
||||
{
|
||||
const lowestPoint = ((chat.top + chat.height) - 1);
|
||||
const requiredSpace = (chat.height + 1);
|
||||
const spaceAvailable = (elementRef.current.offsetHeight - lowestPoint);
|
||||
|
||||
if(spaceAvailable < requiredSpace)
|
||||
if(chatSettings.mode === RoomChatSettings.CHAT_MODE_FREE_FLOW)
|
||||
{
|
||||
const amount = (requiredSpace - spaceAvailable);
|
||||
|
||||
chatMessages.forEach(existingChat =>
|
||||
{
|
||||
if(existingChat === chat) return;
|
||||
|
||||
moveChatUp(existingChat, amount);
|
||||
});
|
||||
checkOverlappingChats(chat, 0, [ chat ]);
|
||||
|
||||
removeHiddenChats();
|
||||
}
|
||||
}, [ chatMessages, moveChatUp, removeHiddenChats ]);
|
||||
else
|
||||
{
|
||||
const lowestPoint = (chat.top + chat.height);
|
||||
const requiredSpace = chat.height;
|
||||
const spaceAvailable = (elementRef.current.offsetHeight - lowestPoint);
|
||||
const amount = (requiredSpace - spaceAvailable);
|
||||
|
||||
const addChat = useCallback((chat: ChatBubbleMessage) => setChatMessages(prevValue => [ ...prevValue, chat ]), []);
|
||||
if(spaceAvailable < requiredSpace)
|
||||
{
|
||||
chatMessages.forEach(existingChat =>
|
||||
{
|
||||
if(existingChat === chat) return;
|
||||
|
||||
existingChat.top -= amount;
|
||||
});
|
||||
|
||||
removeHiddenChats();
|
||||
}
|
||||
}
|
||||
}, [ chatSettings, chatMessages, removeHiddenChats, checkOverlappingChats ]);
|
||||
|
||||
const onRoomWidgetUpdateChatEvent = useCallback((event: RoomWidgetUpdateChatEvent) =>
|
||||
{
|
||||
@ -71,23 +99,16 @@ export const ChatWidgetView: FC<{}> = props =>
|
||||
event.userImage,
|
||||
(event.userColor && (('#' + (event.userColor.toString(16).padStart(6, '0'))) || null)));
|
||||
|
||||
addChat(chatMessage);
|
||||
}, [ addChat ]);
|
||||
setChatMessages(prevValue => [ ...prevValue, chatMessage ]);
|
||||
}, []);
|
||||
|
||||
UseEventDispatcherHook(RoomWidgetUpdateChatEvent.CHAT_EVENT, eventDispatcher, onRoomWidgetUpdateChatEvent);
|
||||
|
||||
const onRoomDragEvent = useCallback((event: RoomDragEvent) =>
|
||||
{
|
||||
if(!chatMessages.length) return;
|
||||
if(!chatMessages.length || (event.roomId !== roomSession.roomId)) return;
|
||||
|
||||
if(event.roomId !== roomSession.roomId) return;
|
||||
|
||||
chatMessages.forEach(chat =>
|
||||
{
|
||||
if(!chat.elementRef) return;
|
||||
|
||||
chat.left += event.offsetX;
|
||||
});
|
||||
chatMessages.forEach(chat => (chat.elementRef && (chat.left += event.offsetX)));
|
||||
}, [ roomSession, chatMessages ]);
|
||||
|
||||
UseRoomEngineEvent(RoomDragEvent.ROOM_DRAG, onRoomDragEvent);
|
||||
@ -98,19 +119,61 @@ export const ChatWidgetView: FC<{}> = props =>
|
||||
widgetHandler.processWidgetMessage(new RoomWidgetChatSelectAvatarMessage(RoomWidgetChatSelectAvatarMessage.MESSAGE_SELECT_AVATAR, chat.senderId, chat.username, chat.roomId));
|
||||
}, [ widgetHandler ]);
|
||||
|
||||
const getScrollSpeed = useCallback(() =>
|
||||
{
|
||||
if(!chatSettings) return 6000;
|
||||
|
||||
switch(chatSettings.speed)
|
||||
{
|
||||
case RoomChatSettings.CHAT_SCROLL_SPEED_FAST:
|
||||
return 3000;
|
||||
case RoomChatSettings.CHAT_SCROLL_SPEED_NORMAL:
|
||||
return 6000;
|
||||
case RoomChatSettings.CHAT_SCROLL_SPEED_SLOW:
|
||||
return 12000;
|
||||
}
|
||||
}, [ chatSettings ])
|
||||
|
||||
const onGetGuestRoomResultEvent = useCallback((event: GetGuestRoomResultEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
if(!parser.roomEnter) return;
|
||||
|
||||
setChatSettings(parser.chat);
|
||||
}, []);
|
||||
|
||||
UseMessageEventHook(GetGuestRoomResultEvent, onGetGuestRoomResultEvent);
|
||||
|
||||
const onRoomChatSettingsEvent = useCallback((event: RoomChatSettingsEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setChatSettings(parser.chat);
|
||||
}, []);
|
||||
|
||||
UseMessageEventHook(RoomChatSettingsEvent, onRoomChatSettingsEvent);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const interval = setInterval(() => moveAllChatsUp(15), 4500);
|
||||
const interval = setInterval(() => moveAllChatsUp(15), getScrollSpeed());
|
||||
|
||||
return () =>
|
||||
{
|
||||
if(interval) clearInterval(interval);
|
||||
}
|
||||
}, [ chatMessages, moveAllChatsUp ]);
|
||||
}, [ chatMessages, moveAllChatsUp, getScrollSpeed ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!elementRef || !elementRef.current) return;
|
||||
|
||||
elementRef.current.style.height = ((document.body.offsetHeight * GetConfiguration<number>('chat.viewer.height.percentage')) + 'px');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div ref={ elementRef } className="nitro-chat-widget">
|
||||
{ chatMessages.map(chat => <ChatWidgetMessageView key={ chat.id } chat={ chat } makeRoom={ makeRoom } onChatClicked={ onChatClicked } />) }
|
||||
{chatMessages.map(chat => <ChatWidgetMessageView key={chat.id} chat={chat} makeRoom={makeRoom} onChatClicked={onChatClicked} bubbleWidth={ chatSettings.weight }/>)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
import { ChatBubbleMessage } from './ChatBubbleMessage';
|
||||
|
||||
export const DoChatsOverlap = (a: ChatBubbleMessage, b: ChatBubbleMessage, additionalBTop: number) =>
|
||||
{
|
||||
return !(((a.left + a.width) < b.left) || (a.left > (b.left + b.width)) || ((a.top + a.height) < (b.top + additionalBTop)) || (a.top > ((b.top + additionalBTop) + b.height)));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user