Chat History update

This commit is contained in:
MyNameIsBatman 2022-09-17 00:11:46 -03:00
parent e9d9fd4253
commit 0d0f1bf62c
4 changed files with 103 additions and 44 deletions

View File

@ -6,6 +6,10 @@ export interface IChatEntry
look?: string; look?: string;
message?: string; message?: string;
entityType?: number; entityType?: number;
style?: number;
chatType?: number;
imageUrl?: string;
color?: string;
roomId: number; roomId: number;
timestamp: string; timestamp: string;
type: number; type: number;

View File

@ -1,33 +1,90 @@
import { ILinkEventTracker } from '@nitrots/nitro-renderer'; import { AvatarFigurePartType, AvatarScaleType, AvatarSetType, ILinkEventTracker } from '@nitrots/nitro-renderer';
import { FC, useEffect, useMemo, useRef, useState } from 'react'; import { FC, useEffect, useMemo, useRef, useState } from 'react';
import { AutoSizer, CellMeasurer, CellMeasurerCache, List, ListRowProps, ListRowRenderer, Size } from 'react-virtualized'; import { AutoSizer, CellMeasurer, CellMeasurerCache, List, ListRowProps, ListRowRenderer, Size } from 'react-virtualized';
import { AddEventLinkTracker, ChatEntryType, LocalizeText, RemoveLinkEventTracker } from '../../api'; import { AddEventLinkTracker, ChatEntryType, GetAvatarRenderManager, LocalizeText, RemoveLinkEventTracker } from '../../api';
import { Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common'; import { Flex, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common';
import { useChatHistory } from '../../hooks'; import { useChatHistory } from '../../hooks';
const avatarColorCache: Map<string, number> = new Map();
const avatarImageCache: Map<string, string> = new Map();
export const ChatHistoryView: FC<{}> = props => export const ChatHistoryView: FC<{}> = props =>
{ {
const [ isVisible, setIsVisible ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const { chatHistory = [] } = useChatHistory(); const { chatHistory = [] } = useChatHistory();
const elementRef = useRef<List>(null); const elementRef = useRef<List>(null);
const [ searchText, setSearchText ] = useState<string>('z');
const setFigureImage = (figure: string) =>
{
const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, null, {
resetFigure: figure =>
{
setFigureImage(figure);
},
dispose: () =>
{},
disposed: false
});
if(!avatarImage) return;
const image = avatarImage.getCroppedImage(AvatarSetType.HEAD);
const color = avatarImage.getPartColor(AvatarFigurePartType.CHEST);
avatarColorCache.set(figure, ((color && color.rgb) || 16777215));
avatarImage.dispose();
avatarImageCache.set(figure, image.src);
return image.src;
}
const getUserImage = (figure: string) =>
{
let existing = avatarImageCache.get(figure);
if(!existing) existing = setFigureImage(figure);
return existing;
}
const cache = useMemo(() => new CellMeasurerCache({ defaultHeight: 25, fixedWidth: true }), []); const cache = useMemo(() => new CellMeasurerCache({ defaultHeight: 25, fixedWidth: true }), []);
const filteredChatHistory = useMemo(() =>
{
if (searchText.length === 0) return chatHistory;
return chatHistory.filter((i) => i.message && i.message.includes(searchText));
}, [ chatHistory, searchText ]);
const RowRenderer: ListRowRenderer = (props: ListRowProps) => const RowRenderer: ListRowRenderer = (props: ListRowProps) =>
{ {
const item = chatHistory[props.index]; const item = filteredChatHistory[props.index];
const isDark = (props.index % 2 === 0); if (!item) return null;
return ( return (
<CellMeasurer cache={ cache } columnIndex={ 0 } key={ props.key } parent={ props.parent } rowIndex={ props.index }> <CellMeasurer cache={ cache } columnIndex={ 0 } key={ props.key } parent={ props.parent } rowIndex={ props.index }>
<Flex key={ props.key } style={ props.style } className="p-1" gap={ 1 }> <Flex key={ props.key } style={ props.style } className="p-1" gap={ 2 }>
<Text variant="muted">{ item.timestamp }</Text> <Text variant="muted">{ item.timestamp }</Text>
{ (item.type === ChatEntryType.TYPE_CHAT) && { (item.type === ChatEntryType.TYPE_CHAT) &&
<> <div className="bubble-container" style={ { position: 'relative' } }>
<Text pointer noWrap dangerouslySetInnerHTML={ { __html: (item.name + ':') } } /> { (item.style === 0) &&
<Text textBreak wrap grow>{ item.message }</Text> <div className="user-container-bg" style={ { backgroundColor: item.color } } /> }
</> } <div className={ `chat-bubble bubble-${ item.style } type-${ item.chatType }` } style={ { maxWidth: '100%' } }>
<div className="user-container">
{ item.imageUrl && (item.imageUrl.length > 0) &&
<div className="user-image" style={ { backgroundImage: `url(${ item.imageUrl })` } } /> }
</div>
<div className="chat-content">
<b className="username mr-1" dangerouslySetInnerHTML={ { __html: `${ item.name }: ` } } />
<span className="message" dangerouslySetInnerHTML={ { __html: `${ item.message }` } } />
</div>
</div>
</div> }
{ (item.type === ChatEntryType.TYPE_ROOM_INFO) && { (item.type === ChatEntryType.TYPE_ROOM_INFO) &&
<> <>
<i className="icon icon-small-room" /> <i className="icon icon-small-room" />
@ -81,22 +138,27 @@ export const ChatHistoryView: FC<{}> = props =>
<NitroCardView uniqueKey="chat-history" className="nitro-chat-history" theme="primary-slim"> <NitroCardView uniqueKey="chat-history" className="nitro-chat-history" theme="primary-slim">
<NitroCardHeaderView headerText={ LocalizeText('room.chathistory.button.text') } onCloseClick={ event => setIsVisible(false) }/> <NitroCardHeaderView headerText={ LocalizeText('room.chathistory.button.text') } onCloseClick={ event => setIsVisible(false) }/>
<NitroCardContentView> <NitroCardContentView>
<AutoSizer defaultWidth={ 300 } defaultHeight={ 200 } onResize={ onResize }> <Flex column fullHeight gap={ 2 }>
{ ({ height, width }) => <input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } value={ searchText } onChange={ event => setSearchText(event.target.value) } />
{ <div className="h-100">
return ( <AutoSizer defaultWidth={ 300 } defaultHeight={ 170 } onResize={ onResize }>
<List { ({ height, width }) =>
ref={ elementRef } {
width={ width } return (
height={ height } <List
rowCount={ chatHistory.length } ref={ elementRef }
rowHeight={ cache.rowHeight } width={ width }
className={ 'chat-history-list' } height={ height }
rowRenderer={ RowRenderer } rowCount={ chatHistory.length }
deferredMeasurementCache={ cache } /> rowHeight={ cache.rowHeight }
) className={ 'chat-history-list' }
} } rowRenderer={ RowRenderer }
</AutoSizer> deferredMeasurementCache={ cache } />
)
} }
</AutoSizer>
</div>
</Flex>
</NitroCardContentView> </NitroCardContentView>
</NitroCardView> </NitroCardView>
); );

View File

@ -1,7 +1,7 @@
import { GetGuestRoomResultEvent, NewConsoleMessageEvent, RoomInviteEvent, RoomSessionChatEvent, RoomSessionEvent } from '@nitrots/nitro-renderer'; import { GetGuestRoomResultEvent, NewConsoleMessageEvent, RoomInviteEvent, RoomSessionEvent } from '@nitrots/nitro-renderer';
import { useState } from 'react'; import { useState } from 'react';
import { useBetween } from 'use-between'; import { useBetween } from 'use-between';
import { ChatEntryType, ChatHistoryCurrentDate, GetRoomSession, IChatEntry, IRoomHistoryEntry, MessengerHistoryCurrentDate } from '../../api'; import { ChatEntryType, ChatHistoryCurrentDate, IChatEntry, IRoomHistoryEntry, MessengerHistoryCurrentDate } from '../../api';
import { useMessageEvent, useRoomSessionManagerEvent } from '../events'; import { useMessageEvent, useRoomSessionManagerEvent } from '../events';
const CHAT_HISTORY_MAX = 1000; const CHAT_HISTORY_MAX = 1000;
@ -64,19 +64,6 @@ const useChatHistoryState = () =>
}); });
} }
useRoomSessionManagerEvent<RoomSessionChatEvent>(RoomSessionChatEvent.CHAT_EVENT, event =>
{
const roomSession = GetRoomSession();
if(!roomSession) return;
const userData = roomSession.userDataManager.getUserDataByIndex(event.objectId);
if(!userData) return;
addChatEntry({ id: -1, entityId: userData.webID, name: userData.name, look: userData.figure, entityType: userData.type, message: event.message, timestamp: ChatHistoryCurrentDate(), type: ChatEntryType.TYPE_CHAT, roomId: roomSession.roomId });
});
useRoomSessionManagerEvent<RoomSessionEvent>(RoomSessionEvent.STARTED, event => setNeedsRoomInsert(true)); useRoomSessionManagerEvent<RoomSessionEvent>(RoomSessionEvent.STARTED, event => setNeedsRoomInsert(true));
useMessageEvent<GetGuestRoomResultEvent>(GetGuestRoomResultEvent, event => useMessageEvent<GetGuestRoomResultEvent>(GetGuestRoomResultEvent, event =>
@ -111,7 +98,7 @@ const useChatHistoryState = () =>
addMessengerEntry({ id: -1, entityId: parser.senderId, name: '', message: parser.messageText, roomId: -1, timestamp: MessengerHistoryCurrentDate(), type: ChatEntryType.TYPE_IM }); addMessengerEntry({ id: -1, entityId: parser.senderId, name: '', message: parser.messageText, roomId: -1, timestamp: MessengerHistoryCurrentDate(), type: ChatEntryType.TYPE_IM });
}); });
return { chatHistory, roomHistory, messengerHistory }; return { addChatEntry, chatHistory, roomHistory, messengerHistory };
} }
export const useChatHistory = () => useBetween(useChatHistoryState); export const useChatHistory = () => useBetween(useChatHistoryState);

View File

@ -1,8 +1,9 @@
import { AvatarFigurePartType, AvatarScaleType, AvatarSetType, GetGuestRoomResultEvent, NitroPoint, PetFigureData, RoomChatSettings, RoomChatSettingsEvent, RoomDragEvent, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomSessionChatEvent, RoomUserData, SystemChatStyleEnum, TextureUtils, Vector3d } from '@nitrots/nitro-renderer'; import { AvatarFigurePartType, AvatarScaleType, AvatarSetType, GetGuestRoomResultEvent, NitroPoint, PetFigureData, RoomChatSettings, RoomChatSettingsEvent, RoomDragEvent, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomSessionChatEvent, RoomUserData, SystemChatStyleEnum, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
import { useEffect, useMemo, useRef, useState } from 'react'; import { useEffect, useMemo, useRef, useState } from 'react';
import { ChatBubbleMessage, GetAvatarRenderManager, GetConfigurationManager, GetRoomEngine, GetRoomObjectScreenLocation, IRoomChatSettings, LocalizeText, PlaySound, RoomChatFormatter } from '../../../api'; import { ChatBubbleMessage, ChatEntryType, ChatHistoryCurrentDate, GetAvatarRenderManager, GetConfigurationManager, GetRoomEngine, GetRoomObjectScreenLocation, IRoomChatSettings, LocalizeText, PlaySound, RoomChatFormatter } from '../../../api';
import { useMessageEvent, useRoomEngineEvent, useRoomSessionManagerEvent } from '../../events'; import { useMessageEvent, useRoomEngineEvent, useRoomSessionManagerEvent } from '../../events';
import { useRoom } from '../useRoom'; import { useRoom } from '../useRoom';
import { useChatHistory } from './../../chat-history/useChatHistory';
const avatarColorCache: Map<string, number> = new Map(); const avatarColorCache: Map<string, number> = new Map();
const avatarImageCache: Map<string, string> = new Map(); const avatarImageCache: Map<string, string> = new Map();
@ -19,6 +20,7 @@ const useChatWidgetState = () =>
protection: RoomChatSettings.FLOOD_FILTER_NORMAL protection: RoomChatSettings.FLOOD_FILTER_NORMAL
}); });
const { roomSession = null } = useRoom(); const { roomSession = null } = useRoom();
const { addChatEntry } = useChatHistory();
const isDisposed = useRef(false); const isDisposed = useRef(false);
const getScrollSpeed = useMemo(() => const getScrollSpeed = useMemo(() =>
@ -229,20 +231,24 @@ const useChatWidgetState = () =>
} }
} }
const formattedText = RoomChatFormatter(text);
const color = (avatarColor && (('#' + (avatarColor.toString(16).padStart(6, '0'))) || null));
const chatMessage = new ChatBubbleMessage( const chatMessage = new ChatBubbleMessage(
userData.roomIndex, userData.roomIndex,
RoomObjectCategory.UNIT, RoomObjectCategory.UNIT,
roomSession.roomId, roomSession.roomId,
text, text,
RoomChatFormatter(text), formattedText,
username, username,
new NitroPoint(bubbleLocation.x, bubbleLocation.y), new NitroPoint(bubbleLocation.x, bubbleLocation.y),
chatType, chatType,
styleId, styleId,
imageUrl, imageUrl,
(avatarColor && (('#' + (avatarColor.toString(16).padStart(6, '0'))) || null))); color);
setChatMessages(prevValue => [ ...prevValue, chatMessage ]); setChatMessages(prevValue => [ ...prevValue, chatMessage ]);
addChatEntry({ id: -1, entityId: userData.roomIndex, name: username, imageUrl, style: styleId, chatType: chatType, entityType: userData.type, message: formattedText, timestamp: ChatHistoryCurrentDate(), type: ChatEntryType.TYPE_CHAT, roomId: roomSession.roomId, color });
}); });
useRoomEngineEvent<RoomDragEvent>(RoomDragEvent.ROOM_DRAG, event => useRoomEngineEvent<RoomDragEvent>(RoomDragEvent.ROOM_DRAG, event =>