feat: add broadcast emit and rename message event
parent
519aac58a2
commit
8f12e7d06b
|
@ -9,7 +9,7 @@ import { Server } from 'socket.io';
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
const io = new Server(server, {
|
const io = new Server(server, {
|
||||||
cors: {
|
cors: {
|
||||||
origin: corsUrl
|
origin: corsUrl,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -35,11 +35,21 @@ io.on('connection', (socket) => {
|
||||||
// );
|
// );
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('message', (message, callback) => {
|
socket.on('broadcast', (broadcast, callback) => {
|
||||||
|
console.log('PRODUCTION SERVER: Broadcast ', broadcast);
|
||||||
|
io.emit('broadcast', broadcast);
|
||||||
|
if (callback) {
|
||||||
|
callback({
|
||||||
|
ok: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('private_message', (message, callback) => {
|
||||||
console.log('PRODUCTION SERVER: ', message);
|
console.log('PRODUCTION SERVER: ', message);
|
||||||
const { from: sourceSocketId, to: targetSocketId } = message;
|
const { from: sourceSocketId, to: targetSocketId } = message;
|
||||||
io.to(targetSocketId).emit('message', message);
|
io.to(targetSocketId).emit('private_message', message);
|
||||||
io.to(sourceSocketId).emit('message', message);
|
io.to(sourceSocketId).emit('private_message', message);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback({
|
callback({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { createStyles, Card, Container, Text, ScrollArea, Avatar } from "@mantine/core";
|
import { createStyles, Card, Container, Text, ScrollArea, Avatar } from "@mantine/core";
|
||||||
import useSocketStore from "@/store/socket";
|
import useSocketStore from "@/store/socket";
|
||||||
import { useEffect, useRef, useState, useLayoutEffect } from "react";
|
import { useEffect, useRef, useState, useLayoutEffect } from "react";
|
||||||
import { MessageWithMe, SocketMessage } from "@/types/next";
|
import { MessageWithMe, SocketPrivateMessage, SocketBroadcastMessage } from "@/types/next";
|
||||||
import ChatroomTitle from "@/components/ChatroomTitle";
|
import ChatroomTitle from "@/components/ChatroomTitle";
|
||||||
import ChatroomInput from "@/components/ChatroomInput";
|
import ChatroomInput from "@/components/ChatroomInput";
|
||||||
|
|
||||||
|
@ -63,7 +63,6 @@ const useStyles = createStyles((theme) => ({
|
||||||
flexWrap: "nowrap",
|
flexWrap: "nowrap",
|
||||||
fontSize: theme.fontSizes.xs,
|
fontSize: theme.fontSizes.xs,
|
||||||
color: theme.colors.gray[5],
|
color: theme.colors.gray[5],
|
||||||
marginLeft: theme.spacing.xs,
|
|
||||||
marginRight: theme.spacing.xs,
|
marginRight: theme.spacing.xs,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
|
@ -82,7 +81,7 @@ const useStyles = createStyles((theme) => ({
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
const { socket, connect } = useSocketStore((state) => state); // deconstructing socket and its method from socket store
|
const { socket, connect } = useSocketStore(); // deconstructing socket and its method from socket store
|
||||||
const chatViewportRef = useRef<HTMLDivElement>(null); // binding chat viewport ref to scroll to bottom
|
const chatViewportRef = useRef<HTMLDivElement>(null); // binding chat viewport ref to scroll to bottom
|
||||||
const [targetSocketId, setTargetSocketId] = useState<string>(""); // target socket id input value
|
const [targetSocketId, setTargetSocketId] = useState<string>(""); // target socket id input value
|
||||||
const [messages, setMessages] = useState<MessageWithMe[]>([]); // show messages on ScrollArea
|
const [messages, setMessages] = useState<MessageWithMe[]>([]); // show messages on ScrollArea
|
||||||
|
@ -102,15 +101,19 @@ export default function Home() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!socket) return;
|
if (!socket) return;
|
||||||
socket.on("message", (message: SocketMessage) => {
|
socket.on("private_message", (message: SocketPrivateMessage) => {
|
||||||
|
setMessages((state) => [...state, { ...message, me: message.from === socket?.id }]);
|
||||||
|
});
|
||||||
|
socket.on("broadcast", (message: SocketBroadcastMessage) => {
|
||||||
setMessages((state) => [...state, { ...message, me: message.from === socket?.id }]);
|
setMessages((state) => [...state, { ...message, me: message.from === socket?.id }]);
|
||||||
});
|
});
|
||||||
socket.on("online_user", (onlineUsers: Record<string, string>) => {
|
socket.on("online_user", (onlineUsers: Record<string, string>) => {
|
||||||
setOnlineUsers(onlineUsers);
|
setOnlineUsers(onlineUsers);
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
socket?.off("message");
|
socket.off("private_message");
|
||||||
socket?.off("online_user");
|
socket.off("online_user");
|
||||||
|
socket.off("broadcast");
|
||||||
};
|
};
|
||||||
}, [socket]);
|
}, [socket]);
|
||||||
|
|
||||||
|
|
|
@ -13,21 +13,43 @@ const useStyles = createStyles((theme) => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
const ChatroomInput: FC<Props> = ({ targetSocketId }) => {
|
const ChatroomInput: FC<Props> = ({ targetSocketId }) => {
|
||||||
const { socket, emit } = useSocketStore((state) => state); // deconstructing socket and its method from socket store
|
const { socket, emitMode, emit } = useSocketStore(); // deconstructing socket and its method from socket store
|
||||||
const [message, setMessage] = useState(""); // message input value
|
const [message, setMessage] = useState(""); // message input value
|
||||||
const messageInputRef = useRef<HTMLTextAreaElement>(null); // binding message input ref to focus
|
const messageInputRef = useRef<HTMLTextAreaElement>(null); // binding message input ref to focus
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
|
|
||||||
const sendMessage = () => {
|
const sendMessage = () => {
|
||||||
console.log("sendMessage", message === "");
|
|
||||||
if (!message) return toast.error("Please enter a message");
|
if (!message) return toast.error("Please enter a message");
|
||||||
if (!socket?.connected) return toast.error("Please reconnect server first");
|
if (!socket?.connected) return toast.error("Please reconnect server first");
|
||||||
if (!targetSocketId) return toast.error("Please enter a target socket id");
|
if (!targetSocketId && emitMode === "private_message")
|
||||||
emit("message", { from: socket?.id, to: targetSocketId, timestamp: Date.now(), message });
|
return toast.error("Please enter a target socket id");
|
||||||
|
|
||||||
|
switch (emitMode) {
|
||||||
|
case "private_message": {
|
||||||
|
const messageObj = {
|
||||||
|
from: socket?.id,
|
||||||
|
to: targetSocketId,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
emit("private_message", messageObj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "broadcast": {
|
||||||
|
const broadcastObj = {
|
||||||
|
from: socket?.id,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
emit("broadcast", broadcastObj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
setMessage("");
|
setMessage("");
|
||||||
messageInputRef.current?.focus();
|
messageInputRef.current?.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
|
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
if (e.key === "Enter" && e.altKey !== true && e.shiftKey !== true && e.ctrlKey !== true) {
|
if (e.key === "Enter" && e.altKey !== true && e.shiftKey !== true && e.ctrlKey !== true) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import {
|
import {
|
||||||
Group,
|
Group,
|
||||||
Text,
|
Text,
|
||||||
Input,
|
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
Popover,
|
Popover,
|
||||||
CopyButton,
|
CopyButton,
|
||||||
|
@ -10,6 +9,10 @@ import {
|
||||||
Avatar as MantineAvatar,
|
Avatar as MantineAvatar,
|
||||||
Indicator,
|
Indicator,
|
||||||
Menu,
|
Menu,
|
||||||
|
SegmentedControl,
|
||||||
|
Center,
|
||||||
|
Box,
|
||||||
|
TextInput,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import {
|
import {
|
||||||
IconPlug,
|
IconPlug,
|
||||||
|
@ -19,6 +22,9 @@ import {
|
||||||
IconChevronDown,
|
IconChevronDown,
|
||||||
IconUserCog,
|
IconUserCog,
|
||||||
IconUser,
|
IconUser,
|
||||||
|
IconBroadcast,
|
||||||
|
IconUserShare,
|
||||||
|
IconClipboard,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import { SetStateAction, Dispatch, FC, useEffect, useState } from "react";
|
import { SetStateAction, Dispatch, FC, useEffect, useState } from "react";
|
||||||
import useSocketStore from "@/store/socket";
|
import useSocketStore from "@/store/socket";
|
||||||
|
@ -31,18 +37,20 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
|
const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
|
||||||
const { socket, connect, disconnect } = useSocketStore(); // deconstructing socket and its method from socket store
|
const { socket, emitMode, setEmitMode, connect, disconnect } = useSocketStore(); // deconstructing socket and its method from socket store
|
||||||
|
|
||||||
const [popoverOpened, setPopoverOpened] = useState(false); // control popover open/close
|
const [popoverOpened, setPopoverOpened] = useState(false); // control popover open/close
|
||||||
const [onlineUsers, setOnlineUsers] = useState<Record<string, string>>({}); // online users
|
const [onlineUsers, setOnlineUsers] = useState<Record<string, string>>({}); // online users
|
||||||
|
const [opened, setOpened] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
socket?.on("online_user", (onlineUsers: Record<string, string>) => {
|
if (!socket) return;
|
||||||
|
socket.on("online_user", (onlineUsers: Record<string, string>) => {
|
||||||
setOnlineUsers(onlineUsers);
|
setOnlineUsers(onlineUsers);
|
||||||
console.log("online_user", onlineUsers);
|
console.log("online_user", onlineUsers);
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
socket?.off("online_user");
|
socket.off("online_user");
|
||||||
};
|
};
|
||||||
}, [socket]);
|
}, [socket]);
|
||||||
|
|
||||||
|
@ -118,25 +126,96 @@ const ChatroomTitle: FC<Props> = ({ targetSocketId, setTargetSocketId }) => {
|
||||||
</Popover.Dropdown>
|
</Popover.Dropdown>
|
||||||
</Popover>
|
</Popover>
|
||||||
</Group>
|
</Group>
|
||||||
<Group noWrap w={220}>
|
<Group noWrap>
|
||||||
<Text w={20}>To:</Text>
|
<Popover
|
||||||
<Input
|
trapFocus
|
||||||
w={170}
|
position="bottom"
|
||||||
placeholder="Target Socket ID"
|
offset={{
|
||||||
value={targetSocketId}
|
mainAxis: 5,
|
||||||
onChange={(e) => setTargetSocketId(e.currentTarget.value)}
|
crossAxis: 30,
|
||||||
/>
|
}}
|
||||||
<Menu shadow="md" width={200}>
|
shadow="md"
|
||||||
|
opened={opened}
|
||||||
|
onChange={setOpened}
|
||||||
|
>
|
||||||
|
<Popover.Target>
|
||||||
|
<SegmentedControl
|
||||||
|
size="xs"
|
||||||
|
value={emitMode}
|
||||||
|
onChange={(value: "broadcast" | "private_message") => {
|
||||||
|
setEmitMode(value);
|
||||||
|
if (value === "broadcast") {
|
||||||
|
setTargetSocketId("");
|
||||||
|
setOpened(false);
|
||||||
|
} else {
|
||||||
|
setOpened(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
value: "broadcast",
|
||||||
|
label: (
|
||||||
|
<Center>
|
||||||
|
<IconBroadcast size="1rem" />
|
||||||
|
<Box ml={10}>Broadcast</Box>
|
||||||
|
</Center>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "private_message",
|
||||||
|
label: (
|
||||||
|
<Center>
|
||||||
|
<IconUserShare
|
||||||
|
size="1rem"
|
||||||
|
onClick={() => setOpened(true)}
|
||||||
|
/>
|
||||||
|
<Box ml={10}>To</Box>
|
||||||
|
</Center>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Popover.Target>
|
||||||
|
<Popover.Dropdown
|
||||||
|
sx={(theme) => ({
|
||||||
|
background:
|
||||||
|
theme.colorScheme === "dark"
|
||||||
|
? theme.colors.dark[7]
|
||||||
|
: theme.white,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<TextInput
|
||||||
|
label="Socket id"
|
||||||
|
placeholder="Target Socket id"
|
||||||
|
size="xs"
|
||||||
|
value={targetSocketId}
|
||||||
|
onChange={(e) => setTargetSocketId(e.currentTarget.value)}
|
||||||
|
rightSection={
|
||||||
|
<ActionIcon variant="subtle">
|
||||||
|
<IconClipboard
|
||||||
|
size="1rem"
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.readText().then((text) => {
|
||||||
|
setTargetSocketId(text);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ActionIcon>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Popover.Dropdown>
|
||||||
|
</Popover>
|
||||||
|
<Menu shadow="md" width="fit-content">
|
||||||
<Menu.Target>
|
<Menu.Target>
|
||||||
<ActionIcon>
|
<ActionIcon variant="subtle">
|
||||||
<IconUserCog size="1.125rem" />
|
<IconUserCog size="1.25em" />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Menu.Target>
|
</Menu.Target>
|
||||||
|
|
||||||
<Menu.Dropdown>
|
<Menu.Dropdown>
|
||||||
<Menu.Label>
|
<Menu.Label>
|
||||||
{environment === "development"
|
{environment === "development"
|
||||||
? "Not available in development"
|
? "Not available" // in development mode, hide online user list because it's a server side feature
|
||||||
: "Online user"}
|
: "Online user"}
|
||||||
</Menu.Label>
|
</Menu.Label>
|
||||||
{socket?.connected &&
|
{socket?.connected &&
|
||||||
|
|
|
@ -23,7 +23,7 @@ const NameModal: FC<NameModalProps> = ({ opened, onClose }) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!socket) return;
|
if (!socket) return;
|
||||||
emit<SocketOnlineUser>("join", { socketId: socket.id, name: name });
|
emit("join", { socketId: socket.id, name: name });
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [socket?.id, name]);
|
}, [socket?.id, name]);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { NextApiResponseServerIO, SocketBroadcastMessage } from "@/types/next";
|
||||||
|
import { NextApiRequest } from "next";
|
||||||
|
|
||||||
|
const broadcast = (req: NextApiRequest, res: NextApiResponseServerIO) => {
|
||||||
|
if (req.method === "POST") {
|
||||||
|
// get message
|
||||||
|
const { from: sourceSocketId, timestamp, message } = req.body as SocketBroadcastMessage;
|
||||||
|
console.log("PRODUCTION SERVER: Broadcast ", message)
|
||||||
|
// dispatch to channel "message"
|
||||||
|
res?.socket?.server?.io?.emit("broadcast", {
|
||||||
|
from: sourceSocketId,
|
||||||
|
message,
|
||||||
|
timestamp,
|
||||||
|
} as SocketBroadcastMessage);
|
||||||
|
// return message
|
||||||
|
res.status(201).json(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default broadcast;
|
|
@ -1,7 +1,7 @@
|
||||||
import { NextApiResponseServerIO, SocketMessage } from "@/types/next";
|
import { NextApiResponseServerIO, SocketPrivateMessage } from "@/types/next";
|
||||||
import { NextApiRequest } from "next";
|
import { NextApiRequest } from "next";
|
||||||
|
|
||||||
const message = (req: NextApiRequest, res: NextApiResponseServerIO) => {
|
const private_message = (req: NextApiRequest, res: NextApiResponseServerIO) => {
|
||||||
if (req.method === "POST") {
|
if (req.method === "POST") {
|
||||||
// get message
|
// get message
|
||||||
const {
|
const {
|
||||||
|
@ -9,25 +9,25 @@ const message = (req: NextApiRequest, res: NextApiResponseServerIO) => {
|
||||||
to: targetSocketId,
|
to: targetSocketId,
|
||||||
timestamp,
|
timestamp,
|
||||||
message,
|
message,
|
||||||
} = req.body as SocketMessage;
|
} = req.body as SocketPrivateMessage;
|
||||||
|
|
||||||
// dispatch to channel "message"
|
// dispatch to channel "message"
|
||||||
res?.socket?.server?.io?.to(targetSocketId).emit("message", {
|
res?.socket?.server?.io?.to(targetSocketId).emit("private_message", {
|
||||||
from: sourceSocketId,
|
from: sourceSocketId,
|
||||||
to: targetSocketId,
|
to: targetSocketId,
|
||||||
message,
|
message,
|
||||||
timestamp,
|
timestamp,
|
||||||
} as SocketMessage);
|
} as SocketPrivateMessage);
|
||||||
res?.socket?.server?.io?.to(sourceSocketId).emit("message", {
|
res?.socket?.server?.io?.to(sourceSocketId).emit("private_message", {
|
||||||
from: sourceSocketId,
|
from: sourceSocketId,
|
||||||
to: targetSocketId,
|
to: targetSocketId,
|
||||||
message,
|
message,
|
||||||
timestamp,
|
timestamp,
|
||||||
} as SocketMessage);
|
} as SocketPrivateMessage);
|
||||||
|
|
||||||
// return message
|
// return message
|
||||||
res.status(201).json(message);
|
res.status(201).json(message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default message;
|
export default private_message;
|
|
@ -1,11 +1,20 @@
|
||||||
import { environment, SOCKET_URL } from "@/config";
|
import { environment, SOCKET_URL } from "@/config";
|
||||||
|
import { SocketBroadcastMessage, SocketOnlineUser, SocketPrivateMessage } from "@/types/next";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { io, Socket } from "socket.io-client";
|
import { io, Socket } from "socket.io-client";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
type EmitModeDataTypes = {
|
||||||
|
join: SocketOnlineUser;
|
||||||
|
broadcast: SocketBroadcastMessage;
|
||||||
|
private_message: SocketPrivateMessage;
|
||||||
|
};
|
||||||
|
|
||||||
type Store = {
|
type Store = {
|
||||||
socket: null | Socket;
|
socket: null | Socket;
|
||||||
emit: <T>(event: string, data: T) => void;
|
emitMode: "broadcast" | "private_message";
|
||||||
|
setEmitMode: (mode: "broadcast" | "private_message") => void;
|
||||||
|
emit: <T extends keyof EmitModeDataTypes>(event: T, data: EmitModeDataTypes[T]) => void;
|
||||||
connect: () => void;
|
connect: () => void;
|
||||||
disconnect: () => void;
|
disconnect: () => void;
|
||||||
};
|
};
|
||||||
|
@ -13,18 +22,22 @@ type Store = {
|
||||||
const useSocketStore = create<Store>((set, get) => {
|
const useSocketStore = create<Store>((set, get) => {
|
||||||
return {
|
return {
|
||||||
socket: null,
|
socket: null,
|
||||||
|
emitMode: "broadcast",
|
||||||
|
setEmitMode: (mode) => {
|
||||||
|
set({ emitMode: mode });
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Emits an event with data.
|
* Emits an event with data.
|
||||||
*
|
*
|
||||||
* @param event - The name of the event to emit.
|
* @param event - The name of the event to emit.
|
||||||
* @param data - The data to send along with the event.
|
* @param data - The data to send along with the event.
|
||||||
*/
|
*/
|
||||||
emit: <T>(event: string, data: T) => {
|
emit: (event, data) => {
|
||||||
console.log("emit", event, data);
|
// console.log("emit", event, data);
|
||||||
// Check if environment is development
|
// Check if environment is development
|
||||||
if (environment === "development") {
|
if (environment === "development") {
|
||||||
// Send a POST request to the /api/socket/message endpoint with the data
|
// Send a POST request to the /api/socket/${event} endpoint with the data
|
||||||
fetch("/api/socket/message", {
|
fetch(`/api/socket/${event}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
|
@ -10,24 +10,33 @@ export type NextApiResponseServerIO = NextApiResponse & {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SocketMessage = {
|
export type SocketPrivateMessage = {
|
||||||
from: string;
|
from: string;
|
||||||
to: string;
|
to: string;
|
||||||
message: string;
|
message: string;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SocketBroadcastMessage = {
|
||||||
|
from: string;
|
||||||
|
message: string;
|
||||||
|
timestamp: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type SocketOnlineUser = {
|
export type SocketOnlineUser = {
|
||||||
socketId: string;
|
socketId: string;
|
||||||
name: string | null;
|
name: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Originally, I used SocketMessage type, and only distinguish whether the message is from me or not by checking the socket id in "from" property.
|
* Originally, I used SocketPrivateMessage type, and only distinguish whether the message is from me or not by checking the socket id in "from" property.
|
||||||
* Then I can put the message on the right side of the ScrollArea if it is from me, and on the left side if it is not.
|
* Then I can put the message on the right side of the ScrollArea if it is from me, and on the left side if it is not.
|
||||||
* But I found when socket reconnects, the message on the right side will be moved to the left side because the "from" property is different.
|
* But I found when socket reconnects, the message on the right side will be moved to the left side because the "from" property is different.
|
||||||
* So I decided to add "me" property to distinguish whether the message is from me or not, and use it to put the message on the right side or left side.
|
* So I decided to add "me" property to distinguish whether the message is from me or not, and use it to put the message on the right side or left side.
|
||||||
*/
|
*/
|
||||||
export interface MessageWithMe extends SocketMessage {
|
export type MessageWithMe = {
|
||||||
|
from: string;
|
||||||
me: boolean;
|
me: boolean;
|
||||||
}
|
message: string;
|
||||||
|
timestamp: number;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue