handle more messages
This commit is contained in:
parent
f40ad116fe
commit
deb55c2c6d
5 changed files with 89 additions and 33 deletions
|
@ -1,5 +1,5 @@
|
|||
import { AppShell, Box, Button, Grid, Group, Input, Stack, TextInput } from "@mantine/core"
|
||||
import { useState } from "react"
|
||||
import { useState, createRef, useEffect } from "react"
|
||||
import { useDispatch, useSelector } from "react-redux"
|
||||
import ChannelList from "./components/ChannelList"
|
||||
import Header from "./components/Header"
|
||||
|
@ -12,16 +12,23 @@ function App() {
|
|||
const [opened, setOpened] = useState(false);
|
||||
const [message, setMessage] = useState("");
|
||||
const dispatch = useDispatch();
|
||||
const isConnected = useSelector((state: RootState) => state.chat.isConnected);
|
||||
const currentChannel = useSelector((state: RootState) => state.chat.currentChannel);
|
||||
const messages = useSelector((state: RootState) => state.chat.messages);
|
||||
|
||||
const chatDisabled = currentChannel === null;
|
||||
const messagesEndRef = createRef<HTMLDivElement>();
|
||||
|
||||
const chatDisabled = currentChannel === null || !isConnected;
|
||||
|
||||
const sendMessage = () => {
|
||||
dispatch(chatActions.sendMessage({ content: message }));
|
||||
setMessage("");
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
messagesEndRef.current?.scrollIntoView();
|
||||
}, [messages]);
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
padding={0}
|
||||
|
@ -30,13 +37,14 @@ function App() {
|
|||
header={<Header opened={opened} setOpened={setOpened} />}
|
||||
aside={<MemberList />}
|
||||
>
|
||||
<Stack sx={{ height: "calc(100vh - 60px)" }} spacing={0}>
|
||||
<Stack sx={{ overflowY: "auto", flexGrow: 1, flexShrink: 0 }} p="md">
|
||||
<Stack sx={{ height: "calc(100vh - 60px)", maxHeight: "calc(100vh - 60px)" }} spacing={0}>
|
||||
<Stack sx={{ overflowY: "scroll", flexGrow: 1 }} p="md">
|
||||
{messages.map((msg, i) => (
|
||||
<Message key={`msg${i}`} message={msg} />
|
||||
))}
|
||||
<div ref={messagesEndRef} />
|
||||
</Stack>
|
||||
<Group p="md" sx={(theme) => ({ borderTop: `1px solid ${theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[2]}`, flexGrow: 0, flexShrink: 0 })}>
|
||||
<Group p="md" sx={(theme) => ({ borderTop: `1px solid ${theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[2]}` })}>
|
||||
<TextInput sx={{ flexGrow: 1 }} placeholder="Message" disabled={chatDisabled} value={message} onChange={(event) => setMessage(event.target.value)} onKeyUp={(event) => {
|
||||
if (event.key === "Enter") {
|
||||
sendMessage();
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { Header as MHeader, Group, Text, MediaQuery, Burger, createStyles } from '@mantine/core';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { RootState } from '../store';
|
||||
|
||||
interface HeaderProps {
|
||||
opened: boolean
|
||||
|
@ -18,6 +20,7 @@ const useStyles = createStyles((theme) => ({
|
|||
|
||||
function Header(props: HeaderProps) {
|
||||
const { classes } = useStyles();
|
||||
const channelName = useSelector((state: RootState) => state.chat.currentChannel);
|
||||
|
||||
return (
|
||||
<MHeader height={60} p="md" sx={(theme) => ({
|
||||
|
@ -34,7 +37,7 @@ function Header(props: HeaderProps) {
|
|||
</MediaQuery>
|
||||
<Text size="xl" my="auto" className={classes.title}>Echo Web</Text>
|
||||
<MediaQuery smallerThan="sm" styles={{ display: 'none' }}>
|
||||
<Text size="md" my="auto">Channel 1</Text>
|
||||
<Text size="md" my="auto">{channelName}</Text>
|
||||
</MediaQuery>
|
||||
</Group>
|
||||
</MHeader>
|
||||
|
|
|
@ -7,19 +7,16 @@ interface MessageProps {
|
|||
|
||||
function Message(props: MessageProps) {
|
||||
return (
|
||||
<Group sx={{ flexWrap: "nowrap" }}>
|
||||
<Avatar color={props.message.color} radius="xl" sx={{ flexGrow: 0, flexShrink: 0, marginBottom: "auto" }}>LS</Avatar>
|
||||
<Stack spacing="xs" sx={{ flexGrow: 1, flexShrink: 1 }}>
|
||||
<Stack spacing={0} sx={{ flexGrow: 1, flexShrink: 1 }}>
|
||||
<Text>
|
||||
{props.message.author}
|
||||
<Text component="span" color={props.message.color}>{props.message.author}</Text>
|
||||
{' '}
|
||||
<Text component="span" size="sm" my="auto" color="dimmed">at {props.message.date}</Text>
|
||||
<Text component="span" size="sm" my="auto" color="dimmed">at {props.message.date.toLocaleString()}</Text>
|
||||
</Text>
|
||||
<Text>
|
||||
<Text sx={{ whiteSpace: "pre-wrap" }}>
|
||||
{props.message.content}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Group>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,8 @@ enum MessageType {
|
|||
ResCommandData = "commandData",
|
||||
ResChannelHistory = "channelHistory",
|
||||
ResErrorOccurred = "errorOccured",
|
||||
ResAdditionalHistory = "additionalHistory"
|
||||
ResAdditionalHistory = "additionalHistory",
|
||||
ResUserlistUpdate = "userlistUpdate"
|
||||
}
|
||||
|
||||
interface ServerMessage {
|
||||
|
@ -37,7 +38,7 @@ export interface ChatMessage {
|
|||
content: string;
|
||||
author: string;
|
||||
color: string;
|
||||
date: number;
|
||||
date: string;
|
||||
}
|
||||
|
||||
const defaultMetadata = ['Unknown', '#ffffff', '0'];
|
||||
|
@ -84,34 +85,63 @@ const chatMiddleware: Middleware = (store) => {
|
|||
case MessageType.ResServerData:
|
||||
store.dispatch(chatActions.updateChannels({ channels: JSON.parse(msg.data[0]) }));
|
||||
store.dispatch(chatActions.updateMotd({ motd: msg.data[1] }));
|
||||
store.dispatch(chatActions.updateUsers({ users: JSON.parse(msg.data[2]).map((user) => {
|
||||
store.dispatch(chatActions.setUsers({ users: JSON.parse(msg.data[2]).map((user: any) => {
|
||||
return user[0];
|
||||
})}));
|
||||
break;
|
||||
case MessageType.ResOutboundMessage:
|
||||
let metadata = msg.metadata || defaultMetadata;
|
||||
let outboundMessageMetadata = msg.metadata || defaultMetadata;
|
||||
store.dispatch(chatActions.recieveMessage({ message: {
|
||||
author: metadata[0],
|
||||
author: outboundMessageMetadata[0],
|
||||
content: msg.data,
|
||||
color: metadata[1],
|
||||
date: 0
|
||||
color: outboundMessageMetadata[1],
|
||||
date: (new Date(parseInt(outboundMessageMetadata[2]) * 1000)).toLocaleString()
|
||||
}}))
|
||||
break;
|
||||
case MessageType.ResCommandData:
|
||||
metadata = msg.metadata || defaultMetadata;
|
||||
let commandDataMetadata = msg.metadata || defaultMetadata;
|
||||
store.dispatch(chatActions.recieveMessage({ message: {
|
||||
author: metadata[0],
|
||||
content: msg.data,
|
||||
color: metadata[1],
|
||||
date: 0
|
||||
author: commandDataMetadata[0],
|
||||
content: msg.data.join("\n"),
|
||||
color: commandDataMetadata[1],
|
||||
date: (new Date(parseInt(commandDataMetadata[2]) * 1000)).toLocaleString()
|
||||
}}));
|
||||
break;
|
||||
case MessageType.ResChannelHistory:
|
||||
store.dispatch(chatActions.addMessages({
|
||||
messages: msg.data.map((message: any) => ({
|
||||
author: message[0],
|
||||
content: message[3],
|
||||
color: message[4],
|
||||
date: (new Date(parseInt(message[5]) * 1000)).toLocaleString()
|
||||
}))
|
||||
}))
|
||||
break;
|
||||
case MessageType.ResChannelUpdate:
|
||||
const [user, fromChannel, toChannel] = msg.data;
|
||||
store.dispatch(chatActions.updateUsers({
|
||||
user,
|
||||
fromChannel,
|
||||
toChannel
|
||||
}));
|
||||
break;
|
||||
case MessageType.ResConnectionTerminated:
|
||||
store.dispatch(chatActions.recieveMessage({ message: {
|
||||
author: "Server",
|
||||
content: msg.data,
|
||||
color: '#ff0000',
|
||||
date: (new Date()).toLocaleString()
|
||||
}}));
|
||||
socket?.close();
|
||||
break;
|
||||
case MessageType.ResUserlistUpdate:
|
||||
break;
|
||||
default:
|
||||
store.dispatch(chatActions.recieveMessage({ message: {
|
||||
author: 'Server',
|
||||
content: JSON.stringify(msg),
|
||||
color: '#ff0000',
|
||||
date: 0
|
||||
date: (new Date()).toLocaleString()
|
||||
}}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,17 +56,35 @@ const chatSlice = createSlice({
|
|||
}>) => {
|
||||
state.motd = action.payload.motd;
|
||||
},
|
||||
updateUsers: (state, action: PayloadAction<{
|
||||
setUsers: (state, action: PayloadAction<{
|
||||
users: string[]
|
||||
}>) => {
|
||||
state.users = action.payload.users;
|
||||
},
|
||||
updateUsers: (state, action: PayloadAction<{
|
||||
user: string,
|
||||
fromChannel: string,
|
||||
toChannel: string
|
||||
}>) => {
|
||||
if (action.payload.toChannel === state.currentChannel) {
|
||||
if (!state.users.includes(action.payload.user)) {
|
||||
state.users.push(action.payload.user);
|
||||
}
|
||||
} else {
|
||||
state.users = state.users.filter((user) => user !== action.payload.user);
|
||||
}
|
||||
},
|
||||
changeChannel: (state, action: PayloadAction<{
|
||||
channel: string
|
||||
}>) => {
|
||||
state.currentChannel = action.payload.channel;
|
||||
state.messages = [];
|
||||
},
|
||||
addMessages: (state, action: PayloadAction<{
|
||||
messages: ChatMessage[]
|
||||
}>) => {
|
||||
state.messages.push(...action.payload.messages);
|
||||
},
|
||||
sendMessage: (state, action: PayloadAction<{
|
||||
content: string
|
||||
}>) => {}
|
||||
|
|
Loading…
Reference in a new issue