mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-26 23:50:52 +01:00
start chat history
This commit is contained in:
parent
700a890b1d
commit
6be3567a0f
9
src/events/chat-history/ChatHistoryEvent.ts
Normal file
9
src/events/chat-history/ChatHistoryEvent.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { NitroEvent } from '@nitrots/nitro-renderer';
|
||||||
|
|
||||||
|
export class ChatHistoryEvent extends NitroEvent
|
||||||
|
{
|
||||||
|
public static SHOW_CHAT_HISTORY: string = 'CHE_SHOW_CHAT_HISTORY';
|
||||||
|
public static HIDE_CHAT_HISTORY: string = 'CHE_HIDE_CHAT_HISTORY';
|
||||||
|
public static TOGGLE_CHAT_HISTORY: string = 'CHE_TOGGLE_CHAT_HISTORY';
|
||||||
|
public static CHAT_HISTORY_CHANGED: string = 'CHE_CHAT_HISTORY_CHANGED';
|
||||||
|
}
|
@ -20,3 +20,4 @@
|
|||||||
@import './achievements/AchievementsView';
|
@import './achievements/AchievementsView';
|
||||||
@import './user-settings/UserSettingsView';
|
@import './user-settings/UserSettingsView';
|
||||||
@import './user-profile/UserProfileVew';
|
@import './user-profile/UserProfileVew';
|
||||||
|
@import './chat-history/ChatHistoryView';
|
||||||
|
98
src/views/chat-history/ChatHistoryMessageHandler.tsx
Normal file
98
src/views/chat-history/ChatHistoryMessageHandler.tsx
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { RoomInfoEvent, RoomSessionChatEvent, RoomSessionEvent } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useCallback, useState } from 'react';
|
||||||
|
import { GetRoomSession } from '../../api';
|
||||||
|
import { ChatHistoryEvent } from '../../events/chat-history/ChatHistoryEvent';
|
||||||
|
import { CreateMessageHook, dispatchUiEvent, useRoomSessionManagerEvent } from '../../hooks';
|
||||||
|
import { useChatHistoryContext } from './context/ChatHistoryContext';
|
||||||
|
import { ChatEntryType, ChatHistoryAction, CHAT_HISTORY_MAX, IChatEntry } from './reducers/ChatHistoryReducer';
|
||||||
|
import { currentDate } from './utils/Utilities';
|
||||||
|
|
||||||
|
export const ChatHistoryMessageHandler: FC<{}> = props =>
|
||||||
|
{
|
||||||
|
const { chatHistoryState = null, dispatchChatHistoryState = null } = useChatHistoryContext();
|
||||||
|
const { chats = null, roomHistory = null } = chatHistoryState;
|
||||||
|
|
||||||
|
const [needsRoomInsert, setNeedsRoomInsert ] = useState(false);
|
||||||
|
|
||||||
|
const addChatEntry = useCallback((entry: IChatEntry) =>
|
||||||
|
{
|
||||||
|
const newChats = [...chats];
|
||||||
|
|
||||||
|
newChats.push(entry);
|
||||||
|
|
||||||
|
//check for overflow
|
||||||
|
if(newChats.length > CHAT_HISTORY_MAX)
|
||||||
|
{
|
||||||
|
newChats.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchChatHistoryState({
|
||||||
|
type: ChatHistoryAction.SET_CHATS,
|
||||||
|
payload: {
|
||||||
|
chats: newChats
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatchUiEvent(new ChatHistoryEvent(ChatHistoryEvent.CHAT_HISTORY_CHANGED));
|
||||||
|
|
||||||
|
}, [chats, dispatchChatHistoryState]);
|
||||||
|
|
||||||
|
const onChatEvent = useCallback((event: RoomSessionChatEvent) =>
|
||||||
|
{
|
||||||
|
const roomSession = GetRoomSession();
|
||||||
|
|
||||||
|
if(!roomSession) return;
|
||||||
|
|
||||||
|
const userData = roomSession.userDataManager.getUserDataByIndex(event.objectId);
|
||||||
|
|
||||||
|
if(!userData) return;
|
||||||
|
|
||||||
|
const timeString = currentDate();
|
||||||
|
|
||||||
|
const entry: IChatEntry = { id: userData.webID, name: userData.name, look: userData.figure, message: event.message, timestamp: timeString, type: ChatEntryType.TYPE_CHAT };
|
||||||
|
|
||||||
|
addChatEntry(entry);
|
||||||
|
}, [addChatEntry]);
|
||||||
|
|
||||||
|
useRoomSessionManagerEvent(RoomSessionChatEvent.CHAT_EVENT, onChatEvent);
|
||||||
|
|
||||||
|
const onRoomSessionEvent = useCallback((event: RoomSessionEvent) =>
|
||||||
|
{
|
||||||
|
switch(event.type)
|
||||||
|
{
|
||||||
|
case RoomSessionEvent.STARTED:
|
||||||
|
setNeedsRoomInsert(true);
|
||||||
|
break;
|
||||||
|
case RoomSessionEvent.ENDED:
|
||||||
|
//dispatchUiEvent(new ChatHistoryEvent(ChatHistoryEvent.HIDE_CHAT_HISTORY));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useRoomSessionManagerEvent(RoomSessionEvent.ENDED, onRoomSessionEvent);
|
||||||
|
useRoomSessionManagerEvent(RoomSessionEvent.STARTED, onRoomSessionEvent);
|
||||||
|
|
||||||
|
const onRoomInfoEvent = useCallback((event: RoomInfoEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
if(!parser) return;
|
||||||
|
|
||||||
|
const session = GetRoomSession();
|
||||||
|
|
||||||
|
if(!session || (session.roomId !== parser.data.roomId)) return;
|
||||||
|
|
||||||
|
if(needsRoomInsert)
|
||||||
|
{
|
||||||
|
const chatEntry: IChatEntry = { id: parser.data.roomId, name: parser.data.roomName, timestamp: currentDate(), type: ChatEntryType.TYPE_ROOM_INFO };
|
||||||
|
|
||||||
|
addChatEntry(chatEntry);
|
||||||
|
|
||||||
|
setNeedsRoomInsert(false);
|
||||||
|
}
|
||||||
|
}, [addChatEntry, needsRoomInsert]);
|
||||||
|
|
||||||
|
CreateMessageHook(RoomInfoEvent, onRoomInfoEvent);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
32
src/views/chat-history/ChatHistoryView.scss
Normal file
32
src/views/chat-history/ChatHistoryView.scss
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
.nitro-chat-history
|
||||||
|
{
|
||||||
|
width: 250px;
|
||||||
|
|
||||||
|
padding: 2px;
|
||||||
|
background-color: #1C323F;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.5);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
|
||||||
|
.container-fluid
|
||||||
|
{
|
||||||
|
.nitro-card-header
|
||||||
|
{
|
||||||
|
background-color: #3d5f6e;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
background-color: #1C323F;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-history-content
|
||||||
|
{
|
||||||
|
background-color: #1C323F;
|
||||||
|
|
||||||
|
.chat-history-container
|
||||||
|
{
|
||||||
|
background-color: #1C323F;
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
128
src/views/chat-history/ChatHistoryView.tsx
Normal file
128
src/views/chat-history/ChatHistoryView.tsx
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import { FC, useCallback, useEffect, useReducer, useRef, useState } from 'react';
|
||||||
|
import { AutoSizer, CellMeasurer, CellMeasurerCache, List, ListRowProps, ListRowRenderer } from 'react-virtualized';
|
||||||
|
import { ChatHistoryEvent } from '../../events/chat-history/ChatHistoryEvent';
|
||||||
|
import { useUiEvent } from '../../hooks';
|
||||||
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../layout';
|
||||||
|
import { ChatHistoryMessageHandler } from './ChatHistoryMessageHandler';
|
||||||
|
import { ChatHistoryContextProvider } from './context/ChatHistoryContext';
|
||||||
|
import { ChatEntryType, ChatHistoryReducer, initialChatHistory } from './reducers/ChatHistoryReducer';
|
||||||
|
|
||||||
|
export const ChatHistoryView: FC<{}> = props =>
|
||||||
|
{
|
||||||
|
const [ isVisible, setIsVisible ] = useState(false);
|
||||||
|
const [ chatHistoryState, dispatchChatHistoryState ] = useReducer(ChatHistoryReducer, initialChatHistory);
|
||||||
|
const { chats = null } = chatHistoryState;
|
||||||
|
const elementRef = useRef<List>(null);
|
||||||
|
|
||||||
|
const onChatHistoryEvent = useCallback((event: ChatHistoryEvent) =>
|
||||||
|
{
|
||||||
|
switch(event.type)
|
||||||
|
{
|
||||||
|
case ChatHistoryEvent.SHOW_CHAT_HISTORY:
|
||||||
|
setIsVisible(true);
|
||||||
|
break;
|
||||||
|
case ChatHistoryEvent.HIDE_CHAT_HISTORY:
|
||||||
|
setIsVisible(false);
|
||||||
|
break;
|
||||||
|
case ChatHistoryEvent.TOGGLE_CHAT_HISTORY:
|
||||||
|
setIsVisible(!isVisible);
|
||||||
|
break;
|
||||||
|
case ChatHistoryEvent.CHAT_HISTORY_CHANGED:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, [isVisible]);
|
||||||
|
|
||||||
|
useUiEvent(ChatHistoryEvent.HIDE_CHAT_HISTORY, onChatHistoryEvent);
|
||||||
|
useUiEvent(ChatHistoryEvent.SHOW_CHAT_HISTORY, onChatHistoryEvent);
|
||||||
|
useUiEvent(ChatHistoryEvent.TOGGLE_CHAT_HISTORY, onChatHistoryEvent);
|
||||||
|
useUiEvent(ChatHistoryEvent.CHAT_HISTORY_CHANGED, onChatHistoryEvent);
|
||||||
|
|
||||||
|
const cache = new CellMeasurerCache({
|
||||||
|
defaultHeight: 25,
|
||||||
|
fixedWidth: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const RowRenderer: ListRowRenderer = (props: ListRowProps) =>
|
||||||
|
{
|
||||||
|
const item = chats[props.index];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CellMeasurer
|
||||||
|
cache={cache}
|
||||||
|
columnIndex={0}
|
||||||
|
key={props.key}
|
||||||
|
parent={props.parent}
|
||||||
|
rowIndex={props.index}
|
||||||
|
>
|
||||||
|
<div key={props.key} style={props.style} className="row chatlog-entry justify-content-start">
|
||||||
|
{(item.type === ChatEntryType.TYPE_CHAT) &&
|
||||||
|
<>
|
||||||
|
<div className="col-auto text-center">{item.timestamp}</div>
|
||||||
|
<div className="col-auto justify-content-start username">
|
||||||
|
<span className="fw-bold cursor-pointer" dangerouslySetInnerHTML={ { __html: item.name }} />
|
||||||
|
</div>
|
||||||
|
<div className="col justify-content-start h-100">
|
||||||
|
<span className="text-break text-wrap h-100">{item.message}</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
{(item.type === ChatEntryType.TYPE_ROOM_INFO) &&
|
||||||
|
<>
|
||||||
|
<div className="col-auto text-center">{item.timestamp}</div>
|
||||||
|
<div className="col-auto justify-content-start username">
|
||||||
|
<span className="fw-bold cursor-pointer">{item.name}</span>
|
||||||
|
</div>
|
||||||
|
<div className="col justify-content-start h-100">
|
||||||
|
<span className="text-break text-wrap h-100">{item.message}</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</CellMeasurer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(elementRef && elementRef.current && isVisible)
|
||||||
|
{
|
||||||
|
elementRef.current.scrollToRow(chats.length - 1);
|
||||||
|
}
|
||||||
|
console.log(chats.length);
|
||||||
|
}, [chats, isVisible])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChatHistoryContextProvider value={ { chatHistoryState, dispatchChatHistoryState } }>
|
||||||
|
<ChatHistoryMessageHandler />
|
||||||
|
{isVisible &&
|
||||||
|
<NitroCardView uniqueKey="chat-history" className="nitro-chat-history" simple={ false }>
|
||||||
|
<NitroCardHeaderView headerText={ 'Chat History' } onCloseClick={ event => setIsVisible(false) } />
|
||||||
|
<NitroCardContentView className="chat-history-content" style={{ backgroundColor: '#1C323F !important' }}>
|
||||||
|
<div className="row w-100 h-100 chat-history-container">
|
||||||
|
<AutoSizer defaultWidth={250} defaultHeight={200}>
|
||||||
|
{({ height, width }) =>
|
||||||
|
{
|
||||||
|
cache.clearAll();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<List
|
||||||
|
ref={elementRef}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
rowCount={chats.length}
|
||||||
|
rowHeight={cache.rowHeight}
|
||||||
|
className={'chat-history-list'}
|
||||||
|
rowRenderer={RowRenderer}
|
||||||
|
deferredMeasurementCache={cache} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</AutoSizer>
|
||||||
|
</div>
|
||||||
|
</NitroCardContentView>
|
||||||
|
</NitroCardView>
|
||||||
|
}
|
||||||
|
</ChatHistoryContextProvider>
|
||||||
|
);
|
||||||
|
}
|
14
src/views/chat-history/context/ChatHistoryContext.tsx
Normal file
14
src/views/chat-history/context/ChatHistoryContext.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { createContext, FC, useContext } from 'react';
|
||||||
|
import { ChatHistoryContextProps, IChatHistoryContext } from './ChatHistoryContext.types';
|
||||||
|
|
||||||
|
const ChatHistoryContext = createContext<IChatHistoryContext>({
|
||||||
|
chatHistoryState: null,
|
||||||
|
dispatchChatHistoryState: null
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChatHistoryContextProvider: FC<ChatHistoryContextProps> = props =>
|
||||||
|
{
|
||||||
|
return <ChatHistoryContext.Provider value={ props.value }>{ props.children }</ChatHistoryContext.Provider>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useChatHistoryContext = () => useContext(ChatHistoryContext);
|
13
src/views/chat-history/context/ChatHistoryContext.types.ts
Normal file
13
src/views/chat-history/context/ChatHistoryContext.types.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Dispatch, ProviderProps } from 'react';
|
||||||
|
import { IChatHistoryAction, IChatHistoryState } from '../reducers/ChatHistoryReducer';
|
||||||
|
|
||||||
|
export interface IChatHistoryContext
|
||||||
|
{
|
||||||
|
chatHistoryState: IChatHistoryState;
|
||||||
|
dispatchChatHistoryState: Dispatch<IChatHistoryAction>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChatHistoryContextProps extends ProviderProps<IChatHistoryContext>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
66
src/views/chat-history/reducers/ChatHistoryReducer.tsx
Normal file
66
src/views/chat-history/reducers/ChatHistoryReducer.tsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { Reducer } from 'react';
|
||||||
|
|
||||||
|
export interface IChatHistoryState {
|
||||||
|
chats: IChatEntry[];
|
||||||
|
roomHistory: IRoomHistoryEntry[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IChatEntry {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
look?: string;
|
||||||
|
message?: string;
|
||||||
|
timestamp: string;
|
||||||
|
type: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRoomHistoryEntry {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChatEntryType
|
||||||
|
{
|
||||||
|
public static TYPE_CHAT = 1;
|
||||||
|
public static TYPE_ROOM_INFO = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IChatHistoryAction
|
||||||
|
{
|
||||||
|
type: string;
|
||||||
|
payload: {
|
||||||
|
chats?: IChatEntry[];
|
||||||
|
roomHistory?: IRoomHistoryEntry[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChatHistoryAction
|
||||||
|
{
|
||||||
|
public static SET_CHATS: string = 'CHA_SET_CHATS';
|
||||||
|
public static SET_ROOM_HISTORY: string = 'CHA_SET_ROOM_HISTORY';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initialChatHistory: IChatHistoryState = {
|
||||||
|
chats: [],
|
||||||
|
roomHistory: []
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CHAT_HISTORY_MAX = 1000;
|
||||||
|
export const ROOM_HISTORY_MAX = 10;
|
||||||
|
|
||||||
|
export const ChatHistoryReducer: Reducer<IChatHistoryState, IChatHistoryAction> = (state, action) =>
|
||||||
|
{
|
||||||
|
switch(action.type)
|
||||||
|
{
|
||||||
|
case ChatHistoryAction.SET_CHATS: {
|
||||||
|
const chats = (action.payload.chats || state.chats || null);
|
||||||
|
|
||||||
|
return { ...state, chats };
|
||||||
|
}
|
||||||
|
case ChatHistoryAction.SET_ROOM_HISTORY: {
|
||||||
|
const roomHistory = (action.payload.roomHistory || state.roomHistory || null);
|
||||||
|
|
||||||
|
return { ...state, roomHistory };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
src/views/chat-history/utils/Utilities.ts
Normal file
5
src/views/chat-history/utils/Utilities.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export const currentDate = () =>
|
||||||
|
{
|
||||||
|
const currentTime = new Date();
|
||||||
|
return `${currentTime.getHours()}:${currentTime.getMinutes()}`;
|
||||||
|
}
|
@ -7,6 +7,7 @@ import { AchievementsView } from '../achievements/AchievementsView';
|
|||||||
import { AvatarEditorView } from '../avatar-editor/AvatarEditorView';
|
import { AvatarEditorView } from '../avatar-editor/AvatarEditorView';
|
||||||
import { CameraWidgetView } from '../camera/CameraWidgetView';
|
import { CameraWidgetView } from '../camera/CameraWidgetView';
|
||||||
import { CatalogView } from '../catalog/CatalogView';
|
import { CatalogView } from '../catalog/CatalogView';
|
||||||
|
import { ChatHistoryView } from '../chat-history/ChatHistoryView';
|
||||||
import { FriendsView } from '../friends/FriendsView';
|
import { FriendsView } from '../friends/FriendsView';
|
||||||
import { GroupsView } from '../groups/GroupsView';
|
import { GroupsView } from '../groups/GroupsView';
|
||||||
import { HelpView } from '../help/HelpView';
|
import { HelpView } from '../help/HelpView';
|
||||||
@ -58,6 +59,7 @@ export const MainView: FC<MainViewProps> = props =>
|
|||||||
<ToolbarView isInRoom={ !landingViewVisible } />
|
<ToolbarView isInRoom={ !landingViewVisible } />
|
||||||
<ModToolsView />
|
<ModToolsView />
|
||||||
<RoomHostView />
|
<RoomHostView />
|
||||||
|
<ChatHistoryView />
|
||||||
<WiredView />
|
<WiredView />
|
||||||
<AvatarEditorView />
|
<AvatarEditorView />
|
||||||
<AchievementsView />
|
<AchievementsView />
|
||||||
|
@ -3,6 +3,7 @@ import classNames from 'classnames';
|
|||||||
import { FC, useCallback, useState } from 'react';
|
import { FC, useCallback, useState } from 'react';
|
||||||
import { LocalizeText, RoomWidgetZoomToggleMessage } from '../../../../api';
|
import { LocalizeText, RoomWidgetZoomToggleMessage } from '../../../../api';
|
||||||
import { NavigatorEvent } from '../../../../events';
|
import { NavigatorEvent } from '../../../../events';
|
||||||
|
import { ChatHistoryEvent } from '../../../../events/chat-history/ChatHistoryEvent';
|
||||||
import { dispatchUiEvent } from '../../../../hooks/events';
|
import { dispatchUiEvent } from '../../../../hooks/events';
|
||||||
import { SendMessageHook } from '../../../../hooks/messages';
|
import { SendMessageHook } from '../../../../hooks/messages';
|
||||||
import { useRoomContext } from '../../context/RoomContext';
|
import { useRoomContext } from '../../context/RoomContext';
|
||||||
@ -27,6 +28,8 @@ export const RoomToolsWidgetView: FC<{}> = props =>
|
|||||||
setIsZoomedIn(value => !value);
|
setIsZoomedIn(value => !value);
|
||||||
return;
|
return;
|
||||||
case 'chat_history':
|
case 'chat_history':
|
||||||
|
dispatchUiEvent(new ChatHistoryEvent(ChatHistoryEvent.TOGGLE_CHAT_HISTORY));
|
||||||
|
//setIsExpanded(false); close this ??
|
||||||
return;
|
return;
|
||||||
case 'like_room':
|
case 'like_room':
|
||||||
if(isLiked) return;
|
if(isLiked) return;
|
||||||
|
Loading…
Reference in New Issue
Block a user